@runtypelabs/persona 2.3.1 → 3.0.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/README.md +221 -4
- package/dist/index.cjs +42 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +832 -571
- package/dist/index.d.ts +832 -571
- package/dist/index.global.js +87 -87
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +42 -42
- package/dist/index.js.map +1 -1
- package/dist/widget.css +205 -15
- package/package.json +2 -2
- package/src/components/artifact-card.ts +39 -5
- package/src/components/artifact-pane.ts +67 -126
- package/src/components/composer-builder.ts +3 -23
- package/src/components/header-builder.ts +29 -34
- package/src/components/header-layouts.ts +109 -41
- package/src/components/launcher.ts +10 -7
- package/src/components/message-bubble.ts +7 -11
- package/src/components/panel.ts +4 -4
- package/src/defaults.ts +22 -93
- package/src/index.ts +20 -7
- package/src/presets.ts +66 -51
- package/src/runtime/host-layout.test.ts +196 -0
- package/src/runtime/host-layout.ts +265 -27
- package/src/runtime/init.test.ts +77 -7
- package/src/styles/widget.css +205 -15
- package/src/types/theme.ts +76 -0
- package/src/types.ts +86 -97
- package/src/ui.docked.test.ts +203 -7
- package/src/ui.ts +119 -88
- package/src/utils/buttons.ts +417 -0
- package/src/utils/code-generators.test.ts +43 -7
- package/src/utils/code-generators.ts +9 -25
- package/src/utils/deep-merge.ts +26 -0
- package/src/utils/dock.ts +18 -5
- package/src/utils/dropdown.ts +178 -0
- package/src/utils/theme.test.ts +90 -15
- package/src/utils/theme.ts +20 -46
- package/src/utils/tokens.ts +108 -11
- package/src/utils/migration.ts +0 -220
package/src/ui.docked.test.ts
CHANGED
|
@@ -10,7 +10,7 @@ describe("createAgentExperience docked mode", () => {
|
|
|
10
10
|
document.body.innerHTML = "";
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
it("
|
|
13
|
+
it("toggles docked panel open/closed; built-in launcher stays hidden (open via controller.open)", () => {
|
|
14
14
|
const mount = document.createElement("div");
|
|
15
15
|
document.body.appendChild(mount);
|
|
16
16
|
|
|
@@ -22,7 +22,6 @@ describe("createAgentExperience docked mode", () => {
|
|
|
22
22
|
dock: {
|
|
23
23
|
side: "right",
|
|
24
24
|
width: "420px",
|
|
25
|
-
collapsedWidth: "72px",
|
|
26
25
|
},
|
|
27
26
|
},
|
|
28
27
|
});
|
|
@@ -40,7 +39,7 @@ describe("createAgentExperience docked mode", () => {
|
|
|
40
39
|
controller.close();
|
|
41
40
|
|
|
42
41
|
expect(wrapper?.style.display).toBe("none");
|
|
43
|
-
expect(launcherButton?.style.display).toBe("");
|
|
42
|
+
expect(launcherButton?.style.display).toBe("none");
|
|
44
43
|
|
|
45
44
|
controller.open();
|
|
46
45
|
|
|
@@ -50,7 +49,141 @@ describe("createAgentExperience docked mode", () => {
|
|
|
50
49
|
controller.destroy();
|
|
51
50
|
});
|
|
52
51
|
|
|
53
|
-
it("
|
|
52
|
+
it("keeps docked panel hidden when closed under mobile fullscreen breakpoint", () => {
|
|
53
|
+
const prevWidth = window.innerWidth;
|
|
54
|
+
try {
|
|
55
|
+
Object.defineProperty(window, "innerWidth", {
|
|
56
|
+
configurable: true,
|
|
57
|
+
value: 480,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const mount = document.createElement("div");
|
|
61
|
+
document.body.appendChild(mount);
|
|
62
|
+
|
|
63
|
+
const controller = createAgentExperience(mount, {
|
|
64
|
+
apiUrl: "https://api.example.com/chat",
|
|
65
|
+
launcher: {
|
|
66
|
+
mountMode: "docked",
|
|
67
|
+
autoExpand: false,
|
|
68
|
+
dock: {
|
|
69
|
+
side: "right",
|
|
70
|
+
width: "420px",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const wrapper = mount.firstElementChild as HTMLElement | null;
|
|
76
|
+
expect(wrapper?.style.display).toBe("none");
|
|
77
|
+
expect(getComputedStyle(wrapper!).display).toBe("none");
|
|
78
|
+
|
|
79
|
+
controller.open();
|
|
80
|
+
expect(wrapper?.style.display).toBe("flex");
|
|
81
|
+
|
|
82
|
+
controller.destroy();
|
|
83
|
+
} finally {
|
|
84
|
+
Object.defineProperty(window, "innerWidth", {
|
|
85
|
+
configurable: true,
|
|
86
|
+
writable: true,
|
|
87
|
+
value: prevWidth,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("keeps docked panel hidden after resize into mobile when closed", () => {
|
|
93
|
+
const prevWidth = window.innerWidth;
|
|
94
|
+
try {
|
|
95
|
+
Object.defineProperty(window, "innerWidth", {
|
|
96
|
+
configurable: true,
|
|
97
|
+
value: 900,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const mount = document.createElement("div");
|
|
101
|
+
document.body.appendChild(mount);
|
|
102
|
+
|
|
103
|
+
const controller = createAgentExperience(mount, {
|
|
104
|
+
apiUrl: "https://api.example.com/chat",
|
|
105
|
+
launcher: {
|
|
106
|
+
mountMode: "docked",
|
|
107
|
+
autoExpand: false,
|
|
108
|
+
dock: {
|
|
109
|
+
side: "right",
|
|
110
|
+
width: "420px",
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const wrapper = mount.firstElementChild as HTMLElement | null;
|
|
116
|
+
expect(wrapper?.style.display).toBe("none");
|
|
117
|
+
|
|
118
|
+
Object.defineProperty(window, "innerWidth", {
|
|
119
|
+
configurable: true,
|
|
120
|
+
value: 480,
|
|
121
|
+
});
|
|
122
|
+
window.dispatchEvent(new Event("resize"));
|
|
123
|
+
|
|
124
|
+
expect(wrapper?.style.display).toBe("none");
|
|
125
|
+
|
|
126
|
+
controller.destroy();
|
|
127
|
+
} finally {
|
|
128
|
+
Object.defineProperty(window, "innerWidth", {
|
|
129
|
+
configurable: true,
|
|
130
|
+
writable: true,
|
|
131
|
+
value: prevWidth,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("collapses the dock width to 0 when the header close button is clicked", () => {
|
|
137
|
+
const target = document.createElement("div");
|
|
138
|
+
document.body.appendChild(target);
|
|
139
|
+
|
|
140
|
+
const hostLayout = createWidgetHostLayout(target, {
|
|
141
|
+
launcher: {
|
|
142
|
+
mountMode: "docked",
|
|
143
|
+
autoExpand: true,
|
|
144
|
+
dock: {
|
|
145
|
+
side: "right",
|
|
146
|
+
width: "420px",
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
const mount = document.createElement("div");
|
|
151
|
+
hostLayout.host.appendChild(mount);
|
|
152
|
+
|
|
153
|
+
const controller = createAgentExperience(mount, {
|
|
154
|
+
apiUrl: "https://api.example.com/chat",
|
|
155
|
+
launcher: {
|
|
156
|
+
mountMode: "docked",
|
|
157
|
+
autoExpand: true,
|
|
158
|
+
dock: {
|
|
159
|
+
side: "right",
|
|
160
|
+
width: "420px",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const syncDockState = () => hostLayout.syncWidgetState(controller.getState());
|
|
166
|
+
const openUnsub = controller.on("widget:opened", syncDockState);
|
|
167
|
+
const closeUnsub = controller.on("widget:closed", syncDockState);
|
|
168
|
+
syncDockState();
|
|
169
|
+
|
|
170
|
+
const dockSlot = hostLayout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
|
|
171
|
+
const closeButton = mount.querySelector<HTMLButtonElement>('[aria-label="Close chat"]');
|
|
172
|
+
|
|
173
|
+
expect(dockSlot?.style.width).toBe("420px");
|
|
174
|
+
expect(closeButton).not.toBeNull();
|
|
175
|
+
|
|
176
|
+
closeButton!.click();
|
|
177
|
+
|
|
178
|
+
expect(dockSlot?.style.width).toBe("0px");
|
|
179
|
+
|
|
180
|
+
openUnsub();
|
|
181
|
+
closeUnsub();
|
|
182
|
+
controller.destroy();
|
|
183
|
+
hostLayout.destroy();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("overlay reveal keeps width when closed; host-layout uses transform", () => {
|
|
54
187
|
const target = document.createElement("div");
|
|
55
188
|
document.body.appendChild(target);
|
|
56
189
|
|
|
@@ -61,7 +194,7 @@ describe("createAgentExperience docked mode", () => {
|
|
|
61
194
|
dock: {
|
|
62
195
|
side: "right",
|
|
63
196
|
width: "420px",
|
|
64
|
-
|
|
197
|
+
reveal: "overlay",
|
|
65
198
|
},
|
|
66
199
|
},
|
|
67
200
|
});
|
|
@@ -76,7 +209,7 @@ describe("createAgentExperience docked mode", () => {
|
|
|
76
209
|
dock: {
|
|
77
210
|
side: "right",
|
|
78
211
|
width: "420px",
|
|
79
|
-
|
|
212
|
+
reveal: "overlay",
|
|
80
213
|
},
|
|
81
214
|
},
|
|
82
215
|
});
|
|
@@ -94,7 +227,70 @@ describe("createAgentExperience docked mode", () => {
|
|
|
94
227
|
|
|
95
228
|
closeButton!.click();
|
|
96
229
|
|
|
97
|
-
expect(dockSlot?.style.width).toBe("
|
|
230
|
+
expect(dockSlot?.style.width).toBe("420px");
|
|
231
|
+
expect(dockSlot?.style.transform).toBe("translateX(100%)");
|
|
232
|
+
|
|
233
|
+
openUnsub();
|
|
234
|
+
closeUnsub();
|
|
235
|
+
controller.destroy();
|
|
236
|
+
hostLayout.destroy();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("push reveal translates the push-track when the header close button is clicked", () => {
|
|
240
|
+
const target = document.createElement("div");
|
|
241
|
+
document.body.appendChild(target);
|
|
242
|
+
|
|
243
|
+
const hostLayout = createWidgetHostLayout(target, {
|
|
244
|
+
launcher: {
|
|
245
|
+
mountMode: "docked",
|
|
246
|
+
autoExpand: true,
|
|
247
|
+
dock: {
|
|
248
|
+
side: "right",
|
|
249
|
+
width: "420px",
|
|
250
|
+
reveal: "push",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
const shell = hostLayout.shell!;
|
|
255
|
+
Object.defineProperty(shell, "clientWidth", { get: () => 1000, configurable: true });
|
|
256
|
+
hostLayout.updateConfig({
|
|
257
|
+
launcher: {
|
|
258
|
+
mountMode: "docked",
|
|
259
|
+
autoExpand: true,
|
|
260
|
+
dock: { side: "right", width: "420px", reveal: "push" },
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const mount = document.createElement("div");
|
|
265
|
+
hostLayout.host.appendChild(mount);
|
|
266
|
+
|
|
267
|
+
const controller = createAgentExperience(mount, {
|
|
268
|
+
apiUrl: "https://api.example.com/chat",
|
|
269
|
+
launcher: {
|
|
270
|
+
mountMode: "docked",
|
|
271
|
+
autoExpand: true,
|
|
272
|
+
dock: {
|
|
273
|
+
side: "right",
|
|
274
|
+
width: "420px",
|
|
275
|
+
reveal: "push",
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const syncDockState = () => hostLayout.syncWidgetState(controller.getState());
|
|
281
|
+
const openUnsub = controller.on("widget:opened", syncDockState);
|
|
282
|
+
const closeUnsub = controller.on("widget:closed", syncDockState);
|
|
283
|
+
syncDockState();
|
|
284
|
+
|
|
285
|
+
const pushTrack = shell.querySelector<HTMLElement>('[data-persona-dock-role="push-track"]');
|
|
286
|
+
const closeButton = mount.querySelector<HTMLButtonElement>('[aria-label="Close chat"]');
|
|
287
|
+
|
|
288
|
+
expect(pushTrack).not.toBeNull();
|
|
289
|
+
expect(pushTrack?.style.transform).toBe("translateX(-420px)");
|
|
290
|
+
|
|
291
|
+
closeButton!.click();
|
|
292
|
+
|
|
293
|
+
expect(pushTrack?.style.transform).toBe("translateX(0)");
|
|
98
294
|
|
|
99
295
|
openUnsub();
|
|
100
296
|
closeUnsub();
|
package/src/ui.ts
CHANGED
|
@@ -27,15 +27,17 @@ import {
|
|
|
27
27
|
} from "./types";
|
|
28
28
|
import { AttachmentManager } from "./utils/attachment-manager";
|
|
29
29
|
import { createTextPart, ALL_SUPPORTED_MIME_TYPES } from "./utils/content";
|
|
30
|
-
import { applyThemeVariables, createThemeObserver } from "./utils/theme";
|
|
30
|
+
import { applyThemeVariables, createThemeObserver, getActiveTheme } from "./utils/theme";
|
|
31
|
+
import { resolveTokenValue } from "./utils/tokens";
|
|
31
32
|
import { renderLucideIcon } from "./utils/icons";
|
|
32
33
|
import { createElement, createElementInDocument } from "./utils/dom";
|
|
33
34
|
import { morphMessages } from "./utils/morph";
|
|
34
35
|
import { computeMessageFingerprint, createMessageCache, getCachedWrapper, setCachedWrapper, pruneCache } from "./utils/message-fingerprint";
|
|
35
36
|
import { statusCopy } from "./utils/constants";
|
|
36
|
-
import { isDockedMountMode } from "./utils/dock";
|
|
37
|
+
import { isDockedMountMode, resolveDockConfig } from "./utils/dock";
|
|
37
38
|
import { createLauncherButton } from "./components/launcher";
|
|
38
39
|
import { createWrapper, buildPanel, buildHeader, buildComposer, attachHeaderToContainer } from "./components/panel";
|
|
40
|
+
import { HEADER_THEME_CSS } from "./components/header-builder";
|
|
39
41
|
import { buildHeaderWithLayout } from "./components/header-layouts";
|
|
40
42
|
import { positionMap } from "./utils/positioning";
|
|
41
43
|
import type { HeaderElements as _HeaderElements, ComposerElements as _ComposerElements } from "./components/panel";
|
|
@@ -1402,6 +1404,7 @@ export const createAgentExperience = (
|
|
|
1402
1404
|
if (!launcherEnabled || !artifactPaneApi) return;
|
|
1403
1405
|
const sidebarMode = config.launcher?.sidebarMode ?? false;
|
|
1404
1406
|
if (sidebarMode) return;
|
|
1407
|
+
if (isDockedMountMode(config) && resolveDockConfig(config).reveal === "emerge") return;
|
|
1405
1408
|
const ownerWindow = mount.ownerDocument.defaultView ?? window;
|
|
1406
1409
|
const mobileFullscreen = config.launcher?.mobileFullscreen ?? true;
|
|
1407
1410
|
const mobileBreakpoint = config.launcher?.mobileBreakpoint ?? 640;
|
|
@@ -1442,7 +1445,12 @@ export const createAgentExperience = (
|
|
|
1442
1445
|
const fullHeight = dockedMode || sidebarMode || (config.launcher?.fullHeight ?? false);
|
|
1443
1446
|
/** Script-tag / div embed: launcher off, host supplies a sized mount. */
|
|
1444
1447
|
const isInlineEmbed = config.launcher?.enabled === false;
|
|
1445
|
-
const
|
|
1448
|
+
const panelPartial = config.theme?.components?.panel;
|
|
1449
|
+
const activeTheme = getActiveTheme(config);
|
|
1450
|
+
const resolvePanelChrome = (raw: string | undefined, fallback: string): string => {
|
|
1451
|
+
if (raw == null || raw === "") return fallback;
|
|
1452
|
+
return resolveTokenValue(activeTheme, raw) ?? raw;
|
|
1453
|
+
};
|
|
1446
1454
|
|
|
1447
1455
|
// Mobile fullscreen detection
|
|
1448
1456
|
// Use mount's ownerDocument window to get correct viewport width when widget is inside an iframe
|
|
@@ -1457,20 +1465,25 @@ export const createAgentExperience = (
|
|
|
1457
1465
|
const isLeftSidebar = position === 'bottom-left' || position === 'top-left';
|
|
1458
1466
|
|
|
1459
1467
|
// Default values based on mode
|
|
1460
|
-
|
|
1461
|
-
|
|
1468
|
+
let defaultPanelBorder = (sidebarMode || shouldGoFullscreen) ? 'none' : '1px solid var(--persona-border)';
|
|
1469
|
+
let defaultPanelShadow = shouldGoFullscreen
|
|
1462
1470
|
? 'none'
|
|
1463
1471
|
: sidebarMode
|
|
1464
1472
|
? (isLeftSidebar ? 'var(--persona-palette-shadows-sidebar-left, 2px 0 12px rgba(0, 0, 0, 0.08))' : 'var(--persona-palette-shadows-sidebar-right, -2px 0 12px rgba(0, 0, 0, 0.08))')
|
|
1465
1473
|
: 'var(--persona-palette-shadows-xl, 0 25px 50px -12px rgba(0, 0, 0, 0.25))';
|
|
1474
|
+
|
|
1475
|
+
if (dockedMode && !shouldGoFullscreen) {
|
|
1476
|
+
defaultPanelShadow = 'none';
|
|
1477
|
+
defaultPanelBorder = 'none';
|
|
1478
|
+
}
|
|
1466
1479
|
const defaultPanelBorderRadius = (sidebarMode || shouldGoFullscreen)
|
|
1467
1480
|
? '0'
|
|
1468
1481
|
: 'var(--persona-panel-radius, var(--persona-radius-xl, 0.75rem))';
|
|
1469
1482
|
|
|
1470
|
-
// Apply theme overrides or defaults
|
|
1471
|
-
const panelBorder =
|
|
1472
|
-
const panelShadow =
|
|
1473
|
-
const panelBorderRadius =
|
|
1483
|
+
// Apply theme overrides or defaults (components.panel.*)
|
|
1484
|
+
const panelBorder = resolvePanelChrome(panelPartial?.border, defaultPanelBorder);
|
|
1485
|
+
const panelShadow = resolvePanelChrome(panelPartial?.shadow, defaultPanelShadow);
|
|
1486
|
+
const panelBorderRadius = resolvePanelChrome(panelPartial?.borderRadius, defaultPanelBorderRadius);
|
|
1474
1487
|
|
|
1475
1488
|
// Reset all inline styles first to handle mode toggling
|
|
1476
1489
|
// This ensures styles don't persist when switching between modes
|
|
@@ -1557,8 +1570,15 @@ export const createAgentExperience = (
|
|
|
1557
1570
|
panel.style.maxWidth = width;
|
|
1558
1571
|
}
|
|
1559
1572
|
} else if (dockedMode) {
|
|
1560
|
-
|
|
1561
|
-
|
|
1573
|
+
const dockReveal = resolveDockConfig(config).reveal;
|
|
1574
|
+
if (dockReveal === "emerge") {
|
|
1575
|
+
const dw = resolveDockConfig(config).width;
|
|
1576
|
+
panel.style.width = dw;
|
|
1577
|
+
panel.style.maxWidth = dw;
|
|
1578
|
+
} else {
|
|
1579
|
+
panel.style.width = "100%";
|
|
1580
|
+
panel.style.maxWidth = "100%";
|
|
1581
|
+
}
|
|
1562
1582
|
}
|
|
1563
1583
|
applyLauncherArtifactPanelWidth();
|
|
1564
1584
|
|
|
@@ -1571,6 +1591,16 @@ export const createAgentExperience = (
|
|
|
1571
1591
|
container.style.border = panelBorder;
|
|
1572
1592
|
container.style.borderRadius = panelBorderRadius;
|
|
1573
1593
|
|
|
1594
|
+
if (dockedMode && !shouldGoFullscreen && panelPartial?.border === undefined) {
|
|
1595
|
+
container.style.border = 'none';
|
|
1596
|
+
const dockSide = resolveDockConfig(config).side;
|
|
1597
|
+
if (dockSide === 'right') {
|
|
1598
|
+
container.style.borderLeft = '1px solid var(--persona-border)';
|
|
1599
|
+
} else {
|
|
1600
|
+
container.style.borderRight = '1px solid var(--persona-border)';
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1574
1604
|
if (fullHeight) {
|
|
1575
1605
|
// Mount container
|
|
1576
1606
|
mount.style.display = 'flex';
|
|
@@ -2432,7 +2462,19 @@ export const createAgentExperience = (
|
|
|
2432
2462
|
const updateOpenState = () => {
|
|
2433
2463
|
if (!launcherEnabled) return;
|
|
2434
2464
|
const dockedMode = isDockedMountMode(config);
|
|
2465
|
+
const ownerWindow = mount.ownerDocument.defaultView ?? window;
|
|
2466
|
+
const mobileBreakpoint = config.launcher?.mobileBreakpoint ?? 640;
|
|
2467
|
+
const mobileFullscreen = config.launcher?.mobileFullscreen ?? true;
|
|
2468
|
+
const isMobileViewport = ownerWindow.innerWidth <= mobileBreakpoint;
|
|
2469
|
+
const shouldGoFullscreen = mobileFullscreen && isMobileViewport && launcherEnabled;
|
|
2470
|
+
const dockReveal = resolveDockConfig(config).reveal;
|
|
2471
|
+
const dockRevealUsesTransform =
|
|
2472
|
+
dockedMode && (dockReveal === "overlay" || dockReveal === "push") && !shouldGoFullscreen;
|
|
2473
|
+
|
|
2435
2474
|
if (open) {
|
|
2475
|
+
// Clear any display:none !important from a closed docked state so mobile fullscreen
|
|
2476
|
+
// (display:flex !important) and dock layout can apply in recalcPanelHeight.
|
|
2477
|
+
wrapper.style.removeProperty("display");
|
|
2436
2478
|
wrapper.style.display = dockedMode ? "flex" : "";
|
|
2437
2479
|
wrapper.classList.remove("persona-pointer-events-none", "persona-opacity-0");
|
|
2438
2480
|
panel.classList.remove("persona-scale-95", "persona-opacity-0");
|
|
@@ -2445,20 +2487,29 @@ export const createAgentExperience = (
|
|
|
2445
2487
|
}
|
|
2446
2488
|
} else {
|
|
2447
2489
|
if (dockedMode) {
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2490
|
+
if (dockRevealUsesTransform) {
|
|
2491
|
+
// Slide/push reveal: keep the panel painted so host-layout `transform` can animate.
|
|
2492
|
+
wrapper.style.removeProperty("display");
|
|
2493
|
+
wrapper.style.display = "flex";
|
|
2494
|
+
wrapper.classList.remove("persona-pointer-events-none", "persona-opacity-0");
|
|
2495
|
+
panel.classList.remove("persona-scale-100", "persona-opacity-100", "persona-scale-95", "persona-opacity-0");
|
|
2496
|
+
} else {
|
|
2497
|
+
// Must beat applyFullHeightStyles() mobile shell: display:flex !important on wrapper
|
|
2498
|
+
wrapper.style.setProperty("display", "none", "important");
|
|
2499
|
+
wrapper.classList.remove("persona-pointer-events-none", "persona-opacity-0");
|
|
2500
|
+
panel.classList.remove("persona-scale-100", "persona-opacity-100", "persona-scale-95", "persona-opacity-0");
|
|
2501
|
+
}
|
|
2451
2502
|
} else {
|
|
2452
2503
|
wrapper.style.display = "";
|
|
2453
2504
|
wrapper.classList.add("persona-pointer-events-none", "persona-opacity-0");
|
|
2454
2505
|
panel.classList.remove("persona-scale-100", "persona-opacity-100");
|
|
2455
2506
|
panel.classList.add("persona-scale-95", "persona-opacity-0");
|
|
2456
2507
|
}
|
|
2457
|
-
// Show launcher
|
|
2508
|
+
// Show launcher when closed, except docked mode (0px column — use controller.open()).
|
|
2458
2509
|
if (launcherButtonInstance) {
|
|
2459
|
-
launcherButtonInstance.element.style.display = "";
|
|
2510
|
+
launcherButtonInstance.element.style.display = dockedMode ? "none" : "";
|
|
2460
2511
|
} else if (customLauncherElement) {
|
|
2461
|
-
customLauncherElement.style.display = "";
|
|
2512
|
+
customLauncherElement.style.display = dockedMode ? "none" : "";
|
|
2462
2513
|
}
|
|
2463
2514
|
}
|
|
2464
2515
|
};
|
|
@@ -2544,24 +2595,9 @@ export const createAgentExperience = (
|
|
|
2544
2595
|
sendButton.textContent = config.copy?.sendButtonLabel ?? "Send";
|
|
2545
2596
|
}
|
|
2546
2597
|
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
const getFontFamilyValue = (family: "sans-serif" | "serif" | "mono"): string => {
|
|
2552
|
-
switch (family) {
|
|
2553
|
-
case "serif":
|
|
2554
|
-
return 'Georgia, "Times New Roman", Times, serif';
|
|
2555
|
-
case "mono":
|
|
2556
|
-
return '"Courier New", Courier, "Lucida Console", Monaco, monospace';
|
|
2557
|
-
case "sans-serif":
|
|
2558
|
-
default:
|
|
2559
|
-
return '-apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif';
|
|
2560
|
-
}
|
|
2561
|
-
};
|
|
2562
|
-
|
|
2563
|
-
textarea.style.fontFamily = getFontFamilyValue(fontFamily);
|
|
2564
|
-
textarea.style.fontWeight = fontWeight;
|
|
2598
|
+
textarea.style.fontFamily =
|
|
2599
|
+
'var(--persona-input-font-family, var(--persona-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif))';
|
|
2600
|
+
textarea.style.fontWeight = "var(--persona-input-font-weight, var(--persona-font-weight, 400))";
|
|
2565
2601
|
};
|
|
2566
2602
|
|
|
2567
2603
|
// Add session ID persistence callbacks for client token mode
|
|
@@ -3383,43 +3419,49 @@ export const createAgentExperience = (
|
|
|
3383
3419
|
const isMobileViewport = ownerWindow.innerWidth <= mobileBreakpoint;
|
|
3384
3420
|
const shouldGoFullscreen = mobileFullscreen && isMobileViewport && launcherEnabled;
|
|
3385
3421
|
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
// Exiting mobile fullscreen (e.g., orientation change to landscape) — reset all styles
|
|
3393
|
-
if (wasMobileFullscreen) {
|
|
3394
|
-
wasMobileFullscreen = false;
|
|
3395
|
-
applyFullHeightStyles();
|
|
3396
|
-
applyThemeVariables(mount, config);
|
|
3397
|
-
}
|
|
3422
|
+
try {
|
|
3423
|
+
if (shouldGoFullscreen) {
|
|
3424
|
+
applyFullHeightStyles();
|
|
3425
|
+
applyThemeVariables(mount, config);
|
|
3426
|
+
return;
|
|
3427
|
+
}
|
|
3398
3428
|
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3429
|
+
// Exiting mobile fullscreen (e.g., orientation change to landscape) — reset all styles
|
|
3430
|
+
if (wasMobileFullscreen) {
|
|
3431
|
+
wasMobileFullscreen = false;
|
|
3432
|
+
applyFullHeightStyles();
|
|
3433
|
+
applyThemeVariables(mount, config);
|
|
3434
|
+
}
|
|
3404
3435
|
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
panel.style.maxWidth = width;
|
|
3411
|
-
}
|
|
3412
|
-
applyLauncherArtifactPanelWidth();
|
|
3436
|
+
if (!launcherEnabled && !dockedMode) {
|
|
3437
|
+
panel.style.height = "";
|
|
3438
|
+
panel.style.width = "";
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
3413
3441
|
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3442
|
+
// In sidebar/fullHeight mode, don't override the width - it's handled by applyFullHeightStyles
|
|
3443
|
+
if (!sidebarMode && !dockedMode) {
|
|
3444
|
+
const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;
|
|
3445
|
+
const width = launcherWidth ?? "min(400px, calc(100vw - 24px))";
|
|
3446
|
+
panel.style.width = width;
|
|
3447
|
+
panel.style.maxWidth = width;
|
|
3448
|
+
}
|
|
3449
|
+
applyLauncherArtifactPanelWidth();
|
|
3450
|
+
|
|
3451
|
+
// In fullHeight mode, don't set a fixed height
|
|
3452
|
+
if (!fullHeight) {
|
|
3453
|
+
const viewportHeight = ownerWindow.innerHeight;
|
|
3454
|
+
const verticalMargin = 64; // leave space for launcher's offset
|
|
3455
|
+
const heightOffset = config.launcher?.heightOffset ?? 0;
|
|
3456
|
+
const available = Math.max(200, viewportHeight - verticalMargin);
|
|
3457
|
+
const clamped = Math.min(640, available);
|
|
3458
|
+
const finalHeight = Math.max(200, clamped - heightOffset);
|
|
3459
|
+
panel.style.height = `${finalHeight}px`;
|
|
3460
|
+
}
|
|
3461
|
+
} finally {
|
|
3462
|
+
// applyFullHeightStyles() assigns wrapper.style.cssText (e.g. display:flex !important), which
|
|
3463
|
+
// overwrites updateOpenState()'s display:none when docked+closed. Re-sync after every recalc.
|
|
3464
|
+
updateOpenState();
|
|
3423
3465
|
}
|
|
3424
3466
|
};
|
|
3425
3467
|
|
|
@@ -3959,14 +4001,9 @@ export const createAgentExperience = (
|
|
|
3959
4001
|
}
|
|
3960
4002
|
}
|
|
3961
4003
|
|
|
3962
|
-
//
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
closeButton.classList.remove("persona-text-persona-muted");
|
|
3966
|
-
} else {
|
|
3967
|
-
closeButton.style.color = "";
|
|
3968
|
-
closeButton.classList.add("persona-text-persona-muted");
|
|
3969
|
-
}
|
|
4004
|
+
// Close icon: launcher color wins; else theme.components.header.actionIconForeground
|
|
4005
|
+
closeButton.style.color =
|
|
4006
|
+
launcher.closeButtonColor || HEADER_THEME_CSS.actionIconColor;
|
|
3970
4007
|
|
|
3971
4008
|
if (launcher.closeButtonBackgroundColor) {
|
|
3972
4009
|
closeButton.style.backgroundColor = launcher.closeButtonBackgroundColor;
|
|
@@ -4017,7 +4054,7 @@ export const createAgentExperience = (
|
|
|
4017
4054
|
|
|
4018
4055
|
// Clear existing content and render new icon
|
|
4019
4056
|
closeButton.innerHTML = "";
|
|
4020
|
-
const iconSvg = renderLucideIcon(closeButtonIconName, "20px",
|
|
4057
|
+
const iconSvg = renderLucideIcon(closeButtonIconName, "20px", "currentColor", 2);
|
|
4021
4058
|
if (iconSvg) {
|
|
4022
4059
|
closeButton.appendChild(iconSvg);
|
|
4023
4060
|
} else {
|
|
@@ -4184,22 +4221,16 @@ export const createAgentExperience = (
|
|
|
4184
4221
|
const clearChatIconName = clearChatConfig.iconName ?? "refresh-cw";
|
|
4185
4222
|
const clearChatIconColor = clearChatConfig.iconColor ?? "";
|
|
4186
4223
|
|
|
4224
|
+
clearChatButton.style.color =
|
|
4225
|
+
clearChatIconColor || HEADER_THEME_CSS.actionIconColor;
|
|
4226
|
+
|
|
4187
4227
|
// Clear existing icon and render new one
|
|
4188
4228
|
clearChatButton.innerHTML = "";
|
|
4189
|
-
const iconSvg = renderLucideIcon(clearChatIconName, "20px",
|
|
4229
|
+
const iconSvg = renderLucideIcon(clearChatIconName, "20px", "currentColor", 2);
|
|
4190
4230
|
if (iconSvg) {
|
|
4191
4231
|
clearChatButton.appendChild(iconSvg);
|
|
4192
4232
|
}
|
|
4193
4233
|
|
|
4194
|
-
// Update icon color
|
|
4195
|
-
if (clearChatIconColor) {
|
|
4196
|
-
clearChatButton.style.color = clearChatIconColor;
|
|
4197
|
-
clearChatButton.classList.remove("persona-text-persona-muted");
|
|
4198
|
-
} else {
|
|
4199
|
-
clearChatButton.style.color = "";
|
|
4200
|
-
clearChatButton.classList.add("persona-text-persona-muted");
|
|
4201
|
-
}
|
|
4202
|
-
|
|
4203
4234
|
// Update background color
|
|
4204
4235
|
if (clearChatConfig.backgroundColor) {
|
|
4205
4236
|
clearChatButton.style.backgroundColor = clearChatConfig.backgroundColor;
|