@runtypelabs/persona 4.23.1 → 4.24.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-9Vxm-PPz.d.cts → types-Db61aI9J.d.cts} +25 -2
- package/dist/animations/{types-9Vxm-PPz.d.ts → types-Db61aI9J.d.ts} +25 -2
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/animations-extra.d.cts +25 -2
- package/dist/animations-extra.d.ts +25 -2
- package/dist/approval-ui-3NMYNXBD.js +1 -0
- package/dist/approval-ui.cjs +2 -2
- package/dist/approval-ui.d.cts +261 -40
- package/dist/approval-ui.d.ts +261 -40
- 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 +256 -28
- package/dist/artifacts-ui.d.ts +256 -28
- 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 +256 -28
- package/dist/context-mentions.d.ts +256 -28
- package/dist/event-stream-view.d.cts +256 -28
- package/dist/event-stream-view.d.ts +256 -28
- package/dist/forms-ui.d.cts +291 -30
- package/dist/forms-ui.d.ts +291 -30
- package/dist/history-view.d.cts +25 -2
- package/dist/history-view.d.ts +25 -2
- package/dist/index.cjs +70 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +337 -51
- package/dist/index.d.ts +337 -51
- 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 +256 -28
- package/dist/session-reconnect.d.ts +256 -28
- package/dist/smart-dom-reader.d.cts +256 -28
- package/dist/smart-dom-reader.d.ts +256 -28
- package/dist/theme-editor-preview.cjs +74 -73
- package/dist/theme-editor-preview.d.cts +263 -28
- package/dist/theme-editor-preview.d.ts +263 -28
- package/dist/theme-editor-preview.js +51 -50
- package/dist/theme-editor.cjs +10 -10
- package/dist/theme-editor.d.cts +276 -30
- package/dist/theme-editor.d.ts +276 -30
- 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/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.tool-pair-replay.test.ts +325 -0
- package/src/client.ts +154 -61
- 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 +28 -13
- package/src/index-core.ts +2 -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 +61 -9
- 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 +179 -23
- 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 +363 -74
- package/src/ui.v5-defaults.test.ts +144 -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/utils/tool-pair-replay.ts +100 -0
- package/src/welcome.test.ts +23 -0
- package/src/welcome.ts +16 -9
- package/dist/approval-ui-K7R5Q5OC.js +0 -1
package/src/utils/tokens.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CIRCLE_LAUNCHER_TOKENS } from "./launcher-variant";
|
|
1
2
|
import type {
|
|
2
3
|
DeepPartial,
|
|
3
4
|
PersonaTheme,
|
|
@@ -9,8 +10,8 @@ import type {
|
|
|
9
10
|
SemanticTokens,
|
|
10
11
|
} from '../types/theme';
|
|
11
12
|
import {
|
|
12
|
-
DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH,
|
|
13
13
|
DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
14
|
+
resolveDefaultsVersion,
|
|
14
15
|
} from '../defaults';
|
|
15
16
|
|
|
16
17
|
// Detached/docked panel defaults, shared by the panel token defaults, the alias
|
|
@@ -25,7 +26,7 @@ const spacing = (() => {
|
|
|
25
26
|
return s as Record<0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64, string>;
|
|
26
27
|
})();
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
const DEFAULT_PALETTE_BASE = {
|
|
29
30
|
colors: {
|
|
30
31
|
primary: {
|
|
31
32
|
50: '#ffffff',
|
|
@@ -209,7 +210,7 @@ export const DEFAULT_PALETTE = {
|
|
|
209
210
|
},
|
|
210
211
|
};
|
|
211
212
|
|
|
212
|
-
|
|
213
|
+
const DEFAULT_SEMANTIC_BASE: SemanticTokens = {
|
|
213
214
|
colors: {
|
|
214
215
|
primary: 'palette.colors.primary.500',
|
|
215
216
|
secondary: 'palette.colors.secondary.500',
|
|
@@ -257,7 +258,7 @@ export const DEFAULT_SEMANTIC: SemanticTokens = {
|
|
|
257
258
|
},
|
|
258
259
|
};
|
|
259
260
|
|
|
260
|
-
|
|
261
|
+
const DEFAULT_COMPONENTS_BASE: ComponentTokens = {
|
|
261
262
|
button: {
|
|
262
263
|
primary: {
|
|
263
264
|
// Primary Actions role: solid primary
|
|
@@ -305,13 +306,31 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
|
|
|
305
306
|
},
|
|
306
307
|
panel: {
|
|
307
308
|
width: DEFAULT_FLOATING_LAUNCHER_WIDTH,
|
|
308
|
-
maxWidth
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
// Matches the existing floating runtime geometry. `maxWidth` and
|
|
310
|
+
// `maxHeight` intentionally do not impose a second cap; ui.ts applies
|
|
311
|
+
// the responsive width and heightOffset rules for this mode.
|
|
312
|
+
maxWidth: 'none',
|
|
313
|
+
height: 'min(640px, max(200px, calc(100vh - 64px)))',
|
|
314
|
+
maxHeight: 'none',
|
|
311
315
|
borderRadius: 'palette.radius.xl',
|
|
312
316
|
shadow: 'palette.shadows.xl',
|
|
313
317
|
inset: DEFAULT_PANEL_INSET,
|
|
314
318
|
canvasBackground: DEFAULT_PANEL_CANVAS_BACKGROUND,
|
|
319
|
+
modes: {
|
|
320
|
+
floating: {
|
|
321
|
+
border: '1px solid var(--persona-border)',
|
|
322
|
+
shadow: 'palette.shadows.xl',
|
|
323
|
+
borderRadius: 'palette.radius.xl',
|
|
324
|
+
},
|
|
325
|
+
inline: {
|
|
326
|
+
border: '1px solid var(--persona-border)',
|
|
327
|
+
shadow: 'none',
|
|
328
|
+
borderRadius: 'palette.radius.xl',
|
|
329
|
+
},
|
|
330
|
+
docked: { border: 'none', shadow: 'none', borderRadius: 'palette.radius.xl' },
|
|
331
|
+
sidebar: { border: 'none', borderRadius: '0' },
|
|
332
|
+
mobile: { border: 'none', shadow: 'none', borderRadius: '0' },
|
|
333
|
+
},
|
|
315
334
|
},
|
|
316
335
|
header: {
|
|
317
336
|
// Header role: solid primary. Border, subtitle, and action icons stay
|
|
@@ -319,11 +338,13 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
|
|
|
319
338
|
background: 'palette.colors.primary.500',
|
|
320
339
|
foreground: 'palette.colors.primary.50',
|
|
321
340
|
borderRadius: 'palette.radius.xl palette.radius.xl 0 0',
|
|
322
|
-
padding: '
|
|
341
|
+
padding: '20px 24px',
|
|
342
|
+
minimalPadding: '16px 24px',
|
|
323
343
|
iconBackground: 'palette.colors.primary.600',
|
|
324
344
|
iconForeground: 'palette.colors.primary.50',
|
|
325
345
|
},
|
|
326
346
|
message: {
|
|
347
|
+
gap: '12px',
|
|
327
348
|
user: {
|
|
328
349
|
// User Messages role: solid primary
|
|
329
350
|
background: 'palette.colors.primary.500',
|
|
@@ -532,6 +553,110 @@ export const DEFAULT_COMPONENTS: ComponentTokens = {
|
|
|
532
553
|
},
|
|
533
554
|
};
|
|
534
555
|
|
|
556
|
+
/** Shared defaults with a small opt-in V5 overlay. */
|
|
557
|
+
const DEFAULT_PALETTE_GRAY_V4 = DEFAULT_PALETTE_BASE.colors.gray;
|
|
558
|
+
const DEFAULT_PALETTE_GRAY_V5 = {
|
|
559
|
+
50: '#fafafa', 100: '#f5f5f5', 200: '#e5e5e5', 300: '#d4d4d4',
|
|
560
|
+
400: '#a3a3a3', 500: '#737373', 600: '#525252', 700: '#404040',
|
|
561
|
+
800: '#262626', 900: '#171717', 950: '#0a0a0a',
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
export const DEFAULT_PALETTE_V4 = {
|
|
565
|
+
...DEFAULT_PALETTE_BASE,
|
|
566
|
+
colors: { ...DEFAULT_PALETTE_BASE.colors, gray: DEFAULT_PALETTE_GRAY_V4 },
|
|
567
|
+
};
|
|
568
|
+
export const DEFAULT_PALETTE_V5 = {
|
|
569
|
+
...DEFAULT_PALETTE_BASE,
|
|
570
|
+
colors: { ...DEFAULT_PALETTE_BASE.colors, gray: DEFAULT_PALETTE_GRAY_V5 },
|
|
571
|
+
radius: { none: '0px', sm: '4px', md: '8px', lg: '12px', xl: '16px', '2xl': '24px', full: '9999px' },
|
|
572
|
+
};
|
|
573
|
+
export const DEFAULT_PALETTE = DEFAULT_PALETTE_V4;
|
|
574
|
+
|
|
575
|
+
export const DEFAULT_SEMANTIC_V4 = DEFAULT_SEMANTIC_BASE;
|
|
576
|
+
export const DEFAULT_SEMANTIC_V5: SemanticTokens = {
|
|
577
|
+
...DEFAULT_SEMANTIC_BASE,
|
|
578
|
+
colors: {
|
|
579
|
+
...DEFAULT_SEMANTIC_BASE.colors,
|
|
580
|
+
surface: '#ffffff', background: '#ffffff', container: 'semantic.colors.surface',
|
|
581
|
+
textInverse: '#ffffff',
|
|
582
|
+
},
|
|
583
|
+
typography: { ...DEFAULT_SEMANTIC_BASE.typography, fontSize: '14px', lineHeight: '1.5' },
|
|
584
|
+
};
|
|
585
|
+
export const DEFAULT_SEMANTIC = DEFAULT_SEMANTIC_V4;
|
|
586
|
+
|
|
587
|
+
export const DEFAULT_COMPONENTS_V4 = DEFAULT_COMPONENTS_BASE;
|
|
588
|
+
export const DEFAULT_COMPONENTS_V5: ComponentTokens = {
|
|
589
|
+
...DEFAULT_COMPONENTS_BASE,
|
|
590
|
+
launcher: CIRCLE_LAUNCHER_TOKENS,
|
|
591
|
+
activity: { rowHeight: "32px", labelSize: "13px", iconSize: "16px", indent: "0px", groupIndent: "0px", groupGap: "0px", groupPadding: "0px", transcriptGap: "0px", responseGap: "12px", bodyPadding: "4px 0", bodySize: "13px", bodyLineHeight: "1.5", bodyFontWeight: "400", sectionGap: "8px", bodySurface: "transparent" },
|
|
592
|
+
toolBubble: { shadow: "none" },
|
|
593
|
+
reasoningBubble: { shadow: "none" },
|
|
594
|
+
input: { ...DEFAULT_COMPONENTS_BASE.input, borderRadius: 'palette.radius.full' },
|
|
595
|
+
introCard: { ...DEFAULT_COMPONENTS_BASE.introCard, title: { fontSize: '22px', fontWeight: '500' } },
|
|
596
|
+
suggestion: {
|
|
597
|
+
chip: { ...DEFAULT_COMPONENTS_BASE.suggestion!.chip, fontWeight: '400' },
|
|
598
|
+
card: { ...DEFAULT_COMPONENTS_BASE.suggestion!.card, fontWeight: '400' },
|
|
599
|
+
list: { ...DEFAULT_COMPONENTS_BASE.suggestion!.list, fontWeight: '400' },
|
|
600
|
+
},
|
|
601
|
+
panel: {
|
|
602
|
+
...DEFAULT_COMPONENTS_BASE.panel,
|
|
603
|
+
width: 'min(400px, calc(100vw - 24px))',
|
|
604
|
+
height: 'min(704px, calc(100dvh - 104px))',
|
|
605
|
+
borderRadius: 'palette.radius.xl',
|
|
606
|
+
},
|
|
607
|
+
header: {
|
|
608
|
+
...DEFAULT_COMPONENTS_BASE.header,
|
|
609
|
+
background: 'semantic.colors.container', foreground: 'semantic.colors.text',
|
|
610
|
+
padding: '8px 8px 8px 16px', minimalPadding: '8px 8px 8px 16px',
|
|
611
|
+
minHeight: '48px', borderBottom: 'none',
|
|
612
|
+
iconBackground: 'transparent', iconForeground: 'semantic.colors.text', iconScale: '1',
|
|
613
|
+
controlSize: '28px', controlIconSize: '18px', controlStrokeWidth: '1.5',
|
|
614
|
+
actionIconForeground: 'semantic.colors.textMuted',
|
|
615
|
+
controlBorderRadius: '8px',
|
|
616
|
+
controlHoverBackground: 'color-mix(in srgb, var(--persona-text) 8%, transparent)',
|
|
617
|
+
controlHoverForeground: 'semantic.colors.text',
|
|
618
|
+
controlFocusOutline: '2px solid color-mix(in srgb, var(--persona-text) 50%, transparent)',
|
|
619
|
+
title: { fontSize: '14px', fontWeight: '500', lineHeight: '1.5' },
|
|
620
|
+
},
|
|
621
|
+
message: {
|
|
622
|
+
...DEFAULT_COMPONENTS_BASE.message,
|
|
623
|
+
gap: '20px', fullscreenGap: '28px', topFadeHeight: '18px',
|
|
624
|
+
user: {
|
|
625
|
+
...DEFAULT_COMPONENTS_BASE.message.user,
|
|
626
|
+
background: 'palette.colors.gray.100', text: 'semantic.colors.text',
|
|
627
|
+
borderRadius: 'palette.radius.xl', shadow: 'none', padding: '8px 14px',
|
|
628
|
+
fontSize: '14px', lineHeight: '1.5',
|
|
629
|
+
},
|
|
630
|
+
assistant: {
|
|
631
|
+
...DEFAULT_COMPONENTS_BASE.message.assistant,
|
|
632
|
+
background: 'transparent', border: 'transparent', borderWidth: '0px',
|
|
633
|
+
borderRadius: '0px', shadow: 'none', padding: '0px',
|
|
634
|
+
fontSize: '14px', lineHeight: '1.55',
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
composer: {
|
|
638
|
+
...DEFAULT_COMPONENTS_BASE.composer,
|
|
639
|
+
padding: '8px', fontSize: '15px', lineHeight: '1.5', controlSize: '32px', controlIconSize: '20px',
|
|
640
|
+
sendIconSize: '18px', sendButtonRadius: '9999px', footerBorder: 'none',
|
|
641
|
+
overlayBand: 'linear-gradient(to bottom, transparent, var(--persona-container) 24px)',
|
|
642
|
+
focusBorderColor: 'color-mix(in srgb, var(--persona-text) 28%, var(--persona-border))',
|
|
643
|
+
focusRing: '1px solid color-mix(in srgb, var(--persona-text) 12%, transparent)',
|
|
644
|
+
},
|
|
645
|
+
};
|
|
646
|
+
export const DEFAULT_COMPONENTS = DEFAULT_COMPONENTS_V4;
|
|
647
|
+
|
|
648
|
+
export type ThemeDefaultsVersion = 'v4' | 'v5';
|
|
649
|
+
|
|
650
|
+
export const resolveThemeDefaultsVersion = (future?: { v5Defaults?: boolean }): ThemeDefaultsVersion =>
|
|
651
|
+
resolveDefaultsVersion({ future });
|
|
652
|
+
|
|
653
|
+
export const resolveThemeDefaults = (future?: { v5Defaults?: boolean }) => {
|
|
654
|
+
const version = resolveThemeDefaultsVersion(future);
|
|
655
|
+
return version === 'v5'
|
|
656
|
+
? { palette: DEFAULT_PALETTE_V5, semantic: DEFAULT_SEMANTIC_V5, components: DEFAULT_COMPONENTS_V5 }
|
|
657
|
+
: { palette: DEFAULT_PALETTE_V4, semantic: DEFAULT_SEMANTIC_V4, components: DEFAULT_COMPONENTS_V4 };
|
|
658
|
+
};
|
|
659
|
+
|
|
535
660
|
export function resolveTokenValue(theme: PersonaTheme, path: string): string | undefined {
|
|
536
661
|
if (
|
|
537
662
|
!path.startsWith('palette.') &&
|
|
@@ -673,10 +798,11 @@ export function createTheme(
|
|
|
673
798
|
userConfig?: DeepPartial<PersonaTheme>,
|
|
674
799
|
options: CreateThemeOptions = {}
|
|
675
800
|
): PersonaTheme {
|
|
801
|
+
const defaults = resolveThemeDefaults(options.future);
|
|
676
802
|
const baseTheme: PersonaTheme = {
|
|
677
|
-
palette:
|
|
678
|
-
semantic:
|
|
679
|
-
components:
|
|
803
|
+
palette: defaults.palette as PersonaTheme['palette'],
|
|
804
|
+
semantic: defaults.semantic as PersonaTheme['semantic'],
|
|
805
|
+
components: defaults.components as PersonaTheme['components'],
|
|
680
806
|
};
|
|
681
807
|
|
|
682
808
|
let theme: PersonaTheme = {
|
|
@@ -738,6 +864,30 @@ export function createTheme(
|
|
|
738
864
|
),
|
|
739
865
|
} as PersonaTheme;
|
|
740
866
|
|
|
867
|
+
// A shared header padding is also the minimal-layout override unless the
|
|
868
|
+
// host supplies the more specific token. Both keys remain emitted CSS vars,
|
|
869
|
+
// so live updates and scheme changes restyle existing header DOM in place.
|
|
870
|
+
const activity = userConfig?.components?.activity;
|
|
871
|
+
for (const kind of ['toolBubble', 'reasoningBubble'] as const) {
|
|
872
|
+
for (const field of ['rowHeight', 'labelSize', 'iconSize', 'indent', 'bodySurface'] as const) {
|
|
873
|
+
if (activity?.[field] !== undefined) theme.components[kind][field] = activity[field];
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
const legacySurface = userConfig?.components?.collapsibleWidget?.surface;
|
|
877
|
+
if (legacySurface !== undefined && activity?.bodySurface === undefined) {
|
|
878
|
+
theme.components.activity = { ...theme.components.activity, bodySurface: legacySurface };
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const suppliedHeader = userConfig?.components?.header;
|
|
882
|
+
if (suppliedHeader?.padding !== undefined && suppliedHeader.minimalPadding === undefined) {
|
|
883
|
+
theme.components.header.minimalPadding = theme.components.header.padding;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const suppliedMessage = userConfig?.components?.message;
|
|
887
|
+
if (suppliedMessage?.gap !== undefined && suppliedMessage.fullscreenGap === undefined) {
|
|
888
|
+
theme.components.message.fullscreenGap = theme.components.message.gap;
|
|
889
|
+
}
|
|
890
|
+
|
|
741
891
|
if (options.validate !== false) {
|
|
742
892
|
const validation = validateTheme(theme);
|
|
743
893
|
if (!validation.valid) {
|
|
@@ -1224,12 +1374,21 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
|
|
|
1224
1374
|
]);
|
|
1225
1375
|
|
|
1226
1376
|
// Collapsible widget chrome (tool/reasoning/approval bubbles)
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1377
|
+
const collapsibleWidgetAliases = [
|
|
1378
|
+
['container', 'surface'],
|
|
1379
|
+
['surface', 'surface'],
|
|
1380
|
+
['border', 'border'],
|
|
1381
|
+
] as const;
|
|
1382
|
+
for (const [name, semanticFallback] of collapsibleWidgetAliases) {
|
|
1383
|
+
const value =
|
|
1384
|
+
cssVars[`--persona-components-collapsibleWidget-${name}`] ??
|
|
1385
|
+
cssVars[`--persona-${semanticFallback}`];
|
|
1386
|
+
if (value !== undefined) {
|
|
1387
|
+
cssVars[`--persona-cw-${name}`] = value;
|
|
1388
|
+
// Kept for custom styles that adopted the original unprefixed aliases.
|
|
1389
|
+
cssVars[`--cw-${name}`] = value;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1233
1392
|
|
|
1234
1393
|
emitAliases(cssVars, [
|
|
1235
1394
|
['message-border', 'components-message-border', 'border'],
|
|
@@ -1246,6 +1405,7 @@ export function themeToCssVariables(theme: PersonaTheme): Record<string, string>
|
|
|
1246
1405
|
['message-user-font-family', 'components-message-user-fontFamily'],
|
|
1247
1406
|
['message-user-line-height', 'components-message-user-lineHeight'],
|
|
1248
1407
|
['message-assistant-padding', 'components-message-assistant-padding'],
|
|
1408
|
+
['message-assistant-border-width', 'components-message-assistant-borderWidth'],
|
|
1249
1409
|
['message-assistant-max-width', 'components-message-assistant-maxWidth'],
|
|
1250
1410
|
['message-assistant-font-size', 'components-message-assistant-fontSize'],
|
|
1251
1411
|
['message-assistant-font-family', 'components-message-assistant-fontFamily'],
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-tool pair replay for the proxy and agent payload builders.
|
|
3
|
+
*
|
|
4
|
+
* In proxy and agent mode the widget holds the only copy of a client-tool
|
|
5
|
+
* result, so it owns replaying it. Each call the browser answered through
|
|
6
|
+
* `/resume` is sent to later turns as an assistant `toolCalls` message plus a
|
|
7
|
+
* `tool` message with the matching `toolResults`, with the full result.
|
|
8
|
+
* A pair replays even after the page stops offering its tool: the model
|
|
9
|
+
* already saw that result, and a follow-up about it must not lose it.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
AgentWidgetMessage,
|
|
14
|
+
AgentWidgetReplayedToolCall,
|
|
15
|
+
AgentWidgetReplayedToolResult,
|
|
16
|
+
AgentWidgetRequestPayloadMessage,
|
|
17
|
+
} from "../types";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The name a tool reaches the model as (`webmcp:get_schema` ->
|
|
21
|
+
* `webmcp_get_schema`).
|
|
22
|
+
*/
|
|
23
|
+
export const modelFacingToolName = (name: string): string =>
|
|
24
|
+
name
|
|
25
|
+
.replace(/[^a-zA-Z0-9_]/g, "_")
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.replace(/_+/g, "_")
|
|
28
|
+
.replace(/^_|_$/g, "");
|
|
29
|
+
|
|
30
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
31
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32
|
+
|
|
33
|
+
const replayablePair = (
|
|
34
|
+
message: AgentWidgetMessage,
|
|
35
|
+
): { call: AgentWidgetReplayedToolCall; result: AgentWidgetReplayedToolResult } | null => {
|
|
36
|
+
if (message.variant !== "tool") return null;
|
|
37
|
+
const answer = message.agentMetadata?.clientToolAnswer;
|
|
38
|
+
if (!answer || !answer.toolCallId || answer.result === undefined) return null;
|
|
39
|
+
const toolName = modelFacingToolName(answer.toolName);
|
|
40
|
+
if (!toolName) return null;
|
|
41
|
+
return {
|
|
42
|
+
call: {
|
|
43
|
+
toolCallId: answer.toolCallId,
|
|
44
|
+
toolName,
|
|
45
|
+
args: isRecord(answer.args) ? answer.args : {},
|
|
46
|
+
},
|
|
47
|
+
result: { toolCallId: answer.toolCallId, toolName, result: answer.result },
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Serialize time-ordered messages, interleaving replayable tool pairs.
|
|
53
|
+
* `toPayload` maps an ordinary message (or returns null to drop it). Answers
|
|
54
|
+
* posted in the same `/resume` batch are one assistant turn's parallel calls,
|
|
55
|
+
* so they group into one call message and one result message; a later batch
|
|
56
|
+
* (a chained call) starts a new pair even with no text between them.
|
|
57
|
+
*/
|
|
58
|
+
export const serializeWithToolPairs = (
|
|
59
|
+
messages: readonly AgentWidgetMessage[],
|
|
60
|
+
toPayload: (message: AgentWidgetMessage) => AgentWidgetRequestPayloadMessage | null,
|
|
61
|
+
): AgentWidgetRequestPayloadMessage[] => {
|
|
62
|
+
const out: AgentWidgetRequestPayloadMessage[] = [];
|
|
63
|
+
let calls: AgentWidgetReplayedToolCall[] = [];
|
|
64
|
+
let results: AgentWidgetReplayedToolResult[] = [];
|
|
65
|
+
let groupCreatedAt = "";
|
|
66
|
+
let groupBatch: string | undefined;
|
|
67
|
+
|
|
68
|
+
const flush = () => {
|
|
69
|
+
if (calls.length === 0) return;
|
|
70
|
+
out.push(
|
|
71
|
+
{ role: "assistant", content: "", createdAt: groupCreatedAt, toolCalls: calls },
|
|
72
|
+
{ role: "tool", content: "", createdAt: groupCreatedAt, toolResults: results },
|
|
73
|
+
);
|
|
74
|
+
calls = [];
|
|
75
|
+
results = [];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
for (const message of messages) {
|
|
79
|
+
const pair = replayablePair(message);
|
|
80
|
+
if (pair) {
|
|
81
|
+
const batch = message.agentMetadata?.clientToolAnswer?.batch;
|
|
82
|
+
if (calls.length > 0 && (batch === undefined || batch !== groupBatch)) flush();
|
|
83
|
+
if (calls.length === 0) {
|
|
84
|
+
groupCreatedAt = message.createdAt;
|
|
85
|
+
groupBatch = batch;
|
|
86
|
+
}
|
|
87
|
+
if (!calls.some((call) => call.toolCallId === pair.call.toolCallId)) {
|
|
88
|
+
calls.push(pair.call);
|
|
89
|
+
results.push(pair.result);
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const mapped = toPayload(message);
|
|
94
|
+
if (!mapped) continue;
|
|
95
|
+
flush();
|
|
96
|
+
out.push(mapped);
|
|
97
|
+
}
|
|
98
|
+
flush();
|
|
99
|
+
return out;
|
|
100
|
+
};
|
package/src/welcome.test.ts
CHANGED
|
@@ -274,3 +274,26 @@ describe("resolveConversationState", () => {
|
|
|
274
274
|
expect(resolveConversationState([])).toBe("empty");
|
|
275
275
|
});
|
|
276
276
|
});
|
|
277
|
+
|
|
278
|
+
describe("V5 welcome layout defaults", () => {
|
|
279
|
+
it("centers the empty greeting with concise copy and dismisses every variant", () => {
|
|
280
|
+
for (const variant of ["card", "hero", "none"] as const) {
|
|
281
|
+
const resolved = resolveWelcomeConfig(config({ future: { v5Defaults: true }, welcome: { variant } }));
|
|
282
|
+
expect(resolved).toMatchObject({ layout: "centered", align: "center", title: "What can I help with?", subtitle: "", dismiss: "on-first-message", anchor: "bottom" });
|
|
283
|
+
expect(isWelcomeVisible(resolved, [userMessage])).toBe(false);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("keeps explicit fields and legacy copy above V5 defaults", () => {
|
|
288
|
+
const resolved = resolveWelcomeConfig(config({ future: { v5Defaults: true }, copy: { welcomeTitle: "Legacy", welcomeSubtitle: "Scope" }, welcome: { layout: "top", variant: "hero", dismiss: "never", anchor: "bottom", align: "start" } }), true);
|
|
289
|
+
expect(resolved).toMatchObject({ title: "Legacy", subtitle: "Scope", layout: "top", dismiss: "never", anchor: "bottom", align: "start" });
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("makes centered fullscreen geometry available in either default mode", () => {
|
|
293
|
+
for (const v5Defaults of [false, true]) {
|
|
294
|
+
const resolved = resolveWelcomeConfig(config({ future: { v5Defaults }, welcome: { layout: "centered" } }), true);
|
|
295
|
+
expect(resolved.anchor).toBe("center");
|
|
296
|
+
expect(resolveWelcomeConfig(config({ future: { v5Defaults }, welcome: { layout: "centered", anchor: "bottom" } }), true).anchor).toBe("bottom");
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
});
|
package/src/welcome.ts
CHANGED
|
@@ -55,6 +55,7 @@ export interface ResolvedWelcomeConfig {
|
|
|
55
55
|
* Optional on the interface, always populated by `resolveWelcomeConfig`:
|
|
56
56
|
* a plugin literal built against an older minor must still type-check.
|
|
57
57
|
*/
|
|
58
|
+
layout?: "top" | "centered";
|
|
58
59
|
anchor?: AgentWidgetWelcomeAnchor;
|
|
59
60
|
/** Percentage string; validated, falls back to "44%". */
|
|
60
61
|
anchorComposerTop?: string;
|
|
@@ -80,10 +81,14 @@ const warnOnce = (config: AgentWidgetConfig | undefined, messages: string[]) =>
|
|
|
80
81
|
* `copy` alias, else the default above.
|
|
81
82
|
*/
|
|
82
83
|
export const resolveWelcomeConfig = (
|
|
83
|
-
config?: AgentWidgetConfig
|
|
84
|
+
config?: AgentWidgetConfig,
|
|
85
|
+
fullscreen = config?.launcher?.fullHeight === true &&
|
|
86
|
+
!config.launcher.sidebarMode && config.launcher.mountMode !== "docked"
|
|
84
87
|
): ResolvedWelcomeConfig => {
|
|
85
88
|
const welcome = config?.welcome;
|
|
86
89
|
const copy = config?.copy;
|
|
90
|
+
const v5 = config?.future?.v5Defaults === true;
|
|
91
|
+
const layout = welcome?.layout ?? (v5 ? "centered" : "top");
|
|
87
92
|
|
|
88
93
|
const variant: AgentWidgetWelcomeVariant = isSet(welcome, "variant")
|
|
89
94
|
? welcome!.variant!
|
|
@@ -91,13 +96,14 @@ export const resolveWelcomeConfig = (
|
|
|
91
96
|
? "none"
|
|
92
97
|
: "card";
|
|
93
98
|
|
|
94
|
-
//
|
|
99
|
+
// Legacy V4 heroes force dismissal; opting into layout makes it configurable.
|
|
100
|
+
// A hero remains the greeting, so its separate message is always suppressed.
|
|
95
101
|
const dismiss: AgentWidgetWelcomeDismiss =
|
|
96
|
-
variant === "hero"
|
|
102
|
+
variant === "hero" && !v5 && welcome?.layout === undefined
|
|
97
103
|
? "on-first-message"
|
|
98
104
|
: isSet(welcome, "dismiss")
|
|
99
105
|
? welcome!.dismiss!
|
|
100
|
-
: "never";
|
|
106
|
+
: v5 || variant === "hero" ? "on-first-message" : "never";
|
|
101
107
|
|
|
102
108
|
const conflicts: string[] = [];
|
|
103
109
|
if (isSet(welcome, "title") && isSet(copy, "welcomeTitle")) {
|
|
@@ -120,14 +126,14 @@ export const resolveWelcomeConfig = (
|
|
|
120
126
|
'welcome.message is ignored when welcome.variant is "hero": the hero is the greeting.'
|
|
121
127
|
);
|
|
122
128
|
}
|
|
123
|
-
if (variant === "hero" && isSet(welcome, "dismiss") && welcome!.dismiss !== "on-first-message") {
|
|
129
|
+
if (!v5 && welcome?.layout === undefined && variant === "hero" && isSet(welcome, "dismiss") && welcome!.dismiss !== "on-first-message") {
|
|
124
130
|
conflicts.push(
|
|
125
131
|
'welcome.dismiss is pinned to "on-first-message" when welcome.variant is "hero".'
|
|
126
132
|
);
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
const anchor: AgentWidgetWelcomeAnchor =
|
|
130
|
-
welcome?.anchor === "
|
|
136
|
+
welcome?.anchor ?? (layout === "centered" && fullscreen ? "center" : "bottom");
|
|
131
137
|
const anchorTopValid = parseAnchorFraction(welcome?.anchorComposerTop) !== null;
|
|
132
138
|
if (isSet(welcome, "anchorComposerTop") && !anchorTopValid) {
|
|
133
139
|
conflicts.push(
|
|
@@ -147,16 +153,16 @@ export const resolveWelcomeConfig = (
|
|
|
147
153
|
return {
|
|
148
154
|
title: isSet(welcome, "title")
|
|
149
155
|
? welcome!.title!
|
|
150
|
-
: copy?.welcomeTitle ?? DEFAULT_WELCOME_TITLE,
|
|
156
|
+
: copy?.welcomeTitle ?? (v5 ? "What can I help with?" : DEFAULT_WELCOME_TITLE),
|
|
151
157
|
kicker: welcome?.kicker ?? "",
|
|
152
158
|
subtitle: isSet(welcome, "subtitle")
|
|
153
159
|
? welcome!.subtitle!
|
|
154
|
-
: copy?.welcomeSubtitle ?? DEFAULT_WELCOME_SUBTITLE,
|
|
160
|
+
: copy?.welcomeSubtitle ?? (v5 ? "" : DEFAULT_WELCOME_SUBTITLE),
|
|
155
161
|
icon: welcome?.icon,
|
|
156
162
|
align:
|
|
157
163
|
welcome?.align === "start" || welcome?.align === "center"
|
|
158
164
|
? welcome.align
|
|
159
|
-
: undefined,
|
|
165
|
+
: layout === "centered" ? "center" : undefined,
|
|
160
166
|
iconPlacement:
|
|
161
167
|
welcome?.icon && typeof welcome.icon !== "function" &&
|
|
162
168
|
welcome.icon.placement === "inline"
|
|
@@ -165,6 +171,7 @@ export const resolveWelcomeConfig = (
|
|
|
165
171
|
variant,
|
|
166
172
|
dismiss,
|
|
167
173
|
message: variant === "hero" ? undefined : welcome?.message,
|
|
174
|
+
layout,
|
|
168
175
|
anchor,
|
|
169
176
|
anchorComposerTop: anchorTopValid
|
|
170
177
|
? welcome!.anchorComposerTop!
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as E,g as P}from"./chunk-NQLDCJAP.js";import{a as x}from"./chunk-7NQ4ZTHF.js";import{b as q,c as $}from"./chunk-Q735PSZL.js";import{a}from"./chunk-76IELU7F.js";var R=()=>{},z=t=>{R=t.webMcpToolTitle},I=t=>R(t);import{ChevronDown as Y,ChevronUp as ee,ShieldAlert as te,ShieldCheck as K,ShieldX as _}from"lucide";var k=new Map,B=t=>{let o=(t.startsWith(E)?t.slice(E.length):t).replace(/([a-z0-9])([A-Z])/g,"$1 $2").split(/[_\-\s.]+/).filter(Boolean);if(o.length===0)return t;let e=o.join(" ").toLowerCase();return e.charAt(0).toUpperCase()+e.slice(1)},F=t=>t?.approval!==!1?t?.approval:void 0,X=(t,r)=>{let o=F(r)?.detailsDisplay??"collapsed";return k.get(t)??o==="expanded"},j=(t,r,o)=>{let e=F(o);t.setAttribute("aria-expanded",r?"true":"false");let u=t.querySelector("[data-approval-details-label]");u&&(u.textContent=r?e?.hideDetailsLabel??"Hide details":e?.showDetailsLabel??"Show details");let i=t.querySelector("[data-approval-details-chevron]");if(i){i.innerHTML="";let g=x(r?ee:Y,14,"currentColor",2);g&&i.appendChild(g)}},V=(t,r,o)=>{let e=r.querySelector('button[data-bubble-type="approval"]'),u=r.querySelector("[data-approval-details]");if(!e||!u)return;let i=X(t,o);j(e,i,o),u.style.display=i?"":"none"};var Z=(t,r)=>{let o=t.approval,e=r?.approval!==!1?r?.approval:void 0,u=o?.status==="pending",i=a("div",["persona-approval-bubble","persona-w-full","persona-rounded-2xl","persona-border","persona-shadow-sm","persona-overflow-hidden"].join(" "));if(i.id=`bubble-${t.id}`,i.setAttribute("data-message-id",t.id),i.style.backgroundColor=e?.backgroundColor??"var(--persona-approval-bg, #fefce8)",i.style.borderColor=e?.borderColor??"var(--persona-approval-border, #fef08a)",i.style.boxShadow=e?.shadow!==void 0?e.shadow.trim()===""?"none":e.shadow:"var(--persona-approval-shadow, 0 5px 15px rgba(15, 23, 42, 0.08))",!o)return i;let g=a("div","persona-flex persona-items-start persona-gap-3 persona-px-4 persona-py-3"),l=a("div","persona-flex-shrink-0 persona-mt-0.5");l.setAttribute("data-approval-icon","true");let v=o.status==="denied"?_:o.status==="timeout"?te:K,C=o.status==="approved"?"var(--persona-feedback-success, #16a34a)":o.status==="denied"?"var(--persona-feedback-error, #dc2626)":o.status==="timeout"?"var(--persona-feedback-warning, #ca8a04)":e?.titleColor??"currentColor",A=x(v,20,C,2);A&&l.appendChild(A);let m=a("div","persona-flex-1 persona-min-w-0"),f=a("div","persona-flex persona-items-center persona-gap-2"),w=a("span","persona-text-sm persona-font-medium persona-text-persona-primary");if(e?.titleColor&&(w.style.color=e.titleColor),w.textContent=e?.title??"Approval Required",f.appendChild(w),!u){let s=a("span","persona-inline-flex persona-items-center persona-px-2 persona-py-0.5 persona-rounded-full persona-text-xs persona-font-medium");s.setAttribute("data-approval-status",o.status),o.status==="approved"?(s.style.backgroundColor="var(--persona-palette-colors-success-100, #dcfce7)",s.style.color="var(--persona-palette-colors-success-700, #15803d)",s.textContent="Approved"):o.status==="denied"?(s.style.backgroundColor="var(--persona-palette-colors-error-100, #fee2e2)",s.style.color="var(--persona-palette-colors-error-700, #b91c1c)",s.textContent="Denied"):o.status==="timeout"&&(s.style.backgroundColor="var(--persona-palette-colors-warning-100, #fef3c7)",s.style.color="var(--persona-palette-colors-warning-700, #b45309)",s.textContent="Timeout"),f.appendChild(s)}m.appendChild(f);let y=o.toolType==="webmcp"||o.toolName.startsWith(E)?I(o.toolName):void 0,N=e?.formatDescription?.({toolName:o.toolName,toolType:o.toolType,description:o.description,parameters:o.parameters,...y?{displayTitle:y}:{},...o.reason?{reason:o.reason}:{}}),D=!o.toolName,H=N||(D?o.description:`The assistant wants to use \u201C${y??B(o.toolName)}\u201D.`),h=a("p","persona-text-sm persona-mt-0.5 persona-text-persona-muted");if(h.setAttribute("data-approval-summary","true"),e?.descriptionColor&&(h.style.color=e.descriptionColor),h.textContent=H,m.appendChild(h),o.reason){let s=a("p","persona-text-sm persona-mt-1 persona-text-persona-muted");s.setAttribute("data-approval-reason","true"),e?.reasonColor?s.style.color=e.reasonColor:e?.descriptionColor&&(s.style.color=e.descriptionColor);let n=a("span","persona-font-medium");n.textContent=`${e?.reasonLabel??"Agent's stated reason:"} `,s.appendChild(n),s.appendChild(document.createTextNode(o.reason)),m.appendChild(s)}let W=e?.detailsDisplay??"collapsed",M=!!o.description&&!D,S=M||!!o.parameters;if(W!=="hidden"&&S){let s=X(t.id,r),n=a("button","persona-inline-flex persona-items-center persona-gap-1 persona-mt-1 persona-p-0 persona-border-none persona-bg-transparent persona-text-xs persona-font-medium persona-cursor-pointer persona-text-persona-muted");n.type="button",n.setAttribute("data-expand-header","true"),n.setAttribute("data-bubble-type","approval"),e?.descriptionColor&&(n.style.color=e.descriptionColor);let p=a("span");p.setAttribute("data-approval-details-label","true");let b=a("span","persona-inline-flex persona-items-center");b.setAttribute("data-approval-details-chevron","true"),n.append(p,b),j(n,s,r),m.appendChild(n);let c=a("div");if(c.setAttribute("data-approval-details","true"),c.style.display=s?"":"none",M){let d=a("p","persona-text-sm persona-mt-1 persona-text-persona-muted");e?.descriptionColor&&(d.style.color=e.descriptionColor),d.textContent=o.description,c.appendChild(d)}if(o.parameters){let d=a("pre","persona-mt-2 persona-text-xs persona-p-2 persona-rounded persona-overflow-x-auto persona-max-h-32 persona-bg-persona-container persona-text-persona-primary");e?.parameterBackgroundColor&&(d.style.backgroundColor=e.parameterBackgroundColor),e?.parameterTextColor&&(d.style.color=e.parameterTextColor),d.style.fontSize="0.75rem",d.style.lineHeight="1rem",d.textContent=P(o.parameters),c.appendChild(d)}m.appendChild(c)}if(u){let s=a("div","persona-flex persona-gap-2 persona-mt-2");s.setAttribute("data-approval-buttons","true");let n=a("button","persona-inline-flex persona-items-center persona-px-3 persona-py-1.5 persona-rounded-md persona-text-xs persona-font-medium persona-border-none persona-cursor-pointer");n.type="button",n.style.backgroundColor=e?.approveButtonColor??"var(--persona-approval-approve-bg, #22c55e)",n.style.color=e?.approveButtonTextColor??"#ffffff",n.setAttribute("data-approval-action","approve");let p=x(K,14,e?.approveButtonTextColor??"#ffffff",2);p&&(p.style.marginRight="4px",n.appendChild(p));let b=document.createTextNode(e?.approveLabel??"Approve");n.appendChild(b);let c=a("button","persona-inline-flex persona-items-center persona-px-3 persona-py-1.5 persona-rounded-md persona-text-xs persona-font-medium persona-cursor-pointer");c.type="button",c.style.backgroundColor=e?.denyButtonColor??"transparent",c.style.color=e?.denyButtonTextColor??"var(--persona-feedback-error, #dc2626)",c.style.border=`1px solid ${e?.denyButtonTextColor?e.denyButtonTextColor:"var(--persona-palette-colors-error-200, #fca5a5)"}`,c.setAttribute("data-approval-action","deny");let d=x(_,14,e?.denyButtonTextColor??"var(--persona-feedback-error, #dc2626)",2);d&&(d.style.marginRight="4px",c.appendChild(d));let L=document.createTextNode(e?.denyLabel??"Deny");c.appendChild(L),s.append(n,c),m.appendChild(s)}return g.append(l,m),i.appendChild(g),i};import{Ban as oe,ChevronDown as G,ShieldCheck as ne}from"lucide";var re=()=>({keyHandlers:new Map,popovers:new Map,pendingOrder:[],latestPendingApprovalId:null}),J=(t,r)=>{let o=t.keyHandlers.get(r);o&&(document.removeEventListener("keydown",o),t.keyHandlers.delete(r));let e=t.popovers.get(r);e&&(e.destroy(),t.popovers.delete(r))},T=(t,r)=>{J(t,r);let o=t.pendingOrder.indexOf(r);o!==-1&&t.pendingOrder.splice(o,1),t.latestPendingApprovalId===r&&(t.latestPendingApprovalId=t.pendingOrder.length?t.pendingOrder[t.pendingOrder.length-1]:null)},ae=t=>t?.approval!==!1?t?.approval:void 0,se=(t,r)=>{let o=r?.detailsDisplay??"collapsed";return k.get(t)??o==="expanded"},U=t=>{let r=a("span","persona-approval-kbd");return r.textContent=t,r},pe=(t,r)=>{let o=a("span","persona-approval-title");r?.titleColor&&(o.style.color=r.titleColor);let u=t.toolType==="webmcp"||t.toolName.startsWith(E)?I(t.toolName):void 0,i=r?.formatDescription?.({toolName:t.toolName,toolType:t.toolType,description:t.description??"",parameters:t.parameters,...u?{displayTitle:u}:{},...t.reason?{reason:t.reason}:{}});if(i)return o.textContent=i,o;let g=u??B(t.toolName),l=t.toolType&&t.toolType!=="webmcp"?t.toolType:null;o.append("The assistant wants to use ");let v=document.createElement("strong");if(v.textContent=g,o.appendChild(v),l){o.append(" from ");let C=document.createElement("strong");C.textContent=l,o.appendChild(C)}return o},le=t=>{let r=a("div","persona-approval-resolved"),o=x(oe,15,"currentColor",2);o&&r.appendChild(o);let e=a("span","persona-approval-resolved-name");return e.textContent=t.toolName?B(t.toolName):"Tool",r.append(e,document.createTextNode(t.status==="timeout"?" timed out":" denied")),r},ie=(t,r,o,e,u,i,g)=>{let l=a("div","persona-approval-card persona-shadow-sm");l.id=`bubble-${r.id}`,l.setAttribute("data-message-id",r.id),l.setAttribute("data-bubble-type","approval"),e?.backgroundColor&&(l.style.background=e.backgroundColor),e?.borderColor&&(l.style.borderColor=e.borderColor),e?.shadow!==void 0&&(l.style.boxShadow=e.shadow.trim()===""?"none":e.shadow);let v=e?.detailsDisplay??"collapsed",C=!!o.description&&v!=="hidden",A=o.parameters!=null&&v!=="hidden",m=C||A,f=m&&se(r.id,e),w=e?.showDetailsLabel??"Show details",O=e?.hideDetailsLabel??"Hide details",y=a("button","persona-approval-head");y.type="button",m?(y.setAttribute("data-action","toggle-params"),y.setAttribute("aria-expanded",f?"true":"false"),y.setAttribute("aria-label",f?O:w)):y.setAttribute("data-static","true");let N=a("span","persona-approval-logo"),D=x(ne,16,"currentColor",2);D&&N.appendChild(D),y.appendChild(N);let H=pe(o,e);if(m){let n=a("span","persona-approval-toggle");n.setAttribute("aria-hidden","true");let p=x(G,14,"currentColor",2);p&&n.appendChild(p),H.append(" "),H.appendChild(n)}y.appendChild(H),l.appendChild(y);let h=a("div","persona-approval-body");if(m){let n=a("div","persona-approval-details");if(n.setAttribute("data-role","params"),n.hidden=!f,C){let p=a("p","persona-approval-desc");e?.descriptionColor&&(p.style.color=e.descriptionColor),p.textContent=o.description,n.appendChild(p)}if(A){let p=a("pre","persona-approval-params");e?.parameterBackgroundColor&&(p.style.background=e.parameterBackgroundColor),e?.parameterTextColor&&(p.style.color=e.parameterTextColor),p.textContent=P(o.parameters),n.appendChild(p)}h.appendChild(n)}if(o.reason){let n=a("p","persona-approval-reason");e?.reasonColor?n.style.color=e.reasonColor:e?.descriptionColor&&(n.style.color=e.descriptionColor);let p=a("span","persona-approval-reason-label");p.textContent=`${e?.reasonLabel??"Agent's stated reason:"} `,n.append(p,document.createTextNode(o.reason)),h.appendChild(n)}let W=a("div","persona-approval-actions"),M=null,S=n=>{e?.approveButtonColor&&(n.style.background=e.approveButtonColor),e?.approveButtonTextColor&&(n.style.color=e.approveButtonTextColor)},s=a("button","persona-approval-deny");if(s.type="button",s.setAttribute("data-action","deny"),e?.denyButtonColor&&(s.style.background=e.denyButtonColor),e?.denyButtonTextColor&&(s.style.color=e.denyButtonTextColor),s.append(e?.denyLabel??"Deny"),g){let n=a("div","persona-approval-split"),p=a("button","persona-approval-primary");p.type="button",p.setAttribute("data-action","always"),S(p),p.append(e?.approveLabel??"Always allow",U("\u23CE"));let b=a("button","persona-approval-caret");b.type="button",b.setAttribute("data-action","toggle-menu"),b.setAttribute("aria-label","More options"),S(b);let c=x(G,15,"currentColor",2);c&&b.appendChild(c),n.append(p,b),W.append(n,s),s.append(U("Esc"));let d=a("div","persona-approval-menu"),L=a("button","persona-approval-menu-item");L.type="button",L.append("Allow once",U("\u2318\u23CE")),d.appendChild(L),M=q({anchor:n,content:d,placement:"bottom-start",matchAnchorWidth:!0}),t.popovers.set(r.id,M),L.addEventListener("click",()=>{T(t,r.id),u()})}else{let n=a("button","persona-approval-primary persona-approval-primary--solo");n.type="button",n.setAttribute("data-action","allow"),S(n),n.append(e?.approveLabel??"Allow"),W.append(n,s)}return h.appendChild(W),l.appendChild(h),l.addEventListener("click",n=>{let p=n.target instanceof Element?n.target.closest("[data-action]"):null;if(!p)return;let b=p.getAttribute("data-action");if(b==="toggle-params"){let c=l.querySelector('[data-role="params"]');if(c){let d=c.hidden;c.hidden=!d,y.setAttribute("aria-expanded",d?"true":"false"),y.setAttribute("aria-label",d?O:w),k.set(r.id,d)}return}if(b==="toggle-menu"){M?.toggle();return}if(b==="always"){T(t,r.id),u({remember:!0});return}if(b==="allow"){T(t,r.id),u();return}if(b==="deny"){T(t,r.id),i();return}}),l},Q=()=>{let t=re();return{plugin:{id:"persona-built-in-approval",renderApproval:({message:e,approve:u,deny:i,config:g})=>{let l=e?.approval;if(!l)return null;let v=ae(g);if(l.status!=="pending"){if(T(t,e.id),l.status==="approved"){let m=document.createElement("div");return m.style.display="none",m}return le(l)}J(t,e.id);let C=v?.enableAlwaysAllow===!0,A=ie(t,e,l,v,u,i,C);if(C){t.pendingOrder.includes(e.id)||t.pendingOrder.push(e.id),t.latestPendingApprovalId=t.pendingOrder[t.pendingOrder.length-1];let m=f=>{$(f)||e.id===t.latestPendingApprovalId&&(f.key!=="Escape"&&f.key!=="Enter"||(f.preventDefault(),f.stopImmediatePropagation(),T(t,e.id),f.key==="Escape"?i():f.metaKey||f.ctrlKey?u():u({remember:!0})))};t.keyHandlers.set(e.id,m),document.addEventListener("keydown",m)}return A}},teardown:()=>{for(let e of[...t.keyHandlers.keys(),...t.popovers.keys()])T(t,e);t.latestPendingApprovalId=null}}};export{k as approvalDetailsExpansionState,Z as createApprovalBubble,Q as createBuiltInApprovalPlugin,B as humanizeToolName,z as initApprovalUi,V as updateApprovalDetailsUI};
|