@runtypelabs/persona 4.8.0 → 4.10.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 (130) hide show
  1. package/README.md +14 -1
  2. package/dist/animations/glyph-cycle.d.cts +1 -1
  3. package/dist/animations/glyph-cycle.d.ts +1 -1
  4. package/dist/animations/{types-BsZtXPKK.d.cts → types-4ROVJ1gA.d.cts} +42 -0
  5. package/dist/animations/{types-BsZtXPKK.d.ts → types-4ROVJ1gA.d.ts} +42 -0
  6. package/dist/animations/wipe.d.cts +1 -1
  7. package/dist/animations/wipe.d.ts +1 -1
  8. package/dist/chunk-IO5VVUKP.js +3 -0
  9. package/dist/chunk-IPVK3KOM.js +1 -0
  10. package/dist/chunk-UPO4GUFC.js +1 -0
  11. package/dist/codegen.cjs +6 -6
  12. package/dist/codegen.js +8 -8
  13. package/dist/context-mentions-7S5KVUTG.js +169 -0
  14. package/dist/context-mentions-inline-TTCN7ZM2.js +4 -0
  15. package/dist/context-mentions-inline.cjs +4 -0
  16. package/dist/context-mentions-inline.d.cts +203 -0
  17. package/dist/context-mentions-inline.d.ts +203 -0
  18. package/dist/context-mentions-inline.js +4 -0
  19. package/dist/context-mentions.cjs +295 -0
  20. package/dist/context-mentions.d.cts +7025 -0
  21. package/dist/context-mentions.d.ts +7025 -0
  22. package/dist/context-mentions.js +295 -0
  23. package/dist/index.cjs +72 -64
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +634 -3
  26. package/dist/index.d.ts +634 -3
  27. package/dist/index.global.js +59 -51
  28. package/dist/index.global.js.map +1 -1
  29. package/dist/index.js +64 -56
  30. package/dist/index.js.map +1 -1
  31. package/dist/launcher.global.js +2 -2
  32. package/dist/launcher.global.js.map +1 -1
  33. package/dist/plugin-kit.cjs +1 -1
  34. package/dist/plugin-kit.d.cts +17 -0
  35. package/dist/plugin-kit.d.ts +17 -0
  36. package/dist/plugin-kit.js +1 -1
  37. package/dist/smart-dom-reader.cjs +17 -16
  38. package/dist/smart-dom-reader.d.cts +507 -1
  39. package/dist/smart-dom-reader.d.ts +507 -1
  40. package/dist/smart-dom-reader.js +17 -16
  41. package/dist/theme-editor-preview.cjs +236 -57
  42. package/dist/theme-editor-preview.d.cts +485 -1
  43. package/dist/theme-editor-preview.d.ts +485 -1
  44. package/dist/theme-editor-preview.js +53 -47
  45. package/dist/theme-editor.cjs +7 -7
  46. package/dist/theme-editor.d.cts +473 -0
  47. package/dist/theme-editor.d.ts +473 -0
  48. package/dist/theme-editor.js +5 -5
  49. package/dist/theme-reference.cjs +1 -1
  50. package/dist/theme-reference.d.cts +2 -0
  51. package/dist/theme-reference.d.ts +2 -0
  52. package/dist/widget.css +1 -1
  53. package/package.json +15 -3
  54. package/src/client.test.ts +69 -0
  55. package/src/client.ts +65 -51
  56. package/src/components/artifact-pane.test.ts +47 -0
  57. package/src/components/artifact-pane.ts +25 -2
  58. package/src/components/composer-parts.ts +3 -12
  59. package/src/components/context-mention-button.test.ts +70 -0
  60. package/src/components/context-mention-button.ts +82 -0
  61. package/src/components/context-mention-chip.ts +134 -0
  62. package/src/components/context-mention-menu.test.ts +508 -0
  63. package/src/components/context-mention-menu.ts +0 -0
  64. package/src/components/message-bubble.test.ts +175 -0
  65. package/src/components/message-bubble.ts +177 -19
  66. package/src/components/panel.ts +7 -10
  67. package/src/context-mentions-bundle.test.ts +163 -0
  68. package/src/context-mentions-entry.ts +185 -0
  69. package/src/context-mentions-inline-entry.test.ts +136 -0
  70. package/src/context-mentions-inline-entry.ts +226 -0
  71. package/src/context-mentions-inline-loader.test.ts +30 -0
  72. package/src/context-mentions-inline-loader.ts +36 -0
  73. package/src/context-mentions-inline.ts +15 -0
  74. package/src/context-mentions-loader.ts +32 -0
  75. package/src/context-mentions.ts +16 -0
  76. package/src/index-core.ts +27 -0
  77. package/src/index-global.ts +51 -0
  78. package/src/markdown-parsers-loader.ts +35 -44
  79. package/src/plugin-kit.test.ts +40 -0
  80. package/src/plugin-kit.ts +39 -5
  81. package/src/runtime/init-update-reset.test.ts +81 -0
  82. package/src/runtime/init.test.ts +62 -0
  83. package/src/runtime/init.ts +7 -14
  84. package/src/session.mentions.test.ts +175 -0
  85. package/src/session.test.ts +52 -4
  86. package/src/session.ts +121 -5
  87. package/src/smart-dom-reader.test.ts +129 -2
  88. package/src/smart-dom-reader.ts +127 -1
  89. package/src/styles/context-mention-menu-css.ts +176 -0
  90. package/src/styles/widget.css +243 -126
  91. package/src/theme-editor/sections.ts +3 -3
  92. package/src/types/theme.ts +2 -0
  93. package/src/types.ts +542 -0
  94. package/src/ui.artifact-pane-gating.test.ts +11 -1
  95. package/src/ui.attachments-drop.test.ts +90 -0
  96. package/src/ui.header-update-stability.test.ts +149 -0
  97. package/src/ui.launcher-update-merge.test.ts +83 -0
  98. package/src/ui.mention-submit.test.ts +235 -0
  99. package/src/ui.send-button-stream-update.test.ts +69 -0
  100. package/src/ui.ts +379 -84
  101. package/src/utils/chunk-loader.test.ts +97 -0
  102. package/src/utils/chunk-loader.ts +88 -0
  103. package/src/utils/composer-contenteditable.test.ts +507 -0
  104. package/src/utils/composer-contenteditable.ts +626 -0
  105. package/src/utils/composer-document.test.ts +280 -0
  106. package/src/utils/composer-document.ts +293 -0
  107. package/src/utils/composer-history.test.ts +35 -7
  108. package/src/utils/composer-history.ts +30 -20
  109. package/src/utils/composer-input.ts +159 -0
  110. package/src/utils/config-merge.test.ts +131 -0
  111. package/src/utils/config-merge.ts +61 -0
  112. package/src/utils/context-mention-controller.test.ts +1215 -0
  113. package/src/utils/context-mention-controller.ts +1186 -0
  114. package/src/utils/context-mention-manager.test.ts +422 -0
  115. package/src/utils/context-mention-manager.ts +410 -0
  116. package/src/utils/context-mention-orchestrator.test.ts +538 -0
  117. package/src/utils/context-mention-orchestrator.ts +348 -0
  118. package/src/utils/live-region.test.ts +108 -0
  119. package/src/utils/live-region.ts +94 -0
  120. package/src/utils/mention-channels.ts +63 -0
  121. package/src/utils/mention-llm-format.test.ts +91 -0
  122. package/src/utils/mention-llm-format.ts +79 -0
  123. package/src/utils/mention-matcher.test.ts +86 -0
  124. package/src/utils/mention-matcher.ts +221 -0
  125. package/src/utils/mention-token.ts +72 -0
  126. package/src/utils/mention-trigger.test.ts +155 -0
  127. package/src/utils/mention-trigger.ts +156 -0
  128. package/src/utils/theme.test.ts +54 -4
  129. package/src/utils/theme.ts +6 -3
  130. package/src/utils/tokens.ts +27 -11
