Commit Graph

34 Commits

Author SHA1 Message Date
simoleo89 a4c9dd87db Split useChatInputWidget into state + actions (flat hooks layout)
Continues the proposal #4 split pattern (doorbell, poll, furni-chooser,
user-chooser, friend-request) for the chat-input widget. Splits the
334-line useChatInputWidget along the natural seam:

- useChatInputState — selectedUsername / floodBlocked / floodBlockedSeconds
  / isTyping / isIdle state plus the three event listeners
  (FLOOD_EVENT, ObjectSelected, ObjectDeselected) and the three lifecycle
  effects (flood-countdown, idle-auto-clear, typing-indicator sync).
- useChatInputActions — sendChat(text, chatType, recipientName, styleId).
  Carries the slash-command handler (":shake", ":rotate", ":zoom",
  ":screenshot", ":pickall", etc.) and the chat-vs-shout-vs-whisper
  dispatch path, with the optional outgoing-translation hook.
- useChatInputWidget — deprecated shim that composes both into the
  historical { selectedUsername, floodBlocked, floodBlockedSeconds,
  setIsTyping, setIsIdle, sendChat } shape so ChatInputView keeps
  working unchanged.

Bonus while in here:
- Guarded all roomSession reads in actions with optional chaining
  (the hook can be called during the brief no-room window between
  enter and leave).
- Dropped the useless 'if(isIdle)' inside the idle effect body — the
  early return guard above it already covers that branch.
2026-05-11 22:00:23 +02:00
simoleo89 f57266af03 Update 3 IGetImageListener.imageReady call sites to v8 single-arg signature
IGetImageListener.imageReady(result: IImageResult) takes a single
IImageResult object (with .id, .data, .image), but three call sites in
the client still used the old 3-arg destructure '(id, texture, image)
=> ...'. The renderer's RoomEngine.ts already passes
'new ImageResult(...)' to the listener, so the runtime payload matches
the new contract; the old call-site shape just type-errored.

Migrated:
- LayoutPetImageView (pet thumbnail loader)
- LayoutRoomObjectImageView (furniture thumbnail loader)
- useFurniturePresentWidget (gift box image generator)

Also tightened imageFailed handlers from 'imageFailed: null' to a
proper no-op arrow — the interface requires a callback.
2026-05-11 21:34:08 +02:00
simoleo89 1083b2ea33 Type useFurniChooserState builders + drop dead getUserData guard
The two helper functions buildWallItem and buildFloorItem took
roomObject as 'any', so 'model.getValue<number>(...)' became an
untyped-function-with-type-args error under tsgo (six hits). Typing
the param as IRoomObject (the renderer's public interface — model is
already typed there) fixes them all at once.

The fallback chain for ownerName was guarded by
'sessionDataManager.getUserData ? sessionDataManager.getUserData(ownerId)?.name : null'
— SessionDataManager.getUserData() does NOT exist on the renderer
(documented in Nitro_Render_V3/CLAUDE.md), so that branch was always
dead. Dropping it removes the four tsgo errors and the misleading
condition.

Net tsgo error count: 90 -> 80.
2026-05-11 21:12:34 +02:00
simoleo89 559d860a7b Pilot: move InfoStand event listeners to useAvatarInfoWidget owner
InfoStandWidgetUserView previously subscribed to three room-session
events (RSUBE_BADGES, USER_FIGURE, FAVOURITE_GROUP_UPDATE) and pushed
the result back to its parent via a setAvatarInfo prop, with each
handler running CloneObject(prev) before patching one field. Three
issues with that shape:

- CloneObject was deep-cloning the whole AvatarInfoUser shape blindly
  with no class-prototype awareness;
- the three listeners raced on shallow merges across the same prev
  reference in StrictMode dev;
- the subscriptions lived outside the state owner, forcing a prop
  callback barrier per event.

The subscriptions are now in useAvatarInfoWidget — the actual owner of
avatarInfo — and call three pure reducers extracted to
src/hooks/rooms/widgets/avatarInfo.reducers.ts (applyUserBadgesUpdate,
applyUserFigureUpdate, applyFavouriteGroupUpdate). Each reducer returns
the same reference when the event doesn't apply so React bail-outs work.
The clone now constructs a fresh AvatarInfoUser preserving prototype.

dedupeBadges is extracted to its own pure module under src/api/avatar/
so Vitest can cover it without pulling in the renderer.

