dsh-sidenote 0.7.3 → 0.7.4

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
@@ -308,6 +308,17 @@ window.__ModuleLoader__.load({
308
308
  function conversationViewOf(node) {
309
309
  return node.closest("[data-conversation-session], [data-sidenote-chat-session]") ?? document.body;
310
310
  }
311
+ /** Find the last rendered member of an explicit history range, not the side-chat tail. */
312
+ function lastConversationEntry(root, keys) {
313
+ return [...root.querySelectorAll("[data-chat-flow-key]")].reverse().find((row) => keys.has(row.dataset.chatFlowKey ?? "") && row.getBoundingClientRect().height > 0);
314
+ }
315
+ /** Land above the native composer, without scrolling another pane or moving focus. */
316
+ function revealConversationEntryEnd(row) {
317
+ const scroller = row.closest("[data-conversation-scroll]");
318
+ if (!scroller) return;
319
+ const clip = quoteClipOf(row, conversationViewOf(row));
320
+ scroller.scrollTop += row.getBoundingClientRect().bottom - clip.bottom + 12;
321
+ }
311
322
  function hasConversationMessages() {
312
323
  return document.querySelector("[data-chat-flow-kind]") !== null;
313
324
  }
@@ -611,7 +622,7 @@ window.__ModuleLoader__.load({
611
622
  let dockRegistered = false;
612
623
  let report = {
613
624
  versions: {
614
- sidenote: "0.7.3",
625
+ sidenote: "0.7.4",
615
626
  dsh: "unknown",
616
627
  betterSidebar: null
617
628
  },
@@ -1262,15 +1273,15 @@ window.__ModuleLoader__.load({
1262
1273
  document.head.appendChild(tag);
1263
1274
  }
1264
1275
  var diagnostics_module_css_default = {
1276
+ "description": "tGe0LW_description",
1277
+ "settingTitle": "tGe0LW_settingTitle",
1265
1278
  "setting": "tGe0LW_setting",
1266
1279
  "list": "tGe0LW_list",
1267
- "header": "tGe0LW_header",
1268
- "versions": "tGe0LW_versions",
1269
1280
  "actions": "tGe0LW_actions",
1270
- "trigger": "tGe0LW_trigger",
1271
- "settingTitle": "tGe0LW_settingTitle",
1272
1281
  "panel": "tGe0LW_panel",
1273
- "description": "tGe0LW_description"
1282
+ "header": "tGe0LW_header",
1283
+ "versions": "tGe0LW_versions",
1284
+ "trigger": "tGe0LW_trigger"
1274
1285
  };
1275
1286
  //#endregion
1276
1287
  //#region src/client/diagnostics/index.tsx
@@ -2323,7 +2334,7 @@ window.__ModuleLoader__.load({
2323
2334
  }
2324
2335
  //#endregion
2325
2336
  //#region \0dsh-css:/home/runner/work/dsh-sidenote/dsh-sidenote/src/client/sidechat/sidechat.module.css.mjs
2326
- const css$2 = "._5KEEsG_nativeHost,._5KEEsG_root,._5KEEsG_nativeConversation{flex-direction:column;min-height:0;display:flex}._5KEEsG_nativeHost,._5KEEsG_root{height:100%}._5KEEsG_root{color:var(--dsw-alias-label-primary)}._5KEEsG_nativeConversation{flex:1}._5KEEsG_chatView{flex-direction:column;flex:1 0 auto;min-width:0;display:flex}._5KEEsG_mainContext{flex:none;padding:12px 24px 4px}._5KEEsG_historyToggle{align-items:center;gap:6px;display:flex}._5KEEsG_contextHint{color:var(--dsw-alias-label-tertiary);font-size:12px}._5KEEsG_contextChevron{transform:rotate(-90deg)}._5KEEsG_contextChevron,._5KEEsG_contextChevronOpen{flex:none}._5KEEsG_progressEntries{max-height:240px;margin:8px 6px;overflow:auto}._5KEEsG_progressEntry{white-space:pre-wrap;overflow-wrap:anywhere;margin:10px 0;font-size:13px;line-height:1.6}._5KEEsG_root [data-sidenote-progress-snapshot],._5KEEsG_root[data-sidenote-context-pending] [data-submission-echo]{display:none}._5KEEsG_reflowAction{width:32px;height:32px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:8px;flex:none;order:1;justify-content:center;align-items:center;margin-left:auto;padding:0;display:inline-flex}._5KEEsG_reflowAction:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}._5KEEsG_reflowAction:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}._5KEEsG_reflowAction:disabled{cursor:default;color:var(--dsw-alias-state-business-primary);background:0 0}._5KEEsG_nativeToolbar{border-bottom:1px solid var(--dsw-alias-border-l1);justify-content:space-between;align-items:center;gap:8px;padding:8px 12px;display:flex}._5KEEsG_nativeToolbarActions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._5KEEsG_nativeAction,._5KEEsG_historyToggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:4px 6px;font-size:13px}._5KEEsG_nativeAction:hover,._5KEEsG_nativeAction:focus-visible,._5KEEsG_historyToggle:hover,._5KEEsG_historyToggle:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._5KEEsG_nativeAction:disabled{opacity:.5;cursor:default}._5KEEsG_historyToggle{text-align:left;margin:0}._5KEEsG_stateScreen{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px 16px;display:flex}._5KEEsG_emptyIcon{color:var(--dsw-alias-label-tertiary)}._5KEEsG_emptyTitle{font-size:14px;font-weight:600}._5KEEsG_emptyText{color:var(--dsw-alias-label-secondary);max-width:280px;font-size:13px;line-height:1.6}._5KEEsG_stateDetail,._5KEEsG_errorRow{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;font-size:13px;line-height:1.6}._5KEEsG_errorRow{padding:8px 12px}._5KEEsG_headerSide{color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:0;border-radius:6px;align-items:center;gap:4px;padding:4px 8px;display:inline-flex}._5KEEsG_headerSide:hover,._5KEEsG_headerSide:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._5KEEsG_headerSide:disabled{opacity:.4;cursor:default}._5KEEsG_visibilitySetting{justify-content:space-between;align-items:center;gap:16px;padding:8px 0;display:flex}._5KEEsG_visibilitySetting p{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px}";
2337
+ const css$2 = "._5KEEsG_nativeHost,._5KEEsG_root,._5KEEsG_nativeConversation{flex-direction:column;min-height:0;display:flex}._5KEEsG_nativeHost,._5KEEsG_root{height:100%}._5KEEsG_root{color:var(--dsw-alias-label-primary)}._5KEEsG_nativeConversation{flex:1}._5KEEsG_chatView{flex-direction:column;flex:1 0 auto;min-width:0;display:flex}._5KEEsG_historyView{display:contents}._5KEEsG_mainContext{flex:none;padding:12px 24px 4px}._5KEEsG_historyToggle{align-items:center;gap:6px;display:flex}._5KEEsG_contextHint{color:var(--dsw-alias-label-tertiary);font-size:12px}._5KEEsG_contextChevron{transform:rotate(-90deg)}._5KEEsG_contextChevron,._5KEEsG_contextChevronOpen{flex:none}._5KEEsG_progressEntries{max-height:240px;margin:8px 6px;overflow:auto}._5KEEsG_progressEntry{white-space:pre-wrap;overflow-wrap:anywhere;margin:10px 0;font-size:13px;line-height:1.6}._5KEEsG_root [data-sidenote-progress-snapshot],._5KEEsG_root[data-sidenote-context-pending] [data-submission-echo]{display:none}._5KEEsG_reflowAction{width:32px;height:32px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:8px;flex:none;order:1;justify-content:center;align-items:center;margin-left:auto;padding:0;display:inline-flex}._5KEEsG_reflowAction:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}._5KEEsG_reflowAction:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}._5KEEsG_reflowAction:disabled{cursor:default;color:var(--dsw-alias-state-business-primary);background:0 0}._5KEEsG_nativeToolbar{border-bottom:1px solid var(--dsw-alias-border-l1);justify-content:space-between;align-items:center;gap:8px;padding:8px 12px;display:flex}._5KEEsG_nativeToolbarActions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._5KEEsG_nativeAction,._5KEEsG_historyToggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:4px 6px;font-size:13px}._5KEEsG_nativeAction:hover,._5KEEsG_nativeAction:focus-visible,._5KEEsG_historyToggle:hover,._5KEEsG_historyToggle:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._5KEEsG_nativeAction:disabled{opacity:.5;cursor:default}._5KEEsG_historyToggle{text-align:left;margin:0}._5KEEsG_stateScreen{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px 16px;display:flex}._5KEEsG_emptyIcon{color:var(--dsw-alias-label-tertiary)}._5KEEsG_emptyTitle{font-size:14px;font-weight:600}._5KEEsG_emptyText{color:var(--dsw-alias-label-secondary);max-width:280px;font-size:13px;line-height:1.6}._5KEEsG_stateDetail,._5KEEsG_errorRow{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;font-size:13px;line-height:1.6}._5KEEsG_errorRow{padding:8px 12px}._5KEEsG_headerSide{color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:0;border-radius:6px;align-items:center;gap:4px;padding:4px 8px;display:inline-flex}._5KEEsG_headerSide:hover,._5KEEsG_headerSide:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._5KEEsG_headerSide:disabled{opacity:.4;cursor:default}._5KEEsG_visibilitySetting{justify-content:space-between;align-items:center;gap:16px;padding:8px 0;display:flex}._5KEEsG_visibilitySetting p{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px}";
2327
2338
  const tagId$2 = "dsh-sidenote/sidechat.module.css";
2328
2339
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
2329
2340
  const tag = document.createElement("style");
@@ -2333,29 +2344,30 @@ window.__ModuleLoader__.load({
2333
2344
  document.head.appendChild(tag);
2334
2345
  }
2335
2346
  var sidechat_module_css_default = {
2336
- "nativeToolbarActions": "_5KEEsG_nativeToolbarActions",
2337
- "nativeAction": "_5KEEsG_nativeAction",
2338
- "stateScreen": "_5KEEsG_stateScreen",
2339
- "nativeHost": "_5KEEsG_nativeHost",
2340
- "root": "_5KEEsG_root",
2341
- "mainContext": "_5KEEsG_mainContext",
2342
- "emptyIcon": "_5KEEsG_emptyIcon",
2343
- "stateDetail": "_5KEEsG_stateDetail",
2344
- "visibilitySetting": "_5KEEsG_visibilitySetting",
2345
2347
  "nativeConversation": "_5KEEsG_nativeConversation",
2346
- "chatView": "_5KEEsG_chatView",
2347
- "historyToggle": "_5KEEsG_historyToggle",
2348
- "contextChevron": "_5KEEsG_contextChevron",
2348
+ "emptyIcon": "_5KEEsG_emptyIcon",
2349
2349
  "progressEntries": "_5KEEsG_progressEntries",
2350
2350
  "nativeToolbar": "_5KEEsG_nativeToolbar",
2351
+ "nativeAction": "_5KEEsG_nativeAction",
2352
+ "emptyText": "_5KEEsG_emptyText",
2353
+ "stateDetail": "_5KEEsG_stateDetail",
2354
+ "errorRow": "_5KEEsG_errorRow",
2355
+ "chatView": "_5KEEsG_chatView",
2356
+ "contextChevronOpen": "_5KEEsG_contextChevronOpen",
2357
+ "root": "_5KEEsG_root",
2358
+ "reflowAction": "_5KEEsG_reflowAction",
2359
+ "historyView": "_5KEEsG_historyView",
2360
+ "mainContext": "_5KEEsG_mainContext",
2361
+ "nativeHost": "_5KEEsG_nativeHost",
2362
+ "nativeToolbarActions": "_5KEEsG_nativeToolbarActions",
2351
2363
  "emptyTitle": "_5KEEsG_emptyTitle",
2364
+ "stateScreen": "_5KEEsG_stateScreen",
2352
2365
  "progressEntry": "_5KEEsG_progressEntry",
2353
2366
  "contextHint": "_5KEEsG_contextHint",
2354
- "reflowAction": "_5KEEsG_reflowAction",
2355
- "contextChevronOpen": "_5KEEsG_contextChevronOpen",
2356
- "emptyText": "_5KEEsG_emptyText",
2357
- "errorRow": "_5KEEsG_errorRow",
2358
- "headerSide": "_5KEEsG_headerSide"
2367
+ "contextChevron": "_5KEEsG_contextChevron",
2368
+ "historyToggle": "_5KEEsG_historyToggle",
2369
+ "headerSide": "_5KEEsG_headerSide",
2370
+ "visibilitySetting": "_5KEEsG_visibilitySetting"
2359
2371
  };
2360
2372
  //#endregion
2361
2373
  //#region src/client/sidechat/NativeConversation.tsx
@@ -2399,6 +2411,7 @@ window.__ModuleLoader__.load({
2399
2411
  ]
2400
2412
  }), expanded && side.progress && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2401
2413
  className: sidechat_module_css_default.progressEntries,
2414
+ "data-sidenote-context-progress": true,
2402
2415
  children: side.progress.entries.map((entry, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2403
2416
  className: sidechat_module_css_default.progressEntry,
2404
2417
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -2494,6 +2507,8 @@ window.__ModuleLoader__.load({
2494
2507
  });
2495
2508
  }
2496
2509
  function HistoryView({ Original, props, side }) {
2510
+ const root = (0, react.useRef)(null);
2511
+ const wasExpanded = (0, react.useRef)(side.contextExpanded);
2497
2512
  const nodes = props.useChat((state) => state.nodes);
2498
2513
  const snapshot = props.useConversation((state) => state);
2499
2514
  const groups = snapshot.views.grouped("chat");
@@ -2519,9 +2534,31 @@ window.__ModuleLoader__.load({
2519
2534
  }
2520
2535
  };
2521
2536
  const useProjected = (0, react.useCallback)((selector) => selector(projected), [projected]);
2522
- return (0, react.createElement)(Original, {
2523
- ...props,
2524
- useConversation: useProjected
2537
+ (0, react.useLayoutEffect)(() => {
2538
+ const opening = side.contextExpanded && !wasExpanded.current;
2539
+ wasExpanded.current = side.contextExpanded;
2540
+ if (!opening) return;
2541
+ const keys = new Set(entries.filter(isInherited).map((entry) => entry.key));
2542
+ let frame = requestAnimationFrame(() => {
2543
+ frame = requestAnimationFrame(() => {
2544
+ if (!root.current) return;
2545
+ const progress = root.current.closest("[data-sidenote-transcript]")?.querySelector("[data-sidenote-context-progress]");
2546
+ if (progress) progress.scrollTop = progress.scrollHeight;
2547
+ const target = progress ?? lastConversationEntry(root.current, keys);
2548
+ if (target) revealConversationEntryEnd(target);
2549
+ });
2550
+ });
2551
+ return () => {
2552
+ cancelAnimationFrame(frame);
2553
+ };
2554
+ }, [side.contextExpanded]);
2555
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2556
+ ref: root,
2557
+ className: sidechat_module_css_default.historyView,
2558
+ children: (0, react.createElement)(Original, {
2559
+ ...props,
2560
+ useConversation: useProjected
2561
+ })
2525
2562
  });
2526
2563
  }
2527
2564
  /** Both admitted hosts publish the native chat View; only 0.2.1 adds a Flow slot. */
@@ -5555,34 +5592,34 @@ ${entries.map((entry) => {
5555
5592
  document.head.appendChild(tag);
5556
5593
  }
5557
5594
  var annotate_module_css_default = {
5558
- "badge": "_SVViG_badge",
5595
+ "deleteButton": "_SVViG_deleteButton",
5596
+ "badgeSent": "_SVViG_badgeSent",
5559
5597
  "editorSpacer": "_SVViG_editorSpacer",
5560
- "editorError": "_SVViG_editorError",
5561
- "confirmButton": "_SVViG_confirmButton",
5562
- "hint": "_SVViG_hint",
5563
- "sentChipRow": "_SVViG_sentChipRow",
5564
- "hintClose": "_SVViG_hintClose",
5565
- "sentCardTitle": "_SVViG_sentCardTitle",
5566
- "editorTextarea": "_SVViG_editorTextarea",
5567
- "editorNew": "_SVViG_editorNew",
5568
- "editorTarget": "_SVViG_editorTarget",
5598
+ "saveButton": "_SVViG_saveButton",
5599
+ "toolbar": "_SVViG_toolbar",
5600
+ "sentChip": "_SVViG_sentChip",
5569
5601
  "editorFooter": "_SVViG_editorFooter",
5570
- "cancelButton": "_SVViG_cancelButton",
5602
+ "hint": "_SVViG_hint",
5571
5603
  "dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
5572
- "sentChip": "_SVViG_sentChip",
5604
+ "confirmButton": "_SVViG_confirmButton",
5605
+ "toolbarBelow": "_SVViG_toolbarBelow",
5573
5606
  "editorInput": "_SVViG_editorInput",
5574
- "highlight": "_SVViG_highlight",
5607
+ "sentCardNote": "_SVViG_sentCardNote",
5575
5608
  "toolbarButton": "_SVViG_toolbarButton",
5576
- "toolbar": "_SVViG_toolbar",
5577
- "deleteButton": "_SVViG_deleteButton",
5578
- "editorAsk": "_SVViG_editorAsk",
5579
- "badgeSent": "_SVViG_badgeSent",
5580
5609
  "sentCardQuote": "_SVViG_sentCardQuote",
5581
- "sentCardNote": "_SVViG_sentCardNote",
5582
- "saveButton": "_SVViG_saveButton",
5610
+ "hintClose": "_SVViG_hintClose",
5611
+ "badge": "_SVViG_badge",
5612
+ "editorError": "_SVViG_editorError",
5613
+ "editorTarget": "_SVViG_editorTarget",
5614
+ "highlight": "_SVViG_highlight",
5615
+ "sentCardTitle": "_SVViG_sentCardTitle",
5616
+ "sentChipRow": "_SVViG_sentChipRow",
5583
5617
  "editorAskRow": "_SVViG_editorAskRow",
5584
- "toolbarBelow": "_SVViG_toolbarBelow",
5585
- "editorEdit": "_SVViG_editorEdit"
5618
+ "editorNew": "_SVViG_editorNew",
5619
+ "editorEdit": "_SVViG_editorEdit",
5620
+ "editorAsk": "_SVViG_editorAsk",
5621
+ "cancelButton": "_SVViG_cancelButton",
5622
+ "editorTextarea": "_SVViG_editorTextarea"
5586
5623
  };
5587
5624
  //#endregion
5588
5625
  //#region src/client/annotate/hint.tsx
@@ -6395,34 +6432,34 @@ ${entries.map((entry) => {
6395
6432
  document.head.appendChild(tag);
6396
6433
  }
6397
6434
  var annotations_module_css_default = {
6398
- "chipClearAll": "SVnebq_chipClearAll",
6399
- "sentChips": "SVnebq_sentChips",
6400
- "chipDismissShade": "SVnebq_chipDismissShade",
6401
- "storageNotice": "SVnebq_storageNotice",
6435
+ "chipDismiss": "SVnebq_chipDismiss",
6436
+ "chipWrap": "SVnebq_chipWrap",
6437
+ "contextActions": "SVnebq_contextActions",
6438
+ "contextEditor": "SVnebq_contextEditor",
6402
6439
  "contextText": "SVnebq_contextText",
6403
6440
  "contextAppear": "SVnebq_contextAppear",
6404
- "chip": "SVnebq_chip",
6405
- "contextList": "SVnebq_contextList",
6406
- "contextActions": "SVnebq_contextActions",
6407
- "contextLabel": "SVnebq_contextLabel",
6408
- "contextAction": "SVnebq_contextAction",
6409
- "sentChipGroup": "SVnebq_sentChipGroup",
6410
- "userContextHost": "SVnebq_userContextHost",
6441
+ "sentContext": "SVnebq_sentContext",
6411
6442
  "sentChip": "SVnebq_sentChip",
6412
- "contextItem": "SVnebq_contextItem",
6443
+ "sentChevron": "SVnebq_sentChevron",
6413
6444
  "sentChevronOpen": "SVnebq_sentChevronOpen",
6414
- "contextFooter": "SVnebq_contextFooter",
6415
- "contextSource": "SVnebq_contextSource",
6445
+ "storageNotice": "SVnebq_storageNotice",
6416
6446
  "sentPreview": "SVnebq_sentPreview",
6417
- "contextNumber": "SVnebq_contextNumber",
6418
- "contextEditor": "SVnebq_contextEditor",
6419
- "sentChevron": "SVnebq_sentChevron",
6420
6447
  "chipMain": "SVnebq_chipMain",
6421
- "sentContext": "SVnebq_sentContext",
6422
- "chipDismiss": "SVnebq_chipDismiss",
6448
+ "chipClearAll": "SVnebq_chipClearAll",
6449
+ "userContextHost": "SVnebq_userContextHost",
6450
+ "sentChips": "SVnebq_sentChips",
6451
+ "contextNumber": "SVnebq_contextNumber",
6452
+ "chip": "SVnebq_chip",
6453
+ "chipDismissShade": "SVnebq_chipDismissShade",
6423
6454
  "contextPanel": "SVnebq_contextPanel",
6455
+ "contextAction": "SVnebq_contextAction",
6456
+ "contextItem": "SVnebq_contextItem",
6457
+ "contextFooter": "SVnebq_contextFooter",
6458
+ "sentChipGroup": "SVnebq_sentChipGroup",
6459
+ "contextSource": "SVnebq_contextSource",
6424
6460
  "contextContent": "SVnebq_contextContent",
6425
- "chipWrap": "SVnebq_chipWrap"
6461
+ "contextLabel": "SVnebq_contextLabel",
6462
+ "contextList": "SVnebq_contextList"
6426
6463
  };
6427
6464
  //#endregion
6428
6465
  //#region src/client/annotations/chip.tsx
@@ -32,6 +32,10 @@ export declare function findSendButtonInCard(root?: ParentNode): HTMLButtonEleme
32
32
  export declare function composerSessionId(seat: HTMLElement, fallback: string): string;
33
33
  /** Bound message hover details to their native conversation pane. */
34
34
  export declare function conversationViewOf(node: Element): HTMLElement;
35
+ /** Find the last rendered member of an explicit history range, not the side-chat tail. */
36
+ export declare function lastConversationEntry(root: HTMLElement, keys: ReadonlySet<string>): HTMLElement | undefined;
37
+ /** Land above the native composer, without scrolling another pane or moving focus. */
38
+ export declare function revealConversationEntryEnd(row: HTMLElement): void;
35
39
  export declare function isMainComposerInput(target: EventTarget | null): boolean;
36
40
  export declare function composerHasMenu(seat: HTMLElement): boolean;
37
41
  export declare function hasConversationMessages(): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-sidenote",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "Follow the tangent. Keep the thread. Annotate, explore on the side, and bring answers back to DeepSeek Harness. / 思路可以分岔,主线始终向前。划选注释、侧边追问,再把结论带回正在推进的任务。",
5
5
  "icon": "docs/assets/logo.svg",
6
6
  "type": "module",
@@ -89,6 +89,20 @@ export function conversationViewOf(node: Element): HTMLElement {
89
89
  return node.closest<HTMLElement>('[data-conversation-session], [data-sidenote-chat-session]') ?? document.body
90
90
  }
91
91
 
92
+ /** Find the last rendered member of an explicit history range, not the side-chat tail. */
93
+ export function lastConversationEntry(root: HTMLElement, keys: ReadonlySet<string>): HTMLElement | undefined {
94
+ return [...root.querySelectorAll<HTMLElement>('[data-chat-flow-key]')].reverse()
95
+ .find(row => keys.has(row.dataset.chatFlowKey ?? '') && row.getBoundingClientRect().height > 0)
96
+ }
97
+
98
+ /** Land above the native composer, without scrolling another pane or moving focus. */
99
+ export function revealConversationEntryEnd(row: HTMLElement): void {
100
+ const scroller = row.closest<HTMLElement>('[data-conversation-scroll]')
101
+ if (!scroller) return
102
+ const clip = quoteClipOf(row, conversationViewOf(row))
103
+ scroller.scrollTop += row.getBoundingClientRect().bottom - clip.bottom + 12
104
+ }
105
+
92
106
 
93
107
  export function isMainComposerInput(target: EventTarget | null): boolean {
94
108
  return target instanceof HTMLElement
@@ -1,8 +1,9 @@
1
- import { createContext, createElement, useCallback, useContext, useMemo, useSyncExternalStore, type ComponentType, type ReactNode } from 'react'
1
+ import { createContext, createElement, useCallback, useContext, useLayoutEffect, useMemo, useRef, useSyncExternalStore, type ComponentType, type ReactNode } from 'react'
2
2
  import type { Context, SessionReference } from '../host/contracts.ts'
3
3
  import { transcriptOf, type ChatMessage } from '../chat/transcript.ts'
4
4
  import { createFoldStore } from '../chat/viewState.ts'
5
5
  import { completedReplySeqsOf, type ChatReplySnapshot } from '../host/chat.ts'
6
+ import { lastConversationEntry, revealConversationEntryEnd } from '../host/dom.ts'
6
7
  import { IconCheckOutline16, IconChevronDownOutline14 } from '../host/icons.ts'
7
8
  import type { SnapshotEntry } from './snapshot.ts'
8
9
  import { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives'
@@ -45,7 +46,7 @@ function ChatView(props: { renderSlot(name: 'conversation.session', owner: { vie
45
46
  <IconChevronDownOutline14 size={12} className={expanded ? css.contextChevronOpen : css.contextChevron} />
46
47
  <span>{t('mainContext')}</span><span className={css.contextHint}>{side.contextPending ? t('contextAtSend') : side.progress?.takenAt ? t('contextAsOf', { time: side.progress.takenAt }) : t('contextInherited')}</span>
47
48
  </button>
48
- {expanded && side.progress && <div className={css.progressEntries}>{side.progress.entries.map((entry, index) => <div key={index} className={css.progressEntry}><span className={css.contextHint}>{entry.kind === 'tool' ? entry.toolName : t(`context${entry.kind}`)}</span><div>{entry.text}</div></div>)}</div>}
49
+ {expanded && side.progress && <div className={css.progressEntries} data-sidenote-context-progress>{side.progress.entries.map((entry, index) => <div key={index} className={css.progressEntry}><span className={css.contextHint}>{entry.kind === 'tool' ? entry.toolName : t(`context${entry.kind}`)}</span><div>{entry.text}</div></div>)}</div>}
49
50
  </div>
50
51
  <SideContext.Provider value={{ ...side, contextExpanded: expanded }}>{props.renderSlot('conversation.session', { view: 'chat' })}</SideContext.Provider>
51
52
  </div>
@@ -107,6 +108,8 @@ interface ViewProps {
107
108
  useConversation<T>(select: (state: ViewSnapshot) => T): T
108
109
  }
109
110
  function HistoryView({ Original, props, side }: { Original: ComponentType<ViewProps>; props: ViewProps; side: SideConversation }) {
111
+ const root = useRef<HTMLDivElement>(null)
112
+ const wasExpanded = useRef(side.contextExpanded)
110
113
  const nodes = props.useChat(state => state.nodes)
111
114
  const snapshot = props.useConversation(state => state)
112
115
  const groups = snapshot.views.grouped('chat')
@@ -125,7 +128,27 @@ function HistoryView({ Original, props, side }: { Original: ComponentType<ViewPr
125
128
  const visible = side.contextExpanded ? entries : entries.filter(entry => !isInherited(entry))
126
129
  const projected = { ...snapshot, views: { ...snapshot.views, grouped: (name: string) => name === 'chat' && groups ? { ...groups, entries: visible } : snapshot.views.grouped(name) } }
127
130
  const useProjected = useCallback(<T,>(selector: (state: ViewSnapshot) => T): T => selector(projected), [projected])
128
- return createElement(Original, { ...props, useConversation: useProjected })
131
+ // Only an explicit expansion navigates. Later stream/layout updates and
132
+ // restored fold state leave the host's reader-position policy in charge.
133
+ useLayoutEffect(() => {
134
+ const opening = side.contextExpanded && !wasExpanded.current
135
+ wasExpanded.current = side.contextExpanded
136
+ if (!opening) return
137
+ const keys = new Set(entries.filter(isInherited).map(entry => entry.key))
138
+ // Native Chat settles its content resize first; navigate in the next frame
139
+ // so its initial tail restoration cannot overwrite this explicit action.
140
+ let frame = requestAnimationFrame(() => {
141
+ frame = requestAnimationFrame(() => {
142
+ if (!root.current) return
143
+ const progress = root.current.closest('[data-sidenote-transcript]')?.querySelector<HTMLElement>('[data-sidenote-context-progress]')
144
+ if (progress) progress.scrollTop = progress.scrollHeight
145
+ const target = progress ?? lastConversationEntry(root.current, keys)
146
+ if (target) revealConversationEntryEnd(target)
147
+ })
148
+ })
149
+ return () => { cancelAnimationFrame(frame) }
150
+ }, [side.contextExpanded])
151
+ return <div ref={root} className={css.historyView}>{createElement(Original, { ...props, useConversation: useProjected })}</div>
129
152
  }
130
153
 
131
154
  /** Both admitted hosts publish the native chat View; only 0.2.1 adds a Flow slot. */
@@ -8,6 +8,7 @@
8
8
  .root { color: var(--dsw-alias-label-primary); }
9
9
  .nativeConversation { flex: 1; }
10
10
  .chatView { display: flex; flex-direction: column; flex: 1 0 auto; min-width: 0; }
11
+ .historyView { display: contents; }
11
12
  .mainContext { flex: none; padding: 12px 24px 4px; }
12
13
  .historyToggle { display: flex; align-items: center; gap: 6px; }
13
14
  .contextHint { color: var(--dsw-alias-label-tertiary); font-size: 12px; }