Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

console list page: the saved-view tab strip inside the toolbar's leading slot folds to one tab and "N more" at 1440 — the bar's root does not grow, so the width the fit is derived from is its own content's width (regression of #12440) #12518

Closed

Description

@objectstack-fleet

Filing gate ① (a product defect, measured on main), filed on the maintainer's word (chat, 2026-10-15, session session_019YFzJp4o8Ck4NsT1toC4UP, seat account os-bill): the seat ran a functional audit of the HotCRM console on objectui main 720f6d4 (report, private to the maintainer: https://claude.ai/artifact/XPrn5eZMsQaKUgasMJEMmR, finding F1), and the maintainer answered 「同意开卡,使用 pm-dispatch 创建卡片」. Quota note: the audit's cards exceed the filing gate's three-per-fire quota on that word, which approved the report's card list as a set.

Parent: objectstack-ai/objectstack#25945. A regression of #12440 (PR #12473, d4bc81e). Blocks nothing; nothing blocks it.

Reader who acts: the domain:ui lane's next dispatch (seat post #9800), one dev round in @object-ui/plugin-view (one class on one element, one test), size S.

Dedupe (MCP search_issues, scope objectstack-ai/objectui, all states, read 2026-10-15T14:14Z): "saved view tab strip folds to one tab and N more inside the list toolbar leading slot width fit ViewTabBar flex-1 regression" → 10, top 8 read: #12504 (open, retiring the view-write family), #12440 and #12423 (closed, this strip's two parents), #11992, #11395, #10987, #10209, #2959 (closed, unrelated). None is this card.

What happens

On the HotCRM Leads list at 1440 px the tab strip shows one tab, "All Leads", and a "7 more" trigger, although the toolbar's leading slot is 873 px wide and all seven tabs fit in it (the design reference on objectstack-ai/objectstack#25945 shows them). Accounts shows "All Accounts · 5 more", Opportunities "Open Deals · 9 more", Tasks "My Open Tasks · 7 more". A window resize of one pixel changes the count to two; shrinking to 1024 and back leaves one. The same page, loaded on another run, showed all seven: the count depends on when the strip was measured.

Readings (objectui origin/main 720f6d4, read 2026-10-15T14:15Z)

  1. packages/plugin-view/src/ViewTabBar.tsx line 977: the bar's root is flex min-w-0 items-center gap-0.5 -mb-px, with no flex-1 or w-full. Line 985: the strip inside it is flex min-w-0 flex-1 items-center gap-0.5 overflow-x-clip. Lines 424–428: fitFromWidths derives the count from layoutWidth(stripRef.current); lines 444–452: a ResizeObserver on the strip re-fits on every size change.
  2. packages/plugin-list/src/ListView.tsx line 4986: the host slot is flex min-w-0 flex-1 items-center (data-testid="list-toolbar-leading"), inside the toolbar's left group (flex flex-1 … min-w-0).
  3. packages/app-shell/src/views/ObjectView.tsx line 3267 passes ViewTabBar into that slot with no className.
  4. Browser reading (1440, Leads): computed flex-grow down the chain is strip 1 → bar 0 → leading slot 1 → left group 1; widths 190 → 224 → 873 → 873 px. On the run where seven tabs showed, the strip measured 663 px inside a 697 px bar: the bar's width is the sum of whatever tabs are drawn.

So inside the slot the bar is content-sized, the strip's flex-1 is a share of its own content's width, and the fit is self-referential: once a measure happens while one tab is drawn (the strip is 190 px), one tab is all that fits, and the observer confirms it on every resize. Before #12440 the bar sat in a row of its own and took the row's width.

