@heroui/agent 0.2.0-beta.8 → 0.2.0-beta.9
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/CHANGELOG.md +5 -0
- package/dist/chart-content-BBHQVMP4.js +1 -0
- package/dist/chunk-2XBFFSW2.js +1 -0
- package/dist/chunk-64RI74L5.js +1 -0
- package/dist/chunk-6ZJJN322.js +1 -0
- package/dist/chunk-7KCGPNEN.js +1 -0
- package/dist/chunk-BBB5EHDN.js +5 -0
- package/dist/chunk-CR3H7AVI.js +1 -0
- package/dist/chunk-HCLXS4HQ.js +1 -0
- package/dist/chunk-MXLGM4WV.js +1 -0
- package/dist/chunk-OA3TG5CS.js +1 -0
- package/dist/chunk-PZJ4NC3J.js +1 -0
- package/dist/chunk-RNNGE7KM.js +1 -0
- package/dist/chunk-S6OIAJSC.js +1 -0
- package/dist/chunk-SUDHJNTJ.js +2 -0
- package/dist/chunk-WUAFMD7Z.js +2 -0
- package/dist/component-renderer-GG6JVRUG.js +1 -0
- package/dist/composer-draft-NSKK65F3.js +1 -0
- package/dist/composer-image-draft-KAGTNQR7.js +1 -0
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +2 -2445
- package/dist/embed-runtime-GYXK7V6N.js +5 -0
- package/dist/index.js +1 -31
- package/dist/interactive-map-surface-WC2LPCR7.js +1 -0
- package/dist/internal/client-tools.js +1 -0
- package/dist/internal/models.js +1 -0
- package/dist/internal/runtime.js +1 -0
- package/dist/internal/theme.js +1 -0
- package/dist/next.js +1 -25
- package/dist/server.js +1 -136
- package/package.json +4 -4
- package/dist/chart-content-E4NPTUPR.js +0 -2696
- package/dist/chunk-3FG5NRTX.js +0 -9
- package/dist/chunk-CMZDZGUZ.js +0 -1132
- package/dist/chunk-DS4X5K2R.js +0 -56
- package/dist/chunk-FNGGMHVR.js +0 -625
- package/dist/chunk-G6GFNSG4.js +0 -284
- package/dist/chunk-Q7LGTVBL.js +0 -43
- package/dist/chunk-TOOT6SZ2.js +0 -74
- package/dist/chunk-UUPU3MYB.js +0 -42
- package/dist/chunk-VJA52U5P.js +0 -137
- package/dist/chunk-W7SCMB3O.js +0 -1285
- package/dist/component-renderer-MDDQMQTU.js +0 -10629
- package/dist/composer-draft-DTQAEO5K.js +0 -20
- package/dist/composer-image-draft-IVJ352E6.js +0 -174
- package/dist/embed-runtime-VVAGDPYY.js +0 -9422
- package/dist/interactive-map-surface-67KHT3VB.js +0 -362
package/dist/chunk-CMZDZGUZ.js
DELETED
|
@@ -1,1132 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AGENT_MODEL_OPTIONS,
|
|
3
|
-
AgentLoadingState,
|
|
4
|
-
DEFAULT_AGENT_PICKER_MODEL_ID,
|
|
5
|
-
EmbedSessionManager,
|
|
6
|
-
PanelShell,
|
|
7
|
-
ShellConversation,
|
|
8
|
-
containsComposerFocus,
|
|
9
|
-
createSubmittedAgentShellHandoff,
|
|
10
|
-
isHeroUIAgentAttachmentContentType,
|
|
11
|
-
readSidebarWidth,
|
|
12
|
-
resolveOptions,
|
|
13
|
-
setFocus,
|
|
14
|
-
writeAgentShellAttachments,
|
|
15
|
-
writeAgentShellHandoff
|
|
16
|
-
} from "./chunk-W7SCMB3O.js";
|
|
17
|
-
import {
|
|
18
|
-
renderInlineMarkdown
|
|
19
|
-
} from "./chunk-Q7LGTVBL.js";
|
|
20
|
-
|
|
21
|
-
// src/embed/provider.tsx
|
|
22
|
-
import {
|
|
23
|
-
useCallback,
|
|
24
|
-
useEffect as useEffect2,
|
|
25
|
-
useEffectEvent,
|
|
26
|
-
useLayoutEffect,
|
|
27
|
-
useMemo,
|
|
28
|
-
useRef as useRef2,
|
|
29
|
-
useState,
|
|
30
|
-
useSyncExternalStore
|
|
31
|
-
} from "react";
|
|
32
|
-
import { createRoot } from "react-dom/client";
|
|
33
|
-
|
|
34
|
-
// src/embed/launcher-spark-icon.tsx
|
|
35
|
-
import { SparklesFill } from "@gravity-ui/icons";
|
|
36
|
-
import { jsx } from "react/jsx-runtime";
|
|
37
|
-
function LauncherSparkIcon(props) {
|
|
38
|
-
return /* @__PURE__ */ jsx(SparklesFill, { ...props });
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// src/embed/launcher-icon.tsx
|
|
42
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
43
|
-
function isSvgIcon(icon) {
|
|
44
|
-
return /\.svg(?:[?#]|$)/i.test(icon) || /^data:image\/svg\+xml[;,]/i.test(icon);
|
|
45
|
-
}
|
|
46
|
-
function cssUrl(value) {
|
|
47
|
-
return `url("${value.replace(/["\\\n\r]/g, encodeURIComponent)}")`;
|
|
48
|
-
}
|
|
49
|
-
function LauncherIcon({ icon }) {
|
|
50
|
-
if (!icon) return /* @__PURE__ */ jsx2(LauncherSparkIcon, {});
|
|
51
|
-
if (isSvgIcon(icon)) {
|
|
52
|
-
return /* @__PURE__ */ jsx2(
|
|
53
|
-
"span",
|
|
54
|
-
{
|
|
55
|
-
"aria-hidden": "true",
|
|
56
|
-
className: "ha-launcher__icon ha-launcher__icon--tinted",
|
|
57
|
-
style: { WebkitMaskImage: cssUrl(icon), maskImage: cssUrl(icon) }
|
|
58
|
-
}
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
return /* @__PURE__ */ jsx2("img", { alt: "", className: "ha-launcher__icon", src: icon });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// src/embed/merge-remote-config.ts
|
|
65
|
-
function isPlainObject(value) {
|
|
66
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
67
|
-
}
|
|
68
|
-
function deepMerge(base, override) {
|
|
69
|
-
if (override === void 0) return base;
|
|
70
|
-
if (!isPlainObject(base) || !isPlainObject(override)) return override;
|
|
71
|
-
const merged = { ...base };
|
|
72
|
-
for (const [key, value] of Object.entries(override)) {
|
|
73
|
-
if (value === void 0) continue;
|
|
74
|
-
merged[key] = key in base ? deepMerge(base[key], value) : value;
|
|
75
|
-
}
|
|
76
|
-
return merged;
|
|
77
|
-
}
|
|
78
|
-
function mergeRemoteConfig(remote, props) {
|
|
79
|
-
if (!remote) return props;
|
|
80
|
-
const groups = {
|
|
81
|
-
appearance: remote.appearance,
|
|
82
|
-
capabilities: remote.capabilities,
|
|
83
|
-
composer: remote.composer,
|
|
84
|
-
markdown: remote.markdown,
|
|
85
|
-
permissions: remote.permissions,
|
|
86
|
-
responseActions: remote.responseActions,
|
|
87
|
-
startScreen: remote.startScreen
|
|
88
|
-
};
|
|
89
|
-
const merged = deepMerge(groups, {
|
|
90
|
-
appearance: props.appearance,
|
|
91
|
-
capabilities: props.capabilities,
|
|
92
|
-
composer: props.composer,
|
|
93
|
-
// Markdown renderer plugins are functions and never survive a JSON
|
|
94
|
-
// document, so they are merged back by identity in the provider.
|
|
95
|
-
markdown: props.markdown ? { ...props.markdown, plugins: void 0 } : void 0,
|
|
96
|
-
permissions: props.permissions,
|
|
97
|
-
responseActions: props.responseActions,
|
|
98
|
-
startScreen: props.startScreen
|
|
99
|
-
});
|
|
100
|
-
return { ...props, ...merged };
|
|
101
|
-
}
|
|
102
|
-
function hostSuppliesFont(props) {
|
|
103
|
-
return Boolean(props.appearance?.theme?.typography?.fontFamily?.trim());
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// src/contracts/appearance.ts
|
|
107
|
-
var HEROUI_AGENT_REMOTE_CONFIG_VERSION = 1;
|
|
108
|
-
var INVALID = Symbol("invalid");
|
|
109
|
-
function isRecord(value) {
|
|
110
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
111
|
-
}
|
|
112
|
-
function optionalString(value) {
|
|
113
|
-
if (value === void 0) return void 0;
|
|
114
|
-
return typeof value === "string" ? value : INVALID;
|
|
115
|
-
}
|
|
116
|
-
function optionalBoolean(value) {
|
|
117
|
-
if (value === void 0) return void 0;
|
|
118
|
-
return typeof value === "boolean" ? value : INVALID;
|
|
119
|
-
}
|
|
120
|
-
function optionalNumber(value) {
|
|
121
|
-
if (value === void 0) return void 0;
|
|
122
|
-
return typeof value === "number" && Number.isFinite(value) ? value : INVALID;
|
|
123
|
-
}
|
|
124
|
-
function optionalEnum(allowed) {
|
|
125
|
-
return (value) => {
|
|
126
|
-
if (value === void 0) return void 0;
|
|
127
|
-
return typeof value === "string" && allowed.includes(value) ? value : INVALID;
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function optionalThemeColor(value) {
|
|
131
|
-
if (value === void 0) return void 0;
|
|
132
|
-
if (typeof value === "string") return value;
|
|
133
|
-
if (!isRecord(value)) return INVALID;
|
|
134
|
-
const dark = optionalString(value["dark"]);
|
|
135
|
-
const light = optionalString(value["light"]);
|
|
136
|
-
if (dark === INVALID || light === INVALID) return INVALID;
|
|
137
|
-
return { ...dark === void 0 ? {} : { dark }, ...light === void 0 ? {} : { light } };
|
|
138
|
-
}
|
|
139
|
-
function optionalStringRecord(value) {
|
|
140
|
-
if (value === void 0) return void 0;
|
|
141
|
-
if (!isRecord(value)) return INVALID;
|
|
142
|
-
const record = {};
|
|
143
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
144
|
-
if (typeof entry !== "string") return INVALID;
|
|
145
|
-
record[key] = entry;
|
|
146
|
-
}
|
|
147
|
-
return record;
|
|
148
|
-
}
|
|
149
|
-
function optionalStringArray(value) {
|
|
150
|
-
if (value === void 0) return void 0;
|
|
151
|
-
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) return INVALID;
|
|
152
|
-
return value;
|
|
153
|
-
}
|
|
154
|
-
function shape(value, validators) {
|
|
155
|
-
if (value === void 0) return INVALID;
|
|
156
|
-
if (!isRecord(value)) return INVALID;
|
|
157
|
-
const result = {};
|
|
158
|
-
for (const [key, validate] of Object.entries(validators)) {
|
|
159
|
-
const parsed = validate(value[key]);
|
|
160
|
-
if (parsed === INVALID) return INVALID;
|
|
161
|
-
if (parsed !== void 0) result[key] = parsed;
|
|
162
|
-
}
|
|
163
|
-
return result;
|
|
164
|
-
}
|
|
165
|
-
function group(parsed) {
|
|
166
|
-
return parsed === INVALID ? void 0 : parsed;
|
|
167
|
-
}
|
|
168
|
-
var COLOR_KEYS = [
|
|
169
|
-
"accent",
|
|
170
|
-
"background",
|
|
171
|
-
"foreground",
|
|
172
|
-
"overlay",
|
|
173
|
-
"surface",
|
|
174
|
-
"surfaceSecondary",
|
|
175
|
-
"tooltip"
|
|
176
|
-
];
|
|
177
|
-
function parseAppearance(value) {
|
|
178
|
-
return shape(value, {
|
|
179
|
-
launcher: (launcher) => launcher === void 0 ? void 0 : shape(launcher, {
|
|
180
|
-
background: optionalThemeColor,
|
|
181
|
-
icon: optionalString,
|
|
182
|
-
position: optionalEnum(["bottom-left", "bottom-right"]),
|
|
183
|
-
style: optionalStringRecord
|
|
184
|
-
}),
|
|
185
|
-
panel: (panel) => panel === void 0 ? void 0 : shape(panel, {
|
|
186
|
-
expandable: optionalBoolean,
|
|
187
|
-
expanded: optionalBoolean,
|
|
188
|
-
initialHeight: (entry) => entry === void 0 || typeof entry === "string" ? entry : optionalNumber(entry),
|
|
189
|
-
initialWidth: (entry) => entry === void 0 || typeof entry === "string" ? entry : optionalNumber(entry)
|
|
190
|
-
}),
|
|
191
|
-
theme: (theme) => theme === void 0 ? void 0 : shape(theme, {
|
|
192
|
-
colorScheme: optionalEnum(["dark", "light", "system"]),
|
|
193
|
-
colors: (colors) => colors === void 0 ? void 0 : shape(
|
|
194
|
-
colors,
|
|
195
|
-
Object.fromEntries(COLOR_KEYS.map((key) => [key, optionalThemeColor]))
|
|
196
|
-
),
|
|
197
|
-
designTheme: optionalEnum(["base", "brutalism", "glass", "mouve"]),
|
|
198
|
-
radius: optionalEnum(["pill", "round", "sharp", "soft"]),
|
|
199
|
-
typography: (typography) => typography === void 0 ? void 0 : shape(typography, { baseSize: optionalNumber, fontFamily: optionalString })
|
|
200
|
-
}),
|
|
201
|
-
viewMode: optionalEnum(["floating", "sidebar"])
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
function parseComposer(value) {
|
|
205
|
-
return shape(value, {
|
|
206
|
-
attachments: (attachments) => {
|
|
207
|
-
if (attachments === void 0 || attachments === false) return attachments;
|
|
208
|
-
if (!Array.isArray(attachments)) return INVALID;
|
|
209
|
-
const types = attachments.filter(
|
|
210
|
-
(entry) => typeof entry === "string" && isHeroUIAgentAttachmentContentType(entry)
|
|
211
|
-
);
|
|
212
|
-
return types.length === attachments.length ? types : INVALID;
|
|
213
|
-
},
|
|
214
|
-
defaultModel: optionalString,
|
|
215
|
-
dictation: optionalBoolean,
|
|
216
|
-
disclaimer: (disclaimer) => disclaimer === false ? false : optionalString(disclaimer),
|
|
217
|
-
modelPicker: optionalBoolean,
|
|
218
|
-
placeholder: optionalString
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
function parseMarkdown(value) {
|
|
222
|
-
return shape(value, {
|
|
223
|
-
animated: (animated) => {
|
|
224
|
-
if (animated === void 0 || animated === false) return animated;
|
|
225
|
-
if (!isRecord(animated)) return INVALID;
|
|
226
|
-
const animation = optionalEnum(["blurIn", "fadeIn", "slideUp"])(
|
|
227
|
-
animated["animation"]
|
|
228
|
-
);
|
|
229
|
-
if (animation === INVALID || animation === void 0) return INVALID;
|
|
230
|
-
return { ...animated, animation };
|
|
231
|
-
},
|
|
232
|
-
caret: (caret) => caret === false ? false : optionalEnum(["block", "circle"])(caret)
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
function parseResponseActions(value) {
|
|
236
|
-
if (value === void 0 || value === false) return value;
|
|
237
|
-
if (!Array.isArray(value)) return INVALID;
|
|
238
|
-
const allowed = ["copy", "feedback", "retry"];
|
|
239
|
-
const actions = value.filter(
|
|
240
|
-
(entry) => typeof entry === "string" && allowed.includes(entry)
|
|
241
|
-
);
|
|
242
|
-
return actions.length === value.length ? actions : INVALID;
|
|
243
|
-
}
|
|
244
|
-
function parseAgentRemoteConfig(value) {
|
|
245
|
-
if (!isRecord(value)) return null;
|
|
246
|
-
if (value["version"] !== HEROUI_AGENT_REMOTE_CONFIG_VERSION) return null;
|
|
247
|
-
const revision = value["revision"];
|
|
248
|
-
if (typeof revision !== "string" || !revision) return null;
|
|
249
|
-
const appearance = group(parseAppearance(value["appearance"]));
|
|
250
|
-
const capabilities = group(
|
|
251
|
-
shape(value["capabilities"], {
|
|
252
|
-
imageSearch: optionalBoolean,
|
|
253
|
-
newsSearch: optionalBoolean,
|
|
254
|
-
webSearch: optionalBoolean
|
|
255
|
-
})
|
|
256
|
-
);
|
|
257
|
-
const composer = group(parseComposer(value["composer"]));
|
|
258
|
-
const markdown = group(parseMarkdown(value["markdown"]));
|
|
259
|
-
const permissions = group(
|
|
260
|
-
shape(value["permissions"], {
|
|
261
|
-
defaultMode: optionalEnum(["ask", "auto", "full"]),
|
|
262
|
-
showPicker: optionalBoolean
|
|
263
|
-
})
|
|
264
|
-
);
|
|
265
|
-
const responseActions = group(parseResponseActions(value["responseActions"]));
|
|
266
|
-
const startScreen = group(
|
|
267
|
-
shape(value["startScreen"], {
|
|
268
|
-
greeting: optionalString,
|
|
269
|
-
promptShortcuts: optionalBoolean,
|
|
270
|
-
prompts: optionalStringArray
|
|
271
|
-
})
|
|
272
|
-
);
|
|
273
|
-
const webfont = group(
|
|
274
|
-
shape(value["webfont"], {
|
|
275
|
-
familyName: (familyName) => typeof familyName === "string" && familyName ? familyName : INVALID,
|
|
276
|
-
fontFaceUrl: optionalString,
|
|
277
|
-
stylesheetUrl: optionalString
|
|
278
|
-
})
|
|
279
|
-
);
|
|
280
|
-
return {
|
|
281
|
-
...appearance ? { appearance } : {},
|
|
282
|
-
...capabilities ? { capabilities } : {},
|
|
283
|
-
...composer ? { composer } : {},
|
|
284
|
-
...markdown ? { markdown } : {},
|
|
285
|
-
...permissions ? { permissions } : {},
|
|
286
|
-
...responseActions === void 0 ? {} : { responseActions },
|
|
287
|
-
...startScreen ? { startScreen } : {},
|
|
288
|
-
...webfont ? { webfont } : {},
|
|
289
|
-
revision,
|
|
290
|
-
version: HEROUI_AGENT_REMOTE_CONFIG_VERSION
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// src/embed/remote-config.ts
|
|
295
|
-
function cacheKey(agentId) {
|
|
296
|
-
return `heroui-agent:config:${agentId}`;
|
|
297
|
-
}
|
|
298
|
-
var REMOTE_CONFIG_CACHE_VERSION = 1;
|
|
299
|
-
function readCachedRemoteConfigSnapshot(agentId) {
|
|
300
|
-
try {
|
|
301
|
-
const raw = window.localStorage.getItem(cacheKey(agentId));
|
|
302
|
-
if (!raw) return null;
|
|
303
|
-
const value = JSON.parse(raw);
|
|
304
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
305
|
-
const envelope = value;
|
|
306
|
-
if (envelope.cacheVersion === REMOTE_CONFIG_CACHE_VERSION && typeof envelope.cachedAt === "number" && Number.isFinite(envelope.cachedAt)) {
|
|
307
|
-
const config = parseAgentRemoteConfig(envelope.config);
|
|
308
|
-
return config ? { cachedAt: envelope.cachedAt, config, legacy: false } : null;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
const legacyConfig = parseAgentRemoteConfig(value);
|
|
312
|
-
return legacyConfig ? { cachedAt: 0, config: legacyConfig, legacy: true } : null;
|
|
313
|
-
} catch {
|
|
314
|
-
return null;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
function writeCachedRemoteConfig(agentId, config, cachedAt = Date.now()) {
|
|
318
|
-
try {
|
|
319
|
-
const envelope = {
|
|
320
|
-
cacheVersion: REMOTE_CONFIG_CACHE_VERSION,
|
|
321
|
-
cachedAt,
|
|
322
|
-
config
|
|
323
|
-
};
|
|
324
|
-
window.localStorage.setItem(cacheKey(agentId), JSON.stringify(envelope));
|
|
325
|
-
} catch {
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
function clearCachedRemoteConfig(agentId) {
|
|
329
|
-
try {
|
|
330
|
-
window.localStorage.removeItem(cacheKey(agentId));
|
|
331
|
-
} catch {
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
async function fetchRemoteConfig(apiBaseUrl, agentId, options = {}) {
|
|
335
|
-
try {
|
|
336
|
-
const response = await fetch(
|
|
337
|
-
`${apiBaseUrl}/v1/agents/${encodeURIComponent(agentId)}/appearance`,
|
|
338
|
-
{
|
|
339
|
-
headers: options.revision ? { "If-None-Match": `"${options.revision}"` } : {},
|
|
340
|
-
...options.signal ? { signal: options.signal } : {}
|
|
341
|
-
}
|
|
342
|
-
);
|
|
343
|
-
if (response.status === 304) return { status: "not-modified" };
|
|
344
|
-
if (response.status === 404 || response.status === 410) return { status: "missing" };
|
|
345
|
-
if (!response.ok) return { status: "unavailable" };
|
|
346
|
-
const config = parseAgentRemoteConfig(await response.json());
|
|
347
|
-
return config ? { config, status: "ok" } : { status: "unavailable" };
|
|
348
|
-
} catch {
|
|
349
|
-
return { status: "unavailable" };
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// src/embed/shell-composer-presentation.tsx
|
|
354
|
-
import { ChevronDown, Microphone, Plus, ShieldCheck } from "@gravity-ui/icons";
|
|
355
|
-
import { useEffect, useRef } from "react";
|
|
356
|
-
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
357
|
-
var PERMISSION_LABELS = {
|
|
358
|
-
ask: "Ask for approval",
|
|
359
|
-
auto: "Approve for me",
|
|
360
|
-
full: "Full access"
|
|
361
|
-
};
|
|
362
|
-
function createShellComposerPresentation({
|
|
363
|
-
onFileDialogActiveChange,
|
|
364
|
-
onIntent,
|
|
365
|
-
options
|
|
366
|
-
}) {
|
|
367
|
-
return {
|
|
368
|
-
end: /* @__PURE__ */ jsx3(ShellComposerControls, { options }),
|
|
369
|
-
footer: options.composerDisclaimer ? /* @__PURE__ */ jsx3("p", { className: "ha-footnote", children: renderInlineMarkdown(options.composerDisclaimer) }) : null,
|
|
370
|
-
start: options.composerAttachmentContentTypes.length > 0 ? /* @__PURE__ */ jsx3(
|
|
371
|
-
ShellAttachmentControl,
|
|
372
|
-
{
|
|
373
|
-
accept: options.composerAttachmentAccept,
|
|
374
|
-
agentId: options.agentId,
|
|
375
|
-
onFileDialogActiveChange,
|
|
376
|
-
onIntent
|
|
377
|
-
}
|
|
378
|
-
) : null
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
function ShellAttachmentControl({
|
|
382
|
-
accept,
|
|
383
|
-
agentId,
|
|
384
|
-
onFileDialogActiveChange,
|
|
385
|
-
onIntent
|
|
386
|
-
}) {
|
|
387
|
-
const inputRef = useRef(null);
|
|
388
|
-
useEffect(() => {
|
|
389
|
-
const input = inputRef.current;
|
|
390
|
-
let focusTimer;
|
|
391
|
-
const release = () => onFileDialogActiveChange(false);
|
|
392
|
-
const releaseAfterFocus = () => {
|
|
393
|
-
focusTimer = window.setTimeout(release, 0);
|
|
394
|
-
};
|
|
395
|
-
input?.addEventListener("cancel", release);
|
|
396
|
-
window.addEventListener("focus", releaseAfterFocus);
|
|
397
|
-
return () => {
|
|
398
|
-
input?.removeEventListener("cancel", release);
|
|
399
|
-
window.removeEventListener("focus", releaseAfterFocus);
|
|
400
|
-
window.clearTimeout(focusTimer);
|
|
401
|
-
};
|
|
402
|
-
}, [onFileDialogActiveChange]);
|
|
403
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
404
|
-
/* @__PURE__ */ jsx3(
|
|
405
|
-
"input",
|
|
406
|
-
{
|
|
407
|
-
ref: inputRef,
|
|
408
|
-
multiple: true,
|
|
409
|
-
accept,
|
|
410
|
-
"aria-label": "Choose files to attach",
|
|
411
|
-
className: "ha-file-input",
|
|
412
|
-
type: "file",
|
|
413
|
-
onChange: (event) => {
|
|
414
|
-
writeAgentShellAttachments(agentId, Array.from(event.currentTarget.files ?? []));
|
|
415
|
-
event.currentTarget.value = "";
|
|
416
|
-
onFileDialogActiveChange(false);
|
|
417
|
-
onIntent();
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
),
|
|
421
|
-
/* @__PURE__ */ jsx3(
|
|
422
|
-
"button",
|
|
423
|
-
{
|
|
424
|
-
"aria-label": "Attach files",
|
|
425
|
-
className: "ha-attach",
|
|
426
|
-
type: "button",
|
|
427
|
-
onClick: () => {
|
|
428
|
-
onFileDialogActiveChange(true);
|
|
429
|
-
inputRef.current?.click();
|
|
430
|
-
},
|
|
431
|
-
children: /* @__PURE__ */ jsx3(Plus, { "aria-hidden": "true" })
|
|
432
|
-
}
|
|
433
|
-
)
|
|
434
|
-
] });
|
|
435
|
-
}
|
|
436
|
-
function ShellComposerControls({ options }) {
|
|
437
|
-
const modelId = options.composerDefaultModel ?? DEFAULT_AGENT_PICKER_MODEL_ID;
|
|
438
|
-
const modelLabel = AGENT_MODEL_OPTIONS.find((option) => option.id === modelId)?.label ?? modelId;
|
|
439
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
440
|
-
options.permissionShowPicker && options.tools.length > 0 ? /* @__PURE__ */ jsx3(
|
|
441
|
-
ShellPicker,
|
|
442
|
-
{
|
|
443
|
-
icon: /* @__PURE__ */ jsx3(ShieldCheck, { "aria-hidden": "true", className: "aui-permission-picker__trigger-icon" }),
|
|
444
|
-
kind: "permission",
|
|
445
|
-
label: PERMISSION_LABELS[options.permissionDefaultMode],
|
|
446
|
-
tone: options.permissionDefaultMode === "full" ? "warning" : void 0
|
|
447
|
-
}
|
|
448
|
-
) : null,
|
|
449
|
-
options.composerModelPicker ? /* @__PURE__ */ jsx3(ShellPicker, { kind: "model", label: modelLabel }) : null,
|
|
450
|
-
options.composerDictation ? /* @__PURE__ */ jsx3("button", { disabled: true, "aria-label": "Dictate message", className: "ha-dictation", type: "button", children: /* @__PURE__ */ jsx3(Microphone, { "aria-hidden": "true" }) }) : null
|
|
451
|
-
] });
|
|
452
|
-
}
|
|
453
|
-
function ShellPicker({
|
|
454
|
-
icon,
|
|
455
|
-
kind,
|
|
456
|
-
label,
|
|
457
|
-
tone
|
|
458
|
-
}) {
|
|
459
|
-
const name = `aui-${kind}-picker`;
|
|
460
|
-
return /* @__PURE__ */ jsx3("div", { className: `${name} ha-${kind}-picker`, "data-tone": tone, children: /* @__PURE__ */ jsxs(
|
|
461
|
-
"button",
|
|
462
|
-
{
|
|
463
|
-
disabled: true,
|
|
464
|
-
"aria-label": kind === "model" ? "AI model" : "Tool permissions",
|
|
465
|
-
className: `${name}__trigger ha-shell-picker-trigger`,
|
|
466
|
-
type: "button",
|
|
467
|
-
children: [
|
|
468
|
-
icon,
|
|
469
|
-
/* @__PURE__ */ jsx3("span", { className: `${name}__value`, children: label }),
|
|
470
|
-
/* @__PURE__ */ jsx3(ChevronDown, { "aria-hidden": "true", className: `${name}__indicator` })
|
|
471
|
-
]
|
|
472
|
-
}
|
|
473
|
-
) });
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// src/embed/webfont.ts
|
|
477
|
-
var FONT_FILE_EXTENSIONS = [".woff2", ".woff", ".ttf", ".otf", ".eot"];
|
|
478
|
-
function elementId(font) {
|
|
479
|
-
return `heroui-agent-font-${font.familyName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
480
|
-
}
|
|
481
|
-
function fontFormat(url) {
|
|
482
|
-
const isVariable = url.includes(":vf@") || url.includes("-wght-") || url.includes("-opsz-");
|
|
483
|
-
if (url.endsWith(".woff")) return "woff";
|
|
484
|
-
if (url.endsWith(".ttf")) return "truetype";
|
|
485
|
-
if (url.endsWith(".otf")) return "opentype";
|
|
486
|
-
return isVariable ? "woff2-variations" : "woff2";
|
|
487
|
-
}
|
|
488
|
-
function isSafeFontUrl(url, requireFontFile) {
|
|
489
|
-
try {
|
|
490
|
-
const parsed = new URL(url);
|
|
491
|
-
if (parsed.protocol !== "https:") return false;
|
|
492
|
-
if (!requireFontFile) return true;
|
|
493
|
-
const pathname = parsed.pathname.toLowerCase();
|
|
494
|
-
return FONT_FILE_EXTENSIONS.some((extension) => pathname.endsWith(extension));
|
|
495
|
-
} catch {
|
|
496
|
-
return false;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
function loadAgentWebfont(font) {
|
|
500
|
-
if (!font || typeof document === "undefined") return () => void 0;
|
|
501
|
-
const id = elementId(font);
|
|
502
|
-
if (document.getElementById(id)) return () => void 0;
|
|
503
|
-
if (font.stylesheetUrl && isSafeFontUrl(font.stylesheetUrl, false)) {
|
|
504
|
-
const link = document.createElement("link");
|
|
505
|
-
link.id = id;
|
|
506
|
-
link.rel = "stylesheet";
|
|
507
|
-
link.href = font.stylesheetUrl;
|
|
508
|
-
document.head.append(link);
|
|
509
|
-
return () => link.remove();
|
|
510
|
-
}
|
|
511
|
-
if (font.fontFaceUrl && isSafeFontUrl(font.fontFaceUrl, true)) {
|
|
512
|
-
const style = document.createElement("style");
|
|
513
|
-
const isVariable = font.fontFaceUrl.includes(":vf@") || font.fontFaceUrl.includes("-wght-") || font.fontFaceUrl.includes("-opsz-");
|
|
514
|
-
style.id = id;
|
|
515
|
-
style.textContent = [
|
|
516
|
-
"@font-face {",
|
|
517
|
-
` font-family: '${font.familyName.replaceAll("'", "")}';`,
|
|
518
|
-
" font-style: normal;",
|
|
519
|
-
" font-display: swap;",
|
|
520
|
-
` font-weight: ${isVariable ? "100 900" : "400"};`,
|
|
521
|
-
` src: url(${font.fontFaceUrl}) format('${fontFormat(font.fontFaceUrl)}');`,
|
|
522
|
-
"}"
|
|
523
|
-
].join("\n");
|
|
524
|
-
document.head.append(style);
|
|
525
|
-
return () => style.remove();
|
|
526
|
-
}
|
|
527
|
-
return () => void 0;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// src/embed/provider.tsx
|
|
531
|
-
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
532
|
-
var REMOTE_CONFIG_RESOLUTION_TIMEOUT_MS = 600;
|
|
533
|
-
var REMOTE_CONFIG_PRESENTATION_MAX_AGE_MS = 5 * 6e4;
|
|
534
|
-
var OPEN_STATE_STORAGE_PREFIX = "heroui-agent:open:";
|
|
535
|
-
function openStateStorageKey(agentId) {
|
|
536
|
-
return `${OPEN_STATE_STORAGE_PREFIX}${agentId}`;
|
|
537
|
-
}
|
|
538
|
-
function readOpenState(agentId) {
|
|
539
|
-
if (typeof window === "undefined") return false;
|
|
540
|
-
try {
|
|
541
|
-
return window.sessionStorage.getItem(openStateStorageKey(agentId)) === "true";
|
|
542
|
-
} catch {
|
|
543
|
-
return false;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
function writeOpenState(agentId, open) {
|
|
547
|
-
try {
|
|
548
|
-
if (open) window.sessionStorage.setItem(openStateStorageKey(agentId), "true");
|
|
549
|
-
else window.sessionStorage.removeItem(openStateStorageKey(agentId));
|
|
550
|
-
} catch {
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
var embedRuntimePromise;
|
|
554
|
-
function loadEmbedRuntime() {
|
|
555
|
-
embedRuntimePromise ??= import(
|
|
556
|
-
/* webpackPrefetch: true */
|
|
557
|
-
"./embed-runtime-VVAGDPYY.js"
|
|
558
|
-
).then(
|
|
559
|
-
(module) => module.default
|
|
560
|
-
);
|
|
561
|
-
return embedRuntimePromise;
|
|
562
|
-
}
|
|
563
|
-
var controllers = /* @__PURE__ */ new Map();
|
|
564
|
-
var controllerListeners = /* @__PURE__ */ new Set();
|
|
565
|
-
function getController(agentId) {
|
|
566
|
-
return agentId ? controllers.get(agentId) : [...controllers.values()][controllers.size - 1];
|
|
567
|
-
}
|
|
568
|
-
function notifyControllerListeners() {
|
|
569
|
-
for (const listener of controllerListeners) listener();
|
|
570
|
-
}
|
|
571
|
-
function subscribeToControllers(listener) {
|
|
572
|
-
controllerListeners.add(listener);
|
|
573
|
-
return () => controllerListeners.delete(listener);
|
|
574
|
-
}
|
|
575
|
-
function subscribeToColorScheme(onChange) {
|
|
576
|
-
const media = window.matchMedia?.("(prefers-color-scheme: dark)");
|
|
577
|
-
media?.addEventListener?.("change", onChange);
|
|
578
|
-
return () => media?.removeEventListener?.("change", onChange);
|
|
579
|
-
}
|
|
580
|
-
function prefersDarkColorScheme() {
|
|
581
|
-
return window.matchMedia?.("(prefers-color-scheme: dark)").matches === true;
|
|
582
|
-
}
|
|
583
|
-
function dispatch(agentId, method, ...args) {
|
|
584
|
-
const controller = getController(agentId);
|
|
585
|
-
if (!controller) {
|
|
586
|
-
console.warn("HeroUI Agent is not mounted yet. Add <HeroUIAgent /> to your layout.");
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
const action = controller[method];
|
|
590
|
-
action(...args);
|
|
591
|
-
}
|
|
592
|
-
function useRemoteConfig(agentId, apiBaseUrl, enabled) {
|
|
593
|
-
const [cachedSnapshot] = useState(() => {
|
|
594
|
-
if (!enabled || typeof window === "undefined") return null;
|
|
595
|
-
return readCachedRemoteConfigSnapshot(agentId);
|
|
596
|
-
});
|
|
597
|
-
const [state, setState] = useState(() => {
|
|
598
|
-
if (!enabled) return { config: null, presentationReady: true };
|
|
599
|
-
const cacheAge = cachedSnapshot ? Date.now() - cachedSnapshot.cachedAt : Number.POSITIVE_INFINITY;
|
|
600
|
-
const fresh = cachedSnapshot !== null && !cachedSnapshot.legacy && cacheAge >= 0 && cacheAge <= REMOTE_CONFIG_PRESENTATION_MAX_AGE_MS;
|
|
601
|
-
return {
|
|
602
|
-
config: fresh ? cachedSnapshot.config : null,
|
|
603
|
-
presentationReady: fresh
|
|
604
|
-
};
|
|
605
|
-
});
|
|
606
|
-
useEffect2(() => {
|
|
607
|
-
if (!enabled) return;
|
|
608
|
-
const controller = new AbortController();
|
|
609
|
-
let active = true;
|
|
610
|
-
const settle = (config) => {
|
|
611
|
-
setState(
|
|
612
|
-
(current) => current.presentationReady ? current : { config, presentationReady: true }
|
|
613
|
-
);
|
|
614
|
-
};
|
|
615
|
-
const budget = window.setTimeout(() => {
|
|
616
|
-
if (!active) return;
|
|
617
|
-
controller.abort();
|
|
618
|
-
settle(cachedSnapshot?.config ?? null);
|
|
619
|
-
}, REMOTE_CONFIG_RESOLUTION_TIMEOUT_MS);
|
|
620
|
-
void fetchRemoteConfig(apiBaseUrl, agentId, {
|
|
621
|
-
...cachedSnapshot?.config.revision ? { revision: cachedSnapshot.config.revision } : {},
|
|
622
|
-
signal: controller.signal
|
|
623
|
-
}).then((result) => {
|
|
624
|
-
if (!active || controller.signal.aborted) return;
|
|
625
|
-
if (result.status === "ok") {
|
|
626
|
-
writeCachedRemoteConfig(agentId, result.config);
|
|
627
|
-
settle(result.config);
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
if (result.status === "not-modified") {
|
|
631
|
-
if (cachedSnapshot) writeCachedRemoteConfig(agentId, cachedSnapshot.config);
|
|
632
|
-
settle(cachedSnapshot?.config ?? null);
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
if (result.status === "missing") {
|
|
636
|
-
clearCachedRemoteConfig(agentId);
|
|
637
|
-
settle(null);
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
settle(cachedSnapshot?.config ?? null);
|
|
641
|
-
}).finally(() => {
|
|
642
|
-
if (!active || controller.signal.aborted) return;
|
|
643
|
-
window.clearTimeout(budget);
|
|
644
|
-
});
|
|
645
|
-
return () => {
|
|
646
|
-
active = false;
|
|
647
|
-
controller.abort();
|
|
648
|
-
window.clearTimeout(budget);
|
|
649
|
-
};
|
|
650
|
-
}, [apiBaseUrl, cachedSnapshot, enabled, agentId]);
|
|
651
|
-
return enabled ? state : { config: null, presentationReady: true };
|
|
652
|
-
}
|
|
653
|
-
function useAgent(agentId) {
|
|
654
|
-
const getReadySnapshot = useCallback(() => getController(agentId)?.ready ?? false, [agentId]);
|
|
655
|
-
const ready = useSyncExternalStore(subscribeToControllers, getReadySnapshot, () => false);
|
|
656
|
-
return useMemo(
|
|
657
|
-
() => ({
|
|
658
|
-
hide: () => dispatch(agentId, "hide"),
|
|
659
|
-
newConversation: (prompt) => dispatch(agentId, "newConversation", prompt),
|
|
660
|
-
preload: () => dispatch(agentId, "preload"),
|
|
661
|
-
ready,
|
|
662
|
-
refreshAuth: () => dispatch(agentId, "refreshAuth"),
|
|
663
|
-
show: () => dispatch(agentId, "show"),
|
|
664
|
-
shutdown: () => dispatch(agentId, "shutdown"),
|
|
665
|
-
toggle: () => dispatch(agentId, "toggle")
|
|
666
|
-
}),
|
|
667
|
-
[agentId, ready]
|
|
668
|
-
);
|
|
669
|
-
}
|
|
670
|
-
function HeroUIAgent(props) {
|
|
671
|
-
const remoteSourceKey = `${props.agentId}\0${props._api?.baseUrl ?? ""}\0${props.remoteConfig !== false}`;
|
|
672
|
-
return /* @__PURE__ */ jsx4(HeroUIAgentMount, { ...props }, remoteSourceKey);
|
|
673
|
-
}
|
|
674
|
-
function HeroUIAgentMount(props) {
|
|
675
|
-
const {
|
|
676
|
-
_api,
|
|
677
|
-
agentId,
|
|
678
|
-
appearance,
|
|
679
|
-
capabilities,
|
|
680
|
-
componentExports,
|
|
681
|
-
composer,
|
|
682
|
-
context,
|
|
683
|
-
getAuthToken,
|
|
684
|
-
markdown,
|
|
685
|
-
onFeedback,
|
|
686
|
-
onReady,
|
|
687
|
-
permissions,
|
|
688
|
-
preload: preloadEnabled,
|
|
689
|
-
remoteConfig: remoteConfigEnabled = true,
|
|
690
|
-
reopenOnRefresh,
|
|
691
|
-
responseActions,
|
|
692
|
-
showLauncher,
|
|
693
|
-
startNewConversationOnOpen,
|
|
694
|
-
startScreen,
|
|
695
|
-
tools
|
|
696
|
-
} = props;
|
|
697
|
-
const markdownPlugins = markdown?.plugins;
|
|
698
|
-
const apiBaseUrl = (_api?.baseUrl ?? "https://api.heroui.com").replace(/\/$/, "");
|
|
699
|
-
const { config: remoteConfig, presentationReady: remoteConfigPresentationReady } = useRemoteConfig(
|
|
700
|
-
agentId,
|
|
701
|
-
apiBaseUrl,
|
|
702
|
-
remoteConfigEnabled
|
|
703
|
-
);
|
|
704
|
-
const serializedConfig = JSON.stringify({
|
|
705
|
-
_api,
|
|
706
|
-
appearance,
|
|
707
|
-
capabilities,
|
|
708
|
-
componentExports,
|
|
709
|
-
composer,
|
|
710
|
-
markdown: markdown ? { ...markdown, plugins: void 0 } : void 0,
|
|
711
|
-
permissions,
|
|
712
|
-
preload: preloadEnabled,
|
|
713
|
-
remote: remoteConfig,
|
|
714
|
-
reopenOnRefresh,
|
|
715
|
-
responseActions,
|
|
716
|
-
showLauncher,
|
|
717
|
-
startNewConversationOnOpen,
|
|
718
|
-
startScreen
|
|
719
|
-
});
|
|
720
|
-
const options = useMemo(() => {
|
|
721
|
-
const { remote, ...config } = JSON.parse(serializedConfig);
|
|
722
|
-
const merged = mergeRemoteConfig(remote, {
|
|
723
|
-
...config,
|
|
724
|
-
agentId,
|
|
725
|
-
getAuthToken
|
|
726
|
-
});
|
|
727
|
-
return resolveOptions({
|
|
728
|
-
...merged,
|
|
729
|
-
context,
|
|
730
|
-
markdown: merged.markdown ? { ...merged.markdown, plugins: markdownPlugins } : markdownPlugins ? { plugins: markdownPlugins } : void 0,
|
|
731
|
-
onFeedback,
|
|
732
|
-
tools
|
|
733
|
-
});
|
|
734
|
-
}, [context, getAuthToken, markdownPlugins, onFeedback, agentId, serializedConfig, tools]);
|
|
735
|
-
const tokenManager = useMemo(
|
|
736
|
-
() => new EmbedSessionManager(agentId, getAuthToken),
|
|
737
|
-
[agentId, getAuthToken]
|
|
738
|
-
);
|
|
739
|
-
const hostFont = hostSuppliesFont(props);
|
|
740
|
-
const remoteWebfont = remoteConfig?.webfont;
|
|
741
|
-
useEffect2(() => {
|
|
742
|
-
if (hostFont) return;
|
|
743
|
-
return loadAgentWebfont(remoteWebfont);
|
|
744
|
-
}, [hostFont, remoteWebfont]);
|
|
745
|
-
const [restoredOpenState] = useState(() => reopenOnRefresh === true && readOpenState(agentId));
|
|
746
|
-
const [openRequested, setOpenRequested] = useState(restoredOpenState);
|
|
747
|
-
const [conversationEpoch, setConversationEpoch] = useState(1);
|
|
748
|
-
const [identityEpoch, setIdentityEpoch] = useState(1);
|
|
749
|
-
const [identityReset, setIdentityReset] = useState(false);
|
|
750
|
-
const [requestedConversationId, setRequestedConversationId] = useState(
|
|
751
|
-
restoredOpenState || options.startNewConversationOnOpen ? null : void 0
|
|
752
|
-
);
|
|
753
|
-
const [portalRoot, setPortalRoot] = useState(null);
|
|
754
|
-
const [EmbedRuntime, setEmbedRuntime] = useState(null);
|
|
755
|
-
const [hasCompletedInitialLoad, setHasCompletedInitialLoad] = useState(false);
|
|
756
|
-
const [hasUnreadMessages, setHasUnreadMessages] = useState(false);
|
|
757
|
-
const [runtimeReadyEpoch, setRuntimeReadyEpoch] = useState(null);
|
|
758
|
-
const conversationEpochRef = useRef2(1);
|
|
759
|
-
const launcherRef = useRef2(null);
|
|
760
|
-
const restoreLauncherFocusRef = useRef2(false);
|
|
761
|
-
const portalRootRef = useRef2(null);
|
|
762
|
-
const reportedReadyEpochRef = useRef2(null);
|
|
763
|
-
const shouldOpenRef = useRef2(restoredOpenState);
|
|
764
|
-
const hostRef = useRef2(null);
|
|
765
|
-
const embedReactRootRef = useRef2(null);
|
|
766
|
-
const rootPropertyNamesRef = useRef2([]);
|
|
767
|
-
const prefersDark = useSyncExternalStore(
|
|
768
|
-
subscribeToColorScheme,
|
|
769
|
-
prefersDarkColorScheme,
|
|
770
|
-
() => false
|
|
771
|
-
);
|
|
772
|
-
const designThemeClass = options.designTheme === "base" ? null : `${options.designTheme}-${options.colorScheme === "system" ? prefersDark ? "dark" : "light" : options.colorScheme}`;
|
|
773
|
-
const ready = remoteConfigPresentationReady && runtimeReadyEpoch === conversationEpoch && EmbedRuntime !== null;
|
|
774
|
-
const isOpen = remoteConfigPresentationReady && openRequested;
|
|
775
|
-
const [shouldLoadRuntime, setShouldLoadRuntime] = useState(restoredOpenState || options.preload);
|
|
776
|
-
const requestRuntime = useCallback(() => setShouldLoadRuntime(true), []);
|
|
777
|
-
const [composerControlsRevealed, setComposerControlsRevealed] = useState(false);
|
|
778
|
-
const [composerFileDialogActive, setComposerFileDialogActive] = useState(false);
|
|
779
|
-
const revealComposerControls = useCallback(() => setComposerControlsRevealed(true), []);
|
|
780
|
-
useEffect2(() => {
|
|
781
|
-
if (!composerControlsRevealed || !portalRoot) return;
|
|
782
|
-
const reconcileComposerFocus = () => {
|
|
783
|
-
queueMicrotask(() => {
|
|
784
|
-
const composer2 = portalRoot.querySelector(".ha-composer:not([hidden] *)");
|
|
785
|
-
if (composer2 && containsComposerFocus(composer2, document.activeElement)) return;
|
|
786
|
-
setComposerControlsRevealed(false);
|
|
787
|
-
});
|
|
788
|
-
};
|
|
789
|
-
portalRoot.addEventListener("focusout", reconcileComposerFocus);
|
|
790
|
-
return () => portalRoot.removeEventListener("focusout", reconcileComposerFocus);
|
|
791
|
-
}, [composerControlsRevealed, portalRoot]);
|
|
792
|
-
useEffect2(() => {
|
|
793
|
-
if (!shouldLoadRuntime) return;
|
|
794
|
-
void tokenManager.get().catch(() => void 0);
|
|
795
|
-
}, [shouldLoadRuntime, tokenManager]);
|
|
796
|
-
useEffect2(() => {
|
|
797
|
-
if (options.reopenOnRefresh) writeOpenState(options.agentId, openRequested);
|
|
798
|
-
else writeOpenState(options.agentId, false);
|
|
799
|
-
}, [openRequested, options.agentId, options.reopenOnRefresh]);
|
|
800
|
-
useEffect2(() => {
|
|
801
|
-
if (!shouldLoadRuntime) return;
|
|
802
|
-
if (!remoteConfigPresentationReady) {
|
|
803
|
-
void loadEmbedRuntime().catch(() => void 0);
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
let active = true;
|
|
807
|
-
void loadEmbedRuntime().then((runtime) => {
|
|
808
|
-
if (active) setEmbedRuntime(() => runtime);
|
|
809
|
-
}).catch(() => void 0);
|
|
810
|
-
return () => {
|
|
811
|
-
active = false;
|
|
812
|
-
};
|
|
813
|
-
}, [remoteConfigPresentationReady, shouldLoadRuntime]);
|
|
814
|
-
useEffect2(() => {
|
|
815
|
-
if (!isOpen || options.viewMode !== "sidebar") return;
|
|
816
|
-
if (window.innerWidth < 640) return;
|
|
817
|
-
const target = document.documentElement;
|
|
818
|
-
const previousMargin = target.style.marginRight;
|
|
819
|
-
const previousTransition = target.style.transition;
|
|
820
|
-
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
821
|
-
const width = Math.min(readSidebarWidth(options.agentId, window.innerWidth), window.innerWidth);
|
|
822
|
-
if (!reducedMotion) {
|
|
823
|
-
target.style.transition = [previousTransition, "margin-right .28s cubic-bezier(.2,.8,.2,1)"].filter(Boolean).join(", ");
|
|
824
|
-
}
|
|
825
|
-
target.style.marginRight = `${width}px`;
|
|
826
|
-
return () => {
|
|
827
|
-
target.style.marginRight = previousMargin;
|
|
828
|
-
window.setTimeout(() => {
|
|
829
|
-
target.style.transition = previousTransition;
|
|
830
|
-
}, 300);
|
|
831
|
-
};
|
|
832
|
-
}, [isOpen, options.agentId, options.viewMode]);
|
|
833
|
-
useEffect2(() => {
|
|
834
|
-
const host = document.createElement("div");
|
|
835
|
-
const root = document.createElement("div");
|
|
836
|
-
host.dataset["herouiAgent"] = options.agentId;
|
|
837
|
-
root.className = "ha-root";
|
|
838
|
-
hostRef.current = host;
|
|
839
|
-
portalRootRef.current = root;
|
|
840
|
-
embedReactRootRef.current = createRoot(root);
|
|
841
|
-
host.append(root);
|
|
842
|
-
document.body.append(host);
|
|
843
|
-
let active = true;
|
|
844
|
-
queueMicrotask(() => {
|
|
845
|
-
if (active) setPortalRoot(root);
|
|
846
|
-
});
|
|
847
|
-
return () => {
|
|
848
|
-
active = false;
|
|
849
|
-
setFocus(options.agentId, false);
|
|
850
|
-
const reactRoot = embedReactRootRef.current;
|
|
851
|
-
portalRootRef.current = null;
|
|
852
|
-
hostRef.current = null;
|
|
853
|
-
embedReactRootRef.current = null;
|
|
854
|
-
rootPropertyNamesRef.current = [];
|
|
855
|
-
queueMicrotask(() => {
|
|
856
|
-
reactRoot?.unmount();
|
|
857
|
-
host.remove();
|
|
858
|
-
});
|
|
859
|
-
};
|
|
860
|
-
}, [options.agentId]);
|
|
861
|
-
useLayoutEffect(() => {
|
|
862
|
-
const root = portalRootRef.current;
|
|
863
|
-
const host = hostRef.current;
|
|
864
|
-
if (!root || !host) return;
|
|
865
|
-
root.className = designThemeClass ? `ha-root ${designThemeClass}` : "ha-root";
|
|
866
|
-
root.dataset["theme"] = options.colorScheme;
|
|
867
|
-
root.dataset["viewMode"] = options.viewMode;
|
|
868
|
-
root.dataset["position"] = options.launcherPosition;
|
|
869
|
-
host.dataset["open"] = isOpen ? "true" : "false";
|
|
870
|
-
host.dataset["viewMode"] = options.viewMode;
|
|
871
|
-
host.dataset["position"] = options.launcherPosition;
|
|
872
|
-
if (options.launcherOffset) {
|
|
873
|
-
host.style.setProperty("--ha-launcher-x", `${options.launcherOffset.x}px`);
|
|
874
|
-
host.style.setProperty("--ha-launcher-y", `${options.launcherOffset.y}px`);
|
|
875
|
-
} else {
|
|
876
|
-
host.style.removeProperty("--ha-launcher-x");
|
|
877
|
-
host.style.removeProperty("--ha-launcher-y");
|
|
878
|
-
}
|
|
879
|
-
if (options.viewMode === "sidebar") {
|
|
880
|
-
host.style.setProperty(
|
|
881
|
-
"--ha-sidebar-width",
|
|
882
|
-
`${readSidebarWidth(options.agentId, window.innerWidth)}px`
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
const rootProperties = { ...options.themeStyle, ...options.panelStyle };
|
|
886
|
-
for (const property of rootPropertyNamesRef.current) {
|
|
887
|
-
if (!(property in rootProperties)) root.style.removeProperty(property);
|
|
888
|
-
}
|
|
889
|
-
for (const [property, value] of Object.entries(rootProperties)) {
|
|
890
|
-
root.style.setProperty(property, value);
|
|
891
|
-
}
|
|
892
|
-
rootPropertyNamesRef.current = Object.keys(rootProperties);
|
|
893
|
-
}, [
|
|
894
|
-
designThemeClass,
|
|
895
|
-
isOpen,
|
|
896
|
-
options.colorScheme,
|
|
897
|
-
options.agentId,
|
|
898
|
-
options.launcherOffset,
|
|
899
|
-
options.launcherPosition,
|
|
900
|
-
options.panelStyle,
|
|
901
|
-
options.themeStyle,
|
|
902
|
-
options.viewMode,
|
|
903
|
-
portalRoot
|
|
904
|
-
]);
|
|
905
|
-
const prepareNextConversation = useCallback(
|
|
906
|
-
(conversationId) => {
|
|
907
|
-
setFocus(options.agentId, false);
|
|
908
|
-
setComposerFileDialogActive(false);
|
|
909
|
-
setRuntimeReadyEpoch(null);
|
|
910
|
-
setRequestedConversationId(conversationId);
|
|
911
|
-
const nextEpoch = conversationEpochRef.current + 1;
|
|
912
|
-
conversationEpochRef.current = nextEpoch;
|
|
913
|
-
setConversationEpoch(nextEpoch);
|
|
914
|
-
},
|
|
915
|
-
[options.agentId]
|
|
916
|
-
);
|
|
917
|
-
const handleRuntimeReady = useCallback((readyEpoch) => {
|
|
918
|
-
if (conversationEpochRef.current !== readyEpoch) return;
|
|
919
|
-
setHasCompletedInitialLoad(true);
|
|
920
|
-
setRuntimeReadyEpoch(readyEpoch);
|
|
921
|
-
}, []);
|
|
922
|
-
const emitReady = useEffectEvent(() => onReady?.());
|
|
923
|
-
useEffect2(() => {
|
|
924
|
-
if (!ready || reportedReadyEpochRef.current === conversationEpoch) return;
|
|
925
|
-
reportedReadyEpochRef.current = conversationEpoch;
|
|
926
|
-
emitReady();
|
|
927
|
-
}, [conversationEpoch, ready]);
|
|
928
|
-
const hide = useCallback(() => {
|
|
929
|
-
shouldOpenRef.current = false;
|
|
930
|
-
restoreLauncherFocusRef.current = true;
|
|
931
|
-
setOpenRequested(false);
|
|
932
|
-
setComposerControlsRevealed(false);
|
|
933
|
-
prepareNextConversation(options.startNewConversationOnOpen ? null : void 0);
|
|
934
|
-
}, [options.startNewConversationOnOpen, prepareNextConversation]);
|
|
935
|
-
useEffect2(() => {
|
|
936
|
-
if (isOpen || !ready || !restoreLauncherFocusRef.current) return;
|
|
937
|
-
restoreLauncherFocusRef.current = false;
|
|
938
|
-
requestAnimationFrame(() => launcherRef.current?.focus());
|
|
939
|
-
}, [isOpen, ready]);
|
|
940
|
-
const show = useCallback(() => {
|
|
941
|
-
shouldOpenRef.current = true;
|
|
942
|
-
setOpenRequested(true);
|
|
943
|
-
requestRuntime();
|
|
944
|
-
}, [requestRuntime]);
|
|
945
|
-
const preload = requestRuntime;
|
|
946
|
-
const newConversation = useCallback(
|
|
947
|
-
(prompt) => {
|
|
948
|
-
const submission = prompt ? createSubmittedAgentShellHandoff(prompt) : null;
|
|
949
|
-
if (submission) writeAgentShellHandoff(options.agentId, submission);
|
|
950
|
-
shouldOpenRef.current = true;
|
|
951
|
-
setOpenRequested(true);
|
|
952
|
-
setComposerControlsRevealed(false);
|
|
953
|
-
requestRuntime();
|
|
954
|
-
prepareNextConversation(null);
|
|
955
|
-
},
|
|
956
|
-
[options.agentId, prepareNextConversation, requestRuntime]
|
|
957
|
-
);
|
|
958
|
-
const selectConversation = useCallback(
|
|
959
|
-
(conversationId) => {
|
|
960
|
-
shouldOpenRef.current = true;
|
|
961
|
-
setOpenRequested(true);
|
|
962
|
-
setComposerControlsRevealed(false);
|
|
963
|
-
requestRuntime();
|
|
964
|
-
prepareNextConversation(conversationId);
|
|
965
|
-
},
|
|
966
|
-
[prepareNextConversation, requestRuntime]
|
|
967
|
-
);
|
|
968
|
-
const toggle = useCallback(() => {
|
|
969
|
-
if (isOpen || shouldOpenRef.current) hide();
|
|
970
|
-
else show();
|
|
971
|
-
}, [hide, isOpen, show]);
|
|
972
|
-
const refreshAuth = useCallback(() => {
|
|
973
|
-
setHasUnreadMessages(false);
|
|
974
|
-
setIdentityReset(false);
|
|
975
|
-
setIdentityEpoch((epoch) => epoch + 1);
|
|
976
|
-
}, []);
|
|
977
|
-
const shutdown = useCallback(() => {
|
|
978
|
-
const prefixes = [
|
|
979
|
-
`heroui-agent:anonymous:${options.agentId}`,
|
|
980
|
-
`heroui-agent:conversation:${options.agentId}:`,
|
|
981
|
-
`heroui-agent:history:${options.agentId}:`
|
|
982
|
-
];
|
|
983
|
-
for (let index = localStorage.length - 1; index >= 0; index -= 1) {
|
|
984
|
-
const key = localStorage.key(index);
|
|
985
|
-
if (key && prefixes.some((prefix) => key.startsWith(prefix))) localStorage.removeItem(key);
|
|
986
|
-
}
|
|
987
|
-
shouldOpenRef.current = false;
|
|
988
|
-
setHasUnreadMessages(false);
|
|
989
|
-
setOpenRequested(false);
|
|
990
|
-
setComposerControlsRevealed(false);
|
|
991
|
-
writeOpenState(options.agentId, false);
|
|
992
|
-
setIdentityReset(true);
|
|
993
|
-
setIdentityEpoch((epoch) => epoch + 1);
|
|
994
|
-
prepareNextConversation();
|
|
995
|
-
window.setTimeout(() => {
|
|
996
|
-
void import("./composer-draft-DTQAEO5K.js").then(
|
|
997
|
-
({ clearAgentComposerDraftsForProject }) => clearAgentComposerDraftsForProject(options.agentId)
|
|
998
|
-
).catch(() => void 0);
|
|
999
|
-
}, 0);
|
|
1000
|
-
}, [options.agentId, prepareNextConversation]);
|
|
1001
|
-
useEffect2(() => {
|
|
1002
|
-
const controller = {
|
|
1003
|
-
hide,
|
|
1004
|
-
newConversation,
|
|
1005
|
-
preload,
|
|
1006
|
-
ready,
|
|
1007
|
-
refreshAuth,
|
|
1008
|
-
show,
|
|
1009
|
-
shutdown,
|
|
1010
|
-
toggle
|
|
1011
|
-
};
|
|
1012
|
-
controllers.set(options.agentId, controller);
|
|
1013
|
-
notifyControllerListeners();
|
|
1014
|
-
return () => {
|
|
1015
|
-
if (controllers.get(options.agentId) === controller) {
|
|
1016
|
-
controllers.delete(options.agentId);
|
|
1017
|
-
notifyControllerListeners();
|
|
1018
|
-
}
|
|
1019
|
-
};
|
|
1020
|
-
}, [hide, newConversation, options.agentId, preload, ready, refreshAuth, show, shutdown, toggle]);
|
|
1021
|
-
useLayoutEffect(() => {
|
|
1022
|
-
const reactRoot = embedReactRootRef.current;
|
|
1023
|
-
if (!portalRoot || !reactRoot) return;
|
|
1024
|
-
const shellComposer = createShellComposerPresentation({
|
|
1025
|
-
onFileDialogActiveChange: setComposerFileDialogActive,
|
|
1026
|
-
onIntent: requestRuntime,
|
|
1027
|
-
options
|
|
1028
|
-
});
|
|
1029
|
-
reactRoot.render(
|
|
1030
|
-
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1031
|
-
remoteConfigPresentationReady && options.showLauncher && !isOpen ? /* @__PURE__ */ jsxs2(
|
|
1032
|
-
"button",
|
|
1033
|
-
{
|
|
1034
|
-
ref: launcherRef,
|
|
1035
|
-
"aria-expanded": false,
|
|
1036
|
-
"aria-haspopup": "dialog",
|
|
1037
|
-
className: "ha-launcher",
|
|
1038
|
-
style: options.launcherStyle,
|
|
1039
|
-
type: "button",
|
|
1040
|
-
"aria-label": hasUnreadMessages ? "Open data assistant, unread messages" : "Open data assistant",
|
|
1041
|
-
onClick: show,
|
|
1042
|
-
children: [
|
|
1043
|
-
/* @__PURE__ */ jsx4(LauncherIcon, { icon: options.launcherIcon }),
|
|
1044
|
-
hasUnreadMessages ? /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", className: "ha-launcher__unread" }) : null
|
|
1045
|
-
]
|
|
1046
|
-
}
|
|
1047
|
-
) : null,
|
|
1048
|
-
EmbedRuntime && !composerFileDialogActive ? /* @__PURE__ */ jsx4(
|
|
1049
|
-
EmbedRuntime,
|
|
1050
|
-
{
|
|
1051
|
-
composerControlsRevealed,
|
|
1052
|
-
conversationEpoch,
|
|
1053
|
-
identityEpoch,
|
|
1054
|
-
identityReset,
|
|
1055
|
-
open: isOpen,
|
|
1056
|
-
options,
|
|
1057
|
-
requestedConversationId,
|
|
1058
|
-
shellComposerEndSlot: shellComposer.end,
|
|
1059
|
-
shellComposerFooterSlot: shellComposer.footer,
|
|
1060
|
-
showReadyLoader: !hasCompletedInitialLoad && requestedConversationId !== null,
|
|
1061
|
-
tokenManager,
|
|
1062
|
-
onClose: hide,
|
|
1063
|
-
onComposerControlsReveal: revealComposerControls,
|
|
1064
|
-
onNewConversation: newConversation,
|
|
1065
|
-
onReady: handleRuntimeReady,
|
|
1066
|
-
onSelectConversation: selectConversation,
|
|
1067
|
-
onUnreadChange: setHasUnreadMessages
|
|
1068
|
-
}
|
|
1069
|
-
) : /* @__PURE__ */ jsx4(
|
|
1070
|
-
PanelShell,
|
|
1071
|
-
{
|
|
1072
|
-
agentId: options.agentId,
|
|
1073
|
-
defaultExpanded: options.panelExpanded,
|
|
1074
|
-
expandable: options.panelExpandable,
|
|
1075
|
-
historySlot: /* @__PURE__ */ jsx4(Fragment2, {}),
|
|
1076
|
-
modal: options.viewMode !== "sidebar",
|
|
1077
|
-
name: "Data assistant",
|
|
1078
|
-
open: isOpen,
|
|
1079
|
-
onClose: hide,
|
|
1080
|
-
children: requestedConversationId === null ? /* @__PURE__ */ jsx4(
|
|
1081
|
-
ShellConversation,
|
|
1082
|
-
{
|
|
1083
|
-
composerConnecting: true,
|
|
1084
|
-
agentId: options.agentId,
|
|
1085
|
-
composerControlsAvailable: options.composerControlsAvailable,
|
|
1086
|
-
composerControlsRevealed,
|
|
1087
|
-
composerEndSlot: shellComposer.end,
|
|
1088
|
-
composerFooterSlot: shellComposer.footer,
|
|
1089
|
-
composerStartSlot: shellComposer.start,
|
|
1090
|
-
greeting: options.greeting,
|
|
1091
|
-
placeholder: options.composerPlaceholder,
|
|
1092
|
-
suggestedPrompts: options.suggestedPrompts ?? [],
|
|
1093
|
-
onComposerControlsReveal: revealComposerControls,
|
|
1094
|
-
onIntent: requestRuntime
|
|
1095
|
-
},
|
|
1096
|
-
`shell-${conversationEpoch}`
|
|
1097
|
-
) : /* @__PURE__ */ jsx4(AgentLoadingState, {})
|
|
1098
|
-
}
|
|
1099
|
-
)
|
|
1100
|
-
] })
|
|
1101
|
-
);
|
|
1102
|
-
}, [
|
|
1103
|
-
EmbedRuntime,
|
|
1104
|
-
composerControlsRevealed,
|
|
1105
|
-
composerFileDialogActive,
|
|
1106
|
-
conversationEpoch,
|
|
1107
|
-
handleRuntimeReady,
|
|
1108
|
-
hasUnreadMessages,
|
|
1109
|
-
hasCompletedInitialLoad,
|
|
1110
|
-
hide,
|
|
1111
|
-
identityEpoch,
|
|
1112
|
-
identityReset,
|
|
1113
|
-
isOpen,
|
|
1114
|
-
newConversation,
|
|
1115
|
-
options,
|
|
1116
|
-
portalRoot,
|
|
1117
|
-
ready,
|
|
1118
|
-
remoteConfigPresentationReady,
|
|
1119
|
-
requestedConversationId,
|
|
1120
|
-
requestRuntime,
|
|
1121
|
-
revealComposerControls,
|
|
1122
|
-
selectConversation,
|
|
1123
|
-
show,
|
|
1124
|
-
tokenManager
|
|
1125
|
-
]);
|
|
1126
|
-
return null;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
export {
|
|
1130
|
-
useAgent,
|
|
1131
|
-
HeroUIAgent
|
|
1132
|
-
};
|