@mulmoclaude/mulmoscript-plugin 1.0.0 → 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/{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/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 +1016 -715
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +1013 -712
- 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 {
|
|
@@ -188,6 +270,333 @@ var EMPTY_ADAPTER = {};
|
|
|
188
270
|
function useHostAdapter() {
|
|
189
271
|
return (0, vue.inject)(MULMOSCRIPT_HOST_ADAPTER_KEY, EMPTY_ADAPTER);
|
|
190
272
|
}
|
|
273
|
+
//#endregion
|
|
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
|
+
}
|
|
191
600
|
/** Reactive message bundle for the active host locale. The plugin carries its
|
|
192
601
|
* own translations (no host i18n dependency); it reads the locale off the
|
|
193
602
|
* injected `BrowserPluginRuntime.locale` ref and falls back to English.
|
|
@@ -451,254 +860,517 @@ var useT = (0, gui_chat_protocol_vue.createUseT)({
|
|
|
451
860
|
}
|
|
452
861
|
});
|
|
453
862
|
//#endregion
|
|
454
|
-
//#region src/vue/
|
|
455
|
-
var _hoisted_1$
|
|
456
|
-
var _hoisted_2$
|
|
457
|
-
var _hoisted_3$
|
|
458
|
-
var _hoisted_4 = {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
};
|
|
462
|
-
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 = {
|
|
463
870
|
key: 0,
|
|
464
|
-
class: "
|
|
465
|
-
"data-testid": "mulmo-script-description"
|
|
466
|
-
};
|
|
467
|
-
var _hoisted_6 = { class: "flex items-center gap-3 mt-1 text-xs text-gray-400" };
|
|
468
|
-
var _hoisted_7 = { key: 0 };
|
|
469
|
-
var _hoisted_8 = {
|
|
470
|
-
key: 1,
|
|
471
|
-
class: "truncate"
|
|
871
|
+
class: "relative w-full h-1"
|
|
472
872
|
};
|
|
473
|
-
var
|
|
474
|
-
var
|
|
475
|
-
|
|
476
|
-
"title",
|
|
477
|
-
"aria-label"
|
|
478
|
-
];
|
|
479
|
-
var _hoisted_11 = ["disabled"];
|
|
480
|
-
var _hoisted_12 = ["title", "aria-label"];
|
|
481
|
-
var _hoisted_13 = ["disabled"];
|
|
482
|
-
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 = {
|
|
483
876
|
key: 0,
|
|
484
|
-
class: "
|
|
485
|
-
viewBox: "0 0 24 24",
|
|
486
|
-
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"
|
|
487
878
|
};
|
|
488
|
-
var
|
|
489
|
-
var
|
|
490
|
-
var _hoisted_17 = ["title", "aria-label"];
|
|
491
|
-
var _hoisted_18 = ["disabled"];
|
|
492
|
-
var _hoisted_19 = {
|
|
879
|
+
var _hoisted_10$3 = ["disabled"];
|
|
880
|
+
var _hoisted_11$3 = {
|
|
493
881
|
key: 0,
|
|
494
|
-
class: "
|
|
495
|
-
viewBox: "0 0 24 24",
|
|
496
|
-
fill: "none"
|
|
882
|
+
class: "relative w-screen flex justify-center px-16"
|
|
497
883
|
};
|
|
498
|
-
var
|
|
499
|
-
var _hoisted_21 = {
|
|
884
|
+
var _hoisted_12$3 = {
|
|
500
885
|
key: 0,
|
|
501
|
-
|
|
502
|
-
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"
|
|
503
|
-
};
|
|
504
|
-
var _hoisted_22 = { class: "flex-1 min-w-0" };
|
|
505
|
-
var _hoisted_23 = { class: "font-medium" };
|
|
506
|
-
var _hoisted_24 = { class: "break-words whitespace-pre-wrap mt-0.5" };
|
|
507
|
-
var _hoisted_25 = ["disabled"];
|
|
508
|
-
var _hoisted_26 = {
|
|
509
|
-
key: 1,
|
|
510
|
-
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)]"
|
|
511
887
|
};
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
var
|
|
515
|
-
|
|
516
|
-
|
|
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 = [
|
|
517
965
|
"onDragover",
|
|
518
966
|
"onDragleave",
|
|
519
967
|
"onDrop"
|
|
520
968
|
];
|
|
521
|
-
var
|
|
969
|
+
var _hoisted_7$2 = [
|
|
522
970
|
"src",
|
|
523
971
|
"alt",
|
|
524
972
|
"onClick"
|
|
525
973
|
];
|
|
526
|
-
var
|
|
974
|
+
var _hoisted_8$2 = {
|
|
527
975
|
key: 1,
|
|
528
976
|
class: "animate-spin w-4 h-4 text-green-400",
|
|
529
977
|
viewBox: "0 0 24 24",
|
|
530
978
|
fill: "none"
|
|
531
979
|
};
|
|
532
|
-
var
|
|
980
|
+
var _hoisted_9$2 = {
|
|
533
981
|
key: 2,
|
|
534
982
|
class: "text-xs text-red-400 text-center px-1"
|
|
535
983
|
};
|
|
536
|
-
var
|
|
984
|
+
var _hoisted_10$2 = {
|
|
537
985
|
key: 3,
|
|
538
986
|
class: "text-xs text-gray-300 text-center px-1 leading-tight"
|
|
539
987
|
};
|
|
540
|
-
var
|
|
988
|
+
var _hoisted_11$2 = {
|
|
541
989
|
key: 4,
|
|
542
990
|
class: "absolute bottom-0 inset-x-0 text-center text-xs text-gray-400 bg-white/70 py-0.5 pointer-events-none"
|
|
543
991
|
};
|
|
544
|
-
var
|
|
992
|
+
var _hoisted_12$2 = {
|
|
545
993
|
key: 5,
|
|
546
994
|
class: "absolute inset-0 flex items-center justify-center bg-blue-50/80 pointer-events-none"
|
|
547
995
|
};
|
|
548
|
-
var
|
|
549
|
-
var
|
|
550
|
-
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 = {
|
|
551
999
|
key: 0,
|
|
552
1000
|
class: "inline-block animate-spin"
|
|
553
1001
|
};
|
|
554
|
-
var
|
|
555
|
-
var
|
|
556
|
-
var
|
|
1002
|
+
var _hoisted_16$1 = { key: 1 };
|
|
1003
|
+
var _hoisted_17$1 = ["disabled", "onClick"];
|
|
1004
|
+
var _hoisted_18$1 = {
|
|
557
1005
|
key: 0,
|
|
558
1006
|
class: "animate-spin w-3 h-3",
|
|
559
1007
|
viewBox: "0 0 24 24",
|
|
560
1008
|
fill: "none"
|
|
561
1009
|
};
|
|
562
|
-
var
|
|
563
|
-
var
|
|
564
|
-
|
|
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 = {
|
|
565
1261
|
key: 2,
|
|
566
1262
|
class: "flex-1 overflow-hidden",
|
|
567
1263
|
"data-testid": "mulmo-script-deck-editor"
|
|
568
1264
|
};
|
|
569
|
-
var
|
|
570
|
-
var
|
|
1265
|
+
var _hoisted_15 = { class: "flex gap-3 items-stretch" };
|
|
1266
|
+
var _hoisted_16 = [
|
|
571
1267
|
"onDragover",
|
|
572
1268
|
"onDragleave",
|
|
573
1269
|
"onDrop"
|
|
574
1270
|
];
|
|
575
|
-
var
|
|
576
|
-
var
|
|
1271
|
+
var _hoisted_17 = ["data-testid"];
|
|
1272
|
+
var _hoisted_18 = ["src", "data-testid"];
|
|
1273
|
+
var _hoisted_19 = [
|
|
577
1274
|
"title",
|
|
578
1275
|
"aria-label",
|
|
579
1276
|
"data-testid",
|
|
580
1277
|
"onClick"
|
|
581
1278
|
];
|
|
582
|
-
var
|
|
1279
|
+
var _hoisted_20 = [
|
|
583
1280
|
"src",
|
|
584
1281
|
"alt",
|
|
585
1282
|
"onClick"
|
|
586
1283
|
];
|
|
587
|
-
var
|
|
1284
|
+
var _hoisted_21 = [
|
|
588
1285
|
"title",
|
|
589
1286
|
"aria-label",
|
|
590
1287
|
"data-testid",
|
|
591
1288
|
"onClick"
|
|
592
1289
|
];
|
|
593
|
-
var
|
|
1290
|
+
var _hoisted_22 = {
|
|
594
1291
|
key: 0,
|
|
595
1292
|
class: "animate-spin w-5 h-5",
|
|
596
1293
|
viewBox: "0 0 24 24",
|
|
597
1294
|
fill: "none"
|
|
598
1295
|
};
|
|
599
|
-
var
|
|
1296
|
+
var _hoisted_23 = {
|
|
600
1297
|
key: 1,
|
|
601
1298
|
class: "material-icons text-3xl"
|
|
602
1299
|
};
|
|
603
|
-
var
|
|
604
|
-
var
|
|
1300
|
+
var _hoisted_24 = ["disabled", "onClick"];
|
|
1301
|
+
var _hoisted_25 = {
|
|
605
1302
|
key: 3,
|
|
606
1303
|
class: "w-full aspect-video flex flex-col items-center justify-center gap-1 p-2"
|
|
607
1304
|
};
|
|
608
|
-
var
|
|
609
|
-
var
|
|
1305
|
+
var _hoisted_26 = { class: "text-xs text-green-500" };
|
|
1306
|
+
var _hoisted_27 = {
|
|
610
1307
|
key: 1,
|
|
611
1308
|
class: "text-xs text-red-400 text-center"
|
|
612
1309
|
};
|
|
613
|
-
var
|
|
1310
|
+
var _hoisted_28 = {
|
|
614
1311
|
key: 0,
|
|
615
1312
|
class: "text-xs text-gray-400 text-center italic leading-relaxed px-1"
|
|
616
1313
|
};
|
|
617
|
-
var
|
|
1314
|
+
var _hoisted_29 = {
|
|
618
1315
|
key: 1,
|
|
619
1316
|
class: "text-xs text-gray-300"
|
|
620
1317
|
};
|
|
621
|
-
var
|
|
1318
|
+
var _hoisted_30 = {
|
|
622
1319
|
key: 2,
|
|
623
1320
|
class: "absolute inset-0 flex items-center justify-center bg-blue-50/80 pointer-events-none"
|
|
624
1321
|
};
|
|
625
|
-
var
|
|
626
|
-
var
|
|
1322
|
+
var _hoisted_31 = { class: "text-xs text-blue-500 font-medium" };
|
|
1323
|
+
var _hoisted_32 = {
|
|
627
1324
|
key: 3,
|
|
628
1325
|
class: "absolute bottom-0 inset-x-0 text-center text-xs text-gray-400 bg-white/70 py-0.5 pointer-events-none"
|
|
629
1326
|
};
|
|
630
|
-
var
|
|
631
|
-
var
|
|
632
|
-
var
|
|
633
|
-
var
|
|
634
|
-
var
|
|
635
|
-
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 = {
|
|
636
1333
|
key: 0,
|
|
637
1334
|
class: "animate-spin w-3 h-3 text-green-400",
|
|
638
1335
|
viewBox: "0 0 24 24",
|
|
639
1336
|
fill: "none"
|
|
640
1337
|
};
|
|
641
|
-
var
|
|
642
|
-
var
|
|
643
|
-
var
|
|
644
|
-
var
|
|
645
|
-
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 = [
|
|
646
1343
|
"title",
|
|
647
1344
|
"data-testid",
|
|
648
1345
|
"onClick"
|
|
649
1346
|
];
|
|
650
|
-
var
|
|
1347
|
+
var _hoisted_44 = {
|
|
651
1348
|
key: 0,
|
|
652
1349
|
class: "border-t border-gray-100"
|
|
653
1350
|
};
|
|
654
|
-
var
|
|
655
|
-
var
|
|
656
|
-
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 = {
|
|
657
1354
|
key: 0,
|
|
658
1355
|
class: "text-xs text-red-600",
|
|
659
1356
|
role: "alert"
|
|
660
1357
|
};
|
|
661
|
-
var
|
|
1358
|
+
var _hoisted_48 = [
|
|
662
1359
|
"disabled",
|
|
663
1360
|
"data-testid",
|
|
664
1361
|
"onClick"
|
|
665
1362
|
];
|
|
666
|
-
var
|
|
1363
|
+
var _hoisted_49 = {
|
|
667
1364
|
key: 0,
|
|
668
1365
|
class: "flex items-center justify-center h-32 text-gray-400 text-sm"
|
|
669
1366
|
};
|
|
670
|
-
var
|
|
671
|
-
var
|
|
672
|
-
var
|
|
673
|
-
var
|
|
674
|
-
var
|
|
675
|
-
var _hoisted_86 = ["title"];
|
|
676
|
-
var _hoisted_87 = { class: "flex items-center gap-4" };
|
|
677
|
-
var _hoisted_88 = ["disabled"];
|
|
678
|
-
var _hoisted_89 = { class: "flex flex-col items-center" };
|
|
679
|
-
var _hoisted_90 = ["src"];
|
|
680
|
-
var _hoisted_91 = {
|
|
681
|
-
key: 0,
|
|
682
|
-
class: "relative w-full h-1"
|
|
683
|
-
};
|
|
684
|
-
var _hoisted_92 = { class: "flex gap-1 h-full" };
|
|
685
|
-
var _hoisted_93 = ["onClick"];
|
|
686
|
-
var _hoisted_94 = {
|
|
687
|
-
key: 0,
|
|
688
|
-
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"
|
|
689
|
-
};
|
|
690
|
-
var _hoisted_95 = ["disabled"];
|
|
691
|
-
var _hoisted_96 = {
|
|
692
|
-
key: 0,
|
|
693
|
-
class: "relative w-screen flex justify-center px-16"
|
|
694
|
-
};
|
|
695
|
-
var _hoisted_97 = {
|
|
696
|
-
key: 0,
|
|
697
|
-
class: "max-w-[80vw] text-center text-white leading-relaxed text-[clamp(0.8rem,1.76vw,1.6rem)]"
|
|
698
|
-
};
|
|
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" };
|
|
699
1372
|
var SILENT_BEAT_DEFAULT_SEC = 3;
|
|
700
1373
|
var MS_PER_SECOND = 1e3;
|
|
701
|
-
var DECK_SAVE_DEBOUNCE_MS = 300;
|
|
702
1374
|
var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.defineComponent)({
|
|
703
1375
|
__name: "View",
|
|
704
1376
|
props: { selectedResult: {} },
|
|
@@ -723,39 +1395,34 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
723
1395
|
const beatSaveErrors = (0, vue.reactive)({});
|
|
724
1396
|
const beatSaving = (0, vue.reactive)({});
|
|
725
1397
|
const localOverrides = (0, vue.reactive)({});
|
|
726
|
-
const movieGenerating = (0, vue.ref)(false);
|
|
727
|
-
const movieDownloading = (0, vue.ref)(false);
|
|
728
|
-
const moviePath = (0, vue.ref)(null);
|
|
729
|
-
const movieError = (0, vue.ref)(null);
|
|
730
|
-
const pdfGenerating = (0, vue.ref)(false);
|
|
731
|
-
const pdfDownloading = (0, vue.ref)(false);
|
|
732
|
-
const pdfPath = (0, vue.ref)(null);
|
|
733
1398
|
const beatAudios = (0, vue.reactive)({});
|
|
734
1399
|
const audioState = (0, vue.reactive)({});
|
|
735
1400
|
const audioErrors = (0, vue.reactive)({});
|
|
736
|
-
const beatMovies = (0, vue.reactive)({});
|
|
737
|
-
const beatMovieUrls = (0, vue.reactive)({});
|
|
738
|
-
const beatMovieOpen = (0, vue.reactive)({});
|
|
739
|
-
const beatMovieLoading = (0, vue.reactive)({});
|
|
740
1401
|
const playingAudio = (0, vue.ref)(null);
|
|
741
1402
|
const silentPlaybackTimer = (0, vue.ref)(null);
|
|
742
1403
|
const audioProgress = (0, vue.ref)(0);
|
|
743
1404
|
const beatListEl = (0, vue.ref)(null);
|
|
744
1405
|
const lightbox = (0, vue.ref)(null);
|
|
745
|
-
const charRenderState = (0, vue.reactive)({});
|
|
746
|
-
const charImages = (0, vue.reactive)({});
|
|
747
|
-
const charErrors = (0, vue.reactive)({});
|
|
748
|
-
const charDragOver = (0, vue.reactive)({});
|
|
749
1406
|
const beatDragOver = (0, vue.reactive)({});
|
|
750
1407
|
const anyBeatRendering = (0, vue.computed)(() => Object.values(renderState).some((state) => state === "rendering"));
|
|
751
|
-
const characterKeys = (0, vue.computed)(() => {
|
|
752
|
-
const imgs = script.value.imageParams?.images ?? {};
|
|
753
|
-
return Object.keys(imgs).filter((key) => imgs[key]?.type === "imagePrompt");
|
|
754
|
-
});
|
|
755
1408
|
const chatSessionId = (0, vue.computed)(() => adapter.chatSessionId?.value);
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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
|
+
});
|
|
759
1426
|
function stopPlayingAudio() {
|
|
760
1427
|
stopAllPlayback();
|
|
761
1428
|
}
|
|
@@ -763,7 +1430,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
763
1430
|
stopPlayingAudio();
|
|
764
1431
|
lightbox.value = {
|
|
765
1432
|
src: renderedImages[index],
|
|
766
|
-
text: effectiveBeat(index).text,
|
|
1433
|
+
text: effectiveBeat$1(index).text,
|
|
767
1434
|
index
|
|
768
1435
|
};
|
|
769
1436
|
}
|
|
@@ -774,7 +1441,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
774
1441
|
const isPlayReady = (0, vue.computed)(() => {
|
|
775
1442
|
if (beats.value.length === 0) return false;
|
|
776
1443
|
if (!renderedImages[0]) return false;
|
|
777
|
-
if (effectiveBeat(0).text && !beatAudios[0]) return false;
|
|
1444
|
+
if (effectiveBeat$1(0).text && !beatAudios[0]) return false;
|
|
778
1445
|
return true;
|
|
779
1446
|
});
|
|
780
1447
|
function playPresentation() {
|
|
@@ -795,19 +1462,19 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
795
1462
|
}
|
|
796
1463
|
function playBeat(index) {
|
|
797
1464
|
stopAllPlayback();
|
|
798
|
-
if (!Boolean(effectiveBeat(index).text)) {
|
|
1465
|
+
if (!Boolean(effectiveBeat$1(index).text)) {
|
|
799
1466
|
scheduleSilentAdvance(index);
|
|
800
1467
|
return;
|
|
801
1468
|
}
|
|
802
1469
|
if (beatAudios[index]) playAudio(index);
|
|
803
1470
|
}
|
|
804
1471
|
function scheduleSilentAdvance(index) {
|
|
805
|
-
const
|
|
1472
|
+
const seconds = resolveSilentAdvanceSeconds(effectiveBeat$1(index).duration, SILENT_BEAT_DEFAULT_SEC);
|
|
806
1473
|
const timer = setTimeout(() => {
|
|
807
1474
|
if (silentPlaybackTimer.value?.index !== index) return;
|
|
808
1475
|
silentPlaybackTimer.value = null;
|
|
809
1476
|
if (lightbox.value?.index === index) advanceFromBeat(index);
|
|
810
|
-
},
|
|
1477
|
+
}, seconds * MS_PER_SECOND);
|
|
811
1478
|
silentPlaybackTimer.value = {
|
|
812
1479
|
index,
|
|
813
1480
|
timer
|
|
@@ -829,6 +1496,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
829
1496
|
for (let i = lightbox.value.index + 1; i < beats.value.length; i++) if (renderedImages[i]) return true;
|
|
830
1497
|
return false;
|
|
831
1498
|
});
|
|
1499
|
+
const beatTexts = (0, vue.computed)(() => beats.value.map((_, index) => effectiveBeat$1(index).text));
|
|
832
1500
|
function jumpToBeat(index) {
|
|
833
1501
|
if (!lightbox.value) return;
|
|
834
1502
|
if (index === lightbox.value.index) return;
|
|
@@ -837,10 +1505,6 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
837
1505
|
openLightbox(index);
|
|
838
1506
|
if (wasPlaying) playBeat(index);
|
|
839
1507
|
}
|
|
840
|
-
function beatTooltip(index) {
|
|
841
|
-
const text = effectiveBeat(index).text ?? "";
|
|
842
|
-
return text.length > 80 ? `${text.slice(0, 80)}…` : text;
|
|
843
|
-
}
|
|
844
1508
|
function lightboxMove(delta) {
|
|
845
1509
|
if (!lightbox.value) return;
|
|
846
1510
|
const total = beats.value.length;
|
|
@@ -863,31 +1527,8 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
863
1527
|
...script.value,
|
|
864
1528
|
beats: beats.value.map((beat, i) => localOverrides[i] ?? beat)
|
|
865
1529
|
}));
|
|
866
|
-
const scriptSourceText = (0, vue.computed)(() =>
|
|
867
|
-
|
|
868
|
-
const deckScriptInput = (0, vue.computed)(() => effectiveScript.value);
|
|
869
|
-
let deckSaveTimer = null;
|
|
870
|
-
let pendingDeckScript = null;
|
|
871
|
-
function scheduleDeckSave(next) {
|
|
872
|
-
pendingDeckScript = next;
|
|
873
|
-
if (deckSaveTimer) clearTimeout(deckSaveTimer);
|
|
874
|
-
deckSaveTimer = setTimeout(() => {
|
|
875
|
-
flushDeckSave();
|
|
876
|
-
}, DECK_SAVE_DEBOUNCE_MS);
|
|
877
|
-
}
|
|
878
|
-
async function flushDeckSave() {
|
|
879
|
-
deckSaveTimer = null;
|
|
880
|
-
const next = pendingDeckScript;
|
|
881
|
-
pendingDeckScript = null;
|
|
882
|
-
if (!next || !filePath.value) return;
|
|
883
|
-
const response = await api.call("updateScript", {
|
|
884
|
-
filePath: filePath.value,
|
|
885
|
-
script: next
|
|
886
|
-
});
|
|
887
|
-
if (!response.ok) {
|
|
888
|
-
console.error("[presentMulmoScript] deck save failed:", response.error);
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
1530
|
+
const scriptSourceText$1 = (0, vue.computed)(() => scriptSourceText(effectiveScript.value));
|
|
1531
|
+
function commitScript(next) {
|
|
891
1532
|
emit("updateResult", {
|
|
892
1533
|
...props.selectedResult,
|
|
893
1534
|
data: {
|
|
@@ -896,14 +1537,14 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
896
1537
|
}
|
|
897
1538
|
});
|
|
898
1539
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1540
|
+
const { isDeck, deckScriptInput, onDeckUpdate, flushPendingDeckSave } = useDeckEditor({
|
|
1541
|
+
api,
|
|
1542
|
+
filePath,
|
|
1543
|
+
effectiveScript,
|
|
1544
|
+
commitScript
|
|
1545
|
+
});
|
|
902
1546
|
(0, vue.onBeforeUnmount)(() => {
|
|
903
|
-
|
|
904
|
-
clearTimeout(deckSaveTimer);
|
|
905
|
-
flushDeckSave();
|
|
906
|
-
}
|
|
1547
|
+
flushPendingDeckSave();
|
|
907
1548
|
resetBeatMovies();
|
|
908
1549
|
unsubscribeGenerationEvents();
|
|
909
1550
|
});
|
|
@@ -920,11 +1561,11 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
920
1561
|
async function onSourceToggle(open) {
|
|
921
1562
|
editing.value = open;
|
|
922
1563
|
if (open) {
|
|
923
|
-
let text = scriptSourceText.value;
|
|
1564
|
+
let text = scriptSourceText$1.value;
|
|
924
1565
|
if (filePath.value) {
|
|
925
1566
|
const response = await api.call("save", { filePath: filePath.value });
|
|
926
1567
|
const diskScript = response.ok ? response.data.script : void 0;
|
|
927
|
-
if (diskScript) text =
|
|
1568
|
+
if (diskScript) text = scriptSourceText(diskScript);
|
|
928
1569
|
}
|
|
929
1570
|
editableSource.value = text;
|
|
930
1571
|
loadedSource.value = text;
|
|
@@ -938,7 +1579,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
938
1579
|
try {
|
|
939
1580
|
parsed = JSON.parse(editableSource.value);
|
|
940
1581
|
} catch (err) {
|
|
941
|
-
alert(errorMessage(err));
|
|
1582
|
+
alert(require_plugin.errorMessage(err));
|
|
942
1583
|
return;
|
|
943
1584
|
}
|
|
944
1585
|
const response = await api.call("updateScript", {
|
|
@@ -949,31 +1590,25 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
949
1590
|
alert(response.error || "Update failed");
|
|
950
1591
|
return;
|
|
951
1592
|
}
|
|
952
|
-
|
|
953
|
-
...props.selectedResult,
|
|
954
|
-
data: {
|
|
955
|
-
...props.selectedResult.data,
|
|
956
|
-
script: parsed
|
|
957
|
-
}
|
|
958
|
-
});
|
|
1593
|
+
commitScript(parsed);
|
|
959
1594
|
if (sourceDetails.value) sourceDetails.value.open = false;
|
|
960
1595
|
await initializeScript();
|
|
961
1596
|
}
|
|
962
1597
|
async function copyText() {
|
|
963
|
-
await copy(scriptSourceText.value);
|
|
1598
|
+
await copy(scriptSourceText$1.value);
|
|
964
1599
|
}
|
|
965
|
-
function effectiveBeat(index) {
|
|
966
|
-
return localOverrides
|
|
1600
|
+
function effectiveBeat$1(index) {
|
|
1601
|
+
return effectiveBeat(localOverrides, beats.value, index);
|
|
967
1602
|
}
|
|
968
1603
|
function toggleSource(index) {
|
|
969
1604
|
if (!sourceOpen[index]) {
|
|
970
|
-
sourceText[index] =
|
|
1605
|
+
sourceText[index] = scriptSourceText(effectiveBeat$1(index));
|
|
971
1606
|
Reflect.deleteProperty(beatSaveErrors, index);
|
|
972
1607
|
}
|
|
973
1608
|
sourceOpen[index] = !sourceOpen[index];
|
|
974
1609
|
}
|
|
975
|
-
function isValidBeat(index) {
|
|
976
|
-
return
|
|
1610
|
+
function isValidBeat$1(index) {
|
|
1611
|
+
return isValidBeat(sourceText[index], _mulmocast_types.mulmoBeatSchema);
|
|
977
1612
|
}
|
|
978
1613
|
async function updateBeat(index) {
|
|
979
1614
|
let beat;
|
|
@@ -982,12 +1617,12 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
982
1617
|
} catch (err) {
|
|
983
1618
|
beatSaveErrors[index] = {
|
|
984
1619
|
kind: "invalidJson",
|
|
985
|
-
error: errorMessage(err)
|
|
1620
|
+
error: require_plugin.errorMessage(err)
|
|
986
1621
|
};
|
|
987
1622
|
return;
|
|
988
1623
|
}
|
|
989
|
-
const prevImage = JSON.stringify(effectiveBeat(index).image);
|
|
990
|
-
const prevText = effectiveBeat(index).text;
|
|
1624
|
+
const prevImage = JSON.stringify(effectiveBeat$1(index).image);
|
|
1625
|
+
const prevText = effectiveBeat$1(index).text;
|
|
991
1626
|
const requestedFilePath = filePath.value;
|
|
992
1627
|
Reflect.deleteProperty(beatSaveErrors, index);
|
|
993
1628
|
beatSaving[index] = true;
|
|
@@ -996,7 +1631,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
996
1631
|
beatIndex: index,
|
|
997
1632
|
beat
|
|
998
1633
|
});
|
|
999
|
-
if (staleSince(requestedFilePath)) return;
|
|
1634
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1000
1635
|
Reflect.deleteProperty(beatSaving, index);
|
|
1001
1636
|
if (!response.ok) {
|
|
1002
1637
|
beatSaveErrors[index] = {
|
|
@@ -1027,7 +1662,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1027
1662
|
beatIndex: index,
|
|
1028
1663
|
chatSessionId: chatSessionId.value
|
|
1029
1664
|
});
|
|
1030
|
-
if (staleSince(requestedFilePath)) return;
|
|
1665
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1031
1666
|
if (!response.ok) {
|
|
1032
1667
|
renderErrors[index] = response.error || "Render failed";
|
|
1033
1668
|
renderState[index] = "error";
|
|
@@ -1036,7 +1671,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1036
1671
|
renderedImages[index] = response.data.image ?? "";
|
|
1037
1672
|
renderState[index] = "done";
|
|
1038
1673
|
refreshMissingCharacterImages();
|
|
1039
|
-
if (beatMayHaveMovie(effectiveBeat(index))) loadExistingBeatMovie(index);
|
|
1674
|
+
if (beatMayHaveMovie(effectiveBeat$1(index))) loadExistingBeatMovie(index);
|
|
1040
1675
|
}
|
|
1041
1676
|
async function regenerateBeat(index) {
|
|
1042
1677
|
const requestedFilePath = filePath.value;
|
|
@@ -1049,7 +1684,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1049
1684
|
force: true,
|
|
1050
1685
|
chatSessionId: chatSessionId.value
|
|
1051
1686
|
});
|
|
1052
|
-
if (staleSince(requestedFilePath)) return;
|
|
1687
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1053
1688
|
if (!response.ok) {
|
|
1054
1689
|
renderErrors[index] = response.error || "Render failed";
|
|
1055
1690
|
renderState[index] = "error";
|
|
@@ -1057,10 +1692,10 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1057
1692
|
}
|
|
1058
1693
|
renderedImages[index] = response.data.image ?? "";
|
|
1059
1694
|
renderState[index] = "done";
|
|
1060
|
-
if (beatMayHaveMovie(effectiveBeat(index))) loadExistingBeatMovie(index);
|
|
1695
|
+
if (beatMayHaveMovie(effectiveBeat$1(index))) loadExistingBeatMovie(index);
|
|
1061
1696
|
}
|
|
1062
|
-
function staleSince(requestedFilePath) {
|
|
1063
|
-
return filePath.value
|
|
1697
|
+
function staleSince$1(requestedFilePath) {
|
|
1698
|
+
return staleSince(filePath.value, requestedFilePath);
|
|
1064
1699
|
}
|
|
1065
1700
|
async function loadExistingBeatImage(index) {
|
|
1066
1701
|
const requestedFilePath = filePath.value;
|
|
@@ -1068,7 +1703,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1068
1703
|
filePath: requestedFilePath,
|
|
1069
1704
|
beatIndex: index
|
|
1070
1705
|
});
|
|
1071
|
-
if (staleSince(requestedFilePath)) return;
|
|
1706
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1072
1707
|
if (response.ok && response.data.image) {
|
|
1073
1708
|
renderedImages[index] = response.data.image;
|
|
1074
1709
|
renderState[index] = "done";
|
|
@@ -1080,61 +1715,12 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1080
1715
|
filePath: requestedFilePath,
|
|
1081
1716
|
beatIndex: index
|
|
1082
1717
|
});
|
|
1083
|
-
if (staleSince(requestedFilePath)) return;
|
|
1718
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1084
1719
|
if (response.ok && response.data.audio) {
|
|
1085
1720
|
beatAudios[index] = response.data.audio;
|
|
1086
1721
|
audioState[index] = "done";
|
|
1087
1722
|
}
|
|
1088
1723
|
}
|
|
1089
|
-
async function loadExistingBeatMovie(index) {
|
|
1090
|
-
const requestedFilePath = filePath.value;
|
|
1091
|
-
const response = await api.call("beatMovie", {
|
|
1092
|
-
filePath: requestedFilePath,
|
|
1093
|
-
beatIndex: index
|
|
1094
|
-
});
|
|
1095
|
-
if (staleSince(requestedFilePath)) return;
|
|
1096
|
-
if (response.ok && response.data.moviePath) beatMovies[index] = response.data.moviePath;
|
|
1097
|
-
}
|
|
1098
|
-
async function playBeatMovie(index) {
|
|
1099
|
-
const fetchMediaBlob = adapter.fetchMediaBlob;
|
|
1100
|
-
if (!fetchMediaBlob || !beatMovies[index] || beatMovieLoading[index]) return;
|
|
1101
|
-
if (beatMovieUrls[index]) {
|
|
1102
|
-
beatMovieOpen[index] = true;
|
|
1103
|
-
return;
|
|
1104
|
-
}
|
|
1105
|
-
beatMovieLoading[index] = true;
|
|
1106
|
-
try {
|
|
1107
|
-
const blob = new Blob([await fetchMediaBlob({ moviePath: beatMovies[index] })], { type: "video/mp4" });
|
|
1108
|
-
beatMovieUrls[index] = URL.createObjectURL(blob);
|
|
1109
|
-
beatMovieOpen[index] = true;
|
|
1110
|
-
} catch (err) {
|
|
1111
|
-
alert(errorMessage(err));
|
|
1112
|
-
} finally {
|
|
1113
|
-
Reflect.deleteProperty(beatMovieLoading, index);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
function closeBeatMovie(index) {
|
|
1117
|
-
Reflect.deleteProperty(beatMovieOpen, index);
|
|
1118
|
-
}
|
|
1119
|
-
function invalidateBeatMovie(index) {
|
|
1120
|
-
if (beatMovieUrls[index]) URL.revokeObjectURL(beatMovieUrls[index]);
|
|
1121
|
-
[
|
|
1122
|
-
beatMovies,
|
|
1123
|
-
beatMovieUrls,
|
|
1124
|
-
beatMovieOpen
|
|
1125
|
-
].forEach((map) => Reflect.deleteProperty(map, index));
|
|
1126
|
-
}
|
|
1127
|
-
function resetBeatMovies() {
|
|
1128
|
-
Object.values(beatMovieUrls).forEach((url) => URL.revokeObjectURL(url));
|
|
1129
|
-
[
|
|
1130
|
-
beatMovies,
|
|
1131
|
-
beatMovieUrls,
|
|
1132
|
-
beatMovieOpen,
|
|
1133
|
-
beatMovieLoading
|
|
1134
|
-
].forEach((map) => {
|
|
1135
|
-
Object.keys(map).forEach((key) => Reflect.deleteProperty(map, key));
|
|
1136
|
-
});
|
|
1137
|
-
}
|
|
1138
1724
|
async function generateAudio(index) {
|
|
1139
1725
|
const requestedFilePath = filePath.value;
|
|
1140
1726
|
audioState[index] = "generating";
|
|
@@ -1144,7 +1730,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1144
1730
|
beatIndex: index,
|
|
1145
1731
|
chatSessionId: chatSessionId.value
|
|
1146
1732
|
});
|
|
1147
|
-
if (staleSince(requestedFilePath)) return;
|
|
1733
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1148
1734
|
if (!response.ok) {
|
|
1149
1735
|
audioErrors[index] = response.error || "Audio generation failed";
|
|
1150
1736
|
audioState[index] = "error";
|
|
@@ -1197,14 +1783,9 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1197
1783
|
Reflect.deleteProperty(renderErrors, index);
|
|
1198
1784
|
let imageData;
|
|
1199
1785
|
try {
|
|
1200
|
-
imageData = await
|
|
1201
|
-
const reader = new FileReader();
|
|
1202
|
-
reader.onload = () => resolve(reader.result);
|
|
1203
|
-
reader.onerror = reject;
|
|
1204
|
-
reader.readAsDataURL(file);
|
|
1205
|
-
});
|
|
1786
|
+
imageData = await readFileAsDataUrl(file);
|
|
1206
1787
|
} catch (err) {
|
|
1207
|
-
renderErrors[index] = errorMessage(err);
|
|
1788
|
+
renderErrors[index] = require_plugin.errorMessage(err);
|
|
1208
1789
|
renderState[index] = "error";
|
|
1209
1790
|
return;
|
|
1210
1791
|
}
|
|
@@ -1214,7 +1795,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1214
1795
|
beatIndex: index,
|
|
1215
1796
|
imageData
|
|
1216
1797
|
});
|
|
1217
|
-
if (staleSince(requestedFilePath)) return;
|
|
1798
|
+
if (staleSince$1(requestedFilePath)) return;
|
|
1218
1799
|
if (!response.ok) {
|
|
1219
1800
|
renderErrors[index] = response.error || "Upload failed";
|
|
1220
1801
|
renderState[index] = "error";
|
|
@@ -1223,49 +1804,6 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1223
1804
|
renderedImages[index] = response.data.image ?? "";
|
|
1224
1805
|
renderState[index] = "done";
|
|
1225
1806
|
}
|
|
1226
|
-
function onCharDragOver(event, key) {
|
|
1227
|
-
if (!event.dataTransfer?.types.includes("Files")) return;
|
|
1228
|
-
event.preventDefault();
|
|
1229
|
-
charDragOver[key] = true;
|
|
1230
|
-
}
|
|
1231
|
-
function onCharDragLeave(key) {
|
|
1232
|
-
charDragOver[key] = false;
|
|
1233
|
-
}
|
|
1234
|
-
async function onCharDrop(event, key) {
|
|
1235
|
-
event.preventDefault();
|
|
1236
|
-
charDragOver[key] = false;
|
|
1237
|
-
const file = event.dataTransfer?.files[0];
|
|
1238
|
-
if (!file || !file.type.startsWith("image/")) return;
|
|
1239
|
-
charRenderState[key] = "rendering";
|
|
1240
|
-
Reflect.deleteProperty(charErrors, key);
|
|
1241
|
-
let imageData;
|
|
1242
|
-
try {
|
|
1243
|
-
imageData = await new Promise((resolve, reject) => {
|
|
1244
|
-
const reader = new FileReader();
|
|
1245
|
-
reader.onload = () => resolve(reader.result);
|
|
1246
|
-
reader.onerror = reject;
|
|
1247
|
-
reader.readAsDataURL(file);
|
|
1248
|
-
});
|
|
1249
|
-
} catch (err) {
|
|
1250
|
-
charErrors[key] = errorMessage(err);
|
|
1251
|
-
charRenderState[key] = "error";
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
|
-
const requestedFilePath = filePath.value;
|
|
1255
|
-
const response = await api.call("uploadCharacterImage", {
|
|
1256
|
-
filePath: requestedFilePath,
|
|
1257
|
-
key,
|
|
1258
|
-
imageData
|
|
1259
|
-
});
|
|
1260
|
-
if (staleSince(requestedFilePath)) return;
|
|
1261
|
-
if (!response.ok) {
|
|
1262
|
-
charErrors[key] = response.error || "Upload failed";
|
|
1263
|
-
charRenderState[key] = "error";
|
|
1264
|
-
return;
|
|
1265
|
-
}
|
|
1266
|
-
charImages[key] = response.data.image ?? "";
|
|
1267
|
-
charRenderState[key] = "done";
|
|
1268
|
-
}
|
|
1269
1807
|
function openCharacterLightbox(key) {
|
|
1270
1808
|
stopAllPlayback();
|
|
1271
1809
|
lightbox.value = {
|
|
@@ -1275,43 +1813,6 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1275
1813
|
isCharacter: true
|
|
1276
1814
|
};
|
|
1277
1815
|
}
|
|
1278
|
-
async function loadExistingCharacterImage(key) {
|
|
1279
|
-
const requestedFilePath = filePath.value;
|
|
1280
|
-
const response = await api.call("characterImage", {
|
|
1281
|
-
filePath: requestedFilePath,
|
|
1282
|
-
key
|
|
1283
|
-
});
|
|
1284
|
-
if (staleSince(requestedFilePath)) return;
|
|
1285
|
-
if (response.ok && response.data.image) {
|
|
1286
|
-
charImages[key] = response.data.image;
|
|
1287
|
-
charRenderState[key] = "done";
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
function refreshMissingCharacterImages() {
|
|
1291
|
-
getMissingCharacterKeys(characterKeys.value, charImages, charRenderState).forEach((key) => loadExistingCharacterImage(key));
|
|
1292
|
-
}
|
|
1293
|
-
async function renderCharacter(key, force) {
|
|
1294
|
-
const requestedFilePath = filePath.value;
|
|
1295
|
-
charRenderState[key] = "rendering";
|
|
1296
|
-
Reflect.deleteProperty(charErrors, key);
|
|
1297
|
-
const response = await api.call("renderCharacter", {
|
|
1298
|
-
filePath: requestedFilePath,
|
|
1299
|
-
key,
|
|
1300
|
-
force,
|
|
1301
|
-
chatSessionId: chatSessionId.value
|
|
1302
|
-
});
|
|
1303
|
-
if (staleSince(requestedFilePath)) return;
|
|
1304
|
-
if (!response.ok) {
|
|
1305
|
-
charErrors[key] = response.error || "Render failed";
|
|
1306
|
-
charRenderState[key] = "error";
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
charImages[key] = response.data.image ?? "";
|
|
1310
|
-
charRenderState[key] = "done";
|
|
1311
|
-
}
|
|
1312
|
-
async function generateAllCharacters() {
|
|
1313
|
-
await Promise.all(characterKeys.value.filter((key) => charRenderState[key] !== "rendering").map((key) => renderCharacter(key, false)));
|
|
1314
|
-
}
|
|
1315
1816
|
async function hydrateBeatImage(beat, index, hasCharacters, autoRenderTypes) {
|
|
1316
1817
|
await loadExistingBeatImage(index);
|
|
1317
1818
|
if (renderedImages[index]) return;
|
|
@@ -1353,39 +1854,16 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1353
1854
|
const diskScript = response.data.script;
|
|
1354
1855
|
if (!diskScript) return;
|
|
1355
1856
|
if (isSameScript(diskScript, script.value)) return;
|
|
1356
|
-
|
|
1357
|
-
...props.selectedResult,
|
|
1358
|
-
data: {
|
|
1359
|
-
...props.selectedResult.data,
|
|
1360
|
-
script: diskScript
|
|
1361
|
-
}
|
|
1362
|
-
});
|
|
1857
|
+
commitScript(diskScript);
|
|
1363
1858
|
}
|
|
1364
1859
|
async function initializeScript() {
|
|
1365
1860
|
stopAllPlayback();
|
|
1366
1861
|
lightbox.value = null;
|
|
1367
1862
|
if (beatListEl.value) beatListEl.value.scrollTop = 0;
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
Object.keys(renderErrors).forEach((key) => Reflect.deleteProperty(renderErrors, key));
|
|
1371
|
-
Object.keys(sourceOpen).forEach((key) => Reflect.deleteProperty(sourceOpen, key));
|
|
1372
|
-
Object.keys(sourceText).forEach((key) => Reflect.deleteProperty(sourceText, key));
|
|
1373
|
-
Object.keys(beatSaveErrors).forEach((key) => Reflect.deleteProperty(beatSaveErrors, key));
|
|
1374
|
-
Object.keys(beatSaving).forEach((key) => Reflect.deleteProperty(beatSaving, key));
|
|
1375
|
-
Object.keys(localOverrides).forEach((key) => Reflect.deleteProperty(localOverrides, key));
|
|
1376
|
-
Object.keys(beatAudios).forEach((key) => Reflect.deleteProperty(beatAudios, key));
|
|
1377
|
-
Object.keys(audioState).forEach((key) => Reflect.deleteProperty(audioState, key));
|
|
1378
|
-
Object.keys(audioErrors).forEach((key) => Reflect.deleteProperty(audioErrors, key));
|
|
1379
|
-
Object.keys(charRenderState).forEach((key) => Reflect.deleteProperty(charRenderState, key));
|
|
1380
|
-
Object.keys(charImages).forEach((key) => Reflect.deleteProperty(charImages, key));
|
|
1381
|
-
Object.keys(charErrors).forEach((key) => Reflect.deleteProperty(charErrors, key));
|
|
1382
|
-
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();
|
|
1383
1865
|
resetBeatMovies();
|
|
1384
|
-
|
|
1385
|
-
pdfPath.value = null;
|
|
1386
|
-
movieGenerating.value = false;
|
|
1387
|
-
pdfGenerating.value = false;
|
|
1388
|
-
movieError.value = null;
|
|
1866
|
+
resetMedia();
|
|
1389
1867
|
if (sourceDetails.value) sourceDetails.value.open = false;
|
|
1390
1868
|
await refreshScriptFromDisk();
|
|
1391
1869
|
const AUTO_RENDER_TYPES = [
|
|
@@ -1444,7 +1922,7 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1444
1922
|
if (entry.kind === "beatImage") {
|
|
1445
1923
|
const idx = Number(entry.key);
|
|
1446
1924
|
await loadExistingBeatImage(idx);
|
|
1447
|
-
if (beatMayHaveMovie(effectiveBeat(idx))) await loadExistingBeatMovie(idx);
|
|
1925
|
+
if (beatMayHaveMovie(effectiveBeat$1(idx))) await loadExistingBeatMovie(idx);
|
|
1448
1926
|
if (renderState[idx] === "rendering") Reflect.deleteProperty(renderState, idx);
|
|
1449
1927
|
refreshMissingCharacterImages();
|
|
1450
1928
|
} else if (entry.kind === "beatAudio") {
|
|
@@ -1462,95 +1940,6 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1462
1940
|
await refreshPdfPath();
|
|
1463
1941
|
}
|
|
1464
1942
|
}
|
|
1465
|
-
async function refreshMoviePath() {
|
|
1466
|
-
const requestedFilePath = filePath.value;
|
|
1467
|
-
if (!requestedFilePath) return;
|
|
1468
|
-
const response = await api.call("movieStatus", { filePath: requestedFilePath });
|
|
1469
|
-
if (filePath.value !== requestedFilePath) return;
|
|
1470
|
-
if (response.ok && response.data.moviePath) moviePath.value = response.data.moviePath;
|
|
1471
|
-
}
|
|
1472
|
-
async function generateMovie() {
|
|
1473
|
-
const requestedFilePath = filePath.value;
|
|
1474
|
-
movieGenerating.value = true;
|
|
1475
|
-
movieError.value = null;
|
|
1476
|
-
const response = await api.call("generateMovie", {
|
|
1477
|
-
filePath: requestedFilePath,
|
|
1478
|
-
chatSessionId: chatSessionId.value
|
|
1479
|
-
});
|
|
1480
|
-
if (filePath.value !== requestedFilePath) return;
|
|
1481
|
-
movieGenerating.value = false;
|
|
1482
|
-
if (!response.ok) {
|
|
1483
|
-
movieError.value = response.error;
|
|
1484
|
-
return;
|
|
1485
|
-
}
|
|
1486
|
-
moviePath.value = response.data.moviePath;
|
|
1487
|
-
}
|
|
1488
|
-
async function downloadMovie() {
|
|
1489
|
-
const fetchMediaBlob = adapter.fetchMediaBlob;
|
|
1490
|
-
if (!fetchMediaBlob || !moviePath.value || movieDownloading.value) return;
|
|
1491
|
-
movieDownloading.value = true;
|
|
1492
|
-
let objectUrl = null;
|
|
1493
|
-
try {
|
|
1494
|
-
const blob = await fetchMediaBlob({ moviePath: moviePath.value });
|
|
1495
|
-
objectUrl = URL.createObjectURL(blob);
|
|
1496
|
-
const filename = moviePath.value.split("/").pop() ?? "movie.mp4";
|
|
1497
|
-
const anchor = document.createElement("a");
|
|
1498
|
-
anchor.href = objectUrl;
|
|
1499
|
-
anchor.download = filename;
|
|
1500
|
-
document.body.appendChild(anchor);
|
|
1501
|
-
anchor.click();
|
|
1502
|
-
anchor.remove();
|
|
1503
|
-
} catch (err) {
|
|
1504
|
-
alert(errorMessage(err));
|
|
1505
|
-
} finally {
|
|
1506
|
-
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
1507
|
-
movieDownloading.value = false;
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
async function refreshPdfPath() {
|
|
1511
|
-
const requestedFilePath = filePath.value;
|
|
1512
|
-
if (!requestedFilePath) return;
|
|
1513
|
-
const response = await api.call("pdfStatus", { filePath: requestedFilePath });
|
|
1514
|
-
if (filePath.value !== requestedFilePath) return;
|
|
1515
|
-
if (response.ok && response.data.pdfPath) pdfPath.value = response.data.pdfPath;
|
|
1516
|
-
}
|
|
1517
|
-
async function generatePdf() {
|
|
1518
|
-
const requestedFilePath = filePath.value;
|
|
1519
|
-
pdfGenerating.value = true;
|
|
1520
|
-
const response = await api.call("generatePdf", {
|
|
1521
|
-
filePath: requestedFilePath,
|
|
1522
|
-
chatSessionId: chatSessionId.value
|
|
1523
|
-
});
|
|
1524
|
-
if (filePath.value !== requestedFilePath) return;
|
|
1525
|
-
pdfGenerating.value = false;
|
|
1526
|
-
if (!response.ok) {
|
|
1527
|
-
alert(response.error);
|
|
1528
|
-
return;
|
|
1529
|
-
}
|
|
1530
|
-
pdfPath.value = response.data.pdfPath;
|
|
1531
|
-
}
|
|
1532
|
-
async function downloadPdf() {
|
|
1533
|
-
const fetchMediaBlob = adapter.fetchMediaBlob;
|
|
1534
|
-
if (!fetchMediaBlob || !pdfPath.value || pdfDownloading.value) return;
|
|
1535
|
-
pdfDownloading.value = true;
|
|
1536
|
-
let objectUrl = null;
|
|
1537
|
-
try {
|
|
1538
|
-
const blob = await fetchMediaBlob({ pdfPath: pdfPath.value });
|
|
1539
|
-
objectUrl = URL.createObjectURL(blob);
|
|
1540
|
-
const filename = pdfPath.value.split("/").pop() ?? "deck.pdf";
|
|
1541
|
-
const anchor = document.createElement("a");
|
|
1542
|
-
anchor.href = objectUrl;
|
|
1543
|
-
anchor.download = filename;
|
|
1544
|
-
document.body.appendChild(anchor);
|
|
1545
|
-
anchor.click();
|
|
1546
|
-
anchor.remove();
|
|
1547
|
-
} catch (err) {
|
|
1548
|
-
alert(errorMessage(err));
|
|
1549
|
-
} finally {
|
|
1550
|
-
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
1551
|
-
pdfDownloading.value = false;
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
1943
|
return (_ctx, _cache) => {
|
|
1555
1944
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$1, [
|
|
1556
1945
|
(0, vue.createElementVNode)("div", _hoisted_2$1, [(0, vue.createElementVNode)("div", _hoisted_3$1, [
|
|
@@ -1561,153 +1950,80 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1561
1950
|
script.value.lang ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_7, (0, vue.toDisplayString)(script.value.lang), 1)) : (0, vue.createCommentVNode)("", true),
|
|
1562
1951
|
filePath.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8, (0, vue.toDisplayString)(filePath.value), 1)) : (0, vue.createCommentVNode)("", true)
|
|
1563
1952
|
])
|
|
1564
|
-
]), (0, vue.
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
"data-testid": "mulmo-script-generate-movie-button",
|
|
1592
|
-
onClick: generateMovie
|
|
1593
|
-
}, [movieGenerating.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_14, [..._cache[9] || (_cache[9] = [(0, vue.createElementVNode)("circle", {
|
|
1594
|
-
class: "opacity-25",
|
|
1595
|
-
cx: "12",
|
|
1596
|
-
cy: "12",
|
|
1597
|
-
r: "10",
|
|
1598
|
-
stroke: "currentColor",
|
|
1599
|
-
"stroke-width": "4"
|
|
1600
|
-
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1601
|
-
class: "opacity-75",
|
|
1602
|
-
fill: "currentColor",
|
|
1603
|
-
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1604
|
-
}, 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)),
|
|
1605
|
-
pdfPath.value && !pdfGenerating.value && canFetchMedia.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1606
|
-
key: 4,
|
|
1607
|
-
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",
|
|
1608
|
-
disabled: pdfDownloading.value,
|
|
1609
|
-
"data-testid": "mulmo-script-download-pdf-button",
|
|
1610
|
-
onClick: downloadPdf
|
|
1611
|
-
}, [_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),
|
|
1612
|
-
pdfPath.value && !pdfGenerating.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1613
|
-
key: 5,
|
|
1614
|
-
class: "h-8 w-8 flex items-center justify-center rounded border border-gray-200 text-gray-600 hover:bg-gray-100 transition-colors",
|
|
1615
|
-
title: (0, vue.unref)(m).regeneratePdf,
|
|
1616
|
-
"aria-label": (0, vue.unref)(m).regeneratePdf,
|
|
1617
|
-
"data-testid": "mulmo-script-regenerate-pdf-button",
|
|
1618
|
-
onClick: generatePdf
|
|
1619
|
-
}, [..._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", {
|
|
1620
|
-
key: 6,
|
|
1621
|
-
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",
|
|
1622
|
-
disabled: pdfGenerating.value,
|
|
1623
|
-
"data-testid": "mulmo-script-generate-pdf-button",
|
|
1624
|
-
onClick: generatePdf
|
|
1625
|
-
}, [pdfGenerating.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_19, [..._cache[13] || (_cache[13] = [(0, vue.createElementVNode)("circle", {
|
|
1626
|
-
class: "opacity-25",
|
|
1627
|
-
cx: "12",
|
|
1628
|
-
cy: "12",
|
|
1629
|
-
r: "10",
|
|
1630
|
-
stroke: "currentColor",
|
|
1631
|
-
"stroke-width": "4"
|
|
1632
|
-
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1633
|
-
class: "opacity-75",
|
|
1634
|
-
fill: "currentColor",
|
|
1635
|
-
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1636
|
-
}, 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"
|
|
1637
1980
|
])]),
|
|
1638
|
-
movieError
|
|
1639
|
-
_cache[
|
|
1640
|
-
(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)]),
|
|
1641
1984
|
(0, vue.createElementVNode)("button", {
|
|
1642
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",
|
|
1643
|
-
disabled: movieGenerating
|
|
1986
|
+
disabled: (0, vue.unref)(movieGenerating),
|
|
1644
1987
|
"data-testid": "mulmo-script-movie-retry-button",
|
|
1645
|
-
onClick: generateMovie
|
|
1646
|
-
}, (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)
|
|
1647
1990
|
])) : (0, vue.createCommentVNode)("", true),
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
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
|
-
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),
|
|
1682
|
-
charImages[key] && charRenderState[key] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1683
|
-
key: 6,
|
|
1684
|
-
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"]),
|
|
1685
|
-
disabled: movieGenerating.value || anyBeatRendering.value,
|
|
1686
|
-
onClick: (0, vue.withModifiers)(($event) => renderCharacter(key, true), ["stop"])
|
|
1687
|
-
}, [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", {
|
|
1688
|
-
key: 7,
|
|
1689
|
-
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"]),
|
|
1690
|
-
disabled: movieGenerating.value || anyBeatRendering.value,
|
|
1691
|
-
onClick: (0, vue.withModifiers)(($event) => renderCharacter(key, false), ["stop"])
|
|
1692
|
-
}, [movieGenerating.value || anyBeatRendering.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_43, [..._cache[17] || (_cache[17] = [(0, vue.createElementVNode)("circle", {
|
|
1693
|
-
class: "opacity-25",
|
|
1694
|
-
cx: "12",
|
|
1695
|
-
cy: "12",
|
|
1696
|
-
r: "10",
|
|
1697
|
-
stroke: "currentColor",
|
|
1698
|
-
"stroke-width": "4"
|
|
1699
|
-
}, null, -1), (0, vue.createElementVNode)("path", {
|
|
1700
|
-
class: "opacity-75",
|
|
1701
|
-
fill: "currentColor",
|
|
1702
|
-
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1703
|
-
}, 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)
|
|
1704
|
-
], 42, _hoisted_31), (0, vue.createElementVNode)("span", _hoisted_45, (0, vue.toDisplayString)(key), 1)]);
|
|
1705
|
-
}), 128))])])) : (0, vue.createCommentVNode)("", true),
|
|
1706
|
-
isDeck.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_46, [(0, vue.createVNode)((0, vue.unref)(MulmoScriptDeckEditor), {
|
|
1707
|
-
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),
|
|
1708
2024
|
layout: "compact",
|
|
1709
|
-
"onUpdate:script": onDeckUpdate
|
|
1710
|
-
}, 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", {
|
|
1711
2027
|
key: 3,
|
|
1712
2028
|
ref_key: "beatListEl",
|
|
1713
2029
|
ref: beatListEl,
|
|
@@ -1716,40 +2032,44 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1716
2032
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1717
2033
|
key: index,
|
|
1718
2034
|
class: "rounded-lg border border-gray-200 overflow-hidden"
|
|
1719
|
-
}, [(0, vue.createElementVNode)("div",
|
|
2035
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_15, [(0, vue.createElementVNode)("div", {
|
|
1720
2036
|
class: (0, vue.normalizeClass)(["relative shrink-0 w-[45%] overflow-hidden bg-gray-50 transition-colors", beatDragOver[index] ? "bg-blue-50" : ""]),
|
|
1721
2037
|
onDragover: ($event) => onBeatDragOver($event, index),
|
|
1722
2038
|
onDragleave: ($event) => onBeatDragLeave(index),
|
|
1723
2039
|
onDrop: ($event) => onBeatDrop($event, index)
|
|
1724
2040
|
}, [
|
|
1725
|
-
|
|
1726
|
-
|
|
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],
|
|
1727
2047
|
class: "w-full object-contain",
|
|
1728
2048
|
controls: "",
|
|
1729
2049
|
autoplay: "",
|
|
1730
2050
|
"data-testid": `mulmo-script-beat-movie-player-${index}`
|
|
1731
|
-
}, null, 8,
|
|
2051
|
+
}, null, 8, _hoisted_18), (0, vue.createElementVNode)("button", {
|
|
1732
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",
|
|
1733
2053
|
title: (0, vue.unref)(m).close,
|
|
1734
2054
|
"aria-label": (0, vue.unref)(m).close,
|
|
1735
2055
|
"data-testid": `mulmo-script-beat-movie-close-${index}`,
|
|
1736
|
-
onClick: (0, vue.withModifiers)(($event) => closeBeatMovie(index), ["stop"])
|
|
1737
|
-
}, [..._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 }, [
|
|
1738
2058
|
renderedImages[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
|
|
1739
2059
|
key: 0,
|
|
1740
2060
|
src: renderedImages[index],
|
|
1741
2061
|
class: "w-full object-contain cursor-zoom-in",
|
|
1742
2062
|
alt: `Beat ${index + 1}`,
|
|
1743
2063
|
onClick: ($event) => openLightbox(index)
|
|
1744
|
-
}, null, 8,
|
|
1745
|
-
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", {
|
|
1746
2066
|
key: 1,
|
|
1747
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",
|
|
1748
2068
|
title: (0, vue.unref)(m).play,
|
|
1749
2069
|
"aria-label": (0, vue.unref)(m).play,
|
|
1750
2070
|
"data-testid": `mulmo-script-beat-movie-play-${index}`,
|
|
1751
|
-
onClick: (0, vue.withModifiers)(($event) => playBeatMovie(index), ["stop"])
|
|
1752
|
-
}, [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", {
|
|
1753
2073
|
class: "opacity-25",
|
|
1754
2074
|
cx: "12",
|
|
1755
2075
|
cy: "12",
|
|
@@ -1760,13 +2080,13 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1760
2080
|
class: "opacity-75",
|
|
1761
2081
|
fill: "currentColor",
|
|
1762
2082
|
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1763
|
-
}, 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),
|
|
1764
2084
|
renderedImages[index] && renderState[index] !== "rendering" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1765
2085
|
key: 2,
|
|
1766
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",
|
|
1767
|
-
disabled: movieGenerating
|
|
2087
|
+
disabled: (0, vue.unref)(movieGenerating),
|
|
1768
2088
|
onClick: (0, vue.withModifiers)(($event) => regenerateBeat(index), ["stop"])
|
|
1769
|
-
}, " ↺ ", 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", {
|
|
1770
2090
|
class: "animate-spin w-4 h-4 text-green-400",
|
|
1771
2091
|
viewBox: "0 0 24 24",
|
|
1772
2092
|
fill: "none"
|
|
@@ -1781,15 +2101,15 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1781
2101
|
class: "opacity-75",
|
|
1782
2102
|
fill: "currentColor",
|
|
1783
2103
|
d: "M4 12a8 8 0 018-8v8H4z"
|
|
1784
|
-
})], -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)
|
|
1785
2105
|
], 64)),
|
|
1786
|
-
beatDragOver[index] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
1787
|
-
!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", {
|
|
1788
2108
|
key: 4,
|
|
1789
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",
|
|
1790
2110
|
onClick: ($event) => renderBeat(index)
|
|
1791
|
-
}, (0, vue.toDisplayString)((0, vue.unref)(m).generate), 9,
|
|
1792
|
-
], 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", {
|
|
1793
2113
|
class: "opacity-25",
|
|
1794
2114
|
cx: "12",
|
|
1795
2115
|
cy: "12",
|
|
@@ -1804,24 +2124,24 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1804
2124
|
key: 1,
|
|
1805
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"]),
|
|
1806
2126
|
onClick: ($event) => playAudio(index)
|
|
1807
|
-
}, (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", {
|
|
1808
2128
|
class: "text-xs text-red-400 truncate min-w-0 max-w-[20rem]",
|
|
1809
2129
|
title: audioErrors[index]
|
|
1810
|
-
}, (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", {
|
|
1811
2131
|
key: 0,
|
|
1812
2132
|
class: "text-xs px-2 py-0.5 rounded border border-gray-300 text-gray-500 hover:bg-gray-50 disabled:opacity-50",
|
|
1813
|
-
disabled: movieGenerating
|
|
2133
|
+
disabled: (0, vue.unref)(movieGenerating),
|
|
1814
2134
|
onClick: ($event) => generateAudio(index)
|
|
1815
|
-
}, " ↺ ", 8,
|
|
2135
|
+
}, " ↺ ", 8, _hoisted_41)) : (0, vue.createCommentVNode)("", true)], 64)) : effectiveBeat$1(index).text ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1816
2136
|
key: 3,
|
|
1817
2137
|
class: "text-xs px-2 py-0.5 rounded border border-gray-300 text-gray-500 hover:bg-gray-50",
|
|
1818
2138
|
onClick: ($event) => generateAudio(index)
|
|
1819
|
-
}, (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", {
|
|
1820
2140
|
class: "text-gray-400 hover:text-gray-600",
|
|
1821
2141
|
title: sourceOpen[index] ? "Hide source" : "Show source",
|
|
1822
2142
|
"data-testid": `mulmo-script-beat-source-toggle-${index}`,
|
|
1823
2143
|
onClick: ($event) => toggleSource(index)
|
|
1824
|
-
}, [..._cache[
|
|
2144
|
+
}, [..._cache[8] || (_cache[8] = [(0, vue.createElementVNode)("svg", {
|
|
1825
2145
|
xmlns: "http://www.w3.org/2000/svg",
|
|
1826
2146
|
class: "w-3.5 h-3.5",
|
|
1827
2147
|
viewBox: "0 0 24 24",
|
|
@@ -1830,36 +2150,36 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1830
2150
|
"stroke-width": "2",
|
|
1831
2151
|
"stroke-linecap": "round",
|
|
1832
2152
|
"stroke-linejoin": "round"
|
|
1833
|
-
}, [(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", {
|
|
1834
2154
|
"onUpdate:modelValue": ($event) => sourceText[index] = $event,
|
|
1835
|
-
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"]),
|
|
1836
2156
|
rows: "8",
|
|
1837
2157
|
spellcheck: "false",
|
|
1838
2158
|
"data-testid": `mulmo-script-beat-source-textarea-${index}`
|
|
1839
|
-
}, null, 10,
|
|
1840
|
-
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"]),
|
|
1841
|
-
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],
|
|
1842
2162
|
"data-testid": `mulmo-script-beat-update-button-${index}`,
|
|
1843
2163
|
onClick: ($event) => updateBeat(index)
|
|
1844
|
-
}, (0, vue.toDisplayString)(beatSaving[index] ? (0, vue.unref)(m).saving : (0, vue.unref)(m).update), 11,
|
|
1845
|
-
}), 128)), beats.value.length === 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div",
|
|
1846
|
-
(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", {
|
|
1847
2167
|
ref_key: "sourceDetails",
|
|
1848
2168
|
ref: sourceDetails,
|
|
1849
2169
|
class: "script-source",
|
|
1850
|
-
onToggle: _cache[
|
|
2170
|
+
onToggle: _cache[2] || (_cache[2] = ($event) => onSourceToggle($event.target.open))
|
|
1851
2171
|
}, [
|
|
1852
2172
|
(0, vue.createElementVNode)("summary", null, (0, vue.toDisplayString)((0, vue.unref)(m).editSource), 1),
|
|
1853
2173
|
(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
|
|
1854
|
-
"onUpdate:modelValue": _cache[
|
|
2174
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => editableSource.value = $event),
|
|
1855
2175
|
class: (0, vue.normalizeClass)(["script-editor", { "script-editor-invalid": sourceChanged.value && !sourceValid.value }]),
|
|
1856
2176
|
spellcheck: "false"
|
|
1857
2177
|
}, null, 2), [[vue.vModelText, editableSource.value]]),
|
|
1858
|
-
(0, vue.createElementVNode)("div",
|
|
2178
|
+
(0, vue.createElementVNode)("div", _hoisted_51, [(0, vue.createElementVNode)("button", {
|
|
1859
2179
|
class: "apply-btn",
|
|
1860
2180
|
disabled: !sourceChanged.value || !sourceValid.value,
|
|
1861
2181
|
onClick: applySource
|
|
1862
|
-
}, (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", {
|
|
1863
2183
|
class: "cancel-btn",
|
|
1864
2184
|
onClick: cancelSourceEdit
|
|
1865
2185
|
}, (0, vue.toDisplayString)((0, vue.unref)(m).cancel), 1)])
|
|
@@ -1867,50 +2187,31 @@ var View_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0, vue.def
|
|
|
1867
2187
|
class: "copy-btn",
|
|
1868
2188
|
title: (0, vue.unref)(copied) ? "Copied!" : "Copy",
|
|
1869
2189
|
onClick: copyText
|
|
1870
|
-
}, [(0, vue.createElementVNode)("span",
|
|
1871
|
-
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, {
|
|
1872
2192
|
key: 4,
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
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"]),
|
|
1896
|
-
onClick: ($event) => jumpToBeat(i - 1)
|
|
1897
|
-
}, [_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);
|
|
1898
|
-
}), 128))]), playingAudio.value && playingAudio.value.index === lightbox.value.index ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
1899
|
-
key: 0,
|
|
1900
|
-
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",
|
|
1901
|
-
style: (0, vue.normalizeStyle)({ left: `${(lightbox.value.index + audioProgress.value) / beats.value.length * 100}%` })
|
|
1902
|
-
}, null, 4)) : (0, vue.createCommentVNode)("", true)])) : (0, vue.createCommentVNode)("", true)]),
|
|
1903
|
-
!lightbox.value.isCharacter ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
1904
|
-
key: 1,
|
|
1905
|
-
class: "text-white/60 hover:text-white disabled:opacity-20 text-5xl leading-none",
|
|
1906
|
-
disabled: !hasNext.value,
|
|
1907
|
-
onClick: _cache[3] || (_cache[3] = ($event) => lightboxMove(1))
|
|
1908
|
-
}, " › ", 8, _hoisted_95)) : (0, vue.createCommentVNode)("", true)
|
|
1909
|
-
]), 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", {
|
|
1910
|
-
key: 1,
|
|
1911
|
-
class: "absolute top-0 right-4 text-sm px-3 py-1 rounded border border-white/60 text-white/60 hover:bg-white/20",
|
|
1912
|
-
onClick: _cache[4] || (_cache[4] = ($event) => playAudio(lightbox.value.index))
|
|
1913
|
-
}, (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)
|
|
1914
2215
|
]);
|
|
1915
2216
|
};
|
|
1916
2217
|
}
|
|
@@ -1924,7 +2225,7 @@ var _plugin_vue_export_helper_default = (sfc, props) => {
|
|
|
1924
2225
|
};
|
|
1925
2226
|
//#endregion
|
|
1926
2227
|
//#region src/vue/View.vue
|
|
1927
|
-
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"]]);
|
|
1928
2229
|
//#endregion
|
|
1929
2230
|
//#region src/vue/Preview.vue?vue&type=script&setup=true&lang.ts
|
|
1930
2231
|
var _hoisted_1 = {
|