@runtypelabs/persona 4.10.1 → 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-Bx_rvjff.d.cts → types-DveIaNx6.d.cts} +2 -0
- package/dist/animations/{types-Bx_rvjff.d.ts → types-DveIaNx6.d.ts} +2 -0
- 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 +26 -0
- package/dist/context-mentions.d.ts +26 -0
- package/dist/index.cjs +67 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- 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 +26 -0
- package/dist/smart-dom-reader.d.ts +26 -0
- package/dist/theme-editor-preview.cjs +66 -66
- package/dist/theme-editor-preview.d.cts +26 -0
- package/dist/theme-editor-preview.d.ts +26 -0
- package/dist/theme-editor-preview.js +55 -55
- package/dist/theme-editor.cjs +5 -5
- package/dist/theme-editor.d.cts +26 -0
- package/dist/theme-editor.d.ts +26 -0
- 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 +2 -0
- package/src/index-core.ts +1 -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.ts +145 -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,
|
|
@@ -4171,6 +4173,46 @@ export const createAgentExperience = (
|
|
|
4171
4173
|
|
|
4172
4174
|
|
|
4173
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
|
+
|
|
4174
4216
|
const renderMessagesWithPluginsImpl = (
|
|
4175
4217
|
container: HTMLElement,
|
|
4176
4218
|
messages: AgentWidgetMessage[],
|
|
@@ -4212,6 +4254,7 @@ export const createAgentExperience = (
|
|
|
4212
4254
|
|
|
4213
4255
|
// Track active message IDs for cache pruning
|
|
4214
4256
|
const activeMessageIds = new Set<string>();
|
|
4257
|
+
const messageRolesById = new Map<string, AgentWidgetMessage["role"]>();
|
|
4215
4258
|
// Track ask_user_question tool-call ids whose bubbles were rendered this
|
|
4216
4259
|
// pass: used to prune stale sheets from the composer overlay afterward.
|
|
4217
4260
|
const liveAskToolIds = new Set<string>();
|
|
@@ -4265,6 +4308,7 @@ export const createAgentExperience = (
|
|
|
4265
4308
|
|
|
4266
4309
|
messages.forEach((message) => {
|
|
4267
4310
|
activeMessageIds.add(message.id);
|
|
4311
|
+
messageRolesById.set(message.id, message.role);
|
|
4268
4312
|
|
|
4269
4313
|
const askWithPlugin = hasAskPlugin && isAskUserQuestionMessage(message);
|
|
4270
4314
|
const approvalWithPlugin =
|
|
@@ -4444,10 +4488,7 @@ export const createAgentExperience = (
|
|
|
4444
4488
|
|
|
4445
4489
|
// Append a stub wrapper for the morph pass; hydrate the real bubble
|
|
4446
4490
|
// into it post-morph so its event listeners survive.
|
|
4447
|
-
const stub =
|
|
4448
|
-
stub.className = "persona-flex";
|
|
4449
|
-
stub.id = `wrapper-${message.id}`;
|
|
4450
|
-
stub.setAttribute("data-wrapper-id", message.id);
|
|
4491
|
+
const stub = createMessageRow(message.id, message.role);
|
|
4451
4492
|
stub.setAttribute("data-ask-plugin-stub", "true");
|
|
4452
4493
|
stub.setAttribute("data-preserve-runtime", "true");
|
|
4453
4494
|
tempContainer.appendChild(stub);
|
|
@@ -4522,10 +4563,7 @@ export const createAgentExperience = (
|
|
|
4522
4563
|
} else {
|
|
4523
4564
|
// A fresh live bubble to hydrate (needsRebuild), or fingerprint
|
|
4524
4565
|
// unchanged so we reuse the preserved live wrapper (`bubble: null`).
|
|
4525
|
-
const stub =
|
|
4526
|
-
stub.className = "persona-flex";
|
|
4527
|
-
stub.id = `wrapper-${message.id}`;
|
|
4528
|
-
stub.setAttribute("data-wrapper-id", message.id);
|
|
4566
|
+
const stub = createMessageRow(message.id, message.role);
|
|
4529
4567
|
stub.setAttribute("data-approval-plugin-stub", "true");
|
|
4530
4568
|
stub.setAttribute("data-preserve-runtime", "true");
|
|
4531
4569
|
tempContainer.appendChild(stub);
|
|
@@ -4642,7 +4680,6 @@ export const createAgentExperience = (
|
|
|
4642
4680
|
const componentWrapper = document.createElement("div");
|
|
4643
4681
|
componentWrapper.className = [
|
|
4644
4682
|
"persona-message-bubble",
|
|
4645
|
-
"persona-max-w-[85%]",
|
|
4646
4683
|
"persona-rounded-2xl",
|
|
4647
4684
|
"persona-bg-persona-surface",
|
|
4648
4685
|
"persona-border",
|
|
@@ -4698,15 +4735,9 @@ export const createAgentExperience = (
|
|
|
4698
4735
|
// Otherwise fall through to the standard render path so the message
|
|
4699
4736
|
// text is at least visible.
|
|
4700
4737
|
if (liveBubble || lastFp != null) {
|
|
4701
|
-
const stub =
|
|
4702
|
-
stub.className = "persona-flex";
|
|
4703
|
-
stub.id = `wrapper-${message.id}`;
|
|
4704
|
-
stub.setAttribute("data-wrapper-id", message.id);
|
|
4738
|
+
const stub = createMessageRow(message.id, message.role);
|
|
4705
4739
|
stub.setAttribute("data-component-directive-stub", "true");
|
|
4706
4740
|
stub.setAttribute("data-preserve-runtime", "true");
|
|
4707
|
-
if (!wrapChrome) {
|
|
4708
|
-
stub.classList.add("persona-w-full");
|
|
4709
|
-
}
|
|
4710
4741
|
tempContainer.appendChild(stub);
|
|
4711
4742
|
componentDirectiveHydrate.push({
|
|
4712
4743
|
messageId: message.id,
|
|
@@ -4763,17 +4794,7 @@ export const createAgentExperience = (
|
|
|
4763
4794
|
}
|
|
4764
4795
|
}
|
|
4765
4796
|
|
|
4766
|
-
const wrapper =
|
|
4767
|
-
wrapper.className = "persona-flex";
|
|
4768
|
-
// Set id for idiomorph matching
|
|
4769
|
-
wrapper.id = `wrapper-${message.id}`;
|
|
4770
|
-
wrapper.setAttribute("data-wrapper-id", message.id);
|
|
4771
|
-
if (message.role === "user") {
|
|
4772
|
-
wrapper.classList.add("persona-justify-end");
|
|
4773
|
-
}
|
|
4774
|
-
if (bubble?.getAttribute("data-persona-component-directive") === "true") {
|
|
4775
|
-
wrapper.classList.add("persona-w-full");
|
|
4776
|
-
}
|
|
4797
|
+
const wrapper = createMessageRow(message.id, message.role);
|
|
4777
4798
|
wrapper.appendChild(bubble);
|
|
4778
4799
|
setCachedWrapper(messageCache, message.id, fingerprint, wrapper);
|
|
4779
4800
|
tempContainer.appendChild(wrapper);
|
|
@@ -4832,10 +4853,11 @@ export const createAgentExperience = (
|
|
|
4832
4853
|
return;
|
|
4833
4854
|
}
|
|
4834
4855
|
|
|
4835
|
-
const groupWrapper =
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4856
|
+
const groupWrapper = createMessageRow(
|
|
4857
|
+
`tool-group-${groupIndex}-${group[0].id}`,
|
|
4858
|
+
"assistant"
|
|
4859
|
+
);
|
|
4860
|
+
groupWrapper.setAttribute("data-persona-tool-group-row", "true");
|
|
4839
4861
|
|
|
4840
4862
|
const groupContainer = document.createElement("div");
|
|
4841
4863
|
groupContainer.className =
|
|
@@ -4875,6 +4897,10 @@ export const createAgentExperience = (
|
|
|
4875
4897
|
wrapper.remove();
|
|
4876
4898
|
return;
|
|
4877
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%");
|
|
4878
4904
|
const item = document.createElement("div");
|
|
4879
4905
|
item.className = "persona-tool-group-item persona-relative";
|
|
4880
4906
|
item.setAttribute("data-persona-tool-group-item", "true");
|
|
@@ -4938,7 +4964,6 @@ export const createAgentExperience = (
|
|
|
4938
4964
|
const showBubble = config.loadingIndicator?.showBubble !== false; // default true
|
|
4939
4965
|
typingBubble.className = showBubble
|
|
4940
4966
|
? [
|
|
4941
|
-
"persona-max-w-[85%]",
|
|
4942
4967
|
"persona-rounded-2xl",
|
|
4943
4968
|
"persona-text-sm",
|
|
4944
4969
|
"persona-leading-relaxed",
|
|
@@ -4950,7 +4975,6 @@ export const createAgentExperience = (
|
|
|
4950
4975
|
"persona-py-3"
|
|
4951
4976
|
].join(" ")
|
|
4952
4977
|
: [
|
|
4953
|
-
"persona-max-w-[85%]",
|
|
4954
4978
|
"persona-text-sm",
|
|
4955
4979
|
"persona-leading-relaxed",
|
|
4956
4980
|
"persona-text-persona-primary"
|
|
@@ -4960,11 +4984,10 @@ export const createAgentExperience = (
|
|
|
4960
4984
|
|
|
4961
4985
|
typingBubble.appendChild(typingIndicator);
|
|
4962
4986
|
|
|
4963
|
-
const typingWrapper =
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
typingWrapper.setAttribute("data-wrapper-id", "typing-indicator");
|
|
4987
|
+
const typingWrapper = createMessageRow(
|
|
4988
|
+
"typing-indicator",
|
|
4989
|
+
"assistant"
|
|
4990
|
+
);
|
|
4968
4991
|
typingWrapper.appendChild(typingBubble);
|
|
4969
4992
|
tempContainer.appendChild(typingWrapper);
|
|
4970
4993
|
}
|
|
@@ -5002,7 +5025,6 @@ export const createAgentExperience = (
|
|
|
5002
5025
|
const showBubble = config.loadingIndicator?.showBubble !== false; // default true
|
|
5003
5026
|
idleBubble.className = showBubble
|
|
5004
5027
|
? [
|
|
5005
|
-
"persona-max-w-[85%]",
|
|
5006
5028
|
"persona-rounded-2xl",
|
|
5007
5029
|
"persona-text-sm",
|
|
5008
5030
|
"persona-leading-relaxed",
|
|
@@ -5015,7 +5037,6 @@ export const createAgentExperience = (
|
|
|
5015
5037
|
"persona-py-3"
|
|
5016
5038
|
].join(" ")
|
|
5017
5039
|
: [
|
|
5018
|
-
"persona-max-w-[85%]",
|
|
5019
5040
|
"persona-text-sm",
|
|
5020
5041
|
"persona-leading-relaxed",
|
|
5021
5042
|
"persona-text-persona-primary"
|
|
@@ -5024,27 +5045,35 @@ export const createAgentExperience = (
|
|
|
5024
5045
|
|
|
5025
5046
|
idleBubble.appendChild(idleIndicator);
|
|
5026
5047
|
|
|
5027
|
-
const idleWrapper =
|
|
5028
|
-
idleWrapper.className = "persona-flex";
|
|
5029
|
-
// Set id for idiomorph matching
|
|
5030
|
-
idleWrapper.id = "wrapper-idle-indicator";
|
|
5031
|
-
idleWrapper.setAttribute("data-wrapper-id", "idle-indicator");
|
|
5048
|
+
const idleWrapper = createMessageRow("idle-indicator", "assistant");
|
|
5032
5049
|
idleWrapper.appendChild(idleBubble);
|
|
5033
5050
|
tempContainer.appendChild(idleWrapper);
|
|
5034
5051
|
}
|
|
5035
5052
|
}
|
|
5036
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
|
+
|
|
5037
5058
|
// Use idiomorph to morph the container contents
|
|
5038
5059
|
morphMessages(container, tempContainer);
|
|
5039
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
|
+
|
|
5040
5065
|
// Hydrate plugin-rendered ask-question bubbles into their stub wrappers.
|
|
5041
5066
|
// Idiomorph imports new nodes via `document.importNode`, which strips
|
|
5042
5067
|
// listeners, so we built only an empty stub during morph and now inject
|
|
5043
5068
|
// the real, listener-bearing bubble directly into the live DOM.
|
|
5044
5069
|
if (askPluginHydrate.length > 0) {
|
|
5045
5070
|
for (const { messageId, fingerprint, bubble } of askPluginHydrate) {
|
|
5046
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5071
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5047
5072
|
if (!wrapper) continue;
|
|
5073
|
+
applyMessageRowLayout(
|
|
5074
|
+
wrapper,
|
|
5075
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5076
|
+
);
|
|
5048
5077
|
if (bubble === null) {
|
|
5049
5078
|
// No fresh bubble built this pass: either the plugin opted out
|
|
5050
5079
|
// and a previously-mounted bubble already lives here (preserved by
|
|
@@ -5070,8 +5099,12 @@ export const createAgentExperience = (
|
|
|
5070
5099
|
// the ask-question hydration above.
|
|
5071
5100
|
if (componentDirectiveHydrate.length > 0) {
|
|
5072
5101
|
for (const { messageId, fingerprint, bubble } of componentDirectiveHydrate) {
|
|
5073
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5102
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5074
5103
|
if (!wrapper) continue;
|
|
5104
|
+
applyMessageRowLayout(
|
|
5105
|
+
wrapper,
|
|
5106
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5107
|
+
);
|
|
5075
5108
|
if (bubble === null) {
|
|
5076
5109
|
// Fingerprint matched the previous pass: the live wrapper (kept
|
|
5077
5110
|
// alive by `data-preserve-runtime`) still holds the listener-bearing
|
|
@@ -5094,8 +5127,12 @@ export const createAgentExperience = (
|
|
|
5094
5127
|
// mirroring the ask-question / component-directive hydration above.
|
|
5095
5128
|
if (approvalPluginHydrate.length > 0) {
|
|
5096
5129
|
for (const { messageId, fingerprint, bubble } of approvalPluginHydrate) {
|
|
5097
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5130
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5098
5131
|
if (!wrapper) continue;
|
|
5132
|
+
applyMessageRowLayout(
|
|
5133
|
+
wrapper,
|
|
5134
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5135
|
+
);
|
|
5099
5136
|
if (bubble === null) {
|
|
5100
5137
|
// Fingerprint matched the previous pass (or the plugin opted out
|
|
5101
5138
|
// after a prior render): the live wrapper, kept alive by
|
|
@@ -6789,7 +6826,8 @@ export const createAgentExperience = (
|
|
|
6789
6826
|
) as HTMLButtonElement;
|
|
6790
6827
|
|
|
6791
6828
|
micButton.type = "button";
|
|
6792
|
-
|
|
6829
|
+
const tooltipText = voiceConfig?.tooltipText ?? "Start voice recognition";
|
|
6830
|
+
micButton.setAttribute("aria-label", tooltipText);
|
|
6793
6831
|
|
|
6794
6832
|
const micIconName = voiceConfig?.iconName ?? "mic";
|
|
6795
6833
|
const buttonSize = sendButtonConfig?.size ?? "40px";
|
|
@@ -6850,15 +6888,13 @@ export const createAgentExperience = (
|
|
|
6850
6888
|
}
|
|
6851
6889
|
|
|
6852
6890
|
micButtonWrapper.appendChild(micButton);
|
|
6853
|
-
|
|
6854
|
-
// Add tooltip if enabled
|
|
6855
|
-
const tooltipText = voiceConfig?.tooltipText ?? "Start voice recognition";
|
|
6856
6891
|
const showTooltip = voiceConfig?.showTooltip ?? false;
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6892
|
+
attachTooltip({
|
|
6893
|
+
anchor: micButton,
|
|
6894
|
+
trigger: micButtonWrapper,
|
|
6895
|
+
text: () => micButton.getAttribute("aria-label") ?? tooltipText,
|
|
6896
|
+
enabled: showTooltip,
|
|
6897
|
+
});
|
|
6862
6898
|
|
|
6863
6899
|
return { micButton, micButtonWrapper };
|
|
6864
6900
|
};
|
|
@@ -7373,6 +7409,9 @@ export const createAgentExperience = (
|
|
|
7373
7409
|
if (typeof ResizeObserver !== "undefined") {
|
|
7374
7410
|
const contentResizeObserver = new ResizeObserver(() => {
|
|
7375
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);
|
|
7376
7415
|
});
|
|
7377
7416
|
contentResizeObserver.observe(messagesWrapper);
|
|
7378
7417
|
contentResizeObserver.observe(body);
|
|
@@ -8198,76 +8237,13 @@ export const createAgentExperience = (
|
|
|
8198
8237
|
closeButton.setAttribute("aria-label", closeButtonTooltipText);
|
|
8199
8238
|
|
|
8200
8239
|
if (closeButtonWrapper) {
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
if (closeButtonShowTooltip && closeButtonTooltipText) {
|
|
8209
|
-
let portaledTooltip: HTMLElement | null = null;
|
|
8210
|
-
|
|
8211
|
-
const showTooltip = () => {
|
|
8212
|
-
if (portaledTooltip || !closeButton) return; // Already showing or button doesn't exist
|
|
8213
|
-
|
|
8214
|
-
const tooltipDocument = closeButton.ownerDocument;
|
|
8215
|
-
const tooltipContainer = tooltipDocument.body;
|
|
8216
|
-
if (!tooltipContainer) return;
|
|
8217
|
-
|
|
8218
|
-
// Create tooltip element
|
|
8219
|
-
portaledTooltip = createElementInDocument(
|
|
8220
|
-
tooltipDocument,
|
|
8221
|
-
"div",
|
|
8222
|
-
"persona-clear-chat-tooltip"
|
|
8223
|
-
);
|
|
8224
|
-
portaledTooltip.textContent = closeButtonTooltipText;
|
|
8225
|
-
|
|
8226
|
-
// Add arrow
|
|
8227
|
-
const arrow = createElementInDocument(tooltipDocument, "div");
|
|
8228
|
-
arrow.className = "persona-clear-chat-tooltip-arrow";
|
|
8229
|
-
portaledTooltip.appendChild(arrow);
|
|
8230
|
-
|
|
8231
|
-
// Get button position
|
|
8232
|
-
const buttonRect = closeButton.getBoundingClientRect();
|
|
8233
|
-
|
|
8234
|
-
// Position tooltip above button
|
|
8235
|
-
portaledTooltip.style.position = "fixed";
|
|
8236
|
-
portaledTooltip.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);
|
|
8237
|
-
portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;
|
|
8238
|
-
portaledTooltip.style.top = `${buttonRect.top - 8}px`;
|
|
8239
|
-
portaledTooltip.style.transform = "translate(-50%, -100%)";
|
|
8240
|
-
|
|
8241
|
-
// Append to body
|
|
8242
|
-
tooltipContainer.appendChild(portaledTooltip);
|
|
8243
|
-
};
|
|
8244
|
-
|
|
8245
|
-
const hideTooltip = () => {
|
|
8246
|
-
if (portaledTooltip && portaledTooltip.parentNode) {
|
|
8247
|
-
portaledTooltip.parentNode.removeChild(portaledTooltip);
|
|
8248
|
-
portaledTooltip = null;
|
|
8249
|
-
}
|
|
8250
|
-
};
|
|
8251
|
-
|
|
8252
|
-
// Add event listeners
|
|
8253
|
-
closeButtonWrapper.addEventListener("mouseenter", showTooltip);
|
|
8254
|
-
closeButtonWrapper.addEventListener("mouseleave", hideTooltip);
|
|
8255
|
-
closeButton.addEventListener("focus", showTooltip);
|
|
8256
|
-
closeButton.addEventListener("blur", hideTooltip);
|
|
8257
|
-
|
|
8258
|
-
// Store cleanup function on the wrapper for later use
|
|
8259
|
-
(closeButtonWrapper as any)._cleanupTooltip = () => {
|
|
8260
|
-
hideTooltip();
|
|
8261
|
-
if (closeButtonWrapper) {
|
|
8262
|
-
closeButtonWrapper.removeEventListener("mouseenter", showTooltip);
|
|
8263
|
-
closeButtonWrapper.removeEventListener("mouseleave", hideTooltip);
|
|
8264
|
-
}
|
|
8265
|
-
if (closeButton) {
|
|
8266
|
-
closeButton.removeEventListener("focus", showTooltip);
|
|
8267
|
-
closeButton.removeEventListener("blur", hideTooltip);
|
|
8268
|
-
}
|
|
8269
|
-
};
|
|
8270
|
-
}
|
|
8240
|
+
attachTooltip({
|
|
8241
|
+
anchor: closeButton,
|
|
8242
|
+
trigger: closeButtonWrapper,
|
|
8243
|
+
text: () =>
|
|
8244
|
+
closeButton?.getAttribute("aria-label") ?? closeButtonTooltipText,
|
|
8245
|
+
enabled: closeButtonShowTooltip,
|
|
8246
|
+
});
|
|
8271
8247
|
}
|
|
8272
8248
|
}
|
|
8273
8249
|
|
|
@@ -8433,76 +8409,14 @@ export const createAgentExperience = (
|
|
|
8433
8409
|
clearChatButton.setAttribute("aria-label", clearChatTooltipText);
|
|
8434
8410
|
|
|
8435
8411
|
if (clearChatButtonWrapper) {
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
let portaledTooltip: HTMLElement | null = null;
|
|
8445
|
-
|
|
8446
|
-
const showTooltip = () => {
|
|
8447
|
-
if (portaledTooltip || !clearChatButton) return; // Already showing or button doesn't exist
|
|
8448
|
-
|
|
8449
|
-
const tooltipDocument = clearChatButton.ownerDocument;
|
|
8450
|
-
const tooltipContainer = tooltipDocument.body;
|
|
8451
|
-
if (!tooltipContainer) return;
|
|
8452
|
-
|
|
8453
|
-
// Create tooltip element
|
|
8454
|
-
portaledTooltip = createElementInDocument(
|
|
8455
|
-
tooltipDocument,
|
|
8456
|
-
"div",
|
|
8457
|
-
"persona-clear-chat-tooltip"
|
|
8458
|
-
);
|
|
8459
|
-
portaledTooltip.textContent = clearChatTooltipText;
|
|
8460
|
-
|
|
8461
|
-
// Add arrow
|
|
8462
|
-
const arrow = createElementInDocument(tooltipDocument, "div");
|
|
8463
|
-
arrow.className = "persona-clear-chat-tooltip-arrow";
|
|
8464
|
-
portaledTooltip.appendChild(arrow);
|
|
8465
|
-
|
|
8466
|
-
// Get button position
|
|
8467
|
-
const buttonRect = clearChatButton.getBoundingClientRect();
|
|
8468
|
-
|
|
8469
|
-
// Position tooltip above button
|
|
8470
|
-
portaledTooltip.style.position = "fixed";
|
|
8471
|
-
portaledTooltip.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);
|
|
8472
|
-
portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;
|
|
8473
|
-
portaledTooltip.style.top = `${buttonRect.top - 8}px`;
|
|
8474
|
-
portaledTooltip.style.transform = "translate(-50%, -100%)";
|
|
8475
|
-
|
|
8476
|
-
// Append to body
|
|
8477
|
-
tooltipContainer.appendChild(portaledTooltip);
|
|
8478
|
-
};
|
|
8479
|
-
|
|
8480
|
-
const hideTooltip = () => {
|
|
8481
|
-
if (portaledTooltip && portaledTooltip.parentNode) {
|
|
8482
|
-
portaledTooltip.parentNode.removeChild(portaledTooltip);
|
|
8483
|
-
portaledTooltip = null;
|
|
8484
|
-
}
|
|
8485
|
-
};
|
|
8486
|
-
|
|
8487
|
-
// Add event listeners
|
|
8488
|
-
clearChatButtonWrapper.addEventListener("mouseenter", showTooltip);
|
|
8489
|
-
clearChatButtonWrapper.addEventListener("mouseleave", hideTooltip);
|
|
8490
|
-
clearChatButton.addEventListener("focus", showTooltip);
|
|
8491
|
-
clearChatButton.addEventListener("blur", hideTooltip);
|
|
8492
|
-
|
|
8493
|
-
// Store cleanup function on the button for later use
|
|
8494
|
-
(clearChatButtonWrapper as any)._cleanupTooltip = () => {
|
|
8495
|
-
hideTooltip();
|
|
8496
|
-
if (clearChatButtonWrapper) {
|
|
8497
|
-
clearChatButtonWrapper.removeEventListener("mouseenter", showTooltip);
|
|
8498
|
-
clearChatButtonWrapper.removeEventListener("mouseleave", hideTooltip);
|
|
8499
|
-
}
|
|
8500
|
-
if (clearChatButton) {
|
|
8501
|
-
clearChatButton.removeEventListener("focus", showTooltip);
|
|
8502
|
-
clearChatButton.removeEventListener("blur", hideTooltip);
|
|
8503
|
-
}
|
|
8504
|
-
};
|
|
8505
|
-
}
|
|
8412
|
+
attachTooltip({
|
|
8413
|
+
anchor: clearChatButton,
|
|
8414
|
+
trigger: clearChatButtonWrapper,
|
|
8415
|
+
text: () =>
|
|
8416
|
+
clearChatButton?.getAttribute("aria-label") ??
|
|
8417
|
+
clearChatTooltipText,
|
|
8418
|
+
enabled: clearChatShowTooltip,
|
|
8419
|
+
});
|
|
8506
8420
|
}
|
|
8507
8421
|
}
|
|
8508
8422
|
}
|
|
@@ -8641,24 +8555,16 @@ export const createAgentExperience = (
|
|
|
8641
8555
|
micButton.style.paddingBottom = "";
|
|
8642
8556
|
}
|
|
8643
8557
|
|
|
8644
|
-
// Update tooltip
|
|
8645
|
-
const tooltip = micButtonWrapper?.querySelector(".persona-send-button-tooltip") as HTMLElement | null;
|
|
8646
8558
|
const tooltipText = voiceConfig.tooltipText ?? "Start voice recognition";
|
|
8647
8559
|
const showTooltip = voiceConfig.showTooltip ?? false;
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
}
|
|
8656
|
-
tooltip.textContent = tooltipText;
|
|
8657
|
-
tooltip.style.display = "";
|
|
8658
|
-
}
|
|
8659
|
-
} else if (tooltip) {
|
|
8660
|
-
// Hide tooltip if disabled
|
|
8661
|
-
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
|
+
});
|
|
8662
8568
|
}
|
|
8663
8569
|
|
|
8664
8570
|
// Show and update disabled state
|
|
@@ -8750,11 +8656,13 @@ export const createAgentExperience = (
|
|
|
8750
8656
|
|
|
8751
8657
|
attachmentButtonWrapper.appendChild(attachmentButton);
|
|
8752
8658
|
|
|
8753
|
-
// Add tooltip
|
|
8754
8659
|
const attachTooltipText = attachmentsConfig.buttonTooltipText ?? "Attach file";
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8660
|
+
attachTooltip({
|
|
8661
|
+
anchor: attachmentButton,
|
|
8662
|
+
trigger: attachmentButtonWrapper,
|
|
8663
|
+
text: () =>
|
|
8664
|
+
attachmentButton?.getAttribute("aria-label") ?? attachTooltipText,
|
|
8665
|
+
});
|
|
8758
8666
|
|
|
8759
8667
|
// Insert into left actions container (fall back to the form when a
|
|
8760
8668
|
// custom composer has no left cluster).
|
|
@@ -8816,10 +8724,14 @@ export const createAgentExperience = (
|
|
|
8816
8724
|
);
|
|
8817
8725
|
if (iconSvg) attachmentButton.appendChild(iconSvg);
|
|
8818
8726
|
else attachmentButton.textContent = "📎";
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8727
|
+
if (attachmentButtonWrapper) {
|
|
8728
|
+
attachTooltip({
|
|
8729
|
+
anchor: attachmentButton,
|
|
8730
|
+
trigger: attachmentButtonWrapper,
|
|
8731
|
+
text: () =>
|
|
8732
|
+
attachmentButton?.getAttribute("aria-label") ?? tooltipText,
|
|
8733
|
+
});
|
|
8734
|
+
}
|
|
8823
8735
|
}
|
|
8824
8736
|
} else {
|
|
8825
8737
|
// Hide attachment button if disabled
|
|
@@ -8948,21 +8860,16 @@ export const createAgentExperience = (
|
|
|
8948
8860
|
sendButton.style.paddingBottom = "";
|
|
8949
8861
|
}
|
|
8950
8862
|
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
}
|
|
8961
|
-
tooltip.textContent = tooltipText;
|
|
8962
|
-
tooltip.style.display = "";
|
|
8963
|
-
}
|
|
8964
|
-
} else if (tooltip) {
|
|
8965
|
-
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
|
+
});
|
|
8966
8873
|
}
|
|
8967
8874
|
|
|
8968
8875
|
// Update contentMaxWidth on messages wrapper and composer. Same
|