@feedmepos/mf-media 0.0.8 → 0.0.10-dev.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.
@@ -0,0 +1,797 @@
1
+ import { defineComponent as j, computed as F, resolveComponent as E, openBlock as m, createElementBlock as h, createElementVNode as s, normalizeStyle as ee, createVNode as k, toDisplayString as u, unref as a, createCommentVNode as U, normalizeClass as N, createTextVNode as ae, createBlock as G, Fragment as D, renderList as V, mergeModels as H, useModel as q, renderSlot as ne, withCtx as te, ref as B, withDirectives as ie, vModelText as le, onMounted as re } from "vue";
2
+ import { defineStore as de, storeToRefs as ce } from "pinia";
3
+ import { useI18n as L, useCoreStore as ue } from "@feedmepos/mf-common";
4
+ import { useDialog as me, useSnackbar as fe } from "@feedmepos/ui-library";
5
+ import { V as K, S as pe, J as ve, X as xe } from "./media-picker-cf281640.js";
6
+ const ge = { class: "rounded-xl border border-fm-color-neutral-gray-200 bg-white overflow-hidden" }, ye = ["src", "alt"], be = {
7
+ key: 1,
8
+ class: "absolute inset-0 flex flex-col items-center justify-center gap-4 p-12 text-center"
9
+ }, he = { class: "text-xs font-semibold" }, _e = { class: "text-xs text-fm-color-typo-secondary line-clamp-3" }, $e = {
10
+ key: 2,
11
+ class: "absolute inset-0 flex items-center justify-center"
12
+ }, ke = { class: "flex flex-col items-center gap-8" }, Ie = { class: "text-xs text-fm-color-typo-secondary" }, we = {
13
+ key: 3,
14
+ class: "absolute left-8 top-8 px-4 py-4 rounded text-xs font-bold bg-black/60 text-white"
15
+ }, Te = {
16
+ key: 0,
17
+ class: "flex items-center gap-4 px-8 py-8"
18
+ }, Ce = { class: "flex-1 text-xs text-fm-color-typo-secondary truncate" }, Se = ["title"], Fe = ["title"], Re = ["title"], Ee = {
19
+ key: 1,
20
+ class: "flex justify-end px-8 py-8"
21
+ }, J = "w-32 h-32 rounded-md flex items-center justify-center text-fm-color-typo-secondary hover:bg-fm-color-neutral-gray-100", Be = /* @__PURE__ */ j({
22
+ __name: "StudioImageCard",
23
+ props: {
24
+ item: {},
25
+ ratio: {},
26
+ fromRef: { type: Boolean }
27
+ },
28
+ emits: ["preview", "variant", "discard", "save"],
29
+ setup(e, { emit: x }) {
30
+ const t = e, l = x, { t: r } = L(), v = F(() => t.item.status === "success" && !!t.item.url), g = F(() => t.item.status === "failed"), w = F(() => t.ratio.replace("x", " / ")), n = F(
31
+ () => t.item.width && t.item.height ? `${t.item.width}×${t.item.height}` : ""
32
+ );
33
+ return ($, b) => {
34
+ const C = E("FmIcon"), T = E("FmCircularProgress"), S = E("FmButton");
35
+ return m(), h("div", ge, [
36
+ s("div", {
37
+ class: "relative bg-fm-color-neutral-gray-100",
38
+ style: ee({ aspectRatio: w.value })
39
+ }, [
40
+ v.value ? (m(), h("img", {
41
+ key: 0,
42
+ src: e.item.url,
43
+ class: "w-full h-full object-cover",
44
+ alt: n.value
45
+ }, null, 8, ye)) : g.value ? (m(), h("div", be, [
46
+ k(C, {
47
+ name: "error_outline",
48
+ size: "md"
49
+ }),
50
+ s("span", he, u(a(r)("media.studio.failed")), 1),
51
+ s("span", _e, u(e.item.error), 1)
52
+ ])) : (m(), h("div", $e, [
53
+ s("div", ke, [
54
+ k(T, { size: "md" }),
55
+ s("span", Ie, u(a(r)("media.studio.rendering")), 1)
56
+ ])
57
+ ])),
58
+ v.value ? (m(), h("span", we, u(e.fromRef ? a(r)("media.studio.refTag") : a(r)("media.studio.aiTag")), 1)) : U("", !0)
59
+ ], 4),
60
+ v.value ? (m(), h("div", Te, [
61
+ s("span", Ce, u(n.value), 1),
62
+ s("button", {
63
+ type: "button",
64
+ class: N(J),
65
+ title: a(r)("media.studio.preview"),
66
+ onClick: b[0] || (b[0] = (f) => l("preview"))
67
+ }, [
68
+ k(C, {
69
+ name: "open_in_full",
70
+ size: "sm"
71
+ })
72
+ ], 8, Se),
73
+ s("button", {
74
+ type: "button",
75
+ class: N(J),
76
+ title: a(r)("media.studio.variant"),
77
+ onClick: b[1] || (b[1] = (f) => l("variant"))
78
+ }, [
79
+ k(C, {
80
+ name: "add_photo_alternate",
81
+ size: "sm"
82
+ })
83
+ ], 8, Fe),
84
+ s("button", {
85
+ type: "button",
86
+ class: N(J),
87
+ title: a(r)("media.studio.discard"),
88
+ onClick: b[2] || (b[2] = (f) => l("discard"))
89
+ }, [
90
+ k(C, {
91
+ name: "delete",
92
+ size: "sm"
93
+ })
94
+ ], 8, Re),
95
+ k(S, {
96
+ label: e.item.saved ? a(r)("media.studio.saved") : a(r)("media.studio.save"),
97
+ variant: "primary",
98
+ size: "md",
99
+ disabled: e.item.saved,
100
+ onClick: b[3] || (b[3] = (f) => l("save"))
101
+ }, null, 8, ["label", "disabled"])
102
+ ])) : g.value ? (m(), h("div", Ee, [
103
+ k(S, {
104
+ label: a(r)("media.studio.retry"),
105
+ variant: "tertiary",
106
+ size: "md",
107
+ onClick: b[4] || (b[4] = (f) => l("variant"))
108
+ }, null, 8, ["label"])
109
+ ])) : U("", !0)
110
+ ]);
111
+ };
112
+ }
113
+ }), ze = { class: "flex flex-col gap-12" }, Pe = { class: "flex items-center gap-12" }, Ue = { class: "flex-1 min-w-0 flex flex-col gap-4" }, Oe = { class: "text-sm font-semibold line-clamp-2" }, Ae = { class: "text-xs text-fm-color-typo-secondary" }, De = {
114
+ key: 0,
115
+ class: "flex items-center gap-4 text-xs font-semibold"
116
+ }, Me = { class: "grid gap-16 grid-cols-[repeat(auto-fill,minmax(min(240px,100%),1fr))]" }, Ve = /* @__PURE__ */ j({
117
+ __name: "StudioBatch",
118
+ props: {
119
+ batch: {}
120
+ },
121
+ emits: ["regenerate", "preview", "variant", "discard", "save"],
122
+ setup(e, { emit: x }) {
123
+ const t = e, l = x, { t: r } = L(), v = F(
124
+ () => t.batch.items.some((n) => n.status === "pending" || n.status === "processing")
125
+ ), g = F(
126
+ () => !!(t.batch.params.refAssetId || t.batch.params.refTmpToken)
127
+ ), w = F(() => {
128
+ const n = {
129
+ style: r(`media.studio.styles.${t.batch.params.style}`),
130
+ ratio: t.batch.params.ratio,
131
+ count: t.batch.items.length
132
+ };
133
+ return g.value ? r("media.studio.batchMetaRef", { ...n, strength: t.batch.params.refStrength ?? 55 }) : r("media.studio.batchMeta", n);
134
+ });
135
+ return (n, $) => {
136
+ const b = E("FmCircularProgress"), C = E("FmButton");
137
+ return m(), h("div", ze, [
138
+ s("div", Pe, [
139
+ s("div", Ue, [
140
+ s("span", Oe, u(e.batch.params.prompt), 1),
141
+ s("span", Ae, u(w.value), 1)
142
+ ]),
143
+ v.value ? (m(), h("span", De, [
144
+ k(b, { size: "md" }),
145
+ ae(" " + u(a(r)("media.studio.generating")), 1)
146
+ ])) : (m(), G(C, {
147
+ key: 1,
148
+ label: a(r)("media.studio.regenerate"),
149
+ variant: "tertiary",
150
+ size: "md",
151
+ "prepend-icon": "refresh",
152
+ onClick: $[0] || ($[0] = (T) => l("regenerate"))
153
+ }, null, 8, ["label"]))
154
+ ]),
155
+ s("div", Me, [
156
+ (m(!0), h(D, null, V(e.batch.items, (T) => (m(), G(Be, {
157
+ key: T.taskId,
158
+ item: T,
159
+ ratio: e.batch.params.ratio,
160
+ "from-ref": g.value,
161
+ onPreview: (S) => l("preview", T),
162
+ onVariant: $[1] || ($[1] = (S) => l("variant")),
163
+ onDiscard: (S) => l("discard", T),
164
+ onSave: (S) => l("save", T)
165
+ }, null, 8, ["item", "ratio", "from-ref", "onPreview", "onDiscard", "onSave"]))), 128))
166
+ ])
167
+ ]);
168
+ };
169
+ }
170
+ }), Ne = [
171
+ "none",
172
+ "photo",
173
+ "flatlay",
174
+ "studio",
175
+ "warm",
176
+ "illus"
177
+ ], je = ["1x1", "4x3", "3x4", "16x9"], Le = [1, 2, 4];
178
+ function X(e) {
179
+ return `/media-v1/businesses/${e}/generations`;
180
+ }
181
+ async function qe(e, x) {
182
+ const { data: t } = await K().post(X(e), x);
183
+ return t;
184
+ }
185
+ async function Ge(e, x) {
186
+ const { data: t } = await K().get(
187
+ `${X(e)}/${x}`
188
+ );
189
+ return t;
190
+ }
191
+ async function He(e, x) {
192
+ const { data: t } = await K().post(
193
+ `${X(e)}/ref-upload-intent`,
194
+ x
195
+ );
196
+ return t;
197
+ }
198
+ async function Ye(e, x) {
199
+ const t = await fetch(e.uploadUrl, {
200
+ method: e.method,
201
+ headers: { "Content-Type": x.type },
202
+ body: x
203
+ });
204
+ if (!t.ok)
205
+ throw new Error(`reference upload failed: ${t.status} ${await t.text()}`);
206
+ }
207
+ const Je = { class: "w-[320px] max-h-screen overflow-auto flex flex-col gap-16 p-16 rounded-xl border border-fm-color-neutral-gray-200 bg-white sticky top-0 self-start" }, Ke = { class: "flex flex-col gap-8" }, Xe = { class: "flex items-baseline gap-8" }, We = { class: "text-sm font-semibold" }, Qe = { class: "text-xs text-fm-color-typo-tertiary" }, Ze = { class: "grid grid-cols-2 gap-8" }, et = ["onClick"], tt = { class: "flex items-center gap-8" }, st = { class: "text-sm font-semibold" }, ot = { class: "text-xs text-fm-color-typo-secondary" }, at = { class: "flex flex-col gap-8" }, nt = {
208
+ key: 0,
209
+ class: "flex items-center gap-8 px-8 py-8 rounded-lg bg-[#FFF7F0]"
210
+ }, it = { class: "text-xs" }, lt = { class: "flex flex-col gap-8" }, rt = { class: "text-sm font-semibold" }, dt = { class: "flex flex-wrap gap-4" }, ct = { class: "flex flex-col gap-8" }, ut = { class: "text-sm font-semibold" }, mt = { class: "grid grid-cols-2 gap-8" }, ft = ["onClick"], pt = { class: "text-fm-color-typo-secondary" }, vt = { class: "flex flex-col gap-8" }, xt = { class: "text-sm font-semibold" }, gt = { class: "flex gap-8" }, yt = ["onClick"], bt = { class: "flex flex-col gap-8 sticky bottom-0 bg-white pt-12" }, ht = { class: "text-xs text-fm-color-typo-tertiary text-center" }, _t = "border-[--fm-color-brand-primary,#FF7823] bg-[#FFF7F0]", $t = "border-fm-color-neutral-gray-200 bg-white", kt = /* @__PURE__ */ j({
211
+ __name: "StudioControls",
212
+ props: /* @__PURE__ */ H({
213
+ busy: { type: Boolean },
214
+ hasRef: { type: Boolean }
215
+ }, {
216
+ prompt: { required: !0 },
217
+ promptModifiers: {},
218
+ style: { required: !0 },
219
+ styleModifiers: {},
220
+ ratio: { required: !0 },
221
+ ratioModifiers: {},
222
+ count: { required: !0 },
223
+ countModifiers: {}
224
+ }),
225
+ emits: /* @__PURE__ */ H(["generate"], ["update:prompt", "update:style", "update:ratio", "update:count"]),
226
+ setup(e, { emit: x }) {
227
+ const t = e, l = q(e, "prompt"), r = q(e, "style"), v = q(e, "ratio"), g = q(e, "count"), w = x, { t: n } = L(), $ = ["hero", "banner", "social", "menu"], b = {
228
+ hero: "restaurant",
229
+ banner: "campaign",
230
+ social: "photo_camera",
231
+ menu: "menu_book"
232
+ }, C = F(
233
+ () => $.map((o) => ({
234
+ key: o,
235
+ icon: b[o],
236
+ name: n(`media.studio.tpl.${o}.name`),
237
+ desc: n(`media.studio.tpl.${o}.desc`),
238
+ prompt: n(`media.studio.tpl.${o}.prompt`)
239
+ }))
240
+ ), T = F(
241
+ () => {
242
+ var o;
243
+ return (o = C.value.find((y) => y.prompt === l.value)) == null ? void 0 : o.key;
244
+ }
245
+ ), S = F(() => /\[[^\]]+\]/.test(l.value)), f = F(() => l.value.trim().length > 0 && !t.busy);
246
+ function i(o) {
247
+ return o ? _t : $t;
248
+ }
249
+ return (o, y) => {
250
+ const _ = E("FmIcon"), z = E("FmTextarea"), O = E("FmChip"), Y = E("FmButton");
251
+ return m(), h("div", Je, [
252
+ s("div", Ke, [
253
+ s("div", Xe, [
254
+ s("span", We, u(a(n)("media.studio.templates")), 1),
255
+ s("span", Qe, u(a(n)("media.studio.templatesHint")), 1)
256
+ ]),
257
+ s("div", Ze, [
258
+ (m(!0), h(D, null, V(C.value, (d) => (m(), h("button", {
259
+ key: d.key,
260
+ type: "button",
261
+ class: N(["flex flex-col items-start gap-4 p-12 rounded-lg border text-left", i(T.value === d.key)]),
262
+ onClick: (M) => l.value = d.prompt
263
+ }, [
264
+ s("span", tt, [
265
+ k(_, {
266
+ name: d.icon,
267
+ size: "sm"
268
+ }, null, 8, ["name"]),
269
+ s("span", st, u(d.name), 1)
270
+ ]),
271
+ s("span", ot, u(d.desc), 1)
272
+ ], 10, et))), 128))
273
+ ])
274
+ ]),
275
+ s("div", at, [
276
+ k(z, {
277
+ modelValue: l.value,
278
+ "onUpdate:modelValue": y[0] || (y[0] = (d) => l.value = d),
279
+ label: a(n)("media.studio.prompt"),
280
+ placeholder: e.hasRef ? a(n)("media.studio.promptPlaceholderRef") : a(n)("media.studio.promptPlaceholder"),
281
+ "max-length": 500,
282
+ "show-word-count": ""
283
+ }, null, 8, ["modelValue", "label", "placeholder"]),
284
+ S.value ? (m(), h("div", nt, [
285
+ k(_, {
286
+ name: "info",
287
+ size: "sm"
288
+ }),
289
+ s("span", it, u(a(n)("media.studio.slotHint")), 1)
290
+ ])) : U("", !0)
291
+ ]),
292
+ ne(o.$slots, "default"),
293
+ s("div", lt, [
294
+ s("span", rt, u(a(n)("media.studio.style")), 1),
295
+ s("div", dt, [
296
+ (m(!0), h(D, null, V(a(Ne), (d) => (m(), G(O, {
297
+ key: d,
298
+ label: a(n)(`media.studio.styles.${d}`),
299
+ selected: r.value === d,
300
+ selectable: "",
301
+ compact: "",
302
+ onClick: (M) => r.value = d
303
+ }, null, 8, ["label", "selected", "onClick"]))), 128))
304
+ ])
305
+ ]),
306
+ s("div", ct, [
307
+ s("span", ut, u(a(n)("media.studio.ratio")), 1),
308
+ s("div", mt, [
309
+ (m(!0), h(D, null, V(a(je), (d) => (m(), h("button", {
310
+ key: d,
311
+ type: "button",
312
+ class: N(["flex items-center justify-center gap-4 h-40 rounded-lg border text-xs font-semibold", i(v.value === d)]),
313
+ onClick: (M) => v.value = d
314
+ }, [
315
+ s("span", null, u(d), 1),
316
+ s("span", pt, u(a(n)(`media.studio.ratios.${d}`)), 1)
317
+ ], 10, ft))), 128))
318
+ ])
319
+ ]),
320
+ s("div", vt, [
321
+ s("span", xt, u(a(n)("media.studio.count")), 1),
322
+ s("div", gt, [
323
+ (m(!0), h(D, null, V(a(Le), (d) => (m(), h("button", {
324
+ key: d,
325
+ type: "button",
326
+ class: N(["flex-1 h-40 rounded-lg border text-sm font-semibold", i(g.value === d)]),
327
+ onClick: (M) => g.value = d
328
+ }, " × " + u(d), 11, yt))), 128))
329
+ ])
330
+ ]),
331
+ s("div", bt, [
332
+ k(Y, {
333
+ label: e.busy ? a(n)("media.studio.generateBusy") : a(n)("media.studio.generate"),
334
+ "prepend-icon": "auto_awesome",
335
+ variant: "primary",
336
+ size: "md",
337
+ disabled: !f.value,
338
+ onClick: y[1] || (y[1] = (d) => w("generate"))
339
+ }, null, 8, ["label", "disabled"]),
340
+ s("span", ht, u(a(n)("media.studio.disclaimer")), 1)
341
+ ])
342
+ ]);
343
+ };
344
+ }
345
+ }), It = {
346
+ key: 0,
347
+ class: "flex h-[560px]"
348
+ }, wt = { class: "flex-1 min-w-0 flex items-center justify-center bg-black p-16" }, Tt = ["src", "alt"], Ct = { class: "w-[300px] flex-shrink-0 flex flex-col gap-12 p-16 overflow-y-auto" }, St = { class: "flex items-start justify-between gap-8" }, Ft = { class: "fm-typo-en-title-sm-600" }, Rt = ["title"], Et = { class: "text-sm font-semibold break-words" }, Bt = { class: "text-xs text-fm-color-typo-secondary" }, zt = /* @__PURE__ */ j({
349
+ __name: "StudioPreviewDialog",
350
+ props: {
351
+ item: {},
352
+ prompt: {}
353
+ },
354
+ emits: ["close", "save", "variant"],
355
+ setup(e, { emit: x }) {
356
+ const t = e, l = x, { t: r } = L();
357
+ return (v, g) => {
358
+ const w = E("FmIcon"), n = E("FmButton"), $ = E("FmDialog");
359
+ return m(), G($, {
360
+ "model-value": !!t.item,
361
+ "max-width": 920,
362
+ "max-height": "560px",
363
+ "content-class": "!p-0 studio-preview-dialog-content",
364
+ "close-button": !1,
365
+ "onUpdate:modelValue": g[3] || (g[3] = (b) => !b && l("close"))
366
+ }, {
367
+ default: te(() => [
368
+ t.item ? (m(), h("div", It, [
369
+ s("div", wt, [
370
+ s("img", {
371
+ src: t.item.url,
372
+ class: "max-w-full max-h-full object-contain",
373
+ alt: t.prompt
374
+ }, null, 8, Tt)
375
+ ]),
376
+ s("div", Ct, [
377
+ s("div", St, [
378
+ s("span", Ft, u(a(r)("media.studio.preview")), 1),
379
+ s("button", {
380
+ type: "button",
381
+ class: "w-32 h-32 rounded-lg bg-fm-color-neutral-gray-100 flex items-center justify-center flex-shrink-0",
382
+ title: a(r)("media.library.cancelButton"),
383
+ onClick: g[0] || (g[0] = (b) => l("close"))
384
+ }, [
385
+ k(w, {
386
+ name: "close",
387
+ size: "sm"
388
+ })
389
+ ], 8, Rt)
390
+ ]),
391
+ s("span", Et, u(t.prompt), 1),
392
+ s("span", Bt, u(t.item.width) + "×" + u(t.item.height), 1),
393
+ g[4] || (g[4] = s("div", { class: "flex-1" }, null, -1)),
394
+ k(n, {
395
+ label: a(r)("media.studio.variant"),
396
+ variant: "tertiary",
397
+ size: "md",
398
+ onClick: g[1] || (g[1] = (b) => l("variant"))
399
+ }, null, 8, ["label"]),
400
+ k(n, {
401
+ label: t.item.saved ? a(r)("media.studio.saved") : a(r)("media.studio.save"),
402
+ variant: "primary",
403
+ size: "md",
404
+ disabled: t.item.saved,
405
+ onClick: g[2] || (g[2] = (b) => l("save"))
406
+ }, null, 8, ["label", "disabled"])
407
+ ])
408
+ ])) : U("", !0)
409
+ ]),
410
+ _: 1
411
+ }, 8, ["model-value"]);
412
+ };
413
+ }
414
+ }), Pt = { class: "flex flex-col gap-8" }, Ut = { class: "flex items-baseline gap-8" }, Ot = { class: "text-sm font-semibold" }, At = { class: "text-xs text-fm-color-typo-tertiary" }, Dt = {
415
+ key: 0,
416
+ class: "flex items-center gap-8 p-8 rounded-lg border border-fm-color-neutral-gray-200"
417
+ }, Mt = ["src"], Vt = { class: "flex-1 text-xs truncate" }, Nt = { class: "flex gap-8" }, jt = { class: "text-xs text-fm-color-typo-tertiary" }, Lt = {
418
+ key: 2,
419
+ class: "text-xs text-[--fm-color-typo-critical,#FF3B30]"
420
+ }, qt = {
421
+ key: 3,
422
+ class: "flex flex-col gap-4"
423
+ }, Gt = { class: "flex items-baseline gap-8" }, Ht = { class: "text-xs font-semibold" }, Yt = { class: "text-xs font-semibold" }, Jt = { class: "text-xs text-fm-color-typo-secondary" }, Kt = /* @__PURE__ */ j({
424
+ __name: "StudioRefPicker",
425
+ props: /* @__PURE__ */ H({
426
+ businessId: {},
427
+ restaurantId: {}
428
+ }, {
429
+ strength: { required: !0 },
430
+ strengthModifiers: {}
431
+ }),
432
+ emits: /* @__PURE__ */ H(["change"], ["update:strength"]),
433
+ setup(e, { emit: x }) {
434
+ const t = e, l = q(e, "strength"), r = x, { t: v } = L(), g = B(!1), w = B(!1), n = B(""), $ = B(null), b = B(null), C = F(() => l.value <= 35 ? v("media.studio.refWeak") : l.value >= 70 ? v("media.studio.refStrong") : v("media.studio.refMid"));
435
+ function T(i) {
436
+ g.value = !1, n.value = "", $.value = { url: i.url, label: i.asset.fileName }, r("change", {
437
+ refAssetId: i.asset.id,
438
+ // null means the original was picked, and the request omits refRatio for that.
439
+ refRatio: i.ratio ?? void 0,
440
+ label: i.asset.fileName
441
+ });
442
+ }
443
+ async function S(i) {
444
+ var y;
445
+ const o = (y = i.target.files) == null ? void 0 : y[0];
446
+ if (o) {
447
+ w.value = !0, n.value = "";
448
+ try {
449
+ const _ = await He(t.businessId, {
450
+ contentType: o.type,
451
+ fileSize: o.size
452
+ });
453
+ await Ye(_, o), $.value = { url: URL.createObjectURL(o), label: o.name }, r("change", { refTmpToken: _.token, label: o.name });
454
+ } catch (_) {
455
+ n.value = _ instanceof Error ? _.message : String(_), $.value = null, r("change", null);
456
+ } finally {
457
+ w.value = !1, b.value && (b.value.value = "");
458
+ }
459
+ }
460
+ }
461
+ function f() {
462
+ $.value = null, n.value = "", r("change", null);
463
+ }
464
+ return (i, o) => {
465
+ const y = E("FmButton");
466
+ return m(), h("div", Pt, [
467
+ s("div", Ut, [
468
+ s("span", Ot, u(a(v)("media.studio.ref")), 1),
469
+ s("span", At, u(a(v)("media.studio.refHint")), 1)
470
+ ]),
471
+ $.value ? (m(), h("div", Dt, [
472
+ $.value.url ? (m(), h("img", {
473
+ key: 0,
474
+ src: $.value.url,
475
+ class: "w-48 h-48 rounded object-cover",
476
+ alt: ""
477
+ }, null, 8, Mt)) : U("", !0),
478
+ s("span", Vt, u($.value.label), 1),
479
+ k(y, {
480
+ label: a(v)("media.studio.refClear"),
481
+ variant: "tertiary",
482
+ size: "md",
483
+ onClick: f
484
+ }, null, 8, ["label"])
485
+ ])) : (m(), h(D, { key: 1 }, [
486
+ s("div", Nt, [
487
+ k(y, {
488
+ label: a(v)("media.studio.refPick"),
489
+ variant: "tertiary",
490
+ size: "md",
491
+ class: "flex-1",
492
+ onClick: o[0] || (o[0] = (_) => g.value = !0)
493
+ }, null, 8, ["label"]),
494
+ k(y, {
495
+ label: a(v)("media.studio.refUpload"),
496
+ variant: "tertiary",
497
+ size: "md",
498
+ class: "flex-1",
499
+ disabled: w.value,
500
+ onClick: o[1] || (o[1] = (_) => {
501
+ var z;
502
+ return (z = b.value) == null ? void 0 : z.click();
503
+ })
504
+ }, null, 8, ["label", "disabled"]),
505
+ s("input", {
506
+ ref_key: "fileInput",
507
+ ref: b,
508
+ type: "file",
509
+ accept: "image/jpeg,image/png,image/webp",
510
+ class: "hidden",
511
+ onChange: S
512
+ }, null, 544)
513
+ ]),
514
+ s("span", jt, u(a(v)("media.studio.refUploadHint")), 1)
515
+ ], 64)),
516
+ n.value ? (m(), h("span", Lt, u(n.value), 1)) : U("", !0),
517
+ $.value ? (m(), h("div", qt, [
518
+ s("div", Gt, [
519
+ s("span", Ht, u(a(v)("media.studio.refStrength")), 1),
520
+ o[4] || (o[4] = s("span", { class: "flex-1" }, null, -1)),
521
+ s("span", Yt, u(l.value) + "%", 1)
522
+ ]),
523
+ ie(s("input", {
524
+ "onUpdate:modelValue": o[2] || (o[2] = (_) => l.value = _),
525
+ type: "range",
526
+ min: "10",
527
+ max: "90",
528
+ step: "5",
529
+ class: "w-full cursor-pointer accent-[--fm-color-brand-primary,#FF7823]"
530
+ }, null, 512), [
531
+ [
532
+ le,
533
+ l.value,
534
+ void 0,
535
+ { number: !0 }
536
+ ]
537
+ ]),
538
+ s("span", Jt, u(C.value), 1)
539
+ ])) : U("", !0),
540
+ k(a(pe), {
541
+ open: g.value,
542
+ "onUpdate:open": o[3] || (o[3] = (_) => g.value = _),
543
+ "business-id": e.businessId,
544
+ "restaurant-id": e.restaurantId,
545
+ "allow-upload": !1,
546
+ onSelect: T
547
+ }, null, 8, ["open", "business-id", "restaurant-id"])
548
+ ]);
549
+ };
550
+ }
551
+ }), Xt = "ai", Wt = "ai-generated";
552
+ function Qt(e) {
553
+ return e === "success" || e === "failed";
554
+ }
555
+ function Z(e) {
556
+ return e.some((x) => !Qt(x.status));
557
+ }
558
+ function Zt(e, x) {
559
+ return e.map((t) => {
560
+ const l = x.find((r) => r.taskId === t.taskId);
561
+ return l ? { ...t, ...l, saved: t.saved } : t;
562
+ });
563
+ }
564
+ function es(e) {
565
+ const x = !!(e.refAssetId || e.refTmpToken);
566
+ return {
567
+ prompt: e.prompt.trim(),
568
+ style: e.style,
569
+ ratio: e.ratio,
570
+ count: e.count,
571
+ refAssetId: e.refAssetId,
572
+ refRatio: e.refRatio,
573
+ refTmpToken: e.refTmpToken,
574
+ refStrength: x ? e.refStrength : void 0,
575
+ restaurantId: e.restaurantId
576
+ };
577
+ }
578
+ const ts = 3e3, ss = de("mediaGeneration", () => {
579
+ const e = B([]), x = F(() => e.value.some((f) => Z(f.items)));
580
+ let t, l = "";
581
+ function r(f, i) {
582
+ const o = e.value.find((y) => y.batchId === f);
583
+ o && (o.items = Zt(o.items, i));
584
+ }
585
+ function v() {
586
+ t !== void 0 || !l || (t = setTimeout(async () => {
587
+ t = void 0;
588
+ const f = e.value.filter((i) => Z(i.items));
589
+ for (const i of f)
590
+ try {
591
+ const o = await Ge(l, i.batchId);
592
+ r(i.batchId, o.items);
593
+ } catch {
594
+ }
595
+ x.value && v();
596
+ }, ts));
597
+ }
598
+ async function g(f, i) {
599
+ l = f;
600
+ const o = await qe(f, es(i));
601
+ e.value.unshift({
602
+ batchId: o.batchId,
603
+ params: i,
604
+ items: o.items.map((y) => ({
605
+ taskId: y.taskId,
606
+ assetId: y.assetId,
607
+ imageId: y.imageId,
608
+ status: y.status,
609
+ attempts: 0,
610
+ error: y.error,
611
+ saved: !1
612
+ }))
613
+ }), v();
614
+ }
615
+ async function w(f, i) {
616
+ await g(f, i);
617
+ }
618
+ async function n(f, i) {
619
+ await g(f, { ...i.params });
620
+ }
621
+ async function $(f, i) {
622
+ await g(f, { ...i.params, count: 1 });
623
+ }
624
+ async function b(f, i, o) {
625
+ var _;
626
+ await ve(f, o.assetId, [Wt]);
627
+ const y = (_ = e.value.find((z) => z.batchId === i.batchId)) == null ? void 0 : _.items.find((z) => z.taskId === o.taskId);
628
+ y && (y.saved = !0);
629
+ }
630
+ async function C(f, i, o) {
631
+ await xe(f, [
632
+ {
633
+ refType: Xt,
634
+ refId: i.batchId,
635
+ imageId: o.imageId,
636
+ aspectRatios: null
637
+ }
638
+ ]);
639
+ const y = e.value.find((_) => _.batchId === i.batchId);
640
+ y && (y.items = y.items.filter((_) => _.taskId !== o.taskId), y.items.length === 0 && (e.value = e.value.filter((_) => _.batchId !== i.batchId)));
641
+ }
642
+ function T(f) {
643
+ l = f, x.value && v();
644
+ }
645
+ function S() {
646
+ t !== void 0 && clearTimeout(t), t = void 0, e.value = [];
647
+ }
648
+ return { batches: e, busy: x, compose: w, regenerate: n, variantOf: $, save: b, discard: C, resume: T, reset: S };
649
+ }), os = { class: "flex items-start gap-24 p-24" }, as = { class: "flex-1 min-w-0 flex flex-col gap-24" }, ns = {
650
+ key: 0,
651
+ class: "min-h-[340px] flex flex-col items-center justify-center gap-12 border-2 border-dashed border-fm-color-neutral-gray-300 rounded-2xl"
652
+ }, is = { class: "text-lg font-semibold" }, ls = { class: "text-sm text-fm-color-typo-secondary" }, fs = /* @__PURE__ */ j({
653
+ __name: "AiStudio",
654
+ setup(e) {
655
+ const x = ue(), { t } = L(), l = me(), r = fe(), v = F(() => {
656
+ var p;
657
+ return ((p = x.currentBusiness.value) == null ? void 0 : p.id) ?? "";
658
+ }), g = F(() => {
659
+ var p;
660
+ return (p = x.currentRestaurant.value) == null ? void 0 : p._id;
661
+ }), w = ss(), { batches: n, busy: $ } = ce(w);
662
+ re(() => w.resume(v.value));
663
+ const b = B(""), C = B("photo"), T = B("1x1"), S = B(2), f = B(55), i = B(null), o = F(() => !!i.value);
664
+ function y() {
665
+ var p, c, R, P;
666
+ return {
667
+ prompt: b.value,
668
+ style: C.value,
669
+ ratio: T.value,
670
+ count: S.value,
671
+ refAssetId: (p = i.value) == null ? void 0 : p.refAssetId,
672
+ refRatio: (c = i.value) == null ? void 0 : c.refRatio,
673
+ refTmpToken: (R = i.value) == null ? void 0 : R.refTmpToken,
674
+ refStrength: i.value ? f.value : void 0,
675
+ refLabel: (P = i.value) == null ? void 0 : P.label,
676
+ restaurantId: g.value
677
+ };
678
+ }
679
+ function _(p) {
680
+ var R, P;
681
+ return ((P = (R = p == null ? void 0 : p.response) == null ? void 0 : R.data) == null ? void 0 : P.message) ?? (p instanceof Error ? p.message : String(p));
682
+ }
683
+ function z(p) {
684
+ var c, R;
685
+ return (R = (c = p == null ? void 0 : p.response) == null ? void 0 : c.data) == null ? void 0 : R.code;
686
+ }
687
+ function O(p) {
688
+ const c = z(p) === "INSUFFICIENT_TOKENS" ? t("media.studio.insufficientTokens") : _(p);
689
+ r.open({ type: "error", title: c });
690
+ }
691
+ async function Y() {
692
+ try {
693
+ await w.compose(v.value, y());
694
+ } catch (p) {
695
+ O(p);
696
+ }
697
+ }
698
+ const d = B(null);
699
+ async function M(p, c) {
700
+ try {
701
+ await w.save(v.value, p, c), r.open({ type: "success", title: t("media.studio.savedToast") });
702
+ } catch (R) {
703
+ O(R);
704
+ }
705
+ }
706
+ function se(p, c) {
707
+ l.open({
708
+ title: t("media.studio.discardTitle"),
709
+ message: t("media.studio.discardMessage"),
710
+ primaryActions: { text: t("media.studio.discard"), close: !1, variant: "destructive" },
711
+ secondaryActions: { text: t("media.library.cancelButton"), close: !0 }
712
+ }).onPrimary(async () => {
713
+ var R;
714
+ try {
715
+ await w.discard(v.value, p, c), ((R = d.value) == null ? void 0 : R.item.taskId) === c.taskId && (d.value = null), r.open({ type: "success", title: t("media.studio.discardedToast") });
716
+ } catch (P) {
717
+ O(P);
718
+ }
719
+ l.close();
720
+ });
721
+ }
722
+ async function W(p) {
723
+ try {
724
+ await w.variantOf(v.value, p);
725
+ } catch (c) {
726
+ O(c);
727
+ }
728
+ }
729
+ async function oe(p) {
730
+ try {
731
+ await w.regenerate(v.value, p);
732
+ } catch (c) {
733
+ O(c);
734
+ }
735
+ }
736
+ return (p, c) => {
737
+ var P, Q;
738
+ const R = E("FmIcon");
739
+ return m(), h("div", os, [
740
+ k(kt, {
741
+ prompt: b.value,
742
+ "onUpdate:prompt": c[2] || (c[2] = (I) => b.value = I),
743
+ style: ee(C.value),
744
+ "onUpdate:style": c[3] || (c[3] = (I) => C.value = I),
745
+ ratio: T.value,
746
+ "onUpdate:ratio": c[4] || (c[4] = (I) => T.value = I),
747
+ count: S.value,
748
+ "onUpdate:count": c[5] || (c[5] = (I) => S.value = I),
749
+ class: "flex-shrink-0",
750
+ busy: a($),
751
+ "has-ref": o.value,
752
+ onGenerate: Y
753
+ }, {
754
+ default: te(() => [
755
+ k(Kt, {
756
+ strength: f.value,
757
+ "onUpdate:strength": c[0] || (c[0] = (I) => f.value = I),
758
+ "business-id": v.value,
759
+ "restaurant-id": g.value,
760
+ onChange: c[1] || (c[1] = (I) => i.value = I)
761
+ }, null, 8, ["strength", "business-id", "restaurant-id"])
762
+ ]),
763
+ _: 1
764
+ }, 8, ["prompt", "style", "ratio", "count", "busy", "has-ref"]),
765
+ s("div", as, [
766
+ a(n).length === 0 ? (m(), h("div", ns, [
767
+ k(R, {
768
+ name: "auto_awesome",
769
+ size: "xl"
770
+ }),
771
+ s("span", is, u(a(t)("media.studio.emptyTitle")), 1),
772
+ s("span", ls, u(a(t)("media.studio.emptyHint")), 1)
773
+ ])) : U("", !0),
774
+ (m(!0), h(D, null, V(a(n), (I) => (m(), G(Ve, {
775
+ key: I.batchId,
776
+ batch: I,
777
+ onRegenerate: (A) => oe(I),
778
+ onVariant: (A) => W(I),
779
+ onPreview: (A) => d.value = { batch: I, item: A },
780
+ onDiscard: (A) => se(I, A),
781
+ onSave: (A) => M(I, A)
782
+ }, null, 8, ["batch", "onRegenerate", "onVariant", "onPreview", "onDiscard", "onSave"]))), 128))
783
+ ]),
784
+ k(zt, {
785
+ item: ((P = d.value) == null ? void 0 : P.item) ?? null,
786
+ prompt: ((Q = d.value) == null ? void 0 : Q.batch.params.prompt) ?? "",
787
+ onClose: c[6] || (c[6] = (I) => d.value = null),
788
+ onSave: c[7] || (c[7] = (I) => d.value && M(d.value.batch, d.value.item)),
789
+ onVariant: c[8] || (c[8] = (I) => d.value && W(d.value.batch))
790
+ }, null, 8, ["item", "prompt"])
791
+ ]);
792
+ };
793
+ }
794
+ });
795
+ export {
796
+ fs as default
797
+ };