@@ -40,10 +40,16 @@ import {
40
40
  type SmartDomAdapterOptions
41
41
  } from "./utils/smart-dom-adapter";
42
42
  import { formatEnrichedContext, type EnrichedPageElement } from "./utils/dom-context";
43
- import type { AgentWidgetContextProvider } from "./types";
43
+ import { defaultMentionFilter } from "./utils/mention-matcher";
44
+ import type {
45
+ AgentWidgetContextProvider,
46
+ AgentWidgetContextMentionItem,
47
+ AgentWidgetContextMentionSource,
48
+ } from "./types";
44
49
 
45
50
  export { smartDomResultToEnriched };
46
51
  export type { SmartDomAdapterOptions } from "./utils/smart-dom-adapter";
52
+ export type { EnrichedPageElement } from "./utils/dom-context";
47
53
 
48
54
  /** Options for {@link collectSmartDomContext} and {@link createSmartDomReaderContextProvider}. */
49
55
  export interface SmartDomContextOptions extends SmartDomAdapterOptions {
@@ -132,3 +138,123 @@ export function createSmartDomReaderContextProvider(
132
138
  return { [contextKey]: formatEnrichedContext(elements) };
133
139
  };
134
140
  }
141
+
142
+ /** Options for {@link createSmartDomMentionSource}. */
143
+ export interface SmartDomMentionSourceOptions extends SmartDomContextOptions {
144
+ /** Source id. Default: "page". */
145
+ id?: string;
146
+ /** Group header shown in the menu. Default: "Page". */
147
+ label?: string;
148
+ /**
149
+ * Reshape each surfaced element into a mention item. Receives the raw
150
+ * {@link EnrichedPageElement} and the default-mapped item, so you can tweak
151
+ * one field (`{ ...defaultItem, iconName: "star" }`) or build from scratch.
152
+ * The returned item's `id` MUST stay a selector `resolve()` can read at submit
153
+ * (default: `el.selector`) unless you also override resolution. @default built-in mapping
154
+ */
155
+ mapItem?: (
156
+ el: EnrichedPageElement,
157
+ defaultItem: AgentWidgetContextMentionItem
158
+ ) => AgentWidgetContextMentionItem;
159
+ }
160
+
161
+ // Empty-query snapshot reuse window: the full Shadow-DOM-piercing scan is
162
+ // synchronous and runs inside the keystroke handler, so rescanning on every `@`
163
+ // / backspace-to-empty stalls the input. Reuse a snapshot this fresh; rescan
164
+ // past it so a menu opened after page changes still sees current content.
165
+ const SNAPSHOT_TTL_MS = 2000;
166
+
167
+ const iconForInteractivity = (kind: EnrichedPageElement["interactivity"]): string => {
168
+ switch (kind) {
169
+ case "clickable":
170
+ return "mouse-pointer-click";
171
+ case "input":
172
+ return "text-cursor-input";
173
+ case "navigable":
174
+ return "link";
175
+ default:
176
+ return "text";
177
+ }
178
+ };
179
+
180
+ const elementToMentionItem = (
181
+ el: EnrichedPageElement
182
+ ): AgentWidgetContextMentionItem => {
183
+ const ariaLabel = el.attributes["aria-label"];
184
+ const raw = (ariaLabel || el.text || el.tagName).trim();
185
+ const label = raw.length > 48 ? `${raw.slice(0, 47)}…` : raw || el.tagName;
186
+ const descParts = [el.role ?? el.tagName, el.interactivity].filter(Boolean);
187
+ return {
188
+ id: el.selector, // stable key + the selector resolve() reads at submit
189
+ label,
190
+ description: descParts.join(" · "),
191
+ iconName: iconForInteractivity(el.interactivity),
192
+ group: undefined,
193
+ };
194
+ };
195
+
196
+ /**
197
+ * First-class **supported** mention source backed by smart-dom-reader: surfaces
198
+ * visible page elements (Shadow-DOM-piercing) as mentionable items, resolving a
199
+ * fresh snapshot of the chosen element's text at SUBMIT (`resolveOn: "submit"`),
200
+ * since the page is time-sensitive. The element list is snapshotted when the
201
+ * menu opens (empty query) and filtered client-side with `defaultMentionFilter`
202
+ * as the user types.
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * import { createSmartDomMentionSource } from "@runtypelabs/persona/smart-dom-reader";
207
+ *
208
+ * initAgentWidget({
209
+ * contextMentions: { enabled: true, sources: [createSmartDomMentionSource()] },
210
+ * });
211
+ * ```
212
+ */
213
+ export function createSmartDomMentionSource(
214
+ opts: SmartDomMentionSourceOptions = {}
215
+ ): AgentWidgetContextMentionSource {
216
+ const id = opts.id ?? "page";
217
+ const label = opts.label ?? "Page";
218
+ let snapshot: AgentWidgetContextMentionItem[] | null = null;
219
+ let capturedAt = 0;
220
+
221
+ const rescan = (): AgentWidgetContextMentionItem[] => {
222
+ const items = collectSmartDomContext(opts).map((el) => {
223
+ const defaultItem = elementToMentionItem(el);
224
+ return opts.mapItem ? opts.mapItem(el, defaultItem) : defaultItem;
225
+ });
226
+ capturedAt = Date.now();
227
+ return items;
228
+ };
229
+
230
+ return {
231
+ id,
232
+ label,
233
+ resolveOn: "submit",
234
+ search: (query) => {
235
+ // Rescan on empty query only when there's no snapshot or it's staler than
236
+ // the TTL; reopening the menu quickly reuses the snapshot, keystrokes always
237
+ // filter the existing one, so we never re-scan mid-typing.
238
+ const stale = !snapshot || Date.now() - capturedAt > SNAPSHOT_TTL_MS;
239
+ if (!snapshot || (query === "" && stale)) {
240
+ snapshot = rescan();
241
+ }
242
+ return defaultMentionFilter(snapshot, query);
243
+ },
244
+ resolve: (item) => {
245
+ const doc =
246
+ opts.document ?? (typeof document !== "undefined" ? document : undefined);
247
+ let text = "";
248
+ try {
249
+ const el = doc?.querySelector(item.id);
250
+ text = el?.textContent?.trim() ?? "";
251
+ } catch {
252
+ /* invalid selector at resolve time — fall through to label only */
253
+ }
254
+ return {
255
+ llmAppend: `Page element "${item.label}" (${item.id}):\n${text || "(no text)"}`,
256
+ context: { selector: item.id },
257
+ };
258
+ },
259
+ };
260
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * CSS for the context-mention MENU (floating popover): search field, grouped
3
+ * result list, options, status/empty/error rows, retry button, and the
4
+ * coarse-pointer touch sizing for its rows.
5
+ *
6
+ * This lives in the lazy `context-mentions` chunk (not `widget.css`) and is
7
+ * injected on first menu open via `injectStyles`. Two reasons:
8
+ * 1. it keeps ~1 kB of menu-only CSS out of the eager core stylesheet;
9
+ * 2. the menu mounts into the popover container (document.body by default),
10
+ * which is OUTSIDE a `useShadowDom` widget's shadow root — where the
11
+ * shadow-scoped `widget.css` never reached it. Injecting at mount time puts
12
+ * the rules in whichever root the menu actually lives in.
13
+ *
14
+ * The chip / context-row / affordance-button rules stay in `widget.css`: they
15
+ * render eagerly (restored-message chips, the always-visible add-context button)
16
+ * before this chunk loads.
17
+ */
18
+ export const MENTION_MENU_CSS = `
19
+ .persona-mention-menu {
20
+ box-sizing: border-box;
21
+ display: flex;
22
+ flex-direction: column;
23
+ max-height: var(--persona-mention-menu-max-height, 280px);
24
+ overflow: hidden;
25
+ background: var(--persona-mention-menu-bg, var(--persona-surface, #ffffff));
26
+ border: 1px solid var(--persona-mention-menu-border, var(--persona-border, #e5e7eb));
27
+ border-radius: var(--persona-mention-menu-radius, 10px);
28
+ box-shadow: var(--persona-mention-menu-shadow, 0 8px 28px rgba(0, 0, 0, 0.12));
29
+ font-family: var(--persona-font-family, inherit);
30
+ }
31
+ .persona-mention-list {
32
+ min-height: 0;
33
+ overflow-y: auto;
34
+ padding: 4px;
35
+ }
36
+ .persona-mention-search {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 6px;
40
+ padding: 8px 10px;
41
+ border-bottom: 1px solid var(--persona-mention-menu-border, var(--persona-border, #e5e7eb));
42
+ }
43
+ .persona-mention-search-icon {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ flex: 0 0 auto;
47
+ color: var(--persona-mention-group-fg, var(--persona-muted, #6b7280));
48
+ }
49
+ .persona-mention-search-input {
50
+ flex: 1 1 auto;
51
+ min-width: 0;
52
+ padding: 0;
53
+ border: none;
54
+ outline: none;
55
+ background: transparent;
56
+ font-family: inherit;
57
+ /* 16px, not 14px: iOS Safari zooms the page when focusing an input under 16px. */
58
+ font-size: 16px;
59
+ line-height: 1.4;
60
+ color: var(--persona-text, #111827);
61
+ }
62
+ .persona-mention-search-input::placeholder {
63
+ color: var(--persona-mention-group-fg, var(--persona-muted, #6b7280));
64
+ }
65
+ .persona-mention-group + .persona-mention-group {
66
+ margin-top: 2px;
67
+ border-top: 1px solid var(--persona-mention-menu-border, var(--persona-border, #f1f1f1));
68
+ padding-top: 2px;
69
+ }
70
+ .persona-mention-group-header {
71
+ padding: 6px 8px 4px;
72
+ font-size: 11px;
73
+ font-weight: 600;
74
+ text-transform: uppercase;
75
+ letter-spacing: 0.04em;
76
+ color: var(--persona-mention-group-fg, var(--persona-muted, #6b7280));
77
+ }
78
+ .persona-mention-option {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 8px;
82
+ padding: 7px 8px;
83
+ border-radius: 6px;
84
+ cursor: pointer;
85
+ color: var(--persona-text, #111827);
86
+ }
87
+ .persona-mention-option[data-active="true"] {
88
+ background: var(--persona-mention-option-active-bg, var(--persona-container, #f1f5f9));
89
+ }
90
+ .persona-mention-option-icon {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ flex: 0 0 auto;
94
+ opacity: 0.7;
95
+ }
96
+ .persona-mention-option-text {
97
+ display: flex;
98
+ flex-direction: column;
99
+ min-width: 0;
100
+ }
101
+ .persona-mention-option-labelline {
102
+ display: flex;
103
+ align-items: baseline;
104
+ gap: 6px;
105
+ min-width: 0;
106
+ }
107
+ .persona-mention-option-label {
108
+ font-size: 13px;
109
+ line-height: 1.3;
110
+ overflow: hidden;
111
+ text-overflow: ellipsis;
112
+ white-space: nowrap;
113
+ }
114
+ .persona-mention-option-arghint {
115
+ flex: 0 0 auto;
116
+ font-size: 12px;
117
+ line-height: 1.3;
118
+ color: var(--persona-muted, #6b7280);
119
+ opacity: 0.85;
120
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
121
+ }
122
+ .persona-mention-option-desc {
123
+ font-size: 11px;
124
+ line-height: 1.3;
125
+ color: var(--persona-muted, #6b7280);
126
+ overflow: hidden;
127
+ text-overflow: ellipsis;
128
+ white-space: nowrap;
129
+ }
130
+ .persona-mention-status,
131
+ .persona-mention-empty,
132
+ .persona-mention-hint {
133
+ padding: 7px 8px;
134
+ font-size: 12px;
135
+ color: var(--persona-muted, #6b7280);
136
+ }
137
+ .persona-mention-error {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ gap: 8px;
142
+ color: var(--persona-mention-error, #dc2626);
143
+ }
144
+ .persona-mention-error-text {
145
+ min-width: 0;
146
+ overflow: hidden;
147
+ text-overflow: ellipsis;
148
+ white-space: nowrap;
149
+ }
150
+ .persona-mention-retry {
151
+ flex: 0 0 auto;
152
+ padding: 4px 8px;
153
+ border: 1px solid var(--persona-mention-error, #dc2626);
154
+ border-radius: 6px;
155
+ background: transparent;
156
+ color: var(--persona-mention-error, #dc2626);
157
+ font: inherit;
158
+ font-size: 12px;
159
+ cursor: pointer;
160
+ }
161
+ .persona-mention-retry:hover {
162
+ background: var(--persona-palette-colors-black-alpha-50, rgba(0, 0, 0, 0.06));
163
+ }
164
+ .persona-mention-hint {
165
+ font-style: italic;
166
+ opacity: 0.8;
167
+ }
168
+ @media (pointer: coarse) {
169
+ .persona-mention-option {
170
+ min-height: 44px;
171
+ }
172
+ .persona-mention-retry {
173
+ min-height: 36px;
174
+ }
175
+ }
176
+ `;