@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.
- package/README.md +128 -4
- package/dist/app.cjs +5311 -0
- package/dist/app.d.cts +29 -0
- package/dist/app.d.ts +29 -0
- package/dist/app.esm.js +81 -0
- package/dist/chart-artifact-C71dk4xI.d.ts +329 -0
- package/dist/chart-artifact-CPEpOmtV.d.cts +329 -0
- package/dist/chat-CWtQWDtJ.d.cts +650 -0
- package/dist/chat-CWtQWDtJ.d.ts +650 -0
- package/dist/chat.cjs +4162 -0
- package/dist/chat.d.cts +13 -0
- package/dist/chat.d.ts +13 -0
- package/dist/chat.esm.js +51 -0
- package/dist/chunk-4TCJQSIX.esm.js +565 -0
- package/dist/chunk-IYENDIRY.esm.js +119 -0
- package/dist/chunk-KC5QLVUG.esm.js +22 -0
- package/dist/chunk-M4V6Q6XO.esm.js +1082 -0
- package/dist/chunk-OFHLFNJH.esm.js +138 -0
- package/dist/chunk-OVHR7J3J.esm.js +1574 -0
- package/dist/chunk-WLTW56MC.esm.js +66 -0
- package/dist/chunk-YJQLLFKP.esm.js +3672 -0
- package/dist/index.cjs +1823 -359
- package/dist/index.d.cts +15 -931
- package/dist/index.d.ts +15 -931
- package/dist/index.esm.js +187 -5578
- package/dist/layout-B9VayJhZ.d.cts +75 -0
- package/dist/layout-CQWngNQ7.d.ts +75 -0
- package/dist/studio.cjs +5734 -0
- package/dist/studio.d.cts +15 -0
- package/dist/studio.d.ts +15 -0
- package/dist/studio.esm.js +27 -0
- package/dist/styles.css +52 -2
- package/dist/timbal-v2-button-F4-z7m33.d.cts +40 -0
- package/dist/timbal-v2-button-F4-z7m33.d.ts +40 -0
- package/dist/ui.cjs +720 -0
- package/dist/ui.d.cts +74 -0
- package/dist/ui.d.ts +74 -0
- package/dist/ui.esm.js +44 -0
- package/dist/welcome--80i_O0p.d.cts +190 -0
- package/dist/welcome-BOizSp5h.d.ts +190 -0
- package/package.json +35 -3
- package/scripts/dev-linked.mjs +66 -0
- package/vite/local-dev.d.ts +4 -0
- 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
|
+
};
|