dsh-smooth-stream 0.1.0 → 0.3.1
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 +103 -0
- package/README.md +62 -47
- package/lib/client.js +545 -44
- package/lib/client.js.map +1 -1
- package/lib/index.js +229 -0
- package/package.json +25 -3
- package/src/client/SmoothStreamCard.module.css +253 -0
- package/src/client/SmoothStreamCard.tsx +122 -0
- package/src/client/TypewriterAssistantNodeView.tsx +11 -3
- package/src/client/index.ts +96 -3
- package/src/client/locales.ts +74 -0
- package/src/client/smooth-stream-card-controller.ts +162 -0
- package/src/client/smooth-stream-settings-api.ts +65 -0
- package/src/package-meta.ts +39 -0
- package/src/plugin.ts +101 -0
- package/src/profile-installation.ts +121 -0
- package/src/settings-api.ts +33 -0
- package/src/settings.ts +30 -0
- package/README.zh.md +0 -88
package/lib/client.js
CHANGED
|
@@ -8,41 +8,42 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
9
|
let _deepseek_ai_dsh_client_ui_attachment = require("@deepseek-ai/dsh-client-ui-attachment");
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
|
+
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
11
12
|
//#region \0dsh-css:/Users/dzlin/work/project/dsh-smooth-stream/src/client/TypewriterAssistantNodeView.module.css.mjs
|
|
12
|
-
const css = ".I17U7q_root{min-width:0;color:var(--dsw-alias-label-primary);flex-direction:column;font-size:16px;line-height:28px;display:flex}.I17U7q_body{flex-direction:column;gap:16px;min-width:0;display:flex}.I17U7q_think{flex-direction:column;display:flex}.I17U7q_thinkRow{position:relative;overflow:hidden}.I17U7q_think[data-state=running] .I17U7q_thinkRow:after{content:\"\";inset-block:0;background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite I17U7q_dsh-smooth-stream-think-sweep;position:absolute;left:0}@keyframes I17U7q_dsh-smooth-stream-think-sweep{0%{left:-300px}90%,to{left:100%}}.I17U7q_thinkLeading{flex-shrink:0}.I17U7q_thinkChevron{color:var(--dsw-alias-label-secondary)}.I17U7q_thinkTitle{font-weight:400}.I17U7q_thinkSeparator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.I17U7q_thinkSummary{min-width:0;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:14px;line-height:24px;overflow:hidden}.I17U7q_thinkSummary[data-follow-end]{text-overflow:clip}.I17U7q_thinkBody{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;word-break:break-word;padding:4px 0 4px 22px;font-size:14px;line-height:24px}.I17U7q_disclosureRoot{flex-direction:column;width:100%;min-width:0;display:flex}.I17U7q_disclosureRow{cursor:pointer;align-items:center;min-width:0;height:24px;display:flex;position:relative;overflow:hidden}.I17U7q_disclosureLeading{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;margin-right:6px;display:inline-flex;position:relative}.I17U7q_disclosureIconIdle{opacity:1;transition:opacity .1s;display:inline-flex}.I17U7q_disclosureChevronHover{opacity:0;margin:auto;transition:opacity .1s;position:absolute;inset:0}.I17U7q_disclosureRow:hover .I17U7q_disclosureIconIdle{opacity:0}.I17U7q_disclosureRow:hover .I17U7q_disclosureChevronHover{opacity:1}.I17U7q_disclosureTitle{color:var(--dsw-alias-label-secondary);flex:none;font-size:14px;line-height:24px}.I17U7q_disclosureContent{visibility:visible;transition:grid-template-rows var(--ds-transition-duration,.2s) var(--ds-ease-in-out,cubic-bezier(.4, 0, .2, 1)), visibility 0s;grid-template-rows:1fr;display:grid}.I17U7q_disclosureContent[data-collapsed]{visibility:hidden;transition:grid-template-rows var(--ds-transition-duration,.2s) var(--ds-ease-in-out,cubic-bezier(.4, 0, .2, 1)), visibility 0s var(--ds-transition-duration,.2s);grid-template-rows:0fr}.I17U7q_disclosureContent>*{min-height:0;overflow:hidden}@media (prefers-reduced-motion:reduce){.I17U7q_think[data-state=running] .I17U7q_thinkRow:after{animation:none}.I17U7q_disclosureContent,.I17U7q_disclosureContent[data-collapsed]{transition:none}}.I17U7q_stopped{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);border-radius:6px;align-self:flex-start;padding:0 6px;font-size:11px;line-height:18px}.I17U7q_visuallyHidden{clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.I17U7q_follow{min-width:0}@supports (text-box-trim:trim-both){.I17U7q_root :is(p,h1,h2,h3,h4,h5,h6,li,blockquote){text-box-trim:trim-both;text-box-edge:text}}";
|
|
13
|
-
const tagId = "dsh-smooth-stream/TypewriterAssistantNodeView.module.css";
|
|
13
|
+
const css$1 = ".I17U7q_root{min-width:0;color:var(--dsw-alias-label-primary);flex-direction:column;font-size:16px;line-height:28px;display:flex}.I17U7q_body{flex-direction:column;gap:16px;min-width:0;display:flex}.I17U7q_think{flex-direction:column;display:flex}.I17U7q_thinkRow{position:relative;overflow:hidden}.I17U7q_think[data-state=running] .I17U7q_thinkRow:after{content:\"\";inset-block:0;background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite I17U7q_dsh-smooth-stream-think-sweep;position:absolute;left:0}@keyframes I17U7q_dsh-smooth-stream-think-sweep{0%{left:-300px}90%,to{left:100%}}.I17U7q_thinkLeading{flex-shrink:0}.I17U7q_thinkChevron{color:var(--dsw-alias-label-secondary)}.I17U7q_thinkTitle{font-weight:400}.I17U7q_thinkSeparator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.I17U7q_thinkSummary{min-width:0;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:14px;line-height:24px;overflow:hidden}.I17U7q_thinkSummary[data-follow-end]{text-overflow:clip}.I17U7q_thinkBody{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;word-break:break-word;padding:4px 0 4px 22px;font-size:14px;line-height:24px}.I17U7q_disclosureRoot{flex-direction:column;width:100%;min-width:0;display:flex}.I17U7q_disclosureRow{cursor:pointer;align-items:center;min-width:0;height:24px;display:flex;position:relative;overflow:hidden}.I17U7q_disclosureLeading{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;margin-right:6px;display:inline-flex;position:relative}.I17U7q_disclosureIconIdle{opacity:1;transition:opacity .1s;display:inline-flex}.I17U7q_disclosureChevronHover{opacity:0;margin:auto;transition:opacity .1s;position:absolute;inset:0}.I17U7q_disclosureRow:hover .I17U7q_disclosureIconIdle{opacity:0}.I17U7q_disclosureRow:hover .I17U7q_disclosureChevronHover{opacity:1}.I17U7q_disclosureTitle{color:var(--dsw-alias-label-secondary);flex:none;font-size:14px;line-height:24px}.I17U7q_disclosureContent{visibility:visible;transition:grid-template-rows var(--ds-transition-duration,.2s) var(--ds-ease-in-out,cubic-bezier(.4, 0, .2, 1)), visibility 0s;grid-template-rows:1fr;display:grid}.I17U7q_disclosureContent[data-collapsed]{visibility:hidden;transition:grid-template-rows var(--ds-transition-duration,.2s) var(--ds-ease-in-out,cubic-bezier(.4, 0, .2, 1)), visibility 0s var(--ds-transition-duration,.2s);grid-template-rows:0fr}.I17U7q_disclosureContent>*{min-height:0;overflow:hidden}@media (prefers-reduced-motion:reduce){.I17U7q_think[data-state=running] .I17U7q_thinkRow:after{animation:none}.I17U7q_disclosureContent,.I17U7q_disclosureContent[data-collapsed]{transition:none}}.I17U7q_stopped{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);border-radius:6px;align-self:flex-start;padding:0 6px;font-size:11px;line-height:18px}.I17U7q_visuallyHidden{clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.I17U7q_follow{min-width:0}@supports (text-box-trim:trim-both){.I17U7q_root :is(p,h1,h2,h3,h4,h5,h6,li,blockquote){text-box-trim:trim-both;text-box-edge:text}}";
|
|
14
|
+
const tagId$1 = "dsh-smooth-stream/TypewriterAssistantNodeView.module.css";
|
|
14
15
|
if (typeof document !== "undefined") {
|
|
15
|
-
let tag = document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]");
|
|
16
|
+
let tag = document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]");
|
|
16
17
|
if (tag === null) {
|
|
17
18
|
tag = document.createElement("style");
|
|
18
19
|
tag.dataset.plugin = "dsh-smooth-stream";
|
|
19
|
-
tag.dataset.pluginCss = tagId;
|
|
20
|
+
tag.dataset.pluginCss = tagId$1;
|
|
20
21
|
document.head.appendChild(tag);
|
|
21
22
|
}
|
|
22
|
-
tag.textContent = css;
|
|
23
|
+
tag.textContent = css$1;
|
|
23
24
|
}
|
|
24
25
|
var TypewriterAssistantNodeView_module_css_default = {
|
|
26
|
+
"root": "I17U7q_root",
|
|
27
|
+
"dsh-smooth-stream-think-sweep": "I17U7q_dsh-smooth-stream-think-sweep",
|
|
28
|
+
"disclosureLeading": "I17U7q_disclosureLeading",
|
|
29
|
+
"thinkBody": "I17U7q_thinkBody",
|
|
25
30
|
"disclosureChevronHover": "I17U7q_disclosureChevronHover",
|
|
26
|
-
"
|
|
31
|
+
"disclosureContent": "I17U7q_disclosureContent",
|
|
32
|
+
"stopped": "I17U7q_stopped",
|
|
33
|
+
"thinkTitle": "I17U7q_thinkTitle",
|
|
27
34
|
"thinkSeparator": "I17U7q_thinkSeparator",
|
|
35
|
+
"think": "I17U7q_think",
|
|
28
36
|
"disclosureTitle": "I17U7q_disclosureTitle",
|
|
29
|
-
"thinkSummary": "I17U7q_thinkSummary",
|
|
30
|
-
"dsh-smooth-stream-think-sweep": "I17U7q_dsh-smooth-stream-think-sweep",
|
|
31
37
|
"disclosureRoot": "I17U7q_disclosureRoot",
|
|
32
|
-
"disclosureIconIdle": "I17U7q_disclosureIconIdle",
|
|
33
|
-
"disclosureContent": "I17U7q_disclosureContent",
|
|
34
|
-
"thinkLeading": "I17U7q_thinkLeading",
|
|
35
|
-
"thinkRow": "I17U7q_thinkRow",
|
|
36
|
-
"thinkChevron": "I17U7q_thinkChevron",
|
|
37
|
-
"disclosureRow": "I17U7q_disclosureRow",
|
|
38
|
-
"disclosureLeading": "I17U7q_disclosureLeading",
|
|
39
|
-
"thinkTitle": "I17U7q_thinkTitle",
|
|
40
|
-
"stopped": "I17U7q_stopped",
|
|
41
|
-
"thinkBody": "I17U7q_thinkBody",
|
|
42
38
|
"visuallyHidden": "I17U7q_visuallyHidden",
|
|
43
|
-
"
|
|
39
|
+
"thinkChevron": "I17U7q_thinkChevron",
|
|
44
40
|
"follow": "I17U7q_follow",
|
|
45
|
-
"
|
|
41
|
+
"disclosureIconIdle": "I17U7q_disclosureIconIdle",
|
|
42
|
+
"body": "I17U7q_body",
|
|
43
|
+
"thinkSummary": "I17U7q_thinkSummary",
|
|
44
|
+
"disclosureRow": "I17U7q_disclosureRow",
|
|
45
|
+
"thinkLeading": "I17U7q_thinkLeading",
|
|
46
|
+
"thinkRow": "I17U7q_thinkRow"
|
|
46
47
|
};
|
|
47
48
|
//#endregion
|
|
48
49
|
//#region src/client/AnimatedDisclosure.tsx
|
|
@@ -201,8 +202,7 @@ window.__ModuleLoader__.load({
|
|
|
201
202
|
const SETTLE_DRAIN_MULTIPLIER = 1.8;
|
|
202
203
|
/** Pure per-frame reveal decision shared by the loop and its tests. */
|
|
203
204
|
function computeRevealStep(config, input, dtSeconds) {
|
|
204
|
-
const
|
|
205
|
-
const baseCps = clamp$1(trackedCps, config.minCps, config.maxFlushCps);
|
|
205
|
+
const baseCps = clamp$1(Math.max(input.emaCps, input.arrivalCpsEma), config.minCps, config.maxFlushCps);
|
|
206
206
|
const targetLagChars = input.inputActive ? Math.max(2, Math.round(baseCps * config.targetBufferMs / 1e3)) : 0;
|
|
207
207
|
let currentCps;
|
|
208
208
|
if (input.steadyCps !== void 0) currentCps = input.inputActive || input.settling ? clamp$1(input.steadyCps * (input.inputActive ? 1 : SETTLE_DRAIN_MULTIPLIER), config.minCps, config.maxFlushCps) : 0;
|
|
@@ -211,10 +211,7 @@ window.__ModuleLoader__.load({
|
|
|
211
211
|
const catchup = overflow > 0 ? overflow / CATCHUP_SECONDS : 0;
|
|
212
212
|
currentCps = clamp$1(baseCps * 1.08 + catchup, config.minCps, config.maxFlushCps);
|
|
213
213
|
} else if (input.settling) currentCps = computeSettleDrain(config, input);
|
|
214
|
-
else
|
|
215
|
-
const idleFlushCps = Math.max(config.flushCps, baseCps * 1.8, input.arrivalCpsEma * .8);
|
|
216
|
-
currentCps = clamp$1(idleFlushCps, config.flushCps, config.maxFlushCps);
|
|
217
|
-
}
|
|
214
|
+
else currentCps = clamp$1(Math.max(config.flushCps, baseCps * 1.8, input.arrivalCpsEma * .8), config.flushCps, config.maxFlushCps);
|
|
218
215
|
const minRevealChars = input.inputActive ? 1 : 2;
|
|
219
216
|
return {
|
|
220
217
|
revealChars: Math.max(minRevealChars, Math.round(currentCps * dtSeconds)),
|
|
@@ -388,11 +385,10 @@ window.__ModuleLoader__.load({
|
|
|
388
385
|
const deltaChars = targetCountRef.current - lastInputCountRef.current;
|
|
389
386
|
const deltaMs = Math.max(1, now - lastInputTsRef.current);
|
|
390
387
|
if (hadSample && deltaChars > 0) {
|
|
391
|
-
const
|
|
392
|
-
const normalizedInstantCps = clamp$1(instantCps, config.minCps, config.maxFlushCps * 3);
|
|
388
|
+
const normalizedInstantCps = clamp$1(deltaChars * 1e3 / deltaMs, config.minCps, config.maxFlushCps * 3);
|
|
393
389
|
const chunkEmaAlpha = .45;
|
|
394
|
-
chunkSizeEmaRef.current = chunkSizeEmaRef.current *
|
|
395
|
-
arrivalCpsEmaRef.current = arrivalCpsEmaRef.current *
|
|
390
|
+
chunkSizeEmaRef.current = chunkSizeEmaRef.current * (1 - chunkEmaAlpha) + appendedCount * chunkEmaAlpha;
|
|
391
|
+
arrivalCpsEmaRef.current = arrivalCpsEmaRef.current * (1 - chunkEmaAlpha) + normalizedInstantCps * chunkEmaAlpha;
|
|
396
392
|
emaCpsRef.current = emaCpsRef.current * (1 - config.emaAlpha) + normalizedInstantCps * config.emaAlpha;
|
|
397
393
|
}
|
|
398
394
|
lastInputTsRef.current = now;
|
|
@@ -864,6 +860,10 @@ window.__ModuleLoader__.load({
|
|
|
864
860
|
*/
|
|
865
861
|
const STREAM_BOOT_GLOBAL = "__DSH_SMOOTH_STREAM_CONFIG__";
|
|
866
862
|
//#endregion
|
|
863
|
+
//#region src/settings.ts
|
|
864
|
+
/** Defaults shared by the Host schema and the client-side fallback. */
|
|
865
|
+
const DEFAULT_STREAM_SETTINGS = { thinkAutoExpand: true };
|
|
866
|
+
//#endregion
|
|
867
867
|
//#region src/client/TypewriterAssistantNodeView.tsx
|
|
868
868
|
function usePrefersReducedMotion() {
|
|
869
869
|
const [reduced, setReduced] = (0, react.useState)(() => typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true);
|
|
@@ -957,9 +957,9 @@ window.__ModuleLoader__.load({
|
|
|
957
957
|
* or the assistant node settling — not when the rest of the reply is
|
|
958
958
|
* still streaming.
|
|
959
959
|
*/
|
|
960
|
-
function AnimatedReasoning({ text, running, preset, shouldHoldBack, t }) {
|
|
960
|
+
function AnimatedReasoning({ text, running, preset, thinkAutoExpand, shouldHoldBack, t }) {
|
|
961
961
|
const reduced = usePrefersReducedMotion();
|
|
962
|
-
const [expanded, setExpanded] = (0, react.useState)(running);
|
|
962
|
+
const [expanded, setExpanded] = (0, react.useState)(running && thinkAutoExpand);
|
|
963
963
|
const summaryRef = (0, react.useRef)(null);
|
|
964
964
|
const speedCpsRef = (0, react.useRef)(35);
|
|
965
965
|
const displayed = useSmoothStreamContent(text, {
|
|
@@ -971,8 +971,8 @@ window.__ModuleLoader__.load({
|
|
|
971
971
|
const shown = running && !reduced ? displayed : text;
|
|
972
972
|
const summary = running ? latestLine(shown) : firstLine(text);
|
|
973
973
|
(0, react.useEffect)(() => {
|
|
974
|
-
setExpanded(running);
|
|
975
|
-
}, [running]);
|
|
974
|
+
if (thinkAutoExpand) setExpanded(running);
|
|
975
|
+
}, [running, thinkAutoExpand]);
|
|
976
976
|
(0, react.useEffect)(() => {
|
|
977
977
|
const element = summaryRef.current;
|
|
978
978
|
if (element === null) return;
|
|
@@ -1026,7 +1026,7 @@ window.__ModuleLoader__.load({
|
|
|
1026
1026
|
* reveals when the frame rate is degraded. Settled text renders with the
|
|
1027
1027
|
* full Markdown pipeline.
|
|
1028
1028
|
*/
|
|
1029
|
-
const TypewriterAssistantNodeView = (0, react.memo)(function TypewriterAssistantNodeView({ mode: _mode = DEFAULT_STREAM_CONFIG.mode, preset = DEFAULT_STREAM_CONFIG.preset, revealCharsPerSec: _revealCharsPerSec = DEFAULT_STREAM_CONFIG.revealCharsPerSec, scrollSpeedPxPerSec: _scrollSpeedPxPerSec = DEFAULT_STREAM_CONFIG.scrollSpeedPxPerSec, maxScrollSpeedPxPerSec: _maxScrollSpeedPxPerSec = DEFAULT_STREAM_CONFIG.maxScrollSpeedPxPerSec, node, useTurnData, openFile, loadImage, fileMentions, t }) {
|
|
1029
|
+
const TypewriterAssistantNodeView = (0, react.memo)(function TypewriterAssistantNodeView({ mode: _mode = DEFAULT_STREAM_CONFIG.mode, preset = DEFAULT_STREAM_CONFIG.preset, revealCharsPerSec: _revealCharsPerSec = DEFAULT_STREAM_CONFIG.revealCharsPerSec, scrollSpeedPxPerSec: _scrollSpeedPxPerSec = DEFAULT_STREAM_CONFIG.scrollSpeedPxPerSec, maxScrollSpeedPxPerSec: _maxScrollSpeedPxPerSec = DEFAULT_STREAM_CONFIG.maxScrollSpeedPxPerSec, thinkAutoExpand = DEFAULT_STREAM_SETTINGS.thinkAutoExpand, node, useTurnData, openFile, loadImage, fileMentions, t }) {
|
|
1030
1030
|
const data = node.data;
|
|
1031
1031
|
const streaming = data.status === "running";
|
|
1032
1032
|
const { ref: guardRef, shouldHoldBack } = useFpsGuard(streaming);
|
|
@@ -1084,6 +1084,7 @@ window.__ModuleLoader__.load({
|
|
|
1084
1084
|
text: block.text,
|
|
1085
1085
|
running: streaming && index === last,
|
|
1086
1086
|
preset,
|
|
1087
|
+
thinkAutoExpand,
|
|
1087
1088
|
shouldHoldBack,
|
|
1088
1089
|
t
|
|
1089
1090
|
}, index));
|
|
@@ -1106,11 +1107,13 @@ window.__ModuleLoader__.load({
|
|
|
1106
1107
|
break;
|
|
1107
1108
|
}
|
|
1108
1109
|
case "tool-call": break;
|
|
1109
|
-
case "other":
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1110
|
+
case "other":
|
|
1111
|
+
rendered.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.JsonBlock, {
|
|
1112
|
+
label: t("message.unknownBlock"),
|
|
1113
|
+
payload: block.block,
|
|
1114
|
+
truncatedLabel: (total) => t("json.truncated", { total })
|
|
1115
|
+
}, index));
|
|
1116
|
+
break;
|
|
1114
1117
|
}
|
|
1115
1118
|
}
|
|
1116
1119
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -1172,8 +1175,436 @@ window.__ModuleLoader__.load({
|
|
|
1172
1175
|
};
|
|
1173
1176
|
}
|
|
1174
1177
|
//#endregion
|
|
1178
|
+
//#region \0dsh-css:/Users/dzlin/work/project/dsh-smooth-stream/src/client/SmoothStreamCard.module.css.mjs
|
|
1179
|
+
const css = ".XNGeRG_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;flex-direction:column;list-style:none;transition:border-color .16s,background .16s;display:flex}.XNGeRG_card:hover{border-color:var(--dsw-alias-label-dimmed)}.XNGeRG_cardOpen{border-color:var(--dsw-alias-label-dimmed);background:var(--dsw-alias-bg-layer-2)}.XNGeRG_header{appearance:none;cursor:pointer;text-align:left;width:100%;font:inherit;color:inherit;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.XNGeRG_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.XNGeRG_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.XNGeRG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.XNGeRG_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.XNGeRG_version{text-overflow:ellipsis;white-space:nowrap;max-width:16rem;color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:1.5;overflow:hidden}.XNGeRG_pending{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.XNGeRG_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.XNGeRG_chevronOpen{transform:rotate(180deg)}.XNGeRG_body{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;margin:0 16px;padding-bottom:8px;display:flex}.XNGeRG_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.XNGeRG_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.XNGeRG_fieldHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.XNGeRG_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.XNGeRG_toggle{width:16px;height:16px;accent-color:var(--dsw-alias-brand-primary);cursor:pointer}.XNGeRG_toggle:disabled{cursor:default}.XNGeRG_toggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.XNGeRG_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.XNGeRG_failure{justify-content:space-between;align-items:center;gap:12px;padding:12px 0;display:flex}.XNGeRG_updateRow{border-top:1px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:12px;padding:12px 0;display:flex}.XNGeRG_updateCopy{flex-direction:column;gap:4px;min-width:0;display:flex}.XNGeRG_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.XNGeRG_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.XNGeRG_save,.XNGeRG_discard,.XNGeRG_update{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.XNGeRG_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.XNGeRG_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.XNGeRG_discard:hover:not(:disabled),.XNGeRG_update:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed);color:var(--dsw-alias-label-primary)}.XNGeRG_update{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0;flex:none;align-items:center;gap:6px;display:inline-flex}.XNGeRG_discard:disabled,.XNGeRG_save:disabled,.XNGeRG_update:disabled{opacity:.4;cursor:default}.XNGeRG_discard:focus-visible,.XNGeRG_save:focus-visible,.XNGeRG_update:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}";
|
|
1180
|
+
const tagId = "dsh-smooth-stream/SmoothStreamCard.module.css";
|
|
1181
|
+
if (typeof document !== "undefined") {
|
|
1182
|
+
let tag = document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]");
|
|
1183
|
+
if (tag === null) {
|
|
1184
|
+
tag = document.createElement("style");
|
|
1185
|
+
tag.dataset.plugin = "dsh-smooth-stream";
|
|
1186
|
+
tag.dataset.pluginCss = tagId;
|
|
1187
|
+
document.head.appendChild(tag);
|
|
1188
|
+
}
|
|
1189
|
+
tag.textContent = css;
|
|
1190
|
+
}
|
|
1191
|
+
var SmoothStreamCard_module_css_default = {
|
|
1192
|
+
"description": "XNGeRG_description",
|
|
1193
|
+
"header": "XNGeRG_header",
|
|
1194
|
+
"fieldHead": "XNGeRG_fieldHead",
|
|
1195
|
+
"updateCopy": "XNGeRG_updateCopy",
|
|
1196
|
+
"footer": "XNGeRG_footer",
|
|
1197
|
+
"discard": "XNGeRG_discard",
|
|
1198
|
+
"readOnly": "XNGeRG_readOnly",
|
|
1199
|
+
"failure": "XNGeRG_failure",
|
|
1200
|
+
"chevronOpen": "XNGeRG_chevronOpen",
|
|
1201
|
+
"cardOpen": "XNGeRG_cardOpen",
|
|
1202
|
+
"update": "XNGeRG_update",
|
|
1203
|
+
"save": "XNGeRG_save",
|
|
1204
|
+
"chevron": "XNGeRG_chevron",
|
|
1205
|
+
"label": "XNGeRG_label",
|
|
1206
|
+
"body": "XNGeRG_body",
|
|
1207
|
+
"hint": "XNGeRG_hint",
|
|
1208
|
+
"headText": "XNGeRG_headText",
|
|
1209
|
+
"pending": "XNGeRG_pending",
|
|
1210
|
+
"name": "XNGeRG_name",
|
|
1211
|
+
"toggle": "XNGeRG_toggle",
|
|
1212
|
+
"updateRow": "XNGeRG_updateRow",
|
|
1213
|
+
"failed": "XNGeRG_failed",
|
|
1214
|
+
"version": "XNGeRG_version",
|
|
1215
|
+
"field": "XNGeRG_field",
|
|
1216
|
+
"card": "XNGeRG_card"
|
|
1217
|
+
};
|
|
1218
|
+
//#endregion
|
|
1219
|
+
//#region src/client/SmoothStreamCard.tsx
|
|
1220
|
+
/**
|
|
1221
|
+
* The smooth-stream plugin configuration card, rendered inside the Web
|
|
1222
|
+
* Settings "plugin configuration" page. One toggle, staged until the user
|
|
1223
|
+
* saves — the same shape as the Host-shipped cards, hand-drawn because the
|
|
1224
|
+
* Host cards' chrome is not exported for reuse.
|
|
1225
|
+
*/
|
|
1226
|
+
/** Render the smooth-stream card independently of the core settings namespace allowlist. */
|
|
1227
|
+
function SmoothStreamCard(props) {
|
|
1228
|
+
const { t } = props;
|
|
1229
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1230
|
+
const state = props.useSmoothStreamCard((snapshot) => snapshot);
|
|
1231
|
+
const blocked = !state.dirty || state.saving || state.status !== "ready";
|
|
1232
|
+
const versionLabel = state.version === void 0 ? null : t(state.installation === "development" ? "developmentVersion" : "version").replace("{version}", state.version);
|
|
1233
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
1234
|
+
className: open ? `${SmoothStreamCard_module_css_default.card} ${SmoothStreamCard_module_css_default.cardOpen}` : SmoothStreamCard_module_css_default.card,
|
|
1235
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1236
|
+
type: "button",
|
|
1237
|
+
className: SmoothStreamCard_module_css_default.header,
|
|
1238
|
+
"aria-expanded": open,
|
|
1239
|
+
onClick: () => {
|
|
1240
|
+
setOpen(!open);
|
|
1241
|
+
},
|
|
1242
|
+
children: [
|
|
1243
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1244
|
+
className: SmoothStreamCard_module_css_default.headText,
|
|
1245
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1246
|
+
className: SmoothStreamCard_module_css_default.name,
|
|
1247
|
+
children: t("title")
|
|
1248
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1249
|
+
className: SmoothStreamCard_module_css_default.description,
|
|
1250
|
+
children: t("description")
|
|
1251
|
+
})]
|
|
1252
|
+
}),
|
|
1253
|
+
versionLabel === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1254
|
+
className: SmoothStreamCard_module_css_default.version,
|
|
1255
|
+
children: versionLabel
|
|
1256
|
+
}),
|
|
1257
|
+
state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1258
|
+
className: SmoothStreamCard_module_css_default.pending,
|
|
1259
|
+
children: t("unsaved")
|
|
1260
|
+
}) : null,
|
|
1261
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: open ? `${SmoothStreamCard_module_css_default.chevron} ${SmoothStreamCard_module_css_default.chevronOpen}` : SmoothStreamCard_module_css_default.chevron })
|
|
1262
|
+
]
|
|
1263
|
+
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1264
|
+
className: SmoothStreamCard_module_css_default.body,
|
|
1265
|
+
children: [
|
|
1266
|
+
state.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1267
|
+
className: SmoothStreamCard_module_css_default.readOnly,
|
|
1268
|
+
role: "status",
|
|
1269
|
+
children: t("loading")
|
|
1270
|
+
}) : null,
|
|
1271
|
+
state.status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1272
|
+
className: SmoothStreamCard_module_css_default.failure,
|
|
1273
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1274
|
+
className: SmoothStreamCard_module_css_default.readOnly,
|
|
1275
|
+
role: "status",
|
|
1276
|
+
children: t("unavailable")
|
|
1277
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1278
|
+
type: "button",
|
|
1279
|
+
className: SmoothStreamCard_module_css_default.discard,
|
|
1280
|
+
onClick: props.reload,
|
|
1281
|
+
children: t("retry")
|
|
1282
|
+
})]
|
|
1283
|
+
}) : null,
|
|
1284
|
+
state.status === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1285
|
+
!state.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1286
|
+
className: SmoothStreamCard_module_css_default.readOnly,
|
|
1287
|
+
role: "status",
|
|
1288
|
+
children: t("readOnly")
|
|
1289
|
+
}) : null,
|
|
1290
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1291
|
+
className: SmoothStreamCard_module_css_default.field,
|
|
1292
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1293
|
+
className: SmoothStreamCard_module_css_default.fieldHead,
|
|
1294
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1295
|
+
className: SmoothStreamCard_module_css_default.label,
|
|
1296
|
+
children: t("thinkAutoExpand")
|
|
1297
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1298
|
+
type: "checkbox",
|
|
1299
|
+
className: SmoothStreamCard_module_css_default.toggle,
|
|
1300
|
+
checked: state.thinkAutoExpand,
|
|
1301
|
+
disabled: !state.writable,
|
|
1302
|
+
onChange: (event) => {
|
|
1303
|
+
props.edit(event.target.checked);
|
|
1304
|
+
}
|
|
1305
|
+
})]
|
|
1306
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1307
|
+
className: SmoothStreamCard_module_css_default.hint,
|
|
1308
|
+
children: t("thinkAutoExpandHint")
|
|
1309
|
+
})]
|
|
1310
|
+
}),
|
|
1311
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1312
|
+
className: SmoothStreamCard_module_css_default.updateRow,
|
|
1313
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1314
|
+
className: SmoothStreamCard_module_css_default.updateCopy,
|
|
1315
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1316
|
+
className: SmoothStreamCard_module_css_default.label,
|
|
1317
|
+
children: t("updates")
|
|
1318
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1319
|
+
className: SmoothStreamCard_module_css_default.hint,
|
|
1320
|
+
children: state.restartRequired ? t("restartRequired") : state.installation === "npm" ? t("updateHint") : state.installation === "development" ? t("developmentBuild") : t("updateUnavailable")
|
|
1321
|
+
})]
|
|
1322
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1323
|
+
type: "button",
|
|
1324
|
+
className: SmoothStreamCard_module_css_default.update,
|
|
1325
|
+
disabled: !state.canUpgrade || state.upgrading || state.restartRequired,
|
|
1326
|
+
title: state.canUpgrade ? void 0 : t("updateUnavailable"),
|
|
1327
|
+
onClick: props.upgrade,
|
|
1328
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1329
|
+
"aria-hidden": "true",
|
|
1330
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline14, {})
|
|
1331
|
+
}), t(state.upgrading ? "updating" : "update")]
|
|
1332
|
+
})]
|
|
1333
|
+
}),
|
|
1334
|
+
state.upgradeFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1335
|
+
className: SmoothStreamCard_module_css_default.failed,
|
|
1336
|
+
role: "status",
|
|
1337
|
+
children: t("updateFailed")
|
|
1338
|
+
}) : null,
|
|
1339
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1340
|
+
className: SmoothStreamCard_module_css_default.footer,
|
|
1341
|
+
children: [
|
|
1342
|
+
state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1343
|
+
className: SmoothStreamCard_module_css_default.failed,
|
|
1344
|
+
role: "status",
|
|
1345
|
+
children: t("saveFailed")
|
|
1346
|
+
}) : null,
|
|
1347
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1348
|
+
type: "button",
|
|
1349
|
+
className: SmoothStreamCard_module_css_default.discard,
|
|
1350
|
+
disabled: !state.dirty || state.saving,
|
|
1351
|
+
onClick: props.discard,
|
|
1352
|
+
children: t("discard")
|
|
1353
|
+
}),
|
|
1354
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1355
|
+
type: "button",
|
|
1356
|
+
className: SmoothStreamCard_module_css_default.save,
|
|
1357
|
+
disabled: blocked,
|
|
1358
|
+
onClick: props.save,
|
|
1359
|
+
children: t(state.saving ? "saving" : "save")
|
|
1360
|
+
})
|
|
1361
|
+
]
|
|
1362
|
+
})
|
|
1363
|
+
] }) : null
|
|
1364
|
+
]
|
|
1365
|
+
}) : null]
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
//#endregion
|
|
1369
|
+
//#region src/client/smooth-stream-card-controller.ts
|
|
1370
|
+
/** Staged form state for the plugin-owned smooth-stream settings RPC. */
|
|
1371
|
+
/** Bridges the plugin's protected Host interface onto a staged settings form. */
|
|
1372
|
+
var SmoothStreamCardController = class {
|
|
1373
|
+
api;
|
|
1374
|
+
store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(this.projection());
|
|
1375
|
+
loaded;
|
|
1376
|
+
staged;
|
|
1377
|
+
saving = false;
|
|
1378
|
+
failed = false;
|
|
1379
|
+
upgrading = false;
|
|
1380
|
+
upgradeFailed = false;
|
|
1381
|
+
restartRequired = false;
|
|
1382
|
+
loadGeneration = 0;
|
|
1383
|
+
loadStatus = "loading";
|
|
1384
|
+
constructor(api) {
|
|
1385
|
+
this.api = api;
|
|
1386
|
+
}
|
|
1387
|
+
/** Begin the background read after the card has been registered. */
|
|
1388
|
+
start() {
|
|
1389
|
+
this.load();
|
|
1390
|
+
}
|
|
1391
|
+
/** Ignore a late response after the surrounding optional services unload. */
|
|
1392
|
+
stop() {
|
|
1393
|
+
this.loadGeneration += 1;
|
|
1394
|
+
}
|
|
1395
|
+
/** Current card snapshot, also consumed by the streaming preference cell. */
|
|
1396
|
+
getSnapshot() {
|
|
1397
|
+
return this.store.getSnapshot();
|
|
1398
|
+
}
|
|
1399
|
+
/** Subscribe to state changes. */
|
|
1400
|
+
subscribe(listener) {
|
|
1401
|
+
return this.store.subscribe(listener);
|
|
1402
|
+
}
|
|
1403
|
+
/** Build the face consumed by the settings slot renderer. */
|
|
1404
|
+
inject() {
|
|
1405
|
+
return {
|
|
1406
|
+
hooks: { smoothStreamCard: this.store },
|
|
1407
|
+
edit: (value) => {
|
|
1408
|
+
this.staged = value;
|
|
1409
|
+
this.failed = false;
|
|
1410
|
+
this.publish();
|
|
1411
|
+
},
|
|
1412
|
+
save: () => {
|
|
1413
|
+
this.save();
|
|
1414
|
+
},
|
|
1415
|
+
discard: () => {
|
|
1416
|
+
if (this.staged === void 0 && !this.failed) return;
|
|
1417
|
+
this.staged = void 0;
|
|
1418
|
+
this.failed = false;
|
|
1419
|
+
this.publish();
|
|
1420
|
+
},
|
|
1421
|
+
reload: () => {
|
|
1422
|
+
this.load();
|
|
1423
|
+
},
|
|
1424
|
+
upgrade: () => {
|
|
1425
|
+
this.upgrade();
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
projection() {
|
|
1430
|
+
return {
|
|
1431
|
+
status: this.loadStatus,
|
|
1432
|
+
writable: this.loaded?.writable ?? false,
|
|
1433
|
+
dirty: this.staged !== void 0,
|
|
1434
|
+
saving: this.saving,
|
|
1435
|
+
failed: this.failed,
|
|
1436
|
+
thinkAutoExpand: this.staged ?? this.loaded?.thinkAutoExpand ?? DEFAULT_STREAM_SETTINGS.thinkAutoExpand,
|
|
1437
|
+
version: this.loaded?.version,
|
|
1438
|
+
installation: this.loaded?.installation ?? "unmanaged",
|
|
1439
|
+
canUpgrade: this.loaded?.canUpgrade ?? false,
|
|
1440
|
+
upgrading: this.upgrading,
|
|
1441
|
+
upgradeFailed: this.upgradeFailed,
|
|
1442
|
+
restartRequired: this.restartRequired
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
async load() {
|
|
1446
|
+
const generation = ++this.loadGeneration;
|
|
1447
|
+
this.loadStatus = "loading";
|
|
1448
|
+
this.loaded = void 0;
|
|
1449
|
+
this.publish();
|
|
1450
|
+
try {
|
|
1451
|
+
const view = await this.api.read();
|
|
1452
|
+
if (generation !== this.loadGeneration) return;
|
|
1453
|
+
this.loaded = view;
|
|
1454
|
+
this.loadStatus = "ready";
|
|
1455
|
+
} catch {
|
|
1456
|
+
if (generation !== this.loadGeneration) return;
|
|
1457
|
+
this.loaded = void 0;
|
|
1458
|
+
this.loadStatus = "unavailable";
|
|
1459
|
+
}
|
|
1460
|
+
this.publish();
|
|
1461
|
+
}
|
|
1462
|
+
async save() {
|
|
1463
|
+
if (this.staged === void 0 || this.saving || this.loaded?.writable !== true) return;
|
|
1464
|
+
const value = this.staged;
|
|
1465
|
+
this.saving = true;
|
|
1466
|
+
this.failed = false;
|
|
1467
|
+
this.publish();
|
|
1468
|
+
try {
|
|
1469
|
+
this.loaded = await this.api.write(value);
|
|
1470
|
+
this.staged = void 0;
|
|
1471
|
+
} catch {
|
|
1472
|
+
this.failed = true;
|
|
1473
|
+
}
|
|
1474
|
+
this.saving = false;
|
|
1475
|
+
this.publish();
|
|
1476
|
+
}
|
|
1477
|
+
async upgrade() {
|
|
1478
|
+
if (this.loaded?.canUpgrade !== true || this.upgrading) return;
|
|
1479
|
+
this.upgrading = true;
|
|
1480
|
+
this.upgradeFailed = false;
|
|
1481
|
+
this.restartRequired = false;
|
|
1482
|
+
this.publish();
|
|
1483
|
+
try {
|
|
1484
|
+
const result = await this.api.upgrade();
|
|
1485
|
+
this.restartRequired = result.restartRequired;
|
|
1486
|
+
} catch {
|
|
1487
|
+
this.upgradeFailed = true;
|
|
1488
|
+
}
|
|
1489
|
+
this.upgrading = false;
|
|
1490
|
+
this.publish();
|
|
1491
|
+
}
|
|
1492
|
+
publish() {
|
|
1493
|
+
this.store.set(this.projection());
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
//#endregion
|
|
1497
|
+
//#region src/settings-api.ts
|
|
1498
|
+
/** Shared wire vocabulary for the plugin-owned settings RPC channel. */
|
|
1499
|
+
/** Dedicated, loopback-only RPC channel registered by the Host half. */
|
|
1500
|
+
const STREAM_SETTINGS_RPC_CHANNEL = "/smooth-stream";
|
|
1501
|
+
/** Endpoints accepted by {@link STREAM_SETTINGS_RPC_CHANNEL}. */
|
|
1502
|
+
const STREAM_SETTINGS_RPC = {
|
|
1503
|
+
read: "settings.read",
|
|
1504
|
+
write: "settings.write",
|
|
1505
|
+
upgrade: "plugin.upgrade"
|
|
1506
|
+
};
|
|
1507
|
+
//#endregion
|
|
1508
|
+
//#region src/client/smooth-stream-settings-api.ts
|
|
1509
|
+
function record(value) {
|
|
1510
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1511
|
+
}
|
|
1512
|
+
function settingsView(value) {
|
|
1513
|
+
const data = record(value);
|
|
1514
|
+
if (data === void 0 || typeof data.version !== "string" || ![
|
|
1515
|
+
"npm",
|
|
1516
|
+
"development",
|
|
1517
|
+
"unmanaged"
|
|
1518
|
+
].includes(data.installation) || typeof data.writable !== "boolean" || typeof data.thinkAutoExpand !== "boolean" || typeof data.canUpgrade !== "boolean") throw new Error("dsh-smooth-stream: malformed settings response");
|
|
1519
|
+
return data;
|
|
1520
|
+
}
|
|
1521
|
+
function upgradeView(value) {
|
|
1522
|
+
if (record(value)?.restartRequired !== true) throw new Error("dsh-smooth-stream: malformed update response");
|
|
1523
|
+
return { restartRequired: true };
|
|
1524
|
+
}
|
|
1525
|
+
function accepted(result) {
|
|
1526
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
1527
|
+
return result.value;
|
|
1528
|
+
}
|
|
1529
|
+
/** Build the typed facade over the generic Connection RPC service. */
|
|
1530
|
+
function createSmoothStreamSettingsApi(connection) {
|
|
1531
|
+
return {
|
|
1532
|
+
async read() {
|
|
1533
|
+
return settingsView(accepted(await connection.rpc.call(STREAM_SETTINGS_RPC_CHANNEL, STREAM_SETTINGS_RPC.read, {})));
|
|
1534
|
+
},
|
|
1535
|
+
async write(thinkAutoExpand) {
|
|
1536
|
+
return settingsView(accepted(await connection.rpc.call(STREAM_SETTINGS_RPC_CHANNEL, STREAM_SETTINGS_RPC.write, { thinkAutoExpand })));
|
|
1537
|
+
},
|
|
1538
|
+
async upgrade() {
|
|
1539
|
+
return upgradeView(accepted(await connection.rpc.call(STREAM_SETTINGS_RPC_CHANNEL, STREAM_SETTINGS_RPC.upgrade, {})));
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
//#endregion
|
|
1544
|
+
//#region src/client/locales.ts
|
|
1545
|
+
/** Locale bundles for the smooth-stream plugin configuration card. */
|
|
1546
|
+
/** Dictionary namespace owned by this plugin's settings card. */
|
|
1547
|
+
const NS = "settings.smoothStream";
|
|
1548
|
+
/** English copy. */
|
|
1549
|
+
const en = {
|
|
1550
|
+
title: "Smooth stream",
|
|
1551
|
+
description: "How replies are revealed while they stream.",
|
|
1552
|
+
thinkAutoExpand: "Auto-expand thinking",
|
|
1553
|
+
thinkAutoExpandHint: "Open the thinking block while it streams. Turn off to keep it collapsed.",
|
|
1554
|
+
readOnly: "This deployment stores settings read-only.",
|
|
1555
|
+
loading: "Loading plugin settings…",
|
|
1556
|
+
unavailable: "Plugin settings are unavailable in this connection.",
|
|
1557
|
+
retry: "Retry",
|
|
1558
|
+
version: "Version {version}",
|
|
1559
|
+
developmentVersion: "Development version {version}",
|
|
1560
|
+
updates: "Updates",
|
|
1561
|
+
updateHint: "Install the newest npm version, then restart Harness.",
|
|
1562
|
+
developmentBuild: "Linked source; updates are managed in the checkout.",
|
|
1563
|
+
updateUnavailable: "Updates are available only for an npm profile installation.",
|
|
1564
|
+
update: "Update",
|
|
1565
|
+
updating: "Updating…",
|
|
1566
|
+
restartRequired: "Updated. Restart Harness to load the new version.",
|
|
1567
|
+
updateFailed: "The package update failed; your current version is unchanged.",
|
|
1568
|
+
save: "Save",
|
|
1569
|
+
saving: "Saving…",
|
|
1570
|
+
discard: "Discard",
|
|
1571
|
+
unsaved: "Unsaved",
|
|
1572
|
+
saveFailed: "The deployment did not accept these values; they were left for you to correct."
|
|
1573
|
+
};
|
|
1574
|
+
/** Simplified Chinese copy. */
|
|
1575
|
+
const zh = {
|
|
1576
|
+
title: "丝滑流式",
|
|
1577
|
+
description: "回复在流式输出时如何逐字展现。",
|
|
1578
|
+
thinkAutoExpand: "自动展开思考",
|
|
1579
|
+
thinkAutoExpandHint: "思考块在流式时自动展开;关闭后保持折叠,可手动展开。",
|
|
1580
|
+
readOnly: "本部署的设置为只读。",
|
|
1581
|
+
loading: "正在加载插件设置…",
|
|
1582
|
+
unavailable: "当前连接无法访问插件设置。",
|
|
1583
|
+
retry: "重试",
|
|
1584
|
+
version: "版本 {version}",
|
|
1585
|
+
developmentVersion: "开发版本 {version}",
|
|
1586
|
+
updates: "更新",
|
|
1587
|
+
updateHint: "安装最新 npm 版本后重启 Harness。",
|
|
1588
|
+
developmentBuild: "当前为本地链接版本,请在源码目录管理更新。",
|
|
1589
|
+
updateUnavailable: "只有 profile 使用 npm 包时才能更新。",
|
|
1590
|
+
update: "更新",
|
|
1591
|
+
updating: "更新中…",
|
|
1592
|
+
restartRequired: "已更新;重启 Harness 后加载新版本。",
|
|
1593
|
+
updateFailed: "包更新失败,当前版本未改变。",
|
|
1594
|
+
save: "保存",
|
|
1595
|
+
saving: "保存中…",
|
|
1596
|
+
discard: "放弃修改",
|
|
1597
|
+
unsaved: "未保存",
|
|
1598
|
+
saveFailed: "本部署没有接受这些值,已保留供你修改。"
|
|
1599
|
+
};
|
|
1600
|
+
//#endregion
|
|
1175
1601
|
//#region src/client/index.ts
|
|
1176
|
-
/**
|
|
1602
|
+
/**
|
|
1603
|
+
* Cordis services required by the browser half. Only `slots` is load-bearing
|
|
1604
|
+
* for the stream itself; locale and Connection power the configuration card
|
|
1605
|
+
* and are wired through `ctx.inject` below so a deployment without them still
|
|
1606
|
+
* streams with defaults.
|
|
1607
|
+
*/
|
|
1177
1608
|
const inject = ["slots"];
|
|
1178
1609
|
const STREAM_MODES = ["typewriter", "teleprompter"];
|
|
1179
1610
|
const STREAM_PRESETS = [
|
|
@@ -1182,7 +1613,7 @@ window.__ModuleLoader__.load({
|
|
|
1182
1613
|
"silky"
|
|
1183
1614
|
];
|
|
1184
1615
|
/** Human-authored rows stay on the built-in renderer; `assistant-step` is replaced. */
|
|
1185
|
-
const SKIP_WRAP =
|
|
1616
|
+
const SKIP_WRAP = new Set([
|
|
1186
1617
|
"assistant-step",
|
|
1187
1618
|
"user",
|
|
1188
1619
|
"steering",
|
|
@@ -1244,23 +1675,93 @@ window.__ModuleLoader__.load({
|
|
|
1244
1675
|
};
|
|
1245
1676
|
}
|
|
1246
1677
|
/**
|
|
1678
|
+
* A live preference cell read by `useSyncExternalStore`. It starts on the
|
|
1679
|
+
* shared default and, once the plugin-owned settings controller is attached,
|
|
1680
|
+
* tracks the resolved `thinkAutoExpand` value. A composition (or test) that
|
|
1681
|
+
* ships no settings surface keeps the cell on the default.
|
|
1682
|
+
*/
|
|
1683
|
+
var PreferenceCell = class {
|
|
1684
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1685
|
+
card;
|
|
1686
|
+
value = DEFAULT_STREAM_SETTINGS.thinkAutoExpand;
|
|
1687
|
+
/** Re-point the cell at the plugin-owned settings controller. */
|
|
1688
|
+
attach(card) {
|
|
1689
|
+
this.card = card;
|
|
1690
|
+
this.refresh();
|
|
1691
|
+
const unsubscribe = card.subscribe(() => {
|
|
1692
|
+
this.refresh();
|
|
1693
|
+
});
|
|
1694
|
+
return () => {
|
|
1695
|
+
unsubscribe();
|
|
1696
|
+
if (this.card !== card) return;
|
|
1697
|
+
this.card = void 0;
|
|
1698
|
+
this.refresh();
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
read() {
|
|
1702
|
+
return this.card?.getSnapshot().thinkAutoExpand ?? DEFAULT_STREAM_SETTINGS.thinkAutoExpand;
|
|
1703
|
+
}
|
|
1704
|
+
refresh() {
|
|
1705
|
+
const next = this.read();
|
|
1706
|
+
if (next === this.value) return;
|
|
1707
|
+
this.value = next;
|
|
1708
|
+
for (const listener of this.listeners) listener();
|
|
1709
|
+
}
|
|
1710
|
+
getSnapshot = () => this.value;
|
|
1711
|
+
subscribe = (listener) => {
|
|
1712
|
+
this.listeners.add(listener);
|
|
1713
|
+
return () => {
|
|
1714
|
+
this.listeners.delete(listener);
|
|
1715
|
+
};
|
|
1716
|
+
};
|
|
1717
|
+
};
|
|
1718
|
+
/**
|
|
1247
1719
|
* Register the typewriter renderer after the conversation package declares the
|
|
1248
1720
|
* keyed Chat node seat. A lower priority shadows the built-in assistant row;
|
|
1249
1721
|
* every other growing row is wrapped in place so Tool cards, retries, and
|
|
1250
1722
|
* workflow runs share conversation follow. The Host-bridged configuration
|
|
1251
|
-
* selects the render direction, smoothing preset, and glide speed
|
|
1723
|
+
* selects the render direction, smoothing preset, and glide speed; the
|
|
1724
|
+
* plugin-owned settings RPC supplies the live auto-expand preference when the
|
|
1725
|
+
* settings surface is composed.
|
|
1252
1726
|
* @param ctx - Browser context carrying the shared slot registry.
|
|
1253
1727
|
*/
|
|
1254
1728
|
function apply(ctx) {
|
|
1255
1729
|
const config = readBootConfig();
|
|
1730
|
+
const preference = new PreferenceCell();
|
|
1731
|
+
ctx.inject([
|
|
1732
|
+
"slots",
|
|
1733
|
+
"locale",
|
|
1734
|
+
"connection"
|
|
1735
|
+
], (settingsCtx) => {
|
|
1736
|
+
const card = new SmoothStreamCardController(createSmoothStreamSettingsApi(settingsCtx.get("connection")));
|
|
1737
|
+
const detachPreference = preference.attach(card);
|
|
1738
|
+
card.start();
|
|
1739
|
+
settingsCtx.effect(() => settingsCtx.locale.register(NS, {
|
|
1740
|
+
zh,
|
|
1741
|
+
en
|
|
1742
|
+
}), "dsh-smooth-stream: settings dictionaries");
|
|
1743
|
+
settingsCtx.slots.inject("settings.plugin.item", () => settingsCtx.slots.register({
|
|
1744
|
+
name: "settings.plugin.item",
|
|
1745
|
+
id: "smooth-stream",
|
|
1746
|
+
order: 30,
|
|
1747
|
+
locale: NS,
|
|
1748
|
+
inject: () => card.inject()
|
|
1749
|
+
}, SmoothStreamCard));
|
|
1750
|
+
return () => {
|
|
1751
|
+
card.stop();
|
|
1752
|
+
detachPreference();
|
|
1753
|
+
};
|
|
1754
|
+
});
|
|
1256
1755
|
const configured = function StreamConfiguredView(props) {
|
|
1756
|
+
const thinkAutoExpand = (0, react.useSyncExternalStore)(preference.subscribe, preference.getSnapshot, preference.getSnapshot);
|
|
1257
1757
|
return (0, react.createElement)(TypewriterAssistantNodeView, {
|
|
1258
1758
|
...props,
|
|
1259
1759
|
mode: config.mode,
|
|
1260
1760
|
preset: config.preset,
|
|
1261
1761
|
revealCharsPerSec: config.revealCharsPerSec,
|
|
1262
1762
|
scrollSpeedPxPerSec: config.scrollSpeedPxPerSec,
|
|
1263
|
-
maxScrollSpeedPxPerSec: config.maxScrollSpeedPxPerSec
|
|
1763
|
+
maxScrollSpeedPxPerSec: config.maxScrollSpeedPxPerSec,
|
|
1764
|
+
thinkAutoExpand
|
|
1264
1765
|
});
|
|
1265
1766
|
};
|
|
1266
1767
|
ctx.slots.inject("conversation.chat.node", () => {
|