@popcraft/kit 0.52.0 → 0.53.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/chunks/{agent-HRK56EXA.js → agent-B4XH7FCC.js} +1 -1
  2. package/dist/chunks/{builtin-3US63AOP.js → builtin-3TWJNRDI.js} +3 -3
  3. package/dist/chunks/{buy-credits-5C4ZIAPQ.js → buy-credits-VZ2J2SR7.js} +2 -2
  4. package/dist/chunks/{check-template-SUCKEUAH.js → check-template-GMNQVCCI.js} +2 -2
  5. package/dist/chunks/{chunk-KWETGFIB.js → chunk-5ICU2TQ2.js} +14 -14
  6. package/dist/chunks/{chunk-OMD5UZER.js → chunk-5JMMBP5Q.js} +5 -5
  7. package/dist/chunks/{chunk-DYSNP6UR.js → chunk-7CNZ5HLR.js} +45 -3
  8. package/dist/chunks/{chunk-IDWJKGNU.js → chunk-7XP5CWQO.js} +41 -41
  9. package/dist/chunks/{chunk-NGFWRQK3.js → chunk-AJPYEZRT.js} +2 -2
  10. package/dist/chunks/{chunk-YOPG6IGL.js → chunk-D7XGMFYM.js} +4 -4
  11. package/dist/chunks/{chunk-AKRFU5IC.js → chunk-DBGETMUH.js} +4 -4
  12. package/dist/chunks/{chunk-EWLFJFDO.js → chunk-DFY5UNVM.js} +1 -1
  13. package/dist/chunks/{chunk-CKS6JHCU.js → chunk-GNNRFMXK.js} +9 -9
  14. package/dist/chunks/{chunk-6OHSAJUQ.js → chunk-KPKVF2TA.js} +2 -2
  15. package/dist/chunks/{chunk-3MLA5BUJ.js → chunk-MYCQCAXZ.js} +4 -4
  16. package/dist/chunks/{chunk-6NVIZIQF.js → chunk-P5HF37GX.js} +2 -2
  17. package/dist/chunks/{chunk-AV6XTZEW.js → chunk-TCPSVZN3.js} +2 -2
  18. package/dist/chunks/{chunk-KHUWNPIW.js → chunk-U3PY5BWW.js} +11 -11
  19. package/dist/chunks/{chunk-YR6YKL6C.js → chunk-UNJ2Y726.js} +3 -3
  20. package/dist/chunks/{chunk-V66THETF.js → chunk-UT6GKZJF.js} +1 -1
  21. package/dist/chunks/{chunk-HKYBS2KY.js → chunk-XJFTD3EZ.js} +1 -1
  22. package/dist/chunks/{chunk-HJ7UVP3J.js → chunk-YHQGAEC5.js} +1 -1
  23. package/dist/chunks/{chunk-KRUBAKYX.js → chunk-Z557VFSN.js} +1 -1
  24. package/dist/chunks/{client-DHB4LF4B.js → client-77HZDECD.js} +3 -3
  25. package/dist/chunks/{client-W4L4L76Q.js → client-DNVGPZXT.js} +1 -1
  26. package/dist/chunks/{client-C5Y3Y5HH.js → client-SE7SVRWK.js} +2 -2
  27. package/dist/chunks/{client-actions-6CIMGIDV.js → client-actions-UNYIWTLI.js} +5 -5
  28. package/dist/chunks/{dangerous-MKUWMKP2.js → dangerous-WMMPM5TV.js} +3 -3
  29. package/dist/chunks/{delete-current-file-VXCBT5UK.js → delete-current-file-WB6ZWMBO.js} +5 -5
  30. package/dist/chunks/{editor-seams-BT4QWT5U.js → editor-seams-AZ2NUX6J.js} +10 -10
  31. package/dist/chunks/{editor-store-5N4YRWV2.js → editor-store-TPVGGNLW.js} +1 -1
  32. package/dist/chunks/{export-watermark-Z2LPEDSP.js → export-watermark-UGGZ5TX6.js} +5 -5
  33. package/dist/chunks/{eyedropper-YG6WXAMC.js → eyedropper-HPK6A4PE.js} +1 -1
  34. package/dist/chunks/{generate-media-OUKD27QT.js → generate-media-Y6MB3ENV.js} +3 -3
  35. package/dist/chunks/{host-ID3XQKWC.js → host-FNER4M32.js} +6 -6
  36. package/dist/chunks/{integrations-OW6CMKMO.js → integrations-LPVAVUUV.js} +1 -1
  37. package/dist/chunks/{libraries-X6HB7FPR.js → libraries-I6J7YJQE.js} +5 -5
  38. package/dist/chunks/{live-YWRRPA6G.js → live-ZRJ2GT3E.js} +1 -1
  39. package/dist/chunks/{media-tool-DCHP5P2O.js → media-tool-LTK46STY.js} +1 -1
  40. package/dist/chunks/{new-file-5T4Y7FST.js → new-file-XH7E3IEZ.js} +3 -3
  41. package/dist/chunks/{plugin-UMUQ76Y6.js → plugin-LAZVW4F4.js} +2 -2
  42. package/dist/chunks/{plugin-import-BCP4JHKO.js → plugin-import-P4PD76X3.js} +14 -14
  43. package/dist/chunks/{presence-7NKX5VRN.js → presence-WSPF6N32.js} +2 -2
  44. package/dist/chunks/{project-RD62AMZA.js → project-M3KF66GG.js} +1 -1
  45. package/dist/chunks/{publish-site-JCSC5ZNC.js → publish-site-PUHKHZF4.js} +6 -6
  46. package/dist/chunks/{publish-store-MVUSRXXI.js → publish-store-PZMIV6LJ.js} +4 -4
  47. package/dist/chunks/{raster-actions-53IZLIPK.js → raster-actions-NS2NSYLC.js} +1 -1
  48. package/dist/chunks/{runner-QXXCWTYC.js → runner-PGFSW23U.js} +17 -17
  49. package/dist/chunks/{store-EI2MB4IN.js → store-BXDDRMK7.js} +6 -6
  50. package/dist/chunks/{sync-KX7WMSPO.js → sync-4AHF7NWR.js} +5 -5
  51. package/dist/chunks/{tabs-O6XBUTGR.js → tabs-KFQNMNUM.js} +8 -8
  52. package/dist/chunks/{team-VCK5JGMP.js → team-QUBELR4W.js} +1 -1
  53. package/dist/chunks/{team-client-IB45QZLF.js → team-client-NCOTLKC5.js} +1 -1
  54. package/dist/chunks/{tools-YU5CI5J5.js → tools-T5XCKKCW.js} +2 -2
  55. package/dist/chunks/{ui-commands-OISNFY2F.js → ui-commands-77JZN35K.js} +12 -12
  56. package/dist/chunks/{versions-O2XIV3FF.js → versions-5HYBXUPA.js} +5 -5
  57. package/dist/index.js +1 -1
  58. package/package.json +1 -1
@@ -18,7 +18,7 @@ import {
18
18
  renderDocument,
19
19
  toolView,
20
20
  underReviewKit
21
- } from "./chunk-YR6YKL6C.js";
21
+ } from "./chunk-UNJ2Y726.js";
22
22
  import "./chunk-ZYK65XJJ.js";
23
23
  import "./chunk-CEVTHE4E.js";
24
24
  import "./chunk-OQZFD4BD.js";
@@ -7,8 +7,8 @@ const __dirname = __popcraftDirname(__filename);
7
7
  import {
8
8
  builtinTools,
9
9
  registerBuiltinTools
10
- } from "./chunk-6NVIZIQF.js";
11
- import "./chunk-IDWJKGNU.js";
10
+ } from "./chunk-P5HF37GX.js";
11
+ import "./chunk-7XP5CWQO.js";
12
12
  import "./chunk-ZLWN6LBH.js";
13
13
  import "./chunk-Q7EITIAT.js";
14
14
  import "./chunk-X36CCQ76.js";
