Commit Graph

1047 Commits

Author SHA1 Message Date
simoleo89 0714bc8e8b fix(toolbar): revert bar to previous gray + invert collapse arrows
Restore the bar surface to rgba(62,64,72,0.55) (the previous look was preferred)
and flip both edge-collapse chevrons so they point the way shown in the
reference screenshots.
2026-06-14 21:28:58 +02:00
simoleo89 a35bb8042e style(toolbar): grayer, more opaque bar surface (less brown bleed-through) 2026-06-14 21:26:24 +02:00
simoleo89 be471ca39b feat(toolbar): add two edge collapse buttons for the icon clusters
Add a tab button at the left and right outer edges of the desktop toolbar.
The left one hides/shows the left action icons, the right one hides/shows the
friends/right cluster — each independent, toggled with a chevron that flips
direction. Styled as a semi-transparent gray edge tab matching the bar.
2026-06-14 21:16:25 +02:00
simoleo89 ba4c2f1027 style(toolbar): make the bar a semi-transparent gray
Change the toolbar surface from near-opaque dark (rgba(18,19,24,0.97)) to a
semi-transparent gray (rgba(62,64,72,0.55)) so the room shows through, per the
reference look.
2026-06-14 21:11:57 +02:00
simoleo89 450db9f817 feat(chat-input): style selector trigger shows selected bubble preview + caret
Replace the generic grayscale styles-icon trigger with a ▼ caret plus a small
clipped preview of the currently-selected chat bubble (chat-bubble bubble-N),
matching the reference layout.
2026-06-14 21:05:47 +02:00
simoleo89 11167cb9b3 feat(chat-input): move chat-style selector to the left of the input
Match the reference layout: the chat-style picker now sits before the text
field (left side) instead of after it. Adds left padding + a small gap so the
trigger, input and emoji selector are evenly spaced.
2026-06-14 21:01:46 +02:00
simoleo89 49cd06a26c feat(toolbar): remove show/hide toggle button and its animation
Drop the chevron toggle (tb-toggle) and the collapse/expand behavior: the
toolbar is now always visible (no isToolbarOpen state, no handleToggleClick,
no lock timers). The nav blocks render statically (initial=visible) so there's
no show/hide slide-in effect, and the chat-input frame sits in the bar at all
times. Removes the now-dead tb-toggle CSS and the unused useRef/useCallback
imports.
2026-06-14 20:53:39 +02:00
simoleo89 c311f3597d fix(friendbar): guarantee a single find-friends chip (filter null friends)
The legacy bar rendered MAX_DISPLAY_COUNT FriendBarItemViews and padded
empty slots with null, so an empty online-friends list produced three
identical 'Trova Amici' buttons. The current bar already renders a single
explicit search chip, but harden it: filter null/undefined out of the
online-friends array before slicing/mapping so the search chip is the only
possible source of that affordance — exactly one, always.
2026-06-14 20:26:47 +02:00
simoleo89 bd71d326fb style(toolbar): solid bottom bar (classic Habbo look, less glassy) 2026-06-14 18:58:07 +02:00
simoleo89 b27f48f2a2 feat(furni-editor): sync empty public_name from furnidata name
When a furni has a matching furnidata entry with a display name but its
items_base.public_name (the DB fallback) is empty, the editor now shows a
'Sync from furnidata' button next to the Public Name field. It reuses the
generic item update (a partial { publicName } payload) to fill the DB column
from the stored furnidata name, so the read-only fallback stops being blank.
Button shows only when the entry's classname matches, the DB field is empty,
and the furnidata name is present; it disappears after the sync re-fetch.
2026-06-14 16:41:55 +02:00
simoleo89 88d2128295 docs(furni-editor): design for syncing empty public_name from furnidata 2026-06-14 15:53:13 +02:00
simoleo89 1676b3c871 fix(furni-editor): surface upsert failures and revert optimistic name on error
When creating a furnidata entry for a furni whose sprite id is already used
by another classname, the server rejects with ID_COLLISION. The client showed
the optimistic public-name change but never reverted it, so a failed create
looked like it had succeeded. On any FurniEditorResult failure, re-fetch the
item detail to restore the true state (the error alert was already shown).
2026-06-14 14:44:25 +02:00
simoleo89 7e72578d2c docs(CLAUDE): add Runtime verification section (Claude-in-Chrome, not headless)
Captures the session learning: the client can't be verified headless (WebGL
hangs + SSO login); use the Claude-in-Chrome extension on the real logged-in
localhost:5173 session (shared SSO cookie auto-logs-in, real GPU). Toolbar
buttons are canvas → locate by screenshot + click by coordinate.
2026-06-14 13:43:52 +02:00
simoleo89 101c1b901f fix(marketplace): re-priced offer vanished when the server kept the same id
The "price raised" (case 3) handler did `set(item.offerId, item)` then an
unconditional `delete(requestedOfferId)`. When the re-priced offer kept the same
id (offerId === requestedOfferId), the set was immediately undone and the offer
disappeared from the list though it was still buyable. Delete the old key first,
then set under the new id.
2026-06-13 18:15:39 +02:00
simoleo89 26d7ccd62b fix(marketplace): reset creditsWaiting after redeeming sold offers
redeemSoldOffers optimistically removed the sold offers but never reset
creditsWaiting, so the redeem panel (gated on creditsWaiting > 0) kept rendering
"get 0 sold items for N credits" with an active Redeem button until the server
re-pushed the offers. Reset creditsWaiting to 0 on redeem.
2026-06-13 18:15:39 +02:00
simoleo89 8e82935312 fix(purse): HC status never reports EXPIRED for ex-Club (non-VIP) users
clubStatus computed `(purse.pastVipDays > 0) || (purse.pastVipDays > 0)` — the
same field twice. The first operand was meant to be `pastClubDays`, so a user who
previously had Habbo Club but never VIP got ClubStatus.NONE instead of EXPIRED,
showing the wrong HC Center status text and Buy-vs-Extend button. Use pastClubDays.
2026-06-13 18:15:36 +02:00
simoleo89 9c57d6a431 fix(groups): colour-picker highlight follows the live selection
The selected-swatch ring compared each swatch against `groupData.groupColors`,
which is only written back on save — so clicking a colour updated the local
`colors` state (and the top preview swatches) but the highlight ring never moved,
leaving the two in visible disagreement until you left and reopened the tab.
Compare against `colors[0]`/`colors[1]` (the live state) instead.
2026-06-13 18:15:34 +02:00
simoleo89 a776960650 fix(camera): full roll no longer discards the newest photo
When the roll hit CAMERA_ROLL_LIMIT the capture path alerted "full" then did
clone.pop() (dropping the NEWEST photo) and pushed the new one — so once full the
roll was pinned at the limit and every further shot just replaced the most-recent
picture. Block the shot instead (return after the alert).
2026-06-13 18:15:31 +02:00
simoleo89 d6cac249c6 fix(camera): reset selected picture index after deleting a photo
The 'delete' action spliced the selected picture out of the roll but never moved
selectedPictureIndex, so it kept pointing at the slot the deleted photo vacated —
now a different picture (or past the end), making the preview show the wrong
photo or vanish while the UI still thinks one is selected. Move the selection
back one after delete.
2026-06-13 18:15:31 +02:00
simoleo89 ed5c9296a8 fix(mod-tools): typos in moderation-action result alerts
The ModeratorActionResultMessageEvent alerts (hardcoded, not LocalizeText) read
"successfull" and "applying tht moderation action". Fix to "successful" / "the".
2026-06-13 18:15:29 +02:00
simoleo89 149b1db38c fix(mod-tools): default-sanction sent the topic array index, not the topic id
sendDefaultSanction passed `selectedTopic` (the index into the topics array) as
the CFH topic id to DefaultSanctionMessageComposer, so the emulator applied the
default sanction against whatever topic happened to sit at that array position.
The sibling sendSanction (and the topic label) correctly use
`topics[selectedTopic].id`. Pass `category.id`, and move the `category` lookup
after the `selectedTopic === -1` guard (+ a `!category` guard).
2026-06-13 18:15:29 +02:00
simoleo89 742f7ee5f7 fix(wired): date-range condition saved NaN for empty/invalid dates
The save guard `if(startDateInstance && endDateInstance)` is always true — a bad
input parses to a truthy *Invalid Date*, so `getTime()/1000` wrote NaN into the
int params. A never-configured furni was worse: the read effect only seeded the
inputs when `intData.length >= 2`, so the first save sent `new Date('')` → NaN.
Guard on `isNaN(getTime())` and seed both inputs to "now" for the empty case.
2026-06-13 18:15:26 +02:00
simoleo89 728eceab65 fix(wired): @altitude shown 100x too large and round-trip-edited wrong
`liveState.altitude` is already z*100, but the @altitude inspector rows multiplied
by 100 again (showing z*10000). The edit-commit path parses the field back as
`parsed / 100` (expecting z*100), so the displayed value and the edit parser
disagreed: tweaking the shown number wrote a wildly wrong altitude (clamped to the
40 ceiling). Display the raw `altitude` so it round-trips with the editor.
2026-06-13 18:15:26 +02:00
simoleo89 648cea698d fix(room): word-quiz countdown mutates Map value objects in place
The 1s tick did `value.secondsLeft--` on the VoteValue objects held by the
previous state Map and returned the same Map reference when nothing expired — an
in-place state mutation (breaks memoization / StrictMode double-invoke). Rebuild
new value objects in a new Map each tick (and drop expired entries).
2026-06-13 18:15:24 +02:00
simoleo89 a2f8a4dd61 fix(room): pet-fertilize chat bubble — bad localization key + wrong target user
Two bugs in the pet rebreed/fertilize chat path: the localization key had a
stray trailing semicolon (`'widget.chatbubble.petrefertilized;'`) so the lookup
failed and the raw key rendered; and the target user was resolved via
`newRoomObject = getRoomObject(event.extraParam)` but then read back by
`roomObject.id` (the speaker), so `userName` was the speaker, not the target.
Use `newRoomObject.id`.
2026-06-13 18:15:24 +02:00
simoleo89 53463b8322 fix(room): name bubbles cleared for the wrong user on USER_REMOVED
The USER_REMOVED handler for a UNIT filtered name bubbles with
`bubble.roomIndex === event.id`, which KEEPS only the leaving user's bubble and
drops everyone else's (the two sibling filters on the next lines correctly use
`!==`). When any user left view, all other floating name bubbles vanished while
the departed user's lingered. Flip to `!==`.
2026-06-13 18:15:24 +02:00
simoleo89 f873d68ee9 fix(messenger): mark thread read in an effect, not during render
FriendsMessengerThreadView called `thread.setRead()` in the render body — a side
effect during render that mutates the MessengerThread instance the messenger hook
also reads to compute the unread indicator, making unread state order-dependent
(and it would NPE if `thread` was null). Move it into a no-dependency useEffect
(runs after every commit, same cadence as before) and guard the null thread.
2026-06-13 18:15:21 +02:00
simoleo89 af6f65b194 fix(inventory): drop leaking badge pending-counter; trust server active set
sendActiveBadges incremented pendingUpdatesRef on every edit, and the BadgesEvent
handler skipped applying the server's active badges while the counter was > 0,
decrementing once per BadgesEvent. The assumption was "one BadgesEvent echo per
SetActivatedBadges" — but the emulator's UserWearBadgeEvent answers with a
UserBadgesComposer room broadcast, NOT a BadgesEvent, so nothing ever decrements
the counter. It leaks upward with every toggle/reorder/swap and then silently
drops legitimate later BadgesEvent updates (the server-authoritative active set
never reapplies). Remove the counter and always apply the server's active badges
on BadgesEvent (edits are already persisted server-side, so this is correct).
2026-06-13 18:15:19 +02:00
simoleo89 39fbfdd9e2 fix(inventory): derive active prefix from the fresh list, not a stale closure
The ActivePrefixUpdatedEvent handler set the active prefix via
`setActivePrefix(prev => { const found = prefixes.find(...) })`, reading the
`prefixes` state from the closure — which lags by a render and is stale/empty when
the prefix was added earlier in the same event batch, so `found` was undefined and
the active prefix fell back to a partial item missing icon/color/displayName. Move
the derivation inside the `setPrefixes` updater so it reads the freshly-mapped list.
2026-06-13 18:15:19 +02:00
simoleo89 9cc9ef86c0 fix(inventory): stop unseen-tracker mutating shared state arrays in place
resetItems/removeUnseen/the UnseenItemsEvent handler each did `new Map(prevValue)`
(a shallow copy) then spliced/pushed the per-category array returned by
`.get(category)` — the SAME array reference still held by the previous Map. That
mutates state outside React's data flow (breaks under StrictMode double-invoke and
any updater replay). resetItems additionally did `splice(existing.indexOf(id), 1)`
with no guard, so an id not present (indexOf === -1) spliced off the wrong LAST
element. Replace each in-place splice/push with a cloned array set back on the new
Map (filter for removals, spread+push for the merge).
2026-06-13 18:15:19 +02:00
simoleo89 74cbeccb52 feat(furni-editor): create furnidata entry when missing (upsert Save)
When a furni has no furnidata entry (furniDataEntry === null), unlock the
name/description fields instead of locking them: the Save button becomes
"Create entry" and sends the existing FurniEditorUpdateFurnidataComposer (10046),
which the emulator now upserts (creates a complete entry from items_base). The
classname-mismatch case (entry resolved by id but for a different classname)
stays locked to avoid an id collision. On success the hook already re-fetches the
detail, so the panel flips to normal edit mode. Name input prefills (placeholder)
from the DB Public Name.
2026-06-13 18:01:02 +02:00
DuckieTM 93baedf206 Merge pull request #235 from duckietm/main
Sync Main to dev
2026-06-12 16:02:40 +02:00
DuckieTM 3c9332e798 Merge pull request #234 from duckietm/Dev
Dev
2026-06-12 16:01:58 +02:00
DuckieTM d0a7d200d1 Merge pull request #230 from hotellidev/multicolorfurnifix
Fix multicolor furni names
2026-06-12 15:54:27 +02:00
DuckieTM c62e041d46 Merge pull request #229 from simoleo89/feat/installer-configurable-renderer-dir
feat(installer): make the renderer folder configurable
2026-06-12 15:53:46 +02:00
DuckieTM 419cb4cbde Merge pull request #228 from RemcoEpicnabbo/Dev
Remove trailing comma in ui-config.example
2026-06-12 15:53:05 +02:00
hotellidev caf80e5386 Merge branch 'duckietm:main' into multicolorfurnifix 2026-06-12 03:15:02 +03:00
DuckieTM 32b0e2a370 Merge pull request #233 from duckietm/Dev
🆙 100% Guild Furni Catalog Page
2026-06-11 13:16:46 +02:00
duckietm de38371069 🆙 100% Guild Furni Catalog Page 2026-06-11 13:16:29 +02:00
DuckieTM 69b643761c Merge pull request #232 from duckietm/Dev
🆙 Fix scrollbar
2026-06-11 10:31:41 +02:00
duckietm 40864cf880 🆙 Fix scrollbar 2026-06-11 10:31:24 +02:00
DuckieTM c4d7070316 Merge pull request #231 from duckietm/Dev
🆙 Added colored background to items in group furni & fix catalog price
2026-06-11 10:14:58 +02:00
duckietm 5aa9dcd650 🆙 Added colored background to items in group furni & fix catalog price 2026-06-11 10:14:27 +02:00
hotellidev afaea87bcd Fix multicolor furni names 2026-06-11 04:07:06 +03:00
simoleo89 1cfa4ca9f0 feat(installer): make the renderer folder configurable
install.mjs hardcoded the renderer SDK location as ../Nitro_Render_V3, so re-running it next to a renderer checkout under a different name silently cloned a second copy.

