@joycostudio/studio 0.0.0-stage → 0.0.2
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 +203 -2
- package/dist/chunk-QKEAOM2U.js +13 -0
- package/dist/chunk-QKEAOM2U.js.map +1 -0
- package/dist/chunk-VYNEG5Z7.js +75 -0
- package/dist/chunk-VYNEG5Z7.js.map +1 -0
- package/dist/cli.js +687 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +63 -0
- package/dist/client.js +273 -0
- package/dist/client.js.map +1 -0
- package/dist/command-menu.css +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol-Bl62UeCa.d.ts +138 -0
- package/dist/react.d.ts +130 -0
- package/dist/react.js +1189 -0
- package/dist/react.js.map +1 -0
- package/dist/runtime.d.ts +41 -0
- package/dist/runtime.js +546 -0
- package/dist/runtime.js.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +99 -4
package/dist/react.js
ADDED
|
@@ -0,0 +1,1189 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// src/react/studio.tsx
|
|
4
|
+
import {
|
|
5
|
+
ChevronLeft,
|
|
6
|
+
ChevronRight,
|
|
7
|
+
LoaderCircle,
|
|
8
|
+
PanelLeftClose,
|
|
9
|
+
PanelLeftOpen,
|
|
10
|
+
PlugZap,
|
|
11
|
+
Unplug
|
|
12
|
+
} from "lucide-react";
|
|
13
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
|
|
14
|
+
|
|
15
|
+
// package.json
|
|
16
|
+
var version = "0.0.2";
|
|
17
|
+
|
|
18
|
+
// src/client/studio.ts
|
|
19
|
+
import { button } from "@joycostudio/xyz/debug";
|
|
20
|
+
var COMPARE_MESSAGE = "joyco-studio:compare";
|
|
21
|
+
function postCompare(target, tool, showSaved) {
|
|
22
|
+
const message = { type: COMPARE_MESSAGE, tool, showSaved };
|
|
23
|
+
target?.postMessage(message, location.origin);
|
|
24
|
+
}
|
|
25
|
+
var instances = globalThis.__joycoStudio ??= /* @__PURE__ */ new Map();
|
|
26
|
+
|
|
27
|
+
// src/react/hooks.ts
|
|
28
|
+
import { useSyncExternalStore } from "react";
|
|
29
|
+
function useStudio(studio) {
|
|
30
|
+
useSyncExternalStore(
|
|
31
|
+
studio.subscribe,
|
|
32
|
+
() => studio.version,
|
|
33
|
+
() => 0
|
|
34
|
+
);
|
|
35
|
+
return studio;
|
|
36
|
+
}
|
|
37
|
+
function useToolConfig(handle) {
|
|
38
|
+
useSyncExternalStore(
|
|
39
|
+
handle.subscribe,
|
|
40
|
+
() => handle.version,
|
|
41
|
+
() => 0
|
|
42
|
+
);
|
|
43
|
+
return handle.config;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/react/inspector.tsx
|
|
47
|
+
import { bindDebugSchema } from "@joycostudio/xyz/debug";
|
|
48
|
+
import { SquareSplitHorizontal } from "lucide-react";
|
|
49
|
+
import { useEffect, useRef, useState } from "react";
|
|
50
|
+
|
|
51
|
+
// src/core/diff.ts
|
|
52
|
+
function changedPaths(before, after, path = "") {
|
|
53
|
+
const nested = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
54
|
+
if (nested(before) && nested(after)) {
|
|
55
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
56
|
+
return [...keys].flatMap((key) => changedPaths(before[key], after[key], path ? `${path}.${key}` : key));
|
|
57
|
+
}
|
|
58
|
+
return JSON.stringify(before) === JSON.stringify(after) ? [] : [path];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/react/theme.ts
|
|
62
|
+
var tweakpaneTheme = {
|
|
63
|
+
"--tp-base-background-color": "transparent",
|
|
64
|
+
"--tp-base-shadow-color": "transparent",
|
|
65
|
+
"--tp-base-border-radius": "0px",
|
|
66
|
+
"--tp-base-font-family": "var(--studio-font)",
|
|
67
|
+
"--tp-blade-border-radius": "var(--radius)",
|
|
68
|
+
"--tp-blade-horizontal-padding": "4px",
|
|
69
|
+
"--tp-blade-value-width": "168px",
|
|
70
|
+
"--tp-container-horizontal-padding": "8px",
|
|
71
|
+
"--tp-container-vertical-padding": "6px",
|
|
72
|
+
"--tp-container-unit-size": "22px",
|
|
73
|
+
"--tp-container-unit-spacing": "6px",
|
|
74
|
+
"--tp-container-background-color": "var(--card)",
|
|
75
|
+
"--tp-container-background-color-hover": "var(--field)",
|
|
76
|
+
"--tp-container-background-color-focus": "var(--field-hover)",
|
|
77
|
+
"--tp-container-background-color-active": "var(--field-active)",
|
|
78
|
+
"--tp-container-foreground-color": "var(--muted-foreground)",
|
|
79
|
+
"--tp-input-background-color": "var(--field)",
|
|
80
|
+
"--tp-input-background-color-hover": "var(--field-hover)",
|
|
81
|
+
"--tp-input-background-color-focus": "var(--field-active)",
|
|
82
|
+
"--tp-input-background-color-active": "var(--field-active)",
|
|
83
|
+
"--tp-input-foreground-color": "var(--foreground)",
|
|
84
|
+
// Buttons and slider knobs: lighter than fields so the knob reads against the track.
|
|
85
|
+
"--tp-button-background-color": "var(--knob)",
|
|
86
|
+
"--tp-button-background-color-hover": "var(--knob-hover)",
|
|
87
|
+
"--tp-button-background-color-focus": "var(--knob-hover)",
|
|
88
|
+
"--tp-button-background-color-active": "var(--muted-foreground)",
|
|
89
|
+
"--tp-button-foreground-color": "var(--foreground)",
|
|
90
|
+
"--tp-groove-foreground-color": "var(--border)",
|
|
91
|
+
"--tp-label-foreground-color": "var(--muted-foreground)",
|
|
92
|
+
"--tp-monitor-background-color": "var(--card)",
|
|
93
|
+
"--tp-monitor-foreground-color": "var(--muted-foreground)"
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// src/react/tool-icon.tsx
|
|
97
|
+
import { SlidersHorizontal } from "lucide";
|
|
98
|
+
import { jsx } from "react/jsx-runtime";
|
|
99
|
+
var camel = (name) => name.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
100
|
+
function ToolIcon({ icon = SlidersHorizontal, ...props }) {
|
|
101
|
+
return /* @__PURE__ */ jsx(
|
|
102
|
+
"svg",
|
|
103
|
+
{
|
|
104
|
+
viewBox: "0 0 24 24",
|
|
105
|
+
fill: "none",
|
|
106
|
+
stroke: "currentColor",
|
|
107
|
+
strokeWidth: 2,
|
|
108
|
+
strokeLinecap: "round",
|
|
109
|
+
strokeLinejoin: "round",
|
|
110
|
+
"aria-hidden": true,
|
|
111
|
+
...props,
|
|
112
|
+
children: icon.map(([Tag, attrs], index) => {
|
|
113
|
+
const Element2 = Tag;
|
|
114
|
+
return /* @__PURE__ */ jsx(
|
|
115
|
+
Element2,
|
|
116
|
+
{
|
|
117
|
+
...Object.fromEntries(Object.entries(attrs).map(([name, value]) => [camel(name), value]))
|
|
118
|
+
},
|
|
119
|
+
index
|
|
120
|
+
);
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/react/ui/button.tsx
|
|
127
|
+
import { cva } from "class-variance-authority";
|
|
128
|
+
|
|
129
|
+
// src/react/ui/utils.ts
|
|
130
|
+
import { clsx } from "clsx";
|
|
131
|
+
import { twMerge } from "tailwind-merge";
|
|
132
|
+
function cn(...inputs) {
|
|
133
|
+
return twMerge(clsx(inputs));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/react/ui/button.tsx
|
|
137
|
+
import { Slot } from "radix-ui";
|
|
138
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
139
|
+
var buttonVariants = cva(
|
|
140
|
+
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
141
|
+
{
|
|
142
|
+
variants: {
|
|
143
|
+
variant: {
|
|
144
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
145
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
146
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
147
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
148
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
149
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
150
|
+
},
|
|
151
|
+
size: {
|
|
152
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
153
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
154
|
+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
155
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
156
|
+
icon: "size-9",
|
|
157
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
158
|
+
"icon-sm": "size-8",
|
|
159
|
+
"icon-lg": "size-10"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
defaultVariants: {
|
|
163
|
+
variant: "default",
|
|
164
|
+
size: "default"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
function Button({
|
|
169
|
+
className,
|
|
170
|
+
variant = "default",
|
|
171
|
+
size = "default",
|
|
172
|
+
asChild = false,
|
|
173
|
+
...props
|
|
174
|
+
}) {
|
|
175
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
176
|
+
return /* @__PURE__ */ jsx2(
|
|
177
|
+
Comp,
|
|
178
|
+
{
|
|
179
|
+
"data-slot": "button",
|
|
180
|
+
"data-variant": variant,
|
|
181
|
+
"data-size": size,
|
|
182
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
183
|
+
...props
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/react/ui/kbd.tsx
|
|
189
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
190
|
+
function Kbd({ className, ...props }) {
|
|
191
|
+
return /* @__PURE__ */ jsx3(
|
|
192
|
+
"kbd",
|
|
193
|
+
{
|
|
194
|
+
"data-slot": "kbd",
|
|
195
|
+
className: cn(
|
|
196
|
+
"bg-muted text-muted-foreground border-border pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm border border-b-2 px-1 font-mono text-xs font-normal select-none",
|
|
197
|
+
"[&_svg:not([class*='size-'])]:size-3",
|
|
198
|
+
"[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
|
|
199
|
+
className
|
|
200
|
+
),
|
|
201
|
+
...props
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
function KbdGroup({ className, ...props }) {
|
|
206
|
+
return /* @__PURE__ */ jsx3("span", { "data-slot": "kbd-group", className: cn("inline-flex items-center gap-1", className), ...props });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/react/ui/tooltip.tsx
|
|
210
|
+
import { Tooltip as TooltipPrimitive } from "radix-ui";
|
|
211
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
212
|
+
function TooltipProvider({ delayDuration = 0, ...props }) {
|
|
213
|
+
return /* @__PURE__ */ jsx4(TooltipPrimitive.Provider, { "data-slot": "tooltip-provider", delayDuration, ...props });
|
|
214
|
+
}
|
|
215
|
+
function Tooltip({ ...props }) {
|
|
216
|
+
return /* @__PURE__ */ jsx4(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props });
|
|
217
|
+
}
|
|
218
|
+
function TooltipTrigger({ ...props }) {
|
|
219
|
+
return /* @__PURE__ */ jsx4(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
220
|
+
}
|
|
221
|
+
function TooltipContent({
|
|
222
|
+
className,
|
|
223
|
+
sideOffset = 0,
|
|
224
|
+
children,
|
|
225
|
+
...props
|
|
226
|
+
}) {
|
|
227
|
+
return /* @__PURE__ */ jsx4(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
228
|
+
TooltipPrimitive.Content,
|
|
229
|
+
{
|
|
230
|
+
"data-slot": "tooltip-content",
|
|
231
|
+
sideOffset,
|
|
232
|
+
className: cn(
|
|
233
|
+
"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
234
|
+
className
|
|
235
|
+
),
|
|
236
|
+
...props,
|
|
237
|
+
children: [
|
|
238
|
+
children,
|
|
239
|
+
/* @__PURE__ */ jsx4(TooltipPrimitive.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
) });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/react/inspector.tsx
|
|
246
|
+
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
247
|
+
function describe(studio, handle) {
|
|
248
|
+
const { state } = handle;
|
|
249
|
+
if (studio.connection.phase !== "connected") return { tone: "text-faint", text: "Not persisting" };
|
|
250
|
+
if (!state) return { tone: "text-destructive", text: "Restart studio dev" };
|
|
251
|
+
if (handle.comparing) return { tone: "text-sky-400", text: "Showing saved" };
|
|
252
|
+
if (state.conflict) return { tone: "text-destructive", text: "Changed on disk" };
|
|
253
|
+
if (state.dirty) return { tone: "text-warning", text: "Unsaved" };
|
|
254
|
+
return { tone: "text-success", text: state.saved.hash ? "Saved" : "Not on disk yet" };
|
|
255
|
+
}
|
|
256
|
+
function Inspector({
|
|
257
|
+
studio,
|
|
258
|
+
handle,
|
|
259
|
+
onCompare
|
|
260
|
+
}) {
|
|
261
|
+
useStudio(studio);
|
|
262
|
+
const container = useRef(null);
|
|
263
|
+
const pane = useRef(null);
|
|
264
|
+
const [failure, setFailure] = useState(null);
|
|
265
|
+
const run = (action) => void action.then((result) => setFailure(result.ok ? null : result.message ?? result.error));
|
|
266
|
+
const save = (force = false) => run(handle.save({ force }));
|
|
267
|
+
useEffect(() => {
|
|
268
|
+
let disposed = false;
|
|
269
|
+
let teardown = () => {
|
|
270
|
+
};
|
|
271
|
+
void import("tweakpane").then(({ Pane }) => {
|
|
272
|
+
if (disposed || !container.current) return;
|
|
273
|
+
const instance = new Pane({ container: container.current });
|
|
274
|
+
pane.current = instance;
|
|
275
|
+
const bound = bindDebugSchema(instance, { schema: () => handle.controls() });
|
|
276
|
+
const unsubscribe = handle.subscribe(() => bound.refresh());
|
|
277
|
+
teardown = () => {
|
|
278
|
+
unsubscribe();
|
|
279
|
+
bound.dispose();
|
|
280
|
+
instance.dispose();
|
|
281
|
+
pane.current = null;
|
|
282
|
+
};
|
|
283
|
+
});
|
|
284
|
+
return () => {
|
|
285
|
+
disposed = true;
|
|
286
|
+
teardown();
|
|
287
|
+
};
|
|
288
|
+
}, [handle]);
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
const onKeyDown = (event) => {
|
|
291
|
+
if (event.key !== "s" || !(event.metaKey || event.ctrlKey)) return;
|
|
292
|
+
event.preventDefault();
|
|
293
|
+
if (handle.state?.dirty) save();
|
|
294
|
+
};
|
|
295
|
+
addEventListener("keydown", onKeyDown);
|
|
296
|
+
return () => removeEventListener("keydown", onKeyDown);
|
|
297
|
+
});
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
if (pane.current) pane.current.disabled = handle.comparing;
|
|
300
|
+
});
|
|
301
|
+
const { definition, state } = handle;
|
|
302
|
+
const status = describe(studio, handle);
|
|
303
|
+
const editable2 = studio.connection.phase === "connected" && !!state?.dirty && !handle.comparing;
|
|
304
|
+
const changes = (state?.dirty ? changedPaths(state.saved.value, state.draft.value) : []).map(
|
|
305
|
+
(path) => path.split(".").map((key) => key.charAt(0).toUpperCase() + key.slice(1)).join(" \u203A ")
|
|
306
|
+
);
|
|
307
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex min-h-0 flex-1 flex-col gap-1 p-1", children: [
|
|
308
|
+
/* @__PURE__ */ jsxs2("header", { className: "rounded-[calc(var(--radius-lg)-0.25rem)] bg-card px-3 pt-2.5 pb-2", children: [
|
|
309
|
+
/* @__PURE__ */ jsxs2("strong", { className: "flex min-w-0 items-center gap-2 text-foreground", children: [
|
|
310
|
+
/* @__PURE__ */ jsx5(ToolIcon, { icon: definition.icon, className: "size-3.5 shrink-0" }),
|
|
311
|
+
/* @__PURE__ */ jsx5("span", { className: "truncate", title: definition.title ?? definition.id, children: definition.title ?? definition.id })
|
|
312
|
+
] }),
|
|
313
|
+
/* @__PURE__ */ jsxs2("div", { className: "mt-1 flex items-center gap-3 pl-5.5 text-[11px]", children: [
|
|
314
|
+
/* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 truncate text-muted-foreground", title: definition.file, children: definition.file }),
|
|
315
|
+
/* @__PURE__ */ jsxs2(
|
|
316
|
+
"span",
|
|
317
|
+
{
|
|
318
|
+
className: `flex shrink-0 items-center gap-1.5 ${status.tone}`,
|
|
319
|
+
title: state ? `Draft revision ${state.draft.revision}` : void 0,
|
|
320
|
+
children: [
|
|
321
|
+
/* @__PURE__ */ jsx5("span", { "aria-hidden": "true", className: "size-1.5 rounded-full bg-current" }),
|
|
322
|
+
status.text
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
)
|
|
326
|
+
] })
|
|
327
|
+
] }),
|
|
328
|
+
/* @__PURE__ */ jsx5(
|
|
329
|
+
"div",
|
|
330
|
+
{
|
|
331
|
+
ref: container,
|
|
332
|
+
"data-slot": "studio-controls",
|
|
333
|
+
className: "min-h-0 flex-1 overflow-auto px-1 py-1.5",
|
|
334
|
+
style: tweakpaneTheme
|
|
335
|
+
}
|
|
336
|
+
),
|
|
337
|
+
/* @__PURE__ */ jsxs2("footer", { className: "grid gap-2 rounded-[calc(var(--radius-lg)-0.25rem)] bg-card p-2", children: [
|
|
338
|
+
failure && /* @__PURE__ */ jsx5("div", { className: "text-destructive", children: failure }),
|
|
339
|
+
changes.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "truncate text-muted-foreground", title: changes.join(", "), children: [
|
|
340
|
+
changes.length,
|
|
341
|
+
" changed: ",
|
|
342
|
+
/* @__PURE__ */ jsx5("span", { className: "text-foreground", children: changes.join(", ") })
|
|
343
|
+
] }),
|
|
344
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
|
|
345
|
+
/* @__PURE__ */ jsxs2(Tooltip, { children: [
|
|
346
|
+
/* @__PURE__ */ jsx5(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx5(
|
|
347
|
+
Button,
|
|
348
|
+
{
|
|
349
|
+
variant: "secondary",
|
|
350
|
+
size: "icon-sm",
|
|
351
|
+
"aria-label": "Hold to compare with saved",
|
|
352
|
+
"aria-pressed": handle.comparing,
|
|
353
|
+
disabled: !state?.dirty,
|
|
354
|
+
className: "shrink-0 aria-pressed:bg-primary aria-pressed:text-primary-foreground",
|
|
355
|
+
onPointerDown: (event) => {
|
|
356
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
357
|
+
onCompare(true);
|
|
358
|
+
},
|
|
359
|
+
onPointerUp: () => onCompare(false),
|
|
360
|
+
onPointerCancel: () => onCompare(false),
|
|
361
|
+
children: /* @__PURE__ */ jsx5(SquareSplitHorizontal, {})
|
|
362
|
+
}
|
|
363
|
+
) }),
|
|
364
|
+
/* @__PURE__ */ jsxs2(TooltipContent, { className: "flex items-center gap-2", children: [
|
|
365
|
+
"Hold to compare with saved ",
|
|
366
|
+
/* @__PURE__ */ jsx5(Kbd, { children: "\\" })
|
|
367
|
+
] })
|
|
368
|
+
] }),
|
|
369
|
+
/* @__PURE__ */ jsx5(
|
|
370
|
+
Button,
|
|
371
|
+
{
|
|
372
|
+
variant: "secondary",
|
|
373
|
+
size: "sm",
|
|
374
|
+
className: "flex-1",
|
|
375
|
+
disabled: !editable2,
|
|
376
|
+
onClick: () => run(handle.discard()),
|
|
377
|
+
children: "Discard"
|
|
378
|
+
}
|
|
379
|
+
),
|
|
380
|
+
state?.conflict ? /* @__PURE__ */ jsx5(Button, { variant: "destructive", size: "sm", className: "flex-1", onClick: () => save(true), children: "Overwrite file" }) : /* @__PURE__ */ jsxs2(Button, { size: "sm", className: "flex-1", disabled: !editable2, onClick: () => save(), children: [
|
|
381
|
+
"Save",
|
|
382
|
+
/* @__PURE__ */ jsxs2(KbdGroup, { children: [
|
|
383
|
+
/* @__PURE__ */ jsx5(Kbd, { children: "\u2318" }),
|
|
384
|
+
/* @__PURE__ */ jsx5(Kbd, { children: "S" })
|
|
385
|
+
] })
|
|
386
|
+
] })
|
|
387
|
+
] })
|
|
388
|
+
] })
|
|
389
|
+
] });
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/react/logo.tsx
|
|
393
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
394
|
+
function Logo(props) {
|
|
395
|
+
return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 352 144", fill: "currentColor", "aria-label": "JOYCO", role: "img", ...props, children: /* @__PURE__ */ jsx6("path", { d: "M328 96L280 144H0V40H176V112H192V24H0V0H352V24H328V96Z" }) });
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/react/ui/sidebar.tsx
|
|
399
|
+
import * as React2 from "react";
|
|
400
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
401
|
+
import { PanelLeftIcon } from "lucide-react";
|
|
402
|
+
import { Slot as Slot2 } from "radix-ui";
|
|
403
|
+
|
|
404
|
+
// src/react/ui/hooks/use-mobile.ts
|
|
405
|
+
import * as React from "react";
|
|
406
|
+
var MOBILE_BREAKPOINT = 768;
|
|
407
|
+
function useIsMobile() {
|
|
408
|
+
const [isMobile, setIsMobile] = React.useState(void 0);
|
|
409
|
+
React.useEffect(() => {
|
|
410
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
411
|
+
const onChange = () => {
|
|
412
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
413
|
+
};
|
|
414
|
+
mql.addEventListener("change", onChange);
|
|
415
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
416
|
+
return () => mql.removeEventListener("change", onChange);
|
|
417
|
+
}, []);
|
|
418
|
+
return !!isMobile;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/react/ui/sheet.tsx
|
|
422
|
+
import { XIcon } from "lucide-react";
|
|
423
|
+
import { Dialog as SheetPrimitive } from "radix-ui";
|
|
424
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
425
|
+
function Sheet({ ...props }) {
|
|
426
|
+
return /* @__PURE__ */ jsx7(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
|
|
427
|
+
}
|
|
428
|
+
function SheetPortal({ ...props }) {
|
|
429
|
+
return /* @__PURE__ */ jsx7(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
|
|
430
|
+
}
|
|
431
|
+
function SheetOverlay({ className, ...props }) {
|
|
432
|
+
return /* @__PURE__ */ jsx7(
|
|
433
|
+
SheetPrimitive.Overlay,
|
|
434
|
+
{
|
|
435
|
+
"data-slot": "sheet-overlay",
|
|
436
|
+
className: cn(
|
|
437
|
+
"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
438
|
+
className
|
|
439
|
+
),
|
|
440
|
+
...props
|
|
441
|
+
}
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
function SheetContent({
|
|
445
|
+
className,
|
|
446
|
+
children,
|
|
447
|
+
side = "right",
|
|
448
|
+
showCloseButton = true,
|
|
449
|
+
...props
|
|
450
|
+
}) {
|
|
451
|
+
return /* @__PURE__ */ jsxs3(SheetPortal, { children: [
|
|
452
|
+
/* @__PURE__ */ jsx7(SheetOverlay, {}),
|
|
453
|
+
/* @__PURE__ */ jsxs3(
|
|
454
|
+
SheetPrimitive.Content,
|
|
455
|
+
{
|
|
456
|
+
"data-slot": "sheet-content",
|
|
457
|
+
className: cn(
|
|
458
|
+
"fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500",
|
|
459
|
+
side === "right" && "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
|
460
|
+
side === "left" && "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
|
461
|
+
side === "top" && "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
|
462
|
+
side === "bottom" && "inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
|
463
|
+
className
|
|
464
|
+
),
|
|
465
|
+
...props,
|
|
466
|
+
children: [
|
|
467
|
+
children,
|
|
468
|
+
showCloseButton && /* @__PURE__ */ jsxs3(SheetPrimitive.Close, { className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary", children: [
|
|
469
|
+
/* @__PURE__ */ jsx7(XIcon, { className: "size-4" }),
|
|
470
|
+
/* @__PURE__ */ jsx7("span", { className: "sr-only", children: "Close" })
|
|
471
|
+
] })
|
|
472
|
+
]
|
|
473
|
+
}
|
|
474
|
+
)
|
|
475
|
+
] });
|
|
476
|
+
}
|
|
477
|
+
function SheetHeader({ className, ...props }) {
|
|
478
|
+
return /* @__PURE__ */ jsx7("div", { "data-slot": "sheet-header", className: cn("flex flex-col gap-1.5 p-4", className), ...props });
|
|
479
|
+
}
|
|
480
|
+
function SheetTitle({ className, ...props }) {
|
|
481
|
+
return /* @__PURE__ */ jsx7(
|
|
482
|
+
SheetPrimitive.Title,
|
|
483
|
+
{
|
|
484
|
+
"data-slot": "sheet-title",
|
|
485
|
+
className: cn("font-semibold text-foreground", className),
|
|
486
|
+
...props
|
|
487
|
+
}
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
function SheetDescription({ className, ...props }) {
|
|
491
|
+
return /* @__PURE__ */ jsx7(
|
|
492
|
+
SheetPrimitive.Description,
|
|
493
|
+
{
|
|
494
|
+
"data-slot": "sheet-description",
|
|
495
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
496
|
+
...props
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/react/ui/sidebar.tsx
|
|
502
|
+
import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
503
|
+
var SIDEBAR_COOKIE_NAME = "sidebar_state";
|
|
504
|
+
var SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|
505
|
+
var SIDEBAR_WIDTH = "16rem";
|
|
506
|
+
var SIDEBAR_WIDTH_MOBILE = "18rem";
|
|
507
|
+
var SIDEBAR_WIDTH_ICON = "3rem";
|
|
508
|
+
var SidebarContext = React2.createContext(null);
|
|
509
|
+
function useSidebar() {
|
|
510
|
+
const context = React2.useContext(SidebarContext);
|
|
511
|
+
if (!context) {
|
|
512
|
+
throw new Error("useSidebar must be used within a SidebarProvider.");
|
|
513
|
+
}
|
|
514
|
+
return context;
|
|
515
|
+
}
|
|
516
|
+
function SidebarProvider({
|
|
517
|
+
defaultOpen = true,
|
|
518
|
+
open: openProp,
|
|
519
|
+
onOpenChange: setOpenProp,
|
|
520
|
+
className,
|
|
521
|
+
style,
|
|
522
|
+
children,
|
|
523
|
+
...props
|
|
524
|
+
}) {
|
|
525
|
+
const isMobile = useIsMobile();
|
|
526
|
+
const [openMobile, setOpenMobile] = React2.useState(false);
|
|
527
|
+
const [_open, _setOpen] = React2.useState(defaultOpen);
|
|
528
|
+
const open = openProp ?? _open;
|
|
529
|
+
const setOpen = React2.useCallback(
|
|
530
|
+
(value) => {
|
|
531
|
+
const openState = typeof value === "function" ? value(open) : value;
|
|
532
|
+
if (setOpenProp) {
|
|
533
|
+
setOpenProp(openState);
|
|
534
|
+
} else {
|
|
535
|
+
_setOpen(openState);
|
|
536
|
+
}
|
|
537
|
+
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
|
538
|
+
},
|
|
539
|
+
[setOpenProp, open]
|
|
540
|
+
);
|
|
541
|
+
const toggleSidebar = React2.useCallback(() => {
|
|
542
|
+
return isMobile ? setOpenMobile((open2) => !open2) : setOpen((open2) => !open2);
|
|
543
|
+
}, [isMobile, setOpen, setOpenMobile]);
|
|
544
|
+
const state = open ? "expanded" : "collapsed";
|
|
545
|
+
const contextValue = React2.useMemo(
|
|
546
|
+
() => ({
|
|
547
|
+
state,
|
|
548
|
+
open,
|
|
549
|
+
setOpen,
|
|
550
|
+
isMobile,
|
|
551
|
+
openMobile,
|
|
552
|
+
setOpenMobile,
|
|
553
|
+
toggleSidebar
|
|
554
|
+
}),
|
|
555
|
+
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
|
|
556
|
+
);
|
|
557
|
+
return /* @__PURE__ */ jsx8(SidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx8(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx8(
|
|
558
|
+
"div",
|
|
559
|
+
{
|
|
560
|
+
"data-slot": "sidebar-wrapper",
|
|
561
|
+
style: {
|
|
562
|
+
"--sidebar-width": SIDEBAR_WIDTH,
|
|
563
|
+
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
|
|
564
|
+
...style
|
|
565
|
+
},
|
|
566
|
+
className: cn("group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar", className),
|
|
567
|
+
...props,
|
|
568
|
+
children
|
|
569
|
+
}
|
|
570
|
+
) }) });
|
|
571
|
+
}
|
|
572
|
+
function Sidebar({
|
|
573
|
+
side = "left",
|
|
574
|
+
variant = "sidebar",
|
|
575
|
+
collapsible = "offcanvas",
|
|
576
|
+
className,
|
|
577
|
+
children,
|
|
578
|
+
...props
|
|
579
|
+
}) {
|
|
580
|
+
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
581
|
+
if (collapsible === "none") {
|
|
582
|
+
return /* @__PURE__ */ jsx8(
|
|
583
|
+
"div",
|
|
584
|
+
{
|
|
585
|
+
"data-slot": "sidebar",
|
|
586
|
+
className: cn("flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground", className),
|
|
587
|
+
...props,
|
|
588
|
+
children
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
if (isMobile) {
|
|
593
|
+
return /* @__PURE__ */ jsx8(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: /* @__PURE__ */ jsxs4(
|
|
594
|
+
SheetContent,
|
|
595
|
+
{
|
|
596
|
+
"data-sidebar": "sidebar",
|
|
597
|
+
"data-slot": "sidebar",
|
|
598
|
+
"data-mobile": "true",
|
|
599
|
+
className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
|
|
600
|
+
style: {
|
|
601
|
+
"--sidebar-width": SIDEBAR_WIDTH_MOBILE
|
|
602
|
+
},
|
|
603
|
+
side,
|
|
604
|
+
children: [
|
|
605
|
+
/* @__PURE__ */ jsxs4(SheetHeader, { className: "sr-only", children: [
|
|
606
|
+
/* @__PURE__ */ jsx8(SheetTitle, { children: "Sidebar" }),
|
|
607
|
+
/* @__PURE__ */ jsx8(SheetDescription, { children: "Displays the mobile sidebar." })
|
|
608
|
+
] }),
|
|
609
|
+
/* @__PURE__ */ jsx8("div", { className: "flex h-full w-full flex-col", children })
|
|
610
|
+
]
|
|
611
|
+
}
|
|
612
|
+
) });
|
|
613
|
+
}
|
|
614
|
+
return /* @__PURE__ */ jsxs4(
|
|
615
|
+
"div",
|
|
616
|
+
{
|
|
617
|
+
className: "group peer hidden text-sidebar-foreground md:block",
|
|
618
|
+
"data-state": state,
|
|
619
|
+
"data-collapsible": state === "collapsed" ? collapsible : "",
|
|
620
|
+
"data-variant": variant,
|
|
621
|
+
"data-side": side,
|
|
622
|
+
"data-slot": "sidebar",
|
|
623
|
+
children: [
|
|
624
|
+
/* @__PURE__ */ jsx8(
|
|
625
|
+
"div",
|
|
626
|
+
{
|
|
627
|
+
"data-slot": "sidebar-gap",
|
|
628
|
+
className: cn(
|
|
629
|
+
"relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
|
|
630
|
+
"group-data-[collapsible=offcanvas]:w-0",
|
|
631
|
+
"group-data-[side=right]:rotate-180",
|
|
632
|
+
variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
|
|
633
|
+
)
|
|
634
|
+
}
|
|
635
|
+
),
|
|
636
|
+
/* @__PURE__ */ jsx8(
|
|
637
|
+
"div",
|
|
638
|
+
{
|
|
639
|
+
"data-slot": "sidebar-container",
|
|
640
|
+
className: cn(
|
|
641
|
+
"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
|
|
642
|
+
side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
|
643
|
+
// Adjust the padding for floating and inset variants.
|
|
644
|
+
variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
645
|
+
className
|
|
646
|
+
),
|
|
647
|
+
...props,
|
|
648
|
+
children: /* @__PURE__ */ jsx8(
|
|
649
|
+
"div",
|
|
650
|
+
{
|
|
651
|
+
"data-sidebar": "sidebar",
|
|
652
|
+
"data-slot": "sidebar-inner",
|
|
653
|
+
className: "flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-sm",
|
|
654
|
+
children
|
|
655
|
+
}
|
|
656
|
+
)
|
|
657
|
+
}
|
|
658
|
+
)
|
|
659
|
+
]
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
function SidebarHeader({ className, ...props }) {
|
|
664
|
+
return /* @__PURE__ */ jsx8(
|
|
665
|
+
"div",
|
|
666
|
+
{
|
|
667
|
+
"data-slot": "sidebar-header",
|
|
668
|
+
"data-sidebar": "header",
|
|
669
|
+
className: cn("flex flex-col gap-2 p-2", className),
|
|
670
|
+
...props
|
|
671
|
+
}
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
function SidebarFooter({ className, ...props }) {
|
|
675
|
+
return /* @__PURE__ */ jsx8(
|
|
676
|
+
"div",
|
|
677
|
+
{
|
|
678
|
+
"data-slot": "sidebar-footer",
|
|
679
|
+
"data-sidebar": "footer",
|
|
680
|
+
className: cn("flex flex-col gap-2 p-2", className),
|
|
681
|
+
...props
|
|
682
|
+
}
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
function SidebarContent({ className, ...props }) {
|
|
686
|
+
return /* @__PURE__ */ jsx8(
|
|
687
|
+
"div",
|
|
688
|
+
{
|
|
689
|
+
"data-slot": "sidebar-content",
|
|
690
|
+
"data-sidebar": "content",
|
|
691
|
+
className: cn(
|
|
692
|
+
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
|
|
693
|
+
className
|
|
694
|
+
),
|
|
695
|
+
...props
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
function SidebarGroup({ className, ...props }) {
|
|
700
|
+
return /* @__PURE__ */ jsx8(
|
|
701
|
+
"div",
|
|
702
|
+
{
|
|
703
|
+
"data-slot": "sidebar-group",
|
|
704
|
+
"data-sidebar": "group",
|
|
705
|
+
className: cn("relative flex w-full min-w-0 flex-col p-2", className),
|
|
706
|
+
...props
|
|
707
|
+
}
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
function SidebarMenu({ className, ...props }) {
|
|
711
|
+
return /* @__PURE__ */ jsx8(
|
|
712
|
+
"ul",
|
|
713
|
+
{
|
|
714
|
+
"data-slot": "sidebar-menu",
|
|
715
|
+
"data-sidebar": "menu",
|
|
716
|
+
className: cn("flex w-full min-w-0 flex-col gap-1", className),
|
|
717
|
+
...props
|
|
718
|
+
}
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
function SidebarMenuItem({ className, ...props }) {
|
|
722
|
+
return /* @__PURE__ */ jsx8(
|
|
723
|
+
"li",
|
|
724
|
+
{
|
|
725
|
+
"data-slot": "sidebar-menu-item",
|
|
726
|
+
"data-sidebar": "menu-item",
|
|
727
|
+
className: cn("group/menu-item relative", className),
|
|
728
|
+
...props
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
var sidebarMenuButtonVariants = cva2(
|
|
733
|
+
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
|
734
|
+
{
|
|
735
|
+
variants: {
|
|
736
|
+
variant: {
|
|
737
|
+
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
738
|
+
outline: "bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]"
|
|
739
|
+
},
|
|
740
|
+
size: {
|
|
741
|
+
default: "h-8 text-sm",
|
|
742
|
+
sm: "h-7 text-xs",
|
|
743
|
+
lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!"
|
|
744
|
+
}
|
|
745
|
+
},
|
|
746
|
+
defaultVariants: {
|
|
747
|
+
variant: "default",
|
|
748
|
+
size: "default"
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
);
|
|
752
|
+
function SidebarMenuButton({
|
|
753
|
+
asChild = false,
|
|
754
|
+
isActive = false,
|
|
755
|
+
variant = "default",
|
|
756
|
+
size = "default",
|
|
757
|
+
tooltip,
|
|
758
|
+
className,
|
|
759
|
+
...props
|
|
760
|
+
}) {
|
|
761
|
+
const Comp = asChild ? Slot2.Root : "button";
|
|
762
|
+
const { isMobile, state } = useSidebar();
|
|
763
|
+
const button2 = /* @__PURE__ */ jsx8(
|
|
764
|
+
Comp,
|
|
765
|
+
{
|
|
766
|
+
"data-slot": "sidebar-menu-button",
|
|
767
|
+
"data-sidebar": "menu-button",
|
|
768
|
+
"data-size": size,
|
|
769
|
+
"data-active": isActive,
|
|
770
|
+
className: cn(sidebarMenuButtonVariants({ variant, size }), className),
|
|
771
|
+
...props
|
|
772
|
+
}
|
|
773
|
+
);
|
|
774
|
+
if (!tooltip) {
|
|
775
|
+
return button2;
|
|
776
|
+
}
|
|
777
|
+
if (typeof tooltip === "string") {
|
|
778
|
+
tooltip = {
|
|
779
|
+
children: tooltip
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
return /* @__PURE__ */ jsxs4(Tooltip, { children: [
|
|
783
|
+
/* @__PURE__ */ jsx8(TooltipTrigger, { asChild: true, children: button2 }),
|
|
784
|
+
/* @__PURE__ */ jsx8(TooltipContent, { side: "right", align: "center", hidden: state !== "collapsed" || isMobile, ...tooltip })
|
|
785
|
+
] });
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/react/studio.tsx
|
|
789
|
+
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
790
|
+
var CONNECTION = {
|
|
791
|
+
connected: { icon: PlugZap, tone: "text-success", label: "Runtime connected" },
|
|
792
|
+
connecting: { icon: LoaderCircle, tone: "text-warning animate-spin", label: "Connecting to runtime" },
|
|
793
|
+
offline: { icon: Unplug, tone: "text-destructive", label: "Runtime offline" },
|
|
794
|
+
local: { icon: Unplug, tone: "text-faint", label: "No runtime" }
|
|
795
|
+
};
|
|
796
|
+
var PANEL = "rounded-lg border border-sidebar-border bg-sidebar/85 shadow-xl shadow-black/40 backdrop-blur-xl";
|
|
797
|
+
var PANEL_CONTAINER = cn(
|
|
798
|
+
"[&>[data-slot=sidebar-inner]]:bg-sidebar/85 [&>[data-slot=sidebar-inner]]:shadow-xl [&>[data-slot=sidebar-inner]]:shadow-black/40 [&>[data-slot=sidebar-inner]]:backdrop-blur-xl"
|
|
799
|
+
);
|
|
800
|
+
function StudioFooter({ connection }) {
|
|
801
|
+
const { state, toggleSidebar } = useSidebar();
|
|
802
|
+
const { icon: Status, tone, label } = CONNECTION[connection.phase];
|
|
803
|
+
const Toggle = state === "expanded" ? PanelLeftClose : PanelLeftOpen;
|
|
804
|
+
return /* @__PURE__ */ jsx9(SidebarFooter, { children: /* @__PURE__ */ jsxs5(SidebarMenu, { children: [
|
|
805
|
+
/* @__PURE__ */ jsx9(SidebarMenuItem, { children: /* @__PURE__ */ jsxs5(SidebarMenuButton, { tooltip: label, className: "cursor-default hover:bg-transparent active:bg-transparent", children: [
|
|
806
|
+
/* @__PURE__ */ jsx9(Status, { className: tone }),
|
|
807
|
+
/* @__PURE__ */ jsx9("span", { className: "text-clip! group-data-[collapsible=icon]:hidden", children: label })
|
|
808
|
+
] }) }),
|
|
809
|
+
/* @__PURE__ */ jsx9(SidebarMenuItem, { children: /* @__PURE__ */ jsxs5(
|
|
810
|
+
SidebarMenuButton,
|
|
811
|
+
{
|
|
812
|
+
"aria-label": `${state === "expanded" ? "Collapse" : "Expand"} sidebar`,
|
|
813
|
+
tooltip: `${state === "expanded" ? "Collapse" : "Expand"} sidebar`,
|
|
814
|
+
onClick: toggleSidebar,
|
|
815
|
+
children: [
|
|
816
|
+
/* @__PURE__ */ jsx9(Toggle, {}),
|
|
817
|
+
/* @__PURE__ */ jsx9("span", { className: "text-clip! group-data-[collapsible=icon]:hidden", children: "Collapse" })
|
|
818
|
+
]
|
|
819
|
+
}
|
|
820
|
+
) })
|
|
821
|
+
] }) });
|
|
822
|
+
}
|
|
823
|
+
var SHORTCUTS = /* @__PURE__ */ new Set(["b", "s", "k"]);
|
|
824
|
+
var COMPARE_KEY = "\\";
|
|
825
|
+
var editable = (target) => target instanceof Element && target.matches("input, textarea, select, [contenteditable]");
|
|
826
|
+
var isShortcut = (event) => (event.metaKey || event.ctrlKey) && SHORTCUTS.has(event.key.toLowerCase()) || event.key === COMPARE_KEY && !editable(event.target);
|
|
827
|
+
function forwardShortcuts(frame) {
|
|
828
|
+
const forward = (event) => {
|
|
829
|
+
if (event.defaultPrevented || !isShortcut(event)) return;
|
|
830
|
+
event.preventDefault();
|
|
831
|
+
window.dispatchEvent(new KeyboardEvent(event.type, event));
|
|
832
|
+
};
|
|
833
|
+
try {
|
|
834
|
+
frame.contentWindow?.addEventListener("keydown", forward);
|
|
835
|
+
frame.contentWindow?.addEventListener("keyup", forward);
|
|
836
|
+
} catch {
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
function Studio({ studio }) {
|
|
840
|
+
useStudio(studio);
|
|
841
|
+
const [mounted, setMounted] = useState4(false);
|
|
842
|
+
const [panelsHidden, setPanelsHidden] = useState4(false);
|
|
843
|
+
const [selected, setSelected] = useState4(studio.tools[0]?.definition.id);
|
|
844
|
+
const preview = useRef2(null);
|
|
845
|
+
const handle = studio.tools.find((tool) => tool.definition.id === selected);
|
|
846
|
+
useEffect3(() => {
|
|
847
|
+
const onKey = (event) => {
|
|
848
|
+
if (event.defaultPrevented || event.isComposing || event.altKey || !(event.metaKey || event.ctrlKey)) return;
|
|
849
|
+
if (event.key.toLowerCase() !== "b") return;
|
|
850
|
+
event.preventDefault();
|
|
851
|
+
if (event.repeat) return;
|
|
852
|
+
const active = document.activeElement;
|
|
853
|
+
if (active instanceof HTMLElement && active.closest('[data-slot="sidebar-container"], [data-slot="studio-inspector"]')) {
|
|
854
|
+
active.blur();
|
|
855
|
+
preview.current?.focus();
|
|
856
|
+
}
|
|
857
|
+
setPanelsHidden((hidden) => !hidden);
|
|
858
|
+
};
|
|
859
|
+
window.addEventListener("keydown", onKey);
|
|
860
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
861
|
+
}, []);
|
|
862
|
+
const compare = (showSaved) => {
|
|
863
|
+
if (!handle) return;
|
|
864
|
+
handle.compare(showSaved);
|
|
865
|
+
postCompare(preview.current?.contentWindow, handle.definition.id, showSaved);
|
|
866
|
+
};
|
|
867
|
+
useEffect3(() => () => handle?.compare(false), [handle]);
|
|
868
|
+
const current = useRef2({ handle, compare });
|
|
869
|
+
current.current = { handle, compare };
|
|
870
|
+
useEffect3(() => {
|
|
871
|
+
const onKey = (event) => {
|
|
872
|
+
if (event.key !== COMPARE_KEY || editable(event.target) || event.repeat) return;
|
|
873
|
+
event.preventDefault();
|
|
874
|
+
const { handle: handle2, compare: compare2 } = current.current;
|
|
875
|
+
compare2(event.type === "keydown" && !!handle2?.state?.dirty);
|
|
876
|
+
};
|
|
877
|
+
const release = () => current.current.compare(false);
|
|
878
|
+
addEventListener("keydown", onKey);
|
|
879
|
+
addEventListener("keyup", onKey);
|
|
880
|
+
addEventListener("blur", release);
|
|
881
|
+
return () => {
|
|
882
|
+
release();
|
|
883
|
+
removeEventListener("keydown", onKey);
|
|
884
|
+
removeEventListener("keyup", onKey);
|
|
885
|
+
removeEventListener("blur", release);
|
|
886
|
+
};
|
|
887
|
+
}, []);
|
|
888
|
+
useEffect3(() => {
|
|
889
|
+
const fromHash = () => {
|
|
890
|
+
const id = decodeURIComponent(location.hash.slice(1));
|
|
891
|
+
if (studio.tools.some((handle2) => handle2.definition.id === id)) setSelected(id);
|
|
892
|
+
};
|
|
893
|
+
setMounted(true);
|
|
894
|
+
fromHash();
|
|
895
|
+
addEventListener("hashchange", fromHash);
|
|
896
|
+
return () => removeEventListener("hashchange", fromHash);
|
|
897
|
+
}, [studio]);
|
|
898
|
+
if (!mounted) return null;
|
|
899
|
+
const sidebarOpen = !document.cookie.split("; ").includes("sidebar_state=false");
|
|
900
|
+
return /* @__PURE__ */ jsxs5(SidebarProvider, { defaultOpen: sidebarOpen, className: "relative h-svh min-h-0 overflow-hidden font-mono text-xs", children: [
|
|
901
|
+
handle?.definition.preview ? /* @__PURE__ */ jsx9(
|
|
902
|
+
"iframe",
|
|
903
|
+
{
|
|
904
|
+
"data-slot": "studio-preview",
|
|
905
|
+
ref: preview,
|
|
906
|
+
src: handle.definition.preview,
|
|
907
|
+
onLoad: (event) => forwardShortcuts(event.currentTarget),
|
|
908
|
+
className: "absolute inset-0 size-full border-0 bg-black"
|
|
909
|
+
},
|
|
910
|
+
handle.definition.id
|
|
911
|
+
) : /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 grid place-items-center text-faint", children: handle ? "This tool has no preview" : "No tools registered" }),
|
|
912
|
+
panelsHidden && ["left", "right"].map((side) => {
|
|
913
|
+
const Chevron = side === "left" ? ChevronRight : ChevronLeft;
|
|
914
|
+
return /* @__PURE__ */ jsx9(
|
|
915
|
+
"div",
|
|
916
|
+
{
|
|
917
|
+
"data-slot": "studio-panel-edge",
|
|
918
|
+
"data-side": side,
|
|
919
|
+
className: cn("group/edge absolute inset-y-0 z-20 w-8", side === "left" ? "left-0" : "right-0"),
|
|
920
|
+
children: /* @__PURE__ */ jsxs5(Tooltip, { children: [
|
|
921
|
+
/* @__PURE__ */ jsx9(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx9(
|
|
922
|
+
Button,
|
|
923
|
+
{
|
|
924
|
+
"data-slot": "studio-panel-reveal",
|
|
925
|
+
variant: "ghost",
|
|
926
|
+
size: "icon",
|
|
927
|
+
"aria-label": "Show Studio panels",
|
|
928
|
+
onClick: () => setPanelsHidden(false),
|
|
929
|
+
className: cn(
|
|
930
|
+
PANEL,
|
|
931
|
+
"absolute top-1/2 h-16 w-6 -translate-y-1/2 cursor-pointer px-0 text-sidebar-foreground opacity-0 transition-[translate,opacity] duration-200 ease-out hover:bg-sidebar/95 hover:text-foreground focus:translate-x-0 focus:opacity-100 group-hover/edge:translate-x-0 group-hover/edge:opacity-100 motion-reduce:transition-none [@media(hover:none)]:translate-x-0 [@media(hover:none)]:opacity-100",
|
|
932
|
+
side === "left" ? "left-1 -translate-x-[calc(100%+0.25rem)]" : "right-1 translate-x-[calc(100%+0.25rem)]"
|
|
933
|
+
),
|
|
934
|
+
children: /* @__PURE__ */ jsx9(Chevron, { "aria-hidden": true })
|
|
935
|
+
}
|
|
936
|
+
) }),
|
|
937
|
+
/* @__PURE__ */ jsxs5(
|
|
938
|
+
TooltipContent,
|
|
939
|
+
{
|
|
940
|
+
side: side === "left" ? "right" : "left",
|
|
941
|
+
sideOffset: 8,
|
|
942
|
+
className: "flex items-center gap-2",
|
|
943
|
+
children: [
|
|
944
|
+
"Show Studio panels",
|
|
945
|
+
/* @__PURE__ */ jsxs5(KbdGroup, { children: [
|
|
946
|
+
/* @__PURE__ */ jsx9(Kbd, { children: "\u2318/Ctrl" }),
|
|
947
|
+
/* @__PURE__ */ jsx9(Kbd, { children: "B" })
|
|
948
|
+
] })
|
|
949
|
+
]
|
|
950
|
+
}
|
|
951
|
+
)
|
|
952
|
+
] })
|
|
953
|
+
},
|
|
954
|
+
side
|
|
955
|
+
);
|
|
956
|
+
}),
|
|
957
|
+
/* @__PURE__ */ jsxs5(
|
|
958
|
+
Sidebar,
|
|
959
|
+
{
|
|
960
|
+
collapsible: "icon",
|
|
961
|
+
variant: "floating",
|
|
962
|
+
inert: panelsHidden,
|
|
963
|
+
"aria-hidden": panelsHidden,
|
|
964
|
+
className: cn(
|
|
965
|
+
PANEL_CONTAINER,
|
|
966
|
+
"transition-[left,right,width,translate] duration-200 ease-out motion-reduce:transition-none",
|
|
967
|
+
panelsHidden && "-translate-x-[calc(100%+1rem)]"
|
|
968
|
+
),
|
|
969
|
+
children: [
|
|
970
|
+
/* @__PURE__ */ jsx9(SidebarHeader, { className: "h-10 flex-row items-center overflow-hidden py-0 pr-0 pl-4 transition-[padding] duration-200 ease-out group-data-[collapsible=icon]:pl-[calc(1.5rem-0.875rem*352/144/2)] motion-reduce:transition-none", children: /* @__PURE__ */ jsxs5("span", { className: "flex items-center gap-2.5 text-foreground", children: [
|
|
971
|
+
/* @__PURE__ */ jsx9(Logo, { className: "h-3.5 w-auto shrink-0" }),
|
|
972
|
+
/* @__PURE__ */ jsx9("strong", { className: "tracking-[0.08em] whitespace-nowrap", children: "STUDIO" }),
|
|
973
|
+
/* @__PURE__ */ jsxs5("span", { "data-slot": "studio-version", className: "text-[10px] text-faint group-data-[collapsible=icon]:hidden", children: [
|
|
974
|
+
"v",
|
|
975
|
+
version
|
|
976
|
+
] })
|
|
977
|
+
] }) }),
|
|
978
|
+
/* @__PURE__ */ jsx9(SidebarContent, { children: /* @__PURE__ */ jsx9(SidebarGroup, { children: /* @__PURE__ */ jsx9(SidebarMenu, { children: studio.tools.map(({ definition, state }) => {
|
|
979
|
+
const title = definition.title ?? definition.id;
|
|
980
|
+
return /* @__PURE__ */ jsx9(SidebarMenuItem, { children: /* @__PURE__ */ jsxs5(
|
|
981
|
+
SidebarMenuButton,
|
|
982
|
+
{
|
|
983
|
+
isActive: definition.id === selected,
|
|
984
|
+
tooltip: title,
|
|
985
|
+
onClick: () => location.hash = definition.id,
|
|
986
|
+
children: [
|
|
987
|
+
/* @__PURE__ */ jsxs5("span", { className: "relative", children: [
|
|
988
|
+
/* @__PURE__ */ jsx9(ToolIcon, { icon: definition.icon, className: "size-4" }),
|
|
989
|
+
state?.dirty && /* @__PURE__ */ jsx9(
|
|
990
|
+
"span",
|
|
991
|
+
{
|
|
992
|
+
"aria-label": state.conflict ? "Changed on disk" : "Unsaved",
|
|
993
|
+
className: cn(
|
|
994
|
+
"absolute -top-0.5 -right-0.5 size-1.5 rounded-full",
|
|
995
|
+
state.conflict ? "bg-destructive" : "bg-warning"
|
|
996
|
+
)
|
|
997
|
+
}
|
|
998
|
+
)
|
|
999
|
+
] }),
|
|
1000
|
+
/* @__PURE__ */ jsx9("span", { className: "text-clip! group-data-[collapsible=icon]:hidden", children: title })
|
|
1001
|
+
]
|
|
1002
|
+
}
|
|
1003
|
+
) }, definition.id);
|
|
1004
|
+
}) }) }) }),
|
|
1005
|
+
/* @__PURE__ */ jsx9(StudioFooter, { connection: studio.connection })
|
|
1006
|
+
]
|
|
1007
|
+
}
|
|
1008
|
+
),
|
|
1009
|
+
handle && /* @__PURE__ */ jsx9(
|
|
1010
|
+
"aside",
|
|
1011
|
+
{
|
|
1012
|
+
"data-slot": "studio-inspector",
|
|
1013
|
+
inert: panelsHidden,
|
|
1014
|
+
"aria-hidden": panelsHidden,
|
|
1015
|
+
className: cn(
|
|
1016
|
+
PANEL,
|
|
1017
|
+
"absolute inset-y-2 right-2 z-10 flex w-80 flex-col transition-transform duration-200 ease-out motion-reduce:transition-none",
|
|
1018
|
+
panelsHidden && "translate-x-[calc(100%+1rem)]"
|
|
1019
|
+
),
|
|
1020
|
+
children: /* @__PURE__ */ jsx9(Inspector, { studio, handle, onCompare: compare }, handle.definition.id)
|
|
1021
|
+
}
|
|
1022
|
+
)
|
|
1023
|
+
] });
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// src/react/command-menu.tsx
|
|
1027
|
+
import { Dialog } from "radix-ui";
|
|
1028
|
+
import { useEffect as useEffect4, useRef as useRef3, useState as useState5 } from "react";
|
|
1029
|
+
|
|
1030
|
+
// src/react/ui/command.tsx
|
|
1031
|
+
import * as React3 from "react";
|
|
1032
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
1033
|
+
import { SearchIcon } from "lucide-react";
|
|
1034
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1035
|
+
function Command({ className, ...props }) {
|
|
1036
|
+
return /* @__PURE__ */ jsx10(
|
|
1037
|
+
CommandPrimitive,
|
|
1038
|
+
{
|
|
1039
|
+
"data-slot": "command",
|
|
1040
|
+
className: cn(
|
|
1041
|
+
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
|
1042
|
+
className
|
|
1043
|
+
),
|
|
1044
|
+
...props
|
|
1045
|
+
}
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
var CommandInput = React3.forwardRef(function CommandInput2({ className, ...props }, ref) {
|
|
1049
|
+
return /* @__PURE__ */ jsxs6("div", { "data-slot": "command-input-wrapper", className: "flex h-9 items-center gap-2 border-b px-3", children: [
|
|
1050
|
+
/* @__PURE__ */ jsx10(SearchIcon, { "aria-hidden": true, className: "size-4 shrink-0 opacity-50" }),
|
|
1051
|
+
/* @__PURE__ */ jsx10(
|
|
1052
|
+
CommandPrimitive.Input,
|
|
1053
|
+
{
|
|
1054
|
+
ref,
|
|
1055
|
+
"data-slot": "command-input",
|
|
1056
|
+
className: cn(
|
|
1057
|
+
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
|
1058
|
+
className
|
|
1059
|
+
),
|
|
1060
|
+
...props
|
|
1061
|
+
}
|
|
1062
|
+
)
|
|
1063
|
+
] });
|
|
1064
|
+
});
|
|
1065
|
+
function CommandList({ className, ...props }) {
|
|
1066
|
+
return /* @__PURE__ */ jsx10(
|
|
1067
|
+
CommandPrimitive.List,
|
|
1068
|
+
{
|
|
1069
|
+
"data-slot": "command-list",
|
|
1070
|
+
className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className),
|
|
1071
|
+
...props
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
function CommandEmpty({ ...props }) {
|
|
1076
|
+
return /* @__PURE__ */ jsx10(CommandPrimitive.Empty, { "data-slot": "command-empty", className: "py-6 text-center text-sm", ...props });
|
|
1077
|
+
}
|
|
1078
|
+
function CommandItem({ className, ...props }) {
|
|
1079
|
+
return /* @__PURE__ */ jsx10(
|
|
1080
|
+
CommandPrimitive.Item,
|
|
1081
|
+
{
|
|
1082
|
+
"data-slot": "command-item",
|
|
1083
|
+
className: cn(
|
|
1084
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
1085
|
+
className
|
|
1086
|
+
),
|
|
1087
|
+
...props
|
|
1088
|
+
}
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// src/react/command-menu.tsx
|
|
1093
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1094
|
+
function StudioCommandMenu({ studio, studioPath = "/studio", navigate }) {
|
|
1095
|
+
const [open, setOpen] = useState5(false);
|
|
1096
|
+
const opener = useRef3(null);
|
|
1097
|
+
const input = useRef3(null);
|
|
1098
|
+
useEffect4(() => {
|
|
1099
|
+
if (window.frameElement?.getAttribute("data-slot") === "studio-preview") return;
|
|
1100
|
+
const onKey = (event) => {
|
|
1101
|
+
if (event.defaultPrevented || event.isComposing || event.altKey || !(event.metaKey || event.ctrlKey)) return;
|
|
1102
|
+
if (event.key.toLowerCase() !== "k") return;
|
|
1103
|
+
event.preventDefault();
|
|
1104
|
+
if (event.repeat) return;
|
|
1105
|
+
setOpen((current) => !current);
|
|
1106
|
+
};
|
|
1107
|
+
window.addEventListener("keydown", onKey);
|
|
1108
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1109
|
+
}, []);
|
|
1110
|
+
const select = (id) => {
|
|
1111
|
+
setOpen(false);
|
|
1112
|
+
const target = new URL(studioPath, window.location.href);
|
|
1113
|
+
target.hash = id;
|
|
1114
|
+
if (target.pathname === window.location.pathname) {
|
|
1115
|
+
window.location.hash = target.hash;
|
|
1116
|
+
} else if (navigate) {
|
|
1117
|
+
navigate(`${target.pathname}${target.search}${target.hash}`);
|
|
1118
|
+
} else {
|
|
1119
|
+
window.location.assign(target.href);
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
return /* @__PURE__ */ jsx11(Dialog.Root, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs7(Dialog.Portal, { children: [
|
|
1123
|
+
/* @__PURE__ */ jsx11(Dialog.Overlay, { "data-slot": "studio-command-overlay" }),
|
|
1124
|
+
/* @__PURE__ */ jsxs7(
|
|
1125
|
+
Dialog.Content,
|
|
1126
|
+
{
|
|
1127
|
+
"data-slot": "studio-command-menu",
|
|
1128
|
+
onOpenAutoFocus: (event) => {
|
|
1129
|
+
event.preventDefault();
|
|
1130
|
+
opener.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
1131
|
+
input.current?.focus();
|
|
1132
|
+
},
|
|
1133
|
+
onCloseAutoFocus: (event) => {
|
|
1134
|
+
event.preventDefault();
|
|
1135
|
+
opener.current?.focus();
|
|
1136
|
+
},
|
|
1137
|
+
children: [
|
|
1138
|
+
/* @__PURE__ */ jsxs7("div", { "data-slot": "studio-command-heading", children: [
|
|
1139
|
+
/* @__PURE__ */ jsxs7(Dialog.Title, { "aria-label": "Studio tools", children: [
|
|
1140
|
+
/* @__PURE__ */ jsx11(Logo, { "aria-hidden": true }),
|
|
1141
|
+
/* @__PURE__ */ jsx11("strong", { children: "STUDIO" })
|
|
1142
|
+
] }),
|
|
1143
|
+
/* @__PURE__ */ jsx11(Dialog.Close, { "aria-label": "Close tool menu", children: /* @__PURE__ */ jsx11(Kbd, { children: "esc" }) })
|
|
1144
|
+
] }),
|
|
1145
|
+
/* @__PURE__ */ jsx11(Dialog.Description, { "data-slot": "studio-command-description", children: "Open a tool in Studio." }),
|
|
1146
|
+
/* @__PURE__ */ jsxs7(Command, { label: "Studio tools", loop: true, vimBindings: false, children: [
|
|
1147
|
+
/* @__PURE__ */ jsx11(CommandInput, { ref: input, placeholder: "Search tools\u2026" }),
|
|
1148
|
+
/* @__PURE__ */ jsxs7(CommandList, { children: [
|
|
1149
|
+
/* @__PURE__ */ jsx11(CommandEmpty, { children: studio.tools.length ? "No tools found." : "No tools registered yet." }),
|
|
1150
|
+
studio.tools.map(({ definition: tool }) => /* @__PURE__ */ jsxs7(
|
|
1151
|
+
CommandItem,
|
|
1152
|
+
{
|
|
1153
|
+
value: tool.id,
|
|
1154
|
+
keywords: [tool.title ?? tool.id],
|
|
1155
|
+
onSelect: () => select(tool.id),
|
|
1156
|
+
children: [
|
|
1157
|
+
/* @__PURE__ */ jsx11(ToolIcon, { icon: tool.icon }),
|
|
1158
|
+
/* @__PURE__ */ jsx11("span", { "data-slot": "studio-command-label", children: tool.title ?? tool.id })
|
|
1159
|
+
]
|
|
1160
|
+
},
|
|
1161
|
+
tool.id
|
|
1162
|
+
))
|
|
1163
|
+
] })
|
|
1164
|
+
] }),
|
|
1165
|
+
/* @__PURE__ */ jsxs7("div", { "data-slot": "studio-command-footer", "aria-hidden": true, children: [
|
|
1166
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
1167
|
+
/* @__PURE__ */ jsxs7(KbdGroup, { children: [
|
|
1168
|
+
/* @__PURE__ */ jsx11(Kbd, { children: "\u2191" }),
|
|
1169
|
+
/* @__PURE__ */ jsx11(Kbd, { children: "\u2193" })
|
|
1170
|
+
] }),
|
|
1171
|
+
"navigate"
|
|
1172
|
+
] }),
|
|
1173
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
1174
|
+
/* @__PURE__ */ jsx11(Kbd, { children: "\u21B5" }),
|
|
1175
|
+
" open tool"
|
|
1176
|
+
] })
|
|
1177
|
+
] })
|
|
1178
|
+
]
|
|
1179
|
+
}
|
|
1180
|
+
)
|
|
1181
|
+
] }) });
|
|
1182
|
+
}
|
|
1183
|
+
export {
|
|
1184
|
+
Studio,
|
|
1185
|
+
StudioCommandMenu,
|
|
1186
|
+
useStudio,
|
|
1187
|
+
useToolConfig
|
|
1188
|
+
};
|
|
1189
|
+
//# sourceMappingURL=react.js.map
|