@mulmoclaude/mulmoscript-plugin 4.1.1 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vue.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_plugin = require("./plugin-BLp8tSYt.cjs");
5
+ const require_plugin = require("./plugin-BOq5YkTl.cjs");
6
6
  const require_contract = require("./contract-BhN3yKJC.cjs");
7
7
  let _mulmocast_types = require("@mulmocast/types");
8
8
  let vue = require("vue");
@@ -204,6 +204,22 @@ function clearReactiveRecords(...records) {
204
204
  Object.keys(record).forEach((key) => Reflect.deleteProperty(record, key));
205
205
  });
206
206
  }
207
+ /**
208
+ * Whether a `focusout` means focus actually LEFT `container`, rather than moving between two
209
+ * fields inside it.
210
+ *
211
+ * The distinction decides whether pending edits are written: treating every focusout as a
212
+ * departure would write on each hop between inputs, and treating none as one would let the user
213
+ * walk away with the last keystroke unsaved.
214
+ *
215
+ * `null` is focus going nowhere the document can name — clicking the page chrome, or the window
216
+ * losing focus. That counts as leaving: the editor is no longer where the typing goes.
217
+ */
218
+ function focusLeftContainer(container, movedTo) {
219
+ if (!container) return false;
220
+ if (!(movedTo instanceof Node)) return true;
221
+ return !container.contains(movedTo);
222
+ }
207
223
  //#endregion
208
224
  //#region src/vue/transport.ts