What to build

  1. The bar's root takes its container's width when hosted in a flex slot: flex-1 min-w-0 on the root (or the equivalent w-full), so the strip's width is the slot's free width whatever is drawn in it. No change to the fit algorithm, the overflow trigger or the host slot.
  2. A test in packages/plugin-view that mounts the bar inside a fixed-width flex host (the slot's classes) with the measured widths stubbed, and pins that the visible count follows the host's width, not the drawn tabs' width: at an 852 px host with seven 90 px tabs, all seven show; at 400 px, four show and "3 more"; growing the host back re-fits to seven.

Acceptance

Out of scope

Attribution: written by the domain:ui seat in session session_019YFzJp4o8Ck4NsT1toC4UP (account os-bill) on the maintainer's words quoted above.

Screenshots (added 2026-10-15T14:45Z, on the maintainer's word 「专用分支存图,卡里嵌 raw 链接」)

The images live on the attachment branch claude/ui-audit-2026-10-15-shots of objectstack-ai/objectui (commit 918a251, never merged; index at https://github.com/objectstack-ai/objectui/blob/918a2510a54bd6875b90eb6996d098c3ec0451dd/docs/design/console-audit-2026-10-15/README.md). Full set and figure numbers: the audit report linked above.

Leads list at 1440 on 720f6d4: one tab and "7 more" in an 873 px slot

leads-1440.png

Leads list at 1024: one tab and "7 more"

leads-1024.png

Accounts list at 1440: one tab and "5 more"

accounts-1440.png

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    and removed on Oct 15, 2026
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (priority:p1; a regression of objectui#12440, filed on the maintainer's 「同意开卡,使用 pm-dispatch 创建卡片」)
    Session: session_01JZnppgHRp4sREcouhkQYDB
    Account: sunhaolin
    Branch: claude/issue-12518-view-tab-strip-width
    Worktree: objectui-issue-12518
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: packages/plugin-view/src/ViewTabBar.tsx, the bar's root element only (:977 on main 720f6d4), and the ViewTabBar tests beside it; one .changeset/12518-*.md. The fit algorithm, the "N more" menu, the mobile view picker, ListView.tsx's host slot and ObjectView.tsx are outside the surface (stop on breach; explain in the report).
    Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui names no path-derived mandate)
    Clause-②: no
    Responsibility:


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 12518,
    "status": "done",
    "branch": "claude/issue-12518-view-tab-strip-width",
    "pr": "#12537",
    "session": "session_01JZnppgHRp4sREcouhkQYDB — the dispatching PM session (subagent = parent's), as named in claim 6194570270",
    "premise_still_valid": true,
    "summary": "ViewTabBar's root now carries w-full (one class on one element, in packages/plugin-view/src/ViewTabBar.tsx). In the list toolbar's leading flex slot the strip's width is therefore the slot's free width, so the fit no longer reads the tabs it is drawing and collapses to one tab and 'N more'. w-full was chosen over flex-1 after measuring every host in real Chromium. A2: the one JSX mount (app-shell ObjectView) places the bar in two hosts, the list-toolbar-leading flex row and the refused-view view-tab-row block; hotcrm and objectstack have no mounts; the mobile picker does not mount it. The block host is byte-identical in width with and without the change, and a column-flex host keeps its 38 px height under w-full but grows to 199.5 px under flex-1. Deviations from the dispatch: the new test narrows to 500 px rather than 400 (400 px leaves 366 px of strip, 4 x 92 + 80 = 448 exceeds 366, so the fit answers three, not four), and A3 is pinned as 200 → 852 → 851 → 852 ending at seven. A1 held on c3de0bc. No new prop, export or type; ListView.tsx and ObjectView.tsx untouched.",
    "tests": "All at HEAD 947e3c2. (1) New packages/plugin-view/src/tests/ViewTabBar.hostWidth-12518.test.tsx: a one-line row-flex layout model read from the rendered classes, plus a ResizeObserver stand-in that delivers until the layout settles. Alone: 'Test Files 1 passed (1) / Tests 3 passed (3)', VERDICT command-exit 0. (2) Ablation, one-off with no permanent file, through objectstack scripts/ablation-replace.mjs (wrap mode): it put the pre-change root class string back ('anchor x1 → x0', 'replace x0 → x1', blob cb01b5f8b12b → cf7ebf1549fc). Result 'Tests 3 failed (3)': 'expected 164 to be 818' (strip collapsed to one 90 px tab + 72 px trigger + gap), "expected [ 'v1' ] to deeply equal [ 'v1', 'v2', 'v3', 'v4' ]", "expected [ 'v1' ] to have a length of 7 but got 1". Restore: 'blob after restore cb01b5f8b12b == blob at HEAD', 'git diff HEAD empty'; my own check gave diff-lines=0 and anchor counts 1/0. Direction: turned red, as expected. The test imports ../ViewTabBar relative to src, so no dist/ was in the path. (3) Union, one vitest invocation under the lock: the whole packages/plugin-view/ (every ViewTabBar* suite, readonlyViewMenus-10209), plugin-list ListView.toolbarLeading-12440, the 20 app-shell ObjectView.* files that reference the bar (incl. viewTabsInToolbar-12440 and viewRefusal-12112, which assert on its test ids, and relayRungCensus-7559), the census suites iconOnlyButtonCensus-12143 and ariaLabelLiteralCensus-12145, and the changeset suites changeset-polarity-census and check-changeset-{claims,no-major,presence,fixed,overwrite}. Result: 'Test Files 106 passed (106) / Tests 1205 passed (1205)', VERDICT command-exit 0. (4) One-off real-Chromium harness, deleted afterwards: the console Vite dev server, the real component and the real Tailwind build, before = className w-auto. Toolbar host after: 7 tabs at 1440; 1440→1439→1440 ends at 7; 3 tabs + '4 more' at 1024 (harness slot 484 px); back to 1440 = 7; reload = 7; load at 1024 then 1440 = 7. Before: 1 tab + '6 more' at every step, bar 218.4 / strip 184.4 inside a 900 px slot. Harness note: my pre-start port check used ss, which is absent here, so its '0' was not a reading; vite --strictPort was the guard that held, and the server was stopped by its own PIDs.",
    "gates": "Local subset at 947e3c2; exit codes were captured to files before any pipe. objectui has no scripts/pm/dispatch-gates.mjs, so the list was derived by hand from package.json check:* and lint.yml / ci.yml / changeset-guard.yml / changeset-presence.yml / line-citation-gate.yml / repo-gates.yml / performance-budget.yml, and no --ran reconciliation exists for this repo. (a) pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-view^...' build, then pnpm --filter @object-ui/plugin-view type-check (echoed 'tsc --noEmit && tsc -p tsconfig.test.json'): VERDICT command-exit 0; tsc -p tsconfig.test.json --listFiles counts the new test file (1 hit, 71 plugin-view test files). (b) eslint --no-inline-config on the two changed .tsx files: EXIT=0, 0 errors; 3 warnings, identical on the base blob. (c) EXIT=0 each, with its own pass line: check:control-bytes ('OK (scanned 8864 tracked text file(s)'), check:new-line-citations ('0 new citation(s)'), check:changeset-claims, check:pending-changeset-literals, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:i18n-keys, check:shell-escape-residue, scripts/check-changeset-no-major.mjs, check-changeset-presence.mjs ('2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-fixed.mjs, check-changeset-overwrite.mjs. (d) check-governed-queue-guard --test on the 3 paths: NOT GOVERNED. CI, read once at PR open (36 runs): Changeset Fixed Group Check success; Changeset Bump Policy success; label success; dependabot, Test (coverage) and the coverage-shard matrix template skipped; Type Check, Lint, Test (shards 1-16/16), Test (dist pins), Build & E2E, Build Docs, Repo Gates, Line Citation Gate, Changeset Declaration, Changeset Claim Re-read, Changeset Overwrite Report, Bundle Analysis, Spec Main Shape Gate, Governed Surface Queue Guard and Live E2E (informational) in_progress. CI convergence belongs to the PM. The PR is 'behind' main by 2 commits (objectui#12474 touched ListView/ObjectView but not the slot or the mount); git merge-tree against the new main is clean, so no merge was made.",
    "mcp_calls": "5 — read-only: issue_read (get #12518; get_comments #12518; get_comments #12518 for this report's read-back), pull_request_read (get #12537 for the body read-back; get_check_runs #12537). No MCP write tool used.",
    "api_writes": "2 — both through the fleet relay (scripts/pm/fleet-write/dispatch.mjs, as objectstack-fleet[bot]): POST /repos/objectstack-ai/objectstack/dispatches carrying pr_create (executed POST /repos/objectstack-ai/objectui/pulls, draft, and POST /repos//issues/12537/assignees [sunhaolin]); POST /repos/objectstack-ai/objectstack/dispatches carrying this comment (POST /repos//issues/12518/comments). No label writes. git pushes of the branch (not REST): empty-branch probe plus three commit pushes.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted in PR 12537's Acceptance notes, not filed · fitFromWidths adds the strip's 2 px gap after every tab, the last included, so 'everything fits' needs 2 px of slack past the drawn content; in a content-sized strip that alone tipped the first fit (642 px of strip for 644 px of tabs); harmless once the bar takes the slot's width · observation, no reach"
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 1 · session_01JZnppgHRp4sREcouhkQYDB · 2026-10-15T17:22Z

    • Merged: PR fix(plugin-view): the view tab bar takes its host's width, so the list toolbar's tab strip no longer folds to one tab (objectui#12518) #12537 landed through the merge queue as main 92318202a1 at 2026-10-15T17:21Z. It has one parent, 646307f37a (objectui#12520's landing), and is an ancestor of origin/main.
    • Verified by content: all 3 files the PR changes are blob-identical on 92318202a1 to the accepted head 947e3c2001.
    • Mis-close scan: the PR body's only closing keyword is Fixes objectstack-ai/objectui#12518. The only issue closed with the merge is this card, as completed.
    • What now holds:
      • The saved-view tab strip takes the toolbar slot's width (w-full min-w-0) instead of folding to one tab and "N more".
      • On HotCRM's Leads list, with its 8 views, the strip shows 6 tabs and "2 more" at 1440, and 3 tabs and "5 more" at 1024, with no tab clipped. Resize and reload end at the same count.
      • The browser reading passed (ACCEPT 6197754861). It notes the card's seven-tab expectation against HotCRM's eight views.
    • The chain: claim and dev report 6195471981, ACCEPT 6197754861.

    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:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p1

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions