deepseek-harness-background 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -62,8 +62,9 @@ window.__ModuleLoader__.load({
62
62
  * button, scroll-to-bottom), the load-earlier history button, the composer
63
63
  * dock family (agent task strips: TodoPanel / GoalBar / QueueDock) and its
64
64
  * takeover panels (approval, question, plan review), the subagent lineage
65
- * popover, the home hero "preview" badge, the sidebar build badge, and the
66
- * timeline rail (the official-rail rules below).
65
+ * popover, the home hero "preview" badge, the sidebar build badge, the turn
66
+ * deliverables (the changed-files card and the diff hover preview it opens),
67
+ * and the timeline rail (the official-rail rules below).
67
68
  * Every glassed surface shares ONE recipe:
68
69
  * fill from the painter's --dsw-specific-input-major token (same alpha
69
70
  * curve + theme dimming), the full shared blur/saturate/brightness chain
@@ -71,10 +72,14 @@ window.__ModuleLoader__.load({
71
72
  * surface reads heavier than the composer card. Dialogs, the settings UI,
72
73
  * menus, tooltips, toasts and every hover/accent fill keep the OFFICIAL
73
74
  * opaque paints — they are reading surfaces and must stay legible.
75
+ * The same rule excludes the HoverCard primitive's compact pod (a fixed
76
+ * #2C2C2E chip): it is hover FEEDBACK, not a floating card, and the official
77
+ * opaque paint is what keeps its label legible over any wallpaper.
74
78
  *
75
79
  * Selectors target authored attributes (`data-dsh-bg`, `data-composer-card`,
76
- * `data-terminal`…), the `:global` `.md-code-block` anchor, and CSS-module
77
- * SUFFIX conventions (`_bubble`, `_newSession`…) audited for collisions.
80
+ * `data-terminal`, `data-changes-hover-preview`…), the `:global`
81
+ * `.md-code-block` anchor, and CSS-module SUFFIX conventions (`_bubble`,
82
+ * `_newSession`…) audited for collisions.
78
83
  *
79
84
  * MAINTENANCE NOTE: the suffix anchors are the host's only stable-enough
80
85
  * integration surface today, but a host restyle that renames them silently
@@ -83,11 +88,14 @@ window.__ModuleLoader__.load({
83
88
  * suffix entries.
84
89
  * `_bubble` excludes `role="tooltip"` (Tooltip.module.css shares the suffix),
85
90
  * `_add` is scoped under `[data-composer-card]` (DiffBlock line markers share
86
- * it), `_newSession` excludes `Label` (the button's inner label span) and
87
- * `_toBottom` excludes `Slot` (the zero-height sticky wrapper) — both share
88
- * the substring with the chrome control but must keep their own paint, and
89
- * the subagent popover is `role="tree"` + `_menu` so the generic menu
90
- * surface stays untouched.
91
+ * it), `_newSession` is scoped to the `button` element — an element test, not
92
+ * another suffix exclusion, because 0.1.7-rc.2 added `newSessionLabelMask` /
93
+ * `newSessionContent` / `newSessionShortcut` INSIDE the button and each of
94
+ * those spans would otherwise take the chrome paint (the composer-sized
95
+ * second box the user sees inside the control) — and `_toBottom` excludes
96
+ * `Slot` (the zero-height sticky wrapper) — both share the substring with the
97
+ * chrome control but must keep their own paint, and the subagent popover is
98
+ * `role="tree"` + `_menu` so the generic menu surface stays untouched.
91
99
  */
92
100
  const BACKGROUND_CSS = `
93
101
  .dsh-bg-layer {
@@ -141,7 +149,7 @@ window.__ModuleLoader__.load({
141
149
  body[data-dsh-bg-glass] [data-web],
142
150
  body[data-dsh-bg-glass] [class*="_ioCard"],
143
151
  body[data-dsh-bg-glass] [class*="_instructionsCard"],
144
- body[data-dsh-bg-glass] [class*="_markdown"] :not(pre) > code {
152
+ body[data-dsh-bg-glass] [class*="_markdown"]:not(pre) > code {
145
153
  background-image: linear-gradient(180deg, rgba(255, 255, 255, var(--bg-glass-sheen, 0.07)), rgba(255, 255, 255, var(--bg-glass-sheen-mid, 0.02)) 38%, rgba(255, 255, 255, 0.01));
146
154
  -webkit-backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.42)) brightness(var(--bg-glass-brightness, 1)) contrast(1.01);
147
155
  backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.42)) brightness(var(--bg-glass-brightness, 1)) contrast(1.01);
@@ -161,19 +169,13 @@ window.__ModuleLoader__.load({
161
169
 
162
170
  /* The sidebar build badge paints with the label-ink token — a TEXT token we
163
171
  must not override globally — so it gets a theme-aware translucent ink. */
164
- body[data-dsh-bg] [class*="_buildRevision"] {
172
+ body[data-dsh-bg] [class*="_buildVersion"] {
165
173
  background-color: rgba(15, 17, 21, 0.62);
166
174
  }
167
- body[data-ds-dark-theme][data-dsh-bg] [class*="_buildRevision"] {
175
+ body[data-ds-dark-theme][data-dsh-bg] [class*="_buildVersion"] {
168
176
  background-color: rgba(249, 250, 251, 0.62);
169
177
  }
170
178
 
171
- /* The empty-state hero glow asset would wash out the wallpaper behind the
172
- welcome message — dim it so the art stays visible. */
173
- body[data-dsh-bg] [class*="_heroGlow"] {
174
- opacity: 0.2;
175
- }
176
-
177
179
  /* ---- Whitelisted chrome buttons --------------------------------------
178
180
  These three buttons' official fills ride button-* tokens shared with
179
181
  unrelated surfaces, so instead of overriding the tokens they get explicit
@@ -184,7 +186,7 @@ window.__ModuleLoader__.load({
184
186
  caps), sheen = the shared sheen vars. Hover = the same white glass at the
185
187
  boosted alpha var. Gated on data-dsh-bg-glass (off when the panel is
186
188
  fully opaque). */
187
- body[data-dsh-bg-glass] [class*="_newSession"]:not([class*="Label"]),
189
+ body[data-dsh-bg-glass] button[class*="_newSession"],
188
190
  body[data-dsh-bg-glass] [data-composer-card] [class*="_add"],
189
191
  body[data-dsh-bg-glass] [class*="_toBottom"]:not([class*="Slot"]) {
190
192
  background-color: var(--dsw-specific-input-major);
@@ -196,15 +198,15 @@ window.__ModuleLoader__.load({
196
198
  inset 0 0 0 0.5px rgba(255, 255, 255, 0.12),
197
199
  0 4px 14px rgba(0, 0, 0, 0.08);
198
200
  }
199
- body[data-dsh-bg-glass] [class*="_newSession"]:not([class*="Label"]):hover,
201
+ body[data-dsh-bg-glass] button[class*="_newSession"]:hover,
200
202
  body[data-dsh-bg-glass] [data-composer-card] [class*="_add"]:hover:not(:disabled),
201
203
  body[data-dsh-bg-glass] [class*="_toBottom"]:not([class*="Slot"]):hover {
202
204
  background-color: color-mix(in srgb, rgb(255 255 255) calc(var(--bg-glass-alpha-strong, 0.8) * 100%), transparent);
203
205
  }
204
206
  /* Collapsed rail renders the new-session control as a bare icon — keep the
205
207
  official transparent paint (and its official translucent hover). */
206
- body[data-dsh-bg-glass] [class*="_collapsed"] [class*="_newSession"],
207
- body[data-dsh-bg-glass] [class*="_collapsed"] [class*="_newSession"]:hover {
208
+ body[data-dsh-bg-glass] [data-sidebar-collapsed="true"] [class*="_newSession"],
209
+ body[data-dsh-bg-glass] [data-sidebar-collapsed="true"] [class*="_newSession"]:hover {
208
210
  background-color: transparent;
209
211
  background-image: none;
210
212
  box-shadow: none;
@@ -279,24 +281,50 @@ window.__ModuleLoader__.load({
279
281
  /* Sidebar build badge: translucent ink already, but it sat directly on
280
282
  the wallpaper without frost. Join the shared blur chain under the same
281
283
  gate (its theme-aware ink fills from the active block above). */
282
- body[data-dsh-bg-glass] [class*="_buildRevision"] {
284
+ body[data-dsh-bg-glass] [class*="_buildVersion"] {
283
285
  -webkit-backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.42)) brightness(var(--bg-glass-brightness, 1)) contrast(1.01);
284
286
  backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.42)) brightness(var(--bg-glass-brightness, 1)) contrast(1.01);
285
287
  }
286
288
 
289
+ /* ---- Turn deliverables: the changed-files card + its hover preview -----
290
+ Both paint with the official opaque --dsw-alias-bg-layer-1 (the token the
291
+ painter deliberately keeps opaque, because it backs the settings UI,
292
+ dialogs and menus), so each needs an EXPLICIT fill on top of the shared
293
+ sheen/blur chain — the token-mediated rule above cannot reach them.
294
+ [data-changed-files] is the card a turn tail renders; its header and
295
+ rows carry their own fills, so the card surface alone is glassed and the
296
+ contents keep their official paints. [data-changes-hover-preview] is
297
+ the fixed HoverCard the changed-files rows open on hover and the shared
298
+ component behind every diff preview; its width/height are set by the
299
+ hover-card primitive, so this only supplies the material. The preview's
300
+ official elevation shadow rides its own token untouched — only the fill
301
+ and the filter chain are taken over here. */
302
+ body[data-dsh-bg-glass] [data-changed-files],
303
+ body[data-dsh-bg-glass] [data-changes-hover-preview] {
304
+ background-color: var(--dsw-specific-input-major);
305
+ background-image: linear-gradient(180deg, rgba(255, 255, 255, var(--bg-glass-sheen, 0.07)), rgba(255, 255, 255, var(--bg-glass-sheen-mid, 0.02)) 38%, rgba(255, 255, 255, 0.01));
306
+ -webkit-backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.42)) brightness(var(--bg-glass-brightness, 1)) contrast(1.01);
307
+ backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.42)) brightness(var(--bg-glass-brightness, 1)) contrast(1.01);
308
+ box-shadow: var(--dsw-elevation-panel, 0 4px 14px rgba(0, 0, 0, 0.08));
309
+ }
310
+
287
311
  /* ---- Official turn rail (dsh >= 0.1.2, TurnNavigator) -----------------
288
312
  The enhance mode borrows the kernel's rail untouched, so its hover
289
313
  preview card keeps the OFFICIAL opaque fill (bg-layer-1, not mediated
290
314
  by any overridden token) — the "悬浮反馈形态不透明" complaint. Glass it
291
315
  explicitly with the same explicit-fill recipe as the chrome buttons
292
- (fill = the painter's composer token + shared blur/sheen). Anchors are
293
- structural, never class names: the kernel's module-CSS class hashes are
294
- not a stable contract, but nav[style*="--turn-natural-height"] (its
295
- inline rail metrics) and the preview's role="tooltip" are. No edge
316
+ (fill = the painter's composer token + shared blur/sheen). The anchor is
317
+ the rail's SEAT, the same one timeline/official-enhance.tsx identifies it
318
+ by: the only nav inside the conversation scrollport. 0.1.7 virtualized
319
+ the rail (commit f89cd786aa), which deleted the turn-natural-height
320
+ inline metric that used to sit on that nav — the nav carries no style
321
+ attribute at all now — so the selector that keyed on it matched nothing
322
+ and the preview went bare. The scrollport attribute and the preview's
323
+ role="tooltip" are the structural hooks; class hashes never are. No edge
296
324
  dissolve on the marks column: the first and last tick dashes straddle
297
325
  the marks box edges by 1px, so a fade zone sized off the box height
298
326
  swallows them instead of the band. */
299
- body[data-dsh-bg-glass] nav[style*="--turn-natural-height"] [role="tooltip"] {
327
+ body[data-dsh-bg-glass] [data-conversation-scroll] nav [role="tooltip"] {
300
328
  background-color: var(--dsw-specific-input-major);
301
329
  background-image: linear-gradient(180deg, rgba(255, 255, 255, var(--bg-glass-sheen, 0.07)), rgba(255, 255, 255, var(--bg-glass-sheen-mid, 0.02)) 38%, rgba(255, 255, 255, 0.01));
302
330
  -webkit-backdrop-filter: blur(var(--bg-glass-blur, 16px)) saturate(var(--bg-glass-saturate, 1.42)) brightness(var(--bg-glass-brightness, 1)) contrast(1.01);
@@ -961,7 +989,9 @@ window.__ModuleLoader__.load({
961
989
  "background.uploadFailed": "上传失败,请重试",
962
990
  "background.saveFailed": "保存失败,请重试",
963
991
  "background.timelineEnhance": "会话时间线增强",
964
- "background.timelineEnhanceNarrowHint": "为官方会话时间线的已加载轮次补上平滑跳转"
992
+ "background.timelineEnhanceNarrowHint": "为官方会话时间线的已加载轮次补上平滑跳转",
993
+ "background.migratedNotice": "已从旧版 dsh 的设置中恢复你的背景设置",
994
+ "background.migratedDismiss": "知道了"
965
995
  };
966
996
  /** English copy. */
967
997
  const en = {
@@ -990,7 +1020,9 @@ window.__ModuleLoader__.load({
990
1020
  "background.uploadFailed": "Upload failed, try again",
991
1021
  "background.saveFailed": "Save failed, try again",
992
1022
  "background.timelineEnhance": "Conversation timeline enhancement",
993
- "background.timelineEnhanceNarrowHint": "Adds smooth jumps for the rail's loaded turns"
1023
+ "background.timelineEnhanceNarrowHint": "Adds smooth jumps for the rail's loaded turns",
1024
+ "background.migratedNotice": "Restored your background settings from the previous dsh version",
1025
+ "background.migratedDismiss": "Got it"
994
1026
  };
995
1027
  //#endregion
996
1028
  //#region node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
@@ -1046,7 +1078,8 @@ window.__ModuleLoader__.load({
1046
1078
  if (!body.ok || body.value === void 0) throw new Error("unexpected payload");
1047
1079
  this.snapshot = {
1048
1080
  status: "ready",
1049
- value: body.value
1081
+ value: body.value,
1082
+ ...body.migrated === void 0 ? {} : { migrated: body.migrated }
1050
1083
  };
1051
1084
  } catch {
1052
1085
  this.snapshot = { status: "error" };
@@ -1075,7 +1108,8 @@ window.__ModuleLoader__.load({
1075
1108
  if (seq !== this.saveSeq) return "superseded";
1076
1109
  this.snapshot = {
1077
1110
  status: "ready",
1078
- value: body.value
1111
+ value: body.value,
1112
+ ...this.snapshot.migrated === void 0 ? {} : { migrated: this.snapshot.migrated }
1079
1113
  };
1080
1114
  this.notify();
1081
1115
  return "ok";
@@ -1115,7 +1149,7 @@ window.__ModuleLoader__.load({
1115
1149
  const settingsClient = new SettingsClient();
1116
1150
  //#endregion
1117
1151
  //#region \0dsh-css:D:\Project\deepseek-harness-background\src\client\SettingsRow.module.css.mjs
1118
- const css = ".-ozqdq_row{border-bottom:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;padding:16px 0;display:flex}.-ozqdq_header{flex-direction:column;gap:4px;display:flex}.-ozqdq_titleRow{justify-content:space-between;align-items:center;gap:12px;min-height:22px;display:flex}.-ozqdq_clearBtn{border-radius:12px;flex:none;height:24px;padding:0 10px;font-size:12px;line-height:24px}.-ozqdq_title{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.-ozqdq_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.-ozqdq_preview{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;height:132px;position:relative;overflow:hidden}.-ozqdq_previewImg{object-fit:var(--bg-object-fit,cover);width:100%;height:100%;opacity:var(--bg-opacity,1);filter:blur(var(--bg-wallpaper-blur,0px));transform:scale(var(--bg-wallpaper-scale,1));display:block;position:absolute;inset:0}.-ozqdq_previewScrim{background:rgba(255, 255, 255, var(--bg-scrim,.25));position:absolute;inset:0}body[data-ds-dark-theme] .-ozqdq_previewScrim{background:rgba(0, 0, 0, var(--bg-scrim,.25))}.-ozqdq_previewGlass{background:var(--bg-preview-glass,#ffffff24);width:44%;height:46px;backdrop-filter:blur(var(--bg-glass-blur,16px)) saturate(var(--bg-glass-saturate,1.42)) brightness(var(--bg-glass-brightness,1));border:1px solid #ffffff38;border-radius:10px;justify-content:center;align-items:center;display:flex;position:absolute;bottom:16px;left:16px;box-shadow:inset 0 1px #ffffff4d}.-ozqdq_previewGlassText{color:#0f172ac7;text-shadow:0 1px 2px #fff6;white-space:nowrap;font-size:11px;line-height:1}body[data-ds-dark-theme] .-ozqdq_previewGlassText{color:#ffffffd9;text-shadow:0 1px 2px #00000059}.-ozqdq_previewEmpty{color:var(--dsw-alias-label-tertiary);justify-content:center;align-items:center;font-size:12px;line-height:1.5;display:flex;position:absolute;inset:0}.-ozqdq_section{flex-direction:column;gap:8px;display:flex}.-ozqdq_sectionLabel{color:var(--dsw-alias-label-tertiary);letter-spacing:.01em;font-size:12px;font-weight:500;line-height:18px}.-ozqdq_sourceRow{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.-ozqdq_urlRow{align-items:center;gap:8px;display:flex}.-ozqdq_urlInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);min-width:0;height:32px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:0 10px;font-size:13px;line-height:1.5}.-ozqdq_urlInput::placeholder{color:var(--dsw-alias-label-dimmed)}.-ozqdq_urlInput:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.-ozqdq_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.-ozqdq_enabledBadge{white-space:nowrap;background:var(--dsw-alias-state-success-tertiary);color:var(--dsw-alias-state-success-primary);border-radius:999px;align-items:center;gap:4px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px;display:inline-flex}.-ozqdq_btn{border:1px solid var(--dsw-alias-border-l2);height:28px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;transition:background-color .15s,border-color .15s,color .15s;display:inline-flex}.-ozqdq_btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.-ozqdq_btn:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}.-ozqdq_btn:disabled{opacity:.45;cursor:default}.-ozqdq_btnPrimary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.-ozqdq_btnPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.-ozqdq_btnDanger{color:var(--dsw-alias-label-error)}.-ozqdq_btnDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.-ozqdq_sliderRow{align-items:center;gap:10px;display:flex}.-ozqdq_sliderLabel{color:var(--dsw-alias-label-primary);flex:0 0 96px;font-size:13px;line-height:1.5}.-ozqdq_slider{-webkit-appearance:none;appearance:none;cursor:pointer;background:0 0;flex:1;min-width:0;height:18px;margin:0}.-ozqdq_slider::-webkit-slider-runnable-track{background:linear-gradient(90deg, var(--dsw-alias-brand-primary) var(--bg-fill,0%), var(--dsw-alias-border-l3) var(--bg-fill,0%));border-radius:2px;height:4px}.-ozqdq_slider::-moz-range-track{background:var(--dsw-alias-border-l3);border-radius:2px;height:4px}.-ozqdq_slider::-moz-range-progress{background:var(--dsw-alias-brand-primary);border-radius:2px;height:4px}.-ozqdq_slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;border:2px solid var(--dsw-alias-brand-primary);width:14px;height:14px;box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent), 0 1px 3px #00000047;background:#fff;border-radius:50%;margin-top:-5px}.-ozqdq_slider::-moz-range-thumb{border:2px solid var(--dsw-alias-brand-primary);width:14px;height:14px;box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent), 0 1px 3px #00000047;background:#fff;border-radius:50%}.-ozqdq_slider:hover::-webkit-slider-thumb{box-shadow:0 0 0 5px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent), 0 1px 4px #0000004d}.-ozqdq_slider:hover::-moz-range-thumb{box-shadow:0 0 0 5px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent), 0 1px 4px #0000004d}.-ozqdq_slider:active::-webkit-slider-thumb{box-shadow:0 0 0 7px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent), 0 1px 5px #00000052;transform:scale(1.12)}.-ozqdq_slider:active::-moz-range-thumb{box-shadow:0 0 0 7px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent), 0 1px 5px #00000052;transform:scale(1.12)}.-ozqdq_slider:focus-visible{outline:none}.-ozqdq_slider:focus-visible::-webkit-slider-thumb{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.-ozqdq_slider:focus-visible::-moz-range-thumb{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.-ozqdq_sliderValue{text-align:right;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary);flex:0 0 52px;font-size:13px;line-height:1.5}.-ozqdq_segRow{align-items:center;gap:10px;display:flex}.-ozqdq_segLabel{color:var(--dsw-alias-label-primary);flex:0 0 96px;font-size:13px;line-height:1.5}.-ozqdq_segGroup{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:9px;flex:1;gap:2px;min-width:0;padding:2px;display:inline-flex}.-ozqdq_segBtn{height:24px;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:7px;flex:1;padding:0 16px;font-size:12px;line-height:24px}.-ozqdq_segBtn:hover:not(.-ozqdq_segActive){color:var(--dsw-alias-label-primary)}.-ozqdq_segActive{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font-weight:500}.-ozqdq_error{color:var(--dsw-alias-label-error);font-size:12px;line-height:1.5}.-ozqdq_clearRow{align-items:center;gap:10px;display:flex}.-ozqdq_switch{background:var(--dsw-alias-border-l3);cursor:pointer;border:none;border-radius:11px;flex:none;width:40px;height:22px;padding:0;transition:background-color .15s;position:relative}.-ozqdq_switch[aria-checked=true]{background:var(--dsw-alias-brand-primary)}.-ozqdq_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.-ozqdq_switchKnob{background:#fff;border-radius:50%;width:16px;height:16px;transition:transform .15s;position:absolute;top:3px;left:3px;box-shadow:0 1px 3px #00000047}.-ozqdq_switch[aria-checked=true] .-ozqdq_switchKnob{transform:translate(18px)}@media (prefers-reduced-motion:reduce){.-ozqdq_btn,.-ozqdq_switch,.-ozqdq_switchKnob{transition:none}}";
1152
+ const css = ".-ozqdq_row{border-bottom:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;padding:16px 0;display:flex}.-ozqdq_header{flex-direction:column;gap:4px;display:flex}.-ozqdq_titleRow{justify-content:space-between;align-items:center;gap:12px;min-height:22px;display:flex}.-ozqdq_clearBtn{border-radius:12px;flex:none;height:24px;padding:0 10px;font-size:12px;line-height:24px}.-ozqdq_title{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.-ozqdq_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.-ozqdq_notice{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;justify-content:space-between;align-items:center;gap:12px;padding:8px 12px;display:flex}.-ozqdq_noticeText{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.-ozqdq_noticeBtn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;flex:none;padding:0;font-size:12px;line-height:18px;text-decoration:underline}.-ozqdq_noticeBtn:hover{color:var(--dsw-alias-label-primary)}.-ozqdq_preview{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;height:132px;position:relative;overflow:hidden}.-ozqdq_previewImg{object-fit:var(--bg-object-fit,cover);width:100%;height:100%;opacity:var(--bg-opacity,1);filter:blur(var(--bg-wallpaper-blur,0px));transform:scale(var(--bg-wallpaper-scale,1));display:block;position:absolute;inset:0}.-ozqdq_previewScrim{background:rgba(255, 255, 255, var(--bg-scrim,.25));position:absolute;inset:0}body[data-ds-dark-theme] .-ozqdq_previewScrim{background:rgba(0, 0, 0, var(--bg-scrim,.25))}.-ozqdq_previewGlass{background:var(--bg-preview-glass,#ffffff24);width:44%;height:46px;backdrop-filter:blur(var(--bg-glass-blur,16px)) saturate(var(--bg-glass-saturate,1.42)) brightness(var(--bg-glass-brightness,1));border:1px solid #ffffff38;border-radius:10px;justify-content:center;align-items:center;display:flex;position:absolute;bottom:16px;left:16px;box-shadow:inset 0 1px #ffffff4d}.-ozqdq_previewGlassText{color:#0f172ac7;text-shadow:0 1px 2px #fff6;white-space:nowrap;font-size:11px;line-height:1}body[data-ds-dark-theme] .-ozqdq_previewGlassText{color:#ffffffd9;text-shadow:0 1px 2px #00000059}.-ozqdq_previewEmpty{color:var(--dsw-alias-label-tertiary);justify-content:center;align-items:center;font-size:12px;line-height:1.5;display:flex;position:absolute;inset:0}.-ozqdq_section{flex-direction:column;gap:8px;display:flex}.-ozqdq_sectionLabel{color:var(--dsw-alias-label-tertiary);letter-spacing:.01em;font-size:12px;font-weight:500;line-height:18px}.-ozqdq_sourceRow{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.-ozqdq_urlRow{align-items:center;gap:8px;display:flex}.-ozqdq_urlInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);min-width:0;height:32px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:0 10px;font-size:13px;line-height:1.5}.-ozqdq_urlInput::placeholder{color:var(--dsw-alias-label-dimmed)}.-ozqdq_urlInput:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.-ozqdq_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.-ozqdq_enabledBadge{white-space:nowrap;background:var(--dsw-alias-state-success-tertiary);color:var(--dsw-alias-state-success-primary);border-radius:999px;align-items:center;gap:4px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px;display:inline-flex}.-ozqdq_btn{border:1px solid var(--dsw-alias-border-l2);height:28px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;transition:background-color .15s,border-color .15s,color .15s;display:inline-flex}.-ozqdq_btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.-ozqdq_btn:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}.-ozqdq_btn:disabled{opacity:.45;cursor:default}.-ozqdq_btnPrimary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.-ozqdq_btnPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.-ozqdq_btnDanger{color:var(--dsw-alias-label-error)}.-ozqdq_btnDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.-ozqdq_sliderRow{align-items:center;gap:10px;display:flex}.-ozqdq_sliderLabel{color:var(--dsw-alias-label-primary);flex:0 0 96px;font-size:13px;line-height:1.5}.-ozqdq_slider{-webkit-appearance:none;appearance:none;cursor:pointer;background:0 0;flex:1;min-width:0;height:18px;margin:0}.-ozqdq_slider::-webkit-slider-runnable-track{background:linear-gradient(90deg, var(--dsw-alias-brand-primary) var(--bg-fill,0%), var(--dsw-alias-border-l3) var(--bg-fill,0%));border-radius:2px;height:4px}.-ozqdq_slider::-moz-range-track{background:var(--dsw-alias-border-l3);border-radius:2px;height:4px}.-ozqdq_slider::-moz-range-progress{background:var(--dsw-alias-brand-primary);border-radius:2px;height:4px}.-ozqdq_slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;border:2px solid var(--dsw-alias-brand-primary);width:14px;height:14px;box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent), 0 1px 3px #00000047;background:#fff;border-radius:50%;margin-top:-5px}.-ozqdq_slider::-moz-range-thumb{border:2px solid var(--dsw-alias-brand-primary);width:14px;height:14px;box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent), 0 1px 3px #00000047;background:#fff;border-radius:50%}.-ozqdq_slider:hover::-webkit-slider-thumb{box-shadow:0 0 0 5px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent), 0 1px 4px #0000004d}.-ozqdq_slider:hover::-moz-range-thumb{box-shadow:0 0 0 5px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent), 0 1px 4px #0000004d}.-ozqdq_slider:active::-webkit-slider-thumb{box-shadow:0 0 0 7px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent), 0 1px 5px #00000052;transform:scale(1.12)}.-ozqdq_slider:active::-moz-range-thumb{box-shadow:0 0 0 7px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent), 0 1px 5px #00000052;transform:scale(1.12)}.-ozqdq_slider:focus-visible{outline:none}.-ozqdq_slider:focus-visible::-webkit-slider-thumb{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.-ozqdq_slider:focus-visible::-moz-range-thumb{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.-ozqdq_sliderValue{text-align:right;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary);flex:0 0 52px;font-size:13px;line-height:1.5}.-ozqdq_segRow{align-items:center;gap:10px;display:flex}.-ozqdq_segLabel{color:var(--dsw-alias-label-primary);flex:0 0 96px;font-size:13px;line-height:1.5}.-ozqdq_segGroup{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:9px;flex:1;gap:2px;min-width:0;padding:2px;display:inline-flex}.-ozqdq_segBtn{height:24px;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:7px;flex:1;padding:0 16px;font-size:12px;line-height:24px}.-ozqdq_segBtn:hover:not(.-ozqdq_segActive){color:var(--dsw-alias-label-primary)}.-ozqdq_segActive{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font-weight:500}.-ozqdq_error{color:var(--dsw-alias-label-error);font-size:12px;line-height:1.5}.-ozqdq_clearRow{align-items:center;gap:10px;display:flex}.-ozqdq_switch{background:var(--dsw-alias-border-l3);cursor:pointer;border:none;border-radius:11px;flex:none;width:40px;height:22px;padding:0;transition:background-color .15s;position:relative}.-ozqdq_switch[aria-checked=true]{background:var(--dsw-alias-brand-primary)}.-ozqdq_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.-ozqdq_switchKnob{background:#fff;border-radius:50%;width:16px;height:16px;transition:transform .15s;position:absolute;top:3px;left:3px;box-shadow:0 1px 3px #00000047}.-ozqdq_switch[aria-checked=true] .-ozqdq_switchKnob{transform:translate(18px)}@media (prefers-reduced-motion:reduce){.-ozqdq_btn,.-ozqdq_switch,.-ozqdq_switchKnob{transition:none}}";
1119
1153
  const tagId = "deepseek-harness-background/SettingsRow.module.css";
1120
1154
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1121
1155
  const tag = document.createElement("style");
@@ -1125,41 +1159,44 @@ window.__ModuleLoader__.load({
1125
1159
  document.head.appendChild(tag);
1126
1160
  }
1127
1161
  var SettingsRow_module_css_default = {
1128
- "enabledBadge": "-ozqdq_enabledBadge",
1129
- "previewImg": "-ozqdq_previewImg",
1130
- "btnPrimary": "-ozqdq_btnPrimary",
1131
- "hint": "-ozqdq_hint",
1132
- "segActive": "-ozqdq_segActive",
1133
- "clearRow": "-ozqdq_clearRow",
1134
- "segBtn": "-ozqdq_segBtn",
1135
- "previewGlassText": "-ozqdq_previewGlassText",
1136
- "btn": "-ozqdq_btn",
1137
- "preview": "-ozqdq_preview",
1138
- "clearBtn": "-ozqdq_clearBtn",
1139
- "urlInput": "-ozqdq_urlInput",
1140
- "btnDanger": "-ozqdq_btnDanger",
1141
- "urlRow": "-ozqdq_urlRow",
1162
+ "sectionLabel": "-ozqdq_sectionLabel",
1163
+ "row": "-ozqdq_row",
1164
+ "previewGlass": "-ozqdq_previewGlass",
1142
1165
  "switchKnob": "-ozqdq_switchKnob",
1166
+ "clearBtn": "-ozqdq_clearBtn",
1143
1167
  "segRow": "-ozqdq_segRow",
1144
- "segLabel": "-ozqdq_segLabel",
1145
- "previewGlass": "-ozqdq_previewGlass",
1146
- "header": "-ozqdq_header",
1147
- "sectionLabel": "-ozqdq_sectionLabel",
1148
- "sliderLabel": "-ozqdq_sliderLabel",
1149
1168
  "desc": "-ozqdq_desc",
1150
- "sliderValue": "-ozqdq_sliderValue",
1151
- "section": "-ozqdq_section",
1169
+ "sourceRow": "-ozqdq_sourceRow",
1170
+ "btnPrimary": "-ozqdq_btnPrimary",
1152
1171
  "titleRow": "-ozqdq_titleRow",
1153
- "previewEmpty": "-ozqdq_previewEmpty",
1172
+ "noticeBtn": "-ozqdq_noticeBtn",
1173
+ "section": "-ozqdq_section",
1154
1174
  "sliderRow": "-ozqdq_sliderRow",
1175
+ "enabledBadge": "-ozqdq_enabledBadge",
1176
+ "noticeText": "-ozqdq_noticeText",
1177
+ "urlRow": "-ozqdq_urlRow",
1155
1178
  "previewScrim": "-ozqdq_previewScrim",
1156
- "slider": "-ozqdq_slider",
1157
- "segGroup": "-ozqdq_segGroup",
1179
+ "sliderLabel": "-ozqdq_sliderLabel",
1180
+ "sliderValue": "-ozqdq_sliderValue",
1181
+ "header": "-ozqdq_header",
1158
1182
  "error": "-ozqdq_error",
1183
+ "urlInput": "-ozqdq_urlInput",
1184
+ "hint": "-ozqdq_hint",
1185
+ "btnDanger": "-ozqdq_btnDanger",
1159
1186
  "title": "-ozqdq_title",
1160
- "row": "-ozqdq_row",
1161
- "sourceRow": "-ozqdq_sourceRow",
1162
- "switch": "-ozqdq_switch"
1187
+ "previewImg": "-ozqdq_previewImg",
1188
+ "segLabel": "-ozqdq_segLabel",
1189
+ "switch": "-ozqdq_switch",
1190
+ "preview": "-ozqdq_preview",
1191
+ "segGroup": "-ozqdq_segGroup",
1192
+ "previewGlassText": "-ozqdq_previewGlassText",
1193
+ "segActive": "-ozqdq_segActive",
1194
+ "btn": "-ozqdq_btn",
1195
+ "clearRow": "-ozqdq_clearRow",
1196
+ "notice": "-ozqdq_notice",
1197
+ "segBtn": "-ozqdq_segBtn",
1198
+ "previewEmpty": "-ozqdq_previewEmpty",
1199
+ "slider": "-ozqdq_slider"
1163
1200
  };
1164
1201
  //#endregion
1165
1202
  //#region src/client/SettingsRow.tsx
@@ -1331,6 +1368,7 @@ window.__ModuleLoader__.load({
1331
1368
  const [uploading, setUploading] = (0, react.useState)(false);
1332
1369
  const [urlText, setUrlText] = (0, react.useState)("");
1333
1370
  const [error, setError] = (0, react.useState)("");
1371
+ const [noticeDismissed, setNoticeDismissed] = (0, react.useState)(false);
1334
1372
  const fileInput = (0, react.useRef)(null);
1335
1373
  const previewRef = (0, react.useRef)(null);
1336
1374
  const draftRef = (0, react.useRef)(DEFAULTS);
@@ -1513,6 +1551,19 @@ window.__ModuleLoader__.load({
1513
1551
  children: t("background.description")
1514
1552
  })]
1515
1553
  }),
1554
+ snapshot.migrated !== void 0 && !noticeDismissed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1555
+ className: SettingsRow_module_css_default.notice,
1556
+ role: "status",
1557
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1558
+ className: SettingsRow_module_css_default.noticeText,
1559
+ children: t("background.migratedNotice")
1560
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1561
+ type: "button",
1562
+ className: SettingsRow_module_css_default.noticeBtn,
1563
+ onClick: () => setNoticeDismissed(true),
1564
+ children: t("background.migratedDismiss")
1565
+ })]
1566
+ }),
1516
1567
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1517
1568
  className: SettingsRow_module_css_default.preview,
1518
1569
  ref: previewRef,
@@ -1740,12 +1791,20 @@ window.__ModuleLoader__.load({
1740
1791
  /**
1741
1792
  * Resolve the gesture that produced a rail click to an entry index.
1742
1793
  *
1743
- * Keyboard activation targets the focused mark button (recovered by its
1744
- * position among the rail's buttons); a mouse click carries coordinates and
1745
- * resolves through the rail geometry — the frame-style mapping over the
1746
- * published `--turn-scroll-top`. Returns -1 when neither applies (a
1747
- * keyboard target outside the rail, or a rail of an unsupported generation,
1748
- * which the kernel's own handler then owns).
1794
+ * Two rail generations answer here, told apart by capability:
1795
+ *
1796
+ * - The virtualized rail (dsh 0.1.7+) tags every RENDERED mark with its own
1797
+ * ladder index (`data-index`), so the gesture resolves by data. This is the
1798
+ * robust path: the virtualizer mounts only a window of marks, and their DOM
1799
+ * order is not the ladder order.
1800
+ * - The frame-style rail tags nothing and paints marks with
1801
+ * `pointer-events: none`, so a MOUSE gesture arrives at the `<nav>` with
1802
+ * coordinates and resolves through the rail geometry over the published
1803
+ * `--turn-scroll-top`; a KEYBOARD gesture activates the focused mark button,
1804
+ * recovered by its position among its siblings.
1805
+ *
1806
+ * Returns -1 when neither applies (a keyboard target outside the rail, or a
1807
+ * rail of an unsupported generation, which the kernel's own handler then owns).
1749
1808
  *
1750
1809
  * Assumption: the mark ticks are the ONLY buttons under the rail — the
1751
1810
  * official rail renders marks exclusively and its preview tooltip carries
@@ -1758,6 +1817,11 @@ window.__ModuleLoader__.load({
1758
1817
  function indexForEvent(rail, event) {
1759
1818
  const target = event.target;
1760
1819
  if (target instanceof Element) {
1820
+ const mark = target.closest("[data-index]");
1821
+ if (mark !== null && rail.contains(mark)) {
1822
+ const tagged = Number(mark.getAttribute("data-index"));
1823
+ if (Number.isSafeInteger(tagged) && tagged >= 0) return tagged;
1824
+ }
1761
1825
  const button = target.closest("button");
1762
1826
  if (button !== null && rail.contains(button)) {
1763
1827
  const index = Array.from(rail.querySelectorAll("button")).indexOf(button);
@@ -1858,33 +1922,36 @@ window.__ModuleLoader__.load({
1858
1922
  * turn navigator (dsh >= 0.1.2-rc.1, `TurnNavigator` in dsh-client-ui-chat).
1859
1923
  *
1860
1924
  * This component renders NOTHING. It borrows the official rail exactly as the
1861
- * kernel painted it and fixes its behaviour, scoped to what the frame-style
1862
- * rail (dsh >= 0.1.2-alpha.3, unchanged on 0.1.2-rc.1) still lacks — told
1863
- * apart by capability, never by version compare:
1925
+ * kernel painted it and fixes its behaviour, scoped to what the running rail
1926
+ * generation still lacks — told apart by capability, never by version compare:
1864
1927
  *
1865
1928
  * Smooth jump. The official `navigateToTurn` is one assignment
1866
1929
  * (`el.scrollTop += flowTop(row, el) - 24`); a click across a long transcript
1867
1930
  * teleports. Clicks on LOADED marks are intercepted in the capture phase and
1868
- * re-run through the shared glide instead. The frame-style rail also renders
1869
- * marks for turns OUTSIDE the loaded window (fed by the whole-log
1870
- * `turnOutline` projection); those jumps page history through the kernel's
1871
- * own loadThrough machinery — marks without an anchor key are left to it, and
1872
- * the click index is resolved against the same merged ladder the kernel
1873
- * renders. While one of those kernel jumps is still paging (a mark pulses
1874
- * `aria-busy`), the plugin stands down entirely: the kernel's loaded branch
1875
- * cancels its own pending jump before landing, and an interception would
1876
- * bypass that cancellation.
1931
+ * re-run through the shared glide instead. Both generations also render marks
1932
+ * for turns OUTSIDE the loaded window (fed by the whole-log `turnOutline`
1933
+ * projection); those jumps page history through the kernel's own loadThrough
1934
+ * machinery — marks without an anchor key are left to it, and the click index
1935
+ * is resolved against the same merged ladder the kernel renders. While one of
1936
+ * those kernel jumps is still paging (a mark pulses `aria-busy`), the plugin
1937
+ * stands down entirely: the kernel's loaded branch cancels its own pending
1938
+ * jump before landing, and an interception would bypass that cancellation.
1939
+ *
1940
+ * Two rail generations are supported, discovered by {@link findOfficialRail}:
1941
+ * the frame-style rail of dsh 0.1.2-rc.1 … 0.1.6 (inline frame metrics, marks
1942
+ * mapped by geometry) and the virtualized rail of dsh 0.1.7+ (no inline
1943
+ * metrics, marks tagged `data-index`, only a window of them mounted at once).
1877
1944
  *
1878
1945
  * State-ownership note (why intercepting the official click loses nothing):
1879
1946
  * the kernel's scrollport (`data-conversation-scroll`, ChatView's
1880
1947
  * `onScrollRef` handler) treats every unaccounted scrollTop assignment as a
1881
1948
  * reader move and on each frame (a) flips its own at-bottom machinery,
1882
- * (b) `chatScroll.save(position)` for view-tab restore, and (c)
1883
- * `scheduleActiveTurn()` to re-derive the rail's active mark. The glide
1884
- * animates the real scrollport, so all three keep converging to the target
1885
- * row while it runs — the only thing the interception skips is the official
1886
- * handler's synchronous `setActiveTurn(item.turn)`, which its own
1887
- * scroll-driven active derivation re-settles by the time the glide lands.
1949
+ * (b) `chatScroll.save(position)` for view-tab restore, and (c) re-derives
1950
+ * the rail's active mark. The glide animates the real scrollport, so all three
1951
+ * keep converging to the target row while it runs — the only thing the
1952
+ * interception skips is the official handler's synchronous active-mark
1953
+ * assignment, which its own scroll-driven derivation re-settles by the time
1954
+ * the glide lands.
1888
1955
  *
1889
1956
  * The jump goes through the shared backend (jump.ts).
1890
1957
  *
@@ -1892,20 +1959,32 @@ window.__ModuleLoader__.load({
1892
1959
  * root container during the BUBBLE phase. A capture listener on the rail
1893
1960
  * itself therefore runs first, and `stopImmediatePropagation()` there prevents
1894
1961
  * the event from ever reaching React's root — the official handler never
1895
- * fires. The rail owns pointer input for its whole column (marks carry
1896
- * `pointer-events: none`), so one listener catches mouse and keyboard alike.
1962
+ * fires. The frame-style rail owns pointer input for its whole column (marks
1963
+ * carry `pointer-events: none`); the virtualized rail's marks are real
1964
+ * buttons. Both arrive at the same capture listener on the rail.
1897
1965
  *
1898
1966
  * Nothing here touches the official DOM: no nodes are injected into a
1899
1967
  * React-owned subtree and no official class or paint is overridden.
1900
1968
  */
1901
1969
  /**
1902
- * Structural anchor of the official rail — its inline style carries the
1903
- * frame's own metric (`--turn-natural-height`), published on every supported
1904
- * kernel (dsh >= 0.1.2-rc.1). Whether the found rail is the supported
1905
- * frame-style generation is then told apart by isFrameRail, never by a
1906
- * version compare.
1970
+ * Find the official turn rail, in either supported generation.
1971
+ *
1972
+ * Identity comes from the SEAT, not from a rendered mark: the rail is the only
1973
+ * `<nav>` inside the conversation scrollport, and dsh 0.1.7's virtualizer
1974
+ * mounts its marks lazily — measured on the real rail, the `<nav>` exists with
1975
+ * an EMPTY marks container until the virtualizer has layout. Requiring a
1976
+ * rendered `button[data-index]` would therefore miss the rail exactly when it
1977
+ * first appears.
1978
+ *
1979
+ * The generation is then told apart by capability where it matters: the click
1980
+ * resolver reads a mark's own `data-index`, and only falls back to the legacy
1981
+ * frame geometry for a rail that publishes `--turn-scroll-top`.
1982
+ * @param root - the tree to search (defaults to the document).
1983
+ * @returns the rail element, or null when the conversation shows none.
1907
1984
  */
1908
- const OFFICIAL_RAIL_SELECTOR = "[data-conversation-scroll] nav[style*=\"--turn-natural-height\"]";
1985
+ function findOfficialRail(root = document) {
1986
+ return root.querySelector("[data-conversation-scroll] nav");
1987
+ }
1909
1988
  /**
1910
1989
  * Rails claimed by an enhancer instance, keyed by the owning instance's
1911
1990
  * claim token. One session-scoped enhancer mounts per session; without a
@@ -1941,7 +2020,7 @@ window.__ModuleLoader__.load({
1941
2020
  const claimToken = react.default.useMemo(() => Symbol("dsh-bg-timeline-claim"), []);
1942
2021
  react.default.useEffect(() => {
1943
2022
  const check = () => {
1944
- const found = document.querySelector(OFFICIAL_RAIL_SELECTOR);
2023
+ const found = findOfficialRail();
1945
2024
  if (found === null) {
1946
2025
  setRail(null);
1947
2026
  return;
@@ -2356,7 +2435,7 @@ window.__ModuleLoader__.load({
2356
2435
  order: 45,
2357
2436
  locale: "ui-background",
2358
2437
  inject: (sessionId) => ({ jumpToAnchor: (anchorKey) => {
2359
- const scrollport = document.querySelector("[data-conversation-scroll] nav[style*=\"--turn-natural-height\"]")?.closest("[data-conversation-scroll]") ?? null;
2438
+ const scrollport = findOfficialRail()?.closest("[data-conversation-scroll]") ?? null;
2360
2439
  return jumpToMessage(ctx.sessions, sessionId, anchorKey, {
2361
2440
  followZonePx: 24,
2362
2441
  targetTop: officialTargetTopFor,