@runtypelabs/persona 4.23.2 → 4.25.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/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-CI7OckWI.d.cts → types-Db61aI9J.d.cts} +2 -0
- package/dist/animations/{types-CI7OckWI.d.ts → types-Db61aI9J.d.ts} +2 -0
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/animations-extra.d.cts +2 -0
- package/dist/animations-extra.d.ts +2 -0
- package/dist/approval-ui-3NMYNXBD.js +1 -0
- package/dist/approval-ui.cjs +2 -2
- package/dist/approval-ui.d.cts +278 -37
- package/dist/approval-ui.d.ts +278 -37
- package/dist/approval-ui.js +2 -2
- package/dist/{artifacts-ui-F4N6VMKF.js → artifacts-ui-IGB5YKLO.js} +4 -4
- package/dist/artifacts-ui.d.cts +273 -25
- package/dist/artifacts-ui.d.ts +273 -25
- package/dist/{chunk-NIKYKSNZ.js → chunk-7CO4T5KB.js} +1 -1
- package/dist/codegen.cjs +11 -11
- package/dist/codegen.js +13 -13
- package/dist/context-mentions.d.cts +273 -25
- package/dist/context-mentions.d.ts +273 -25
- package/dist/event-stream-view.d.cts +273 -25
- package/dist/event-stream-view.d.ts +273 -25
- package/dist/forms-ui.d.cts +319 -27
- package/dist/forms-ui.d.ts +319 -27
- package/dist/history-view.d.cts +2 -0
- package/dist/history-view.d.ts +2 -0
- package/dist/index.cjs +70 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +344 -39
- package/dist/index.d.ts +344 -39
- package/dist/index.global.js +57 -54
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +70 -69
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/install.global.js.map +1 -1
- package/dist/launcher.global.js +2 -2
- package/dist/launcher.global.js.map +1 -1
- package/dist/session-reconnect.d.cts +273 -25
- package/dist/session-reconnect.d.ts +273 -25
- package/dist/smart-dom-reader.d.cts +273 -25
- package/dist/smart-dom-reader.d.ts +273 -25
- package/dist/theme-editor-preview.cjs +74 -73
- package/dist/theme-editor-preview.d.cts +280 -25
- package/dist/theme-editor-preview.d.ts +280 -25
- package/dist/theme-editor-preview.js +51 -50
- package/dist/theme-editor.cjs +10 -10
- package/dist/theme-editor.d.cts +293 -27
- package/dist/theme-editor.d.ts +293 -27
- package/dist/theme-editor.js +10 -10
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +1522 -11
- package/dist/theme-reference.d.ts +1522 -11
- package/dist/theme-reference.js +1 -1
- package/dist/voice-runtime-7DA7MJH4.js +1 -0
- package/dist/voice-runtime.cjs +1 -1
- package/dist/voice-runtime.d.cts +33 -0
- package/dist/voice-runtime.d.ts +33 -0
- package/dist/voice-runtime.js +1 -1
- package/dist/widget.css +1 -1
- package/package.json +3 -2
- package/src/__fixtures__/defaults-parity.v4.json +776 -0
- package/src/approval-ui-entry.ts +0 -1
- package/src/approval-ui-loader.ts +5 -4
- package/src/approval-ui.ts +0 -1
- package/src/client-identity-proof.test.ts +193 -0
- package/src/client-session-warm.test.ts +216 -0
- package/src/client-visitor-history.test.ts +6 -0
- package/src/client.ts +122 -30
- package/src/codegen.test.ts +24 -0
- package/src/components/activity-row.test.ts +100 -0
- package/src/components/activity-row.ts +136 -0
- package/src/components/approval-actions.test.ts +8 -5
- package/src/components/approval-actions.ts +19 -6
- package/src/components/approval-bubble.test.ts +56 -9
- package/src/components/approval-bubble.ts +36 -41
- package/src/components/composer-builder.ts +5 -2
- package/src/components/composer-parts.ts +10 -2
- package/src/components/header-builder.test.ts +32 -0
- package/src/components/header-builder.ts +7 -4
- package/src/components/header-layouts.test.ts +12 -0
- package/src/components/header-layouts.ts +3 -2
- package/src/components/launcher.ts +11 -2
- package/src/components/message-bubble.test.ts +22 -0
- package/src/components/message-bubble.ts +7 -3
- package/src/components/panel.test.ts +52 -1
- package/src/components/panel.ts +47 -8
- package/src/components/pill-composer-builder.test.ts +2 -0
- package/src/components/pill-composer-builder.ts +0 -1
- package/src/components/reasoning-bubble.ts +29 -17
- package/src/components/suggestions.test.ts +1 -0
- package/src/components/suggestions.ts +0 -16
- package/src/components/tool-bubble.ts +131 -114
- package/src/components/tool-details.test.ts +87 -0
- package/src/components/tool-details.ts +102 -0
- package/src/components/welcome.test.ts +8 -0
- package/src/components/welcome.ts +4 -2
- package/src/defaults-parity.test.ts +158 -0
- package/src/defaults.test.ts +69 -0
- package/src/defaults.ts +128 -49
- package/src/generated/runtype-openapi-contract.ts +2 -2
- package/src/index-core.ts +4 -0
- package/src/install.test.ts +24 -0
- package/src/install.ts +3 -1
- package/src/launcher-global.ts +2 -1
- package/src/launcher-variants.test.ts +52 -0
- package/src/runtime/init-update-reset.test.ts +21 -0
- package/src/session.ts +59 -7
- package/src/session.voice.test.ts +72 -1
- package/src/styles/widget-styles.test.ts +19 -0
- package/src/styles/widget.css +295 -23
- package/src/theme-editor/index.ts +2 -0
- package/src/theme-editor/preview-utils.test.ts +8 -0
- package/src/theme-editor/preview-utils.ts +7 -3
- package/src/theme-editor/sections.test.ts +31 -0
- package/src/theme-editor/sections.ts +55 -1
- package/src/theme-editor/state-preferences.test.ts +49 -0
- package/src/theme-editor/state.ts +73 -15
- package/src/theme-reference.test.ts +20 -0
- package/src/theme-reference.ts +11 -5
- package/src/tool-call-display-defaults.test.ts +33 -1
- package/src/types/theme.ts +80 -3
- package/src/types.ts +207 -22
- package/src/ui.activity-row.test.ts +91 -0
- package/src/ui.approval-plugin.test.ts +63 -0
- package/src/ui.composer-compact.test.ts +24 -2
- package/src/ui.composer-placement.test.ts +23 -0
- package/src/ui.detached-panel.test.ts +33 -6
- package/src/ui.docked.test.ts +19 -0
- package/src/ui.header-update-stability.test.ts +25 -0
- package/src/ui.identity-proof.test.ts +58 -0
- package/src/ui.launcher-teaser.test.ts +25 -0
- package/src/ui.message-width.test.ts +17 -2
- package/src/ui.reasoning-display.test.ts +92 -0
- package/src/ui.rebuild-composer.test.ts +20 -3
- package/src/ui.scroll.test.ts +32 -0
- package/src/ui.session-init.test.ts +312 -0
- package/src/ui.status-indicator.test.ts +82 -0
- package/src/ui.suggest-replies.test.ts +11 -0
- package/src/ui.ts +422 -74
- package/src/ui.v5-defaults.test.ts +144 -0
- package/src/ui.voice-prewarm.test.ts +222 -0
- package/src/ui.welcome.test.ts +92 -3
- package/src/utils/__snapshots__/tokens-parity.test.ts.snap +811 -140
- package/src/utils/code-generators.ts +14 -0
- package/src/utils/composer-compact.test.ts +0 -2
- package/src/utils/composer-compact.ts +2 -4
- package/src/utils/composer-placement.ts +2 -3
- package/src/utils/config-merge.test.ts +81 -5
- package/src/utils/config-merge.ts +13 -3
- package/src/utils/defaults-provenance.ts +67 -0
- package/src/utils/dock.ts +7 -1
- package/src/utils/launcher-variant.ts +16 -0
- package/src/utils/panel-config.ts +97 -0
- package/src/utils/status-indicator.ts +39 -0
- package/src/utils/theme.test.ts +182 -0
- package/src/utils/theme.ts +131 -18
- package/src/utils/tokens.test.ts +122 -1
- package/src/utils/tokens.ts +177 -17
- package/src/voice/runtype-voice-provider.ts +238 -27
- package/src/voice/voice.test.ts +272 -2
- package/src/welcome.test.ts +23 -0
- package/src/welcome.ts +16 -9
- package/dist/approval-ui-K7R5Q5OC.js +0 -1
- package/dist/voice-runtime-EFR3R5ES.js +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** Headless state management for the theme editor (no DOM, no localStorage, no side effects) */
|
|
2
2
|
|
|
3
3
|
import type { AgentWidgetConfig } from '../types';
|
|
4
|
-
import type { PersonaTheme } from '../types/theme';
|
|
4
|
+
import type { DeepPartial, PersonaTheme } from '../types/theme';
|
|
5
5
|
import { createTheme } from '../utils/theme';
|
|
6
|
-
import {
|
|
6
|
+
import { resolveDefaults, resolveDefaultsVersion } from '../defaults';
|
|
7
|
+
import { deepMerge } from '../utils/deep-merge';
|
|
7
8
|
import type { ConfiguratorSnapshot, ConfigChangeListener } from './types';
|
|
8
9
|
|
|
9
10
|
// ─── Dot-path utilities ─────────────────────────────────────────
|
|
@@ -65,18 +66,29 @@ export class ThemeEditorState {
|
|
|
65
66
|
private history: ConfiguratorSnapshot[] = [];
|
|
66
67
|
private historyIndex = -1;
|
|
67
68
|
private suppressHistory = false;
|
|
69
|
+
private authoredConfig: Partial<AgentWidgetConfig> = {};
|
|
70
|
+
private authoredTheme: DeepPartial<PersonaTheme> = {};
|
|
71
|
+
private mergeDefaults = true;
|
|
72
|
+
private snapshotIntent = new WeakMap<ConfiguratorSnapshot, { config: Partial<AgentWidgetConfig>; theme: DeepPartial<PersonaTheme> }>();
|
|
68
73
|
|
|
69
74
|
constructor(
|
|
70
|
-
initialTheme?:
|
|
75
|
+
initialTheme?: DeepPartial<PersonaTheme>,
|
|
71
76
|
initialConfig?: Partial<AgentWidgetConfig>,
|
|
72
77
|
options?: { mergeDefaults?: boolean }
|
|
73
78
|
) {
|
|
74
79
|
const mergeDefaults = options?.mergeDefaults ?? true;
|
|
80
|
+
this.mergeDefaults = mergeDefaults;
|
|
81
|
+
this.authoredConfig = { ...initialConfig };
|
|
82
|
+
this.authoredTheme = initialTheme ?? {};
|
|
83
|
+
const defaults = resolveDefaults(initialConfig);
|
|
75
84
|
this.config = (mergeDefaults
|
|
76
|
-
? { ...
|
|
77
|
-
: (initialConfig ??
|
|
85
|
+
? { ...defaults, ...initialConfig }
|
|
86
|
+
: (initialConfig ?? defaults)
|
|
78
87
|
) as AgentWidgetConfig;
|
|
79
|
-
this.theme = createTheme(initialTheme, {
|
|
88
|
+
this.theme = createTheme(initialTheme, {
|
|
89
|
+
validate: false,
|
|
90
|
+
future: this.config.future,
|
|
91
|
+
});
|
|
80
92
|
this.syncThemeIntoConfig();
|
|
81
93
|
this.pushHistorySnapshot(this.exportSnapshot(), true);
|
|
82
94
|
}
|
|
@@ -116,13 +128,15 @@ export class ThemeEditorState {
|
|
|
116
128
|
* - everything else → writes into AgentWidgetConfig
|
|
117
129
|
*/
|
|
118
130
|
set(path: string, value: unknown): void {
|
|
131
|
+
const previousVersion = resolveDefaultsVersion(this.config);
|
|
132
|
+
this.recordIntent(path, value);
|
|
119
133
|
if (path.startsWith('theme.')) {
|
|
120
134
|
const themePath = path.replace('theme.', '');
|
|
121
135
|
this.theme = setByPath(this.theme, themePath, value) as PersonaTheme;
|
|
122
136
|
this.syncThemeIntoConfig();
|
|
123
137
|
} else if (path.startsWith('darkTheme.')) {
|
|
124
138
|
const themePath = path.replace('darkTheme.', '');
|
|
125
|
-
const dark = this.config.darkTheme ?? createTheme();
|
|
139
|
+
const dark = this.config.darkTheme ?? createTheme(undefined, { future: this.config.future });
|
|
126
140
|
this.config = {
|
|
127
141
|
...this.config,
|
|
128
142
|
darkTheme: setByPath(dark, themePath, value) as AgentWidgetConfig['darkTheme'],
|
|
@@ -131,12 +145,15 @@ export class ThemeEditorState {
|
|
|
131
145
|
this.config = setByPath(this.config, path, value) as AgentWidgetConfig;
|
|
132
146
|
}
|
|
133
147
|
|
|
148
|
+
this.rebaseDefaults(previousVersion);
|
|
134
149
|
this.recordHistory();
|
|
135
150
|
this.notifyListeners();
|
|
136
151
|
}
|
|
137
152
|
|
|
138
153
|
/** Delete a dot-path and prune empty parents so sparse preferences inherit. */
|
|
139
154
|
unset(path: string): void {
|
|
155
|
+
const previousVersion = resolveDefaultsVersion(this.config);
|
|
156
|
+
this.recordIntent(path, undefined, true);
|
|
140
157
|
if (path.startsWith('theme.')) {
|
|
141
158
|
const themePath = path.replace('theme.', '');
|
|
142
159
|
this.theme = unsetByPath(this.theme, themePath) as PersonaTheme;
|
|
@@ -153,24 +170,27 @@ export class ThemeEditorState {
|
|
|
153
170
|
} else {
|
|
154
171
|
this.config = unsetByPath(this.config, path) as AgentWidgetConfig;
|
|
155
172
|
}
|
|
173
|
+
this.rebaseDefaults(previousVersion);
|
|
156
174
|
this.recordHistory();
|
|
157
175
|
this.notifyListeners();
|
|
158
176
|
}
|
|
159
177
|
|
|
160
178
|
/** Batch-set multiple paths at once */
|
|
161
179
|
setBatch(updates: Record<string, unknown>): void {
|
|
180
|
+
const previousVersion = resolveDefaultsVersion(this.config);
|
|
162
181
|
let themeChanged = false;
|
|
163
182
|
let darkThemeChanged = false;
|
|
164
183
|
let configChanged = false;
|
|
165
184
|
|
|
166
185
|
for (const [path, value] of Object.entries(updates)) {
|
|
186
|
+
this.recordIntent(path, value);
|
|
167
187
|
if (path.startsWith('theme.')) {
|
|
168
188
|
const themePath = path.replace('theme.', '');
|
|
169
189
|
this.theme = setByPath(this.theme, themePath, value) as PersonaTheme;
|
|
170
190
|
themeChanged = true;
|
|
171
191
|
} else if (path.startsWith('darkTheme.')) {
|
|
172
192
|
const themePath = path.replace('darkTheme.', '');
|
|
173
|
-
const dark = this.config.darkTheme ?? createTheme();
|
|
193
|
+
const dark = this.config.darkTheme ?? createTheme(undefined, { future: this.config.future });
|
|
174
194
|
this.config = {
|
|
175
195
|
...this.config,
|
|
176
196
|
darkTheme: setByPath(dark, themePath, value) as AgentWidgetConfig['darkTheme'],
|
|
@@ -185,6 +205,7 @@ export class ThemeEditorState {
|
|
|
185
205
|
if (themeChanged) {
|
|
186
206
|
this.syncThemeIntoConfig();
|
|
187
207
|
}
|
|
208
|
+
this.rebaseDefaults(previousVersion);
|
|
188
209
|
if (themeChanged || darkThemeChanged || configChanged) {
|
|
189
210
|
this.recordHistory();
|
|
190
211
|
this.notifyListeners();
|
|
@@ -193,6 +214,7 @@ export class ThemeEditorState {
|
|
|
193
214
|
|
|
194
215
|
/** Replace the entire theme */
|
|
195
216
|
setTheme(theme: PersonaTheme): void {
|
|
217
|
+
this.authoredTheme = theme;
|
|
196
218
|
this.theme = theme;
|
|
197
219
|
this.syncThemeIntoConfig();
|
|
198
220
|
this.recordHistory();
|
|
@@ -201,6 +223,8 @@ export class ThemeEditorState {
|
|
|
201
223
|
|
|
202
224
|
/** Replace the entire config (for preset loading) */
|
|
203
225
|
setFullConfig(config: AgentWidgetConfig, theme?: PersonaTheme): void {
|
|
226
|
+
this.authoredConfig = { ...config };
|
|
227
|
+
if (theme) this.authoredTheme = theme;
|
|
204
228
|
this.config = { ...config };
|
|
205
229
|
if (theme) {
|
|
206
230
|
this.theme = theme;
|
|
@@ -221,21 +245,27 @@ export class ThemeEditorState {
|
|
|
221
245
|
if ('config' in parsed || 'theme' in parsed || parsed.version === 2) {
|
|
222
246
|
const config = (parsed.config ?? this.config) as AgentWidgetConfig;
|
|
223
247
|
const theme = createTheme(
|
|
224
|
-
(parsed.theme ?? this.theme) as
|
|
225
|
-
{ validate: false }
|
|
248
|
+
(parsed.theme ?? this.theme) as DeepPartial<PersonaTheme>,
|
|
249
|
+
{ validate: false, future: config.future }
|
|
226
250
|
);
|
|
227
251
|
this.setFullConfig(config, theme);
|
|
228
252
|
return;
|
|
229
253
|
}
|
|
230
254
|
|
|
231
|
-
const theme = createTheme(parsed as
|
|
255
|
+
const theme = createTheme(parsed as DeepPartial<PersonaTheme>, { validate: false, future: this.config.future });
|
|
232
256
|
this.setTheme(theme);
|
|
233
257
|
}
|
|
234
258
|
|
|
235
259
|
/** Reset to defaults */
|
|
236
260
|
resetToDefaults(): void {
|
|
237
|
-
this.config
|
|
238
|
-
this.
|
|
261
|
+
const future = this.config.future;
|
|
262
|
+
this.authoredConfig = future ? { future } : {};
|
|
263
|
+
this.authoredTheme = {};
|
|
264
|
+
this.config = {
|
|
265
|
+
...resolveDefaults({ future }),
|
|
266
|
+
...(future ? { future } : {}),
|
|
267
|
+
} as AgentWidgetConfig;
|
|
268
|
+
this.theme = createTheme(undefined, { future: this.config.future });
|
|
239
269
|
this.syncThemeIntoConfig();
|
|
240
270
|
this.history = [];
|
|
241
271
|
this.historyIndex = -1;
|
|
@@ -295,6 +325,27 @@ export class ThemeEditorState {
|
|
|
295
325
|
|
|
296
326
|
// ─── Private ────────────────────────────────────────────────
|
|
297
327
|
|
|
328
|
+
private recordIntent(path: string, value: unknown, remove = false): void {
|
|
329
|
+
const write = (target: object, key: string) => remove || value === undefined
|
|
330
|
+
? unsetByPath(target, key) : setByPath(target, key, value);
|
|
331
|
+
if (path.startsWith('theme.')) {
|
|
332
|
+
this.authoredTheme = write(this.authoredTheme, path.slice(6)) as DeepPartial<PersonaTheme>;
|
|
333
|
+
} else if (path === 'theme') {
|
|
334
|
+
this.authoredTheme = (value ?? {}) as DeepPartial<PersonaTheme>;
|
|
335
|
+
} else {
|
|
336
|
+
this.authoredConfig = write(this.authoredConfig, path) as Partial<AgentWidgetConfig>;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private rebaseDefaults(previousVersion: string): void {
|
|
341
|
+
if (previousVersion === resolveDefaultsVersion(this.config)) return;
|
|
342
|
+
this.config = (this.mergeDefaults
|
|
343
|
+
? deepMerge(resolveDefaults(this.authoredConfig), this.authoredConfig)
|
|
344
|
+
: { ...this.authoredConfig }) as AgentWidgetConfig;
|
|
345
|
+
this.theme = createTheme(this.authoredTheme, { validate: false, future: this.config.future });
|
|
346
|
+
this.syncThemeIntoConfig();
|
|
347
|
+
}
|
|
348
|
+
|
|
298
349
|
private syncThemeIntoConfig(): void {
|
|
299
350
|
this.config = {
|
|
300
351
|
...this.config,
|
|
@@ -314,6 +365,7 @@ export class ThemeEditorState {
|
|
|
314
365
|
|
|
315
366
|
private pushHistorySnapshot(snapshot: ConfiguratorSnapshot, replaceCurrent = false): void {
|
|
316
367
|
if (this.suppressHistory) return;
|
|
368
|
+
this.snapshotIntent.set(snapshot, { config: this.authoredConfig, theme: this.authoredTheme });
|
|
317
369
|
|
|
318
370
|
const serialized = JSON.stringify(snapshot);
|
|
319
371
|
const currentSerialized =
|
|
@@ -326,7 +378,10 @@ export class ThemeEditorState {
|
|
|
326
378
|
return;
|
|
327
379
|
}
|
|
328
380
|
|
|
329
|
-
|
|
381
|
+
const currentIntent = this.historyIndex >= 0
|
|
382
|
+
? this.snapshotIntent.get(this.history[this.historyIndex]) : undefined;
|
|
383
|
+
if (serialized === currentSerialized &&
|
|
384
|
+
JSON.stringify(this.snapshotIntent.get(snapshot)) === JSON.stringify(currentIntent)) return;
|
|
330
385
|
|
|
331
386
|
this.history = this.history.slice(0, this.historyIndex + 1);
|
|
332
387
|
this.history.push(snapshot);
|
|
@@ -335,8 +390,11 @@ export class ThemeEditorState {
|
|
|
335
390
|
|
|
336
391
|
private restoreSnapshot(snapshot: ConfiguratorSnapshot): void {
|
|
337
392
|
this.suppressHistory = true;
|
|
393
|
+
const intent = this.snapshotIntent.get(snapshot);
|
|
394
|
+
this.authoredConfig = intent?.config ?? snapshot.config as Partial<AgentWidgetConfig>;
|
|
395
|
+
this.authoredTheme = intent?.theme ?? snapshot.theme;
|
|
338
396
|
this.config = snapshot.config as unknown as AgentWidgetConfig;
|
|
339
|
-
this.theme = createTheme(snapshot.theme, { validate: false });
|
|
397
|
+
this.theme = createTheme(snapshot.theme, { validate: false, future: this.config.future });
|
|
340
398
|
this.syncThemeIntoConfig();
|
|
341
399
|
this.suppressHistory = false;
|
|
342
400
|
this.notifyListeners();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { getThemeReference } from "./theme-reference";
|
|
4
|
+
import { resolveDefaults } from "./defaults";
|
|
5
|
+
import { resolveThemeDefaults } from "./utils/tokens";
|
|
6
|
+
|
|
7
|
+
describe.each([false, true])("theme reference defaults versioning (v5=%s)", (v5Defaults) => {
|
|
8
|
+
it("reads its documented defaults from the shared future-aware resolvers", () => {
|
|
9
|
+
const reference = getThemeReference({ v5Defaults });
|
|
10
|
+
const future = { v5Defaults };
|
|
11
|
+
|
|
12
|
+
expect(reference.defaultsVersion).toBe(v5Defaults ? "v5" : "v4");
|
|
13
|
+
expect(reference.themeDefaultsVersion).toBe(v5Defaults ? "v5" : "v4");
|
|
14
|
+
expect(reference.defaultColorPalette).toBe(
|
|
15
|
+
resolveThemeDefaults(future).palette.colors
|
|
16
|
+
);
|
|
17
|
+
expect(reference.defaultRadius).toBe(resolveThemeDefaults(future).palette.radius);
|
|
18
|
+
expect(reference.defaultLauncher).toEqual(resolveDefaults({ future }).launcher);
|
|
19
|
+
});
|
|
20
|
+
});
|
package/src/theme-reference.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* out of the IIFE widget bundle. Intended for AI/MCP tool consumption.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { resolveThemeDefaults, resolveThemeDefaultsVersion } from './utils/tokens'
|
|
9
|
+
import { resolveDefaults, resolveDefaultsVersion } from './defaults'
|
|
9
10
|
import type { DeepPartial, PersonaTheme } from './types/theme'
|
|
10
11
|
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
@@ -173,7 +174,7 @@ export const THEME_TOKEN_DOCS = {
|
|
|
173
174
|
'Shared scrollbar appearance for every scroller: thumb (default semantic.colors.border), track (default transparent).',
|
|
174
175
|
reasoningBubble: 'shadow: reasoning/thinking row box-shadow.',
|
|
175
176
|
composer:
|
|
176
|
-
'Message input form: shadow (box-shadow), borderColor, padding (form inset), gap (textarea row to actions row), fontSize and lineHeight (textarea type), controlSize (edge of every icon control in the action row: attachment, mention, mic, action buttons, overflow trigger, icon-mode send; default 40px) and controlIconSize (glyph inside them, default 24px). Per-control config keys (sendButton.size, voiceRecognition.iconSize) override controlSize; coarse-pointer devices floor the hit area at 40px. overlayBand paints the band behind an overlaid footer (composer.placement: "overlay"). segmented styles a segmented mode-group track (trackBackground, trackBorderRadius, padding, activeBackground, activeForeground, activeShadow, inactiveForeground, itemPadding); every key is optional and the stylesheet owns the defaults. modelPicker styles the model picker (background, hoverBackground, borderColor, borderRadius and labelColor on the closed control, which the native select and the popover trigger share; menuBackground, menuBorderRadius, rowHoverBackground, descriptionColor and suffixColor on the popover only), also all optional. overflowMenu styles the composer overflow "+" menu panel (background, borderColor, borderRadius, foreground, shadow), also all optional.',
|
|
177
|
+
'Message input form: sendIconSize (send/stop glyph; explicit sendButton.iconSize wins), sendButtonRadius (icon-mode submit radius), footerBorder (footer top border shorthand; none hides it), shadow (box-shadow), borderColor, focusBorderColor (focused form border; V4 keeps the resting border, V5 uses a stronger neutral mix), focusRing (CSS outline shorthand, 1px offset; V4 none, V5 a thin neutral ring; preserves shadow and causes no layout shift), padding (form inset), gap (textarea row to actions row), fontSize and lineHeight (textarea type), controlSize (edge of every icon control in the action row: attachment, mention, mic, action buttons, overflow trigger, icon-mode send; default 40px) and controlIconSize (glyph inside them, default 24px). Per-control config keys (sendButton.size, voiceRecognition.iconSize) override controlSize; coarse-pointer devices floor the hit area at 40px. overlayBand paints the band behind an overlaid footer (composer.placement: "overlay"). segmented styles a segmented mode-group track (trackBackground, trackBorderRadius, padding, activeBackground, activeForeground, activeShadow, inactiveForeground, itemPadding); every key is optional and the stylesheet owns the defaults. modelPicker styles the model picker (background, hoverBackground, borderColor, borderRadius and labelColor on the closed control, which the native select and the popover trigger share; menuBackground, menuBorderRadius, rowHoverBackground, descriptionColor and suffixColor on the popover only), also all optional. overflowMenu styles the composer overflow "+" menu panel (background, borderColor, borderRadius, foreground, shadow), also all optional.',
|
|
177
178
|
artifact:
|
|
178
179
|
'toolbar (icon styling, copy menu), tab (background, active states), pane (background, toolbarBackground).',
|
|
179
180
|
},
|
|
@@ -466,11 +467,16 @@ export const THEME_EXAMPLES: Record<string, ThemeExample> = {
|
|
|
466
467
|
* Returns token system docs, the default color palette and radius scale,
|
|
467
468
|
* example themes, and a list of SDK-bundled presets.
|
|
468
469
|
*/
|
|
469
|
-
export function getThemeReference() {
|
|
470
|
+
export function getThemeReference(future?: { v5Defaults?: boolean }) {
|
|
471
|
+
const themeDefaults = resolveThemeDefaults(future)
|
|
472
|
+
const defaults = resolveDefaults({ future })
|
|
470
473
|
return {
|
|
471
474
|
tokenDocs: THEME_TOKEN_DOCS,
|
|
472
|
-
|
|
473
|
-
|
|
475
|
+
defaultsVersion: resolveDefaultsVersion({ future }),
|
|
476
|
+
themeDefaultsVersion: resolveThemeDefaultsVersion(future),
|
|
477
|
+
defaultColorPalette: themeDefaults.palette.colors,
|
|
478
|
+
defaultRadius: themeDefaults.palette.radius,
|
|
479
|
+
defaultLauncher: defaults.launcher,
|
|
474
480
|
examples: THEME_EXAMPLES,
|
|
475
481
|
sdkPresets: ['shop', 'minimal', 'fullscreen'],
|
|
476
482
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
|
-
import { DEFAULT_WIDGET_CONFIG } from "./defaults";
|
|
3
|
+
import { DEFAULT_WIDGET_CONFIG, mergeWithDefaults } from "./defaults";
|
|
4
4
|
|
|
5
5
|
describe("tool call display defaults", () => {
|
|
6
6
|
it("keeps advanced tool call transcript modes disabled by default", () => {
|
|
@@ -23,4 +23,36 @@ describe("tool call display defaults", () => {
|
|
|
23
23
|
loadingAnimation: "none",
|
|
24
24
|
});
|
|
25
25
|
});
|
|
26
|
+
|
|
27
|
+
it("fills omitted tool display options when one option is overridden", () => {
|
|
28
|
+
const override = { collapsedMode: "tool-name" as const, expandable: false };
|
|
29
|
+
expect(mergeWithDefaults({ features: { toolCallDisplay: override } }).features?.toolCallDisplay)
|
|
30
|
+
.toEqual({ ...DEFAULT_WIDGET_CONFIG.features?.toolCallDisplay, ...override });
|
|
31
|
+
expect(DEFAULT_WIDGET_CONFIG.features?.toolCallDisplay?.collapsedMode).toBe("tool-call");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("fills omitted reasoning display options without losing explicit false or zero", () => {
|
|
35
|
+
const override = { activePreview: false, previewMaxLines: 0, expandable: false };
|
|
36
|
+
expect(mergeWithDefaults({ features: { reasoningDisplay: override } }).features?.reasoningDisplay)
|
|
37
|
+
.toEqual({ ...DEFAULT_WIDGET_CONFIG.features?.reasoningDisplay, ...override });
|
|
38
|
+
expect(DEFAULT_WIDGET_CONFIG.features?.reasoningDisplay?.previewMaxLines).toBe(3);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("retains optional display settings and leaves unrelated features intact", () => {
|
|
42
|
+
const result = mergeWithDefaults({
|
|
43
|
+
features: {
|
|
44
|
+
showToolCalls: false,
|
|
45
|
+
toolCallDisplay: { grouped: true, activeMinHeight: "100px", completedVisibility: "removed" },
|
|
46
|
+
reasoningDisplay: { loadingAnimation: "shimmer" },
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
expect(result.features?.showToolCalls).toBe(false);
|
|
50
|
+
expect(result.features?.toolCallDisplay?.activeMinHeight).toBe("100px");
|
|
51
|
+
expect(result.features?.toolCallDisplay?.completedVisibility).toBe("removed");
|
|
52
|
+
expect(result.features?.toolCallDisplay?.grouped).toBe(true);
|
|
53
|
+
expect(result.features?.reasoningDisplay).toEqual({
|
|
54
|
+
...DEFAULT_WIDGET_CONFIG.features?.reasoningDisplay,
|
|
55
|
+
loadingAnimation: "shimmer",
|
|
56
|
+
});
|
|
57
|
+
});
|
|
26
58
|
});
|
package/src/types/theme.ts
CHANGED
|
@@ -212,6 +212,8 @@ export interface InputTokens extends ComponentTokenSet {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
export interface LauncherTokens extends ComponentTokenSet {
|
|
215
|
+
/** Distance from the viewport edges. Circle default: 20px; pill: 24px. */
|
|
216
|
+
offset?: string;
|
|
215
217
|
size: string;
|
|
216
218
|
iconSize: string;
|
|
217
219
|
shadow: TokenReference<'shadow'>;
|
|
@@ -235,6 +237,15 @@ export interface PanelTokens extends ComponentTokenSet {
|
|
|
235
237
|
inset?: string;
|
|
236
238
|
/** Background of the region revealed behind a detached panel. */
|
|
237
239
|
canvasBackground?: string;
|
|
240
|
+
/** Chrome defaults by rendered panel mode. */
|
|
241
|
+
modes?: Partial<Record<'floating' | 'inline' | 'sidebar' | 'docked' | 'mobile', {
|
|
242
|
+
/** Full border shorthand (for example, `"1px solid var(--persona-border)"`). */
|
|
243
|
+
border?: string;
|
|
244
|
+
/** Box-shadow token reference or raw CSS shadow. */
|
|
245
|
+
shadow?: string;
|
|
246
|
+
/** Corner radius token reference or raw CSS radius. */
|
|
247
|
+
borderRadius?: TokenReference<'radius'>;
|
|
248
|
+
}>>;
|
|
238
249
|
}
|
|
239
250
|
|
|
240
251
|
/** Per-element text styling shared by themable text surfaces. */
|
|
@@ -263,10 +274,16 @@ export interface HeaderTokens extends ComponentTokenSet {
|
|
|
263
274
|
*/
|
|
264
275
|
border?: TokenReference<'color'>;
|
|
265
276
|
borderRadius: TokenReference<'radius'>;
|
|
277
|
+
/** Standard-header padding. @default "20px 24px" */
|
|
278
|
+
padding?: string;
|
|
279
|
+
/** Minimal-header padding. Explicitly wins over `padding`. @default "16px 24px" */
|
|
280
|
+
minimalPadding?: string;
|
|
266
281
|
/** Background of the rounded avatar tile next to the title (Lucide / emoji / image). */
|
|
267
282
|
iconBackground: TokenReference<'color'>;
|
|
268
283
|
/** Foreground (glyph stroke or emoji text) on the header avatar tile. */
|
|
269
284
|
iconForeground: TokenReference<'color'>;
|
|
285
|
+
/** Header identity glyph size as a fraction of its icon box. @default "0.6" */
|
|
286
|
+
iconScale?: string;
|
|
270
287
|
/**
|
|
271
288
|
* Legacy alias of `title.color`; `title.color` wins when both are set.
|
|
272
289
|
* Unset, the title takes `foreground`, then the primary color.
|
|
@@ -305,6 +322,14 @@ export interface HeaderTokens extends ComponentTokenSet {
|
|
|
305
322
|
* sibling. @default "1.5"
|
|
306
323
|
*/
|
|
307
324
|
controlStrokeWidth?: string;
|
|
325
|
+
/** Header control corner radius. Per-control radius overrides win. */
|
|
326
|
+
controlBorderRadius?: string;
|
|
327
|
+
/** Default control hover/focus fill. Explicit button backgrounds win. */
|
|
328
|
+
controlHoverBackground?: TokenReference<'color'>;
|
|
329
|
+
/** Default control hover/focus icon color. Explicit button colors win. */
|
|
330
|
+
controlHoverForeground?: TokenReference<'color'>;
|
|
331
|
+
/** Keyboard focus outline; unset preserves the browser default. */
|
|
332
|
+
controlFocusOutline?: string;
|
|
308
333
|
/** Box-shadow on the header (e.g., a fade shadow to replace the default border). */
|
|
309
334
|
shadow?: string;
|
|
310
335
|
/** Override the header bottom border (e.g., `none`). */
|
|
@@ -484,6 +509,12 @@ export interface MessageGeometryTokens {
|
|
|
484
509
|
}
|
|
485
510
|
|
|
486
511
|
export interface MessageTokens {
|
|
512
|
+
/** Height of the optional transcript top fade. Default 18px. */
|
|
513
|
+
topFadeHeight?: string;
|
|
514
|
+
/** Vertical gap between transcript turns. @default "12px" */
|
|
515
|
+
gap?: string;
|
|
516
|
+
/** Gap between turns in fullscreen mode; falls back to `gap`. */
|
|
517
|
+
fullscreenGap?: string;
|
|
487
518
|
user: MessageGeometryTokens & {
|
|
488
519
|
background: TokenReference<'color'>;
|
|
489
520
|
text: TokenReference<'color'>;
|
|
@@ -497,6 +528,8 @@ export interface MessageTokens {
|
|
|
497
528
|
borderRadius: TokenReference<'radius'>;
|
|
498
529
|
/** Assistant bubble border color (CSS color). */
|
|
499
530
|
border?: TokenReference<'color'>;
|
|
531
|
+
/** Border width; set to 0px for a flat assistant response. @default "1px" */
|
|
532
|
+
borderWidth?: string;
|
|
500
533
|
/** Assistant bubble box-shadow (token ref or raw CSS, e.g. `none`). */
|
|
501
534
|
shadow?: string;
|
|
502
535
|
};
|
|
@@ -622,13 +655,42 @@ export interface AttachmentTokens {
|
|
|
622
655
|
}
|
|
623
656
|
|
|
624
657
|
/** Tool-call row chrome (collapsible tool bubbles). */
|
|
625
|
-
export interface
|
|
658
|
+
export interface ActivityTokens {
|
|
659
|
+
/** Minimum activity row height. */
|
|
660
|
+
rowHeight?: string;
|
|
661
|
+
/** Group child indentation; falls back to indent when unset. V5: 0px. */
|
|
662
|
+
groupIndent?: string;
|
|
663
|
+
/** Extra vertical gap between grouped rows. V5: 0px. */
|
|
664
|
+
groupGap?: string;
|
|
665
|
+
/** Padding around expanded group children. V5: 0px. */
|
|
666
|
+
groupPadding?: string;
|
|
667
|
+
/** Gap between consecutive activity entries. V5: 0px. */
|
|
668
|
+
transcriptGap?: string;
|
|
669
|
+
/** Gap from activity to an assistant response. V5: 12px. */
|
|
670
|
+
responseGap?: string;
|
|
671
|
+
/** Expanded body padding. V5: 4px 0. */
|
|
672
|
+
bodyPadding?: string;
|
|
673
|
+
/** Expanded reasoning text size. V5: 13px. */
|
|
674
|
+
bodySize?: string;
|
|
675
|
+
/** Expanded reasoning line height. V5: 1.5. */
|
|
676
|
+
bodyLineHeight?: string;
|
|
677
|
+
/** Expanded reasoning font weight. V5: 400. */
|
|
678
|
+
bodyFontWeight?: string;
|
|
679
|
+
/** Gap between expanded detail sections. V5: 8px. */
|
|
680
|
+
sectionGap?: string;
|
|
681
|
+
labelSize?: string;
|
|
682
|
+
iconSize?: string;
|
|
683
|
+
indent?: string;
|
|
684
|
+
bodySurface?: TokenReference<'color'>;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
export interface ToolBubbleTokens extends ActivityTokens {
|
|
626
688
|
/** Box-shadow for tool bubbles (token ref or raw CSS, e.g. `none`). */
|
|
627
689
|
shadow: string;
|
|
628
690
|
}
|
|
629
691
|
|
|
630
692
|
/** Reasoning / “thinking” row chrome. */
|
|
631
|
-
export interface ReasoningBubbleTokens {
|
|
693
|
+
export interface ReasoningBubbleTokens extends ActivityTokens {
|
|
632
694
|
shadow: string;
|
|
633
695
|
}
|
|
634
696
|
|
|
@@ -769,6 +831,10 @@ export interface ComposerChromeTokens {
|
|
|
769
831
|
shadow: string;
|
|
770
832
|
/** Border color of the composer form. @default semantic.colors.border */
|
|
771
833
|
borderColor?: TokenReference<'color'>;
|
|
834
|
+
/** Focused form border color; falls back to borderColor. V5: a stronger neutral border. */
|
|
835
|
+
focusBorderColor?: TokenReference<'color'>;
|
|
836
|
+
/** Outer focus ring (CSS outline shorthand). V4: none; V5: a thin neutral ring. */
|
|
837
|
+
focusRing?: string;
|
|
772
838
|
/** Inner padding of the composer form (raw CSS shorthand). @default "0.75rem 1rem" */
|
|
773
839
|
padding?: string;
|
|
774
840
|
/** Gap between the textarea row and the actions row. @default "0.5rem" */
|
|
@@ -787,12 +853,18 @@ export interface ComposerChromeTokens {
|
|
|
787
853
|
controlSize?: string;
|
|
788
854
|
/** Glyph box inside those controls. @default "24px" */
|
|
789
855
|
controlIconSize?: string;
|
|
856
|
+
/** Send/stop glyph size. Explicit sendButton.iconSize wins. V5: 18px. */
|
|
857
|
+
sendIconSize?: string;
|
|
858
|
+
/** Icon-mode submit button radius. V4 inherits the primary button; V5: 9999px. */
|
|
859
|
+
sendButtonRadius?: string;
|
|
860
|
+
/** Footer top border (CSS shorthand). V4: 1px solid divider; V5: none. */
|
|
861
|
+
footerBorder?: string;
|
|
790
862
|
/**
|
|
791
863
|
* Background painted behind an overlaid composer footer
|
|
792
864
|
* (`composer.placement: "overlay"`). Any CSS `background` value, gradients
|
|
793
865
|
* included — same convention as the shadow tokens. Inert under
|
|
794
866
|
* `placement: "block"`.
|
|
795
|
-
* @default "transparent"
|
|
867
|
+
* @default "transparent" (V4); a theme-aware 24px fade (V5)
|
|
796
868
|
*/
|
|
797
869
|
overlayBand?: string;
|
|
798
870
|
/** Segmented mode-group track. */
|
|
@@ -979,6 +1051,8 @@ export interface ScrollToBottomTokens extends ComponentTokenSet {
|
|
|
979
1051
|
|
|
980
1052
|
/** Visual tokens shared by one suggestion presentation variant. */
|
|
981
1053
|
export interface SuggestionVariantTokens extends ComponentTokenSet {
|
|
1054
|
+
/** Label weight. V4: 500; V5: 400. */
|
|
1055
|
+
fontWeight?: string;
|
|
982
1056
|
/** Space inside one item, between its icon and its copy. */
|
|
983
1057
|
gap?: string;
|
|
984
1058
|
/** Space between suggestion items in the container. */
|
|
@@ -1030,6 +1104,7 @@ export interface ComponentTokens {
|
|
|
1030
1104
|
voice: VoiceTokens;
|
|
1031
1105
|
approval: ApprovalTokens;
|
|
1032
1106
|
attachment: AttachmentTokens;
|
|
1107
|
+
activity?: ActivityTokens;
|
|
1033
1108
|
toolBubble: ToolBubbleTokens;
|
|
1034
1109
|
reasoningBubble: ReasoningBubbleTokens;
|
|
1035
1110
|
/** Event-stream inspector badge chips. */
|
|
@@ -1124,4 +1199,6 @@ export interface CreateThemeOptions {
|
|
|
1124
1199
|
plugins?: PersonaThemePlugin[];
|
|
1125
1200
|
validate?: boolean;
|
|
1126
1201
|
extend?: PersonaTheme;
|
|
1202
|
+
/** Selects the defaults overlay; explicit theme tokens always take precedence. */
|
|
1203
|
+
future?: { v5Defaults?: boolean };
|
|
1127
1204
|
}
|