Resolve the renderer dir by priority: --renderer-dir=<path> flag, NITRO_RENDERER_DIR env, interactive prompt, then auto-detect an existing sibling among Nitro_Render_V3 / renderer. A fresh clone with no flag/env still defaults to ../Nitro_Render_V3, and existing checkouts are auto-detected (no more duplicate clones). Adds --renderer-repo=<url> for the clone URL; --help, the step labels and the summary reflect the resolved dir.

Backward-compatible, pure installer change — no client/runtime code touched.
2026-06-10 22:18:48 +02:00
Remco Epicnabbo 477f974b7a Remove trailing comma in ui-config.example
Remove a trailing comma after the closing object in public/configuration/ui-config.example. This fixes invalid JSON syntax that could cause parsing errors when loading the UI configuration.
2026-06-10 21:45:21 +02:00
DuckieTM 7e9af4f37e Merge pull request #225 from duckietm/Dev
🆙 Fix Group Forum buy
2026-06-10 14:16:44 +02:00
duckietm 9fcbfba22f 🆙 Fix Group Forum buy 2026-06-10 14:16:18 +02:00
DuckieTM 7e63d5b844 Merge pull request #224 from duckietm/Dev
Dev
2026-06-10 10:24:47 +02:00
duckietm 4bfd5e96ec 🆙 Update catalogue Forum view 2026-06-10 10:17:20 +02:00