@joycostudio/studio 0.1.4 → 0.1.6
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 +17 -2
- package/dist/{chunk-2RGQF5X5.js → chunk-6KZJEKOL.js} +147 -59
- package/dist/chunk-6KZJEKOL.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +27 -4
- package/dist/client.js +1 -1
- package/dist/next.js +1 -1
- package/dist/react.d.ts +26 -3
- package/dist/react.js +445 -322
- package/dist/react.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/chunk-2RGQF5X5.js.map +0 -1
package/README.md
CHANGED
|
@@ -197,8 +197,9 @@ Live edits also work without `studio dev`, including in production. Studio share
|
|
|
197
197
|
same-origin previews and tabs in the same browser using `BroadcastChannel`. Peers match by tool ID,
|
|
198
198
|
config file, and runtime URL (or no runtime); a newly opened or reloaded preview receives the current
|
|
199
199
|
config from an open peer. Nothing is persisted: closing every page resets these edits to the shipped
|
|
200
|
-
config.
|
|
201
|
-
|
|
200
|
+
config. Discard works the same either way: it resets the draft to the saved config (the runtime's last
|
|
201
|
+
reported one, or the shipped config) and syncs like any edit. Save and MCP require a runtime. If a configured
|
|
202
|
+
runtime disconnects, browser sync continues; reconnecting restores the runtime's draft.
|
|
202
203
|
|
|
203
204
|
Floating debug pane, with the same controls and save/discard actions:
|
|
204
205
|
|
|
@@ -257,6 +258,20 @@ Undo is per tool and per page: it restores only the values you changed, leaving
|
|
|
257
258
|
in place, and syncs like any other edit. Text fields keep their native undo while focused. History is not persisted.
|
|
258
259
|
`tool.undo()`, `tool.redo()`, `tool.canUndo`, and `tool.canRedo` expose the same history to host code.
|
|
259
260
|
|
|
261
|
+
The inspector footer summarizes unsaved changes on one line. Click it to expand the footer into the full list: each changed
|
|
262
|
+
value shows its saved and current value (with a swatch for colors) and a revert button that resets just that value. A revert
|
|
263
|
+
syncs like any edit and can be undone. The list collapses once nothing is left to save. `tool.diff` lists the same
|
|
264
|
+
`{ path, before, after }` changes, and `tool.revert(path)` resets one from host code.
|
|
265
|
+
|
|
266
|
+
To share a config with a teammate, click the link button in the inspector footer (or **Copy link** in the floating debug
|
|
267
|
+
pane). It copies the current page's URL with the tool's draft encoded in a `studio-share` query parameter; links from
|
|
268
|
+
Studio also select the tool. Opening the link applies that draft as an unsaved edit on top of whatever the recipient has,
|
|
269
|
+
with or without a runtime: it syncs to their previews, other tabs, and runtime like any edit, can be undone, and still needs
|
|
270
|
+
Save to reach the JSON file. The parameter is removed from the address bar once applied, so reloading doesn't reapply it.
|
|
271
|
+
A draft that no longer matches the tool's controls (for example, from another branch) is ignored with a console warning.
|
|
272
|
+
If a configured runtime is unreachable when the link opens, the draft applies locally and the runtime's draft wins once it
|
|
273
|
+
connects. `tool.shareUrl(base?)` builds the same link from host code.
|
|
274
|
+
|
|
260
275
|
In Studio, **Command+B** (Mac) or **Ctrl+B** slides both side panels out of view; press it again to bring them back.
|
|
261
276
|
This also works from the same-origin preview iframe. The sidebar's collapse/expand button controls its width separately,
|
|
262
277
|
and that choice is preserved when hiding and showing the panels. Move the pointer near a hidden panel's screen edge to
|
|
@@ -19,9 +19,6 @@ function diff(before, after, path = []) {
|
|
|
19
19
|
}
|
|
20
20
|
return JSON.stringify(before) === JSON.stringify(after) ? [] : [{ path, before, after }];
|
|
21
21
|
}
|
|
22
|
-
function changedPaths(before, after) {
|
|
23
|
-
return diff(before, after).map(({ path }) => path.join("."));
|
|
24
|
-
}
|
|
25
22
|
|
|
26
23
|
// src/client/bridge.ts
|
|
27
24
|
function connectBridge(url, handlers) {
|
|
@@ -105,7 +102,8 @@ function connectBrowserSync(definition, runtime, handlers) {
|
|
|
105
102
|
return {
|
|
106
103
|
publish(value) {
|
|
107
104
|
if (closed) return;
|
|
108
|
-
|
|
105
|
+
clock = Math.max(clock + 1, Date.now());
|
|
106
|
+
latest = { type: "config", clock, sender, value: structuredClone(value) };
|
|
109
107
|
channel.postMessage(latest);
|
|
110
108
|
},
|
|
111
109
|
close() {
|
|
@@ -199,6 +197,7 @@ function live(target, onWrite) {
|
|
|
199
197
|
return wrap(target);
|
|
200
198
|
}
|
|
201
199
|
function assignDeep(target, source) {
|
|
200
|
+
for (const key of Object.keys(target)) if (!(key in source)) delete target[key];
|
|
202
201
|
for (const [key, value] of Object.entries(source)) {
|
|
203
202
|
const current = target[key];
|
|
204
203
|
const nested = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -208,6 +207,47 @@ function assignDeep(target, source) {
|
|
|
208
207
|
}
|
|
209
208
|
}
|
|
210
209
|
|
|
210
|
+
// src/client/share.ts
|
|
211
|
+
var PARAM = "studio-share";
|
|
212
|
+
var toBase64Url = (text) => {
|
|
213
|
+
let binary = "";
|
|
214
|
+
for (const byte of new TextEncoder().encode(text)) binary += String.fromCharCode(byte);
|
|
215
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
216
|
+
};
|
|
217
|
+
var fromBase64Url = (data) => new TextDecoder().decode(
|
|
218
|
+
Uint8Array.from(atob(data.replace(/-/g, "+").replace(/_/g, "/")), (char) => char.charCodeAt(0))
|
|
219
|
+
);
|
|
220
|
+
function shareUrl(id, config, base = location.href) {
|
|
221
|
+
const url = new URL(base);
|
|
222
|
+
url.searchParams.set(PARAM, `${id}.${toBase64Url(JSON.stringify(config))}`);
|
|
223
|
+
return url.href;
|
|
224
|
+
}
|
|
225
|
+
function readShared(id) {
|
|
226
|
+
if (typeof window === "undefined" || !window.location?.search) return null;
|
|
227
|
+
const param = new URLSearchParams(location.search).get(PARAM);
|
|
228
|
+
if (!param?.startsWith(`${id}.`)) return null;
|
|
229
|
+
try {
|
|
230
|
+
return { value: JSON.parse(fromBase64Url(param.slice(id.length + 1))) };
|
|
231
|
+
} catch {
|
|
232
|
+
console.warn(`[studio] ${id}: ignoring a malformed share link`);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function clearShared() {
|
|
237
|
+
const url = new URL(location.href);
|
|
238
|
+
if (!url.searchParams.has(PARAM)) return;
|
|
239
|
+
url.searchParams.delete(PARAM);
|
|
240
|
+
history.replaceState(history.state, "", url);
|
|
241
|
+
}
|
|
242
|
+
async function copyLink(url) {
|
|
243
|
+
if (navigator.clipboard?.writeText) return navigator.clipboard.writeText(url);
|
|
244
|
+
window.prompt("Copy this link", url);
|
|
245
|
+
}
|
|
246
|
+
function afterLoad(apply) {
|
|
247
|
+
if (document.readyState === "complete") setTimeout(apply);
|
|
248
|
+
else addEventListener("load", () => setTimeout(apply), { once: true });
|
|
249
|
+
}
|
|
250
|
+
|
|
211
251
|
// src/client/studio.ts
|
|
212
252
|
var COMPARE_MESSAGE = "joyco-studio:compare";
|
|
213
253
|
function postCompare(target, tool, showSaved) {
|
|
@@ -264,14 +304,19 @@ function createStudio(config, options = {}) {
|
|
|
264
304
|
version++;
|
|
265
305
|
for (const listener of studioListeners) listener();
|
|
266
306
|
};
|
|
307
|
+
const runtimeOwnsDrafts = () => connection.phase === "connected";
|
|
267
308
|
const createHandle = (definition) => {
|
|
268
309
|
const raw = structuredClone(definition.initial);
|
|
269
310
|
const listeners = /* @__PURE__ */ new Set();
|
|
270
311
|
let pushing = Promise.resolve();
|
|
271
312
|
let scheduled = false;
|
|
313
|
+
let saved = definition.initial;
|
|
272
314
|
let stashed = null;
|
|
273
|
-
const
|
|
315
|
+
const history2 = createHistory();
|
|
274
316
|
let snapshot = structuredClone(raw);
|
|
317
|
+
const draft = () => stashed ?? raw;
|
|
318
|
+
let shared = readShared(definition.id);
|
|
319
|
+
let closed = false;
|
|
275
320
|
const changed = () => {
|
|
276
321
|
snapshot = structuredClone(raw);
|
|
277
322
|
handle.version++;
|
|
@@ -280,25 +325,24 @@ function createStudio(config, options = {}) {
|
|
|
280
325
|
};
|
|
281
326
|
const adopt = (state) => {
|
|
282
327
|
handle.state = state;
|
|
328
|
+
saved = state.saved.value;
|
|
283
329
|
touch();
|
|
284
330
|
};
|
|
285
|
-
const
|
|
286
|
-
if (stashed)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const result = await bridge?.request("tool.get", { tool: definition.id });
|
|
294
|
-
if (result?.ok) receive(result.data);
|
|
331
|
+
const replace = (value) => {
|
|
332
|
+
if (stashed) {
|
|
333
|
+
stashed = structuredClone(value);
|
|
334
|
+
touch();
|
|
335
|
+
} else {
|
|
336
|
+
assignDeep(raw, value);
|
|
337
|
+
changed();
|
|
338
|
+
}
|
|
295
339
|
};
|
|
296
340
|
const push = () => {
|
|
297
341
|
if (scheduled || !bridge) return;
|
|
298
342
|
scheduled = true;
|
|
299
343
|
pushing = Promise.resolve().then(async () => {
|
|
300
344
|
scheduled = false;
|
|
301
|
-
const result = await bridge?.request("tool.update", { tool: definition.id, value:
|
|
345
|
+
const result = await bridge?.request("tool.update", { tool: definition.id, value: draft() });
|
|
302
346
|
if (!result) return;
|
|
303
347
|
if (result.ok) adopt(result.data);
|
|
304
348
|
else if (result.error !== "runtime_unavailable") {
|
|
@@ -307,32 +351,65 @@ function createStudio(config, options = {}) {
|
|
|
307
351
|
}
|
|
308
352
|
});
|
|
309
353
|
};
|
|
354
|
+
const sync = () => {
|
|
355
|
+
browserSync?.publish(draft());
|
|
356
|
+
if (runtimeOwnsDrafts()) push();
|
|
357
|
+
};
|
|
358
|
+
const receive = (state) => {
|
|
359
|
+
adopt(state);
|
|
360
|
+
replace(state.draft.value);
|
|
361
|
+
browserSync?.publish(draft());
|
|
362
|
+
};
|
|
363
|
+
const resync = async () => {
|
|
364
|
+
const result = await bridge?.request("tool.get", { tool: definition.id });
|
|
365
|
+
if (result?.ok) receive(result.data);
|
|
366
|
+
};
|
|
310
367
|
const edited = () => {
|
|
368
|
+
if (stashed) return changed();
|
|
369
|
+
history2.record(snapshot, raw);
|
|
311
370
|
changed();
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
371
|
+
sync();
|
|
372
|
+
};
|
|
373
|
+
const hydrate = () => {
|
|
374
|
+
if (!shared) return;
|
|
375
|
+
const { value } = shared;
|
|
376
|
+
shared = null;
|
|
377
|
+
afterLoad(() => {
|
|
378
|
+
if (closed) return;
|
|
379
|
+
clearShared();
|
|
380
|
+
const result = validateConfig(definition, value);
|
|
381
|
+
if (!result.ok) return console.warn(`[studio] ${definition.id}: ignoring shared config: ${result.message}`);
|
|
382
|
+
handle.compare(false);
|
|
383
|
+
commit(() => assignDeep(raw, result.data));
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
const commit = (apply) => {
|
|
387
|
+
const before = snapshot;
|
|
388
|
+
apply();
|
|
389
|
+
changed();
|
|
390
|
+
history2.record(before, raw, { merge: false });
|
|
391
|
+
sync();
|
|
316
392
|
};
|
|
317
393
|
const restore = (patch) => {
|
|
318
394
|
if (!patch) return false;
|
|
319
395
|
for (const { path, value } of patch) writePath(raw, path, value);
|
|
320
|
-
|
|
396
|
+
changed();
|
|
397
|
+
sync();
|
|
321
398
|
return true;
|
|
322
399
|
};
|
|
323
400
|
const handle = {
|
|
324
401
|
definition,
|
|
325
|
-
config: live(raw,
|
|
326
|
-
if (!stashed) history.record(snapshot, raw);
|
|
327
|
-
edited();
|
|
328
|
-
}),
|
|
402
|
+
config: live(raw, edited),
|
|
329
403
|
state: null,
|
|
330
404
|
version: 0,
|
|
331
405
|
get saved() {
|
|
332
|
-
return
|
|
406
|
+
return saved;
|
|
407
|
+
},
|
|
408
|
+
get diff() {
|
|
409
|
+
return diff(saved, draft());
|
|
333
410
|
},
|
|
334
411
|
get changes() {
|
|
335
|
-
return
|
|
412
|
+
return handle.diff.map(({ path }) => path.join("."));
|
|
336
413
|
},
|
|
337
414
|
get dirty() {
|
|
338
415
|
return handle.changes.length > 0;
|
|
@@ -341,30 +418,31 @@ function createStudio(config, options = {}) {
|
|
|
341
418
|
compare(showSaved) {
|
|
342
419
|
if (showSaved === handle.comparing) return;
|
|
343
420
|
if (showSaved) stashed = structuredClone(raw);
|
|
344
|
-
const next = showSaved ?
|
|
421
|
+
const next = showSaved ? saved : stashed;
|
|
345
422
|
assignDeep(raw, next);
|
|
346
423
|
if (!showSaved) stashed = null;
|
|
347
424
|
handle.comparing = showSaved;
|
|
348
425
|
changed();
|
|
349
426
|
},
|
|
350
427
|
get canUndo() {
|
|
351
|
-
return
|
|
428
|
+
return history2.canUndo;
|
|
352
429
|
},
|
|
353
430
|
get canRedo() {
|
|
354
|
-
return
|
|
431
|
+
return history2.canRedo;
|
|
355
432
|
},
|
|
356
|
-
undo: () => !stashed && restore(
|
|
357
|
-
redo: () => !stashed && restore(
|
|
433
|
+
undo: () => !stashed && restore(history2.undo()),
|
|
434
|
+
redo: () => !stashed && restore(history2.redo()),
|
|
358
435
|
subscribe(listener) {
|
|
359
436
|
listeners.add(listener);
|
|
360
437
|
return () => void listeners.delete(listener);
|
|
361
438
|
},
|
|
362
439
|
controls: () => definition.controls(handle.config),
|
|
363
440
|
debug() {
|
|
364
|
-
const actions =
|
|
365
|
-
save: button("Save", () => handle.save().then(fail), "Saved \u2713"),
|
|
366
|
-
discard: button("Discard", () => handle.discard()
|
|
367
|
-
|
|
441
|
+
const actions = {
|
|
442
|
+
...runtime ? { save: button("Save", () => handle.save().then(fail), "Saved \u2713") } : {},
|
|
443
|
+
discard: button("Discard", () => void handle.discard(), "Discarded"),
|
|
444
|
+
share: button("Copy link", () => copyLink(handle.shareUrl()), "Copied \u2713")
|
|
445
|
+
};
|
|
368
446
|
return {
|
|
369
447
|
title: definition.title ?? definition.id,
|
|
370
448
|
schema: () => ({ ...liveSchema(handle.controls()), ...actions }),
|
|
@@ -380,33 +458,43 @@ function createStudio(config, options = {}) {
|
|
|
380
458
|
if (result.ok) adopt(result.data);
|
|
381
459
|
return result;
|
|
382
460
|
},
|
|
383
|
-
|
|
384
|
-
if (!
|
|
385
|
-
await pushing;
|
|
461
|
+
discard() {
|
|
462
|
+
if (!handle.dirty) return false;
|
|
386
463
|
const before = snapshot;
|
|
387
|
-
|
|
388
|
-
if (
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
464
|
+
replace(saved);
|
|
465
|
+
if (!stashed) history2.record(before, raw, { merge: false });
|
|
466
|
+
sync();
|
|
467
|
+
return true;
|
|
468
|
+
},
|
|
469
|
+
revert(path) {
|
|
470
|
+
const change = handle.diff.find(
|
|
471
|
+
(change2) => typeof path === "string" ? change2.path.join(".") === path : change2.path.length === path.length && change2.path.every((key, i) => key === path[i])
|
|
472
|
+
);
|
|
473
|
+
if (stashed || !change) return false;
|
|
474
|
+
commit(() => writePath(raw, change.path, change.before));
|
|
475
|
+
return true;
|
|
476
|
+
},
|
|
477
|
+
shareUrl: (base) => shareUrl(definition.id, draft(), base),
|
|
478
|
+
/**
|
|
479
|
+
* Connection changes: the runtime's draft wins on (re)connect; without one there is no runtime state.
|
|
480
|
+
* A share link applies on top of the runtime's draft, or locally once the runtime proves unreachable.
|
|
481
|
+
*/
|
|
482
|
+
connection() {
|
|
483
|
+
if (runtimeOwnsDrafts()) return void resync().then(hydrate);
|
|
484
|
+
handle.state = null;
|
|
485
|
+
if (connection.phase === "offline") hydrate();
|
|
393
486
|
},
|
|
394
487
|
receive,
|
|
395
|
-
|
|
396
|
-
|
|
488
|
+
close() {
|
|
489
|
+
closed = true;
|
|
490
|
+
browserSync?.close();
|
|
491
|
+
}
|
|
397
492
|
};
|
|
398
493
|
const browserSync = connectBrowserSync(definition, runtime, {
|
|
399
|
-
enabled: () =>
|
|
400
|
-
receive
|
|
401
|
-
if (stashed) {
|
|
402
|
-
stashed = value;
|
|
403
|
-
touch();
|
|
404
|
-
} else {
|
|
405
|
-
assignDeep(raw, value);
|
|
406
|
-
changed();
|
|
407
|
-
}
|
|
408
|
-
}
|
|
494
|
+
enabled: () => !runtimeOwnsDrafts(),
|
|
495
|
+
receive: replace
|
|
409
496
|
});
|
|
497
|
+
if (!runtime) hydrate();
|
|
410
498
|
return handle;
|
|
411
499
|
};
|
|
412
500
|
const handles = new Map(tools.filter(hasConfig).map((definition) => [definition.id, createHandle(definition)]));
|
|
@@ -421,8 +509,8 @@ function createStudio(config, options = {}) {
|
|
|
421
509
|
state: (state) => handles.get(state.tool)?.receive(state),
|
|
422
510
|
connection(next) {
|
|
423
511
|
connection = next;
|
|
512
|
+
for (const handle of handles.values()) handle.connection();
|
|
424
513
|
touch();
|
|
425
|
-
if (next.phase === "connected") for (const handle of handles.values()) void handle.resync();
|
|
426
514
|
}
|
|
427
515
|
});
|
|
428
516
|
}
|
|
@@ -467,4 +555,4 @@ export {
|
|
|
467
555
|
everyFrame,
|
|
468
556
|
createStudio
|
|
469
557
|
};
|
|
470
|
-
//# sourceMappingURL=chunk-
|
|
558
|
+
//# sourceMappingURL=chunk-6KZJEKOL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/studio.ts","../src/core/diff.ts","../src/client/bridge.ts","../src/client/browser-sync.ts","../src/client/history.ts","../src/client/live.ts","../src/client/share.ts"],"sourcesContent":["import {\n button,\n type DebugDefinition,\n type DebugField,\n type DebugFolder,\n type DebugSchema,\n schemaValues,\n} from '@joycostudio/xyz/debug'\nimport { diff, type Change } from '../core/diff'\nimport type { ToolState } from '../core/protocol'\nimport { error, type Result } from '../core/result'\nimport { hasConfig, type StudioTool, type ToolDefinition, type ToolReference } from '../core/tool'\nimport { validateConfig } from '../core/validate'\nimport { connectBridge, type BridgeClient, type Connection } from './bridge'\nimport { connectBrowserSync } from './browser-sync'\nimport { createHistory, writePath } from './history'\nimport { assignDeep, live } from './live'\nimport { afterLoad, clearShared, copyLink, readShared, shareUrl } from './share'\n\nexport type ToolHandle<TConfig extends object = any> = {\n readonly definition: ToolDefinition<TConfig>\n /** The live config. Writes sync to same-origin pages and, when connected, the runtime. */\n readonly config: TConfig\n /** Runtime-owned state; null while no runtime is connected or until it answers. */\n readonly state: ToolState<TConfig> | null\n /** Bumps on every config change. Handy as a React snapshot. */\n readonly version: number\n /** Called after every config change, local or remote. */\n subscribe(listener: (config: TConfig) => void): () => void\n /** The tool's Xyz schema bound to the live config. */\n controls(): DebugSchema\n /** Xyz `Debuggable`: `debugManager.register(handle)` mounts this tool in the app's floating pane. */\n debug(): DebugFolder\n /** The config on disk: the runtime's last reported saved value or, until one answers, the bundled `initial`. */\n readonly saved: TConfig\n /** Dotted paths where the draft differs from `saved`. Tracked locally, so it works without a runtime too. */\n readonly changes: string[]\n /** Each leaf where the draft differs from `saved`: `before` is the saved value, `after` the draft's. */\n readonly diff: Change[]\n /** The draft differs from `saved`. */\n readonly dirty: boolean\n /** True while `compare(true)` shows the saved config in place of the draft. */\n readonly comparing: boolean\n /**\n * Temporarily show `saved` (true) or go back to the draft (false). Local only: nothing\n * reaches the runtime, and edits made while comparing are dropped when it ends.\n */\n compare(showSaved: boolean): void\n /** True when `undo()` has an edit made on this page to step back through. */\n readonly canUndo: boolean\n readonly canRedo: boolean\n /**\n * Step back through edits made on this page (a slider drag counts as one), then sync the result like any\n * other edit. Only the leaves each edit changed are restored. Returns false with nothing to undo or while comparing.\n */\n undo(): boolean\n redo(): boolean\n /** Persist the draft. Needs a connected runtime. */\n save(options?: { force?: boolean }): Promise<Result<ToolState<TConfig>>>\n /**\n * Reset the draft to `saved`, with or without a runtime, then sync it like any other edit.\n * Undoable as its own step. Returns false when there is nothing to discard.\n */\n discard(): boolean\n /**\n * Reset one changed leaf (a dotted path from `changes`, or its keys, which also address keys containing dots) to its saved value, then sync it like any\n * other edit. Undoable as its own step. Returns false when that leaf is unchanged or while comparing.\n */\n revert(path: string | readonly string[]): boolean\n /**\n * A link to `base` (the current page by default) carrying the draft. Opening it on another machine applies\n * that draft as an unsaved, undoable edit; a draft that no longer fits the tool's controls is ignored.\n */\n shareUrl(base?: string | URL): string\n}\n\nexport type Studio = {\n /** Every registered tool in order, including preview-only ones. */\n readonly definitions: readonly StudioTool[]\n /** A handle per tool with a config. Preview-only tools have none. */\n readonly tools: readonly ToolHandle[]\n tool<TConfig extends object>(definition: ToolReference<TConfig>): ToolHandle<TConfig>\n readonly connection: Connection\n /** Bumps on connection or any tool state change. */\n readonly version: number\n subscribe(listener: () => void): () => void\n close(): void\n}\n\nexport type StudioOptions = {\n tools: readonly StudioTool[]\n /**\n * Runtime bridge URL (e.g. `ws://127.0.0.1:5179`). Omit or pass false for browser-only syncing, as in production.\n * Only browsers connect, so a module-scope `createStudio` is safe to evaluate during SSR.\n */\n runtime?: string | false\n}\n\nconst COMPARE_MESSAGE = 'joyco-studio:compare'\ntype CompareMessage = { type: typeof COMPARE_MESSAGE; tool: string; showSaved: boolean }\n\n/** Make the studio running in `target` (e.g. a preview iframe) compare `tool` as well. */\nexport function postCompare(target: Window | null | undefined, tool: string, showSaved: boolean) {\n const message: CompareMessage = { type: COMPARE_MESSAGE, tool, showSaved }\n target?.postMessage(message, location.origin)\n}\n\n/** Mark every value control live so a pane registered with `registerFrame` reflects remote edits. */\nfunction liveSchema(schema: DebugSchema): DebugSchema {\n return Object.fromEntries(\n Object.entries(schema).map(([key, field]): [string, DebugField] => {\n if (field.kind === 'button') return [key, field]\n if (field.kind === 'folder')\n return [\n key,\n { ...field, definition: { ...field.definition, schema: () => liveSchema(field.definition.schema()) } },\n ]\n return [key, { ...field, live: true }]\n }),\n )\n}\n\n/**\n * Xyz `extend` for a folder bound to `handle.controls()` (optionally followed by more fields, as in `debug()`):\n * double-clicking a slider resets it to its saved value.\n */\nexport function resetSlidersOnDoubleClick(handle: ToolHandle): NonNullable<DebugDefinition['extend']> {\n return (folder, { fields }) => {\n const sliders = fields().filter(({ field }) => field.kind === 'slider')\n const onDoubleClick = (event: MouseEvent) => {\n // Double-clicking the number input selects its text instead.\n if (handle.comparing || (event.target as Element).closest('input')) return\n const slider = sliders.find(({ blade }) => blade.element.contains(event.target as Node))\n if (slider?.field.kind !== 'slider') return\n const saved = slider.path.reduce<unknown>(\n (values, key) => (values as Record<string, unknown> | undefined)?.[key],\n schemaValues(handle.definition.controls(structuredClone(handle.saved))),\n )\n if (typeof saved === 'number') slider.field.write(saved)\n }\n folder.element.addEventListener('dblclick', onDoubleClick)\n return () => folder.element.removeEventListener('dblclick', onDoubleClick)\n }\n}\n\n/** A `registerFrame` for Xyz's `DebugManager` / `bindDebugSchema`: refreshes live controls every animation frame. */\nexport function everyFrame(refresh: () => void) {\n let frame = requestAnimationFrame(function tick() {\n refresh()\n frame = requestAnimationFrame(tick)\n })\n return () => cancelAnimationFrame(frame)\n}\n\n/** One studio per runtime URL (or local tool set) per page; replace HMR instances without leaking transports. */\nconst instances: Map<string, Studio> = ((globalThis as { __joycoStudio?: Map<string, Studio> }).__joycoStudio ??=\n new Map())\n\nexport function createStudio(config: StudioOptions, options: Pick<StudioOptions, 'runtime'> = {}): Studio {\n const { tools } = config\n const runtime = options.runtime ?? config.runtime\n const instanceKey = runtime || JSON.stringify(tools.map(({ id, file }) => [id, file]).sort())\n let bridge: BridgeClient | null = null\n let connection: Connection = { phase: runtime ? 'connecting' : 'local' }\n let version = 0\n const studioListeners = new Set<() => void>()\n const touch = () => {\n version++\n for (const listener of studioListeners) listener()\n }\n\n /**\n * Who owns drafts: the runtime while connected, otherwise same-origin pages share them over BroadcastChannel.\n * The only place a draft change depends on the connection; reconnecting hands ownership back to the runtime.\n */\n const runtimeOwnsDrafts = () => connection.phase === 'connected'\n\n const createHandle = <TConfig extends object>(definition: ToolDefinition<TConfig>) => {\n const raw = structuredClone(definition.initial)\n const listeners = new Set<(config: TConfig) => void>()\n let pushing: Promise<unknown> = Promise.resolve()\n let scheduled = false\n /** The last saved config the runtime reported, kept while it is away; the bundled `initial` until one answers. */\n let saved: TConfig = definition.initial\n /** The draft to restore while comparing; null when showing the draft. */\n let stashed: TConfig | null = null\n const history = createHistory()\n /** A copy of `raw` as of the last change, so the next local edit can be diffed against it. */\n let snapshot = structuredClone(raw)\n /** The draft, even while `raw` shows the saved config for comparing. */\n const draft = () => stashed ?? raw\n /** A draft from this page's share link, applied once the runtime (if any) has had its say. */\n let shared = readShared(definition.id)\n let closed = false\n\n const changed = () => {\n snapshot = structuredClone(raw)\n handle.version++\n for (const listener of listeners) listener(handle.config)\n touch()\n }\n const adopt = (state: ToolState) => {\n handle.state = state as ToolState<TConfig>\n saved = state.saved.value as TConfig\n touch()\n }\n /** Replace the whole draft. While comparing, the shown saved config stays put; `changes` reads the new draft. */\n const replace = (value: TConfig) => {\n if (stashed) {\n stashed = structuredClone(value)\n touch()\n } else {\n assignDeep(raw as Record<string, unknown>, value as Record<string, unknown>)\n changed()\n }\n }\n const push = () => {\n if (scheduled || !bridge) return\n scheduled = true\n pushing = Promise.resolve().then(async () => {\n scheduled = false\n const result = await bridge?.request('tool.update', { tool: definition.id, value: draft() })\n if (!result) return\n if (result.ok) adopt(result.data)\n else if (result.error !== 'runtime_unavailable') {\n console.warn(`[studio] ${definition.id}: ${result.message ?? result.error}`)\n await resync()\n }\n })\n }\n /**\n * Send a local draft change on: to the runtime while it owns drafts. Same-origin pages always get a copy,\n * so one opened after the runtime stops starts from it, but they only read it while no runtime is connected.\n */\n const sync = () => {\n browserSync?.publish(draft())\n if (runtimeOwnsDrafts()) push()\n }\n /** The runtime's state, from a broadcast or a reply. */\n const receive = (state: ToolState) => {\n adopt(state)\n replace(state.draft.value as TConfig)\n browserSync?.publish(draft())\n }\n const resync = async () => {\n const result = await bridge?.request('tool.get', { tool: definition.id })\n if (result?.ok) receive(result.data)\n }\n /** Every local change to the live config. */\n const edited = () => {\n // Edits made while comparing stay on this page and are dropped when it ends.\n if (stashed) return changed()\n history.record(snapshot, raw)\n changed()\n sync()\n }\n /** Apply the share link as a local edit, so it syncs and undoes like one. Runs once. */\n const hydrate = () => {\n if (!shared) return\n const { value } = shared\n shared = null\n afterLoad(() => {\n if (closed) return\n clearShared()\n const result = validateConfig(definition, value)\n if (!result.ok) return console.warn(`[studio] ${definition.id}: ignoring shared config: ${result.message}`)\n handle.compare(false)\n commit(() => assignDeep(raw as Record<string, unknown>, result.data as Record<string, unknown>))\n })\n }\n /** Apply a change to the draft as an undo step of its own, never merged with the edits around it. */\n const commit = (apply: () => void) => {\n const before = snapshot\n apply()\n changed()\n history.record(before, raw, { merge: false })\n sync()\n }\n const restore = (patch: ReturnType<typeof history.undo>) => {\n if (!patch) return false\n for (const { path, value } of patch) writePath(raw as Record<string, unknown>, path, value)\n changed()\n sync()\n return true\n }\n\n const handle = {\n definition,\n config: live(raw, edited),\n state: null as ToolState<TConfig> | null,\n version: 0,\n get saved(): TConfig {\n return saved\n },\n get diff() {\n return diff(saved, draft())\n },\n get changes() {\n return handle.diff.map(({ path }) => path.join('.'))\n },\n get dirty() {\n return handle.changes.length > 0\n },\n comparing: false,\n compare(showSaved: boolean) {\n if (showSaved === handle.comparing) return\n if (showSaved) stashed = structuredClone(raw)\n const next = showSaved ? saved : stashed!\n assignDeep(raw as Record<string, unknown>, next as Record<string, unknown>)\n if (!showSaved) stashed = null\n handle.comparing = showSaved\n changed()\n },\n get canUndo() {\n return history.canUndo\n },\n get canRedo() {\n return history.canRedo\n },\n undo: () => !stashed && restore(history.undo()),\n redo: () => !stashed && restore(history.redo()),\n subscribe(listener: (config: TConfig) => void) {\n listeners.add(listener)\n return () => void listeners.delete(listener)\n },\n controls: () => definition.controls(handle.config),\n debug(): DebugFolder {\n const actions: DebugSchema = {\n ...(runtime ? { save: button('Save', () => handle.save().then(fail), 'Saved ✓') } : {}),\n discard: button('Discard', () => void handle.discard(), 'Discarded'),\n share: button('Copy link', () => copyLink(handle.shareUrl()), 'Copied ✓'),\n }\n return {\n title: definition.title ?? definition.id,\n schema: () => ({ ...liveSchema(handle.controls()), ...actions }),\n extend: resetSlidersOnDoubleClick(handle),\n }\n },\n async save({ force = false } = {}) {\n if (!bridge) return error('runtime_unavailable')\n await pushing\n const revision = handle.state?.draft.revision\n if (revision === undefined) return error('runtime_unavailable')\n const result = await bridge.request('tool.save', { tool: definition.id, revision, force })\n if (result.ok) adopt(result.data)\n return result as Result<ToolState<TConfig>>\n },\n discard() {\n if (!handle.dirty) return false\n const before = snapshot\n replace(saved)\n // Undoable, as a step of its own.\n if (!stashed) history.record(before, raw, { merge: false })\n sync()\n return true\n },\n revert(path: string | readonly string[]) {\n const change = handle.diff.find((change) =>\n typeof path === 'string'\n ? change.path.join('.') === path\n : change.path.length === path.length && change.path.every((key, i) => key === path[i]),\n )\n if (stashed || !change) return false\n commit(() => writePath(raw as Record<string, unknown>, change.path, change.before))\n return true\n },\n shareUrl: (base?: string | URL) => shareUrl(definition.id, draft(), base),\n /**\n * Connection changes: the runtime's draft wins on (re)connect; without one there is no runtime state.\n * A share link applies on top of the runtime's draft, or locally once the runtime proves unreachable.\n */\n connection() {\n if (runtimeOwnsDrafts()) return void resync().then(hydrate)\n handle.state = null\n if (connection.phase === 'offline') hydrate()\n },\n receive,\n close() {\n closed = true\n browserSync?.close()\n },\n }\n const browserSync = connectBrowserSync(definition, runtime, {\n enabled: () => !runtimeOwnsDrafts(),\n receive: replace,\n })\n if (!runtime) hydrate()\n return handle\n }\n\n const handles = new Map(tools.filter(hasConfig).map((definition) => [definition.id, createHandle(definition)]))\n\n // /studio asks its same-origin preview iframe to compare too (see `postCompare`).\n const onMessage = (event: MessageEvent) => {\n const message = event.data as CompareMessage | undefined\n if (event.origin !== location.origin || message?.type !== COMPARE_MESSAGE) return\n handles.get(message.tool)?.compare(message.showSaved)\n }\n if (typeof window !== 'undefined') window.addEventListener?.('message', onMessage)\n\n if (runtime && typeof window !== 'undefined') {\n bridge = connectBridge(runtime, {\n state: (state) => handles.get(state.tool)?.receive(state),\n connection(next) {\n connection = next\n for (const handle of handles.values()) handle.connection()\n touch()\n },\n })\n }\n\n const studio: Studio = {\n definitions: tools,\n tools: [...handles.values()],\n tool<TConfig extends object>(definition: ToolDefinition<TConfig>) {\n const handle = handles.get(definition.id)\n if (!handle) throw new Error(`[studio] tool \"${definition.id}\" was not passed to createStudio`)\n return handle as unknown as ToolHandle<TConfig>\n },\n get connection() {\n return connection\n },\n get version() {\n return version\n },\n subscribe(listener) {\n studioListeners.add(listener)\n return () => void studioListeners.delete(listener)\n },\n close() {\n bridge?.close()\n for (const handle of handles.values()) handle.close()\n studioListeners.clear()\n if (typeof window !== 'undefined') window.removeEventListener?.('message', onMessage)\n if (instances.get(instanceKey) === studio) instances.delete(instanceKey)\n },\n }\n if (typeof window !== 'undefined') {\n instances.get(instanceKey)?.close()\n instances.set(instanceKey, studio)\n }\n return studio\n}\n\nfunction fail(result: Result<unknown>) {\n if (!result.ok) throw new Error(result.message ?? result.error)\n}\n","export type Change = { path: string[]; before: unknown; after: unknown }\n\n/** Every leaf that differs between two configs of the same shape. Arrays count as leaves. */\nexport function diff(before: unknown, after: unknown, path: string[] = []): Change[] {\n const nested = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n if (nested(before) && nested(after)) {\n const keys = new Set([...Object.keys(before), ...Object.keys(after)])\n return [...keys].flatMap((key) => diff(before[key], after[key], [...path, key]))\n }\n return JSON.stringify(before) === JSON.stringify(after) ? [] : [{ path, before, after }]\n}\n","import type { Op, Operations, ServerMessage, ToolState } from '../core/protocol'\nimport { error, type Result } from '../core/result'\n\nexport type Connection = { phase: 'local' } | { phase: 'connecting' } | { phase: 'connected' } | { phase: 'offline' }\n\ntype Pending = (result: Result<unknown>) => void\n\n/** A reconnecting WebSocket to the Studio runtime with request/response correlation. */\nexport function connectBridge(\n url: string,\n handlers: { state: (state: ToolState) => void; connection: (connection: Connection) => void },\n) {\n let socket: WebSocket | null = null\n let nextId = 0\n let retry = 0\n let timer: ReturnType<typeof setTimeout> | undefined\n let closed = false\n const pending = new Map<number, Pending>()\n\n const open = () => {\n handlers.connection({ phase: 'connecting' })\n const ws = new WebSocket(url)\n socket = ws\n ws.onopen = () => {\n retry = 0\n handlers.connection({ phase: 'connected' })\n }\n ws.onmessage = (event) => {\n const message = JSON.parse(String(event.data)) as ServerMessage\n if (message.type === 'state') return handlers.state(message.state)\n pending.get(message.id)?.(message.result)\n pending.delete(message.id)\n }\n ws.onclose = () => {\n socket = null\n for (const resolve of pending.values()) resolve(error('runtime_unavailable'))\n pending.clear()\n if (closed) return\n handlers.connection({ phase: 'offline' })\n timer = setTimeout(open, Math.min(500 * 2 ** retry++, 5000))\n }\n }\n open()\n\n return {\n request<TOp extends Op>(op: TOp, params: Operations[TOp]['params']): Promise<Result<Operations[TOp]['result']>> {\n if (socket?.readyState !== WebSocket.OPEN) return Promise.resolve(error('runtime_unavailable'))\n const id = nextId++\n socket.send(JSON.stringify({ id, op, params }))\n return new Promise((resolve) => pending.set(id, resolve as Pending))\n },\n close() {\n closed = true\n clearTimeout(timer)\n socket?.close()\n },\n }\n}\n\nexport type BridgeClient = ReturnType<typeof connectBridge>\n","import type { ToolDefinition } from '../core/tool'\nimport { validateConfig } from '../core/validate'\n\ntype Snapshot = { type: 'config'; clock: number; sender: string; value: unknown }\n\n/** Ephemeral config sharing between same-origin pages, including Studio's preview iframe. */\nexport function connectBrowserSync<T extends object>(\n definition: ToolDefinition<T>,\n runtime: string | false | undefined,\n handlers: { enabled(): boolean; receive(value: T): void },\n) {\n // Also exclude SSR and Node environments that expose their own BroadcastChannel implementation.\n if (typeof window === 'undefined' || !window.location?.origin || !window.BroadcastChannel) return null\n\n let channel: BroadcastChannel\n try {\n channel = new window.BroadcastChannel(\n JSON.stringify(['joyco-studio:config', runtime || null, definition.id, definition.file]),\n )\n } catch {\n return null\n }\n const sender = crypto.getRandomValues(new Uint32Array(4)).join('-')\n let clock = 0\n let latest: Snapshot | undefined\n let closed = false\n\n channel.onmessage = ({ data }) => {\n if (closed || !data || typeof data !== 'object') return\n if (data.type === 'request') {\n if (latest) channel.postMessage(latest)\n return\n }\n if (\n data.type !== 'config' ||\n !Number.isSafeInteger(data.clock) ||\n data.clock < 1 ||\n typeof data.sender !== 'string'\n )\n return\n clock = Math.max(clock, data.clock)\n if (!handlers.enabled()) return\n // A stable tie-break keeps simultaneous edits and multiple snapshot replies from diverging.\n if (latest && (data.clock < latest.clock || (data.clock === latest.clock && data.sender <= latest.sender))) return\n const result = validateConfig(definition, data.value)\n if (!result.ok) return\n latest = { type: 'config', clock: data.clock, sender: data.sender, value: result.data }\n handlers.receive(result.data)\n }\n // A newly mounted/reloaded preview asks existing peers for their current draft.\n channel.postMessage({ type: 'request' })\n\n return {\n publish(value: T) {\n if (closed) return\n // Wall time as a floor (peers share this browser's clock), so an edit made on a page that just opened,\n // such as an applied share link, outranks older drafts it hasn't heard about yet.\n clock = Math.max(clock + 1, Date.now())\n latest = { type: 'config', clock, sender, value: structuredClone(value) }\n channel.postMessage(latest)\n },\n close() {\n closed = true\n channel.onmessage = null\n channel.close()\n },\n }\n}\n","import { diff, type Change } from '../core/diff'\n\ntype Entry = { changes: Change[]; key: string; at: number }\n\nconst LIMIT = 100\n/** Edits to the same leaves this close together (a slider drag) undo as one step. */\nconst MERGE_MS = 500\n\n/**\n * Undo/redo for one page's edits. Entries hold only the leaves an edit changed, so stepping back\n * never reverts what another page, the runtime, or an agent changed in between.\n */\nexport function createHistory() {\n const past: Entry[] = []\n let future: Entry[] = []\n /** The newest entry while later edits to the same leaves may still extend it. */\n let open: Entry | null = null\n\n const step = (from: Entry[], to: Entry[], side: 'before' | 'after') => {\n const entry = from.pop()\n if (!entry) return null\n to.push(entry)\n open = null\n return entry.changes.map((change) => ({ path: change.path, value: change[side] }))\n }\n\n return {\n get canUndo() {\n return past.length > 0\n },\n get canRedo() {\n return future.length > 0\n },\n /** Record the edit between two snapshots. `merge: false` keeps it a step of its own (e.g. Discard). */\n record(before: unknown, after: unknown, { merge = true } = {}) {\n // Cloned so later in-place edits (e.g. to an array leaf) cannot rewrite history.\n const changes = structuredClone(diff(before, after))\n if (!changes.length) return\n future = []\n const key = JSON.stringify(changes.map(({ path }) => path))\n const now = Date.now()\n if (merge && open && open.key === key && now - open.at < MERGE_MS) {\n open.changes = open.changes.map((change, i) => ({ ...change, after: changes[i]!.after }))\n open.at = now\n // Dragged back to where it started: nothing left to undo.\n if (open.changes.every((change) => JSON.stringify(change.before) === JSON.stringify(change.after))) {\n past.pop()\n open = null\n }\n return\n }\n const entry = { changes, key, at: now }\n past.push(entry)\n if (past.length > LIMIT) past.shift()\n open = merge ? entry : null\n },\n /** The leaves to write back, or null when there is nothing to undo. */\n undo: () => step(past, future, 'before'),\n redo: () => step(future, past, 'after'),\n }\n}\n\n/** Write `value` at `path` in place; undefined removes the key (it did not exist before the edit). */\nexport function writePath(target: Record<string, unknown>, path: string[], value: unknown) {\n const key = path.at(-1)!\n let parent = target\n for (const segment of path.slice(0, -1)) {\n const next = parent[segment]\n parent = (typeof next === 'object' && next !== null ? next : (parent[segment] = {})) as Record<string, unknown>\n }\n if (value === undefined) delete parent[key]\n else parent[key] = structuredClone(value)\n}\n","/**\n * A deep proxy that reports every write, so plain `property(config, 'key')`\n * bindings from Xyz feed Studio without the tool author wiring callbacks.\n */\nexport function live<T extends object>(target: T, onWrite: () => void): T {\n const proxies = new WeakMap<object, object>()\n const wrap = <O extends object>(object: O): O => {\n let proxy = proxies.get(object)\n if (!proxy) {\n proxy = new Proxy(object, {\n get(target, key, receiver) {\n const value = Reflect.get(target, key, receiver)\n return typeof value === 'object' && value !== null ? wrap(value) : value\n },\n set(target, key, value, receiver) {\n const changed = Reflect.get(target, key, receiver) !== value\n const ok = Reflect.set(target, key, value, receiver)\n if (changed) onWrite()\n return ok\n },\n })\n proxies.set(object, proxy)\n }\n return proxy as O\n }\n return wrap(target)\n}\n\n/**\n * Make `target` equal `source` in place, keeping nested object identities (and every binding to them) intact.\n * Keys `source` lacks are removed.\n */\nexport function assignDeep(target: Record<string, unknown>, source: Record<string, unknown>) {\n for (const key of Object.keys(target)) if (!(key in source)) delete target[key]\n for (const [key, value] of Object.entries(source)) {\n const current = target[key]\n const nested = (v: unknown) => typeof v === 'object' && v !== null && !Array.isArray(v)\n if (nested(current) && nested(value))\n assignDeep(current as Record<string, unknown>, value as Record<string, unknown>)\n else target[key] = structuredClone(value)\n }\n}\n","/**\n * Share links carry one tool's draft in the URL: `?studio-share=<tool id>.<base64url JSON>`.\n * Opening one applies it as an unsaved edit, so teammates can review, save, or discard it.\n */\nconst PARAM = 'studio-share'\n\nconst toBase64Url = (text: string) => {\n // Byte by byte: spreading a large config into `String.fromCharCode` would exceed the argument limit.\n let binary = ''\n for (const byte of new TextEncoder().encode(text)) binary += String.fromCharCode(byte)\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nconst fromBase64Url = (data: string) =>\n new TextDecoder().decode(\n Uint8Array.from(atob(data.replace(/-/g, '+').replace(/_/g, '/')), (char) => char.charCodeAt(0)),\n )\n\n/** `base` (the current page by default) with `config` for tool `id` encoded into it. */\nexport function shareUrl(id: string, config: unknown, base: string | URL = location.href) {\n const url = new URL(base)\n url.searchParams.set(PARAM, `${id}.${toBase64Url(JSON.stringify(config))}`)\n return url.href\n}\n\n/** The config this page's URL shares for tool `id`, if any. Unvalidated. */\nexport function readShared(id: string): { value: unknown } | null {\n if (typeof window === 'undefined' || !window.location?.search) return null\n const param = new URLSearchParams(location.search).get(PARAM)\n if (!param?.startsWith(`${id}.`)) return null\n try {\n return { value: JSON.parse(fromBase64Url(param.slice(id.length + 1))) }\n } catch {\n console.warn(`[studio] ${id}: ignoring a malformed share link`)\n return null\n }\n}\n\n/** Drop the share from the address bar, so reloading after further edits doesn't reapply it. */\nexport function clearShared() {\n const url = new URL(location.href)\n if (!url.searchParams.has(PARAM)) return\n url.searchParams.delete(PARAM)\n history.replaceState(history.state, '', url)\n}\n\n/** Copy a share link. Plain-HTTP origins other than localhost have no clipboard API, so show it to copy by hand. */\nexport async function copyLink(url: string) {\n if (navigator.clipboard?.writeText) return navigator.clipboard.writeText(url)\n window.prompt('Copy this link', url)\n}\n\n/**\n * Run `apply` once the page has loaded. Routers such as Next.js rewrite the URL while hydrating,\n * which would restore a share parameter cleared any earlier.\n */\nexport function afterLoad(apply: () => void) {\n if (document.readyState === 'complete') setTimeout(apply)\n else addEventListener('load', () => setTimeout(apply), { once: true })\n}\n"],"mappings":"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","names":["target","history","change"]}
|
package/dist/cli.js
CHANGED
|
@@ -970,7 +970,7 @@ import { parseArgs } from "util";
|
|
|
970
970
|
// package.json
|
|
971
971
|
var package_default = {
|
|
972
972
|
name: "@joycostudio/studio",
|
|
973
|
-
version: "0.1.
|
|
973
|
+
version: "0.1.6",
|
|
974
974
|
description: "Plug project-specific tools into a /studio route, a floating debug pane, and MCP agents.",
|
|
975
975
|
type: "module",
|
|
976
976
|
license: "ISC",
|