Repository navigation
Commit b7f2b0c
fix(app-shell): a framed console (Live Canvas preview) no longer opens a second assistant dock (#26504)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi94WEaVGC3gCAgvKR4LWw1qE)_
Fixes #26470
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么:** 控制台被嵌在 iframe
里(实时画布的草稿预览)时,助手面板不再读写宿主页的「面板展开」状态:框内面板默认收起,开关只在框内生效。
- **为什么改:** 让助手改应用时自动打开的预览框共享了标签页的 sessionStorage,框里又开了一个助手面板;1024 px
下两个 420 px 面板把列表挤到约 200 px。
- **风险与回滚:** 低。只改 `chatDockState.ts` 的读写两个函数;顶层页面行为不变(对照测试钉住)。回滚即 revert
本 PR。
- **先看:** `ui/packages/app-shell/src/layout/chatDockState.ts` 的
`isFramedConsole` 与读写函数开头的守卫;新测试文件。
- **两分钟验证:** `pnpm --dir ui exec vitest run
packages/app-shell/src/layout/__tests__/ConsoleLayout.previewFrameDock-26470.test.tsx`,预期
4 个测试全绿。
- **需要你做的:** 无,席位落地。
## What changes
`ui/packages/app-shell/src/layout/chatDockState.ts`:
- New `isFramedConsole()`: `window.self !== window.top`, and `true` when
the parent refuses access.
- `readStoredDockExpanded` returns its `fallback` inside a frame, so the
framed console's dock starts from its own default (collapsed for the
console rail) instead of the host page's `ai-chat-dock-expanded` flag.
- `writeStoredDockExpanded` is a no-op inside a frame.
`useChatDockState` keeps the frame's expanded state in React state only,
so opening or closing the dock in the preview never writes the host's
flag. `armChatDockExpanded` goes through the same writer.
The guard sits in the dock state module rather than in a `ConsoleLayout`
special case, as the card asked. Every reader and writer of the flag
passes through these two helpers, so `ConsoleLayout` is unchanged.
### Frame signal: `window.self !== window.top`, not `?preview=draft`
One signal, chosen for these reasons:
1. The query misses a case the card's defect covers. `LiveCanvas`'s
`canvasSrc` drops `?preview=draft` once the app is materialized (the
frame then loads the plain `/apps/SEGMENT`). That frame shares
sessionStorage just the same and would still open a second dock.
2. The query also matches a page that is not framed. A draft preview
opened in its own tab is an ordinary console page; its dock is the only
assistant on screen and should keep working as before.
3. The frame test is the thing that actually causes the defect: sharing
the tab's sessionStorage with a host page that has its own dock.
## Tests
New
`ui/packages/app-shell/src/layout/__tests__/ConsoleLayout.previewFrameDock-26470.test.tsx`.
It renders the real `ConsoleLayout`, `AppShell`, `useChatDockState` and
storage helpers; only the rail body is a stand-in. "Framed" is
`window.top` answering a different window.
1. Framed, host flag `'1'`: the dock does not mount; the launcher is
there.
2. Framed, host flag `'0'`: opening (launcher click) and closing (the
keyboard toggle) the dock in the frame leaves the flag `'0'`.
3. Framed, host flag unset: toggling leaves it unset.
4. Control, top-level: flag `'1'` mounts the dock, and toggling writes
`'0'` then `'1'` as before.
No existing test was edited.
## Acceptance notes
- The card's "Expected" also suggests that a framed console show no
assistant launcher and arguably no sidebar or top bar. The seat's delta
asked for a dock that starts closed and is scoped to the frame, so the
launcher stays in the frame and the chrome is unchanged. Hiding preview
chrome is a product call, listed in the report's open questions.
- Any console in a frame now keeps its dock state per frame, not only
the Live Canvas. A console embedded by some other host starts collapsed
and does not carry the open dock across a full reload inside the frame;
the dock still works there.
- The rail WIDTH still persists through localStorage
(`ai-chat-dock-width`) inside a frame, as it does across tabs. The card
is about the expanded flag; width is a shared user preference, not a
second dock.
- `rememberDockReturnLocation` (the maximize-to-`/ai` origin) is not
framed-scoped. Maximizing from inside a preview frame is out of this
card's scope.
## Verification
All runs on head `d368ab53`, worktree based on `origin/main` `00a4593a`.
<details><summary>Targeted vitest (new file + the dock
siblings)</summary>
```
pnpm --dir ui exec vitest run (under os-verify-lock) \
.../ConsoleLayout.previewFrameDock-26470.test.tsx .../chatDockState.test.ts \
.../ConsoleLayout.fabYieldsToOpenDock-10899.test.tsx .../chatDockReturnLocation.test.tsx \
.../ChatDock.test.tsx .../ConsoleLayout.dockBinding-10926.test.tsx
Test Files 6 passed (6)
Tests 33 passed (33)
os-verify-lock: VERDICT command-exit 0
```
</details>
<details><summary>Ablation: source reverted to origin/main, new test
re-run</summary>
`chatDockState.ts` restored from `00a4593a` (tree only;
`isFramedConsole` count on disk 0), new test file run, file restored
with `git checkout HEAD --` (hash equals the HEAD blob, `git diff HEAD`
empty).
```
× inside a frame, the host page's EXPANDED flag does not mount a second dock
× opening and closing the dock inside a frame leaves the host page's flag as it was
× inside a frame, an unset host flag stays unset after the dock is toggled
Tests 3 failed | 1 passed (4)
os-verify-lock: VERDICT command-exit 1
```
The three framed cases go red and the top-level control stays green.
</details>
<details><summary>Type check and gates</summary>
`pnpm exec turbo run build --filter='@object-ui/app-shell^...'
--concurrency=2 && pnpm --filter @object-ui/app-shell type-check` (tsc
--noEmit, then tsc -p tsconfig.test.json, which covers the new test):
`os-verify-lock: VERDICT command-exit 0`.
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 35 commands; each ran with exit 0.
`--ran` reconciliation: `35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN` (a
derived zero: all 35 recorded an exit code).
</details>
The full app-shell suite and the repo-wide farm are left to CI (`Test
UI`, `Type Check UI`, `Lint UI`).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmPEQvCWPCD2sAQWd3D)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 14f3099 commit b7f2b0c
3 files changed
Lines changed: 186 additions & 2 deletions
File tree
- .changeset
- ui/packages/app-shell/src/layout
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
79 | | - | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
80 | 104 | | |
| 105 | + | |
81 | 106 | | |
82 | 107 | | |
83 | 108 | | |
| |||
89 | 114 | | |
90 | 115 | | |
91 | 116 | | |
92 | | - | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
93 | 120 | | |
94 | 121 | | |
| 122 | + | |
95 | 123 | | |
96 | 124 | | |
97 | 125 | | |
| |||
0 commit comments