dsh-smooth-stream 0.3.4 → 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 +14 -4
- package/README.md +14 -4
- package/lib/client.js +2111 -140
- package/lib/client.js.map +1 -1
- package/lib/index.js +123 -6
- package/package.json +9 -9
- package/src/client/DebugPanel.module.css +370 -0
- package/src/client/DebugPanel.tsx +248 -0
- package/src/client/SmoothStreamCard.module.css +8 -0
- package/src/client/SmoothStreamCard.tsx +42 -3
- package/src/client/auto-collapse-controller.ts +977 -0
- package/src/client/debugRuntime.ts +355 -0
- package/src/client/index.ts +121 -30
- package/src/client/locales.ts +122 -0
- package/src/client/smooth-stream-card-controller.ts +97 -14
- package/src/client/smooth-stream-settings-api.ts +53 -3
- package/src/client/teleprompterGlide.ts +97 -31
- package/src/client/useFpsGuard.ts +7 -0
- package/src/client/useProgressiveDomText.ts +99 -17
- package/src/client/useSmoothStreamContent.ts +20 -4
- package/src/plugin.ts +137 -7
- package/src/settings-api.ts +12 -0
- package/src/settings.ts +55 -0
package/src/plugin.ts
CHANGED
|
@@ -7,8 +7,18 @@ import { DEFAULT_STREAM_CONFIG, type StreamConfig } from './config.ts'
|
|
|
7
7
|
import { injectStreamConfig } from './boot-config.ts'
|
|
8
8
|
import { STREAM_PACKAGE_NAME, STREAM_PACKAGE_VERSION } from './package-meta.ts'
|
|
9
9
|
import { inspectProfileInstallation, updateNpmProfilePackage } from './profile-installation.ts'
|
|
10
|
-
import {
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
STREAM_SETTINGS_RPC,
|
|
12
|
+
STREAM_SETTINGS_RPC_CHANNEL,
|
|
13
|
+
type StreamDebugSettingsView,
|
|
14
|
+
type StreamSettingsView,
|
|
15
|
+
} from './settings-api.ts'
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_STREAM_SETTINGS,
|
|
18
|
+
STREAM_SETTINGS_NS,
|
|
19
|
+
type StreamDebugTuning,
|
|
20
|
+
type StreamSettings,
|
|
21
|
+
} from './settings.ts'
|
|
12
22
|
|
|
13
23
|
/** Display name shown by the Host loader while the plugin is mounted. */
|
|
14
24
|
export const name = 'dsh-smooth-stream'
|
|
@@ -42,7 +52,21 @@ export const Config: Schema<Config> = Schema.object({
|
|
|
42
52
|
* settings provider while the browser edits it through the plugin RPC below.
|
|
43
53
|
*/
|
|
44
54
|
export const StreamSettingsSchema: Schema<StreamSettings> = Schema.object({
|
|
55
|
+
enabled: Schema.boolean().default(DEFAULT_STREAM_SETTINGS.enabled),
|
|
45
56
|
thinkAutoExpand: Schema.boolean().default(DEFAULT_STREAM_SETTINGS.thinkAutoExpand),
|
|
57
|
+
autoCollapse: Schema.boolean().default(DEFAULT_STREAM_SETTINGS.autoCollapse),
|
|
58
|
+
debugEnabled: Schema.boolean().default(DEFAULT_STREAM_SETTINGS.debugEnabled),
|
|
59
|
+
debugTuning: Schema.object({
|
|
60
|
+
revealScale: Schema.number().min(0.25).max(2).default(DEFAULT_STREAM_SETTINGS.debugTuning.revealScale),
|
|
61
|
+
queuePressure: Schema.number().min(0).max(2).default(DEFAULT_STREAM_SETTINGS.debugTuning.queuePressure),
|
|
62
|
+
maxRevealCps: Schema.number().min(120).max(1000).default(DEFAULT_STREAM_SETTINGS.debugTuning.maxRevealCps),
|
|
63
|
+
springStiffness: Schema.number().min(40).max(320).default(DEFAULT_STREAM_SETTINGS.debugTuning.springStiffness),
|
|
64
|
+
springDamping: Schema.number().min(8).max(80).default(DEFAULT_STREAM_SETTINGS.debugTuning.springDamping),
|
|
65
|
+
springMass: Schema.number().min(0.5).max(3).default(DEFAULT_STREAM_SETTINGS.debugTuning.springMass),
|
|
66
|
+
runwayPx: Schema.number().min(0).max(120).default(DEFAULT_STREAM_SETTINGS.debugTuning.runwayPx),
|
|
67
|
+
reserveResponseMs: Schema.number().min(60).max(600).default(DEFAULT_STREAM_SETTINGS.debugTuning.reserveResponseMs),
|
|
68
|
+
backpressureMinScale: Schema.number().min(0.25).max(1).default(DEFAULT_STREAM_SETTINGS.debugTuning.backpressureMinScale),
|
|
69
|
+
}),
|
|
46
70
|
})
|
|
47
71
|
|
|
48
72
|
/**
|
|
@@ -79,25 +103,61 @@ export function apply(ctx: Context, config: Config): void {
|
|
|
79
103
|
|
|
80
104
|
const view = (): StreamSettingsView => {
|
|
81
105
|
const installation = inspectProfileInstallation(connectionCtx.baseUrl, STREAM_PACKAGE_NAME)
|
|
106
|
+
const settings = scope.get()
|
|
82
107
|
return {
|
|
83
108
|
version: STREAM_PACKAGE_VERSION,
|
|
84
109
|
installation: installation.kind,
|
|
85
110
|
writable: connectionCtx.settings.writable,
|
|
86
|
-
|
|
111
|
+
enabled: settings.enabled,
|
|
112
|
+
thinkAutoExpand: settings.thinkAutoExpand,
|
|
113
|
+
autoCollapse: settings.autoCollapse,
|
|
87
114
|
canUpgrade: installation.kind === 'npm',
|
|
88
115
|
}
|
|
89
116
|
}
|
|
90
117
|
|
|
118
|
+
const debugView = (): StreamDebugSettingsView => {
|
|
119
|
+
const settings = scope.get()
|
|
120
|
+
return {
|
|
121
|
+
debugEnabled: settings.debugEnabled,
|
|
122
|
+
tuning: { ...settings.debugTuning },
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const validDebugTuning = (value: unknown): value is StreamDebugTuning => {
|
|
127
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return false
|
|
128
|
+
const tuning = value as Record<string, unknown>
|
|
129
|
+
return typeof tuning.revealScale === 'number'
|
|
130
|
+
&& tuning.revealScale >= 0.25 && tuning.revealScale <= 2
|
|
131
|
+
&& typeof tuning.queuePressure === 'number'
|
|
132
|
+
&& tuning.queuePressure >= 0 && tuning.queuePressure <= 2
|
|
133
|
+
&& typeof tuning.maxRevealCps === 'number'
|
|
134
|
+
&& tuning.maxRevealCps >= 120 && tuning.maxRevealCps <= 1000
|
|
135
|
+
&& typeof tuning.springStiffness === 'number'
|
|
136
|
+
&& tuning.springStiffness >= 40 && tuning.springStiffness <= 320
|
|
137
|
+
&& typeof tuning.springDamping === 'number'
|
|
138
|
+
&& tuning.springDamping >= 8 && tuning.springDamping <= 80
|
|
139
|
+
&& typeof tuning.springMass === 'number'
|
|
140
|
+
&& tuning.springMass >= 0.5 && tuning.springMass <= 3
|
|
141
|
+
&& typeof tuning.runwayPx === 'number'
|
|
142
|
+
&& tuning.runwayPx >= 0 && tuning.runwayPx <= 120
|
|
143
|
+
&& typeof tuning.reserveResponseMs === 'number'
|
|
144
|
+
&& tuning.reserveResponseMs >= 60 && tuning.reserveResponseMs <= 600
|
|
145
|
+
&& typeof tuning.backpressureMinScale === 'number'
|
|
146
|
+
&& tuning.backpressureMinScale >= 0.25 && tuning.backpressureMinScale <= 1
|
|
147
|
+
}
|
|
148
|
+
|
|
91
149
|
const handle: ConnectionRpcHandler = async (endpoint, payload) => {
|
|
92
150
|
if (endpoint === STREAM_SETTINGS_RPC.read) return { ok: true, value: view() }
|
|
93
151
|
if (endpoint === STREAM_SETTINGS_RPC.write) {
|
|
94
|
-
if (typeof payload !== 'object' || payload === null
|
|
95
|
-
|| typeof (payload as {
|
|
152
|
+
if (typeof payload !== 'object' || payload === null || Array.isArray(payload)
|
|
153
|
+
|| typeof (payload as { enabled?: unknown }).enabled !== 'boolean'
|
|
154
|
+
|| typeof (payload as { thinkAutoExpand?: unknown }).thinkAutoExpand !== 'boolean'
|
|
155
|
+
|| typeof (payload as { autoCollapse?: unknown }).autoCollapse !== 'boolean') {
|
|
96
156
|
return {
|
|
97
157
|
ok: false,
|
|
98
158
|
error: {
|
|
99
159
|
code: 'settings-rejected',
|
|
100
|
-
message: 'thinkAutoExpand must be
|
|
160
|
+
message: 'enabled, thinkAutoExpand and autoCollapse must be booleans',
|
|
101
161
|
details: { ns: STREAM_SETTINGS_NS },
|
|
102
162
|
},
|
|
103
163
|
}
|
|
@@ -113,7 +173,30 @@ export function apply(ctx: Context, config: Config): void {
|
|
|
113
173
|
}
|
|
114
174
|
}
|
|
115
175
|
try {
|
|
116
|
-
|
|
176
|
+
const next = payload as {
|
|
177
|
+
enabled: boolean
|
|
178
|
+
thinkAutoExpand: boolean
|
|
179
|
+
autoCollapse: boolean
|
|
180
|
+
debugEnabled?: unknown
|
|
181
|
+
debugTuning?: unknown
|
|
182
|
+
}
|
|
183
|
+
const hasDebug = next.debugEnabled !== undefined || next.debugTuning !== undefined
|
|
184
|
+
if (hasDebug && (typeof next.debugEnabled !== 'boolean' || !validDebugTuning(next.debugTuning))) {
|
|
185
|
+
return {
|
|
186
|
+
ok: false,
|
|
187
|
+
error: {
|
|
188
|
+
code: 'settings-rejected',
|
|
189
|
+
message: 'debugEnabled and debugTuning must be provided together and be valid',
|
|
190
|
+
details: { ns: STREAM_SETTINGS_NS },
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
await scope.update({
|
|
195
|
+
enabled: next.enabled,
|
|
196
|
+
thinkAutoExpand: next.thinkAutoExpand,
|
|
197
|
+
autoCollapse: next.autoCollapse,
|
|
198
|
+
...(hasDebug ? { debugEnabled: next.debugEnabled, debugTuning: next.debugTuning } : {}),
|
|
199
|
+
})
|
|
117
200
|
} catch {
|
|
118
201
|
return {
|
|
119
202
|
ok: false,
|
|
@@ -126,6 +209,53 @@ export function apply(ctx: Context, config: Config): void {
|
|
|
126
209
|
}
|
|
127
210
|
return { ok: true, value: view() }
|
|
128
211
|
}
|
|
212
|
+
if (endpoint === STREAM_SETTINGS_RPC.debugRead) return { ok: true, value: debugView() }
|
|
213
|
+
if (endpoint === STREAM_SETTINGS_RPC.debugWrite) {
|
|
214
|
+
if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
|
|
215
|
+
return {
|
|
216
|
+
ok: false,
|
|
217
|
+
error: {
|
|
218
|
+
code: 'settings-rejected',
|
|
219
|
+
message: 'debug settings must be an object',
|
|
220
|
+
details: { ns: STREAM_SETTINGS_NS },
|
|
221
|
+
},
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const next = payload as { debugEnabled?: unknown; tuning?: unknown }
|
|
225
|
+
if (typeof next.debugEnabled !== 'boolean' || !validDebugTuning(next.tuning)) {
|
|
226
|
+
return {
|
|
227
|
+
ok: false,
|
|
228
|
+
error: {
|
|
229
|
+
code: 'settings-rejected',
|
|
230
|
+
message: 'debugEnabled and tuning are malformed',
|
|
231
|
+
details: { ns: STREAM_SETTINGS_NS },
|
|
232
|
+
},
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!connectionCtx.settings.writable) {
|
|
236
|
+
return {
|
|
237
|
+
ok: false,
|
|
238
|
+
error: {
|
|
239
|
+
code: 'settings-rejected',
|
|
240
|
+
message: 'smooth-stream debug settings are read-only',
|
|
241
|
+
details: { ns: STREAM_SETTINGS_NS },
|
|
242
|
+
},
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
await scope.update({ debugEnabled: next.debugEnabled, debugTuning: next.tuning })
|
|
247
|
+
} catch {
|
|
248
|
+
return {
|
|
249
|
+
ok: false,
|
|
250
|
+
error: {
|
|
251
|
+
code: 'settings-rejected',
|
|
252
|
+
message: 'smooth-stream debug settings update failed',
|
|
253
|
+
details: { ns: STREAM_SETTINGS_NS },
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return { ok: true, value: debugView() }
|
|
258
|
+
}
|
|
129
259
|
if (endpoint === STREAM_SETTINGS_RPC.upgrade) {
|
|
130
260
|
const installation = inspectProfileInstallation(connectionCtx.baseUrl, STREAM_PACKAGE_NAME)
|
|
131
261
|
if (installation.kind !== 'npm') {
|
package/src/settings-api.ts
CHANGED
|
@@ -7,6 +7,8 @@ export const STREAM_SETTINGS_RPC_CHANNEL = '/smooth-stream'
|
|
|
7
7
|
export const STREAM_SETTINGS_RPC = {
|
|
8
8
|
read: 'settings.read',
|
|
9
9
|
write: 'settings.write',
|
|
10
|
+
debugRead: 'debug.read',
|
|
11
|
+
debugWrite: 'debug.write',
|
|
10
12
|
upgrade: 'plugin.upgrade',
|
|
11
13
|
} as const
|
|
12
14
|
|
|
@@ -21,12 +23,22 @@ export interface StreamSettingsView {
|
|
|
21
23
|
installation: StreamInstallationKind
|
|
22
24
|
/** Whether the Host settings provider permits a write. */
|
|
23
25
|
writable: boolean
|
|
26
|
+
/** Whether smooth-stream currently owns conversation rendering. */
|
|
27
|
+
enabled: boolean
|
|
24
28
|
/** Current resolved preference. */
|
|
25
29
|
thinkAutoExpand: boolean
|
|
30
|
+
/** Whether finished turns fold their work process behind a summary row. */
|
|
31
|
+
autoCollapse: boolean
|
|
26
32
|
/** Whether a fixed npm update command is safe to offer. */
|
|
27
33
|
canUpgrade: boolean
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
/** Debug-only settings returned by the diagnostics RPC. */
|
|
37
|
+
export interface StreamDebugSettingsView {
|
|
38
|
+
debugEnabled: boolean
|
|
39
|
+
tuning: import('./settings.ts').StreamDebugTuning
|
|
40
|
+
}
|
|
41
|
+
|
|
30
42
|
/** Successful package update acknowledgement; loading the new code needs a restart. */
|
|
31
43
|
export interface StreamUpgradeView {
|
|
32
44
|
restartRequired: true
|
package/src/settings.ts
CHANGED
|
@@ -10,21 +10,76 @@
|
|
|
10
10
|
/** Settings namespace registered by the Host and served through the plugin RPC. */
|
|
11
11
|
export const STREAM_SETTINGS_NS = 'smooth-stream'
|
|
12
12
|
|
|
13
|
+
/** Runtime knobs exposed only while the diagnostics switch is enabled. */
|
|
14
|
+
export interface StreamDebugTuning {
|
|
15
|
+
/** Multiplier applied to the reveal cadence (1 = preset default). */
|
|
16
|
+
revealScale: number
|
|
17
|
+
/** Multiplier for backlog pressure in the adaptive reveal queue. */
|
|
18
|
+
queuePressure: number
|
|
19
|
+
/** Upper bound for reveal cadence, in characters per second. */
|
|
20
|
+
maxRevealCps: number
|
|
21
|
+
/** Spring stiffness used by the conversation follower. */
|
|
22
|
+
springStiffness: number
|
|
23
|
+
/** Spring damping used by the conversation follower. */
|
|
24
|
+
springDamping: number
|
|
25
|
+
/** Spring mass used by the conversation follower. */
|
|
26
|
+
springMass: number
|
|
27
|
+
/** Predictive runway before the fixed conversation chrome, in pixels. */
|
|
28
|
+
runwayPx: number
|
|
29
|
+
/** Response time for opening/closing the predictive runway, in ms. */
|
|
30
|
+
reserveResponseMs: number
|
|
31
|
+
/** Lowest reveal multiplier when visual lag fills the safe paint room. */
|
|
32
|
+
backpressureMinScale: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Defaults preserve the production engine exactly. */
|
|
36
|
+
export const DEFAULT_STREAM_DEBUG_TUNING: StreamDebugTuning = {
|
|
37
|
+
revealScale: 1,
|
|
38
|
+
queuePressure: 0.85,
|
|
39
|
+
maxRevealCps: 600,
|
|
40
|
+
springStiffness: 130,
|
|
41
|
+
springDamping: 24,
|
|
42
|
+
springMass: 1,
|
|
43
|
+
runwayPx: 48,
|
|
44
|
+
reserveResponseMs: 180,
|
|
45
|
+
backpressureMinScale: 0.55,
|
|
46
|
+
}
|
|
47
|
+
|
|
13
48
|
/**
|
|
14
49
|
* Preferences a user may set. Deliberately separate from {@link StreamConfig}
|
|
15
50
|
* because the two change at different times: composition-time values go
|
|
16
51
|
* through the boot global, a live UI edit goes through the protected plugin RPC.
|
|
17
52
|
*/
|
|
18
53
|
export interface StreamSettings {
|
|
54
|
+
/**
|
|
55
|
+
* Whether this plugin replaces and wraps Harness conversation renderers.
|
|
56
|
+
* Off returns all rendering ownership to the built-in UI.
|
|
57
|
+
*/
|
|
58
|
+
enabled: boolean
|
|
19
59
|
/**
|
|
20
60
|
* Whether a reasoning ("Think") block auto-expands while it is the
|
|
21
61
|
* streaming tail. Off keeps the block collapsed — the user can still open
|
|
22
62
|
* it by hand — and stops the running state from re-owning the disclosure.
|
|
23
63
|
*/
|
|
24
64
|
thinkAutoExpand: boolean
|
|
65
|
+
/**
|
|
66
|
+
* Whether a finished turn folds its work process (thinking, tool calls,
|
|
67
|
+
* context injection, intermediate replies) behind one "已处理" summary row,
|
|
68
|
+
* leaving only the final answer visible. The summary row can always be
|
|
69
|
+
* toggled by hand, and turns still expand live while they stream.
|
|
70
|
+
*/
|
|
71
|
+
autoCollapse: boolean
|
|
72
|
+
/** Whether the live renderer diagnostics panel is enabled. */
|
|
73
|
+
debugEnabled: boolean
|
|
74
|
+
/** Values edited by the diagnostics panel. */
|
|
75
|
+
debugTuning: StreamDebugTuning
|
|
25
76
|
}
|
|
26
77
|
|
|
27
78
|
/** Defaults shared by the Host schema and the client-side fallback. */
|
|
28
79
|
export const DEFAULT_STREAM_SETTINGS: StreamSettings = {
|
|
80
|
+
enabled: true,
|
|
29
81
|
thinkAutoExpand: true,
|
|
82
|
+
autoCollapse: true,
|
|
83
|
+
debugEnabled: false,
|
|
84
|
+
debugTuning: DEFAULT_STREAM_DEBUG_TUNING,
|
|
30
85
|
}
|