209
225
  var GENERATION_EVENT_KINDS = /* @__PURE__ */ new Set([
@@ -1309,118 +1325,113 @@ var _hoisted_10 = { class: "flex-1 min-w-0" };
1309
1325
  var _hoisted_11 = { class: "font-medium" };
1310
1326
  var _hoisted_12 = { class: "break-words whitespace-pre-wrap mt-0.5" };
1311
1327
  var _hoisted_13 = ["disabled"];
1312
- var _hoisted_14 = {
1313
- key: 2,
1314
- class: "flex-1 overflow-hidden",
1315
- "data-testid": "mulmo-script-deck-editor"
1316
- };
1317
- var _hoisted_15 = { class: "flex gap-3 items-stretch" };
1318
- var _hoisted_16 = [
1328
+ var _hoisted_14 = { class: "flex gap-3 items-stretch" };
1329
+ var _hoisted_15 = [
1319
1330
  "onDragover",
1320
1331
  "onDragleave",
1321
1332
  "onDrop"
1322
1333
  ];
1323
- var _hoisted_17 = ["data-testid"];
1324
- var _hoisted_18 = ["src", "data-testid"];
1325
- var _hoisted_19 = [
1334
+ var _hoisted_16 = ["data-testid"];
1335
+ var _hoisted_17 = ["src", "data-testid"];
1336
+ var _hoisted_18 = [
1326
1337
  "title",
1327
1338
  "aria-label",
1328
1339
  "data-testid",
1329
1340
  "onClick"
1330
1341
  ];
1331
- var _hoisted_20 = [
1342
+ var _hoisted_19 = [
1332
1343
  "src",
1333
1344
  "alt",
1334
1345
  "onClick"
1335
1346
  ];
1336
- var _hoisted_21 = [
1347
+ var _hoisted_20 = [
1337
1348
  "title",
1338
1349
  "aria-label",
1339
1350
  "data-testid",
1340
1351
  "onClick"
1341
1352
  ];
1342
- var _hoisted_22 = {
1353
+ var _hoisted_21 = {
1343
1354
  key: 0,
1344
1355
  class: "animate-spin w-5 h-5",
1345
1356
  viewBox: "0 0 24 24",
1346
1357
  fill: "none"
1347
1358
  };
1348
- var _hoisted_23 = {
1359
+ var _hoisted_22 = {
1349
1360
  key: 1,
1350
1361
  class: "material-icons text-3xl"
1351
1362
  };
1352
- var _hoisted_24 = ["disabled", "onClick"];
1353
- var _hoisted_25 = {
1363
+ var _hoisted_23 = ["disabled", "onClick"];
1364
+ var _hoisted_24 = {
1354
1365
  key: 3,
1355
1366
  class: "w-full aspect-video flex flex-col items-center justify-center gap-1 p-2"
1356
1367
  };
1357
- var _hoisted_26 = { class: "text-xs text-green-500" };
1358
- var _hoisted_27 = {
1368
+ var _hoisted_25 = { class: "text-xs text-green-500" };
1369
+ var _hoisted_26 = {
1359
1370
  key: 1,
1360
1371
  class: "text-xs text-red-400 text-center"
1361
1372
  };
1362
- var _hoisted_28 = {
1373
+ var _hoisted_27 = {
1363
1374
  key: 0,
1364
1375
  class: "text-xs text-gray-400 text-center italic leading-relaxed px-1"
1365
1376
  };
1366
- var _hoisted_29 = {
1377
+ var _hoisted_28 = {
1367
1378
  key: 1,
1368
1379
  class: "text-xs text-gray-300"
1369
1380
  };
1370
- var _hoisted_30 = {
1381
+ var _hoisted_29 = {
1371
1382
  key: 2,
1372
1383
  class: "absolute inset-0 flex items-center justify-center bg-blue-50/80 pointer-events-none"
1373
1384
  };
1374
- var _hoisted_31 = { class: "text-xs text-blue-500 font-medium" };
1375
- var _hoisted_32 = {
1385
+ var _hoisted_30 = { class: "text-xs text-blue-500 font-medium" };
1386
+ var _hoisted_31 = {
1376
1387
  key: 3,
1377
1388
  class: "absolute bottom-0 inset-x-0 text-center text-xs text-gray-400 bg-white/70 py-0.5 pointer-events-none"
1378
1389
  };
1379
- var _hoisted_33 = ["onClick"];
1380
- var _hoisted_34 = { class: "flex flex-col flex-1 min-w-0 px-2 py-1.5" };
1381
- var _hoisted_35 = { class: "text-sm text-gray-800 leading-relaxed" };
1382
- var _hoisted_36 = { class: "flex justify-between mt-auto pt-1" };
1383
- var _hoisted_37 = { class: "flex items-center gap-1" };
1384
- var _hoisted_38 = {
1390
+ var _hoisted_32 = ["onClick"];
1391
+ var _hoisted_33 = { class: "flex flex-col flex-1 min-w-0 px-2 py-1.5" };
1392
+ var _hoisted_34 = { class: "text-sm text-gray-800 leading-relaxed" };
1393
+ var _hoisted_35 = { class: "flex justify-between mt-auto pt-1" };
1394
+ var _hoisted_36 = { class: "flex items-center gap-1" };
1395
+ var _hoisted_37 = {
1385
1396
  key: 0,
1386
1397
  class: "animate-spin w-3 h-3 text-green-400",
1387
1398
  viewBox: "0 0 24 24",
1388
1399
  fill: "none"
1389
1400
  };
1390
- var _hoisted_39 = ["onClick"];
1391
- var _hoisted_40 = ["title"];
1392
- var _hoisted_41 = ["disabled", "onClick"];
1393
- var _hoisted_42 = ["onClick"];
1394
- var _hoisted_43 = [
1401
+ var _hoisted_38 = ["onClick"];
1402
+ var _hoisted_39 = ["title"];
1403
+ var _hoisted_40 = ["disabled", "onClick"];
1404
+ var _hoisted_41 = ["onClick"];
1405
+ var _hoisted_42 = [
1395
1406
  "title",
1396
1407
  "data-testid",
1397
1408
  "onClick"
1398
1409
  ];
1399
- var _hoisted_44 = {
1410
+ var _hoisted_43 = {
1400
1411
  key: 0,
1401
1412
  class: "border-t border-gray-100"
1402
1413
  };
1403
- var _hoisted_45 = ["onUpdate:modelValue", "data-testid"];
1404
- var _hoisted_46 = { class: "flex items-center justify-end gap-2 px-2 pb-2" };
1405
- var _hoisted_47 = {
1414
+ var _hoisted_44 = ["onUpdate:modelValue", "data-testid"];
1415
+ var _hoisted_45 = { class: "flex items-center justify-end gap-2 px-2 pb-2" };
1416
+ var _hoisted_46 = {
1406
1417
  key: 0,
1407
1418
  class: "text-xs text-red-600",
1408
1419
  role: "alert"
1409
1420
  };
1410
- var _hoisted_48 = [
1421
+ var _hoisted_47 = [
1411
1422
  "disabled",
1412
1423
  "data-testid",
1413
1424
  "onClick"
1414
1425
  ];
1415
- var _hoisted_49 = {
1426
+ var _hoisted_48 = {
1416
1427
  key: 0,
1417
1428
  class: "flex items-center justify-center h-32 text-gray-400 text-sm"
1418
1429
  };
1419
- var _hoisted_50 = { class: "bottom-bar-wrapper" };
1420
- var _hoisted_51 = { class: "editor-actions" };
1421
- var _hoisted_52 = ["disabled"];
1422
- var _hoisted_53 = ["title"];
1423
- var _hoisted_54 = { class: "material-icons" };
1430
+ var _hoisted_49 = { class: "bottom-bar-wrapper" };
1431
+ var _hoisted_50 = { class: "editor-actions" };
1432
+ var _hoisted_51 = ["disabled"];
1433
+ var _hoisted_52 = ["title"];
1434
+ var _hoisted_53 = { class: "material-icons" };
1424
1435
  var SILENT_BEAT_DEFAULT_SEC = 3;
1425
1436
  var MS_PER_SECOND = 1e3;
1426
1437
  var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.defineComponent)({
@@ -1602,6 +1613,18 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
1602
1613
  function onDeckBeatsUpdate(beats) {
1603
1614
  onDeckUpdate((0, _mulmocast_beat_editor.withBeats)(deckScriptInput.value, beats));
1604
1615
  }
1616
+ /**
1617
+ * Leaving the editor writes whatever is still in the debounce.
1618
+ *
1619
+ * Asking the agent to change the script means moving focus out of here first, so this lands
1620
+ * ahead of every request without the host having to announce one — MulmoTerminal's agent is a
1621
+ * terminal, and there is no "sent" event to hook. The debounce is short enough that it has
1622
+ * usually fired already; this closes the case where it has not, which would otherwise have the
1623
+ * agent read a file missing the last thing the user typed.
1624
+ */
1625
+ function onDeckFocusOut(event) {
1626
+ if (focusLeftContainer(event.currentTarget instanceof Node ? event.currentTarget : null, event.relatedTarget)) flushPendingDeckSave();
1627
+ }
1605
1628
  (0, vue.onBeforeUnmount)(() => {
1606
1629
  flushPendingDeckSave();
1607
1630
  resetBeatMovies();
@@ -2079,10 +2102,15 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2079
2102
  "onCharDrop",
2080
2103
  "onRenderCharacter"
2081
2104
  ])) : (0, vue.createCommentVNode)("", true),
2082
- (0, vue.unref)(isDeck) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_14, [(0, vue.createVNode)((0, vue.unref)(BeatListEditor), {
2105
+ (0, vue.unref)(isDeck) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2106
+ key: 2,
2107
+ class: "flex-1 overflow-hidden",
2108
+ "data-testid": "mulmo-script-deck-editor",
2109
+ onFocusout: onDeckFocusOut
2110
+ }, [(0, vue.createVNode)((0, vue.unref)(BeatListEditor), {
2083
2111
  beats: deckBeats.value,
2084
2112
  "onUpdate:beats": onDeckBeatsUpdate
2085
- }, null, 8, ["beats"])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2113
+ }, null, 8, ["beats"])], 32)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2086
2114
  key: 3,
2087
2115
  ref_key: "beatListEl",
2088
2116
  ref: beatListEl,
@@ -2091,7 +2119,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2091
2119
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2092
2120
  key: index,
2093
2121
  class: "rounded-lg border border-gray-200 overflow-hidden"
2094
- }, [(0, vue.createElementVNode)("div", _hoisted_15, [(0, vue.createElementVNode)("div", {
2122
+ }, [(0, vue.createElementVNode)("div", _hoisted_14, [(0, vue.createElementVNode)("div", {
2095
2123
  class: (0, vue.normalizeClass)(["relative shrink-0 w-[45%] overflow-hidden bg-gray-50 transition-colors", beatDragOver[index] ? "bg-blue-50" : ""]),
2096
2124
  onDragover: ($event) => onBeatDragOver($event, index),
2097
2125
  onDragleave: ($event) => onBeatDragLeave(index),
@@ -2100,27 +2128,27 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2100
2128
  (0, vue.createElementVNode)("div", {
2101
2129
  class: "absolute top-1.5 left-1.5 z-10 px-1.5 py-0.5 rounded bg-black/55 text-white text-xs font-medium leading-none pointer-events-none",
2102
2130
  "data-testid": `mulmo-script-beat-number-${index}`
2103
- }, (0, vue.toDisplayString)(index + 1), 9, _hoisted_17),
2131
+ }, (0, vue.toDisplayString)(index + 1), 9, _hoisted_16),
2104
2132
  (0, vue.unref)(beatMovieOpen)[index] && (0, vue.unref)(beatMovieUrls)[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createElementVNode)("video", {
2105
2133
  src: (0, vue.unref)(beatMovieUrls)[index],
2106
2134
  class: "w-full object-contain",
2107
2135
  controls: "",
2108
2136
  autoplay: "",
2109
2137
  "data-testid": `mulmo-script-beat-movie-player-${index}`
2110
- }, null, 8, _hoisted_18), (0, vue.createElementVNode)("button", {
2138
+ }, null, 8, _hoisted_17), (0, vue.createElementVNode)("button", {
2111
2139
  class: "absolute top-1.5 right-1.5 flex items-center justify-center w-6 h-6 rounded border border-gray-400 text-gray-600 bg-white hover:bg-gray-50",
2112
2140
  title: (0, vue.unref)(m).close,
2113
2141
  "aria-label": (0, vue.unref)(m).close,
2114
2142
  "data-testid": `mulmo-script-beat-movie-close-${index}`,
2115
2143
  onClick: (0, vue.withModifiers)(($event) => (0, vue.unref)(closeBeatMovie)(index), ["stop"])
2116
- }, [..._cache[4] || (_cache[4] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_19)], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
2144
+ }, [..._cache[4] || (_cache[4] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_18)], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
2117
2145
  renderedImages[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
2118
2146
  key: 0,
2119
2147
  src: renderedImages[index],
2120
2148
  class: "w-full object-contain cursor-zoom-in",
2121
2149
  alt: `Beat ${index + 1}`,
2122
2150
  onClick: ($event) => openLightbox(index)
2123
- }, null, 8, _hoisted_20)) : (0, vue.createCommentVNode)("", true),
2151
+ }, null, 8, _hoisted_19)) : (0, vue.createCommentVNode)("", true),
2124
2152
  renderedImages[index] && (0, vue.unref)(beatMovies)[index] && canFetchMedia.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2125
2153
  key: 1,
2126
2154
  class: "absolute inset-0 m-auto w-12 h-12 flex items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70",
@@ -2128,7 +2156,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2128
2156
  "aria-label": (0, vue.unref)(m).play,
2129
2157
  "data-testid": `mulmo-script-beat-movie-play-${index}`,
2130
2158
  onClick: (0, vue.withModifiers)(($event) => (0, vue.unref)(playBeatMovie)(index), ["stop"])
2131
- }, [(0, vue.unref)(beatMovieLoading)[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_22, [..._cache[5] || (_cache[5] = [(0, vue.createElementVNode)("circle", {
2159
+ }, [(0, vue.unref)(beatMovieLoading)[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_21, [..._cache[5] || (_cache[5] = [(0, vue.createElementVNode)("circle", {
2132
2160
  class: "opacity-25",
2133
2161
  cx: "12",
2134
2162
  cy: "12",
@@ -2139,13 +2167,13 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2139
2167
  class: "opacity-75",
2140
2168
  fill: "currentColor",
2141
2169
  d: "M4 12a8 8 0 018-8v8H4z"
2142
- }, null, -1)])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_23, "play_arrow"))], 8, _hoisted_21)) : (0, vue.createCommentVNode)("", true),
2170
+ }, null, -1)])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_22, "play_arrow"))], 8, _hoisted_20)) : (0, vue.createCommentVNode)("", true),
2143
2171
  renderedImages[index] && renderState[index] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2144
2172
  key: 2,
2145
2173
  class: "absolute top-1.5 right-1.5 flex items-center gap-1 px-2 py-0.5 text-xs rounded border border-gray-400 text-gray-600 bg-white hover:bg-gray-50 disabled:opacity-60 disabled:cursor-not-allowed",
2146
2174
  disabled: (0, vue.unref)(movieGenerating),
2147
2175
  onClick: (0, vue.withModifiers)(($event) => regenerateBeat(index), ["stop"])
2148
- }, " ↺ ", 8, _hoisted_24)) : !renderedImages[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_25, [renderState[index] === "rendering" || (0, vue.unref)(movieGenerating) && !renderedImages[index] && effectiveBeat$1(index).imagePrompt ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [_cache[6] || (_cache[6] = (0, vue.createElementVNode)("svg", {
2176
+ }, " ↺ ", 8, _hoisted_23)) : !renderedImages[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_24, [renderState[index] === "rendering" || (0, vue.unref)(movieGenerating) && !renderedImages[index] && effectiveBeat$1(index).imagePrompt ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [_cache[6] || (_cache[6] = (0, vue.createElementVNode)("svg", {
2149
2177
  class: "animate-spin w-4 h-4 text-green-400",
2150
2178
  viewBox: "0 0 24 24",
2151
2179
  fill: "none"
@@ -2160,15 +2188,15 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2160
2188
  class: "opacity-75",
2161
2189
  fill: "currentColor",
2162
2190
  d: "M4 12a8 8 0 018-8v8H4z"
2163
- })], -1)), (0, vue.createElementVNode)("span", _hoisted_26, (0, vue.toDisplayString)((0, vue.unref)(m).rendering), 1)], 64)) : renderState[index] === "error" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_27, (0, vue.toDisplayString)(renderErrors[index]), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [effectiveBeat$1(index).imagePrompt ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_28, (0, vue.toDisplayString)(effectiveBeat$1(index).imagePrompt), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_29, (0, vue.toDisplayString)(beat.image?.type ?? "—"), 1))], 64))])) : (0, vue.createCommentVNode)("", true)
2191
+ })], -1)), (0, vue.createElementVNode)("span", _hoisted_25, (0, vue.toDisplayString)((0, vue.unref)(m).rendering), 1)], 64)) : renderState[index] === "error" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_26, (0, vue.toDisplayString)(renderErrors[index]), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [effectiveBeat$1(index).imagePrompt ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_27, (0, vue.toDisplayString)(effectiveBeat$1(index).imagePrompt), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_28, (0, vue.toDisplayString)(beat.image?.type ?? "—"), 1))], 64))])) : (0, vue.createCommentVNode)("", true)
2164
2192
  ], 64)),
