@stellartech/image-style-widget-directus 1.0.1 → 1.0.2
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/index.js +1 -1809
- package/package.json +21 -4
- package/src/components/EditModal.vue +0 -237
- package/src/components/ImageCard.vue +0 -380
- package/src/components/StyleCard.vue +0 -258
- package/src/composables/useDirectusApi.ts +0 -549
- package/src/index.ts +0 -44
- package/src/interface.vue +0 -1100
- package/tsconfig.json +0 -17
package/dist/index.js
CHANGED
|
@@ -1,1809 +1 @@
|
|
|
1
|
-
import { useApi, defineInterface } from '@directus/extensions-sdk';
|
|
2
|
-
import { defineComponent, computed, resolveComponent, openBlock, createElementBlock, normalizeClass, createCommentVNode, createElementVNode, createVNode, toDisplayString, ref, watch, nextTick, withModifiers, createTextVNode, withDirectives, vModelText, createBlock, inject, onMounted, normalizeStyle, unref, Fragment, withKeys, renderList } from 'vue';
|
|
3
|
-
|
|
4
|
-
const _hoisted_1$3 = { class: "style-card__images" };
|
|
5
|
-
const _hoisted_2$3 = { class: "style-card__image" };
|
|
6
|
-
const _hoisted_3$3 = ["src", "alt"];
|
|
7
|
-
const _hoisted_4$3 = {
|
|
8
|
-
key: 1,
|
|
9
|
-
class: "style-card__placeholder"
|
|
10
|
-
};
|
|
11
|
-
const _hoisted_5$3 = { class: "style-card__image" };
|
|
12
|
-
const _hoisted_6$3 = ["src", "alt"];
|
|
13
|
-
const _hoisted_7$3 = {
|
|
14
|
-
key: 1,
|
|
15
|
-
class: "style-card__placeholder"
|
|
16
|
-
};
|
|
17
|
-
const _hoisted_8$3 = { class: "style-card__content" };
|
|
18
|
-
const _hoisted_9$2 = { class: "style-card__header" };
|
|
19
|
-
const _hoisted_10$2 = { class: "style-card__radio" };
|
|
20
|
-
const _hoisted_11$2 = ["checked", "name"];
|
|
21
|
-
const _hoisted_12$2 = { class: "style-card__name" };
|
|
22
|
-
const _hoisted_13$2 = { class: "style-card__prompt" };
|
|
23
|
-
const _hoisted_14$2 = { class: "style-card__actions" };
|
|
24
|
-
const _hoisted_16$2 = ["disabled"];
|
|
25
|
-
var _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
26
|
-
__name: "StyleCard",
|
|
27
|
-
props: {
|
|
28
|
-
style: {},
|
|
29
|
-
isSelected: { type: Boolean },
|
|
30
|
-
radioGroupName: {},
|
|
31
|
-
loading: { type: Boolean },
|
|
32
|
-
getFileUrl: { type: Function }
|
|
33
|
-
},
|
|
34
|
-
emits: ["select", "edit", "regenerate"],
|
|
35
|
-
setup(__props) {
|
|
36
|
-
const props = __props;
|
|
37
|
-
const truncatedPrompt = computed(() => {
|
|
38
|
-
const maxLength = 100;
|
|
39
|
-
if (props.style.prompt.length <= maxLength)
|
|
40
|
-
return props.style.prompt;
|
|
41
|
-
return props.style.prompt.substring(0, maxLength) + "...";
|
|
42
|
-
});
|
|
43
|
-
return (_ctx, _cache) => {
|
|
44
|
-
const _component_v_icon = resolveComponent("v-icon");
|
|
45
|
-
return openBlock(), createElementBlock(
|
|
46
|
-
"div",
|
|
47
|
-
{
|
|
48
|
-
class: normalizeClass(["style-card", { "style-card--selected": __props.isSelected }])
|
|
49
|
-
},
|
|
50
|
-
[
|
|
51
|
-
createCommentVNode(" Images Section "),
|
|
52
|
-
createElementVNode("div", _hoisted_1$3, [
|
|
53
|
-
createElementVNode("div", _hoisted_2$3, [
|
|
54
|
-
__props.style.example_1 ? (openBlock(), createElementBlock("img", {
|
|
55
|
-
key: 0,
|
|
56
|
-
src: __props.getFileUrl(__props.style.example_1),
|
|
57
|
-
alt: `${__props.style.name} example 1`
|
|
58
|
-
}, null, 8, _hoisted_3$3)) : (openBlock(), createElementBlock("div", _hoisted_4$3, [
|
|
59
|
-
createVNode(_component_v_icon, { name: "image" })
|
|
60
|
-
]))
|
|
61
|
-
]),
|
|
62
|
-
createElementVNode("div", _hoisted_5$3, [
|
|
63
|
-
__props.style.example_2 ? (openBlock(), createElementBlock("img", {
|
|
64
|
-
key: 0,
|
|
65
|
-
src: __props.getFileUrl(__props.style.example_2),
|
|
66
|
-
alt: `${__props.style.name} example 2`
|
|
67
|
-
}, null, 8, _hoisted_6$3)) : (openBlock(), createElementBlock("div", _hoisted_7$3, [
|
|
68
|
-
createVNode(_component_v_icon, { name: "image" })
|
|
69
|
-
]))
|
|
70
|
-
])
|
|
71
|
-
]),
|
|
72
|
-
createCommentVNode(" Content Section "),
|
|
73
|
-
createElementVNode("div", _hoisted_8$3, [
|
|
74
|
-
createCommentVNode(" Header: Radio + Name "),
|
|
75
|
-
createElementVNode("div", _hoisted_9$2, [
|
|
76
|
-
createElementVNode("label", _hoisted_10$2, [
|
|
77
|
-
createElementVNode("input", {
|
|
78
|
-
type: "radio",
|
|
79
|
-
checked: __props.isSelected,
|
|
80
|
-
name: __props.radioGroupName,
|
|
81
|
-
onChange: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("select", __props.style.id))
|
|
82
|
-
}, null, 40, _hoisted_11$2),
|
|
83
|
-
_cache[4] || (_cache[4] = createElementVNode(
|
|
84
|
-
"span",
|
|
85
|
-
{ class: "style-card__radio-custom" },
|
|
86
|
-
null,
|
|
87
|
-
-1
|
|
88
|
-
/* CACHED */
|
|
89
|
-
))
|
|
90
|
-
]),
|
|
91
|
-
createElementVNode(
|
|
92
|
-
"h3",
|
|
93
|
-
_hoisted_12$2,
|
|
94
|
-
toDisplayString(__props.style.name),
|
|
95
|
-
1
|
|
96
|
-
/* TEXT */
|
|
97
|
-
)
|
|
98
|
-
]),
|
|
99
|
-
createCommentVNode(" Prompt Preview "),
|
|
100
|
-
createElementVNode(
|
|
101
|
-
"p",
|
|
102
|
-
_hoisted_13$2,
|
|
103
|
-
toDisplayString(truncatedPrompt.value),
|
|
104
|
-
1
|
|
105
|
-
/* TEXT */
|
|
106
|
-
),
|
|
107
|
-
createCommentVNode(" Actions "),
|
|
108
|
-
createElementVNode("div", _hoisted_14$2, [
|
|
109
|
-
createCommentVNode(' Hidden for now - set v-if="true" to restore '),
|
|
110
|
-
createCommentVNode("v-if", true),
|
|
111
|
-
createElementVNode("button", {
|
|
112
|
-
class: "style-card__btn style-card__btn--secondary",
|
|
113
|
-
disabled: __props.loading,
|
|
114
|
-
onClick: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("regenerate", __props.style))
|
|
115
|
-
}, [
|
|
116
|
-
createVNode(_component_v_icon, {
|
|
117
|
-
name: "refresh",
|
|
118
|
-
small: ""
|
|
119
|
-
}),
|
|
120
|
-
createElementVNode(
|
|
121
|
-
"span",
|
|
122
|
-
null,
|
|
123
|
-
toDisplayString(__props.loading ? "Generating..." : "Generate an example"),
|
|
124
|
-
1
|
|
125
|
-
/* TEXT */
|
|
126
|
-
)
|
|
127
|
-
], 8, _hoisted_16$2),
|
|
128
|
-
createElementVNode(
|
|
129
|
-
"button",
|
|
130
|
-
{
|
|
131
|
-
class: normalizeClass(["style-card__btn", { "style-card__btn--primary": __props.isSelected }]),
|
|
132
|
-
onClick: _cache[3] || (_cache[3] = ($event) => _ctx.$emit("select", __props.style.id))
|
|
133
|
-
},
|
|
134
|
-
[
|
|
135
|
-
createVNode(_component_v_icon, {
|
|
136
|
-
name: __props.isSelected ? "radio_button_checked" : "radio_button_unchecked",
|
|
137
|
-
small: ""
|
|
138
|
-
}, null, 8, ["name"]),
|
|
139
|
-
_cache[6] || (_cache[6] = createElementVNode(
|
|
140
|
-
"span",
|
|
141
|
-
null,
|
|
142
|
-
"Select",
|
|
143
|
-
-1
|
|
144
|
-
/* CACHED */
|
|
145
|
-
))
|
|
146
|
-
],
|
|
147
|
-
2
|
|
148
|
-
/* CLASS */
|
|
149
|
-
)
|
|
150
|
-
])
|
|
151
|
-
])
|
|
152
|
-
],
|
|
153
|
-
2
|
|
154
|
-
/* CLASS */
|
|
155
|
-
);
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
var e=[],t=[];function n(n,r){if(n&&"undefined"!=typeof document){var a,s=!0===r.prepend?"prepend":"append",d=!0===r.singleTag,i="string"==typeof r.container?document.querySelector(r.container):document.getElementsByTagName("head")[0];if(d){var u=e.indexOf(i);-1===u&&(u=e.push(i)-1,t[u]={}),a=t[u]&&t[u][s]?t[u][s]:t[u][s]=c();}else a=c();65279===n.charCodeAt(0)&&(n=n.substring(1)),a.styleSheet?a.styleSheet.cssText+=n:a.appendChild(document.createTextNode(n));}function c(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),r.attributes)for(var t=Object.keys(r.attributes),n=0;n<t.length;n++)e.setAttribute(t[n],r.attributes[t[n]]);var a="prepend"===s?"afterbegin":"beforeend";return i.insertAdjacentElement(a,e),e}}
|
|
161
|
-
|
|
162
|
-
var css$3 = "\n.style-card[data-v-99b31e92] {\n display: flex;\n gap: 20px;\n padding: 16px;\n border: 2px solid var(--theme--border-color-subdued, #e0e0e0);\n border-radius: 8px;\n background: var(--theme--background, #fff);\n transition: all 0.2s ease;\n}\n.style-card[data-v-99b31e92]:hover {\n border-color: var(--theme--border-color, #ccc);\n}\n.style-card--selected[data-v-99b31e92] {\n border-color: var(--theme--primary, #6644ff);\n background: var(--theme--primary-background, #f5f3ff);\n}\n.style-card__images[data-v-99b31e92] {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n.style-card__image[data-v-99b31e92] {\n width: 80px;\n height: 80px;\n border-radius: 6px;\n overflow: hidden;\n background: var(--theme--background-subdued, #f5f5f5);\n}\n.style-card__image img[data-v-99b31e92] {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.style-card__placeholder[data-v-99b31e92] {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--theme--foreground-subdued, #999);\n}\n.style-card__content[data-v-99b31e92] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-width: 0;\n}\n.style-card__header[data-v-99b31e92] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.style-card__radio[data-v-99b31e92] {\n position: relative;\n display: flex;\n align-items: center;\n cursor: pointer;\n}\n.style-card__radio input[data-v-99b31e92] {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n}\n.style-card__radio-custom[data-v-99b31e92] {\n width: 18px;\n height: 18px;\n border: 2px solid var(--theme--border-color, #ccc);\n border-radius: 50%;\n transition: all 0.2s ease;\n}\n.style-card__radio input:checked + .style-card__radio-custom[data-v-99b31e92] {\n border-color: var(--theme--primary, #6644ff);\n background: var(--theme--primary, #6644ff);\n box-shadow: inset 0 0 0 3px var(--theme--background, #fff);\n}\n.style-card__name[data-v-99b31e92] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.style-card__prompt[data-v-99b31e92] {\n margin: 0;\n font-size: 13px;\n color: var(--theme--foreground-subdued, #666);\n line-height: 1.4;\n flex: 1;\n}\n.style-card__actions[data-v-99b31e92] {\n display: flex;\n gap: 8px;\n margin-top: auto;\n}\n.style-card__btn[data-v-99b31e92] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 12px;\n border: 1px solid var(--theme--border-color, #ccc);\n border-radius: 4px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground-subdued, #666);\n font-size: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.style-card__btn[data-v-99b31e92]:hover:not(:disabled) {\n border-color: var(--theme--primary, #6644ff);\n color: var(--theme--primary, #6644ff);\n}\n.style-card__btn[data-v-99b31e92]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.style-card__btn--primary[data-v-99b31e92] {\n background: var(--theme--primary, #6644ff);\n border-color: var(--theme--primary, #6644ff);\n color: #fff;\n}\n.style-card__btn--primary[data-v-99b31e92]:hover:not(:disabled) {\n background: var(--theme--primary-accent, #5533ee);\n border-color: var(--theme--primary-accent, #5533ee);\n color: #fff;\n}\n.style-card__btn--secondary[data-v-99b31e92] {\n background: transparent;\n}\n";
|
|
163
|
-
n(css$3,{});
|
|
164
|
-
|
|
165
|
-
var _export_sfc = (sfc, props) => {
|
|
166
|
-
const target = sfc.__vccOpts || sfc;
|
|
167
|
-
for (const [key, val] of props) {
|
|
168
|
-
target[key] = val;
|
|
169
|
-
}
|
|
170
|
-
return target;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
var StyleCard = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-99b31e92"], ["__file", "StyleCard.vue"]]);
|
|
174
|
-
|
|
175
|
-
const _hoisted_1$2 = { class: "image-card" };
|
|
176
|
-
const _hoisted_2$2 = { class: "image-card__header" };
|
|
177
|
-
const _hoisted_3$2 = { class: "image-card__placeholder-text" };
|
|
178
|
-
const _hoisted_4$2 = { class: "image-card__body" };
|
|
179
|
-
const _hoisted_5$2 = { class: "image-card__images" };
|
|
180
|
-
const _hoisted_6$2 = ["src"];
|
|
181
|
-
const _hoisted_7$2 = {
|
|
182
|
-
key: 1,
|
|
183
|
-
class: "image-card__image-placeholder"
|
|
184
|
-
};
|
|
185
|
-
const _hoisted_8$2 = ["src"];
|
|
186
|
-
const _hoisted_9$1 = {
|
|
187
|
-
key: 1,
|
|
188
|
-
class: "image-card__image-placeholder"
|
|
189
|
-
};
|
|
190
|
-
const _hoisted_10$1 = { class: "image-card__content" };
|
|
191
|
-
const _hoisted_11$1 = { class: "image-card__prompts-section" };
|
|
192
|
-
const _hoisted_12$1 = { class: "image-card__prompt-item" };
|
|
193
|
-
const _hoisted_13$1 = { class: "image-card__prompt" };
|
|
194
|
-
const _hoisted_14$1 = { class: "image-card__actions" };
|
|
195
|
-
const _hoisted_15$1 = ["disabled"];
|
|
196
|
-
const _hoisted_16$1 = ["disabled"];
|
|
197
|
-
const _hoisted_17$1 = ["disabled"];
|
|
198
|
-
const _hoisted_18$1 = { class: "image-card__selection" };
|
|
199
|
-
const _hoisted_19$1 = ["src"];
|
|
200
|
-
var _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
201
|
-
__name: "ImageCard",
|
|
202
|
-
props: {
|
|
203
|
-
item: {},
|
|
204
|
-
loading: { type: Boolean },
|
|
205
|
-
getFileUrl: { type: Function }
|
|
206
|
-
},
|
|
207
|
-
emits: ["select-image", "edit", "regenerate", "delete"],
|
|
208
|
-
setup(__props) {
|
|
209
|
-
const props = __props;
|
|
210
|
-
const enlargedImage = ref(null);
|
|
211
|
-
function getOriginalFileUrl(fileId) {
|
|
212
|
-
if (!fileId)
|
|
213
|
-
return "";
|
|
214
|
-
return `/assets/${fileId}`;
|
|
215
|
-
}
|
|
216
|
-
function openLightbox(fileId) {
|
|
217
|
-
enlargedImage.value = getOriginalFileUrl(fileId);
|
|
218
|
-
}
|
|
219
|
-
function closeLightbox() {
|
|
220
|
-
enlargedImage.value = null;
|
|
221
|
-
}
|
|
222
|
-
const truncatedPrompt1 = computed(() => {
|
|
223
|
-
const maxLength = 120;
|
|
224
|
-
if (props.item.prompt1.length <= maxLength)
|
|
225
|
-
return props.item.prompt1;
|
|
226
|
-
return props.item.prompt1.substring(0, maxLength) + "...";
|
|
227
|
-
});
|
|
228
|
-
return (_ctx, _cache) => {
|
|
229
|
-
const _component_v_icon = resolveComponent("v-icon");
|
|
230
|
-
return openBlock(), createElementBlock("div", _hoisted_1$2, [
|
|
231
|
-
createCommentVNode(" Placeholder Header "),
|
|
232
|
-
createElementVNode("div", _hoisted_2$2, [
|
|
233
|
-
createVNode(_component_v_icon, { name: "image" }),
|
|
234
|
-
createElementVNode(
|
|
235
|
-
"span",
|
|
236
|
-
_hoisted_3$2,
|
|
237
|
-
toDisplayString(__props.item.placeholder),
|
|
238
|
-
1
|
|
239
|
-
/* TEXT */
|
|
240
|
-
)
|
|
241
|
-
]),
|
|
242
|
-
createElementVNode("div", _hoisted_4$2, [
|
|
243
|
-
createCommentVNode(" Images Section "),
|
|
244
|
-
createElementVNode("div", _hoisted_5$2, [
|
|
245
|
-
createElementVNode(
|
|
246
|
-
"div",
|
|
247
|
-
{
|
|
248
|
-
class: normalizeClass(["image-card__image", { "image-card__image--selected": __props.item.selected === 1 }]),
|
|
249
|
-
onClick: _cache[0] || (_cache[0] = ($event) => __props.item.image1 && openLightbox(__props.item.image1))
|
|
250
|
-
},
|
|
251
|
-
[
|
|
252
|
-
__props.item.image1 ? (openBlock(), createElementBlock("img", {
|
|
253
|
-
key: 0,
|
|
254
|
-
src: __props.getFileUrl(__props.item.image1),
|
|
255
|
-
alt: "Generated image 1"
|
|
256
|
-
}, null, 8, _hoisted_6$2)) : (openBlock(), createElementBlock("div", _hoisted_7$2, [
|
|
257
|
-
createVNode(_component_v_icon, { name: "image" })
|
|
258
|
-
])),
|
|
259
|
-
_cache[7] || (_cache[7] = createElementVNode(
|
|
260
|
-
"div",
|
|
261
|
-
{ class: "image-card__image-badge" },
|
|
262
|
-
"1",
|
|
263
|
-
-1
|
|
264
|
-
/* CACHED */
|
|
265
|
-
))
|
|
266
|
-
],
|
|
267
|
-
2
|
|
268
|
-
/* CLASS */
|
|
269
|
-
),
|
|
270
|
-
createElementVNode(
|
|
271
|
-
"div",
|
|
272
|
-
{
|
|
273
|
-
class: normalizeClass(["image-card__image", { "image-card__image--selected": __props.item.selected === 2 }]),
|
|
274
|
-
onClick: _cache[1] || (_cache[1] = ($event) => __props.item.image2 && openLightbox(__props.item.image2))
|
|
275
|
-
},
|
|
276
|
-
[
|
|
277
|
-
__props.item.image2 ? (openBlock(), createElementBlock("img", {
|
|
278
|
-
key: 0,
|
|
279
|
-
src: __props.getFileUrl(__props.item.image2),
|
|
280
|
-
alt: "Generated image 2"
|
|
281
|
-
}, null, 8, _hoisted_8$2)) : (openBlock(), createElementBlock("div", _hoisted_9$1, [
|
|
282
|
-
createVNode(_component_v_icon, { name: "image" })
|
|
283
|
-
])),
|
|
284
|
-
_cache[8] || (_cache[8] = createElementVNode(
|
|
285
|
-
"div",
|
|
286
|
-
{ class: "image-card__image-badge" },
|
|
287
|
-
"2",
|
|
288
|
-
-1
|
|
289
|
-
/* CACHED */
|
|
290
|
-
))
|
|
291
|
-
],
|
|
292
|
-
2
|
|
293
|
-
/* CLASS */
|
|
294
|
-
)
|
|
295
|
-
]),
|
|
296
|
-
createCommentVNode(" Content Section "),
|
|
297
|
-
createElementVNode("div", _hoisted_10$1, [
|
|
298
|
-
createCommentVNode(" Prompt Display "),
|
|
299
|
-
createElementVNode("div", _hoisted_11$1, [
|
|
300
|
-
createElementVNode("div", _hoisted_12$1, [
|
|
301
|
-
_cache[9] || (_cache[9] = createElementVNode(
|
|
302
|
-
"span",
|
|
303
|
-
{ class: "image-card__prompt-label" },
|
|
304
|
-
"Prompt:",
|
|
305
|
-
-1
|
|
306
|
-
/* CACHED */
|
|
307
|
-
)),
|
|
308
|
-
createElementVNode(
|
|
309
|
-
"p",
|
|
310
|
-
_hoisted_13$1,
|
|
311
|
-
toDisplayString(truncatedPrompt1.value),
|
|
312
|
-
1
|
|
313
|
-
/* TEXT */
|
|
314
|
-
)
|
|
315
|
-
])
|
|
316
|
-
]),
|
|
317
|
-
createCommentVNode(" Actions "),
|
|
318
|
-
createElementVNode("div", _hoisted_14$1, [
|
|
319
|
-
createElementVNode("button", {
|
|
320
|
-
class: "image-card__btn image-card__btn--secondary",
|
|
321
|
-
disabled: __props.loading,
|
|
322
|
-
onClick: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("edit", __props.item))
|
|
323
|
-
}, [
|
|
324
|
-
createVNode(_component_v_icon, {
|
|
325
|
-
name: "edit",
|
|
326
|
-
small: ""
|
|
327
|
-
}),
|
|
328
|
-
_cache[10] || (_cache[10] = createElementVNode(
|
|
329
|
-
"span",
|
|
330
|
-
null,
|
|
331
|
-
"Edit Prompt",
|
|
332
|
-
-1
|
|
333
|
-
/* CACHED */
|
|
334
|
-
))
|
|
335
|
-
], 8, _hoisted_15$1),
|
|
336
|
-
createElementVNode("button", {
|
|
337
|
-
class: "image-card__btn image-card__btn--secondary",
|
|
338
|
-
disabled: __props.loading,
|
|
339
|
-
onClick: _cache[3] || (_cache[3] = ($event) => _ctx.$emit("regenerate", __props.item))
|
|
340
|
-
}, [
|
|
341
|
-
createVNode(_component_v_icon, {
|
|
342
|
-
name: "refresh",
|
|
343
|
-
small: ""
|
|
344
|
-
}),
|
|
345
|
-
createElementVNode(
|
|
346
|
-
"span",
|
|
347
|
-
null,
|
|
348
|
-
toDisplayString(__props.loading ? "Generating..." : "Regenerate"),
|
|
349
|
-
1
|
|
350
|
-
/* TEXT */
|
|
351
|
-
)
|
|
352
|
-
], 8, _hoisted_16$1),
|
|
353
|
-
createElementVNode("button", {
|
|
354
|
-
class: "image-card__btn image-card__btn--danger",
|
|
355
|
-
disabled: __props.loading,
|
|
356
|
-
onClick: _cache[4] || (_cache[4] = ($event) => _ctx.$emit("delete", __props.item))
|
|
357
|
-
}, [
|
|
358
|
-
createVNode(_component_v_icon, {
|
|
359
|
-
name: "delete",
|
|
360
|
-
small: ""
|
|
361
|
-
}),
|
|
362
|
-
_cache[11] || (_cache[11] = createElementVNode(
|
|
363
|
-
"span",
|
|
364
|
-
null,
|
|
365
|
-
"Delete",
|
|
366
|
-
-1
|
|
367
|
-
/* CACHED */
|
|
368
|
-
))
|
|
369
|
-
], 8, _hoisted_17$1)
|
|
370
|
-
]),
|
|
371
|
-
createCommentVNode(" Selection Buttons "),
|
|
372
|
-
createElementVNode("div", _hoisted_18$1, [
|
|
373
|
-
createElementVNode(
|
|
374
|
-
"button",
|
|
375
|
-
{
|
|
376
|
-
class: normalizeClass(["image-card__select-btn", { "image-card__select-btn--active": __props.item.selected === 1 }]),
|
|
377
|
-
onClick: _cache[5] || (_cache[5] = ($event) => _ctx.$emit("select-image", __props.item.placeholder, 1))
|
|
378
|
-
},
|
|
379
|
-
[
|
|
380
|
-
createVNode(_component_v_icon, {
|
|
381
|
-
name: __props.item.selected === 1 ? "check_box" : "check_box_outline_blank",
|
|
382
|
-
small: ""
|
|
383
|
-
}, null, 8, ["name"]),
|
|
384
|
-
_cache[12] || (_cache[12] = createElementVNode(
|
|
385
|
-
"span",
|
|
386
|
-
null,
|
|
387
|
-
"Select 1",
|
|
388
|
-
-1
|
|
389
|
-
/* CACHED */
|
|
390
|
-
))
|
|
391
|
-
],
|
|
392
|
-
2
|
|
393
|
-
/* CLASS */
|
|
394
|
-
),
|
|
395
|
-
createElementVNode(
|
|
396
|
-
"button",
|
|
397
|
-
{
|
|
398
|
-
class: normalizeClass(["image-card__select-btn", { "image-card__select-btn--active": __props.item.selected === 2 }]),
|
|
399
|
-
onClick: _cache[6] || (_cache[6] = ($event) => _ctx.$emit("select-image", __props.item.placeholder, 2))
|
|
400
|
-
},
|
|
401
|
-
[
|
|
402
|
-
createVNode(_component_v_icon, {
|
|
403
|
-
name: __props.item.selected === 2 ? "check_box" : "check_box_outline_blank",
|
|
404
|
-
small: ""
|
|
405
|
-
}, null, 8, ["name"]),
|
|
406
|
-
_cache[13] || (_cache[13] = createElementVNode(
|
|
407
|
-
"span",
|
|
408
|
-
null,
|
|
409
|
-
"Select 2",
|
|
410
|
-
-1
|
|
411
|
-
/* CACHED */
|
|
412
|
-
))
|
|
413
|
-
],
|
|
414
|
-
2
|
|
415
|
-
/* CLASS */
|
|
416
|
-
)
|
|
417
|
-
])
|
|
418
|
-
])
|
|
419
|
-
]),
|
|
420
|
-
createCommentVNode(" Lightbox Modal "),
|
|
421
|
-
enlargedImage.value ? (openBlock(), createElementBlock("div", {
|
|
422
|
-
key: 0,
|
|
423
|
-
class: "image-card__lightbox",
|
|
424
|
-
onClick: closeLightbox
|
|
425
|
-
}, [
|
|
426
|
-
createElementVNode("img", {
|
|
427
|
-
src: enlargedImage.value,
|
|
428
|
-
alt: "Enlarged image"
|
|
429
|
-
}, null, 8, _hoisted_19$1)
|
|
430
|
-
])) : createCommentVNode("v-if", true)
|
|
431
|
-
]);
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
var css$2 = "\n.image-card[data-v-20b0594f] {\n border: 2px solid var(--theme--border-color-subdued, #e0e0e0);\n border-radius: 8px;\n background: var(--theme--background, #fff);\n overflow: hidden;\n}\n.image-card__header[data-v-20b0594f] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: var(--theme--background-subdued, #f5f5f5);\n border-bottom: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n}\n.image-card__placeholder-text[data-v-20b0594f] {\n font-size: 14px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.image-card__body[data-v-20b0594f] {\n display: flex;\n gap: 20px;\n padding: 16px;\n}\n.image-card__images[data-v-20b0594f] {\n display: flex;\n gap: 12px;\n flex-shrink: 0;\n}\n.image-card__image[data-v-20b0594f] {\n position: relative;\n width: 100px;\n height: 100px;\n border-radius: 6px;\n overflow: hidden;\n background: var(--theme--background-subdued, #f5f5f5);\n border: 3px solid transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.image-card__image[data-v-20b0594f]:hover {\n border-color: var(--theme--primary-subdued, #c4b8ff);\n}\n.image-card__image--selected[data-v-20b0594f] {\n border-color: var(--theme--primary, #6644ff);\n}\n.image-card__image img[data-v-20b0594f] {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.image-card__image-placeholder[data-v-20b0594f] {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--theme--foreground-subdued, #999);\n}\n.image-card__image-badge[data-v-20b0594f] {\n position: absolute;\n top: 4px;\n right: 4px;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: rgba(0, 0, 0, 0.6);\n color: #fff;\n font-size: 11px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.image-card__image--selected .image-card__image-badge[data-v-20b0594f] {\n background: var(--theme--primary, #6644ff);\n}\n.image-card__content[data-v-20b0594f] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n}\n.image-card__prompts-section[data-v-20b0594f] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.image-card__prompt-item[data-v-20b0594f] {\n padding: 8px;\n background: var(--theme--background-subdued, #f9f9f9);\n border-radius: 4px;\n}\n.image-card__prompt-label[data-v-20b0594f] {\n font-size: 11px;\n font-weight: 600;\n color: var(--theme--foreground-subdued, #666);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n.image-card__prompt[data-v-20b0594f] {\n margin: 4px 0 0 0;\n font-size: 12px;\n color: var(--theme--foreground, #333);\n line-height: 1.4;\n}\n.image-card__actions[data-v-20b0594f] {\n display: flex;\n gap: 8px;\n}\n.image-card__btn[data-v-20b0594f] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 12px;\n border: 1px solid var(--theme--border-color, #ccc);\n border-radius: 4px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground-subdued, #666);\n font-size: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.image-card__btn[data-v-20b0594f]:hover:not(:disabled) {\n border-color: var(--theme--primary, #6644ff);\n color: var(--theme--primary, #6644ff);\n}\n.image-card__btn[data-v-20b0594f]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.image-card__btn--secondary[data-v-20b0594f] {\n background: transparent;\n}\n.image-card__btn--danger[data-v-20b0594f] {\n background: transparent;\n border-color: var(--theme--danger, #f44336);\n color: var(--theme--danger, #f44336);\n}\n.image-card__btn--danger[data-v-20b0594f]:hover:not(:disabled) {\n background: var(--theme--danger-background, #ffebee);\n border-color: var(--theme--danger, #f44336);\n color: var(--theme--danger, #f44336);\n}\n.image-card__selection[data-v-20b0594f] {\n display: flex;\n gap: 8px;\n}\n.image-card__select-btn[data-v-20b0594f] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 2px solid var(--theme--border-color, #ccc);\n border-radius: 6px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground-subdued, #666);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.image-card__select-btn[data-v-20b0594f]:hover {\n border-color: var(--theme--primary-subdued, #c4b8ff);\n}\n.image-card__select-btn--active[data-v-20b0594f] {\n border-color: var(--theme--primary, #6644ff);\n background: var(--theme--primary-background, #f5f3ff);\n color: var(--theme--primary, #6644ff);\n}\n\n/* Lightbox Modal */\n.image-card__lightbox[data-v-20b0594f] {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.85);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n cursor: zoom-out;\n padding: 40px;\n}\n.image-card__lightbox img[data-v-20b0594f] {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border-radius: 8px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);\n}\n";
|
|
437
|
-
n(css$2,{});
|
|
438
|
-
|
|
439
|
-
var ImageCard = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-20b0594f"], ["__file", "ImageCard.vue"]]);
|
|
440
|
-
|
|
441
|
-
const _hoisted_1$1 = { class: "edit-modal" };
|
|
442
|
-
const _hoisted_2$1 = { class: "edit-modal__header" };
|
|
443
|
-
const _hoisted_3$1 = { class: "edit-modal__title" };
|
|
444
|
-
const _hoisted_4$1 = { class: "edit-modal__body" };
|
|
445
|
-
const _hoisted_5$1 = { class: "edit-modal__label" };
|
|
446
|
-
const _hoisted_6$1 = ["placeholder"];
|
|
447
|
-
const _hoisted_7$1 = { class: "edit-modal__footer" };
|
|
448
|
-
const _hoisted_8$1 = ["disabled"];
|
|
449
|
-
var _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
450
|
-
__name: "EditModal",
|
|
451
|
-
props: {
|
|
452
|
-
isOpen: { type: Boolean },
|
|
453
|
-
title: {},
|
|
454
|
-
prompt: {},
|
|
455
|
-
placeholder: {},
|
|
456
|
-
saving: { type: Boolean }
|
|
457
|
-
},
|
|
458
|
-
emits: ["close", "save"],
|
|
459
|
-
setup(__props, { emit: __emit }) {
|
|
460
|
-
const props = __props;
|
|
461
|
-
const emit = __emit;
|
|
462
|
-
const localPrompt = ref(props.prompt);
|
|
463
|
-
const textareaRef = ref(null);
|
|
464
|
-
const hasChanges = computed(() => localPrompt.value !== props.prompt);
|
|
465
|
-
watch(() => props.prompt, (newPrompt) => {
|
|
466
|
-
localPrompt.value = newPrompt;
|
|
467
|
-
});
|
|
468
|
-
watch(() => props.isOpen, async (isOpen) => {
|
|
469
|
-
if (isOpen) {
|
|
470
|
-
localPrompt.value = props.prompt;
|
|
471
|
-
await nextTick();
|
|
472
|
-
textareaRef.value?.focus();
|
|
473
|
-
textareaRef.value?.select();
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
function handleSave() {
|
|
477
|
-
if (hasChanges.value) {
|
|
478
|
-
emit("save", localPrompt.value);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
return (_ctx, _cache) => {
|
|
482
|
-
const _component_v_icon = resolveComponent("v-icon");
|
|
483
|
-
return __props.isOpen ? (openBlock(), createElementBlock("div", {
|
|
484
|
-
key: 0,
|
|
485
|
-
class: "edit-modal__overlay",
|
|
486
|
-
onClick: _cache[3] || (_cache[3] = withModifiers(($event) => _ctx.$emit("close"), ["self"]))
|
|
487
|
-
}, [
|
|
488
|
-
createElementVNode("div", _hoisted_1$1, [
|
|
489
|
-
createElementVNode("div", _hoisted_2$1, [
|
|
490
|
-
createElementVNode("h3", _hoisted_3$1, [
|
|
491
|
-
createVNode(_component_v_icon, { name: "edit" }),
|
|
492
|
-
_cache[4] || (_cache[4] = createTextVNode(
|
|
493
|
-
" Edit Prompt ",
|
|
494
|
-
-1
|
|
495
|
-
/* CACHED */
|
|
496
|
-
))
|
|
497
|
-
]),
|
|
498
|
-
createElementVNode("button", {
|
|
499
|
-
class: "edit-modal__close",
|
|
500
|
-
onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("close"))
|
|
501
|
-
}, [
|
|
502
|
-
createVNode(_component_v_icon, { name: "close" })
|
|
503
|
-
])
|
|
504
|
-
]),
|
|
505
|
-
createElementVNode("div", _hoisted_4$1, [
|
|
506
|
-
createElementVNode(
|
|
507
|
-
"label",
|
|
508
|
-
_hoisted_5$1,
|
|
509
|
-
toDisplayString(__props.title),
|
|
510
|
-
1
|
|
511
|
-
/* TEXT */
|
|
512
|
-
),
|
|
513
|
-
withDirectives(createElementVNode("textarea", {
|
|
514
|
-
ref_key: "textareaRef",
|
|
515
|
-
ref: textareaRef,
|
|
516
|
-
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => localPrompt.value = $event),
|
|
517
|
-
class: "edit-modal__textarea",
|
|
518
|
-
rows: "6",
|
|
519
|
-
placeholder: __props.placeholder
|
|
520
|
-
}, null, 8, _hoisted_6$1), [
|
|
521
|
-
[vModelText, localPrompt.value]
|
|
522
|
-
]),
|
|
523
|
-
_cache[5] || (_cache[5] = createElementVNode(
|
|
524
|
-
"p",
|
|
525
|
-
{ class: "edit-modal__hint" },
|
|
526
|
-
" Edit the prompt text and click Save to apply changes. ",
|
|
527
|
-
-1
|
|
528
|
-
/* CACHED */
|
|
529
|
-
))
|
|
530
|
-
]),
|
|
531
|
-
createElementVNode("div", _hoisted_7$1, [
|
|
532
|
-
createElementVNode("button", {
|
|
533
|
-
class: "edit-modal__btn edit-modal__btn--secondary",
|
|
534
|
-
onClick: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("close"))
|
|
535
|
-
}, " Cancel "),
|
|
536
|
-
createElementVNode("button", {
|
|
537
|
-
class: "edit-modal__btn edit-modal__btn--primary",
|
|
538
|
-
disabled: !hasChanges.value || __props.saving,
|
|
539
|
-
onClick: handleSave
|
|
540
|
-
}, [
|
|
541
|
-
__props.saving ? (openBlock(), createBlock(_component_v_icon, {
|
|
542
|
-
key: 0,
|
|
543
|
-
name: "refresh",
|
|
544
|
-
small: ""
|
|
545
|
-
})) : createCommentVNode("v-if", true),
|
|
546
|
-
createElementVNode(
|
|
547
|
-
"span",
|
|
548
|
-
null,
|
|
549
|
-
toDisplayString(__props.saving ? "Saving..." : "Save"),
|
|
550
|
-
1
|
|
551
|
-
/* TEXT */
|
|
552
|
-
)
|
|
553
|
-
], 8, _hoisted_8$1)
|
|
554
|
-
])
|
|
555
|
-
])
|
|
556
|
-
])) : createCommentVNode("v-if", true);
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
var css$1 = "\n.edit-modal__overlay[data-v-44d552bc] {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n padding: 20px;\n}\n.edit-modal[data-v-44d552bc] {\n width: 100%;\n max-width: 600px;\n background: var(--theme--background, #fff);\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n overflow: hidden;\n}\n.edit-modal__header[data-v-44d552bc] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n}\n.edit-modal__title[data-v-44d552bc] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.edit-modal__close[data-v-44d552bc] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--theme--foreground-subdued, #666);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.edit-modal__close[data-v-44d552bc]:hover {\n background: var(--theme--background-subdued, #f5f5f5);\n color: var(--theme--foreground, #333);\n}\n.edit-modal__body[data-v-44d552bc] {\n padding: 20px;\n}\n.edit-modal__label[data-v-44d552bc] {\n display: block;\n margin-bottom: 8px;\n font-size: 13px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.edit-modal__textarea[data-v-44d552bc] {\n width: 100%;\n padding: 12px;\n border: 2px solid var(--theme--border-color-subdued, #e0e0e0);\n border-radius: 6px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground, #333);\n font-size: 14px;\n font-family: inherit;\n line-height: 1.5;\n resize: vertical;\n transition: border-color 0.2s ease;\n}\n.edit-modal__textarea[data-v-44d552bc]:focus {\n outline: none;\n border-color: var(--theme--primary, #6644ff);\n}\n.edit-modal__textarea[data-v-44d552bc]::placeholder {\n color: var(--theme--foreground-subdued, #999);\n}\n.edit-modal__hint[data-v-44d552bc] {\n margin: 8px 0 0 0;\n font-size: 12px;\n color: var(--theme--foreground-subdued, #666);\n}\n.edit-modal__footer[data-v-44d552bc] {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 20px;\n border-top: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n background: var(--theme--background-subdued, #f9f9f9);\n}\n.edit-modal__btn[data-v-44d552bc] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 10px 20px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.edit-modal__btn[data-v-44d552bc]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.edit-modal__btn--secondary[data-v-44d552bc] {\n background: var(--theme--background, #fff);\n color: var(--theme--foreground-subdued, #666);\n border: 1px solid var(--theme--border-color, #ccc);\n}\n.edit-modal__btn--secondary[data-v-44d552bc]:hover:not(:disabled) {\n background: var(--theme--background-subdued, #f5f5f5);\n}\n.edit-modal__btn--primary[data-v-44d552bc] {\n background: var(--theme--primary, #6644ff);\n color: #fff;\n}\n.edit-modal__btn--primary[data-v-44d552bc]:hover:not(:disabled) {\n background: var(--theme--primary-accent, #5533ee);\n}\n";
|
|
562
|
-
n(css$1,{});
|
|
563
|
-
|
|
564
|
-
var EditModal = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-44d552bc"], ["__file", "EditModal.vue"]]);
|
|
565
|
-
|
|
566
|
-
const IMAGE_MODELS = [
|
|
567
|
-
{ id: "flux-kontext-pro", name: "Flux" },
|
|
568
|
-
{ id: "gemini-2.5-flash-image", name: "Nano Banana" }
|
|
569
|
-
];
|
|
570
|
-
const DEFAULT_SERVICE_URL = "http://localhost:8004";
|
|
571
|
-
function useDirectusApi() {
|
|
572
|
-
const api = useApi();
|
|
573
|
-
const loading = ref(false);
|
|
574
|
-
const error = ref(null);
|
|
575
|
-
async function fetchServiceUrl() {
|
|
576
|
-
try {
|
|
577
|
-
const response = await api.get("/items/WidgetConfig");
|
|
578
|
-
return response.data.data?.service_url || DEFAULT_SERVICE_URL;
|
|
579
|
-
} catch (e) {
|
|
580
|
-
console.warn("Failed to fetch service URL, using default:", e.message);
|
|
581
|
-
return DEFAULT_SERVICE_URL;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
async function updateServiceUrl(url) {
|
|
585
|
-
try {
|
|
586
|
-
await api.patch("/items/WidgetConfig", {
|
|
587
|
-
service_url: url
|
|
588
|
-
});
|
|
589
|
-
return true;
|
|
590
|
-
} catch (e) {
|
|
591
|
-
error.value = e.message || "Failed to update service URL";
|
|
592
|
-
return false;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
async function checkIsAdmin() {
|
|
596
|
-
try {
|
|
597
|
-
const response = await api.get("/users/me", {
|
|
598
|
-
params: {
|
|
599
|
-
fields: ["role.admin_access"]
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
return response.data.data?.role?.admin_access === true;
|
|
603
|
-
} catch (e) {
|
|
604
|
-
console.warn("Failed to check admin status:", e.message);
|
|
605
|
-
return false;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
async function fetchStyles(collection = "ImageStyles") {
|
|
609
|
-
loading.value = true;
|
|
610
|
-
error.value = null;
|
|
611
|
-
try {
|
|
612
|
-
const response = await api.get(`/items/${collection}`, {
|
|
613
|
-
params: {
|
|
614
|
-
filter: { is_active: { _eq: true } },
|
|
615
|
-
sort: ["sort"],
|
|
616
|
-
fields: ["id", "name", "prompt", "example_1", "example_2", "is_active", "sort"]
|
|
617
|
-
}
|
|
618
|
-
});
|
|
619
|
-
return response.data.data || [];
|
|
620
|
-
} catch (e) {
|
|
621
|
-
error.value = e.message || "Failed to fetch styles";
|
|
622
|
-
return [];
|
|
623
|
-
} finally {
|
|
624
|
-
loading.value = false;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
async function updateStylePrompt(collection, styleId, newPrompt) {
|
|
628
|
-
loading.value = true;
|
|
629
|
-
error.value = null;
|
|
630
|
-
try {
|
|
631
|
-
await api.patch(`/items/${collection}/${styleId}`, {
|
|
632
|
-
prompt: newPrompt
|
|
633
|
-
});
|
|
634
|
-
return true;
|
|
635
|
-
} catch (e) {
|
|
636
|
-
error.value = e.message || "Failed to update prompt";
|
|
637
|
-
return false;
|
|
638
|
-
} finally {
|
|
639
|
-
loading.value = false;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
async function generateSingleImage(prompt, model = "flux-kontext-pro", serviceUrl = DEFAULT_SERVICE_URL) {
|
|
643
|
-
const miscServiceUrl = `${serviceUrl}/api/misc/images/upload`;
|
|
644
|
-
const response = await fetch(miscServiceUrl, {
|
|
645
|
-
method: "POST",
|
|
646
|
-
headers: {
|
|
647
|
-
"Content-Type": "application/json"
|
|
648
|
-
},
|
|
649
|
-
body: JSON.stringify({
|
|
650
|
-
prompt,
|
|
651
|
-
model,
|
|
652
|
-
format: "png",
|
|
653
|
-
aspect_ratio: "16:9"
|
|
654
|
-
})
|
|
655
|
-
});
|
|
656
|
-
if (!response.ok) {
|
|
657
|
-
throw new Error(`Image generation failed: ${response.status}`);
|
|
658
|
-
}
|
|
659
|
-
const data = await response.json();
|
|
660
|
-
return data?.id || null;
|
|
661
|
-
}
|
|
662
|
-
async function regenerateStyleExamples(styleId, prompt, collection = "ImageStyles", model = "flux-kontext-pro", serviceUrl = DEFAULT_SERVICE_URL) {
|
|
663
|
-
loading.value = true;
|
|
664
|
-
error.value = null;
|
|
665
|
-
try {
|
|
666
|
-
const [example_1, example_2] = await Promise.all([
|
|
667
|
-
generateSingleImage(prompt, model, serviceUrl),
|
|
668
|
-
generateSingleImage(prompt, model, serviceUrl)
|
|
669
|
-
]);
|
|
670
|
-
if (example_1 && example_2) {
|
|
671
|
-
await api.patch(`/items/${collection}/${styleId}`, {
|
|
672
|
-
example_1,
|
|
673
|
-
example_2
|
|
674
|
-
});
|
|
675
|
-
return { example_1, example_2 };
|
|
676
|
-
}
|
|
677
|
-
error.value = "Failed to generate images";
|
|
678
|
-
return null;
|
|
679
|
-
} catch (e) {
|
|
680
|
-
error.value = e.message || "Failed to regenerate images";
|
|
681
|
-
return null;
|
|
682
|
-
} finally {
|
|
683
|
-
loading.value = false;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
async function regeneratePlaceholderImages(prompt1, prompt2, model = "flux-kontext-pro", serviceUrl = DEFAULT_SERVICE_URL) {
|
|
687
|
-
loading.value = true;
|
|
688
|
-
error.value = null;
|
|
689
|
-
try {
|
|
690
|
-
const [image1, image2] = await Promise.all([
|
|
691
|
-
generateSingleImage(prompt1, model, serviceUrl),
|
|
692
|
-
generateSingleImage(prompt2, model, serviceUrl)
|
|
693
|
-
]);
|
|
694
|
-
if (image1 && image2) {
|
|
695
|
-
return { image1, image2 };
|
|
696
|
-
}
|
|
697
|
-
error.value = "Failed to generate images";
|
|
698
|
-
return null;
|
|
699
|
-
} catch (e) {
|
|
700
|
-
error.value = e.message || "Failed to regenerate images";
|
|
701
|
-
return null;
|
|
702
|
-
} finally {
|
|
703
|
-
loading.value = false;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
function getFileUrl(fileId) {
|
|
707
|
-
if (!fileId)
|
|
708
|
-
return "";
|
|
709
|
-
return `/assets/${fileId}?width=200&height=200&fit=cover`;
|
|
710
|
-
}
|
|
711
|
-
async function generateImagePrompts(style, description, serviceUrl = DEFAULT_SERVICE_URL) {
|
|
712
|
-
loading.value = true;
|
|
713
|
-
error.value = null;
|
|
714
|
-
try {
|
|
715
|
-
const miscServiceUrl = `${serviceUrl}/api/misc/prompts`;
|
|
716
|
-
const response = await fetch(miscServiceUrl, {
|
|
717
|
-
method: "POST",
|
|
718
|
-
headers: {
|
|
719
|
-
"Content-Type": "application/json"
|
|
720
|
-
},
|
|
721
|
-
body: JSON.stringify({
|
|
722
|
-
style,
|
|
723
|
-
description,
|
|
724
|
-
model: "gpt-4o-mini"
|
|
725
|
-
})
|
|
726
|
-
});
|
|
727
|
-
if (!response.ok) {
|
|
728
|
-
const errorData = await response.json().catch(() => ({ detail: response.statusText }));
|
|
729
|
-
throw new Error(errorData.detail || `HTTP ${response.status}`);
|
|
730
|
-
}
|
|
731
|
-
return await response.json();
|
|
732
|
-
} catch (e) {
|
|
733
|
-
error.value = e.message || "Failed to generate image prompts";
|
|
734
|
-
return null;
|
|
735
|
-
} finally {
|
|
736
|
-
loading.value = false;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
async function getImageVariants(lessonId) {
|
|
740
|
-
loading.value = true;
|
|
741
|
-
error.value = null;
|
|
742
|
-
try {
|
|
743
|
-
const response = await api.get("/items/ImageVariants", {
|
|
744
|
-
params: {
|
|
745
|
-
filter: { lesson: { _eq: lessonId } },
|
|
746
|
-
fields: ["id", "title", "status", "lesson", "images.id", "images.image", "images.prompt", "images.status"]
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
return response.data.data || [];
|
|
750
|
-
} catch (e) {
|
|
751
|
-
error.value = e.message || "Failed to fetch image variants";
|
|
752
|
-
return [];
|
|
753
|
-
} finally {
|
|
754
|
-
loading.value = false;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
async function saveGeneratedImages(lessonId, items) {
|
|
758
|
-
loading.value = true;
|
|
759
|
-
error.value = null;
|
|
760
|
-
try {
|
|
761
|
-
for (const item of items) {
|
|
762
|
-
const variantResponse = await api.post("/items/ImageVariants", {
|
|
763
|
-
title: item.placeholder,
|
|
764
|
-
lesson: lessonId,
|
|
765
|
-
status: "found"
|
|
766
|
-
});
|
|
767
|
-
const variantId = variantResponse.data.data.id;
|
|
768
|
-
const contentImages = [];
|
|
769
|
-
if (item.image1) {
|
|
770
|
-
contentImages.push({
|
|
771
|
-
image: item.image1,
|
|
772
|
-
prompt: item.prompt1,
|
|
773
|
-
variants: variantId,
|
|
774
|
-
status: "generated"
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
if (item.image2) {
|
|
778
|
-
contentImages.push({
|
|
779
|
-
image: item.image2,
|
|
780
|
-
prompt: item.prompt2,
|
|
781
|
-
variants: variantId,
|
|
782
|
-
status: "generated"
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
|
-
if (contentImages.length > 0) {
|
|
786
|
-
await api.post("/items/ContentImage", contentImages);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
return true;
|
|
790
|
-
} catch (e) {
|
|
791
|
-
error.value = e.message || "Failed to save generated images";
|
|
792
|
-
return false;
|
|
793
|
-
} finally {
|
|
794
|
-
loading.value = false;
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
async function deleteImageVariant(lessonId, placeholder) {
|
|
798
|
-
loading.value = true;
|
|
799
|
-
error.value = null;
|
|
800
|
-
try {
|
|
801
|
-
const response = await api.get("/items/ImageVariants", {
|
|
802
|
-
params: {
|
|
803
|
-
filter: {
|
|
804
|
-
lesson: { _eq: lessonId },
|
|
805
|
-
title: { _eq: placeholder }
|
|
806
|
-
},
|
|
807
|
-
fields: ["id", "images.id", "images.image"]
|
|
808
|
-
}
|
|
809
|
-
});
|
|
810
|
-
const variants = response.data.data || [];
|
|
811
|
-
for (const variant of variants) {
|
|
812
|
-
const fileIdsToDelete = [];
|
|
813
|
-
if (variant.images && variant.images.length > 0) {
|
|
814
|
-
for (const img of variant.images) {
|
|
815
|
-
if (img.image) {
|
|
816
|
-
fileIdsToDelete.push(img.image);
|
|
817
|
-
}
|
|
818
|
-
await api.delete(`/items/ContentImage/${img.id}`);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
await api.delete(`/items/ImageVariants/${variant.id}`);
|
|
822
|
-
for (const fileId of fileIdsToDelete) {
|
|
823
|
-
try {
|
|
824
|
-
await api.delete(`/files/${fileId}`);
|
|
825
|
-
} catch (fileError) {
|
|
826
|
-
console.warn(`Failed to delete file ${fileId}:`, fileError.message);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
return true;
|
|
831
|
-
} catch (e) {
|
|
832
|
-
error.value = e.message || "Failed to delete image variant";
|
|
833
|
-
return false;
|
|
834
|
-
} finally {
|
|
835
|
-
loading.value = false;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
function extractPlaceholders(content) {
|
|
839
|
-
const regex = /\[([^\]]+)\]/g;
|
|
840
|
-
const matches = [];
|
|
841
|
-
let match;
|
|
842
|
-
while ((match = regex.exec(content)) !== null) {
|
|
843
|
-
matches.push(match[1]);
|
|
844
|
-
}
|
|
845
|
-
return matches;
|
|
846
|
-
}
|
|
847
|
-
function updateContentWithImages(content, selections) {
|
|
848
|
-
let updated = content;
|
|
849
|
-
for (const { caption, fileId } of selections) {
|
|
850
|
-
const placeholder = `[${caption}]`;
|
|
851
|
-
const html = `<img src="/assets/${fileId}" alt="${caption}" />`;
|
|
852
|
-
updated = updated.replace(placeholder, html);
|
|
853
|
-
}
|
|
854
|
-
return updated;
|
|
855
|
-
}
|
|
856
|
-
async function updateLessonContent(lessonId, content) {
|
|
857
|
-
loading.value = true;
|
|
858
|
-
error.value = null;
|
|
859
|
-
try {
|
|
860
|
-
await api.patch(`/items/SM_Lessons/${lessonId}`, {
|
|
861
|
-
description: content
|
|
862
|
-
});
|
|
863
|
-
return true;
|
|
864
|
-
} catch (e) {
|
|
865
|
-
error.value = e.message || "Failed to update lesson content";
|
|
866
|
-
return false;
|
|
867
|
-
} finally {
|
|
868
|
-
loading.value = false;
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
async function fetchLessonContent(lessonId) {
|
|
872
|
-
try {
|
|
873
|
-
const response = await api.get(`/items/SM_Lessons/${lessonId}`, {
|
|
874
|
-
params: {
|
|
875
|
-
fields: ["description"]
|
|
876
|
-
}
|
|
877
|
-
});
|
|
878
|
-
return response.data.data?.description || response.data.data?.Description || null;
|
|
879
|
-
} catch (e) {
|
|
880
|
-
console.error("Failed to fetch lesson content:", e);
|
|
881
|
-
return null;
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
return {
|
|
885
|
-
loading,
|
|
886
|
-
error,
|
|
887
|
-
fetchServiceUrl,
|
|
888
|
-
updateServiceUrl,
|
|
889
|
-
checkIsAdmin,
|
|
890
|
-
fetchStyles,
|
|
891
|
-
updateStylePrompt,
|
|
892
|
-
regenerateStyleExamples,
|
|
893
|
-
regeneratePlaceholderImages,
|
|
894
|
-
getFileUrl,
|
|
895
|
-
generateImagePrompts,
|
|
896
|
-
getImageVariants,
|
|
897
|
-
saveGeneratedImages,
|
|
898
|
-
deleteImageVariant,
|
|
899
|
-
extractPlaceholders,
|
|
900
|
-
generateSingleImage,
|
|
901
|
-
updateContentWithImages,
|
|
902
|
-
updateLessonContent,
|
|
903
|
-
fetchLessonContent
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
const _hoisted_1 = { class: "image-style-widget" };
|
|
908
|
-
const _hoisted_2 = {
|
|
909
|
-
key: 0,
|
|
910
|
-
class: "widget__processing"
|
|
911
|
-
};
|
|
912
|
-
const _hoisted_3 = { class: "widget__progress" };
|
|
913
|
-
const _hoisted_4 = { class: "widget__progress-bar" };
|
|
914
|
-
const _hoisted_5 = { class: "widget__loading" };
|
|
915
|
-
const _hoisted_6 = { class: "widget__error" };
|
|
916
|
-
const _hoisted_7 = { class: "widget__header" };
|
|
917
|
-
const _hoisted_8 = { class: "widget__header-row" };
|
|
918
|
-
const _hoisted_9 = { class: "widget__header-text" };
|
|
919
|
-
const _hoisted_10 = { class: "widget__title" };
|
|
920
|
-
const _hoisted_11 = { class: "widget__header-controls" };
|
|
921
|
-
const _hoisted_12 = { class: "widget__url-input" };
|
|
922
|
-
const _hoisted_13 = ["value", "disabled"];
|
|
923
|
-
const _hoisted_14 = {
|
|
924
|
-
key: 0,
|
|
925
|
-
class: "widget__url-saving"
|
|
926
|
-
};
|
|
927
|
-
const _hoisted_15 = { class: "widget__model-selector" };
|
|
928
|
-
const _hoisted_16 = { class: "widget__model-buttons" };
|
|
929
|
-
const _hoisted_17 = ["onClick"];
|
|
930
|
-
const _hoisted_18 = { class: "widget__list" };
|
|
931
|
-
const _hoisted_19 = {
|
|
932
|
-
key: 0,
|
|
933
|
-
class: "widget__empty"
|
|
934
|
-
};
|
|
935
|
-
const _hoisted_20 = { class: "widget__header" };
|
|
936
|
-
const _hoisted_21 = { class: "widget__header-row" };
|
|
937
|
-
const _hoisted_22 = { class: "widget__header-text" };
|
|
938
|
-
const _hoisted_23 = { class: "widget__title" };
|
|
939
|
-
const _hoisted_24 = { class: "widget__header-controls" };
|
|
940
|
-
const _hoisted_25 = { class: "widget__url-input" };
|
|
941
|
-
const _hoisted_26 = ["value", "disabled"];
|
|
942
|
-
const _hoisted_27 = {
|
|
943
|
-
key: 0,
|
|
944
|
-
class: "widget__url-saving"
|
|
945
|
-
};
|
|
946
|
-
const _hoisted_28 = { class: "widget__model-selector" };
|
|
947
|
-
const _hoisted_29 = { class: "widget__model-buttons" };
|
|
948
|
-
const _hoisted_30 = ["onClick"];
|
|
949
|
-
const _hoisted_31 = { class: "widget__list" };
|
|
950
|
-
const _hoisted_32 = {
|
|
951
|
-
key: 0,
|
|
952
|
-
class: "widget__empty"
|
|
953
|
-
};
|
|
954
|
-
const _hoisted_33 = {
|
|
955
|
-
key: 5,
|
|
956
|
-
class: "widget__footer"
|
|
957
|
-
};
|
|
958
|
-
const _hoisted_34 = ["disabled"];
|
|
959
|
-
var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
960
|
-
__name: "interface",
|
|
961
|
-
props: {
|
|
962
|
-
value: {},
|
|
963
|
-
mode: { default: "style-select" },
|
|
964
|
-
stylesCollection: { default: "ImageStyles" },
|
|
965
|
-
primaryKey: {},
|
|
966
|
-
collection: {}
|
|
967
|
-
},
|
|
968
|
-
emits: ["input"],
|
|
969
|
-
setup(__props, { emit: __emit }) {
|
|
970
|
-
const uid = Math.random().toString(36).substring(2, 9);
|
|
971
|
-
const props = __props;
|
|
972
|
-
const emit = __emit;
|
|
973
|
-
const injectedValuesRef = inject("values");
|
|
974
|
-
function getValues() {
|
|
975
|
-
if (!injectedValuesRef)
|
|
976
|
-
return {};
|
|
977
|
-
if (injectedValuesRef.value !== void 0) {
|
|
978
|
-
return injectedValuesRef.value;
|
|
979
|
-
}
|
|
980
|
-
return injectedValuesRef;
|
|
981
|
-
}
|
|
982
|
-
const lessonId = computed(() => {
|
|
983
|
-
const vals = getValues();
|
|
984
|
-
return props.primaryKey || vals?.id || null;
|
|
985
|
-
});
|
|
986
|
-
const lessonContent = computed(() => {
|
|
987
|
-
const vals = getValues();
|
|
988
|
-
const desc = vals?.description || vals?.Description || "";
|
|
989
|
-
return desc;
|
|
990
|
-
});
|
|
991
|
-
const {
|
|
992
|
-
loading,
|
|
993
|
-
error,
|
|
994
|
-
fetchServiceUrl,
|
|
995
|
-
updateServiceUrl,
|
|
996
|
-
checkIsAdmin,
|
|
997
|
-
fetchStyles,
|
|
998
|
-
updateStylePrompt,
|
|
999
|
-
regenerateStyleExamples,
|
|
1000
|
-
regeneratePlaceholderImages,
|
|
1001
|
-
getFileUrl,
|
|
1002
|
-
extractPlaceholders,
|
|
1003
|
-
generateImagePrompts,
|
|
1004
|
-
generateSingleImage,
|
|
1005
|
-
updateContentWithImages,
|
|
1006
|
-
updateLessonContent,
|
|
1007
|
-
fetchLessonContent,
|
|
1008
|
-
getImageVariants,
|
|
1009
|
-
saveGeneratedImages,
|
|
1010
|
-
deleteImageVariant
|
|
1011
|
-
} = useDirectusApi();
|
|
1012
|
-
const currentMode = ref(props.mode);
|
|
1013
|
-
const styles = ref([]);
|
|
1014
|
-
const selectedStyleId = ref(null);
|
|
1015
|
-
const regeneratingStyleId = ref(null);
|
|
1016
|
-
const selectedModel = ref("flux-kontext-pro");
|
|
1017
|
-
const serviceUrl = ref("http://localhost:8004");
|
|
1018
|
-
const isAdmin = ref(false);
|
|
1019
|
-
const savingUrl = ref(false);
|
|
1020
|
-
const imageItems = ref([]);
|
|
1021
|
-
const regeneratingPlaceholder = ref(null);
|
|
1022
|
-
const processing = ref(false);
|
|
1023
|
-
const processingMessage = ref("");
|
|
1024
|
-
const progressPercent = ref(0);
|
|
1025
|
-
const editModalOpen = ref(false);
|
|
1026
|
-
const editModalTitle = ref("");
|
|
1027
|
-
const editModalPrompt = ref("");
|
|
1028
|
-
const editingStyle = ref(null);
|
|
1029
|
-
const editingImageItem = ref(null);
|
|
1030
|
-
const savingPrompt = ref(false);
|
|
1031
|
-
computed(() => {
|
|
1032
|
-
if (currentMode.value === "style-select") {
|
|
1033
|
-
return styles.value.length > 0;
|
|
1034
|
-
}
|
|
1035
|
-
return imageItems.value.length > 0;
|
|
1036
|
-
});
|
|
1037
|
-
const isValid = computed(() => {
|
|
1038
|
-
if (currentMode.value === "style-select") {
|
|
1039
|
-
return selectedStyleId.value !== null;
|
|
1040
|
-
}
|
|
1041
|
-
return imageItems.value.every((item) => item.selected);
|
|
1042
|
-
});
|
|
1043
|
-
const confirmButtonText = computed(() => {
|
|
1044
|
-
if (currentMode.value === "style-select") {
|
|
1045
|
-
return "Generate Images";
|
|
1046
|
-
}
|
|
1047
|
-
return "Confirm Selections";
|
|
1048
|
-
});
|
|
1049
|
-
function initFromValue() {
|
|
1050
|
-
if (!props.value)
|
|
1051
|
-
return;
|
|
1052
|
-
if (typeof props.value === "object") {
|
|
1053
|
-
if (props.value.selectedStyleId) {
|
|
1054
|
-
selectedStyleId.value = props.value.selectedStyleId;
|
|
1055
|
-
}
|
|
1056
|
-
if (props.value.items && props.value.items.length > 0) {
|
|
1057
|
-
imageItems.value = props.value.items;
|
|
1058
|
-
currentMode.value = "image-review";
|
|
1059
|
-
}
|
|
1060
|
-
if (props.value.currentMode) {
|
|
1061
|
-
currentMode.value = props.value.currentMode;
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
async function loadStyles() {
|
|
1066
|
-
styles.value = await fetchStyles(props.stylesCollection);
|
|
1067
|
-
if (!selectedStyleId.value && styles.value.length > 0) {
|
|
1068
|
-
selectedStyleId.value = styles.value[0].id;
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
function goBackToStyleSelect() {
|
|
1072
|
-
currentMode.value = "style-select";
|
|
1073
|
-
emitValue();
|
|
1074
|
-
}
|
|
1075
|
-
async function viewGeneratedImages() {
|
|
1076
|
-
if (!lessonId.value)
|
|
1077
|
-
return;
|
|
1078
|
-
processing.value = true;
|
|
1079
|
-
processingMessage.value = "Loading existing images...";
|
|
1080
|
-
progressPercent.value = 10;
|
|
1081
|
-
try {
|
|
1082
|
-
const variants = await getImageVariants(String(lessonId.value));
|
|
1083
|
-
progressPercent.value = 50;
|
|
1084
|
-
if (!variants || variants.length === 0) {
|
|
1085
|
-
error.value = "No generated images found for this lesson. Generate images first.";
|
|
1086
|
-
processing.value = false;
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
const existingItems = variants.map((variant) => {
|
|
1090
|
-
const images = variant.images || [];
|
|
1091
|
-
const img1 = images[0];
|
|
1092
|
-
const img2 = images[1];
|
|
1093
|
-
return {
|
|
1094
|
-
placeholder: variant.title || "Untitled",
|
|
1095
|
-
prompt1: img1?.prompt || "",
|
|
1096
|
-
prompt2: img2?.prompt || img1?.prompt || "",
|
|
1097
|
-
image1: img1?.image || null,
|
|
1098
|
-
image2: img2?.image || null,
|
|
1099
|
-
selected: img1?.image ? 1 : img2?.image ? 2 : null
|
|
1100
|
-
};
|
|
1101
|
-
});
|
|
1102
|
-
progressPercent.value = 90;
|
|
1103
|
-
imageItems.value = existingItems;
|
|
1104
|
-
currentMode.value = "image-review";
|
|
1105
|
-
progressPercent.value = 100;
|
|
1106
|
-
emitValue();
|
|
1107
|
-
} catch (e) {
|
|
1108
|
-
error.value = e.message || "Failed to load existing images";
|
|
1109
|
-
} finally {
|
|
1110
|
-
processing.value = false;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
function handleStyleSelect(styleId) {
|
|
1114
|
-
selectedStyleId.value = styleId;
|
|
1115
|
-
emitValue();
|
|
1116
|
-
}
|
|
1117
|
-
async function handleStyleRegenerate(style) {
|
|
1118
|
-
regeneratingStyleId.value = style.id;
|
|
1119
|
-
const result = await regenerateStyleExamples(style.id, style.prompt, props.stylesCollection, selectedModel.value, serviceUrl.value);
|
|
1120
|
-
if (result) {
|
|
1121
|
-
const index = styles.value.findIndex((s) => s.id === style.id);
|
|
1122
|
-
if (index >= 0) {
|
|
1123
|
-
styles.value[index] = {
|
|
1124
|
-
...styles.value[index],
|
|
1125
|
-
example_1: result.example_1,
|
|
1126
|
-
example_2: result.example_2
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
regeneratingStyleId.value = null;
|
|
1131
|
-
}
|
|
1132
|
-
function handleImageSelect(placeholder, imageNum) {
|
|
1133
|
-
const item = imageItems.value.find((i) => i.placeholder === placeholder);
|
|
1134
|
-
if (item) {
|
|
1135
|
-
item.selected = imageNum;
|
|
1136
|
-
emitValue();
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
async function handleImageRegenerate(item) {
|
|
1140
|
-
regeneratingPlaceholder.value = item.placeholder;
|
|
1141
|
-
const result = await regeneratePlaceholderImages(item.prompt1, item.prompt2, selectedModel.value, serviceUrl.value);
|
|
1142
|
-
if (result) {
|
|
1143
|
-
const index = imageItems.value.findIndex((i) => i.placeholder === item.placeholder);
|
|
1144
|
-
if (index >= 0) {
|
|
1145
|
-
imageItems.value[index] = {
|
|
1146
|
-
...imageItems.value[index],
|
|
1147
|
-
image1: result.image1,
|
|
1148
|
-
image2: result.image2,
|
|
1149
|
-
selected: 1
|
|
1150
|
-
// Reset selection to first image
|
|
1151
|
-
};
|
|
1152
|
-
emitValue();
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
regeneratingPlaceholder.value = null;
|
|
1156
|
-
}
|
|
1157
|
-
async function handleImageDelete(item) {
|
|
1158
|
-
if (!lessonId.value)
|
|
1159
|
-
return;
|
|
1160
|
-
if (!confirm(`Delete "${item.placeholder}" and its images? This cannot be undone.`)) {
|
|
1161
|
-
return;
|
|
1162
|
-
}
|
|
1163
|
-
regeneratingPlaceholder.value = item.placeholder;
|
|
1164
|
-
const success = await deleteImageVariant(String(lessonId.value), item.placeholder);
|
|
1165
|
-
if (success) {
|
|
1166
|
-
const index = imageItems.value.findIndex((i) => i.placeholder === item.placeholder);
|
|
1167
|
-
if (index >= 0) {
|
|
1168
|
-
imageItems.value.splice(index, 1);
|
|
1169
|
-
emitValue();
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
regeneratingPlaceholder.value = null;
|
|
1173
|
-
}
|
|
1174
|
-
function openEditModal(style) {
|
|
1175
|
-
editingStyle.value = style;
|
|
1176
|
-
editingImageItem.value = null;
|
|
1177
|
-
editModalTitle.value = `Style: ${style.name}`;
|
|
1178
|
-
editModalPrompt.value = style.prompt;
|
|
1179
|
-
editModalOpen.value = true;
|
|
1180
|
-
}
|
|
1181
|
-
function openImageEditModal(item) {
|
|
1182
|
-
editingStyle.value = null;
|
|
1183
|
-
editingImageItem.value = item;
|
|
1184
|
-
editModalTitle.value = `Image: ${item.placeholder}`;
|
|
1185
|
-
editModalPrompt.value = item.prompt1;
|
|
1186
|
-
editModalOpen.value = true;
|
|
1187
|
-
}
|
|
1188
|
-
function closeEditModal() {
|
|
1189
|
-
editModalOpen.value = false;
|
|
1190
|
-
editingStyle.value = null;
|
|
1191
|
-
editingImageItem.value = null;
|
|
1192
|
-
}
|
|
1193
|
-
async function handleSavePrompt(newPrompt) {
|
|
1194
|
-
savingPrompt.value = true;
|
|
1195
|
-
if (editingStyle.value) {
|
|
1196
|
-
const success = await updateStylePrompt(
|
|
1197
|
-
props.stylesCollection,
|
|
1198
|
-
editingStyle.value.id,
|
|
1199
|
-
newPrompt
|
|
1200
|
-
);
|
|
1201
|
-
if (success) {
|
|
1202
|
-
const index = styles.value.findIndex((s) => s.id === editingStyle.value.id);
|
|
1203
|
-
if (index >= 0) {
|
|
1204
|
-
styles.value[index] = {
|
|
1205
|
-
...styles.value[index],
|
|
1206
|
-
prompt: newPrompt
|
|
1207
|
-
};
|
|
1208
|
-
}
|
|
1209
|
-
closeEditModal();
|
|
1210
|
-
}
|
|
1211
|
-
} else if (editingImageItem.value) {
|
|
1212
|
-
const index = imageItems.value.findIndex(
|
|
1213
|
-
(i) => i.placeholder === editingImageItem.value.placeholder
|
|
1214
|
-
);
|
|
1215
|
-
if (index >= 0) {
|
|
1216
|
-
imageItems.value[index] = {
|
|
1217
|
-
...imageItems.value[index],
|
|
1218
|
-
prompt1: newPrompt
|
|
1219
|
-
};
|
|
1220
|
-
emitValue();
|
|
1221
|
-
closeEditModal();
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
savingPrompt.value = false;
|
|
1225
|
-
}
|
|
1226
|
-
function emitValue() {
|
|
1227
|
-
emit("input", {
|
|
1228
|
-
currentMode: currentMode.value,
|
|
1229
|
-
selectedStyleId: selectedStyleId.value,
|
|
1230
|
-
selectedStyle: styles.value.find((s) => s.id === selectedStyleId.value) || null,
|
|
1231
|
-
items: imageItems.value
|
|
1232
|
-
});
|
|
1233
|
-
}
|
|
1234
|
-
async function handleConfirm() {
|
|
1235
|
-
if (!isValid.value)
|
|
1236
|
-
return;
|
|
1237
|
-
if (currentMode.value === "style-select") {
|
|
1238
|
-
await runImageGenerationFlow();
|
|
1239
|
-
} else {
|
|
1240
|
-
await applySelectedImages();
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
async function runImageGenerationFlow() {
|
|
1244
|
-
const selectedStyle = styles.value.find((s) => s.id === selectedStyleId.value);
|
|
1245
|
-
if (!selectedStyle) {
|
|
1246
|
-
error.value = "No style selected. Please select an image style first.";
|
|
1247
|
-
return;
|
|
1248
|
-
}
|
|
1249
|
-
if (!lessonContent.value) {
|
|
1250
|
-
error.value = "No content available in the Description field. Add text with [image placeholders] first.";
|
|
1251
|
-
return;
|
|
1252
|
-
}
|
|
1253
|
-
processing.value = true;
|
|
1254
|
-
progressPercent.value = 0;
|
|
1255
|
-
try {
|
|
1256
|
-
processingMessage.value = "Generating image prompts with AI...";
|
|
1257
|
-
progressPercent.value = 10;
|
|
1258
|
-
const promptResponse = await generateImagePrompts(selectedStyle.prompt, lessonContent.value, serviceUrl.value);
|
|
1259
|
-
if (!promptResponse || !promptResponse.images || promptResponse.images.length === 0) {
|
|
1260
|
-
error.value = "No [image placeholders] found in content. Add placeholders like [Rabbit image] to generate images.";
|
|
1261
|
-
processing.value = false;
|
|
1262
|
-
return;
|
|
1263
|
-
}
|
|
1264
|
-
progressPercent.value = 30;
|
|
1265
|
-
processingMessage.value = `Generating images for ${promptResponse.images.length} placeholder(s)...`;
|
|
1266
|
-
const newImageItems = [];
|
|
1267
|
-
const progressPerItem = 60 / promptResponse.images.length;
|
|
1268
|
-
for (let i = 0; i < promptResponse.images.length; i++) {
|
|
1269
|
-
const imagePrompt = promptResponse.images[i];
|
|
1270
|
-
processingMessage.value = `Generating images for "${imagePrompt.placeholder}"... (${i + 1}/${promptResponse.images.length})`;
|
|
1271
|
-
const prompt1 = imagePrompt.prompts[0] || `${selectedStyle.prompt}. Subject: ${imagePrompt.placeholder}`;
|
|
1272
|
-
const prompt2 = imagePrompt.prompts[1] || prompt1;
|
|
1273
|
-
const [img1Result, img2Result] = await Promise.all([
|
|
1274
|
-
generateSingleImage(prompt1, selectedModel.value, serviceUrl.value),
|
|
1275
|
-
generateSingleImage(prompt2, selectedModel.value, serviceUrl.value)
|
|
1276
|
-
]);
|
|
1277
|
-
newImageItems.push({
|
|
1278
|
-
placeholder: imagePrompt.placeholder,
|
|
1279
|
-
prompt1,
|
|
1280
|
-
prompt2,
|
|
1281
|
-
image1: img1Result,
|
|
1282
|
-
image2: img2Result,
|
|
1283
|
-
selected: img1Result ? 1 : img2Result ? 2 : null
|
|
1284
|
-
// Default select first available image
|
|
1285
|
-
});
|
|
1286
|
-
progressPercent.value = 30 + (i + 1) * progressPerItem;
|
|
1287
|
-
}
|
|
1288
|
-
processingMessage.value = "Saving generated images...";
|
|
1289
|
-
progressPercent.value = 90;
|
|
1290
|
-
if (lessonId.value) {
|
|
1291
|
-
await saveGeneratedImages(String(lessonId.value), newImageItems);
|
|
1292
|
-
}
|
|
1293
|
-
processingMessage.value = "Preparing image review...";
|
|
1294
|
-
progressPercent.value = 95;
|
|
1295
|
-
imageItems.value = newImageItems;
|
|
1296
|
-
currentMode.value = "image-review";
|
|
1297
|
-
progressPercent.value = 100;
|
|
1298
|
-
emitValue();
|
|
1299
|
-
} catch (e) {
|
|
1300
|
-
error.value = e.message || "Image generation failed";
|
|
1301
|
-
} finally {
|
|
1302
|
-
processing.value = false;
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
async function applySelectedImages() {
|
|
1306
|
-
if (!lessonId.value) {
|
|
1307
|
-
error.value = "No lesson ID available";
|
|
1308
|
-
return;
|
|
1309
|
-
}
|
|
1310
|
-
const selections = imageItems.value.filter((item) => item.selected && (item.selected === 1 ? item.image1 : item.image2)).map((item) => ({
|
|
1311
|
-
caption: item.placeholder,
|
|
1312
|
-
fileId: item.selected === 1 ? item.image1 : item.image2
|
|
1313
|
-
}));
|
|
1314
|
-
if (selections.length === 0) {
|
|
1315
|
-
error.value = "No images selected. Please select at least one image.";
|
|
1316
|
-
return;
|
|
1317
|
-
}
|
|
1318
|
-
processing.value = true;
|
|
1319
|
-
processingMessage.value = "Fetching current content...";
|
|
1320
|
-
progressPercent.value = 20;
|
|
1321
|
-
try {
|
|
1322
|
-
const currentContent = await fetchLessonContent(String(lessonId.value));
|
|
1323
|
-
if (!currentContent) {
|
|
1324
|
-
error.value = "Could not fetch lesson content. Make sure Description field has content.";
|
|
1325
|
-
processing.value = false;
|
|
1326
|
-
return;
|
|
1327
|
-
}
|
|
1328
|
-
progressPercent.value = 40;
|
|
1329
|
-
processingMessage.value = "Applying selected images to content...";
|
|
1330
|
-
const updatedContent = updateContentWithImages(currentContent, selections);
|
|
1331
|
-
progressPercent.value = 80;
|
|
1332
|
-
const success = await updateLessonContent(String(lessonId.value), updatedContent);
|
|
1333
|
-
if (success) {
|
|
1334
|
-
progressPercent.value = 100;
|
|
1335
|
-
processingMessage.value = "Images applied successfully! Refreshing...";
|
|
1336
|
-
setTimeout(() => {
|
|
1337
|
-
window.location.reload();
|
|
1338
|
-
}, 1e3);
|
|
1339
|
-
} else {
|
|
1340
|
-
error.value = "Failed to update lesson content";
|
|
1341
|
-
}
|
|
1342
|
-
} catch (e) {
|
|
1343
|
-
error.value = e.message || "Failed to apply images";
|
|
1344
|
-
} finally {
|
|
1345
|
-
processing.value = false;
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
async function loadSettings() {
|
|
1349
|
-
const [url, admin] = await Promise.all([
|
|
1350
|
-
fetchServiceUrl(),
|
|
1351
|
-
checkIsAdmin()
|
|
1352
|
-
]);
|
|
1353
|
-
serviceUrl.value = url;
|
|
1354
|
-
isAdmin.value = admin;
|
|
1355
|
-
}
|
|
1356
|
-
async function handleServiceUrlChange(newUrl) {
|
|
1357
|
-
if (newUrl === serviceUrl.value)
|
|
1358
|
-
return;
|
|
1359
|
-
if (!isAdmin.value) {
|
|
1360
|
-
const confirmed = confirm(
|
|
1361
|
-
"Warning: Changing the service URL will affect all users.\n\nAre you sure you want to continue?"
|
|
1362
|
-
);
|
|
1363
|
-
if (!confirmed)
|
|
1364
|
-
return;
|
|
1365
|
-
}
|
|
1366
|
-
savingUrl.value = true;
|
|
1367
|
-
const success = await updateServiceUrl(newUrl);
|
|
1368
|
-
if (success) {
|
|
1369
|
-
serviceUrl.value = newUrl;
|
|
1370
|
-
}
|
|
1371
|
-
savingUrl.value = false;
|
|
1372
|
-
}
|
|
1373
|
-
function handleUrlBlur(event) {
|
|
1374
|
-
const input = event.target;
|
|
1375
|
-
handleServiceUrlChange(input.value.trim());
|
|
1376
|
-
}
|
|
1377
|
-
function handleUrlEnter(event) {
|
|
1378
|
-
const input = event.target;
|
|
1379
|
-
input.blur();
|
|
1380
|
-
}
|
|
1381
|
-
onMounted(() => {
|
|
1382
|
-
initFromValue();
|
|
1383
|
-
loadStyles();
|
|
1384
|
-
loadSettings();
|
|
1385
|
-
});
|
|
1386
|
-
watch(() => props.mode, (newMode) => {
|
|
1387
|
-
if (newMode) {
|
|
1388
|
-
currentMode.value = newMode;
|
|
1389
|
-
}
|
|
1390
|
-
});
|
|
1391
|
-
watch(() => props.value, () => {
|
|
1392
|
-
initFromValue();
|
|
1393
|
-
}, { deep: true });
|
|
1394
|
-
return (_ctx, _cache) => {
|
|
1395
|
-
const _component_v_icon = resolveComponent("v-icon");
|
|
1396
|
-
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
1397
|
-
createCommentVNode(" Processing State "),
|
|
1398
|
-
processing.value ? (openBlock(), createElementBlock("div", _hoisted_2, [
|
|
1399
|
-
createElementVNode("div", _hoisted_3, [
|
|
1400
|
-
createVNode(_component_v_icon, {
|
|
1401
|
-
name: "refresh",
|
|
1402
|
-
class: "spinning"
|
|
1403
|
-
}),
|
|
1404
|
-
createElementVNode(
|
|
1405
|
-
"span",
|
|
1406
|
-
null,
|
|
1407
|
-
toDisplayString(processingMessage.value),
|
|
1408
|
-
1
|
|
1409
|
-
/* TEXT */
|
|
1410
|
-
)
|
|
1411
|
-
]),
|
|
1412
|
-
createElementVNode("div", _hoisted_4, [
|
|
1413
|
-
createElementVNode(
|
|
1414
|
-
"div",
|
|
1415
|
-
{
|
|
1416
|
-
class: "widget__progress-fill",
|
|
1417
|
-
style: normalizeStyle({ width: progressPercent.value + "%" })
|
|
1418
|
-
},
|
|
1419
|
-
null,
|
|
1420
|
-
4
|
|
1421
|
-
/* STYLE */
|
|
1422
|
-
)
|
|
1423
|
-
])
|
|
1424
|
-
])) : unref(loading) && !styles.value.length ? (openBlock(), createElementBlock(
|
|
1425
|
-
Fragment,
|
|
1426
|
-
{ key: 1 },
|
|
1427
|
-
[
|
|
1428
|
-
createCommentVNode(" Loading State "),
|
|
1429
|
-
createElementVNode("div", _hoisted_5, [
|
|
1430
|
-
createVNode(_component_v_icon, { name: "refresh" }),
|
|
1431
|
-
_cache[0] || (_cache[0] = createElementVNode(
|
|
1432
|
-
"span",
|
|
1433
|
-
null,
|
|
1434
|
-
"Loading styles...",
|
|
1435
|
-
-1
|
|
1436
|
-
/* CACHED */
|
|
1437
|
-
))
|
|
1438
|
-
])
|
|
1439
|
-
],
|
|
1440
|
-
2112
|
|
1441
|
-
/* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */
|
|
1442
|
-
)) : unref(error) ? (openBlock(), createElementBlock(
|
|
1443
|
-
Fragment,
|
|
1444
|
-
{ key: 2 },
|
|
1445
|
-
[
|
|
1446
|
-
createCommentVNode(" Error State "),
|
|
1447
|
-
createElementVNode("div", _hoisted_6, [
|
|
1448
|
-
createVNode(_component_v_icon, { name: "error" }),
|
|
1449
|
-
createElementVNode(
|
|
1450
|
-
"span",
|
|
1451
|
-
null,
|
|
1452
|
-
toDisplayString(unref(error)),
|
|
1453
|
-
1
|
|
1454
|
-
/* TEXT */
|
|
1455
|
-
),
|
|
1456
|
-
createElementVNode("button", {
|
|
1457
|
-
class: "widget__retry",
|
|
1458
|
-
onClick: loadStyles
|
|
1459
|
-
}, "Retry")
|
|
1460
|
-
])
|
|
1461
|
-
],
|
|
1462
|
-
2112
|
|
1463
|
-
/* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */
|
|
1464
|
-
)) : currentMode.value === "style-select" ? (openBlock(), createElementBlock(
|
|
1465
|
-
Fragment,
|
|
1466
|
-
{ key: 3 },
|
|
1467
|
-
[
|
|
1468
|
-
createCommentVNode(" Mode 1: Style Selection "),
|
|
1469
|
-
createElementVNode("div", _hoisted_7, [
|
|
1470
|
-
createElementVNode("div", _hoisted_8, [
|
|
1471
|
-
createElementVNode("div", _hoisted_9, [
|
|
1472
|
-
createElementVNode("h2", _hoisted_10, [
|
|
1473
|
-
createVNode(_component_v_icon, { name: "palette" }),
|
|
1474
|
-
_cache[1] || (_cache[1] = createTextVNode(
|
|
1475
|
-
" Select Image Style ",
|
|
1476
|
-
-1
|
|
1477
|
-
/* CACHED */
|
|
1478
|
-
))
|
|
1479
|
-
]),
|
|
1480
|
-
_cache[2] || (_cache[2] = createElementVNode(
|
|
1481
|
-
"p",
|
|
1482
|
-
{ class: "widget__subtitle" },
|
|
1483
|
-
" Choose a style for your generated images ",
|
|
1484
|
-
-1
|
|
1485
|
-
/* CACHED */
|
|
1486
|
-
))
|
|
1487
|
-
]),
|
|
1488
|
-
createElementVNode("div", _hoisted_11, [
|
|
1489
|
-
createElementVNode("div", _hoisted_12, [
|
|
1490
|
-
_cache[3] || (_cache[3] = createElementVNode(
|
|
1491
|
-
"label",
|
|
1492
|
-
{ class: "widget__url-label" },
|
|
1493
|
-
"Service URL:",
|
|
1494
|
-
-1
|
|
1495
|
-
/* CACHED */
|
|
1496
|
-
)),
|
|
1497
|
-
createElementVNode("input", {
|
|
1498
|
-
type: "text",
|
|
1499
|
-
class: "widget__url-field",
|
|
1500
|
-
value: serviceUrl.value,
|
|
1501
|
-
disabled: savingUrl.value,
|
|
1502
|
-
onBlur: handleUrlBlur,
|
|
1503
|
-
onKeydown: withKeys(handleUrlEnter, ["enter"])
|
|
1504
|
-
}, null, 40, _hoisted_13),
|
|
1505
|
-
savingUrl.value ? (openBlock(), createElementBlock("span", _hoisted_14, [
|
|
1506
|
-
createVNode(_component_v_icon, {
|
|
1507
|
-
name: "refresh",
|
|
1508
|
-
class: "spinning",
|
|
1509
|
-
small: ""
|
|
1510
|
-
})
|
|
1511
|
-
])) : createCommentVNode("v-if", true)
|
|
1512
|
-
]),
|
|
1513
|
-
createElementVNode("div", _hoisted_15, [
|
|
1514
|
-
_cache[4] || (_cache[4] = createElementVNode(
|
|
1515
|
-
"label",
|
|
1516
|
-
{ class: "widget__model-label" },
|
|
1517
|
-
"AI Model:",
|
|
1518
|
-
-1
|
|
1519
|
-
/* CACHED */
|
|
1520
|
-
)),
|
|
1521
|
-
createElementVNode("div", _hoisted_16, [
|
|
1522
|
-
(openBlock(true), createElementBlock(
|
|
1523
|
-
Fragment,
|
|
1524
|
-
null,
|
|
1525
|
-
renderList(unref(IMAGE_MODELS), (model) => {
|
|
1526
|
-
return openBlock(), createElementBlock("button", {
|
|
1527
|
-
key: model.id,
|
|
1528
|
-
class: normalizeClass(["widget__model-btn", { "widget__model-btn--active": selectedModel.value === model.id }]),
|
|
1529
|
-
onClick: ($event) => selectedModel.value = model.id
|
|
1530
|
-
}, toDisplayString(model.name), 11, _hoisted_17);
|
|
1531
|
-
}),
|
|
1532
|
-
128
|
|
1533
|
-
/* KEYED_FRAGMENT */
|
|
1534
|
-
))
|
|
1535
|
-
])
|
|
1536
|
-
])
|
|
1537
|
-
])
|
|
1538
|
-
])
|
|
1539
|
-
]),
|
|
1540
|
-
createElementVNode("div", _hoisted_18, [
|
|
1541
|
-
(openBlock(true), createElementBlock(
|
|
1542
|
-
Fragment,
|
|
1543
|
-
null,
|
|
1544
|
-
renderList(styles.value, (style) => {
|
|
1545
|
-
return openBlock(), createBlock(StyleCard, {
|
|
1546
|
-
key: style.id,
|
|
1547
|
-
style: normalizeStyle(style),
|
|
1548
|
-
"is-selected": selectedStyleId.value === style.id,
|
|
1549
|
-
loading: regeneratingStyleId.value === style.id,
|
|
1550
|
-
"radio-group-name": `style-select-${unref(uid)}`,
|
|
1551
|
-
"get-file-url": unref(getFileUrl),
|
|
1552
|
-
onSelect: handleStyleSelect,
|
|
1553
|
-
onEdit: ($event) => openEditModal(style),
|
|
1554
|
-
onRegenerate: handleStyleRegenerate
|
|
1555
|
-
}, null, 8, ["style", "is-selected", "loading", "radio-group-name", "get-file-url", "onEdit"]);
|
|
1556
|
-
}),
|
|
1557
|
-
128
|
|
1558
|
-
/* KEYED_FRAGMENT */
|
|
1559
|
-
))
|
|
1560
|
-
]),
|
|
1561
|
-
styles.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_19, [
|
|
1562
|
-
createVNode(_component_v_icon, { name: "palette" }),
|
|
1563
|
-
_cache[5] || (_cache[5] = createElementVNode(
|
|
1564
|
-
"p",
|
|
1565
|
-
null,
|
|
1566
|
-
"No styles available",
|
|
1567
|
-
-1
|
|
1568
|
-
/* CACHED */
|
|
1569
|
-
)),
|
|
1570
|
-
createElementVNode(
|
|
1571
|
-
"span",
|
|
1572
|
-
null,
|
|
1573
|
-
"Add styles to the " + toDisplayString(__props.stylesCollection) + " collection",
|
|
1574
|
-
1
|
|
1575
|
-
/* TEXT */
|
|
1576
|
-
)
|
|
1577
|
-
])) : createCommentVNode("v-if", true)
|
|
1578
|
-
],
|
|
1579
|
-
64
|
|
1580
|
-
/* STABLE_FRAGMENT */
|
|
1581
|
-
)) : currentMode.value === "image-review" ? (openBlock(), createElementBlock(
|
|
1582
|
-
Fragment,
|
|
1583
|
-
{ key: 4 },
|
|
1584
|
-
[
|
|
1585
|
-
createCommentVNode(" Mode 2: Image Review "),
|
|
1586
|
-
createElementVNode("div", _hoisted_20, [
|
|
1587
|
-
createElementVNode("div", _hoisted_21, [
|
|
1588
|
-
createElementVNode("div", _hoisted_22, [
|
|
1589
|
-
createElementVNode("h2", _hoisted_23, [
|
|
1590
|
-
createVNode(_component_v_icon, { name: "photo_library" }),
|
|
1591
|
-
_cache[6] || (_cache[6] = createTextVNode(
|
|
1592
|
-
" Review Generated Images ",
|
|
1593
|
-
-1
|
|
1594
|
-
/* CACHED */
|
|
1595
|
-
))
|
|
1596
|
-
]),
|
|
1597
|
-
_cache[7] || (_cache[7] = createElementVNode(
|
|
1598
|
-
"p",
|
|
1599
|
-
{ class: "widget__subtitle" },
|
|
1600
|
-
" Select the best image for each placeholder ",
|
|
1601
|
-
-1
|
|
1602
|
-
/* CACHED */
|
|
1603
|
-
))
|
|
1604
|
-
]),
|
|
1605
|
-
createElementVNode("div", _hoisted_24, [
|
|
1606
|
-
createElementVNode("div", _hoisted_25, [
|
|
1607
|
-
_cache[8] || (_cache[8] = createElementVNode(
|
|
1608
|
-
"label",
|
|
1609
|
-
{ class: "widget__url-label" },
|
|
1610
|
-
"Service URL:",
|
|
1611
|
-
-1
|
|
1612
|
-
/* CACHED */
|
|
1613
|
-
)),
|
|
1614
|
-
createElementVNode("input", {
|
|
1615
|
-
type: "text",
|
|
1616
|
-
class: "widget__url-field",
|
|
1617
|
-
value: serviceUrl.value,
|
|
1618
|
-
disabled: savingUrl.value,
|
|
1619
|
-
onBlur: handleUrlBlur,
|
|
1620
|
-
onKeydown: withKeys(handleUrlEnter, ["enter"])
|
|
1621
|
-
}, null, 40, _hoisted_26),
|
|
1622
|
-
savingUrl.value ? (openBlock(), createElementBlock("span", _hoisted_27, [
|
|
1623
|
-
createVNode(_component_v_icon, {
|
|
1624
|
-
name: "refresh",
|
|
1625
|
-
class: "spinning",
|
|
1626
|
-
small: ""
|
|
1627
|
-
})
|
|
1628
|
-
])) : createCommentVNode("v-if", true)
|
|
1629
|
-
]),
|
|
1630
|
-
createElementVNode("div", _hoisted_28, [
|
|
1631
|
-
_cache[9] || (_cache[9] = createElementVNode(
|
|
1632
|
-
"label",
|
|
1633
|
-
{ class: "widget__model-label" },
|
|
1634
|
-
"AI Model:",
|
|
1635
|
-
-1
|
|
1636
|
-
/* CACHED */
|
|
1637
|
-
)),
|
|
1638
|
-
createElementVNode("div", _hoisted_29, [
|
|
1639
|
-
(openBlock(true), createElementBlock(
|
|
1640
|
-
Fragment,
|
|
1641
|
-
null,
|
|
1642
|
-
renderList(unref(IMAGE_MODELS), (model) => {
|
|
1643
|
-
return openBlock(), createElementBlock("button", {
|
|
1644
|
-
key: model.id,
|
|
1645
|
-
class: normalizeClass(["widget__model-btn", { "widget__model-btn--active": selectedModel.value === model.id }]),
|
|
1646
|
-
onClick: ($event) => selectedModel.value = model.id
|
|
1647
|
-
}, toDisplayString(model.name), 11, _hoisted_30);
|
|
1648
|
-
}),
|
|
1649
|
-
128
|
|
1650
|
-
/* KEYED_FRAGMENT */
|
|
1651
|
-
))
|
|
1652
|
-
])
|
|
1653
|
-
])
|
|
1654
|
-
])
|
|
1655
|
-
])
|
|
1656
|
-
]),
|
|
1657
|
-
createElementVNode("div", _hoisted_31, [
|
|
1658
|
-
(openBlock(true), createElementBlock(
|
|
1659
|
-
Fragment,
|
|
1660
|
-
null,
|
|
1661
|
-
renderList(imageItems.value, (item) => {
|
|
1662
|
-
return openBlock(), createBlock(ImageCard, {
|
|
1663
|
-
key: item.placeholder,
|
|
1664
|
-
item,
|
|
1665
|
-
loading: regeneratingPlaceholder.value === item.placeholder,
|
|
1666
|
-
"get-file-url": unref(getFileUrl),
|
|
1667
|
-
onSelectImage: handleImageSelect,
|
|
1668
|
-
onEdit: ($event) => openImageEditModal(item),
|
|
1669
|
-
onRegenerate: handleImageRegenerate,
|
|
1670
|
-
onDelete: handleImageDelete
|
|
1671
|
-
}, null, 8, ["item", "loading", "get-file-url", "onEdit"]);
|
|
1672
|
-
}),
|
|
1673
|
-
128
|
|
1674
|
-
/* KEYED_FRAGMENT */
|
|
1675
|
-
))
|
|
1676
|
-
]),
|
|
1677
|
-
imageItems.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_32, [
|
|
1678
|
-
createVNode(_component_v_icon, { name: "photo_library" }),
|
|
1679
|
-
_cache[10] || (_cache[10] = createElementVNode(
|
|
1680
|
-
"p",
|
|
1681
|
-
null,
|
|
1682
|
-
"No images to review",
|
|
1683
|
-
-1
|
|
1684
|
-
/* CACHED */
|
|
1685
|
-
)),
|
|
1686
|
-
_cache[11] || (_cache[11] = createElementVNode(
|
|
1687
|
-
"span",
|
|
1688
|
-
null,
|
|
1689
|
-
"Generate images first",
|
|
1690
|
-
-1
|
|
1691
|
-
/* CACHED */
|
|
1692
|
-
))
|
|
1693
|
-
])) : createCommentVNode("v-if", true)
|
|
1694
|
-
],
|
|
1695
|
-
64
|
|
1696
|
-
/* STABLE_FRAGMENT */
|
|
1697
|
-
)) : createCommentVNode("v-if", true),
|
|
1698
|
-
createCommentVNode(" Footer Buttons "),
|
|
1699
|
-
!processing.value ? (openBlock(), createElementBlock("div", _hoisted_33, [
|
|
1700
|
-
createCommentVNode(" Back button always visible in image-review mode "),
|
|
1701
|
-
currentMode.value === "image-review" ? (openBlock(), createElementBlock("button", {
|
|
1702
|
-
key: 0,
|
|
1703
|
-
class: "widget__back",
|
|
1704
|
-
onClick: goBackToStyleSelect
|
|
1705
|
-
}, [
|
|
1706
|
-
createVNode(_component_v_icon, { name: "arrow_back" }),
|
|
1707
|
-
_cache[12] || (_cache[12] = createElementVNode(
|
|
1708
|
-
"span",
|
|
1709
|
-
null,
|
|
1710
|
-
"Back",
|
|
1711
|
-
-1
|
|
1712
|
-
/* CACHED */
|
|
1713
|
-
))
|
|
1714
|
-
])) : createCommentVNode("v-if", true),
|
|
1715
|
-
createCommentVNode(" View Images button in style-select mode when styles exist "),
|
|
1716
|
-
currentMode.value === "style-select" && styles.value.length > 0 ? (openBlock(), createElementBlock("button", {
|
|
1717
|
-
key: 1,
|
|
1718
|
-
class: "widget__view-images",
|
|
1719
|
-
onClick: viewGeneratedImages
|
|
1720
|
-
}, [
|
|
1721
|
-
createVNode(_component_v_icon, { name: "photo_library" }),
|
|
1722
|
-
_cache[13] || (_cache[13] = createElementVNode(
|
|
1723
|
-
"span",
|
|
1724
|
-
null,
|
|
1725
|
-
"View Generated Images",
|
|
1726
|
-
-1
|
|
1727
|
-
/* CACHED */
|
|
1728
|
-
))
|
|
1729
|
-
])) : createCommentVNode("v-if", true),
|
|
1730
|
-
createElementVNode("button", {
|
|
1731
|
-
class: "widget__confirm",
|
|
1732
|
-
disabled: !isValid.value,
|
|
1733
|
-
onClick: handleConfirm
|
|
1734
|
-
}, [
|
|
1735
|
-
createVNode(_component_v_icon, {
|
|
1736
|
-
name: currentMode.value === "style-select" ? "arrow_forward" : "check"
|
|
1737
|
-
}, null, 8, ["name"]),
|
|
1738
|
-
createElementVNode(
|
|
1739
|
-
"span",
|
|
1740
|
-
null,
|
|
1741
|
-
toDisplayString(confirmButtonText.value),
|
|
1742
|
-
1
|
|
1743
|
-
/* TEXT */
|
|
1744
|
-
)
|
|
1745
|
-
], 8, _hoisted_34)
|
|
1746
|
-
])) : createCommentVNode("v-if", true),
|
|
1747
|
-
createCommentVNode(" Edit Modal "),
|
|
1748
|
-
createVNode(EditModal, {
|
|
1749
|
-
"is-open": editModalOpen.value,
|
|
1750
|
-
title: editModalTitle.value,
|
|
1751
|
-
prompt: editModalPrompt.value,
|
|
1752
|
-
saving: savingPrompt.value,
|
|
1753
|
-
placeholder: "Enter prompt text...",
|
|
1754
|
-
onClose: closeEditModal,
|
|
1755
|
-
onSave: handleSavePrompt
|
|
1756
|
-
}, null, 8, ["is-open", "title", "prompt", "saving"])
|
|
1757
|
-
]);
|
|
1758
|
-
};
|
|
1759
|
-
}
|
|
1760
|
-
});
|
|
1761
|
-
|
|
1762
|
-
var css = "\n.image-style-widget[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n.widget__loading[data-v-0995de25],\n.widget__error[data-v-0995de25],\n.widget__empty[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 40px 20px;\n text-align: center;\n color: var(--theme--foreground-subdued, #666);\n}\n.widget__loading v-icon[data-v-0995de25],\n.widget__error v-icon[data-v-0995de25],\n.widget__empty v-icon[data-v-0995de25] {\n font-size: 32px;\n}\n.widget__error[data-v-0995de25] {\n color: var(--theme--danger, #f44336);\n}\n.widget__retry[data-v-0995de25] {\n padding: 8px 16px;\n border: 1px solid currentColor;\n border-radius: 4px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n}\n.widget__header[data-v-0995de25] {\n padding-bottom: 16px;\n border-bottom: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n}\n.widget__header-row[data-v-0995de25] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 16px;\n}\n.widget__header-text[data-v-0995de25] {\n flex: 1;\n}\n.widget__header-controls[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n gap: 10px;\n align-items: flex-end;\n}\n.widget__url-input[data-v-0995de25] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.widget__url-label[data-v-0995de25] {\n font-size: 12px;\n font-weight: 500;\n color: var(--theme--foreground-subdued, #666);\n white-space: nowrap;\n}\n.widget__url-field[data-v-0995de25] {\n width: 240px;\n padding: 6px 10px;\n border: 1px solid var(--theme--border-color, #ccc);\n border-radius: 4px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground, #333);\n font-size: 12px;\n font-family: monospace;\n}\n.widget__url-field[data-v-0995de25]:focus {\n outline: none;\n border-color: var(--theme--primary, #6644ff);\n}\n.widget__url-field[data-v-0995de25]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.widget__url-saving[data-v-0995de25] {\n display: flex;\n align-items: center;\n}\n.widget__model-selector[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n align-items: flex-end;\n}\n.widget__model-label[data-v-0995de25] {\n font-size: 12px;\n font-weight: 500;\n color: var(--theme--foreground-subdued, #666);\n}\n.widget__model-buttons[data-v-0995de25] {\n display: flex;\n gap: 4px;\n background: var(--theme--background-subdued, #f5f5f5);\n padding: 4px;\n border-radius: 6px;\n}\n.widget__model-btn[data-v-0995de25] {\n padding: 6px 12px;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--theme--foreground-subdued, #666);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.widget__model-btn[data-v-0995de25]:hover {\n color: var(--theme--foreground, #333);\n}\n.widget__model-btn--active[data-v-0995de25] {\n background: var(--theme--primary, #6644ff);\n color: #fff;\n}\n.widget__model-btn--active[data-v-0995de25]:hover {\n color: #fff;\n}\n.widget__title[data-v-0995de25] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.widget__subtitle[data-v-0995de25] {\n margin: 4px 0 0 0;\n font-size: 13px;\n color: var(--theme--foreground-subdued, #666);\n}\n.widget__list[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.widget__footer[data-v-0995de25] {\n display: flex;\n justify-content: flex-end;\n padding-top: 16px;\n border-top: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n}\n.widget__confirm[data-v-0995de25] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n background: var(--theme--primary, #6644ff);\n color: #fff;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.widget__confirm[data-v-0995de25]:hover:not(:disabled) {\n background: var(--theme--primary-accent, #5533ee);\n}\n.widget__confirm[data-v-0995de25]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.widget__back[data-v-0995de25] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: 1px solid var(--theme--border-color, #ccc);\n border-radius: 6px;\n background: transparent;\n color: var(--theme--foreground, #333);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n margin-right: auto;\n}\n.widget__back[data-v-0995de25]:hover {\n background: var(--theme--background-subdued, #f0f0f0);\n}\n.widget__view-images[data-v-0995de25] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 20px;\n border: 2px solid var(--theme--primary, #6644ff);\n border-radius: 6px;\n background: transparent;\n color: var(--theme--primary, #6644ff);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.widget__view-images[data-v-0995de25]:hover {\n background: var(--theme--primary-background, #f5f3ff);\n}\n.widget__processing[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 60px 20px;\n text-align: center;\n}\n.widget__progress[data-v-0995de25] {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 16px;\n font-weight: 500;\n color: var(--theme--foreground, #333);\n}\n.widget__progress-bar[data-v-0995de25] {\n width: 100%;\n max-width: 300px;\n height: 6px;\n background: var(--theme--border-color-subdued, #e0e0e0);\n border-radius: 3px;\n overflow: hidden;\n}\n.widget__progress-fill[data-v-0995de25] {\n height: 100%;\n background: var(--theme--primary, #6644ff);\n border-radius: 3px;\n transition: width 0.3s ease;\n}\n.spinning[data-v-0995de25] {\n animation: spin-0995de25 1s linear infinite;\n}\n@keyframes spin-0995de25 {\nfrom { transform: rotate(0deg);\n}\nto { transform: rotate(360deg);\n}\n}\n.widget__empty p[data-v-0995de25] {\n margin: 0;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.widget__empty span[data-v-0995de25] {\n font-size: 13px;\n}\n";
|
|
1763
|
-
n(css,{});
|
|
1764
|
-
|
|
1765
|
-
var InterfaceComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-0995de25"], ["__file", "interface.vue"]]);
|
|
1766
|
-
|
|
1767
|
-
var index = defineInterface({
|
|
1768
|
-
id: "image-style-widget",
|
|
1769
|
-
name: "Image Style Widget",
|
|
1770
|
-
icon: "palette",
|
|
1771
|
-
description: "Visual style selector and image reviewer",
|
|
1772
|
-
component: InterfaceComponent,
|
|
1773
|
-
options: [
|
|
1774
|
-
{
|
|
1775
|
-
field: "mode",
|
|
1776
|
-
name: "Mode",
|
|
1777
|
-
type: "string",
|
|
1778
|
-
meta: {
|
|
1779
|
-
width: "half",
|
|
1780
|
-
interface: "select-dropdown",
|
|
1781
|
-
options: {
|
|
1782
|
-
choices: [
|
|
1783
|
-
{ text: "Style Selection", value: "style-select" },
|
|
1784
|
-
{ text: "Image Review", value: "image-review" }
|
|
1785
|
-
]
|
|
1786
|
-
}
|
|
1787
|
-
},
|
|
1788
|
-
schema: {
|
|
1789
|
-
default_value: "style-select"
|
|
1790
|
-
}
|
|
1791
|
-
},
|
|
1792
|
-
{
|
|
1793
|
-
field: "stylesCollection",
|
|
1794
|
-
name: "Styles Collection",
|
|
1795
|
-
type: "string",
|
|
1796
|
-
meta: {
|
|
1797
|
-
width: "half",
|
|
1798
|
-
interface: "input",
|
|
1799
|
-
note: "Collection containing style definitions (default: ImageStyles)"
|
|
1800
|
-
},
|
|
1801
|
-
schema: {
|
|
1802
|
-
default_value: "ImageStyles"
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
],
|
|
1806
|
-
types: ["json", "string"]
|
|
1807
|
-
});
|
|
1808
|
-
|
|
1809
|
-
export { index as default };
|
|
1
|
+
import{useApi as e,defineInterface as a}from"@directus/extensions-sdk";import{defineComponent as n,computed as t,resolveComponent as l,openBlock as i,createElementBlock as r,normalizeClass as d,createCommentVNode as o,createElementVNode as s,createVNode as c,toDisplayString as u,ref as m,watch as g,nextTick as p,withModifiers as v,createTextVNode as f,withDirectives as _,vModelText as b,createBlock as h,inject as y,onMounted as x,normalizeStyle as w,unref as k,Fragment as S,withKeys as I,renderList as C}from"vue";const $={class:"style-card__images"},F={class:"style-card__image"},z=["src","alt"],P={key:1,class:"style-card__placeholder"},j={class:"style-card__image"},E=["src","alt"],U={key:1,class:"style-card__placeholder"},G={class:"style-card__content"},M={class:"style-card__header"},A={class:"style-card__radio"},L=["checked","name"],N={class:"style-card__name"},R={class:"style-card__prompt"},B={class:"style-card__actions"},T=["disabled"];var V=n({__name:"StyleCard",props:{style:{},isSelected:{type:Boolean},radioGroupName:{},loading:{type:Boolean},getFileUrl:{type:Function}},emits:["select","edit","regenerate"],setup(e){const a=e,n=t(()=>a.style.prompt.length<=100?a.style.prompt:a.style.prompt.substring(0,100)+"...");return(a,t)=>{const m=l("v-icon");return i(),r("div",{class:d(["style-card",{"style-card--selected":e.isSelected}])},[o(" Images Section "),s("div",$,[s("div",F,[e.style.example_1?(i(),r("img",{key:0,src:e.getFileUrl(e.style.example_1),alt:`${e.style.name} example 1`},null,8,z)):(i(),r("div",P,[c(m,{name:"image"})]))]),s("div",j,[e.style.example_2?(i(),r("img",{key:0,src:e.getFileUrl(e.style.example_2),alt:`${e.style.name} example 2`},null,8,E)):(i(),r("div",U,[c(m,{name:"image"})]))])]),o(" Content Section "),s("div",G,[o(" Header: Radio + Name "),s("div",M,[s("label",A,[s("input",{type:"radio",checked:e.isSelected,name:e.radioGroupName,onChange:t[0]||(t[0]=n=>a.$emit("select",e.style.id))},null,40,L),t[4]||(t[4]=s("span",{class:"style-card__radio-custom"},null,-1))]),s("h3",N,u(e.style.name),1)]),o(" Prompt Preview "),s("p",R,u(n.value),1),o(" Actions "),s("div",B,[o(' Hidden for now - set v-if="true" to restore '),o("v-if",!0),s("button",{class:"style-card__btn style-card__btn--secondary",disabled:e.loading,onClick:t[2]||(t[2]=n=>a.$emit("regenerate",e.style))},[c(m,{name:"refresh",small:""}),s("span",null,u(e.loading?"Generating...":"Generate an example"),1)],8,T),s("button",{class:d(["style-card__btn",{"style-card__btn--primary":e.isSelected}]),onClick:t[3]||(t[3]=n=>a.$emit("select",e.style.id))},[c(m,{name:e.isSelected?"radio_button_checked":"radio_button_unchecked",small:""},null,8,["name"]),t[6]||(t[6]=s("span",null,"Select",-1))],2)])])],2)}}}),O=[],D=[];function W(e,a){if(e&&"undefined"!=typeof document){var n,t=!0===a.prepend?"prepend":"append",l=!0===a.singleTag,i="string"==typeof a.container?document.querySelector(a.container):document.getElementsByTagName("head")[0];if(l){var r=O.indexOf(i);-1===r&&(r=O.push(i)-1,D[r]={}),n=D[r]&&D[r][t]?D[r][t]:D[r][t]=d()}else n=d();65279===e.charCodeAt(0)&&(e=e.substring(1)),n.styleSheet?n.styleSheet.cssText+=e:n.appendChild(document.createTextNode(e))}function d(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),a.attributes)for(var n=Object.keys(a.attributes),l=0;l<n.length;l++)e.setAttribute(n[l],a.attributes[n[l]]);var r="prepend"===t?"afterbegin":"beforeend";return i.insertAdjacentElement(r,e),e}}W("\n.style-card[data-v-99b31e92] {\n display: flex;\n gap: 20px;\n padding: 16px;\n border: 2px solid var(--theme--border-color-subdued, #e0e0e0);\n border-radius: 8px;\n background: var(--theme--background, #fff);\n transition: all 0.2s ease;\n}\n.style-card[data-v-99b31e92]:hover {\n border-color: var(--theme--border-color, #ccc);\n}\n.style-card--selected[data-v-99b31e92] {\n border-color: var(--theme--primary, #6644ff);\n background: var(--theme--primary-background, #f5f3ff);\n}\n.style-card__images[data-v-99b31e92] {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n.style-card__image[data-v-99b31e92] {\n width: 80px;\n height: 80px;\n border-radius: 6px;\n overflow: hidden;\n background: var(--theme--background-subdued, #f5f5f5);\n}\n.style-card__image img[data-v-99b31e92] {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.style-card__placeholder[data-v-99b31e92] {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--theme--foreground-subdued, #999);\n}\n.style-card__content[data-v-99b31e92] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-width: 0;\n}\n.style-card__header[data-v-99b31e92] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.style-card__radio[data-v-99b31e92] {\n position: relative;\n display: flex;\n align-items: center;\n cursor: pointer;\n}\n.style-card__radio input[data-v-99b31e92] {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n}\n.style-card__radio-custom[data-v-99b31e92] {\n width: 18px;\n height: 18px;\n border: 2px solid var(--theme--border-color, #ccc);\n border-radius: 50%;\n transition: all 0.2s ease;\n}\n.style-card__radio input:checked + .style-card__radio-custom[data-v-99b31e92] {\n border-color: var(--theme--primary, #6644ff);\n background: var(--theme--primary, #6644ff);\n box-shadow: inset 0 0 0 3px var(--theme--background, #fff);\n}\n.style-card__name[data-v-99b31e92] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.style-card__prompt[data-v-99b31e92] {\n margin: 0;\n font-size: 13px;\n color: var(--theme--foreground-subdued, #666);\n line-height: 1.4;\n flex: 1;\n}\n.style-card__actions[data-v-99b31e92] {\n display: flex;\n gap: 8px;\n margin-top: auto;\n}\n.style-card__btn[data-v-99b31e92] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 12px;\n border: 1px solid var(--theme--border-color, #ccc);\n border-radius: 4px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground-subdued, #666);\n font-size: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.style-card__btn[data-v-99b31e92]:hover:not(:disabled) {\n border-color: var(--theme--primary, #6644ff);\n color: var(--theme--primary, #6644ff);\n}\n.style-card__btn[data-v-99b31e92]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.style-card__btn--primary[data-v-99b31e92] {\n background: var(--theme--primary, #6644ff);\n border-color: var(--theme--primary, #6644ff);\n color: #fff;\n}\n.style-card__btn--primary[data-v-99b31e92]:hover:not(:disabled) {\n background: var(--theme--primary-accent, #5533ee);\n border-color: var(--theme--primary-accent, #5533ee);\n color: #fff;\n}\n.style-card__btn--secondary[data-v-99b31e92] {\n background: transparent;\n}\n",{});var q=(e,a)=>{const n=e.__vccOpts||e;for(const[e,t]of a)n[e]=t;return n},H=q(V,[["__scopeId","data-v-99b31e92"],["__file","StyleCard.vue"]]);const K={class:"image-card"},J={class:"image-card__header"},Q={class:"image-card__placeholder-text"},X={class:"image-card__body"},Y={class:"image-card__images"},Z=["src"],ee={key:1,class:"image-card__image-placeholder"},ae=["src"],ne={key:1,class:"image-card__image-placeholder"},te={class:"image-card__content"},le={class:"image-card__prompts-section"},ie={class:"image-card__prompt-item"},re={class:"image-card__prompt"},de={class:"image-card__actions"},oe=["disabled"],se=["disabled"],ce=["disabled"],ue={class:"image-card__selection"},me=["src"];var ge=n({__name:"ImageCard",props:{item:{},loading:{type:Boolean},getFileUrl:{type:Function}},emits:["select-image","edit","regenerate","delete"],setup(e){const a=e,n=m(null);function g(e){n.value=function(e){return e?`/assets/${e}`:""}(e)}function p(){n.value=null}const v=t(()=>a.item.prompt1.length<=120?a.item.prompt1:a.item.prompt1.substring(0,120)+"...");return(a,t)=>{const m=l("v-icon");return i(),r("div",K,[o(" Placeholder Header "),s("div",J,[c(m,{name:"image"}),s("span",Q,u(e.item.placeholder),1)]),s("div",X,[o(" Images Section "),s("div",Y,[s("div",{class:d(["image-card__image",{"image-card__image--selected":1===e.item.selected}]),onClick:t[0]||(t[0]=a=>e.item.image1&&g(e.item.image1))},[e.item.image1?(i(),r("img",{key:0,src:e.getFileUrl(e.item.image1),alt:"Generated image 1"},null,8,Z)):(i(),r("div",ee,[c(m,{name:"image"})])),t[7]||(t[7]=s("div",{class:"image-card__image-badge"},"1",-1))],2),s("div",{class:d(["image-card__image",{"image-card__image--selected":2===e.item.selected}]),onClick:t[1]||(t[1]=a=>e.item.image2&&g(e.item.image2))},[e.item.image2?(i(),r("img",{key:0,src:e.getFileUrl(e.item.image2),alt:"Generated image 2"},null,8,ae)):(i(),r("div",ne,[c(m,{name:"image"})])),t[8]||(t[8]=s("div",{class:"image-card__image-badge"},"2",-1))],2)]),o(" Content Section "),s("div",te,[o(" Prompt Display "),s("div",le,[s("div",ie,[t[9]||(t[9]=s("span",{class:"image-card__prompt-label"},"Prompt:",-1)),s("p",re,u(v.value),1)])]),o(" Actions "),s("div",de,[s("button",{class:"image-card__btn image-card__btn--secondary",disabled:e.loading,onClick:t[2]||(t[2]=n=>a.$emit("edit",e.item))},[c(m,{name:"edit",small:""}),t[10]||(t[10]=s("span",null,"Edit Prompt",-1))],8,oe),s("button",{class:"image-card__btn image-card__btn--secondary",disabled:e.loading,onClick:t[3]||(t[3]=n=>a.$emit("regenerate",e.item))},[c(m,{name:"refresh",small:""}),s("span",null,u(e.loading?"Generating...":"Regenerate"),1)],8,se),s("button",{class:"image-card__btn image-card__btn--danger",disabled:e.loading,onClick:t[4]||(t[4]=n=>a.$emit("delete",e.item))},[c(m,{name:"delete",small:""}),t[11]||(t[11]=s("span",null,"Delete",-1))],8,ce)]),o(" Selection Buttons "),s("div",ue,[s("button",{class:d(["image-card__select-btn",{"image-card__select-btn--active":1===e.item.selected}]),onClick:t[5]||(t[5]=n=>a.$emit("select-image",e.item.placeholder,1))},[c(m,{name:1===e.item.selected?"check_box":"check_box_outline_blank",small:""},null,8,["name"]),t[12]||(t[12]=s("span",null,"Select 1",-1))],2),s("button",{class:d(["image-card__select-btn",{"image-card__select-btn--active":2===e.item.selected}]),onClick:t[6]||(t[6]=n=>a.$emit("select-image",e.item.placeholder,2))},[c(m,{name:2===e.item.selected?"check_box":"check_box_outline_blank",small:""},null,8,["name"]),t[13]||(t[13]=s("span",null,"Select 2",-1))],2)])])]),o(" Lightbox Modal "),n.value?(i(),r("div",{key:0,class:"image-card__lightbox",onClick:p},[s("img",{src:n.value,alt:"Enlarged image"},null,8,me)])):o("v-if",!0)])}}});W("\n.image-card[data-v-20b0594f] {\n border: 2px solid var(--theme--border-color-subdued, #e0e0e0);\n border-radius: 8px;\n background: var(--theme--background, #fff);\n overflow: hidden;\n}\n.image-card__header[data-v-20b0594f] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: var(--theme--background-subdued, #f5f5f5);\n border-bottom: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n}\n.image-card__placeholder-text[data-v-20b0594f] {\n font-size: 14px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.image-card__body[data-v-20b0594f] {\n display: flex;\n gap: 20px;\n padding: 16px;\n}\n.image-card__images[data-v-20b0594f] {\n display: flex;\n gap: 12px;\n flex-shrink: 0;\n}\n.image-card__image[data-v-20b0594f] {\n position: relative;\n width: 100px;\n height: 100px;\n border-radius: 6px;\n overflow: hidden;\n background: var(--theme--background-subdued, #f5f5f5);\n border: 3px solid transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.image-card__image[data-v-20b0594f]:hover {\n border-color: var(--theme--primary-subdued, #c4b8ff);\n}\n.image-card__image--selected[data-v-20b0594f] {\n border-color: var(--theme--primary, #6644ff);\n}\n.image-card__image img[data-v-20b0594f] {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.image-card__image-placeholder[data-v-20b0594f] {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--theme--foreground-subdued, #999);\n}\n.image-card__image-badge[data-v-20b0594f] {\n position: absolute;\n top: 4px;\n right: 4px;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: rgba(0, 0, 0, 0.6);\n color: #fff;\n font-size: 11px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.image-card__image--selected .image-card__image-badge[data-v-20b0594f] {\n background: var(--theme--primary, #6644ff);\n}\n.image-card__content[data-v-20b0594f] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n}\n.image-card__prompts-section[data-v-20b0594f] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.image-card__prompt-item[data-v-20b0594f] {\n padding: 8px;\n background: var(--theme--background-subdued, #f9f9f9);\n border-radius: 4px;\n}\n.image-card__prompt-label[data-v-20b0594f] {\n font-size: 11px;\n font-weight: 600;\n color: var(--theme--foreground-subdued, #666);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n.image-card__prompt[data-v-20b0594f] {\n margin: 4px 0 0 0;\n font-size: 12px;\n color: var(--theme--foreground, #333);\n line-height: 1.4;\n}\n.image-card__actions[data-v-20b0594f] {\n display: flex;\n gap: 8px;\n}\n.image-card__btn[data-v-20b0594f] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 12px;\n border: 1px solid var(--theme--border-color, #ccc);\n border-radius: 4px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground-subdued, #666);\n font-size: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.image-card__btn[data-v-20b0594f]:hover:not(:disabled) {\n border-color: var(--theme--primary, #6644ff);\n color: var(--theme--primary, #6644ff);\n}\n.image-card__btn[data-v-20b0594f]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.image-card__btn--secondary[data-v-20b0594f] {\n background: transparent;\n}\n.image-card__btn--danger[data-v-20b0594f] {\n background: transparent;\n border-color: var(--theme--danger, #f44336);\n color: var(--theme--danger, #f44336);\n}\n.image-card__btn--danger[data-v-20b0594f]:hover:not(:disabled) {\n background: var(--theme--danger-background, #ffebee);\n border-color: var(--theme--danger, #f44336);\n color: var(--theme--danger, #f44336);\n}\n.image-card__selection[data-v-20b0594f] {\n display: flex;\n gap: 8px;\n}\n.image-card__select-btn[data-v-20b0594f] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 2px solid var(--theme--border-color, #ccc);\n border-radius: 6px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground-subdued, #666);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.image-card__select-btn[data-v-20b0594f]:hover {\n border-color: var(--theme--primary-subdued, #c4b8ff);\n}\n.image-card__select-btn--active[data-v-20b0594f] {\n border-color: var(--theme--primary, #6644ff);\n background: var(--theme--primary-background, #f5f3ff);\n color: var(--theme--primary, #6644ff);\n}\n\n/* Lightbox Modal */\n.image-card__lightbox[data-v-20b0594f] {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.85);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n cursor: zoom-out;\n padding: 40px;\n}\n.image-card__lightbox img[data-v-20b0594f] {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border-radius: 8px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);\n}\n",{});var pe=q(ge,[["__scopeId","data-v-20b0594f"],["__file","ImageCard.vue"]]);const ve={class:"edit-modal"},fe={class:"edit-modal__header"},_e={class:"edit-modal__title"},be={class:"edit-modal__body"},he={class:"edit-modal__label"},ye=["placeholder"],xe={class:"edit-modal__footer"},we=["disabled"];var ke=n({__name:"EditModal",props:{isOpen:{type:Boolean},title:{},prompt:{},placeholder:{},saving:{type:Boolean}},emits:["close","save"],setup(e,{emit:a}){const n=e,d=a,y=m(n.prompt),x=m(null),w=t(()=>y.value!==n.prompt);function k(){w.value&&d("save",y.value)}return g(()=>n.prompt,e=>{y.value=e}),g(()=>n.isOpen,async e=>{e&&(y.value=n.prompt,await p(),x.value?.focus(),x.value?.select())}),(a,n)=>{const t=l("v-icon");return e.isOpen?(i(),r("div",{key:0,class:"edit-modal__overlay",onClick:n[3]||(n[3]=v(e=>a.$emit("close"),["self"]))},[s("div",ve,[s("div",fe,[s("h3",_e,[c(t,{name:"edit"}),n[4]||(n[4]=f(" Edit Prompt ",-1))]),s("button",{class:"edit-modal__close",onClick:n[0]||(n[0]=e=>a.$emit("close"))},[c(t,{name:"close"})])]),s("div",be,[s("label",he,u(e.title),1),_(s("textarea",{ref_key:"textareaRef",ref:x,"onUpdate:modelValue":n[1]||(n[1]=e=>y.value=e),class:"edit-modal__textarea",rows:"6",placeholder:e.placeholder},null,8,ye),[[b,y.value]]),n[5]||(n[5]=s("p",{class:"edit-modal__hint"}," Edit the prompt text and click Save to apply changes. ",-1))]),s("div",xe,[s("button",{class:"edit-modal__btn edit-modal__btn--secondary",onClick:n[2]||(n[2]=e=>a.$emit("close"))}," Cancel "),s("button",{class:"edit-modal__btn edit-modal__btn--primary",disabled:!w.value||e.saving,onClick:k},[e.saving?(i(),h(t,{key:0,name:"refresh",small:""})):o("v-if",!0),s("span",null,u(e.saving?"Saving...":"Save"),1)],8,we)])])])):o("v-if",!0)}}});W("\n.edit-modal__overlay[data-v-44d552bc] {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n padding: 20px;\n}\n.edit-modal[data-v-44d552bc] {\n width: 100%;\n max-width: 600px;\n background: var(--theme--background, #fff);\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n overflow: hidden;\n}\n.edit-modal__header[data-v-44d552bc] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n}\n.edit-modal__title[data-v-44d552bc] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.edit-modal__close[data-v-44d552bc] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--theme--foreground-subdued, #666);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.edit-modal__close[data-v-44d552bc]:hover {\n background: var(--theme--background-subdued, #f5f5f5);\n color: var(--theme--foreground, #333);\n}\n.edit-modal__body[data-v-44d552bc] {\n padding: 20px;\n}\n.edit-modal__label[data-v-44d552bc] {\n display: block;\n margin-bottom: 8px;\n font-size: 13px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.edit-modal__textarea[data-v-44d552bc] {\n width: 100%;\n padding: 12px;\n border: 2px solid var(--theme--border-color-subdued, #e0e0e0);\n border-radius: 6px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground, #333);\n font-size: 14px;\n font-family: inherit;\n line-height: 1.5;\n resize: vertical;\n transition: border-color 0.2s ease;\n}\n.edit-modal__textarea[data-v-44d552bc]:focus {\n outline: none;\n border-color: var(--theme--primary, #6644ff);\n}\n.edit-modal__textarea[data-v-44d552bc]::placeholder {\n color: var(--theme--foreground-subdued, #999);\n}\n.edit-modal__hint[data-v-44d552bc] {\n margin: 8px 0 0 0;\n font-size: 12px;\n color: var(--theme--foreground-subdued, #666);\n}\n.edit-modal__footer[data-v-44d552bc] {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 20px;\n border-top: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n background: var(--theme--background-subdued, #f9f9f9);\n}\n.edit-modal__btn[data-v-44d552bc] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 10px 20px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.edit-modal__btn[data-v-44d552bc]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.edit-modal__btn--secondary[data-v-44d552bc] {\n background: var(--theme--background, #fff);\n color: var(--theme--foreground-subdued, #666);\n border: 1px solid var(--theme--border-color, #ccc);\n}\n.edit-modal__btn--secondary[data-v-44d552bc]:hover:not(:disabled) {\n background: var(--theme--background-subdued, #f5f5f5);\n}\n.edit-modal__btn--primary[data-v-44d552bc] {\n background: var(--theme--primary, #6644ff);\n color: #fff;\n}\n.edit-modal__btn--primary[data-v-44d552bc]:hover:not(:disabled) {\n background: var(--theme--primary-accent, #5533ee);\n}\n",{});var Se=q(ke,[["__scopeId","data-v-44d552bc"],["__file","EditModal.vue"]]);const Ie=[{id:"flux-kontext-pro",name:"Flux"},{id:"gemini-2.5-flash-image",name:"Nano Banana"}],Ce="http://localhost:8004";const $e={class:"image-style-widget"},Fe={key:0,class:"widget__processing"},ze={class:"widget__progress"},Pe={class:"widget__progress-bar"},je={class:"widget__loading"},Ee={class:"widget__error"},Ue={class:"widget__header"},Ge={class:"widget__header-row"},Me={class:"widget__header-text"},Ae={class:"widget__title"},Le={class:"widget__header-controls"},Ne={class:"widget__url-input"},Re=["value","disabled"],Be={key:0,class:"widget__url-saving"},Te={class:"widget__model-selector"},Ve={class:"widget__model-buttons"},Oe=["onClick"],De={class:"widget__list"},We={key:0,class:"widget__empty"},qe={class:"widget__header"},He={class:"widget__header-row"},Ke={class:"widget__header-text"},Je={class:"widget__title"},Qe={class:"widget__header-controls"},Xe={class:"widget__url-input"},Ye=["value","disabled"],Ze={key:0,class:"widget__url-saving"},ea={class:"widget__model-selector"},aa={class:"widget__model-buttons"},na=["onClick"],ta={class:"widget__list"},la={key:0,class:"widget__empty"},ia={key:5,class:"widget__footer"},ra=["disabled"];var da=n({__name:"interface",props:{value:{},mode:{default:"style-select"},stylesCollection:{default:"ImageStyles"},primaryKey:{},collection:{}},emits:["input"],setup(a,{emit:n}){const p=Math.random().toString(36).substring(2,9),v=a,_=n,b=y("values");function $(){return b?void 0!==b.value?b.value:b:{}}const F=t(()=>{const e=$();return v.primaryKey||e?.id||null}),z=t(()=>{const e=$();return e?.description||e?.Description||""}),{loading:P,error:j,fetchServiceUrl:E,updateServiceUrl:U,checkIsAdmin:G,fetchStyles:M,updateStylePrompt:A,regenerateStyleExamples:L,regeneratePlaceholderImages:N,getFileUrl:R,extractPlaceholders:B,generateImagePrompts:T,generateSingleImage:V,updateContentWithImages:O,updateLessonContent:D,fetchLessonContent:W,getImageVariants:q,saveGeneratedImages:K,deleteImageVariant:J}=function(){const a=e(),n=m(!1),t=m(null);async function l(e,a="flux-kontext-pro",n=Ce){const t=`${n}/api/misc/images/upload`,l=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:e,model:a,format:"png",aspect_ratio:"16:9"})});if(!l.ok)throw new Error(`Image generation failed: ${l.status}`);const i=await l.json();return i?.id||null}return{loading:n,error:t,fetchServiceUrl:async function(){try{const e=await a.get("/items/WidgetConfig");return e.data.data?.service_url||Ce}catch(e){return console.warn("Failed to fetch service URL, using default:",e.message),Ce}},updateServiceUrl:async function(e){try{return await a.patch("/items/WidgetConfig",{service_url:e}),!0}catch(e){return t.value=e.message||"Failed to update service URL",!1}},checkIsAdmin:async function(){try{const e=await a.get("/users/me",{params:{fields:["role.admin_access"]}});return!0===e.data.data?.role?.admin_access}catch(e){return console.warn("Failed to check admin status:",e.message),!1}},fetchStyles:async function(e="ImageStyles"){n.value=!0,t.value=null;try{return(await a.get(`/items/${e}`,{params:{filter:{is_active:{_eq:!0}},sort:["sort"],fields:["id","name","prompt","example_1","example_2","is_active","sort"]}})).data.data||[]}catch(e){return t.value=e.message||"Failed to fetch styles",[]}finally{n.value=!1}},updateStylePrompt:async function(e,l,i){n.value=!0,t.value=null;try{return await a.patch(`/items/${e}/${l}`,{prompt:i}),!0}catch(e){return t.value=e.message||"Failed to update prompt",!1}finally{n.value=!1}},regenerateStyleExamples:async function(e,i,r="ImageStyles",d="flux-kontext-pro",o=Ce){n.value=!0,t.value=null;try{const[n,s]=await Promise.all([l(i,d,o),l(i,d,o)]);return n&&s?(await a.patch(`/items/${r}/${e}`,{example_1:n,example_2:s}),{example_1:n,example_2:s}):(t.value="Failed to generate images",null)}catch(e){return t.value=e.message||"Failed to regenerate images",null}finally{n.value=!1}},regeneratePlaceholderImages:async function(e,a,i="flux-kontext-pro",r=Ce){n.value=!0,t.value=null;try{const[n,d]=await Promise.all([l(e,i,r),l(a,i,r)]);return n&&d?{image1:n,image2:d}:(t.value="Failed to generate images",null)}catch(e){return t.value=e.message||"Failed to regenerate images",null}finally{n.value=!1}},getFileUrl:function(e){return e?`/assets/${e}?width=200&height=200&fit=cover`:""},generateImagePrompts:async function(e,a,l=Ce){n.value=!0,t.value=null;try{const n=`${l}/api/misc/prompts`,t=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({style:e,description:a,model:"gpt-4o-mini"})});if(!t.ok){const e=await t.json().catch(()=>({detail:t.statusText}));throw new Error(e.detail||`HTTP ${t.status}`)}return await t.json()}catch(e){return t.value=e.message||"Failed to generate image prompts",null}finally{n.value=!1}},getImageVariants:async function(e){n.value=!0,t.value=null;try{return(await a.get("/items/ImageVariants",{params:{filter:{lesson:{_eq:e}},fields:["id","title","status","lesson","images.id","images.image","images.prompt","images.status"]}})).data.data||[]}catch(e){return t.value=e.message||"Failed to fetch image variants",[]}finally{n.value=!1}},saveGeneratedImages:async function(e,l){n.value=!0,t.value=null;try{for(const n of l){const t=(await a.post("/items/ImageVariants",{title:n.placeholder,lesson:e,status:"found"})).data.data.id,l=[];n.image1&&l.push({image:n.image1,prompt:n.prompt1,variants:t,status:"generated"}),n.image2&&l.push({image:n.image2,prompt:n.prompt2,variants:t,status:"generated"}),l.length>0&&await a.post("/items/ContentImage",l)}return!0}catch(e){return t.value=e.message||"Failed to save generated images",!1}finally{n.value=!1}},deleteImageVariant:async function(e,l){n.value=!0,t.value=null;try{const n=(await a.get("/items/ImageVariants",{params:{filter:{lesson:{_eq:e},title:{_eq:l}},fields:["id","images.id","images.image"]}})).data.data||[];for(const e of n){const n=[];if(e.images&&e.images.length>0)for(const t of e.images)t.image&&n.push(t.image),await a.delete(`/items/ContentImage/${t.id}`);await a.delete(`/items/ImageVariants/${e.id}`);for(const e of n)try{await a.delete(`/files/${e}`)}catch(a){console.warn(`Failed to delete file ${e}:`,a.message)}}return!0}catch(e){return t.value=e.message||"Failed to delete image variant",!1}finally{n.value=!1}},extractPlaceholders:function(e){const a=/\[([^\]]+)\]/g,n=[];let t;for(;null!==(t=a.exec(e));)n.push(t[1]);return n},generateSingleImage:l,updateContentWithImages:function(e,a){let n=e;for(const{caption:e,fileId:t}of a){const a=`[${e}]`,l=`<img src="/assets/${t}" alt="${e}" />`;n=n.replace(a,l)}return n},updateLessonContent:async function(e,l){n.value=!0,t.value=null;try{return await a.patch(`/items/SM_Lessons/${e}`,{description:l}),!0}catch(e){return t.value=e.message||"Failed to update lesson content",!1}finally{n.value=!1}},fetchLessonContent:async function(e){try{const n=await a.get(`/items/SM_Lessons/${e}`,{params:{fields:["description"]}});return n.data.data?.description||n.data.data?.Description||null}catch(e){return console.error("Failed to fetch lesson content:",e),null}}}}(),Q=m(v.mode),X=m([]),Y=m(null),Z=m(null),ee=m("flux-kontext-pro"),ae=m("http://localhost:8004"),ne=m(!1),te=m(!1),le=m([]),ie=m(null),re=m(!1),de=m(""),oe=m(0),se=m(!1),ce=m(""),ue=m(""),me=m(null),ge=m(null),ve=m(!1);t(()=>"style-select"===Q.value?X.value.length>0:le.value.length>0);const fe=t(()=>"style-select"===Q.value?null!==Y.value:le.value.every(e=>e.selected)),_e=t(()=>"style-select"===Q.value?"Generate Images":"Confirm Selections");function be(){v.value&&"object"==typeof v.value&&(v.value.selectedStyleId&&(Y.value=v.value.selectedStyleId),v.value.items&&v.value.items.length>0&&(le.value=v.value.items,Q.value="image-review"),v.value.currentMode&&(Q.value=v.value.currentMode))}async function he(){X.value=await M(v.stylesCollection),!Y.value&&X.value.length>0&&(Y.value=X.value[0].id)}function ye(){Q.value="style-select",ma()}async function xe(){if(F.value){re.value=!0,de.value="Loading existing images...",oe.value=10;try{const e=await q(String(F.value));if(oe.value=50,!e||0===e.length)return j.value="No generated images found for this lesson. Generate images first.",void(re.value=!1);const a=e.map(e=>{const a=e.images||[],n=a[0],t=a[1];return{placeholder:e.title||"Untitled",prompt1:n?.prompt||"",prompt2:t?.prompt||n?.prompt||"",image1:n?.image||null,image2:t?.image||null,selected:n?.image?1:t?.image?2:null}});oe.value=90,le.value=a,Q.value="image-review",oe.value=100,ma()}catch(e){j.value=e.message||"Failed to load existing images"}finally{re.value=!1}}}function we(e){Y.value=e,ma()}async function ke(e){Z.value=e.id;const a=await L(e.id,e.prompt,v.stylesCollection,ee.value,ae.value);if(a){const n=X.value.findIndex(a=>a.id===e.id);n>=0&&(X.value[n]={...X.value[n],example_1:a.example_1,example_2:a.example_2})}Z.value=null}function da(e,a){const n=le.value.find(a=>a.placeholder===e);n&&(n.selected=a,ma())}async function oa(e){ie.value=e.placeholder;const a=await N(e.prompt1,e.prompt2,ee.value,ae.value);if(a){const n=le.value.findIndex(a=>a.placeholder===e.placeholder);n>=0&&(le.value[n]={...le.value[n],image1:a.image1,image2:a.image2,selected:1},ma())}ie.value=null}async function sa(e){if(!F.value)return;if(!confirm(`Delete "${e.placeholder}" and its images? This cannot be undone.`))return;ie.value=e.placeholder;if(await J(String(F.value),e.placeholder)){const a=le.value.findIndex(a=>a.placeholder===e.placeholder);a>=0&&(le.value.splice(a,1),ma())}ie.value=null}function ca(){se.value=!1,me.value=null,ge.value=null}async function ua(e){if(ve.value=!0,me.value){if(await A(v.stylesCollection,me.value.id,e)){const a=X.value.findIndex(e=>e.id===me.value.id);a>=0&&(X.value[a]={...X.value[a],prompt:e}),ca()}}else if(ge.value){const a=le.value.findIndex(e=>e.placeholder===ge.value.placeholder);a>=0&&(le.value[a]={...le.value[a],prompt1:e},ma(),ca())}ve.value=!1}function ma(){_("input",{currentMode:Q.value,selectedStyleId:Y.value,selectedStyle:X.value.find(e=>e.id===Y.value)||null,items:le.value})}async function ga(){fe.value&&("style-select"===Q.value?await async function(){const e=X.value.find(e=>e.id===Y.value);if(!e)return void(j.value="No style selected. Please select an image style first.");if(!z.value)return void(j.value="No content available in the Description field. Add text with [image placeholders] first.");re.value=!0,oe.value=0;try{de.value="Generating image prompts with AI...",oe.value=10;const a=await T(e.prompt,z.value,ae.value);if(!a||!a.images||0===a.images.length)return j.value="No [image placeholders] found in content. Add placeholders like [Rabbit image] to generate images.",void(re.value=!1);oe.value=30,de.value=`Generating images for ${a.images.length} placeholder(s)...`;const n=[],t=60/a.images.length;for(let l=0;l<a.images.length;l++){const i=a.images[l];de.value=`Generating images for "${i.placeholder}"... (${l+1}/${a.images.length})`;const r=i.prompts[0]||`${e.prompt}. Subject: ${i.placeholder}`,d=i.prompts[1]||r,[o,s]=await Promise.all([V(r,ee.value,ae.value),V(d,ee.value,ae.value)]);n.push({placeholder:i.placeholder,prompt1:r,prompt2:d,image1:o,image2:s,selected:o?1:s?2:null}),oe.value=30+(l+1)*t}de.value="Saving generated images...",oe.value=90,F.value&&await K(String(F.value),n),de.value="Preparing image review...",oe.value=95,le.value=n,Q.value="image-review",oe.value=100,ma()}catch(e){j.value=e.message||"Image generation failed"}finally{re.value=!1}}():await async function(){if(!F.value)return void(j.value="No lesson ID available");const e=le.value.filter(e=>e.selected&&(1===e.selected?e.image1:e.image2)).map(e=>({caption:e.placeholder,fileId:1===e.selected?e.image1:e.image2}));if(0===e.length)return void(j.value="No images selected. Please select at least one image.");re.value=!0,de.value="Fetching current content...",oe.value=20;try{const a=await W(String(F.value));if(!a)return j.value="Could not fetch lesson content. Make sure Description field has content.",void(re.value=!1);oe.value=40,de.value="Applying selected images to content...";const n=O(a,e);oe.value=80;await D(String(F.value),n)?(oe.value=100,de.value="Images applied successfully! Refreshing...",setTimeout(()=>{window.location.reload()},1e3)):j.value="Failed to update lesson content"}catch(e){j.value=e.message||"Failed to apply images"}finally{re.value=!1}}())}function pa(e){!async function(e){if(e===ae.value)return;if(!ne.value&&!confirm("Warning: Changing the service URL will affect all users.\n\nAre you sure you want to continue?"))return;te.value=!0,await U(e)&&(ae.value=e),te.value=!1}(e.target.value.trim())}function va(e){e.target.blur()}return x(()=>{be(),he(),async function(){const[e,a]=await Promise.all([E(),G()]);ae.value=e,ne.value=a}()}),g(()=>v.mode,e=>{e&&(Q.value=e)}),g(()=>v.value,()=>{be()},{deep:!0}),(e,n)=>{const t=l("v-icon");return i(),r("div",$e,[o(" Processing State "),re.value?(i(),r("div",Fe,[s("div",ze,[c(t,{name:"refresh",class:"spinning"}),s("span",null,u(de.value),1)]),s("div",Pe,[s("div",{class:"widget__progress-fill",style:w({width:oe.value+"%"})},null,4)])])):k(P)&&!X.value.length?(i(),r(S,{key:1},[o(" Loading State "),s("div",je,[c(t,{name:"refresh"}),n[0]||(n[0]=s("span",null,"Loading styles...",-1))])],2112)):k(j)?(i(),r(S,{key:2},[o(" Error State "),s("div",Ee,[c(t,{name:"error"}),s("span",null,u(k(j)),1),s("button",{class:"widget__retry",onClick:he},"Retry")])],2112)):"style-select"===Q.value?(i(),r(S,{key:3},[o(" Mode 1: Style Selection "),s("div",Ue,[s("div",Ge,[s("div",Me,[s("h2",Ae,[c(t,{name:"palette"}),n[1]||(n[1]=f(" Select Image Style ",-1))]),n[2]||(n[2]=s("p",{class:"widget__subtitle"}," Choose a style for your generated images ",-1))]),s("div",Le,[s("div",Ne,[n[3]||(n[3]=s("label",{class:"widget__url-label"},"Service URL:",-1)),s("input",{type:"text",class:"widget__url-field",value:ae.value,disabled:te.value,onBlur:pa,onKeydown:I(va,["enter"])},null,40,Re),te.value?(i(),r("span",Be,[c(t,{name:"refresh",class:"spinning",small:""})])):o("v-if",!0)]),s("div",Te,[n[4]||(n[4]=s("label",{class:"widget__model-label"},"AI Model:",-1)),s("div",Ve,[(i(!0),r(S,null,C(k(Ie),e=>(i(),r("button",{key:e.id,class:d(["widget__model-btn",{"widget__model-btn--active":ee.value===e.id}]),onClick:a=>ee.value=e.id},u(e.name),11,Oe))),128))])])])])]),s("div",De,[(i(!0),r(S,null,C(X.value,e=>(i(),h(H,{key:e.id,style:w(e),"is-selected":Y.value===e.id,loading:Z.value===e.id,"radio-group-name":`style-select-${k(p)}`,"get-file-url":k(R),onSelect:we,onEdit:a=>function(e){me.value=e,ge.value=null,ce.value=`Style: ${e.name}`,ue.value=e.prompt,se.value=!0}(e),onRegenerate:ke},null,8,["style","is-selected","loading","radio-group-name","get-file-url","onEdit"]))),128))]),0===X.value.length?(i(),r("div",We,[c(t,{name:"palette"}),n[5]||(n[5]=s("p",null,"No styles available",-1)),s("span",null,"Add styles to the "+u(a.stylesCollection)+" collection",1)])):o("v-if",!0)],64)):"image-review"===Q.value?(i(),r(S,{key:4},[o(" Mode 2: Image Review "),s("div",qe,[s("div",He,[s("div",Ke,[s("h2",Je,[c(t,{name:"photo_library"}),n[6]||(n[6]=f(" Review Generated Images ",-1))]),n[7]||(n[7]=s("p",{class:"widget__subtitle"}," Select the best image for each placeholder ",-1))]),s("div",Qe,[s("div",Xe,[n[8]||(n[8]=s("label",{class:"widget__url-label"},"Service URL:",-1)),s("input",{type:"text",class:"widget__url-field",value:ae.value,disabled:te.value,onBlur:pa,onKeydown:I(va,["enter"])},null,40,Ye),te.value?(i(),r("span",Ze,[c(t,{name:"refresh",class:"spinning",small:""})])):o("v-if",!0)]),s("div",ea,[n[9]||(n[9]=s("label",{class:"widget__model-label"},"AI Model:",-1)),s("div",aa,[(i(!0),r(S,null,C(k(Ie),e=>(i(),r("button",{key:e.id,class:d(["widget__model-btn",{"widget__model-btn--active":ee.value===e.id}]),onClick:a=>ee.value=e.id},u(e.name),11,na))),128))])])])])]),s("div",ta,[(i(!0),r(S,null,C(le.value,e=>(i(),h(pe,{key:e.placeholder,item:e,loading:ie.value===e.placeholder,"get-file-url":k(R),onSelectImage:da,onEdit:a=>function(e){me.value=null,ge.value=e,ce.value=`Image: ${e.placeholder}`,ue.value=e.prompt1,se.value=!0}(e),onRegenerate:oa,onDelete:sa},null,8,["item","loading","get-file-url","onEdit"]))),128))]),0===le.value.length?(i(),r("div",la,[c(t,{name:"photo_library"}),n[10]||(n[10]=s("p",null,"No images to review",-1)),n[11]||(n[11]=s("span",null,"Generate images first",-1))])):o("v-if",!0)],64)):o("v-if",!0),o(" Footer Buttons "),re.value?o("v-if",!0):(i(),r("div",ia,[o(" Back button always visible in image-review mode "),"image-review"===Q.value?(i(),r("button",{key:0,class:"widget__back",onClick:ye},[c(t,{name:"arrow_back"}),n[12]||(n[12]=s("span",null,"Back",-1))])):o("v-if",!0),o(" View Images button in style-select mode when styles exist "),"style-select"===Q.value&&X.value.length>0?(i(),r("button",{key:1,class:"widget__view-images",onClick:xe},[c(t,{name:"photo_library"}),n[13]||(n[13]=s("span",null,"View Generated Images",-1))])):o("v-if",!0),s("button",{class:"widget__confirm",disabled:!fe.value,onClick:ga},[c(t,{name:"style-select"===Q.value?"arrow_forward":"check"},null,8,["name"]),s("span",null,u(_e.value),1)],8,ra)])),o(" Edit Modal "),c(Se,{"is-open":se.value,title:ce.value,prompt:ue.value,saving:ve.value,placeholder:"Enter prompt text...",onClose:ca,onSave:ua},null,8,["is-open","title","prompt","saving"])])}}});W("\n.image-style-widget[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n.widget__loading[data-v-0995de25],\n.widget__error[data-v-0995de25],\n.widget__empty[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 40px 20px;\n text-align: center;\n color: var(--theme--foreground-subdued, #666);\n}\n.widget__loading v-icon[data-v-0995de25],\n.widget__error v-icon[data-v-0995de25],\n.widget__empty v-icon[data-v-0995de25] {\n font-size: 32px;\n}\n.widget__error[data-v-0995de25] {\n color: var(--theme--danger, #f44336);\n}\n.widget__retry[data-v-0995de25] {\n padding: 8px 16px;\n border: 1px solid currentColor;\n border-radius: 4px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n}\n.widget__header[data-v-0995de25] {\n padding-bottom: 16px;\n border-bottom: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n}\n.widget__header-row[data-v-0995de25] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 16px;\n}\n.widget__header-text[data-v-0995de25] {\n flex: 1;\n}\n.widget__header-controls[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n gap: 10px;\n align-items: flex-end;\n}\n.widget__url-input[data-v-0995de25] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.widget__url-label[data-v-0995de25] {\n font-size: 12px;\n font-weight: 500;\n color: var(--theme--foreground-subdued, #666);\n white-space: nowrap;\n}\n.widget__url-field[data-v-0995de25] {\n width: 240px;\n padding: 6px 10px;\n border: 1px solid var(--theme--border-color, #ccc);\n border-radius: 4px;\n background: var(--theme--background, #fff);\n color: var(--theme--foreground, #333);\n font-size: 12px;\n font-family: monospace;\n}\n.widget__url-field[data-v-0995de25]:focus {\n outline: none;\n border-color: var(--theme--primary, #6644ff);\n}\n.widget__url-field[data-v-0995de25]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.widget__url-saving[data-v-0995de25] {\n display: flex;\n align-items: center;\n}\n.widget__model-selector[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n align-items: flex-end;\n}\n.widget__model-label[data-v-0995de25] {\n font-size: 12px;\n font-weight: 500;\n color: var(--theme--foreground-subdued, #666);\n}\n.widget__model-buttons[data-v-0995de25] {\n display: flex;\n gap: 4px;\n background: var(--theme--background-subdued, #f5f5f5);\n padding: 4px;\n border-radius: 6px;\n}\n.widget__model-btn[data-v-0995de25] {\n padding: 6px 12px;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--theme--foreground-subdued, #666);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.widget__model-btn[data-v-0995de25]:hover {\n color: var(--theme--foreground, #333);\n}\n.widget__model-btn--active[data-v-0995de25] {\n background: var(--theme--primary, #6644ff);\n color: #fff;\n}\n.widget__model-btn--active[data-v-0995de25]:hover {\n color: #fff;\n}\n.widget__title[data-v-0995de25] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.widget__subtitle[data-v-0995de25] {\n margin: 4px 0 0 0;\n font-size: 13px;\n color: var(--theme--foreground-subdued, #666);\n}\n.widget__list[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.widget__footer[data-v-0995de25] {\n display: flex;\n justify-content: flex-end;\n padding-top: 16px;\n border-top: 1px solid var(--theme--border-color-subdued, #e0e0e0);\n}\n.widget__confirm[data-v-0995de25] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n background: var(--theme--primary, #6644ff);\n color: #fff;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.widget__confirm[data-v-0995de25]:hover:not(:disabled) {\n background: var(--theme--primary-accent, #5533ee);\n}\n.widget__confirm[data-v-0995de25]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.widget__back[data-v-0995de25] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: 1px solid var(--theme--border-color, #ccc);\n border-radius: 6px;\n background: transparent;\n color: var(--theme--foreground, #333);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n margin-right: auto;\n}\n.widget__back[data-v-0995de25]:hover {\n background: var(--theme--background-subdued, #f0f0f0);\n}\n.widget__view-images[data-v-0995de25] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 20px;\n border: 2px solid var(--theme--primary, #6644ff);\n border-radius: 6px;\n background: transparent;\n color: var(--theme--primary, #6644ff);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n.widget__view-images[data-v-0995de25]:hover {\n background: var(--theme--primary-background, #f5f3ff);\n}\n.widget__processing[data-v-0995de25] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 60px 20px;\n text-align: center;\n}\n.widget__progress[data-v-0995de25] {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 16px;\n font-weight: 500;\n color: var(--theme--foreground, #333);\n}\n.widget__progress-bar[data-v-0995de25] {\n width: 100%;\n max-width: 300px;\n height: 6px;\n background: var(--theme--border-color-subdued, #e0e0e0);\n border-radius: 3px;\n overflow: hidden;\n}\n.widget__progress-fill[data-v-0995de25] {\n height: 100%;\n background: var(--theme--primary, #6644ff);\n border-radius: 3px;\n transition: width 0.3s ease;\n}\n.spinning[data-v-0995de25] {\n animation: spin-0995de25 1s linear infinite;\n}\n@keyframes spin-0995de25 {\nfrom { transform: rotate(0deg);\n}\nto { transform: rotate(360deg);\n}\n}\n.widget__empty p[data-v-0995de25] {\n margin: 0;\n font-weight: 600;\n color: var(--theme--foreground, #333);\n}\n.widget__empty span[data-v-0995de25] {\n font-size: 13px;\n}\n",{});var oa=a({id:"image-style-widget",name:"Image Style Widget",icon:"palette",description:"Visual style selector and image reviewer",component:q(da,[["__scopeId","data-v-0995de25"],["__file","interface.vue"]]),options:[{field:"mode",name:"Mode",type:"string",meta:{width:"half",interface:"select-dropdown",options:{choices:[{text:"Style Selection",value:"style-select"},{text:"Image Review",value:"image-review"}]}},schema:{default_value:"style-select"}},{field:"stylesCollection",name:"Styles Collection",type:"string",meta:{width:"half",interface:"input",note:"Collection containing style definitions (default: ImageStyles)"},schema:{default_value:"ImageStyles"}}],types:["json","string"]});export{oa as default};
|