@truefoundry/trueforge-ui 0.1.0 → 0.2.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +26 -15
  2. package/README.md +38 -38
  3. package/dist/{ConnectorSettings-H37ZFC6Z.js → ConnectorSettings-4CYGRWPH.js} +6 -4
  4. package/dist/{ConnectorSettings-H37ZFC6Z.js.map → ConnectorSettings-4CYGRWPH.js.map} +1 -1
  5. package/dist/{DockLayout-KEMFRWIR.js → DockLayout-FEDQX2UT.js} +8 -6
  6. package/dist/{DockLayout-KEMFRWIR.js.map → DockLayout-FEDQX2UT.js.map} +1 -1
  7. package/dist/{DrawerLayout-ZVWCKEIS.js → DrawerLayout-NMA7P64K.js} +10 -7
  8. package/dist/{DrawerLayout-ZVWCKEIS.js.map → DrawerLayout-NMA7P64K.js.map} +1 -1
  9. package/dist/{ModelSettings-QJMYHCMD.js → ModelSettings-H5FKRZLK.js} +6 -4
  10. package/dist/{ModelSettings-QJMYHCMD.js.map → ModelSettings-H5FKRZLK.js.map} +1 -1
  11. package/dist/{SandboxSettings-BUBMLW7Q.js → SandboxSettings-CBSC3C3G.js} +6 -4
  12. package/dist/{SandboxSettings-BUBMLW7Q.js.map → SandboxSettings-CBSC3C3G.js.map} +1 -1
  13. package/dist/{SettingsBuilder-26XSGIHK.js → SettingsBuilder-EGSH7BUA.js} +9 -7
  14. package/dist/{SettingsBuilder-26XSGIHK.js.map → SettingsBuilder-EGSH7BUA.js.map} +1 -1
  15. package/dist/ShellRouteSync-53FTXNZZ.js +172 -0
  16. package/dist/ShellRouteSync-53FTXNZZ.js.map +1 -0
  17. package/dist/{SidebarLayout-F3XFXW7B.js → SidebarLayout-5WM2BHZG.js} +10 -7
  18. package/dist/{SidebarLayout-F3XFXW7B.js.map → SidebarLayout-5WM2BHZG.js.map} +1 -1
  19. package/dist/{SkillSettings-IRMGDFRG.js → SkillSettings-A2OQSQUT.js} +6 -4
  20. package/dist/{SkillSettings-IRMGDFRG.js.map → SkillSettings-A2OQSQUT.js.map} +1 -1
  21. package/dist/TrueForgeUIWithRouter-2AQ3HW6L.js +30 -0
  22. package/dist/TrueForgeUIWithRouter-2AQ3HW6L.js.map +1 -0
  23. package/dist/{WidgetLayout-SZMABKIR.js → WidgetLayout-IKGVAZFV.js} +8 -6
  24. package/dist/{WidgetLayout-SZMABKIR.js.map → WidgetLayout-IKGVAZFV.js.map} +1 -1
  25. package/dist/{chunk-7EEOWDLE.js → chunk-4STVL6W6.js} +9 -7
  26. package/dist/{chunk-7EEOWDLE.js.map → chunk-4STVL6W6.js.map} +1 -1
  27. package/dist/chunk-7LAVNBJC.js +477 -0
  28. package/dist/chunk-7LAVNBJC.js.map +1 -0
  29. package/dist/{chunk-LRQHHZDX.js → chunk-BMLCISMH.js} +6 -4
  30. package/dist/{chunk-LRQHHZDX.js.map → chunk-BMLCISMH.js.map} +1 -1
  31. package/dist/chunk-EK6OUU24.js +441 -0
  32. package/dist/chunk-EK6OUU24.js.map +1 -0
  33. package/dist/chunk-FXCYLEFB.js +64 -0
  34. package/dist/chunk-FXCYLEFB.js.map +1 -0
  35. package/dist/{chunk-H7DR64W3.js → chunk-GJ3SK4G3.js} +38 -93
  36. package/dist/chunk-GJ3SK4G3.js.map +1 -0
  37. package/dist/chunk-IDXUUOSM.js +436 -0
  38. package/dist/chunk-IDXUUOSM.js.map +1 -0
  39. package/dist/{chunk-QGNLP2EE.js → chunk-MIHHZQOQ.js} +6 -4
  40. package/dist/{chunk-QGNLP2EE.js.map → chunk-MIHHZQOQ.js.map} +1 -1
  41. package/dist/chunk-YIWUG6MC.js +101 -0
  42. package/dist/chunk-YIWUG6MC.js.map +1 -0
  43. package/dist/index.d.ts +54 -9
  44. package/dist/index.js +39 -426
  45. package/dist/index.js.map +1 -1
  46. package/dist/plugins/trueforge-agent-server-adapter/index.js.map +1 -1
  47. package/package.json +14 -6
  48. package/dist/chunk-H7DR64W3.js.map +0 -1
  49. package/dist/chunk-XAZ5HJXV.js +0 -862
  50. package/dist/chunk-XAZ5HJXV.js.map +0 -1
@@ -0,0 +1,441 @@
1
+ import {
2
+ useOptionalServer
3
+ } from "./chunk-IDXUUOSM.js";
4
+ import {
5
+ ThemeContext,
6
+ cn
7
+ } from "./chunk-DTF7ZMUJ.js";
8
+
9
+ // src/icons/Icon.tsx
10
+ import { createElement, isValidElement, useContext } from "react";
11
+
12
+ // src/icons/IconRegistry.tsx
13
+ import {
14
+ Archive,
15
+ ArrowDown,
16
+ ArrowLeft,
17
+ ArrowUp,
18
+ BookOpen,
19
+ Bot,
20
+ Box,
21
+ Brain,
22
+ Check,
23
+ ChevronDown,
24
+ ChevronLeft,
25
+ ChevronRight,
26
+ CircleAlert,
27
+ CircleCheck,
28
+ CircleX,
29
+ Clock,
30
+ Code2,
31
+ Copy,
32
+ Cpu,
33
+ Download,
34
+ Ellipsis,
35
+ ExternalLink,
36
+ File,
37
+ Github,
38
+ History,
39
+ Info,
40
+ Lightbulb,
41
+ ListChecks,
42
+ ListFilter,
43
+ ListOrdered,
44
+ Loader2,
45
+ Lock,
46
+ LogOut,
47
+ Maximize2,
48
+ Menu,
49
+ Minimize2,
50
+ Moon,
51
+ PanelLeftClose,
52
+ PanelLeftOpen,
53
+ Paperclip,
54
+ Pencil,
55
+ Play,
56
+ Plug,
57
+ Plus,
58
+ RotateCw,
59
+ Save,
60
+ Search,
61
+ Settings,
62
+ Sparkle,
63
+ SquarePen,
64
+ Sun,
65
+ Terminal,
66
+ Trash2,
67
+ TriangleAlert,
68
+ Wrench,
69
+ X
70
+ } from "lucide-react";
71
+ import { jsx, jsxs } from "react/jsx-runtime";
72
+ function Broom({ size = 24, ...props }) {
73
+ return /* @__PURE__ */ jsxs(
74
+ "svg",
75
+ {
76
+ xmlns: "http://www.w3.org/2000/svg",
77
+ width: size,
78
+ height: size,
79
+ viewBox: "0 0 24 24",
80
+ fill: "none",
81
+ stroke: "currentColor",
82
+ strokeWidth: "2",
83
+ strokeLinecap: "round",
84
+ strokeLinejoin: "round",
85
+ ...props,
86
+ children: [
87
+ /* @__PURE__ */ jsx("path", { d: "M13.5 10.5 22 2" }),
88
+ /* @__PURE__ */ jsx("path", { d: "M14.734 13.841a2 2 0 00-.314-2.42L12.58 9.58a2 2 0 00-2.421-.314l-7.657 4.461A1 1 0 002.3 15.3l6.403 6.403a1 1 0 001.571-.204z" }),
89
+ /* @__PURE__ */ jsx("path", { d: "m5 18 2-2" }),
90
+ /* @__PURE__ */ jsx("path", { d: "m7.699 10.7 5.602 5.601" })
91
+ ]
92
+ }
93
+ );
94
+ }
95
+ var registry = /* @__PURE__ */ new Map();
96
+ function registerIcons(icons) {
97
+ for (const [name, icon] of Object.entries(icons)) {
98
+ registry.set(name, icon);
99
+ }
100
+ }
101
+ function resolveIconName(icon) {
102
+ if (typeof icon === "string") return icon;
103
+ return icon[icon.length - 1] ?? "";
104
+ }
105
+ var defaults = {
106
+ paperclip: Paperclip,
107
+ "arrow-up": ArrowUp,
108
+ "arrow-down": ArrowDown,
109
+ "arrow-left": ArrowLeft,
110
+ xmark: X,
111
+ check: Check,
112
+ pencil: Pencil,
113
+ play: Play,
114
+ "square-pen": SquarePen,
115
+ "rotate-right": RotateCw,
116
+ broom: Broom,
117
+ ellipsis: Ellipsis,
118
+ trash: Trash2,
119
+ "box-archive": Archive,
120
+ "clock-rotate-left": History,
121
+ "circle-exclamation": CircleAlert,
122
+ info: Info,
123
+ "triangle-exclamation": TriangleAlert,
124
+ "chevron-down": ChevronDown,
125
+ "chevron-left": ChevronLeft,
126
+ "chevron-right": ChevronRight,
127
+ loader: Loader2,
128
+ file: File,
129
+ clone: Copy,
130
+ copy: Copy,
131
+ bars: Menu,
132
+ "panel-left-close": PanelLeftClose,
133
+ "panel-left-open": PanelLeftOpen,
134
+ plus: Plus,
135
+ sun: Sun,
136
+ moon: Moon,
137
+ settings: Settings,
138
+ "log-out": LogOut,
139
+ search: Search,
140
+ clock: Clock,
141
+ "circle-check": CircleCheck,
142
+ "circle-xmark": CircleX,
143
+ "oauth-success": CircleCheck,
144
+ "oauth-error": CircleX,
145
+ "oauth-loading": Loader2,
146
+ "welcome-sparkle": Sparkle,
147
+ robot: Bot,
148
+ "book-open": BookOpen,
149
+ funnel: ListFilter,
150
+ plug: Plug,
151
+ "list-check": ListChecks,
152
+ lock: Lock,
153
+ lightbulb: Lightbulb,
154
+ wrench: Wrench,
155
+ cpu: Cpu,
156
+ cube: Box,
157
+ "list-ol": ListOrdered,
158
+ download: Download,
159
+ save: Save,
160
+ brain: Brain,
161
+ terminal: Terminal,
162
+ code: Code2,
163
+ expand: Maximize2,
164
+ "expand-alt": Maximize2,
165
+ compress: Minimize2,
166
+ "external-link": ExternalLink,
167
+ github: Github
168
+ };
169
+ for (const [name, icon] of Object.entries(defaults)) {
170
+ registry.set(name, icon);
171
+ }
172
+ function lookupIcon(name, themeIcons) {
173
+ if (themeIcons && name in themeIcons) {
174
+ return themeIcons[name];
175
+ }
176
+ return registry.get(name);
177
+ }
178
+
179
+ // src/icons/Icon.tsx
180
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
181
+ function isComponent(entry) {
182
+ if (typeof entry === "function") return true;
183
+ return typeof entry === "object" && entry !== null && !isValidElement(entry) && !Array.isArray(entry) && "$$typeof" in entry;
184
+ }
185
+ function renderEntry(entry, props) {
186
+ if (isComponent(entry)) {
187
+ const { size, className, style, ...aria } = props;
188
+ return createElement(entry, {
189
+ className: cn("inline-block shrink-0", className),
190
+ style,
191
+ size,
192
+ width: size,
193
+ height: size,
194
+ "aria-hidden": aria["aria-label"] ? void 0 : aria["aria-hidden"] ?? true,
195
+ "aria-label": aria["aria-label"],
196
+ ...aria["data-testid"] === void 0 ? {} : { "data-testid": aria["data-testid"] },
197
+ role: aria["aria-label"] ? "img" : void 0
198
+ });
199
+ }
200
+ return entry;
201
+ }
202
+ function Icon({ name, className, style, size = "1em", ...aria }) {
203
+ const themeIcons = useContext(ThemeContext)?.icons;
204
+ const resolved = resolveIconName(name);
205
+ const entry = lookupIcon(resolved, themeIcons);
206
+ if (!entry) return null;
207
+ return /* @__PURE__ */ jsx2(Fragment, { children: renderEntry(entry, { className, style, size, ...aria }) });
208
+ }
209
+
210
+ // src/atoms/primitives/Spinner.tsx
211
+ import "react";
212
+ import { jsx as jsx3 } from "react/jsx-runtime";
213
+ function Spinner({ size = 16, className, ...props }) {
214
+ const r = size / 2 * 0.75;
215
+ const cx = size / 2;
216
+ const circumference = 2 * Math.PI * r;
217
+ return /* @__PURE__ */ jsx3(
218
+ "svg",
219
+ {
220
+ role: "status",
221
+ "aria-label": "Loading",
222
+ width: size,
223
+ height: size,
224
+ viewBox: `0 0 ${size} ${size}`,
225
+ fill: "none",
226
+ className: cn("animate-spin", className),
227
+ ...props,
228
+ children: /* @__PURE__ */ jsx3(
229
+ "circle",
230
+ {
231
+ cx,
232
+ cy: cx,
233
+ r,
234
+ stroke: "currentColor",
235
+ strokeWidth: 2,
236
+ strokeLinecap: "round",
237
+ strokeDasharray: `${circumference * 0.75} ${circumference * 0.25}`,
238
+ opacity: 0.8
239
+ }
240
+ )
241
+ }
242
+ );
243
+ }
244
+
245
+ // src/atoms/lib/buttonClasses.ts
246
+ var variantClasses = {
247
+ default: "bg-primary-button-bg text-primary-button-text shadow hover:bg-primary-button-hover",
248
+ secondary: "border border-input-border bg-secondary-button-bg text-secondary-button-text shadow-sm hover:bg-secondary-button-hover",
249
+ ghost: "bg-ghost-button-bg text-ghost-button-text hover:bg-ghost-button-hover",
250
+ destructive: "bg-failure-bg text-failure-text shadow-sm hover:bg-failure-bg/90",
251
+ outline: "border border-input-border bg-secondary-button-bg text-secondary-button-text shadow-sm hover:bg-secondary-button-hover"
252
+ };
253
+ var sizeClasses = {
254
+ default: "h-9 px-4 py-2 text-sm",
255
+ sm: "h-8 rounded-md px-3 text-xs",
256
+ lg: "h-10 rounded-md px-8 text-base",
257
+ icon: "h-8 w-8"
258
+ };
259
+ var baseClasses = "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus-ring disabled:cursor-not-allowed disabled:opacity-50";
260
+ function auiButtonClass({
261
+ variant = "default",
262
+ size = "default",
263
+ className
264
+ } = {}) {
265
+ return cn(baseClasses, variantClasses[variant], sizeClasses[size], className);
266
+ }
267
+
268
+ // src/utils/getErrorMessage.ts
269
+ function isRecord(value) {
270
+ return value != null && typeof value === "object";
271
+ }
272
+ function readNonEmptyString(value) {
273
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
274
+ }
275
+ function messageFromErrorShaped(value) {
276
+ if (!isRecord(value)) return void 0;
277
+ const direct = readNonEmptyString(value.message);
278
+ if (direct != null) return direct;
279
+ if (isRecord(value.error)) {
280
+ return readNonEmptyString(value.error.message);
281
+ }
282
+ return void 0;
283
+ }
284
+ function stringifyError(error) {
285
+ if (typeof error === "string") return error;
286
+ if (isRecord(error)) {
287
+ try {
288
+ return JSON.stringify(error);
289
+ } catch {
290
+ return Object.prototype.toString.call(error);
291
+ }
292
+ }
293
+ return String(error);
294
+ }
295
+ function getErrorMessage(error, fallback) {
296
+ if (typeof error === "string") {
297
+ const trimmed = error.trim();
298
+ if (trimmed !== "") return error;
299
+ return fallback ?? error;
300
+ }
301
+ if (isRecord(error) && "body" in error) {
302
+ const fromBody = messageFromErrorShaped(error.body);
303
+ if (fromBody != null) return fromBody;
304
+ if (error.body != null) {
305
+ if (typeof error.body === "string") {
306
+ const trimmed = error.body.trim();
307
+ if (trimmed !== "") return error.body;
308
+ } else {
309
+ return stringifyError(error.body);
310
+ }
311
+ }
312
+ }
313
+ const fromShape = messageFromErrorShaped(error);
314
+ if (fromShape != null) return fromShape;
315
+ if (fallback != null) return fallback;
316
+ return stringifyError(error);
317
+ }
318
+
319
+ // src/atoms/lib/CompactLayoutContext.tsx
320
+ import { createContext, useContext as useContext2 } from "react";
321
+ import { jsx as jsx4 } from "react/jsx-runtime";
322
+ var CompactLayoutContext = createContext(false);
323
+ function CompactLayoutProvider({ children }) {
324
+ return /* @__PURE__ */ jsx4(CompactLayoutContext.Provider, { value: true, children });
325
+ }
326
+ function useCompactLayout() {
327
+ return useContext2(CompactLayoutContext);
328
+ }
329
+
330
+ // src/atoms/draft/DraftCatalogProvider.tsx
331
+ import { createContext as createContext2, useCallback, useContext as useContext3, useEffect, useMemo, useState } from "react";
332
+ import { jsx as jsx5 } from "react/jsx-runtime";
333
+ var DraftCatalogContext = createContext2(null);
334
+ var IDLE_ENSURE = () => void 0;
335
+ var IDLE_REFRESH = async () => void 0;
336
+ function DraftCatalogProvider({ children }) {
337
+ const server = useOptionalServer();
338
+ const existing = useContext3(DraftCatalogContext);
339
+ if (existing?.server === server) {
340
+ return children;
341
+ }
342
+ return /* @__PURE__ */ jsx5(DraftCatalogStore, { server, children });
343
+ }
344
+ function DraftCatalogStore({ server, children }) {
345
+ const [requestEpoch, setRequestEpoch] = useState(null);
346
+ const [models, setModels] = useState([]);
347
+ const [skills, setSkills] = useState([]);
348
+ const [connectors, setConnectors] = useState([]);
349
+ const [completedEpoch, setCompletedEpoch] = useState(null);
350
+ const [loading, setLoading] = useState(false);
351
+ const [error, setError] = useState(null);
352
+ const ensureLoaded = useCallback(() => {
353
+ setRequestEpoch((current) => current ?? 0);
354
+ }, []);
355
+ const refresh = useCallback(() => {
356
+ setRequestEpoch((current) => (current ?? -1) + 1);
357
+ }, []);
358
+ const refreshConnectors = useCallback(async () => {
359
+ if (!server) return;
360
+ setLoading(true);
361
+ setError(null);
362
+ try {
363
+ const nextConnectors = await server.getMcp();
364
+ setConnectors(nextConnectors);
365
+ } catch (reason) {
366
+ setError(getErrorMessage(reason, "Failed to load connectors."));
367
+ } finally {
368
+ setLoading(false);
369
+ }
370
+ }, [server]);
371
+ useEffect(() => {
372
+ if (requestEpoch === null || !server) return;
373
+ let cancelled = false;
374
+ setLoading(true);
375
+ setError(null);
376
+ void Promise.allSettled([server.getModels(), server.getSkills(), server.getMcp()]).then(([modelsResult, skillsResult, mcpResult]) => {
377
+ if (cancelled) return;
378
+ const errors = [];
379
+ if (modelsResult.status === "fulfilled") {
380
+ setModels(modelsResult.value);
381
+ } else {
382
+ errors.push(getErrorMessage(modelsResult.reason, "Failed to load models."));
383
+ }
384
+ if (skillsResult.status === "fulfilled") {
385
+ setSkills(skillsResult.value);
386
+ } else {
387
+ errors.push(getErrorMessage(skillsResult.reason, "Failed to load skills."));
388
+ }
389
+ if (mcpResult.status === "fulfilled") {
390
+ setConnectors(mcpResult.value);
391
+ } else {
392
+ errors.push(getErrorMessage(mcpResult.reason, "Failed to load connectors."));
393
+ }
394
+ setError(errors[0] ?? null);
395
+ }).finally(() => {
396
+ if (!cancelled) {
397
+ setCompletedEpoch(requestEpoch);
398
+ setLoading(false);
399
+ }
400
+ });
401
+ return () => {
402
+ cancelled = true;
403
+ };
404
+ }, [requestEpoch, server]);
405
+ const loaded = requestEpoch !== null && completedEpoch === requestEpoch;
406
+ const value = useMemo(
407
+ () => ({ server, models, skills, connectors, loaded, loading, error, ensureLoaded, refresh, refreshConnectors }),
408
+ [server, models, skills, connectors, loaded, loading, error, ensureLoaded, refresh, refreshConnectors]
409
+ );
410
+ return /* @__PURE__ */ jsx5(DraftCatalogContext.Provider, { value, children });
411
+ }
412
+ function useDraftCatalog() {
413
+ const ctx = useContext3(DraftCatalogContext);
414
+ if (ctx == null) {
415
+ return {
416
+ models: [],
417
+ skills: [],
418
+ connectors: [],
419
+ loaded: false,
420
+ loading: false,
421
+ error: null,
422
+ ensureLoaded: IDLE_ENSURE,
423
+ refresh: IDLE_ENSURE,
424
+ refreshConnectors: IDLE_REFRESH
425
+ };
426
+ }
427
+ return ctx;
428
+ }
429
+
430
+ export {
431
+ registerIcons,
432
+ Icon,
433
+ Spinner,
434
+ auiButtonClass,
435
+ getErrorMessage,
436
+ CompactLayoutProvider,
437
+ useCompactLayout,
438
+ DraftCatalogProvider,
439
+ useDraftCatalog
440
+ };
441
+ //# sourceMappingURL=chunk-EK6OUU24.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/icons/Icon.tsx","../src/icons/IconRegistry.tsx","../src/atoms/primitives/Spinner.tsx","../src/atoms/lib/buttonClasses.ts","../src/utils/getErrorMessage.ts","../src/atoms/lib/CompactLayoutContext.tsx","../src/atoms/draft/DraftCatalogProvider.tsx"],"sourcesContent":["import { createElement, isValidElement, useContext, type CSSProperties, type ReactNode } from 'react';\n\nimport { cn } from '../atoms/lib/cn.js';\nimport { ThemeContext } from '../theme/ThemeProvider.js';\nimport type { IconComponent, IconEntry, IconProps as ThemeIconProps } from '../theme/types.js';\nimport { lookupIcon, resolveIconName } from './IconRegistry.js';\n\nexport type IconProps = ThemeIconProps & {\n name: string | readonly string[];\n};\n\ntype IconRenderProps = {\n className?: string;\n style?: CSSProperties;\n size?: string | number;\n width?: string | number;\n height?: string | number;\n role?: string;\n} & Pick<IconProps, 'aria-hidden' | 'aria-label' | 'data-testid'>;\n\nfunction isComponent(entry: IconEntry): entry is IconComponent {\n if (typeof entry === 'function') return true;\n // Lucide (and other) icons are forwardRef objects, not functions\n return (\n typeof entry === 'object' &&\n entry !== null &&\n !isValidElement(entry) &&\n !Array.isArray(entry) &&\n '$$typeof' in entry\n );\n}\n\nfunction renderEntry(entry: IconEntry, props: IconRenderProps): ReactNode {\n if (isComponent(entry)) {\n const { size, className, style, ...aria } = props;\n return createElement(entry, {\n className: cn('inline-block shrink-0', className),\n style,\n size,\n width: size,\n height: size,\n 'aria-hidden': aria['aria-label'] ? undefined : (aria['aria-hidden'] ?? true),\n 'aria-label': aria['aria-label'],\n ...(aria['data-testid'] === undefined ? {} : { 'data-testid': aria['data-testid'] }),\n role: aria['aria-label'] ? 'img' : undefined,\n });\n }\n return entry;\n}\n\nexport function Icon({ name, className, style, size = '1em', ...aria }: IconProps) {\n const themeIcons = useContext(ThemeContext)?.icons;\n const resolved = resolveIconName(name);\n const entry = lookupIcon(resolved, themeIcons);\n if (!entry) return null;\n return <>{renderEntry(entry, { className, style, size, ...aria })}</>;\n}\n\nexport function useIcon(name: string | readonly string[]): IconEntry | undefined {\n const themeIcons = useContext(ThemeContext)?.icons;\n return lookupIcon(resolveIconName(name), themeIcons);\n}\n","import {\n Archive,\n ArrowDown,\n ArrowLeft,\n ArrowUp,\n BookOpen,\n Bot,\n Box,\n Brain,\n Check,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n CircleAlert,\n CircleCheck,\n CircleX,\n Clock,\n Code2,\n Copy,\n Cpu,\n Download,\n Ellipsis,\n ExternalLink,\n File,\n Github,\n History,\n Info,\n Lightbulb,\n ListChecks,\n ListFilter,\n ListOrdered,\n Loader2,\n Lock,\n LogOut,\n Maximize2,\n Menu,\n Minimize2,\n Moon,\n PanelLeftClose,\n PanelLeftOpen,\n Paperclip,\n Pencil,\n Play,\n Plug,\n Plus,\n RotateCw,\n Save,\n Search,\n Settings,\n Sparkle,\n SquarePen,\n Sun,\n Terminal,\n Trash2,\n TriangleAlert,\n Wrench,\n X,\n} from 'lucide-react';\nimport type { SVGProps } from 'react';\n\nimport type { IconEntry, IconMap } from '../theme/types.js';\n\n/** Lucide `broom` (https://lucide.dev/icons/broom) — not in lucide-react 0.x. */\nfunction Broom({ size = 24, ...props }: SVGProps<SVGSVGElement> & { size?: string | number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M13.5 10.5 22 2\" />\n <path d=\"M14.734 13.841a2 2 0 00-.314-2.42L12.58 9.58a2 2 0 00-2.421-.314l-7.657 4.461A1 1 0 002.3 15.3l6.403 6.403a1 1 0 001.571-.204z\" />\n <path d=\"m5 18 2-2\" />\n <path d=\"m7.699 10.7 5.602 5.601\" />\n </svg>\n );\n}\n\nconst registry = new Map<string, IconEntry>();\n\nexport function registerIcon(name: string, icon: IconEntry): void {\n registry.set(name, icon);\n}\n\nexport function registerIcons(icons: IconMap): void {\n for (const [name, icon] of Object.entries(icons)) {\n registry.set(name, icon);\n }\n}\n\nexport function getIcon(name: string): IconEntry | undefined {\n return registry.get(name);\n}\n\nexport function resolveIconName(icon: string | readonly string[]): string {\n if (typeof icon === 'string') return icon;\n // e.g. [\"far\", \"clone\"] → \"clone\"\n return icon[icon.length - 1] ?? '';\n}\n\nconst defaults: Record<string, IconEntry> = {\n paperclip: Paperclip,\n 'arrow-up': ArrowUp,\n 'arrow-down': ArrowDown,\n 'arrow-left': ArrowLeft,\n xmark: X,\n check: Check,\n pencil: Pencil,\n play: Play,\n 'square-pen': SquarePen,\n 'rotate-right': RotateCw,\n broom: Broom,\n ellipsis: Ellipsis,\n trash: Trash2,\n 'box-archive': Archive,\n 'clock-rotate-left': History,\n 'circle-exclamation': CircleAlert,\n info: Info,\n 'triangle-exclamation': TriangleAlert,\n 'chevron-down': ChevronDown,\n 'chevron-left': ChevronLeft,\n 'chevron-right': ChevronRight,\n loader: Loader2,\n file: File,\n clone: Copy,\n copy: Copy,\n bars: Menu,\n 'panel-left-close': PanelLeftClose,\n 'panel-left-open': PanelLeftOpen,\n plus: Plus,\n sun: Sun,\n moon: Moon,\n settings: Settings,\n 'log-out': LogOut,\n search: Search,\n clock: Clock,\n 'circle-check': CircleCheck,\n 'circle-xmark': CircleX,\n 'oauth-success': CircleCheck,\n 'oauth-error': CircleX,\n 'oauth-loading': Loader2,\n 'welcome-sparkle': Sparkle,\n robot: Bot,\n 'book-open': BookOpen,\n funnel: ListFilter,\n plug: Plug,\n 'list-check': ListChecks,\n lock: Lock,\n lightbulb: Lightbulb,\n wrench: Wrench,\n cpu: Cpu,\n cube: Box,\n 'list-ol': ListOrdered,\n download: Download,\n save: Save,\n brain: Brain,\n terminal: Terminal,\n code: Code2,\n expand: Maximize2,\n 'expand-alt': Maximize2,\n compress: Minimize2,\n 'external-link': ExternalLink,\n github: Github,\n};\n\nfor (const [name, icon] of Object.entries(defaults)) {\n registry.set(name, icon);\n}\n\n/** Merge host `theme.icons` over the registry for a single lookup (does not mutate). */\nexport function lookupIcon(name: string, themeIcons?: IconMap): IconEntry | undefined {\n if (themeIcons && name in themeIcons) {\n return themeIcons[name];\n }\n return registry.get(name);\n}\n","import React from 'react';\n\nimport { cn } from '../lib/cn.js';\n\nexport type SpinnerProps = React.SVGProps<SVGSVGElement> & {\n size?: number;\n};\n\nexport function Spinner({ size = 16, className, ...props }: SpinnerProps) {\n const r = (size / 2) * 0.75;\n const cx = size / 2;\n const circumference = 2 * Math.PI * r;\n\n return (\n <svg\n role=\"status\"\n aria-label=\"Loading\"\n width={size}\n height={size}\n viewBox={`0 0 ${size} ${size}`}\n fill=\"none\"\n className={cn('animate-spin', className)}\n {...props}\n >\n <circle\n cx={cx}\n cy={cx}\n r={r}\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeDasharray={`${circumference * 0.75} ${circumference * 0.25}`}\n opacity={0.8}\n />\n </svg>\n );\n}\n","import { cn } from './cn.js';\n\nexport type AuiButtonVariant = 'default' | 'secondary' | 'ghost' | 'destructive' | 'outline';\nexport type AuiButtonSize = 'default' | 'sm' | 'lg' | 'icon';\n\nconst variantClasses: Record<AuiButtonVariant, string> = {\n default: 'bg-primary-button-bg text-primary-button-text shadow hover:bg-primary-button-hover',\n secondary:\n 'border border-input-border bg-secondary-button-bg text-secondary-button-text shadow-sm hover:bg-secondary-button-hover',\n ghost: 'bg-ghost-button-bg text-ghost-button-text hover:bg-ghost-button-hover',\n destructive: 'bg-failure-bg text-failure-text shadow-sm hover:bg-failure-bg/90',\n outline:\n 'border border-input-border bg-secondary-button-bg text-secondary-button-text shadow-sm hover:bg-secondary-button-hover',\n};\n\nconst sizeClasses: Record<AuiButtonSize, string> = {\n default: 'h-9 px-4 py-2 text-sm',\n sm: 'h-8 rounded-md px-3 text-xs',\n lg: 'h-10 rounded-md px-8 text-base',\n icon: 'h-8 w-8',\n};\n\nconst baseClasses =\n 'inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus-ring disabled:cursor-not-allowed disabled:opacity-50';\n\n/** Shared Button / native `<button>` className helper (SDK utilities are layered). */\nexport function auiButtonClass({\n variant = 'default',\n size = 'default',\n className,\n}: {\n variant?: AuiButtonVariant;\n size?: AuiButtonSize;\n className?: string;\n} = {}): string {\n return cn(baseClasses, variantClasses[variant], sizeClasses[size], className);\n}\n","function isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === 'object';\n}\n\nfunction readNonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== '' ? value : undefined;\n}\n\n/**\n * Reads a display string from API-shaped errors: `message`, then nested\n * `error.message`. When neither exists, stringifies the value.\n */\nfunction messageFromErrorShaped(value: unknown): string | undefined {\n if (!isRecord(value)) return undefined;\n const direct = readNonEmptyString(value.message);\n if (direct != null) return direct;\n if (isRecord(value.error)) {\n return readNonEmptyString(value.error.message);\n }\n return undefined;\n}\n\nfunction stringifyError(error: unknown): string {\n if (typeof error === 'string') return error;\n if (isRecord(error)) {\n try {\n return JSON.stringify(error);\n } catch {\n return Object.prototype.toString.call(error);\n }\n }\n return String(error);\n}\n\n/**\n * User-facing error text. Prefers HTTP body `{ error: { message } }` when\n * present (e.g. TrueForgeError), then `error.message` / `error.error.message`,\n * then `fallback`, then a string form of the value.\n */\nexport function getErrorMessage(error: unknown, fallback?: string): string {\n if (typeof error === 'string') {\n const trimmed = error.trim();\n if (trimmed !== '') return error;\n return fallback ?? error;\n }\n\n if (isRecord(error) && 'body' in error) {\n const fromBody = messageFromErrorShaped(error.body);\n if (fromBody != null) return fromBody;\n if (error.body != null) {\n if (typeof error.body === 'string') {\n const trimmed = error.body.trim();\n if (trimmed !== '') return error.body;\n } else {\n return stringifyError(error.body);\n }\n }\n }\n\n const fromShape = messageFromErrorShaped(error);\n if (fromShape != null) return fromShape;\n\n if (fallback != null) return fallback;\n return stringifyError(error);\n}\n","'use client';\n\nimport { createContext, useContext, type ReactNode } from 'react';\n\nconst CompactLayoutContext = createContext(false);\n\nexport function CompactLayoutProvider({ children }: { children: ReactNode }) {\n return <CompactLayoutContext.Provider value>{children}</CompactLayoutContext.Provider>;\n}\n\nexport function useCompactLayout() {\n return useContext(CompactLayoutContext);\n}\n","'use client';\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';\n\nimport { useOptionalServer } from '../../server/ServerContext.js';\nimport type { AgentSkill, AgentUIServer, ConnectorState, ModelSelection } from '../../server/types.js';\nimport { getErrorMessage } from '../../utils/getErrorMessage.js';\n\ntype DraftCatalogValue = {\n models: ModelSelection[];\n skills: AgentSkill[];\n connectors: ConnectorState[];\n loaded: boolean;\n loading: boolean;\n error: string | null;\n /** Kick off catalog fetch (idempotent). Call when a picker opens. */\n ensureLoaded: () => void;\n /** Reload all collections after settings mutate catalog-backed configuration. */\n refresh: () => void;\n /** Refresh connector auth state without reloading unrelated catalogs. */\n refreshConnectors: () => Promise<void>;\n};\n\ntype DraftCatalogContextValue = DraftCatalogValue & {\n server: AgentUIServer | null;\n};\n\nconst DraftCatalogContext = createContext<DraftCatalogContextValue | null>(null);\n\nconst IDLE_ENSURE = () => undefined;\nconst IDLE_REFRESH = async () => undefined;\n\nexport function DraftCatalogProvider({ children }: { children: ReactNode }) {\n const server = useOptionalServer();\n const existing = useContext(DraftCatalogContext);\n if (existing?.server === server) {\n return children;\n }\n return <DraftCatalogStore server={server}>{children}</DraftCatalogStore>;\n}\n\nfunction DraftCatalogStore({ server, children }: { server: AgentUIServer | null; children: ReactNode }) {\n const [requestEpoch, setRequestEpoch] = useState<number | null>(null);\n const [models, setModels] = useState<ModelSelection[]>([]);\n const [skills, setSkills] = useState<AgentSkill[]>([]);\n const [connectors, setConnectors] = useState<ConnectorState[]>([]);\n const [completedEpoch, setCompletedEpoch] = useState<number | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const ensureLoaded = useCallback(() => {\n setRequestEpoch(current => current ?? 0);\n }, []);\n\n const refresh = useCallback(() => {\n setRequestEpoch(current => (current ?? -1) + 1);\n }, []);\n\n const refreshConnectors = useCallback(async () => {\n if (!server) return;\n setLoading(true);\n setError(null);\n try {\n const nextConnectors = await server.getMcp();\n setConnectors(nextConnectors);\n } catch (reason: unknown) {\n setError(getErrorMessage(reason, 'Failed to load connectors.'));\n } finally {\n setLoading(false);\n }\n }, [server]);\n\n useEffect(() => {\n if (requestEpoch === null || !server) return;\n let cancelled = false;\n setLoading(true);\n setError(null);\n // Settle each list alone so one failing picker does not blank the others.\n void Promise.allSettled([server.getModels(), server.getSkills(), server.getMcp()])\n .then(([modelsResult, skillsResult, mcpResult]) => {\n if (cancelled) return;\n const errors: string[] = [];\n if (modelsResult.status === 'fulfilled') {\n setModels(modelsResult.value);\n } else {\n errors.push(getErrorMessage(modelsResult.reason, 'Failed to load models.'));\n }\n if (skillsResult.status === 'fulfilled') {\n setSkills(skillsResult.value);\n } else {\n errors.push(getErrorMessage(skillsResult.reason, 'Failed to load skills.'));\n }\n if (mcpResult.status === 'fulfilled') {\n setConnectors(mcpResult.value);\n } else {\n errors.push(getErrorMessage(mcpResult.reason, 'Failed to load connectors.'));\n }\n setError(errors[0] ?? null);\n })\n .finally(() => {\n if (!cancelled) {\n setCompletedEpoch(requestEpoch);\n setLoading(false);\n }\n });\n return () => {\n cancelled = true;\n };\n }, [requestEpoch, server]);\n\n const loaded = requestEpoch !== null && completedEpoch === requestEpoch;\n const value = useMemo(\n () => ({ server, models, skills, connectors, loaded, loading, error, ensureLoaded, refresh, refreshConnectors }),\n [server, models, skills, connectors, loaded, loading, error, ensureLoaded, refresh, refreshConnectors],\n );\n\n return <DraftCatalogContext.Provider value={value}>{children}</DraftCatalogContext.Provider>;\n}\n\nexport function useDraftCatalog(): DraftCatalogValue {\n const ctx = useContext(DraftCatalogContext);\n if (ctx == null) {\n return {\n models: [],\n skills: [],\n connectors: [],\n loaded: false,\n loading: false,\n error: null,\n ensureLoaded: IDLE_ENSURE,\n refresh: IDLE_ENSURE,\n refreshConnectors: IDLE_REFRESH,\n };\n }\n return ctx;\n}\n"],"mappings":"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","names":["jsx","jsx","useContext","jsx","createContext","useContext","jsx","createContext","useContext"]}
@@ -0,0 +1,64 @@
1
+ // src/hooks/useComposerBusyState.ts
2
+ import { useThreadIsRunning } from "@assistant-ui/core/react";
3
+ import { createContext, createElement, useCallback, useContext, useEffect, useState } from "react";
4
+ var ComposerBusyContext = createContext(null);
5
+ var busyFailureListeners = /* @__PURE__ */ new Set();
6
+ function notifyComposerBusyFailure() {
7
+ for (const listener of busyFailureListeners) {
8
+ listener();
9
+ }
10
+ }
11
+ function useComposerBusyStateValue() {
12
+ const isRunning = useThreadIsRunning();
13
+ const [isSubmitting, setIsSubmitting] = useState(false);
14
+ useEffect(() => {
15
+ if (!isRunning) {
16
+ setIsSubmitting(false);
17
+ }
18
+ }, [isRunning]);
19
+ useEffect(() => {
20
+ const clearSubmitting = () => {
21
+ setIsSubmitting(false);
22
+ };
23
+ busyFailureListeners.add(clearSubmitting);
24
+ return () => {
25
+ busyFailureListeners.delete(clearSubmitting);
26
+ };
27
+ }, []);
28
+ const isBusy = isRunning || isSubmitting;
29
+ const resetBusy = useCallback(() => {
30
+ setIsSubmitting(false);
31
+ }, []);
32
+ const send = useCallback((sendFn) => {
33
+ setIsSubmitting(true);
34
+ try {
35
+ const result = sendFn();
36
+ if (result != null && typeof result.then === "function") {
37
+ void result.catch(() => {
38
+ setIsSubmitting(false);
39
+ });
40
+ }
41
+ } catch {
42
+ setIsSubmitting(false);
43
+ }
44
+ }, []);
45
+ return { isBusy, isRunning, isSubmitting, send, resetBusy };
46
+ }
47
+ function ComposerBusyProvider({ children }) {
48
+ const value = useComposerBusyStateValue();
49
+ return createElement(ComposerBusyContext.Provider, { value }, children);
50
+ }
51
+ function useComposerBusyState() {
52
+ const value = useContext(ComposerBusyContext);
53
+ if (value == null) {
54
+ throw new Error("useComposerBusyState must be used within ComposerBusyProvider");
55
+ }
56
+ return value;
57
+ }
58
+
59
+ export {
60
+ notifyComposerBusyFailure,
61
+ ComposerBusyProvider,
62
+ useComposerBusyState
63
+ };
64
+ //# sourceMappingURL=chunk-FXCYLEFB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useComposerBusyState.ts"],"sourcesContent":["'use client';\n\nimport { useThreadIsRunning } from '@assistant-ui/core/react';\nimport { createContext, createElement, useCallback, useContext, useEffect, useState, type ReactNode } from 'react';\n\nexport type ComposerBusyState = {\n /** True while submitting or while the turn stream is active. */\n isBusy: boolean;\n isRunning: boolean;\n isSubmitting: boolean;\n send: (sendFn: () => void | Promise<void>) => void;\n resetBusy: () => void;\n};\n\nconst ComposerBusyContext = createContext<ComposerBusyState | null>(null);\n\n/**\n * `aui.composer().send()` returns void and swallows `onNew` rejections, so the\n * optimistic submitting flag cannot clear via Promise.catch. First-message\n * failures (e.g. createSession) also never flip `useThreadIsRunning()`. Chat\n * runtime `onError` calls this so the Cancel/spinner state does not stick.\n */\nconst busyFailureListeners = new Set<() => void>();\n\nexport function notifyComposerBusyFailure(): void {\n for (const listener of busyFailureListeners) {\n listener();\n }\n}\n\nfunction useComposerBusyStateValue(): ComposerBusyState {\n const isRunning = useThreadIsRunning();\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n useEffect(() => {\n if (!isRunning) {\n setIsSubmitting(false);\n }\n }, [isRunning]);\n\n useEffect(() => {\n const clearSubmitting = () => {\n setIsSubmitting(false);\n };\n busyFailureListeners.add(clearSubmitting);\n return () => {\n busyFailureListeners.delete(clearSubmitting);\n };\n }, []);\n\n const isBusy = isRunning || isSubmitting;\n\n const resetBusy = useCallback(() => {\n setIsSubmitting(false);\n }, []);\n\n const send = useCallback((sendFn: () => void | Promise<void>) => {\n setIsSubmitting(true);\n try {\n const result = sendFn();\n if (result != null && typeof result.then === 'function') {\n void result.catch(() => {\n setIsSubmitting(false);\n });\n }\n } catch {\n setIsSubmitting(false);\n }\n }, []);\n\n return { isBusy, isRunning, isSubmitting, send, resetBusy };\n}\n\nexport function ComposerBusyProvider({ children }: { children: ReactNode }) {\n const value = useComposerBusyStateValue();\n return createElement(ComposerBusyContext.Provider, { value }, children);\n}\n\n/**\n * Optimistic composer busy state shared across the thread. `useThreadIsRunning()`\n * only becomes true once the turn stream starts; this hook flips to busy\n * immediately on submit so the send button shows loading before session setup /\n * turn API work finishes.\n *\n * Must be used within `<ComposerBusyProvider>` (wired by default in `<Thread />`).\n */\nexport function useComposerBusyState(): ComposerBusyState {\n const value = useContext(ComposerBusyContext);\n if (value == null) {\n throw new Error('useComposerBusyState must be used within ComposerBusyProvider');\n }\n return value;\n}\n"],"mappings":";AAEA,SAAS,0BAA0B;AACnC,SAAS,eAAe,eAAe,aAAa,YAAY,WAAW,gBAAgC;AAW3G,IAAM,sBAAsB,cAAwC,IAAI;AAQxE,IAAM,uBAAuB,oBAAI,IAAgB;AAE1C,SAAS,4BAAkC;AAChD,aAAW,YAAY,sBAAsB;AAC3C,aAAS;AAAA,EACX;AACF;AAEA,SAAS,4BAA+C;AACtD,QAAM,YAAY,mBAAmB;AACrC,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AAEtD,YAAU,MAAM;AACd,QAAI,CAAC,WAAW;AACd,sBAAgB,KAAK;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,YAAU,MAAM;AACd,UAAM,kBAAkB,MAAM;AAC5B,sBAAgB,KAAK;AAAA,IACvB;AACA,yBAAqB,IAAI,eAAe;AACxC,WAAO,MAAM;AACX,2BAAqB,OAAO,eAAe;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,SAAS,aAAa;AAE5B,QAAM,YAAY,YAAY,MAAM;AAClC,oBAAgB,KAAK;AAAA,EACvB,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO,YAAY,CAAC,WAAuC;AAC/D,oBAAgB,IAAI;AACpB,QAAI;AACF,YAAM,SAAS,OAAO;AACtB,UAAI,UAAU,QAAQ,OAAO,OAAO,SAAS,YAAY;AACvD,aAAK,OAAO,MAAM,MAAM;AACtB,0BAAgB,KAAK;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AACN,sBAAgB,KAAK;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,QAAQ,WAAW,cAAc,MAAM,UAAU;AAC5D;AAEO,SAAS,qBAAqB,EAAE,SAAS,GAA4B;AAC1E,QAAM,QAAQ,0BAA0B;AACxC,SAAO,cAAc,oBAAoB,UAAU,EAAE,MAAM,GAAG,QAAQ;AACxE;AAUO,SAAS,uBAA0C;AACxD,QAAM,QAAQ,WAAW,mBAAmB;AAC5C,MAAI,SAAS,MAAM;AACjB,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AACA,SAAO;AACT;","names":[]}