@@ -19,7 +19,7 @@ import "./chunk-A6XXXQQO.js";
19
19
  import "./chunk-ATSLWELA.js";
20
20
  import "./chunk-6B6ULEDJ.js";
21
21
  import "./chunk-VOOUF5TT.js";
22
- import "./chunk-YR6YKL6C.js";
22
+ import "./chunk-UNJ2Y726.js";
23
23
  import "./chunk-ZYK65XJJ.js";
24
24
  import "./chunk-4LOAHKDJ.js";
25
25
  import "./chunk-UCNBKWJQ.js";
@@ -7,11 +7,11 @@ const __dirname = __popcraftDirname(__filename);
7
7
  import {
8
8
  HYPERY_TEAM_HEADER,
9
9
  hyperyRequestHeaders
10
- } from "./chunk-V66THETF.js";
10
+ } from "./chunk-UT6GKZJF.js";
11
11
  import "./chunk-7SMURRFY.js";
12
12
  import {
13
13
  useHyperyTeam
14
- } from "./chunk-KRUBAKYX.js";
14
+ } from "./chunk-Z557VFSN.js";
15
15
  import {
16
16
  ApiError,
17
17
  inAppPurchases,
@@ -6,10 +6,10 @@ const __filename = __popcraftPath(import.meta.url);
6
6
  const __dirname = __popcraftDirname(__filename);
7
7
  import {
8
8
  appCommands
9
- } from "./chunk-CKS6JHCU.js";
9
+ } from "./chunk-GNNRFMXK.js";
10
10
  import {
11
11
  builtinCommands
12
- } from "./chunk-IDWJKGNU.js";
12
+ } from "./chunk-7XP5CWQO.js";
13
13
  import "./chunk-ZLWN6LBH.js";
14
14
  import "./chunk-Q7EITIAT.js";
15
15
  import "./chunk-X36CCQ76.js";
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-ZYK65XJJ.js";
16
16
  import {
17
17
  syncStatus
18
- } from "./chunk-AKRFU5IC.js";
18
+ } from "./chunk-DBGETMUH.js";
19
19
  import {
20
20
  exportNodeToSVG
21
21
  } from "./chunk-T4FFRJAH.js";
@@ -28,7 +28,7 @@ import {
28
28
  import {
29
29
  setPluginTabs,
30
30
  useEditorStore
31
- } from "./chunk-OMD5UZER.js";
31
+ } from "./chunk-5JMMBP5Q.js";
32
32
  import {
33
33
  openBeside
34
34
  } from "./chunk-PMLSPVLR.js";
@@ -69,22 +69,22 @@ init_define_POPCRAFT_COMPONENT_IDS();
69
69
  // lib/plugins/api.ts
70
70
  init_define_POPCRAFT_COMPONENT_IDS();
71
71
  async function handlePluginCall(env, method, params = {}) {
72
- const { dispatchPluginCall } = await import("./plugin-UMUQ76Y6.js");
72
+ const { dispatchPluginCall } = await import("./plugin-LAZVW4F4.js");
73
73
  return dispatchPluginCall(env, method, params);
74
74
  }
75
75
 
76
- // node_modules/.pnpm/@popcraft+plugins@1.0.0_patch_hash=6826cb40a12857607fa1f7d7d102e5d8316bd483c1ff06865dac73139dfe709b/node_modules/@popcraft/plugins/dist/index.js
76
+ // node_modules/.pnpm/@popcraft+plugins@1.3.0/node_modules/@popcraft/plugins/dist/index.js
77
77
  init_define_POPCRAFT_COMPONENT_IDS();
78
78
 
79
- // node_modules/.pnpm/@popcraft+plugins@1.0.0_patch_hash=6826cb40a12857607fa1f7d7d102e5d8316bd483c1ff06865dac73139dfe709b/node_modules/@popcraft/plugins/dist/host.js
79
+ // node_modules/.pnpm/@popcraft+plugins@1.3.0/node_modules/@popcraft/plugins/dist/host.js
80
80
  init_define_POPCRAFT_COMPONENT_IDS();
81
81
 
82
- // node_modules/.pnpm/@popcraft+plugins@1.0.0_patch_hash=6826cb40a12857607fa1f7d7d102e5d8316bd483c1ff06865dac73139dfe709b/node_modules/@popcraft/plugins/dist/generated/runtime.js
82
+ // node_modules/.pnpm/@popcraft+plugins@1.3.0/node_modules/@popcraft/plugins/dist/generated/runtime.js
83
83
  init_define_POPCRAFT_COMPONENT_IDS();
