Repository navigation
Console lists: the list-view switcher becomes a dropdown on the list title (search, pinned default, recent), the flat view-tab row retires, and a starred view appears in the sidebar's 收藏 group #27594
Copy link
Copy link
Closed
Labels
area:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchdomain:uienhancementNew feature or requestNew feature or requestpriority:p1High: required for production / M2High: required for production / M2
Description
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
Filed from the record-page epic #26676 as a sub-issue (the maintainer, 2026-10-29: 「另立两张 ui 卡:…列表视图选择器改下拉 + 收藏进侧栏」; the reasoning, verbatim: 「我们是元数据开发平台,类似 salesforce,每一种元数据的视图数量都不一定的」 and, on Attio: 「他可以把某个列表视图加到收藏,显示在左侧菜单上」). It is list-page work; it also frees a 44 px row inside #27545's narrowed list.
Source (main 273137d227)
- The object list page draws its views as a flat tab row (e.g. HotCRM leads: 全部线索 · 我的线索 · 🔥 高热度线索 · 高优先级 · 还有 N 个 ▾) under the list header; authored views come from the object's
*.view.ts(views:/navigation.tabs), reader-saved views join the same row. The row overflows at five and is cramped at 464 px (the split's list width). - Apps already pin a view into the sidebar on the AUTHOR side:
crm.app.ts{ type: 'object', objectName: 'crm_lead', viewName: 'my_leads', label: 'My Leads' }(nav_my_tasks, nav_my_deals, nav_my_leads, nav_my_calendar). There is no reader-side equivalent. - Salesforce (list view picker with search and pin), Attio (「All Deals ▾」 + favorites in the sidebar) and Twenty (「All People · 5 ▾」) all switch views from a dropdown on the title.
Target
- The list title is the view switcher: 「 ▾ · 」 at the top-left of the list header (the object name stays in the breadcrumb and the sidebar highlight); the flat view-tab row retires. The filter / sort / display controls keep their own row.
- The dropdown: a search box at the top (always, not only past N views); sections 📌 固定 · 最近 · 全部; in 全部 the authored views first in their declared order, then the reader's saved views; each row with its view-type icon (grid / kanban / calendar / gantt / gallery / map) and, on hover, ☆ and 📌; 「+ 新建视图」 at the bottom (the existing save-view flow).
- Pinned = default: one pinned view per object per reader opens by default; stored in console: one workspace-preference module (record open mode, drawer width, timeline / related column toggles) read per user × object, with a shared panel-toggle toolbar on the record page — defaults derived from the object, never metadata #27052's
workspacePreferencemodule, scopeobject:NAME; with none pinned the authored default (isDefault, else the first) opens. - ☆ Favorite → sidebar 收藏 group: starring a view adds 「 」 to a 收藏 group at the top of the app's sidebar (reader preference, same module, scope
app:NAME); unstarring removes it; drag to reorder inside the group; authoredviewNamenavigation items are untouched and not duplicated (a view that is already an authored item shows ☆ disabled with the tooltip 「已在菜单中」). - The split pane's list (Record drawer v4: a bounded peek over the list — md cap instead of the field-count bucket, no dim overlay, the list stays readable and clickable, ⤢ maximizes in place at the same URL; the full page is reached only from the row ↗ and the header ⋯ #27545) uses the same title switcher; nothing else is special-cased. No metadata key; ⛔
packages/spec/**. - Tests: the title opens the dropdown with search, sections and the authored-then-reader order; pin stores and restores the default per object; star adds / removes the sidebar entry and reorders; an authored
viewNameitem is not duplicated; the tab row no longer renders; the split pane's list shows the same switcher at 464 px. - Screenshots on HotCRM leads (dropdown open at 1440; the sidebar with a 收藏 entry) and in the split pane, on a never-merged evidence branch.
- The epic seat verifies in the console preview before ACCEPT (maintainer: 「车队开发完后你审核效果」).
File surface
ui/packages/plugin-list/src/** (the view tab row → the title switcher), ui/packages/app-shell/src/views/ObjectView.tsx + objectViewListRenderer.tsx (header), ui/packages/app-shell/src/components/UnifiedSidebar.tsx (收藏 group), ui/packages/app-shell/src/workspacePreference* (pinned / favorites), i18n in all ten packs, tests beside, changeset; ⛔ packages/spec/**.
Acceptance
Metadata
Metadata
Assignees
Labels
area:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchdomain:uienhancementNew feature or requestNew feature or requestpriority:p1High: required for production / M2High: required for production / M2
Activity
objectstack-fleet commentedon Oct 30, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Claim: PM loop round 4 · 2026-10-30T01:55Z
Session:
session_01Utp6CY2n1JmZZKrHgtp9jC(seat; implementation by a card thread,mode:thread)Account:
os-billBranch:
claude/project-thread-vbf1ad(the card thread's harness push branch, self-reported — the measured platform exception toclaude/issue-N-slug)Worktree:
objectstack-issue-27594Domain:
domain:uiSeat:
domain:ui#2File surface: the card body's File surface:
ui/packages/plugin-list/src/**(the view tab row → the title switcher),ObjectView.tsx+objectViewListRenderer.tsx(header),ui/packages/app-shell/src/layout/UnifiedSidebar.tsx(收藏 group),ui/packages/app-shell/src/utils/workspacePreference*, i18n in the ten packs, tests beside, changeset; ⛔packages/spec/**Container & model:
L,mode:thread,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate),effort: xhigh(p1)Clause-②: no (expected; the dev measures from the diff)
Metadata-protocol: none (the card adds no metadata key; the dev measures from the diff)
Responsibility: the flat view-tab row that overflows at five and has no reader-side favorite | authored
viewNamenav items and the save-view flow are fixed inputs | every console reader of an object list with more than a few viewsThread-read: none
Premise: the Target as rewritten by the epic PM against canvas 60 (back-to-queue comment, 2026-10-29T14:19Z); the dev re-measures the Source section on current
mainbefore the first editSerial constraints cleared: none on the card; overlaps named in the order: #27545 (seat 2,
ObjectView.tsx, the narrowed list) and #27593 (seat 4,UnifiedSidebar.tsx). Whoever lands second mergesmainAcceptance gate: the epic seat verifies the PR in the console preview against the boards before the seat's ACCEPT (the maintainer: 「车队开发完后你审核效果」)
Dispatched on the maintainer's word of 2026-10-30T01:49Z (「现在就派,临时加并发」): the ui seats take the six p1 #26676 children now, with concurrency temporarily raised.
Dispatch order:
/mnt/project-files/ui-seat2/dispatch-27594.mdin the Claude Code project (not on GitHub); the card body is the ruling text.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
For the epic seat (
epic:#26676#1): Source drift and one storage call · 2026-10-30T01:58ZThe card thread re-measured Source on
mainddab8b99:ViewTabBar. It is mounted by app-shellviews/objectViewTabBar.tsx(from theobjectViewPageHeader/objectViewViewCrudsplit out ofObjectView.tsx).plugin-list/srcholds only the grid/kanban visual switch. So the work lands in app-shell plus a new title-switcher file, andplugin-listis untouched.console.nav.favorites, objectui#12427).FavoritesProviderfeeds it from theui.favoritespreference, which syncs across devices. Pinned nav items already reorder by drag (platform-objects' keyed-text-bounds pin cannot reach plugin/service packages — four keyed unbounded text columns in plugin-audit and plugin-security still break MySQL schema-sync #12059).view, filtered to the app by href), not in a secondworkspacePreferenceapp:NAMEstore as Target 4's "same module, scope app:NAME" reads.workspacePreferenceobject:NAME.RecentViewItem, Platform reading: the session proxy serves repo-scoped paths only —GET /search/*returns 403, and its body parses as a clean zero #12604). The dropdown reads it.ManageViewsDialog), so retiring the tab row drops no capability.One PR is expected (under the size threshold). It lands through Acceptance 3's preview verification.
Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
os-dev-report
Report of 2026-10-30T04:46Z.
{ "issue": 27594, "status": "done", "branch": "claude/project-thread-vbf1ad", "pr": "https://github.com/objectstack-ai/objectstack/pull/27943", "head": "16c257005a5b189a942a501486b82023579cba7a", "session": "session_01Utp6CanDVECEsojSwtvSs7 (card thread; seat session_01Utp6CY2n1JmZZKrHgtp9jC)", "premise_still_valid": true, "summary": "The object list title is the view switcher (ObjectViewTitleSwitcher, app-shell): label, chevron and the list's reported count; a popover with a search box, Pinned, Recent (<=3) and All (authored first, then saved), rows with type icons and hover star/pin, New view and (admin) Manage views at the foot. The flat tab row retires (objectViewTabBar.tsx deleted; ListView gets no toolbarLeading). Pin = the reader's default per object in workspacePreference list.view.pinned@object:NAME (address > pin > isDefault > first). Star = a FavoritesProvider favorite of type view (seat ruling B), drawn in the sidebar's Favorites only in its own app, beside the object icon; the section's own rows are now sortable (drag or Space+arrows, reorderFavorites); a view an app nav entry already opens shows its star disabled ('Already in the menu'). The split pane is the same page and keeps the switcher. i18n view.switcher.* in ten packs; docs ui/views 'Switching views'; changeset patch app-shell + i18n.", "tests": "head 16c257005a, all under os-verify-lock. vitest over 238 named files (ObjectView, retired tab bar/slot, favorites, workspacePreference, list renderer/header, StarredApps, list-view-switcher readers) + packages/i18n/: Test Files 330 passed (330), Tests 3335 passed, 13 skipped, VITEST_EXIT=0. app-shell type-check TYPECHECK_EXIT=0. New: objectViewTitleSwitcher.test (13), ObjectView.titleViewSwitcher-27594 (7; pin stored at os:pref:list.view.pinned@object:crm_lead and restored on remount, URL beats pin, split pane switcher), ObjectView.starredView-27594 (2), UnifiedSidebar.starredViews-27594 (2). 20 existing tests updated (tab row/tab text/plural h1 readers; ObjectView.viewTabsInToolbar-12440 replaced). Ablations via scripts/ablation-replace.mjs, each restored (blob == HEAD, git diff HEAD empty): pin dropped from open-view resolution -> 1/7 red; authored-first dropped -> 1/13 red; app filter on view favorites dropped -> 2/2 red; inMenu forced false -> 1/2 red; sidebar onReorder no-op -> 1/2 red. The machineLocaleCensus-9909 pin caught count.toLocaleString() with no locale on the first post-merge run; fixed with useDisplayLocale.", "gates": "dispatch-gates --commands at 16c257005a: 66 derived (same list as at 8aec374c05); each run, exit captured before any pipe; all 66 exit 0 incl. node scripts/check-sdui-manifest.mjs and pnpm --dir ui exec vitest run scripts/ (run with nothing else running). check:skill-examples was exit 3 NOT MEASURED on the first pass (stale client-react d.ts), 0 after pnpm --filter @objectstack/client-react build, 0 again at head. --ran: 66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN. Outside derivation: check:i18n-drift / check:i18n-keys / check:i18n-dead-keys / check:new-line-citations (ui) exit 0; eslint on changed app-shell files 0 errors. CI families: PR #27943 check-runs, not concluded at report time.", "line_budget": "51 files, +2206 / -560 = 2766 changed lines (< 3000, no split)", "files_changed": [ ".changeset/list-view-title-switcher.md", "content/docs/ui/views.mdx", "ui/apps/console/src/__tests__/recordOpenFromEveryView-27057.test.tsx", "ui/packages/app-shell/src/console/home/StarredApps.tsx", "ui/packages/app-shell/src/context/FavoritesProvider.tsx", "ui/packages/app-shell/src/layout/MobileViewSwitcherContext.tsx", "ui/packages/app-shell/src/layout/SortableFavoriteRows.tsx", "ui/packages/app-shell/src/layout/UnifiedSidebar.tsx", "ui/packages/app-shell/src/layout/__tests__/UnifiedSidebar.starredViews-27594.test.tsx", "ui/packages/app-shell/src/layout/listViewFavorites.ts", "ui/packages/app-shell/src/utils/warnSuppressedListNav.ts", "ui/packages/app-shell/src/utils/workspacePreference.ts", "ui/packages/app-shell/src/views/ObjectView.chartViewTabs-13114.test.tsx", "ui/packages/app-shell/src/views/ObjectView.createChartView-11576.test.tsx", "ui/packages/app-shell/src/views/ObjectView.createKanbanView-11581.test.tsx", "ui/packages/app-shell/src/views/ObjectView.createViewRefusal-11578.test.tsx", "ui/packages/app-shell/src/views/ObjectView.externalRefreshSum-10572.test.tsx", "ui/packages/app-shell/src/views/ObjectView.labelLessViewTab-12774.test.tsx", "ui/packages/app-shell/src/views/ObjectView.listActionsReadGate-12109.test.tsx", "ui/packages/app-shell/src/views/ObjectView.listTitlePlural-11696.test.tsx", "ui/packages/app-shell/src/views/ObjectView.managedByBadgeAuthoring-12194.test.tsx", "ui/packages/app-shell/src/views/ObjectView.objectBoundActions-7234.test.tsx", "ui/packages/app-shell/src/views/ObjectView.recordOpenPreference-26677.test.tsx", "ui/packages/app-shell/src/views/ObjectView.refreshInPlace-10035.test.tsx", "ui/packages/app-shell/src/views/ObjectView.relayRungCensus-7559.test.ts", "ui/packages/app-shell/src/views/ObjectView.servedViewLabel-11295.test.tsx", "ui/packages/app-shell/src/views/ObjectView.setupListBadge-13385.test.tsx", "ui/packages/app-shell/src/views/ObjectView.starredView-27594.test.tsx", "ui/packages/app-shell/src/views/ObjectView.titleViewSwitcher-27594.test.tsx", "ui/packages/app-shell/src/views/ObjectView.tsx", "ui/packages/app-shell/src/views/ObjectView.ufViewSwitch-11992.test.tsx", "ui/packages/app-shell/src/views/ObjectView.userListState-12418.test.tsx", "ui/packages/app-shell/src/views/ObjectView.viewRefusal-12112.test.tsx", "ui/packages/app-shell/src/views/ObjectView.viewTabsInToolbar-12440.test.tsx", "ui/packages/app-shell/src/views/ObjectView.viewWriteRefusal-11583.test.tsx", "ui/packages/app-shell/src/views/objectViewListRenderer.tsx", "ui/packages/app-shell/src/views/objectViewPageHeader.tsx", "ui/packages/app-shell/src/views/objectViewTabBar.tsx", "ui/packages/app-shell/src/views/objectViewTitleSwitcher.test.tsx", "ui/packages/app-shell/src/views/objectViewTitleSwitcher.tsx", "ui/packages/app-shell/src/views/objectViewViewCrud.tsx", "ui/packages/i18n/src/locales/ar.ts", "ui/packages/i18n/src/locales/de.ts", "ui/packages/i18n/src/locales/en.ts", "ui/packages/i18n/src/locales/es.ts", "ui/packages/i18n/src/locales/fr.ts", "ui/packages/i18n/src/locales/ja.ts", "ui/packages/i18n/src/locales/ko.ts", "ui/packages/i18n/src/locales/pt.ts", "ui/packages/i18n/src/locales/ru.ts", "ui/packages/i18n/src/locales/zh.ts" ], "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "9 — all reads: mcp__github__issue_read x5 (card #27594, seat-3 report shape on #27799), mcp__github__pull_request_read x2 (PR #27826 body shape, #27943 body read-back), mcp__github__search_pull_requests x1, mcp__github__list_pull_requests x1 (#27943 read-back after the relay's unverified read)", "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write pr_create #27943, draft, no labels, no assignees; run 41004585264 success; its own read-back was HTTP 0, re-read by MCP: stored, single footer); this report comment via post-stamped. git push is not a REST write.", "deviations": [ "Seat-agreed before code: switcher in app-shell (objectViewTitleSwitcher.tsx), plugin-list untouched; stars in FavoritesProvider (ruling B) instead of 'same module, scope app:NAME'; Recent from RecentViewItem; Manage views at the dropdown foot as an addition.", "Screenshots on examples/app-crm (HotCRM not in this workspace), zh-CN 1440x900, saved to /mnt/project-files/ui-seat2/card-27594/shots/ (8 PNGs), not on an evidence branch.", "View description: shown once. A list face draws ListView's own info control (no toolbarLeading any more); a chart face carries it as the switcher's tooltip.", "Commit trailer: the model-free pair per AGENTS.md." ], "open_questions": [ { "question": "The card says the object name stays in the breadcrumb, but object routes draw no crumb (objectui#12461). With the title naming the view, the object is named only by the sidebar's active entry and the header icon. Restore an object crumb on list routes?", "options": [ "A keep as shipped: sidebar highlight + header icon name the object", "B restore the object crumb on object list routes (AppHeader), a small follow-up" ], "recommendation": "B, because a collapsed sidebar or a deep link leaves the page with no object name on screen; it is a one-line follow-up the seat can file against this PR or the epic." } ], "out_of_scope_findings": [ "class: c · reach: public door — a drawer-mode list in the split arm (record open in split): ListView's record-count-bar and ObjectView's record-count-footer both draw, so the count shows twice (seen on app-crm leads, 06-split-pane.png) · dedupe words: split pane record count twice, record-count-footer record-count-bar duplicate", "class: c · reach: named producer — packages/spec view.list.tabs removal description still says 'the tab strip above an object's records is the saved-view switcher (ViewTabBar)'; prose only, spec lane · dedupe words: view.list.tabs description ViewTabBar tab strip", "carrier: PR #27943 · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Status: claimed and in review, not unclaimed · 2026-10-30T10:44Z
Fixes)16c25700Clause-②: no)The epic-seat preview gate is now ruled B (6520722066). The maintainer's words were 「你可以自主验收合并,有问题会开新卡」 at 2026-10-30T10:39Z. The card thread now runs the proxy preview review on the PR head. On PASS the seat ACCEPTs, marks it ready and turns on auto-merge. The epic seat re-checks the end state on
mainand files new cards for any deviation from the boards. The seat's earlier requests to the epic seat stay in place and are answered by that ruling.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Landed · 2026-10-30T11:26Z
847e4248e9b7059e5b069056bf636c75c1f53b14completedby the PR'sFixeslineState:
pm:dispatchedremoved; the assignee stays as the record of who carried it.Generated by Claude Code