2165
- beatDragOver[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_30, [(0, vue.createElementVNode)("span", _hoisted_31, (0, vue.toDisplayString)((0, vue.unref)(m).drop), 1)])) : !renderedImages[index] && renderState[index] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_32, (0, vue.toDisplayString)((0, vue.unref)(m).orDropImage), 1)) : (0, vue.createCommentVNode)("", true),
2193
+ beatDragOver[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_29, [(0, vue.createElementVNode)("span", _hoisted_30, (0, vue.toDisplayString)((0, vue.unref)(m).drop), 1)])) : !renderedImages[index] && renderState[index] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_31, (0, vue.toDisplayString)((0, vue.unref)(m).orDropImage), 1)) : (0, vue.createCommentVNode)("", true),
2166
2194
  !renderedImages[index] && renderState[index] !== "rendering" && !(0, vue.unref)(movieGenerating) && !(0, vue.unref)(isBeatImageReference)(effectiveBeat$1(index)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2167
2195
  key: 4,
2168
2196
  class: "absolute top-1.5 right-1.5 flex items-center gap-1 px-2 py-0.5 text-xs rounded border border-blue-400 text-blue-600 bg-white hover:bg-blue-50",
2169
2197
  onClick: ($event) => renderBeat(index)
2170
- }, (0, vue.toDisplayString)((0, vue.unref)(m).generate), 9, _hoisted_33)) : (0, vue.createCommentVNode)("", true)
2171
- ], 42, _hoisted_16), (0, vue.createElementVNode)("div", _hoisted_34, [(0, vue.createElementVNode)("span", _hoisted_35, (0, vue.toDisplayString)(effectiveBeat$1(index).text), 1), (0, vue.createElementVNode)("div", _hoisted_36, [(0, vue.createElementVNode)("div", _hoisted_37, [audioState[index] === "generating" || (0, vue.unref)(movieGenerating) && !beatAudios[index] && effectiveBeat$1(index).text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_38, [..._cache[7] || (_cache[7] = [(0, vue.createElementVNode)("circle", {
2198
+ }, (0, vue.toDisplayString)((0, vue.unref)(m).generate), 9, _hoisted_32)) : (0, vue.createCommentVNode)("", true)
2199
+ ], 42, _hoisted_15), (0, vue.createElementVNode)("div", _hoisted_33, [(0, vue.createElementVNode)("span", _hoisted_34, (0, vue.toDisplayString)(effectiveBeat$1(index).text), 1), (0, vue.createElementVNode)("div", _hoisted_35, [(0, vue.createElementVNode)("div", _hoisted_36, [audioState[index] === "generating" || (0, vue.unref)(movieGenerating) && !beatAudios[index] && effectiveBeat$1(index).text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_37, [..._cache[7] || (_cache[7] = [(0, vue.createElementVNode)("circle", {
2172
2200
  class: "opacity-25",
2173
2201
  cx: "12",
2174
2202
  cy: "12",
@@ -2183,19 +2211,19 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2183
2211
  key: 1,
2184
2212
  class: (0, vue.normalizeClass)(["text-xs px-2 py-0.5 rounded border", playingAudio.value?.index === index ? "border-red-400 text-red-600 hover:bg-red-50" : "border-green-400 text-green-600 hover:bg-green-50"]),
2185
2213
  onClick: ($event) => playAudio(index)
2186
- }, (0, vue.toDisplayString)(playingAudio.value?.index === index ? (0, vue.unref)(m).stop : (0, vue.unref)(m).play), 11, _hoisted_39)) : audioErrors[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [(0, vue.createElementVNode)("span", {
2214
+ }, (0, vue.toDisplayString)(playingAudio.value?.index === index ? (0, vue.unref)(m).stop : (0, vue.unref)(m).play), 11, _hoisted_38)) : audioErrors[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [(0, vue.createElementVNode)("span", {
2187
2215
  class: "text-xs text-red-400 truncate min-w-0 max-w-[20rem]",
2188
2216
  title: audioErrors[index]
2189
- }, (0, vue.toDisplayString)((0, vue.unref)(m).errPrefix) + " " + (0, vue.toDisplayString)(audioErrors[index]), 9, _hoisted_40), effectiveBeat$1(index).text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2217
+ }, (0, vue.toDisplayString)((0, vue.unref)(m).errPrefix) + " " + (0, vue.toDisplayString)(audioErrors[index]), 9, _hoisted_39), effectiveBeat$1(index).text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2190
2218
  key: 0,
2191
2219
  class: "text-xs px-2 py-0.5 rounded border border-gray-300 text-gray-500 hover:bg-gray-50 disabled:opacity-50",
2192
2220
  disabled: (0, vue.unref)(movieGenerating),
2193
2221
  onClick: ($event) => generateAudio(index)
2194
- }, " ↺ ", 8, _hoisted_41)) : (0, vue.createCommentVNode)("", true)], 64)) : effectiveBeat$1(index).text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2222
+ }, " ↺ ", 8, _hoisted_40)) : (0, vue.createCommentVNode)("", true)], 64)) : effectiveBeat$1(index).text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2195
2223
  key: 3,
2196
2224
  class: "text-xs px-2 py-0.5 rounded border border-gray-300 text-gray-500 hover:bg-gray-50",
2197
2225
  onClick: ($event) => generateAudio(index)
2198
- }, (0, vue.toDisplayString)((0, vue.unref)(m).generateAudio), 9, _hoisted_42)) : (0, vue.createCommentVNode)("", true)]), (0, vue.createElementVNode)("button", {
2226
+ }, (0, vue.toDisplayString)((0, vue.unref)(m).generateAudio), 9, _hoisted_41)) : (0, vue.createCommentVNode)("", true)]), (0, vue.createElementVNode)("button", {
2199
2227
  class: "text-gray-400 hover:text-gray-600",
2200
2228
  title: sourceOpen[index] ? "Hide source" : "Show source",
2201
2229
  "data-testid": `mulmo-script-beat-source-toggle-${index}`,
@@ -2209,20 +2237,20 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2209
2237
  "stroke-width": "2",
2210
2238
  "stroke-linecap": "round",
2211
2239
  "stroke-linejoin": "round"
2212
- }, [(0, vue.createElementVNode)("polyline", { points: "16 18 22 12 16 6" }), (0, vue.createElementVNode)("polyline", { points: "8 6 2 12 8 18" })], -1)])], 8, _hoisted_43)])])]), sourceOpen[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_44, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
2240
+ }, [(0, vue.createElementVNode)("polyline", { points: "16 18 22 12 16 6" }), (0, vue.createElementVNode)("polyline", { points: "8 6 2 12 8 18" })], -1)])], 8, _hoisted_42)])])]), sourceOpen[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_43, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
2213
2241
  "onUpdate:modelValue": ($event) => sourceText[index] = $event,
2214
2242
  class: (0, vue.normalizeClass)(["w-full text-xs text-gray-600 bg-gray-50 p-2 font-mono resize-none", isValidBeat$1(index) ? "outline-none" : "outline outline-2 outline-red-400"]),
2215
2243
  rows: "8",
2216
2244
  spellcheck: "false",
2217
2245
  "data-testid": `mulmo-script-beat-source-textarea-${index}`
2218
- }, null, 10, _hoisted_45), [[vue.vModelText, sourceText[index]]]), (0, vue.createElementVNode)("div", _hoisted_46, [beatSaveErrors[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_47, (0, vue.toDisplayString)(beatSaveErrors[index].kind === "invalidJson" ? (0, vue.unref)(m).saveErrorInvalidJson(beatSaveErrors[index].error) : (0, vue.unref)(m).saveErrorSaveFailed(beatSaveErrors[index].error)), 1)) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("button", {
2246
+ }, null, 10, _hoisted_44), [[vue.vModelText, sourceText[index]]]), (0, vue.createElementVNode)("div", _hoisted_45, [beatSaveErrors[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_46, (0, vue.toDisplayString)(beatSaveErrors[index].kind === "invalidJson" ? (0, vue.unref)(m).saveErrorInvalidJson(beatSaveErrors[index].error) : (0, vue.unref)(m).saveErrorSaveFailed(beatSaveErrors[index].error)), 1)) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("button", {
2219
2247
  class: (0, vue.normalizeClass)(["px-2 py-1 text-xs rounded border", isValidBeat$1(index) && !beatSaving[index] ? "border-blue-400 text-blue-600 hover:bg-blue-50 cursor-pointer" : "border-gray-200 text-gray-300 cursor-not-allowed"]),
2220
2248
  disabled: !isValidBeat$1(index) || !!beatSaving[index],
2221
2249
  "data-testid": `mulmo-script-beat-update-button-${index}`,
2222
2250
  onClick: ($event) => updateBeat(index)
2223
- }, (0, vue.toDisplayString)(beatSaving[index] ? (0, vue.unref)(m).saving : (0, vue.unref)(m).update), 11, _hoisted_48)])])) : (0, vue.createCommentVNode)("", true)]);
2224
- }), 128)), beats.value.length === 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_49, (0, vue.toDisplayString)((0, vue.unref)(m).noBeats), 1)) : (0, vue.createCommentVNode)("", true)], 512)),
2225
- (0, vue.createElementVNode)("div", _hoisted_50, [(0, vue.createElementVNode)("details", {
2251
+ }, (0, vue.toDisplayString)(beatSaving[index] ? (0, vue.unref)(m).saving : (0, vue.unref)(m).update), 11, _hoisted_47)])])) : (0, vue.createCommentVNode)("", true)]);
2252
+ }), 128)), beats.value.length === 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_48, (0, vue.toDisplayString)((0, vue.unref)(m).noBeats), 1)) : (0, vue.createCommentVNode)("", true)], 512)),
2253
+ (0, vue.createElementVNode)("div", _hoisted_49, [(0, vue.createElementVNode)("details", {
2226
2254
  ref_key: "sourceDetails",
2227
2255
  ref: sourceDetails,
2228
2256
  class: "script-source",
@@ -2234,11 +2262,11 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2234
2262
  class: (0, vue.normalizeClass)(["script-editor", { "script-editor-invalid": sourceChanged.value && !sourceValid.value }]),
2235
2263
  spellcheck: "false"
2236
2264
  }, null, 2), [[vue.vModelText, editableSource.value]]),
2237
- (0, vue.createElementVNode)("div", _hoisted_51, [(0, vue.createElementVNode)("button", {
2265
+ (0, vue.createElementVNode)("div", _hoisted_50, [(0, vue.createElementVNode)("button", {
2238
2266
  class: "apply-btn",
2239
2267
  disabled: !sourceChanged.value || !sourceValid.value,
2240
2268
  onClick: applySource
2241
- }, (0, vue.toDisplayString)((0, vue.unref)(m).applyChanges), 9, _hoisted_52), (0, vue.createElementVNode)("button", {
2269
+ }, (0, vue.toDisplayString)((0, vue.unref)(m).applyChanges), 9, _hoisted_51), (0, vue.createElementVNode)("button", {
2242
2270
  class: "cancel-btn",
2243
2271
  onClick: cancelSourceEdit
2244
2272
  }, (0, vue.toDisplayString)((0, vue.unref)(m).cancel), 1)])
@@ -2246,7 +2274,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
2246
2274
  class: "copy-btn",
2247
2275
  title: (0, vue.unref)(copied) ? "Copied!" : "Copy",
2248
2276
  onClick: copyText
2249
- }, [(0, vue.createElementVNode)("span", _hoisted_54, (0, vue.toDisplayString)((0, vue.unref)(copied) ? "check" : "content_copy"), 1)], 8, _hoisted_53), [[vue.vShow, !editing.value]])]),
2277
+ }, [(0, vue.createElementVNode)("span", _hoisted_53, (0, vue.toDisplayString)((0, vue.unref)(copied) ? "check" : "content_copy"), 1)], 8, _hoisted_52), [[vue.vShow, !editing.value]])]),
2250
2278
  lightbox.value ? ((0, vue.openBlock)(), (0, vue.createBlock)(BeatLightbox_default, {
2251
2279
  key: 4,
2252
2280
  lightbox: lightbox.value,
@@ -2284,7 +2312,7 @@ var _plugin_vue_export_helper_default = (sfc, props) => {
2284
2312
  };
2285
2313
  //#endregion
2286
2314
  //#region src/vue/View.vue
2287
- var View_default = /*#__PURE__*/ _plugin_vue_export_helper_default(View_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-87bf6e4c"]]);
2315
+ var View_default = /*#__PURE__*/ _plugin_vue_export_helper_default(View_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-d78be72d"]]);
2288
2316
  //#endregion
2289
2317
  //#region src/vue/Preview.vue?vue&type=script&setup=true&lang.ts
2290
2318
  var _hoisted_1 = {