Skip to content

Navigation edit mode keeps the 240 px rail and shows the remove control without hover #12519

Closed

Description

@objectstack-fleet

Source

Measured on objectui main d4bc81eab, 2026-10-15, Interfaces pillar of a writable package: pressing Edit turns the rail into "TICKET DESK · NAVIGATION" with an "Unpublished" chip, a "NAVIGATION · 1 item · Add nav item" header and one row per entry with a drag handle. The rail grows from 240 px to about 300 px, so the content column and the aside shift right while editing and back on Done. Each row's "Remove nav item" trash button (engine.inspector.appNav.remove, AppNavInspector.tsx / the edit list) exists only while the pointer hovers the row; a keyboard user tabbing through the rows never sees it.

Target

  1. Edit mode keeps the rail at STUDIO_RAIL_WIDTH (240 px): a 16 px handle, the label, the kind chip and a 24 px trash fit a 32 px row; the api name moves to the row's title.
  2. The trash is always rendered at reduced opacity and full on hover or focus; it is reachable by keyboard.
  3. The content column does not move when entering or leaving edit mode.

File surface

packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (InterfacesPillar edit mode), packages/app-shell/src/views/metadata-admin/inspectors/AppNavInspector.tsx, StudioRail.tsx, tests beside.

Acceptance

  1. Tests: the rail's width class is the same in and out of edit mode; the remove button is in the document without hover.
  2. Screenshots in and out of edit mode at 1280 px, light and dark, with the content column at the same x.
  3. pnpm --filter @object-ui/app-shell test green.

Constraints

Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-15 Studio test pass: objectui main d4bc81eab, HotCRM 63db83e2, the vite console on :5180 against objectstack serve --dev on :4001, viewport 1482 × 1119, dark theme with a light-theme check.

Evidence

Screenshots from the test pass, kept on the temporary branch claude/studio-test-pass-evidence-20261015 of objectstack-ai/objectui (commit 72115588c, never merged: the maintainer ruled that screenshots do not enter main, and asked for a temporary branch referenced from the card). The branch's docs/design/studio-redesign/findings/2026-10-15/README.md maps every file to its card. Pane captures, 800 × 604 of a 1482 × 1119 viewport, dark unless named light.

nav edit item added
docs/design/studio-redesign/findings/2026-10-15/12519-nav-edit-item-added.jpg — nav edit item added.

nav edit mode wider rail
docs/design/studio-redesign/findings/2026-10-15/12519-nav-edit-mode-wider-rail.jpg — nav edit mode wider rail.

nav normal new menu
docs/design/studio-redesign/findings/2026-10-15/12519-nav-normal-new-menu.jpg — nav normal new menu.

Parent: #12446

Body refreshed 2026-10-15T15:19Z by the epic PM session: an Evidence section with the test-pass screenshots added.