InfoStandWidgetUserView loses the setAvatarInfo prop (parent updated)
and the CloneObject import.
2026-05-11 21:11:02 +02:00
simoleo89 f3442f8aa0 Split useFriendRequestWidget into state + actions (flat hooks layout)
Stesso pattern di doorbell / poll / furni-chooser / user-chooser:
flat split sotto src/hooks/rooms/widgets/, no co-location dentro
src/components/.

Split
- src/hooks/rooms/widgets/useFriendRequestState.ts (new):
  activeRequests state + displayedRequests derived (filter su
  dismissedRequestIds) + due bridge events (user added/removed) +
  un useEffect che riallinea activeRequests quando cambia il set
  di requests dal friends-store. Esporta anche il tipo
  ActiveFriendRequest per consumi futuri.
  Plus: ?. su roomSession e userDataManager per evitare il bug
  pattern "session è null in transition" (vedi PetTrainingPanel,
  precedentemente fixato).
- src/hooks/rooms/widgets/useFriendRequestActions.ts (new):
  hideFriendRequest. Thin adapter sul friends-store
  (setDismissedRequestIds), nessuna subscription.
- src/hooks/rooms/widgets/useFriendRequestWidget.ts: deprecated
  shim che compone i due e preserva
  { displayedRequests, hideFriendRequest } per il consumer
  FriendRequestWidgetView.

Verifica
- yarn eslint sui 4 file toccati: 1 errore pre-esistente
  (set-state-in-effect sul useEffect che ri-derive activeRequests
  da requests — già nel file originale, baseline invariata).
- yarn test: 49/49 passing.
- yarn tsc: clean.

Sequence widget split adesso a 5 (doorbell, poll, furni-chooser,
user-chooser, friend-request). Rimangono: usePetPackageWidget,
useWordQuizWidget, useChatInputWidget, useChatWidget,
useAvatarInfoWidget, useFilterWordsWidget.
2026-05-11 17:47:09 +00:00
simoleo89 85fc82794d Split useUserChooserWidget into state + actions (flat hooks layout)
Speculare di useFurniChooserWidget — stesso split + stesso layout (flat
in src/hooks/rooms/widgets/). User chooser è il gemello del furni
chooser nella shape: items list popolata da room scan + due bridge
events (added/removed) + selectItem imperativo.

Split
- src/hooks/rooms/widgets/useUserChooserState.ts (new):
    items + onClose + populateChooser + useUserAddedEvent +
    useUserRemovedEvent. Helper buildUserItem dedupa la costruzione
    di RoomObjectItem fra populateChooser e l'add handler (~20
    righe di duplicazione in meno).
    Plus: aggiunto ?. su roomSession e userDataManager (lo stesso
    bug pattern del PetTrainingPanel fixato altrove).
- src/hooks/rooms/widgets/useUserChooserActions.ts (new):
    selectItem puro.
- src/hooks/rooms/widgets/useUserChooserWidget.ts: kept as a
    deprecated shim that composes both and preserves
    { items, onClose, selectItem, populateChooser } per il consumer
    UserChooserWidgetView.

Verifica
- yarn eslint sui 4 file toccati: 0 errors / 0 warnings.
- yarn test: 49/49 passing.
- yarn tsc: clean.

