From 91938985a261d42c4bd6df3aa8a1c49c049bbb1a Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Wed, 20 May 2026 21:21:40 +0200 Subject: [PATCH] refactor(mod-tools): launcher box gets context strip + section grouping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The launcher panel was a flat stack of four buttons (Room Tool, Chatlog Tool, selected-user + presence dot inline, Report Tool) with no visual hierarchy. The selected-user row was particularly cramped — name, the 2px dot and the 4×4 close-X all crammed into a single button row, easy to misclick. Reorganize into four logical groups, each with a small uppercase section label: Context — gradient strip (emerald when in a room, zinc when not) showing "Room #" or "Enter a room first" with a matching door icon. Source of truth for "what is the mod observing right now"; both Room Tool and Chatlog Tool feed from the same currentRoomId. Room — Room Tool + Chatlog Tool stacked. Both still gate on isInRoom; the disabled state now reads from a single flag instead of repeating `currentRoomId <= 0`. User — When a user is selected: a card with the presence dot (emerald = still in room, zinc = left), the username at a real legible size, a bigger close button, plus a dedicated "Open Info" button to toggle ModToolsUserView. Splitting the click target from the close action removes the misclick footgun. When no user is selected: a dashed-border empty state with a FaUserSlash icon and the "Select a user" hint — reads as a clear "no selection" instead of an active button you can't press. Reports — Report Tool with the open-ticket badge. Badge gets a 2px rose halo box-shadow so a new ticket pulses into view instead of competing with the button background. Locale keys added under modtools.window.section.* and modtools.window.context.room / modtools.window.user.open_info, in both the runtime UITexts.json and the versioned UITexts.example template. The "Open Info" button label is a fix in flight — the old layout overloaded the username row to also open user info, with no separate label. The new explicit button gets its own key so the action is unambiguous (the previous version mislabelled the button as "Mod Action", which is actually a different sub-panel). typecheck + vitest 214/214 + JSON validation all clean. --- public/configuration/UITexts.example | 6 + src/components/mod-tools/ModToolsView.tsx | 127 ++++++++++++++-------- 2 files changed, 86 insertions(+), 47 deletions(-) diff --git a/public/configuration/UITexts.example b/public/configuration/UITexts.example index 63077be..62248b3 100644 --- a/public/configuration/UITexts.example +++ b/public/configuration/UITexts.example @@ -124,6 +124,12 @@ "modtools.window.user.clear": "Clear selection", "modtools.window.tickets.open": "%count% open ticket", "modtools.window.tickets.open.many": "%count% open tickets", + "modtools.window.section.context": "Context", + "modtools.window.section.room": "Room", + "modtools.window.section.user": "User", + "modtools.window.section.reports": "Reports", + "modtools.window.context.room": "Room #%roomId%", + "modtools.window.user.open_info": "Open Info", "modtools.userinfo.refresh": "Refresh user info", "modtools.userinfo.presence.in_room": "In room", "modtools.userinfo.presence.in_room.title": "In the room you are observing", diff --git a/src/components/mod-tools/ModToolsView.tsx b/src/components/mod-tools/ModToolsView.tsx index ac9bd82..ffb2164 100644 --- a/src/components/mod-tools/ModToolsView.tsx +++ b/src/components/mod-tools/ModToolsView.tsx @@ -1,6 +1,6 @@ import { AddLinkEventTracker, CreateLinkEvent, ILinkEventTracker, RemoveLinkEventTracker, RoomEngineEvent, RoomId, RoomObjectCategory, RoomObjectType } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useRef, useState } from 'react'; -import { FaTimes } from 'react-icons/fa'; +import { FaDoorClosed, FaDoorOpen, FaTimes, FaUserSlash } from 'react-icons/fa'; import { GetRoomSession, ISelectedUser, LocalizeText } from '../../api'; import { Button, DraggableWindowPosition, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common'; import { useModTools, useNitroEvent, useObjectSelectedEvent, useRoomUserListSnapshot } from '../../hooks'; @@ -134,63 +134,96 @@ export const ModToolsView: FC<{}> = props => return () => RemoveLinkEventTracker(linkTracker); }, [ openRoomInfo, closeRoomInfo, toggleRoomInfo, openRoomChatlog, closeRoomChatlog, toggleRoomChatlog, openUserInfo, closeUserInfo, toggleUserInfo, openUserChatlog, closeUserChatlog, toggleUserChatlog ]); - const isRoomInfoOpen = currentRoomId > 0 && openRooms.includes(currentRoomId); - const isRoomChatlogOpen = currentRoomId > 0 && openRoomChatlogs.includes(currentRoomId); + const isInRoom = currentRoomId > 0; + const isRoomInfoOpen = isInRoom && openRooms.includes(currentRoomId); + const isRoomChatlogOpen = isInRoom && openRoomChatlogs.includes(currentRoomId); const isUserInfoOpen = selectedUser && openUserInfos.includes(selectedUser.userId); - const noRoomHint = LocalizeText('mod.tools.no.room') || 'Enter a room first'; + const noRoomHint = LocalizeText('modtools.window.no.room'); return ( <> { isVisible && - + setIsVisible(false) } /> - - - + + + + {/* Selected user */} +
+
{ LocalizeText('modtools.window.section.user') }
{ selectedUser ? ( - <> - { selectedUser.username } - - - { - event.stopPropagation(); - setSelectedUser(null); - } } - role="button" - tabIndex={ 0 } - title={ LocalizeText('modtools.window.user.clear') }> - - - +
+
+ + { selectedUser.username } + +
+ +
+ ) + : ( +
+ + { LocalizeText('modtools.window.select.user') } +
) - : { LocalizeText('modtools.window.select.user') } } - - +
+ + {/* Reports */} +
+
{ LocalizeText('modtools.window.section.reports') }
+ +
} { (openRooms.length > 0) && openRooms.map(roomId => CreateLinkEvent(`mod-tools/close-room-info/${ roomId }`) } />) }