mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
🆙 Catalog & Inventory preview now to original
This commit is contained in:
@@ -8,6 +8,14 @@ export const LayoutRoomPreviewerView: FC<{
|
||||
{
|
||||
const { roomPreviewer = null, height = 0 } = props;
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
// Counter that disables further renders once Pixi throws in this
|
||||
// previewer too many times in a row. The Pixi v8 null-texture bug
|
||||
// (see src/pixiPatch.ts) is mostly absorbed at the prototype level,
|
||||
// but any stray throw still cascades every animation frame. Allow
|
||||
// a small number of consecutive failures so a transient bad frame
|
||||
// self-recovers; permanently disable only if the previewer is truly
|
||||
// wedged, which is what produces the "disabling further renders"
|
||||
// log the user sees.
|
||||
const renderFailuresRef = useRef(0);
|
||||
const MAX_RENDER_FAILURES = 6;
|
||||
|
||||
@@ -62,6 +70,8 @@ export const LayoutRoomPreviewerView: FC<{
|
||||
canvas.height = 0;
|
||||
|
||||
elementRef.current.style.backgroundImage = `url(${ base64 })`;
|
||||
// A successful paint is the signal we've recovered from
|
||||
// a transient bad frame; reset the failure counter.
|
||||
renderFailuresRef.current = 0;
|
||||
}
|
||||
catch(error)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { GetAvatarRenderManager, GetSessionDataManager, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { GetAvatarRenderManager, GetRoomEngine, GetSessionDataManager, RoomObjectVariable, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect } from 'react';
|
||||
import { BuildPurchasableClothingFigure, FurniCategory, Offer, ProductTypeEnum } from '../../../../../api';
|
||||
import { AutoGrid, Column, LayoutGridItem, LayoutRoomPreviewerView } from '../../../../../common';
|
||||
@@ -19,7 +19,25 @@ export const CatalogViewProductWidgetView: FC<{}> = props =>
|
||||
if(!product) return;
|
||||
|
||||
roomPreviewer.reset(false);
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
|
||||
// Mirror the user's current room so the catalog preview shows
|
||||
// the item against the wallpaper / floor / landscape they
|
||||
// actually have decorated. Same approach as
|
||||
// InventoryFurnitureView - read the active room's pattern ids
|
||||
// off the room engine, fall back to '101' / '101' / '1.1' if
|
||||
// the user isn't in a room yet (those are real Habbo pattern
|
||||
// ids, the literal 'default' we used before is not and made
|
||||
// the previewer fall back to blank white surfaces).
|
||||
const roomEngine = GetRoomEngine();
|
||||
let floorType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE);
|
||||
let wallType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_WALL_TYPE);
|
||||
let landscapeType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE);
|
||||
|
||||
floorType = (floorType && floorType.length) ? floorType : '3002';
|
||||
wallType = (wallType && wallType.length) ? wallType : '3001';
|
||||
landscapeType = (landscapeType && landscapeType.length) ? landscapeType : '1.1';
|
||||
|
||||
roomPreviewer.updateObjectRoom(floorType, wallType, landscapeType);
|
||||
roomPreviewer.updateRoomWallsAndFloorVisibility(true, true);
|
||||
|
||||
const populate = () =>
|
||||
@@ -91,7 +109,7 @@ export const CatalogViewProductWidgetView: FC<{}> = props =>
|
||||
return;
|
||||
}
|
||||
default:
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
roomPreviewer.updateObjectRoom('101', '101', '1.1');
|
||||
roomPreviewer.addWallItemIntoRoom(product.productClassId, new Vector3d(90), product.extraParam);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ export const InventoryBotView: FC<{
|
||||
let floorType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE);
|
||||
let landscapeType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE);
|
||||
|
||||
wallType = (wallType && wallType.length) ? wallType : '101';
|
||||
floorType = (floorType && floorType.length) ? floorType : '101';
|
||||
wallType = (wallType && wallType.length) ? wallType : '3001';
|
||||
floorType = (floorType && floorType.length) ? floorType : '3002';
|
||||
landscapeType = (landscapeType && landscapeType.length) ? landscapeType : '1.1';
|
||||
|
||||
roomPreviewer.reset(false);
|
||||
|
||||
@@ -61,8 +61,8 @@ export const InventoryFurnitureView: FC<{
|
||||
let floorType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE);
|
||||
let landscapeType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE);
|
||||
|
||||
wallType = (wallType && wallType.length) ? wallType : '101';
|
||||
floorType = (floorType && floorType.length) ? floorType : '101';
|
||||
wallType = (wallType && wallType.length) ? wallType : '3001';
|
||||
floorType = (floorType && floorType.length) ? floorType : '3002';
|
||||
landscapeType = (landscapeType && landscapeType.length) ? landscapeType : '1.1';
|
||||
|
||||
roomPreviewer.updateRoomWallsAndFloorVisibility(true, true);
|
||||
@@ -83,7 +83,24 @@ export const InventoryFurnitureView: FC<{
|
||||
return;
|
||||
}
|
||||
|
||||
roomPreviewer.updateObjectRoom('default', 'default', 'default');
|
||||
// Mirror the active room's pattern ids so the furniture
|
||||
// preview lands on the same wallpaper / floor / landscape the
|
||||
// user is decorated with. Same fallback as the
|
||||
// isRoomDecoration branch above; 'default' isn't a real
|
||||
// pattern id, so passing it made the previewer fall back to
|
||||
// blank white surfaces.
|
||||
{
|
||||
const roomEngine = GetRoomEngine();
|
||||
let wallType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_WALL_TYPE);
|
||||
let floorType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE);
|
||||
let landscapeType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE);
|
||||
|
||||
wallType = (wallType && wallType.length) ? wallType : '3001';
|
||||
floorType = (floorType && floorType.length) ? floorType : '3002';
|
||||
landscapeType = (landscapeType && landscapeType.length) ? landscapeType : '1.1';
|
||||
|
||||
roomPreviewer.updateObjectRoom(floorType, wallType, landscapeType);
|
||||
}
|
||||
roomPreviewer.updateRoomWallsAndFloorVisibility(true, true);
|
||||
|
||||
if(selectedItem.isWallItem)
|
||||
|
||||
@@ -44,8 +44,8 @@ export const InventoryPetView: FC<{
|
||||
let floorType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_FLOOR_TYPE);
|
||||
let landscapeType = roomEngine.getRoomInstanceVariable<string>(roomEngine.activeRoomId, RoomObjectVariable.ROOM_LANDSCAPE_TYPE);
|
||||
|
||||
wallType = (wallType && wallType.length) ? wallType : '101';
|
||||
floorType = (floorType && floorType.length) ? floorType : '101';
|
||||
wallType = (wallType && wallType.length) ? wallType : '3001';
|
||||
floorType = (floorType && floorType.length) ? floorType : '3002';
|
||||
landscapeType = (landscapeType && landscapeType.length) ? landscapeType : '1.1';
|
||||
|
||||
roomPreviewer.reset(false);
|
||||
|
||||
@@ -12,10 +12,19 @@
|
||||
--catalog-swf-select-outer: #82d1ed;
|
||||
--catalog-swf-bc: #ff8d00;
|
||||
--catalog-swf-bc-outer: #ffb53c;
|
||||
/* Light gray secondary button - cropped from catalog_skin1.png
|
||||
at (10, 190, 25x22). Drives the gift button "Cadeau", the
|
||||
preview-room control button and the generic .nitro-catalog-swf-
|
||||
button via border-image-slice 3 3 3 3 fill. */
|
||||
--habbo-slice-button-default: url("../../assets/images/catalog/buttons/btn_secondary.png");
|
||||
--habbo-slice-button-hover: url("../../assets/images/catalog/buttons/btn_secondary_hover.png");
|
||||
--habbo-slice-button-pressed: url("../../assets/images/catalog/buttons/btn_secondary_pressed.png");
|
||||
--habbo-slice-button-disabled: url("../../assets/images/catalog/buttons/btn_secondary_disabled.png");
|
||||
/* Classic Habbo "Osta!" Buy button - cropped from catalog_skin3.png
|
||||
yellow band. The historical name says "green" but the user's
|
||||
skin sheet ships yellow for the action colour, so that's what
|
||||
we paint. The 27x34 sprite border-image-slices nicely at 6px
|
||||
since the rounded corner is ~5px. */
|
||||
--habbo-slice-button-buy: url("../../assets/images/catalog/buttons/buy.png");
|
||||
--habbo-slice-button-large: url("../../assets/images/catalog/buttons/buy.png");
|
||||
--habbo-slice-button-large-hover: url("../../assets/images/catalog/buttons/buy_hover.png");
|
||||
@@ -39,6 +48,9 @@
|
||||
--habbo-stepper-minus-hover: url("../../assets/images/catalog/buttons/minus_hover.png");
|
||||
--habbo-stepper-minus-pressed: url("../../assets/images/catalog/buttons/minus_pressed.png");
|
||||
--habbo-stepper-minus-disabled: url("../../assets/images/catalog/buttons/minus_disabled.png");
|
||||
/* Scrollbar sprites cropped from catalog_skin1.png. The single-piece
|
||||
thumb has caps + grip baked into one 17x34 image - stretch it
|
||||
full-height with background-size: 17px 100%. */
|
||||
--habbo-scrollbar-up: url("../../assets/images/catalog/scrollbar/scroll_v_up.png");
|
||||
--habbo-scrollbar-up-pressed: url("../../assets/images/catalog/scrollbar/scroll_v_up_pressed.png");
|
||||
--habbo-scrollbar-down: url("../../assets/images/catalog/scrollbar/scroll_v_down.png");
|
||||
@@ -877,6 +889,11 @@
|
||||
}
|
||||
|
||||
.nitro-catalog-classic-window .layout-grid-item {
|
||||
/* Let the tile flex to whatever min/max width the AutoGrid sets
|
||||
via repeat(auto-fill, minmax(N, 1fr)) - hard-pinning 53x74 was
|
||||
overriding the layout's columnMinWidth prop, so the row count
|
||||
never changed when we reduced it. Width is now 100% of the
|
||||
column cell, height tracks --nitro-grid-column-min-height. */
|
||||
width: 100% !important;
|
||||
height: var(--nitro-grid-column-min-height, 70px) !important;
|
||||
min-width: 0 !important;
|
||||
@@ -888,10 +905,19 @@
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/* Furni tiles drive their look from the icon image and need a clear
|
||||
background. Color-grouping swatches use itemHighlight (.has-highlight)
|
||||
to ask LayoutGridItem for a solid colour via inline backgroundColor -
|
||||
keep the transparent override off those so the swatch is visible. */
|
||||
.nitro-catalog-classic-window .layout-grid-item:not(.has-highlight) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* Pets breed picker: the pet-head image renders at its native ~81x69px
|
||||
regardless of the grid cell size. The old grid-cols-6 stretched
|
||||
each .layout-grid-item to ~150-200px so the head looked huge in
|
||||
acres of empty tile. Pin each tile just big enough to host the
|
||||
head and let the container flex-wrap. */
|
||||
.nitro-catalog-classic-window .nitro-catalog-classic-pet-breeds .layout-grid-item {
|
||||
width: 84px !important;
|
||||
min-width: 84px !important;
|
||||
@@ -912,6 +938,12 @@
|
||||
inset -2px -2px 0 #ecece4 !important;
|
||||
}
|
||||
|
||||
/* Habbo-classic colour swatches: small chip with a 1px dark border
|
||||
and a subtle inner highlight so light tones still read as buttons.
|
||||
Hover lifts the border; the selected swatch is "pressed" with a
|
||||
sunken inner shadow and a bright cyan ring matching the catalog
|
||||
selection accent. The cream inset from the generic .is-active rule
|
||||
above would wash out the swatch colour, so we replace it here. */
|
||||
.nitro-catalog-classic-window .layout-grid-item.has-highlight {
|
||||
width: 26px !important;
|
||||
height: 26px !important;
|
||||
@@ -975,10 +1007,18 @@
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
/* When the tile shows a full-tile bot/pet avatar (instead of a small
|
||||
icon), pin the price strip to the bottom of the tile and give it a
|
||||
translucent backdrop so it doesn't overlap with the avatar body. */
|
||||
.nitro-catalog-classic-grid .layout-grid-item:has(.avatar-image) .nitro-catalog-classic-grid-price,
|
||||
.nitro-catalog-classic-grid .layout-grid-item:has(> .avatar-image) > .nitro-catalog-classic-grid-price,
|
||||
.nitro-catalog-classic-grid .avatar-image ~ .nitro-catalog-classic-grid-price {
|
||||
top: auto !important;
|
||||
/* Re-anchor horizontally too: the parent rule's left: 2px /
|
||||
right: 2px combined with content-sized inner flex was visually
|
||||
parking the pill at the left side of the tile. Center it via
|
||||
explicit left/right + transform so it lands smack in the
|
||||
middle regardless of inner content width. */
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
bottom: 4px !important;
|
||||
@@ -996,6 +1036,10 @@
|
||||
z-index: 5 !important;
|
||||
}
|
||||
|
||||
/* Tighten the price entry inside the avatar-tile pill so the number
|
||||
and currency icon center on the same baseline (the global
|
||||
.grid-price-entry height: 13px clipped the 15px wallet icon and
|
||||
pushed it visually below the number). */
|
||||
.nitro-catalog-classic-grid .layout-grid-item:has(.avatar-image) .nitro-catalog-classic-grid-price-entry,
|
||||
.nitro-catalog-classic-grid .avatar-image ~ .nitro-catalog-classic-grid-price .nitro-catalog-classic-grid-price-entry {
|
||||
height: auto !important;
|
||||
@@ -1048,6 +1092,8 @@
|
||||
.nitro-catalog-classic-price-row {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
/* Anchored from the bottom so the Aantal/Prezzo row sits just
|
||||
above the Cadeau/Koop buttons regardless of layout height. */
|
||||
bottom: 38px;
|
||||
width: 360px;
|
||||
height: 25px;
|
||||
@@ -1065,6 +1111,8 @@
|
||||
|
||||
.nitro-catalog-classic-total-price-slot {
|
||||
position: absolute;
|
||||
/* Anchored to the right of the now-100% wide price row so the
|
||||
Prezzo + amount stays flush with the right edge of the panel. */
|
||||
right: 2px;
|
||||
top: 0;
|
||||
width: auto;
|
||||
@@ -1087,6 +1135,9 @@
|
||||
.nitro-catalog-classic-purchase-row {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
/* Anchored to the bottom of the panel with a 4px breathing strip
|
||||
so the Cadeau / Koop buttons stay flush at the bottom of the
|
||||
window no matter how tall the catalog is. */
|
||||
bottom: 4px;
|
||||
width: 360px;
|
||||
height: 30px;
|
||||
@@ -1100,6 +1151,9 @@
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 10px !important;
|
||||
/* Fill the now-100% wide purchase row instead of staying pinned at
|
||||
330px (which used to match the old 360px column - 15px each
|
||||
side). */
|
||||
width: auto;
|
||||
height: 24px;
|
||||
margin-left: 15px;
|
||||
@@ -1159,6 +1213,13 @@
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* Buy / Gift buttons - pure CSS. border-image-slicing the bitmap
|
||||
sprites produced thin highlight/shadow stripes at the top and
|
||||
bottom because the source rounded corners are ~5-6px tall but the
|
||||
buttons render at 22-24px, so the slice rows stretched into a
|
||||
visible band. CSS gradients give a crisp pixel-art classic-habbo
|
||||
look without those artefacts. */
|
||||
|
||||
.nitro-catalog-classic-window .nitro-catalog-swf-buy-button {
|
||||
width: 160px !important;
|
||||
min-width: 160px !important;
|
||||
@@ -1168,6 +1229,8 @@
|
||||
border-radius: 4px !important;
|
||||
border-image: none !important;
|
||||
border-image-source: none !important;
|
||||
/* Yellow body with the same #f0a318 / #ffd54d tones as the
|
||||
skin3-yellow Buy sprite. */
|
||||
background:
|
||||
linear-gradient(180deg, #ffe66b 0%, #ffc828 45%, #f0a318 100%) !important;
|
||||
box-shadow:
|
||||
@@ -1194,6 +1257,9 @@
|
||||
|
||||
.nitro-catalog-classic-window .nitro-catalog-swf-buy-button.pointer-events-none,
|
||||
.nitro-catalog-classic-window .nitro-catalog-swf-buy-button:disabled {
|
||||
/* Stay yellow when disabled - the user wants the action colour
|
||||
to be recognisable regardless of state. Drop opacity + flip
|
||||
the cursor so it still reads as non-interactive. */
|
||||
background:
|
||||
linear-gradient(180deg, #ffe66b 0%, #ffc828 45%, #f0a318 100%) !important;
|
||||
color: #4a2b00 !important;
|
||||
@@ -1211,6 +1277,7 @@
|
||||
border-radius: 4px !important;
|
||||
border-image: none !important;
|
||||
border-image-source: none !important;
|
||||
/* Cream / light-gray body matching the catalog cardstock. */
|
||||
background:
|
||||
linear-gradient(180deg, #ececec 0%, #cfcfc4 100%) !important;
|
||||
box-shadow:
|
||||
@@ -1243,12 +1310,19 @@
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Pet purchase card lives in a tight flex row alongside the price,
|
||||
so the main 160px Buy button doesn't fit. Shrink it down here. */
|
||||
.nitro-catalog-classic-pet-card .nitro-catalog-swf-buy-button {
|
||||
width: auto !important;
|
||||
min-width: 0 !important;
|
||||
padding: 0 14px !important;
|
||||
}
|
||||
|
||||
/* All catalog grids must scroll vertically only - horizontal overflow
|
||||
produces a stray horizontal scrollbar at the bottom of the items
|
||||
strip on narrow columns (e.g. guild_furni). minmax(N, 1fr) usually
|
||||
contains content but the safety net stops any odd item from
|
||||
triggering a horizontal bar. */
|
||||
.nitro-catalog-classic-window .layout-grid,
|
||||
.nitro-catalog-classic-window [class*="grid-cols-["] {
|
||||
overflow-x: hidden !important;
|
||||
@@ -1295,6 +1369,8 @@
|
||||
image-rendering: pixelated !important;
|
||||
}
|
||||
|
||||
/* react-icons FaMinus/FaPlus glyphs ride inside these buttons; hide
|
||||
them - the sprite already contains the +/- mark. */
|
||||
.nitro-catalog-classic-window button.nitro-catalog-swf-spinner-button svg {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -1390,6 +1466,9 @@
|
||||
min-width: 25px;
|
||||
padding: 0 !important;
|
||||
overflow: hidden;
|
||||
/* font-size: 0 was killing the SVG: react-icons emits
|
||||
<svg width="1em" height="1em">, so 0em -> 0x0. Use a real
|
||||
font-size and pin the SVG to explicit pixels below. */
|
||||
font-size: 14px !important;
|
||||
line-height: 1 !important;
|
||||
display: inline-flex !important;
|
||||
@@ -1416,6 +1495,13 @@
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
/* Bulletproof override for the rotate/state buttons. The shared SWF
|
||||
button rule above lays a transparent body + border-image skin on
|
||||
top, which works only when the catalog/buttons/btn_secondary*.png
|
||||
sprites resolve - if they're missing the button renders 0x0
|
||||
invisible. Pin the box and paint a visible gradient + outline so
|
||||
the controls are always discoverable, and force z-index above the
|
||||
room-previewer DIV so they sit on top of the rendered scene. */
|
||||
.nitro-catalog-classic-window button.nitro-catalog-classic-preview-btn {
|
||||
width: 28px !important;
|
||||
height: 26px !important;
|
||||
@@ -1456,6 +1542,12 @@
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
/* ===== Classic catalog scrollbar (pure CSS, no sprites) =====
|
||||
Drew this with CSS gradients instead of stretching the 17x34
|
||||
skin1 thumb sprite. The sprite version pixelated into visible
|
||||
horizontal bands on tall scroll areas because every source row
|
||||
stretched 5-10x. CSS gradients stay crisp at any height. */
|
||||
|
||||
.nitro-catalog-classic-window * {
|
||||
scrollbar-color: auto !important;
|
||||
scrollbar-width: auto;
|
||||
@@ -1474,6 +1566,10 @@
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
/* Habbo thumb: symmetric light-edges -> darker-middle gradient (the
|
||||
"pinched in the middle" look of the classic Ubuntu scrollbar),
|
||||
1px near-black outline, three central grip lines via SVG centered
|
||||
no-repeat. */
|
||||
.nitro-catalog-classic-window *::-webkit-scrollbar-thumb {
|
||||
min-height: 28px !important;
|
||||
border: 1px solid #2a2a26 !important;
|
||||
@@ -1503,6 +1599,8 @@
|
||||
inset 0 -1px 0 rgba(255, 255, 255, 0.25) !important;
|
||||
}
|
||||
|
||||
/* Arrow buttons: cream cap with a 1px black outline + dark inset
|
||||
chevron. SVG glyphs so they stay crisp at any zoom. */
|
||||
.nitro-catalog-classic-window *::-webkit-scrollbar-button:single-button:vertical:decrement {
|
||||
display: block !important;
|
||||
width: 17px !important;
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
.habbo-navigator-desktop {
|
||||
border: 1px solid #000;
|
||||
border-radius: 7px;
|
||||
background: #e9e9e1;
|
||||
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.35);
|
||||
color: #111;
|
||||
font-family: Ubuntu, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-header-shell {
|
||||
min-height: 32px;
|
||||
max-height: 32px;
|
||||
background: #418db0;
|
||||
border-bottom: 1px solid #000;
|
||||
border-radius: 6px 6px 0 0;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-title {
|
||||
color: #fff;
|
||||
font-family: UbuntuCondensed, Ubuntu, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-shadow: 1px 1px 0 #000;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-close-button {
|
||||
width: 19px;
|
||||
height: 20px;
|
||||
right: 6px;
|
||||
border: 2px solid #000;
|
||||
border-radius: 4px;
|
||||
background: #c73a32;
|
||||
box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-close-button::before,
|
||||
.habbo-navigator-desktop .nitro-card-close-button::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 2px;
|
||||
background: #fff;
|
||||
box-shadow: 1px 1px 0 #64120f;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-close-button::before {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-close-button::after {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-tabs-shell {
|
||||
justify-content: flex-start;
|
||||
gap: 0;
|
||||
min-height: 27px;
|
||||
max-height: 27px;
|
||||
padding: 4px 8px 0;
|
||||
background: #e9e9e1;
|
||||
border-bottom: 1px solid #b8b8ad;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-tab-item {
|
||||
min-height: 23px;
|
||||
margin-right: -1px;
|
||||
padding: 4px 12px 3px;
|
||||
border: 1px solid #555;
|
||||
border-bottom: 0;
|
||||
border-radius: 6px 6px 0 0;
|
||||
background-color: #d5d8cf;
|
||||
background-image: url("../../assets/images/navigator/swf/tab_bg_unsel.png");
|
||||
background-repeat: repeat-x;
|
||||
background-size: auto 100%;
|
||||
color: #111;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
box-shadow: inset 1px 1px 0 #fff;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-tab-item:hover {
|
||||
background-color: #e7e8df;
|
||||
background-image: url("../../assets/images/navigator/swf/tab_bg_hilite.png");
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-tab-item-active {
|
||||
z-index: 2;
|
||||
margin-bottom: -1px;
|
||||
background-color: #f4f4ed;
|
||||
background-image: url("../../assets/images/navigator/swf/tab_bg_sel.png");
|
||||
border-bottom: 1px solid #f4f4ed;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .habbo-navigator-desktop-content {
|
||||
padding: 8px 9px 9px;
|
||||
overflow: hidden;
|
||||
background: #f4f4ed;
|
||||
border-radius: 0 0 6px 6px;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .habbo-navigator-desktop-content input,
|
||||
.habbo-navigator-desktop .habbo-navigator-desktop-content select,
|
||||
.habbo-navigator-desktop .habbo-navigator-desktop-content textarea {
|
||||
height: 22px;
|
||||
border: 1px solid #a0a49c;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
background-image: url("../../assets/images/navigator/swf/hdr_search.png");
|
||||
background-repeat: repeat-x;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
box-shadow: inset 1px 1px 0 rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .habbo-navigator-desktop-content button,
|
||||
.habbo-navigator-desktop .habbo-navigator-desktop-content .btn {
|
||||
min-height: 22px;
|
||||
border: 1px solid #3a3a3a;
|
||||
border-radius: 4px;
|
||||
background-color: #d6d6d1;
|
||||
background-image: url("../../assets/images/navigator/swf/button.png");
|
||||
background-repeat: repeat-x;
|
||||
background-size: auto 100%;
|
||||
color: #111;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
box-shadow: inset 1px 1px 0 #fff;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-panel {
|
||||
border: 1px solid #babdb4;
|
||||
border-radius: 6px;
|
||||
background: #efefe8;
|
||||
box-shadow: inset 1px 1px 0 #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-card-panel > .flex:first-child {
|
||||
min-height: 28px;
|
||||
padding: 5px 8px;
|
||||
border-bottom: 1px solid #d3d5cd;
|
||||
background: #efefe8;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .navigator-grid {
|
||||
padding: 0 4px 5px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .navigator-item {
|
||||
min-height: 28px;
|
||||
margin: 2px 0;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 5px;
|
||||
background: #f5f5ef;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .navigator-item:nth-child(even) {
|
||||
background: #e7e8e0;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .navigator-item:hover {
|
||||
border-color: #777;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-navigator-search-saves-result {
|
||||
width: 155px;
|
||||
min-width: 155px;
|
||||
height: 100%;
|
||||
border: 1px solid #babdb4;
|
||||
border-radius: 6px;
|
||||
background: #efefe8;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-navigator-search-saves-result > .flex:first-child {
|
||||
min-height: 24px;
|
||||
border: 1px solid #d58e00;
|
||||
border-radius: 4px;
|
||||
background: #f8a900;
|
||||
box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-navigator-search-saves-result span {
|
||||
color: #111;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop .nitro-icon.icon-navigator-info {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop ::-webkit-scrollbar {
|
||||
width: 17px !important;
|
||||
height: 17px !important;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop ::-webkit-scrollbar-track {
|
||||
border: 0 !important;
|
||||
background-color: transparent !important;
|
||||
background-image: url("../../assets/images/catalog/swf/ubuntu_scrollbar_track_v_17x2.png") !important;
|
||||
background-repeat: repeat-y !important;
|
||||
background-position: center top !important;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop ::-webkit-scrollbar-thumb {
|
||||
min-height: 24px !important;
|
||||
border: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
background-color: transparent !important;
|
||||
background-image:
|
||||
url("../../assets/images/catalog/swf/ubuntu_scrollbar_thumb_v_grip_7x10.png"),
|
||||
url("../../assets/images/catalog/swf/ubuntu_scrollbar_thumb_v_top_17x2.png"),
|
||||
url("../../assets/images/catalog/swf/ubuntu_scrollbar_thumb_v_bottom_17x2.png"),
|
||||
url("../../assets/images/catalog/swf/ubuntu_scrollbar_thumb_v_mid_17x1.png") !important;
|
||||
background-repeat: repeat-y, no-repeat, no-repeat, repeat-y !important;
|
||||
background-position: center center, center top, center bottom, center top !important;
|
||||
background-size: 7px 10px, 17px 2px, 17px 2px, 17px 1px !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop ::-webkit-scrollbar-button {
|
||||
width: 17px !important;
|
||||
height: 16px !important;
|
||||
background-color: transparent !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop ::-webkit-scrollbar-button:single-button:vertical:decrement {
|
||||
background-image: url("../../assets/images/catalog/swf/ubuntu_scrollbar_up_17x16.png") !important;
|
||||
}
|
||||
|
||||
.habbo-navigator-desktop ::-webkit-scrollbar-button:single-button:vertical:increment {
|
||||
background-image: url("../../assets/images/catalog/swf/ubuntu_scrollbar_down_17x16.png") !important;
|
||||
}
|
||||
+33
-1
@@ -1,3 +1,34 @@
|
||||
/**
|
||||
* Runtime patches for pixi.js v8 batcher edge cases.
|
||||
*
|
||||
* Pixi v8 (through 8.19 at least) has a long-running family of crashes
|
||||
* where `getAdjustedBlendModeBlend(blendMode, textureSource)` is invoked
|
||||
* with a `null` textureSource and throws on `null.alphaMode` or
|
||||
* `null.uid`. We've seen it from at least four call sites:
|
||||
* - Batcher.break() (FilterPipe, StencilMaskPipe, AlphaMaskPipe)
|
||||
* - Batcher.checkAndUpdateTexture() (SpritePipe.validateRenderable)
|
||||
*
|
||||
* The trigger varies, but the symptom is always the same: a single bad
|
||||
* frame inside the catalog room previewer (or anywhere RoomSpriteCanvas
|
||||
* drives Pixi) tanks the whole render loop with an endless cascade of
|
||||
* requestAnimationFrame errors.
|
||||
*
|
||||
* We removed the two custom filters we owned (BlackToAlphaFilter,
|
||||
* PlaneMaskFilter) earlier, but several call sites are inside Pixi
|
||||
* itself or inside renderer-side mask setup that we can't sensibly
|
||||
* delete (RoomSpriteCanvas pins a Sprite mask on the master display to
|
||||
* clip the room to the canvas).
|
||||
*
|
||||
* Patch every known throwing entry point so that when it throws because
|
||||
* of a null textureSource we treat the frame as a no-op instead of
|
||||
* propagating the exception. The visible cost is a missed batch this
|
||||
* tick - the next tick re-renders cleanly. Without this patch the
|
||||
* LayoutRoomPreviewerView safety latch fires permanently on the first
|
||||
* affected offer.
|
||||
*
|
||||
* Importing this module has the side effect of installing the patch
|
||||
* exactly once, idempotent across HMR reloads.
|
||||
*/
|
||||
import * as PIXI from 'pixi.js';
|
||||
|
||||
type AnyFn = (...args: unknown[]) => unknown;
|
||||
@@ -64,8 +95,9 @@ const installPatch = (): void =>
|
||||
const proto = (ctor as { prototype?: MethodHost } | undefined)?.prototype;
|
||||
if(!proto) continue;
|
||||
|
||||
// break() is called during FilterPipe / StencilMaskPipe / AlphaMaskPipe.pop
|
||||
if(guardMethod(proto, 'break', name)) patched = true;
|
||||
|
||||
// checkAndUpdateTexture() is called during SpritePipe.validateRenderable
|
||||
if(guardMethod(proto, 'checkAndUpdateTexture', name)) patched = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user