@runtypelabs/persona 4.17.1 → 4.18.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 (158) hide show
  1. package/dist/chunk-3I2S3I5P.js +1 -0
  2. package/dist/chunk-76IELU7F.js +1 -0
  3. package/dist/chunk-EFJBUH5F.js +1 -0
  4. package/dist/chunk-PDPVORUF.js +1 -0
  5. package/dist/chunk-Q735PSZL.js +1 -0
  6. package/dist/chunk-VK6JOIYJ.js +1 -0
  7. package/dist/codegen.cjs +4 -4
  8. package/dist/codegen.js +4 -4
  9. package/dist/{context-mentions-BTSTIBFC.js → context-mentions-MJL6S6P2.js} +1 -1
  10. package/dist/context-mentions.cjs +9 -8
  11. package/dist/context-mentions.d.cts +1212 -14
  12. package/dist/context-mentions.d.ts +1212 -14
  13. package/dist/context-mentions.js +9 -8
  14. package/dist/event-stream-view-4EZ7BGFK.js +1 -0
  15. package/dist/event-stream-view.cjs +129 -0
  16. package/dist/event-stream-view.d.cts +8384 -0
  17. package/dist/event-stream-view.d.ts +8384 -0
  18. package/dist/event-stream-view.js +129 -0
  19. package/dist/history-view-BD6OXLNA.js +898 -0
  20. package/dist/history-view.cjs +898 -0
  21. package/dist/history-view.d.cts +1461 -0
  22. package/dist/history-view.d.ts +1461 -0
  23. package/dist/history-view.js +898 -0
  24. package/dist/index.cjs +77 -77
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +1987 -17
  27. package/dist/index.d.ts +1987 -17
  28. package/dist/index.global.js +67 -66
  29. package/dist/index.global.js.map +1 -1
  30. package/dist/index.js +77 -77
  31. package/dist/index.js.map +1 -1
  32. package/dist/launcher.global.js +5 -4
  33. package/dist/launcher.global.js.map +1 -1
  34. package/dist/plugin-kit.cjs +1 -1
  35. package/dist/plugin-kit.d.cts +0 -8
  36. package/dist/plugin-kit.d.ts +0 -8
  37. package/dist/plugin-kit.js +1 -1
  38. package/dist/smart-dom-reader.d.cts +1212 -14
  39. package/dist/smart-dom-reader.d.ts +1212 -14
  40. package/dist/theme-editor-preview.cjs +970 -73
  41. package/dist/theme-editor-preview.d.cts +1331 -15
  42. package/dist/theme-editor-preview.d.ts +1331 -15
  43. package/dist/theme-editor-preview.js +81 -81
  44. package/dist/theme-editor.cjs +7 -7
  45. package/dist/theme-editor.d.cts +1215 -15
  46. package/dist/theme-editor.d.ts +1215 -15
  47. package/dist/theme-editor.js +9 -9
  48. package/dist/theme-reference.cjs +1 -1
  49. package/dist/theme-reference.d.cts +226 -7
  50. package/dist/theme-reference.d.ts +226 -7
  51. package/dist/theme-reference.js +1 -1
  52. package/dist/widget.css +1 -1
  53. package/package.json +16 -4
  54. package/src/client-history-rest.test.ts +1034 -0
  55. package/src/client-visitor-history.test.ts +596 -0
  56. package/src/client.ts +1040 -24
  57. package/src/components/artifact-inline.ts +8 -1
  58. package/src/components/artifact-pane.test.ts +278 -1
  59. package/src/components/artifact-pane.ts +135 -67
  60. package/src/components/artifact-preview.test.ts +14 -1
  61. package/src/components/artifact-preview.ts +9 -0
  62. package/src/components/header-builder.ts +3 -0
  63. package/src/components/header-layouts.test.ts +171 -7
  64. package/src/components/header-layouts.ts +83 -53
  65. package/src/components/header-parts.test.ts +154 -1
  66. package/src/components/header-parts.ts +266 -138
  67. package/src/components/history-confirm-dialog.ts +177 -0
  68. package/src/components/history-shell-copy.ts +99 -0
  69. package/src/components/history-view/announcer.ts +40 -0
  70. package/src/components/history-view/copy.ts +105 -0
  71. package/src/components/history-view/css.ts +1078 -0
  72. package/src/components/history-view/grouping.test.ts +142 -0
  73. package/src/components/history-view/grouping.ts +142 -0
  74. package/src/components/history-view/icons.ts +73 -0
  75. package/src/components/history-view/rows.ts +341 -0
  76. package/src/components/history-view/state.ts +309 -0
  77. package/src/components/history-view.test.ts +2660 -0
  78. package/src/components/history-view.ts +1891 -0
  79. package/src/components/launcher-surface.test.ts +133 -0
  80. package/src/components/launcher.test.ts +18 -0
  81. package/src/components/launcher.ts +7 -7
  82. package/src/components/panel.ts +17 -7
  83. package/src/components/welcome.ts +72 -2
  84. package/src/defaults.ts +36 -3
  85. package/src/event-stream-view-bundle.test.ts +69 -0
  86. package/src/event-stream-view-entry.ts +19 -0
  87. package/src/event-stream-view-loader.ts +31 -0
  88. package/src/event-stream-view.ts +19 -0
  89. package/src/generated/runtype-openapi-contract.ts +1 -0
  90. package/src/history-render.ts +325 -0
  91. package/src/history-view-bundle.test.ts +78 -0
  92. package/src/history-view-entry.ts +41 -0
  93. package/src/history-view-loader.test.ts +30 -0
  94. package/src/history-view-loader.ts +32 -0
  95. package/src/history-view.ts +28 -0
  96. package/src/index-core.ts +50 -2
  97. package/src/index-global.ts +55 -0
  98. package/src/internal/demo-history-provider.test.ts +402 -0
  99. package/src/internal/demo-history-provider.ts +689 -0
  100. package/src/internal/history-provider-registry.ts +25 -0
  101. package/src/internal/history-provider.ts +150 -0
  102. package/src/internal/runtype-history-provider.test.ts +313 -0
  103. package/src/internal/runtype-history-provider.ts +240 -0
  104. package/src/plugin-kit.test.ts +16 -0
  105. package/src/plugin-kit.ts +13 -5
  106. package/src/plugins/types.ts +177 -1
  107. package/src/session-history.test.ts +1397 -0
  108. package/src/session.ts +1200 -26
  109. package/src/styles/widget-styles.test.ts +133 -0
  110. package/src/styles/widget.css +227 -8
  111. package/src/theme-editor/preview-utils.test.ts +19 -0
  112. package/src/theme-editor/preview-utils.ts +15 -1
  113. package/src/theme-editor/preview.ts +7 -1
  114. package/src/theme-editor/role-mappings.ts +4 -1
  115. package/src/theme-editor/sections.test.ts +168 -1
  116. package/src/theme-editor/sections.ts +136 -7
  117. package/src/theme-editor/webmcp/summary.ts +16 -2
  118. package/src/theme-reference.ts +20 -6
  119. package/src/types/theme.ts +213 -7
  120. package/src/types.ts +927 -7
  121. package/src/ui.artifact-rail-resize.test.ts +235 -0
  122. package/src/ui.attachments-drop.test.ts +1 -0
  123. package/src/ui.header-update-stability.test.ts +2 -2
  124. package/src/ui.history-chunk.test.ts +72 -0
  125. package/src/ui.history-config-provider.test.ts +318 -0
  126. package/src/ui.history-controller.test.ts +711 -0
  127. package/src/ui.history-lifecycle.test.ts +456 -0
  128. package/src/ui.history-render-hooks.test.ts +904 -0
  129. package/src/ui.history-shell.test.ts +1887 -0
  130. package/src/ui.rail-overlay.test.ts +573 -0
  131. package/src/ui.rail-resize.test.ts +284 -0
  132. package/src/ui.rebuild-composer.test.ts +21 -0
  133. package/src/ui.render-welcome.test.ts +42 -0
  134. package/src/ui.shortcuts.test.ts +166 -0
  135. package/src/ui.ts +3071 -240
  136. package/src/utils/__fixtures__/client-conversation-history-wire.json +108 -0
  137. package/src/utils/artifact-file.ts +15 -0
  138. package/src/utils/artifact-gate.test.ts +4 -0
  139. package/src/utils/artifact-gate.ts +13 -17
  140. package/src/utils/artifact-resize.test.ts +28 -0
  141. package/src/utils/artifact-resize.ts +9 -2
  142. package/src/utils/buttons.ts +188 -1
  143. package/src/utils/config-merge.test.ts +91 -0
  144. package/src/utils/dropdown.ts +10 -1
  145. package/src/utils/history-messages.test.ts +284 -0
  146. package/src/utils/history-messages.ts +284 -0
  147. package/src/utils/icons.ts +3 -0
  148. package/src/utils/shortcuts.test.ts +288 -0
  149. package/src/utils/shortcuts.ts +221 -0
  150. package/src/utils/theme.test.ts +501 -4
  151. package/src/utils/theme.ts +42 -3
  152. package/src/utils/tokens.ts +153 -15
  153. package/src/utils/tooltip-timing.ts +4 -0
  154. package/src/utils/tooltip.test.ts +249 -7
  155. package/src/utils/tooltip.ts +146 -4
  156. package/src/utils/visitor-store.test.ts +425 -0
  157. package/src/utils/visitor-store.ts +337 -0
  158. package/dist/chunk-QJ4LV2KW.js +0 -1
