dsh-smooth-stream 0.5.0 → 0.6.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/lib/client.js CHANGED
@@ -23,27 +23,27 @@ window.__ModuleLoader__.load({
23
23
  tag.textContent = css$3;
24
24
  }
25
25
  var TypewriterAssistantNodeView_module_css_default = {
26
- "disclosureIconIdle": "I17U7q_disclosureIconIdle",
27
- "disclosureTitle": "I17U7q_disclosureTitle",
28
- "thinkRow": "I17U7q_thinkRow",
26
+ "stopped": "I17U7q_stopped",
29
27
  "thinkChevron": "I17U7q_thinkChevron",
30
- "root": "I17U7q_root",
28
+ "thinkTitle": "I17U7q_thinkTitle",
29
+ "thinkLeading": "I17U7q_thinkLeading",
30
+ "thinkSeparator": "I17U7q_thinkSeparator",
31
31
  "body": "I17U7q_body",
32
- "disclosureRow": "I17U7q_disclosureRow",
33
- "stopped": "I17U7q_stopped",
32
+ "thinkBody": "I17U7q_thinkBody",
33
+ "disclosureContent": "I17U7q_disclosureContent",
34
34
  "visuallyHidden": "I17U7q_visuallyHidden",
35
- "thinkSeparator": "I17U7q_thinkSeparator",
36
- "thinkSummary": "I17U7q_thinkSummary",
37
- "think": "I17U7q_think",
35
+ "disclosureTitle": "I17U7q_disclosureTitle",
38
36
  "dsh-smooth-stream-think-sweep": "I17U7q_dsh-smooth-stream-think-sweep",
39
- "disclosureChevronHover": "I17U7q_disclosureChevronHover",
40
- "thinkLeading": "I17U7q_thinkLeading",
41
- "disclosureContent": "I17U7q_disclosureContent",
37
+ "disclosureIconIdle": "I17U7q_disclosureIconIdle",
42
38
  "follow": "I17U7q_follow",
39
+ "think": "I17U7q_think",
40
+ "thinkSummary": "I17U7q_thinkSummary",
41
+ "disclosureRow": "I17U7q_disclosureRow",
42
+ "root": "I17U7q_root",
43
43
  "disclosureLeading": "I17U7q_disclosureLeading",
44
- "thinkTitle": "I17U7q_thinkTitle",
45
- "disclosureRoot": "I17U7q_disclosureRoot",
46
- "thinkBody": "I17U7q_thinkBody"
44
+ "thinkRow": "I17U7q_thinkRow",
45
+ "disclosureChevronHover": "I17U7q_disclosureChevronHover",
46
+ "disclosureRoot": "I17U7q_disclosureRoot"
47
47
  };
48
48
  //#endregion
49
49
  //#region src/client/AnimatedDisclosure.tsx
@@ -126,6 +126,7 @@ window.__ModuleLoader__.load({
126
126
  const DEFAULT_STREAM_SETTINGS = {
127
127
  enabled: true,
128
128
  controlScroll: true,
129
+ motionPreference: "auto",
129
130
  thinkAutoExpand: true,
130
131
  debugEnabled: false,
131
132
  debugTuning: DEFAULT_STREAM_DEBUG_TUNING
@@ -2681,6 +2682,20 @@ window.__ModuleLoader__.load({
2681
2682
  }, []);
2682
2683
  return reduced;
2683
2684
  }
2685
+ /**
2686
+ * Resolve whether the reveal engine should stay off for this view. The OS
2687
+ * preference wins only in `auto` mode: `force-smooth` keeps the engine on
2688
+ * machines where a system-wide reduce-motion switch (or a forced browser
2689
+ * flag) would otherwise silently bypass smoothing, and `force-reduced`
2690
+ * disables it even when the OS asks for motion. Credit: three-state design
2691
+ * proposed by @Zn-Dk in #21/#22.
2692
+ */
2693
+ function useMotionReduced(preference) {
2694
+ const system = usePrefersReducedMotion();
2695
+ if (preference === "force-smooth") return false;
2696
+ if (preference === "force-reduced") return true;
2697
+ return system;
2698
+ }
2684
2699
  /** Conservative fallback before the streaming Markdown tail has geometry. */
2685
2700
  const PREDICTIVE_WRAP_FALLBACK_CHARS = 32;
2686
2701
  const STREAM_ANNOUNCEMENT_INTERVAL_MS = 800;
@@ -2886,8 +2901,8 @@ window.__ModuleLoader__.load({
2886
2901
  * drains, the settled full parse (KaTeX math, fence highlighting, file
2887
2902
  * mentions) swaps in exactly once.
2888
2903
  */
2889
- function AnimatedMarkdownText({ text, labels, fileMentions, streaming, ownFollow, followSpeedCpsRef, followRevealedCharsRef, followRevealScaleRef, onPredictiveChange, preset, shouldHoldBack, controlScroll = true }) {
2890
- const reduced = usePrefersReducedMotion();
2904
+ function AnimatedMarkdownText({ text, labels, fileMentions, streaming, motionReduced, ownFollow, followSpeedCpsRef, followRevealedCharsRef, followRevealScaleRef, onPredictiveChange, preset, shouldHoldBack, controlScroll = true }) {
2905
+ const reduced = motionReduced;
2891
2906
  const [typing, setTyping] = (0, react.useState)(streaming);
2892
2907
  const localSpeedCpsRef = (0, react.useRef)(35);
2893
2908
  const followRootRef = (0, react.useRef)(null);
@@ -2943,6 +2958,9 @@ window.__ModuleLoader__.load({
2943
2958
  text,
2944
2959
  typing
2945
2960
  ]);
2961
+ (0, react.useEffect)(() => {
2962
+ if (streaming) setTyping(true);
2963
+ }, [streaming]);
2946
2964
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FollowHost, {
2947
2965
  active: live && ownFollow,
2948
2966
  speedCpsRef,
@@ -2972,6 +2990,50 @@ window.__ModuleLoader__.load({
2972
2990
  }
2973
2991
  };
2974
2992
  }
2993
+ /**
2994
+ * Apply searchable hidden state without unmounting a stable subtree — the
2995
+ * Host's completion fold hides the answer-inline reasoning this way, so the
2996
+ * takeover renderer must reproduce it to stay inside the contract: the row
2997
+ * disappears into the Host's process summary and comes back on find-in-page.
2998
+ * (Mirrors the Harness chat kit's `useSearchableHidden`.)
2999
+ */
3000
+ function useSearchableHidden(hidden, reveal) {
3001
+ const ref = (0, react.useRef)(null);
3002
+ (0, react.useLayoutEffect)(() => {
3003
+ const element = ref.current;
3004
+ if (element === null) return;
3005
+ if (hidden && element.contains(element.ownerDocument.activeElement)) {
3006
+ reveal();
3007
+ return;
3008
+ }
3009
+ if (hidden) element.setAttribute("hidden", "until-found");
3010
+ else element.removeAttribute("hidden");
3011
+ }, [hidden, reveal]);
3012
+ (0, react.useEffect)(() => {
3013
+ const element = ref.current;
3014
+ if (element === null) return;
3015
+ element.addEventListener("beforematch", reveal);
3016
+ return () => {
3017
+ element.removeEventListener("beforematch", reveal);
3018
+ };
3019
+ }, [reveal]);
3020
+ return ref;
3021
+ }
3022
+ /**
3023
+ * One answer-inline reasoning block wrapped in the Host's fold contract. The
3024
+ * Host computes the fold decision (turn closed, compact transcript, this node
3025
+ * is the answer) and delivers it through the `turnProcess` owner prop; when
3026
+ * folded the block hides into the Host's "thought" summary row instead of
3027
+ * staying mounted above the answer.
3028
+ */
3029
+ function FoldableReasoning({ hidden, reveal, children }) {
3030
+ const ref = useSearchableHidden(hidden, reveal);
3031
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3032
+ ref,
3033
+ "data-turn-process-inline": hidden || void 0,
3034
+ children
3035
+ });
3036
+ }
2975
3037
  function firstLine(text) {
2976
3038
  const newline = text.indexOf("\n");
2977
3039
  return newline === -1 ? text : text.slice(0, newline);
@@ -2990,8 +3052,8 @@ window.__ModuleLoader__.load({
2990
3052
  * or the assistant node settling — not when the rest of the reply is
2991
3053
  * still streaming.
2992
3054
  */
2993
- function AnimatedReasoning({ text, running, preset, thinkAutoExpand, shouldHoldBack, followSpeedCpsRef, followRevealScaleRef, t }) {
2994
- const reduced = usePrefersReducedMotion();
3055
+ function AnimatedReasoning({ text, running, preset, thinkAutoExpand, motionReduced, shouldHoldBack, followSpeedCpsRef, followRevealScaleRef, t }) {
3056
+ const reduced = motionReduced;
2995
3057
  const [expanded, setExpanded] = (0, react.useState)(running && thinkAutoExpand);
2996
3058
  const [autoClosed, setAutoClosed] = (0, react.useState)(false);
2997
3059
  const summaryRef = (0, react.useRef)(null);
@@ -3070,10 +3132,14 @@ window.__ModuleLoader__.load({
3070
3132
  * holds offscreen reveals when the frame rate is degraded. Settled text
3071
3133
  * renders with the full Markdown pipeline.
3072
3134
  */
3073
- 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, controlScroll = true, node, useTurnData, openFile, loadImage, fileMentions, t }) {
3135
+ 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, controlScroll = true, motionPreference = DEFAULT_STREAM_SETTINGS.motionPreference, node, useTurnData, openFile, loadImage, fileMentions, turnProcess, t }) {
3074
3136
  const data = node.data;
3075
3137
  const streaming = data.status === "running";
3076
- const reduced = usePrefersReducedMotion();
3138
+ const reduced = useMotionReduced(motionPreference);
3139
+ const reasoningHidden = turnProcess !== void 0 && turnProcess.foldable && turnProcess.spec.answerStep === data.step && turnProcess.spec.inlineReasoning && !turnProcess.open;
3140
+ const revealProcess = (0, react.useCallback)(() => {
3141
+ turnProcess?.setOpen(true);
3142
+ }, [turnProcess]);
3077
3143
  const { ref: guardRef, shouldHoldBack } = useFpsGuard(streaming);
3078
3144
  const rootSpeedRef = (0, react.useRef)(35);
3079
3145
  const rootRevealedCharsRef = (0, react.useRef)(0);
@@ -3136,6 +3202,7 @@ window.__ModuleLoader__.load({
3136
3202
  labels: markdownLabels,
3137
3203
  fileMentions: mentions,
3138
3204
  streaming,
3205
+ motionReduced: reduced,
3139
3206
  ownFollow: !streaming && index === lastFollow,
3140
3207
  followSpeedCpsRef: index === lastFollow ? rootSpeedRef : void 0,
3141
3208
  followRevealedCharsRef: index === lastFollow ? rootRevealedCharsRef : void 0,
@@ -3147,15 +3214,20 @@ window.__ModuleLoader__.load({
3147
3214
  }, index));
3148
3215
  break;
3149
3216
  case "reasoning":
3150
- rendered.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnimatedReasoning, {
3151
- text: block.text,
3152
- running: streaming && index === last,
3153
- preset,
3154
- thinkAutoExpand,
3155
- shouldHoldBack,
3156
- followSpeedCpsRef: reasoningOwnsSpeed && index === last ? rootSpeedRef : void 0,
3157
- followRevealScaleRef: reasoningOwnsSpeed && index === last ? rootRevealScaleRef : void 0,
3158
- t
3217
+ rendered.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FoldableReasoning, {
3218
+ hidden: reasoningHidden,
3219
+ reveal: revealProcess,
3220
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnimatedReasoning, {
3221
+ text: block.text,
3222
+ running: streaming && index === last,
3223
+ preset,
3224
+ thinkAutoExpand,
3225
+ motionReduced: reduced,
3226
+ shouldHoldBack,
3227
+ followSpeedCpsRef: reasoningOwnsSpeed && index === last ? rootSpeedRef : void 0,
3228
+ followRevealScaleRef: reasoningOwnsSpeed && index === last ? rootRevealScaleRef : void 0,
3229
+ t
3230
+ })
3159
3231
  }, index));
3160
3232
  break;
3161
3233
  case "image": {
@@ -3482,8 +3554,8 @@ window.__ModuleLoader__.load({
3482
3554
  tag.textContent = css$2;
3483
3555
  }
3484
3556
  var AgentRowEntrance_module_css_default = {
3485
- "surface": "_7P9_Ta_surface",
3486
- "dsh-smooth-stream-agent-row-in": "_7P9_Ta_dsh-smooth-stream-agent-row-in"
3557
+ "dsh-smooth-stream-agent-row-in": "_7P9_Ta_dsh-smooth-stream-agent-row-in",
3558
+ "surface": "_7P9_Ta_surface"
3487
3559
  };
3488
3560
  //#endregion
3489
3561
  //#region src/client/TypewriterToolNodeView.tsx
@@ -3662,7 +3734,7 @@ window.__ModuleLoader__.load({
3662
3734
  }
3663
3735
  //#endregion
3664
3736
  //#region \0dsh-css:/Users/dzlin/work/project/dsh-smooth-stream/src/client/SmoothStreamCard.module.css.mjs
3665
- const css$1 = ".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_field+.XNGeRG_field{border-top:1px solid var(--dsw-alias-border-l2)}.XNGeRG_fieldDisabled{opacity:.5}.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}";
3737
+ const css$1 = ".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_field+.XNGeRG_field{border-top:1px solid var(--dsw-alias-border-l2)}.XNGeRG_fieldDisabled{opacity:.5}.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_choiceRow{flex-wrap:wrap;gap:6px 14px;margin:2px 0 0;padding:0;display:flex}.XNGeRG_choice{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:5px;font-size:12px;line-height:1.5;display:flex}.XNGeRG_choiceInput{width:14px;height:14px;accent-color:var(--dsw-alias-brand-primary);cursor:pointer}.XNGeRG_choice:has(.XNGeRG_choiceInput:disabled){cursor:default;opacity:.55}.XNGeRG_choiceInput:disabled{cursor:default}.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}";
3666
3738
  const tagId$1 = "dsh-smooth-stream/SmoothStreamCard.module.css";
3667
3739
  if (typeof document !== "undefined") {
3668
3740
  let tag = document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]");
@@ -3675,32 +3747,35 @@ window.__ModuleLoader__.load({
3675
3747
  tag.textContent = css$1;
3676
3748
  }
3677
3749
  var SmoothStreamCard_module_css_default = {
3678
- "updateCopy": "XNGeRG_updateCopy",
3679
- "footer": "XNGeRG_footer",
3680
- "name": "XNGeRG_name",
3681
- "update": "XNGeRG_update",
3682
- "updateRow": "XNGeRG_updateRow",
3750
+ "body": "XNGeRG_body",
3751
+ "fieldDisabled": "XNGeRG_fieldDisabled",
3752
+ "readOnly": "XNGeRG_readOnly",
3753
+ "field": "XNGeRG_field",
3754
+ "choiceInput": "XNGeRG_choiceInput",
3683
3755
  "headText": "XNGeRG_headText",
3684
- "label": "XNGeRG_label",
3756
+ "version": "XNGeRG_version",
3757
+ "choice": "XNGeRG_choice",
3685
3758
  "description": "XNGeRG_description",
3686
- "discard": "XNGeRG_discard",
3687
- "save": "XNGeRG_save",
3688
- "cardOpen": "XNGeRG_cardOpen",
3689
- "failed": "XNGeRG_failed",
3759
+ "pending": "XNGeRG_pending",
3690
3760
  "fieldHead": "XNGeRG_fieldHead",
3761
+ "label": "XNGeRG_label",
3691
3762
  "toggle": "XNGeRG_toggle",
3692
- "chevron": "XNGeRG_chevron",
3693
3763
  "hint": "XNGeRG_hint",
3764
+ "save": "XNGeRG_save",
3765
+ "updateRow": "XNGeRG_updateRow",
3766
+ "chevron": "XNGeRG_chevron",
3767
+ "cardOpen": "XNGeRG_cardOpen",
3694
3768
  "card": "XNGeRG_card",
3695
3769
  "header": "XNGeRG_header",
3696
- "pending": "XNGeRG_pending",
3697
- "chevronOpen": "XNGeRG_chevronOpen",
3698
- "readOnly": "XNGeRG_readOnly",
3699
- "field": "XNGeRG_field",
3700
- "version": "XNGeRG_version",
3701
3770
  "failure": "XNGeRG_failure",
3702
- "fieldDisabled": "XNGeRG_fieldDisabled",
3703
- "body": "XNGeRG_body"
3771
+ "failed": "XNGeRG_failed",
3772
+ "chevronOpen": "XNGeRG_chevronOpen",
3773
+ "discard": "XNGeRG_discard",
3774
+ "updateCopy": "XNGeRG_updateCopy",
3775
+ "update": "XNGeRG_update",
3776
+ "footer": "XNGeRG_footer",
3777
+ "name": "XNGeRG_name",
3778
+ "choiceRow": "XNGeRG_choiceRow"
3704
3779
  };
3705
3780
  //#endregion
3706
3781
  //#region src/client/SmoothStreamCard.tsx
@@ -3816,6 +3891,57 @@ window.__ModuleLoader__.load({
3816
3891
  children: t("controlScrollHint")
3817
3892
  })]
3818
3893
  }),
3894
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3895
+ className: state.enabled ? SmoothStreamCard_module_css_default.field : `${SmoothStreamCard_module_css_default.field} ${SmoothStreamCard_module_css_default.fieldDisabled}`,
3896
+ children: [
3897
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3898
+ className: SmoothStreamCard_module_css_default.fieldHead,
3899
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3900
+ className: SmoothStreamCard_module_css_default.label,
3901
+ children: t("motionPreference")
3902
+ })
3903
+ }),
3904
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3905
+ className: SmoothStreamCard_module_css_default.hint,
3906
+ children: t("motionPreferenceHint")
3907
+ }),
3908
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3909
+ className: SmoothStreamCard_module_css_default.choiceRow,
3910
+ role: "radiogroup",
3911
+ "aria-label": t("motionPreference"),
3912
+ children: [
3913
+ [
3914
+ "auto",
3915
+ "motionAuto",
3916
+ "motionAutoHint"
3917
+ ],
3918
+ [
3919
+ "force-smooth",
3920
+ "motionForceSmooth",
3921
+ "motionForceSmoothHint"
3922
+ ],
3923
+ [
3924
+ "force-reduced",
3925
+ "motionForceReduced",
3926
+ "motionForceReducedHint"
3927
+ ]
3928
+ ].map(([value, label, hint]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3929
+ className: SmoothStreamCard_module_css_default.choice,
3930
+ title: t(hint),
3931
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3932
+ type: "radio",
3933
+ className: SmoothStreamCard_module_css_default.choiceInput,
3934
+ name: "smooth-stream-motion",
3935
+ checked: state.motionPreference === value,
3936
+ disabled: !state.writable || state.saving || !state.enabled,
3937
+ onChange: () => {
3938
+ props.edit({ motionPreference: value });
3939
+ }
3940
+ }), t(label)]
3941
+ }, value))
3942
+ })
3943
+ ]
3944
+ }),
3819
3945
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3820
3946
  className: state.enabled ? SmoothStreamCard_module_css_default.field : `${SmoothStreamCard_module_css_default.field} ${SmoothStreamCard_module_css_default.fieldDisabled}`,
