dsh-smooth-stream 0.3.1 → 0.3.3

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.
@@ -28,9 +28,22 @@ type AssistantProps = ChatNodeViewProps<'assistant-step'>
28
28
  const STREAM_MODES: readonly string[] = ['typewriter', 'teleprompter']
29
29
  const STREAM_PRESETS: readonly string[] = ['realtime', 'balanced', 'silky']
30
30
 
31
- /** Human-authored rows stay on the built-in renderer; `assistant-step` is replaced. */
31
+ /**
32
+ * The assistant renderer owns its own character queue and conversation
33
+ * follower, so wrapping it again would create two scroll owners. Human input
34
+ * stays immediate; every Agent-owned output renderer goes through the same
35
+ * generic follow boundary. This is deliberately keyed by the owner that
36
+ * provides the renderer, not by individual tool names, so new Context,
37
+ * Command, and Tool rows are covered automatically.
38
+ */
32
39
  const SKIP_WRAP = new Set(['assistant-step', 'user', 'steering', 'command-input'])
33
40
 
41
+ /** React function/class or an exotic component such as memo/forwardRef/lazy. */
42
+ function isWrappableComponent(value: unknown): value is ComponentType<FollowWrapProps> {
43
+ return typeof value === 'function'
44
+ || (value !== null && typeof value === 'object' && '$$typeof' in value)
45
+ }
46
+
34
47
  /**
35
48
  * Read the Host-bridged boot config. The inline script is produced by this
36
49
  * plugin's Host half from a schema-validated value, so only the structural
@@ -60,16 +73,19 @@ function readBootConfig(): StreamConfig {
60
73
  }
61
74
 
62
75
  /**
63
- * Wrap every keyed Chat row except `assistant-step` in place. A second
76
+ * Wrap every Agent-owned keyed Chat row except the assistant renderer in
77
+ * place. A second
64
78
  * register with the same `children` table throws because the child slot is
65
79
  * already declared, and only the winning entry receives `renderSlot`;
66
80
  * swapping `entry.component` keeps the original children, locale, and inject
67
81
  * seats. `assistant-step` is replaced below so text and Think use the
68
- * typewriter reveal.
82
+ * typewriter reveal. The wrapper owns only the shared layout-growth/follow
83
+ * lifecycle; the Harness keeps each renderer's controls, disclosures, and
84
+ * cards intact.
69
85
  * @param ctx - Browser context carrying the slot registry.
70
86
  * @returns Restorer that puts the original components back.
71
87
  */
72
- function wrapGrowingChatRows(ctx: ClientContext): () => void {
88
+ function wrapAgentChatRows(ctx: ClientContext): () => void {
73
89
  const restores: Array<() => void> = []
74
90
  const wrapped = new WeakSet<object>()
75
91
 
@@ -78,7 +94,7 @@ function wrapGrowingChatRows(ctx: ClientContext): () => void {
78
94
  const key = entry.options.key
79
95
  if (key === undefined || SKIP_WRAP.has(key)) continue
80
96
  const current = entry.component
81
- if (typeof current !== 'function' || wrapped.has(current)) continue
97
+ if (!isWrappableComponent(current) || wrapped.has(current)) continue
82
98
  const inner = current as ComponentType<FollowWrapProps>
83
99
  const next = wrapFollowNodeView(inner)
84
100
  wrapped.add(next)
@@ -145,8 +161,9 @@ class PreferenceCell {
145
161
  /**
146
162
  * Register the typewriter renderer after the conversation package declares the
147
163
  * keyed Chat node seat. A lower priority shadows the built-in assistant row;
148
- * every other growing row is wrapped in place so Tool cards, retries, and
149
- * workflow runs share conversation follow. The Host-bridged configuration
164
+ * every other keyed renderer is wrapped in place so Context, commands, Tool
165
+ * cards, retries, and workflow runs share one extensible follow boundary. The
166
+ * Host-bridged configuration
150
167
  * selects the render direction, smoothing preset, and glide speed; the
151
168
  * plugin-owned settings RPC supplies the live auto-expand preference when the
152
169
  * settings surface is composed.
@@ -199,7 +216,7 @@ export function apply(ctx: ClientContext): void {
199
216
  })
200
217
  }
201
218
  ctx.slots.inject('conversation.chat.node', () => {
202
- const unwrap = wrapGrowingChatRows(ctx)
219
+ const unwrap = wrapAgentChatRows(ctx)
203
220
  const unshadow = ctx.slots.register({
204
221
  name: 'conversation.chat.node',
205
222
  key: 'assistant-step',