Runtime-loaded visual re-skin system (no client rebuild, real themes never
hit git). A theme = a folder on the server (theme.base.url) with a manifest +
CSS "pieces"; each piece is toggled from Settings > Themes (checkboxes). A
broken/404 piece auto-falls back to the default (per piece). Hotel-wide default
via ui-config theme.default (+ theme.default.pieces), per-user override in
localStorage (same pattern as the catalog style toggle).
- api/theme/ThemeManager: fetch index/manifest + inject/remove <link> + fallback
- hooks/theme/useThemes: state + persist + default-from-config + live apply
- components/theme/ThemeApplier: applies on boot (mounted in MainView)
- UserSettings: General/Themes tabs with theme selector + per-piece checkboxes
- custom-themes/: reference template (demo theme "Neon Viola" + README)
- .gitignore: public/custom-themes/ (real themes are never committed)
Aggiunge un checkbox nelle impostazioni utente per scegliere lo stile del
catalogo (classico vs moderno) + flag globale catalog.classic.style in
ui-config.json come default per tutti. Override per-utente in localStorage.
Eliminate the parallel `tests/` tree. Each `*.test.ts` / `*.test.tsx`
now sits in the same directory as the module it covers, mirroring its
filename (`Foo.ts` ↔ `Foo.test.ts`). The renderer-SDK mock used by
component / hook tests moves to `src/__mocks__/nitro-renderer.ts` and
the Vitest setup file becomes `src/test-setup.ts` — both still wired
through `vitest.config.mts` exactly as before, only the paths changed.
All 13 suites + 178/178 cases still pass. The production build is
unaffected: rollup only follows imports from `src/index.tsx` and never
crosses into `.test.ts` files, so test code is naturally tree-shaken
out of the bundle. `yarn build` output is byte-for-byte the same on
the user-facing chunks.
tsconfig drops the now-redundant `tests` include entry. CLAUDE.md
'Layout convention' replaces the old `tests/` row with three rows
documenting the new co-located convention, the `__mocks__/` directory
and the `test-setup.ts` entry; ARCHITECTURE.md picks up the same
update. The 'DO NOT CHANGE' qualifier on the layout is preserved —
this rewrite IS the change, decided deliberately to make tests a
first-class part of the source tree rather than a sibling project.
- ProductImageUtility: 'CatalogPageMessageProductData.I' was clearly a
placeholder/typo in the WALL branch — getProductCategory's first
param is FurnitureType, so use the enclosing productType.
- YouTubePlayerView: IRoomUserData has webID, not userId. Two
spectator/watcher-list sites used the wrong field.
- AvatarInfoWidgetView REQUEST_MANIPULATION handler: avatarInfo is
IAvatarInfo (union); .category / .id only exist on AvatarInfoFurni.
Type-guard before reading.
- InfoStandWidgetPetView: deleted the duplicate local 'interface
AvatarInfoPet' — was shadowing the imported one. Drop AvatarInfoPet
from the import (local interface stands alone).
- FurnitureExternalImageView: missing GetSessionDataManager import (the
reportedUserId field reads it inline). Added.
- GroupCreatorView setGroupData call: null values for groupName /
groupDescription / groupColors / groupBadgeParts where IGroupData
expects string / number[] / GroupBadgePart[]. Empty defaults. Also
added the previously-omitted groupHasForum field.
- ContextMenuView + WiredCreatorToolsView: 'return () =>
ticker.remove(updateOverlays)' — Pixi Ticker.remove() returns the
ticker, leaking the value to React's EffectCallback cleanup which
expects 'void | (() => void)'. Wrap in block body.
- Deleted src/components/room/widgets/chat/ChatWidgetWindowView_old.tsx
— dead code (zero references in the codebase), tripping the
NitroCardHeaderView onCloseClick prop change.
Net tsgo error count: -11.
Run eslint --fix across src/ to clear ~1900 mechanical lint errors
surfaced by the @typescript-eslint v8 + react-hooks v7 + react-compiler
upgrade in the React 19 modernization PR.
Issues fixed automatically:
- brace-style (Allman): try/catch one-liners reformatted to multi-line
- indent: tab-vs-space and depth corrections
- semi: missing trailing semicolons
- no-trailing-spaces
No semantic changes. Remaining 701 errors are real-code issues
(set-state-in-effect, rules-of-hooks, no-unsafe-* type checks) that
need manual per-file review.
https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
- XSS fix: Created SanitizeHtml.ts utility using DOMPurify (already in package.json but never used). Wrapped all 21 dangerouslySetInnerHTML calls in catalog views with SanitizeHtml() — only allows safe tags (b, i, u, br, span, div, p, a, strong, em, img)
- Race condition fix: Added 10-second timeout fallbacks on purchase flags in CatalogPurchaseWidgetView and CatalogGiftView so the flag auto-resets even if the server never responds
- Catalog page for creating custom prefixes with text, per-letter colors, emoji icon and visual effects
- Emoji picker via @emoji-mart/react with createPortal + Shadow DOM blur fix
- Inventory prefix management (activate/deactivate/delete)
- Chat bubble rendering with multi-color prefix and effect support
- Prefix utilities (getPrefixEffectStyle, parsePrefixColors, PREFIX_EFFECT_KEYFRAMES)
- All UI text in English