@@ -78,6 +78,22 @@ describe("injectStyles / getStyleRoot", () => {
78
78
  expect(shadow.querySelector('style[data-persona-plugin-style="kit-deferred"]')).not.toBeNull();
79
79
  });
80
80
 
81
+ it("re-injects into a foreign document's head for a node adopted after the call", async () => {
82
+ // Mirrors the theme-editor preview: nodes built against the outer document
83
+ // and mounted into an iframe's. The branch must resolve by nodeType, since
84
+ // a real iframe document fails cross-realm instanceof checks.
85
+ const foreignDoc = document.implementation.createHTMLDocument("frame");
86
+ const el = document.createElement("div");
87
+ injectStyles(el, "kit-foreign", ".f {}");
88
+ foreignDoc.body.appendChild(el);
89
+
90
+ await flushMicrotasks();
91
+
92
+ expect(
93
+ foreignDoc.head.querySelector('style[data-persona-plugin-style="kit-foreign"]')
94
+ ).not.toBeNull();
95
+ });
96
+
81
97
  it("accepts an explicit Document or ShadowRoot target", () => {
82
98
  const host = document.createElement("div");
83
99
  document.body.appendChild(host);
package/src/plugin-kit.ts CHANGED
@@ -30,21 +30,29 @@
30
30
  * regardless of whether the widget runs in light or shadow DOM. For a detached
31
31
  * node (one not yet mounted) this falls back to the owning document.
32
32
  */
33
+ // nodeType checks, not instanceof: an iframe's Document/ShadowRoot fail
34
+ // cross-realm instanceof against this realm's constructors.
35
+ const isDocumentNode = (node: Node): node is Document =>
36
+ node.nodeType === Node.DOCUMENT_NODE;
37
+ const isShadowRootNode = (node: Node): node is ShadowRoot =>
38
+ node.nodeType === Node.DOCUMENT_FRAGMENT_NODE &&
39
+ (node as ShadowRoot).host !== undefined;
40
+
33
41
  export function getStyleRoot(node: Node): Document | ShadowRoot {
34
42
  const root = node.getRootNode?.();
35
- if (root instanceof ShadowRoot) return root;
36
- if (root instanceof Document) return root;
43
+ if (root && isShadowRootNode(root)) return root;
44
+ if (root && isDocumentNode(root)) return root;
37
45
  return node.ownerDocument ?? document;
38
46
  }
39
47
 
40
48
  function injectInto(root: Document | ShadowRoot, id: string, css: string): void {
41
49
  const container: Document | ShadowRoot | HTMLHeadElement =
42
- root instanceof Document ? root.head : root;
50
+ isDocumentNode(root) ? root.head : root;
43
51
  const escaped = id.replace(/["\\]/g, "\\$&");
44
52
  if (container.querySelector(`style[data-persona-plugin-style="${escaped}"]`)) {
45
53
  return; // Already present in this root: idempotent.
46
54
  }
47
- const doc = root instanceof Document ? root : root.ownerDocument ?? document;
55
+ const doc = isDocumentNode(root) ? root : root.ownerDocument ?? document;
48
56
  const style = doc.createElement("style");
49
57
  style.setAttribute("data-persona-plugin-style", id);
50
58
  style.textContent = css;
@@ -77,7 +85,7 @@ export function injectStyles(
77
85
  id: string,
78
86
  css: string
79
87
  ): void {
80
- if (target instanceof Document || target instanceof ShadowRoot) {
88
+ if (isDocumentNode(target) || isShadowRootNode(target)) {
81
89
  injectInto(target, id, css);
82
90
  return;
83
91
  }
@@ -1,5 +1,6 @@
1
1
  import type { ResolvedWelcomeConfig } from "../welcome";
2
2
  import type { AgentWidgetPluginStorage } from "../utils/plugin-storage";
3
+ import type { ShortcutScope } from "../utils/shortcuts";
3
4
  import {
4
5
  AgentWidgetMessage,
5
6
  AgentWidgetConfig,
@@ -16,7 +17,15 @@ import {
16
17
  AgentWidgetSuggestionSource,
17
18
  AgentWidgetSuggestionSurface,
18
19
  AgentWidgetSuggestionVariant,
19
- AgentWidgetWelcomeVariant
20
+ AgentWidgetWelcomeVariant,
21
+ AgentWidgetRenderHistoryViewContext,
22
+ AgentWidgetRenderHistoryHeaderContext,
23
+ AgentWidgetRenderHistoryConversationContext,
24
+ AgentWidgetRenderHistoryOpenErrorContext,
25
+ AgentWidgetRenderHistoryStateContext,
26
+ AgentWidgetHistoryRailSection,
27
+ AgentWidgetHistoryRenderActions,
28
+ ResolvedHistoryPresentation
20
29
  } from "../types";
21
30
 
22
31
  export type AgentWidgetTransformSuggestionsContext = {
@@ -70,6 +79,40 @@ export type AgentWidgetRenderWelcomeContext = {
70
79
  onCleanup: (fn: () => void) => void;
71
80
  };
72
81
 
82
+ /** Context for one `railSections` entry. Frozen per invocation. */
83
+ export type AgentWidgetRailSectionContext = {
84
+ /** Current rail width state. The section re-renders whenever this flips. */
85
+ collapsed: boolean;
86
+ presentation: ResolvedHistoryPresentation;
87
+ /** The same frozen action path the default rows and `renderHistoryView` use. */
88
+ actions: AgentWidgetHistoryRenderActions;
89
+ };
90
+
91
+ /** A plugin-contributed navigation section in the history rail. */
92
+ export type AgentWidgetRailSection = {
93
+ /** Stable identity, stamped as `data-persona-rail-section`. */
94
+ id: string;
95
+ /** Default `"above-conversations"`. */
96
+ placement?: AgentWidgetHistoryRailSection["placement"];
97
+ /** Optional heading, styled like a conversation date-group heading. */
98
+ title?: string;
99
+ /** Section body. Null renders nothing for the current state. */
100
+ render: (context: AgentWidgetRailSectionContext) => Element | null;
101
+ };
102
+
103
+ /** A plugin-contributed keyboard shortcut, bound per widget instance. */
104
+ export type AgentWidgetPluginShortcut = {
105
+ /** Stable identity, used in duplicate/throw warnings. */
106
+ id: string;
107
+ /** e.g. `"mod+b"`. "mod" is Command on Apple platforms, Control elsewhere. */
108
+ combo: string;
109
+ /** Default `"widget"`: fires only from inside the widget mount. */
110
+ scope?: ShortcutScope;
111
+ /** Returning false skips the binding for this keypress. */
112
+ when?: () => boolean;
113
+ run: () => void;
114
+ };
115
+
73
116
  export type AgentWidgetSuggestionSelectContext = {
74
117
  suggestion: AgentWidgetResolvedSuggestion;
75
118
  surface: AgentWidgetSuggestionSurface;
@@ -360,6 +403,10 @@ export interface AgentWidgetPlugin {
360
403
  * Custom renderer for loading indicator
361
404
  * Return null to use default renderer (or config-based renderer)
362
405
  *
406
+ * Covers the streaming indicator ('inline', 'standalone') and the transcript
407
+ * skeleton shown while a conversation opened from the Messages list loads
408
+ * ('conversation-open').
409
+ *
363
410
  * @example
364
411
  * ```typescript
365
412
  * renderLoadingIndicator: ({ location, defaultRenderer }) => {
@@ -417,6 +464,135 @@ export interface AgentWidgetPlugin {
417
464
  */
418
465
  renderEventStreamPayload?: (context: EventStreamPayloadRenderContext) => HTMLElement | null;
419
466
 
467
+ /**
468
+ * Replace the complete history ("Messages") navigation contents in either
469
+ * host. Plugins run by priority and the first non-null element wins; null
470
+ * falls through to the default full view, which then applies the first
471
+ * non-null header/row/state hook at each slot.
472
+ *
473
+ * Calling `defaultRenderer()` bypasses this hook only: the default view is
474
+ * built and its lower-level slots still arbitrate, including this plugin's.
475
+ * Return it (or an element containing it) to compose rather than replace.
476
+ *
477
+ * The hook replaces contents, not orchestration. Persona keeps placement,
478
+ * open/close, responsive rail <-> panel movement, inertness, Escape, the
479
+ * confirmation dialogs, live announcements, and focus fallback. Fetch nothing
480
+ * directly: `actions.refresh()` is the only way to reload the list.
481
+ *
482
+ * @example
483
+ * ```typescript
484
+ * renderHistoryView: ({ conversations, actions, presentation, onCleanup }) => {
485
+ * const rail = document.createElement("nav");
486
+ * rail.dataset.presentation = presentation;
487
+ * for (const conversation of conversations) {
488
+ * const row = document.createElement("button");
489
+ * row.textContent = conversation.title;
490
+ * row.addEventListener("click", () => void actions.openConversation(conversation.id));
491
+ * rail.appendChild(row);
492
+ * }
493
+ * const timer = setInterval(() => void actions.refresh(), 60_000);
494
+ * onCleanup(() => clearInterval(timer));
495
+ * return rail;
496
+ * }
497
+ * ```
498
+ */
499
+ renderHistoryView?: (
500
+ context: AgentWidgetRenderHistoryViewContext
501
+ ) => HTMLElement | null;
502
+
503
+ /**
504
+ * Replace the standard Messages header while retaining its list/state UI.
505
+ * Return null to use the default top bar. A custom header may not remove the
506
+ * keyboard close path or falsify the resolved open state.
507
+ */
508
+ renderHistoryHeader?: (
509
+ context: AgentWidgetRenderHistoryHeaderContext
510
+ ) => HTMLElement | null;
511
+
512
+ /**
513
+ * Replace one conversation row while retaining standard paging/grouping.
514
+ * Return null to use the default row.
515
+ */
516
+ renderHistoryConversation?: (
517
+ context: AgentWidgetRenderHistoryConversationContext
518
+ ) => HTMLElement | null;
519
+
520
+ /**
521
+ * Replace loading, empty, error, authentication, rate-limit, or recovery
522
+ * content in the list region. Return null to use the default block.
523
+ */
524
+ renderHistoryState?: (
525
+ context: AgentWidgetRenderHistoryStateContext
526
+ ) => HTMLElement | null;
527
+
528
+ /**
529
+ * Replace the transcript-level error surface shown when opening a
530
+ * conversation from the Messages list fails. Plugins run by priority and
531
+ * the first non-null element wins; null falls through to the default block.
532
+ *
533
+ * Persona keeps the centered transcript column, the alert role, and the
534
+ * composer gate around the returned element. `retry`/`back` are the only
535
+ * exits: both route through the token-guarded open flow, so do not fetch
536
+ * directly. `back` is absent when there is no Messages surface to return
537
+ * to; do not render a back affordance without it.
538
+ */
539
+ renderHistoryOpenError?: (
540
+ context: AgentWidgetRenderHistoryOpenErrorContext
541
+ ) => HTMLElement | null;
542
+
543
+ /**
544
+ * Contribute navigation sections to the history rail, stacked around the
545
+ * conversation list in `placement` order. Rail only: the panel presentation
546
+ * renders none of them, and `features.history.rail.sections` (config) comes
547
+ * first inside each bucket, then plugins in plugin order.
548
+ *
549
+ * `render` is re-invoked with the new `collapsed` value whenever the rail
550
+ * collapses or expands, and its return replaces the previous content;
551
+ * returning null renders nothing for that state. A throwing `render` warns
552
+ * once and drops that section only, leaving the rest of the rail alone. An id
553
+ * a config section already owns is dropped with a warning.
554
+ *
555
+ * Keep heavy UI in the host bundle: `render` may return a skeleton and
556
+ * hydrate it after the host's own dynamic import resolves.
557
+ *
558
+ * @example
559
+ * ```typescript
560
+ * railSections: [
561
+ * {
562
+ * id: "pinned",
563
+ * title: "Pinned",
564
+ * render: ({ collapsed, actions }) => {
565
+ * if (collapsed) return null;
566
+ * const list = document.createElement("div");
567
+ * const row = document.createElement("button");
568
+ * row.textContent = "Weekly report";
569
+ * row.addEventListener("click", () => void actions.openConversation("c1"));
570
+ * list.appendChild(row);
571
+ * return list;
572
+ * }
573
+ * }
574
+ * ]
575
+ * ```
576
+ */
577
+ railSections?: AgentWidgetRailSection[];
578
+
579
+ /**
580
+ * Keyboard shortcuts registered for the lifetime of each widget instance the
581
+ * plugin is active in, and unregistered on its teardown. `combo` is written
582
+ * as `"mod+b"` / `"mod+shift+k"`, where "mod" is Command on Apple platforms
583
+ * and Control elsewhere. `scope` defaults to `"widget"` (only while focus is
584
+ * inside the mount); `when` returning false skips the keypress. A combo
585
+ * another binding already owns warns and is ignored, first registration wins.
586
+ *
587
+ * @example
588
+ * ```typescript
589
+ * shortcuts: [
590
+ * { id: "open-inbox", combo: "mod+shift+i", run: () => openInbox() }
591
+ * ]
592
+ * ```
593
+ */
594
+ shortcuts?: AgentWidgetPluginShortcut[];
595
+
420
596
  /**
421
597
  * Called when plugin is registered
422
598
  */