@runtypelabs/persona 4.7.0 → 4.8.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.
Files changed (117) hide show
  1. package/dist/animations/glyph-cycle.cjs +2 -2
  2. package/dist/animations/glyph-cycle.d.cts +1 -1
  3. package/dist/animations/glyph-cycle.d.ts +1 -1
  4. package/dist/animations/glyph-cycle.js +2 -2
  5. package/dist/animations/{types-CSmiKRVa.d.cts → types-BsZtXPKK.d.cts} +43 -3
  6. package/dist/animations/{types-CSmiKRVa.d.ts → types-BsZtXPKK.d.ts} +43 -3
  7. package/dist/animations/wipe.cjs +2 -2
  8. package/dist/animations/wipe.d.cts +1 -1
  9. package/dist/animations/wipe.d.ts +1 -1
  10. package/dist/chunk-5EIIHQLQ.js +1 -0
  11. package/dist/codegen.cjs +12 -12
  12. package/dist/codegen.js +14 -14
  13. package/dist/index.cjs +91 -68
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +877 -172
  16. package/dist/index.d.ts +877 -172
  17. package/dist/index.global.js +81 -57
  18. package/dist/index.global.js.map +1 -1
  19. package/dist/index.js +91 -68
  20. package/dist/index.js.map +1 -1
  21. package/dist/install.global.js +1 -1
  22. package/dist/install.global.js.map +1 -1
  23. package/dist/launcher.global.js +3 -2
  24. package/dist/launcher.global.js.map +1 -1
  25. package/dist/markdown-parsers.js +24 -24
  26. package/dist/plugin-kit.cjs +1 -1
  27. package/dist/plugin-kit.js +1 -1
  28. package/dist/runtype-tts-entry-UJAEF7NZ.js +1 -0
  29. package/dist/runtype-tts.js +1 -1
  30. package/dist/session-reconnect-JKIJBHS5.js +1 -0
  31. package/dist/smart-dom-reader.cjs +17 -17
  32. package/dist/smart-dom-reader.d.cts +753 -15
  33. package/dist/smart-dom-reader.d.ts +753 -15
  34. package/dist/smart-dom-reader.js +17 -17
  35. package/dist/testing.cjs +3 -3
  36. package/dist/testing.js +3 -3
  37. package/dist/theme-editor-preview.cjs +81 -58
  38. package/dist/theme-editor-preview.d.cts +761 -15
  39. package/dist/theme-editor-preview.d.ts +761 -15
  40. package/dist/theme-editor-preview.js +81 -58
  41. package/dist/theme-editor.cjs +6 -6
  42. package/dist/theme-editor.d.cts +753 -15
  43. package/dist/theme-editor.d.ts +753 -15
  44. package/dist/theme-editor.js +10 -10
  45. package/dist/theme-reference.cjs +1 -1
  46. package/dist/theme-reference.d.cts +74 -0
  47. package/dist/theme-reference.d.ts +74 -0
  48. package/dist/theme-reference.js +1 -1
  49. package/dist/voice-worklet-player.cjs +2 -2
  50. package/dist/voice-worklet-player.js +2 -2
  51. package/dist/webmcp-polyfill.js +2 -2
  52. package/dist/widget.css +1 -1
  53. package/package.json +2 -3
  54. package/src/artifacts-session.test.ts +178 -0
  55. package/src/client.test.ts +186 -1
  56. package/src/client.ts +92 -9
  57. package/src/components/artifact-card.test.ts +333 -0
  58. package/src/components/artifact-card.ts +75 -28
  59. package/src/components/artifact-inline.test.ts +1328 -0
  60. package/src/components/artifact-inline.ts +920 -0
  61. package/src/components/artifact-pane.test.ts +1042 -0
  62. package/src/components/artifact-pane.ts +440 -131
  63. package/src/components/artifact-preview.test.ts +1155 -0
  64. package/src/components/artifact-preview.ts +994 -0
  65. package/src/components/pill-composer-builder.test.ts +6 -2
  66. package/src/components/pill-composer-builder.ts +6 -6
  67. package/src/components/reasoning-bubble.ts +1 -13
  68. package/src/components/registry.ts +38 -3
  69. package/src/components/tool-bubble.ts +1 -13
  70. package/src/defaults.ts +1 -0
  71. package/src/generated/runtype-openapi-contract.ts +55 -3
  72. package/src/index-core.ts +20 -1
  73. package/src/index.ts +8 -0
  74. package/src/markdown-parsers-loader.test.ts +158 -0
  75. package/src/markdown-parsers-loader.ts +74 -9
  76. package/src/runtime/host-layout.test.ts +163 -0
  77. package/src/runtime/host-layout.ts +110 -7
  78. package/src/runtime/init.ts +18 -61
  79. package/src/session.ts +76 -22
  80. package/src/styles/widget.css +773 -26
  81. package/src/theme-editor/preview.ts +2 -0
  82. package/src/theme-editor/sections.test.ts +26 -1
  83. package/src/theme-editor/sections.ts +10 -2
  84. package/src/theme-reference.ts +2 -2
  85. package/src/tool-call-display-defaults.test.ts +1 -0
  86. package/src/types/theme.ts +77 -0
  87. package/src/types.ts +516 -17
  88. package/src/ui.artifact-pane-gating.test.ts +636 -0
  89. package/src/ui.component-directive.test.ts +104 -0
  90. package/src/ui.composer-bar.test.ts +60 -2
  91. package/src/ui.detached-panel.test.ts +1049 -0
  92. package/src/ui.tool-display.test.ts +51 -0
  93. package/src/ui.ts +698 -109
  94. package/src/utils/artifact-custom-actions.ts +128 -0
  95. package/src/utils/artifact-display.test.ts +42 -0
  96. package/src/utils/artifact-display.ts +84 -0
  97. package/src/utils/artifact-file.test.ts +116 -0
  98. package/src/utils/artifact-file.ts +117 -0
  99. package/src/utils/artifact-gate.test.ts +112 -5
  100. package/src/utils/artifact-gate.ts +39 -14
  101. package/src/utils/artifact-loading-status.ts +55 -0
  102. package/src/utils/artifact-status-label.ts +190 -0
  103. package/src/utils/buttons.ts +7 -1
  104. package/src/utils/code-highlight.test.ts +186 -0
  105. package/src/utils/code-highlight.ts +400 -0
  106. package/src/utils/icons.ts +2 -0
  107. package/src/utils/roving-tablist.test.ts +152 -0
  108. package/src/utils/roving-tablist.ts +111 -0
  109. package/src/utils/spinner.ts +45 -0
  110. package/src/utils/theme.test.ts +48 -0
  111. package/src/utils/theme.ts +7 -0
  112. package/src/utils/tokens.ts +91 -0
  113. package/src/utils/tool-loading-animation.test.ts +32 -0
  114. package/src/utils/tool-loading-animation.ts +24 -0
  115. package/dist/chunk-DFBSCFYN.js +0 -1
  116. package/dist/runtype-tts-entry-HFUV2UF7.js +0 -1
  117. package/dist/session-reconnect-U77QFUR7.js +0 -1
