@mulmoclaude/mulmoscript-plugin 0.2.3 → 1.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/core/paths.d.ts +2 -5
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/plugin.d.ts.map +1 -1
- package/dist/core/validate.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/lang/index.d.ts +1 -2
- package/dist/lang/index.d.ts.map +1 -1
- package/dist/{plugin-Bi986Wga.cjs → plugin-BLp8tSYt.cjs} +86 -46
- package/dist/plugin-BLp8tSYt.cjs.map +1 -0
- package/dist/{plugin-BQyzxkui.js → plugin-CXhOP_xU.js} +81 -47
- package/dist/plugin-CXhOP_xU.js.map +1 -0
- package/dist/server/mulmoErrorCapture.d.ts.map +1 -1
- package/dist/server/ops.d.ts.map +1 -1
- package/dist/server/support.d.ts +0 -5
- package/dist/server/support.d.ts.map +1 -1
- package/dist/server/types.d.ts +3 -7
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server.cjs +34 -58
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +33 -57
- package/dist/server.js.map +1 -1
- package/dist/style.css +36 -18
- package/dist/vue/View.vue.d.ts.map +1 -1
- package/dist/vue/components/BeatLightbox.vue.d.ts +25 -0
- package/dist/vue/components/BeatLightbox.vue.d.ts.map +1 -0
- package/dist/vue/components/CharacterStrip.vue.d.ts +29 -0
- package/dist/vue/components/CharacterStrip.vue.d.ts.map +1 -0
- package/dist/vue/components/MulmoScriptToolbar.vue.d.ts +26 -0
- package/dist/vue/components/MulmoScriptToolbar.vue.d.ts.map +1 -0
- package/dist/vue/composables/useBeatMovie.d.ts +20 -0
- package/dist/vue/composables/useBeatMovie.d.ts.map +1 -0
- package/dist/vue/composables/useCharacterImages.d.ts +31 -0
- package/dist/vue/composables/useCharacterImages.d.ts.map +1 -0
- package/dist/vue/composables/useDeckEditor.d.ts +18 -0
- package/dist/vue/composables/useDeckEditor.d.ts.map +1 -0
- package/dist/vue/composables/useMediaExport.d.ts +26 -0
- package/dist/vue/composables/useMediaExport.d.ts.map +1 -0
- package/dist/vue/helpers.d.ts +78 -0
- package/dist/vue/helpers.d.ts.map +1 -1
- package/dist/vue/index.d.ts +1 -1
- package/dist/vue/support.d.ts +6 -4
- package/dist/vue/support.d.ts.map +1 -1
- package/dist/vue/transport.d.ts.map +1 -1
- package/dist/vue/viewTypes.d.ts +49 -0
- package/dist/vue/viewTypes.d.ts.map +1 -0
- package/dist/vue.cjs +1021 -729
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +1019 -727
- package/dist/vue.js.map +1 -1
- package/package.json +11 -7
- package/dist/plugin-BQyzxkui.js.map +0 -1
- package/dist/plugin-Bi986Wga.cjs.map +0 -1
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-
|
|
5
|
+
const require_plugin = require("./plugin-BLp8tSYt.cjs");
|
|
6
6
|
const require_contract = require("./contract-DxKVCRQk.cjs");
|
|
7
7
|
let _mulmocast_types = require("@mulmocast/types");
|
|
8
8
|
let vue = require("vue");
|
|
@@ -11,18 +11,22 @@ let gui_chat_protocol_vue = require("gui-chat-protocol/vue");
|
|
|
11
11
|
function isRecord(value) {
|
|
12
12
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
/** Read a dropped image File as a base64 data URL, the form the upload
|
|
15
|
+
* dispatches expect. Shared by the beat and character drop handlers.
|
|
16
|
+
* `readAsDataURL` always yields a string on load; the non-string reject
|
|
17
|
+
* is an unreachable guard kept so the resolve type stays `string` without
|
|
18
|
+
* a cast. */
|
|
19
|
+
function readFileAsDataUrl(file) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const reader = new FileReader();
|
|
22
|
+
reader.onload = () => {
|
|
23
|
+
const { result } = reader;
|
|
24
|
+
if (typeof result === "string") resolve(result);
|
|
25
|
+
else reject(/* @__PURE__ */ new Error("FileReader did not return a data URL string"));
|
|
26
|
+
};
|
|
27
|
+
reader.onerror = reject;
|
|
28
|
+
reader.readAsDataURL(file);
|
|
29
|
+
});
|
|
26
30
|
}
|
|
27
31
|
/** Clipboard failures (permissions, insecure context) are swallowed on
|
|
28
32
|
* purpose: the UI just leaves the "Copied!" hint off, which is what
|
|
@@ -104,6 +108,16 @@ function beatMayHaveMovie(beat) {
|
|
|
104
108
|
if (beat.moviePrompt) return true;
|
|
105
109
|
return beat.image?.type === "html_tailwind" && Boolean(beat.image.animation);
|
|
106
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* True for a beat whose image merely REFERENCES another beat's image
|
|
113
|
+
* (`image: { type: "beat", id }` — mulmoBeatReferenceMediaSchema). Such a
|
|
114
|
+
* beat owns no asset of its own, so there is nothing to generate for it:
|
|
115
|
+
* the View hides the Generate button (offering it produced a render that
|
|
116
|
+
* could never succeed on its own terms).
|
|
117
|
+
*/
|
|
118
|
+
function isBeatImageReference(beat) {
|
|
119
|
+
return beat.image?.type === "beat";
|
|
120
|
+
}
|
|
107
121
|
/** Pure check: is every beat in the script a `slide`-typed beat?
|
|
108
122
|
* When true, the View mounts `@mulmocast/deck-web`'s
|
|
109
123
|
* `MulmoScriptDeckEditor` instead of the per-beat list UI (#1575).
|
|
@@ -121,6 +135,74 @@ function isAllSlideDeck(script) {
|
|
|
121
135
|
return isRecord(image) && image.type === "slide";
|
|
122
136
|
});
|
|
123
137
|
}
|
|
138
|
+
/** Resolve the beat the View should render at `index`: the user's
|
|
139
|
+
* in-place edit (`overrides`) wins over the on-disk beat, and an
|
|
140
|
+
* out-of-range index yields an empty beat so callers can read
|
|
141
|
+
* `.text` / `.image` without a guard. */
|
|
142
|
+
function effectiveBeat(overrides, beats, index) {
|
|
143
|
+
return overrides[index] ?? beats[index] ?? {};
|
|
144
|
+
}
|
|
145
|
+
var BEAT_TOOLTIP_MAX_CHARS = 80;
|
|
146
|
+
/** Beat-strip hover tooltip: the beat text, truncated with an ellipsis
|
|
147
|
+
* past the cap. Missing text yields an empty string. Text of exactly
|
|
148
|
+
* the cap length is returned whole (only a longer string is cut). */
|
|
149
|
+
function beatTooltip(text) {
|
|
150
|
+
const value = text ?? "";
|
|
151
|
+
return value.length > BEAT_TOOLTIP_MAX_CHARS ? `${value.slice(0, BEAT_TOOLTIP_MAX_CHARS)}…` : value;
|
|
152
|
+
}
|
|
153
|
+
/** The prompt for a character image, or "" when the key or its prompt
|
|
154
|
+
* is absent — the character strip renders the empty string as no
|
|
155
|
+
* caption rather than `undefined`. */
|
|
156
|
+
function characterPrompt(images, key) {
|
|
157
|
+
return images?.[key]?.prompt ?? "";
|
|
158
|
+
}
|
|
159
|
+
/** Is the in-editor JSON for a beat currently valid? A missing entry
|
|
160
|
+
* (source editor never opened) validates the empty string, which is
|
|
161
|
+
* not parseable JSON, so it reports invalid rather than throwing. */
|
|
162
|
+
function isValidBeat(source, schema) {
|
|
163
|
+
return validateBeatJSON(source ?? "", schema);
|
|
164
|
+
}
|
|
165
|
+
/** Stale-response guard: a per-beat / per-character response is stale
|
|
166
|
+
* once the View has navigated to a different result, i.e. the current
|
|
167
|
+
* file path no longer matches the one captured when the call was made.
|
|
168
|
+
* Keeping the direction pinned matters — an inverted check would let
|
|
169
|
+
* script A's late responses write into script B's state. */
|
|
170
|
+
function staleSince(currentFilePath, requestedFilePath) {
|
|
171
|
+
return currentFilePath !== requestedFilePath;
|
|
172
|
+
}
|
|
173
|
+
var JSON_INDENT = 2;
|
|
174
|
+
/** Pretty-print a script (or any value) as the source-editor / clipboard
|
|
175
|
+
* text — two-space indent, matching what the beat and disk views emit. */
|
|
176
|
+
function scriptSourceText(value) {
|
|
177
|
+
return JSON.stringify(value, null, JSON_INDENT);
|
|
178
|
+
}
|
|
179
|
+
/** Basename for a download `<a download>` attribute, falling back when
|
|
180
|
+
* the path has no basename. Mirrors the exact existing behaviour, and
|
|
181
|
+
* it has a sharp edge: `.pop()` returns "" (not undefined) for a
|
|
182
|
+
* trailing slash or empty path, and `??` does NOT replace "", so those
|
|
183
|
+
* yield an empty filename rather than the fallback. Server paths always
|
|
184
|
+
* carry a basename, so this never bites in practice — pinned so a later
|
|
185
|
+
* reader doesn't "simplify" `??` to `||` and change behaviour. */
|
|
186
|
+
function downloadFilename(path, fallback) {
|
|
187
|
+
return path.split("/").pop() ?? fallback;
|
|
188
|
+
}
|
|
189
|
+
/** Narrow a script-supplied silent-beat duration to a safe positive number.
|
|
190
|
+
* Zero / negative / NaN / Infinity / non-number collapse the auto-advance
|
|
191
|
+
* timer to an immediate fire, which races the Play loop through every silent
|
|
192
|
+
* beat in a single tick (#1365) — fall back to the default so a run of silent
|
|
193
|
+
* beats stays watchable. The script's own valid `duration` always wins. */
|
|
194
|
+
function resolveSilentAdvanceSeconds(raw, defaultSec) {
|
|
195
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : defaultSec;
|
|
196
|
+
}
|
|
197
|
+
/** Delete every own enumerable key of each record, in place. Used to reset the
|
|
198
|
+
* View's per-beat / per-character reactive maps between scripts — passing the
|
|
199
|
+
* reactive proxies mutates them so the template re-renders empty. Replaces a
|
|
200
|
+
* wall of hand-rolled `Object.keys(map).forEach(delete)` loops. */
|
|
201
|
+
function clearReactiveRecords(...records) {
|
|
202
|
+
records.forEach((record) => {
|
|
203
|
+
Object.keys(record).forEach((key) => Reflect.deleteProperty(record, key));
|
|
204
|
+
});
|
|
205
|
+
}
|
|
124
206
|
//#endregion
|
|
125
207
|
//#region src/vue/transport.ts
|
|
126
208
|
var GENERATION_EVENT_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -155,7 +237,7 @@ function useMulmoScriptTransport() {
|
|
|
155
237
|
} catch (err) {
|
|
156
238
|
return {
|
|
157
239
|
ok: false,
|
|
158
|
-
error: errorMessage(err)
|
|
240
|
+
error: require_plugin.errorMessage(err)
|
|
159
241
|
};
|
|
160
242
|
}
|
|
161
243
|
if (!isRecord(result) || result.ok !== true) return {
|
|
@@ -189,8 +271,337 @@ function useHostAdapter() {
|
|
|
189
271
|
return (0, vue.inject)(MULMOSCRIPT_HOST_ADAPTER_KEY, EMPTY_ADAPTER);
|
|
190
272
|
}
|
|
191
273
|
//#endregion
|
|
192
|
-
//#region src/
|
|
193
|
-
|
|
274
|
+
//#region src/vue/composables/useMediaExport.ts
|
|
275
|
+
function useMediaExport({ api, adapter, filePath, chatSessionId }) {
|
|
276
|
+
const movieGenerating = (0, vue.ref)(false);
|
|
277
|
+
const movieDownloading = (0, vue.ref)(false);
|
|
278
|
+
const moviePath = (0, vue.ref)(null);
|
|
279
|
+
const movieError = (0, vue.ref)(null);
|
|
280
|
+
const pdfGenerating = (0, vue.ref)(false);
|
|
281
|
+
const pdfDownloading = (0, vue.ref)(false);
|
|
282
|
+
const pdfPath = (0, vue.ref)(null);
|
|
283
|
+
async function generateMovie() {
|
|
284
|
+
const requestedFilePath = filePath.value;
|
|
285
|
+
movieGenerating.value = true;
|
|
286
|
+
movieError.value = null;
|
|
287
|
+
const response = await api.call("generateMovie", {
|
|
288
|
+
filePath: requestedFilePath,
|
|
289
|
+
chatSessionId: chatSessionId.value
|
|
290
|
+
});
|
|
291
|
+
if (filePath.value !== requestedFilePath) return;
|
|
292
|
+
movieGenerating.value = false;
|
|
293
|
+
if (!response.ok) {
|
|
294
|
+
movieError.value = response.error;
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
moviePath.value = response.data.moviePath;
|
|
298
|
+
}
|
|
299
|
+
async function generatePdf() {
|
|
300
|
+
const requestedFilePath = filePath.value;
|
|
301
|
+
pdfGenerating.value = true;
|
|
302
|
+
const response = await api.call("generatePdf", {
|
|
303
|
+
filePath: requestedFilePath,
|
|
304
|
+
chatSessionId: chatSessionId.value
|
|
305
|
+
});
|
|
306
|
+
if (filePath.value !== requestedFilePath) return;
|
|
307
|
+
pdfGenerating.value = false;
|
|
308
|
+
if (!response.ok) {
|
|
309
|
+
alert(response.error);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
pdfPath.value = response.data.pdfPath;
|
|
313
|
+
}
|
|
314
|
+
async function refreshMoviePath() {
|
|
315
|
+
const requestedFilePath = filePath.value;
|
|
316
|
+
if (!requestedFilePath) return;
|
|
317
|
+
const response = await api.call("movieStatus", { filePath: requestedFilePath });
|
|
318
|
+
if (filePath.value !== requestedFilePath) return;
|
|
319
|
+
if (response.ok && response.data.moviePath) moviePath.value = response.data.moviePath;
|
|
320
|
+
}
|
|
321
|
+
async function refreshPdfPath() {
|
|
322
|
+
const requestedFilePath = filePath.value;
|
|
323
|
+
if (!requestedFilePath) return;
|
|
324
|
+
const response = await api.call("pdfStatus", { filePath: requestedFilePath });
|
|
325
|
+
if (filePath.value !== requestedFilePath) return;
|
|
326
|
+
if (response.ok && response.data.pdfPath) pdfPath.value = response.data.pdfPath;
|
|
327
|
+
}
|
|
328
|
+
async function downloadMedia(kind, sourcePath, fallbackName, downloading) {
|
|
329
|
+
const fetchMediaBlob = adapter.fetchMediaBlob;
|
|
330
|
+
if (!fetchMediaBlob || !sourcePath || downloading.value) return;
|
|
331
|
+
downloading.value = true;
|
|
332
|
+
let objectUrl = null;
|
|
333
|
+
try {
|
|
334
|
+
const blob = await fetchMediaBlob(kind === "movie" ? { moviePath: sourcePath } : { pdfPath: sourcePath });
|
|
335
|
+
objectUrl = URL.createObjectURL(blob);
|
|
336
|
+
clickDownloadAnchor(objectUrl, downloadFilename(sourcePath, fallbackName));
|
|
337
|
+
} catch (err) {
|
|
338
|
+
alert(require_plugin.errorMessage(err));
|
|
339
|
+
} finally {
|
|
340
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
341
|
+
downloading.value = false;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function downloadMovie() {
|
|
345
|
+
return downloadMedia("movie", moviePath.value, "movie.mp4", movieDownloading);
|
|
346
|
+
}
|
|
347
|
+
function downloadPdf() {
|
|
348
|
+
return downloadMedia("pdf", pdfPath.value, "deck.pdf", pdfDownloading);
|
|
349
|
+
}
|
|
350
|
+
function resetMedia() {
|
|
351
|
+
moviePath.value = null;
|
|
352
|
+
pdfPath.value = null;
|
|
353
|
+
movieGenerating.value = false;
|
|
354
|
+
pdfGenerating.value = false;
|
|
355
|
+
movieError.value = null;
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
moviePath,
|
|
359
|
+
movieGenerating,
|
|
360
|
+
movieDownloading,
|
|
361
|
+
movieError,
|
|
362
|
+
pdfPath,
|
|
363
|
+
pdfGenerating,
|
|
364
|
+
pdfDownloading,
|
|
365
|
+
generateMovie,
|
|
366
|
+
downloadMovie,
|
|
367
|
+
refreshMoviePath,
|
|
368
|
+
generatePdf,
|
|
369
|
+
downloadPdf,
|
|
370
|
+
refreshPdfPath,
|
|
371
|
+
resetMedia
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function clickDownloadAnchor(href, filename) {
|
|
375
|
+
const anchor = document.createElement("a");
|
|
376
|
+
anchor.href = href;
|
|
377
|
+
anchor.download = filename;
|
|
378
|
+
document.body.appendChild(anchor);
|
|
379
|
+
anchor.click();
|
|
380
|
+
anchor.remove();
|
|
381
|
+
}
|
|
382
|
+
//#endregion
|
|
383
|
+
//#region src/vue/composables/useBeatMovie.ts
|
|
384
|
+
function useBeatMovie({ api, adapter, filePath }) {
|
|
385
|
+
const beatMovies = (0, vue.reactive)({});
|
|
386
|
+
const beatMovieUrls = (0, vue.reactive)({});
|
|
387
|
+
const beatMovieOpen = (0, vue.reactive)({});
|
|
388
|
+
const beatMovieLoading = (0, vue.reactive)({});
|
|
389
|
+
const staleSince$3 = (requestedFilePath) => staleSince(filePath.value, requestedFilePath);
|
|
390
|
+
async function loadExistingBeatMovie(index) {
|
|
391
|
+
const requestedFilePath = filePath.value;
|
|
392
|
+
const response = await api.call("beatMovie", {
|
|
393
|
+
filePath: requestedFilePath,
|
|
394
|
+
beatIndex: index
|
|
395
|
+
});
|
|
396
|
+
if (staleSince$3(requestedFilePath)) return;
|
|
397
|
+
if (response.ok && response.data.moviePath) beatMovies[index] = response.data.moviePath;
|
|
398
|
+
}
|
|
399
|
+
async function playBeatMovie(index) {
|
|
400
|
+
const fetchMediaBlob = adapter.fetchMediaBlob;
|
|
401
|
+
if (!fetchMediaBlob || !beatMovies[index] || beatMovieLoading[index]) return;
|
|
402
|
+
if (beatMovieUrls[index]) {
|
|
403
|
+
beatMovieOpen[index] = true;
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
beatMovieLoading[index] = true;
|
|
407
|
+
try {
|
|
408
|
+
const blob = new Blob([await fetchMediaBlob({ moviePath: beatMovies[index] })], { type: "video/mp4" });
|
|
409
|
+
beatMovieUrls[index] = URL.createObjectURL(blob);
|
|
410
|
+
beatMovieOpen[index] = true;
|
|
411
|
+
} catch (err) {
|
|
412
|
+
alert(require_plugin.errorMessage(err));
|
|
413
|
+
} finally {
|
|
414
|
+
Reflect.deleteProperty(beatMovieLoading, index);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function closeBeatMovie(index) {
|
|
418
|
+
Reflect.deleteProperty(beatMovieOpen, index);
|
|
419
|
+
}
|
|
420
|
+
function invalidateBeatMovie(index) {
|
|
421
|
+
if (beatMovieUrls[index]) URL.revokeObjectURL(beatMovieUrls[index]);
|
|
422
|
+
[
|
|
423
|
+
beatMovies,
|
|
424
|
+
beatMovieUrls,
|
|
425
|
+
beatMovieOpen
|
|
426
|
+
].forEach((map) => Reflect.deleteProperty(map, index));
|
|
427
|
+
}
|
|
428
|
+
function resetBeatMovies() {
|
|
429
|
+
Object.values(beatMovieUrls).forEach((url) => URL.revokeObjectURL(url));
|
|
430
|
+
clearReactiveRecords(beatMovies, beatMovieUrls, beatMovieOpen, beatMovieLoading);
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
beatMovies,
|
|
434
|
+
beatMovieUrls,
|
|
435
|
+
beatMovieOpen,
|
|
436
|
+
beatMovieLoading,
|
|
437
|
+
loadExistingBeatMovie,
|
|
438
|
+
playBeatMovie,
|
|
439
|
+
closeBeatMovie,
|
|
440
|
+
invalidateBeatMovie,
|
|
441
|
+
resetBeatMovies
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region src/vue/composables/useCharacterImages.ts
|
|
446
|
+
function useCharacterImages({ api, filePath, chatSessionId, getImages }) {
|
|
447
|
+
const charRenderState = (0, vue.reactive)({});
|
|
448
|
+
const charImages = (0, vue.reactive)({});
|
|
449
|
+
const charErrors = (0, vue.reactive)({});
|
|
450
|
+
const charDragOver = (0, vue.reactive)({});
|
|
451
|
+
const staleSince$2 = (requestedFilePath) => staleSince(filePath.value, requestedFilePath);
|
|
452
|
+
const characterKeys = (0, vue.computed)(() => {
|
|
453
|
+
const imgs = getImages() ?? {};
|
|
454
|
+
return Object.keys(imgs).filter((key) => imgs[key]?.type === "imagePrompt");
|
|
455
|
+
});
|
|
456
|
+
function characterPrompt$1(key) {
|
|
457
|
+
return characterPrompt(getImages(), key);
|
|
458
|
+
}
|
|
459
|
+
function onCharDragOver(event, key) {
|
|
460
|
+
if (!event.dataTransfer?.types.includes("Files")) return;
|
|
461
|
+
event.preventDefault();
|
|
462
|
+
charDragOver[key] = true;
|
|
463
|
+
}
|
|
464
|
+
function onCharDragLeave(key) {
|
|
465
|
+
charDragOver[key] = false;
|
|
466
|
+
}
|
|
467
|
+
async function onCharDrop(event, key) {
|
|
468
|
+
event.preventDefault();
|
|
469
|
+
charDragOver[key] = false;
|
|
470
|
+
const file = event.dataTransfer?.files[0];
|
|
471
|
+
if (!file || !file.type.startsWith("image/")) return;
|
|
472
|
+
charRenderState[key] = "rendering";
|
|
473
|
+
Reflect.deleteProperty(charErrors, key);
|
|
474
|
+
let imageData;
|
|
475
|
+
try {
|
|
476
|
+
imageData = await readFileAsDataUrl(file);
|
|
477
|
+
} catch (err) {
|
|
478
|
+
charErrors[key] = require_plugin.errorMessage(err);
|
|
479
|
+
charRenderState[key] = "error";
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const requestedFilePath = filePath.value;
|
|
483
|
+
const response = await api.call("uploadCharacterImage", {
|
|
484
|
+
filePath: requestedFilePath,
|
|
485
|
+
key,
|
|
486
|
+
imageData
|
|
487
|
+
});
|
|
488
|
+
if (staleSince$2(requestedFilePath)) return;
|
|
489
|
+
if (!response.ok) {
|
|
490
|
+
charErrors[key] = response.error || "Upload failed";
|
|
491
|
+
charRenderState[key] = "error";
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
charImages[key] = response.data.image ?? "";
|
|
495
|
+
charRenderState[key] = "done";
|
|
496
|
+
}
|
|
497
|
+
async function loadExistingCharacterImage(key) {
|
|
498
|
+
const requestedFilePath = filePath.value;
|
|
499
|
+
const response = await api.call("characterImage", {
|
|
500
|
+
filePath: requestedFilePath,
|
|
501
|
+
key
|
|
502
|
+
});
|
|
503
|
+
if (staleSince$2(requestedFilePath)) return;
|
|
504
|
+
if (response.ok && response.data.image) {
|
|
505
|
+
charImages[key] = response.data.image;
|
|
506
|
+
charRenderState[key] = "done";
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function refreshMissingCharacterImages() {
|
|
510
|
+
getMissingCharacterKeys(characterKeys.value, charImages, charRenderState).forEach((key) => loadExistingCharacterImage(key));
|
|
511
|
+
}
|
|
512
|
+
async function renderCharacter(key, force) {
|
|
513
|
+
const requestedFilePath = filePath.value;
|
|
514
|
+
charRenderState[key] = "rendering";
|
|
515
|
+
Reflect.deleteProperty(charErrors, key);
|
|
516
|
+
const response = await api.call("renderCharacter", {
|
|
517
|
+
filePath: requestedFilePath,
|
|
518
|
+
key,
|
|
519
|
+
force,
|
|
520
|
+
chatSessionId: chatSessionId.value
|
|
521
|
+
});
|
|
522
|
+
if (staleSince$2(requestedFilePath)) return;
|
|
523
|
+
if (!response.ok) {
|
|
524
|
+
charErrors[key] = response.error || "Render failed";
|
|
525
|
+
charRenderState[key] = "error";
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
charImages[key] = response.data.image ?? "";
|
|
529
|
+
charRenderState[key] = "done";
|
|
530
|
+
}
|
|
531
|
+
async function generateAllCharacters() {
|
|
532
|
+
await Promise.all(characterKeys.value.filter((key) => charRenderState[key] !== "rendering").map((key) => renderCharacter(key, false)));
|
|
533
|
+
}
|
|
534
|
+
function resetCharacters() {
|
|
535
|
+
clearReactiveRecords(charRenderState, charImages, charErrors, charDragOver);
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
charRenderState,
|
|
539
|
+
charImages,
|
|
540
|
+
charErrors,
|
|
541
|
+
charDragOver,
|
|
542
|
+
characterKeys,
|
|
543
|
+
characterPrompt: characterPrompt$1,
|
|
544
|
+
onCharDragOver,
|
|
545
|
+
onCharDragLeave,
|
|
546
|
+
onCharDrop,
|
|
547
|
+
loadExistingCharacterImage,
|
|
548
|
+
refreshMissingCharacterImages,
|
|
549
|
+
renderCharacter,
|
|
550
|
+
generateAllCharacters,
|
|
551
|
+
resetCharacters
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
//#endregion
|
|
555
|
+
//#region src/vue/composables/useDeckEditor.ts
|
|
556
|
+
var DECK_SAVE_DEBOUNCE_MS = 300;
|
|
557
|
+
function useDeckEditor({ api, filePath, effectiveScript, commitScript }) {
|
|
558
|
+
const isDeck = (0, vue.computed)(() => isAllSlideDeck(effectiveScript.value));
|
|
559
|
+
const deckScriptInput = (0, vue.computed)(() => effectiveScript.value);
|
|
560
|
+
let deckSaveTimer = null;
|
|
561
|
+
let pendingDeckScript = null;
|
|
562
|
+
function scheduleDeckSave(next) {
|
|
563
|
+
pendingDeckScript = next;
|
|
564
|
+
if (deckSaveTimer) clearTimeout(deckSaveTimer);
|
|
565
|
+
deckSaveTimer = setTimeout(() => {
|
|
566
|
+
flushDeckSave();
|
|
567
|
+
}, DECK_SAVE_DEBOUNCE_MS);
|
|
568
|
+
}
|
|
569
|
+
async function flushDeckSave() {
|
|
570
|
+
deckSaveTimer = null;
|
|
571
|
+
const next = pendingDeckScript;
|
|
572
|
+
pendingDeckScript = null;
|
|
573
|
+
if (!next || !filePath.value) return;
|
|
574
|
+
const response = await api.call("updateScript", {
|
|
575
|
+
filePath: filePath.value,
|
|
576
|
+
script: next
|
|
577
|
+
});
|
|
578
|
+
if (!response.ok) {
|
|
579
|
+
console.error("[presentMulmoScript] deck save failed:", response.error);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
commitScript(next);
|
|
583
|
+
}
|
|
584
|
+
function onDeckUpdate(next) {
|
|
585
|
+
scheduleDeckSave(next);
|
|
586
|
+
}
|
|
587
|
+
function flushPendingDeckSave() {
|
|
588
|
+
if (deckSaveTimer) {
|
|
589
|
+
clearTimeout(deckSaveTimer);
|
|
590
|
+
flushDeckSave();
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return {
|
|
594
|
+
isDeck,
|
|
595
|
+
deckScriptInput,
|
|
596
|
+
onDeckUpdate,
|
|
597
|
+
flushPendingDeckSave
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
/** Reactive message bundle for the active host locale. The plugin carries its
|
|
601
|
+
* own translations (no host i18n dependency); it reads the locale off the
|
|
602
|
+
* injected `BrowserPluginRuntime.locale` ref and falls back to English.
|
|
603
|
+
* Same pattern as @mulmoclaude/html-plugin. */
|
|
604
|
+
var useT = (0, gui_chat_protocol_vue.createUseT)({
|
|
194
605
|
de: {
|
|
195
606
|
beatCount: (count) => count === 1 ? `${count} Beat` : `${count} Beats`,
|
|
196
607
|
movie: "Video",
|
|
@@ -447,267 +858,519 @@ var MESSAGES = {
|
|
|
447
858
|
close: "关闭",
|
|
448
859
|
cancel: "取消"
|
|
449
860
|
}
|
|
450
|
-
};
|
|
451
|
-
function isSupportedLocale(value) {
|
|
452
|
-
return Object.hasOwn(MESSAGES, value);
|
|
453
|
-
}
|
|
454
|
-
/** Reactive message bundle for the active host locale. The plugin carries its
|
|
455
|
-
* own translations (no host i18n dependency); it reads the locale off the
|
|
456
|
-
* injected `BrowserPluginRuntime.locale` ref and falls back to English.
|
|
457
|
-
* Same pattern as @mulmoclaude/html-plugin. */
|
|
458
|
-
function useT() {
|
|
459
|
-
const locale = (0, vue.inject)(gui_chat_protocol_vue.PLUGIN_RUNTIME_KEY, void 0)?.locale ?? (0, vue.ref)("en");
|
|
460
|
-
return (0, vue.computed)(() => isSupportedLocale(locale.value) ? MESSAGES[locale.value] : MESSAGES.en);
|
|
461
|
-
}
|
|
861
|
+
});
|
|
462
862
|
//#endregion
|
|
463
|
-
//#region src/vue/
|
|
464
|
-
var _hoisted_1$
|
|
465
|
-
var _hoisted_2$
|
|
466
|
-
var _hoisted_3$
|
|
467
|
-
var _hoisted_4 = {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
};
|
|
471
|
-
var _hoisted_5 = {
|
|
863
|
+
//#region src/vue/components/BeatLightbox.vue?vue&type=script&setup=true&lang.ts
|
|
864
|
+
var _hoisted_1$4 = ["title"];
|
|
865
|
+
var _hoisted_2$4 = { class: "flex items-center gap-4" };
|
|
866
|
+
var _hoisted_3$4 = ["disabled"];
|
|
867
|
+
var _hoisted_4$3 = { class: "flex flex-col items-center" };
|
|
868
|
+
var _hoisted_5$3 = ["src"];
|
|
869
|
+
var _hoisted_6$3 = {
|
|
472
870
|
key: 0,
|
|
473
|
-
class: "
|
|
474
|
-
"data-testid": "mulmo-script-description"
|
|
475
|
-
};
|
|
476
|
-
var _hoisted_6 = { class: "flex items-center gap-3 mt-1 text-xs text-gray-400" };
|
|
477
|
-
var _hoisted_7 = { key: 0 };
|
|
478
|
-
var _hoisted_8 = {
|
|
479
|
-
key: 1,
|
|
480
|
-
class: "truncate"
|
|
871
|
+
class: "relative w-full h-1"
|
|
481
872
|
};
|
|
482
|
-
var
|
|
483
|
-
var
|
|
484
|
-
|
|
485
|
-
"title",
|
|
486
|
-
"aria-label"
|
|
487
|
-
];
|
|
488
|
-
var _hoisted_11 = ["disabled"];
|
|
489
|
-
var _hoisted_12 = ["title", "aria-label"];
|
|
490
|
-
var _hoisted_13 = ["disabled"];
|
|
491
|
-
var _hoisted_14 = {
|
|
873
|
+
var _hoisted_7$3 = { class: "flex gap-1 h-full" };
|
|
874
|
+
var _hoisted_8$3 = ["onClick"];
|
|
875
|
+
var _hoisted_9$3 = {
|
|
492
876
|
key: 0,
|
|
493
|
-
class: "
|
|
494
|
-
viewBox: "0 0 24 24",
|
|
495
|
-
fill: "none"
|
|
877
|
+
class: "absolute bottom-full mb-2 left-1/2 -translate-x-1/2 z-20 px-2 py-1 rounded bg-black/90 text-white text-xs leading-tight w-48 max-h-[53px] overflow-hidden opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity"
|
|
496
878
|
};
|
|
497
|
-
var
|
|
498
|
-
var
|
|
499
|
-
var _hoisted_17 = ["title", "aria-label"];
|
|
500
|
-
var _hoisted_18 = ["disabled"];
|
|
501
|
-
var _hoisted_19 = {
|
|
879
|
+
var _hoisted_10$3 = ["disabled"];
|
|
880
|
+
var _hoisted_11$3 = {
|
|
502
881
|
key: 0,
|
|
503
|
-
class: "
|
|
504
|
-
viewBox: "0 0 24 24",
|
|
505
|
-
fill: "none"
|
|
882
|
+
class: "relative w-screen flex justify-center px-16"
|
|
506
883
|
};
|
|
507
|
-
var
|
|
508
|
-
var _hoisted_21 = {
|
|
884
|
+
var _hoisted_12$3 = {
|
|
509
885
|
key: 0,
|
|
510
|
-
|
|
511
|
-
class: "bg-red-50 border border-red-200 text-red-800 text-xs px-3 py-2 mx-4 mt-3 mb-1 rounded flex items-start gap-2"
|
|
512
|
-
};
|
|
513
|
-
var _hoisted_22 = { class: "flex-1 min-w-0" };
|
|
514
|
-
var _hoisted_23 = { class: "font-medium" };
|
|
515
|
-
var _hoisted_24 = { class: "break-words whitespace-pre-wrap mt-0.5" };
|
|
516
|
-
var _hoisted_25 = ["disabled"];
|
|
517
|
-
var _hoisted_26 = {
|
|
518
|
-
key: 1,
|
|
519
|
-
class: "border-b border-gray-100 shrink-0 px-4 py-3"
|
|
886
|
+
class: "max-w-[80vw] text-center text-white leading-relaxed text-[clamp(0.8rem,1.76vw,1.6rem)]"
|
|
520
887
|
};
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
var
|
|
524
|
-
|
|
525
|
-
|
|
888
|
+
//#endregion
|
|
889
|
+
//#region src/vue/components/BeatLightbox.vue
|
|
890
|
+
var BeatLightbox_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
891
|
+
__name: "BeatLightbox",
|
|
892
|
+
props: {
|
|
893
|
+
lightbox: {},
|
|
894
|
+
beatCount: {},
|
|
895
|
+
beatTexts: {},
|
|
896
|
+
hasPrev: { type: Boolean },
|
|
897
|
+
hasNext: { type: Boolean },
|
|
898
|
+
playingAudioIndex: {},
|
|
899
|
+
audioProgress: {},
|
|
900
|
+
hasCurrentAudio: { type: Boolean }
|
|
901
|
+
},
|
|
902
|
+
emits: [
|
|
903
|
+
"close",
|
|
904
|
+
"move",
|
|
905
|
+
"jump",
|
|
906
|
+
"playAudio"
|
|
907
|
+
],
|
|
908
|
+
setup(__props, { emit: __emit }) {
|
|
909
|
+
const m = useT();
|
|
910
|
+
const emit = __emit;
|
|
911
|
+
return (_ctx, _cache) => {
|
|
912
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
913
|
+
class: "fixed inset-0 z-50 bg-black/80 overflow-y-auto",
|
|
914
|
+
onClick: _cache[5] || (_cache[5] = ($event) => emit("close"))
|
|
915
|
+
}, [(0, vue.createElementVNode)("button", {
|
|
916
|
+
class: "fixed top-2 right-4 z-10 text-white/60 hover:text-white text-3xl leading-none",
|
|
917
|
+
title: (0, vue.unref)(m).close,
|
|
918
|
+
onClick: _cache[0] || (_cache[0] = (0, vue.withModifiers)(($event) => emit("close"), ["stop"]))
|
|
919
|
+
}, "✕", 8, _hoisted_1$4), (0, vue.createElementVNode)("div", {
|
|
920
|
+
class: "flex flex-col items-center gap-4 pt-4 pb-8",
|
|
921
|
+
onClick: _cache[4] || (_cache[4] = (0, vue.withModifiers)(() => {}, ["stop"]))
|
|
922
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_2$4, [
|
|
923
|
+
!__props.lightbox.isCharacter ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
924
|
+
key: 0,
|
|
925
|
+
class: "text-white/60 hover:text-white disabled:opacity-20 text-5xl leading-none",
|
|
926
|
+
disabled: !__props.hasPrev,
|
|
927
|
+
onClick: _cache[1] || (_cache[1] = ($event) => emit("move", -1))
|
|
928
|
+
}, " ‹ ", 8, _hoisted_3$4)) : (0, vue.createCommentVNode)("", true),
|
|
929
|
+
(0, vue.createElementVNode)("div", _hoisted_4$3, [(0, vue.createElementVNode)("img", {
|
|
930
|
+
src: __props.lightbox.src,
|
|
931
|
+
class: "max-w-[80vw] max-h-[85vh] object-contain rounded shadow-2xl"
|
|
932
|
+
}, null, 8, _hoisted_5$3), !__props.lightbox.isCharacter && __props.beatCount > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6$3, [(0, vue.createElementVNode)("div", _hoisted_7$3, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.beatCount, (i) => {
|
|
933
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
934
|
+
key: i - 1,
|
|
935
|
+
class: (0, vue.normalizeClass)(["group flex-1 cursor-pointer relative transition-colors", i - 1 === __props.lightbox.index ? "bg-white/80 hover:bg-white" : i - 1 < __props.lightbox.index ? "bg-white/40 hover:bg-white/60" : "bg-white/20 hover:bg-white/40"]),
|
|
936
|
+
onClick: ($event) => emit("jump", i - 1)
|
|
937
|
+
}, [_cache[6] || (_cache[6] = (0, vue.createElementVNode)("span", { class: "absolute -inset-y-3 inset-x-0" }, null, -1)), (0, vue.unref)(beatTooltip)(__props.beatTexts[i - 1]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_9$3, (0, vue.toDisplayString)((0, vue.unref)(beatTooltip)(__props.beatTexts[i - 1])), 1)) : (0, vue.createCommentVNode)("", true)], 10, _hoisted_8$3);
|
|
938
|
+
}), 128))]), __props.playingAudioIndex !== null && __props.playingAudioIndex === __props.lightbox.index ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
939
|
+
key: 0,
|
|
940
|
+
class: "absolute top-1/2 w-3.5 h-3.5 rounded-full bg-white shadow ring-2 ring-black/30 -translate-y-1/2 -translate-x-1/2 pointer-events-none",
|
|
941
|
+
style: (0, vue.normalizeStyle)({ left: `${(__props.lightbox.index + __props.audioProgress) / __props.beatCount * 100}%` })
|
|
942
|
+
}, null, 4)) : (0, vue.createCommentVNode)("", true)])) : (0, vue.createCommentVNode)("", true)]),
|
|
943
|
+
!__props.lightbox.isCharacter ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
944
|
+
key: 1,
|
|
945
|
+
class: "text-white/60 hover:text-white disabled:opacity-20 text-5xl leading-none",
|
|
946
|
+
disabled: !__props.hasNext,
|
|
947
|
+
onClick: _cache[2] || (_cache[2] = ($event) => emit("move", 1))
|
|
948
|
+
}, " › ", 8, _hoisted_10$3)) : (0, vue.createCommentVNode)("", true)
|
|
949
|
+
]), __props.lightbox.text || __props.hasCurrentAudio ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_11$3, [__props.lightbox.text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_12$3, (0, vue.toDisplayString)(__props.lightbox.text), 1)) : (0, vue.createCommentVNode)("", true), __props.hasCurrentAudio ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
950
|
+
key: 1,
|
|
951
|
+
class: "absolute top-0 right-4 text-sm px-3 py-1 rounded border border-white/60 text-white/60 hover:bg-white/20",
|
|
952
|
+
onClick: _cache[3] || (_cache[3] = ($event) => emit("playAudio", __props.lightbox.index))
|
|
953
|
+
}, (0, vue.toDisplayString)(__props.playingAudioIndex === __props.lightbox.index ? (0, vue.unref)(m).stop : (0, vue.unref)(m).play), 1)) : (0, vue.createCommentVNode)("", true)])) : (0, vue.createCommentVNode)("", true)])]);
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
//#endregion
|
|
958
|
+
//#region src/vue/components/CharacterStrip.vue?vue&type=script&setup=true&lang.ts
|
|
959
|
+
var _hoisted_1$3 = { class: "border-b border-gray-100 shrink-0 px-4 py-3" };
|
|
960
|
+
var _hoisted_2$3 = { class: "flex items-center justify-between mb-2" };
|
|
961
|
+
var _hoisted_3$3 = { class: "text-xs font-semibold text-gray-500 uppercase tracking-wide" };
|
|
962
|
+
var _hoisted_4$2 = ["disabled"];
|
|
963
|
+
var _hoisted_5$2 = { class: "flex gap-3 flex-wrap" };
|
|
964
|
+
var _hoisted_6$2 = [
|
|
526
965
|
"onDragover",
|
|
527
966
|
"onDragleave",
|
|
528
967
|
"onDrop"
|
|
529
968
|
];
|
|
530
|
-
var
|
|
969
|
+
var _hoisted_7$2 = [
|
|
531
970
|
"src",
|
|
532
971
|
"alt",
|
|
533
972
|
"onClick"
|
|
534
973
|
];
|
|
535
|
-
var
|
|
974
|
+
var _hoisted_8$2 = {
|
|
536
975
|
key: 1,
|
|
537
976
|
class: "animate-spin w-4 h-4 text-green-400",
|
|
538
977
|
viewBox: "0 0 24 24",
|
|
539
978
|
fill: "none"
|
|
540
979
|
};
|
|
541
|
-
var
|
|
980
|
+
var _hoisted_9$2 = {
|
|
542
981
|
key: 2,
|
|
543
982
|
class: "text-xs text-red-400 text-center px-1"
|
|
544
983
|
};
|
|
545
|
-
var
|
|
984
|
+
var _hoisted_10$2 = {
|
|
546
985
|
key: 3,
|
|
547
986
|
class: "text-xs text-gray-300 text-center px-1 leading-tight"
|
|
548
987
|
};
|
|
549
|
-
var
|
|
988
|
+
var _hoisted_11$2 = {
|
|
550
989
|
key: 4,
|
|
551
990
|
class: "absolute bottom-0 inset-x-0 text-center text-xs text-gray-400 bg-white/70 py-0.5 pointer-events-none"
|
|
552
991
|
};
|
|
553
|
-
var
|
|
992
|
+
var _hoisted_12$2 = {
|
|
554
993
|
key: 5,
|
|
555
994
|
class: "absolute inset-0 flex items-center justify-center bg-blue-50/80 pointer-events-none"
|
|
556
995
|
};
|
|
557
|
-
var
|
|
558
|
-
var
|
|
559
|
-
var
|
|
996
|
+
var _hoisted_13$1 = { class: "text-xs text-blue-500 font-medium" };
|
|
997
|
+
var _hoisted_14$1 = ["disabled", "onClick"];
|
|
998
|
+
var _hoisted_15$1 = {
|
|
560
999
|
key: 0,
|
|
561
1000
|
class: "inline-block animate-spin"
|
|
562
1001
|
};
|
|
563
|
-
var
|
|
564
|
-
var
|
|
565
|
-
var
|
|
1002
|
+
var _hoisted_16$1 = { key: 1 };
|
|
1003
|
+
var _hoisted_17$1 = ["disabled", "onClick"];
|
|
1004
|
+
var _hoisted_18$1 = {
|
|
566
1005
|
key: 0,
|
|
567
1006
|
class: "animate-spin w-3 h-3",
|
|
568
1007
|
viewBox: "0 0 24 24",
|
|
569
1008
|
fill: "none"
|
|
570
1009
|
};
|
|
571
|
-
var
|
|
572
|
-
var
|
|
573
|
-
|
|
1010
|
+
var _hoisted_19$1 = { key: 1 };
|
|
1011
|
+
var _hoisted_20$1 = { class: "text-xs text-gray-600 text-center truncate w-full" };
|
|
1012
|
+
//#endregion
|
|
1013
|
+
//#region src/vue/components/CharacterStrip.vue
|
|
1014
|
+
var CharacterStrip_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
1015
|
+
__name: "CharacterStrip",
|
|
1016
|
+
props: {
|
|
1017
|
+
characterKeys: {},
|
|
1018
|
+
images: {},
|
|
1019
|
+
thumbnails: {},
|
|
1020
|
+
renderState: {},
|
|
1021
|
+
errors: {},
|
|
1022
|
+
dragOver: {},
|
|
1023
|
+
movieGenerating: { type: Boolean },
|
|
1024
|
+
anyBeatRendering: { type: Boolean }
|
|
1025
|
+
},
|
|
1026
|
+
emits: [
|
|
1027
|
+
"generateAll",
|
|
1028
|
+
"charDragOver",
|
|
1029
|
+
"charDragLeave",
|
|
1030
|
+
"charDrop",
|
|
1031
|
+
"openLightbox",
|
|
1032
|
+
"renderCharacter"
|
|
1033
|
+
],
|
|
1034
|
+
setup(__props, { emit: __emit }) {
|
|
1035
|
+
const m = useT();
|
|
1036
|
+
const props = __props;
|
|
1037
|
+
const emit = __emit;
|
|
1038
|
+
const busy = (0, vue.computed)(() => props.movieGenerating || props.anyBeatRendering);
|
|
1039
|
+
return (_ctx, _cache) => {
|
|
1040
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$3, [(0, vue.createElementVNode)("div", _hoisted_2$3, [(0, vue.createElementVNode)("span", _hoisted_3$3, (0, vue.toDisplayString)((0, vue.unref)(m).characters), 1), (0, vue.createElementVNode)("button", {
|
|
1041
|
+
class: "px-2 py-0.5 text-xs rounded border border-gray-300 text-gray-500 hover:bg-gray-50 disabled:opacity-50",
|
|
1042
|
+
disabled: busy.value || __props.characterKeys.every((key) => __props.renderState[key] === "rendering"),
|
|
1043
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("generateAll"))
|
|
1044
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(m).generateAll), 9, _hoisted_4$2)]), (0, vue.createElementVNode)("div", _hoisted_5$2, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.characterKeys, (key) => {
|
|
1045
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1046
|
+
key,
|
|
1047
|
+
class: "flex flex-col items-center gap-1 w-36"
|
|
1048
|
+
}, [(0, vue.createElementVNode)("div", {
|
|
1049
|
+
class: (0, vue.normalizeClass)(["relative w-36 h-36 rounded-lg border overflow-hidden bg-gray-50 flex items-center justify-center transition-colors", __props.dragOver[key] ? "border-blue-400 bg-blue-50" : "border-gray-200"]),
|
|
1050
|
+
onDragover: ($event) => emit("charDragOver", $event, key),
|
|
1051
|
+
onDragleave: ($event) => emit("charDragLeave", key),
|
|
1052
|
+
onDrop: ($event) => emit("charDrop", $event, key)
|
|
1053
|
+
}, [
|
|
1054
|
+
__props.thumbnails[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
|
|
1055
|
+
key: 0,
|
|
1056
|
+
src: __props.thumbnails[key],
|
|
1057
|
+
class: "w-full h-full object-cover cursor-zoom-in",
|
|
1058
|
+
alt: key,
|
|
1059
|
+
onClick: ($event) => emit("openLightbox", key)
|
|
1060
|
+
}, null, 8, _hoisted_7$2)) : __props.renderState[key] === "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_8$2, [..._cache[1] || (_cache[1] = [(0, vue.createElementVNode)("circle", {
|
|
1061
|
+
class: "opacity-25",
|
|
1062
|
+
cx: "12",
|
|
1063
|
+
cy: "12",
|
|
1064
|
+
r: "10",
|
|
1065
|
+
stroke: "currentColor",
|
|
1066
|
+
"stroke-width": "4"
|
|
1067
|
+
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1068
|
+
class: "opacity-75",
|
|
1069
|
+
fill: "currentColor",
|
|
1070
|
+
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1071
|
+
}, null, -1)])])) : __props.renderState[key] === "error" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_9$2, (0, vue.toDisplayString)(__props.errors[key]), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_10$2, (0, vue.toDisplayString)((0, vue.unref)(characterPrompt)(__props.images, key)), 1)),
|
|
1072
|
+
!__props.dragOver[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_11$2, (0, vue.toDisplayString)((0, vue.unref)(m).orDropImage), 1)) : (0, vue.createCommentVNode)("", true),
|
|
1073
|
+
__props.dragOver[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12$2, [(0, vue.createElementVNode)("span", _hoisted_13$1, (0, vue.toDisplayString)((0, vue.unref)(m).drop), 1)])) : (0, vue.createCommentVNode)("", true),
|
|
1074
|
+
__props.thumbnails[key] && __props.renderState[key] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1075
|
+
key: 6,
|
|
1076
|
+
class: (0, vue.normalizeClass)(["absolute top-0.5 right-0.5 px-1 py-0.5 text-xs rounded border bg-white", busy.value ? "border-yellow-400 text-yellow-500 cursor-not-allowed" : "border-gray-400 text-gray-600 hover:bg-gray-50"]),
|
|
1077
|
+
disabled: busy.value,
|
|
1078
|
+
onClick: (0, vue.withModifiers)(($event) => emit("renderCharacter", key, true), ["stop"])
|
|
1079
|
+
}, [busy.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_15$1, "↺")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_16$1, "↺"))], 10, _hoisted_14$1)) : !__props.thumbnails[key] && __props.renderState[key] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1080
|
+
key: 7,
|
|
1081
|
+
class: (0, vue.normalizeClass)(["absolute top-0.5 right-0.5 px-1 py-0.5 text-xs rounded border bg-white", busy.value ? "border-yellow-400 text-yellow-500 cursor-not-allowed" : "border-blue-400 text-blue-600 hover:bg-blue-50"]),
|
|
1082
|
+
disabled: busy.value,
|
|
1083
|
+
onClick: (0, vue.withModifiers)(($event) => emit("renderCharacter", key, false), ["stop"])
|
|
1084
|
+
}, [busy.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_18$1, [..._cache[2] || (_cache[2] = [(0, vue.createElementVNode)("circle", {
|
|
1085
|
+
class: "opacity-25",
|
|
1086
|
+
cx: "12",
|
|
1087
|
+
cy: "12",
|
|
1088
|
+
r: "10",
|
|
1089
|
+
stroke: "currentColor",
|
|
1090
|
+
"stroke-width": "4"
|
|
1091
|
+
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1092
|
+
class: "opacity-75",
|
|
1093
|
+
fill: "currentColor",
|
|
1094
|
+
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1095
|
+
}, null, -1)])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_19$1, (0, vue.toDisplayString)((0, vue.unref)(m).gen), 1))], 10, _hoisted_17$1)) : (0, vue.createCommentVNode)("", true)
|
|
1096
|
+
], 42, _hoisted_6$2), (0, vue.createElementVNode)("span", _hoisted_20$1, (0, vue.toDisplayString)(key), 1)]);
|
|
1097
|
+
}), 128))])]);
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
//#endregion
|
|
1102
|
+
//#region src/vue/components/MulmoScriptToolbar.vue?vue&type=script&setup=true&lang.ts
|
|
1103
|
+
var _hoisted_1$2 = { class: "ml-4 shrink-0 flex items-center gap-2" };
|
|
1104
|
+
var _hoisted_2$2 = [
|
|
1105
|
+
"disabled",
|
|
1106
|
+
"title",
|
|
1107
|
+
"aria-label"
|
|
1108
|
+
];
|
|
1109
|
+
var _hoisted_3$2 = ["disabled"];
|
|
1110
|
+
var _hoisted_4$1 = ["title", "aria-label"];
|
|
1111
|
+
var _hoisted_5$1 = ["disabled"];
|
|
1112
|
+
var _hoisted_6$1 = {
|
|
1113
|
+
key: 0,
|
|
1114
|
+
class: "animate-spin w-4 h-4 shrink-0",
|
|
1115
|
+
viewBox: "0 0 24 24",
|
|
1116
|
+
fill: "none"
|
|
1117
|
+
};
|
|
1118
|
+
var _hoisted_7$1 = { key: 1 };
|
|
1119
|
+
var _hoisted_8$1 = ["disabled"];
|
|
1120
|
+
var _hoisted_9$1 = ["title", "aria-label"];
|
|
1121
|
+
var _hoisted_10$1 = ["disabled"];
|
|
1122
|
+
var _hoisted_11$1 = {
|
|
1123
|
+
key: 0,
|
|
1124
|
+
class: "animate-spin w-4 h-4 shrink-0",
|
|
1125
|
+
viewBox: "0 0 24 24",
|
|
1126
|
+
fill: "none"
|
|
1127
|
+
};
|
|
1128
|
+
var _hoisted_12$1 = { key: 1 };
|
|
1129
|
+
//#endregion
|
|
1130
|
+
//#region src/vue/components/MulmoScriptToolbar.vue
|
|
1131
|
+
var MulmoScriptToolbar_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
1132
|
+
__name: "MulmoScriptToolbar",
|
|
1133
|
+
props: {
|
|
1134
|
+
moviePath: {},
|
|
1135
|
+
movieGenerating: { type: Boolean },
|
|
1136
|
+
movieDownloading: { type: Boolean },
|
|
1137
|
+
isPlayReady: { type: Boolean },
|
|
1138
|
+
canFetchMedia: { type: Boolean },
|
|
1139
|
+
pdfPath: {},
|
|
1140
|
+
pdfGenerating: { type: Boolean },
|
|
1141
|
+
pdfDownloading: { type: Boolean }
|
|
1142
|
+
},
|
|
1143
|
+
emits: [
|
|
1144
|
+
"play",
|
|
1145
|
+
"generateMovie",
|
|
1146
|
+
"downloadMovie",
|
|
1147
|
+
"generatePdf",
|
|
1148
|
+
"downloadPdf"
|
|
1149
|
+
],
|
|
1150
|
+
setup(__props, { emit: __emit }) {
|
|
1151
|
+
const m = useT();
|
|
1152
|
+
const emit = __emit;
|
|
1153
|
+
return (_ctx, _cache) => {
|
|
1154
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$2, [
|
|
1155
|
+
__props.moviePath && !__props.movieGenerating ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1156
|
+
key: 0,
|
|
1157
|
+
class: "h-8 w-8 flex items-center justify-center rounded border border-green-600 text-green-600 hover:bg-green-50 disabled:opacity-40 disabled:cursor-not-allowed transition-colors",
|
|
1158
|
+
disabled: !__props.isPlayReady,
|
|
1159
|
+
title: (0, vue.unref)(m).playPresentation,
|
|
1160
|
+
"aria-label": (0, vue.unref)(m).playPresentation,
|
|
1161
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("play"))
|
|
1162
|
+
}, [..._cache[7] || (_cache[7] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "play_arrow", -1)])], 8, _hoisted_2$2)) : (0, vue.createCommentVNode)("", true),
|
|
1163
|
+
__props.moviePath && !__props.movieGenerating && __props.canFetchMedia ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1164
|
+
key: 1,
|
|
1165
|
+
class: "h-8 px-2.5 flex items-center gap-1 rounded bg-green-600 hover:bg-green-700 text-white text-sm disabled:opacity-60 disabled:cursor-not-allowed transition-colors",
|
|
1166
|
+
disabled: __props.movieDownloading,
|
|
1167
|
+
"data-testid": "mulmo-script-download-movie-button",
|
|
1168
|
+
onClick: _cache[1] || (_cache[1] = ($event) => emit("downloadMovie"))
|
|
1169
|
+
}, [_cache[8] || (_cache[8] = (0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "download", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(m).movie), 1)], 8, _hoisted_3$2)) : (0, vue.createCommentVNode)("", true),
|
|
1170
|
+
__props.moviePath && !__props.movieGenerating ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1171
|
+
key: 2,
|
|
1172
|
+
class: "h-8 w-8 flex items-center justify-center rounded border border-gray-200 text-gray-600 hover:bg-gray-100 transition-colors",
|
|
1173
|
+
title: (0, vue.unref)(m).regenerateMovie,
|
|
1174
|
+
"aria-label": (0, vue.unref)(m).regenerateMovie,
|
|
1175
|
+
"data-testid": "mulmo-script-regenerate-movie-button",
|
|
1176
|
+
onClick: _cache[2] || (_cache[2] = ($event) => emit("generateMovie"))
|
|
1177
|
+
}, [..._cache[9] || (_cache[9] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "refresh", -1)])], 8, _hoisted_4$1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1178
|
+
key: 3,
|
|
1179
|
+
class: "h-8 px-2.5 flex items-center gap-1 text-sm rounded border border-gray-200 text-gray-600 hover:bg-gray-100 disabled:opacity-40 disabled:cursor-not-allowed transition-colors",
|
|
1180
|
+
disabled: __props.movieGenerating,
|
|
1181
|
+
"data-testid": "mulmo-script-generate-movie-button",
|
|
1182
|
+
onClick: _cache[3] || (_cache[3] = ($event) => emit("generateMovie"))
|
|
1183
|
+
}, [__props.movieGenerating ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_6$1, [..._cache[10] || (_cache[10] = [(0, vue.createElementVNode)("circle", {
|
|
1184
|
+
class: "opacity-25",
|
|
1185
|
+
cx: "12",
|
|
1186
|
+
cy: "12",
|
|
1187
|
+
r: "10",
|
|
1188
|
+
stroke: "currentColor",
|
|
1189
|
+
"stroke-width": "4"
|
|
1190
|
+
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1191
|
+
class: "opacity-75",
|
|
1192
|
+
fill: "currentColor",
|
|
1193
|
+
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1194
|
+
}, null, -1)])])) : (0, vue.createCommentVNode)("", true), __props.movieGenerating ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_7$1, (0, vue.toDisplayString)((0, vue.unref)(m).generating), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [_cache[11] || (_cache[11] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "refresh", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(m).movie), 1)], 64))], 8, _hoisted_5$1)),
|
|
1195
|
+
__props.pdfPath && !__props.pdfGenerating && __props.canFetchMedia ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1196
|
+
key: 4,
|
|
1197
|
+
class: "h-8 px-2.5 flex items-center gap-1 rounded bg-red-600 hover:bg-red-700 text-white text-sm disabled:opacity-60 disabled:cursor-not-allowed transition-colors",
|
|
1198
|
+
disabled: __props.pdfDownloading,
|
|
1199
|
+
"data-testid": "mulmo-script-download-pdf-button",
|
|
1200
|
+
onClick: _cache[4] || (_cache[4] = ($event) => emit("downloadPdf"))
|
|
1201
|
+
}, [_cache[12] || (_cache[12] = (0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "download", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(m).pdf), 1)], 8, _hoisted_8$1)) : (0, vue.createCommentVNode)("", true),
|
|
1202
|
+
__props.pdfPath && !__props.pdfGenerating ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1203
|
+
key: 5,
|
|
1204
|
+
class: "h-8 w-8 flex items-center justify-center rounded border border-gray-200 text-gray-600 hover:bg-gray-100 transition-colors",
|
|
1205
|
+
title: (0, vue.unref)(m).regeneratePdf,
|
|
1206
|
+
"aria-label": (0, vue.unref)(m).regeneratePdf,
|
|
1207
|
+
"data-testid": "mulmo-script-regenerate-pdf-button",
|
|
1208
|
+
onClick: _cache[5] || (_cache[5] = ($event) => emit("generatePdf"))
|
|
1209
|
+
}, [..._cache[13] || (_cache[13] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "refresh", -1)])], 8, _hoisted_9$1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1210
|
+
key: 6,
|
|
1211
|
+
class: "h-8 px-2.5 flex items-center gap-1 text-sm rounded border border-gray-200 text-gray-600 hover:bg-gray-100 disabled:opacity-40 disabled:cursor-not-allowed transition-colors",
|
|
1212
|
+
disabled: __props.pdfGenerating,
|
|
1213
|
+
"data-testid": "mulmo-script-generate-pdf-button",
|
|
1214
|
+
onClick: _cache[6] || (_cache[6] = ($event) => emit("generatePdf"))
|
|
1215
|
+
}, [__props.pdfGenerating ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_11$1, [..._cache[14] || (_cache[14] = [(0, vue.createElementVNode)("circle", {
|
|
1216
|
+
class: "opacity-25",
|
|
1217
|
+
cx: "12",
|
|
1218
|
+
cy: "12",
|
|
1219
|
+
r: "10",
|
|
1220
|
+
stroke: "currentColor",
|
|
1221
|
+
"stroke-width": "4"
|
|
1222
|
+
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1223
|
+
class: "opacity-75",
|
|
1224
|
+
fill: "currentColor",
|
|
1225
|
+
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1226
|
+
}, null, -1)])])) : (0, vue.createCommentVNode)("", true), __props.pdfGenerating ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_12$1, (0, vue.toDisplayString)((0, vue.unref)(m).generatingPdf), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [_cache[15] || (_cache[15] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "picture_as_pdf", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(m).pdf), 1)], 64))], 8, _hoisted_10$1))
|
|
1227
|
+
]);
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
//#endregion
|
|
1232
|
+
//#region src/vue/View.vue?vue&type=script&setup=true&lang.ts
|
|
1233
|
+
var _hoisted_1$1 = { class: "h-full bg-white flex flex-col overflow-hidden" };
|
|
1234
|
+
var _hoisted_2$1 = { class: "flex items-start justify-between px-6 py-4 border-b border-gray-100 shrink-0" };
|
|
1235
|
+
var _hoisted_3$1 = { class: "min-w-0 flex-1" };
|
|
1236
|
+
var _hoisted_4 = {
|
|
1237
|
+
class: "text-lg font-semibold text-gray-800 truncate",
|
|
1238
|
+
"data-testid": "mulmo-script-title"
|
|
1239
|
+
};
|
|
1240
|
+
var _hoisted_5 = {
|
|
1241
|
+
key: 0,
|
|
1242
|
+
class: "text-sm text-gray-500 mt-0.5 truncate",
|
|
1243
|
+
"data-testid": "mulmo-script-description"
|
|
1244
|
+
};
|
|
1245
|
+
var _hoisted_6 = { class: "flex items-center gap-3 mt-1 text-xs text-gray-400" };
|
|
1246
|
+
var _hoisted_7 = { key: 0 };
|
|
1247
|
+
var _hoisted_8 = {
|
|
1248
|
+
key: 1,
|
|
1249
|
+
class: "truncate"
|
|
1250
|
+
};
|
|
1251
|
+
var _hoisted_9 = {
|
|
1252
|
+
key: 0,
|
|
1253
|
+
"data-testid": "mulmo-script-movie-error-chip",
|
|
1254
|
+
class: "bg-red-50 border border-red-200 text-red-800 text-xs px-3 py-2 mx-4 mt-3 mb-1 rounded flex items-start gap-2"
|
|
1255
|
+
};
|
|
1256
|
+
var _hoisted_10 = { class: "flex-1 min-w-0" };
|
|
1257
|
+
var _hoisted_11 = { class: "font-medium" };
|
|
1258
|
+
var _hoisted_12 = { class: "break-words whitespace-pre-wrap mt-0.5" };
|
|
1259
|
+
var _hoisted_13 = ["disabled"];
|
|
1260
|
+
var _hoisted_14 = {
|
|
574
1261
|
key: 2,
|
|
575
1262
|
class: "flex-1 overflow-hidden",
|
|
576
1263
|
"data-testid": "mulmo-script-deck-editor"
|
|
577
1264
|
};
|
|
578
|
-
var
|
|
579
|
-
var
|
|
1265
|
+
var _hoisted_15 = { class: "flex gap-3 items-stretch" };
|
|
1266
|
+
var _hoisted_16 = [
|
|
580
1267
|
"onDragover",
|
|
581
1268
|
"onDragleave",
|
|
582
1269
|
"onDrop"
|
|
583
1270
|
];
|
|
584
|
-
var
|
|
585
|
-
var
|
|
1271
|
+
var _hoisted_17 = ["data-testid"];
|
|
1272
|
+
var _hoisted_18 = ["src", "data-testid"];
|
|
1273
|
+
var _hoisted_19 = [
|
|
586
1274
|
"title",
|
|
587
1275
|
"aria-label",
|
|
588
1276
|
"data-testid",
|
|
589
1277
|
"onClick"
|
|
590
1278
|
];
|
|
591
|
-
var
|
|
1279
|
+
var _hoisted_20 = [
|
|
592
1280
|
"src",
|
|
593
1281
|
"alt",
|
|
594
1282
|
"onClick"
|
|
595
1283
|
];
|
|
596
|
-
var
|
|
1284
|
+
var _hoisted_21 = [
|
|
597
1285
|
"title",
|
|
598
1286
|
"aria-label",
|
|
599
1287
|
"data-testid",
|
|
600
1288
|
"onClick"
|
|
601
1289
|
];
|
|
602
|
-
var
|
|
1290
|
+
var _hoisted_22 = {
|
|
603
1291
|
key: 0,
|
|
604
1292
|
class: "animate-spin w-5 h-5",
|
|
605
1293
|
viewBox: "0 0 24 24",
|
|
606
1294
|
fill: "none"
|
|
607
1295
|
};
|
|
608
|
-
var
|
|
1296
|
+
var _hoisted_23 = {
|
|
609
1297
|
key: 1,
|
|
610
1298
|
class: "material-icons text-3xl"
|
|
611
1299
|
};
|
|
612
|
-
var
|
|
613
|
-
var
|
|
1300
|
+
var _hoisted_24 = ["disabled", "onClick"];
|
|
1301
|
+
var _hoisted_25 = {
|
|
614
1302
|
key: 3,
|
|
615
1303
|
class: "w-full aspect-video flex flex-col items-center justify-center gap-1 p-2"
|
|
616
1304
|
};
|
|
617
|
-
var
|
|
618
|
-
var
|
|
1305
|
+
var _hoisted_26 = { class: "text-xs text-green-500" };
|
|
1306
|
+
var _hoisted_27 = {
|
|
619
1307
|
key: 1,
|
|
620
1308
|
class: "text-xs text-red-400 text-center"
|
|
621
1309
|
};
|
|
622
|
-
var
|
|
1310
|
+
var _hoisted_28 = {
|
|
623
1311
|
key: 0,
|
|
624
1312
|
class: "text-xs text-gray-400 text-center italic leading-relaxed px-1"
|
|
625
1313
|
};
|
|
626
|
-
var
|
|
1314
|
+
var _hoisted_29 = {
|
|
627
1315
|
key: 1,
|
|
628
1316
|
class: "text-xs text-gray-300"
|
|
629
1317
|
};
|
|
630
|
-
var
|
|
1318
|
+
var _hoisted_30 = {
|
|
631
1319
|
key: 2,
|
|
632
1320
|
class: "absolute inset-0 flex items-center justify-center bg-blue-50/80 pointer-events-none"
|
|
633
1321
|
};
|
|
634
|
-
var
|
|
635
|
-
var
|
|
1322
|
+
var _hoisted_31 = { class: "text-xs text-blue-500 font-medium" };
|
|
1323
|
+
var _hoisted_32 = {
|
|
636
1324
|
key: 3,
|
|
637
1325
|
class: "absolute bottom-0 inset-x-0 text-center text-xs text-gray-400 bg-white/70 py-0.5 pointer-events-none"
|
|
638
1326
|
};
|
|
639
|
-
var
|
|
640
|
-
var
|
|
641
|
-
var
|
|
642
|
-
var
|
|
643
|
-
var
|
|
644
|
-
var
|
|
1327
|
+
var _hoisted_33 = ["onClick"];
|
|
1328
|
+
var _hoisted_34 = { class: "flex flex-col flex-1 min-w-0 px-2 py-1.5" };
|
|
1329
|
+
var _hoisted_35 = { class: "text-sm text-gray-800 leading-relaxed" };
|
|
1330
|
+
var _hoisted_36 = { class: "flex justify-between mt-auto pt-1" };
|
|
1331
|
+
var _hoisted_37 = { class: "flex items-center gap-1" };
|
|
1332
|
+
var _hoisted_38 = {
|
|
645
1333
|
key: 0,
|
|
646
1334
|
class: "animate-spin w-3 h-3 text-green-400",
|
|
647
1335
|
viewBox: "0 0 24 24",
|
|
648
1336
|
fill: "none"
|
|
649
1337
|
};
|
|
650
|
-
var
|
|
651
|
-
var
|
|
652
|
-
var
|
|
653
|
-
var
|
|
654
|
-
var
|
|
1338
|
+
var _hoisted_39 = ["onClick"];
|
|
1339
|
+
var _hoisted_40 = ["title"];
|
|
1340
|
+
var _hoisted_41 = ["disabled", "onClick"];
|
|
1341
|
+
var _hoisted_42 = ["onClick"];
|
|
1342
|
+
var _hoisted_43 = [
|
|
655
1343
|
"title",
|
|
656
1344
|
"data-testid",
|
|
657
1345
|
"onClick"
|
|
658
1346
|
];
|
|
659
|
-
var
|
|
1347
|
+
var _hoisted_44 = {
|
|
660
1348
|
key: 0,
|
|
661
1349
|
class: "border-t border-gray-100"
|
|
662
1350
|
};
|
|
663
|
-
var
|
|
664
|
-
var
|
|
665
|
-
var
|
|
1351
|
+
var _hoisted_45 = ["onUpdate:modelValue", "data-testid"];
|
|
1352
|
+
var _hoisted_46 = { class: "flex items-center justify-end gap-2 px-2 pb-2" };
|
|
1353
|
+
var _hoisted_47 = {
|
|
666
1354
|
key: 0,
|
|
667
1355
|
class: "text-xs text-red-600",
|
|
668
1356
|
role: "alert"
|
|
669
1357
|
};
|
|
670
|
-
var
|
|
1358
|
+
var _hoisted_48 = [
|
|
671
1359
|
"disabled",
|
|
672
1360
|
"data-testid",
|
|
673
1361
|
"onClick"
|
|
674
1362
|
];
|
|
675
|
-
var
|
|
1363
|
+
var _hoisted_49 = {
|
|
676
1364
|
key: 0,
|
|
677
1365
|
class: "flex items-center justify-center h-32 text-gray-400 text-sm"
|
|
678
1366
|
};
|
|
679
|
-
var
|
|
680
|
-
var
|
|
681
|
-
var
|
|
682
|
-
var
|
|
683
|
-
var
|
|
684
|
-
var _hoisted_86 = ["title"];
|
|
685
|
-
var _hoisted_87 = { class: "flex items-center gap-4" };
|
|
686
|
-
var _hoisted_88 = ["disabled"];
|
|
687
|
-
var _hoisted_89 = { class: "flex flex-col items-center" };
|
|
688
|
-
var _hoisted_90 = ["src"];
|
|
689
|
-
var _hoisted_91 = {
|
|
690
|
-
key: 0,
|
|
691
|
-
class: "relative w-full h-1"
|
|
692
|
-
};
|
|
693
|
-
var _hoisted_92 = { class: "flex gap-1 h-full" };
|
|
694
|
-
var _hoisted_93 = ["onClick"];
|
|
695
|
-
var _hoisted_94 = {
|
|
696
|
-
key: 0,
|
|
697
|
-
class: "absolute bottom-full mb-2 left-1/2 -translate-x-1/2 z-20 px-2 py-1 rounded bg-black/90 text-white text-xs leading-tight w-48 max-h-[53px] overflow-hidden opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity"
|
|
698
|
-
};
|
|
699
|
-
var _hoisted_95 = ["disabled"];
|
|
700
|
-
var _hoisted_96 = {
|
|
701
|
-
key: 0,
|
|
702
|
-
class: "relative w-screen flex justify-center px-16"
|
|
703
|
-
};
|
|
704
|
-
var _hoisted_97 = {
|
|
705
|
-
key: 0,
|
|
706
|
-
class: "max-w-[80vw] text-center text-white leading-relaxed text-[clamp(0.8rem,1.76vw,1.6rem)]"
|
|
707
|
-
};
|
|
1367
|
+
var _hoisted_50 = { class: "bottom-bar-wrapper" };
|
|
1368
|
+
var _hoisted_51 = { class: "editor-actions" };
|
|
1369
|
+
var _hoisted_52 = ["disabled"];
|
|
1370
|
+
var _hoisted_53 = ["title"];
|
|
1371
|
+
var _hoisted_54 = { class: "material-icons" };
|
|
708
1372
|
var SILENT_BEAT_DEFAULT_SEC = 3;
|
|
709
1373
|
var MS_PER_SECOND = 1e3;
|
|
710
|
-
var DECK_SAVE_DEBOUNCE_MS = 300;
|
|
711
1374
|
var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.defineComponent)({
|
|
712
1375
|
__name: "View",
|
|
713
1376
|
props: { selectedResult: {} },
|
|
@@ -732,39 +1395,34 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
732
1395
|
const beatSaveErrors = (0, vue.reactive)({});
|
|
733
1396
|
const beatSaving = (0, vue.reactive)({});
|
|
734
1397
|
const localOverrides = (0, vue.reactive)({});
|
|
735
|
-
const movieGenerating = (0, vue.ref)(false);
|
|
736
|
-
const movieDownloading = (0, vue.ref)(false);
|
|
737
|
-
const moviePath = (0, vue.ref)(null);
|
|
738
|
-
const movieError = (0, vue.ref)(null);
|
|
739
|
-
const pdfGenerating = (0, vue.ref)(false);
|
|
740
|
-
const pdfDownloading = (0, vue.ref)(false);
|
|
741
|
-
const pdfPath = (0, vue.ref)(null);
|
|
742
1398
|
const beatAudios = (0, vue.reactive)({});
|
|
743
1399
|
const audioState = (0, vue.reactive)({});
|
|
744
1400
|
const audioErrors = (0, vue.reactive)({});
|
|
745
|
-
const beatMovies = (0, vue.reactive)({});
|
|
746
|
-
const beatMovieUrls = (0, vue.reactive)({});
|
|
747
|
-
const beatMovieOpen = (0, vue.reactive)({});
|
|
748
|
-
const beatMovieLoading = (0, vue.reactive)({});
|
|
749
1401
|
const playingAudio = (0, vue.ref)(null);
|
|
750
1402
|
const silentPlaybackTimer = (0, vue.ref)(null);
|
|
751
1403
|
const audioProgress = (0, vue.ref)(0);
|
|
752
1404
|
const beatListEl = (0, vue.ref)(null);
|
|
753
1405
|
const lightbox = (0, vue.ref)(null);
|
|
754
|
-
const charRenderState = (0, vue.reactive)({});
|
|
755
|
-
const charImages = (0, vue.reactive)({});
|
|
756
|
-
const charErrors = (0, vue.reactive)({});
|
|
757
|
-
const charDragOver = (0, vue.reactive)({});
|
|
758
1406
|
const beatDragOver = (0, vue.reactive)({});
|
|
759
1407
|
const anyBeatRendering = (0, vue.computed)(() => Object.values(renderState).some((state) => state === "rendering"));
|
|
760
|
-
const characterKeys = (0, vue.computed)(() => {
|
|
761
|
-
const imgs = script.value.imageParams?.images ?? {};
|
|
762
|
-
return Object.keys(imgs).filter((key) => imgs[key]?.type === "imagePrompt");
|
|
763
|
-
});
|
|
764
1408
|
const chatSessionId = (0, vue.computed)(() => adapter.chatSessionId?.value);
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
1409
|
+
const { moviePath, movieGenerating, movieDownloading, movieError, pdfPath, pdfGenerating, pdfDownloading, generateMovie, downloadMovie, refreshMoviePath, generatePdf, downloadPdf, refreshPdfPath, resetMedia } = useMediaExport({
|
|
1410
|
+
api,
|
|
1411
|
+
adapter,
|
|
1412
|
+
filePath,
|
|
1413
|
+
chatSessionId
|
|
1414
|
+
});
|
|
1415
|
+
const { beatMovies, beatMovieUrls, beatMovieOpen, beatMovieLoading, loadExistingBeatMovie, playBeatMovie, closeBeatMovie, invalidateBeatMovie, resetBeatMovies } = useBeatMovie({
|
|
1416
|
+
api,
|
|
1417
|
+
adapter,
|
|
1418
|
+
filePath
|
|
1419
|
+
});
|
|
1420
|
+
const { charRenderState, charImages, charErrors, charDragOver, characterKeys, onCharDragOver, onCharDragLeave, onCharDrop, loadExistingCharacterImage, refreshMissingCharacterImages, renderCharacter, generateAllCharacters, resetCharacters } = useCharacterImages({
|
|
1421
|
+
api,
|
|
1422
|
+
filePath,
|
|
1423
|
+
chatSessionId,
|
|
1424
|
+
getImages: () => script.value.imageParams?.images
|
|
1425
|
+
});
|
|
768
1426
|
function stopPlayingAudio() {
|
|
769
1427
|
stopAllPlayback();
|
|
770
1428
|
}
|
|
@@ -772,7 +1430,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
772
1430
|
stopPlayingAudio();
|
|
773
1431
|
lightbox.value = {
|
|
774
1432
|
src: renderedImages[index],
|
|
775
|
-
text: effectiveBeat(index).text,
|
|
1433
|
+
text: effectiveBeat$1(index).text,
|
|
776
1434
|
index
|
|
777
1435
|
};
|
|
778
1436
|
}
|
|
@@ -783,7 +1441,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
783
1441
|
const isPlayReady = (0, vue.computed)(() => {
|
|
784
1442
|
if (beats.value.length === 0) return false;
|
|
785
1443
|
if (!renderedImages[0]) return false;
|
|
786
|
-
if (effectiveBeat(0).text && !beatAudios[0]) return false;
|
|
1444
|
+
if (effectiveBeat$1(0).text && !beatAudios[0]) return false;
|
|
787
1445
|
return true;
|
|
788
1446
|
});
|
|
789
1447
|
function playPresentation() {
|
|
@@ -804,19 +1462,19 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
804
1462
|
}
|
|
805
1463
|
function playBeat(index) {
|
|
806
1464
|
stopAllPlayback();
|
|
807
|
-
if (!Boolean(effectiveBeat(index).text)) {
|
|
1465
|
+
if (!Boolean(effectiveBeat$1(index).text)) {
|
|
808
1466
|
scheduleSilentAdvance(index);
|
|
809
1467
|
return;
|
|
810
1468
|
}
|
|
811
1469
|
if (beatAudios[index]) playAudio(index);
|
|
812
1470
|
}
|
|
813
1471
|
function scheduleSilentAdvance(index) {
|
|
814
|
-
const
|
|
1472
|
+
const seconds = resolveSilentAdvanceSeconds(effectiveBeat$1(index).duration, SILENT_BEAT_DEFAULT_SEC);
|
|
815
1473
|
const timer = setTimeout(() => {
|
|
816
1474
|
if (silentPlaybackTimer.value?.index !== index) return;
|
|
817
1475
|
silentPlaybackTimer.value = null;
|
|
818
1476
|
if (lightbox.value?.index === index) advanceFromBeat(index);
|
|
819
|
-
},
|
|
1477
|
+
}, seconds * MS_PER_SECOND);
|
|
820
1478
|
silentPlaybackTimer.value = {
|
|
821
1479
|
index,
|
|
822
1480
|
timer
|
|
@@ -838,6 +1496,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
838
1496
|
for (let i = lightbox.value.index + 1; i < beats.value.length; i++) if (renderedImages[i]) return true;
|
|
839
1497
|
return false;
|
|
840
1498
|
});
|
|
1499
|
+
const beatTexts = (0, vue.computed)(() => beats.value.map((_, index) => effectiveBeat$1(index).text));
|
|
841
1500
|
function jumpToBeat(index) {
|
|
842
1501
|
if (!lightbox.value) return;
|
|
843
1502
|
if (index === lightbox.value.index) return;
|
|
@@ -846,10 +1505,6 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
846
1505
|
openLightbox(index);
|
|
847
1506
|
if (wasPlaying) playBeat(index);
|
|
848
1507
|
}
|
|
849
|
-
function beatTooltip(index) {
|
|
850
|
-
const text = effectiveBeat(index).text ?? "";
|
|
851
|
-
return text.length > 80 ? `${text.slice(0, 80)}…` : text;
|
|
852
|
-
}
|
|
853
1508
|
function lightboxMove(delta) {
|
|
854
1509
|
if (!lightbox.value) return;
|
|
855
1510
|
const total = beats.value.length;
|
|
@@ -872,31 +1527,8 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
872
1527
|
...script.value,
|
|
873
1528
|
beats: beats.value.map((beat, i) => localOverrides[i] ?? beat)
|
|
874
1529
|
}));
|
|
875
|
-
const scriptSourceText = (0, vue.computed)(() =>
|
|
876
|
-
|
|
877
|
-
const deckScriptInput = (0, vue.computed)(() => effectiveScript.value);
|
|
878
|
-
let deckSaveTimer = null;
|
|
879
|
-
let pendingDeckScript = null;
|
|
880
|
-
function scheduleDeckSave(next) {
|
|
881
|
-
pendingDeckScript = next;
|
|
882
|
-
if (deckSaveTimer) clearTimeout(deckSaveTimer);
|
|
883
|
-
deckSaveTimer = setTimeout(() => {
|
|
884
|
-
flushDeckSave();
|
|
885
|
-
}, DECK_SAVE_DEBOUNCE_MS);
|
|
886
|
-
}
|
|
887
|
-
async function flushDeckSave() {
|
|
888
|
-
deckSaveTimer = null;
|
|
889
|
-
const next = pendingDeckScript;
|
|
890
|
-
pendingDeckScript = null;
|
|
891
|
-
if (!next || !filePath.value) return;
|
|
892
|
-
const response = await api.call("updateScript", {
|
|
893
|
-
filePath: filePath.value,
|
|
894
|
-
script: next
|
|
895
|
-
});
|
|
896
|
-
if (!response.ok) {
|
|
897
|
-
console.error("[presentMulmoScript] deck save failed:", response.error);
|
|
898
|
-
return;
|
|
899
|
-
}
|
|
1530
|
+
const scriptSourceText$1 = (0, vue.computed)(() => scriptSourceText(effectiveScript.value));
|
|
1531
|
+
function commitScript(next) {
|
|
900
1532
|
emit("updateResult", {
|
|
901
1533
|
...props.selectedResult,
|
|
902
1534
|
data: {
|
|
@@ -905,14 +1537,14 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
905
1537
|
}
|
|
906
1538
|
});
|
|
907
1539
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1540
|
+
const { isDeck, deckScriptInput, onDeckUpdate, flushPendingDeckSave } = useDeckEditor({
|
|
1541
|
+
api,
|
|
1542
|
+
filePath,
|
|
1543
|
+
effectiveScript,
|
|
1544
|
+
commitScript
|
|
1545
|
+
});
|
|
911
1546
|
(0, vue.onBeforeUnmount)(() => {
|
|
912
|
-
|
|
913
|
-
clearTimeout(deckSaveTimer);
|
|
914
|
-
flushDeckSave();
|
|
915
|
-
}
|
|
1547
|
+
flushPendingDeckSave();
|
|
916
1548
|
resetBeatMovies();
|
|
917
1549
|
unsubscribeGenerationEvents();
|
|
918
1550
|
});
|
|
@@ -929,11 +1561,11 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
929
1561
|
async function onSourceToggle(open) {
|
|
930
1562
|
editing.value = open;
|
|
931
1563
|
if (open) {
|
|
932
|
-
let text = scriptSourceText.value;
|
|
1564
|
+
let text = scriptSourceText$1.value;
|
|
933
1565
|
if (filePath.value) {
|
|
934
1566
|
const response = await api.call("save", { filePath: filePath.value });
|
|
935
1567
|
const diskScript = response.ok ? response.data.script : void 0;
|
|
936
|
-
if (diskScript) text =
|
|
1568
|
+
if (diskScript) text = scriptSourceText(diskScript);
|
|
937
1569
|
}
|
|
938
1570
|
editableSource.value = text;
|
|
939
1571
|
loadedSource.value = text;
|
|
@@ -947,7 +1579,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
947
1579
|
try {
|
|
948
1580
|
parsed = JSON.parse(editableSource.value);
|
|
949
1581
|
} catch (err) {
|
|
950
|
-
alert(errorMessage(err));
|
|
1582
|
+
alert(require_plugin.errorMessage(err));
|
|
951
1583
|
return;
|
|
952
1584
|
}
|
|
953
1585
|
const response = await api.call("updateScript", {
|
|
@@ -958,31 +1590,25 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
958
1590
|
alert(response.error || "Update failed");
|
|
959
1591
|
return;
|
|
960
1592
|
}
|
|
961
|
-
|
|
962
|
-
...props.selectedResult,
|
|
963
|
-
data: {
|
|
964
|
-
...props.selectedResult.data,
|
|
965
|
-
script: parsed
|
|
966
|
-
}
|
|
967
|
-
});
|
|
1593
|
+
commitScript(parsed);
|
|
968
1594
|
if (sourceDetails.value) sourceDetails.value.open = false;
|
|
969
1595
|
await initializeScript();
|
|
970
1596
|
}
|
|
971
1597
|
async function copyText() {
|
|
972
|
-
await copy(scriptSourceText.value);
|
|
1598
|
+
await copy(scriptSourceText$1.value);
|
|
973
1599
|
}
|
|
974
|
-
function effectiveBeat(index) {
|
|
975
|
-
return localOverrides
|
|
1600
|
+
function effectiveBeat$1(index) {
|
|
1601
|
+
return effectiveBeat(localOverrides, beats.value, index);
|
|
976
1602
|
}
|
|
977
1603
|
function toggleSource(index) {
|
|
978
1604
|
if (!sourceOpen[index]) {
|
|
979
|
-
sourceText[index] =
|
|
1605
|
+
sourceText[index] = scriptSourceText(effectiveBeat$1(index));
|
|
980
1606
|
Reflect.deleteProperty(beatSaveErrors, index);
|
|
981
1607
|
}
|
|
982
1608
|
sourceOpen[index] = !sourceOpen[index];
|
|
983
1609
|
}
|
|
984
|
-
function isValidBeat(index) {
|
|
985
|
-
return
|
|
1610
|
+
function isValidBeat$1(index) {
|
|
1611
|
+
return isValidBeat(sourceText[index], _mulmocast_types.mulmoBeatSchema);
|
|
986
1612
|
}
|
|
987
1613
|
async function updateBeat(index) {
|
|
988
1614
|
let beat;
|
|
@@ -991,12 +1617,12 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
991
1617
|
} catch (err) {
|
|
992
1618
|
beatSaveErrors[index] = {
|
|
993
1619
|
kind: "invalidJson",
|
|
994
|
-
error: errorMessage(err)
|
|
1620
|
+
error: require_plugin.errorMessage(err)
|
|
995
1621
|
};
|
|
996
1622
|
return;
|
|
997
1623
|
}
|
|
998
|
-
const prevImage = JSON.stringify(effectiveBeat(index).image);
|
|
999
|
-
const prevText = effectiveBeat(index).text;
|
|
1624
|
+
const prevImage = JSON.stringify(effectiveBeat$1(index).image);
|
|
1625
|
+
const prevText = effectiveBeat$1(index).text;
|
|
1000
1626
|
const requestedFilePath = filePath.value;
|
|
1001
1627
|
Reflect.deleteProperty(beatSaveErrors, index);
|
|
1002
1628
|
beatSaving[index] = true;
|
|
@@ -1005,7 +1631,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1005
1631
|
beatIndex: index,
|
|
1006
1632
|
beat
|
|
1007
1633
|
});
|
|
1008
|
-
if (staleSince(requestedFilePath)) return;
|
|
1634
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1009
1635
|
Reflect.deleteProperty(beatSaving, index);
|
|
1010
1636
|
if (!response.ok) {
|
|
1011
1637
|
beatSaveErrors[index] = {
|
|
@@ -1036,7 +1662,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1036
1662
|
beatIndex: index,
|
|
1037
1663
|
chatSessionId: chatSessionId.value
|
|
1038
1664
|
});
|
|
1039
|
-
if (staleSince(requestedFilePath)) return;
|
|
1665
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1040
1666
|
if (!response.ok) {
|
|
1041
1667
|
renderErrors[index] = response.error || "Render failed";
|
|
1042
1668
|
renderState[index] = "error";
|
|
@@ -1045,7 +1671,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1045
1671
|
renderedImages[index] = response.data.image ?? "";
|
|
1046
1672
|
renderState[index] = "done";
|
|
1047
1673
|
refreshMissingCharacterImages();
|
|
1048
|
-
if (beatMayHaveMovie(effectiveBeat(index))) loadExistingBeatMovie(index);
|
|
1674
|
+
if (beatMayHaveMovie(effectiveBeat$1(index))) loadExistingBeatMovie(index);
|
|
1049
1675
|
}
|
|
1050
1676
|
async function regenerateBeat(index) {
|
|
1051
1677
|
const requestedFilePath = filePath.value;
|
|
@@ -1058,7 +1684,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1058
1684
|
force: true,
|
|
1059
1685
|
chatSessionId: chatSessionId.value
|
|
1060
1686
|
});
|
|
1061
|
-
if (staleSince(requestedFilePath)) return;
|
|
1687
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1062
1688
|
if (!response.ok) {
|
|
1063
1689
|
renderErrors[index] = response.error || "Render failed";
|
|
1064
1690
|
renderState[index] = "error";
|
|
@@ -1066,10 +1692,10 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1066
1692
|
}
|
|
1067
1693
|
renderedImages[index] = response.data.image ?? "";
|
|
1068
1694
|
renderState[index] = "done";
|
|
1069
|
-
if (beatMayHaveMovie(effectiveBeat(index))) loadExistingBeatMovie(index);
|
|
1695
|
+
if (beatMayHaveMovie(effectiveBeat$1(index))) loadExistingBeatMovie(index);
|
|
1070
1696
|
}
|
|
1071
|
-
function staleSince(requestedFilePath) {
|
|
1072
|
-
return filePath.value
|
|
1697
|
+
function staleSince$1(requestedFilePath) {
|
|
1698
|
+
return staleSince(filePath.value, requestedFilePath);
|
|
1073
1699
|
}
|
|
1074
1700
|
async function loadExistingBeatImage(index) {
|
|
1075
1701
|
const requestedFilePath = filePath.value;
|
|
@@ -1077,7 +1703,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1077
1703
|
filePath: requestedFilePath,
|
|
1078
1704
|
beatIndex: index
|
|
1079
1705
|
});
|
|
1080
|
-
if (staleSince(requestedFilePath)) return;
|
|
1706
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1081
1707
|
if (response.ok && response.data.image) {
|
|
1082
1708
|
renderedImages[index] = response.data.image;
|
|
1083
1709
|
renderState[index] = "done";
|
|
@@ -1089,61 +1715,12 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1089
1715
|
filePath: requestedFilePath,
|
|
1090
1716
|
beatIndex: index
|
|
1091
1717
|
});
|
|
1092
|
-
if (staleSince(requestedFilePath)) return;
|
|
1718
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1093
1719
|
if (response.ok && response.data.audio) {
|
|
1094
1720
|
beatAudios[index] = response.data.audio;
|
|
1095
1721
|
audioState[index] = "done";
|
|
1096
1722
|
}
|
|
1097
1723
|
}
|
|
1098
|
-
async function loadExistingBeatMovie(index) {
|
|
1099
|
-
const requestedFilePath = filePath.value;
|
|
1100
|
-
const response = await api.call("beatMovie", {
|
|
1101
|
-
filePath: requestedFilePath,
|
|
1102
|
-
beatIndex: index
|
|
1103
|
-
});
|
|
1104
|
-
if (staleSince(requestedFilePath)) return;
|
|
1105
|
-
if (response.ok && response.data.moviePath) beatMovies[index] = response.data.moviePath;
|
|
1106
|
-
}
|
|
1107
|
-
async function playBeatMovie(index) {
|
|
1108
|
-
const fetchMediaBlob = adapter.fetchMediaBlob;
|
|
1109
|
-
if (!fetchMediaBlob || !beatMovies[index] || beatMovieLoading[index]) return;
|
|
1110
|
-
if (beatMovieUrls[index]) {
|
|
1111
|
-
beatMovieOpen[index] = true;
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
beatMovieLoading[index] = true;
|
|
1115
|
-
try {
|
|
1116
|
-
const blob = new Blob([await fetchMediaBlob({ moviePath: beatMovies[index] })], { type: "video/mp4" });
|
|
1117
|
-
beatMovieUrls[index] = URL.createObjectURL(blob);
|
|
1118
|
-
beatMovieOpen[index] = true;
|
|
1119
|
-
} catch (err) {
|
|
1120
|
-
alert(errorMessage(err));
|
|
1121
|
-
} finally {
|
|
1122
|
-
Reflect.deleteProperty(beatMovieLoading, index);
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
function closeBeatMovie(index) {
|
|
1126
|
-
Reflect.deleteProperty(beatMovieOpen, index);
|
|
1127
|
-
}
|
|
1128
|
-
function invalidateBeatMovie(index) {
|
|
1129
|
-
if (beatMovieUrls[index]) URL.revokeObjectURL(beatMovieUrls[index]);
|
|
1130
|
-
[
|
|
1131
|
-
beatMovies,
|
|
1132
|
-
beatMovieUrls,
|
|
1133
|
-
beatMovieOpen
|
|
1134
|
-
].forEach((map) => Reflect.deleteProperty(map, index));
|
|
1135
|
-
}
|
|
1136
|
-
function resetBeatMovies() {
|
|
1137
|
-
Object.values(beatMovieUrls).forEach((url) => URL.revokeObjectURL(url));
|
|
1138
|
-
[
|
|
1139
|
-
beatMovies,
|
|
1140
|
-
beatMovieUrls,
|
|
1141
|
-
beatMovieOpen,
|
|
1142
|
-
beatMovieLoading
|
|
1143
|
-
].forEach((map) => {
|
|
1144
|
-
Object.keys(map).forEach((key) => Reflect.deleteProperty(map, key));
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
1724
|
async function generateAudio(index) {
|
|
1148
1725
|
const requestedFilePath = filePath.value;
|
|
1149
1726
|
audioState[index] = "generating";
|
|
@@ -1153,7 +1730,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1153
1730
|
beatIndex: index,
|
|
1154
1731
|
chatSessionId: chatSessionId.value
|
|
1155
1732
|
});
|
|
1156
|
-
if (staleSince(requestedFilePath)) return;
|
|
1733
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1157
1734
|
if (!response.ok) {
|
|
1158
1735
|
audioErrors[index] = response.error || "Audio generation failed";
|
|
1159
1736
|
audioState[index] = "error";
|
|
@@ -1206,14 +1783,9 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1206
1783
|
Reflect.deleteProperty(renderErrors, index);
|
|
1207
1784
|
let imageData;
|
|
1208
1785
|
try {
|
|
1209
|
-
imageData = await
|
|
1210
|
-
const reader = new FileReader();
|
|
1211
|
-
reader.onload = () => resolve(reader.result);
|
|
1212
|
-
reader.onerror = reject;
|
|
1213
|
-
reader.readAsDataURL(file);
|
|
1214
|
-
});
|
|
1786
|
+
imageData = await readFileAsDataUrl(file);
|
|
1215
1787
|
} catch (err) {
|
|
1216
|
-
renderErrors[index] = errorMessage(err);
|
|
1788
|
+
renderErrors[index] = require_plugin.errorMessage(err);
|
|
1217
1789
|
renderState[index] = "error";
|
|
1218
1790
|
return;
|
|
1219
1791
|
}
|
|
@@ -1223,7 +1795,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1223
1795
|
beatIndex: index,
|
|
1224
1796
|
imageData
|
|
1225
1797
|
});
|
|
1226
|
-
if (staleSince(requestedFilePath)) return;
|
|
1798
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1227
1799
|
if (!response.ok) {
|
|
1228
1800
|
renderErrors[index] = response.error || "Upload failed";
|
|
1229
1801
|
renderState[index] = "error";
|
|
@@ -1232,49 +1804,6 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1232
1804
|
renderedImages[index] = response.data.image ?? "";
|
|
1233
1805
|
renderState[index] = "done";
|
|
1234
1806
|
}
|
|
1235
|
-
function onCharDragOver(event, key) {
|
|
1236
|
-
if (!event.dataTransfer?.types.includes("Files")) return;
|
|
1237
|
-
event.preventDefault();
|
|
1238
|
-
charDragOver[key] = true;
|
|
1239
|
-
}
|
|
1240
|
-
function onCharDragLeave(key) {
|
|
1241
|
-
charDragOver[key] = false;
|
|
1242
|
-
}
|
|
1243
|
-
async function onCharDrop(event, key) {
|
|
1244
|
-
event.preventDefault();
|
|
1245
|
-
charDragOver[key] = false;
|
|
1246
|
-
const file = event.dataTransfer?.files[0];
|
|
1247
|
-
if (!file || !file.type.startsWith("image/")) return;
|
|
1248
|
-
charRenderState[key] = "rendering";
|
|
1249
|
-
Reflect.deleteProperty(charErrors, key);
|
|
1250
|
-
let imageData;
|
|
1251
|
-
try {
|
|
1252
|
-
imageData = await new Promise((resolve, reject) => {
|
|
1253
|
-
const reader = new FileReader();
|
|
1254
|
-
reader.onload = () => resolve(reader.result);
|
|
1255
|
-
reader.onerror = reject;
|
|
1256
|
-
reader.readAsDataURL(file);
|
|
1257
|
-
});
|
|
1258
|
-
} catch (err) {
|
|
1259
|
-
charErrors[key] = errorMessage(err);
|
|
1260
|
-
charRenderState[key] = "error";
|
|
1261
|
-
return;
|
|
1262
|
-
}
|
|
1263
|
-
const requestedFilePath = filePath.value;
|
|
1264
|
-
const response = await api.call("uploadCharacterImage", {
|
|
1265
|
-
filePath: requestedFilePath,
|
|
1266
|
-
key,
|
|
1267
|
-
imageData
|
|
1268
|
-
});
|
|
1269
|
-
if (staleSince(requestedFilePath)) return;
|
|
1270
|
-
if (!response.ok) {
|
|
1271
|
-
charErrors[key] = response.error || "Upload failed";
|
|
1272
|
-
charRenderState[key] = "error";
|
|
1273
|
-
return;
|
|
1274
|
-
}
|
|
1275
|
-
charImages[key] = response.data.image ?? "";
|
|
1276
|
-
charRenderState[key] = "done";
|
|
1277
|
-
}
|
|
1278
1807
|
function openCharacterLightbox(key) {
|
|
1279
1808
|
stopAllPlayback();
|
|
1280
1809
|
lightbox.value = {
|
|
@@ -1284,43 +1813,6 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1284
1813
|
isCharacter: true
|
|
1285
1814
|
};
|
|
1286
1815
|
}
|
|
1287
|
-
async function loadExistingCharacterImage(key) {
|
|
1288
|
-
const requestedFilePath = filePath.value;
|
|
1289
|
-
const response = await api.call("characterImage", {
|
|
1290
|
-
filePath: requestedFilePath,
|
|
1291
|
-
key
|
|
1292
|
-
});
|
|
1293
|
-
if (staleSince(requestedFilePath)) return;
|
|
1294
|
-
if (response.ok && response.data.image) {
|
|
1295
|
-
charImages[key] = response.data.image;
|
|
1296
|
-
charRenderState[key] = "done";
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
function refreshMissingCharacterImages() {
|
|
1300
|
-
getMissingCharacterKeys(characterKeys.value, charImages, charRenderState).forEach((key) => loadExistingCharacterImage(key));
|
|
1301
|
-
}
|
|
1302
|
-
async function renderCharacter(key, force) {
|
|
1303
|
-
const requestedFilePath = filePath.value;
|
|
1304
|
-
charRenderState[key] = "rendering";
|
|
1305
|
-
Reflect.deleteProperty(charErrors, key);
|
|
1306
|
-
const response = await api.call("renderCharacter", {
|
|
1307
|
-
filePath: requestedFilePath,
|
|
1308
|
-
key,
|
|
1309
|
-
force,
|
|
1310
|
-
chatSessionId: chatSessionId.value
|
|
1311
|
-
});
|
|
1312
|
-
if (staleSince(requestedFilePath)) return;
|
|
1313
|
-
if (!response.ok) {
|
|
1314
|
-
charErrors[key] = response.error || "Render failed";
|
|
1315
|
-
charRenderState[key] = "error";
|
|
1316
|
-
return;
|
|
1317
|
-
}
|
|
1318
|
-
charImages[key] = response.data.image ?? "";
|
|
1319
|
-
charRenderState[key] = "done";
|
|
1320
|
-
}
|
|
1321
|
-
async function generateAllCharacters() {
|
|
1322
|
-
await Promise.all(characterKeys.value.filter((key) => charRenderState[key] !== "rendering").map((key) => renderCharacter(key, false)));
|
|
1323
|
-
}
|
|
1324
1816
|
async function hydrateBeatImage(beat, index, hasCharacters, autoRenderTypes) {
|
|
1325
1817
|
await loadExistingBeatImage(index);
|
|
1326
1818
|
if (renderedImages[index]) return;
|
|
@@ -1362,39 +1854,16 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1362
1854
|
const diskScript = response.data.script;
|
|
1363
1855
|
if (!diskScript) return;
|
|
1364
1856
|
if (isSameScript(diskScript, script.value)) return;
|
|
1365
|
-
|
|
1366
|
-
...props.selectedResult,
|
|
1367
|
-
data: {
|
|
1368
|
-
...props.selectedResult.data,
|
|
1369
|
-
script: diskScript
|
|
1370
|
-
}
|
|
1371
|
-
});
|
|
1857
|
+
commitScript(diskScript);
|
|
1372
1858
|
}
|
|
1373
1859
|
async function initializeScript() {
|
|
1374
1860
|
stopAllPlayback();
|
|
1375
1861
|
lightbox.value = null;
|
|
1376
1862
|
if (beatListEl.value) beatListEl.value.scrollTop = 0;
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
Object.keys(renderErrors).forEach((key) => Reflect.deleteProperty(renderErrors, key));
|
|
1380
|
-
Object.keys(sourceOpen).forEach((key) => Reflect.deleteProperty(sourceOpen, key));
|
|
1381
|
-
Object.keys(sourceText).forEach((key) => Reflect.deleteProperty(sourceText, key));
|
|
1382
|
-
Object.keys(beatSaveErrors).forEach((key) => Reflect.deleteProperty(beatSaveErrors, key));
|
|
1383
|
-
Object.keys(beatSaving).forEach((key) => Reflect.deleteProperty(beatSaving, key));
|
|
1384
|
-
Object.keys(localOverrides).forEach((key) => Reflect.deleteProperty(localOverrides, key));
|
|
1385
|
-
Object.keys(beatAudios).forEach((key) => Reflect.deleteProperty(beatAudios, key));
|
|
1386
|
-
Object.keys(audioState).forEach((key) => Reflect.deleteProperty(audioState, key));
|
|
1387
|
-
Object.keys(audioErrors).forEach((key) => Reflect.deleteProperty(audioErrors, key));
|
|
1388
|
-
Object.keys(charRenderState).forEach((key) => Reflect.deleteProperty(charRenderState, key));
|
|
1389
|
-
Object.keys(charImages).forEach((key) => Reflect.deleteProperty(charImages, key));
|
|
1390
|
-
Object.keys(charErrors).forEach((key) => Reflect.deleteProperty(charErrors, key));
|
|
1391
|
-
Object.keys(beatDragOver).forEach((key) => Reflect.deleteProperty(beatDragOver, key));
|
|
1863
|
+
clearReactiveRecords(renderState, renderedImages, renderErrors, sourceOpen, sourceText, beatSaveErrors, beatSaving, localOverrides, beatAudios, audioState, audioErrors, beatDragOver);
|
|
1864
|
+
resetCharacters();
|
|
1392
1865
|
resetBeatMovies();
|
|
1393
|
-
|
|
1394
|
-
pdfPath.value = null;
|
|
1395
|
-
movieGenerating.value = false;
|
|
1396
|
-
pdfGenerating.value = false;
|
|
1397
|
-
movieError.value = null;
|
|
1866
|
+
resetMedia();
|
|
1398
1867
|
if (sourceDetails.value) sourceDetails.value.open = false;
|
|
1399
1868
|
await refreshScriptFromDisk();
|
|
1400
1869
|
const AUTO_RENDER_TYPES = [
|
|
@@ -1453,7 +1922,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1453
1922
|
if (entry.kind === "beatImage") {
|
|
1454
1923
|
const idx = Number(entry.key);
|
|
1455
1924
|
await loadExistingBeatImage(idx);
|
|
1456
|
-
if (beatMayHaveMovie(effectiveBeat(idx))) await loadExistingBeatMovie(idx);
|
|
1925
|
+
if (beatMayHaveMovie(effectiveBeat$1(idx))) await loadExistingBeatMovie(idx);
|
|
1457
1926
|
if (renderState[idx] === "rendering") Reflect.deleteProperty(renderState, idx);
|
|
1458
1927
|
refreshMissingCharacterImages();
|
|
1459
1928
|
} else if (entry.kind === "beatAudio") {
|
|
@@ -1471,95 +1940,6 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1471
1940
|
await refreshPdfPath();
|
|
1472
1941
|
}
|
|
1473
1942
|
}
|
|
1474
|
-
async function refreshMoviePath() {
|
|
1475
|
-
const requestedFilePath = filePath.value;
|
|
1476
|
-
if (!requestedFilePath) return;
|
|
1477
|
-
const response = await api.call("movieStatus", { filePath: requestedFilePath });
|
|
1478
|
-
if (filePath.value !== requestedFilePath) return;
|
|
1479
|
-
if (response.ok && response.data.moviePath) moviePath.value = response.data.moviePath;
|
|
1480
|
-
}
|
|
1481
|
-
async function generateMovie() {
|
|
1482
|
-
const requestedFilePath = filePath.value;
|
|
1483
|
-
movieGenerating.value = true;
|
|
1484
|
-
movieError.value = null;
|
|
1485
|
-
const response = await api.call("generateMovie", {
|
|
1486
|
-
filePath: requestedFilePath,
|
|
1487
|
-
chatSessionId: chatSessionId.value
|
|
1488
|
-
});
|
|
1489
|
-
if (filePath.value !== requestedFilePath) return;
|
|
1490
|
-
movieGenerating.value = false;
|
|
1491
|
-
if (!response.ok) {
|
|
1492
|
-
movieError.value = response.error;
|
|
1493
|
-
return;
|
|
1494
|
-
}
|
|
1495
|
-
moviePath.value = response.data.moviePath;
|
|
1496
|
-
}
|
|
1497
|
-
async function downloadMovie() {
|
|
1498
|
-
const fetchMediaBlob = adapter.fetchMediaBlob;
|
|
1499
|
-
if (!fetchMediaBlob || !moviePath.value || movieDownloading.value) return;
|
|
1500
|
-
movieDownloading.value = true;
|
|
1501
|
-
let objectUrl = null;
|
|
1502
|
-
try {
|
|
1503
|
-
const blob = await fetchMediaBlob({ moviePath: moviePath.value });
|
|
1504
|
-
objectUrl = URL.createObjectURL(blob);
|
|
1505
|
-
const filename = moviePath.value.split("/").pop() ?? "movie.mp4";
|
|
1506
|
-
const anchor = document.createElement("a");
|
|
1507
|
-
anchor.href = objectUrl;
|
|
1508
|
-
anchor.download = filename;
|
|
1509
|
-
document.body.appendChild(anchor);
|
|
1510
|
-
anchor.click();
|
|
1511
|
-
anchor.remove();
|
|
1512
|
-
} catch (err) {
|
|
1513
|
-
alert(errorMessage(err));
|
|
1514
|
-
} finally {
|
|
1515
|
-
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
1516
|
-
movieDownloading.value = false;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
async function refreshPdfPath() {
|
|
1520
|
-
const requestedFilePath = filePath.value;
|
|
1521
|
-
if (!requestedFilePath) return;
|
|
1522
|
-
const response = await api.call("pdfStatus", { filePath: requestedFilePath });
|
|
1523
|
-
if (filePath.value !== requestedFilePath) return;
|
|
1524
|
-
if (response.ok && response.data.pdfPath) pdfPath.value = response.data.pdfPath;
|
|
1525
|
-
}
|
|
1526
|
-
async function generatePdf() {
|
|
1527
|
-
const requestedFilePath = filePath.value;
|
|
1528
|
-
pdfGenerating.value = true;
|
|
1529
|
-
const response = await api.call("generatePdf", {
|
|
1530
|
-
filePath: requestedFilePath,
|
|
1531
|
-
chatSessionId: chatSessionId.value
|
|
1532
|
-
});
|
|
1533
|
-
if (filePath.value !== requestedFilePath) return;
|
|
1534
|
-
pdfGenerating.value = false;
|
|
1535
|
-
if (!response.ok) {
|
|
1536
|
-
alert(response.error);
|
|
1537
|
-
return;
|
|
1538
|
-
}
|
|
1539
|
-
pdfPath.value = response.data.pdfPath;
|
|
1540
|
-
}
|
|
1541
|
-
async function downloadPdf() {
|
|
1542
|
-
const fetchMediaBlob = adapter.fetchMediaBlob;
|
|
1543
|
-
if (!fetchMediaBlob || !pdfPath.value || pdfDownloading.value) return;
|
|
1544
|
-
pdfDownloading.value = true;
|
|
1545
|
-
let objectUrl = null;
|
|
1546
|
-
try {
|
|
1547
|
-
const blob = await fetchMediaBlob({ pdfPath: pdfPath.value });
|
|
1548
|
-
objectUrl = URL.createObjectURL(blob);
|
|
1549
|
-
const filename = pdfPath.value.split("/").pop() ?? "deck.pdf";
|
|
1550
|
-
const anchor = document.createElement("a");
|
|
1551
|
-
anchor.href = objectUrl;
|
|
1552
|
-
anchor.download = filename;
|
|
1553
|
-
document.body.appendChild(anchor);
|
|
1554
|
-
anchor.click();
|
|
1555
|
-
anchor.remove();
|
|
1556
|
-
} catch (err) {
|
|
1557
|
-
alert(errorMessage(err));
|
|
1558
|
-
} finally {
|
|
1559
|
-
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
1560
|
-
pdfDownloading.value = false;
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
1943
|
return (_ctx, _cache) => {
|
|
1564
1944
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$1, [
|
|
1565
1945
|
(0, vue.createElementVNode)("div", _hoisted_2$1, [(0, vue.createElementVNode)("div", _hoisted_3$1, [
|
|
@@ -1570,153 +1950,80 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1570
1950
|
script.value.lang ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_7, (0, vue.toDisplayString)(script.value.lang), 1)) : (0, vue.createCommentVNode)("", true),
|
|
1571
1951
|
filePath.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8, (0, vue.toDisplayString)(filePath.value), 1)) : (0, vue.createCommentVNode)("", true)
|
|
1572
1952
|
])
|
|
1573
|
-
]), (0, vue.
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
"data-testid": "mulmo-script-generate-movie-button",
|
|
1601
|
-
onClick: generateMovie
|
|
1602
|
-
}, [movieGenerating.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_14, [..._cache[9] || (_cache[9] = [(0, vue.createElementVNode)("circle", {
|
|
1603
|
-
class: "opacity-25",
|
|
1604
|
-
cx: "12",
|
|
1605
|
-
cy: "12",
|
|
1606
|
-
r: "10",
|
|
1607
|
-
stroke: "currentColor",
|
|
1608
|
-
"stroke-width": "4"
|
|
1609
|
-
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1610
|
-
class: "opacity-75",
|
|
1611
|
-
fill: "currentColor",
|
|
1612
|
-
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1613
|
-
}, null, -1)])])) : (0, vue.createCommentVNode)("", true), movieGenerating.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_15, (0, vue.toDisplayString)((0, vue.unref)(m).generating), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [_cache[10] || (_cache[10] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "refresh", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(m).movie), 1)], 64))], 8, _hoisted_13)),
|
|
1614
|
-
pdfPath.value && !pdfGenerating.value && canFetchMedia.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1615
|
-
key: 4,
|
|
1616
|
-
class: "h-8 px-2.5 flex items-center gap-1 rounded bg-red-600 hover:bg-red-700 text-white text-sm disabled:opacity-60 disabled:cursor-not-allowed transition-colors",
|
|
1617
|
-
disabled: pdfDownloading.value,
|
|
1618
|
-
"data-testid": "mulmo-script-download-pdf-button",
|
|
1619
|
-
onClick: downloadPdf
|
|
1620
|
-
}, [_cache[11] || (_cache[11] = (0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "download", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(m).pdf), 1)], 8, _hoisted_16)) : (0, vue.createCommentVNode)("", true),
|
|
1621
|
-
pdfPath.value && !pdfGenerating.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1622
|
-
key: 5,
|
|
1623
|
-
class: "h-8 w-8 flex items-center justify-center rounded border border-gray-200 text-gray-600 hover:bg-gray-100 transition-colors",
|
|
1624
|
-
title: (0, vue.unref)(m).regeneratePdf,
|
|
1625
|
-
"aria-label": (0, vue.unref)(m).regeneratePdf,
|
|
1626
|
-
"data-testid": "mulmo-script-regenerate-pdf-button",
|
|
1627
|
-
onClick: generatePdf
|
|
1628
|
-
}, [..._cache[12] || (_cache[12] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "refresh", -1)])], 8, _hoisted_17)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1629
|
-
key: 6,
|
|
1630
|
-
class: "h-8 px-2.5 flex items-center gap-1 text-sm rounded border border-gray-200 text-gray-600 hover:bg-gray-100 disabled:opacity-40 disabled:cursor-not-allowed transition-colors",
|
|
1631
|
-
disabled: pdfGenerating.value,
|
|
1632
|
-
"data-testid": "mulmo-script-generate-pdf-button",
|
|
1633
|
-
onClick: generatePdf
|
|
1634
|
-
}, [pdfGenerating.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_19, [..._cache[13] || (_cache[13] = [(0, vue.createElementVNode)("circle", {
|
|
1635
|
-
class: "opacity-25",
|
|
1636
|
-
cx: "12",
|
|
1637
|
-
cy: "12",
|
|
1638
|
-
r: "10",
|
|
1639
|
-
stroke: "currentColor",
|
|
1640
|
-
"stroke-width": "4"
|
|
1641
|
-
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1642
|
-
class: "opacity-75",
|
|
1643
|
-
fill: "currentColor",
|
|
1644
|
-
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1645
|
-
}, null, -1)])])) : (0, vue.createCommentVNode)("", true), pdfGenerating.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_20, (0, vue.toDisplayString)((0, vue.unref)(m).generatingPdf), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [_cache[14] || (_cache[14] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "picture_as_pdf", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(m).pdf), 1)], 64))], 8, _hoisted_18))
|
|
1953
|
+
]), (0, vue.createVNode)(MulmoScriptToolbar_default, {
|
|
1954
|
+
"movie-path": (0, vue.unref)(moviePath),
|
|
1955
|
+
"movie-generating": (0, vue.unref)(movieGenerating),
|
|
1956
|
+
"movie-downloading": (0, vue.unref)(movieDownloading),
|
|
1957
|
+
"is-play-ready": isPlayReady.value,
|
|
1958
|
+
"can-fetch-media": canFetchMedia.value,
|
|
1959
|
+
"pdf-path": (0, vue.unref)(pdfPath),
|
|
1960
|
+
"pdf-generating": (0, vue.unref)(pdfGenerating),
|
|
1961
|
+
"pdf-downloading": (0, vue.unref)(pdfDownloading),
|
|
1962
|
+
onPlay: playPresentation,
|
|
1963
|
+
onGenerateMovie: (0, vue.unref)(generateMovie),
|
|
1964
|
+
onDownloadMovie: (0, vue.unref)(downloadMovie),
|
|
1965
|
+
onGeneratePdf: (0, vue.unref)(generatePdf),
|
|
1966
|
+
onDownloadPdf: (0, vue.unref)(downloadPdf)
|
|
1967
|
+
}, null, 8, [
|
|
1968
|
+
"movie-path",
|
|
1969
|
+
"movie-generating",
|
|
1970
|
+
"movie-downloading",
|
|
1971
|
+
"is-play-ready",
|
|
1972
|
+
"can-fetch-media",
|
|
1973
|
+
"pdf-path",
|
|
1974
|
+
"pdf-generating",
|
|
1975
|
+
"pdf-downloading",
|
|
1976
|
+
"onGenerateMovie",
|
|
1977
|
+
"onDownloadMovie",
|
|
1978
|
+
"onGeneratePdf",
|
|
1979
|
+
"onDownloadPdf"
|
|
1646
1980
|
])]),
|
|
1647
|
-
movieError
|
|
1648
|
-
_cache[
|
|
1649
|
-
(0, vue.createElementVNode)("div",
|
|
1981
|
+
(0, vue.unref)(movieError) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_9, [
|
|
1982
|
+
_cache[3] || (_cache[3] = (0, vue.createElementVNode)("span", { class: "material-icons text-base shrink-0 mt-px" }, "error_outline", -1)),
|
|
1983
|
+
(0, vue.createElementVNode)("div", _hoisted_10, [(0, vue.createElementVNode)("div", _hoisted_11, (0, vue.toDisplayString)((0, vue.unref)(m).movieGenerationFailed), 1), (0, vue.createElementVNode)("div", _hoisted_12, (0, vue.toDisplayString)((0, vue.unref)(movieError)), 1)]),
|
|
1650
1984
|
(0, vue.createElementVNode)("button", {
|
|
1651
1985
|
class: "shrink-0 h-7 px-2 text-xs rounded border border-red-300 text-red-700 hover:bg-red-100 disabled:opacity-50",
|
|
1652
|
-
disabled: movieGenerating
|
|
1986
|
+
disabled: (0, vue.unref)(movieGenerating),
|
|
1653
1987
|
"data-testid": "mulmo-script-movie-retry-button",
|
|
1654
|
-
onClick: generateMovie
|
|
1655
|
-
}, (0, vue.toDisplayString)((0, vue.unref)(m).retry), 9,
|
|
1988
|
+
onClick: _cache[0] || (_cache[0] = (...args) => (0, vue.unref)(generateMovie) && (0, vue.unref)(generateMovie)(...args))
|
|
1989
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(m).retry), 9, _hoisted_13)
|
|
1656
1990
|
])) : (0, vue.createCommentVNode)("", true),
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
charDragOver[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_37, [(0, vue.createElementVNode)("span", _hoisted_38, (0, vue.toDisplayString)((0, vue.unref)(m).drop), 1)])) : (0, vue.createCommentVNode)("", true),
|
|
1691
|
-
charImages[key] && charRenderState[key] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1692
|
-
key: 6,
|
|
1693
|
-
class: (0, vue.normalizeClass)(["absolute top-0.5 right-0.5 px-1 py-0.5 text-xs rounded border bg-white", movieGenerating.value || anyBeatRendering.value ? "border-yellow-400 text-yellow-500 cursor-not-allowed" : "border-gray-400 text-gray-600 hover:bg-gray-50"]),
|
|
1694
|
-
disabled: movieGenerating.value || anyBeatRendering.value,
|
|
1695
|
-
onClick: (0, vue.withModifiers)(($event) => renderCharacter(key, true), ["stop"])
|
|
1696
|
-
}, [movieGenerating.value || anyBeatRendering.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_40, "↺")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_41, "↺"))], 10, _hoisted_39)) : !charImages[key] && charRenderState[key] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1697
|
-
key: 7,
|
|
1698
|
-
class: (0, vue.normalizeClass)(["absolute top-0.5 right-0.5 px-1 py-0.5 text-xs rounded border bg-white", movieGenerating.value || anyBeatRendering.value ? "border-yellow-400 text-yellow-500 cursor-not-allowed" : "border-blue-400 text-blue-600 hover:bg-blue-50"]),
|
|
1699
|
-
disabled: movieGenerating.value || anyBeatRendering.value,
|
|
1700
|
-
onClick: (0, vue.withModifiers)(($event) => renderCharacter(key, false), ["stop"])
|
|
1701
|
-
}, [movieGenerating.value || anyBeatRendering.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_43, [..._cache[17] || (_cache[17] = [(0, vue.createElementVNode)("circle", {
|
|
1702
|
-
class: "opacity-25",
|
|
1703
|
-
cx: "12",
|
|
1704
|
-
cy: "12",
|
|
1705
|
-
r: "10",
|
|
1706
|
-
stroke: "currentColor",
|
|
1707
|
-
"stroke-width": "4"
|
|
1708
|
-
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1709
|
-
class: "opacity-75",
|
|
1710
|
-
fill: "currentColor",
|
|
1711
|
-
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1712
|
-
}, null, -1)])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_44, (0, vue.toDisplayString)((0, vue.unref)(m).gen), 1))], 10, _hoisted_42)) : (0, vue.createCommentVNode)("", true)
|
|
1713
|
-
], 42, _hoisted_31), (0, vue.createElementVNode)("span", _hoisted_45, (0, vue.toDisplayString)(key), 1)]);
|
|
1714
|
-
}), 128))])])) : (0, vue.createCommentVNode)("", true),
|
|
1715
|
-
isDeck.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_46, [(0, vue.createVNode)((0, vue.unref)(MulmoScriptDeckEditor), {
|
|
1716
|
-
script: deckScriptInput.value,
|
|
1991
|
+
(0, vue.unref)(characterKeys).length > 0 ? ((0, vue.openBlock)(), (0, vue.createBlock)(CharacterStrip_default, {
|
|
1992
|
+
key: 1,
|
|
1993
|
+
"character-keys": (0, vue.unref)(characterKeys),
|
|
1994
|
+
images: script.value.imageParams?.images,
|
|
1995
|
+
thumbnails: (0, vue.unref)(charImages),
|
|
1996
|
+
"render-state": (0, vue.unref)(charRenderState),
|
|
1997
|
+
errors: (0, vue.unref)(charErrors),
|
|
1998
|
+
"drag-over": (0, vue.unref)(charDragOver),
|
|
1999
|
+
"movie-generating": (0, vue.unref)(movieGenerating),
|
|
2000
|
+
"any-beat-rendering": anyBeatRendering.value,
|
|
2001
|
+
onGenerateAll: (0, vue.unref)(generateAllCharacters),
|
|
2002
|
+
onCharDragOver: (0, vue.unref)(onCharDragOver),
|
|
2003
|
+
onCharDragLeave: (0, vue.unref)(onCharDragLeave),
|
|
2004
|
+
onCharDrop: (0, vue.unref)(onCharDrop),
|
|
2005
|
+
onOpenLightbox: openCharacterLightbox,
|
|
2006
|
+
onRenderCharacter: (0, vue.unref)(renderCharacter)
|
|
2007
|
+
}, null, 8, [
|
|
2008
|
+
"character-keys",
|
|
2009
|
+
"images",
|
|
2010
|
+
"thumbnails",
|
|
2011
|
+
"render-state",
|
|
2012
|
+
"errors",
|
|
2013
|
+
"drag-over",
|
|
2014
|
+
"movie-generating",
|
|
2015
|
+
"any-beat-rendering",
|
|
2016
|
+
"onGenerateAll",
|
|
2017
|
+
"onCharDragOver",
|
|
2018
|
+
"onCharDragLeave",
|
|
2019
|
+
"onCharDrop",
|
|
2020
|
+
"onRenderCharacter"
|
|
2021
|
+
])) : (0, vue.createCommentVNode)("", true),
|
|
2022
|
+
(0, vue.unref)(isDeck) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_14, [(0, vue.createVNode)((0, vue.unref)(MulmoScriptDeckEditor), {
|
|
2023
|
+
script: (0, vue.unref)(deckScriptInput),
|
|
1717
2024
|
layout: "compact",
|
|
1718
|
-
"onUpdate:script": onDeckUpdate
|
|
1719
|
-
}, null, 8, ["script"])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
2025
|
+
"onUpdate:script": (0, vue.unref)(onDeckUpdate)
|
|
2026
|
+
}, null, 8, ["script", "onUpdate:script"])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1720
2027
|
key: 3,
|
|
1721
2028
|
ref_key: "beatListEl",
|
|
1722
2029
|
ref: beatListEl,
|
|
@@ -1725,40 +2032,44 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1725
2032
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1726
2033
|
key: index,
|
|
1727
2034
|
class: "rounded-lg border border-gray-200 overflow-hidden"
|
|
1728
|
-
}, [(0, vue.createElementVNode)("div",
|
|
2035
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_15, [(0, vue.createElementVNode)("div", {
|
|
1729
2036
|
class: (0, vue.normalizeClass)(["relative shrink-0 w-[45%] overflow-hidden bg-gray-50 transition-colors", beatDragOver[index] ? "bg-blue-50" : ""]),
|
|
1730
2037
|
onDragover: ($event) => onBeatDragOver($event, index),
|
|
1731
2038
|
onDragleave: ($event) => onBeatDragLeave(index),
|
|
1732
2039
|
onDrop: ($event) => onBeatDrop($event, index)
|
|
1733
2040
|
}, [
|
|
1734
|
-
|
|
1735
|
-
|
|
2041
|
+
(0, vue.createElementVNode)("div", {
|
|
2042
|
+
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",
|
|
2043
|
+
"data-testid": `mulmo-script-beat-number-${index}`
|
|
2044
|
+
}, (0, vue.toDisplayString)(index + 1), 9, _hoisted_17),
|
|
2045
|
+
(0, vue.unref)(beatMovieOpen)[index] && (0, vue.unref)(beatMovieUrls)[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createElementVNode)("video", {
|
|
2046
|
+
src: (0, vue.unref)(beatMovieUrls)[index],
|
|
1736
2047
|
class: "w-full object-contain",
|
|
1737
2048
|
controls: "",
|
|
1738
2049
|
autoplay: "",
|
|
1739
2050
|
"data-testid": `mulmo-script-beat-movie-player-${index}`
|
|
1740
|
-
}, null, 8,
|
|
2051
|
+
}, null, 8, _hoisted_18), (0, vue.createElementVNode)("button", {
|
|
1741
2052
|
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",
|
|
1742
2053
|
title: (0, vue.unref)(m).close,
|
|
1743
2054
|
"aria-label": (0, vue.unref)(m).close,
|
|
1744
2055
|
"data-testid": `mulmo-script-beat-movie-close-${index}`,
|
|
1745
|
-
onClick: (0, vue.withModifiers)(($event) => closeBeatMovie(index), ["stop"])
|
|
1746
|
-
}, [..._cache[
|
|
2056
|
+
onClick: (0, vue.withModifiers)(($event) => (0, vue.unref)(closeBeatMovie)(index), ["stop"])
|
|
2057
|
+
}, [..._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 }, [
|
|
1747
2058
|
renderedImages[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
|
|
1748
2059
|
key: 0,
|
|
1749
2060
|
src: renderedImages[index],
|
|
1750
2061
|
class: "w-full object-contain cursor-zoom-in",
|
|
1751
2062
|
alt: `Beat ${index + 1}`,
|
|
1752
2063
|
onClick: ($event) => openLightbox(index)
|
|
1753
|
-
}, null, 8,
|
|
1754
|
-
renderedImages[index] && beatMovies[index] && canFetchMedia.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
2064
|
+
}, null, 8, _hoisted_20)) : (0, vue.createCommentVNode)("", true),
|
|
2065
|
+
renderedImages[index] && (0, vue.unref)(beatMovies)[index] && canFetchMedia.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1755
2066
|
key: 1,
|
|
1756
2067
|
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",
|
|
1757
2068
|
title: (0, vue.unref)(m).play,
|
|
1758
2069
|
"aria-label": (0, vue.unref)(m).play,
|
|
1759
2070
|
"data-testid": `mulmo-script-beat-movie-play-${index}`,
|
|
1760
|
-
onClick: (0, vue.withModifiers)(($event) => playBeatMovie(index), ["stop"])
|
|
1761
|
-
}, [beatMovieLoading[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg",
|
|
2071
|
+
onClick: (0, vue.withModifiers)(($event) => (0, vue.unref)(playBeatMovie)(index), ["stop"])
|
|
2072
|
+
}, [(0, vue.unref)(beatMovieLoading)[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_22, [..._cache[5] || (_cache[5] = [(0, vue.createElementVNode)("circle", {
|
|
1762
2073
|
class: "opacity-25",
|
|
1763
2074
|
cx: "12",
|
|
1764
2075
|
cy: "12",
|
|
@@ -1769,13 +2080,13 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1769
2080
|
class: "opacity-75",
|
|
1770
2081
|
fill: "currentColor",
|
|
1771
2082
|
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1772
|
-
}, null, -1)])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span",
|
|
2083
|
+
}, null, -1)])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_23, "play_arrow"))], 8, _hoisted_21)) : (0, vue.createCommentVNode)("", true),
|
|
1773
2084
|
renderedImages[index] && renderState[index] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1774
2085
|
key: 2,
|
|
1775
2086
|
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",
|
|
1776
|
-
disabled: movieGenerating
|
|
2087
|
+
disabled: (0, vue.unref)(movieGenerating),
|
|
1777
2088
|
onClick: (0, vue.withModifiers)(($event) => regenerateBeat(index), ["stop"])
|
|
1778
|
-
}, " ↺ ", 8,
|
|
2089
|
+
}, " ↺ ", 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", {
|
|
1779
2090
|
class: "animate-spin w-4 h-4 text-green-400",
|
|
1780
2091
|
viewBox: "0 0 24 24",
|
|
1781
2092
|
fill: "none"
|
|
@@ -1790,15 +2101,15 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1790
2101
|
class: "opacity-75",
|
|
1791
2102
|
fill: "currentColor",
|
|
1792
2103
|
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1793
|
-
})], -1)), (0, vue.createElementVNode)("span",
|
|
2104
|
+
})], -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)
|
|
1794
2105
|
], 64)),
|
|
1795
|
-
beatDragOver[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
1796
|
-
!renderedImages[index] && renderState[index] !== "rendering" && !movieGenerating.
|
|
2106
|
+
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),
|
|
2107
|
+
!renderedImages[index] && renderState[index] !== "rendering" && !(0, vue.unref)(movieGenerating) && !(0, vue.unref)(isBeatImageReference)(effectiveBeat$1(index)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1797
2108
|
key: 4,
|
|
1798
2109
|
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",
|
|
1799
2110
|
onClick: ($event) => renderBeat(index)
|
|
1800
|
-
}, (0, vue.toDisplayString)((0, vue.unref)(m).generate), 9,
|
|
1801
|
-
], 42,
|
|
2111
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(m).generate), 9, _hoisted_33)) : (0, vue.createCommentVNode)("", true)
|
|
2112
|
+
], 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", {
|
|
1802
2113
|
class: "opacity-25",
|
|
1803
2114
|
cx: "12",
|
|
1804
2115
|
cy: "12",
|
|
@@ -1813,24 +2124,24 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1813
2124
|
key: 1,
|
|
1814
2125
|
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"]),
|
|
1815
2126
|
onClick: ($event) => playAudio(index)
|
|
1816
|
-
}, (0, vue.toDisplayString)(playingAudio.value?.index === index ? (0, vue.unref)(m).stop : (0, vue.unref)(m).play), 11,
|
|
2127
|
+
}, (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", {
|
|
1817
2128
|
class: "text-xs text-red-400 truncate min-w-0 max-w-[20rem]",
|
|
1818
2129
|
title: audioErrors[index]
|
|
1819
|
-
}, (0, vue.toDisplayString)((0, vue.unref)(m).errPrefix) + " " + (0, vue.toDisplayString)(audioErrors[index]), 9,
|
|
2130
|
+
}, (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", {
|
|
1820
2131
|
key: 0,
|
|
1821
2132
|
class: "text-xs px-2 py-0.5 rounded border border-gray-300 text-gray-500 hover:bg-gray-50 disabled:opacity-50",
|
|
1822
|
-
disabled: movieGenerating
|
|
2133
|
+
disabled: (0, vue.unref)(movieGenerating),
|
|
1823
2134
|
onClick: ($event) => generateAudio(index)
|
|
1824
|
-
}, " ↺ ", 8,
|
|
2135
|
+
}, " ↺ ", 8, _hoisted_41)) : (0, vue.createCommentVNode)("", true)], 64)) : effectiveBeat$1(index).text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1825
2136
|
key: 3,
|
|
1826
2137
|
class: "text-xs px-2 py-0.5 rounded border border-gray-300 text-gray-500 hover:bg-gray-50",
|
|
1827
2138
|
onClick: ($event) => generateAudio(index)
|
|
1828
|
-
}, (0, vue.toDisplayString)((0, vue.unref)(m).generateAudio), 9,
|
|
2139
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(m).generateAudio), 9, _hoisted_42)) : (0, vue.createCommentVNode)("", true)]), (0, vue.createElementVNode)("button", {
|
|
1829
2140
|
class: "text-gray-400 hover:text-gray-600",
|
|
1830
2141
|
title: sourceOpen[index] ? "Hide source" : "Show source",
|
|
1831
2142
|
"data-testid": `mulmo-script-beat-source-toggle-${index}`,
|
|
1832
2143
|
onClick: ($event) => toggleSource(index)
|
|
1833
|
-
}, [..._cache[
|
|
2144
|
+
}, [..._cache[8] || (_cache[8] = [(0, vue.createElementVNode)("svg", {
|
|
1834
2145
|
xmlns: "http://www.w3.org/2000/svg",
|
|
1835
2146
|
class: "w-3.5 h-3.5",
|
|
1836
2147
|
viewBox: "0 0 24 24",
|
|
@@ -1839,36 +2150,36 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1839
2150
|
"stroke-width": "2",
|
|
1840
2151
|
"stroke-linecap": "round",
|
|
1841
2152
|
"stroke-linejoin": "round"
|
|
1842
|
-
}, [(0, vue.createElementVNode)("polyline", { points: "16 18 22 12 16 6" }), (0, vue.createElementVNode)("polyline", { points: "8 6 2 12 8 18" })], -1)])], 8,
|
|
2153
|
+
}, [(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", {
|
|
1843
2154
|
"onUpdate:modelValue": ($event) => sourceText[index] = $event,
|
|
1844
|
-
class: (0, vue.normalizeClass)(["w-full text-xs text-gray-600 bg-gray-50 p-2 font-mono resize-none", isValidBeat(index) ? "outline-none" : "outline outline-2 outline-red-400"]),
|
|
2155
|
+
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"]),
|
|
1845
2156
|
rows: "8",
|
|
1846
2157
|
spellcheck: "false",
|
|
1847
2158
|
"data-testid": `mulmo-script-beat-source-textarea-${index}`
|
|
1848
|
-
}, null, 10,
|
|
1849
|
-
class: (0, vue.normalizeClass)(["px-2 py-1 text-xs rounded border", isValidBeat(index) && !beatSaving[index] ? "border-blue-400 text-blue-600 hover:bg-blue-50 cursor-pointer" : "border-gray-200 text-gray-300 cursor-not-allowed"]),
|
|
1850
|
-
disabled: !isValidBeat(index) || !!beatSaving[index],
|
|
2159
|
+
}, 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", {
|
|
2160
|
+
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"]),
|
|
2161
|
+
disabled: !isValidBeat$1(index) || !!beatSaving[index],
|
|
1851
2162
|
"data-testid": `mulmo-script-beat-update-button-${index}`,
|
|
1852
2163
|
onClick: ($event) => updateBeat(index)
|
|
1853
|
-
}, (0, vue.toDisplayString)(beatSaving[index] ? (0, vue.unref)(m).saving : (0, vue.unref)(m).update), 11,
|
|
1854
|
-
}), 128)), beats.value.length === 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
1855
|
-
(0, vue.createElementVNode)("div",
|
|
2164
|
+
}, (0, vue.toDisplayString)(beatSaving[index] ? (0, vue.unref)(m).saving : (0, vue.unref)(m).update), 11, _hoisted_48)])])) : (0, vue.createCommentVNode)("", true)]);
|
|
2165
|
+
}), 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)),
|
|
2166
|
+
(0, vue.createElementVNode)("div", _hoisted_50, [(0, vue.createElementVNode)("details", {
|
|
1856
2167
|
ref_key: "sourceDetails",
|
|
1857
2168
|
ref: sourceDetails,
|
|
1858
2169
|
class: "script-source",
|
|
1859
|
-
onToggle: _cache[
|
|
2170
|
+
onToggle: _cache[2] || (_cache[2] = ($event) => onSourceToggle($event.target.open))
|
|
1860
2171
|
}, [
|
|
1861
2172
|
(0, vue.createElementVNode)("summary", null, (0, vue.toDisplayString)((0, vue.unref)(m).editSource), 1),
|
|
1862
2173
|
(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
|
|
1863
|
-
"onUpdate:modelValue": _cache[
|
|
2174
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => editableSource.value = $event),
|
|
1864
2175
|
class: (0, vue.normalizeClass)(["script-editor", { "script-editor-invalid": sourceChanged.value && !sourceValid.value }]),
|
|
1865
2176
|
spellcheck: "false"
|
|
1866
2177
|
}, null, 2), [[vue.vModelText, editableSource.value]]),
|
|
1867
|
-
(0, vue.createElementVNode)("div",
|
|
2178
|
+
(0, vue.createElementVNode)("div", _hoisted_51, [(0, vue.createElementVNode)("button", {
|
|
1868
2179
|
class: "apply-btn",
|
|
1869
2180
|
disabled: !sourceChanged.value || !sourceValid.value,
|
|
1870
2181
|
onClick: applySource
|
|
1871
|
-
}, (0, vue.toDisplayString)((0, vue.unref)(m).applyChanges), 9,
|
|
2182
|
+
}, (0, vue.toDisplayString)((0, vue.unref)(m).applyChanges), 9, _hoisted_52), (0, vue.createElementVNode)("button", {
|
|
1872
2183
|
class: "cancel-btn",
|
|
1873
2184
|
onClick: cancelSourceEdit
|
|
1874
2185
|
}, (0, vue.toDisplayString)((0, vue.unref)(m).cancel), 1)])
|
|
@@ -1876,50 +2187,31 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1876
2187
|
class: "copy-btn",
|
|
1877
2188
|
title: (0, vue.unref)(copied) ? "Copied!" : "Copy",
|
|
1878
2189
|
onClick: copyText
|
|
1879
|
-
}, [(0, vue.createElementVNode)("span",
|
|
1880
|
-
lightbox.value ? ((0, vue.openBlock)(), (0, vue.
|
|
2190
|
+
}, [(0, vue.createElementVNode)("span", _hoisted_54, (0, vue.toDisplayString)((0, vue.unref)(copied) ? "check" : "content_copy"), 1)], 8, _hoisted_53), [[vue.vShow, !editing.value]])]),
|
|
2191
|
+
lightbox.value ? ((0, vue.openBlock)(), (0, vue.createBlock)(BeatLightbox_default, {
|
|
1881
2192
|
key: 4,
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
class: (0, vue.normalizeClass)(["group flex-1 cursor-pointer relative transition-colors", i - 1 === lightbox.value.index ? "bg-white/80 hover:bg-white" : i - 1 < lightbox.value.index ? "bg-white/40 hover:bg-white/60" : "bg-white/20 hover:bg-white/40"]),
|
|
1905
|
-
onClick: ($event) => jumpToBeat(i - 1)
|
|
1906
|
-
}, [_cache[23] || (_cache[23] = (0, vue.createElementVNode)("span", { class: "absolute -inset-y-3 inset-x-0" }, null, -1)), beatTooltip(i - 1) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_94, (0, vue.toDisplayString)(beatTooltip(i - 1)), 1)) : (0, vue.createCommentVNode)("", true)], 10, _hoisted_93);
|
|
1907
|
-
}), 128))]), playingAudio.value && playingAudio.value.index === lightbox.value.index ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1908
|
-
key: 0,
|
|
1909
|
-
class: "absolute top-1/2 w-3.5 h-3.5 rounded-full bg-white shadow ring-2 ring-black/30 -translate-y-1/2 -translate-x-1/2 pointer-events-none",
|
|
1910
|
-
style: (0, vue.normalizeStyle)({ left: `${(lightbox.value.index + audioProgress.value) / beats.value.length * 100}%` })
|
|
1911
|
-
}, null, 4)) : (0, vue.createCommentVNode)("", true)])) : (0, vue.createCommentVNode)("", true)]),
|
|
1912
|
-
!lightbox.value.isCharacter ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1913
|
-
key: 1,
|
|
1914
|
-
class: "text-white/60 hover:text-white disabled:opacity-20 text-5xl leading-none",
|
|
1915
|
-
disabled: !hasNext.value,
|
|
1916
|
-
onClick: _cache[3] || (_cache[3] = ($event) => lightboxMove(1))
|
|
1917
|
-
}, " › ", 8, _hoisted_95)) : (0, vue.createCommentVNode)("", true)
|
|
1918
|
-
]), lightbox.value.text || beatAudios[lightbox.value.index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_96, [lightbox.value.text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_97, (0, vue.toDisplayString)(lightbox.value.text), 1)) : (0, vue.createCommentVNode)("", true), beatAudios[lightbox.value.index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1919
|
-
key: 1,
|
|
1920
|
-
class: "absolute top-0 right-4 text-sm px-3 py-1 rounded border border-white/60 text-white/60 hover:bg-white/20",
|
|
1921
|
-
onClick: _cache[4] || (_cache[4] = ($event) => playAudio(lightbox.value.index))
|
|
1922
|
-
}, (0, vue.toDisplayString)(playingAudio.value?.index === lightbox.value.index ? (0, vue.unref)(m).stop : (0, vue.unref)(m).play), 1)) : (0, vue.createCommentVNode)("", true)])) : (0, vue.createCommentVNode)("", true)])])) : (0, vue.createCommentVNode)("", true)
|
|
2193
|
+
lightbox: lightbox.value,
|
|
2194
|
+
"beat-count": beats.value.length,
|
|
2195
|
+
"beat-texts": beatTexts.value,
|
|
2196
|
+
"has-prev": hasPrev.value,
|
|
2197
|
+
"has-next": hasNext.value,
|
|
2198
|
+
"playing-audio-index": playingAudio.value?.index ?? null,
|
|
2199
|
+
"audio-progress": audioProgress.value,
|
|
2200
|
+
"has-current-audio": Boolean(beatAudios[lightbox.value.index]),
|
|
2201
|
+
onClose: closeLightbox,
|
|
2202
|
+
onMove: lightboxMove,
|
|
2203
|
+
onJump: jumpToBeat,
|
|
2204
|
+
onPlayAudio: playAudio
|
|
2205
|
+
}, null, 8, [
|
|
2206
|
+
"lightbox",
|
|
2207
|
+
"beat-count",
|
|
2208
|
+
"beat-texts",
|
|
2209
|
+
"has-prev",
|
|
2210
|
+
"has-next",
|
|
2211
|
+
"playing-audio-index",
|
|
2212
|
+
"audio-progress",
|
|
2213
|
+
"has-current-audio"
|
|
2214
|
+
])) : (0, vue.createCommentVNode)("", true)
|
|
1923
2215
|
]);
|
|
1924
2216
|
};
|
|
1925
2217
|
}
|
|
@@ -1933,7 +2225,7 @@ var _plugin_vue_export_helper_default = (sfc, props) => {
|
|
|
1933
2225
|
};
|
|
1934
2226
|
//#endregion
|
|
1935
2227
|
//#region src/vue/View.vue
|
|
1936
|
-
var View_default = /*#__PURE__*/ _plugin_vue_export_helper_default(View_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
2228
|
+
var View_default = /*#__PURE__*/ _plugin_vue_export_helper_default(View_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-3776a73b"]]);
|
|
1937
2229
|
//#endregion
|
|
1938
2230
|
//#region src/vue/Preview.vue?vue&type=script&setup=true&lang.ts
|
|
1939
2231
|
var _hoisted_1 = {
|