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