84
- var MAIN_RUNTIME = '"use strict";\n(() => {\n // src/runtime/rpc.ts\n function createRpcClient() {\n let port = null;\n const queue = [];\n const pending = {};\n const listeners = {};\n let nextId = 1;\n function send(msg) {\n if (port) port.postMessage(msg);\n else queue.push(msg);\n }\n function call(method, params) {\n return new Promise((resolve, reject) => {\n const id = nextId++;\n pending[id] = { resolve, reject };\n send({ kind: "rpc", id, method, params });\n });\n }\n function emit(event, data) {\n for (const cb of (listeners[event] || []).slice()) {\n try {\n cb(data);\n } catch (e) {\n console.error(e);\n }\n }\n }\n window.addEventListener("message", (e) => {\n const data = e.data;\n if (e.source !== window.parent || !data || data.type !== "popcraft-plugin-port" || port) return;\n port = e.ports[0];\n port.onmessage = (ev) => {\n const m = ev.data || {};\n if (m.kind === "result" && m.id !== void 0 && pending[m.id]) {\n const p = pending[m.id];\n delete pending[m.id];\n if (m.error) p.reject(new Error(m.error));\n else p.resolve(m.result);\n } else if (m.kind === "event" && m.event) {\n emit(m.event, m.data);\n }\n };\n for (const msg of queue.splice(0)) port.postMessage(msg);\n });\n return {\n call,\n send,\n on(event, cb) {\n (listeners[event] = listeners[event] || []).push(cb);\n },\n off(event, cb) {\n listeners[event] = (listeners[event] || []).filter((x) => x !== cb);\n }\n };\n }\n\n // src/runtime/main.ts\n function clone(v) {\n return v === void 0 ? {} : JSON.parse(JSON.stringify(v));\n }\n function createMainApi(rpc = createRpcClient()) {\n const uiHandlers = [];\n let uiOnMessage = null;\n const uiQueue = [];\n function deliverUI(msg) {\n for (const cb of uiHandlers) {\n try {\n cb(msg);\n } catch (e) {\n console.error(e);\n }\n }\n if (typeof uiOnMessage === "function") {\n try {\n uiOnMessage(msg);\n } catch (e) {\n console.error(e);\n }\n }\n }\n function flushUI() {\n if (uiHandlers.length || typeof uiOnMessage === "function") uiQueue.splice(0).forEach(deliverUI);\n }\n rpc.on("uimessage", (msg) => {\n if (!uiHandlers.length && typeof uiOnMessage !== "function") uiQueue.push(msg);\n else deliverUI(msg);\n });\n let widgetDef = null;\n const widgetQueue = [];\n function renderWidget(state) {\n const handlers = {};\n function walk(el, path) {\n if (!el || typeof el !== "object") return el;\n const out = {};\n for (const k of Object.keys(el)) {\n const v = el[k];\n if (k === "children") {\n out.children = (Array.isArray(v) ? v : [v]).filter((c) => c !== null && c !== void 0 && c !== false).map((c, i) => walk(c, path + "." + i));\n } else if (k === "onClick" && typeof v === "function") {\n handlers[path] = v;\n out.onClick = path;\n } else {\n out[k] = v;\n }\n }\n return out;\n }\n return { tree: walk(widgetDef.render(state), "root"), handlers };\n }\n function initialWidgetState() {\n const s = widgetDef && widgetDef.initialState;\n return clone(typeof s === "function" ? s() : s);\n }\n function commitWidget(nodeId, state) {\n return rpc.call("widgetUpdate", { id: nodeId, state, tree: renderWidget(clone(state)).tree });\n }\n function handleWidgetClick(e) {\n Promise.resolve().then(() => {\n const state = clone(e.state);\n const rendered = renderWidget(state);\n const h = rendered.handlers[e.handler] || widgetDef.handlers && widgetDef.handlers[e.handler];\n if (typeof h !== "function") return;\n return Promise.resolve(h(state, { nodeId: e.nodeId })).then((ret) => commitWidget(e.nodeId, ret === void 0 ? state : ret));\n }).catch((err) => rpc.send({ kind: "error", message: String((err == null ? void 0 : err.message) || err) }));\n }\n rpc.on("widgetclick", (e) => {\n if (widgetDef) handleWidgetClick(e);\n else widgetQueue.push(e);\n });\n const popcraft = {\n /** Which menu command the user picked, or null when the plugin ran some other way. */\n command: null,\n /** Host import mode when launched as an importer, else null. */\n importMode: null,\n /** Pending import file when the host handed one over, else null. */\n importFile: null,\n /** Pending export request (File \u2192 Export \u2192 this plugin\'s format), else null. */\n exportRequest: null,\n getSelection: () => rpc.call("getSelection"),\n setSelection: (ids) => rpc.call("setSelection", { ids }),\n getNode: (id) => rpc.call("getNode", { id }),\n findNodes: (query) => rpc.call("findNodes", query || {}),\n getCurrentPage: () => rpc.call("getCurrentPage"),\n createNode: (type, props) => rpc.call("createNode", { type, props: props || {} }),\n updateNode: (id, props) => rpc.call("updateNode", { id, props }),\n deleteNode: (id) => rpc.call("deleteNode", { id }),\n setText: (id, characters) => rpc.call("setText", { id, characters }),\n /** Put TEXT layers on a path (another layer\'s outline, or their own arc, circle or wave), or tune one: brackets, alignment, placement, effect, overflow. */\n setTextPath: (opts) => rpc.call("setTextPath", opts || {}),\n /** Area type: flow a TEXT layer inside a closed shape, kept `inset` px in. */\n flowTextIntoShape: (opts) => rpc.call("flowTextIntoShape", opts || {}),\n /** Take TEXT layers off their path or out of their shape (or free every text linked to a path / shape). */\n detachText: (opts) => rpc.call("detachText", opts || {}),\n /** Auto-size TEXT layers\' type to their box, shape or path. */\n setTextFit: (opts) => rpc.call("setTextFit", opts || {}),\n createPage: (name) => rpc.call("createPage", name != null ? { name: String(name) } : {}),\n setCurrentPage: (pageId) => rpc.call("setCurrentPage", { pageId }),\n createImage: (opts) => rpc.call("createImage", opts || {}),\n createDocument: (opts) => rpc.call("createDocument", opts || {}),\n getImportMode: () => rpc.call("getImportMode"),\n getImportFile: () => rpc.call("getImportFile"),\n /**\n * The open page as a storefront design in a neutral form, for an exporter that writes its own store\'s templates:\n * each page\'s HTML with the store\'s data marked (`{{page.title}}`, `{{item.price}}`, `<!-- pc:each \u2026 -->` lists,\n * `data-pc-action` forms), and its collections with their commerce schema and sample records.\n */\n getStorefront: () => rpc.call("getStorefront"),\n /**\n * A design\'s data (docs: Collections): a collection and its fields (`schema`: what it stands for, e.g.\n * `commerce.product`, whose fields are made for it), its records, and reading them.\n */\n editCollection: (opts) => rpc.call("editCollection", opts || {}),\n editRecords: (opts) => rpc.call("editRecords", opts || {}),\n getRecords: (opts) => rpc.call("getRecords", opts || {}),\n /** A layer as a web page\'s form control (a FORM\'s `formAction`, an INPUT\'s name and value), repeated per record, a page\'s address, its semantics. */\n setControl: (opts) => rpc.call("setControl", opts || {}),\n setRepeat: (opts) => rpc.call("setRepeat", opts || {}),\n setRoute: (opts) => rpc.call("setRoute", opts || {}),\n setSemantics: (opts) => rpc.call("setSemantics", opts || {}),\n /** Which record layers are about (one product, picked from a collection), and which of its fields a layer\'s words, picture or an instance\'s properties follow. */\n setRecord: (opts) => rpc.call("setRecord", opts || {}),\n bindField: (opts) => rpc.call("bindField", opts || {}),\n /** The site\'s environment: public settings its host reads by name when it is deployed (never a secret: the file keeps them). */\n getSiteEnvironment: () => rpc.call("getSiteEnvironment"),\n setSiteEnvironment: (set) => rpc.call("setSiteEnvironment", { set }),\n /** What this plugin keeps with the open file (its settings for this design), under its own id; JSON only. */\n getFileData: (key) => rpc.call("getFileData", { key }),\n setFileData: (key, value) => rpc.call("setFileData", { key, value }),\n /** A store\'s project built from the open page, zipped (needs `export`): what an exporter saves with `saveExport`. */\n getStorefrontFiles: (target) => rpc.call("getStorefrontFiles", { target }),\n getExportRequest: () => rpc.call("getExportRequest"),\n /** Save the exporter\'s file (once per export request); the host enforces the contributed extension. */\n saveExport: (file) => rpc.call("saveExport", file || {}),\n /**\n * Presentation audio (docs/plugins/presentation-audio.md): the deck\'s slides, notes, narration and timing; the\n * soundtrack\'s settings, bed and stinger.\n */\n getPresentationAudio: (opts) => rpc.call("getPresentationAudio", opts || {}),\n /** Change the soundtrack\'s settings (voice, style, mood, loudness, beat alignment, levels) and re-time it. */\n setPresentationAudioSettings: (opts) => rpc.call("setPresentationAudioSettings", opts || {}),\n /** Put a sound in (a slide\'s narration, the bed or the stinger): bytes are normalised to the soundtrack\'s target and stored. */\n setPresentationAudioClip: (opts) => rpc.call("setPresentationAudioClip", opts || {}),\n removePresentationAudioClip: (opts) => rpc.call("removePresentationAudioClip", opts || {}),\n /** Re-time: fixed slide lengths (ms, null clears) and beat-aligned changes. */\n setPresentationTiming: (opts) => rpc.call("setPresentationTiming", opts || {}),\n /** The mix as the export carries it: loudness, true peak, and (wav: true) the 48 kHz stereo WAV. */\n getPresentationMix: (opts) => rpc.call("getPresentationMix", opts || {}),\n /** Ask the user to generate with AI (the panel opens with the request filled in; the user decides). */\n requestPresentationAudio: (opts) => rpc.call("requestPresentationAudio", opts || {}),\n /** Editor commands: each takes the arguments documented in docs/plugins/methods.md (the agent tool of the same purpose). */\n moveNodes: (opts) => rpc.call("moveNodes", opts || {}),\n resizeNodes: (opts) => rpc.call("resizeNodes", opts || {}),\n renameNodes: (opts) => rpc.call("renameNodes", opts || {}),\n reparentNodes: (opts) => rpc.call("reparentNodes", opts || {}),\n duplicateNodes: (opts) => rpc.call("duplicateNodes", opts || {}),\n groupNodes: (opts) => rpc.call("groupNodes", opts || {}),\n ungroupNodes: (opts) => rpc.call("ungroupNodes", opts || {}),\n alignNodes: (opts) => rpc.call("alignNodes", opts || {}),\n distributeNodes: (opts) => rpc.call("distributeNodes", opts || {}),\n flipNodes: (opts) => rpc.call("flipNodes", opts || {}),\n rotateNodes: (opts) => rpc.call("rotateNodes", opts || {}),\n reorderNodes: (opts) => rpc.call("reorderNodes", opts || {}),\n setLocked: (opts) => rpc.call("setLocked", opts || {}),\n setVisible: (opts) => rpc.call("setVisible", opts || {}),\n setAutoLayout: (opts) => rpc.call("setAutoLayout", opts || {}),\n importSvg: (opts) => rpc.call("importSvg", opts || {}),\n measureNodes: (opts) => rpc.call("measureNodes", opts || {}),\n setFills: (opts) => rpc.call("setFills", opts || {}),\n setStrokes: (opts) => rpc.call("setStrokes", opts || {}),\n setEffects: (opts) => rpc.call("setEffects", opts || {}),\n replaceColor: (opts) => rpc.call("replaceColor", opts || {}),\n setTypography: (opts) => rpc.call("setTypography", opts || {}),\n applyStyle: (opts) => rpc.call("applyStyle", opts || {}),\n createComponent: (opts) => rpc.call("createComponent", opts || {}),\n createInstance: (opts) => rpc.call("createInstance", opts || {}),\n renamePage: (opts) => rpc.call("renamePage", opts || {}),\n getDocumentOutline: (opts) => rpc.call("getDocumentOutline", opts || {}),\n setPageSetup: (opts) => rpc.call("setPageSetup", opts || {}),\n setShowAdvance: (opts) => rpc.call("setShowAdvance", opts || {}),\n booleanOperation: (opts) => rpc.call("booleanOperation", opts || {}),\n flattenNodes: (opts) => rpc.call("flattenNodes", opts || {}),\n outlineStroke: (opts) => rpc.call("outlineStroke", opts || {}),\n setMask: (opts) => rpc.call("setMask", opts || {}),\n setMaskType: (opts) => rpc.call("setMaskType", opts || {}),\n shapeBuilder: (opts) => rpc.call("shapeBuilder", opts || {}),\n setKeyframes: (opts) => rpc.call("setKeyframes", opts || {}),\n getAnimation: (opts) => rpc.call("getAnimation", opts || {}),\n getCompositions: (opts) => rpc.call("getCompositions", opts || {}),\n editBehaviours: (opts) => rpc.call("editBehaviours", opts || {}),\n setTimeline: (opts) => rpc.call("setTimeline", opts || {}),\n setLayerTiming: (opts) => rpc.call("setLayerTiming", opts || {}),\n editTextAnimation: (opts) => rpc.call("editTextAnimation", opts || {}),\n edit3DScene: (opts) => rpc.call("edit3DScene", opts || {}),\n setInstancePlayback: (opts) => rpc.call("setInstancePlayback", opts || {}),\n editComponentProperty: (opts) => rpc.call("editComponentProperty", opts || {}),\n setShaderFill: (opts) => rpc.call("setShaderFill", opts || {}),\n createComposition: (opts) => rpc.call("createComposition", opts || {}),\n editComposition: (opts) => rpc.call("editComposition", opts || {}),\n editStateMachine: (opts) => rpc.call("editStateMachine", opts || {}),\n getStateMachine: (opts) => rpc.call("getStateMachine", opts || {}),\n applyMotionPreset: (opts) => rpc.call("applyMotionPreset", opts || {}),\n staggerMotion: (opts) => rpc.call("staggerMotion", opts || {}),\n createMotionComponent: (opts) => rpc.call("createMotionComponent", opts || {}),\n addAudio: (opts) => rpc.call("addAudio", opts || {}),\n editFrameAudio: (opts) => rpc.call("editFrameAudio", opts || {}),\n editVideoClip: (opts) => rpc.call("editVideoClip", opts || {}),\n detectAudioOnsets: (opts) => rpc.call("detectAudioOnsets", opts || {}),\n cutToBeat: (opts) => rpc.call("cutToBeat", opts || {}),\n editPrototype: (opts) => rpc.call("editPrototype", opts || {}),\n usePresentationFallbackAudio: (opts) => rpc.call("usePresentationFallbackAudio", opts || {}),\n /** Bring a Lottie, PDF or PopCraft file into the open document (the `importFile` field is the file an importer was handed). */\n importDocumentFile: (opts) => rpc.call("importDocumentFile", opts || {}),\n getStyles: () => rpc.call("getStyles"),\n createPaintStyle: (name, paints) => rpc.call("createPaintStyle", { name, paints }),\n getVariables: () => rpc.call("getVariables"),\n setVariableValue: (variableId, modeId, value) => rpc.call("setVariableValue", { variableId, modeId, value }),\n exportNode: (id, options) => rpc.call("exportNode", { id, options: options || {} }),\n openExternal: (url) => rpc.call("openExternal", { url }),\n notify: (message, options) => rpc.call("notify", { message: String(message), error: !!(options && options.error) }),\n showUI: (options) => rpc.call("showUI", options || {}),\n hideUI: () => rpc.call("hideUI"),\n /** Ends the run. The optional message is shown as a notification. */\n closePlugin: (message) => rpc.call("closePlugin", { message }),\n storage: {\n get: (key) => rpc.call("storageGet", { key }),\n set: (key, value) => rpc.call("storageSet", { key, value })\n },\n ui: {\n get onmessage() {\n return uiOnMessage;\n },\n set onmessage(cb) {\n uiOnMessage = cb;\n flushUI();\n },\n postMessage: (msg) => rpc.send({ kind: "toUI", data: msg }),\n on: (cb) => {\n uiHandlers.push(cb);\n flushUI();\n }\n },\n widget: {\n register(def) {\n if (!def || typeof def.render !== "function") throw new Error("popcraft.widget.register needs a render(state) function");\n widgetDef = def;\n widgetQueue.splice(0).forEach(handleWidgetClick);\n },\n insert(options) {\n if (!widgetDef) return Promise.reject(new Error("Call popcraft.widget.register first"));\n const o = options || {};\n const state = o.state !== void 0 ? clone(o.state) : initialWidgetState();\n return rpc.call("widgetInsert", { state, tree: renderWidget(state).tree, x: o.x, y: o.y });\n },\n getState: (nodeId) => rpc.call("widgetGet", { id: nodeId }),\n setState(nodeId, state) {\n if (!widgetDef) return Promise.reject(new Error("Call popcraft.widget.register first"));\n return commitWidget(nodeId, clone(state));\n }\n },\n on: (event, cb) => rpc.on(event, cb),\n off: (event, cb) => rpc.off(event, cb)\n };\n rpc.on("init", (data) => {\n var _a, _b, _c;\n popcraft.command = data.command;\n popcraft.importMode = (_a = data.importMode) != null ? _a : null;\n popcraft.importFile = (_b = data.importFile) != null ? _b : null;\n popcraft.exportRequest = (_c = data.exportRequest) != null ? _c : null;\n });\n return popcraft;\n }\n function installMainApi() {\n const rpc = createRpcClient();\n const api = createMainApi(rpc);\n const w = window;\n w.__popcraftRpc = rpc;\n w.popcraft = api;\n w.vizpops = api;\n window.addEventListener("error", (e) => rpc.send({ kind: "error", message: String(e.message || e) }));\n window.addEventListener("unhandledrejection", (e) => rpc.send({ kind: "error", message: String(e.reason && e.reason.message || e.reason) }));\n return api;\n }\n\n // src/runtime/main.entry.ts\n installMainApi();\n})();';
84
+ var MAIN_RUNTIME = '"use strict";\n(() => {\n // src/runtime/rpc.ts\n function createRpcClient() {\n let port = null;\n const queue = [];\n const pending = {};\n const listeners = {};\n let nextId = 1;\n function send(msg) {\n if (port) port.postMessage(msg);\n else queue.push(msg);\n }\n function call(method, params) {\n return new Promise((resolve, reject) => {\n const id = nextId++;\n pending[id] = { resolve, reject };\n send({ kind: "rpc", id, method, params });\n });\n }\n function emit(event, data) {\n for (const cb of (listeners[event] || []).slice()) {\n try {\n cb(data);\n } catch (e) {\n console.error(e);\n }\n }\n }\n window.addEventListener("message", (e) => {\n const data = e.data;\n if (e.source !== window.parent || !data || data.type !== "popcraft-plugin-port" || port) return;\n port = e.ports[0];\n port.onmessage = (ev) => {\n const m = ev.data || {};\n if (m.kind === "result" && m.id !== void 0 && pending[m.id]) {\n const p = pending[m.id];\n delete pending[m.id];\n if (m.error) p.reject(new Error(m.error));\n else p.resolve(m.result);\n } else if (m.kind === "event" && m.event) {\n emit(m.event, m.data);\n }\n };\n for (const msg of queue.splice(0)) port.postMessage(msg);\n });\n return {\n call,\n send,\n on(event, cb) {\n (listeners[event] = listeners[event] || []).push(cb);\n },\n off(event, cb) {\n listeners[event] = (listeners[event] || []).filter((x) => x !== cb);\n }\n };\n }\n\n // src/runtime/main.ts\n function clone(v) {\n return v === void 0 ? {} : JSON.parse(JSON.stringify(v));\n }\n function createMainApi(rpc = createRpcClient()) {\n const uiHandlers = [];\n let uiOnMessage = null;\n const uiQueue = [];\n function deliverUI(msg) {\n for (const cb of uiHandlers) {\n try {\n cb(msg);\n } catch (e) {\n console.error(e);\n }\n }\n if (typeof uiOnMessage === "function") {\n try {\n uiOnMessage(msg);\n } catch (e) {\n console.error(e);\n }\n }\n }\n function flushUI() {\n if (uiHandlers.length || typeof uiOnMessage === "function") uiQueue.splice(0).forEach(deliverUI);\n }\n rpc.on("uimessage", (msg) => {\n if (!uiHandlers.length && typeof uiOnMessage !== "function") uiQueue.push(msg);\n else deliverUI(msg);\n });\n let widgetDef = null;\n const widgetQueue = [];\n function renderWidget(state) {\n const handlers = {};\n function walk(el, path) {\n if (!el || typeof el !== "object") return el;\n const out = {};\n for (const k of Object.keys(el)) {\n const v = el[k];\n if (k === "children") {\n out.children = (Array.isArray(v) ? v : [v]).filter((c) => c !== null && c !== void 0 && c !== false).map((c, i) => walk(c, path + "." + i));\n } else if (k === "onClick" && typeof v === "function") {\n handlers[path] = v;\n out.onClick = path;\n } else {\n out[k] = v;\n }\n }\n return out;\n }\n return { tree: walk(widgetDef.render(state), "root"), handlers };\n }\n function initialWidgetState() {\n const s = widgetDef && widgetDef.initialState;\n return clone(typeof s === "function" ? s() : s);\n }\n function commitWidget(nodeId, state) {\n return rpc.call("widgetUpdate", { id: nodeId, state, tree: renderWidget(clone(state)).tree });\n }\n function handleWidgetClick(e) {\n Promise.resolve().then(() => {\n const state = clone(e.state);\n const rendered = renderWidget(state);\n const h = rendered.handlers[e.handler] || widgetDef.handlers && widgetDef.handlers[e.handler];\n if (typeof h !== "function") return;\n return Promise.resolve(h(state, { nodeId: e.nodeId })).then((ret) => commitWidget(e.nodeId, ret === void 0 ? state : ret));\n }).catch((err) => rpc.send({ kind: "error", message: String((err == null ? void 0 : err.message) || err) }));\n }\n rpc.on("widgetclick", (e) => {\n if (widgetDef) handleWidgetClick(e);\n else widgetQueue.push(e);\n });\n const popcraft = {\n /** Which menu command the user picked, or null when the plugin ran some other way. */\n command: null,\n /** Host import mode when launched as an importer, else null. */\n importMode: null,\n /** Pending import file when the host handed one over, else null. */\n importFile: null,\n /** Pending export request (File \u2192 Export \u2192 this plugin\'s format), else null. */\n exportRequest: null,\n getSelection: () => rpc.call("getSelection"),\n setSelection: (ids) => rpc.call("setSelection", { ids }),\n getNode: (id) => rpc.call("getNode", { id }),\n findNodes: (query) => rpc.call("findNodes", query || {}),\n getCurrentPage: () => rpc.call("getCurrentPage"),\n createNode: (type, props) => rpc.call("createNode", { type, props: props || {} }),\n updateNode: (id, props) => rpc.call("updateNode", { id, props }),\n deleteNode: (id) => rpc.call("deleteNode", { id }),\n setText: (id, characters) => rpc.call("setText", { id, characters }),\n /** Put TEXT layers on a path (another layer\'s outline, or their own arc, circle or wave), or tune one: brackets, alignment, placement, effect, overflow. */\n setTextPath: (opts) => rpc.call("setTextPath", opts || {}),\n /** Area type: flow a TEXT layer inside a closed shape, kept `inset` px in. */\n flowTextIntoShape: (opts) => rpc.call("flowTextIntoShape", opts || {}),\n /** Take TEXT layers off their path or out of their shape (or free every text linked to a path / shape). */\n detachText: (opts) => rpc.call("detachText", opts || {}),\n /** Auto-size TEXT layers\' type to their box, shape or path. */\n setTextFit: (opts) => rpc.call("setTextFit", opts || {}),\n createPage: (name) => rpc.call("createPage", name != null ? { name: String(name) } : {}),\n setCurrentPage: (pageId) => rpc.call("setCurrentPage", { pageId }),\n createImage: (opts) => rpc.call("createImage", opts || {}),\n createDocument: (opts) => rpc.call("createDocument", opts || {}),\n getImportMode: () => rpc.call("getImportMode"),\n getImportFile: () => rpc.call("getImportFile"),\n /**\n * The open page as a storefront design in a neutral form, for an exporter that writes its own store\'s templates:\n * each page\'s HTML with the store\'s data marked (`{{page.title}}`, `{{item.price}}`, `<!-- pc:each \u2026 -->` lists,\n * `data-pc-action` forms), and its collections with their commerce schema and sample records.\n */\n getStorefront: () => rpc.call("getStorefront"),\n /**\n * A design\'s data (docs: Collections): a collection and its fields (`schema`: what it stands for, e.g.\n * `commerce.product`, whose fields are made for it), its records, and reading them.\n */\n editCollection: (opts) => rpc.call("editCollection", opts || {}),\n editRecords: (opts) => rpc.call("editRecords", opts || {}),\n getRecords: (opts) => rpc.call("getRecords", opts || {}),\n /** A layer as a web page\'s form control (a FORM\'s `formAction`, an INPUT\'s name and value), repeated per record, a page\'s address, its semantics. */\n setControl: (opts) => rpc.call("setControl", opts || {}),\n setRepeat: (opts) => rpc.call("setRepeat", opts || {}),\n setRoute: (opts) => rpc.call("setRoute", opts || {}),\n setSemantics: (opts) => rpc.call("setSemantics", opts || {}),\n /** Which record layers are about (one product, picked from a collection), and which of its fields a layer\'s words, picture or an instance\'s properties follow. */\n setRecord: (opts) => rpc.call("setRecord", opts || {}),\n bindField: (opts) => rpc.call("bindField", opts || {}),\n /** The site\'s environment: public settings its host reads by name when it is deployed (never a secret: the file keeps them). */\n getSiteEnvironment: () => rpc.call("getSiteEnvironment"),\n setSiteEnvironment: (set) => rpc.call("setSiteEnvironment", { set }),\n /** What this plugin keeps with the open file (its settings for this design), under its own id; JSON only. */\n getFileData: (key) => rpc.call("getFileData", { key }),\n setFileData: (key, value) => rpc.call("setFileData", { key, value }),\n /** A store\'s project built from the open page, zipped (needs `export`): what an exporter saves with `saveExport`. */\n getStorefrontFiles: (target) => rpc.call("getStorefrontFiles", { target }),\n getExportRequest: () => rpc.call("getExportRequest"),\n /** Save the exporter\'s file (once per export request); the host enforces the contributed extension. */\n saveExport: (file) => rpc.call("saveExport", file || {}),\n /**\n * Presentation audio (docs/plugins/presentation-audio.md): the deck\'s slides, notes, narration and timing; the\n * soundtrack\'s settings, bed and stinger.\n */\n getPresentationAudio: (opts) => rpc.call("getPresentationAudio", opts || {}),\n /** Change the soundtrack\'s settings (voice, style, mood, loudness, beat alignment, levels) and re-time it. */\n setPresentationAudioSettings: (opts) => rpc.call("setPresentationAudioSettings", opts || {}),\n /** Put a sound in (a slide\'s narration, the bed or the stinger): bytes are normalised to the soundtrack\'s target and stored. */\n setPresentationAudioClip: (opts) => rpc.call("setPresentationAudioClip", opts || {}),\n removePresentationAudioClip: (opts) => rpc.call("removePresentationAudioClip", opts || {}),\n /** Re-time: fixed slide lengths (ms, null clears) and beat-aligned changes. */\n setPresentationTiming: (opts) => rpc.call("setPresentationTiming", opts || {}),\n /** The mix as the export carries it: loudness, true peak, and (wav: true) the 48 kHz stereo WAV. */\n getPresentationMix: (opts) => rpc.call("getPresentationMix", opts || {}),\n /** Ask the user to generate with AI (the panel opens with the request filled in; the user decides). */\n requestPresentationAudio: (opts) => rpc.call("requestPresentationAudio", opts || {}),\n /** Editor commands: each takes the arguments documented in docs/plugins/methods.md (the agent tool of the same purpose). */\n moveNodes: (opts) => rpc.call("moveNodes", opts || {}),\n resizeNodes: (opts) => rpc.call("resizeNodes", opts || {}),\n renameNodes: (opts) => rpc.call("renameNodes", opts || {}),\n reparentNodes: (opts) => rpc.call("reparentNodes", opts || {}),\n duplicateNodes: (opts) => rpc.call("duplicateNodes", opts || {}),\n groupNodes: (opts) => rpc.call("groupNodes", opts || {}),\n ungroupNodes: (opts) => rpc.call("ungroupNodes", opts || {}),\n alignNodes: (opts) => rpc.call("alignNodes", opts || {}),\n distributeNodes: (opts) => rpc.call("distributeNodes", opts || {}),\n flipNodes: (opts) => rpc.call("flipNodes", opts || {}),\n rotateNodes: (opts) => rpc.call("rotateNodes", opts || {}),\n reorderNodes: (opts) => rpc.call("reorderNodes", opts || {}),\n setLocked: (opts) => rpc.call("setLocked", opts || {}),\n setVisible: (opts) => rpc.call("setVisible", opts || {}),\n setAutoLayout: (opts) => rpc.call("setAutoLayout", opts || {}),\n importSvg: (opts) => rpc.call("importSvg", opts || {}),\n measureNodes: (opts) => rpc.call("measureNodes", opts || {}),\n setFills: (opts) => rpc.call("setFills", opts || {}),\n setStrokes: (opts) => rpc.call("setStrokes", opts || {}),\n setEffects: (opts) => rpc.call("setEffects", opts || {}),\n replaceColor: (opts) => rpc.call("replaceColor", opts || {}),\n setTypography: (opts) => rpc.call("setTypography", opts || {}),\n applyStyle: (opts) => rpc.call("applyStyle", opts || {}),\n createComponent: (opts) => rpc.call("createComponent", opts || {}),\n createInstance: (opts) => rpc.call("createInstance", opts || {}),\n renamePage: (opts) => rpc.call("renamePage", opts || {}),\n getDocumentOutline: (opts) => rpc.call("getDocumentOutline", opts || {}),\n setPageSetup: (opts) => rpc.call("setPageSetup", opts || {}),\n setShowAdvance: (opts) => rpc.call("setShowAdvance", opts || {}),\n booleanOperation: (opts) => rpc.call("booleanOperation", opts || {}),\n flattenNodes: (opts) => rpc.call("flattenNodes", opts || {}),\n outlineStroke: (opts) => rpc.call("outlineStroke", opts || {}),\n setMask: (opts) => rpc.call("setMask", opts || {}),\n setMaskType: (opts) => rpc.call("setMaskType", opts || {}),\n shapeBuilder: (opts) => rpc.call("shapeBuilder", opts || {}),\n setKeyframes: (opts) => rpc.call("setKeyframes", opts || {}),\n getAnimation: (opts) => rpc.call("getAnimation", opts || {}),\n getCompositions: (opts) => rpc.call("getCompositions", opts || {}),\n editBehaviours: (opts) => rpc.call("editBehaviours", opts || {}),\n setTimeline: (opts) => rpc.call("setTimeline", opts || {}),\n setLayerTiming: (opts) => rpc.call("setLayerTiming", opts || {}),\n editTextAnimation: (opts) => rpc.call("editTextAnimation", opts || {}),\n edit3DScene: (opts) => rpc.call("edit3DScene", opts || {}),\n setInstancePlayback: (opts) => rpc.call("setInstancePlayback", opts || {}),\n editComponentProperty: (opts) => rpc.call("editComponentProperty", opts || {}),\n setShaderFill: (opts) => rpc.call("setShaderFill", opts || {}),\n createComposition: (opts) => rpc.call("createComposition", opts || {}),\n editComposition: (opts) => rpc.call("editComposition", opts || {}),\n editStateMachine: (opts) => rpc.call("editStateMachine", opts || {}),\n getStateMachine: (opts) => rpc.call("getStateMachine", opts || {}),\n applyMotionPreset: (opts) => rpc.call("applyMotionPreset", opts || {}),\n staggerMotion: (opts) => rpc.call("staggerMotion", opts || {}),\n createMotionComponent: (opts) => rpc.call("createMotionComponent", opts || {}),\n addAudio: (opts) => rpc.call("addAudio", opts || {}),\n editFrameAudio: (opts) => rpc.call("editFrameAudio", opts || {}),\n editVideoClip: (opts) => rpc.call("editVideoClip", opts || {}),\n detectAudioOnsets: (opts) => rpc.call("detectAudioOnsets", opts || {}),\n cutToBeat: (opts) => rpc.call("cutToBeat", opts || {}),\n editPrototype: (opts) => rpc.call("editPrototype", opts || {}),\n usePresentationFallbackAudio: (opts) => rpc.call("usePresentationFallbackAudio", opts || {}),\n /** Bring a Lottie, PDF or PopCraft file into the open document (the `importFile` field is the file an importer was handed). */\n importDocumentFile: (opts) => rpc.call("importDocumentFile", opts || {}),\n getStyles: () => rpc.call("getStyles"),\n createPaintStyle: (name, paints) => rpc.call("createPaintStyle", { name, paints }),\n getVariables: () => rpc.call("getVariables"),\n setVariableValue: (variableId, modeId, value) => rpc.call("setVariableValue", { variableId, modeId, value }),\n exportNode: (id, options) => rpc.call("exportNode", { id, options: options || {} }),\n openExternal: (url) => rpc.call("openExternal", { url }),\n /**\n * Send a file to an https URL. PopCraft makes the request, not the sandbox, so it works for files of\n * any size the server accepts, including straight to a presigned storage URL. Needs `network`.\n */\n upload: (url, options) => {\n const body = options.body instanceof ArrayBuffer ? new Uint8Array(options.body) : options.body;\n return rpc.call("upload", {\n url,\n body,\n method: options.method,\n contentType: options.contentType,\n headers: options.headers\n });\n },\n notify: (message, options) => rpc.call("notify", { message: String(message), error: !!(options && options.error) }),\n showUI: (options) => rpc.call("showUI", options || {}),\n hideUI: () => rpc.call("hideUI"),\n /** Ends the run. The optional message is shown as a notification. */\n closePlugin: (message) => rpc.call("closePlugin", { message }),\n storage: {\n get: (key) => rpc.call("storageGet", { key }),\n set: (key, value) => rpc.call("storageSet", { key, value })\n },\n ui: {\n get onmessage() {\n return uiOnMessage;\n },\n set onmessage(cb) {\n uiOnMessage = cb;\n flushUI();\n },\n postMessage: (msg) => rpc.send({ kind: "toUI", data: msg }),\n on: (cb) => {\n uiHandlers.push(cb);\n flushUI();\n }\n },\n widget: {\n register(def) {\n if (!def || typeof def.render !== "function") throw new Error("popcraft.widget.register needs a render(state) function");\n widgetDef = def;\n widgetQueue.splice(0).forEach(handleWidgetClick);\n },\n insert(options) {\n if (!widgetDef) return Promise.reject(new Error("Call popcraft.widget.register first"));\n const o = options || {};\n const state = o.state !== void 0 ? clone(o.state) : initialWidgetState();\n return rpc.call("widgetInsert", { state, tree: renderWidget(state).tree, x: o.x, y: o.y });\n },\n getState: (nodeId) => rpc.call("widgetGet", { id: nodeId }),\n setState(nodeId, state) {\n if (!widgetDef) return Promise.reject(new Error("Call popcraft.widget.register first"));\n return commitWidget(nodeId, clone(state));\n }\n },\n on: (event, cb) => rpc.on(event, cb),\n off: (event, cb) => rpc.off(event, cb)\n };\n rpc.on("init", (data) => {\n var _a, _b, _c;\n popcraft.command = data.command;\n popcraft.importMode = (_a = data.importMode) != null ? _a : null;\n popcraft.importFile = (_b = data.importFile) != null ? _b : null;\n popcraft.exportRequest = (_c = data.exportRequest) != null ? _c : null;\n });\n return popcraft;\n }\n function installMainApi() {\n const rpc = createRpcClient();\n const api = createMainApi(rpc);\n const w = window;\n w.__popcraftRpc = rpc;\n w.popcraft = api;\n w.vizpops = api;\n window.addEventListener("error", (e) => rpc.send({ kind: "error", message: String(e.message || e) }));\n window.addEventListener("unhandledrejection", (e) => rpc.send({ kind: "error", message: String(e.reason && e.reason.message || e.reason) }));\n return api;\n }\n\n // src/runtime/main.entry.ts\n installMainApi();\n})();';
85
85
  var UI_RUNTIME = '"use strict";\n(() => {\n // src/runtime/rpc.ts\n function createRpcClient() {\n let port = null;\n const queue = [];\n const pending = {};\n const listeners = {};\n let nextId = 1;\n function send(msg) {\n if (port) port.postMessage(msg);\n else queue.push(msg);\n }\n function call(method, params) {\n return new Promise((resolve, reject) => {\n const id = nextId++;\n pending[id] = { resolve, reject };\n send({ kind: "rpc", id, method, params });\n });\n }\n function emit(event, data) {\n for (const cb of (listeners[event] || []).slice()) {\n try {\n cb(data);\n } catch (e) {\n console.error(e);\n }\n }\n }\n window.addEventListener("message", (e) => {\n const data = e.data;\n if (e.source !== window.parent || !data || data.type !== "popcraft-plugin-port" || port) return;\n port = e.ports[0];\n port.onmessage = (ev) => {\n const m = ev.data || {};\n if (m.kind === "result" && m.id !== void 0 && pending[m.id]) {\n const p = pending[m.id];\n delete pending[m.id];\n if (m.error) p.reject(new Error(m.error));\n else p.resolve(m.result);\n } else if (m.kind === "event" && m.event) {\n emit(m.event, m.data);\n }\n };\n for (const msg of queue.splice(0)) port.postMessage(msg);\n });\n return {\n call,\n send,\n on(event, cb) {\n (listeners[event] = listeners[event] || []).push(cb);\n },\n off(event, cb) {\n listeners[event] = (listeners[event] || []).filter((x) => x !== cb);\n }\n };\n }\n\n // src/runtime/ui.ts\n function createUiApi(rpc = createRpcClient()) {\n const popcraft = {\n onmessage: null,\n postMessage: (msg) => rpc.send({ kind: "fromUI", data: msg }),\n close: () => rpc.send({ kind: "closeUI" })\n };\n rpc.on("message", (msg) => {\n if (typeof popcraft.onmessage === "function") popcraft.onmessage(msg);\n window.dispatchEvent(new MessageEvent("message", { data: { pluginMessage: msg } }));\n });\n return popcraft;\n }\n function installUiApi() {\n const rpc = createRpcClient();\n const api = createUiApi(rpc);\n const w = window;\n w.__popcraftRpc = rpc;\n w.popcraft = api;\n w.vizpops = api;\n return api;\n }\n\n // src/runtime/ui.entry.ts\n installUiApi();\n})();';
