Repository navigation
console list page: the toolbar's settings cluster folds into one Display popover, view tabs size to their width and carry the view's type icon, the row-number cell yields to the checkbox on hover, and the page header is one row #12423
Description
Filing gate ③, a task the maintainer dispatched in person (chat, 2026-10-15, session session_019YFzJp4o8Ck4NsT1toC4UP, seat account os-bill). The maintainer's words, verbatim: 「我关心的是平台整体界面的布局优化,帮我设计一个按照你的意见改进之后的版本给我看看。」, then on the first redesign draft 「有的的工具栏还是太多,是否建议文字去掉,视图切换是否建议改为下拉,或者你有更好的建议。」, and the dispatch order 「按这个方案在 objectui 开一个 issue,使用 dispatch 技能,然后你负责派发开发。开发完了使用右侧的浏览器测试验证并给我看效果。crm 本身的元数据不重要,我关心的还是平台的基础能力。」 The approved design is the interactive prototype at https://claude.ai/artifact/8JLqDFmcuK82RS1Cb8219f (private to the maintainer; the artboard "Leads 列表页 · 改进版" and its three notes). This card carries the platform half only; nothing in it is HotCRM metadata.
Reader who acts: the domain:ui lane, seat 3 (this session), one dev round across @object-ui/plugin-list, @object-ui/plugin-view, @object-ui/app-shell, @object-ui/components, @object-ui/plugin-grid and the ten @object-ui/i18n packs.
Dedupe (MCP search_issues, scope objectstack-ai/objectui, all states, read 2026-10-15T03:58Z; the control query "per-user list state view tab order filter state sort column widths" answered #12418 as expected, so the channel was live for these reads): "list toolbar Display popover settings columns density group view type" → 3 (#12418 open, #11984 closed, #11642 closed); "view tabs overflow more maxVisibleTabs adaptive width tab strip" → 3 (#2959, #753, #637, all closed); "row numbers column grid showRowNumbers toggle checkbox hover selection" → 4 (#11816, #11322, #4140, #2159, all closed). None is this card. #12418 (per-user list state onto sys_user_preference) is the sibling that owns PERSISTENCE; this card changes what the toolbar shows and persists nothing new (see Out of scope).
The problem in one screen
On a 1440-px desktop the Leads list of the HotCRM demo spends about 290 px of chrome before its first row — app bar, a two-line page header, the view-tab row, a second toolbar row, the table header — and the toolbar row offers up to twelve controls at once: the visualization switcher, Edit inline, Hide fields, Filter, Group, Sort, Color, Refresh, Export, Search, plus the view tabs' own filter/sort micro-icons. The saved-view strip shows a fixed four tabs and folds the rest into "4 more" whatever the width. Every row draws a row number AND a checkbox AND a row-actions button. The maintainer's verdict on the first redesign draft was that the row still carried too many controls. The agreed shape (the prototype above) is: saved-view tabs on the left sized to the width, four controls on the right — Filter, Sort, Display, Search — and everything else inside Display.
Readings (objectui origin/main fc528c3, read 2026-10-15T03:56Z)
- The toolbar's right cluster is
packages/plugin-list/src/ListView.tsxlines 4938–5500:ViewSwitcherDropdown(4943, when offered), the inline-edit toggle (4958), Hide fields (4976), Filter (5047), Group (5083), Sort (5131), Color (5207), the compact gearViewSettingsPopover(5338, rendered only whentoolbarFlags.compactToolbar), Refresh (5369), Export (5385), Search (5481). Hide fields, Group, Color and the inline-edit toggle each carry!toolbarFlags.compactToolbar, so the bundled form already exists as a MODE; it is not the desktop default. packages/plugin-list/src/components/ViewSettingsPopover.tsx(392 lines) already bundles Group, Color, Density, Hide fields and the inline-edit entry under one trigger, with the hintlist.viewSettingsHint"Grouping, color, density, and visible fields. Applies to everyone who uses this view." It has no Layout row, no row-number row, no count of non-default settings and no reset.- View tabs:
packages/plugin-view/src/ViewTabBar.tsxline 271maxVisibleTabs = 4is the default cap, independent of width; line 870 is the overflow "N more" menu (view.moreViews) with the active-in-tail swap (352–363); lines 517 and 907 draw 10-px filter/sort micro-icons per tab. The console (packages/app-shell/src/views/ObjectView.tsx4099) passesviewTypeIcons={VIEW_TYPE_ICONS}and nomaxVisibleTabs, and feedshasActiveFilters/hasActiveSortfrom the view's AUTHORED filter and sort (4085–4086), so the micro-icons say "this saved view has a filter", which the Filter button's own count already says once that view is active. - Row numbers:
packages/plugin-grid/src/ObjectGrid.tsxline 5913 passesshowRowNumbers: trueunconditionally.packages/specdeclares no row-number key on the list view (grepshowRowNumbersoverpackages/spec/srcon objectstackorigin/mainanswers 0 files; the control greprowHeightover the same tree answers hits), so it is a renderer default, not metadata. - Row cells:
packages/components/src/renderers/complex/data-table.tsx2878–2893 is the header select-all; 3206–3214 draws the row checkbox on every row unconditionally; 3216 is the row-number cell; the row-actions cell is always drawn. - Page header:
packages/app-shell/src/layout/PageHeader.tsx155–190 draws title and subtitle on two lines (anh1and ap,py-2.5 sm:py-3); the console passes the object description assubtitle(ObjectView.tsx3888).
What to build
A. View row.
ViewTabBar: the number of visible tabs is derived from the available width (measure the strip with aResizeObserveror equivalent); themaxVisibleTabsconfig key becomes an optional CAP on top of that; the "N more" overflow with the active-in-tail swap stays exactly as pinned today (data-testid="view-tab-overflow"and its siblings keep their names).- A tab shows its view-type icon only for a non-default type (board, calendar, gallery, timeline and the rest: whatever
viewTypeIconsresolves for aview.typeother than the grid/list type); a grid tab shows the label alone. The per-tab filter/sort micro-icons (517, 907) are removed;hasActiveFilters/hasActiveSortstay on the prop type so a consumer may still pass them, but they draw nothing in the strip.
B. Toolbar right cluster (ListView.tsx), desktop: Filter, Sort, Display, Search, in that order, with the existing count chips on Filter and Sort.
- Display is the existing
ViewSettingsPopoverpromoted to the default desktop shape (today'scompactToolbarbehaviour becomes the default; keep the flag for a host that wants the wide form), relabelled with a new keylist.display"Display", and extended with: a Layout row that hosts the existingViewSwitcherDropdowncontent, shown only whenviewSwitcherOffered; the existing Group, Color, Hide fields and Density rows; a Row numbers switch; the Edit inline entry as a switch; a footer with "Reset to view default" plus the existing Refresh and Export entries. The trigger shows a count chip of settings that differ from the view's authored defaults (same chip style as Filter and Sort) and carriesaria-expanded. - Refresh and Export leave the bar (they live in the Display footer); the wide-form buttons Hide fields, Group, Color, Edit inline, Refresh and Export are no longer rendered by default on desktop.
- Persistence stays EXACTLY what each control persists today (view settings through the existing write path; inline edit and the search term as they are). Row numbers is a session-only, in-memory setting defaulting to the renderer's current default (on). ⛔ No new storage key, no localStorage key, no preference slot: that is console lists: per-user list state (view tab order, filter state, sort, column widths) moves from localStorage and shared-view writes onto sys_user_preference ui.* keys; a toolbar sort or drag-reorder no longer rewrites the shared view #12418's card.
C. Grid rows (data-table.tsx, ObjectGrid.tsx).
- The leading cell draws the row number by default and swaps to the selection checkbox on row hover, on focus-within, and whenever the table's selection is non-empty (every row shows its checkbox while anything is selected). The header keeps its select-all checkbox always. With Row numbers off, the cell is blank until hover. No layout shift: one cell of fixed width, the two children toggled by CSS classes, not by mounting and unmounting.
- The row-actions trigger is visible on row hover, on focus-within and when the row is selected (by opacity, never
display: none, so it stays reachable by keyboard and assistive tech).
D. Page header (PageHeader.tsx): one row at desktop — icon, title, the favorite star, the description inline after a hairline divider (truncated; hidden below the sm breakpoint as the whole header already is), actions on the right; target height about 52 px. The managed-by badge stays where it is. Mobile unchanged.
E. i18n: every new key (list.display, list.layout, list.rowNumbers, list.resetToViewDefault, and whatever else is needed) lands in all ten locale packs; the parity tests decide the roster.
F. Accessibility: the Display trigger aria-expanded; the switches role="switch" with aria-checked; the hover-only controls stay focusable; no role on a div.
Acceptance
ViewTabBartests: with a measured width that fits six tabs and eight views, six render and "2 more" lists two; choosing a folded view swaps it in; withmaxVisibleTabs: 3and the same width, three render. A board, calendar or gallery tab renders its icon; a grid tab renders none; no filter/sort micro-icon renders for a view passed withhasActiveFilters: true.ListViewtests: on desktop the right cluster renders exactly the Filter, Sort, Display and Search buttons (by testid); Hide fields, Group, Color, Edit inline, Refresh and Export are not in the bar; the Display popover contains Group, Hide fields, Density, Row numbers, Edit inline, Reset, Refresh and Export; the Layout row renders only whenviewSwitcherOffered; the count chip is absent by default and reads 1 after density is changed; Reset removes it.data-tabletests: a row renders its number and no visible checkbox by default; after one row is selected, every row renders its checkbox; the header checkbox is always present; the row-actions button is in the DOM for every row.PageHeadertest: the title and the description render in one flex row (oneh1, the description a sibling in the same row, no block paragraph under it).- i18n parity green;
pnpm testfrom the repo root green on the affected files; a changeset (minor; user-visible) for every published package touched. - Browser check on the terminal head: the seat drives the console against a running backend and screenshots the list page; those screenshots are the maintainer's acceptance view. The dev need not run a backend.
Out of scope (named so nobody widens the PR)
- Persisting any Display setting per user: console lists: per-user list state (view tab order, filter state, sort, column widths) moves from localStorage and shared-view writes onto sys_user_preference ui.* keys; a toolbar sort or drag-reorder no longer rewrites the shared view #12418.
- Removing the object breadcrumb from the app bar; moving
list_toolbarmetadata actions into the selection bar. Both are follow-up cards if the maintainer wants them. - Any
packages/specchange; any HotCRM metadata; the mobile layout.
Attribution: written by the domain:ui seat in session session_019YFzJp4o8Ck4NsT1toC4UP (account os-bill) on the maintainer's instruction quoted above.
Activity
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1
Session:
session_019YFzJp4o8Ck4NsT1toC4UPAccount:
os-bill(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-12423-list-page-display-popoverWorktree:
objectui-issue-12423Domain:
domain:uiSeat:
domain:ui#3File surface:
packages/plugin-list/src/ListView.tsx(the right toolbar cluster),packages/plugin-list/src/components/ViewSettingsPopover.tsx,packages/plugin-view/src/ViewTabBar.tsxand its tests,packages/components/src/renderers/complex/data-table.tsx(leading cell and row-actions cell),packages/plugin-grid/src/ObjectGrid.tsx(the row-number default only),packages/app-shell/src/layout/PageHeader.tsx,packages/app-shell/src/views/ObjectView.tsx(the ViewTabBar and PageHeader call sites only),packages/i18n/src/locales/*.ts, one.changeset/12423-*.md(stop on breach; explain in the report)Container & model:
M — design judgment in the layout, not mechanical,mode:subagent,model: TIER_DEFAULT (opus) — read from scripts/pm/dispatch-gates.data.mjs at objectstack 16abea8 (TIER_DEFAULT = opus, TIER_FLOOR = sonnet); the sister-repo --tier verdict with explicit paths refused in this checkout with MODULE_NOT_FOUND (objectstack node_modules absent), so the constants file is the reading; ceiling CONTRACT_REVIEW_TIER not reached: no MANDATORY_TIER_GLOB_ROWS path on this surfaceClause-②: yes
Responsibility:
n/a — not a defect cardThread-read: none
Serial constraints cleared:
domain:uiin-flight claims read 2026-10-15T03:57Z — objectui#12413 (data-objectstack MetadataDraftHeader, seat 2) and objectui#11862 (studio principal names, seat 1): neither touches this file surface. Same-day merges on the surface, all on main already: 4b0a087 (#12414), 2fc31ea (#12408), 70e843f (#12394), 1040a0a (#12377), df05784 (#12373), b64fda3 (#12324). No queued sibling shares a hot file; objectui#12418 (pm:queue, per-user persistence) is the named sibling and this card persists nothing new.Resume-from: none
Maintainer direct-dispatch channel, verbatim authorisation (chat, 2026-10-15): 「按这个方案在 objectui 开一个 issue,使用 dispatch 技能,然后你负责派发开发。开发完了使用右侧的浏览器测试验证并给我看效果。crm 本身的元数据不重要,我关心的还是平台的基础能力。」 Routing (
domain:ui,area:records,priority:p2) was written by this seat under that channel; triage may regrade.Reading time 2026-10-15T04:08Z.
objectstack-fleet commentedon Oct 15, 2026
Scope note 2026-10-15T05:01Z from the filing seat (
session_019YFzJp4o8Ck4NsT1toC4UP, os-bill) after the maintainer reviewed the prototype (chat, 2026-10-15). Three items; the dev reads them together with the card.object-favorite-btn-*pins deliberately; theuseFavoriteswiring inObjectView.tsxstays, since the record page and console shell: the page-header star and the sidebar pin become one "Pin to sidebar" action in the page overflow menu, landing in the Pinned section at the top; the app sidebar's bottom Favorites group goes #12427 still read it.Reading time 2026-10-15T05:01Z.
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 12423,
"status": "done",
"branch": "claude/issue-12423-list-page-display-popover",
"pr": "#12436",
"session": "session_019YFzJp4o8Ck4NsT1toC4UP",
"premise_still_valid": true,
"summary": "Draft PR objectui#12436 (head 4ba299c, 5 commits) implements card items A-F with scope note 6184810876 applied. (A) ViewTabBar derives its visible tab count from the strip width: tabs are measured before paint, re-fitted on resize, and the current tab's menu button counts toward the width. maxVisibleTabs is now an optional cap. Grid/list tabs show no icon, the filter/sort micro-icons and the tab's default star are gone, and the Set-as-default entry carries the state (filled star, check, aria-current). (B) compactToolbar defaults on, decided where ListView derives toolbarFlags (schema.compactToolbar !== false). The bar is Filter, Sort, Display, Search; Display is the extended ViewSettingsPopover with Layout, Group, Color, Hide fields, Density, a session-only Row numbers switch, an Edit inline switch, and a footer with Reset, Refresh and Export. The trigger shows a changed-count chip, and every control persists exactly as before. (C) A selectable data-table draws one leading cell: the number, swapped for the checkbox on hover, focus-within, coarse pointer and any selection. Row-actions triggers in data-table and plugin-grid RowActionMenu are revealed by opacity. ObjectGrid takes a showRowNumbers host prop. (D) PageHeader is one desktop row and the list page draws no favorite star. (E) Four new list.* keys are in all ten packs, and three dead ones are removed. Mechanism readings: compactToolbar is spec-declared (view.zod.ts, optional, no default), so the flip lives in the renderer. ViewSwitcherDropdown works as the Layout row without a fork. The console grid's row menu is plugin-grid RowActionMenu, not data-table's, so that file needed the same classes. Deviations: (1) File surface extended beyond the claim, each edit forced by the change: packages/types docs and the selectionStyle zod refusal message (they said checkboxes are always visible, and the compactToolbar default and ViewTabBarConfig docs had gone stale), plugin-grid RowActionMenu.tsx, the plugin-list README, data-table.mdx, and test files. (2) Scope note item 1 said the useFavorites wiring in ObjectView.tsx stays. Measured: that file's only reader was the removed star button, so the call, its import and the unused appName binding went with it. The hook, provider, record page and sidebar are untouched. (3) Diff is about 2.26k lines (1752+/507-): above the 1.5k estimate, under the 2.5k stop. Most of it is intentional wide-form test pins (WIDE_TOOLBAR in 21 plugin-list test files). (4) C.6 is applied literally to every selectable data-table. On touch the checkbox stays visible. (5) The first commit message's body carries a 'Part of objectui#12423.' line. It is not a trailer, and it was pushed before I noticed it; it was not rewritten.",
"tests": "All at HEAD 4ba299c from the worktree root via os-verify-lock. (A) vitest run packages/plugin-list/: 'Test Files 134 passed (134) / Tests 1434 passed (1434)'. (B) vitest run packages/plugin-view/ packages/i18n/ packages/app-shell/src/layout/: 'Test Files 226 passed (226) / Tests 2508 passed | 13 skipped (2521)'. (C) vitest run packages/components/src/renderers/complex/ packages/plugin-grid/: 'Test Files 240 passed (240) / Tests 2200 passed (2200)'. (D) vitest run packages/types/ plus app-shell views/ObjectView*, InterfaceListPage*, ObjectDataPage*, RecordDetailView*, src/tests/, PageView.refreshInPlace-10519, useNavActionDispatch: 'Test Files 1 failed | 539 passed (540) / Tests 1 failed | 11862 passed'. The one failure is entryRegistersBuiltinDesigners-11939, timing out at 1011ms against its 1000ms vi.waitFor around the entry's dynamic designers import. It also failed in an earlier loaded run, and alone at 4ba299c it reads 'Tests 1 passed (1)'; this diff does not touch that import. NOT MEASURED: whether it also fails at base under the same load. New tests, all green inside the runs above: ViewTabBar.widthFit-12423 (six of eight views with '2 more', swap-in, cap 3, chevron counted, no-layout case, icon rule, no micro-icons, default state in the menu), ListView.displayPopover-12423 (bar is exactly the four test ids, named absences, compactToolbar:false control, Display contents, Layout row only when offered, aria-expanded, count 0 to 1 to 0 via density and Reset, Row numbers reaches the grid), data-table-leadingCell-12423, PageHeader.oneRow-12423. Existing pins updated on purpose, not to make them pass: the 6921 cellClassName population, sticky offsets, row-tint sticky, PageHeader.subtitle, 11733 (the favorite cases became an absence pin), chartRelay-7823, emptyColumnsNoProjection-10694, listUrlState-11860 and PageView.refreshInPlace-10519, which now go through the Display panel. Before the change, the plugin-list plus plugin-view baseline was 208 files and 2125 tests, all green. No ablation was run: the new tests assert behaviour that did not exist at base (base has no Display trigger, no leading-cell slot and no strip testid), so each is red at base by construction. That claim was not separately measured.",
"gates": "Local, at 4ba299c, exit codes captured before any pipe: check-changeset-presence exit 0 ('62 source file(s) of 7 released package(s) changed, and this change declares 1 changeset(s)'); check-changeset-no-major exit 0; check-i18n-call-site-keys exit 0; check-i18n-en-drift exit 0 ('No en value changed in this range'); check-i18n-dead-keys exit 0 (report-only; view.activeFilters and view.activeSort are listed confirmed but were already dead before this branch); check-test-path-roots exit 0; check-control-bytes exit 0; check-new-cross-file-line-citations exit 0 ('0 new citation(s)'). Type-check with pnpm --filter for @object-ui/types, i18n, components, plugin-grid, plugin-list, plugin-view and app-shell: each echoed its type-check script and exited 0. These ran at 56bd0ab; 4ba299c adds only the changeset, the README and the mdx. The dependency closure was built through the lock first, and each built dist was checked for this change's markers. The app-shell type-check ran with a 6144 MB heap as a precaution for the largest program; no out-of-memory was observed and 4096 was not tried. CI on the PR head 4ba299c, read once at report time: 36 check-runs, of which 29 in_progress, 4 success and 3 skipped. Every family is in_progress for PM to collect. pnpm lint, the full pnpm test and Spec Main Shape Gate belong to CI and were not run locally. objectui has no dispatch-gates.mjs, so the local list is the dispatch's hand-derived set; --ran reconciliation does not apply.",
"mcp_calls": "0",
"api_writes": "2 — both through the fleet-write relay as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectui/pulls (draft) plus POST /repos//issues/12436/assignees (os-bill); relay run 38689910535 succeeded and the read-back was byte-identical (11468 bytes). (2) This os-dev-report comment: POST /repos//issues/12423/comments via post-stamped.mjs. Labels: 0. git push is not counted.",
"open_questions": [
{
"question": "The card's 'problem in one screen' counts the view-tab row and the toolbar row as two rows of chrome, and its agreed shape reads 'tabs on the left, four controls on the right'. Items A-F do not ask to merge the two rows, and ListView has no slot for the tabs, so this PR keeps two rows. Should the tabs and the four controls share one row?",
"options": [
"A: keep two rows (this PR). Zero further work. The rows are shorter now, but about 40px of chrome remains.",
"B: follow-up card: ListView takes a leading host slot and the console passes ViewTabBar into it. Medium work (plugin-list slot, app-shell call site, mobile path, tests). Removes one full row."
],
"recommendation": "Decide on the seat's screenshots at 1440px. If the maintainer's prototype shows one row, choose B as its own card. It changes a component contract (a ListView host slot), which this card did not authorize."
}
],
"out_of_scope_findings": [
"carrier: none · noted, not filed — view.activeFilters and view.activeSort were dead i18n keys before this branch (the removed tab tooltip spelled 'Active filters' / 'Active sort' in English and never read them); check-i18n-dead-keys lists both as confirmed. dedupe words: view.activeFilters dead key, view.activeSort, tab indicator tooltip",
"carrier: none · noted, not filed — packages/app-shell/src/tests/entryRegistersBuiltinDesigners-11939.test.tsx times out at 1011ms against its 1000ms vi.waitFor in loaded multi-package runs (two of two) and passes alone. This is the AGENTS.md load-timing class: the window covers the entry's dynamic import of register-builtin-designers. dedupe words: entryRegistersBuiltinDesigners flaky, builtin designers waitFor, 11939 timeout"
]
}
objectstack-fleet commentedon Oct 15, 2026
Seat verdict ACCEPT on PR #12436 (head
4ba299c85679202ee8780969098ba9cdcc6bd1d1), written 2026-10-15T06:41Z: #12436 (comment). Contract review PASS on the same head: comment 6186088489. Governed-merges audit: not governed, 2,259 lines, ordinary queue landing. Follow-up #12440 filed for the one-row tab strip (Blocked-by this card). The seat's screenshots at 1440 / 1024 / 390 px are the maintainer's acceptance view, delivered in chat.Landing NOT done, per landing-operations (a classifier refusal stops the hand): the relay stroke
node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectui --actions-file land-12436.actions.jsoncarryingpr_ready+automerge_enablefor pull 12436 was refused by the seat container's auto-mode classifier with the reason "Self-Approval" (the reviewing session is also the one that dispatched the dev). The PR stays draft with the PASS record and this ACCEPT in place; the maintainer either flips it ready and enables auto-merge, or authorizes the seat to send that stroke. Not re-spelled, not routed around.Thread-read: 6185623889
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landing stroke sent 2026-10-15T07:18Z on the maintainer's authorization in chat (verbatim: 「我授权你 翻 ready + 挂 auto-merge」): relay run https://github.com/objectstack-ai/objectstack/actions/runs/38697728166 (
pr_ready+automerge_enablefor PR #12436) completedsuccess; read back on the PR object:draft: false,auto_mergeenabled (method merge, byobjectstack-fleet[bot]), head unchanged at4ba299c85679202ee8780969098ba9cdcc6bd1d1,mergeable_state: behind(main moved three commits since the base; not dirty, so no merge-back is owed before the queue takes it). The earlier "landing NOT done" line above is superseded by this one. The MERGED confirmation with the two readings follows as the landing comment.Thread-read: 6186156262
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landing 2026-10-15T07:30Z: PR #12436 MERGED. Two readings, both taken after the merge event:
merged: true,merged_at2026-10-15T07:29Z, merged byobjectstack-fleet[bot]out of the merge queue; head4ba299c85679202ee8780969098ba9cdcc6bd1d1; merge commita2225f9131a4d30dacb6cfb9470d312db68c02ddorigin/maina2225f9"feat(list): Filter · Sort · Display · Search toolbar, width-fitted view tabs, one leading row cell, one-row page header (#12436)";ObjectView.tsxon it carries the #12423 marker (1 hit);.changeset/12423-list-page-display.mdpresent, awaiting the release PRStatus change: this card closed by the merge (
Fixes);pm:dispatched→pm:donein the same stroke. Follow-ups: #12440 (one-row tab strip) is serial behind #12427's PR #12443 onObjectView.tsx; parent objectstack-ai/objectstack#25945 ticks this card.Thread-read: 6186725963
Generated by Claude Code
feat(list): Filter · Sort · Display · Search toolbar, width-fitted vi…