@runtypelabs/persona 4.10.0 → 4.11.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-4ROVJ1gA.d.cts → types-DveIaNx6.d.cts} +11 -316
- package/dist/animations/{types-4ROVJ1gA.d.ts → types-DveIaNx6.d.ts} +11 -316
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/chunk-MMUPR2JW.js +1 -0
- package/dist/codegen.cjs +1 -1
- package/dist/codegen.js +1 -1
- package/dist/{context-mentions-7S5KVUTG.js → context-mentions-ONG7A7M6.js} +1 -1
- package/dist/context-mentions.d.cts +35 -316
- package/dist/context-mentions.d.ts +35 -316
- package/dist/index.cjs +67 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -317
- package/dist/index.d.ts +40 -317
- package/dist/index.global.js +55 -55
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +78 -78
- package/dist/index.js.map +1 -1
- package/dist/smart-dom-reader.d.cts +35 -316
- package/dist/smart-dom-reader.d.ts +35 -316
- package/dist/theme-editor-preview.cjs +66 -66
- package/dist/theme-editor-preview.d.cts +35 -316
- package/dist/theme-editor-preview.d.ts +35 -316
- package/dist/theme-editor-preview.js +55 -55
- package/dist/theme-editor.cjs +5 -5
- package/dist/theme-editor.d.cts +35 -316
- package/dist/theme-editor.d.ts +35 -316
- package/dist/theme-editor.js +9 -9
- package/dist/widget.css +1 -1
- package/package.json +1 -1
- package/src/components/approval-bubble.ts +0 -1
- package/src/components/composer-parts.ts +19 -19
- package/src/components/context-mention-button.test.ts +5 -1
- package/src/components/context-mention-button.ts +6 -4
- package/src/components/header-builder.test.ts +3 -3
- package/src/components/header-parts.ts +14 -108
- package/src/components/message-bubble.test.ts +49 -0
- package/src/components/message-bubble.ts +8 -7
- package/src/components/reasoning-bubble.ts +0 -2
- package/src/components/tool-bubble.ts +0 -2
- package/src/generated/runtype-openapi-contract.ts +11 -317
- package/src/index-core.ts +7 -1
- package/src/runtime/host-layout.test.ts +94 -0
- package/src/runtime/host-layout.ts +9 -1
- package/src/runtime/init.test.ts +33 -0
- package/src/runtime/init.ts +4 -0
- package/src/styles/widget.css +118 -58
- package/src/theme-editor/sections.test.ts +33 -0
- package/src/theme-editor/sections.ts +10 -1
- package/src/types.ts +25 -0
- package/src/ui.attachments-drop.test.ts +2 -1
- package/src/ui.message-width.test.ts +407 -0
- package/src/ui.mount-fill-min-width.test.ts +81 -0
- package/src/ui.ts +158 -238
- package/src/utils/table-scroll-fade.test.ts +123 -0
- package/src/utils/table-scroll-fade.ts +75 -0
- package/src/utils/tooltip.test.ts +226 -0
- package/src/utils/tooltip.ts +245 -0
- package/dist/chunk-IPVK3KOM.js +0 -1
package/src/ui.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { escapeHtml, createMarkdownProcessorFromConfig } from "./postprocessors";
|
|
2
2
|
import { resolveSanitizer } from "./utils/sanitize";
|
|
3
3
|
import { stabilizeStreamingTables } from "./utils/streaming-table";
|
|
4
|
+
import { wrapScrollableTables, refreshTableScrollFades } from "./utils/table-scroll-fade";
|
|
4
5
|
import { onMarkdownParsersReady, getMarkdownParsersSync } from "./markdown-parsers-loader";
|
|
5
6
|
import { AgentWidgetSession, AgentWidgetSessionStatus } from "./session";
|
|
6
7
|
import {
|
|
@@ -45,7 +46,8 @@ import { createTextPart, ALL_SUPPORTED_MIME_TYPES } from "./utils/content";
|
|
|
45
46
|
import { applyThemeVariables, createThemeObserver, getActiveTheme } from "./utils/theme";
|
|
46
47
|
import { resolveTokenValue } from "./utils/tokens";
|
|
47
48
|
import { renderLucideIcon } from "./utils/icons";
|
|
48
|
-
import { createElement
|
|
49
|
+
import { createElement } from "./utils/dom";
|
|
50
|
+
import { attachTooltip } from "./utils/tooltip";
|
|
49
51
|
import { downloadInfoFor } from "./utils/artifact-file";
|
|
50
52
|
import { artifactCopyText } from "./components/artifact-preview";
|
|
51
53
|
import { morphMessages } from "./utils/morph";
|
|
@@ -66,7 +68,7 @@ import {
|
|
|
66
68
|
resolveFollowStateFromScroll,
|
|
67
69
|
resolveFollowStateFromWheel
|
|
68
70
|
} from "./utils/auto-follow";
|
|
69
|
-
import { statusCopy, DEFAULT_OVERLAY_Z_INDEX
|
|
71
|
+
import { statusCopy, DEFAULT_OVERLAY_Z_INDEX } from "./utils/constants";
|
|
70
72
|
import {
|
|
71
73
|
applyStreamBuffer,
|
|
72
74
|
createSkeletonPlaceholder,
|
|
@@ -2965,6 +2967,17 @@ export const createAgentExperience = (
|
|
|
2965
2967
|
body.style.display = "none";
|
|
2966
2968
|
}
|
|
2967
2969
|
|
|
2970
|
+
// Shrinkable baseline for every fill mount (launcher-off embed OR fullHeight/
|
|
2971
|
+
// docked/sidebar), applied outside the fullHeight branch so the launcher-off,
|
|
2972
|
+
// no-fullHeight embed keeps it too: the cssText reset above wipes the min-width
|
|
2973
|
+
// the host layer set, and without it a wide artifact split isn't forced to
|
|
2974
|
+
// shrink within the mount. In practice the column host's own min-width:0 is
|
|
2975
|
+
// what caps container overflow (the mount is a cross-axis child), but the
|
|
2976
|
+
// baseline is kept here so intent is consistent across every fill mode.
|
|
2977
|
+
if (fullHeight || isInlineEmbed) {
|
|
2978
|
+
mount.style.minWidth = "0";
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2968
2981
|
const restoreBodyScrollTop = (): void => {
|
|
2969
2982
|
if (prevBodyScrollTop <= 0) return;
|
|
2970
2983
|
const ownerWindow = body.ownerDocument.defaultView ?? window;
|
|
@@ -3131,6 +3144,8 @@ export const createAgentExperience = (
|
|
|
3131
3144
|
mount.style.flexDirection = 'column';
|
|
3132
3145
|
mount.style.height = '100%';
|
|
3133
3146
|
mount.style.minHeight = '0';
|
|
3147
|
+
// min-width:0 baseline is applied above (outside this branch) for every
|
|
3148
|
+
// fill mount, including the launcher-off, no-fullHeight embed.
|
|
3134
3149
|
if (isInlineEmbed) {
|
|
3135
3150
|
mount.style.width = '100%';
|
|
3136
3151
|
}
|
|
@@ -4158,6 +4173,46 @@ export const createAgentExperience = (
|
|
|
4158
4173
|
|
|
4159
4174
|
|
|
4160
4175
|
// Message rendering with plugin support (implementation)
|
|
4176
|
+
const applyMessageRowLayout = (
|
|
4177
|
+
wrapper: HTMLElement,
|
|
4178
|
+
role: AgentWidgetMessage["role"]
|
|
4179
|
+
): void => {
|
|
4180
|
+
const sizingRole = role === "user" ? "user" : "assistant";
|
|
4181
|
+
const roleLayout = config.layout?.messages?.[sizingRole];
|
|
4182
|
+
const width = roleLayout?.width ?? "content";
|
|
4183
|
+
const maxWidth =
|
|
4184
|
+
roleLayout?.maxWidth ?? (width === "full" ? "100%" : "85%");
|
|
4185
|
+
|
|
4186
|
+
wrapper.classList.add("persona-message-row");
|
|
4187
|
+
wrapper.classList.remove(
|
|
4188
|
+
"persona-message-row-user",
|
|
4189
|
+
"persona-message-row-assistant",
|
|
4190
|
+
"persona-message-row-system",
|
|
4191
|
+
"persona-message-width-content",
|
|
4192
|
+
"persona-message-width-full"
|
|
4193
|
+
);
|
|
4194
|
+
wrapper.classList.add(
|
|
4195
|
+
`persona-message-row-${role}`,
|
|
4196
|
+
`persona-message-width-${width}`
|
|
4197
|
+
);
|
|
4198
|
+
wrapper.classList.toggle("persona-justify-end", role === "user");
|
|
4199
|
+
wrapper.setAttribute("data-message-role", role);
|
|
4200
|
+
wrapper.setAttribute("data-message-width", width);
|
|
4201
|
+
wrapper.style.setProperty("--persona-message-row-max-width", maxWidth);
|
|
4202
|
+
};
|
|
4203
|
+
|
|
4204
|
+
const createMessageRow = (
|
|
4205
|
+
id: string,
|
|
4206
|
+
role: AgentWidgetMessage["role"]
|
|
4207
|
+
): HTMLElement => {
|
|
4208
|
+
const wrapper = document.createElement("div");
|
|
4209
|
+
wrapper.className = "persona-flex";
|
|
4210
|
+
wrapper.id = `wrapper-${id}`;
|
|
4211
|
+
wrapper.setAttribute("data-wrapper-id", id);
|
|
4212
|
+
applyMessageRowLayout(wrapper, role);
|
|
4213
|
+
return wrapper;
|
|
4214
|
+
};
|
|
4215
|
+
|
|
4161
4216
|
const renderMessagesWithPluginsImpl = (
|
|
4162
4217
|
container: HTMLElement,
|
|
4163
4218
|
messages: AgentWidgetMessage[],
|
|
@@ -4199,6 +4254,7 @@ export const createAgentExperience = (
|
|
|
4199
4254
|
|
|
4200
4255
|
// Track active message IDs for cache pruning
|
|
4201
4256
|
const activeMessageIds = new Set<string>();
|
|
4257
|
+
const messageRolesById = new Map<string, AgentWidgetMessage["role"]>();
|
|
4202
4258
|
// Track ask_user_question tool-call ids whose bubbles were rendered this
|
|
4203
4259
|
// pass: used to prune stale sheets from the composer overlay afterward.
|
|
4204
4260
|
const liveAskToolIds = new Set<string>();
|
|
@@ -4252,6 +4308,7 @@ export const createAgentExperience = (
|
|
|
4252
4308
|
|
|
4253
4309
|
messages.forEach((message) => {
|
|
4254
4310
|
activeMessageIds.add(message.id);
|
|
4311
|
+
messageRolesById.set(message.id, message.role);
|
|
4255
4312
|
|
|
4256
4313
|
const askWithPlugin = hasAskPlugin && isAskUserQuestionMessage(message);
|
|
4257
4314
|
const approvalWithPlugin =
|
|
@@ -4431,10 +4488,7 @@ export const createAgentExperience = (
|
|
|
4431
4488
|
|
|
4432
4489
|
// Append a stub wrapper for the morph pass; hydrate the real bubble
|
|
4433
4490
|
// into it post-morph so its event listeners survive.
|
|
4434
|
-
const stub =
|
|
4435
|
-
stub.className = "persona-flex";
|
|
4436
|
-
stub.id = `wrapper-${message.id}`;
|
|
4437
|
-
stub.setAttribute("data-wrapper-id", message.id);
|
|
4491
|
+
const stub = createMessageRow(message.id, message.role);
|
|
4438
4492
|
stub.setAttribute("data-ask-plugin-stub", "true");
|
|
4439
4493
|
stub.setAttribute("data-preserve-runtime", "true");
|
|
4440
4494
|
tempContainer.appendChild(stub);
|
|
@@ -4509,10 +4563,7 @@ export const createAgentExperience = (
|
|
|
4509
4563
|
} else {
|
|
4510
4564
|
// A fresh live bubble to hydrate (needsRebuild), or fingerprint
|
|
4511
4565
|
// unchanged so we reuse the preserved live wrapper (`bubble: null`).
|
|
4512
|
-
const stub =
|
|
4513
|
-
stub.className = "persona-flex";
|
|
4514
|
-
stub.id = `wrapper-${message.id}`;
|
|
4515
|
-
stub.setAttribute("data-wrapper-id", message.id);
|
|
4566
|
+
const stub = createMessageRow(message.id, message.role);
|
|
4516
4567
|
stub.setAttribute("data-approval-plugin-stub", "true");
|
|
4517
4568
|
stub.setAttribute("data-preserve-runtime", "true");
|
|
4518
4569
|
tempContainer.appendChild(stub);
|
|
@@ -4629,7 +4680,6 @@ export const createAgentExperience = (
|
|
|
4629
4680
|
const componentWrapper = document.createElement("div");
|
|
4630
4681
|
componentWrapper.className = [
|
|
4631
4682
|
"persona-message-bubble",
|
|
4632
|
-
"persona-max-w-[85%]",
|
|
4633
4683
|
"persona-rounded-2xl",
|
|
4634
4684
|
"persona-bg-persona-surface",
|
|
4635
4685
|
"persona-border",
|
|
@@ -4685,15 +4735,9 @@ export const createAgentExperience = (
|
|
|
4685
4735
|
// Otherwise fall through to the standard render path so the message
|
|
4686
4736
|
// text is at least visible.
|
|
4687
4737
|
if (liveBubble || lastFp != null) {
|
|
4688
|
-
const stub =
|
|
4689
|
-
stub.className = "persona-flex";
|
|
4690
|
-
stub.id = `wrapper-${message.id}`;
|
|
4691
|
-
stub.setAttribute("data-wrapper-id", message.id);
|
|
4738
|
+
const stub = createMessageRow(message.id, message.role);
|
|
4692
4739
|
stub.setAttribute("data-component-directive-stub", "true");
|
|
4693
4740
|
stub.setAttribute("data-preserve-runtime", "true");
|
|
4694
|
-
if (!wrapChrome) {
|
|
4695
|
-
stub.classList.add("persona-w-full");
|
|
4696
|
-
}
|
|
4697
4741
|
tempContainer.appendChild(stub);
|
|
4698
4742
|
componentDirectiveHydrate.push({
|
|
4699
4743
|
messageId: message.id,
|
|
@@ -4750,17 +4794,7 @@ export const createAgentExperience = (
|
|
|
4750
4794
|
}
|
|
4751
4795
|
}
|
|
4752
4796
|
|
|
4753
|
-
const wrapper =
|
|
4754
|
-
wrapper.className = "persona-flex";
|
|
4755
|
-
// Set id for idiomorph matching
|
|
4756
|
-
wrapper.id = `wrapper-${message.id}`;
|
|
4757
|
-
wrapper.setAttribute("data-wrapper-id", message.id);
|
|
4758
|
-
if (message.role === "user") {
|
|
4759
|
-
wrapper.classList.add("persona-justify-end");
|
|
4760
|
-
}
|
|
4761
|
-
if (bubble?.getAttribute("data-persona-component-directive") === "true") {
|
|
4762
|
-
wrapper.classList.add("persona-w-full");
|
|
4763
|
-
}
|
|
4797
|
+
const wrapper = createMessageRow(message.id, message.role);
|
|
4764
4798
|
wrapper.appendChild(bubble);
|
|
4765
4799
|
setCachedWrapper(messageCache, message.id, fingerprint, wrapper);
|
|
4766
4800
|
tempContainer.appendChild(wrapper);
|
|
@@ -4819,10 +4853,11 @@ export const createAgentExperience = (
|
|
|
4819
4853
|
return;
|
|
4820
4854
|
}
|
|
4821
4855
|
|
|
4822
|
-
const groupWrapper =
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4856
|
+
const groupWrapper = createMessageRow(
|
|
4857
|
+
`tool-group-${groupIndex}-${group[0].id}`,
|
|
4858
|
+
"assistant"
|
|
4859
|
+
);
|
|
4860
|
+
groupWrapper.setAttribute("data-persona-tool-group-row", "true");
|
|
4826
4861
|
|
|
4827
4862
|
const groupContainer = document.createElement("div");
|
|
4828
4863
|
groupContainer.className =
|
|
@@ -4862,6 +4897,10 @@ export const createAgentExperience = (
|
|
|
4862
4897
|
wrapper.remove();
|
|
4863
4898
|
return;
|
|
4864
4899
|
}
|
|
4900
|
+
// The outer group row owns role sizing. Inner message rows fill that
|
|
4901
|
+
// resolved track instead of applying the role max-width a second
|
|
4902
|
+
// time (which would turn an 85% cap into 72.25%).
|
|
4903
|
+
wrapper.style.setProperty("--persona-message-row-max-width", "100%");
|
|
4865
4904
|
const item = document.createElement("div");
|
|
4866
4905
|
item.className = "persona-tool-group-item persona-relative";
|
|
4867
4906
|
item.setAttribute("data-persona-tool-group-item", "true");
|
|
@@ -4925,7 +4964,6 @@ export const createAgentExperience = (
|
|
|
4925
4964
|
const showBubble = config.loadingIndicator?.showBubble !== false; // default true
|
|
4926
4965
|
typingBubble.className = showBubble
|
|
4927
4966
|
? [
|
|
4928
|
-
"persona-max-w-[85%]",
|
|
4929
4967
|
"persona-rounded-2xl",
|
|
4930
4968
|
"persona-text-sm",
|
|
4931
4969
|
"persona-leading-relaxed",
|
|
@@ -4937,7 +4975,6 @@ export const createAgentExperience = (
|
|
|
4937
4975
|
"persona-py-3"
|
|
4938
4976
|
].join(" ")
|
|
4939
4977
|
: [
|
|
4940
|
-
"persona-max-w-[85%]",
|
|
4941
4978
|
"persona-text-sm",
|
|
4942
4979
|
"persona-leading-relaxed",
|
|
4943
4980
|
"persona-text-persona-primary"
|
|
@@ -4947,11 +4984,10 @@ export const createAgentExperience = (
|
|
|
4947
4984
|
|
|
4948
4985
|
typingBubble.appendChild(typingIndicator);
|
|
4949
4986
|
|
|
4950
|
-
const typingWrapper =
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
typingWrapper.setAttribute("data-wrapper-id", "typing-indicator");
|
|
4987
|
+
const typingWrapper = createMessageRow(
|
|
4988
|
+
"typing-indicator",
|
|
4989
|
+
"assistant"
|
|
4990
|
+
);
|
|
4955
4991
|
typingWrapper.appendChild(typingBubble);
|
|
4956
4992
|
tempContainer.appendChild(typingWrapper);
|
|
4957
4993
|
}
|
|
@@ -4989,7 +5025,6 @@ export const createAgentExperience = (
|
|
|
4989
5025
|
const showBubble = config.loadingIndicator?.showBubble !== false; // default true
|
|
4990
5026
|
idleBubble.className = showBubble
|
|
4991
5027
|
? [
|
|
4992
|
-
"persona-max-w-[85%]",
|
|
4993
5028
|
"persona-rounded-2xl",
|
|
4994
5029
|
"persona-text-sm",
|
|
4995
5030
|
"persona-leading-relaxed",
|
|
@@ -5002,7 +5037,6 @@ export const createAgentExperience = (
|
|
|
5002
5037
|
"persona-py-3"
|
|
5003
5038
|
].join(" ")
|
|
5004
5039
|
: [
|
|
5005
|
-
"persona-max-w-[85%]",
|
|
5006
5040
|
"persona-text-sm",
|
|
5007
5041
|
"persona-leading-relaxed",
|
|
5008
5042
|
"persona-text-persona-primary"
|
|
@@ -5011,27 +5045,35 @@ export const createAgentExperience = (
|
|
|
5011
5045
|
|
|
5012
5046
|
idleBubble.appendChild(idleIndicator);
|
|
5013
5047
|
|
|
5014
|
-
const idleWrapper =
|
|
5015
|
-
idleWrapper.className = "persona-flex";
|
|
5016
|
-
// Set id for idiomorph matching
|
|
5017
|
-
idleWrapper.id = "wrapper-idle-indicator";
|
|
5018
|
-
idleWrapper.setAttribute("data-wrapper-id", "idle-indicator");
|
|
5048
|
+
const idleWrapper = createMessageRow("idle-indicator", "assistant");
|
|
5019
5049
|
idleWrapper.appendChild(idleBubble);
|
|
5020
5050
|
tempContainer.appendChild(idleWrapper);
|
|
5021
5051
|
}
|
|
5022
5052
|
}
|
|
5023
5053
|
|
|
5054
|
+
// Wrap wide tables in a horizontal-scroll container before morphing so the
|
|
5055
|
+
// wrapper exists on both sides of the diff (survives streaming re-renders).
|
|
5056
|
+
wrapScrollableTables(tempContainer);
|
|
5057
|
+
|
|
5024
5058
|
// Use idiomorph to morph the container contents
|
|
5025
5059
|
morphMessages(container, tempContainer);
|
|
5026
5060
|
|
|
5061
|
+
// Set initial edge-fade state on the live table wrappers and attach the
|
|
5062
|
+
// delegated scroll listener (once) that keeps the fades in sync.
|
|
5063
|
+
refreshTableScrollFades(container);
|
|
5064
|
+
|
|
5027
5065
|
// Hydrate plugin-rendered ask-question bubbles into their stub wrappers.
|
|
5028
5066
|
// Idiomorph imports new nodes via `document.importNode`, which strips
|
|
5029
5067
|
// listeners, so we built only an empty stub during morph and now inject
|
|
5030
5068
|
// the real, listener-bearing bubble directly into the live DOM.
|
|
5031
5069
|
if (askPluginHydrate.length > 0) {
|
|
5032
5070
|
for (const { messageId, fingerprint, bubble } of askPluginHydrate) {
|
|
5033
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5071
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5034
5072
|
if (!wrapper) continue;
|
|
5073
|
+
applyMessageRowLayout(
|
|
5074
|
+
wrapper,
|
|
5075
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5076
|
+
);
|
|
5035
5077
|
if (bubble === null) {
|
|
5036
5078
|
// No fresh bubble built this pass: either the plugin opted out
|
|
5037
5079
|
// and a previously-mounted bubble already lives here (preserved by
|
|
@@ -5057,8 +5099,12 @@ export const createAgentExperience = (
|
|
|
5057
5099
|
// the ask-question hydration above.
|
|
5058
5100
|
if (componentDirectiveHydrate.length > 0) {
|
|
5059
5101
|
for (const { messageId, fingerprint, bubble } of componentDirectiveHydrate) {
|
|
5060
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5102
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5061
5103
|
if (!wrapper) continue;
|
|
5104
|
+
applyMessageRowLayout(
|
|
5105
|
+
wrapper,
|
|
5106
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5107
|
+
);
|
|
5062
5108
|
if (bubble === null) {
|
|
5063
5109
|
// Fingerprint matched the previous pass: the live wrapper (kept
|
|
5064
5110
|
// alive by `data-preserve-runtime`) still holds the listener-bearing
|
|
@@ -5081,8 +5127,12 @@ export const createAgentExperience = (
|
|
|
5081
5127
|
// mirroring the ask-question / component-directive hydration above.
|
|
5082
5128
|
if (approvalPluginHydrate.length > 0) {
|
|
5083
5129
|
for (const { messageId, fingerprint, bubble } of approvalPluginHydrate) {
|
|
5084
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5130
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5085
5131
|
if (!wrapper) continue;
|
|
5132
|
+
applyMessageRowLayout(
|
|
5133
|
+
wrapper,
|
|
5134
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5135
|
+
);
|
|
5086
5136
|
if (bubble === null) {
|
|
5087
5137
|
// Fingerprint matched the previous pass (or the plugin opted out
|
|
5088
5138
|
// after a prior render): the live wrapper, kept alive by
|
|
@@ -6776,7 +6826,8 @@ export const createAgentExperience = (
|
|
|
6776
6826
|
) as HTMLButtonElement;
|
|
6777
6827
|
|
|
6778
6828
|
micButton.type = "button";
|
|
6779
|
-
|
|
6829
|
+
const tooltipText = voiceConfig?.tooltipText ?? "Start voice recognition";
|
|
6830
|
+
micButton.setAttribute("aria-label", tooltipText);
|
|
6780
6831
|
|
|
6781
6832
|
const micIconName = voiceConfig?.iconName ?? "mic";
|
|
6782
6833
|
const buttonSize = sendButtonConfig?.size ?? "40px";
|
|
@@ -6837,15 +6888,13 @@ export const createAgentExperience = (
|
|
|
6837
6888
|
}
|
|
6838
6889
|
|
|
6839
6890
|
micButtonWrapper.appendChild(micButton);
|
|
6840
|
-
|
|
6841
|
-
// Add tooltip if enabled
|
|
6842
|
-
const tooltipText = voiceConfig?.tooltipText ?? "Start voice recognition";
|
|
6843
6891
|
const showTooltip = voiceConfig?.showTooltip ?? false;
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6892
|
+
attachTooltip({
|
|
6893
|
+
anchor: micButton,
|
|
6894
|
+
trigger: micButtonWrapper,
|
|
6895
|
+
text: () => micButton.getAttribute("aria-label") ?? tooltipText,
|
|
6896
|
+
enabled: showTooltip,
|
|
6897
|
+
});
|
|
6849
6898
|
|
|
6850
6899
|
return { micButton, micButtonWrapper };
|
|
6851
6900
|
};
|
|
@@ -7360,6 +7409,9 @@ export const createAgentExperience = (
|
|
|
7360
7409
|
if (typeof ResizeObserver !== "undefined") {
|
|
7361
7410
|
const contentResizeObserver = new ResizeObserver(() => {
|
|
7362
7411
|
handleContentResize();
|
|
7412
|
+
// A resize can start or stop a table overflowing without a render or a
|
|
7413
|
+
// scroll, which are the only other things that recompute the fades.
|
|
7414
|
+
refreshTableScrollFades(messagesWrapper);
|
|
7363
7415
|
});
|
|
7364
7416
|
contentResizeObserver.observe(messagesWrapper);
|
|
7365
7417
|
contentResizeObserver.observe(body);
|
|
@@ -8185,76 +8237,13 @@ export const createAgentExperience = (
|
|
|
8185
8237
|
closeButton.setAttribute("aria-label", closeButtonTooltipText);
|
|
8186
8238
|
|
|
8187
8239
|
if (closeButtonWrapper) {
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
if (closeButtonShowTooltip && closeButtonTooltipText) {
|
|
8196
|
-
let portaledTooltip: HTMLElement | null = null;
|
|
8197
|
-
|
|
8198
|
-
const showTooltip = () => {
|
|
8199
|
-
if (portaledTooltip || !closeButton) return; // Already showing or button doesn't exist
|
|
8200
|
-
|
|
8201
|
-
const tooltipDocument = closeButton.ownerDocument;
|
|
8202
|
-
const tooltipContainer = tooltipDocument.body;
|
|
8203
|
-
if (!tooltipContainer) return;
|
|
8204
|
-
|
|
8205
|
-
// Create tooltip element
|
|
8206
|
-
portaledTooltip = createElementInDocument(
|
|
8207
|
-
tooltipDocument,
|
|
8208
|
-
"div",
|
|
8209
|
-
"persona-clear-chat-tooltip"
|
|
8210
|
-
);
|
|
8211
|
-
portaledTooltip.textContent = closeButtonTooltipText;
|
|
8212
|
-
|
|
8213
|
-
// Add arrow
|
|
8214
|
-
const arrow = createElementInDocument(tooltipDocument, "div");
|
|
8215
|
-
arrow.className = "persona-clear-chat-tooltip-arrow";
|
|
8216
|
-
portaledTooltip.appendChild(arrow);
|
|
8217
|
-
|
|
8218
|
-
// Get button position
|
|
8219
|
-
const buttonRect = closeButton.getBoundingClientRect();
|
|
8220
|
-
|
|
8221
|
-
// Position tooltip above button
|
|
8222
|
-
portaledTooltip.style.position = "fixed";
|
|
8223
|
-
portaledTooltip.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);
|
|
8224
|
-
portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;
|
|
8225
|
-
portaledTooltip.style.top = `${buttonRect.top - 8}px`;
|
|
8226
|
-
portaledTooltip.style.transform = "translate(-50%, -100%)";
|
|
8227
|
-
|
|
8228
|
-
// Append to body
|
|
8229
|
-
tooltipContainer.appendChild(portaledTooltip);
|
|
8230
|
-
};
|
|
8231
|
-
|
|
8232
|
-
const hideTooltip = () => {
|
|
8233
|
-
if (portaledTooltip && portaledTooltip.parentNode) {
|
|
8234
|
-
portaledTooltip.parentNode.removeChild(portaledTooltip);
|
|
8235
|
-
portaledTooltip = null;
|
|
8236
|
-
}
|
|
8237
|
-
};
|
|
8238
|
-
|
|
8239
|
-
// Add event listeners
|
|
8240
|
-
closeButtonWrapper.addEventListener("mouseenter", showTooltip);
|
|
8241
|
-
closeButtonWrapper.addEventListener("mouseleave", hideTooltip);
|
|
8242
|
-
closeButton.addEventListener("focus", showTooltip);
|
|
8243
|
-
closeButton.addEventListener("blur", hideTooltip);
|
|
8244
|
-
|
|
8245
|
-
// Store cleanup function on the wrapper for later use
|
|
8246
|
-
(closeButtonWrapper as any)._cleanupTooltip = () => {
|
|
8247
|
-
hideTooltip();
|
|
8248
|
-
if (closeButtonWrapper) {
|
|
8249
|
-
closeButtonWrapper.removeEventListener("mouseenter", showTooltip);
|
|
8250
|
-
closeButtonWrapper.removeEventListener("mouseleave", hideTooltip);
|
|
8251
|
-
}
|
|
8252
|
-
if (closeButton) {
|
|
8253
|
-
closeButton.removeEventListener("focus", showTooltip);
|
|
8254
|
-
closeButton.removeEventListener("blur", hideTooltip);
|
|
8255
|
-
}
|
|
8256
|
-
};
|
|
8257
|
-
}
|
|
8240
|
+
attachTooltip({
|
|
8241
|
+
anchor: closeButton,
|
|
8242
|
+
trigger: closeButtonWrapper,
|
|
8243
|
+
text: () =>
|
|
8244
|
+
closeButton?.getAttribute("aria-label") ?? closeButtonTooltipText,
|
|
8245
|
+
enabled: closeButtonShowTooltip,
|
|
8246
|
+
});
|
|
8258
8247
|
}
|
|
8259
8248
|
}
|
|
8260
8249
|
|
|
@@ -8420,76 +8409,14 @@ export const createAgentExperience = (
|
|
|
8420
8409
|
clearChatButton.setAttribute("aria-label", clearChatTooltipText);
|
|
8421
8410
|
|
|
8422
8411
|
if (clearChatButtonWrapper) {
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
let portaledTooltip: HTMLElement | null = null;
|
|
8432
|
-
|
|
8433
|
-
const showTooltip = () => {
|
|
8434
|
-
if (portaledTooltip || !clearChatButton) return; // Already showing or button doesn't exist
|
|
8435
|
-
|
|
8436
|
-
const tooltipDocument = clearChatButton.ownerDocument;
|
|
8437
|
-
const tooltipContainer = tooltipDocument.body;
|
|
8438
|
-
if (!tooltipContainer) return;
|
|
8439
|
-
|
|
8440
|
-
// Create tooltip element
|
|
8441
|
-
portaledTooltip = createElementInDocument(
|
|
8442
|
-
tooltipDocument,
|
|
8443
|
-
"div",
|
|
8444
|
-
"persona-clear-chat-tooltip"
|
|
8445
|
-
);
|
|
8446
|
-
portaledTooltip.textContent = clearChatTooltipText;
|
|
8447
|
-
|
|
8448
|
-
// Add arrow
|
|
8449
|
-
const arrow = createElementInDocument(tooltipDocument, "div");
|
|
8450
|
-
arrow.className = "persona-clear-chat-tooltip-arrow";
|
|
8451
|
-
portaledTooltip.appendChild(arrow);
|
|
8452
|
-
|
|
8453
|
-
// Get button position
|
|
8454
|
-
const buttonRect = clearChatButton.getBoundingClientRect();
|
|
8455
|
-
|
|
8456
|
-
// Position tooltip above button
|
|
8457
|
-
portaledTooltip.style.position = "fixed";
|
|
8458
|
-
portaledTooltip.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);
|
|
8459
|
-
portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;
|
|
8460
|
-
portaledTooltip.style.top = `${buttonRect.top - 8}px`;
|
|
8461
|
-
portaledTooltip.style.transform = "translate(-50%, -100%)";
|
|
8462
|
-
|
|
8463
|
-
// Append to body
|
|
8464
|
-
tooltipContainer.appendChild(portaledTooltip);
|
|
8465
|
-
};
|
|
8466
|
-
|
|
8467
|
-
const hideTooltip = () => {
|
|
8468
|
-
if (portaledTooltip && portaledTooltip.parentNode) {
|
|
8469
|
-
portaledTooltip.parentNode.removeChild(portaledTooltip);
|
|
8470
|
-
portaledTooltip = null;
|
|
8471
|
-
}
|
|
8472
|
-
};
|
|
8473
|
-
|
|
8474
|
-
// Add event listeners
|
|
8475
|
-
clearChatButtonWrapper.addEventListener("mouseenter", showTooltip);
|
|
8476
|
-
clearChatButtonWrapper.addEventListener("mouseleave", hideTooltip);
|
|
8477
|
-
clearChatButton.addEventListener("focus", showTooltip);
|
|
8478
|
-
clearChatButton.addEventListener("blur", hideTooltip);
|
|
8479
|
-
|
|
8480
|
-
// Store cleanup function on the button for later use
|
|
8481
|
-
(clearChatButtonWrapper as any)._cleanupTooltip = () => {
|
|
8482
|
-
hideTooltip();
|
|
8483
|
-
if (clearChatButtonWrapper) {
|
|
8484
|
-
clearChatButtonWrapper.removeEventListener("mouseenter", showTooltip);
|
|
8485
|
-
clearChatButtonWrapper.removeEventListener("mouseleave", hideTooltip);
|
|
8486
|
-
}
|
|
8487
|
-
if (clearChatButton) {
|
|
8488
|
-
clearChatButton.removeEventListener("focus", showTooltip);
|
|
8489
|
-
clearChatButton.removeEventListener("blur", hideTooltip);
|
|
8490
|
-
}
|
|
8491
|
-
};
|
|
8492
|
-
}
|
|
8412
|
+
attachTooltip({
|
|
8413
|
+
anchor: clearChatButton,
|
|
8414
|
+
trigger: clearChatButtonWrapper,
|
|
8415
|
+
text: () =>
|
|
8416
|
+
clearChatButton?.getAttribute("aria-label") ??
|
|
8417
|
+
clearChatTooltipText,
|
|
8418
|
+
enabled: clearChatShowTooltip,
|
|
8419
|
+
});
|
|
8493
8420
|
}
|
|
8494
8421
|
}
|
|
8495
8422
|
}
|
|
@@ -8628,24 +8555,16 @@ export const createAgentExperience = (
|
|
|
8628
8555
|
micButton.style.paddingBottom = "";
|
|
8629
8556
|
}
|
|
8630
8557
|
|
|
8631
|
-
// Update tooltip
|
|
8632
|
-
const tooltip = micButtonWrapper?.querySelector(".persona-send-button-tooltip") as HTMLElement | null;
|
|
8633
8558
|
const tooltipText = voiceConfig.tooltipText ?? "Start voice recognition";
|
|
8634
8559
|
const showTooltip = voiceConfig.showTooltip ?? false;
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
}
|
|
8643
|
-
tooltip.textContent = tooltipText;
|
|
8644
|
-
tooltip.style.display = "";
|
|
8645
|
-
}
|
|
8646
|
-
} else if (tooltip) {
|
|
8647
|
-
// Hide tooltip if disabled
|
|
8648
|
-
tooltip.style.display = "none";
|
|
8560
|
+
micButton.setAttribute("aria-label", tooltipText);
|
|
8561
|
+
if (micButtonWrapper) {
|
|
8562
|
+
attachTooltip({
|
|
8563
|
+
anchor: micButton,
|
|
8564
|
+
trigger: micButtonWrapper,
|
|
8565
|
+
text: () => micButton?.getAttribute("aria-label") ?? tooltipText,
|
|
8566
|
+
enabled: showTooltip,
|
|
8567
|
+
});
|
|
8649
8568
|
}
|
|
8650
8569
|
|
|
8651
8570
|
// Show and update disabled state
|
|
@@ -8737,11 +8656,13 @@ export const createAgentExperience = (
|
|
|
8737
8656
|
|
|
8738
8657
|
attachmentButtonWrapper.appendChild(attachmentButton);
|
|
8739
8658
|
|
|
8740
|
-
// Add tooltip
|
|
8741
8659
|
const attachTooltipText = attachmentsConfig.buttonTooltipText ?? "Attach file";
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8660
|
+
attachTooltip({
|
|
8661
|
+
anchor: attachmentButton,
|
|
8662
|
+
trigger: attachmentButtonWrapper,
|
|
8663
|
+
text: () =>
|
|
8664
|
+
attachmentButton?.getAttribute("aria-label") ?? attachTooltipText,
|
|
8665
|
+
});
|
|
8745
8666
|
|
|
8746
8667
|
// Insert into left actions container (fall back to the form when a
|
|
8747
8668
|
// custom composer has no left cluster).
|
|
@@ -8803,10 +8724,14 @@ export const createAgentExperience = (
|
|
|
8803
8724
|
);
|
|
8804
8725
|
if (iconSvg) attachmentButton.appendChild(iconSvg);
|
|
8805
8726
|
else attachmentButton.textContent = "📎";
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8727
|
+
if (attachmentButtonWrapper) {
|
|
8728
|
+
attachTooltip({
|
|
8729
|
+
anchor: attachmentButton,
|
|
8730
|
+
trigger: attachmentButtonWrapper,
|
|
8731
|
+
text: () =>
|
|
8732
|
+
attachmentButton?.getAttribute("aria-label") ?? tooltipText,
|
|
8733
|
+
});
|
|
8734
|
+
}
|
|
8810
8735
|
}
|
|
8811
8736
|
} else {
|
|
8812
8737
|
// Hide attachment button if disabled
|
|
@@ -8935,21 +8860,16 @@ export const createAgentExperience = (
|
|
|
8935
8860
|
sendButton.style.paddingBottom = "";
|
|
8936
8861
|
}
|
|
8937
8862
|
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
}
|
|
8948
|
-
tooltip.textContent = tooltipText;
|
|
8949
|
-
tooltip.style.display = "";
|
|
8950
|
-
}
|
|
8951
|
-
} else if (tooltip) {
|
|
8952
|
-
tooltip.style.display = "none";
|
|
8863
|
+
if (!session.isStreaming()) {
|
|
8864
|
+
sendButton.setAttribute("aria-label", tooltipText);
|
|
8865
|
+
}
|
|
8866
|
+
if (sendButtonWrapper) {
|
|
8867
|
+
attachTooltip({
|
|
8868
|
+
anchor: sendButton,
|
|
8869
|
+
trigger: sendButtonWrapper,
|
|
8870
|
+
text: () => sendButton.getAttribute("aria-label") ?? tooltipText,
|
|
8871
|
+
enabled: showTooltip,
|
|
8872
|
+
});
|
|
8953
8873
|
}
|
|
8954
8874
|
|
|
8955
8875
|
// Update contentMaxWidth on messages wrapper and composer. Same
|