The previous fix put 'flex' on the Text component, but Text forces
display:inline as its base class, so the flex never applied and the X
icon dropped onto its own line. Use a Flex container with the icon and
a Text child instead, so icon + label sit on one centered row.
Replace the cramped horizontal label/control rows with a vertical
stacked-label layout (bold label above each full-width control),
matching the sibling Access tab. Fixes multi-word labels wrapping in
the narrow panel. Tags share one label with the two inputs side-by-side.
Drops the now-unused Base spacer elements. Layout-only: no change to
handleChange, validation thresholds, save-on-blur, or field order.
The Base tab labels used col-3 and the delete row used
d-flex/justify-content-center — Bootstrap utilities that don't exist
in this Tailwind 4 build, so labels collapsed and wrapped. Swap to
w-1/4 shrink-0 and flex/justify-center/items-center, and drop the
dead col-4 on the Access-tab password inputs. Also fix a missing
break in the tag save-error switch (invalid-tag always showed the
non-choosable message) and a && / || precedence bug in saveTags
that checked the wrong tag field.
Client side of the soundboard. Room owners enable it in Room Settings >
Misc (next to the YouTube TV toggle). When enabled, a soundboard icon
appears in the toolbar for everyone in the room; pressing a pad broadcasts
the sound so all occupants hear it. Incoming SoundboardPlay is played via
the HTML5 Audio API.
Also: fix FloorplanCanvasSVG to use ReactElement instead of the removed
global JSX namespace (React 19), and pair the client Dev branch with the
renderer fork that carries the custom features in CI.
How sounds are managed (works with any CMS):
Sounds are rows in the `soundboard_sounds` table:
id, name, url, enabled, sort_order
The emulator loads every row with enabled=1 (ordered by sort_order, id)
and sends the list to clients on room enter; the client plays `url`
directly, so any publicly reachable audio URL works (mp3/ogg/wav).
To add a sound from an admin/housekeeping panel of any CMS:
1. Upload the audio file to wherever the CMS stores public assets
(same approach as custom badge images).
2. INSERT a row into `soundboard_sounds` with the display name and the
public URL of the uploaded file, enabled = 1.
3. Reload the emulator soundboard (or restart) to pick it up.
Relative urls resolve against the `soundboard.url.prefix` config key
(falls back to `asset.url`); absolute urls are used as-is.
- GuideToolOngoingView classNames clause: classNames(..., 'chat.roomId'
&& 'cursor-pointer') — the property name was quoted so the literal
string 'chat.roomId' was always-truthy. Unquote to read the actual
chat.roomId field.
- NavigatorRoomSettingsModTabView: UserProfileIconView userName={ user.userId }
put a number into the string-typed userName prop; the right prop for
a numeric id is userId.
- WiredExtraVariableEchoView resolvedVariableEntries: the inline
fallback-entry literal at the bottom of the useMemo got its kind
field widened to string (instead of the 'custom' literal needed by
IWiredVariablePickerEntry). Lift it into a typed const + rename to
namedFallback to avoid the shadowing of the upstream
createFallbackVariableEntry result.
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
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