86
86
 
87
- // node_modules/.pnpm/@popcraft+plugins@1.0.0_patch_hash=6826cb40a12857607fa1f7d7d102e5d8316bd483c1ff06865dac73139dfe709b/node_modules/@popcraft/plugins/dist/host.js
87
+ // node_modules/.pnpm/@popcraft+plugins@1.3.0/node_modules/@popcraft/plugins/dist/host.js
88
88
  var escapeScript = (code) => code.replace(/<\/script/gi, "<\\/script");
89
89
  function csp(network) {
90
90
  const connect = network ? "connect-src * data: blob:;" : "connect-src 'none';";
@@ -112,16 +112,16 @@ function buildUIDocument(uiHtml, network) {
112
112
  return `<!doctype html><html><head>${head}</head><body>${uiHtml}</body></html>`;
113
113
  }
114
114
 
115
- // node_modules/.pnpm/@popcraft+plugins@1.0.0_patch_hash=6826cb40a12857607fa1f7d7d102e5d8316bd483c1ff06865dac73139dfe709b/node_modules/@popcraft/plugins/dist/runtime/main.js
115
+ // node_modules/.pnpm/@popcraft+plugins@1.3.0/node_modules/@popcraft/plugins/dist/runtime/main.js
116
116
  init_define_POPCRAFT_COMPONENT_IDS();
117
117
 
118
- // node_modules/.pnpm/@popcraft+plugins@1.0.0_patch_hash=6826cb40a12857607fa1f7d7d102e5d8316bd483c1ff06865dac73139dfe709b/node_modules/@popcraft/plugins/dist/runtime/rpc.js
118
+ // node_modules/.pnpm/@popcraft+plugins@1.3.0/node_modules/@popcraft/plugins/dist/runtime/rpc.js
119
119
  init_define_POPCRAFT_COMPONENT_IDS();
120
120
 
121
- // node_modules/.pnpm/@popcraft+plugins@1.0.0_patch_hash=6826cb40a12857607fa1f7d7d102e5d8316bd483c1ff06865dac73139dfe709b/node_modules/@popcraft/plugins/dist/types.js
121
+ // node_modules/.pnpm/@popcraft+plugins@1.3.0/node_modules/@popcraft/plugins/dist/types.js
122
122
  init_define_POPCRAFT_COMPONENT_IDS();
123
123
 
124
- // node_modules/.pnpm/@popcraft+plugins@1.0.0_patch_hash=6826cb40a12857607fa1f7d7d102e5d8316bd483c1ff06865dac73139dfe709b/node_modules/@popcraft/plugins/dist/widget.js
124
+ // node_modules/.pnpm/@popcraft+plugins@1.3.0/node_modules/@popcraft/plugins/dist/widget.js
125
125
  init_define_POPCRAFT_COMPONENT_IDS();
126
126
 
127
127
  // lib/plugins/ui-theme.ts
@@ -579,7 +579,7 @@ async function runPlugin(pluginId, commandId, opts = {}) {
579
579
  if (!openBeside(url)) throw new Error("The browser blocked the new tab");
580
580
  },
581
581
  createDocument: async (opts2) => {
582
- const { createFreshFileOrExplain } = await import("./new-file-5T4Y7FST.js");
582
+ const { createFreshFileOrExplain } = await import("./new-file-XH7E3IEZ.js");
583
583
  const created = await createFreshFileOrExplain({}, { name: opts2?.name });
584
584
  if (!created.ok) throw new Error(created.reason === "plan" ? "Plan limit reached" : created.reason === "guest-limit" ? "Sign in to create another file" : `Could not create file (${created.reason})`);
585
585
  await useEditorStore.getState().openDocument(created.docId);
@@ -769,7 +769,7 @@ if (typeof window !== "undefined") {
769
769
  const name = d.name || "Imported";
770
770
  void (async () => {
771
771
  try {
772
- const { importViaPlugin, applyPluginLayoutIr } = await import("./plugin-import-BCP4JHKO.js");
772
+ const { importViaPlugin, applyPluginLayoutIr } = await import("./plugin-import-P4PD76X3.js");
773
773
  if (d.ir && typeof d.ir === "object") {
774
774
  await applyPluginLayoutIr(d.ir, { mode, name });
775
775
  return;
@@ -4588,7 +4588,7 @@ function createPopcornSlice(set2, get2) {
4588
4588
  privateMode: false,
4589
4589
  setPrivateMode: (on) => {
4590
4590
  set2({ privateMode: on });
4591
- void import("./presence-7NKX5VRN.js").then((m) => m.setPresencePrivate(on)).catch(() => {
4591
+ void import("./presence-WSPF6N32.js").then((m) => m.setPresencePrivate(on)).catch(() => {
4592
4592
  });
4593
4593
  },
4594
4594
  /** Reveal every private item THIS user created (they sync to collaborators). Others' stay theirs. */
@@ -6652,7 +6652,7 @@ var useEditorStore = create()(
6652
6652
  // A copy is a new file, so it is cloud-backed like any other (see lib/client/cloud/new-file.ts).
6653
6653
  // Returns '' when it was refused (offline, signed out, plan full) or failed; nothing is left behind.
6654
6654
  duplicateDocument: async (id) => {
6655
- const { duplicateDocumentToCloud } = await import("./sync-KX7WMSPO.js");
6655
+ const { duplicateDocumentToCloud } = await import("./sync-4AHF7NWR.js");
6656
6656
  return duplicateDocumentToCloud(id);
6657
6657
  },
6658
6658
  deleteDocument: async (id) => {
@@ -10115,9 +10115,9 @@ if (typeof window !== "undefined") {
10115
10115
  if (typeof window !== "undefined" && navigator.webdriver) {
10116
10116
  window.__vpDesign = async (script, o = {}) => {
10117
10117
  const [{ registerBuiltinTools, runTool }, { editorToolContext }, { createDesignPreview }] = await Promise.all([
10118
- import("./tools-YU5CI5J5.js").then(async (m) => ({ ...m, registerBuiltinTools: (await import("./builtin-3US63AOP.js")).registerBuiltinTools })),
10119
- import("./editor-seams-BT4QWT5U.js"),
10120
- import("./live-YWRRPA6G.js")
10118
+ import("./tools-T5XCKKCW.js").then(async (m) => ({ ...m, registerBuiltinTools: (await import("./builtin-3TWJNRDI.js")).registerBuiltinTools })),
10119
+ import("./editor-seams-AZ2NUX6J.js"),
10120
+ import("./live-ZRJ2GT3E.js")
10121
10121
  ]);
10122
10122
  registerBuiltinTools();
10123
10123
  const args = JSON.stringify({ ...o.name ? { name: o.name } : {}, ...o.size ? { size: o.size } : {}, ...o.frame ? { frame: o.frame } : {}, script });
@@ -9,7 +9,7 @@ import {
9
9
  hostEnv,
10
10
  plain,
11
11
  registerBuiltinCommands
12
- } from "./chunk-IDWJKGNU.js";
12
+ } from "./chunk-7XP5CWQO.js";
13
13
  import {
14
14
  storeOpsSeam
15
15
  } from "./chunk-KOAEKWIA.js";
@@ -391,6 +391,8 @@ function isOpenableUrl(v) {
391
391
  return false;
392
392
  }
393
393
  }
394
+ var MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
395
+ var MAX_UPLOAD_REPLY_CHARS = 1024 * 1024;
394
396
  function pluginOf(ctx) {
395
397
  if (ctx.actor.kind !== "plugin" || !ctx.actor.id) throw new PluginApiError("Only a plugin keeps file data");
396
398
  return ctx.actor.id;
@@ -474,6 +476,46 @@ var commands = [
474
476
  return { summary: "Opened link" };
475
477
  }
476
478
  }),
479
+ defineCommand({
480
+ id: pluginCommandId("upload"),
481
+ title: "Upload a file",
482
+ domain: "ui",
483
+ // The sandbox's own fetch is fine for small requests; a book-sized export goes through here so it can be
484
+ // PUT straight onto a presigned storage URL rather than through a server that caps its request body.
485
+ params: external_exports.object({
486
+ url: external_exports.string().refine(isOpenableUrl, { error: "url must be https, or http on localhost" }),
487
+ body: external_exports.union([external_exports.instanceof(Uint8Array), external_exports.string()]),
488
+ method: external_exports.enum(["PUT", "POST"]).optional(),
489
+ contentType: external_exports.string().max(200).optional(),
490
+ headers: external_exports.record(external_exports.string(), external_exports.string()).optional()
491
+ }),
492
+ effect: "none",
493
+ permission: "network",
494
+ expose: { plugin: { method: "upload", shape: (r) => r.data } },
495
+ run: async (p) => {
496
+ const size2 = typeof p.body === "string" ? p.body.length : p.body.byteLength;
497
+ if (size2 > MAX_UPLOAD_BYTES) throw new PluginApiError(`upload is limited to ${MAX_UPLOAD_BYTES / 1024 / 1024} MB`, "invalid_params");
498
+ if (typeof location !== "undefined" && new URL(p.url).origin === location.origin) {
499
+ throw new PluginApiError("upload cannot send to PopCraft itself", "invalid_params");
500
+ }
501
+ const headers = new Headers(p.headers ?? {});
502
+ if (p.contentType) headers.set("content-type", p.contentType);
503
+ let res;
504
+ try {
505
+ res = await fetch(p.url, { method: p.method ?? "PUT", body: p.body, headers, credentials: "omit", mode: "cors" });
506
+ } catch (err) {
507
+ throw new PluginApiError(`Could not reach ${new URL(p.url).origin}: ${err instanceof Error ? err.message : String(err)}`, "unsupported");
508
+ }
509
+ const text = await res.text();
510
+ const data = {
511
+ ok: res.ok,
512
+ status: res.status,
513
+ headers: Object.fromEntries(res.headers.entries()),
514
+ body: text.length > MAX_UPLOAD_REPLY_CHARS ? text.slice(0, MAX_UPLOAD_REPLY_CHARS) : text
515
+ };
516
+ return { summary: `Uploaded ${size2} bytes (${res.status})`, data };
517
+ }
518
+ }),
477
519
  defineCommand({
478
520
  id: pluginCommandId("showUI"),
479
521
  title: "Show plugin UI",
@@ -573,7 +615,7 @@ var commands = [
573
615
  const [{ storefrontInputs }, { STOREFRONT_TARGETS }, { bytesOf }, { zipFiles }, { EXPORT_NODE_CAP }] = await Promise.all([
574
616
  import("./storefront-prepare-EXR7OIWW.js"),
575
617
  import("./storefront-BPMM6QWK.js"),
576
- import("./project-RD62AMZA.js"),
618
+ import("./project-M3KF66GG.js"),
577
619
  import("./zip-export-ZYXDK4SM.js"),
578
620
  import("./tree-YKYXBPRZ.js")
579
621
  ]);
@@ -770,7 +812,7 @@ var pluginRun = defineCommand({
770
812
  }
771
813
  },
772
814
  run: async (p, ctx) => {
773
- const { runPlugin } = await import("./host-ID3XQKWC.js");
815
+ const { runPlugin } = await import("./host-FNER4M32.js");
774
816
  await runPlugin(p.pluginId, p.commandId);
775
817
  ctx.notice(`Started ${p.pluginId}.`);
776
818
  return {