package/src/types.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
  RuntypeStopReasonKind,
7
7
  } from "./generated/runtype-openapi-contract";
8
8
  import type { TargetResolver } from "./utils/target";
9
+ import type { IconName } from "./utils/icons";
9
10
 
10
11
  export type { TargetResolver, ResolvedTarget } from "./utils/target";
11
12
 
@@ -727,7 +728,13 @@ export type AgentWidgetControllerEventMap = {
727
728
  * new artifact content arrives or the host calls `showArtifacts()` on the widget handle.
728
729
  */
729
730
  export type AgentWidgetArtifactsLayoutConfig = {
730
- /** Flex gap between chat column and artifact pane. @default 0.5rem */
731
+ /**
732
+ * Flex gap between chat column and artifact pane.
733
+ * @default 0 (welded); detached uses the panel inset
734
+ * @remarks In welded appearances (`panel` / `seamless`) a nonzero gap opens a
735
+ * visible seam in the single card. It is honored geometrically (resize clamps
736
+ * and the seam handle account for it) but is usually undesired.
737
+ */
731
738
  splitGap?: string;
732
739
  /** Artifact column width in split mode. @default 40% */
733
740
  paneWidth?: string;
@@ -758,16 +765,39 @@ export type AgentWidgetArtifactsLayoutConfig = {
758
765
  /** Optional max artifact width cap while resizing (`px` only). Layout still bounds by chat min width. */
759
766
  resizableMaxWidth?: string;
760
767
  /**
761
- * Visual treatment for the artifact column in split mode.
762
- * - `'panel'`: bordered sidebar with left border, gap, and shadow (default).
763
- * - `'seamless'`: flush with chat: no border or shadow, container background, zero gap.
768
+ * Visual treatment for the artifact column in a desktop split.
769
+ * - `'panel'`: one welded card (border + radius wrap chat + pane) with a hairline
770
+ * divider on the pane's chat-facing edge, zero gap (default).
771
+ * - `'seamless'`: the same welded card with no internal divider or chrome, zero gap.
772
+ * - `'detached'`: two separate elevated cards: gap from `--persona-panel-inset`,
773
+ * all-side border and radius on each, and canvas background behind the split.
774
+ * In an inline embed this also insets the split from the container edges on all
775
+ * sides (the page canvas shows through), so the all-side margin no longer requires
776
+ * `launcher.detachedPanel`.
777
+ *
778
+ * Default is `'panel'` unless `launcher.detachedPanel` is true, in which case an unset
779
+ * `paneAppearance` resolves to `'detached'`. An explicit value always wins.
764
780
  * @default 'panel'
765
781
  */
766
- paneAppearance?: "panel" | "seamless";
782
+ paneAppearance?: "panel" | "seamless" | "detached";
767
783
  /** Border radius on the artifact pane (CSS length). Works with any `paneAppearance`. */
768
784
  paneBorderRadius?: string;
769
785
  /** CSS `box-shadow` on the artifact pane. Set `"none"` to suppress the default shadow. */
770
786
  paneShadow?: string;
787
+ /**
788
+ * CSS `box-shadow` for the chat column card in a detached split. Defaults to the same
789
+ * elevation as the artifact pane; set to `"none"` to keep the chat flat while the pane stays raised.
790
+ */
791
+ chatShadow?: string;
792
+ /**
793
+ * Chat column surface in a detached split. `"card"` (default) renders the chat as an inset card
794
+ * matching the artifact pane; `"flush"` renders the chat flush with the container (no border, radius,
795
+ * or shadow) and insets only the artifact pane, so the chat is flat background and the pane floats.
796
+ * Only affects an inline-embedded detached split (the container-filling case); in floating, docked,
797
+ * or sidebar modes it falls back to the card look.
798
+ * @default 'card'
799
+ */
800
+ chatSurface?: "card" | "flush";
771
801
  /**
772
802
  * Full `border` shorthand for the artifact `<aside>` (all sides). Overrides default pane borders.
773
803
  * Example: `"1px solid #cccccc"`.
@@ -779,14 +809,12 @@ export type AgentWidgetArtifactsLayoutConfig = {
779
809
  */
780
810
  paneBorderLeft?: string;
781
811
  /**
782
- * Desktop split only (not narrow-host drawer / not ≤640px): square the **main chat card’s**
783
- * top-right and bottom-right radii, and round the **artifact pane’s** top-right and bottom-right
784
- * to match `persona-rounded-2xl` (`--persona-radius-lg`) so the two columns read as one shell.
812
+ * @deprecated No-op. Panel and seamless splits now weld into one card by default.
785
813
  */
786
814
  unifiedSplitChrome?: boolean;
787
815
  /**
788
- * When `unifiedSplitChrome` is true, outer-right corner radius on the artifact column (CSS length).
789
- * @default matches theme large radius (`--persona-radius-lg`)
816
+ * Desktop welded split only: override the artifact pane's outer-right corner radius (CSS length).
817
+ * @default matches the panel radius (`--persona-panel-radius`)
790
818
  */
791
819
  unifiedSplitOuterRadius?: string;
792
820
  /**
@@ -806,6 +834,10 @@ export type AgentWidgetArtifactsLayoutConfig = {
806
834
  * @default 'default'
807
835
  */
808
836
  toolbarPreset?: "default" | "document";
837
+ /** Visible title in the artifact toolbar. @default "Artifacts" */
838
+ toolbarTitle?: string;
839
+ /** Label for the artifact dismiss control. @default "Close" */
840
+ closeButtonLabel?: string;
809
841
  /**
810
842
  * When `toolbarPreset` is `document`, show a visible "Copy" label next to the copy icon.
811
843
  */
@@ -820,6 +852,14 @@ export type AgentWidgetArtifactsLayoutConfig = {
820
852
  documentToolbarToggleActiveBackground?: string;
821
853
  /** Active view/source toggle border color. Sets `--persona-artifact-doc-toggle-active-border`. */
822
854
  documentToolbarToggleActiveBorderColor?: string;
855
+ /** Show an expand/collapse toggle in the artifact toolbar (both presets). Expanded fills the widget with the pane and hides the chat column. Default: false. */
856
+ showExpandToggle?: boolean;
857
+ /** Show the copy control in the `default` toolbar preset (the `document` preset always shows it). Default: false. */
858
+ showCopyButton?: boolean;
859
+ /** Edge fade on the scrollable tab strip. true = both ends (dynamic). @default true */
860
+ tabFade?: boolean | { start?: boolean; end?: boolean };
861
+ /** Tab fade width (CSS length). @default 24px */
862
+ tabFadeSize?: string;
823
863
  /**
824
864
  * Invoked when the document toolbar Refresh control is used (before the pane re-renders).
825
865
  * Use to replay `connectStream`, refetch, etc.
@@ -841,21 +881,357 @@ export type AgentWidgetArtifactsLayoutConfig = {
841
881
  }) => void | Promise<void>;
842
882
  };
843
883
 
884
+ export type PersonaArtifactDisplayMode =
885
+ | "card" // reference card in transcript; pane opens on click
886
+ | "panel" // reference card in transcript; pane also auto-opens on artifact_start (current default behavior)
887
+ | "inline"; // artifact preview renders directly in the transcript; no pane involvement
888
+
889
+ /** Context handed to custom artifact actions. Content fields are best-effort: resolved from live session state or, after a refresh, from the persisted reference-card payload. */
890
+ export type PersonaArtifactActionContext = {
891
+ artifactId: string | null;
892
+ title: string;
893
+ artifactType: string;
894
+ markdown?: string;
895
+ file?: PersonaArtifactFileMeta;
896
+ /** Component artifacts: pretty-printed { component, props } JSON. */
897
+ jsonPayload?: string;
898
+ };
899
+
900
+ /**
901
+ * Context passed to a `features.artifacts.statusLabel` function on every
902
+ * artifact streaming update, once per visible surface. Treat it as read-only:
903
+ * the function should be pure and fast (it runs per streaming delta, per
904
+ * surface).
905
+ */
906
+ export type PersonaArtifactStatusLabelContext = {
907
+ artifactId: string;
908
+ artifactType: "markdown" | "component";
909
+ title?: string;
910
+ /** The default subject, e.g. "document", "component", or a file type label like "HTML file". */
911
+ typeLabel: string;
912
+ file?: PersonaArtifactFileMeta;
913
+ /** Accumulated streamed content length so far (0 for component artifacts). */
914
+ chars: number;
915
+ /** Accumulated line count so far (0 for component artifacts). */
916
+ lines: number;
917
+ /** Milliseconds since this artifact's first streaming update was seen. */
918
+ elapsedMs: number;
919
+ /** Lazy accessor for the accumulated markdown source (empty string for component artifacts). Call only if needed; parsing cost is opt-in. */
920
+ content: () => string;
921
+ /** Which surface is asking, so hosts can shorten for cramped spots. */
922
+ surface: "card" | "inline-chrome" | "status-body";
923
+ };
924
+
925
+ /** A custom action button rendered in an artifact toolbar/card slot. */
926
+ export type PersonaArtifactCustomAction = {
927
+ /** Stable id, used for DOM wiring and keyed re-renders. */
928
+ id: string;
929
+ /** Accessible label; also the visible text when showLabel is true. */
930
+ label: string;
931
+ /** Registry icon name, or a factory returning your own element (e.g. a brand SVG). The factory result is author-owned code, not sanitized wire data. */
932
+ icon?: IconName | (() => HTMLElement | SVGElement);
933
+ /** Render icon + text instead of icon-only. Default: false. */
934
+ showLabel?: boolean;
935
+ /** Per-artifact gate, evaluated on each render. Omitted = always visible. */
936
+ visible?: (ctx: PersonaArtifactActionContext) => boolean;
937
+ /** May be async; rejections are swallowed like built-in action errors. */
938
+ onClick: (ctx: PersonaArtifactActionContext) => void | Promise<void>;
939
+ };
940
+
844
941
  export type AgentWidgetArtifactsFeature = {
845
942
  /** When true, Persona shows the artifact pane and handles artifact_* SSE events */
846
943
  enabled?: boolean;
847
944
  /** If set, artifact events for other types are ignored */
848
945
  allowedTypes?: PersonaArtifactKind[];
946
+ /**
947
+ * Where artifact bodies render. A single mode applies to all artifacts;
948
+ * the object form sets a default plus per-type overrides.
949
+ * Defaults to "panel" (current behavior).
950
+ */
951
+ display?:
952
+ | PersonaArtifactDisplayMode
953
+ | {
954
+ default?: PersonaArtifactDisplayMode;
955
+ byType?: Partial<Record<PersonaArtifactKind, PersonaArtifactDisplayMode>>;
956
+ };
849
957
  /** Split / drawer dimensions and launcher widen behavior */
850
958
  layout?: AgentWidgetArtifactsLayoutConfig;
851
959
  /**
852
- * Called when an artifact card action is triggered (open, download).
853
- * Return `true` to prevent the default behavior.
960
+ * Controls inline preview of previewable file artifacts (HTML/SVG written by
961
+ * a Claude Managed agent). When a markdown artifact carries `file` metadata,
962
+ * the pane can render an isolated `<iframe srcdoc>` sandbox instead of a code
963
+ * block.
964
+ */
965
+ filePreview?: {
966
+ /** Enable rendered preview for html/svg files. Defaults to enabled. Set false to force source view. */
967
+ enabled?: boolean;
968
+ /**
969
+ * `sandbox` attribute for the preview iframe. Defaults to `"allow-scripts"`.
970
+ * `allow-same-origin` is stripped (with a console warning) unless
971
+ * `dangerouslyAllowSameOrigin` is set: the opaque origin is what isolates
972
+ * the preview from the host page. Embedder-trusted config.
973
+ */
974
+ iframeSandbox?: string;
975
+ /**
976
+ * Allows `allow-same-origin` to pass through `iframeSandbox`. Off by
977
+ * default because it lets agent-written file content run with the host
978
+ * page's origin (DOM, storage, credentialed requests). Only set this when
979
+ * every previewed file source is fully trusted.
980
+ */
981
+ dangerouslyAllowSameOrigin?: boolean;
982
+ /**
983
+ * Loading treatment for the preview iframe. Default on.
984
+ *
985
+ * A sandboxed `iframe srcdoc` (opaque origin, `allow-scripts` only) paints
986
+ * blank until its content renders — seconds for artifacts that pull CDN
987
+ * scripts (React/Babel). The default shows a themed "Loading preview…"
988
+ * overlay over the iframe, dismissed the instant the content signals it has
989
+ * painted (an injected `postMessage` reporter) or a hard timeout elapses.
990
+ *
991
+ * `false` disables the overlay and the injected ready signal entirely (the
992
+ * srcdoc becomes the raw file source, the prior behavior). An object tunes
993
+ * the timing; omitted / `true` uses all defaults.
994
+ */
995
+ loading?:
996
+ | boolean
997
+ | {
998
+ /** Show the overlay only if the preview is not ready after this many ms. Default 200. */
999
+ delayMs?: number;
1000
+ /** Once shown, keep the overlay visible at least this long (anti-flicker). Default 300. */
1001
+ minVisibleMs?: number;
1002
+ /** Give up waiting and reveal the iframe regardless after this many ms. Default 8000. */
1003
+ timeoutMs?: number;
1004
+ /**
1005
+ * Append the `postMessage` ready reporter to the srcdoc so the overlay
1006
+ * dismisses on first paint rather than the iframe `load` event (which
1007
+ * fires before post-DOMContentLoaded rendering like Babel compilation).
1008
+ * Default true. When false, the overlay dismisses on `load` + a double
1009
+ * `requestAnimationFrame`, or the timeout.
1010
+ */
1011
+ injectReadySignal?: boolean;
1012
+ /**
1013
+ * Escalation text shown beside the spinner after `labelDelayMs`.
1014
+ * Default `"Starting preview..."`. `false` renders an icon-only
1015
+ * indicator forever (no text ever). A short work-naming label is
1016
+ * added only after a delay by design — icon-first, with text reserved
1017
+ * for genuinely slow loads (per Geist/Sandpack guidance; HIG warns
1018
+ * against the vague word "loading").
1019
+ */
1020
+ label?: string | false;
1021
+ /**
1022
+ * Delay before the escalation label fades in, measured from when the
1023
+ * overlay actually becomes visible (i.e. after `delayMs`), not from
1024
+ * iframe creation. Default 2000.
1025
+ */
1026
+ labelDelayMs?: number;
1027
+ /**
1028
+ * Full replacement for the default indicator (spinner + escalation
1029
+ * label). Called once when the overlay is built; return any element
1030
+ * (a brand mark, skeleton, custom animation) to own the indicator
1031
+ * content entirely (the escalation-label logic is then skipped), or
1032
+ * return `null` to fall back to the default. A thrown error also falls
1033
+ * back. Mirrors `renderInline` / `renderCard`'s null-falls-back
1034
+ * contract. The overlay backdrop, timing, and dismissal stay
1035
+ * widget-owned either way.
1036
+ */
1037
+ renderIndicator?: (ctx: {
1038
+ artifactId: string;
1039
+ config: AgentWidgetConfig;
1040
+ }) => HTMLElement | null;
1041
+ };
1042
+ };
1043
+ /**
1044
+ * Custom action buttons for the artifact pane toolbar, rendered between the
1045
+ * refresh and expand/close controls in both toolbar presets. Re-read on live
1046
+ * config updates.
1047
+ */
1048
+ toolbarActions?: PersonaArtifactCustomAction[];
1049
+ /**
1050
+ * Custom action buttons for the artifact reference card, rendered before the
1051
+ * Download button on complete artifacts. Clicks are delegated, so they
1052
+ * survive re-renders and page refresh.
1053
+ */
1054
+ cardActions?: PersonaArtifactCustomAction[];
1055
+ /**
1056
+ * File chrome (title bar + toolbar) around inline preview blocks
1057
+ * (`display: "inline"`). Default when undefined: chrome on, with the built-in
1058
+ * Copy and Expand controls. Pass `false` to render the bare preview body
1059
+ * (frame → padded body, no chrome bar), or an object to toggle the built-in
1060
+ * controls individually.
1061
+ *
1062
+ * Note: default-on chrome adds a title bar and buttons to every existing
1063
+ * inline host on upgrade; set `inlineChrome: false` to opt out.
1064
+ */
1065
+ inlineChrome?:
1066
+ | boolean
1067
+ | {
1068
+ /** Show the built-in Copy button (complete artifacts only). Default true. */
1069
+ showCopy?: boolean;
1070
+ /** Show the built-in Expand button (opens the pane for this artifact). Default true. */
1071
+ showExpand?: boolean;
1072
+ /**
1073
+ * Show the built-in rendered/source view toggle (complete artifacts
1074
+ * only). Default true. The toggle is availability-gated: it appears only
1075
+ * for file-backed markdown artifacts that have a rendered alternative to
1076
+ * their source (previewable HTML/SVG, or markdown-kind files), and never
1077
+ * when `inlineBody.viewMode: "source"` forces a source-only body. Plain
1078
+ * markdown and component artifacts don't get it (the pane toggle covers
1079
+ * those).
1080
+ */
1081
+ showViewToggle?: boolean;
1082
+ };
1083
+ /**
1084
+ * Custom action buttons for the inline chrome toolbar, rendered before the
1085
+ * built-in Copy/Expand controls on complete artifacts. Same shape as
1086
+ * `cardActions`; each action's `visible` gate is respected and clicks are
1087
+ * delegated, so they survive re-renders and page refresh.
1088
+ */
1089
+ inlineActions?: PersonaArtifactCustomAction[];
1090
+ /**
1091
+ * Replace the artifact tab strip entirely (Seam A). Return an element mounted in
1092
+ * place of the built-in `.persona-artifact-list`; the pane still owns the toolbar,
1093
+ * preview body, iframe reuse, and lazy-render gate. Re-invoked when the records or
1094
+ * selection change. Custom bars own their own accessibility; use `createRovingTablist`
1095
+ * to keep keyboard/tablist behavior. When set, the built-in strip (scroll, fade,
1096
+ * tablist) is not rendered.
1097
+ *
1098
+ * Return either a fresh element per call, or the SAME element updated in place.
1099
+ * Returning a stable element preserves internal state across selection changes:
1100
+ * the pane skips remounting an unchanged node, so a roving tablist keeps keyboard
1101
+ * focus on the selected tab (a fresh element per call drops focus and breaks
1102
+ * Arrow-key nav). See the reference bars in `apps/web/src/artifact-demo.ts`.
1103
+ */
1104
+ renderTabBar?: (ctx: {
1105
+ records: PersonaArtifactRecord[];
1106
+ selectedId: string | null;
1107
+ onSelect: (id: string) => void;
1108
+ }) => HTMLElement;
1109
+ /**
1110
+ * Body layout for inline artifact blocks (`display: "inline"`). Controls how
1111
+ * the block reserves height and behaves while a file/document artifact
1112
+ * streams, so the streaming source no longer grows an unbounded `<pre>` and
1113
+ * then snaps to the completed preview. Affects only the inline block; the
1114
+ * artifact pane is unchanged.
1115
+ *
1116
+ * Defaults (a behavior change from prior versions, which grew with content
1117
+ * during streaming): a fixed 320px streaming source window with tail-follow,
1118
+ * a top edge fade, and an animated streaming→complete swap. Pass
1119
+ * `height: "auto"` to restore the old grow-with-content streaming behavior.
1120
+ */
1121
+ inlineBody?: {
1122
+ /** What the body shows while streaming. Default `"source"`. */
1123
+ streamingView?: "source" | "status";
1124
+ /**
1125
+ * How the completed body renders. `"rendered"` (default) keeps the current
1126
+ * behavior: file previews in a sandboxed iframe, markdown through the
1127
+ * markdown pipeline. `"source"` always shows the raw syntax-highlighted
1128
+ * source instead — for hosts where the artifact is input to the host system
1129
+ * (a code editor, a query runner) rather than something to preview. Unlike
1130
+ * `filePreview.enabled: false`, this also covers markdown-kind files and
1131
+ * plain markdown artifacts. Component artifacts always render through the
1132
+ * registry.
1133
+ */
1134
+ viewMode?: "rendered" | "source";
1135
+ /**
1136
+ * Body height per state. A number reserves that many px for the whole body
1137
+ * region (border-box, padding included) with internal scroll — the
1138
+ * zero-layout-shift path; `"auto"` means content-sized. A
1139
+ * scalar applies to both the streaming and complete states; the object form
1140
+ * sets them independently. Default `320` for both states.
1141
+ *
1142
+ * Note: with `"auto"` on the complete state, non-iframe bodies (rendered
1143
+ * markdown, source view, component) are content-sized, but a file-preview
1144
+ * iframe cannot be content-sized without postMessage plumbing, so it falls
1145
+ * back to the CSS-var default (`--persona-artifact-inline-frame-height`,
1146
+ * 320px).
1147
+ *
1148
+ * Numeric heights are applied via the CSS var
1149
+ * `--persona-artifact-inline-body-height` on the block root. The older
1150
+ * `--persona-artifact-inline-frame-height` var still overrides the iframe
1151
+ * height when this one is unset, so themes pinned to it keep working.
1152
+ */
1153
+ height?:
1154
+ | number
1155
+ | "auto"
1156
+ | { streaming?: number | "auto"; complete?: number | "auto" };
1157
+ /**
1158
+ * Tail-follow the newest streamed lines in a fixed-height source window: if
1159
+ * the viewport is already at the bottom, keep it pinned as content arrives;
1160
+ * if the reader scrolled up, don't fight them. Only meaningful for a numeric
1161
+ * streaming height. Default `true`.
1162
+ *
1163
+ * Precedence: ignored when `overflow: "clip"` (a clipped window shows the
1164
+ * top of the document and never scrolls, so there is no tail to follow).
1165
+ */
1166
+ followOutput?: boolean;
1167
+ /**
1168
+ * How a fixed-height source window handles content taller than the window.
1169
+ *
1170
+ * - `"scroll"` (default) — the current behavior: an internally scrollable
1171
+ * window that tail-follows the newest streamed lines (see `followOutput`),
1172
+ * so the reader sees the growing tail of the document.
1173
+ * - `"clip"` — a fixed-height window that shows the TOP of the document,
1174
+ * `overflow: hidden` with no internal scroll and no tail-follow
1175
+ * (`followOutput` is ignored in this mode). When content overflows the
1176
+ * window a bottom edge fade signals there is more below (the top is always
1177
+ * visible, so a top fade never applies); an explicit `fadeMask` still wins
1178
+ * over this clip-mode default. When the inline chrome's Expand control is
1179
+ * enabled the whole body doubles as an expand hitbox (click / Enter / Space
1180
+ * opens the artifact in the pane), matching the industry inline-card
1181
+ * pattern; otherwise the clip is purely visual.
1182
+ *
1183
+ * Only meaningful for a numeric streaming/complete height. Default
1184
+ * `"scroll"`.
1185
+ */
1186
+ overflow?: "scroll" | "clip";
1187
+ /**
1188
+ * Edge fade masks on the fixed-height streaming window (a top fade signals
1189
+ * clipped content above, a bottom fade signals clipped content below). Each
1190
+ * fade renders only when content is actually clipped on that edge, so with
1191
+ * tail-follow pinned at the bottom the bottom fade stays inert until the
1192
+ * reader scrolls up mid-stream. `true` = `{ top: true, bottom: true }`,
1193
+ * `false` = none. Default `{ top: true, bottom: true }`.
1194
+ */
1195
+ fadeMask?: boolean | { top?: boolean; bottom?: boolean };
1196
+ /**
1197
+ * Animate the streaming→complete body swap. `"auto"` uses the View
1198
+ * Transitions API when supported and `prefers-reduced-motion` is off;
1199
+ * `"none"` swaps instantly. Default `"auto"`.
1200
+ */
1201
+ transition?: "auto" | "none";
1202
+ /**
1203
+ * What the block becomes once the artifact completes.
1204
+ *
1205
+ * - `"inline"` (default) — the streamed body stays inline forever, as
1206
+ * configured by the other `inlineBody` options.
1207
+ * - `"card"` — the block streams inline as configured, then collapses to the
1208
+ * compact artifact reference card once the artifact completes (the Copilot
1209
+ * "chiclet" / Claude side-panel handoff pattern), keeping the thread
1210
+ * scannable. The collapse plays as a short CSS height transition (skipped
1211
+ * under `prefers-reduced-motion`); it does not use the View Transitions
1212
+ * API, so `transition` does not affect it. After the collapse the card's
1213
+ * normal actions apply (click opens the artifact pane). `viewMode`, the
1214
+ * inline rendered/source view toggle, `inlineActions`, and inline copy are
1215
+ * streaming-phase-only in this mode and inert once the block is a card
1216
+ * (there is nothing to toggle or copy on a collapsed card). Blocks that are
1217
+ * already complete when they first render (page-refresh hydration) render
1218
+ * the card directly, with no flash of the inline body and no animation.
1219
+ *
1220
+ * Default `"inline"`.
1221
+ */
1222
+ completeDisplay?: "inline" | "card";
1223
+ };
1224
+ /**
1225
+ * Called when an artifact action is triggered (open, download, expand).
1226
+ * Return `true` to intercept: the widget then does not change state.
1227
+ * For `expand`, `expanded` is the state the toggle is about to enter and
1228
+ * `artifactId` is the currently selected artifact (or `null` when none).
854
1229
  */
855
- onArtifactAction?: (action: {
856
- type: 'open' | 'download';
857
- artifactId: string;
858
- }) => boolean | void;
1230
+ onArtifactAction?: (
1231
+ action:
1232
+ | { type: 'open' | 'download'; artifactId: string }
1233
+ | { type: 'expand'; artifactId: string | null; expanded: boolean }
1234
+ ) => boolean | void;
859
1235
  /**
860
1236
  * Custom renderer for artifact reference cards shown in the message thread.
861
1237
  * Return an HTMLElement to replace the default card, or `null` to use the default.
@@ -870,6 +1246,73 @@ export type AgentWidgetArtifactsFeature = {
870
1246
  config: AgentWidgetConfig;
871
1247
  defaultRenderer: () => HTMLElement;
872
1248
  }) => HTMLElement | null;
1249
+ /**
1250
+ * Custom renderer for inline artifact blocks (display: "inline"), mirroring
1251
+ * `renderCard`. Return an element to replace the default inline preview,
1252
+ * or null to use the default.
1253
+ */
1254
+ renderInline?: (context: {
1255
+ artifact: {
1256
+ artifactId: string;
1257
+ title: string;
1258
+ artifactType: string;
1259
+ status: string;
1260
+ };
1261
+ config: AgentWidgetConfig;
1262
+ defaultRenderer: () => HTMLElement;
1263
+ }) => HTMLElement | null;
1264
+ /**
1265
+ * Text shown while an artifact streams, in place of the default
1266
+ * `Generating <type>...` status. Applies to every streaming surface: the
1267
+ * reference card status line, the inline chrome meta label, and the inline
1268
+ * streaming status body (`inlineBody.streamingView: "status"`).
1269
+ *
1270
+ * - A plain string replaces the default label everywhere (localization or
1271
+ * brand voice).
1272
+ * - A function is called on every artifact update for each visible surface;
1273
+ * it must be pure and fast. Returning a string sets the animated label
1274
+ * only. Returning `{ label, detail }` splits the status into an animated
1275
+ * label (re-applied only when its text changes, so the loading animation
1276
+ * stays stable) and a plain un-animated detail span that may update freely
1277
+ * per delta (live counters).
1278
+ *
1279
+ * Default when unset: the current behavior, `Generating <type>...` with no
1280
+ * detail. If the function throws, the default label is used for that update
1281
+ * (a bad host callback must never break rendering). There is deliberately no
1282
+ * percent progress: streams do not announce total length, so counts and
1283
+ * elapsed time are the only truthful signals.
1284
+ */
1285
+ statusLabel?:
1286
+ | string
1287
+ | ((ctx: PersonaArtifactStatusLabelContext) =>
1288
+ | string
1289
+ | { label: string; detail?: string });
1290
+ /**
1291
+ * Animation applied to the artifact card's "Generating …" status text while
1292
+ * the artifact streams. Character-by-character modes (`shimmer`,
1293
+ * `shimmer-color`, `rainbow`) wrap each character in a span with a staggered
1294
+ * `animation-delay`; `pulse` fades the whole status text; `none` disables the
1295
+ * animation. Honors `prefers-reduced-motion`.
1296
+ * @default "shimmer"
1297
+ */
1298
+ loadingAnimation?: AgentWidgetToolCallLoadingAnimation;
1299
+ /**
1300
+ * Duration of one full animation cycle in milliseconds.
1301
+ * Applies to pulse, shimmer, shimmer-color, and rainbow modes.
1302
+ * @default 2000
1303
+ */
1304
+ loadingAnimationDuration?: number;
1305
+ /**
1306
+ * Primary color for shimmer-color animation mode.
1307
+ * Defaults to the current text color.
1308
+ */
1309
+ loadingAnimationColor?: string;
1310
+ /**
1311
+ * Secondary/end color for shimmer-color animation mode.
1312
+ * Creates a gradient sweep between `loadingAnimationColor` and this color.
1313
+ * @default "#3b82f6"
1314
+ */
1315
+ loadingAnimationSecondaryColor?: string;
873
1316
  };
874
1317
 
875
1318
  /**
@@ -1012,6 +1455,13 @@ export type AgentWidgetToolCallDisplayFeature = {
1012
1455
  * @default false
1013
1456
  */
1014
1457
  grouped?: boolean;
1458
+ /**
1459
+ * Controls how a grouped tool sequence is rendered.
1460
+ * - `"stack"`: show the group summary and each child tool row.
1461
+ * - `"summary"`: show one consolidated summary row only.
1462
+ * @default "stack"
1463
+ */
1464
+ groupedMode?: "stack" | "summary";
1015
1465
  /**
1016
1466
  * When false, tool call bubbles show only the collapsed summary with no
1017
1467
  * expand/collapse toggle. Users see tool awareness without full details.
@@ -1740,6 +2190,24 @@ export type AgentWidgetLauncherConfig = {
1740
2190
  * @default false
1741
2191
  */
1742
2192
  sidebarMode?: boolean;
2193
+ /**
2194
+ * When true, the widget panel renders as a detached card inset within its region,
2195
+ * with rounded corners on all sides and elevation (shadow plus hairline border),
2196
+ * instead of sitting flush against the region edges.
2197
+ *
2198
+ * The gap around the card is themed via `theme.components.panel.inset`. The
2199
+ * background revealed behind it is themed via `theme.components.panel.canvasBackground`
2200
+ * in docked and inline embed modes. In sidebar mode the gap stays click-through, so the
2201
+ * host page shows through it the same way it does in floating mode, and canvasBackground
2202
+ * is a no-op there.
2203
+ *
2204
+ * Applies to sidebar, docked, and inline embed modes. Ignored in mobile fullscreen,
2205
+ * where chrome is already suppressed. Floating mode is unchanged, since it already
2206
+ * renders as a detached card.
2207
+ *
2208
+ * @default false
2209
+ */
2210
+ detachedPanel?: boolean;
1743
2211
  /**
1744
2212
  * Width of the sidebar panel when sidebarMode is true.
1745
2213
  * @default "420px"
@@ -4801,6 +5269,22 @@ export type InjectComponentDirectiveOptions = {
4801
5269
  sequence?: number;
4802
5270
  };
4803
5271
 
5272
+ /**
5273
+ * Optional file metadata attached to a markdown artifact that represents a
5274
+ * concrete file written by a Claude Managed agent (e.g. `/mnt/session/outputs/cat.html`).
5275
+ * Additive: the artifact content stays a fenced code block on the wire for
5276
+ * backward compatibility; new widgets use this to recover the raw source and
5277
+ * offer a preview. Mirrors the `file` field on the core `artifact_start` event.
5278
+ */
5279
+ export type PersonaArtifactFileMeta = {
5280
+ /** Full path of the written file (also used as the artifact title). */
5281
+ path: string;
5282
+ /** MIME type of the file (used for download and preview decisions). */
5283
+ mimeType: string;
5284
+ /** Fence language the content was encoded with (html/xml/md/text). */
5285
+ language?: string;
5286
+ };
5287
+
4804
5288
  export type PersonaArtifactRecord = {
4805
5289
  id: string;
4806
5290
  artifactType: PersonaArtifactKind;
@@ -4809,17 +5293,30 @@ export type PersonaArtifactRecord = {
4809
5293
  markdown?: string;
4810
5294
  component?: string;
4811
5295
  props?: Record<string, unknown>;
5296
+ /** Present when this markdown artifact is a previewable file (see PersonaArtifactFileMeta). */
5297
+ file?: PersonaArtifactFileMeta;
4812
5298
  };
4813
5299
 
4814
5300
  /** Programmatic artifact upsert (controller / window API) */
4815
5301
  export type PersonaArtifactManualUpsert =
4816
- | { id?: string; artifactType: "markdown"; title?: string; content: string }
5302
+ | {
5303
+ id?: string;
5304
+ artifactType: "markdown";
5305
+ title?: string;
5306
+ content: string;
5307
+ /** Optional file metadata for previewable file artifacts. */
5308
+ file?: PersonaArtifactFileMeta;
5309
+ /** Set false to update the registry/pane without a transcript block (pre-4.x behavior). */
5310
+ transcript?: boolean;
5311
+ }
4817
5312
  | {
4818
5313
  id?: string;
4819
5314
  artifactType: "component";
4820
5315
  title?: string;
4821
5316
  component: string;
4822
5317
  props?: Record<string, unknown>;
5318
+ /** Set false to update the registry/pane without a transcript block (pre-4.x behavior). */
5319
+ transcript?: boolean;
4823
5320
  };
4824
5321
 
4825
5322
  export type AgentWidgetEvent =
@@ -4850,6 +5347,8 @@ export type AgentWidgetEvent =
4850
5347
  artifactType: PersonaArtifactKind;
4851
5348
  title?: string;
4852
5349
  component?: string;
5350
+ /** Present when this markdown artifact is a previewable file. */
5351
+ file?: PersonaArtifactFileMeta;
4853
5352
  }
4854
5353
  | { type: "artifact_delta"; id: string; artDelta: string }
4855
5354
  | {