Skip to content

Commit 1b6709b

Browse files
fix(console): derive the sidebar fold width from the record drawer over the list (#29352)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} --> _Requested by **Steedos** · [project thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbHKR9NdXoRePJwYZ8bT6wA4)_ Fixes #29109 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:控制台侧栏自动折叠阈值仍是 1416 px,只把它的推导理由从已退役的分屏改为「记录抽屉(720)+其旁最窄可用列表(440)+展开侧栏(256)」;记录页列宽档位的注释同步改正。 - **为什么改**:分屏已于 #28915 退役,1416 的原推导理由不复存在;按裁决 6576273751(字母 D,维护者「同意」)保留数值、重写理由。 - **风险与回滚**:零行为变化,任何窗口宽度下表现与 main 相同;常量不从包入口导出。回滚即还原本 PR。 - **先看**:`ui/packages/app-shell/src/layout/consoleSidebarRail.ts`(常量与 docblock)、`consoleSidebarRail.width-27593.test.tsx`(推导钉)。 - **两分钟验证**:`cd ui && pnpm exec vitest run packages/app-shell/src/layout/__tests__/consoleSidebarRail.width-27593.test.tsx`,预期 5 条全绿。 - **需要你做的**:无,席位落地。 ## What changes - `consoleSidebarRail.ts`: `RECORD_SPLIT_MIN_CONTENT_WIDTH` (the retired split pane) becomes `RECORD_DRAWER_MIN_CONTENT_WIDTH = LIST_MIN_COLUMNS_WIDTH (440, the #27545 measurement) + RECORD_DRAWER_WIDTH (720)`. `CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH` is that plus the expanded sidebar (256), still 1416. The docblocks name the non-modal drawer over the full-width list as the reason and no longer mention the split pane. None of these constants is exported from the `@object-ui/app-shell` entry. - `consoleSidebarRail.width-27593.test.tsx`: holds `RECORD_DRAWER_WIDTH` equal to plugin-view's `overlayWidthFor('md', …)` (`min(92vw, 720px)`), pins the sum and the 1416 value, and checks that 1280 px and 1366 px windows fold (the rail leaves 512 / 598 px of list beside the drawer, where the expanded sidebar would leave 304 / 390) and that a 1440 px window does not. - `plugin-detail/src/synth/recordPageColumns.ts`: comment only. The window arithmetic now says the console folds to the 48 px rail below 1416 px (1232 px of page root at 1280), with the expanded-sidebar figures kept for a reader who keeps the sidebar open. - Changeset `29109-console-sidebar-fold-width-drawer.md`, `@object-ui/app-shell` patch. ## Tests On head e8cb255, and the 13-file set again on the merged head b64f11a (main merged at a19759a..origin/main; the merge touched none of these files): - Every ui test file naming `consoleSidebarRail`, `recordPageColumns` or `useResponsiveSidebar` (13 files): Test Files 13 passed (13), Tests 109 passed (109), lock VERDICT command-exit 0, on both heads. - Ablation via `scripts/ablation-replace.mjs`: `RECORD_DRAWER_WIDTH = 720` to `640` (anchor 1 to 0) made the width test go red, 3 failed and 2 passed (5). Restored: blob equals HEAD and `git diff HEAD` is empty. - `turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/plugin-detail`: 66 tasks successful, VERDICT command-exit 0. - `eslint` on the three touched files: 0 errors (11 existing `no-explicit-any` warnings in `recordPageColumns.ts`). ## Acceptance notes - The maintainer's ruling 6576273751 picked letter D: the threshold stays 1416 px and the user-visible behaviour is unchanged. The brief's ablation ("restore 1416") does not apply because 1416 is kept. The ablation narrows the drawer width instead, which proves the pin binds the drawer. - The 440 px list floor has no other constant in code since the split pane retired. It is restated here as a named constant that cites #27545. - The two pending changesets that state 1416 (`27593-console-sidebar-narrow-rail.md`, `28077-console-sidebar-rail-rework.md`) are left as they are, as the ruling says. No `content/docs/**` line states 1416. - No browser run was done, as the ruling allows: the sum is unchanged. ## Verification <details><summary>Derived gates on e8cb255: 35 derived, 35 run, all exit 0</summary> `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 35 families. Each was run and recorded with its exit code, and `--ran` reconciled them: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. That set includes `check-sdui-manifest.mjs` and its `--self-test`, the changeset and closing-keyword body gates (run against this body), `check:cross-package-test-inputs`, `check:test-source-alias`, `check:published-files` and `check:type-check-coverage` / `check:type-check-debt`. `pnpm --dir ui exec vitest run scripts/`: Test Files 169 passed, 2 skipped (171); Tests 4903 passed, 2 skipped (4905); lock VERDICT command-exit 0. </details> On the merged head b64f11a the ratchet and package-surface gates were run again, all exit 0: `check:type-check-coverage`, `check:type-check-debt`, `check:cross-package-test-inputs`, `check:test-source-alias`, `check:published-files` and `check-sdui-manifest.mjs`. Re-deriving the gate list on that head is NOT MEASURED: the `--commands` call did not finish within 600 s and was stopped. The merge brought in no change to the four files in this diff. Repo-wide scans (`pnpm lint`, Test UI and the rest) are CI's: see this PR's check-runs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01QsR2jQy62PTjRj5La7392w --- _Generated by [Claude Code](https://claude.ai/code/session_01QsR2jQy62PTjRj5La7392w)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent c92ae76 commit 1b6709b

4 files changed

Lines changed: 109 additions & 34 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
fix(console): the sidebar's fold width is derived from the record drawer over the list, now that the split pane is retired
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The console sidebar still folds to its icon rail below 1416 px, and nothing changes at any width: a 1280 px or 1366 px window keeps the rail, a click still expands it over the content, and "Keep sidebar open" still keeps it expanded. What changes is the reason the code gives for 1416. It was the split pane beside the list, which is retired. It is now the record drawer a list record opens in (720 px, non-modal, so the list beside it stays readable and clickable), the narrowest list that still draws its columns beside it (440 px), and the expanded sidebar (256 px). The module-internal `RECORD_SPLIT_MIN_CONTENT_WIDTH` is now `RECORD_DRAWER_MIN_CONTENT_WIDTH`, built from `RECORD_DRAWER_WIDTH` and `LIST_MIN_COLUMNS_WIDTH`; none of them is exported from the package entry.
Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22

33
/**
4-
* consoleSidebarRail — the fold width is derived from the sidebar it folds
5-
* (objectstack#27593). `CONSOLE_EXPANDED_SIDEBAR_WIDTH` restates the sidebar
6-
* primitive's `--sidebar-width`, so this reads the primitive's value off a
7-
* mounted provider and holds the two equal: a wider or narrower sidebar moves
8-
* the fold width with it, or this goes red.
4+
* consoleSidebarRail — the fold width is derived from what covers the content
5+
* and from the sidebar it folds (objectstack#27593, re-derived on
6+
* objectstack#29109 once the split pane retired). `CONSOLE_EXPANDED_SIDEBAR_WIDTH`
7+
* restates the sidebar primitive's `--sidebar-width` and `RECORD_DRAWER_WIDTH`
8+
* restates plugin-view's default drawer width, so this reads both off their
9+
* owners and holds them equal: a wider or narrower sidebar or drawer moves the
10+
* fold width with it, or this goes red.
911
*/
1012

1113
import '@testing-library/jest-dom/vitest';
1214
import { render } from '@testing-library/react';
1315
import { SidebarProvider } from '@object-ui/components';
16+
import { overlayWidthFor } from '@object-ui/plugin-view';
1417
import { describe, expect, it } from 'vitest';
1518
import {
1619
CONSOLE_EXPANDED_SIDEBAR_WIDTH,
1720
CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH,
18-
RECORD_SPLIT_MIN_CONTENT_WIDTH,
21+
LIST_MIN_COLUMNS_WIDTH,
22+
RECORD_DRAWER_MIN_CONTENT_WIDTH,
23+
RECORD_DRAWER_WIDTH,
1924
} from '../consoleSidebarRail';
2025

2126
/** A CSS length in px: `rem` against the 16 px root the console renders at. */
@@ -26,24 +31,59 @@ function toPx(length: string): number {
2631
return match![2] === 'rem' ? value * 16 : value;
2732
}
2833

29-
describe('the console sidebar fold width (objectstack#27593)', () => {
34+
/** The sidebar primitive's expanded and rail widths, read off a mounted provider. */
35+
function primitiveSidebarWidths(): { expanded: number; rail: number } {
36+
const { container } = render(
37+
<SidebarProvider>
38+
<div />
39+
</SidebarProvider>,
40+
);
41+
const wrapper = container.firstElementChild as HTMLElement;
42+
return {
43+
expanded: toPx(wrapper.style.getPropertyValue('--sidebar-width')),
44+
rail: toPx(wrapper.style.getPropertyValue('--sidebar-width-icon')),
45+
};
46+
}
47+
48+
/** The list left beside the open drawer in a window `width` px wide, the sidebar `sidebar` px wide. */
49+
function listBesideDrawer(width: number, sidebar: number): number {
50+
return width - sidebar - RECORD_DRAWER_WIDTH;
51+
}
52+
53+
describe('the console sidebar fold width (objectstack#27593, #29109)', () => {
3054
it('restates the primitive’s expanded sidebar width', () => {
31-
const { container } = render(
32-
<SidebarProvider>
33-
<div />
34-
</SidebarProvider>,
55+
expect(primitiveSidebarWidths().expanded).toBe(CONSOLE_EXPANDED_SIDEBAR_WIDTH);
56+
});
57+
58+
it('restates the drawer a list record opens in (plugin-view’s md bucket)', () => {
59+
// `min(92vw, 720px)`: the px term is the drawer's width on any window the fold applies to.
60+
const match = /^min\(92vw, (\d+)px\)$/.exec(overlayWidthFor('md', {}));
61+
expect(match, 'overlayWidthFor answers min(92vw, Npx)').not.toBeNull();
62+
expect(Number(match![1])).toBe(RECORD_DRAWER_WIDTH);
63+
});
64+
65+
it('is the drawer beside the narrowest list that draws its columns, plus the expanded sidebar', () => {
66+
expect(RECORD_DRAWER_MIN_CONTENT_WIDTH).toBe(LIST_MIN_COLUMNS_WIDTH + RECORD_DRAWER_WIDTH);
67+
expect(CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH).toBe(RECORD_DRAWER_MIN_CONTENT_WIDTH + CONSOLE_EXPANDED_SIDEBAR_WIDTH);
68+
// The value the maintainer kept when the split pane retired: 440 + 720 + 256.
69+
expect(CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH).toBe(1416);
70+
// At the threshold the expanded sidebar leaves exactly the list's floor beside the drawer.
71+
expect(listBesideDrawer(CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH, CONSOLE_EXPANDED_SIDEBAR_WIDTH)).toBe(
72+
LIST_MIN_COLUMNS_WIDTH,
3573
);
36-
const wrapper = container.firstElementChild as HTMLElement;
37-
const primitiveWidth = wrapper.style.getPropertyValue('--sidebar-width');
38-
expect(toPx(primitiveWidth)).toBe(CONSOLE_EXPANDED_SIDEBAR_WIDTH);
3974
});
4075

41-
it('is the split’s content beside the expanded sidebar', () => {
42-
expect(CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH).toBe(RECORD_SPLIT_MIN_CONTENT_WIDTH + CONSOLE_EXPANDED_SIDEBAR_WIDTH);
76+
it('folds a 1280 px and a 1366 px window, whose rail leaves the list its columns beside the drawer', () => {
77+
const { expanded, rail } = primitiveSidebarWidths();
78+
for (const width of [1280, 1366]) {
79+
expect(width).toBeLessThan(CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH);
80+
expect(listBesideDrawer(width, expanded)).toBeLessThan(LIST_MIN_COLUMNS_WIDTH);
81+
expect(listBesideDrawer(width, rail)).toBeGreaterThanOrEqual(LIST_MIN_COLUMNS_WIDTH);
82+
}
4383
});
4484

45-
it('folds a 1280 px window and leaves a 1440 px one alone, as the card accepts', () => {
46-
expect(1280).toBeLessThan(CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH);
85+
it('leaves a 1440 px window’s sidebar expanded', () => {
4786
expect(1440).toBeGreaterThanOrEqual(CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH);
87+
expect(listBesideDrawer(1440, CONSOLE_EXPANDED_SIDEBAR_WIDTH)).toBeGreaterThanOrEqual(LIST_MIN_COLUMNS_WIDTH);
4888
});
4989
});

‎ui/packages/app-shell/src/layout/consoleSidebarRail.ts‎

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
* consoleSidebarRail — when the console sidebar folds to its icon rail on its
55
* own, and the reader's override (objectstack#27593).
66
*
7-
* On a window too narrow for the record split beside an expanded sidebar the
8-
* sidebar mounts as the icon rail, and a click on the rail (or the app bar's
9-
* trigger, or ⌘B) expands it OVER the content instead of beside it. A reader
7+
* On a window too narrow for the record drawer and the list beside it next to
8+
* an expanded sidebar, the sidebar mounts as the icon rail, and a click on the
9+
* rail (or the app bar's trigger, or ⌘B) expands it OVER the content instead
10+
* of beside it. A reader
1011
* who keeps it open ({@link CONSOLE_SIDEBAR_KEEP_OPEN_PREFERENCE}) gets the
1112
* wide-window behaviour at every width above the phone breakpoint, where the
1213
* primitive's drawer takes over.
@@ -36,23 +37,42 @@ import {
3637
export const CONSOLE_EXPANDED_SIDEBAR_WIDTH = 256;
3738

3839
/**
39-
* The content width the record split needs to draw its list's columns: the
40-
* narrowest list that still draws them (440) beside the record pane at its
41-
* default width (720), objectstack#27545.
40+
* The record drawer's width in CSS pixels: a list record opens in the drawer
41+
* at plugin-view's `md` overlay bucket, `overlayWidthFor('md', …)` =
42+
* `min(92vw, 720px)` (objectstack#27545). `consoleSidebarRail.width-27593.test.tsx`
43+
* reads that value and holds this equal to it, so the derived threshold below
44+
* follows the drawer.
4245
*/
43-
export const RECORD_SPLIT_MIN_CONTENT_WIDTH = 440 + 720;
46+
export const RECORD_DRAWER_WIDTH = 720;
47+
48+
/**
49+
* The narrowest list that still draws its columns, in CSS pixels: the 440 px
50+
* measured on objectstack#27545. The drawer is non-modal over a list and draws
51+
* no dimming there, so the list beside it stays readable and a click on
52+
* another row swaps the record (`NavigationOverlay` in `@object-ui/components`).
53+
*/
54+
export const LIST_MIN_COLUMNS_WIDTH = 440;
55+
56+
/**
57+
* The content width the record drawer needs over the full-width list: the
58+
* drawer (720) and, beside it, the narrowest list that still draws its
59+
* columns (440).
60+
*/
61+
export const RECORD_DRAWER_MIN_CONTENT_WIDTH = LIST_MIN_COLUMNS_WIDTH + RECORD_DRAWER_WIDTH;
4462

4563
/**
4664
* Below this window width the sidebar folds to the icon rail on its own.
4765
*
48-
* Derived, not picked: a window narrower than the split's content plus the
49-
* expanded sidebar cannot fit the split beside the expanded sidebar, so the
50-
* sidebar gives the content its width. 1160 + 256 = 1416: a 1280 px window
51-
* folds (1280 − the 48 px rail leaves 1232 for the split), a 1440 px window
52-
* keeps the sidebar expanded (1184 left). The record page's own column tiers
53-
* are measured on the container it gets, so they follow either way.
66+
* Derived, not picked: a window narrower than the drawer's content plus the
67+
* expanded sidebar cannot keep the list's columns beside the open drawer with
68+
* the sidebar expanded, so the sidebar gives the content its width.
69+
* 1160 + 256 = 1416: a 1280 px window folds (1280 − the 48 px rail − the
70+
* drawer leaves 512 px of list beside it, where the expanded sidebar would
71+
* leave 304), a 1440 px window keeps the sidebar expanded (464 left). The
72+
* record page's own column tiers are measured on the container it gets, so
73+
* they follow either way.
5474
*/
55-
export const CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH = RECORD_SPLIT_MIN_CONTENT_WIDTH + CONSOLE_EXPANDED_SIDEBAR_WIDTH;
75+
export const CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH = RECORD_DRAWER_MIN_CONTENT_WIDTH + CONSOLE_EXPANDED_SIDEBAR_WIDTH;
5676

5777
/**
5878
* The reader's "keep the sidebar open": `on` turns the fold off, so a narrow

‎ui/packages/plugin-detail/src/synth/recordPageColumns.ts‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,13 @@
5252
* The console sidebar's width when it is expanded: `SIDEBAR_WIDTH` (16rem) of
5353
* the sidebar primitive in `@object-ui/components`. Measured on the console
5454
* (HotCRM contact record page, sidebar expanded, objectstack#27241): the
55-
* record page root is the window minus this — 1184 px in a 1440 px window,
56-
* 1024 px in 1280, 768 px in 1024.
55+
* record page root is the window minus this — 1184 px in a 1440 px window.
56+
* Below 1416 px the console folds the sidebar to its 48 px icon rail
57+
* (`CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH` in `@object-ui/app-shell`,
58+
* objectstack#27593), so a 1280 px window gives the root 1232 px and a 1024 px
59+
* one 976 px; only a reader who keeps the sidebar open gets 1024 px in 1280
60+
* and 768 px in 1024. The floors below are derived for the expanded sidebar,
61+
* so the rail only ever gives the page more room than they assume.
5762
*/
5863
const EXPANDED_SIDEBAR_WIDTH = 256;
5964

0 commit comments

Comments
 (0)