Przeglądaj źródła

fix(web): keep turn previews above code banners

Yichen Jiang 1 miesiąc temu
rodzic
commit
515eca7dc7

+ 12 - 0
apps/web/tests/chat-scroll-contract.e2e.ts

@@ -591,6 +591,18 @@ describe('web e2e: long Chat scroll contract', () => {
       await expect.poll(() => tooltip.count(), { timeout: 15_000 }).toBe(1)
       expect(await tooltip.textContent()).toContain(HISTORY_FIXTURE.markers.user(1))
       expect(await tooltip.textContent()).toContain(HISTORY_FIXTURE.markers.assistant(1))
+      const transcriptLayers = await tooltip.evaluate((preview) => {
+        const railSlot = preview.closest('nav')?.parentElement
+        const codeBanner = document.querySelector<HTMLElement>('.md-code-block > :first-child')
+        if (!(railSlot instanceof HTMLElement) || codeBanner === null) {
+          throw new Error('turn preview or code-block banner stacking context is unavailable')
+        }
+        return {
+          codeBanner: Number(getComputedStyle(codeBanner).zIndex),
+          rail: Number(getComputedStyle(railSlot).zIndex),
+        }
+      })
+      expect(transcriptLayers.rail).toBeGreaterThan(transcriptLayers.codeBanner)
       await world.page.keyboard.press('Enter')
 
       // The jump pages history in and lands on turn 1: its mark flips to the

+ 2 - 2
packages/client/ui-chat/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/client/ui-chat/README.md
-README.md: 19cfcf4d14f7e805d6113eeb4e34a586f7150574
-README.zh.md: 18e20eeea15cf3eeb5e8e4a777fa534251232a81
+README.md: 79214bf2feb8384cecbb152aa31af162dc6dfdde
+README.zh.md: 674959e1974c74abba75f179eff50fe8488458fc

+ 1 - 1
packages/client/ui-chat/README.md

@@ -46,7 +46,7 @@ Settings → General exposes a persisted `Normal` / `Compact` conversation-displ
 <a id="scroll-ownership"></a>
 ## Scroll ownership
 
-Chat restores semantic anchors across history prepend and renderer remounts. While the reader is pinned to the floor, `ResizeObserver` follows the new floor and selects the latest loaded Turn without reading row geometry. Once the reader moves away, flow-height changes preserve the top position and the reading-line geometry selects the active Turn ([loaded-Turn navigation](../../../.agents/notes/implemented/feature/2026-08-25-loaded-turn-chat-navigation.md)).
+Chat restores semantic anchors across history prepend and renderer remounts. While the reader is pinned to the floor, `ResizeObserver` follows the new floor and selects the latest loaded Turn without reading row geometry. Once the reader moves away, flow-height changes preserve the top position and the reading-line geometry selects the active Turn. Turn-rail previews paint above sticky Markdown code-block banners, while the rail frame remains inside the transcript band above the composer ([loaded-Turn navigation](../../../.agents/notes/implemented/feature/2026-08-25-loaded-turn-chat-navigation.md)).
 
 -----
 

+ 1 - 1
packages/client/ui-chat/README.zh.md

@@ -46,7 +46,7 @@ Chat 会为每个非空的初始或恢复请求、显式消息序列起点或真
 <a id="scroll-ownership"></a>
 ## 滚动归属
 
-Chat 会在历史前插与 renderer 重新挂载时恢复语义锚点。读者跟随底部时,`ResizeObserver` 追随新的底部,并且无需读取行几何就选中最后一个已加载 Turn;读者离开底部后,高度变化会保持顶部位置,再由阅读线几何选择活跃 Turn([已加载 Turn 导航](../../../.agents/notes/implemented/feature/2026-08-25-loaded-turn-chat-navigation.zh.md))。
+Chat 会在历史前插与 renderer 重新挂载时恢复语义锚点。读者跟随底部时,`ResizeObserver` 追随新的底部,并且无需读取行几何就选中最后一个已加载 Turn;读者离开底部后,高度变化会保持顶部位置,再由阅读线几何选择活跃 Turn。轮次导航预览位于 Markdown 代码块粘性头栏上方,而导航外框始终处于 composer 上方的 transcript 区域内([已加载 Turn 导航](../../../.agents/notes/implemented/feature/2026-08-25-loaded-turn-chat-navigation.zh.md))。
 
 -----
 

+ 3 - 1
packages/client/ui-chat/src/client/chat/TurnNavigator.module.css

@@ -3,7 +3,9 @@
 .slot {
   position: sticky;
   top: 0;
-  z-index: 6;
+  /* Above transcript CodeBlock banners (z-index 6). The rail band excludes
+     the composer, so its preview never needs to cross the footer. */
+  z-index: 7;
   height: 0;
   pointer-events: none;
 }