@mulmoclaude/mulmoscript-plugin 4.0.0 → 4.1.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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as pluginCore, d as errorMessage, f as TOOL_DEFINITION, p as TOOL_NAME } from "./plugin-CXhOP_xU.js";
2
- import { t as GENERATION_EVENT } from "./contract-vY0niHkh.js";
2
+ import { n as SCRIPT_CHANGED_EVENT, r as shouldReloadForScriptChange, t as GENERATION_EVENT } from "./contract-CIt1ZAtt.js";
3
3
  import { mulmoBeatSchema, mulmoScriptSchema } from "@mulmocast/types";
4
4
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineAsyncComponent, defineComponent, inject, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, reactive, ref, renderList, toDisplayString, unref, vModelText, vShow, watch, withDirectives, withModifiers } from "vue";
5
5
  import { beatsOf, withBeats } from "@mulmocast/beat-editor";
@@ -209,6 +209,15 @@ var GENERATION_EVENT_KINDS = /* @__PURE__ */ new Set([
209
209
  "movie",
210
210
  "pdf"
211
211
  ]);
212
+ function parseScriptChangedEvent(payload) {
213
+ if (!isRecord(payload)) return null;
214
+ const { filePath, origin } = payload;
215
+ if (typeof filePath !== "string") return null;
216
+ return {
217
+ filePath,
218
+ ...typeof origin === "string" ? { origin } : {}
219
+ };
220
+ }
212
221
  function parseGenerationEvent(payload) {
213
222
  if (!isRecord(payload)) return null;
214
223
  const { kind, filePath, key, done, error } = payload;
@@ -255,9 +264,25 @@ function useMulmoScriptTransport() {
255
264
  handler(event);
256
265
  });
257
266
  }
267
+ /**
268
+ * A write to this script landed — reload from disk.
269
+ *
270
+ * `ownOrigin` is this View's id. Its own writes echo back on the same channel, and acting
271
+ * on them would rebuild the element the caret is in on every keystroke, so they are dropped
272
+ * here. A write from the agent carries no origin and always reaches the handler.
273
+ */
274
+ function onScriptChanged(filePath, ownOrigin, handler) {
275
+ return runtime.pubsub.subscribe(SCRIPT_CHANGED_EVENT, (payload) => {
276
+ const event = parseScriptChangedEvent(payload);
277
+ if (!event) return;
278
+ if (!shouldReloadForScriptChange(event, filePath(), ownOrigin)) return;
279
+ handler();
280
+ });
281
+ }
258
282
  return {
259
283
  call,
260
- onGenerationEvent
284
+ onGenerationEvent,
285
+ onScriptChanged
261
286
  };
262
287
  }
263
288
  //#endregion
@@ -551,6 +576,17 @@ function useCharacterImages({ api, filePath, chatSessionId, getImages }) {
551
576
  //#endregion
552
577
  //#region src/vue/composables/useDeckEditor.ts
553
578
  var DECK_SAVE_DEBOUNCE_MS = 300;
579
+ /**
580
+ * Who this editor is, on the wire.
581
+ *
582
+ * Every write carries it so the server's "this script changed" broadcast can be told apart
583
+ * from someone else's. Without it a save would echo back and reload the editor mid-keystroke,
584
+ * rebuilding the element the caret sits in.
585
+ *
586
+ * Per module instance rather than per component: one View is mounted at a time, and a value
587
+ * that survives a remount keeps a save in flight from being mistaken for a foreign write.
588
+ */
589
+ var EDITOR_ORIGIN = `deck-editor-${Math.random().toString(36).slice(2)}`;
554
590
  function useDeckEditor({ api, filePath, effectiveScript, commitScript }) {
555
591
  const isDeck = computed(() => isAllSlideDeck(effectiveScript.value));
556
592
  const deckScriptInput = computed(() => effectiveScript.value);
@@ -570,7 +606,8 @@ function useDeckEditor({ api, filePath, effectiveScript, commitScript }) {
570
606
  if (!next || !filePath.value) return;
571
607
  const response = await api.call("updateScript", {
572
608
  filePath: filePath.value,
573
- script: next
609
+ script: next,
610
+ origin: EDITOR_ORIGIN
574
611
  });
575
612
  if (!response.ok) {
576
613
  console.error("[presentMulmoScript] deck save failed:", response.error);
@@ -587,11 +624,25 @@ function useDeckEditor({ api, filePath, effectiveScript, commitScript }) {
587
624
  flushDeckSave();
588
625
  }
589
626
  }
627
+ /**
628
+ * Reload when someone else writes this script — the agent, or another window.
629
+ *
630
+ * A pending local edit is flushed first rather than dropped: the user's keystrokes are the
631
+ * thing they would notice losing, and the write that triggered this has already landed, so
632
+ * flushing cannot clobber it out of order.
633
+ */
634
+ function watchForeignWrites(reload) {
635
+ return api.onScriptChanged(() => filePath.value, EDITOR_ORIGIN, () => {
636
+ flushPendingDeckSave();
637
+ reload();
638
+ });
639
+ }
590
640
  return {
591
641
  isDeck,
592
642
  deckScriptInput,
593
643
  onDeckUpdate,
594
- flushPendingDeckSave
644
+ flushPendingDeckSave,
645
+ watchForeignWrites
595
646
  };
596
647
  }
597
648
  /** Reactive message bundle for the active host locale. The plugin carries its
@@ -1534,12 +1585,15 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
1534
1585
  }
1535
1586
  });
1536
1587
  }
1537
- const { isDeck, deckScriptInput, onDeckUpdate, flushPendingDeckSave } = useDeckEditor({
1588
+ const { isDeck, deckScriptInput, onDeckUpdate, flushPendingDeckSave, watchForeignWrites } = useDeckEditor({
1538
1589
  api,
1539
1590
  filePath,
1540
1591
  effectiveScript,
1541
1592
  commitScript
1542
1593
  });
1594
+ const unsubscribeForeignWrites = watchForeignWrites(() => {
1595
+ refreshScriptFromDisk();
1596
+ });
1543
1597
  const deckBeats = computed(() => beatsOf(deckScriptInput.value));
1544
1598
  function onDeckBeatsUpdate(beats) {
1545
1599
  onDeckUpdate(withBeats(deckScriptInput.value, beats));
@@ -1548,6 +1602,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
1548
1602
  flushPendingDeckSave();
1549
1603
  resetBeatMovies();
1550
1604
  unsubscribeGenerationEvents();
1605
+ unsubscribeForeignWrites();
1551
1606
  });
1552
1607
  const loadedSource = ref("");
1553
1608
  const sourceChanged = computed(() => editableSource.value !== loadedSource.value);
@@ -2225,7 +2280,7 @@ var _plugin_vue_export_helper_default = (sfc, props) => {
2225
2280
  };
2226
2281
  //#endregion
2227
2282
  //#region src/vue/View.vue
2228
- var View_default = /*#__PURE__*/ _plugin_vue_export_helper_default(View_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-222045a6"]]);
2283
+ var View_default = /*#__PURE__*/ _plugin_vue_export_helper_default(View_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-87bf6e4c"]]);
2229
2284
  //#endregion
2230
2285
  //#region src/vue/Preview.vue?vue&type=script&setup=true&lang.ts
2231
2286
  var _hoisted_1 = {