3821
3947
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
@@ -3958,10 +4084,11 @@ window.__ModuleLoader__.load({
3958
4084
  hooks: { smoothStreamCard: this.store },
3959
4085
  edit: (patch) => {
3960
4086
  if (this.saving) return;
3961
- if (patch.enabled !== void 0 || patch.controlScroll !== void 0 || patch.thinkAutoExpand !== void 0) this.stagedBase = {
4087
+ if (patch.enabled !== void 0 || patch.controlScroll !== void 0 || patch.motionPreference !== void 0 || patch.thinkAutoExpand !== void 0) this.stagedBase = {
3962
4088
  ...this.baseValues(),
3963
4089
  ...patch.enabled === void 0 ? {} : { enabled: patch.enabled },
3964
4090
  ...patch.controlScroll === void 0 ? {} : { controlScroll: patch.controlScroll },
4091
+ ...patch.motionPreference === void 0 ? {} : { motionPreference: patch.motionPreference },
3965
4092
  ...patch.thinkAutoExpand === void 0 ? {} : { thinkAutoExpand: patch.thinkAutoExpand }
3966
4093
  };
3967
4094
  if (this.loadedDebug !== void 0 && (patch.debugEnabled !== void 0 || patch.debugTuning !== void 0)) this.stagedDebug = {
@@ -4015,6 +4142,7 @@ window.__ModuleLoader__.load({
4015
4142
  return this.stagedBase ?? {
4016
4143
  enabled: this.loaded?.enabled ?? DEFAULT_STREAM_SETTINGS.enabled,
4017
4144
  controlScroll: this.loaded?.controlScroll ?? DEFAULT_STREAM_SETTINGS.controlScroll,
4145
+ motionPreference: this.loaded?.motionPreference ?? DEFAULT_STREAM_SETTINGS.motionPreference,
4018
4146
  thinkAutoExpand: this.loaded?.thinkAutoExpand ?? DEFAULT_STREAM_SETTINGS.thinkAutoExpand
4019
4147
  };
4020
4148
  }
@@ -4135,7 +4263,11 @@ window.__ModuleLoader__.load({
4135
4263
  "npm",
4136
4264
  "development",
4137
4265
  "unmanaged"
4138
- ].includes(data.installation) || typeof data.writable !== "boolean" || typeof data.enabled !== "boolean" || typeof data.controlScroll !== "boolean" || typeof data.thinkAutoExpand !== "boolean" || typeof data.canUpgrade !== "boolean") throw new Error("dsh-smooth-stream: malformed settings response");
4266
+ ].includes(data.installation) || typeof data.writable !== "boolean" || typeof data.enabled !== "boolean" || typeof data.controlScroll !== "boolean" || ![
4267
+ "auto",
4268
+ "force-smooth",
4269
+ "force-reduced"
4270
+ ].includes(data.motionPreference) || typeof data.thinkAutoExpand !== "boolean" || typeof data.canUpgrade !== "boolean") throw new Error("dsh-smooth-stream: malformed settings response");
4139
4271
  return data;
4140
4272
  }
4141
4273
  function upgradeView(value) {
@@ -4165,6 +4297,7 @@ window.__ModuleLoader__.load({
4165
4297
  return settingsView(accepted(await connection.rpc.call(STREAM_SETTINGS_RPC_CHANNEL, STREAM_SETTINGS_RPC.write, {
4166
4298
  enabled: settings.enabled,
4167
4299
  controlScroll: settings.controlScroll,
4300
+ motionPreference: settings.motionPreference,
4168
4301
  thinkAutoExpand: settings.thinkAutoExpand,
4169
4302
  ...settings.debugEnabled === void 0 || settings.debugTuning === void 0 ? {} : {
4170
4303
  debugEnabled: settings.debugEnabled,
@@ -4201,34 +4334,34 @@ window.__ModuleLoader__.load({
4201
4334
  tag.textContent = css;
4202
4335
  }
4203
4336
  var DebugPanel_module_css_default = {
4204
- "metric": "m4_tdG_metric",
4205
- "trigger": "m4_tdG_trigger",
4206
- "panel": "m4_tdG_panel",
4207
- "statusLive": "m4_tdG_statusLive",
4208
- "guide": "m4_tdG_guide",
4337
+ "iconButton": "m4_tdG_iconButton",
4209
4338
  "triggerActive": "m4_tdG_triggerActive",
4210
- "secondaryButton": "m4_tdG_secondaryButton",
4211
- "number": "m4_tdG_number",
4212
- "title": "m4_tdG_title",
4213
4339
  "controlHead": "m4_tdG_controlHead",
4214
- "unit": "m4_tdG_unit",
4215
- "visuallyHidden": "m4_tdG_visuallyHidden",
4216
- "panelHeader": "m4_tdG_panelHeader",
4217
- "section": "m4_tdG_section",
4340
+ "number": "m4_tdG_number",
4218
4341
  "range": "m4_tdG_range",
4219
- "unsaved": "m4_tdG_unsaved",
4220
- "state": "m4_tdG_state",
4221
- "control": "m4_tdG_control",
4342
+ "statusLive": "m4_tdG_statusLive",
4343
+ "title": "m4_tdG_title",
4344
+ "controlLabel": "m4_tdG_controlLabel",
4345
+ "guide": "m4_tdG_guide",
4346
+ "infoButton": "m4_tdG_infoButton",
4222
4347
  "numberWrap": "m4_tdG_numberWrap",
4348
+ "footer": "m4_tdG_footer",
4349
+ "control": "m4_tdG_control",
4350
+ "secondaryButton": "m4_tdG_secondaryButton",
4351
+ "unsaved": "m4_tdG_unsaved",
4223
4352
  "metrics": "m4_tdG_metrics",
4224
- "footerSpacer": "m4_tdG_footerSpacer",
4353
+ "scrollArea": "m4_tdG_scrollArea",
4354
+ "state": "m4_tdG_state",
4355
+ "metric": "m4_tdG_metric",
4356
+ "panel": "m4_tdG_panel",
4357
+ "trigger": "m4_tdG_trigger",
4225
4358
  "statusDot": "m4_tdG_statusDot",
4226
- "controlLabel": "m4_tdG_controlLabel",
4227
- "footer": "m4_tdG_footer",
4228
- "iconButton": "m4_tdG_iconButton",
4229
- "infoButton": "m4_tdG_infoButton",
4359
+ "section": "m4_tdG_section",
4360
+ "unit": "m4_tdG_unit",
4361
+ "footerSpacer": "m4_tdG_footerSpacer",
4362
+ "visuallyHidden": "m4_tdG_visuallyHidden",
4230
4363
  "primaryButton": "m4_tdG_primaryButton",
4231
- "scrollArea": "m4_tdG_scrollArea"
4364
+ "panelHeader": "m4_tdG_panelHeader"
4232
4365
  };
4233
4366
  //#endregion
4234
4367
  //#region src/client/DebugPanel.tsx
@@ -4626,6 +4759,14 @@ window.__ModuleLoader__.load({
4626
4759
  enabledHint: "Let this plugin render and follow streaming replies. Turn off to use the built-in Harness renderer.",
4627
4760
  controlScroll: "Take over scrolling",
4628
4761
  controlScrollHint: "On by default; smooth-stream writes the conversation scroll position. Turn off to leave bottom-follow to Harness.",
4762
+ motionPreference: "Motion",
4763
+ motionPreferenceHint: "How streaming responds to the system reduce-motion setting.",
4764
+ motionAuto: "Follow system",
4765
+ motionAutoHint: "Reduced-motion systems get raw text (accessibility first).",
4766
+ motionForceSmooth: "Always smooth",
4767
+ motionForceSmoothHint: "Keep the smoothing engine even when the system asks for reduced motion.",
4768
+ motionForceReduced: "Always raw",
4769
+ motionForceReducedHint: "Render raw text even when the system allows motion.",
4629
4770
  thinkAutoExpand: "Auto-expand thinking",
4630
4771
  thinkAutoExpandHint: "Open the thinking block while it streams. Turn off to keep it collapsed.",
4631
4772
  debugEnabled: "Show render diagnostics",
@@ -4705,6 +4846,14 @@ window.__ModuleLoader__.load({
4705
4846
  enabledHint: "由本插件渲染并跟随流式回复;关闭后使用 Harness 内置渲染。",
4706
4847
  controlScroll: "接管滚动",
4707
4848
  controlScrollHint: "默认开启,由丝滑流式写入会话滚动位置;关闭后贴底滚动交给 Harness。",
4849
+ motionPreference: "动效偏好",
4850
+ motionPreferenceHint: "流式渲染如何响应系统的「减少动态效果」设置。",
4851
+ motionAuto: "跟随系统",
4852
+ motionAutoHint: "系统开启减少动态效果时直接呈现原始文本(优先无障碍)。",
4853
+ motionForceSmooth: "始终平滑",
4854
+ motionForceSmoothHint: "即使系统要求减少动态效果,仍保持平滑流式渲染。",
4855
+ motionForceReduced: "始终原始",
4856
+ motionForceReducedHint: "即使系统允许动效,也直接呈现原始文本。",
4708
4857
  thinkAutoExpand: "自动展开思考",
4709
4858
  thinkAutoExpandHint: "思考块在流式时自动展开;关闭后保持折叠,可手动展开。",
4710
4859
  debugEnabled: "显示渲染调试面板",
@@ -4899,7 +5048,7 @@ window.__ModuleLoader__.load({
4899
5048
  refresh() {
4900
5049
  const next = this.read();
4901
5050
  const pending = this.card?.getSnapshot().status === "loading";
4902
- if (pending === this.pending && next.enabled === this.value.enabled && next.controlScroll === this.value.controlScroll && next.thinkAutoExpand === this.value.thinkAutoExpand && next.debugEnabled === this.value.debugEnabled && next.debugTuning === this.value.debugTuning) return;
5051
+ if (pending === this.pending && next.enabled === this.value.enabled && next.controlScroll === this.value.controlScroll && next.motionPreference === this.value.motionPreference && next.thinkAutoExpand === this.value.thinkAutoExpand && next.debugEnabled === this.value.debugEnabled && next.debugTuning === this.value.debugTuning) return;
4903
5052
  this.pending = pending;
4904
5053
  this.value = next;
4905
5054
  for (const listener of this.listeners) listener();
@@ -4999,7 +5148,8 @@ window.__ModuleLoader__.load({
4999
5148
  scrollSpeedPxPerSec: config.scrollSpeedPxPerSec,
5000
5149
  maxScrollSpeedPxPerSec: config.maxScrollSpeedPxPerSec,
5001
5150
  thinkAutoExpand: preferences.thinkAutoExpand,
5002
- controlScroll: preferences.controlScroll
5151
+ controlScroll: preferences.controlScroll,
5152
+ motionPreference: preferences.motionPreference
5003
5153
  });
5004
5154
  };
5005
5155
  ctx.slots.inject("conversation.chat.node", () => {