dsh-sidenote 0.7.2 → 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/README.md +5 -5
- package/docs/assets/README.md +29 -14
- package/lib/client.js +109 -70
- package/lib/types/client/chat/transcript.d.ts +2 -1
- package/lib/types/client/host/dom.d.ts +4 -0
- package/package.json +1 -1
- package/src/client/chat/transcript.ts +5 -3
- package/src/client/host/dom.ts +14 -0
- package/src/client/sidechat/NativeConversation.tsx +26 -3
- package/src/client/sidechat/sidechat.module.css +1 -0
package/README.md
CHANGED
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-60708E" alt="MIT license"></a>
|
|
24
24
|
</p>
|
|
25
25
|
|
|
26
|
-

|
|
27
27
|
|
|
28
|
-
<p align="center"><sub>dsh-sidenote 0.
|
|
28
|
+
<p align="center"><sub>dsh-sidenote 0.7.2 · DSH 0.2.1-rc.2 · DeepSeek V4.1 Flash · 真实交互录制</sub></p>
|
|
29
29
|
|
|
30
30
|
## 安装
|
|
31
31
|
|
|
@@ -51,7 +51,7 @@ dsh plugin --profile web add dsh-sidenote
|
|
|
51
51
|
|
|
52
52
|
划选主对话或侧聊中的一段回复,写下问题或修改意见。**原文和注释一起保留**,可累积多条,发送前预览、移除,正文草稿照常编辑。
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
读到「提示“未找到”,并给出放宽条件建议」,就选中这一句,追问「给一版可直接使用的空状态」。
|
|
55
55
|
|
|
56
56
|
### 有结论,带回正在做的事
|
|
57
57
|
|
|
@@ -61,11 +61,11 @@ dsh plugin --profile web add dsh-sidenote
|
|
|
61
61
|
|
|
62
62
|
**划选一句 → 带着上下文追问 → 把结论带回主线。**
|
|
63
63
|
|
|
64
|
-

