feat(web): slice 2a — Servers list and Server Detail on HeroUI (feature 014, T066-T089) - #364
Conversation
…server, modules, backups, admin, share links (feature 014, T062-T065 T090-T096 T122-T125 T141-T144 T164-T171) design.pen: every remaining screen, dialog and composition redrawn from the HeroUI definitions; zero lunaris c: refs remain outside the lunaris library root; c:Mode axis dropped document-wide (OD-10); dQV9N disabled table removed (OD-11); placeholder buttons relabelled (OD-12: Add port override / Remove / Discard / Restore / Refresh / Download / Delete / overflow); HeroUI tabs bar height kept (OD-13); Create server button keeps the leading check icon (OD-14). Three dialog definitions recreated with new ids (T1LzpU->Jpl8j Clone Server, rdlrx->NVN2r Transfer Ownership, t9irnv->FhrUm Wipe World); iGBIs->F9pUrx, mQ1zB->Hy9r0. CodeRabbit PR #349 design findings applied here: z9ShNE/rNhll wrap HeroUI button refs, DWztv/tooKB labels on $typography/font-sans, N1GkB/ljdA5 license text below the SSO buttons, FyV6E separator fill cleared. design-export: JSON (depth 20, no elisions, no c: refs, no c:Mode) and 2x screenshots re-exported for every touched node; MANIFEST sections for slices 2a-5 and the fix waves. specs: component-map/plan/tasks reference the new node ids; design tasks marked done. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…, T066-T067) Servers: HeroUI Table/Tabs/Input/Chip with hero PhaseChip, FilterPopover, StatCard, GameIcon; data hooks and lifecycle mutations unchanged; all 24 test cases kept on role queries. ServerDetail: header on HeroUI Button + PhaseChip, tab bar on HeroUI Tabs; tunnel tests untouched. Design: F9pUrx, EZFW0, S4k0x, I9kvlZ (design commit 3c40edb). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…Logs, Files, Players (feature 014, T068-T073) Overview: HeroUI Card/Alert with hero StatCard/Sparkline, PVC provisioning-failure alert (o4LH8W). Console/Logs: xterm and virtualised engines untouched, chrome only (ConsoleShell toolbar on HeroUI Button/Input). Files: HeroUI Table/Breadcrumbs and Modal compound parts for new file/folder. Design: EZFW0 Hy9r0 IzuY2 TE2jI o4LH8W P08Uw Xn5ns kPmoo FtdkI Burtr dPP50. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…014, T074-T083) ServerStatusCard/ServerSleepCard/ServerActionsCard on HeroUI Card/Chip/Button/Modal/Select; ServerActionsMenu on Dropdown; Clone/Transfer/Wipe/Delete dialogs on Modal and AlertDialog/Danger with new co-located tests; EventList and PortOverridesEditor rebuilt. Design: EZFW0 Hy9r0 IzuY2 F9pUrx Jpl8j NVN2r FhrUm P08Uw J5pjJ3. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…e objects, live servers-core spec, slice2a screenshot spec (feature 014, T084-T086) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…vers and ServerDetail (feature 014, T087) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…sts, awaited option clicks in TransferServerDialog.test, escaped apostrophe in Overview alert (CI ESLint on fe90a64) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Tabs = Tabs.List + Tab id/children (Servers, ServerDetail; tablist aria-labels on the list); Table.Root/ScrollContainer/Content compound with renderEmptyState (Servers); Input/Button without startContent, icons in children (Servers, Files, Players, Logs, ConsoleShell, Overview); Button variants outline/ghost/secondary/danger and no color prop; icon-only buttons carry aria-label with a span title for the tooltip (Servers, Players, PortOverridesEditor); ServerActionsCard Select compound; Breadcrumbs current item aria-current; unused imports dropped; DeleteServerDialog.test typing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…, tablist names, drawer-scoped queries (CI mock+live on a9dc498) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012eNzkxWht6owTjFzMKWr98 Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Use vi.hoisted() to allow mock functions to be used in vi.mock factory. Vitest requires mocks to be declared in hoisted scope when referenced in vi.mock calls; wrap with vi.hoisted() and destructure for proper ordering. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
… on 6868e0f) - Remove inner span wrapper with aria-current from Breadcrumbs: HeroUI v3's Breadcrumbs.Item automatically applies aria-current="page" when href is undefined (current item), so the extra span was creating duplicate attributes. Playwright strict mode requires exactly one matching element. - Update e2e selectors in consolePTY, dataScreens, liveAgentScreens: use getByRole("tablist") and getByRole("tab") instead of generic nav/button queries for better semantics and robustness. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
- CloneServerDialog: wrap Input in TextField with Label and FieldError for proper v3 field compound structure. - ServerActionsCard: use Checkbox.Control + Checkbox.Content instead of Label wrapper for HeroUI v3 checkbox compound API. - ServerActionsMenu: unchanged structure (kept as-is from prior commit). - ServerDetail: wrap Tabs in <nav> to apply scrollbar-thin styling consistently. - Overview, Players: minor updates for v3 component API. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…k+live on 6868e0f) Update unit and integration tests to work with HeroUI v3 component changes: - Use userEvent.click instead of fireEvent.click: HeroUI v3's Button/Trigger components use react-aria's onPress handler, which requires full pointer sequence (not just bare click event). userEvent simulates complete user interaction. - Update dialog/modal selectors: Modal, ModalDialog, and related elements have updated roles and attributes in v3. - Update select/listbox queries: adapt to v3 Select compound structure. - Preserve all test assertions and counts; fix only the element selection queries and interaction methods. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…est's Filter button query matches once (review of f9b8a83) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…ismatch (CI vitest on 9923a96) - TransferServerDialog: the owner-picker ListBox never set selectionMode, so HeroUI/react-aria's onSelectionChange never fired on click — the popover never closed and userId never updated. Set selectionMode="single" and read the selected key out of the Selection set/"all" value instead of stringifying it directly. - FilterPopover: Apply/Clear buttons used onClick (not wired for a react-aria Button, which listens on onPress) and set aria-label text ("Apply filters"/"Clear all filters") that diverged from their visible label, so clicks never fired and role queries for "Apply"/"Clear" never matched. Switch to onPress and drop the diverging aria-label; update FilterPopover.test.tsx's own name queries to match. - ServerActionsMenu.test.tsx: three tests call vi.mocked(can).mockReturnValue(false), which vi.clearAllMocks() in beforeEach does not undo (it clears call history, not implementations) — the override leaked into every later test in the file, disabling Clone/Transfer/etc. Restore the default can() implementation in beforeEach. - ServerActionsCard.test.tsx: "cancels dialog without running action" asserted queryByText("Test") was gone after Cancel, but the action button underneath is permanently labeled "Test" too — scope the assertion to queryByRole("dialog") instead. - DeleteServerDialog.test.tsx: asserted the fallback text ("delete failed") that errorText() only uses for non-Error rejections; a real Error's own message ("API error") is what actually renders. Assert the real message. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
… console seed (CI live on 9923a96) - servers-core.spec.ts: scope the clone/transfer/wipe/delete dialog locator to getByRole("dialog", { name: dialogHeading }) — a bare getByRole("dialog") is a Playwright strict-mode violation once the Server Actions dropdown's own role="dialog" popover is still data-exiting when the modal opens. - servers-core.spec.ts: FilterPopover's Apply/Clear buttons dropped their diverging aria-label ("Apply filters"/"Clear all filters") in the matching vitest fix — update the live spec's button name queries to the plain visible text, and force the facet checkbox .check() calls through HeroUI's Checkbox label (which visually covers the native input and fails Playwright's actionability check, though a real click on the label toggles the associated input natively). - _seed.ts: seedTemplate declared no console capability, so resolveConsoleMode() defaulted to "none" and ServerDetail hid the Console tab outright — "all six core tabs render" clicks it. Add consoleMode: "pty" to the shared seed template. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…button, alertdialog role for wipe/delete live spec (review of 1b91f0c) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…CI on 6f13e3b) The Servers table's Table.Header had no Column with isRowHeader set, which react-aria-components' TableCollection.updateColumns requires. Every render of the servers table (46 renders across Servers.test.tsx) threw a synchronous-recovery exception from inside react-aria's CollectionBuilder that vitest reported as an unhandled error, even though the visible assertions all passed. Add isRowHeader to the "name" column to satisfy the collection invariant. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
"server detail: all six core tabs render" asserted getByText(/players online/i) on the Players tab, but Players.tsx has never rendered that phrase — it renders "<online> online" or "<online> / <max> online". Match the actual dynamic text instead. Also wait for domcontentloaded after ServerDetailPage.goto's navigation, mirroring every other live spec's beforeEach, so the first assertion isn't racing the navigation. "clone/transfer/wipe/delete dialogs open and cancel..." timed out clicking the "Server actions" trigger between dialogs: HeroUI's ModalBackdrop is a DOM sibling of the dialog with its own exit animation, and can still be a pointer-events-opaque data-slot="modal-backdrop" layer over the page after the dialog's own role element already reports hidden. Wait for every modal backdrop to fully detach before the helper hands control back to the next openMenuAndDialog call. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…e, Overview selected explicitly with retry (review of ef6e551) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…ot unmount (CI live on 2180f45) HeroUI v3's Modal backdrop animates out with opacity 0 rather than immediately unmounting from the DOM. The exit animation takes 100ms. Updated the wait condition to check that all backdrops are either removed from the DOM or have opacity 0, instead of waiting for complete removal which never happens. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…ug + raf polling (CI live on cf1891d) Trace from run 34162882945 (job 101869389019) showed the failing waitForFunction call carried arg.value = {timeout: 5000} — the second positional parameter of page.waitForFunction(fn, arg, options) is the function's *argument*, not its options, so no five prior attempts at this wait (dialog toBeHidden, backdrop count 0, backdrop opacity 0) ever actually applied a 5s timeout. The wait silently fell back to Playwright's default polling with no timeout of its own, then died with the surrounding test's 30s timeout — exactly the "Test timeout of 30000ms exceeded" (not a waitForFunction-specific timeout) reported on every run. Also switch off the default raf polling to a numeric interval: raf polling re-checks the predicate once per requestAnimationFrame callback on the page, which can be throttled far below real-time on a backgrounded (never-foregrounded) live-mode tab, stalling a predicate that may already be true. An explicit polling interval re-checks on a plain interval independent of the page's own animation-frame loop. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
… a real HeroUI backdrop-leak bug (CI live on cf1891d) A second trace (run 34167471686, job 101882626929, after the arg/options + polling fix) showed the wait now correctly reports its own "TimeoutError: page.waitForFunction: Timeout 10000ms exceeded" — and querying the reconstructed DOM at that exact moment (via the trace's snapshot iframes) shows the [data-slot="modal-backdrop"] element is still present, data-exiting="true", opacity back at its resting value of 1, pointer-events: auto, 10+ real seconds after Cancel. It never goes away. Reading react-aria-components' ModalOverlay (node_modules/react-aria-components/dist/private/Modal.mjs) explains why: it tracks isExiting = isOverlayExiting || isModalExiting, where isModalExiting comes from a second useExitAnimation bound to an internal `modalRef` meant to be attached by *nested* modal content. HeroUI v3.2.4's ModalBackdrop/ModalContainer (and the equivalent AlertDialogBackdrop/AlertDialogContainer used by the wipe/delete AlertDialogs) are composed as *siblings* under <Modal> — the pattern every dialog in this codebase already uses — so that modalRef is never attached to anything. useAnimation's `if (isActive && ref.current)` guard (react-aria/dist/private/utils/animation.mjs) then never fires for it, isModalExiting latches true the first time any dialog closes, and the backdrop's "should I unmount" check is permanently disabled for the rest of that component instance's life — a real HeroUI/ react-aria-components bug, not a test timing issue, reachable from any dialog in the app. Fixing the composition itself touches production markup for six components and is out of scope here; tracked as a follow-up. This test works around it the way a real user would recover: reload for a fresh, never-yet-closed component instance before opening the next dialog, so one dialog's stuck backdrop can't block the next one's trigger click. No assertion is weakened — every dialog still gets its own open/ visible-heading/cancel/hidden/still-exists checks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
… the backdrop unmounts after close (HeroUI v3 exit-state latch) HeroUI v3.2.4's ModalBackdrop/ModalContainer (and the equivalent AlertDialogBackdrop/AlertDialogContainer) wrap react-aria-components' ModalOverlay and Modal respectively. ModalOverlay's exit state is `isOverlayExiting || isModalExiting`, where isModalExiting tracks an internal modalRef that react-aria-components only attaches when the Modal (ModalContainer) is rendered as a descendant of the ModalOverlay (ModalBackdrop), via InternalModalContext (react-aria-components/dist/private/Modal.mjs). All six dialog components in this codebase composed the backdrop and container as siblings under <Modal>/<AlertDialog> instead, so that modalRef was never attached and isModalExiting latched true the first time any dialog closed, permanently disabling the backdrop's "should I unmount" check for the rest of that component instance's life — a full-viewport position:fixed backdrop left in the DOM blocking every later interaction on the page. Fix: nest ModalContainer/AlertDialogContainer inside ModalBackdrop/AlertDialogBackdrop in CloneServerDialog, TransferServerDialog, WipeServerDialog, ConfirmDialog (used by DeleteServerDialog), ServerActionsCard, and Files.tsx — matching HeroUI's own type composition (ModalContainerProps/ AlertDialogContainerProps wrap react-aria-components' Modal, which ModalBackdrop/AlertDialogBackdrop's ModalOverlay expects as a nested child). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…ge again; backdrop must unmount Now that the six dialog components nest ModalContainer/ AlertDialogContainer inside their backdrop, the backdrop actually unmounts once its exit animation completes — so the reload-between- dialogs workaround from 02b1f47 is no longer needed. Restore a single page load exercising all four dialogs (clone/transfer/wipe/delete) in sequence, keeping every existing assertion (visible, heading, cancel, hidden, server still exists), and add the assertion that proves the fix: the backdrop element ([data-slot="modal-backdrop"] / [data-slot="alert-dialog-backdrop"]) has count 0 after each dialog is cancelled. Also fixes the comment's inverted description of the root cause: react-aria-components expects the container nested inside the backdrop; composing them as siblings (this codebase's prior pattern, now fixed) is what leaked the backdrop. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…debar so the backdrop unmounts after close (same HeroUI v3 latch as d51de9d) HeroUI v3's DrawerBackdrop (wrapping react-aria's ModalOverlay) tracks exit state as isOverlayExiting || isDrawerExiting. The isDrawerExiting flag only gets set when DrawerContent is rendered as a descendant of DrawerBackdrop (via InternalModalContext). Before the fix, they were siblings under Drawer.Root, so isDrawerExiting never attached to anything and latched true on first close, permanently disabling the backdrop's unmount check. Nesting DrawerContent inside DrawerBackdrop matches HeroUI's type composition and fixes the exit-state latch. Also extends the e2e backdrop-count locator to include [data-slot="drawer-backdrop"] so all three backdrop types (modal, alertdialog, drawer) are verified to unmount after close. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0179Kk2kNC9XQD8n8nKG2fsZ Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
feat(web): slice 2a — Servers list and Server Detail on HeroUI (feature 014, T066-T087)
|
Important Review skippedToo many files! This PR contains 195 files, which is 95 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (122)
📒 Files selected for processing (195)
You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CI Report - 48cc512run 34262393335 - attempt 1 0 failed - 7 passed - 10 skipped - 13m20s elapsed Coverage
Changed
Slowest jobs
e2e buckets run
|
Summary
Lands feature 014 slice 2a — Servers list and Server Detail on HeroUI — into
master. This is exactly the content of #351 (014c-servers-core → 014b-shell-login, merged 2026-09-08) plus its merge commit; 014b carries nothing else beyond what #349 already merged.feat(design): Pencil frames anddesign-export/snapshots for slices 2a–5feat(web): Servers list + ServerDetail shell (T066–T067); detail tabs Overview/Events/Console/Logs/Files/Players (T068–T073); server cards, actions menu and dialogs (T074–T083)test(web)/docs(web): e2e role selectors and page objects,web/specs.mdSlice 2a section, tasks T066–T089 marked doneSpec:
specs/014-heroui-web-rebuild/(tasks T066–T089). Downstream slices 2b/3/4/5 (#363, #352, #362, #353) are stacked on 014c and will be rebased onto master once this lands.Test plan
🤖 Generated with Claude Code
https://claude.ai/code/session_01Euoj6Jxu2a2gBVqSM6VjcB