@runtypelabs/persona 4.10.1 → 4.11.1
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 +28 -0
- package/dist/context-mentions.d.ts +28 -0
- package/dist/index.cjs +67 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.global.js +56 -56
- 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 +28 -0
- package/dist/smart-dom-reader.d.ts +28 -0
- package/dist/theme-editor-preview.cjs +66 -66
- package/dist/theme-editor-preview.d.cts +28 -0
- package/dist/theme-editor-preview.d.ts +28 -0
- package/dist/theme-editor-preview.js +55 -55
- package/dist/theme-editor.cjs +5 -5
- package/dist/theme-editor.d.cts +28 -0
- package/dist/theme-editor.d.ts +28 -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 +27 -0
- package/src/ui.attachments-drop.test.ts +2 -1
- package/src/ui.message-width.test.ts +407 -0
- package/src/ui.tool-display.test.ts +177 -8
- package/src/ui.ts +159 -244
- 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);
|
|
@@ -4796,6 +4817,8 @@ export const createAgentExperience = (
|
|
|
4796
4817
|
if (config.features?.toolCallDisplay?.grouped) {
|
|
4797
4818
|
const toolGroups: AgentWidgetMessage[][] = [];
|
|
4798
4819
|
let currentGroup: AgentWidgetMessage[] = [];
|
|
4820
|
+
const summaryOnly =
|
|
4821
|
+
config.features.toolCallDisplay.groupedMode === "summary";
|
|
4799
4822
|
|
|
4800
4823
|
messages.forEach((message) => {
|
|
4801
4824
|
if (message.variant === "tool" && message.toolCall && showToolCalls) {
|
|
@@ -4808,16 +4831,20 @@ export const createAgentExperience = (
|
|
|
4808
4831
|
if (message.variant === "reasoning" && !showReasoning) {
|
|
4809
4832
|
return;
|
|
4810
4833
|
}
|
|
4811
|
-
|
|
4834
|
+
// Summary mode owns the visible row from the first tool onward. Waiting
|
|
4835
|
+
// for a second tool would replace the standalone tool bubble with a new
|
|
4836
|
+
// group row, restarting custom activity animations and transiently
|
|
4837
|
+
// dropping interactive DOM state during the handoff.
|
|
4838
|
+
if (currentGroup.length > 1 || (summaryOnly && currentGroup.length > 0)) {
|
|
4812
4839
|
toolGroups.push(currentGroup);
|
|
4813
4840
|
}
|
|
4814
4841
|
currentGroup = [];
|
|
4815
4842
|
});
|
|
4816
|
-
if (currentGroup.length > 1) {
|
|
4843
|
+
if (currentGroup.length > 1 || (summaryOnly && currentGroup.length > 0)) {
|
|
4817
4844
|
toolGroups.push(currentGroup);
|
|
4818
4845
|
}
|
|
4819
4846
|
|
|
4820
|
-
toolGroups.forEach((group
|
|
4847
|
+
toolGroups.forEach((group) => {
|
|
4821
4848
|
const wrappers = group
|
|
4822
4849
|
.map((groupMessage) =>
|
|
4823
4850
|
Array.from(tempContainer.children).find(
|
|
@@ -4828,14 +4855,15 @@ export const createAgentExperience = (
|
|
|
4828
4855
|
)
|
|
4829
4856
|
.filter((wrapper): wrapper is HTMLElement => Boolean(wrapper));
|
|
4830
4857
|
|
|
4831
|
-
if (wrappers.length < 2) {
|
|
4858
|
+
if (wrappers.length < (summaryOnly ? 1 : 2)) {
|
|
4832
4859
|
return;
|
|
4833
4860
|
}
|
|
4834
4861
|
|
|
4835
|
-
const groupWrapper =
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4862
|
+
const groupWrapper = createMessageRow(
|
|
4863
|
+
`tool-group-${group[0].id}`,
|
|
4864
|
+
"assistant"
|
|
4865
|
+
);
|
|
4866
|
+
groupWrapper.setAttribute("data-persona-tool-group-row", "true");
|
|
4839
4867
|
|
|
4840
4868
|
const groupContainer = document.createElement("div");
|
|
4841
4869
|
groupContainer.className =
|
|
@@ -4845,8 +4873,9 @@ export const createAgentExperience = (
|
|
|
4845
4873
|
const summary = document.createElement("div");
|
|
4846
4874
|
summary.className =
|
|
4847
4875
|
"persona-tool-group-summary persona-text-xs persona-text-persona-muted";
|
|
4876
|
+
summary.id = `tool-group-summary-${group[0].id}`;
|
|
4848
4877
|
|
|
4849
|
-
const defaultSummary = `Called ${group.length} tools`;
|
|
4878
|
+
const defaultSummary = `Called ${group.length} ${group.length === 1 ? "tool" : "tools"}`;
|
|
4850
4879
|
const renderedSummary = config.toolCall?.renderGroupedSummary?.({
|
|
4851
4880
|
messages: group,
|
|
4852
4881
|
toolCalls: group
|
|
@@ -4861,8 +4890,9 @@ export const createAgentExperience = (
|
|
|
4861
4890
|
|
|
4862
4891
|
const stack = document.createElement("div");
|
|
4863
4892
|
stack.className = "persona-tool-group-stack persona-flex persona-flex-col";
|
|
4893
|
+
stack.id = `tool-group-stack-${group[0].id}`;
|
|
4894
|
+
stack.setAttribute("data-persona-tool-group-stack", "true");
|
|
4864
4895
|
|
|
4865
|
-
const summaryOnly = config.features?.toolCallDisplay?.groupedMode === "summary";
|
|
4866
4896
|
groupContainer.appendChild(summary);
|
|
4867
4897
|
if (!summaryOnly) {
|
|
4868
4898
|
groupContainer.appendChild(stack);
|
|
@@ -4875,6 +4905,10 @@ export const createAgentExperience = (
|
|
|
4875
4905
|
wrapper.remove();
|
|
4876
4906
|
return;
|
|
4877
4907
|
}
|
|
4908
|
+
// The outer group row owns role sizing. Inner message rows fill that
|
|
4909
|
+
// resolved track instead of applying the role max-width a second
|
|
4910
|
+
// time (which would turn an 85% cap into 72.25%).
|
|
4911
|
+
wrapper.style.setProperty("--persona-message-row-max-width", "100%");
|
|
4878
4912
|
const item = document.createElement("div");
|
|
4879
4913
|
item.className = "persona-tool-group-item persona-relative";
|
|
4880
4914
|
item.setAttribute("data-persona-tool-group-item", "true");
|
|
@@ -4938,7 +4972,6 @@ export const createAgentExperience = (
|
|
|
4938
4972
|
const showBubble = config.loadingIndicator?.showBubble !== false; // default true
|
|
4939
4973
|
typingBubble.className = showBubble
|
|
4940
4974
|
? [
|
|
4941
|
-
"persona-max-w-[85%]",
|
|
4942
4975
|
"persona-rounded-2xl",
|
|
4943
4976
|
"persona-text-sm",
|
|
4944
4977
|
"persona-leading-relaxed",
|
|
@@ -4950,7 +4983,6 @@ export const createAgentExperience = (
|
|
|
4950
4983
|
"persona-py-3"
|
|
4951
4984
|
].join(" ")
|
|
4952
4985
|
: [
|
|
4953
|
-
"persona-max-w-[85%]",
|
|
4954
4986
|
"persona-text-sm",
|
|
4955
4987
|
"persona-leading-relaxed",
|
|
4956
4988
|
"persona-text-persona-primary"
|
|
@@ -4960,11 +4992,10 @@ export const createAgentExperience = (
|
|
|
4960
4992
|
|
|
4961
4993
|
typingBubble.appendChild(typingIndicator);
|
|
4962
4994
|
|
|
4963
|
-
const typingWrapper =
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
typingWrapper.setAttribute("data-wrapper-id", "typing-indicator");
|
|
4995
|
+
const typingWrapper = createMessageRow(
|
|
4996
|
+
"typing-indicator",
|
|
4997
|
+
"assistant"
|
|
4998
|
+
);
|
|
4968
4999
|
typingWrapper.appendChild(typingBubble);
|
|
4969
5000
|
tempContainer.appendChild(typingWrapper);
|
|
4970
5001
|
}
|
|
@@ -5002,7 +5033,6 @@ export const createAgentExperience = (
|
|
|
5002
5033
|
const showBubble = config.loadingIndicator?.showBubble !== false; // default true
|
|
5003
5034
|
idleBubble.className = showBubble
|
|
5004
5035
|
? [
|
|
5005
|
-
"persona-max-w-[85%]",
|
|
5006
5036
|
"persona-rounded-2xl",
|
|
5007
5037
|
"persona-text-sm",
|
|
5008
5038
|
"persona-leading-relaxed",
|
|
@@ -5015,7 +5045,6 @@ export const createAgentExperience = (
|
|
|
5015
5045
|
"persona-py-3"
|
|
5016
5046
|
].join(" ")
|
|
5017
5047
|
: [
|
|
5018
|
-
"persona-max-w-[85%]",
|
|
5019
5048
|
"persona-text-sm",
|
|
5020
5049
|
"persona-leading-relaxed",
|
|
5021
5050
|
"persona-text-persona-primary"
|
|
@@ -5024,27 +5053,35 @@ export const createAgentExperience = (
|
|
|
5024
5053
|
|
|
5025
5054
|
idleBubble.appendChild(idleIndicator);
|
|
5026
5055
|
|
|
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");
|
|
5056
|
+
const idleWrapper = createMessageRow("idle-indicator", "assistant");
|
|
5032
5057
|
idleWrapper.appendChild(idleBubble);
|
|
5033
5058
|
tempContainer.appendChild(idleWrapper);
|
|
5034
5059
|
}
|
|
5035
5060
|
}
|
|
5036
5061
|
|
|
5062
|
+
// Wrap wide tables in a horizontal-scroll container before morphing so the
|
|
5063
|
+
// wrapper exists on both sides of the diff (survives streaming re-renders).
|
|
5064
|
+
wrapScrollableTables(tempContainer);
|
|
5065
|
+
|
|
5037
5066
|
// Use idiomorph to morph the container contents
|
|
5038
5067
|
morphMessages(container, tempContainer);
|
|
5039
5068
|
|
|
5069
|
+
// Set initial edge-fade state on the live table wrappers and attach the
|
|
5070
|
+
// delegated scroll listener (once) that keeps the fades in sync.
|
|
5071
|
+
refreshTableScrollFades(container);
|
|
5072
|
+
|
|
5040
5073
|
// Hydrate plugin-rendered ask-question bubbles into their stub wrappers.
|
|
5041
5074
|
// Idiomorph imports new nodes via `document.importNode`, which strips
|
|
5042
5075
|
// listeners, so we built only an empty stub during morph and now inject
|
|
5043
5076
|
// the real, listener-bearing bubble directly into the live DOM.
|
|
5044
5077
|
if (askPluginHydrate.length > 0) {
|
|
5045
5078
|
for (const { messageId, fingerprint, bubble } of askPluginHydrate) {
|
|
5046
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5079
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5047
5080
|
if (!wrapper) continue;
|
|
5081
|
+
applyMessageRowLayout(
|
|
5082
|
+
wrapper,
|
|
5083
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5084
|
+
);
|
|
5048
5085
|
if (bubble === null) {
|
|
5049
5086
|
// No fresh bubble built this pass: either the plugin opted out
|
|
5050
5087
|
// and a previously-mounted bubble already lives here (preserved by
|
|
@@ -5070,8 +5107,12 @@ export const createAgentExperience = (
|
|
|
5070
5107
|
// the ask-question hydration above.
|
|
5071
5108
|
if (componentDirectiveHydrate.length > 0) {
|
|
5072
5109
|
for (const { messageId, fingerprint, bubble } of componentDirectiveHydrate) {
|
|
5073
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5110
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5074
5111
|
if (!wrapper) continue;
|
|
5112
|
+
applyMessageRowLayout(
|
|
5113
|
+
wrapper,
|
|
5114
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5115
|
+
);
|
|
5075
5116
|
if (bubble === null) {
|
|
5076
5117
|
// Fingerprint matched the previous pass: the live wrapper (kept
|
|
5077
5118
|
// alive by `data-preserve-runtime`) still holds the listener-bearing
|
|
@@ -5094,8 +5135,12 @@ export const createAgentExperience = (
|
|
|
5094
5135
|
// mirroring the ask-question / component-directive hydration above.
|
|
5095
5136
|
if (approvalPluginHydrate.length > 0) {
|
|
5096
5137
|
for (const { messageId, fingerprint, bubble } of approvalPluginHydrate) {
|
|
5097
|
-
const wrapper = container.querySelector(`#wrapper-${messageId}`);
|
|
5138
|
+
const wrapper = container.querySelector<HTMLElement>(`#wrapper-${messageId}`);
|
|
5098
5139
|
if (!wrapper) continue;
|
|
5140
|
+
applyMessageRowLayout(
|
|
5141
|
+
wrapper,
|
|
5142
|
+
messageRolesById.get(messageId) ?? "assistant"
|
|
5143
|
+
);
|
|
5099
5144
|
if (bubble === null) {
|
|
5100
5145
|
// Fingerprint matched the previous pass (or the plugin opted out
|
|
5101
5146
|
// after a prior render): the live wrapper, kept alive by
|
|
@@ -6789,7 +6834,8 @@ export const createAgentExperience = (
|
|
|
6789
6834
|
) as HTMLButtonElement;
|
|
6790
6835
|
|
|
6791
6836
|
micButton.type = "button";
|
|
6792
|
-
|
|
6837
|
+
const tooltipText = voiceConfig?.tooltipText ?? "Start voice recognition";
|
|
6838
|
+
micButton.setAttribute("aria-label", tooltipText);
|
|
6793
6839
|
|
|
6794
6840
|
const micIconName = voiceConfig?.iconName ?? "mic";
|
|
6795
6841
|
const buttonSize = sendButtonConfig?.size ?? "40px";
|
|
@@ -6850,15 +6896,13 @@ export const createAgentExperience = (
|
|
|
6850
6896
|
}
|
|
6851
6897
|
|
|
6852
6898
|
micButtonWrapper.appendChild(micButton);
|
|
6853
|
-
|
|
6854
|
-
// Add tooltip if enabled
|
|
6855
|
-
const tooltipText = voiceConfig?.tooltipText ?? "Start voice recognition";
|
|
6856
6899
|
const showTooltip = voiceConfig?.showTooltip ?? false;
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6900
|
+
attachTooltip({
|
|
6901
|
+
anchor: micButton,
|
|
6902
|
+
trigger: micButtonWrapper,
|
|
6903
|
+
text: () => micButton.getAttribute("aria-label") ?? tooltipText,
|
|
6904
|
+
enabled: showTooltip,
|
|
6905
|
+
});
|
|
6862
6906
|
|
|
6863
6907
|
return { micButton, micButtonWrapper };
|
|
6864
6908
|
};
|
|
@@ -7373,6 +7417,9 @@ export const createAgentExperience = (
|
|
|
7373
7417
|
if (typeof ResizeObserver !== "undefined") {
|
|
7374
7418
|
const contentResizeObserver = new ResizeObserver(() => {
|
|
7375
7419
|
handleContentResize();
|
|
7420
|
+
// A resize can start or stop a table overflowing without a render or a
|
|
7421
|
+
// scroll, which are the only other things that recompute the fades.
|
|
7422
|
+
refreshTableScrollFades(messagesWrapper);
|
|
7376
7423
|
});
|
|
7377
7424
|
contentResizeObserver.observe(messagesWrapper);
|
|
7378
7425
|
contentResizeObserver.observe(body);
|
|
@@ -8198,76 +8245,13 @@ export const createAgentExperience = (
|
|
|
8198
8245
|
closeButton.setAttribute("aria-label", closeButtonTooltipText);
|
|
8199
8246
|
|
|
8200
8247
|
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
|
-
}
|
|
8248
|
+
attachTooltip({
|
|
8249
|
+
anchor: closeButton,
|
|
8250
|
+
trigger: closeButtonWrapper,
|
|
8251
|
+
text: () =>
|
|
8252
|
+
closeButton?.getAttribute("aria-label") ?? closeButtonTooltipText,
|
|
8253
|
+
enabled: closeButtonShowTooltip,
|
|
8254
|
+
});
|
|
8271
8255
|
}
|
|
8272
8256
|
}
|
|
8273
8257
|
|
|
@@ -8433,76 +8417,14 @@ export const createAgentExperience = (
|
|
|
8433
8417
|
clearChatButton.setAttribute("aria-label", clearChatTooltipText);
|
|
8434
8418
|
|
|
8435
8419
|
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
|
-
}
|
|
8420
|
+
attachTooltip({
|
|
8421
|
+
anchor: clearChatButton,
|
|
8422
|
+
trigger: clearChatButtonWrapper,
|
|
8423
|
+
text: () =>
|
|
8424
|
+
clearChatButton?.getAttribute("aria-label") ??
|
|
8425
|
+
clearChatTooltipText,
|
|
8426
|
+
enabled: clearChatShowTooltip,
|
|
8427
|
+
});
|
|
8506
8428
|
}
|
|
8507
8429
|
}
|
|
8508
8430
|
}
|
|
@@ -8641,24 +8563,16 @@ export const createAgentExperience = (
|
|
|
8641
8563
|
micButton.style.paddingBottom = "";
|
|
8642
8564
|
}
|
|
8643
8565
|
|
|
8644
|
-
// Update tooltip
|
|
8645
|
-
const tooltip = micButtonWrapper?.querySelector(".persona-send-button-tooltip") as HTMLElement | null;
|
|
8646
8566
|
const tooltipText = voiceConfig.tooltipText ?? "Start voice recognition";
|
|
8647
8567
|
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";
|
|
8568
|
+
micButton.setAttribute("aria-label", tooltipText);
|
|
8569
|
+
if (micButtonWrapper) {
|
|
8570
|
+
attachTooltip({
|
|
8571
|
+
anchor: micButton,
|
|
8572
|
+
trigger: micButtonWrapper,
|
|
8573
|
+
text: () => micButton?.getAttribute("aria-label") ?? tooltipText,
|
|
8574
|
+
enabled: showTooltip,
|
|
8575
|
+
});
|
|
8662
8576
|
}
|
|
8663
8577
|
|
|
8664
8578
|
// Show and update disabled state
|
|
@@ -8750,11 +8664,13 @@ export const createAgentExperience = (
|
|
|
8750
8664
|
|
|
8751
8665
|
attachmentButtonWrapper.appendChild(attachmentButton);
|
|
8752
8666
|
|
|
8753
|
-
// Add tooltip
|
|
8754
8667
|
const attachTooltipText = attachmentsConfig.buttonTooltipText ?? "Attach file";
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8668
|
+
attachTooltip({
|
|
8669
|
+
anchor: attachmentButton,
|
|
8670
|
+
trigger: attachmentButtonWrapper,
|
|
8671
|
+
text: () =>
|
|
8672
|
+
attachmentButton?.getAttribute("aria-label") ?? attachTooltipText,
|
|
8673
|
+
});
|
|
8758
8674
|
|
|
8759
8675
|
// Insert into left actions container (fall back to the form when a
|
|
8760
8676
|
// custom composer has no left cluster).
|
|
@@ -8816,10 +8732,14 @@ export const createAgentExperience = (
|
|
|
8816
8732
|
);
|
|
8817
8733
|
if (iconSvg) attachmentButton.appendChild(iconSvg);
|
|
8818
8734
|
else attachmentButton.textContent = "📎";
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8735
|
+
if (attachmentButtonWrapper) {
|
|
8736
|
+
attachTooltip({
|
|
8737
|
+
anchor: attachmentButton,
|
|
8738
|
+
trigger: attachmentButtonWrapper,
|
|
8739
|
+
text: () =>
|
|
8740
|
+
attachmentButton?.getAttribute("aria-label") ?? tooltipText,
|
|
8741
|
+
});
|
|
8742
|
+
}
|
|
8823
8743
|
}
|
|
8824
8744
|
} else {
|
|
8825
8745
|
// Hide attachment button if disabled
|
|
@@ -8948,21 +8868,16 @@ export const createAgentExperience = (
|
|
|
8948
8868
|
sendButton.style.paddingBottom = "";
|
|
8949
8869
|
}
|
|
8950
8870
|
|
|
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";
|
|
8871
|
+
if (!session.isStreaming()) {
|
|
8872
|
+
sendButton.setAttribute("aria-label", tooltipText);
|
|
8873
|
+
}
|
|
8874
|
+
if (sendButtonWrapper) {
|
|
8875
|
+
attachTooltip({
|
|
8876
|
+
anchor: sendButton,
|
|
8877
|
+
trigger: sendButtonWrapper,
|
|
8878
|
+
text: () => sendButton.getAttribute("aria-label") ?? tooltipText,
|
|
8879
|
+
enabled: showTooltip,
|
|
8880
|
+
});
|
|
8966
8881
|
}
|
|
8967
8882
|
|
|
8968
8883
|
// Update contentMaxWidth on messages wrapper and composer. Same
|