|
|
65
65
|
|
|
66
66
|
[观看清晰版 MP4](docs/assets/demo.mp4) · [查看静态截图](docs/assets/workflow.jpg)
|
|
67
67
|
|
|
68
|
-
<sub
|
|
68
|
+
<sub>2026-10-25 录制于 dsh-sidenote 0.7.2 / DSH 0.2.1-rc.2。以笔记搜索设计为例,真实调用官方 DeepSeek V4.1 Flash 生成回答;约 43 秒的录屏展示实际操作与完整回复过程。<a href="docs/assets/README.md">素材说明</a></sub>
|
|
69
69
|
|
|
70
70
|
## 用起来顺手的细节
|
|
71
71
|
|
package/docs/assets/README.md
CHANGED
|
@@ -1,23 +1,38 @@
|
|
|
1
|
-
# README media /
|
|
1
|
+
# README and storefront media / 首页与商店素材
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Captured on **2026-10-25** with the published **dsh-sidenote 0.7.2** package (source: `0d55fcd`) and **DSH 0.2.1-rc.2**, using native Conversation and the native right sidebar. The mounted plugin's version and bundle digest, plus the host's active conversation, composer, and sidebar packages, were checked before capture.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
素材于 **2026-10-25** 重新录制,使用已发布的 **dsh-sidenote 0.7.2**(源码 `0d55fcd`)与 **DSH 0.2.1-rc.2** 原生对话和右侧栏。录制前核对了实际挂载的插件版本与构建摘要,以及宿主正在运行的对话、输入框和侧栏组件版本。
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- `selection.jpg`: a selected passage and its note editor / 划选与注释编辑器。
|
|
9
|
-
- `sidechat.jpg`: a completed side-chat response / 侧聊问答。
|
|
10
|
-
- `demo.gif`, `demo.mp4`: the same continuous selection → annotation → side reply → reflow sequence / 同一段连续操作的 GIF 与 MP4。
|
|
11
|
-
- `hero.svg`, `hero-en.svg` and their `-compact.svg` variants: original vector covers; regenerate with `python3 scripts/build-brand-assets.py` / 原创矢量封面,可用该命令重新生成。
|
|
7
|
+
## Real model responses / 真实模型回答
|
|
12
8
|
|
|
13
|
-
The
|
|
9
|
+
The example task is designing a note app's search experience. All assistant replies were generated through the official **DeepSeek V4.1 Flash** API at `api.deepseek.com`, using model ID `deepseek-flash` with thinking off. This ID's mapping is documented in the [official model updates](https://api-docs.deepseek.com/updates/). Request metadata confirmed successful, completed responses. No fixed-answer adapter or prewritten assistant messages were used.
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
演示任务是设计笔记应用的搜索体验。全部助手回复都实际调用 `api.deepseek.com` 的 **DeepSeek V4.1 Flash** 生成,模型 ID 为 `deepseek-flash`,关闭思考模式;该 ID 的对应关系见[官方更新说明](https://api-docs.deepseek.com/updates/)。请求元数据确认调用成功并完成,没有使用固定回答的适配器或预先写入的助手消息。
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
The recording starts with the real main-chat answer already visible. It then shows selection and annotation, a real side-chat response, hover previews, editing a pending note, the clear button's reveal, the reply-toolbar handback action, sending the main draft with both kinds of context, and inspecting the sent message. The main model's final answer incorporates the returned empty-state copy and the edited annotation.
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
录屏从已经生成的主线回答开始,随后展示划选与批注、真实侧聊回答、悬浮预览、原位编辑、清除按钮显现、回复操作栏回流、携带两类上下文发送,以及发送后的气泡详情。主线最后的真实回答同时采纳了带回的空状态文案与修改后的批注。
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
## Assets / 文件
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
- `workflow.jpg`: a follow-up capture showing the selection toolbar's two destinations, a real side-chat answer, pending annotation and handback chips, and context bubbles in sent messages / 补拍主图:同屏展示划选菜单的两种去向、真实侧聊回答、待发送的批注与回流标签,以及已发送消息中的上下文气泡。
|
|
20
|
+
- `context-preview.jpg`: a compact preview with the selected passage, editable note, and horizontal context chips / 紧凑悬浮层中的原文、可编辑注解与横向标签。
|
|
21
|
+
- `selection.jpg`: selected text and the two annotation destinations, taken from the recording / 从录屏提取的划选画面与两种批注去向。
|
|
22
|
+
- `sidechat.jpg`: a real side-chat answer and the hovered reply-toolbar handback icon / 真实侧聊回答与悬停中的回流图标。
|
|
23
|
+
- `demo.gif`, `demo.mp4`: the same approximately **43-second** continuous workflow / 同一段约 **43 秒**的连续操作。
|
|
24
|
+
- `hero.svg`, `hero-en.svg` and their `-compact.svg` variants: vector brand covers; regenerate with `python3 scripts/build-brand-assets.py` / 品牌矢量封面,可用该命令重新生成。
|
|
25
|
+
|
|
26
|
+
## Capture and distribution / 录制与展示
|
|
27
|
+
|
|
28
|
+
The real interface uses the host's **light theme** and **18 px** conversation font. The hero uses a **1440 × 780** viewport and is exported at 2160 × 1170 so the selection menu and both composers fit together. Other stills use a **1440 × 900** viewport, captured at 2× pixel density and exported at 2160 × 1350; the selection still is a native 1440 × 900 recording frame. MP4 is 1440 × 900 at 30 fps; GIF is 1120 × 700 at 12 fps. Encoding preserves the browser's frame timing without speeding up responses. A small pointer overlay follows actual mouse events in the recording; product controls and model replies are not redrawn or substituted.
|
|
29
|
+
|
|
30
|
+
真实界面使用宿主的**浅色主题**和 **18 px** 会话字号。主图以 **1440 × 780** 视口补拍,导出为 2160 × 1170,让划选菜单和两侧输入框完整同屏。其余静态截图使用 **1440 × 900** 视口,按 2 倍像素密度采集后导出为 2160 × 1350;划选截图直接取自 1440 × 900 的录屏帧。MP4 为 1440 × 900 / 30 fps,GIF 为 1120 × 700 / 12 fps,均按浏览器帧时间编码,没有加速模型响应。录屏中仅添加跟随真实鼠标事件的小光标辅助层,没有重绘产品控件或替换模型回答。
|
|
31
|
+
|
|
32
|
+
[`screenshots.json`](../../screenshots.json) declares the storefront gallery. The [catalog's screenshot convention](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/blob/main/contributing.md#screenshots--截图optional-recommended--可选推荐) resolves those paths from this repository, and the next catalog build refreshes the gallery. Existing screenshot paths remain available for cached catalogs.
|
|
33
|
+
|
|
34
|
+
[`screenshots.json`](../../screenshots.json) 声明商店展示顺序。[商店截图约定](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/blob/main/contributing.md#screenshots--截图optional-recommended--可选推荐) 会从本仓库读取这些路径,下一次索引构建时更新图集;原有图片路径继续提供新版内容,供已缓存的索引读取。
|
|
35
|
+
|
|
36
|
+
For future captures, use a fresh isolated DSH home, install the published plugin with the official CLI, verify the active bundles, and run the complete workflow against the official model API. Keep credentials, launch tokens, private conversations, raw frames, and recording logs out of public assets. The older `capture-demo.mjs` and `capture-video.mjs` scripts target historical layouts and synthetic sessions; they do not reproduce this recording unchanged.
|
|
37
|
+
|
|
38
|
+
后续重录应使用全新隔离的 DSH home,通过官方 CLI 安装已发布插件,核对实际加载的构建,并调用官方模型走完完整流程。凭证、启动 token、私人会话、原始帧和录制日志不进入公开素材。旧 `capture-demo.mjs`、`capture-video.mjs` 面向历史布局与合成会话,不能原样用于复现本次录制。
|
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.
|
|
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 = {
|
|
1265
|
-
"
|
|
1266
|
-
"trigger": "tGe0LW_trigger",
|
|
1267
|
-
"actions": "tGe0LW_actions",
|
|
1268
|
-
"panel": "tGe0LW_panel",
|
|
1276
|
+
"description": "tGe0LW_description",
|
|
1269
1277
|
"settingTitle": "tGe0LW_settingTitle",
|
|
1270
1278
|
"setting": "tGe0LW_setting",
|
|
1279
|
+
"list": "tGe0LW_list",
|
|
1280
|
+
"actions": "tGe0LW_actions",
|
|
1281
|
+
"panel": "tGe0LW_panel",
|
|
1271
1282
|
"header": "tGe0LW_header",
|
|
1272
|
-
"
|
|
1273
|
-
"
|
|
1283
|
+
"versions": "tGe0LW_versions",
|
|
1284
|
+
"trigger": "tGe0LW_trigger"
|
|
1274
1285
|
};
|
|
1275
1286
|
//#endregion
|
|
1276
1287
|
//#region src/client/diagnostics/index.tsx
|
|
@@ -1855,17 +1866,19 @@ window.__ModuleLoader__.load({
|
|
|
1855
1866
|
const sideChatBridge = { current: null };
|
|
1856
1867
|
//#endregion
|
|
1857
1868
|
//#region src/client/chat/transcript.ts
|
|
1858
|
-
/**
|
|
1869
|
+
/** Protocol readers need actual text, not attachment preview labels before it.
|
|
1870
|
+
* Native Conversation renders images; only an image-only message needs a label. */
|
|
1859
1871
|
function contentTextOf(content) {
|
|
1860
1872
|
if (!Array.isArray(content)) return "";
|
|
1861
1873
|
const parts = [];
|
|
1874
|
+
let hasImage = false;
|
|
1862
1875
|
for (const block of content) {
|
|
1863
1876
|
if (typeof block !== "object" || block === null) continue;
|
|
1864
1877
|
const b = block;
|
|
1865
1878
|
if (b.type === "text" && typeof b.text === "string") parts.push(b.text);
|
|
1866
|
-
else if (b.type === "image")
|
|
1879
|
+
else if (b.type === "image") hasImage = true;
|
|
1867
1880
|
}
|
|
1868
|
-
return parts.join("\n");
|
|
1881
|
+
return parts.join("\n") || (hasImage ? t("imagePlaceholder") : "");
|
|
1869
1882
|
}
|
|
1870
1883
|
function seqKey(prefix, node) {
|
|
1871
1884
|
return `${prefix}:${typeof node.seq === "number" ? node.seq : "?"}`;
|
|
@@ -2321,7 +2334,7 @@ window.__ModuleLoader__.load({
|
|
|
2321
2334
|
}
|
|
2322
2335
|
//#endregion
|
|
2323
2336
|
//#region \0dsh-css:/home/runner/work/dsh-sidenote/dsh-sidenote/src/client/sidechat/sidechat.module.css.mjs
|
|
2324
|
-
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}";
|
|
2325
2338
|
const tagId$2 = "dsh-sidenote/sidechat.module.css";
|
|
2326
2339
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
2327
2340
|
const tag = document.createElement("style");
|
|
@@ -2331,29 +2344,30 @@ window.__ModuleLoader__.load({
|
|
|
2331
2344
|
document.head.appendChild(tag);
|
|
2332
2345
|
}
|
|
2333
2346
|
var sidechat_module_css_default = {
|
|
2334
|
-
"emptyText": "_5KEEsG_emptyText",
|
|
2335
|
-
"headerSide": "_5KEEsG_headerSide",
|
|
2336
2347
|
"nativeConversation": "_5KEEsG_nativeConversation",
|
|
2337
|
-
"
|
|
2338
|
-
"nativeAction": "_5KEEsG_nativeAction",
|
|
2339
|
-
"stateDetail": "_5KEEsG_stateDetail",
|
|
2348
|
+
"emptyIcon": "_5KEEsG_emptyIcon",
|
|
2340
2349
|
"progressEntries": "_5KEEsG_progressEntries",
|
|
2341
|
-
"contextChevron": "_5KEEsG_contextChevron",
|
|
2342
2350
|
"nativeToolbar": "_5KEEsG_nativeToolbar",
|
|
2343
|
-
"
|
|
2351
|
+
"nativeAction": "_5KEEsG_nativeAction",
|
|
2352
|
+
"emptyText": "_5KEEsG_emptyText",
|
|
2353
|
+
"stateDetail": "_5KEEsG_stateDetail",
|
|
2354
|
+
"errorRow": "_5KEEsG_errorRow",
|
|
2355
|
+
"chatView": "_5KEEsG_chatView",
|
|
2356
|
+
"contextChevronOpen": "_5KEEsG_contextChevronOpen",
|
|
2344
2357
|
"root": "_5KEEsG_root",
|
|
2345
|
-
"nativeHost": "_5KEEsG_nativeHost",
|
|
2346
|
-
"contextHint": "_5KEEsG_contextHint",
|
|
2347
|
-
"visibilitySetting": "_5KEEsG_visibilitySetting",
|
|
2348
|
-
"progressEntry": "_5KEEsG_progressEntry",
|
|
2349
2358
|
"reflowAction": "_5KEEsG_reflowAction",
|
|
2350
|
-
"
|
|
2359
|
+
"historyView": "_5KEEsG_historyView",
|
|
2360
|
+
"mainContext": "_5KEEsG_mainContext",
|
|
2361
|
+
"nativeHost": "_5KEEsG_nativeHost",
|
|
2351
2362
|
"nativeToolbarActions": "_5KEEsG_nativeToolbarActions",
|
|
2352
|
-
"contextChevronOpen": "_5KEEsG_contextChevronOpen",
|
|
2353
|
-
"stateScreen": "_5KEEsG_stateScreen",
|
|
2354
2363
|
"emptyTitle": "_5KEEsG_emptyTitle",
|
|
2364
|
+
"stateScreen": "_5KEEsG_stateScreen",
|
|
2365
|
+
"progressEntry": "_5KEEsG_progressEntry",
|
|
2366
|
+
"contextHint": "_5KEEsG_contextHint",
|
|
2367
|
+
"contextChevron": "_5KEEsG_contextChevron",
|
|
2355
2368
|
"historyToggle": "_5KEEsG_historyToggle",
|
|
2356
|
-
"
|
|
2369
|
+
"headerSide": "_5KEEsG_headerSide",
|
|
2370
|
+
"visibilitySetting": "_5KEEsG_visibilitySetting"
|
|
2357
2371
|
};
|
|
2358
2372
|
//#endregion
|
|
2359
2373
|
//#region src/client/sidechat/NativeConversation.tsx
|
|
@@ -2397,6 +2411,7 @@ window.__ModuleLoader__.load({
|
|
|
2397
2411
|
]
|
|
2398
2412
|
}), expanded && side.progress && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2399
2413
|
className: sidechat_module_css_default.progressEntries,
|
|
2414
|
+
"data-sidenote-context-progress": true,
|
|
2400
2415
|
children: side.progress.entries.map((entry, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2401
2416
|
className: sidechat_module_css_default.progressEntry,
|
|
2402
2417
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -2492,6 +2507,8 @@ window.__ModuleLoader__.load({
|
|
|
2492
2507
|
});
|
|
2493
2508
|
}
|
|
2494
2509
|
function HistoryView({ Original, props, side }) {
|
|
2510
|
+
const root = (0, react.useRef)(null);
|
|
2511
|
+
const wasExpanded = (0, react.useRef)(side.contextExpanded);
|
|
2495
2512
|
const nodes = props.useChat((state) => state.nodes);
|
|
2496
2513
|
const snapshot = props.useConversation((state) => state);
|
|
2497
2514
|
const groups = snapshot.views.grouped("chat");
|
|
@@ -2517,9 +2534,31 @@ window.__ModuleLoader__.load({
|
|
|
2517
2534
|
}
|
|
2518
2535
|
};
|
|
2519
2536
|
const useProjected = (0, react.useCallback)((selector) => selector(projected), [projected]);
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
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
|
+
})
|
|
2523
2562
|
});
|
|
2524
2563
|
}
|
|
2525
2564
|
/** Both admitted hosts publish the native chat View; only 0.2.1 adds a Flow slot. */
|
|
@@ -5553,34 +5592,34 @@ ${entries.map((entry) => {
|
|
|
5553
5592
|
document.head.appendChild(tag);
|
|
5554
5593
|
}
|
|
5555
5594
|
var annotate_module_css_default = {
|
|
5556
|
-
"dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
|
|
5557
5595
|
"deleteButton": "_SVViG_deleteButton",
|
|
5558
|
-
"editorEdit": "_SVViG_editorEdit",
|
|
5559
|
-
"hintClose": "_SVViG_hintClose",
|
|
5560
|
-
"editorTarget": "_SVViG_editorTarget",
|
|
5561
|
-
"sentChipRow": "_SVViG_sentChipRow",
|
|
5562
|
-
"editorInput": "_SVViG_editorInput",
|
|
5563
|
-
"toolbarButton": "_SVViG_toolbarButton",
|
|
5564
|
-
"highlight": "_SVViG_highlight",
|
|
5565
|
-
"sentChip": "_SVViG_sentChip",
|
|
5566
|
-
"editorTextarea": "_SVViG_editorTextarea",
|
|
5567
|
-
"editorNew": "_SVViG_editorNew",
|
|
5568
|
-
"toolbarBelow": "_SVViG_toolbarBelow",
|
|
5569
5596
|
"badgeSent": "_SVViG_badgeSent",
|
|
5597
|
+
"editorSpacer": "_SVViG_editorSpacer",
|
|
5598
|
+
"saveButton": "_SVViG_saveButton",
|
|
5570
5599
|
"toolbar": "_SVViG_toolbar",
|
|
5600
|
+
"sentChip": "_SVViG_sentChip",
|
|
5571
5601
|
"editorFooter": "_SVViG_editorFooter",
|
|
5572
|
-
"editorAsk": "_SVViG_editorAsk",
|
|
5573
|
-
"confirmButton": "_SVViG_confirmButton",
|
|
5574
5602
|
"hint": "_SVViG_hint",
|
|
5575
|
-
"
|
|
5576
|
-
"
|
|
5603
|
+
"dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
|
|
5604
|
+
"confirmButton": "_SVViG_confirmButton",
|
|
5605
|
+
"toolbarBelow": "_SVViG_toolbarBelow",
|
|
5606
|
+
"editorInput": "_SVViG_editorInput",
|
|
5577
5607
|
"sentCardNote": "_SVViG_sentCardNote",
|
|
5578
|
-
"
|
|
5579
|
-
"
|
|
5580
|
-
"
|
|
5608
|
+
"toolbarButton": "_SVViG_toolbarButton",
|
|
5609
|
+
"sentCardQuote": "_SVViG_sentCardQuote",
|
|
5610
|
+
"hintClose": "_SVViG_hintClose",
|
|
5611
|
+
"badge": "_SVViG_badge",
|
|
5581
5612
|
"editorError": "_SVViG_editorError",
|
|
5613
|
+
"editorTarget": "_SVViG_editorTarget",
|
|
5614
|
+
"highlight": "_SVViG_highlight",
|
|
5582
5615
|
"sentCardTitle": "_SVViG_sentCardTitle",
|
|
5583
|
-
"
|
|
5616
|
+
"sentChipRow": "_SVViG_sentChipRow",
|
|
5617
|
+
"editorAskRow": "_SVViG_editorAskRow",
|
|
5618
|
+
"editorNew": "_SVViG_editorNew",
|
|
5619
|
+
"editorEdit": "_SVViG_editorEdit",
|
|
5620
|
+
"editorAsk": "_SVViG_editorAsk",
|
|
5621
|
+
"cancelButton": "_SVViG_cancelButton",
|
|
5622
|
+
"editorTextarea": "_SVViG_editorTextarea"
|
|
5584
5623
|
};
|
|
5585
5624
|
//#endregion
|
|
5586
5625
|
//#region src/client/annotate/hint.tsx
|
|
@@ -6393,33 +6432,33 @@ ${entries.map((entry) => {
|
|
|
6393
6432
|
document.head.appendChild(tag);
|
|
6394
6433
|
}
|
|
6395
6434
|
var annotations_module_css_default = {
|
|
6396
|
-
"
|
|
6397
|
-
"
|
|
6398
|
-
"
|
|
6399
|
-
"chipDismissShade": "SVnebq_chipDismissShade",
|
|
6400
|
-
"sentChipGroup": "SVnebq_sentChipGroup",
|
|
6401
|
-
"sentChip": "SVnebq_sentChip",
|
|
6402
|
-
"contextFooter": "SVnebq_contextFooter",
|
|
6403
|
-
"sentContext": "SVnebq_sentContext",
|
|
6404
|
-
"sentChevronOpen": "SVnebq_sentChevronOpen",
|
|
6405
|
-
"contextPanel": "SVnebq_contextPanel",
|
|
6406
|
-
"contextAppear": "SVnebq_contextAppear",
|
|
6407
|
-
"sentChevron": "SVnebq_sentChevron",
|
|
6435
|
+
"chipDismiss": "SVnebq_chipDismiss",
|
|
6436
|
+
"chipWrap": "SVnebq_chipWrap",
|
|
6437
|
+
"contextActions": "SVnebq_contextActions",
|
|
6408
6438
|
"contextEditor": "SVnebq_contextEditor",
|
|
6409
|
-
"contextNumber": "SVnebq_contextNumber",
|
|
6410
6439
|
"contextText": "SVnebq_contextText",
|
|
6411
|
-
"
|
|
6412
|
-
"
|
|
6440
|
+
"contextAppear": "SVnebq_contextAppear",
|
|
6441
|
+
"sentContext": "SVnebq_sentContext",
|
|
6442
|
+
"sentChip": "SVnebq_sentChip",
|
|
6443
|
+
"sentChevron": "SVnebq_sentChevron",
|
|
6444
|
+
"sentChevronOpen": "SVnebq_sentChevronOpen",
|
|
6445
|
+
"storageNotice": "SVnebq_storageNotice",
|
|
6413
6446
|
"sentPreview": "SVnebq_sentPreview",
|
|
6414
6447
|
"chipMain": "SVnebq_chipMain",
|
|
6415
|
-
"contextItem": "SVnebq_contextItem",
|
|
6416
|
-
"contextLabel": "SVnebq_contextLabel",
|
|
6417
|
-
"chipWrap": "SVnebq_chipWrap",
|
|
6418
|
-
"contextActions": "SVnebq_contextActions",
|
|
6419
|
-
"contextSource": "SVnebq_contextSource",
|
|
6420
6448
|
"chipClearAll": "SVnebq_chipClearAll",
|
|
6449
|
+
"userContextHost": "SVnebq_userContextHost",
|
|
6450
|
+
"sentChips": "SVnebq_sentChips",
|
|
6451
|
+
"contextNumber": "SVnebq_contextNumber",
|
|
6421
6452
|
"chip": "SVnebq_chip",
|
|
6422
|
-
"
|
|
6453
|
+
"chipDismissShade": "SVnebq_chipDismissShade",
|
|
6454
|
+
"contextPanel": "SVnebq_contextPanel",
|
|
6455
|
+
"contextAction": "SVnebq_contextAction",
|
|
6456
|
+
"contextItem": "SVnebq_contextItem",
|
|
6457
|
+
"contextFooter": "SVnebq_contextFooter",
|
|
6458
|
+
"sentChipGroup": "SVnebq_sentChipGroup",
|
|
6459
|
+
"contextSource": "SVnebq_contextSource",
|
|
6460
|
+
"contextContent": "SVnebq_contextContent",
|
|
6461
|
+
"contextLabel": "SVnebq_contextLabel",
|
|
6423
6462
|
"contextList": "SVnebq_contextList"
|
|
6424
6463
|
};
|
|
6425
6464
|
//#endregion
|
|
@@ -21,7 +21,8 @@ export interface ChatMessage {
|
|
|
21
21
|
/** 被打断冻结的 assistant 输出(渲染「已停止」标记)。 */
|
|
22
22
|
interrupted?: boolean;
|
|
23
23
|
}
|
|
24
|
-
/**
|
|
24
|
+
/** Protocol readers need actual text, not attachment preview labels before it.
|
|
25
|
+
* Native Conversation renders images; only an image-only message needs a label. */
|
|
25
26
|
export declare function contentTextOf(content: unknown): string;
|
|
26
27
|
/**
|
|
27
28
|
* 单个 ConversationNode → ChatMessage;返回 null = 面板不渲染
|
|
@@ -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
|
+
"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",
|
|
@@ -31,17 +31,19 @@ interface LooseBlock {
|
|
|
31
31
|
name?: unknown
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/**
|
|
34
|
+
/** Protocol readers need actual text, not attachment preview labels before it.
|
|
35
|
+
* Native Conversation renders images; only an image-only message needs a label. */
|
|
35
36
|
export function contentTextOf(content: unknown): string {
|
|
36
37
|
if (!Array.isArray(content)) return ''
|
|
37
38
|
const parts: string[] = []
|
|
39
|
+
let hasImage = false
|
|
38
40
|
for (const block of content) {
|
|
39
41
|
if (typeof block !== 'object' || block === null) continue
|
|
40
42
|
const b = block as LooseBlock
|
|
41
43
|
if (b.type === 'text' && typeof b.text === 'string') parts.push(b.text)
|
|
42
|
-
else if (b.type === 'image')
|
|
44
|
+
else if (b.type === 'image') hasImage = true
|
|
43
45
|
}
|
|
44
|
-
return parts.join('\n')
|
|
46
|
+
return parts.join('\n') || (hasImage ? t('imagePlaceholder') : '')
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
function seqKey(prefix: string, node: Record<string, unknown>): string {
|
package/src/client/host/dom.ts
CHANGED
|
@@ -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
|
-
|
|
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; }
|