Sequenza god-hook split adesso a 4 (doorbell, poll, furni-chooser,
user-chooser). Rimangono: useFriendRequestWidget, usePetPackageWidget,
useWordQuizWidget, useChatInputWidget, useChatWidget,
useAvatarInfoWidget, useFilterWordsWidget.
2026-05-11 17:45:31 +00:00
simoleo89 0ae371ee09 Split useFurniChooserWidget into state + actions (flat hooks layout)
Apply the same data/actions split pattern (proposal #4) to
useFurniChooserWidget, the largest god-hook still on the widgets
side (161 LOC). Layout follows the main branch convention:
flat files under src/hooks/rooms/widgets/, no per-feature subfolder,
no co-location of hooks inside src/components/.

Split
- src/hooks/rooms/widgets/useFurniChooserState.ts (new): owns the
  items array, the populateChooser action that scans the current
  room, the two RoomEngine event bridges (added/removed), and
  onClose. Helper buildWallItem/buildFloorItem dedupes the two
  copies of the RoomObjectItem construction that used to live
  inline in both populateChooser and the added-event handler
  (~50 lines of duplication removed).
- src/hooks/rooms/widgets/useFurniChooserActions.ts (new): the
  one pure imperative action — selectItem — that doesn't need to
  subscribe to anything.
- src/hooks/rooms/widgets/useFurniChooserWidget.ts: kept as a
  deprecated shim that composes both and returns the same
  { items, onClose, selectItem, populateChooser } shape so
  FurniChooserWidgetView (the only consumer) doesn't change.

Layout note
- This is consistent with the main branch: each widget hook is a
  flat file under src/hooks/rooms/widgets/ (no <feature>/ subfolder),
  while the view sits under src/components/room/widgets/<feature>/.
- The parallel feat/react19-hooks-adapter branch chose the opposite
  convention (hooks co-located inside src/components/...). Per the
  team decision recorded in docs/ARCHITECTURE.md proposal #3, this
  repo stays on the flat-hooks-folder layout.

Verification
- yarn tsc on the touched files: 6 TS2347 errors after the split,
  12 before — the buildWallItem/buildFloorItem helpers actually
  *reduce* the local sandbox TS2347 surface (the renderer SDK is
  not installed locally, so `roomObject.model.getValue<T>` is
  flagged as "untyped function with type arg"; merging the two
  callsites into one helper halves the count).
- yarn eslint on the touched files: 0 errors, 0 warnings.
- yarn test: 49/49 passing.
2026-05-11 17:09:41 +00:00
simoleo89 419de09638 Hoist usePollSubscriptions to RoomWidgetsView; drop the side effect from usePollWidget
Follow-up to the previous commit's poll split. The compat shim
usePollWidget used to call usePollSubscriptions() inside its body so
the three RoomSessionPollEvent listeners were still registered for
existing consumers — but that meant:
- listeners would be re-registered per consumer (today nobody, since
  useWordQuizWidget was already migrated to usePollActions);
- the lifetime of the subscriptions was tied to a leaf widget instead
  of the room session;
- a render of a component using the shim had the side effect of
  attaching three global event listeners.

Move
- src/components/room/widgets/RoomWidgetsView.tsx now calls
  usePollSubscriptions() once at the top of the room-widget tree. The
  bridge from RoomSessionPollEvent (OFFER/ERROR/CONTENT) to the UI
  event bus is now mounted for exactly the lifetime of an in-room
  session, regardless of which leaf widget renders.
- src/hooks/rooms/widgets/usePollWidget.ts (compat shim) is reduced
  to a one-liner that just returns usePollActions(). It is still
  deprecated; remove once nothing imports it.

Verification
- yarn eslint on the two touched files: 1 pre-existing error
  (the same FC<{}> in RoomWidgetsView that was there before — baseline
  unchanged; I deliberately did not touch it in this commit to keep
  the diff minimal).
- yarn test: 22/22 still passing.
- grep confirms usePollWidget has zero in-tree consumers; the only
  importer is the barrel re-export.
2026-05-11 16:36:11 +00:00
simoleo89 7218285583 Split usePollWidget into subscriptions + actions (proposal #4) + doc update
usePollWidget bundled two unrelated responsibilities:
- three useNitroEvent listeners that bridge RoomSessionPollEvent
  (OFFER / ERROR / CONTENT) onto the UI event bus via DispatchUiEvent
  — pure side-effects, zero local state, should mount once;
- three imperative actions (startPoll, rejectPoll, answerPoll) that
  every consumer wants, but which shouldn't re-register the listeners.

In practice the only consumer of usePollWidget was useWordQuizWidget,
which needed only `answerPoll` — but pulled in the three subscriptions
as a side effect every time the word-quiz widget rendered. That's the
classic god-hook anti-pattern this proposal targets.

Split (mirrors the doorbell pattern already in place):
- src/hooks/rooms/widgets/usePollSubscriptions.ts (new): the three
  bridge listeners, returns void. Should be mounted ONCE at the
  highest stable level above poll-aware UI (room widgets root). For
  now still mounted by the shim — follow-up PR can move it.
- src/hooks/rooms/widgets/usePollActions.ts (new): the three
  imperative actions. Defensive `?.` on roomSession so a poll action
  during a room transition no longer crashes.
- src/hooks/rooms/widgets/usePollWidget.ts: kept as a deprecated shim
  that composes both — preserves the old `{ startPoll, rejectPoll,
  answerPoll }` shape so existing consumers don't break.
- src/hooks/rooms/widgets/useWordQuizWidget.ts: migrated to import
  usePollActions directly. The word-quiz widget no longer registers
  poll subscriptions transitively — its render no longer has the side
  effect of subscribing to three renderer events.

Doc
- docs/ARCHITECTURE.md "What's already in place": records both god-hook
  splits (doorbell + poll), the now-enabled React Query and Zustand,
  and the test infrastructure. Removes the "not yet enabled" markers
  for #2 and #5.
- "How to pick the next refactor PR": rewritten to reflect that the
  foundations are done. New priority order:
    1. migrate useCatalog's read-only fetches to useNitroQuery,
    2. hoist usePollSubscriptions to room-session level,
    3. split useCatalog along the doorbell/poll lines,
    4. broaden Vitest coverage,
    5. per-tab WiredCreatorToolsView split.

Verification
- yarn eslint on the touched files: 0 errors / 0 warnings.
- yarn test: 22/22 passing, 2 files, ~1.0s.
- Existing useWordQuizWidget consumers (RoomWidgetsView ->
  WordQuizWidgetView) unaffected — they import from the barrel which
  still re-exports the same shape.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:53 +00:00
simoleo89 0755285708 Revert feature-folder migration; keep classic src/components + src/hooks layout
Decision: the src/features/<feature>/ layout introduced as proposal #3
(pilot on doorbell in 8ec9d27) is not the convention the team wants. The
existing src/components/<area>/ + src/hooks/<area>/ split is the one
that stays.

What's reverted
- src/features/doorbell/ is removed entirely. The doorbell view and the
  two hooks move back under the classic paths:
    src/features/doorbell/views/DoorbellWidgetView.tsx
      -> src/components/room/widgets/doorbell/DoorbellWidgetView.tsx
    src/features/doorbell/hooks/useDoorbellState.ts
      -> src/hooks/rooms/widgets/useDoorbellState.ts
    src/features/doorbell/hooks/useDoorbellActions.ts
      -> src/hooks/rooms/widgets/useDoorbellActions.ts
- The compat shims that lived in those classic paths are dropped now
  that the real files are back.
- src/hooks/rooms/widgets/index.ts adds the two new hooks alongside the
  existing useDoorbellWidget shim (kept as a deprecated wrapper so any
  external consumer importing the old shape via the barrel keeps
  working).

What's preserved
- The split between data and actions (proposal #4) — useDoorbellState
  and useDoorbellActions remain two separate hooks. This was the actual
  improvement, and it's independent of where the files sit.
- The bug fixes from 8ec9d27 (close button race, optimistic-remove
  rollback) — both still present, just in the new path.
- src/state/createNitroStore.ts and src/api/nitro-query/createNitroQuery.ts
  are left where they are. They aren't feature folders; they're
  cross-cutting framework code (Zustand skeleton, React Query adapter
  prototype) that any feature can consume.

Doc
- docs/ARCHITECTURE.md section #3 is rewritten to record the decision
  rather than recommend the layout. It now describes the convention to
  follow:
    * views under src/components/<area>/<feature>/
    * hooks under src/hooks/<area>/<feature?>/ (siblings, not subfolders
      per widget)
    * sibling .types/.constants/.helpers files for view-specific code
      (e.g. WiredCreatorTools.*.ts)
- "What's already in place" and "Recently fixed" sections updated to
  point at the new paths.
- "How to pick the next refactor PR" no longer mentions feature-folder
  migration as an option.

Note: the five extra feature folders started this session (reconnect,
nitropedia, ads, hc-center, campaign) were never committed; they only
existed in the working tree and have been restored from HEAD.

Verification
- find src/features -type f -> 0 (directory removed).
- npx tsc --noEmit on all touched files: clean (only the project-wide
  pre-existing TS2307 about @nitrots/nitro-renderer not installed
  locally remains, same as before).
- npx eslint on all touched files: 0 errors, 0 warnings.

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
2026-05-11 16:31:53 +00:00
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 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
duckietm d88defb4a5 🆙 Fixed the commands 2026-05-08 16:44:45 +02:00
Lorenzune 57b83c1097 Refine mobile avatar widgets and login flow 2026-05-07 21:19:15 +02:00
Lorenzune 58e0ed30f6 Merge remote-tracking branch 'duckie-temp/main' into duckie-merge-2026-04-21
# Conflicts:
#	src/components/room/widgets/chat-input/ChatInputView.tsx
#	src/components/toolbar/ToolbarView.tsx
#	src/css/chat/Chats.css
#	src/css/nitrocard/NitroCardView.css
#	src/css/purse/PurseView.css
#	src/css/room/RoomWidgets.css
2026-04-21 11:19:59 +02:00
Lorenzune 9b36513def WIP preserve local changes before duckie merge 2026-04-21 11:13:32 +02:00
duckietm bbd4ccf30c 🆙 Stage 1 Youtube broadcast 2026-04-09 11:54:57 +02:00
Lorenzune c4e1318fd5 fix: polish furniture widgets and area hide toggle 2026-04-03 13:00:05 +02:00
Lorenzune e4b1f14fa2 feat: update room control widgets and menus 2026-04-03 12:09:16 +02:00
duckietm 6609c0325f 🆙 Fix Youtube TV's 2026-03-31 11:41:21 +02:00
Lorenzune 6e76c617c1 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	public/UITexts.example
#	src/api/wired/WiredActionLayoutCode.ts
#	src/api/wired/WiredConditionLayoutCode.ts
#	src/api/wired/WiredTriggerLayoutCode.ts
#	src/components/wired/views/WiredBaseView.tsx
#	src/components/wired/views/WiredSourcesSelector.tsx
#	src/components/wired/views/actions/WiredActionLayoutView.tsx
#	src/components/wired/views/conditions/WiredConditionLayoutView.tsx
#	src/components/wired/views/conditions/WiredConditionTriggererMatchView.tsx
#	src/components/wired/views/triggers/WiredTriggerClickFurniView.tsx
#	src/components/wired/views/triggers/WiredTriggerClickTileView.tsx
#	src/components/wired/views/triggers/WiredTriggerClickUserView.tsx
#	src/components/wired/views/triggers/WiredTriggerLayoutView.tsx
#	src/components/wired/views/triggers/WiredTriggerToggleFurniView.tsx
2026-03-21 14:47:52 +01:00
Lorenzune 27cb71f0cc feat(wired-ui): expand advanced wired editors 2026-03-21 14:27:57 +01:00
DuckieTM 581f7957e8 Merge pull request #31 from duckietm/Dev
Dev
2026-03-21 08:43:37 +01:00
Medievalshell 7be552a523 Merge branch 'main' into improve-mod-tools-ui 2026-03-20 22:25:19 +01:00
simoleo89 11543bb64c feat: custom prefix system with effects, emoji picker and per-letter colors
- 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
2026-03-20 17:07:33 +01:00
Lorenzune 2bbc31b1c7 Merge branch 'duckietm:main' into main 2026-03-18 17:18:48 +01:00
Lorenzune 12e50ff1cd fix(wired-ui): clarify reward fields and mute alerts 2026-03-18 17:03:26 +01:00
Lorenzune 97aae71708 fix(wired-ui): clarify reward fields and mute alerts 2026-03-18 17:01:10 +01:00
duckietm 50a0e3911a 🆙 Fix screen offset being stale after resize 2026-03-18 13:53:14 +01:00
simoleo89 119d12a5ea Add quick commands autocomplete dropdown in chat input
Server-authoritative command list via packet 4050, merged with
client-only commands. Supports keyboard navigation, filtering,
and module-level caching to handle login-time packet timing.

Co-Authored-By: medievalshell <medievalshell@users.noreply.github.com>
2026-03-16 22:41:35 +01:00
simoleo89 38f38d7209 Add badge drag & drop system for InfoStand and inventory
- Drag & drop badges between slots in InfoStand (own user only)
- Mini badge picker on empty slot click with search
- Swap badges between occupied slots
- Hover animation (scale, glow) on badge slots
- Configurable group slot (user.badges.group.slot.enabled)
- Support for 6 badge slots when group slot disabled
- Race condition fix with localChangeRef
- Fixed-size array logic to prevent badge disappearing

Co-Authored-By: medievalshell <medievalshell@users.noreply.github.com>
2026-03-15 20:48:05 +01:00
duckietm 6f44dce04b 🆕 Updated Chooser & Furni command - now glowing ! 2026-02-24 08:16:37 +01:00
duckietm 291fdf80dc 🆙 New: Added a new Chat window, handy for in game / building etc. 2026-02-23 13:18:35 +01:00
DuckieTM 7feb10ab15 🆙 Init V3 2026-01-31 09:10:52 +01:00