dsh-focus-overlay 0.3.0 → 0.4.0
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.en.md +10 -9
- package/README.md +10 -9
- package/lib/client.js +178 -65
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -57,7 +57,7 @@ Once in focus mode, an assistant turn no longer shows every step — it folds in
|
|
|
57
57
|
| Back to latest | A centered "↓ back to latest" floating button appears when you scroll away from the bottom |
|
|
58
58
|
| File mentions | Wires `chatFileMentions`, turning inline-code tokens that name real files into clickable links |
|
|
59
59
|
| i18n | Chinese / English copy, follows the UI language |
|
|
60
|
-
|
|
|
60
|
+
| Plugin configuration card | A collapsible card under "Settings → Plugins → Plugin configuration": navbar toggle, open-position strategy, text-area width — persisted to `localStorage` |
|
|
61
61
|
| Auto-enter focus | After a reply **completes normally**, auto-open focus at your question; abnormal endings (stop / error / max-tokens / interrupt) never fire |
|
|
62
62
|
| Reply / waiting reminders | In focus, a "New reply ready + View" toast on completion; an "AI is waiting for your reply + Reply" toast on a question/approval, auto-cleared once answered |
|
|
63
63
|
|
|
@@ -72,6 +72,14 @@ dsh plugin --profile web add dsh-focus-overlay
|
|
|
72
72
|
dsh web
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
> If `add` doesn't install the latest version, pnpm's `minimumReleaseAge` (minimum release age) security policy is at work: by default it won't resolve a version as `latest` within **24 hours** of publication, falling back to the previous stable release. To install the latest immediately, pin the version explicitly:
|
|
76
|
+
>
|
|
77
|
+
> ```sh
|
|
78
|
+
> dsh plugin --profile web add dsh-focus-overlay@<version>
|
|
79
|
+
> ```
|
|
80
|
+
>
|
|
81
|
+
> Or wait 24 hours, then re-run the plain `add` command.
|
|
82
|
+
|
|
75
83
|
**From GitHub**
|
|
76
84
|
|
|
77
85
|
```sh
|
|
@@ -88,13 +96,6 @@ dsh web
|
|
|
88
96
|
>
|
|
89
97
|
> Then re-run `add`. **This authorization lets the package's code run on your machine at install time** — only grant it to sources you trust, and pin the commit (`github:boogoo619/dsh-focus-overlay#<sha>`).
|
|
90
98
|
|
|
91
|
-
**Local directory / tarball**
|
|
92
|
-
|
|
93
|
-
```sh
|
|
94
|
-
dsh plugin --profile web add ./dsh-focus-overlay # local directory
|
|
95
|
-
dsh plugin --profile web add ./dsh-focus-overlay-0.1.0.tgz # tarball
|
|
96
|
-
```
|
|
97
|
-
|
|
98
99
|
Restart `dsh web` after install.
|
|
99
100
|
|
|
100
101
|
## Usage
|
|
@@ -106,7 +107,7 @@ Restart `dsh web` after install.
|
|
|
106
107
|
|
|
107
108
|
## Settings
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
In the sidebar "Settings → Plugins → Plugin configuration", expand the "Focus Mode" card:
|
|
110
111
|
|
|
111
112
|
| Option | Description |
|
|
112
113
|
| --- | --- |
|
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
| 回到最新 | 离开底部时显示居中的「↓ 回到最新」悬浮按钮 |
|
|
56
56
|
| 文件提及 | 接 `chatFileMentions`,内联代码命中真实文件时变成可点链接 |
|
|
57
57
|
| i18n | 中 / 英文案,跟随界面语言 |
|
|
58
|
-
|
|
|
58
|
+
| 插件配置卡片 | 在「设置 → 插件 → 插件配置」中的可折叠卡片,含导航条开关、打开定位策略、文字区宽度,持久化到 `localStorage` |
|
|
59
59
|
| 自动进入专注 | 回复**正常完成**后自动打开专注并定位到本轮你的提问;异常结束(停止 / 报错 / 超 token / 打断)不触发 |
|
|
60
60
|
| 回复 / 等待提醒 | 专注中回复完成弹「新回复已生成 + 查看」;AI 提问 / 审批等待时弹「AI 正在等待你的回复 + 去回复」,回答完自动消失 |
|
|
61
61
|
|
|
@@ -70,6 +70,14 @@ dsh plugin --profile web add dsh-focus-overlay
|
|
|
70
70
|
dsh web
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
> 如果 `add` 装到的不是最新版本:这是 pnpm 的 `minimumReleaseAge`(最小发布年龄)安全机制在起作用——默认 **24 小时**内不会把刚发布的版本当作 `latest` 解析,而是回退到上一个稳定版。想立即装最新版,显式指定版本号即可:
|
|
74
|
+
>
|
|
75
|
+
> ```sh
|
|
76
|
+
> dsh plugin --profile web add dsh-focus-overlay@<版本号>
|
|
77
|
+
> ```
|
|
78
|
+
>
|
|
79
|
+
> 或者等满 24 小时,再执行不带版本号的 `add` 命令。
|
|
80
|
+
|
|
73
81
|
**从 GitHub**
|
|
74
82
|
|
|
75
83
|
```sh
|
|
@@ -86,13 +94,6 @@ dsh web
|
|
|
86
94
|
>
|
|
87
95
|
> 然后重新执行 `add`。**该授权允许本包代码在安装时于你的机器上执行**——请只对可信来源授权,并锁定 commit(`github:boogoo619/dsh-focus-overlay#<sha>`)。
|
|
88
96
|
|
|
89
|
-
**本地目录 / tarball**
|
|
90
|
-
|
|
91
|
-
```sh
|
|
92
|
-
dsh plugin --profile web add ./dsh-focus-overlay # 本地目录
|
|
93
|
-
dsh plugin --profile web add ./dsh-focus-overlay-0.1.0.tgz # tarball
|
|
94
|
-
```
|
|
95
|
-
|
|
96
97
|
安装后重启 `dsh web` 生效。
|
|
97
98
|
|
|
98
99
|
## 使用
|
|
@@ -104,7 +105,7 @@ dsh plugin --profile web add ./dsh-focus-overlay-0.1.0.tgz # tarball
|
|
|
104
105
|
|
|
105
106
|
## 设置
|
|
106
107
|
|
|
107
|
-
侧栏「设置 →
|
|
108
|
+
侧栏「设置 → 插件 → 插件配置」中,展开「专注模式」卡片:
|
|
108
109
|
|
|
109
110
|
| 选项 | 说明 |
|
|
110
111
|
| --- | --- |
|
package/lib/client.js
CHANGED
|
@@ -264,15 +264,26 @@ window.__ModuleLoader__.load({
|
|
|
264
264
|
return -1;
|
|
265
265
|
}
|
|
266
266
|
/**
|
|
267
|
-
* Decide whether
|
|
268
|
-
*
|
|
267
|
+
* Decide whether a settled conversation snapshot (`running === false`) ends in
|
|
268
|
+
* a *normal* completed reply, and which user message started it.
|
|
269
269
|
*
|
|
270
|
-
* The caller owns the `running true → false` edge
|
|
271
|
-
*
|
|
272
|
-
* turn
|
|
273
|
-
*
|
|
270
|
+
* The caller owns the `running true → false` edge. `running` is relayed on a
|
|
271
|
+
* different stream than the conversation events, so the edge can fire before an
|
|
272
|
+
* aborted turn's `interrupted` node has landed — at that instant the tail is
|
|
273
|
+
* still held in `partial` and `nodes` is missing the final assistant (a
|
|
274
|
+
* finalized question message can then be mistaken for the tail). To close that
|
|
275
|
+
* race we require the snapshot to be *stable* first: while `partial` is non-null
|
|
276
|
+
* we return `settled: false` and the caller must re-evaluate on a later
|
|
277
|
+
* snapshot. Once stable, this walks backwards to the first turn-terminating node
|
|
278
|
+
* (`assistant` / `turn-error` / `turn-max-tokens`) and accepts only a finalized,
|
|
279
|
+
* non-interrupted assistant node.
|
|
274
280
|
*/
|
|
275
281
|
function detectSettledCompletion(snap) {
|
|
282
|
+
if (snap && snap.partial != null) return {
|
|
283
|
+
settled: false,
|
|
284
|
+
completed: false,
|
|
285
|
+
anchorSeq: null
|
|
286
|
+
};
|
|
276
287
|
const nodes = snap && snap.nodes ? snap.nodes : [];
|
|
277
288
|
let terminal = null;
|
|
278
289
|
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
@@ -284,10 +295,12 @@ window.__ModuleLoader__.load({
|
|
|
284
295
|
}
|
|
285
296
|
}
|
|
286
297
|
if (!terminal || terminal.kind !== "assistant" || terminal.interrupted) return {
|
|
298
|
+
settled: true,
|
|
287
299
|
completed: false,
|
|
288
300
|
anchorSeq: null
|
|
289
301
|
};
|
|
290
302
|
return {
|
|
303
|
+
settled: true,
|
|
291
304
|
completed: true,
|
|
292
305
|
anchorSeq: lastUserSeq(nodes)
|
|
293
306
|
};
|
|
@@ -739,51 +752,112 @@ window.__ModuleLoader__.load({
|
|
|
739
752
|
children: t("toggle")
|
|
740
753
|
});
|
|
741
754
|
}
|
|
742
|
-
function
|
|
755
|
+
function FocusSettingsCard({ t }) {
|
|
743
756
|
const prefs = usePrefs();
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
})
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
757
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
758
|
+
const title = t("settings.label");
|
|
759
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
760
|
+
className: "fm-plugin-card" + (open ? " fm-plugin-card-open" : ""),
|
|
761
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
762
|
+
type: "button",
|
|
763
|
+
className: "fm-plugin-card-header",
|
|
764
|
+
"aria-expanded": open,
|
|
765
|
+
"aria-label": `${t(open ? "settings.collapse" : "settings.expand")}: ${title}`,
|
|
766
|
+
onClick: () => setOpen(!open),
|
|
767
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
768
|
+
className: "fm-plugin-card-headtext",
|
|
769
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
770
|
+
className: "fm-plugin-card-name",
|
|
771
|
+
children: title
|
|
772
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
773
|
+
className: "fm-plugin-card-desc",
|
|
774
|
+
children: t("settings.description")
|
|
775
|
+
})]
|
|
776
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "fm-plugin-card-chevron" + (open ? " fm-plugin-card-chevron-open" : "") })]
|
|
777
|
+
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
778
|
+
className: "fm-plugin-card-body",
|
|
779
|
+
children: [
|
|
780
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
781
|
+
className: "fm-plugin-field",
|
|
782
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
783
|
+
className: "fm-plugin-check",
|
|
784
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
785
|
+
type: "checkbox",
|
|
786
|
+
checked: prefs.navbar,
|
|
787
|
+
onChange: (e) => prefsStore.update({ navbar: e.target.checked })
|
|
788
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
789
|
+
className: "fm-plugin-check-label",
|
|
790
|
+
children: t("settings.navbar")
|
|
791
|
+
})]
|
|
792
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
793
|
+
className: "fm-plugin-field-hint",
|
|
794
|
+
children: t("settings.navbar.hint")
|
|
795
|
+
})]
|
|
796
|
+
}),
|
|
797
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
798
|
+
className: "fm-plugin-field",
|
|
799
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
800
|
+
className: "fm-plugin-check",
|
|
801
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
802
|
+
type: "checkbox",
|
|
803
|
+
checked: prefs.scroll === "preserve",
|
|
804
|
+
onChange: (e) => prefsStore.update({ scroll: e.target.checked ? "preserve" : "bottom" })
|
|
805
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
806
|
+
className: "fm-plugin-check-label",
|
|
807
|
+
children: t("settings.scrollPreserve")
|
|
808
|
+
})]
|
|
809
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
810
|
+
className: "fm-plugin-field-hint",
|
|
811
|
+
children: t("settings.scrollPreserve.hint")
|
|
812
|
+
})]
|
|
813
|
+
}),
|
|
814
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
815
|
+
className: "fm-plugin-field",
|
|
816
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
817
|
+
className: "fm-plugin-check",
|
|
818
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
819
|
+
type: "checkbox",
|
|
820
|
+
checked: prefs.autoFocus,
|
|
821
|
+
onChange: (e) => prefsStore.update({ autoFocus: e.target.checked })
|
|
822
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
823
|
+
className: "fm-plugin-check-label",
|
|
824
|
+
children: t("settings.autoFocus")
|
|
825
|
+
})]
|
|
826
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
827
|
+
className: "fm-plugin-field-hint",
|
|
828
|
+
children: t("settings.autoFocus.hint")
|
|
829
|
+
})]
|
|
830
|
+
}),
|
|
831
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
832
|
+
className: "fm-plugin-field",
|
|
833
|
+
children: [
|
|
834
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
835
|
+
className: "fm-plugin-field-head",
|
|
836
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
837
|
+
className: "fm-plugin-field-label",
|
|
838
|
+
children: t("settings.width")
|
|
839
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
840
|
+
className: "fm-plugin-field-value",
|
|
841
|
+
children: [prefs.width, "px"]
|
|
842
|
+
})]
|
|
843
|
+
}),
|
|
844
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
845
|
+
type: "range",
|
|
846
|
+
className: "fm-plugin-range",
|
|
847
|
+
min: 480,
|
|
848
|
+
max: 1200,
|
|
849
|
+
step: 40,
|
|
850
|
+
value: prefs.width,
|
|
851
|
+
onChange: (e) => prefsStore.update({ width: Number(e.target.value) })
|
|
852
|
+
}),
|
|
853
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
854
|
+
className: "fm-plugin-field-hint",
|
|
855
|
+
children: t("settings.width.hint")
|
|
856
|
+
})
|
|
857
|
+
]
|
|
858
|
+
})
|
|
859
|
+
]
|
|
860
|
+
}) : null]
|
|
787
861
|
});
|
|
788
862
|
}
|
|
789
863
|
//#endregion
|
|
@@ -820,10 +894,27 @@ window.__ModuleLoader__.load({
|
|
|
820
894
|
.fm-reply-toast{position:absolute;left:50%;transform:translateX(-50%);bottom:64px;z-index:2;display:flex;align-items:center;gap:10px;background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1a1a1a);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:100px;padding:8px 8px 8px 16px;box-shadow:0 4px 16px rgba(0,0,0,.12);font-size:13px}
|
|
821
895
|
.fm-reply-toast-dot{width:8px;height:8px;border-radius:999px;background:var(--dsw-static-deepseek-500,#4176e6);flex:0 0 auto}
|
|
822
896
|
.fm-reply-toast-text{white-space:nowrap}
|
|
823
|
-
.fm-
|
|
824
|
-
.fm-
|
|
825
|
-
.fm-
|
|
826
|
-
.fm-
|
|
897
|
+
.fm-plugin-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
|
|
898
|
+
.fm-plugin-card:hover{border-color:var(--dsw-alias-label-dimmed)}
|
|
899
|
+
.fm-plugin-card-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
|
|
900
|
+
.fm-plugin-card-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
|
|
901
|
+
.fm-plugin-card-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
902
|
+
.fm-plugin-card-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
|
|
903
|
+
.fm-plugin-card-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
|
|
904
|
+
.fm-plugin-card-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
|
|
905
|
+
.fm-plugin-card-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}
|
|
906
|
+
.fm-plugin-card-chevron-open{transform:rotate(180deg)}
|
|
907
|
+
.fm-plugin-card-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:12px}
|
|
908
|
+
.fm-plugin-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}
|
|
909
|
+
.fm-plugin-field+.fm-plugin-field{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
910
|
+
.fm-plugin-field-head{align-items:center;gap:8px;display:flex}
|
|
911
|
+
.fm-plugin-field-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
912
|
+
.fm-plugin-field-value{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5}
|
|
913
|
+
.fm-plugin-field-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
914
|
+
.fm-plugin-check{align-items:center;gap:8px;display:flex;cursor:pointer;user-select:none}
|
|
915
|
+
.fm-plugin-check input[type="checkbox"]{width:16px;height:16px;margin:0;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#4176e6)}
|
|
916
|
+
.fm-plugin-check-label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}
|
|
917
|
+
.fm-plugin-range{width:100%;max-width:280px;cursor:pointer;accent-color:var(--dsw-alias-brand-primary,#4176e6)}
|
|
827
918
|
`;
|
|
828
919
|
//#endregion
|
|
829
920
|
//#region src/client/locales.ts
|
|
@@ -834,10 +925,17 @@ window.__ModuleLoader__.load({
|
|
|
834
925
|
"exit": "退出专注 (Esc)",
|
|
835
926
|
"backToLatest": "回到最新",
|
|
836
927
|
"settings.label": "专注模式",
|
|
928
|
+
"settings.description": "全屏阅读遮罩的偏好设置",
|
|
929
|
+
"settings.expand": "展开",
|
|
930
|
+
"settings.collapse": "收起",
|
|
837
931
|
"settings.navbar": "显示右侧导航条",
|
|
932
|
+
"settings.navbar.hint": "在右侧显示用户消息导航圆点",
|
|
838
933
|
"settings.scrollPreserve": "打开时保留聊天位置",
|
|
934
|
+
"settings.scrollPreserve.hint": "打开时定位到当前阅读位置;关闭则定位到最新",
|
|
839
935
|
"settings.autoFocus": "回复结束后自动进入专注模式",
|
|
936
|
+
"settings.autoFocus.hint": "回复正常完成后自动打开;AI 提问 / 审批等待时弹出提醒",
|
|
840
937
|
"settings.width": "文字区宽度",
|
|
938
|
+
"settings.width.hint": "专注模式阅读列宽度(480–1200px)",
|
|
841
939
|
"empty": "暂无打开的会话",
|
|
842
940
|
"loading": "正在读取会话…",
|
|
843
941
|
"running": "· 正在工作 …",
|
|
@@ -867,10 +965,17 @@ window.__ModuleLoader__.load({
|
|
|
867
965
|
"exit": "Exit focus (Esc)",
|
|
868
966
|
"backToLatest": "Back to latest",
|
|
869
967
|
"settings.label": "Focus Mode",
|
|
968
|
+
"settings.description": "Preferences for the full-screen reading overlay",
|
|
969
|
+
"settings.expand": "Expand",
|
|
970
|
+
"settings.collapse": "Collapse",
|
|
870
971
|
"settings.navbar": "Show right-side navbar",
|
|
972
|
+
"settings.navbar.hint": "Show navigation dots for user messages on the right",
|
|
871
973
|
"settings.scrollPreserve": "Preserve chat position on open",
|
|
974
|
+
"settings.scrollPreserve.hint": "Open at your current reading position; off opens at the latest",
|
|
872
975
|
"settings.autoFocus": "Auto-enter focus mode when a reply completes",
|
|
976
|
+
"settings.autoFocus.hint": "Auto-open after a normal completion; toast while AI waits for you",
|
|
873
977
|
"settings.width": "Text width",
|
|
978
|
+
"settings.width.hint": "Reading column width (480–1200px)",
|
|
874
979
|
"empty": "No open session",
|
|
875
980
|
"loading": "Reading session…",
|
|
876
981
|
"running": "· Working …",
|
|
@@ -899,7 +1004,8 @@ window.__ModuleLoader__.load({
|
|
|
899
1004
|
/**
|
|
900
1005
|
* Focus Mode, browser half: a full-screen reading overlay in `shell.overlay`
|
|
901
1006
|
* (additive, never replaces shipped UI), a "专注" button in the session header
|
|
902
|
-
* action row, and a
|
|
1007
|
+
* action row, and a collapsible plugin card in the Plugins settings tab
|
|
1008
|
+
* (`settings.plugin.item`). Rendering reuses the official
|
|
903
1009
|
* `@deepseek-ai/dsh-client-ui-primitives` Markdown/MessageText/Tooltip/Button
|
|
904
1010
|
* components and the conversation service's image resolver.
|
|
905
1011
|
*/
|
|
@@ -936,12 +1042,14 @@ window.__ModuleLoader__.load({
|
|
|
936
1042
|
let currentId = void 0;
|
|
937
1043
|
let unsubSession = null;
|
|
938
1044
|
let prevRunning = false;
|
|
1045
|
+
let pendingSettle = false;
|
|
939
1046
|
const watch = (id) => {
|
|
940
1047
|
if (unsubSession) {
|
|
941
1048
|
unsubSession();
|
|
942
1049
|
unsubSession = null;
|
|
943
1050
|
}
|
|
944
1051
|
prevRunning = false;
|
|
1052
|
+
pendingSettle = false;
|
|
945
1053
|
if (id == null) return;
|
|
946
1054
|
const binding = sessions.binding(id);
|
|
947
1055
|
if (!binding) return;
|
|
@@ -950,12 +1058,17 @@ window.__ModuleLoader__.load({
|
|
|
950
1058
|
const snap = face.getSnapshot();
|
|
951
1059
|
if (!snap) return;
|
|
952
1060
|
const running = !!snap.running;
|
|
953
|
-
if (prevRunning && !running)
|
|
1061
|
+
if (prevRunning && !running) pendingSettle = true;
|
|
1062
|
+
if (running) pendingSettle = false;
|
|
1063
|
+
if (pendingSettle && !running) {
|
|
954
1064
|
const outcome = detectSettledCompletion(snap);
|
|
955
|
-
if (outcome.
|
|
956
|
-
|
|
957
|
-
focusStore.
|
|
958
|
-
|
|
1065
|
+
if (outcome.settled) {
|
|
1066
|
+
pendingSettle = false;
|
|
1067
|
+
if (outcome.completed && prefsStore.get().autoFocus) if (focusStore.get()) focusStore.notifyDone();
|
|
1068
|
+
else {
|
|
1069
|
+
focusStore.setAutoAnchor(outcome.anchorSeq);
|
|
1070
|
+
focusStore.set(true);
|
|
1071
|
+
}
|
|
959
1072
|
}
|
|
960
1073
|
}
|
|
961
1074
|
prevRunning = running;
|
|
@@ -996,12 +1109,12 @@ window.__ModuleLoader__.load({
|
|
|
996
1109
|
order: 1e3,
|
|
997
1110
|
label: () => t("toggle")
|
|
998
1111
|
}, () => (0, react.createElement)(FocusToggle, { t })));
|
|
999
|
-
ctx.slots.inject("settings.
|
|
1000
|
-
name: "settings.
|
|
1001
|
-
id: "focus-
|
|
1002
|
-
order:
|
|
1112
|
+
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
1113
|
+
name: "settings.plugin.item",
|
|
1114
|
+
id: "dsh-focus-overlay",
|
|
1115
|
+
order: 1e3,
|
|
1003
1116
|
label: () => t("settings.label")
|
|
1004
|
-
}, () => (0, react.createElement)(
|
|
1117
|
+
}, () => (0, react.createElement)(FocusSettingsCard, { t })));
|
|
1005
1118
|
}
|
|
1006
1119
|
};
|
|
1007
1120
|
return module.exports;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-focus-overlay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Focus Mode for the dsh web GUI: a full-screen reading overlay that hides chrome and folds the AI tool-call flow into summaries, reusing the official Markdown / image primitives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.mjs",
|