Activity

  1. added
    enhancementNew feature or request
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    and removed
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    on Oct 15, 2026
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (domain:ui lane queue)
    Session: session_01SJn1GEkrbbBQkhGC26wEDw
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-12519-nav-edit-rail
    Worktree: objectui-issue-12519 (in the cloud session's own container)
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx (the edit-mode row: grip, label, kind chip, api name, trash; measured on main 9231820 as the real producer of the hover-only trash, about :717, which the card's list did not name), packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (the InterfacesPillar rail's edit-mode width override only, about :4443), studio-design/StudioRail.tsx and studio-tokens.ts (only if the width needs them), metadata-admin/inspectors/AppNavInspector.tsx (only if the api name's move needs it), metadata-admin/i18n.ts (new keys only), new tests beside them, the screenshots docs/design/studio-redesign/after/12519-*.png, one .changeset/12519-*.md (stop on breach; explain in the report)
    Container & model: M, mode:cloud, model: opus (cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 5 on 「上限提到 5,提速」, this session's chat, 2026-10-15)
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: none
    Serial constraints cleared: objectui#12455 (InterfacesPillar) landed as PR objectui#12544 (b541694), so the Interfaces rail is free. No open PR touches AppNavCanvas.tsx, AppNavInspector.tsx or StudioRail.tsx. In flight on StudioDesignSurface.tsx, all this seat: objectui#12453 (the read-only banners and notes, near the Interfaces header but not its edit toggle), objectui#12508 (the client.layered load sites), objectui#12507 (the flow canvas). Any textual overlap is resolved by merging origin/main. objectui#12522 and objectui#12531 (same rail and inspector) stay queued behind this card.

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 12519,
    "status": "done",
    "branch": "claude/issue-12519-nav-edit-rail",
    "pr": null,
    "session": "session_01G4FxFLFyuUkMHJGFLChY8S",
    "premise_still_valid": true,
    "summary": "In navigation edit mode the Interfaces rail now stays at STUDIO_RAIL_WIDTH (240 px), so the content column stays at x 240 on Edit and on Done. Before, edit mode set width={editNav ? 'w-72' : undefined}, which made the rail 288 px. That override is gone, and StudioRail's width prop, which nothing passed any more, is retired too. The AppNavCanvas row (the real producer of the hover-only trash, as the claim said; AppNavInspector was not touched) is now a 32 px line: a 16 px grip, the label, the kind chip and a remove control that is always drawn. The control is a fixed 24 px, sits at opacity-50, and goes to full opacity on hover or focus. The api name moved onto the row's title as 'LABEL · TARGET'. Nested rows indent with classes instead of the inline paddingLeft style. In a narrow rail the canvas header lets the Add button take a line of its own. The compact row applies to both mounts (Studio and the metadata-admin app designer) with no new prop. The premise holds with two corrections: the edit rail measured 288 px, not about 300 px; and the trash was already Tab-reachable on main (objectui#11776), so the defect left was that it was hidden (w-0 opacity-0) until hover or focus, which also shifted the label when it appeared.",
    "tests": "All at HEAD 14a7dd4. (1) Acceptance item 3, the full suite: pnpm --filter @object-ui/app-shell test under the os-verify-lock gave VERDICT command-exit 0, Test Files 1398 passed and 1 skipped (1399), Tests 13382 passed and 9 skipped (13391). (2) Targeted run, root form, vitest run over the AppNavCanvas*, AppPreview*, AppNavInspector*, studio-locale.i18n, InterfacesPillar*, StudioDesignSurface.interfaces* and StudioDesignSurface.nav* files: 35 files, 275 tests. One existing pin failed (navDraftLabels-11862, which read the api name inside the row). After updating it, the six affected files are 72 of 72 green. (3) New pins: InterfacesPillar.editRailWidth-12519.test.tsx (2 tests) checks that the rail's w-* class is exactly STUDIO_RAIL_WIDTH before Edit, during edit mode and after Done; that the content column stays the rail's next sibling; and that in edit mode each top-level row has a remove button without hover (w-6, never w-0 or opacity-0) which Tab focuses right after its row. AppNavCanvas.compactRow-12519.test.tsx (3 tests) checks that the row is h-8, has no api name in its text, has title 'Support Overview · support_overview' (and just 'Queues' for a group), that the remove control is h-6 w-6 shrink-0 opacity-50 and reachable by Tab, and that a nested row has pl-4 and no style attribute. (4) Ablation: each mutation went through objectstack scripts/ablation-replace.mjs in wrap mode (anchor x1 to x0, replacement x0 to x1, blob changed). Each restore was proven with blob == HEAD and an empty git diff HEAD. A1, which puts className={editNav ? 'w-72' : undefined} back on the rail, turned the width pin red ('expected [ w-72 ] to deeply equal [ w-60 ]'). The first A1 attempt was refused by the tool because the replacement contained its own anchor; nothing ran, and I re-anchored. A2 (trash back to w-0 opacity-0): 3 red. A3 (api name code chip back in the row): 3 red. A4 (inline paddingLeft style back): 1 red. A5 (title removed): 6 red. Unmutated, the same six files are 72 of 72 green. No dist was involved: the tests import the source by relative path.",
    "gates": "All local, on 14a7dd4, exit codes captured before any pipe. Dependency closure build: pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build, VERDICT 0. pnpm --filter @object-ui/app-shell type-check, VERDICT 0 (tsc --noEmit plus tsconfig.test.json, which compiles the tests). pnpm --filter @object-ui/app-shell build, VERDICT 0, with dist completeness at 1104 files. pnpm --filter @object-ui/app-shell test, VERDICT 0 (counts in tests). eslint on the 9 touched ts/tsx files: exit 0, 0 errors and 16 warnings, all on untouched pre-existing lines (the two in AppNavCanvas are the (draft as any)[rootKey] line and the setDraft effect). pnpm check:i18n-keys exit 0. pnpm check:i18n-drift exit 0 ('No en value changed in this range'). check:i18n-designer-parity was not owed because no keys were added. node scripts/check-changeset-presence.mjs exit 0 (9 source files in 1 released package, 1 changeset declared). node scripts/check-changeset-no-major.mjs exit 0. pnpm check:new-line-citations exit 0 (0 new). The product-neutral-renderer ratchet test: 32 passed. NOT MEASURED: scripts/check-nul-bytes.mjs does not exist in objectui (MODULE_NOT_FOUND, so the gate never ran). Instead, a control-byte grep over the diff's files found nothing. objectui has no dispatch-gates.mjs, so there was no --ran reconciliation; the gate list above is hand-derived from the dispatch. CI check-runs: none yet, because the PR is not open.",
    "mcp_calls": "5 GitHub MCP reads, no writes: issue_read get #12519, issue_read get_comments #12519, issue_read get #12446, issue_read get_comments #12446, pull_request_read get #12544. Plus 2 claude-code-remote add_repo calls (read access, nothing attached: objectstack and hotcrm are public anonymous reads).",
    "api_writes": "0 REST writes. Only git pushes: the feature branch claude/issue-12519-nav-edit-rail (empty probe at 9231820, then 14a7dd4), and the evidence branch claude/issue-12519-evidence at a813acb. No label writes, no PR, no card comment.",
    "pr_title_draft": "fix(app-shell): navigation edit mode keeps the 240 px rail and draws the remove control without hover (objectui#12519)",
    "pr_body_draft": "(elided by the seat: opened verbatim as the body of PR #12566, read back identical)",
    "files_changed": [
    "packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.tsx",
    "packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (one line: the edit-mode width override)",
    "packages/app-shell/src/views/studio-design/StudioRail.tsx (width prop retired, docblock)",
    "packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.compactRow-12519.test.tsx (new)",
    "packages/app-shell/src/views/studio-design/InterfacesPillar.editRailWidth-12519.test.tsx (new)",
    "packages/app-shell/src/views/metadata-admin/previews/AppNavCanvas.navPayload-11776.test.tsx (pin updated)",
    "packages/app-shell/src/views/metadata-admin/previews/AppPreview.test.tsx (pin updated)",
    "packages/app-shell/src/views/metadata-admin/previews/AppPreview.docEntry-11197.test.tsx (pin updated)",
    "packages/app-shell/src/views/studio-design/StudioDesignSurface.navDraftLabels-11862.test.tsx (pin updated)",
    ".changeset/12519-nav-edit-rail.md"
    ],
    "screenshots": [
    "claude/issue-12519-evidence@a813acb: docs/design/studio-redesign/findings/12519/interfaces-view-light.png",
    "claude/issue-12519-evidence@a813acb: docs/design/studio-redesign/findings/12519/interfaces-edit-light.png",
    "claude/issue-12519-evidence@a813acb: docs/design/studio-redesign/findings/12519/interfaces-view-dark.png",
    "claude/issue-12519-evidence@a813acb: docs/design/studio-redesign/findings/12519/interfaces-edit-dark.png",
    "claude/issue-12519-evidence@a813acb: docs/design/studio-redesign/findings/12519/app-designer-nav-canvas-light.png",
    "claude/issue-12519-evidence@a813acb: docs/design/studio-redesign/findings/12519/README.md"
    ],
    "deviations": [
    "File surface: four existing test files outside the claim's 'new tests beside them' were updated, because they read the api name in the row or the hidden trash classes: AppPreview.test, AppPreview.docEntry-11197, AppNavCanvas.navPayload-11776, StudioDesignSurface.navDraftLabels-11862. Each keeps its subject.",
    "StudioRail.tsx: the width prop was removed, not just left unused, because no caller remains. This is inside the claim's 'only if the width needs them'.",
    "The edit row drops the kind icon before the label. The card's target lists the row as handle, label, kind chip and trash, and at 240 px the icon would cost the label about 20 px. The toned chip still names the kind.",
    "The row title is 'LABEL · TARGET' rather than the api name alone, because the label can truncate at 240 px. The browse rail of objectui#12455 uses 'kind · name' on its tooltip; the edit row already shows its kind as the chip.",
    "Bounded additions in AppNavCanvas: the inline paddingLeft style became DEPTH_INDENT classes (ruling: no inline style), and the canvas header now wraps so Add nav item does not fold onto three lines at 240 px. Both were needed for target 1's fit.",
    "Evidence path: the claim names docs/design/studio-redesign/after/12519-*.png. The dispatch rules that screenshots go to a separate never-merged branch under findings/12519/, so I followed the dispatch.",
    "No new before screenshots were taken; the card's evidence branch has them. Before is given as numbers measured on 9231820: rail 288 px in edit mode, content column x 288, labels 0 to 6 px.",
    "Commit trailers use the model-free pair from objectui AGENTS.md (Claude-Session plus 'Co-authored-by: Claude'), not the harness's model-bearing trailer. That follows the repo's precedence rule.",
    "PR body footer: I used the os-dev session-URL footer form. The harness's separate PR footer line was not added, because two footers would collide with the footer rule.",
    "The shared /home/user/objectui checkout was moved to a detached HEAD at 9231820. A stale local branch claude/issue-12519-nav-edit-rail with no commits was checked out there, and it had to be deleted before the worktree could be created, as the dispatch instructs.",
    "AppNavInspector.tsx was not touched; the api name's move did not need it."
    ],
    "line_budget": "n/a",
    "open_questions": [
    {
    "question": "Assumption 3 scoping: should the compact row apply to the Studio only (a new prop) or to both AppNavCanvas mounts?",
    "options": [
    "A: both mounts, no new prop (built)",
    "B: a Studio-only compact prop, leaving AppPreview's row as it was"
    ],
    "recommendation": "A. On long-term soundness it keeps one row and one look; on startup focus it adds no prop and retires StudioRail.width; on business need the only pull for the shape is the 240 px rail, and the api name is still one hover away; on AI-error resistance it is neutral, since no metadata key moves. The app designer screenshot shows the result."
    },
    {
    "question": "Assumption 1 measured: the rail was w-72 (288 px), not about 300 px. Dropping the override was the whole width fix, and target 3 followed from it (column x 240 in both modes and both themes).",
    "options": [
    "recorded"
    ],
    "recommendation": "No action."
    },
    {
    "question": "Assumption 2 measured: the producer is AppNavCanvas, as the claim said. The card's 'a keyboard user never sees it' was already stale on main, because objectui#11776 had made the trash focusable and shown on focus. What remained was the hover-only drawing and the label shift.",
    "options": [
    "recorded"
    ],
    "recommendation": "No action."
    },
    {
    "question": "Assumption 4 measured: HotCRM's deepest nesting is 1 (crm.app.ts, the only *.app.ts; six groups with leaf children). At depth 1 a row is 189 px wide, its label gets about 104 px and it does not overflow. Child rows carry no trash because they are edited in the inspector.",
    "options": [
    "recorded"
    ],
    "recommendation": "No action."
    }
    ],
    "out_of_scope_findings": [
    "carrier: none · The rail heading 'APP · NAVIGATION' truncates at 240 px beside the Edit/Done toggle in both modes. This was the same before this change and is cosmetic. Noted, not filed.",
    "carrier: none · PR objectui#12544 (#12455) committed screenshots into main under docs/design/studio-redesign/after/, against the maintainer's ruling the card body cites. Read only, not filed."
    ]
    }

    Relayed verbatim by seat domain:ui#4 (session session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final message, the degraded write channel. The seat opened the draft PR #12566 from pr_title_draft and pr_body_draft, with assignee os-sales. "pr": null above is the session's own value at report time.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portaldomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions