@timbal-ai/timbal-react 0.5.4 → 0.6.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 (44) hide show
  1. package/README.md +128 -4
  2. package/dist/app.cjs +5311 -0
  3. package/dist/app.d.cts +29 -0
  4. package/dist/app.d.ts +29 -0
  5. package/dist/app.esm.js +81 -0
  6. package/dist/chart-artifact-C71dk4xI.d.ts +329 -0
  7. package/dist/chart-artifact-CPEpOmtV.d.cts +329 -0
  8. package/dist/chat-CWtQWDtJ.d.cts +650 -0
  9. package/dist/chat-CWtQWDtJ.d.ts +650 -0
  10. package/dist/chat.cjs +4162 -0
  11. package/dist/chat.d.cts +13 -0
  12. package/dist/chat.d.ts +13 -0
  13. package/dist/chat.esm.js +51 -0
  14. package/dist/chunk-4TCJQSIX.esm.js +565 -0
  15. package/dist/chunk-IYENDIRY.esm.js +119 -0
  16. package/dist/chunk-KC5QLVUG.esm.js +22 -0
  17. package/dist/chunk-M4V6Q6XO.esm.js +1082 -0
  18. package/dist/chunk-OFHLFNJH.esm.js +138 -0
  19. package/dist/chunk-OVHR7J3J.esm.js +1574 -0
  20. package/dist/chunk-WLTW56MC.esm.js +66 -0
  21. package/dist/chunk-YJQLLFKP.esm.js +3672 -0
  22. package/dist/index.cjs +1823 -359
  23. package/dist/index.d.cts +15 -931
  24. package/dist/index.d.ts +15 -931
  25. package/dist/index.esm.js +187 -5578
  26. package/dist/layout-B9VayJhZ.d.cts +75 -0
  27. package/dist/layout-CQWngNQ7.d.ts +75 -0
  28. package/dist/studio.cjs +5734 -0
  29. package/dist/studio.d.cts +15 -0
  30. package/dist/studio.d.ts +15 -0
  31. package/dist/studio.esm.js +27 -0
  32. package/dist/styles.css +52 -2
  33. package/dist/timbal-v2-button-F4-z7m33.d.cts +40 -0
  34. package/dist/timbal-v2-button-F4-z7m33.d.ts +40 -0
  35. package/dist/ui.cjs +720 -0
  36. package/dist/ui.d.cts +74 -0
  37. package/dist/ui.d.ts +74 -0
  38. package/dist/ui.esm.js +44 -0
  39. package/dist/welcome--80i_O0p.d.cts +190 -0
  40. package/dist/welcome-BOizSp5h.d.ts +190 -0
  41. package/package.json +35 -3
  42. package/scripts/dev-linked.mjs +66 -0
  43. package/vite/local-dev.d.ts +4 -0
  44. package/vite/local-dev.mjs +71 -0
@@ -0,0 +1,1574 @@
1
+ import {
2
+ DOM_IDS,
3
+ SIDEBAR_INSET_PX_COLLAPSED,
4
+ SIDEBAR_INSET_PX_EXPANDED,
5
+ SIDEBAR_MOBILE_PX,
6
+ SIDEBAR_WIDTH_COLLAPSED_PX,
7
+ SIDEBAR_WIDTH_PX,
8
+ STORAGE_KEYS,
9
+ STUDIO_SIDEBAR_ENTRIES_OUT_S,
10
+ STUDIO_SIDEBAR_EXPAND_REVEAL_FRAC,
11
+ STUDIO_SIDEBAR_WIDTH_S,
12
+ studioChromeShellStyle,
13
+ studioSidebarBackdropTransition,
14
+ studioSidebarDrawerTransition,
15
+ studioSidebarEntriesContainerVariants,
16
+ studioSidebarEntriesTransition,
17
+ studioSidebarEntryItemVariants,
18
+ studioSidebarWidthTransition,
19
+ useShellInsetReporter
20
+ } from "./chunk-OFHLFNJH.esm.js";
21
+ import {
22
+ WorkforceSelector
23
+ } from "./chunk-WLTW56MC.esm.js";
24
+ import {
25
+ Composer,
26
+ TimbalChat,
27
+ authFetch,
28
+ clearTokens,
29
+ fetchCurrentUser,
30
+ getRefreshToken,
31
+ luxuryEase,
32
+ refreshAccessToken,
33
+ setAccessToken,
34
+ setRefreshToken,
35
+ studioPlaygroundGradientClass,
36
+ studioSidebarCollapsedRailItemActiveClass,
37
+ studioSidebarCollapsedRailItemIdleClass,
38
+ studioSidebarNavItemActiveClass,
39
+ studioSidebarNavItemClass,
40
+ studioSidebarNavItemIdleClass,
41
+ studioSidebarNavItemLayout,
42
+ studioSidebarPanelClass,
43
+ studioTopbarIconPillClass,
44
+ studioTopbarPillHeightClass,
45
+ useTimbalRuntime
46
+ } from "./chunk-YJQLLFKP.esm.js";
47
+ import {
48
+ PillSegmentedTabs
49
+ } from "./chunk-IYENDIRY.esm.js";
50
+ import {
51
+ Avatar,
52
+ AvatarFallback,
53
+ AvatarImage,
54
+ TimbalV2Button,
55
+ Tooltip,
56
+ TooltipContent,
57
+ TooltipTrigger,
58
+ cn
59
+ } from "./chunk-4TCJQSIX.esm.js";
60
+
61
+ // src/hooks/use-workforces.ts
62
+ import { useEffect, useMemo, useRef, useState } from "react";
63
+ function useWorkforces(options = {}) {
64
+ const { baseUrl = "/api", fetch: fetchFn, pickInitial, enabled = true } = options;
65
+ const [workforces, setWorkforces] = useState([]);
66
+ const [selectedId, setSelectedId] = useState("");
67
+ const [isLoading, setIsLoading] = useState(enabled);
68
+ const [error, setError] = useState(null);
69
+ const fetchFnRef = useRef(fetchFn ?? authFetch);
70
+ useEffect(() => {
71
+ fetchFnRef.current = fetchFn ?? authFetch;
72
+ }, [fetchFn]);
73
+ const pickInitialRef = useRef(pickInitial);
74
+ useEffect(() => {
75
+ pickInitialRef.current = pickInitial;
76
+ }, [pickInitial]);
77
+ const load = useMemo(() => {
78
+ return async () => {
79
+ if (!enabled) {
80
+ setIsLoading(false);
81
+ return;
82
+ }
83
+ setIsLoading(true);
84
+ setError(null);
85
+ try {
86
+ const res = await fetchFnRef.current(`${baseUrl}/workforce`);
87
+ if (!res.ok) throw new Error(`Failed to load workforces (${res.status})`);
88
+ const data = await res.json();
89
+ setWorkforces(data);
90
+ setSelectedId((current) => {
91
+ if (current && data.some((w) => idOf(w) === current)) return current;
92
+ const initial = pickInitialRef.current?.(data) ?? data.find((w) => w.type === "agent") ?? data[0];
93
+ return initial ? idOf(initial) : "";
94
+ });
95
+ } catch (err) {
96
+ setError(err instanceof Error ? err : new Error(String(err)));
97
+ } finally {
98
+ setIsLoading(false);
99
+ }
100
+ };
101
+ }, [baseUrl, enabled]);
102
+ useEffect(() => {
103
+ load();
104
+ }, [load]);
105
+ const selected = useMemo(
106
+ () => workforces.find((w) => idOf(w) === selectedId),
107
+ [workforces, selectedId]
108
+ );
109
+ return {
110
+ workforces,
111
+ selectedId,
112
+ setSelectedId,
113
+ selected,
114
+ isLoading,
115
+ error,
116
+ refresh: load
117
+ };
118
+ }
119
+ function idOf(item) {
120
+ return item.id ?? item.uid ?? item.name ?? "";
121
+ }
122
+
123
+ // src/studio/shell/chat-shell.tsx
124
+ import { jsx, jsxs } from "react/jsx-runtime";
125
+ var TimbalChatShell = ({
126
+ workforceId,
127
+ brand,
128
+ headerActions,
129
+ hideWorkforceSelector,
130
+ className,
131
+ headerClassName,
132
+ baseUrl,
133
+ fetch: fetch2,
134
+ ...chatProps
135
+ }) => {
136
+ const { workforces, selectedId, setSelectedId } = useWorkforces({
137
+ baseUrl,
138
+ fetch: fetch2
139
+ });
140
+ const effectiveId = workforceId ?? selectedId;
141
+ const showSelector = !hideWorkforceSelector && !workforceId && workforces.length > 0;
142
+ return /* @__PURE__ */ jsxs(
143
+ "div",
144
+ {
145
+ className: cn(
146
+ "aui-chat-shell relative flex h-dvh flex-col overflow-hidden bg-background",
147
+ className
148
+ ),
149
+ style: studioChromeShellStyle,
150
+ children: [
151
+ /* @__PURE__ */ jsx(
152
+ "div",
153
+ {
154
+ className: cn(
155
+ "pointer-events-none absolute inset-0 z-0",
156
+ studioPlaygroundGradientClass
157
+ ),
158
+ "aria-hidden": true
159
+ }
160
+ ),
161
+ /* @__PURE__ */ jsxs(
162
+ "header",
163
+ {
164
+ className: cn(
165
+ "aui-chat-shell-header relative z-10 flex shrink-0 items-center justify-between bg-transparent px-4 pt-[var(--studio-topbar-gap)] pb-2",
166
+ headerClassName
167
+ ),
168
+ style: { minHeight: "var(--studio-topbar-height)" },
169
+ children: [
170
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
171
+ brand,
172
+ showSelector && /* @__PURE__ */ jsx(
173
+ WorkforceSelector,
174
+ {
175
+ workforces,
176
+ value: selectedId,
177
+ onChange: setSelectedId
178
+ }
179
+ )
180
+ ] }),
181
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-1", children: headerActions })
182
+ ]
183
+ }
184
+ ),
185
+ /* @__PURE__ */ jsx(
186
+ TimbalChat,
187
+ {
188
+ workforceId: effectiveId,
189
+ baseUrl,
190
+ fetch: fetch2,
191
+ className: "relative z-10 min-h-0 flex-1",
192
+ ...chatProps
193
+ },
194
+ effectiveId
195
+ )
196
+ ]
197
+ }
198
+ );
199
+ };
200
+
201
+ // src/studio/sidebar/sidebar-backdrop.tsx
202
+ import { AnimatePresence, motion, useReducedMotion } from "motion/react";
203
+ import { jsx as jsx2 } from "react/jsx-runtime";
204
+ var StudioSidebarBackdrop = ({
205
+ open,
206
+ onClose
207
+ }) => {
208
+ const reducedMotion = useReducedMotion();
209
+ return /* @__PURE__ */ jsx2(AnimatePresence, { children: open ? /* @__PURE__ */ jsx2(
210
+ motion.button,
211
+ {
212
+ type: "button",
213
+ className: "fixed inset-0 z-40 bg-foreground/30 backdrop-blur-[2px] md:hidden",
214
+ "aria-label": "Close menu",
215
+ initial: { opacity: 0 },
216
+ animate: { opacity: 1 },
217
+ exit: { opacity: 0 },
218
+ transition: studioSidebarBackdropTransition(!!reducedMotion),
219
+ onClick: onClose
220
+ }
221
+ ) : null });
222
+ };
223
+
224
+ // src/auth/provider.tsx
225
+ import {
226
+ createContext,
227
+ useCallback,
228
+ useContext,
229
+ useEffect as useEffect2,
230
+ useState as useState2
231
+ } from "react";
232
+ import { jsx as jsx3 } from "react/jsx-runtime";
233
+ function isInsideIframe() {
234
+ try {
235
+ return typeof window !== "undefined" && window.self !== window.top;
236
+ } catch {
237
+ return true;
238
+ }
239
+ }
240
+ var SessionContext = createContext(void 0);
241
+ var useSession = () => {
242
+ const context = useContext(SessionContext);
243
+ if (context === void 0) {
244
+ throw new Error("useSession must be used within a SessionProvider");
245
+ }
246
+ return context;
247
+ };
248
+ var useOptionalSession = () => {
249
+ const context = useContext(SessionContext);
250
+ return context ?? null;
251
+ };
252
+ var SessionProvider = ({
253
+ children,
254
+ enabled = true
255
+ }) => {
256
+ const [user, setUser] = useState2(null);
257
+ const [loading, setLoading] = useState2(enabled);
258
+ const [embedded] = useState2(isInsideIframe);
259
+ useEffect2(() => {
260
+ if (!enabled) {
261
+ setLoading(false);
262
+ return;
263
+ }
264
+ let ignore = false;
265
+ const restoreSession = async () => {
266
+ try {
267
+ const u = await fetchCurrentUser();
268
+ if (ignore) return;
269
+ if (u) {
270
+ setUser(u);
271
+ setLoading(false);
272
+ return;
273
+ }
274
+ if (getRefreshToken()) {
275
+ const ok = await refreshAccessToken();
276
+ if (ignore) return;
277
+ if (ok) {
278
+ const refreshedUser = await fetchCurrentUser();
279
+ if (ignore) return;
280
+ if (refreshedUser) {
281
+ setUser(refreshedUser);
282
+ setLoading(false);
283
+ return;
284
+ }
285
+ }
286
+ }
287
+ } catch {
288
+ if (ignore) return;
289
+ clearTokens();
290
+ }
291
+ if (!ignore && !embedded) {
292
+ setLoading(false);
293
+ }
294
+ };
295
+ restoreSession();
296
+ let messageCleanup;
297
+ if (embedded) {
298
+ const handleMessage = async (event) => {
299
+ if (ignore) return;
300
+ if (event.data?.type !== "timbal:auth" || !event.data.token) return;
301
+ setAccessToken(event.data.token);
302
+ if (event.data.refreshToken) {
303
+ setRefreshToken(event.data.refreshToken);
304
+ }
305
+ const u = await fetchCurrentUser();
306
+ if (!ignore) {
307
+ setUser(u);
308
+ setLoading(false);
309
+ }
310
+ };
311
+ window.addEventListener("message", handleMessage);
312
+ window.parent.postMessage({ type: "timbal:request-session" }, "*");
313
+ messageCleanup = () => window.removeEventListener("message", handleMessage);
314
+ }
315
+ return () => {
316
+ ignore = true;
317
+ messageCleanup?.();
318
+ };
319
+ }, [enabled, embedded]);
320
+ const logout = useCallback(() => {
321
+ clearTokens();
322
+ setUser(null);
323
+ const returnTo = encodeURIComponent(
324
+ window.location.pathname + window.location.search
325
+ );
326
+ fetch("/api/auth/logout", { method: "POST" }).finally(
327
+ () => window.location.href = `/api/auth/login?return_to=${returnTo}`
328
+ );
329
+ }, []);
330
+ return /* @__PURE__ */ jsx3(
331
+ SessionContext.Provider,
332
+ {
333
+ value: {
334
+ user,
335
+ loading,
336
+ isAuthenticated: !!user,
337
+ isEmbedded: embedded,
338
+ logout
339
+ },
340
+ children
341
+ }
342
+ );
343
+ };
344
+
345
+ // src/studio/sidebar/timbal-mark.tsx
346
+ import { LiquidMetal } from "@paper-design/shaders-react";
347
+ import { jsx as jsx4 } from "react/jsx-runtime";
348
+ var DEFAULT_SIZE = 64;
349
+ var TRANSPARENT_BACK = "#00000000";
350
+ var TIMBAL_SYMBOL_DATA_URI = "data:image/png;base64,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";
351
+ function TimbalMark({
352
+ className,
353
+ size = DEFAULT_SIZE,
354
+ src = TIMBAL_SYMBOL_DATA_URI
355
+ }) {
356
+ return /* @__PURE__ */ jsx4(
357
+ "div",
358
+ {
359
+ className: cn("relative shrink-0 bg-transparent", className),
360
+ style: { width: size, height: size },
361
+ role: "img",
362
+ "aria-label": "Timbal",
363
+ children: /* @__PURE__ */ jsx4(
364
+ LiquidMetal,
365
+ {
366
+ width: size,
367
+ height: size,
368
+ image: src,
369
+ colorBack: TRANSPARENT_BACK,
370
+ colorTint: "#ffffff",
371
+ shape: "none",
372
+ repetition: 2,
373
+ softness: 0.1,
374
+ shiftRed: 0.3,
375
+ shiftBlue: 0.3,
376
+ distortion: 0.07,
377
+ contour: 0.4,
378
+ angle: 70,
379
+ speed: 1,
380
+ scale: 0.6,
381
+ fit: "contain",
382
+ className: "size-full bg-transparent",
383
+ style: { background: "transparent" },
384
+ webGlContextAttributes: {
385
+ alpha: true,
386
+ premultipliedAlpha: false
387
+ }
388
+ }
389
+ )
390
+ }
391
+ );
392
+ }
393
+
394
+ // src/studio/sidebar/sidebar.tsx
395
+ import {
396
+ useCallback as useCallback3,
397
+ useEffect as useEffect4,
398
+ useMemo as useMemo2,
399
+ useState as useState4
400
+ } from "react";
401
+ import { motion as motion4, useReducedMotion as useReducedMotion4 } from "motion/react";
402
+
403
+ // src/hooks/use-sidebar-collapse-phase.ts
404
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
405
+ var WIDTH_OVERLAP_FRAC = 0.7;
406
+ function useSidebarCollapsePhase(collapsed, reducedMotion) {
407
+ const [widthCollapsed, setWidthCollapsed] = useState3(collapsed);
408
+ const [entriesVisible, setEntriesVisible] = useState3(true);
409
+ const collapsedTarget = useRef2(collapsed);
410
+ const isFirstRender = useRef2(true);
411
+ const widthTimerRef = useRef2(null);
412
+ const revealTimerRef = useRef2(null);
413
+ useEffect3(() => {
414
+ collapsedTarget.current = collapsed;
415
+ }, [collapsed]);
416
+ const clearWidthTimer = () => {
417
+ if (widthTimerRef.current !== null) {
418
+ clearTimeout(widthTimerRef.current);
419
+ widthTimerRef.current = null;
420
+ }
421
+ };
422
+ const clearRevealTimer = () => {
423
+ if (revealTimerRef.current !== null) {
424
+ clearTimeout(revealTimerRef.current);
425
+ revealTimerRef.current = null;
426
+ }
427
+ };
428
+ const applyWidthTarget = useCallback2(() => {
429
+ const willExpand = !collapsedTarget.current;
430
+ setWidthCollapsed(collapsedTarget.current);
431
+ clearRevealTimer();
432
+ if (willExpand && !reducedMotion) {
433
+ revealTimerRef.current = setTimeout(
434
+ () => setEntriesVisible(true),
435
+ STUDIO_SIDEBAR_WIDTH_S * 1e3 * STUDIO_SIDEBAR_EXPAND_REVEAL_FRAC
436
+ );
437
+ }
438
+ }, [reducedMotion]);
439
+ useEffect3(() => {
440
+ clearWidthTimer();
441
+ clearRevealTimer();
442
+ if (reducedMotion) {
443
+ setWidthCollapsed(collapsed);
444
+ setEntriesVisible(true);
445
+ return;
446
+ }
447
+ if (isFirstRender.current) {
448
+ isFirstRender.current = false;
449
+ setWidthCollapsed(collapsed);
450
+ setEntriesVisible(true);
451
+ return;
452
+ }
453
+ setEntriesVisible(false);
454
+ widthTimerRef.current = setTimeout(
455
+ applyWidthTarget,
456
+ STUDIO_SIDEBAR_ENTRIES_OUT_S * 1e3 * WIDTH_OVERLAP_FRAC
457
+ );
458
+ return () => {
459
+ clearWidthTimer();
460
+ clearRevealTimer();
461
+ };
462
+ }, [collapsed, reducedMotion, applyWidthTarget]);
463
+ const onEntriesBlurOutComplete = useCallback2(() => {
464
+ applyWidthTarget();
465
+ }, [applyWidthTarget]);
466
+ const onPanelWidthComplete = useCallback2(() => {
467
+ clearRevealTimer();
468
+ setEntriesVisible(true);
469
+ }, []);
470
+ const isCollapsedRail = widthCollapsed;
471
+ return {
472
+ widthCollapsed,
473
+ isCollapsedRail,
474
+ entriesVisible,
475
+ onEntriesBlurOutComplete,
476
+ onPanelWidthComplete
477
+ };
478
+ }
479
+
480
+ // src/studio/sidebar/sidebar-context.tsx
481
+ import { createContext as createContext2, useContext as useContext2 } from "react";
482
+ var StudioSidebarContext = createContext2({
483
+ collapsed: false,
484
+ isMobile: false,
485
+ isCollapsedRail: false,
486
+ iconOnlyLayout: false
487
+ });
488
+ function useStudioSidebarLayout() {
489
+ return useContext2(StudioSidebarContext);
490
+ }
491
+
492
+ // src/studio/sidebar/sidebar-entries.tsx
493
+ import { motion as motion2, useReducedMotion as useReducedMotion2 } from "motion/react";
494
+ import { jsx as jsx5 } from "react/jsx-runtime";
495
+ var StudioSidebarEntries = ({
496
+ visible,
497
+ onBlurOutComplete,
498
+ children,
499
+ className
500
+ }) => {
501
+ const reducedMotion = useReducedMotion2();
502
+ if (reducedMotion) {
503
+ return visible ? /* @__PURE__ */ jsx5("div", { className: cn("flex min-h-0 flex-1 flex-col", className), children }) : null;
504
+ }
505
+ return /* @__PURE__ */ jsx5(
506
+ motion2.div,
507
+ {
508
+ className: cn("flex min-h-0 flex-1 flex-col", className),
509
+ initial: false,
510
+ variants: studioSidebarEntriesContainerVariants,
511
+ animate: visible ? "visible" : "hidden",
512
+ transition: studioSidebarEntriesTransition(visible, false),
513
+ onAnimationComplete: (definition) => {
514
+ if (definition === "hidden") {
515
+ onBlurOutComplete();
516
+ }
517
+ },
518
+ style: { pointerEvents: visible ? "auto" : "none" },
519
+ "aria-hidden": !visible,
520
+ children
521
+ }
522
+ );
523
+ };
524
+
525
+ // src/studio/sidebar/sidebar-footer.tsx
526
+ import { LogOut } from "lucide-react";
527
+
528
+ // src/studio/sidebar/sidebar-layout.ts
529
+ function studioSidebarIconOnlyLayout(isMobile, isCollapsedRail) {
530
+ if (isMobile) return false;
531
+ return isCollapsedRail;
532
+ }
533
+ var studioSidebarCollapsedRailInsetClass = "box-border w-full px-1.5";
534
+ var studioSidebarCollapsedRailChipRowClass = "flex w-full justify-center";
535
+ function studioSidebarNavItemClasses(iconOnly, isActive) {
536
+ if (iconOnly) {
537
+ return cn(
538
+ studioSidebarNavItemClass,
539
+ studioSidebarNavItemLayout(true),
540
+ isActive ? studioSidebarCollapsedRailItemActiveClass : studioSidebarCollapsedRailItemIdleClass
541
+ );
542
+ }
543
+ return cn(
544
+ studioSidebarNavItemClass,
545
+ studioSidebarNavItemLayout(false),
546
+ isActive ? studioSidebarNavItemActiveClass : studioSidebarNavItemIdleClass
547
+ );
548
+ }
549
+
550
+ // src/studio/sidebar/sidebar-entry-motion.tsx
551
+ import { motion as motion3, useReducedMotion as useReducedMotion3 } from "motion/react";
552
+ import { jsx as jsx6 } from "react/jsx-runtime";
553
+ var StudioSidebarEntryMotion = ({
554
+ children,
555
+ className
556
+ }) => {
557
+ const reducedMotion = useReducedMotion3();
558
+ if (reducedMotion) {
559
+ return /* @__PURE__ */ jsx6("div", { className, children });
560
+ }
561
+ return /* @__PURE__ */ jsx6(motion3.div, { variants: studioSidebarEntryItemVariants, className: cn(className), children });
562
+ };
563
+
564
+ // src/studio/sidebar/sidebar-tooltip.tsx
565
+ import { Fragment, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
566
+ var StudioSidebarTooltip = ({
567
+ label,
568
+ enabled,
569
+ children
570
+ }) => {
571
+ if (!enabled) return /* @__PURE__ */ jsx7(Fragment, { children });
572
+ return /* @__PURE__ */ jsxs2(Tooltip, { children: [
573
+ /* @__PURE__ */ jsx7(TooltipTrigger, { asChild: true, children }),
574
+ /* @__PURE__ */ jsx7(TooltipContent, { side: "right", className: "text-xs", children: label })
575
+ ] });
576
+ };
577
+
578
+ // src/studio/sidebar/sidebar-footer.tsx
579
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
580
+ function userInitials(name, email) {
581
+ const fromName = name.trim().split(/\s+/).map((part) => part.charAt(0)).join("").slice(0, 2).toUpperCase();
582
+ if (fromName) return fromName;
583
+ return email.charAt(0).toUpperCase() || "?";
584
+ }
585
+ var StudioSidebarFooter = ({
586
+ iconOnlyLayout,
587
+ showTooltips,
588
+ onSignOut,
589
+ emptyCaption = null
590
+ }) => {
591
+ const session = useOptionalSession();
592
+ const user = session?.user ?? null;
593
+ const handleSignOut = () => {
594
+ session?.logout();
595
+ onSignOut?.();
596
+ };
597
+ return /* @__PURE__ */ jsx8(StudioSidebarEntryMotion, { children: /* @__PURE__ */ jsx8(
598
+ "footer",
599
+ {
600
+ className: cn(
601
+ "mt-auto w-full shrink-0 py-2.5",
602
+ iconOnlyLayout ? studioSidebarCollapsedRailInsetClass : "px-2.5"
603
+ ),
604
+ children: user ? /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-2", children: [
605
+ iconOnlyLayout ? /* @__PURE__ */ jsx8("div", { className: studioSidebarCollapsedRailChipRowClass, children: /* @__PURE__ */ jsxs3(Avatar, { size: "sm", className: "size-8", children: [
606
+ user.user_photo_url ? /* @__PURE__ */ jsx8(AvatarImage, { src: user.user_photo_url, alt: user.user_name }) : null,
607
+ /* @__PURE__ */ jsx8(AvatarFallback, { className: "text-[10px]", children: userInitials(user.user_name, user.user_email) })
608
+ ] }) }) : /* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-center gap-2.5", children: [
609
+ /* @__PURE__ */ jsxs3(Avatar, { size: "sm", children: [
610
+ user.user_photo_url ? /* @__PURE__ */ jsx8(AvatarImage, { src: user.user_photo_url, alt: user.user_name }) : null,
611
+ /* @__PURE__ */ jsx8(AvatarFallback, { children: userInitials(user.user_name, user.user_email) })
612
+ ] }),
613
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
614
+ /* @__PURE__ */ jsx8("p", { className: "truncate text-sm font-medium text-foreground", children: user.user_name }),
615
+ /* @__PURE__ */ jsx8("p", { className: "truncate text-xs text-muted-foreground", children: user.user_email })
616
+ ] })
617
+ ] }),
618
+ /* @__PURE__ */ jsx8(
619
+ "div",
620
+ {
621
+ className: iconOnlyLayout ? studioSidebarCollapsedRailChipRowClass : void 0,
622
+ children: /* @__PURE__ */ jsx8(StudioSidebarTooltip, { label: "Sign out", enabled: showTooltips, children: /* @__PURE__ */ jsxs3(
623
+ "button",
624
+ {
625
+ type: "button",
626
+ onClick: handleSignOut,
627
+ className: cn(
628
+ studioSidebarNavItemClasses(iconOnlyLayout, false),
629
+ iconOnlyLayout && "inline-flex"
630
+ ),
631
+ "aria-label": "Sign out",
632
+ children: [
633
+ /* @__PURE__ */ jsx8(LogOut, { className: "size-3.5 shrink-0" }),
634
+ !iconOnlyLayout ? "Sign out" : null
635
+ ]
636
+ }
637
+ ) })
638
+ }
639
+ )
640
+ ] }) : !iconOnlyLayout && emptyCaption ? /* @__PURE__ */ jsx8("p", { className: "px-1 text-xs text-muted-foreground", children: emptyCaption }) : null
641
+ }
642
+ ) });
643
+ };
644
+
645
+ // src/studio/sidebar/sidebar-header.tsx
646
+ import { ChevronLeft, ChevronRight, X } from "lucide-react";
647
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
648
+ var sidebarHeaderClass = "flex h-12 shrink-0 items-center px-2";
649
+ var toggleButtonClass = cn(
650
+ "flex shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors",
651
+ "hover:bg-muted hover:text-foreground",
652
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15"
653
+ );
654
+ var SidebarToggleButton = ({ ariaLabel, expanded, onClick, children }) => /* @__PURE__ */ jsx9(
655
+ "button",
656
+ {
657
+ type: "button",
658
+ onClick,
659
+ className: cn(toggleButtonClass, "size-7"),
660
+ "aria-label": ariaLabel,
661
+ "aria-expanded": expanded,
662
+ children
663
+ }
664
+ );
665
+ var CollapsedBrandToggle = ({
666
+ onExpand,
667
+ brand
668
+ }) => /* @__PURE__ */ jsx9("div", { className: studioSidebarCollapsedRailChipRowClass, children: /* @__PURE__ */ jsx9(StudioSidebarTooltip, { label: "Expand sidebar", enabled: true, children: /* @__PURE__ */ jsxs4(
669
+ "button",
670
+ {
671
+ type: "button",
672
+ onClick: onExpand,
673
+ "aria-label": "Expand sidebar",
674
+ "aria-expanded": false,
675
+ className: cn(
676
+ toggleButtonClass,
677
+ "group relative inline-flex size-8 items-center justify-center overflow-hidden rounded-lg"
678
+ ),
679
+ children: [
680
+ /* @__PURE__ */ jsx9(
681
+ "span",
682
+ {
683
+ "aria-hidden": true,
684
+ className: cn(
685
+ "pointer-events-none flex items-center justify-center",
686
+ "transition-[opacity,transform] duration-200 ease-out",
687
+ "group-hover:scale-90 group-hover:opacity-0"
688
+ ),
689
+ children: brand
690
+ }
691
+ ),
692
+ /* @__PURE__ */ jsx9(
693
+ ChevronRight,
694
+ {
695
+ "aria-hidden": true,
696
+ className: cn(
697
+ "pointer-events-none absolute inset-0 m-auto size-4",
698
+ "opacity-0 transition-[opacity,transform] duration-200 ease-out",
699
+ "group-hover:opacity-100"
700
+ )
701
+ }
702
+ )
703
+ ]
704
+ }
705
+ ) }) });
706
+ var StudioSidebarHeader = ({
707
+ isCollapsedRail,
708
+ isMobile,
709
+ mobileOpen,
710
+ onToggle,
711
+ brand
712
+ }) => {
713
+ if (isMobile) {
714
+ return /* @__PURE__ */ jsxs4("header", { className: cn(sidebarHeaderClass, "justify-between gap-2 pr-2"), children: [
715
+ brand,
716
+ /* @__PURE__ */ jsx9(
717
+ SidebarToggleButton,
718
+ {
719
+ ariaLabel: "Close menu",
720
+ expanded: mobileOpen,
721
+ onClick: onToggle,
722
+ children: /* @__PURE__ */ jsx9(X, { className: "size-3.5" })
723
+ }
724
+ )
725
+ ] });
726
+ }
727
+ if (isCollapsedRail) {
728
+ return /* @__PURE__ */ jsx9(
729
+ "header",
730
+ {
731
+ className: cn(
732
+ "flex h-12 shrink-0 items-center",
733
+ studioSidebarCollapsedRailInsetClass
734
+ ),
735
+ children: /* @__PURE__ */ jsx9(CollapsedBrandToggle, { onExpand: onToggle, brand })
736
+ }
737
+ );
738
+ }
739
+ return /* @__PURE__ */ jsxs4("header", { className: cn(sidebarHeaderClass, "justify-between gap-1 pr-2"), children: [
740
+ brand,
741
+ /* @__PURE__ */ jsx9(
742
+ SidebarToggleButton,
743
+ {
744
+ ariaLabel: "Collapse sidebar",
745
+ expanded: true,
746
+ onClick: onToggle,
747
+ children: /* @__PURE__ */ jsx9(ChevronLeft, { className: "size-4" })
748
+ }
749
+ )
750
+ ] });
751
+ };
752
+
753
+ // src/studio/sidebar/sidebar-workforce.ts
754
+ function workforceItemId(w) {
755
+ return w.id ?? w.uid ?? w.name ?? "";
756
+ }
757
+ function workforceItemLabel(w) {
758
+ return w.name ?? workforceItemId(w);
759
+ }
760
+ function workforceItemInitial(w) {
761
+ const label = workforceItemLabel(w);
762
+ return label.charAt(0).toUpperCase() || "?";
763
+ }
764
+
765
+ // src/studio/sidebar/sidebar-nav.tsx
766
+ import { jsx as jsx10 } from "react/jsx-runtime";
767
+ var StudioSidebarNav = ({
768
+ workforces,
769
+ selectedId,
770
+ onSelect,
771
+ iconOnlyLayout,
772
+ showTooltips
773
+ }) => {
774
+ if (workforces.length === 0) return null;
775
+ return /* @__PURE__ */ jsx10(
776
+ "nav",
777
+ {
778
+ className: cn(
779
+ "flex min-h-0 flex-1 flex-col overflow-y-auto py-1",
780
+ iconOnlyLayout ? cn(studioSidebarCollapsedRailInsetClass, "gap-1") : "gap-0.5 px-2"
781
+ ),
782
+ "aria-label": "Agents",
783
+ children: workforces.map((w) => {
784
+ const id = workforceItemId(w);
785
+ const isActive = id === selectedId;
786
+ const label = workforceItemLabel(w);
787
+ return /* @__PURE__ */ jsx10(
788
+ StudioSidebarEntryMotion,
789
+ {
790
+ className: iconOnlyLayout ? studioSidebarCollapsedRailChipRowClass : void 0,
791
+ children: /* @__PURE__ */ jsx10(StudioSidebarTooltip, { label, enabled: showTooltips, children: /* @__PURE__ */ jsx10(
792
+ "button",
793
+ {
794
+ type: "button",
795
+ onClick: () => onSelect(id),
796
+ "aria-pressed": isActive,
797
+ "aria-label": label,
798
+ className: cn(
799
+ studioSidebarNavItemClasses(iconOnlyLayout, isActive),
800
+ iconOnlyLayout && "inline-flex"
801
+ ),
802
+ children: iconOnlyLayout ? /* @__PURE__ */ jsx10("span", { className: "text-xs font-semibold leading-none", children: workforceItemInitial(w) }) : /* @__PURE__ */ jsx10("span", { className: "min-w-0 truncate", children: label })
803
+ }
804
+ ) })
805
+ },
806
+ id
807
+ );
808
+ })
809
+ }
810
+ );
811
+ };
812
+
813
+ // src/studio/sidebar/shell-inset-bridge-context.tsx
814
+ import { useLayoutEffect } from "react";
815
+ var StudioSidebarShellInsetBridge = ({
816
+ onInsetChange
817
+ }) => {
818
+ const reportInset = useShellInsetReporter();
819
+ const { isMobile, isCollapsedRail } = useStudioSidebarLayout();
820
+ useLayoutEffect(() => {
821
+ const insetPx = isMobile ? 0 : isCollapsedRail ? SIDEBAR_INSET_PX_COLLAPSED : SIDEBAR_INSET_PX_EXPANDED;
822
+ reportInset?.(insetPx);
823
+ onInsetChange?.(insetPx);
824
+ }, [reportInset, onInsetChange, isMobile, isCollapsedRail]);
825
+ return null;
826
+ };
827
+
828
+ // src/studio/sidebar/sidebar.tsx
829
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
830
+ var DEFAULT_BREAKPOINT_PX = 768;
831
+ function readPersistedCollapsed(key) {
832
+ if (!key || typeof window === "undefined") return false;
833
+ try {
834
+ return window.localStorage.getItem(key) === "1";
835
+ } catch {
836
+ return false;
837
+ }
838
+ }
839
+ function writePersistedCollapsed(key, collapsed) {
840
+ if (!key || typeof window === "undefined") return;
841
+ try {
842
+ window.localStorage.setItem(key, collapsed ? "1" : "0");
843
+ } catch {
844
+ }
845
+ }
846
+ var StudioSidebarPanel = ({
847
+ workforces,
848
+ selectedId,
849
+ onSelect,
850
+ collapsed,
851
+ onCollapsedChange,
852
+ isMobile,
853
+ mobileOpen,
854
+ onMobileOpenChange,
855
+ widthCollapsed,
856
+ entriesVisible,
857
+ onEntriesBlurOutComplete,
858
+ onPanelWidthComplete,
859
+ brand,
860
+ emptyCaption = null
861
+ }) => {
862
+ const reducedMotion = useReducedMotion4();
863
+ const isCollapsedRail = widthCollapsed && !isMobile;
864
+ const iconOnlyLayout = studioSidebarIconOnlyLayout(isMobile, isCollapsedRail);
865
+ const isDrawerOpen = isMobile && mobileOpen;
866
+ const widthDirection = widthCollapsed ? "collapse" : "expand";
867
+ const widthTransition = studioSidebarWidthTransition(
868
+ !!reducedMotion,
869
+ widthDirection
870
+ );
871
+ const handleToggle = () => {
872
+ if (isMobile) {
873
+ onMobileOpenChange(false);
874
+ return;
875
+ }
876
+ onCollapsedChange(!collapsed);
877
+ };
878
+ const panelWidthPx = isMobile ? SIDEBAR_MOBILE_PX : widthCollapsed ? SIDEBAR_WIDTH_COLLAPSED_PX : SIDEBAR_WIDTH_PX;
879
+ const brandNode = brand ?? /* @__PURE__ */ jsx11(TimbalMark, { size: 32 });
880
+ const panel = /* @__PURE__ */ jsxs5(
881
+ motion4.div,
882
+ {
883
+ "data-sidebar-collapsed": isCollapsedRail ? "" : void 0,
884
+ className: cn(
885
+ "flex h-full flex-col overflow-hidden",
886
+ studioSidebarPanelClass,
887
+ isMobile ? "rounded-none rounded-r-2xl" : "rounded-2xl"
888
+ ),
889
+ initial: false,
890
+ animate: { width: panelWidthPx },
891
+ transition: widthTransition,
892
+ style: { willChange: entriesVisible ? void 0 : "width" },
893
+ onAnimationComplete: isMobile || entriesVisible ? void 0 : () => onPanelWidthComplete(),
894
+ children: [
895
+ /* @__PURE__ */ jsx11(
896
+ StudioSidebarHeader,
897
+ {
898
+ isCollapsedRail,
899
+ isMobile,
900
+ mobileOpen,
901
+ onToggle: handleToggle,
902
+ brand: brandNode
903
+ }
904
+ ),
905
+ /* @__PURE__ */ jsxs5(
906
+ StudioSidebarEntries,
907
+ {
908
+ visible: entriesVisible,
909
+ onBlurOutComplete: onEntriesBlurOutComplete,
910
+ children: [
911
+ /* @__PURE__ */ jsx11(
912
+ "div",
913
+ {
914
+ id: DOM_IDS.sidebarRuntimeAnchor,
915
+ className: cn(
916
+ "min-h-0 shrink-0 empty:hidden",
917
+ iconOnlyLayout ? "px-1.5 pt-1.5" : "px-2 pt-1.5"
918
+ )
919
+ }
920
+ ),
921
+ /* @__PURE__ */ jsx11(
922
+ StudioSidebarNav,
923
+ {
924
+ workforces,
925
+ selectedId,
926
+ onSelect,
927
+ iconOnlyLayout,
928
+ showTooltips: isCollapsedRail
929
+ }
930
+ ),
931
+ workforces.length === 0 ? /* @__PURE__ */ jsx11("div", { className: "min-h-0 flex-1" }) : null,
932
+ /* @__PURE__ */ jsx11(
933
+ StudioSidebarFooter,
934
+ {
935
+ iconOnlyLayout,
936
+ showTooltips: isCollapsedRail,
937
+ onSignOut: isMobile ? () => onMobileOpenChange(false) : void 0,
938
+ emptyCaption
939
+ }
940
+ )
941
+ ]
942
+ }
943
+ )
944
+ ]
945
+ }
946
+ );
947
+ if (isMobile) {
948
+ return /* @__PURE__ */ jsx11(
949
+ motion4.aside,
950
+ {
951
+ className: "fixed inset-y-0 left-0 z-[60] flex",
952
+ "aria-label": "Studio navigation",
953
+ "aria-hidden": !mobileOpen,
954
+ initial: false,
955
+ animate: {
956
+ x: isDrawerOpen ? 0 : -(SIDEBAR_MOBILE_PX + 32)
957
+ },
958
+ transition: studioSidebarDrawerTransition(!!reducedMotion),
959
+ style: { pointerEvents: isDrawerOpen ? "auto" : "none" },
960
+ children: panel
961
+ }
962
+ );
963
+ }
964
+ return /* @__PURE__ */ jsx11(
965
+ "aside",
966
+ {
967
+ className: "absolute inset-y-0 left-0 z-[60] flex py-[var(--studio-sidebar-gap)] pl-[var(--studio-sidebar-gap)]",
968
+ "aria-label": "Studio navigation",
969
+ children: panel
970
+ }
971
+ );
972
+ };
973
+ var StudioSidebar = ({
974
+ workforces: workforcesProp,
975
+ selectedId: selectedIdProp,
976
+ onSelect,
977
+ defaultCollapsed = false,
978
+ persistKey = STORAGE_KEYS.sidebarCollapsed,
979
+ mobileBreakpointPx = DEFAULT_BREAKPOINT_PX,
980
+ brand,
981
+ emptyCaption,
982
+ mobileOpen: mobileOpenProp,
983
+ onMobileOpenChange: onMobileOpenChangeProp,
984
+ onInsetChange
985
+ }) => {
986
+ const reducedMotion = useReducedMotion4();
987
+ const fetched = useWorkforces({ enabled: workforcesProp === void 0 });
988
+ const workforces = workforcesProp ?? fetched.workforces;
989
+ const [internalSelected, setInternalSelected] = useState4(
990
+ selectedIdProp ?? ""
991
+ );
992
+ useEffect4(() => {
993
+ if (selectedIdProp !== void 0) return;
994
+ if (internalSelected) return;
995
+ const first = workforces[0]?.id ?? workforces[0]?.uid ?? workforces[0]?.name;
996
+ if (first) setInternalSelected(first);
997
+ }, [workforces, selectedIdProp, internalSelected]);
998
+ const selectedId = selectedIdProp ?? internalSelected ?? workforces[0]?.id ?? workforces[0]?.uid ?? workforces[0]?.name ?? "";
999
+ const handleSelect = useCallback3(
1000
+ (id) => {
1001
+ if (selectedIdProp === void 0) setInternalSelected(id);
1002
+ onSelect?.(id);
1003
+ },
1004
+ [selectedIdProp, onSelect]
1005
+ );
1006
+ const [collapsed, setCollapsed] = useState4(() => {
1007
+ const persisted = readPersistedCollapsed(persistKey);
1008
+ return persisted || defaultCollapsed;
1009
+ });
1010
+ const handleCollapsedChange = useCallback3(
1011
+ (next) => {
1012
+ setCollapsed(next);
1013
+ writePersistedCollapsed(persistKey, next);
1014
+ },
1015
+ [persistKey]
1016
+ );
1017
+ const [isMobile, setIsMobile] = useState4(() => {
1018
+ if (typeof window === "undefined") return false;
1019
+ return window.innerWidth < mobileBreakpointPx;
1020
+ });
1021
+ useEffect4(() => {
1022
+ if (typeof window === "undefined") return;
1023
+ const onResize = () => setIsMobile(window.innerWidth < mobileBreakpointPx);
1024
+ onResize();
1025
+ window.addEventListener("resize", onResize);
1026
+ return () => window.removeEventListener("resize", onResize);
1027
+ }, [mobileBreakpointPx]);
1028
+ const [internalMobileOpen, setInternalMobileOpen] = useState4(false);
1029
+ const mobileOpen = mobileOpenProp ?? internalMobileOpen;
1030
+ const setMobileOpen = useCallback3(
1031
+ (next) => {
1032
+ if (mobileOpenProp === void 0) setInternalMobileOpen(next);
1033
+ onMobileOpenChangeProp?.(next);
1034
+ },
1035
+ [mobileOpenProp, onMobileOpenChangeProp]
1036
+ );
1037
+ const effectiveCollapsed = isMobile ? false : collapsed;
1038
+ const {
1039
+ widthCollapsed,
1040
+ entriesVisible: phaseEntriesVisible,
1041
+ onEntriesBlurOutComplete,
1042
+ onPanelWidthComplete
1043
+ } = useSidebarCollapsePhase(effectiveCollapsed, !!reducedMotion);
1044
+ const entriesVisible = isMobile || phaseEntriesVisible;
1045
+ const isCollapsedRail = widthCollapsed && !isMobile;
1046
+ const iconOnlyLayout = studioSidebarIconOnlyLayout(isMobile, isCollapsedRail);
1047
+ const contextValue = useMemo2(
1048
+ () => ({
1049
+ collapsed: effectiveCollapsed,
1050
+ isMobile,
1051
+ isCollapsedRail,
1052
+ iconOnlyLayout
1053
+ }),
1054
+ [effectiveCollapsed, isMobile, isCollapsedRail, iconOnlyLayout]
1055
+ );
1056
+ return /* @__PURE__ */ jsxs5(StudioSidebarContext.Provider, { value: contextValue, children: [
1057
+ /* @__PURE__ */ jsx11(StudioSidebarShellInsetBridge, { onInsetChange }),
1058
+ /* @__PURE__ */ jsx11(
1059
+ StudioSidebarPanel,
1060
+ {
1061
+ workforces,
1062
+ selectedId,
1063
+ onSelect: handleSelect,
1064
+ collapsed: effectiveCollapsed,
1065
+ onCollapsedChange: handleCollapsedChange,
1066
+ isMobile,
1067
+ mobileOpen,
1068
+ onMobileOpenChange: setMobileOpen,
1069
+ widthCollapsed,
1070
+ entriesVisible,
1071
+ onEntriesBlurOutComplete,
1072
+ onPanelWidthComplete,
1073
+ brand,
1074
+ emptyCaption
1075
+ }
1076
+ )
1077
+ ] });
1078
+ };
1079
+
1080
+ // src/studio/sidebar/welcome.tsx
1081
+ import { motion as motion5 } from "motion/react";
1082
+ import { useThread } from "@assistant-ui/react";
1083
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
1084
+ var welcomeStagger = {
1085
+ initial: {},
1086
+ animate: {
1087
+ transition: { staggerChildren: 0.16, delayChildren: 0.18 }
1088
+ }
1089
+ };
1090
+ var welcomeItem = {
1091
+ initial: { opacity: 0, y: 14 },
1092
+ animate: {
1093
+ opacity: 1,
1094
+ y: 0,
1095
+ transition: { duration: 0.9, ease: luxuryEase }
1096
+ }
1097
+ };
1098
+ var welcomeIcon = {
1099
+ initial: { opacity: 0, y: 10, scale: 0.96 },
1100
+ animate: {
1101
+ opacity: 1,
1102
+ y: 0,
1103
+ scale: 1,
1104
+ transition: { duration: 1.1, ease: luxuryEase }
1105
+ }
1106
+ };
1107
+ var StudioWelcome = ({ config, icon }) => {
1108
+ const isEmpty = useThread((s) => s.messages.length === 0);
1109
+ if (!isEmpty) return null;
1110
+ const iconNode = icon ?? /* @__PURE__ */ jsx12(
1111
+ TimbalMark,
1112
+ {
1113
+ size: 112,
1114
+ className: "max-md:scale-[0.58] max-md:origin-center"
1115
+ }
1116
+ );
1117
+ return /* @__PURE__ */ jsx12("div", { className: "aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col", children: /* @__PURE__ */ jsx12("div", { className: "aui-thread-welcome-center flex w-full grow flex-col items-center justify-center", children: /* @__PURE__ */ jsxs6(
1118
+ motion5.div,
1119
+ {
1120
+ className: "aui-thread-welcome-message flex flex-col items-center justify-center px-2 text-center sm:px-4",
1121
+ variants: welcomeStagger,
1122
+ initial: "initial",
1123
+ animate: "animate",
1124
+ children: [
1125
+ /* @__PURE__ */ jsx12(motion5.div, { variants: welcomeIcon, className: "mb-4 md:mb-5", children: iconNode }),
1126
+ /* @__PURE__ */ jsx12(
1127
+ motion5.h1,
1128
+ {
1129
+ variants: welcomeItem,
1130
+ className: "aui-thread-welcome-message-inner text-xl font-semibold sm:text-2xl",
1131
+ children: config?.heading ?? "How can I help you today?"
1132
+ }
1133
+ ),
1134
+ /* @__PURE__ */ jsx12(
1135
+ motion5.p,
1136
+ {
1137
+ variants: welcomeItem,
1138
+ className: "aui-thread-welcome-message-inner mt-2 text-muted-foreground",
1139
+ children: config?.subheading ?? "Send a message to start a conversation."
1140
+ }
1141
+ )
1142
+ ]
1143
+ }
1144
+ ) }) });
1145
+ };
1146
+
1147
+ // src/studio/shell/studio-shell.tsx
1148
+ import {
1149
+ useCallback as useCallback5,
1150
+ useEffect as useEffect5,
1151
+ useMemo as useMemo3,
1152
+ useState as useState6
1153
+ } from "react";
1154
+ import { Menu } from "lucide-react";
1155
+ import { motion as motion6, useReducedMotion as useReducedMotion5 } from "motion/react";
1156
+
1157
+ // src/studio/sidebar/sidebar-runtime-portal.tsx
1158
+ import { useCallback as useCallback4, useLayoutEffect as useLayoutEffect2, useState as useState5 } from "react";
1159
+ import { createPortal } from "react-dom";
1160
+ import { MessageSquarePlus } from "lucide-react";
1161
+ import { useThread as useThread2 } from "@assistant-ui/react";
1162
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
1163
+ var StudioSidebarRuntimePortal = ({
1164
+ label = "New chat"
1165
+ }) => {
1166
+ const { iconOnlyLayout } = useStudioSidebarLayout();
1167
+ const hasMessages = useThread2((s) => s.messages.length > 0);
1168
+ const { clear } = useTimbalRuntime();
1169
+ const [anchor, setAnchor] = useState5(null);
1170
+ const startNewChat = useCallback4(() => {
1171
+ clear();
1172
+ }, [clear]);
1173
+ useLayoutEffect2(() => {
1174
+ setAnchor(document.getElementById(DOM_IDS.sidebarRuntimeAnchor));
1175
+ }, []);
1176
+ if (!anchor || !hasMessages) return null;
1177
+ const button = /* @__PURE__ */ jsxs7(
1178
+ "button",
1179
+ {
1180
+ type: "button",
1181
+ onClick: startNewChat,
1182
+ "aria-label": label,
1183
+ className: studioSidebarNavItemClasses(iconOnlyLayout, false),
1184
+ children: [
1185
+ /* @__PURE__ */ jsx13(MessageSquarePlus, { className: "size-3.5 shrink-0" }),
1186
+ !iconOnlyLayout ? /* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate", children: label }) : null
1187
+ ]
1188
+ }
1189
+ );
1190
+ return createPortal(
1191
+ iconOnlyLayout ? /* @__PURE__ */ jsxs7(Tooltip, { children: [
1192
+ /* @__PURE__ */ jsx13(TooltipTrigger, { asChild: true, children: button }),
1193
+ /* @__PURE__ */ jsx13(TooltipContent, { side: "right", className: "text-xs", children: label })
1194
+ ] }) : button,
1195
+ anchor
1196
+ );
1197
+ };
1198
+
1199
+ // src/studio/shell/studio-shell.tsx
1200
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1201
+ import { createElement } from "react";
1202
+ var DEFAULT_BREAKPOINT_PX2 = 768;
1203
+ function readPersistedCollapsed2(key) {
1204
+ if (!key || typeof window === "undefined") return false;
1205
+ try {
1206
+ return window.localStorage.getItem(key) === "1";
1207
+ } catch {
1208
+ return false;
1209
+ }
1210
+ }
1211
+ function writePersistedCollapsed2(key, collapsed) {
1212
+ if (!key || typeof window === "undefined") return;
1213
+ try {
1214
+ window.localStorage.setItem(key, collapsed ? "1" : "0");
1215
+ } catch {
1216
+ }
1217
+ }
1218
+ function makeComposerWithPortal(BaseComposer) {
1219
+ const Resolved = BaseComposer ?? Composer;
1220
+ return function StudioComposerWithSidebar(props) {
1221
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
1222
+ /* @__PURE__ */ jsx14(StudioSidebarRuntimePortal, {}),
1223
+ /* @__PURE__ */ jsx14(Resolved, { ...props })
1224
+ ] });
1225
+ };
1226
+ }
1227
+ var TimbalStudioShell = ({
1228
+ workforceId,
1229
+ workforces: workforcesProp,
1230
+ workforcesFetch,
1231
+ workforcesBaseUrl,
1232
+ brand,
1233
+ headerActions,
1234
+ headerStart,
1235
+ defaultCollapsed = false,
1236
+ persistKey = STORAGE_KEYS.sidebarCollapsed,
1237
+ mobileBreakpointPx = DEFAULT_BREAKPOINT_PX2,
1238
+ sidebarEmptyCaption = null,
1239
+ welcome,
1240
+ components,
1241
+ ...chatProps
1242
+ }) => {
1243
+ const reducedMotion = useReducedMotion5();
1244
+ const shouldFetchWorkforces = !workforceId && workforcesProp === void 0;
1245
+ const fetched = useWorkforces({
1246
+ enabled: shouldFetchWorkforces,
1247
+ fetch: workforcesFetch,
1248
+ baseUrl: workforcesBaseUrl
1249
+ });
1250
+ const workforces = workforcesProp ?? fetched.workforces;
1251
+ const [internalSelected, setInternalSelected] = useState6(
1252
+ workforceId ?? ""
1253
+ );
1254
+ useEffect5(() => {
1255
+ if (workforceId) return;
1256
+ if (internalSelected) return;
1257
+ const first = workforces[0]?.id ?? workforces[0]?.uid ?? workforces[0]?.name;
1258
+ if (first) setInternalSelected(first);
1259
+ }, [workforces, workforceId, internalSelected]);
1260
+ const activeWorkforceId = workforceId ?? internalSelected ?? fetched.selectedId ?? "";
1261
+ const [collapsed, setCollapsed] = useState6(() => {
1262
+ const persisted = readPersistedCollapsed2(persistKey);
1263
+ return persisted || defaultCollapsed;
1264
+ });
1265
+ const [isMobile, setIsMobile] = useState6(() => {
1266
+ if (typeof window === "undefined") return false;
1267
+ return window.innerWidth < mobileBreakpointPx;
1268
+ });
1269
+ const [mobileSidebarOpen, setMobileSidebarOpen] = useState6(false);
1270
+ useEffect5(() => {
1271
+ if (typeof window === "undefined") return;
1272
+ const onResize = () => setIsMobile(window.innerWidth < mobileBreakpointPx);
1273
+ onResize();
1274
+ window.addEventListener("resize", onResize);
1275
+ return () => window.removeEventListener("resize", onResize);
1276
+ }, [mobileBreakpointPx]);
1277
+ useEffect5(() => {
1278
+ if (!isMobile) setMobileSidebarOpen(false);
1279
+ }, [isMobile]);
1280
+ useEffect5(() => {
1281
+ if (!mobileSidebarOpen) return;
1282
+ const onKeyDown = (e) => {
1283
+ if (e.key === "Escape") setMobileSidebarOpen(false);
1284
+ };
1285
+ window.addEventListener("keydown", onKeyDown);
1286
+ return () => window.removeEventListener("keydown", onKeyDown);
1287
+ }, [mobileSidebarOpen]);
1288
+ const effectiveCollapsed = isMobile ? false : collapsed;
1289
+ const {
1290
+ widthCollapsed,
1291
+ entriesVisible: phaseEntriesVisible,
1292
+ onEntriesBlurOutComplete,
1293
+ onPanelWidthComplete
1294
+ } = useSidebarCollapsePhase(effectiveCollapsed, !!reducedMotion);
1295
+ const entriesVisible = isMobile || phaseEntriesVisible;
1296
+ const isCollapsedRail = widthCollapsed && !isMobile;
1297
+ const iconOnlyLayout = studioSidebarIconOnlyLayout(isMobile, isCollapsedRail);
1298
+ const layoutDirection = widthCollapsed ? "collapse" : "expand";
1299
+ const layoutTransition = studioSidebarWidthTransition(
1300
+ !!reducedMotion,
1301
+ layoutDirection
1302
+ );
1303
+ const desktopInsetPx = widthCollapsed ? SIDEBAR_INSET_PX_COLLAPSED : SIDEBAR_INSET_PX_EXPANDED;
1304
+ const onCollapsedChange = useCallback5(
1305
+ (next) => {
1306
+ setCollapsed(next);
1307
+ writePersistedCollapsed2(persistKey, next);
1308
+ },
1309
+ [persistKey]
1310
+ );
1311
+ const handleSelectWorkforce = useCallback5(
1312
+ (id) => {
1313
+ if (!workforceId) setInternalSelected(id);
1314
+ if (isMobile) setMobileSidebarOpen(false);
1315
+ },
1316
+ [workforceId, isMobile]
1317
+ );
1318
+ const sidebarContext = useMemo3(
1319
+ () => ({
1320
+ collapsed: effectiveCollapsed,
1321
+ isMobile,
1322
+ isCollapsedRail,
1323
+ iconOnlyLayout
1324
+ }),
1325
+ [effectiveCollapsed, isMobile, isCollapsedRail, iconOnlyLayout]
1326
+ );
1327
+ const resolvedComponents = useMemo3(() => {
1328
+ const next = { Welcome: StudioWelcome, ...components };
1329
+ next.Composer = makeComposerWithPortal(components?.Composer);
1330
+ return next;
1331
+ }, [components]);
1332
+ return /* @__PURE__ */ jsx14(StudioSidebarContext.Provider, { value: sidebarContext, children: /* @__PURE__ */ jsxs8(
1333
+ "div",
1334
+ {
1335
+ className: cn(
1336
+ "relative h-dvh overflow-hidden bg-background",
1337
+ isMobile && mobileSidebarOpen && "max-md:overflow-hidden"
1338
+ ),
1339
+ style: studioChromeShellStyle,
1340
+ children: [
1341
+ /* @__PURE__ */ jsx14(
1342
+ "div",
1343
+ {
1344
+ className: "pointer-events-none absolute inset-0 z-0 bg-background",
1345
+ "aria-hidden": true
1346
+ }
1347
+ ),
1348
+ /* @__PURE__ */ jsx14(
1349
+ "div",
1350
+ {
1351
+ className: cn(
1352
+ "pointer-events-none absolute inset-0 z-0",
1353
+ studioPlaygroundGradientClass
1354
+ ),
1355
+ "aria-hidden": true
1356
+ }
1357
+ ),
1358
+ /* @__PURE__ */ jsx14(
1359
+ StudioSidebarBackdrop,
1360
+ {
1361
+ open: isMobile && mobileSidebarOpen,
1362
+ onClose: () => setMobileSidebarOpen(false)
1363
+ }
1364
+ ),
1365
+ /* @__PURE__ */ jsx14(
1366
+ StudioSidebarPanel,
1367
+ {
1368
+ workforces,
1369
+ selectedId: activeWorkforceId,
1370
+ onSelect: handleSelectWorkforce,
1371
+ collapsed: effectiveCollapsed,
1372
+ onCollapsedChange,
1373
+ isMobile,
1374
+ mobileOpen: mobileSidebarOpen,
1375
+ onMobileOpenChange: setMobileSidebarOpen,
1376
+ widthCollapsed,
1377
+ entriesVisible,
1378
+ onEntriesBlurOutComplete,
1379
+ onPanelWidthComplete,
1380
+ brand,
1381
+ emptyCaption: sidebarEmptyCaption
1382
+ }
1383
+ ),
1384
+ /* @__PURE__ */ jsxs8(
1385
+ motion6.header,
1386
+ {
1387
+ className: cn(
1388
+ "absolute top-0 right-0 z-40 flex items-start justify-between gap-2",
1389
+ "px-3 pt-[var(--studio-topbar-gap)] md:px-4",
1390
+ "left-0"
1391
+ ),
1392
+ initial: false,
1393
+ animate: { left: isMobile ? 0 : desktopInsetPx },
1394
+ transition: layoutTransition,
1395
+ children: [
1396
+ /* @__PURE__ */ jsxs8(
1397
+ "div",
1398
+ {
1399
+ className: cn(
1400
+ "flex min-w-0 flex-1 items-center gap-2",
1401
+ studioTopbarPillHeightClass
1402
+ ),
1403
+ children: [
1404
+ isMobile && !mobileSidebarOpen ? /* @__PURE__ */ jsx14(
1405
+ TimbalV2Button,
1406
+ {
1407
+ variant: "secondary",
1408
+ size: "sm",
1409
+ isIconOnly: true,
1410
+ className: studioTopbarIconPillClass,
1411
+ onClick: () => setMobileSidebarOpen(true),
1412
+ "aria-label": "Open menu",
1413
+ "aria-expanded": false,
1414
+ children: /* @__PURE__ */ jsx14(Menu, { className: "size-4" })
1415
+ }
1416
+ ) : null,
1417
+ headerStart
1418
+ ]
1419
+ }
1420
+ ),
1421
+ headerActions ? /* @__PURE__ */ jsx14("div", { className: "flex shrink-0 items-center gap-1", children: headerActions }) : null
1422
+ ]
1423
+ }
1424
+ ),
1425
+ /* @__PURE__ */ jsx14(
1426
+ motion6.main,
1427
+ {
1428
+ className: cn(
1429
+ "relative z-10 flex h-full min-w-0 flex-col",
1430
+ "pt-[var(--studio-inset-top)]",
1431
+ "px-3 md:px-0"
1432
+ ),
1433
+ initial: false,
1434
+ animate: { paddingLeft: isMobile ? 12 : desktopInsetPx },
1435
+ transition: layoutTransition,
1436
+ children: activeWorkforceId ? /* @__PURE__ */ createElement(
1437
+ TimbalChat,
1438
+ {
1439
+ ...chatProps,
1440
+ workforceId: activeWorkforceId,
1441
+ key: activeWorkforceId,
1442
+ welcome,
1443
+ components: resolvedComponents,
1444
+ className: cn("min-h-0 flex-1", chatProps.className)
1445
+ }
1446
+ ) : null
1447
+ }
1448
+ )
1449
+ ]
1450
+ }
1451
+ ) });
1452
+ };
1453
+
1454
+ // src/studio/sidebar/mode-toggle.tsx
1455
+ import { useCallback as useCallback6, useEffect as useEffect6, useState as useState7 } from "react";
1456
+ import { Moon, Sun } from "lucide-react";
1457
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
1458
+ var ModeToggle = ({
1459
+ theme,
1460
+ setTheme,
1461
+ className,
1462
+ label = "Toggle theme"
1463
+ }) => {
1464
+ const isControlled = theme !== void 0;
1465
+ const [internalIsDark, setInternalIsDark] = useState7(false);
1466
+ useEffect6(() => {
1467
+ if (isControlled) return;
1468
+ if (typeof document === "undefined") return;
1469
+ setInternalIsDark(document.documentElement.classList.contains("dark"));
1470
+ }, [isControlled]);
1471
+ const isDark = isControlled ? theme === "dark" : internalIsDark;
1472
+ const onClick = useCallback6(() => {
1473
+ const next = isDark ? "light" : "dark";
1474
+ if (setTheme) {
1475
+ setTheme(next);
1476
+ return;
1477
+ }
1478
+ if (typeof document === "undefined") return;
1479
+ document.documentElement.classList.toggle("dark", next === "dark");
1480
+ setInternalIsDark(next === "dark");
1481
+ }, [isDark, setTheme]);
1482
+ return /* @__PURE__ */ jsxs9(
1483
+ TimbalV2Button,
1484
+ {
1485
+ variant: "secondary",
1486
+ size: "sm",
1487
+ isIconOnly: true,
1488
+ onClick,
1489
+ className: cn(
1490
+ studioTopbarPillHeightClass,
1491
+ studioTopbarIconPillClass,
1492
+ "relative",
1493
+ className
1494
+ ),
1495
+ "aria-label": label,
1496
+ title: label,
1497
+ children: [
1498
+ /* @__PURE__ */ jsx15(Sun, { className: "size-3.5 scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" }),
1499
+ /* @__PURE__ */ jsx15(Moon, { className: "absolute size-3.5 scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" }),
1500
+ /* @__PURE__ */ jsx15("span", { className: "sr-only", children: label })
1501
+ ]
1502
+ }
1503
+ );
1504
+ };
1505
+
1506
+ // src/studio/mode-switch.tsx
1507
+ import { memo, useCallback as useCallback7, useMemo as useMemo4 } from "react";
1508
+ import { jsx as jsx16 } from "react/jsx-runtime";
1509
+ var STUDIO_NAV_MODE = {
1510
+ BUILD: "build",
1511
+ OPERATE: "operate"
1512
+ };
1513
+ var StudioModeSwitch = memo(
1514
+ function StudioModeSwitch2({
1515
+ value,
1516
+ onChange,
1517
+ className,
1518
+ buildLabel = "Build",
1519
+ manageLabel = "Manage",
1520
+ "aria-label": ariaLabel = "Studio mode"
1521
+ }) {
1522
+ const tabs = useMemo4(
1523
+ () => [
1524
+ { key: STUDIO_NAV_MODE.BUILD, label: buildLabel },
1525
+ { key: STUDIO_NAV_MODE.OPERATE, label: manageLabel }
1526
+ ],
1527
+ [buildLabel, manageLabel]
1528
+ );
1529
+ const handleChange = useCallback7(
1530
+ (key) => {
1531
+ if (key === STUDIO_NAV_MODE.BUILD || key === STUDIO_NAV_MODE.OPERATE) {
1532
+ onChange(key);
1533
+ }
1534
+ },
1535
+ [onChange]
1536
+ );
1537
+ return /* @__PURE__ */ jsx16(
1538
+ "div",
1539
+ {
1540
+ "data-tour": "studio-mode-switch",
1541
+ "data-studio-chrome-align": "mode-switch",
1542
+ id: "studio-chrome-mode-switch",
1543
+ className,
1544
+ children: /* @__PURE__ */ jsx16(
1545
+ PillSegmentedTabs,
1546
+ {
1547
+ value,
1548
+ onChange: handleChange,
1549
+ tabs,
1550
+ trackVariant: "flush",
1551
+ layoutId: "studio-nav-mode-segmented",
1552
+ "aria-label": ariaLabel
1553
+ }
1554
+ )
1555
+ }
1556
+ );
1557
+ }
1558
+ );
1559
+
1560
+ export {
1561
+ useWorkforces,
1562
+ TimbalChatShell,
1563
+ StudioSidebarBackdrop,
1564
+ useSession,
1565
+ useOptionalSession,
1566
+ SessionProvider,
1567
+ TimbalMark,
1568
+ StudioSidebar,
1569
+ StudioWelcome,
1570
+ TimbalStudioShell,
1571
+ ModeToggle,
1572
+ STUDIO_NAV_MODE,
1573
+ StudioModeSwitch
1574
+ };