Commit Graph

413 Commits

Author SHA1 Message Date
simoleo89 48d62c5c6b Architecture refactor: docs + 5 pilot implementations + error boundary
This is the structural plan promised in the previous session, with concrete
pilots for all five proposals + the bonus error-boundary work.

== docs/ARCHITECTURE.md (new, ~370 lines)

Living document describing:
- where the project stands today (event-bus pattern friction with React 19,
  god-hooks, oversized files);
- the five proposed structural improvements with the why/how/status of each;
- what's already in place across this branch;
- recommended order for the next refactor PRs.

This is the deliverable the rest of this commit references.

== Proposal #3 + #4 pilots: src/features/doorbell/ (new)

Concrete feature-folder migration on the doorbell widget (chosen because
it's small enough to migrate end-to-end in one commit).

  src/features/doorbell/
    index.ts                    public API
    views/DoorbellWidgetView.tsx
    hooks/useDoorbellState.ts   reduces 3 events into a users array (data only)
    hooks/useDoorbellActions.ts answer(name, flag) (imperative actions only)

The split (data vs actions) is the pattern proposal #4 wants applied to
useCatalog/useChat/useWiredTools later. The original useDoorbellWidget had
both concerns + a buggy `useEffect(() => setIsVisible(!!users.length), [users])`
derive-state-in-effect. The new view computes visibility in render.

Compat shims kept so existing imports keep working:
- src/components/room/widgets/doorbell/DoorbellWidgetView.tsx -> 1-line re-export
- src/hooks/rooms/widgets/useDoorbellWidget.ts -> deprecated wrapper around
  the two new hooks, returning the same { users, answer } shape.

== Proposal #2 prototype: src/api/nitro-query/ (new)

Adapter outline for wrapping composer/parser request-response pairs in
TanStack Query. Not yet enabled because @tanstack/react-query is not in
package.json. The file documents the activation steps:

  yarn add @tanstack/react-query @tanstack/react-query-devtools
  + mount QueryClientProvider in src/index.tsx

awaitNitroResponse() throws with a helpful pointer to the doc section if
called before activation, so accidental adoption fails loudly.

== Proposal #5 skeleton: src/state/createNitroStore.ts (new)

Same pattern: skeleton + activation instructions. Not yet enabled because
zustand is not in package.json.

  yarn add zustand
  + replace the throw with `import { create } from 'zustand'; export const createNitroStore = create;`

The doc inside the file shows the recommended slice shape and points to
the suggested first migration target (the let isCreatingRoom singleton in
NavigatorRoomCreatorView).

== Bonus: WidgetErrorBoundary

src/common/error-boundary/WidgetErrorBoundary.tsx wraps react-error-boundary
with a sensible default (silent fallback, NitroLogger.error). Re-exported
from src/common/index.ts.

Applied as the umbrella around RoomWidgetsView's children — a widget
crash in a room (e.g. malformed pet data) now degrades gracefully
instead of unmounting the whole UI.

== Verification

- yarn eslint on all new + modified files: 0 errors / 0 warnings introduced.
  RoomWidgetsView still has its 1 pre-existing FC<{}> error (1 before, 1 after).
- yarn tsc on all new files: clean (only project-wide pre-existing
  TS2307 about @nitrots/nitro-renderer not installed locally remains).
- No regressions: existing imports of DoorbellWidgetView and
  useDoorbellWidget keep resolving via the compat shims.

== What's NOT in this commit (intentionally)

- Mass adoption of the new patterns elsewhere — left as follow-up PRs in
  the order documented in ARCHITECTURE.md "How to pick the next refactor PR".
- Installation of @tanstack/react-query / zustand — explicit team decision,
  not the LLM's to make.
- Test infrastructure (Vitest setup) — listed as the #1 missing piece in
  the doc, but a separate PR.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:52 +00:00
simoleo89 22a44d18b0 Add useNitroEventState / useMessageEventState hooks (proposal #1)
Introduce the building block for reducing the state-from-event
boilerplate that pervades the codebase:

  // Before
  const [foo, setFoo] = useState(initial);
  useNitroEvent(SOME_EVENT, e => setFoo(e.payload));

  // After
  const foo = useNitroEventState(SOME_EVENT, e => e.payload, initial);

Implementation notes:
- src/hooks/events/useNitroEventState.ts wraps useNitroEvent so the
  selector closure can use up-to-date surrounding values (captured in
  a ref refreshed in commit via useLayoutEffect) without forcing a
  re-subscription on every render. Listener is registered once and
  always reads the latest selector.
- src/hooks/events/useMessageEventState.ts is the mirror for
  useMessageEvent (server message channel — request/response composers
  and push parsers).
- Both pass the new react-hooks v7 rules cleanly (in particular the
  strict react-hooks/refs that forbids ref mutation during render).
- Re-exported from src/hooks/events/index.ts so callers reach them
  via the existing `from '../../hooks'` import path.

Pilot adoption (1 site) to demonstrate the pattern:
- src/components/catalog/views/targeted-offer/OfferView.tsx:
  the offer state was a clean derive-from-event case
  (setOffer(parser.data) on TargetedOfferEvent, no other writes).
  Replaced with a single useMessageEventState call using the optional
  chain `evt.getParser()?.data ?? null` as selector. Removes the
  useState pair and the explicit subscription block.

Honest scope note:
A broader sweep is intentionally NOT done. Most existing event
subscriptions in this codebase are multi-state updates, state
machines, conditional filters ("skip if not my id"), or have side
effects mixed in (notifications, redirects). Forcing those into
useNitroEventState would lose information and risk regressions in
behavior the lint won't catch. Adoption should happen organically
when contributors see a clean derive-from-event case, not as a
mechanical replace-all.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:52 +00:00
simoleo89 5d8717dedb Split WiredCreatorToolsView: extract types/constants/helpers into 3 sibling files
The single-file WiredCreatorToolsView.tsx was 4493 lines, which is one
of the main reasons the React Compiler reports
"Compilation Skipped: Existing memoization could not be preserved" on
this module. Split is conservative — only the pure leading sections
move out, the component itself is untouched (state, effects, JSX all
stay in place).

New files (sibling to the view):
- WiredCreatorTools.types.ts (~233 lines): every interface and type
  alias declared at the top of the original file.
- WiredCreatorTools.constants.ts (~225 lines): TABS, MONITOR_LOG_ORDER,
  poll constants, MONITOR_ERROR_INFO, INSPECTION_ELEMENTS,
  VARIABLES_ELEMENTS, EDITABLE_*, VARIABLE_DEFINITIONS,
  WIRED_FREEZE_EFFECT_IDS, TEAM_COLOR_NAMES, WEEKDAY/MONTH/DIRECTION
  names. The createVariableDefinition factory is kept as a local helper
  in this file (only used to build VARIABLE_DEFINITIONS).
- WiredCreatorTools.helpers.ts (~147 lines): createEmptyMonitorSnapshot,
  getHotelTimeFormatter (with its module-private cache map),
  getHotelDateTimeParts, formatMonitorLatestOccurrence,
  formatMonitorHistoryOccurrence, formatVariableTimestamp,
  formatMonitorSource, normalizeMonitorReason. All pure (or
  cache-stable), no closure on component state.

WiredCreatorToolsView.tsx changes:
- 4493 -> 3901 lines (-592, ~13% reduction).
- The four inspection-icon asset imports (furni/global/user/context)
  move to the constants file alongside the only consumers
  (INSPECTION_ELEMENTS / VARIABLES_ELEMENTS).
- AvatarInfoFurni was only referenced by the extracted
  InspectionFurniSelection interface and is removed from the main
  file's api import.
- New import block at the top pulls back the symbols actually used by
  the component body.

Verification:
- yarn eslint on the three new files: 0 errors / 0 warnings.
- yarn eslint on WiredCreatorToolsView.tsx: 26 errors before split,
  26 errors after split (identical pre-existing set; nothing new
  introduced).
- yarn tsc --noEmit on the four files: clean (only the project-wide
  pre-existing TS2307 about @nitrots/nitro-renderer not being
  installed locally remains, same as before).

This unblocks future per-tab splits (Monitor / Inspection / Variables
JSX panels are still inline in the view and represent the next ~1600
lines that could move out, but require introducing a shared state
context first since the current setState chain is intertwined).

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:52 +00:00
simoleo89 39eb2c6b84 Phase C (targeted): clear 4 set-state-in-effect violations on safe candidates
Fix only the cases that are unambiguous anti-patterns; leave the
event-driven setState patterns (useNitroEvent / useMessageEvent
subscriptions, async fetches with cleanup) alone since they're
legitimate in this architecture.

- src/components/catalog/views/catalog-header/CatalogHeaderView.tsx:
  displayImageUrl was pure-derived from imageUrl. Drop the useState +
  useEffect entirely; compute in render.
- src/components/navigator/views/NavigatorRoomCreatorView.tsx:
  the maxVisitors list (10..100 step 10) and roomModels/selectedModel
  came from static config; convert to module-level MAX_VISITORS_LIST
  constant + useState lazy initializers. Removes 2 init effects.
  setCategory(categories[0].id) is left as-is because categories
  arrives async from a hook.
- src/components/login/LoginView.tsx:
  Replace useEffect(() => setLocalError(null), [step]) with the
  React-recommended "track previous prop" render-time reset:
  if(prevStep !== step) { setPrevStep(step); setLocalError(null); }
  Same observable behavior, no extra render.
- src/components/room/widgets/choosers/ChooserWidgetView.tsx:
  Wrap the selectItem callback prop call in useEffectEvent so a
  parent re-render that changes selectItem identity doesn't
  re-fire the visualizer side-effects.

Net: 4 fewer set-state-in-effect violations; behavior preserved.
The remaining ~328 violations across the codebase are predominantly
legitimate event-bus / async-fetch patterns and need per-case
review with running app, not a sweep.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:52 +00:00
simoleo89 d382635597 Phase A: clear all react-hooks/exhaustive-deps warnings via useEffectEvent or hoisting
Eliminate the four remaining missing-dependency warnings reported by
react-hooks v7. Each one was a real stale-closure or re-trigger hazard;
the fix matches the intent rather than just silencing the linter.

- src/App.tsx (line 448): wrap showSessionExpired with useEffectEvent
  (onSessionExpired) so the prepare effect doesn't re-run on every
  showSessionExpired identity change but still calls the latest
  callback. Replace the two in-effect call sites.
- src/components/furni-editor/views/FurniEditorSearchView.tsx: wrap
  the on-mount onSearch('', '', 1) call with useEffectEvent so the
  callback prop isn't a missing dependency.
- src/components/notification-center/views/bubble-layouts/
  NotificationBadgeReceivedBubbleView.tsx: wrap the
  "fetch badges only if empty on mount" check with useEffectEvent
  so badgeCodes.length isn't required as a dep (and won't re-fetch
  every count change).
- src/components/navigator/views/room-settings/
  NavigatorRoomSettingsRightsTabView.tsx: switch deps from
  roomData?.roomId to roomData (the body uses roomData.roomId after
  an early return; the linter wanted the whole object).
- src/api/ui-settings/UiSettingsContext.tsx: hoist ALL_CSS_VARS
  outside the component (it's a static constant).

After this, yarn eslint reports zero exhaustive-deps warnings across
the whole src/.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:51 +00:00
simoleo89 f18c917fc4 Add TypeScript 7 (tsgo) as fast type-checker alongside TS 6
TypeScript 7 is the Go-native rewrite of tsc, ~10x faster but only
distributed as @typescript/native-preview daily builds at the time
of writing (npm typescript@latest is still 6.0.3). Add it as a
non-disruptive type-check tool: yarn typecheck → tsgo --noEmit.

Vite still uses esbuild for transpilation, ESLint still uses TS 6
through @typescript-eslint v8, IDEs continue using their bundled TS.
This commit only adds a type-check tool — nothing replaces.

Required tsconfig.json adjustments for TS 7 compatibility (still
valid for TS 6):

- Drop baseUrl: "./src" (removed in TS 7). The codebase has no
  bare/non-relative imports that depended on it; all imports are
  relative or aliased.
- Drop downlevelIteration: true (removed in TS 7; target es2022
  doesn't need it).
- moduleResolution: "node" → "bundler" (TS 7 dropped node10; bundler
  is the right mode for Vite anyway).
- paths "@layout/*" entries now use leading "./" (TS 7 disallows
  non-relative path mappings). Add "@/*" → "./src/*" to match the
  Vite alias used in some components.

Other TS 7 adjustments:

- src/react-app-env.d.ts: add module declarations for *.css/.scss/.sass
  side-effect imports (TS 7 with bundler resolution requires them) +
  Window.NitroConfig / Window.NitroSecureApiUrl globals which were
  used in App.tsx without a declaration.
- src/common/Popover.tsx: explicit `import { JSX } from 'react'`
  because TS 7 dropped the implicit global JSX namespace.

Verification:
- yarn eslint still passes (TS 6 / @typescript-eslint v8 happy with
  the migrated config).
- yarn typecheck (tsgo) runs and reports only cascading errors
  rooted in the missing @nitrots/nitro-renderer sibling repo
  (environmental, not introduced here).

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:51 +00:00
simoleo89 6c9f414028 Apply useEffectEvent (React 19.2) to TurnstileWidget callbacks
The Turnstile render effect had a stale-closure hazard: it captured
onToken/onExpire/onError props but didn't list them in the dependency
array (deps: scriptReady, siteKey, theme, size). On parent re-renders
the captured callbacks could go stale.

Wrap the three callback props with useEffectEvent so they always read
the latest props without invalidating the render effect. The render
effect still only re-runs when the script readiness or widget config
truly change.

useEffectEvent shipped in React 19.2 (already on the project) and
@types/react 19.2.x exports it.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:51 +00:00
simoleo89 5697d169ee Fix rules-of-hooks violation in InfiniteGrid
InfiniteGridRoot called useVirtualizer + 2 useEffect after an early
return for the squareItems branch, which violates the rules of hooks
(react-hooks v7 now flags this as an error and react-compiler skips
the component entirely).

Split the component into three:

- useColumnMeasure: shared custom hook that owns parentRef +
  ResizeObserver-based column measurement (used by both branches).
- InfiniteGridSquare: the non-virtualized grid for squareItems mode.
  Doesn't call useVirtualizer.
- InfiniteGridVirtualized: the virtualized branch with TanStack
  Virtual + scroll/padding effects.

InfiniteGridRoot becomes a thin selector that routes by props.squareItems.
All hooks in each sub-component are now unconditional.

The remaining lint findings on this file (set-state-in-effect inside
InfiniteGridItem, react-hooks/incompatible-library on useVirtualizer)
are pre-existing/informational and out of scope.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:51 +00:00
simoleo89 13dc483938 Bump ecosystem dependencies (minor/patch)
- @radix-ui/react-popover: ^1.1.6 → ^1.1.15
- @radix-ui/react-slider:  ^1.2.4 → ^1.3.6
- react-icons:             ^5.5.0 → ^5.6.0
- dompurify:               ^3.4.1 → ^3.4.2
- @tanstack/react-virtual: pin → ^3.13.24 (loosen to caret; no version change)

framer-motion (12.38.0), @tanstack/react-virtual (3.13.24), emoji-mart
(5.6.0), and @emoji-mart/* are already at latest. react-player was
intentionally not bumped (3.x is a major release).

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:51 +00:00
simoleo89 25d51aff3f Enable <StrictMode> + make App.tsx renderer init idempotent
App.tsx's prepare() useEffect ran four .init() calls
(SessionDataManager, RoomSessionManager, RoomEngine, Communication)
without any guard, plus an immediate heartbeat ping and a legacy
authentication track. Under StrictMode dev double-invoke, those
fire twice — risking duplicate session/communication state.

- Gate the four .init() chain behind gameInitPromiseRef: both the
  first and the simulated second invocation await the same promise.
- Gate the legacy track + immediate heartbeat behind bootstrapDoneRef.
- Heartbeat and remember-rotate intervals were already idempotent
  (clearInterval before setInterval); ticker registration was already
  guarded by tickersStartedRef; renderer/warmup were already gated by
  rendererPromiseRef/warmupPromiseRef. No change needed there.

Wrap <App /> in <StrictMode> in src/index.tsx now that the renderer
init path is double-invoke safe.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:50 +00:00
simoleo89 535fa71020 ESLint --fix: auto-fix brace-style, indent, semi, no-trailing-spaces
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
2026-05-11 16:31:50 +00:00
simoleo89 1b1e0c18bf React 19 Phase 3: login/forgot/register forms → useActionState + useFormStatus
Migrate all three inline forms in LoginView.tsx to React 19 Actions:

- Login form: handleLoginSubmit → loginAction(prevState, FormData) wrapped in
  useActionState. Submit button extracted as <LoginSubmitButton/> reading
  pending via useFormStatus, dropping the local `submitting` flag for the
  login flow. Reads username/password/remember from FormData; rememberMe
  checkbox now carries name="remember".
- Forgot form (inline): forgotAction wrapped in useActionState; awaits
  parent's onSubmit so pending stays true through the parent fetch.
  ForgotSubmitButton uses useFormStatus.
- Register credentials step: credentialsAction with useActionState; the
  step transition (setStep('avatar')) happens inside the action after
  pingServer + onCheckEmail.
- Register avatar step: avatarAction validates username, pings server,
  checks availability, then awaits onSubmit. The button label uses
  isAvatarPending to show "Creating…" without prop drilling submitting.
- DialogSharedProps onSubmit signatures updated to return Promise<void>
  so dialog actions can await the parent's fetch.
- lockState memo replaced with a direct readLock() call in render: the
  previous useMemo depended on `submitting` to refresh after a failed
  attempt; now any re-render (triggered by the action's pending toggle)
  recomputes it.
- Remove unused FormEvent import; remove unused checking state in
  RegisterDialog (replaced by isCredentialsPending / isAvatarPending).

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:50 +00:00
simoleo89 a1bee1d825 React 19 modernization: forwardRef removal, Compiler, ErrorBoundary, Suspense, native <script>
Adopt React 19 idioms across the codebase. The runtime was already on
react@19.2.5 but no React 19 APIs were in use.

- forwardRef -> ref-as-prop in 7 layout/component files
  (NitroInput/Button/ItemCountBadge/Card×5/InfiniteGridItem,
  ToolbarItemView, AvatarEditorIcon)
- <Ctx.Provider> -> <Ctx> in 6 contexts (CatalogAdmin, FloorplanEditor,
  UiSettings, GridContext, NitroCardContext, NitroCardAccordionContext)
- Native <script> hoisting for Turnstile, ExternalPluginLoader, GoogleAdsView
  (React 19 dedupes by src; removes manual document.head.appendChild +
  module-level promise caches)
- React Compiler enabled at build time via babel-plugin-react-compiler
  in vite.config.mjs (target: '19'), plus eslint-plugin-react-compiler
  in lint mode
- Global <ErrorBoundary> + <Suspense> in src/index.tsx using
  react-error-boundary, with LoadingView as fallback
- BackgroundsView migrated to use(promise) as a demonstrator pattern
  for Suspense-driven config loading
- ESLint react setting bumped 18.3.1 -> 19.2; legacy
  @typescript-eslint/ban-types replaced with no-restricted-types
  (the old rule was removed in @typescript-eslint v8)
- Refresh public/configuration/{asset-loader,bootstrap}.js to match
  current write-asset-loader.mjs output

Phase 3 (login forms -> useActionState/useFormStatus) deferred:
LoginView is 1623 lines with lockout + Turnstile + heartbeat
interleaving; safer as its own PR.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:50 +00:00
DuckieTM 2137d23ac0 Merge pull request #125 from duckietm/Dev
🆙 Fixed the commands
2026-05-08 16:45:02 +02:00
duckietm d88defb4a5 🆙 Fixed the commands 2026-05-08 16:44:45 +02:00
DuckieTM 98e250e49a Merge pull request #124 from duckietm/Dev
Dev
2026-05-08 11:58:48 +02:00
duckietm 6124610736 🆙 Small fix Avatar loading & moved news to path wich you can enter
The example data has been provided in /Content-Gamedata so you could place it in /gamadata or anything you like.
Do not forget the render-config.json to update :

"login.health.method": "GET",
"login.news.url": "${asset.url}/news/news.json",
2026-05-08 11:58:32 +02:00
DuckieTM fa3bb7b9ac Merge pull request #123 from Lorenzune/merge-duckie-main-2026-05-06
Refine mobile avatar widgets and login flow
2026-05-08 07:45:28 +02:00
DuckieTM 71725b7f67 Merge branch 'Dev' into merge-duckie-main-2026-05-06 2026-05-08 07:45:17 +02:00
Lorenzune 57b83c1097 Refine mobile avatar widgets and login flow 2026-05-07 21:19:15 +02:00
DuckieTM 83ee05957d 🆙 Fix font colors in chat 2026-05-07 20:49:50 +02:00
DuckieTM 247ada2fb5 Merge pull request #121 from duckietm/Dev
🆙 Fix when using : --base=/client/ in package.json
2026-05-07 15:17:12 +02:00
duckietm 3d88ec8cfc 🆙 Fix when using : --base=/client/ in package.json 2026-05-07 15:16:54 +02:00
DuckieTM 72fea20e0b Merge pull request #120 from duckietm/Dev
🆙 Small update
2026-05-07 10:24:16 +02:00
duckietm 5e26514143 🆙 Small update 2026-05-07 10:21:48 +02:00
DuckieTM 4da1af605d Merge pull request #119 from duckietm/Dev
Dev
2026-05-06 12:50:29 +02:00
duckietm dbf5ae875c 🆙 Small update in building 2026-05-06 12:50:00 +02:00
duckietm 7396413f11 🆙 Fixed Multiple SCSS-isms ended up inside plain .css 2026-05-06 10:48:09 +02:00
duckietm f6b7a3c9d7 🆙 Update Render example 2026-05-06 10:38:55 +02:00
duckietm aa20b0acbe 🆙 Small fix Background to load from json 2026-05-06 08:57:44 +02:00
duckietm a243640741 🆙 News windows in UI login update 2026-05-06 08:48:17 +02:00
duckietm 793c260fc5 🆙 Small update to the login texts 2026-05-06 08:32:36 +02:00
duckietm 606afa7792 🆙 Small fix login page 2026-05-06 08:21:08 +02:00
DuckieTM c4cd29e709 Merge pull request #118 from Lorenzune/merge-duckie-main-2026-05-06
Merge live runtime configuration updates into Dev
2026-05-06 07:07:37 +02:00
Lorenzune 851d82f93f Document secure runtime configuration 2026-05-06 06:27:40 +02:00
Lorenzune 71171dc205 Merge remote-tracking branch 'duckie/main' into merge-duckie-main-2026-05-06
# Conflicts:
#	index.html
#	public/UITexts.example
#	public/renderer-config.example
#	src/App.tsx
#	src/components/login/LoginView.tsx
#	src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserView.tsx
#	src/components/toolbar/ToolbarView.tsx
#	src/components/user-profile/UserContainerView.tsx
2026-05-06 04:23:15 +02:00
DuckieTM db2985d763 Merge pull request #116 from duckietm/Dev
🆙 Small fix for the badge selector in the infostand view
2026-05-04 15:59:26 +02:00
duckietm 5857362d61 🆙 Small fix for the badge selector in the infostand view 2026-05-04 15:59:08 +02:00
DuckieTM 938ecb4816 Merge pull request #115 from duckietm/Dev
🆙 Updated Background profiles
2026-05-04 15:38:06 +02:00
duckietm 6fce62fb47 🆙 Updated Background profiles
Please make sure you change your UI-Config !!!
2026-05-04 15:26:29 +02:00
DuckieTM 1821b2df08 Merge pull request #114 from duckietm/Dev
Dev
2026-05-04 10:32:02 +02:00
duckietm da9e394901 🆙 Small fix Badge tooltip view 2026-05-04 10:24:02 +02:00
DuckieTM 182654f308 Merge pull request #113 from simoleo89/feat/full-box-background
Feat/full box background
2026-05-04 08:03:25 +02:00
DuckieTM 00bcfd7173 Merge pull request #112 from simoleo89/fix/toolbar-face-vertical-align
fix(toolbar): align face button with other toolbar icons
2026-05-04 08:03:13 +02:00
DuckieTM 6e50cb180c Merge pull request #111 from simoleo89/fix/me-menu-popup-position
fix(toolbar): anchor Me menu popup to face button instead of fixed of…
2026-05-04 08:02:53 +02:00
DuckieTM fad1243774 Merge pull request #110 from simoleo89/feat/edit-furni-permission-gate
Feat/edit furni permission gate
2026-05-04 08:02:38 +02:00
simoleo89 72bc4da3c0 feat(profile): add full-box card background tab and rendering
Adds a "Cards" tab to the Profile Background picker (BackgroundsView)
that selects a pattern applied to the entire user info card and the
extended profile container, in addition to the existing avatar-pad
background/stand/overlay layers.

- AvatarInfoUser/Utilities: propagate cardBackgroundId from RoomUserData.
- InfoStandWidgetUserView: stateful cardBackgroundId, applied as
  .profile-card-background.card-background-{id} on the outer Column
  with bg-color suppressed when active.
- UserContainerView: same class on the wrapper of the extended profile.
- BackgroundsView: 4th tab "cards" backed by cards.data config
  (falls back to backgrounds.data); sends 4-id message via the
  extended sendBackgroundMessage signature.
- ui-config.example: cards.data dataset (15 entries).
- BackgroundsView.css: 188 .card-background-{N} rules cloned from
  background-{N} (repeat-tiled) plus 15 CSS-pattern overrides for the
  provisional dataset (gradients, stripes, dots, grid, checker).
2026-05-03 22:09:12 +02:00
simoleo89 017e780e74 fix(toolbar): align face button with other toolbar icons
The face avatar (headOnly LayoutAvatarImageView) sits in a 63px-tall
box (44px on mobile) while sibling toolbar icons are smaller, so its
head sprite rendered visually higher than the other icons. Bumped
marginTop from 2px → 12px (desktop) and 4px → 9px (mobile) so the
head sits on the same horizontal axis as the rest of the toolbar.
2026-05-03 20:49:15 +02:00
simoleo89 0f6bf7e9eb fix(toolbar): anchor Me menu popup to face button instead of fixed offset
Removed `absolute bottom-[60px] left-[33px]` from the inner Flex of
ToolbarMeView. The outer wrapper in ToolbarView already anchors the
popup above the face button (bottom-[calc(100%+8px)] left-1/2 -translate-x-1/2),
so the inner pixel-perfect override was detaching it and making it float
mid-screen.
2026-05-03 20:40:30 +02:00
simoleo89 d9b6a3eb0c feat(infostand): gate Edit Furni button behind moderator permission
Mirrors the isModerator check already used by the toolbar furni-editor
icon, so users without the moderator rank no longer see the button.
2026-05-03 20:08:20 +02:00