Repository navigation
feat(app-shell,plugin-detail,components,i18n): the record page's timeline and related columns are each reader's per-object switches, toggled from the crumb row - #27120
Conversation
…with recordOpenPreference as its alias Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4EjjR2sbXvZiQ3HsgSBhb
…line and related columns are the reader's switches, per object, drawn as panel toggles on the crumb row Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4EjjR2sbXvZiQ3HsgSBhb
…nd tint by aria-pressed; changeset Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4EjjR2sbXvZiQ3HsgSBhb
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4EjjR2sbXvZiQ3HsgSBhb
…rkspace-preferences
… file Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4EjjR2sbXvZiQ3HsgSBhb
… apart from the rail toggle and the Related tab Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4EjjR2sbXvZiQ3HsgSBhb
席位核对 · PR #27120 ← #27052 · epic PM
|
…rkspace-preferences # Conflicts: # ui/packages/app-shell/src/views/RecordDetailView.tsx
…nly what the object declares, and the panel chords move off the browser's ⌘⇧T / ⌘⇧R Two seat rulings on the patch round: - recordTimelineDefaultReason drops its master_detail-parent branch. A parent that merely owns child rows (a price book, a campaign, an event) is a container, not a worked record; the rule is now stage field / option kinds and enable.files only, read off the object alone, so recordPageColumnDefaults no longer takes the app's object list. - The panel chords are ⌘⇧. (timeline) and ⌘⇧, (related), Ctrl+Shift elsewhere, matched by KeyboardEvent.code because Shift changes the character those keys type. Chromium and Firefox keep Ctrl/⌘+Shift+T and Ctrl/⌘+Shift+R for themselves and the page never receives them. Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
…n declaration The project in the tier suite was worked only as the master_detail parent of its tasks, which no longer turns the timeline on. Its status options now declare lifecycle kinds, so the wide tier stays three columns for the reason the derivation actually reads. Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
…ts name An object definition is passed with its name; dropping the property made literal definitions fail the excess-property check. Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
… fixtures pass none The derivation reads only what the object declares of itself (stage field, option kinds, enable.files), so the shape it reads has no name and the switch fixtures stop passing one. Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
席位核对 · PR #27120 ← #27052 · epic PM
|
Fixes #27052
Clause-②: no
Metadata-protocol: none
维护者速读
ui/packages/plugin-detail/src/synth/recordPageColumns.ts(默认值推导与排布规则)、ui/packages/app-shell/src/views/RecordDetailView.tsx(crumb 行与快捷键)、ui/packages/app-shell/src/utils/workspacePreference.ts(偏好存储)。pnpm --dir ui exec vitest run packages/app-shell/src/views/RecordDetailView.panelToggles-27052.test.tsx,预期 6 passed;或看下方证据分支的三张截图。What changes
app-shell/src/utils/workspacePreference.ts). A preference is declared once and read per scope:user, orobject:OBJECT_NAME. Each preference and scope pair has onelocalStoragekey. The value is a small JSON record carryingv, and a record of another version reads as "no choice". Reads are live throughuseSyncExternalStore, other tabs arrive through thestorageevent, and with no storage (SSR, a blocked origin, a refused write) a read falls back to the default while a refused choice is held for the page.recordOpenPreferenceis a thin alias over it. Its keyos:record-open-modeand its baredrawer/splitspelling are unchanged, so a reader's earlier choice reads back and the record-open preference tests pass unchanged.record.columns.timelineandrecord.columns.relatedtakeon/offper object. With no choice,recordPageColumnDefaultsderives the default: the timeline is on for a worked object and off otherwise, and the related column is on.recordTimelineDefaultReasonnames the branch:stageFieldnames a field, a field's options declare a lifecyclekind(unlessstageField: false), orenable.files: true. Being themaster_detailparent of another object is not a signal (seat ruling 1, PR comment 6464903026): a container that owns child rows is not a worked record. The derivation reads only the object itself, sorecordTimelineDefaultReason/recordPageColumnDefaultstake no object list.recordPageArrangementFor(tier, switches)is the rule, andRecordPageColumnstakesswitches. With the timeline off, a wide page draws details | related and the timeline's blocks become an Activity card at the foot of the related column. With related off, the related column stays behind the existing rail toggle. Below the column widths the page is tabbed as before, the switches are kept, and?tab=is untouched. Authoredkind: fullpages andslots.tabsnever reach this code.PanelToggles(@object-ui/components). It is a named group of icon toggles witharia-pressed, a tinted pressed state and a tooltip showing the shortcut's keycaps. The related switch is nameddetail.relatedColumn(「关联列」), apart from the rail toggle and the Related tab, so the row never carries two controls named 「关联」. The record page draws it on its crumb row, right side, only in a column tier. It is not drawn in the drawer or split pane.⌘⇧./Ctrl+Shift+.(timeline) and⌘⇧,/Ctrl+Shift+,(related) flip the switches while the full record page is in a column tier — matched by physical key (KeyboardEvent.codePeriod/Comma), since Shift changes the character those keys type. The card's⌘⇧T/⌘⇧Rare browser-reserved (reopen closed tab, hard reload) and were replaced by seat ruling 2; a⌘⇧T/⌘⇧Rkeydown now leaves the columns and the stored preference untouched. They do nothing while a text field has focus, and they are advertised to the keyboard-shortcuts dialog under the same gate.detail.timeline,detail.timelineFolded,detail.relatedColumn,detail.panelToggles,console.shortcuts.toggleTimelineandconsole.shortcuts.toggleRelatedare added in all ten packs; no pack names a chord.Merge order with the record-page navigation PR
The navigation PR for the record page, #27038, adds ↑ ↓ and a back link to the same crumb row. This PR does not depend on it. Whichever lands second places its controls so the row reads ↑ ↓ · divider · panel toggles. The first to land does not wait for the other.
Tests
workspacePreference.test.tscovers keys per preference and scope, the versioned record, unreadable spellings, no storage and SSR.workspacePreference.hook.test.tsxcovers the live hook: same-scope callers re-render, scopes stay apart, cross-tabstorageevents arrive, and a refusing storage holds the choice for the page.recordPageColumns.switches-27052.test.tshas one row per derivation branch and per arrangement case.RecordPageColumns.switches-27052.test.tsxchecks where each block lands when a column is off.panel-toggles.test.tsxcovers the group name,aria-pressed, the tint class, click behaviour and the tooltip keycaps.RecordDetailView.panelToggles-27052.test.tsxmounts the real view. It covers the card's acceptance 1: timeline off on a worked object draws details | related with the folded card, and on restores it. The choice survives a remount and is per object. An object that is not worked opens with the timeline off.⌘⇧Twith an input focused does nothing. Nothing is drawn in the tabbed tier or when embedded.recordOpenPreference.test.tsand the twoObjectViewrecord-open suites pass unchanged (acceptance 3).Acceptance notes
f660fd8, fresh DB, with this branch's console at 1720 px wide. HotCRM'scrm_leadcarries an authoredkind: 'full'record page, which the card leaves untouched, so the column tiers never draw there. The worked-object shots therefore usecrm_contact, which hasenable.files: trueand no authored page. Evidence branch (never merge):claude/issue-27052-evidence, folderevidence-27052/.1-contact-both-columns.png: three columns and both toggles pressed.1b-contact-toggle-tooltip.png: the tooltip with its keycaps.2-contact-timeline-off.png: details | related.3-product-default.png:crm_productopens as details | related, with the timeline folded at the foot of the related column.Ctrl+Shift+Tsent by Playwright turned the timeline back on (that run predates ruling 2; the chord is nowCtrl+Shift+., pinned inpanelToggles-27052, not re-measured in a real browser: Playwright injects keys below the browser's accelerator layer). The root measured 1464 px.ov:drawer-width:OBJECTkey to go through the store too. That read and write live innavigation-overlay.tsx, which this dispatch fences off because the navigation PR edits it. It is left for after that PR lands.CommandPaletteProvideronly opens and closes it, andCommandPalettehard-codes its groups. So this PR adds the shortcuts and no palette commands.AppHeader's, which are private to that file. Sharing them would edit a file outside this card's surface.v. The record-open preference keeps its bare stored value withoutv, because its tests pin the stored bytes.HotCRM reading of the default derivation
Read with
recordPageColumnDefaultsover the 20 object definitions of HotCRMf660fd8, loaded from source, on headfd0f941f70(after ruling 1):8 ON / 12 OFF; the related column defaults to ON for all 20. This matches the card's expected list exactly. Before ruling 1 the four container objects (campaign, price book, event, knowledge article) were ON through a
master_detail-parent branch that is now gone.Open questions — resolved by the seat (PR comment 6464903026), both landed in patch round 1
master_detailparent opens with the timeline off) and the HotCRM table above.⌘⇧./⌘⇧,(Ctrl+Shift+./Ctrl+Shift+,), matched by physical key; tooltip keycaps,aria-keyshortcutsand the shortcuts dialog read the same constants. Delivery through a real browser's accelerator layer is NOT MEASURED on this host (Playwright and devtools inject below it); the page-side behaviour is pinned.Verification
Final head
fd0f941f70. Cloud run: report comment 6464041380 on the card (34 / 34 derived gates exit 0 onb36136e2, ablation of the typing-target guard). Patch round 1 (local, user-token pushes): report comment 6466430387 (34 / 34 gates ona6d6ac6fb4with the list re-derived identical onfd0f941f70; 634 files / 6348 tests across plugin-detail, i18n, components custom, app-shell utils / chrome / RecordDetailView* / ObjectView*; plugin-detail + app-shell type-check with their test tsconfigs; two reverse verifications with blob hashes and a cleangit diff HEAD). CI onfd0f941f70: Test UI 17 / 17 shards, Type Check UI, Lint UI, Build & E2E UI all success.Generated by Claude Code