@ecsplico/editor 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/image-studio/components/ImageStudioDialog.js +149 -86
- package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
- package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +4 -0
- package/dist/features/image-studio/components/ImageStudioLibraryPanel.js +186 -0
- package/dist/features/image-studio/components/ImageStudioLibraryPanel.js.map +1 -0
- package/dist/features/image-studio/components/ImageStudioLibraryTile.js +134 -0
- package/dist/features/image-studio/components/ImageStudioLibraryTile.js.map +1 -0
- package/dist/features/image-studio/index.d.ts +3 -2
- package/dist/features/image-studio/index.js +2 -2
- package/dist/features/image-studio/library.d.ts +35 -0
- package/dist/features/image-studio/react/ImageStudioDialog.js +45 -4
- package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
- package/dist/features/image-studio/react/ImageStudioLibraryPanel.js +141 -0
- package/dist/features/image-studio/react/ImageStudioLibraryPanel.js.map +1 -0
- package/dist/features/image-studio/react/ImageStudioLibraryTile.js +105 -0
- package/dist/features/image-studio/react/ImageStudioLibraryTile.js.map +1 -0
- package/dist/features/image-studio/react/store.d.ts +5 -0
- package/dist/features/image-studio/react/store.js.map +1 -1
- package/dist/features/image-studio/react.d.ts +3 -2
- package/dist/features/image-studio/react.js +2 -2
- package/dist/features/image-studio/react.js.map +1 -1
- package/dist/features/image-studio/state.d.ts +5 -0
- package/dist/features/image-studio/state.js +9 -1
- package/dist/features/image-studio/state.js.map +1 -1
- package/dist/features/image-studio/store.d.ts +5 -0
- package/dist/features/image-studio/vue.d.ts +3 -2
- package/dist/features/image-studio/vue.js +2 -2
- package/dist/features/image-studio/vue.js.map +1 -1
- package/dist/features/media/index.js +1 -1
- package/dist/features/media/react/AudioRecorderModal.js +1 -1
- package/dist/features/media/react.js +1 -1
- package/dist/i18n/de/image-studio.js +9 -0
- package/dist/i18n/de/image-studio.js.map +1 -1
- package/dist/i18n/de.d.ts +9 -0
- package/dist/i18n/en/image-studio.js +9 -0
- package/dist/i18n/en/image-studio.js.map +1 -1
- package/dist/integrations/composite-ai/transfer-transforms.js +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +5 -5
- package/dist/nuxt/module.d.ts +18 -0
- package/dist/presets/compose.js +1 -1
- package/dist/react/compose.js +1 -1
- package/dist/tree/react/use-tree-view.d.ts +3 -1
- package/dist/tree/react/use-tree-view.js +3 -1
- package/dist/tree/react/use-tree-view.js.map +1 -1
- package/dist/tree/tree-host.js +1 -1
- package/dist/tree/tree-model.js +12 -10
- package/dist/tree/tree-model.js.map +1 -1
- package/dist/tree/tree-view-model.d.ts +5 -0
- package/dist/tree/tree-view-model.js +1 -0
- package/dist/tree/tree-view-model.js.map +1 -1
- package/dist/tree/tree-view.d.ts +4 -2
- package/dist/tree/tree-view.js +4 -2
- package/dist/tree/tree-view.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +4 -4
- package/dist/vue/SimpleEditor.vue.d.ts +4 -4
- package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
- package/dist/vue/compose.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.2.0 - 2026-10-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `collapsedOutline: Store<string[]>` on `TreeViewModel` (`/tree`), and as `collapsedOutline` from `useTreeView()` (`/tree/vue`: a ref; `/tree/react`: the plain value): the keys of the closed outline rows (`outline:<block id>`). A host can set it to start a tree with rows closed.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- A closed outline row of the document tree stays closed for the session in every tree of the app, also in a tree mounted again (it was the state of one tree and lost when the tree unmounted).
|
|
12
|
+
|
|
13
|
+
## 4.1.0 - 2026-10-22
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Gallery tab of the Bild-Studio dialog (`/image-studio`, Vue and React): a store with `library?: ImageStudioLibrary` gets the tabs «Dieser Block» and «Galerie». The gallery searches (300 ms after the last keystroke), filters with one select per host facet (`ImageStudioLibraryFacet`; the first option is the default, values must be non-empty), lists `ImageStudioLibraryItem`s and offers «Als Bild verwenden», «Als Referenz», renaming (`rename`, optional) and removing (`remove`, optional; a rejection's message is shown and the tile stays). New types `ImageStudioLibrary`, `ImageStudioLibraryFacet`, `ImageStudioLibraryQuery`, `ImageStudioLibraryItem`. Without `library` the dialog is unchanged.
|
|
18
|
+
- `placeholderUrl?: string` on `ImageStudioStore` and `ImageStudioReactStore`: shown in the preview while no image is selected (instead of the «Noch kein Bild» text).
|
|
19
|
+
- `adoptImage(state, image)` reducer: makes an image the selection, adding it to the references unless the state already holds it as a reference or generation; unchanged when it is already selected.
|
|
20
|
+
|
|
3
21
|
## 4.0.0 - 2026-10-21
|
|
4
22
|
|
|
5
23
|
### Added
|
|
@@ -12,16 +12,16 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
|
12
12
|
view: import("vue").ShallowRef<EditorView | undefined, EditorView | undefined>;
|
|
13
13
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
14
|
"update:modelValue": (text: string) => any;
|
|
15
|
-
ready: (view: EditorView) => any;
|
|
16
15
|
save: (text: string) => any;
|
|
16
|
+
ready: (view: EditorView) => any;
|
|
17
17
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
18
18
|
"onUpdate:modelValue"?: ((text: string) => any) | undefined;
|
|
19
|
-
onReady?: ((view: EditorView) => any) | undefined;
|
|
20
19
|
onSave?: ((text: string) => any) | undefined;
|
|
20
|
+
onReady?: ((view: EditorView) => any) | undefined;
|
|
21
21
|
}>, {
|
|
22
|
-
modelValue: string;
|
|
23
22
|
editable: boolean;
|
|
24
23
|
wordWrap: boolean;
|
|
24
|
+
modelValue: string;
|
|
25
25
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
26
26
|
declare const _default: typeof __VLS_export;
|
|
27
27
|
//#endregion
|
|
@@ -33,16 +33,16 @@ type __VLS_Slots = {} & { [K in NonNullable<typeof __VLS_13>]?: (props: typeof _
|
|
|
33
33
|
};
|
|
34
34
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
35
35
|
"update:modelValue": (text: string) => any;
|
|
36
|
-
ready: (target: DocumentSurfaceTarget) => any;
|
|
37
36
|
save: (text: string) => any;
|
|
37
|
+
ready: (target: DocumentSurfaceTarget) => any;
|
|
38
38
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
39
39
|
"onUpdate:modelValue"?: ((text: string) => any) | undefined;
|
|
40
|
-
onReady?: ((target: DocumentSurfaceTarget) => any) | undefined;
|
|
41
40
|
onSave?: ((text: string) => any) | undefined;
|
|
41
|
+
onReady?: ((target: DocumentSurfaceTarget) => any) | undefined;
|
|
42
42
|
}>, {
|
|
43
|
-
modelValue: string;
|
|
44
43
|
editable: boolean;
|
|
45
44
|
wordWrap: boolean;
|
|
45
|
+
modelValue: string;
|
|
46
46
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
47
47
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
48
48
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { fetchErrorMessage } from "../../../core/fetch-error.js";
|
|
2
2
|
import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
|
|
3
3
|
import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
|
|
4
|
-
import { addGeneration, addReference, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "../state.js";
|
|
4
|
+
import { addGeneration, addReference, adoptImage, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "../state.js";
|
|
5
5
|
import _sfc_main$1 from "./ImageStudioChat.js";
|
|
6
|
-
import _sfc_main$2 from "./
|
|
7
|
-
import
|
|
6
|
+
import _sfc_main$2 from "./ImageStudioLibraryPanel.js";
|
|
7
|
+
import _sfc_main$3 from "./ImageStudioTile.js";
|
|
8
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, onBeforeUnmount, openBlock, ref, renderList, toDisplayString, unref, useId, useTemplateRef, vShow, watch, withCtx, withDirectives, withModifiers } from "vue";
|
|
8
9
|
//#region src/features/image-studio/components/ImageStudioDialog.vue
|
|
9
10
|
const _hoisted_1 = {
|
|
10
11
|
class: "flex flex-col gap-3",
|
|
@@ -20,39 +21,46 @@ const _hoisted_7 = {
|
|
|
20
21
|
class: "text-sm text-[var(--sk-muted)]"
|
|
21
22
|
};
|
|
22
23
|
const _hoisted_8 = { class: "flex min-h-0 flex-col gap-4 lg:overflow-y-auto" };
|
|
23
|
-
const _hoisted_9 =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const _hoisted_9 = {
|
|
25
|
+
key: 0,
|
|
26
|
+
class: "flex gap-2",
|
|
27
|
+
role: "tablist",
|
|
28
|
+
"data-image-studio-tabs": ""
|
|
29
|
+
};
|
|
30
|
+
const _hoisted_10 = ["data-dragging", "aria-labelledby"];
|
|
31
|
+
const _hoisted_11 = { class: "flex items-center justify-between gap-2" };
|
|
32
|
+
const _hoisted_12 = ["id"];
|
|
33
|
+
const _hoisted_13 = {
|
|
27
34
|
key: 0,
|
|
28
35
|
class: "text-xs text-[var(--sk-muted)]",
|
|
29
36
|
role: "status"
|
|
30
37
|
};
|
|
31
|
-
const
|
|
38
|
+
const _hoisted_14 = {
|
|
32
39
|
key: 2,
|
|
33
40
|
class: "grid grid-cols-2 gap-2 sm:grid-cols-3"
|
|
34
41
|
};
|
|
35
|
-
const
|
|
42
|
+
const _hoisted_15 = {
|
|
36
43
|
key: 3,
|
|
37
44
|
class: "text-sm text-[var(--sk-muted)]"
|
|
38
45
|
};
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
46
|
+
const _hoisted_16 = ["aria-labelledby"];
|
|
47
|
+
const _hoisted_17 = ["id"];
|
|
48
|
+
const _hoisted_18 = {
|
|
42
49
|
key: 0,
|
|
43
50
|
class: "grid grid-cols-2 gap-2 sm:grid-cols-3"
|
|
44
51
|
};
|
|
45
|
-
const
|
|
52
|
+
const _hoisted_19 = {
|
|
46
53
|
key: 1,
|
|
47
54
|
class: "text-sm text-[var(--sk-muted)]"
|
|
48
55
|
};
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
+
const _hoisted_20 = { class: "flex min-h-0 flex-col gap-3" };
|
|
57
|
+
const _hoisted_21 = ["aria-labelledby"];
|
|
58
|
+
const _hoisted_22 = ["id"];
|
|
59
|
+
const _hoisted_23 = { class: "flex aspect-square items-center justify-center overflow-hidden rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg-elevated)]" };
|
|
60
|
+
const _hoisted_24 = ["src", "alt"];
|
|
61
|
+
const _hoisted_25 = ["src"];
|
|
62
|
+
const _hoisted_26 = {
|
|
63
|
+
key: 2,
|
|
56
64
|
class: "text-sm text-[var(--sk-muted)]"
|
|
57
65
|
};
|
|
58
66
|
/**
|
|
@@ -60,6 +68,10 @@ const _hoisted_24 = {
|
|
|
60
68
|
* the block's image and the chat on the right. The dialog applies the reducers of `../state` to `store.state` and
|
|
61
69
|
* hands every result to `store.update`; the store only generates, uploads and persists (see `ImageStudioStore`).
|
|
62
70
|
*
|
|
71
|
+
* With `store.library` the middle column has two tabs: „Dieser Block“ (the above) and „Galerie“ (the host's images to
|
|
72
|
+
* use as the block's image, add as references, rename or delete). `store.placeholderUrl` is shown in the preview while
|
|
73
|
+
* no image is selected.
|
|
74
|
+
*
|
|
63
75
|
* The description is committed 500 ms after the last keystroke and at once on blur, on „Erzeugen“ and on close, so the
|
|
64
76
|
* host never builds a prompt from a stale text. Closing (or unmounting) aborts a running generation; an aborted
|
|
65
77
|
* generation adds no turn, a failed one adds a turn with the problem.
|
|
@@ -110,6 +122,20 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
110
122
|
const toggle = (id) => update(toggleImage(props.store.state.value, id));
|
|
111
123
|
const use = (id) => update(select(props.store.state.value, id));
|
|
112
124
|
const remove = (id) => update(removeImage(props.store.state.value, id));
|
|
125
|
+
const tab = ref("block");
|
|
126
|
+
const library = computed(() => props.store.library);
|
|
127
|
+
const plain = ({ id, url, title }) => ({
|
|
128
|
+
id,
|
|
129
|
+
url,
|
|
130
|
+
title,
|
|
131
|
+
active: false
|
|
132
|
+
});
|
|
133
|
+
const useFromLibrary = (item) => update(adoptImage(props.store.state.value, plain(item)));
|
|
134
|
+
const referenceFromLibrary = (item) => update(addReference(props.store.state.value, plain(item)));
|
|
135
|
+
function removedFromLibrary(id) {
|
|
136
|
+
const current = props.store.state.value;
|
|
137
|
+
if (current.references.some((image) => image.id === id) || current.generations.some((image) => image.id === id)) update(removeImage(current, id));
|
|
138
|
+
}
|
|
113
139
|
const fileInputRef = useTemplateRef("fileInput");
|
|
114
140
|
const uploading = ref(0);
|
|
115
141
|
const uploadProblem = ref();
|
|
@@ -187,6 +213,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
187
213
|
}
|
|
188
214
|
watch(() => props.open, (open) => {
|
|
189
215
|
if (open) return;
|
|
216
|
+
tab.value = "block";
|
|
190
217
|
commitDescription();
|
|
191
218
|
abortGeneration();
|
|
192
219
|
});
|
|
@@ -255,49 +282,100 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
255
282
|
}), 128))], 8, _hoisted_6)) : (openBlock(), createElementBlock("p", _hoisted_7, toDisplayString(unref(t)("imageStudio.dialog.blocks.empty")), 1))
|
|
256
283
|
], 8, _hoisted_3),
|
|
257
284
|
createCommentVNode(" Middle: references and previous generations. "),
|
|
258
|
-
createElementVNode("div", _hoisted_8, [
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
onDragenter: _cache[2] || (_cache[2] = withModifiers(($event) => dragging.value = true, ["prevent"])),
|
|
264
|
-
onDragover: _cache[3] || (_cache[3] = withModifiers(($event) => dragging.value = true, ["prevent"])),
|
|
265
|
-
onDragleave: _cache[4] || (_cache[4] = ($event) => dragging.value = false),
|
|
266
|
-
onDrop: withModifiers(onDrop, ["prevent"])
|
|
267
|
-
}, [
|
|
268
|
-
createElementVNode("div", _hoisted_10, [
|
|
269
|
-
createElementVNode("h3", {
|
|
270
|
-
id: `${unref(uid)}-references`,
|
|
271
|
-
class: "text-sm font-medium text-[var(--sk-fg-highlighted)]"
|
|
272
|
-
}, toDisplayString(unref(t)("imageStudio.dialog.references.heading")), 9, _hoisted_11),
|
|
273
|
-
createVNode(unref(UButton), {
|
|
274
|
-
label: unref(t)("imageStudio.dialog.references.upload"),
|
|
275
|
-
icon: "i-lucide-upload",
|
|
285
|
+
createElementVNode("div", _hoisted_8, [
|
|
286
|
+
library.value ? (openBlock(), createElementBlock("div", _hoisted_9, [(openBlock(), createElementBlock(Fragment, null, renderList(["block", "library"], (entry) => {
|
|
287
|
+
return createVNode(unref(UButton), {
|
|
288
|
+
key: entry,
|
|
289
|
+
label: unref(t)(`imageStudio.dialog.tabs.${entry}`),
|
|
276
290
|
size: "xs",
|
|
277
291
|
color: "neutral",
|
|
278
|
-
variant: "
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
"
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
uploading.value ? (openBlock(), createElementBlock("p", _hoisted_12, toDisplayString(unref(t)("imageStudio.dialog.references.uploading")), 1)) : createCommentVNode("v-if", true),
|
|
293
|
-
uploadProblem.value ? (openBlock(), createBlock(unref(UAlert), {
|
|
292
|
+
variant: tab.value === entry ? "solid" : "ghost",
|
|
293
|
+
role: "tab",
|
|
294
|
+
"aria-selected": tab.value === entry,
|
|
295
|
+
"data-image-studio-tab": entry,
|
|
296
|
+
onClick: ($event) => tab.value = entry
|
|
297
|
+
}, null, 8, [
|
|
298
|
+
"label",
|
|
299
|
+
"variant",
|
|
300
|
+
"aria-selected",
|
|
301
|
+
"data-image-studio-tab",
|
|
302
|
+
"onClick"
|
|
303
|
+
]);
|
|
304
|
+
}), 64))])) : createCommentVNode("v-if", true),
|
|
305
|
+
library.value && tab.value === "library" ? (openBlock(), createBlock(_sfc_main$2, {
|
|
294
306
|
key: 1,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
307
|
+
library: library.value,
|
|
308
|
+
state: state.value,
|
|
309
|
+
onUse: useFromLibrary,
|
|
310
|
+
onReference: referenceFromLibrary,
|
|
311
|
+
onRemoved: removedFromLibrary
|
|
312
|
+
}, null, 8, ["library", "state"])) : createCommentVNode("v-if", true),
|
|
313
|
+
withDirectives(createElementVNode("section", {
|
|
314
|
+
class: "flex flex-col gap-2 rounded-md border border-dashed border-transparent p-1 data-[dragging=true]:border-[var(--sk-primary)]",
|
|
315
|
+
"data-dragging": dragging.value,
|
|
316
|
+
"aria-labelledby": `${unref(uid)}-references`,
|
|
317
|
+
"data-image-studio-drop": "",
|
|
318
|
+
onDragenter: _cache[2] || (_cache[2] = withModifiers(($event) => dragging.value = true, ["prevent"])),
|
|
319
|
+
onDragover: _cache[3] || (_cache[3] = withModifiers(($event) => dragging.value = true, ["prevent"])),
|
|
320
|
+
onDragleave: _cache[4] || (_cache[4] = ($event) => dragging.value = false),
|
|
321
|
+
onDrop: withModifiers(onDrop, ["prevent"])
|
|
322
|
+
}, [
|
|
323
|
+
createElementVNode("div", _hoisted_11, [
|
|
324
|
+
createElementVNode("h3", {
|
|
325
|
+
id: `${unref(uid)}-references`,
|
|
326
|
+
class: "text-sm font-medium text-[var(--sk-fg-highlighted)]"
|
|
327
|
+
}, toDisplayString(unref(t)("imageStudio.dialog.references.heading")), 9, _hoisted_12),
|
|
328
|
+
createVNode(unref(UButton), {
|
|
329
|
+
label: unref(t)("imageStudio.dialog.references.upload"),
|
|
330
|
+
icon: "i-lucide-upload",
|
|
331
|
+
size: "xs",
|
|
332
|
+
color: "neutral",
|
|
333
|
+
variant: "outline",
|
|
334
|
+
loading: uploading.value > 0,
|
|
335
|
+
onClick: _cache[1] || (_cache[1] = ($event) => fileInputRef.value?.click())
|
|
336
|
+
}, null, 8, ["label", "loading"]),
|
|
337
|
+
createElementVNode("input", {
|
|
338
|
+
ref: "fileInput",
|
|
339
|
+
type: "file",
|
|
340
|
+
accept: "image/*",
|
|
341
|
+
multiple: "",
|
|
342
|
+
hidden: "",
|
|
343
|
+
"data-image-studio-file": "",
|
|
344
|
+
onChange: onFilesChosen
|
|
345
|
+
}, null, 544)
|
|
346
|
+
]),
|
|
347
|
+
uploading.value ? (openBlock(), createElementBlock("p", _hoisted_13, toDisplayString(unref(t)("imageStudio.dialog.references.uploading")), 1)) : createCommentVNode("v-if", true),
|
|
348
|
+
uploadProblem.value ? (openBlock(), createBlock(unref(UAlert), {
|
|
349
|
+
key: 1,
|
|
350
|
+
color: "error",
|
|
351
|
+
variant: "subtle",
|
|
352
|
+
title: uploadProblem.value
|
|
353
|
+
}, null, 8, ["title"])) : createCommentVNode("v-if", true),
|
|
354
|
+
state.value.references.length ? (openBlock(), createElementBlock("ul", _hoisted_14, [(openBlock(true), createElementBlock(Fragment, null, renderList(state.value.references, (image) => {
|
|
355
|
+
return openBlock(), createBlock(_sfc_main$3, {
|
|
356
|
+
key: image.id,
|
|
357
|
+
image,
|
|
358
|
+
selected: state.value.selected === image.id,
|
|
359
|
+
onToggle: ($event) => toggle(image.id),
|
|
360
|
+
onSelect: ($event) => use(image.id),
|
|
361
|
+
onRemove: ($event) => remove(image.id)
|
|
362
|
+
}, null, 8, [
|
|
363
|
+
"image",
|
|
364
|
+
"selected",
|
|
365
|
+
"onToggle",
|
|
366
|
+
"onSelect",
|
|
367
|
+
"onRemove"
|
|
368
|
+
]);
|
|
369
|
+
}), 128))])) : (openBlock(), createElementBlock("p", _hoisted_15, toDisplayString(unref(t)("imageStudio.dialog.references.drop")), 1))
|
|
370
|
+
], 40, _hoisted_10), [[vShow, tab.value === "block" || !library.value]]),
|
|
371
|
+
withDirectives(createElementVNode("section", {
|
|
372
|
+
class: "flex flex-col gap-2 p-1",
|
|
373
|
+
"aria-labelledby": `${unref(uid)}-generations`
|
|
374
|
+
}, [createElementVNode("h3", {
|
|
375
|
+
id: `${unref(uid)}-generations`,
|
|
376
|
+
class: "text-sm font-medium text-[var(--sk-fg-highlighted)]"
|
|
377
|
+
}, toDisplayString(unref(t)("imageStudio.dialog.generations.heading")), 9, _hoisted_17), state.value.generations.length ? (openBlock(), createElementBlock("ul", _hoisted_18, [(openBlock(true), createElementBlock(Fragment, null, renderList(state.value.generations, (image) => {
|
|
378
|
+
return openBlock(), createBlock(_sfc_main$3, {
|
|
301
379
|
key: image.id,
|
|
302
380
|
image,
|
|
303
381
|
selected: state.value.selected === image.id,
|
|
@@ -311,43 +389,28 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
311
389
|
"onSelect",
|
|
312
390
|
"onRemove"
|
|
313
391
|
]);
|
|
314
|
-
}), 128))])) : (openBlock(), createElementBlock("p",
|
|
315
|
-
]
|
|
316
|
-
class: "flex flex-col gap-2 p-1",
|
|
317
|
-
"aria-labelledby": `${unref(uid)}-generations`
|
|
318
|
-
}, [createElementVNode("h3", {
|
|
319
|
-
id: `${unref(uid)}-generations`,
|
|
320
|
-
class: "text-sm font-medium text-[var(--sk-fg-highlighted)]"
|
|
321
|
-
}, toDisplayString(unref(t)("imageStudio.dialog.generations.heading")), 9, _hoisted_16), state.value.generations.length ? (openBlock(), createElementBlock("ul", _hoisted_17, [(openBlock(true), createElementBlock(Fragment, null, renderList(state.value.generations, (image) => {
|
|
322
|
-
return openBlock(), createBlock(_sfc_main$2, {
|
|
323
|
-
key: image.id,
|
|
324
|
-
image,
|
|
325
|
-
selected: state.value.selected === image.id,
|
|
326
|
-
onToggle: ($event) => toggle(image.id),
|
|
327
|
-
onSelect: ($event) => use(image.id),
|
|
328
|
-
onRemove: ($event) => remove(image.id)
|
|
329
|
-
}, null, 8, [
|
|
330
|
-
"image",
|
|
331
|
-
"selected",
|
|
332
|
-
"onToggle",
|
|
333
|
-
"onSelect",
|
|
334
|
-
"onRemove"
|
|
335
|
-
]);
|
|
336
|
-
}), 128))])) : (openBlock(), createElementBlock("p", _hoisted_18, toDisplayString(unref(t)("imageStudio.dialog.generations.empty")), 1))], 8, _hoisted_15)]),
|
|
392
|
+
}), 128))])) : (openBlock(), createElementBlock("p", _hoisted_19, toDisplayString(unref(t)("imageStudio.dialog.generations.empty")), 1))], 8, _hoisted_16), [[vShow, tab.value === "block" || !library.value]])
|
|
393
|
+
]),
|
|
337
394
|
createCommentVNode(" Right: the block's image and the chat. "),
|
|
338
|
-
createElementVNode("div",
|
|
395
|
+
createElementVNode("div", _hoisted_20, [createElementVNode("section", {
|
|
339
396
|
class: "flex flex-col gap-2",
|
|
340
397
|
"aria-labelledby": `${unref(uid)}-preview`
|
|
341
398
|
}, [createElementVNode("h3", {
|
|
342
399
|
id: `${unref(uid)}-preview`,
|
|
343
400
|
class: "text-sm font-medium text-[var(--sk-fg-highlighted)]"
|
|
344
|
-
}, toDisplayString(unref(t)("imageStudio.dialog.preview.heading")), 9,
|
|
401
|
+
}, toDisplayString(unref(t)("imageStudio.dialog.preview.heading")), 9, _hoisted_22), createElementVNode("div", _hoisted_23, [picture.value ? (openBlock(), createElementBlock("img", {
|
|
345
402
|
key: 0,
|
|
346
403
|
src: picture.value.url,
|
|
347
404
|
alt: picture.value.title || unref(t)("imageStudio.dialog.tile.untitled"),
|
|
348
405
|
class: "size-full object-contain",
|
|
349
406
|
"data-image-studio-preview": ""
|
|
350
|
-
}, null, 8,
|
|
407
|
+
}, null, 8, _hoisted_24)) : __props.store.placeholderUrl ? (openBlock(), createElementBlock("img", {
|
|
408
|
+
key: 1,
|
|
409
|
+
src: __props.store.placeholderUrl,
|
|
410
|
+
alt: "",
|
|
411
|
+
class: "size-full object-contain opacity-60",
|
|
412
|
+
"data-image-studio-placeholder": ""
|
|
413
|
+
}, null, 8, _hoisted_25)) : (openBlock(), createElementBlock("p", _hoisted_26, toDisplayString(unref(t)("imageStudio.dialog.preview.empty")), 1))])], 8, _hoisted_21), createVNode(_sfc_main$1, {
|
|
351
414
|
turns: state.value.turns,
|
|
352
415
|
images: images.value,
|
|
353
416
|
running: running.value,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageStudioDialog.js","names":["_openBlock","_createBlock","_unref","_withCtx","_createElementVNode","_createCommentVNode","_toDisplayString","_createVNode","_createElementBlock","_Fragment","_renderList","_withModifiers","ImageStudioTile","ImageStudioChat"],"sources":["../../../../src/features/image-studio/components/ImageStudioDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, useId, useTemplateRef, watch } from 'vue'\nimport { fetchErrorMessage } from '../../../core'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport {\n addGeneration,\n addReference,\n type ImageStudioState,\n type ImageStudioTurn,\n removeImage,\n select,\n selectedImage,\n setDescription,\n toggleImage,\n togglePromptBlock,\n} from '../state'\nimport type { ImageStudioStore } from '../store'\nimport ImageStudioChat from './ImageStudioChat.vue'\nimport ImageStudioTile from './ImageStudioTile.vue'\n\n/**\n * The Bild-Studio: description and prompt blocks on the left, references and previous generations in the middle,\n * the block's image and the chat on the right. The dialog applies the reducers of `../state` to `store.state` and\n * hands every result to `store.update`; the store only generates, uploads and persists (see `ImageStudioStore`).\n *\n * The description is committed 500 ms after the last keystroke and at once on blur, on „Erzeugen“ and on close, so the\n * host never builds a prompt from a stale text. Closing (or unmounting) aborts a running generation; an aborted\n * generation adds no turn, a failed one adds a turn with the problem.\n */\nconst props = defineProps<{ open: boolean, store: ImageStudioStore, title?: string }>()\nconst emit = defineEmits<{ 'update:open': [open: boolean] }>()\n\nconst { Alert: UAlert, Button: UButton, Modal: UModal, Switch: USwitch, Textarea: UTextarea } = useUiKit()\nconst { t } = useEditorI18n()\n\nconst uid = useId()\nconst state = computed(() => props.store.state.value)\nconst promptBlocks = computed(() => props.store.promptBlocks.value)\nconst problem = computed(() => props.store.problem.value)\nconst images = computed(() => [...state.value.generations, ...state.value.references])\nconst picture = computed(() => selectedImage(state.value))\n\n// --- Description ----------------------------------------------------------------------------------------------------\n\nconst COMMIT_DELAY_MS = 500\nconst draft = ref(state.value.description)\n/** The description as last committed or received: the host's echo of our own commit must not overwrite a newer draft. */\nlet known = state.value.description\nlet commitTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction commitDescription() {\n clearTimeout(commitTimer)\n commitTimer = undefined\n if (draft.value === props.store.state.value.description) return\n known = draft.value\n props.store.update(setDescription(props.store.state.value, draft.value))\n}\n\nwatch(draft, (text) => {\n if (text === props.store.state.value.description) return\n clearTimeout(commitTimer)\n commitTimer = setTimeout(commitDescription, COMMIT_DELAY_MS)\n})\n// A change of the host (undo, another tab) shows unless the user has typed something that is not committed yet.\nwatch(() => state.value.description, (description) => {\n if (description === known) return\n known = description\n if (commitTimer === undefined) draft.value = description\n})\n\n// --- Reducers -------------------------------------------------------------------------------------------------------\n\nconst update = (next: ImageStudioState) => props.store.update(next)\nconst toggleBlock = (id: string) => update(togglePromptBlock(props.store.state.value, id))\nconst toggle = (id: string) => update(toggleImage(props.store.state.value, id))\nconst use = (id: string) => update(select(props.store.state.value, id))\nconst remove = (id: string) => update(removeImage(props.store.state.value, id))\n\n// --- References: upload and drop ------------------------------------------------------------------------------------\n\nconst fileInputRef = useTemplateRef<HTMLInputElement>('fileInput')\nconst uploading = ref(0)\nconst uploadProblem = ref<string>()\nconst dragging = ref(false)\n\nasync function upload(files: readonly File[]) {\n const pictures = files.filter(file => file.type.startsWith('image/'))\n if (!pictures.length) return\n uploadProblem.value = undefined\n uploading.value += pictures.length\n await Promise.all(pictures.map(async (file) => {\n try {\n const image = await props.store.upload(file)\n update(addReference(props.store.state.value, image))\n } catch (error) {\n uploadProblem.value = t('imageStudio.dialog.references.failed', { message: fetchErrorMessage(error) })\n } finally {\n uploading.value -= 1\n }\n }))\n}\n\nfunction onFilesChosen(event: Event) {\n const input = event.target as HTMLInputElement\n const files = [...(input.files ?? [])]\n input.value = '' // the same file can be chosen again\n void upload(files)\n}\n\nfunction onDrop(event: DragEvent) {\n dragging.value = false\n void upload([...(event.dataTransfer?.files ?? [])])\n}\n\n// --- Generation -----------------------------------------------------------------------------------------------------\n\nconst running = ref(false)\nlet controller: AbortController | undefined\n\nfunction abortGeneration() {\n controller?.abort()\n controller = undefined\n running.value = false\n}\n\nasync function generate(instruction: string) {\n if (controller) return\n commitDescription()\n const own = new AbortController()\n controller = own\n running.value = true\n const turn: ImageStudioTurn = { id: crypto.randomUUID(), instruction, imageId: null, createdAt: new Date().toISOString() }\n try {\n const image = await props.store.generate(instruction, own.signal)\n if (own.signal.aborted) return\n props.store.update(addGeneration(props.store.state.value, image, turn))\n } catch (error) {\n if (own.signal.aborted) return\n const current = props.store.state.value\n props.store.update({ ...current, turns: [...current.turns, { ...turn, problem: fetchErrorMessage(error) }] })\n } finally {\n if (controller === own) {\n controller = undefined\n running.value = false\n }\n }\n}\n\n// --- Closing --------------------------------------------------------------------------------------------------------\n\nfunction close() {\n commitDescription()\n abortGeneration()\n emit('update:open', false)\n}\n\nwatch(() => props.open, (open) => {\n if (open) return\n commitDescription()\n abortGeneration()\n})\n\nonBeforeUnmount(() => {\n commitDescription()\n abortGeneration()\n})\n</script>\n\n<template>\n <UModal\n :open=\"open\"\n :title=\"title ?? t('imageStudio.dialog.title')\"\n :ui=\"{ content: 'sm:w-[72rem] sm:max-w-[calc(100vw-2rem)]' }\"\n @update:open=\"(value: boolean) => { if (!value) close() }\"\n >\n <template #body>\n <div class=\"flex flex-col gap-3\" data-image-studio-dialog>\n <UAlert v-if=\"problem\" color=\"error\" variant=\"subtle\" :title=\"problem\" />\n <div class=\"grid gap-4 lg:h-[68vh] lg:grid-cols-[17rem_minmax(0,1fr)_minmax(0,22rem)]\">\n <!-- Left: what the picture shows. -->\n <section class=\"flex min-h-0 flex-col gap-3 lg:overflow-y-auto\" :aria-labelledby=\"`${uid}-description`\">\n <h3 :id=\"`${uid}-description`\" class=\"text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.description.heading') }}\n </h3>\n <UTextarea\n v-model=\"draft\"\n :rows=\"6\"\n :maxrows=\"14\"\n autoresize\n :placeholder=\"t('imageStudio.dialog.description.placeholder')\"\n :aria-label=\"t('imageStudio.dialog.description.heading')\"\n @blur=\"commitDescription\"\n />\n <h3 :id=\"`${uid}-blocks`\" class=\"mt-2 text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.blocks.heading') }}\n </h3>\n <ul v-if=\"promptBlocks.length\" class=\"flex flex-col gap-3\" :aria-labelledby=\"`${uid}-blocks`\">\n <li v-for=\"block in promptBlocks\" :key=\"block.id\">\n <USwitch\n :model-value=\"state.promptBlockIds.includes(block.id)\"\n :label=\"block.label\"\n :description=\"block.text\"\n @update:model-value=\"toggleBlock(block.id)\"\n />\n </li>\n </ul>\n <p v-else class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.blocks.empty') }}\n </p>\n </section>\n\n <!-- Middle: references and previous generations. -->\n <div class=\"flex min-h-0 flex-col gap-4 lg:overflow-y-auto\">\n <section\n class=\"flex flex-col gap-2 rounded-md border border-dashed border-transparent p-1 data-[dragging=true]:border-[var(--sk-primary)]\"\n :data-dragging=\"dragging\"\n :aria-labelledby=\"`${uid}-references`\"\n data-image-studio-drop\n @dragenter.prevent=\"dragging = true\"\n @dragover.prevent=\"dragging = true\"\n @dragleave=\"dragging = false\"\n @drop.prevent=\"onDrop\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <h3 :id=\"`${uid}-references`\" class=\"text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.references.heading') }}\n </h3>\n <UButton\n :label=\"t('imageStudio.dialog.references.upload')\"\n icon=\"i-lucide-upload\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"outline\"\n :loading=\"uploading > 0\"\n @click=\"fileInputRef?.click()\"\n />\n <input ref=\"fileInput\" type=\"file\" accept=\"image/*\" multiple hidden data-image-studio-file @change=\"onFilesChosen\">\n </div>\n <p v-if=\"uploading\" class=\"text-xs text-[var(--sk-muted)]\" role=\"status\">\n {{ t('imageStudio.dialog.references.uploading') }}\n </p>\n <UAlert v-if=\"uploadProblem\" color=\"error\" variant=\"subtle\" :title=\"uploadProblem\" />\n <ul v-if=\"state.references.length\" class=\"grid grid-cols-2 gap-2 sm:grid-cols-3\">\n <ImageStudioTile\n v-for=\"image in state.references\"\n :key=\"image.id\"\n :image=\"image\"\n :selected=\"state.selected === image.id\"\n @toggle=\"toggle(image.id)\"\n @select=\"use(image.id)\"\n @remove=\"remove(image.id)\"\n />\n </ul>\n <p v-else class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.references.drop') }}\n </p>\n </section>\n\n <section class=\"flex flex-col gap-2 p-1\" :aria-labelledby=\"`${uid}-generations`\">\n <h3 :id=\"`${uid}-generations`\" class=\"text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.generations.heading') }}\n </h3>\n <ul v-if=\"state.generations.length\" class=\"grid grid-cols-2 gap-2 sm:grid-cols-3\">\n <ImageStudioTile\n v-for=\"image in state.generations\"\n :key=\"image.id\"\n :image=\"image\"\n :selected=\"state.selected === image.id\"\n @toggle=\"toggle(image.id)\"\n @select=\"use(image.id)\"\n @remove=\"remove(image.id)\"\n />\n </ul>\n <p v-else class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.generations.empty') }}\n </p>\n </section>\n </div>\n\n <!-- Right: the block's image and the chat. -->\n <div class=\"flex min-h-0 flex-col gap-3\">\n <section class=\"flex flex-col gap-2\" :aria-labelledby=\"`${uid}-preview`\">\n <h3 :id=\"`${uid}-preview`\" class=\"text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.preview.heading') }}\n </h3>\n <div class=\"flex aspect-square items-center justify-center overflow-hidden rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg-elevated)]\">\n <img\n v-if=\"picture\"\n :src=\"picture.url\"\n :alt=\"picture.title || t('imageStudio.dialog.tile.untitled')\"\n class=\"size-full object-contain\"\n data-image-studio-preview\n >\n <p v-else class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.preview.empty') }}\n </p>\n </div>\n </section>\n <ImageStudioChat\n :turns=\"state.turns\"\n :images=\"images\"\n :running=\"running\"\n :empty-allowed=\"draft.trim() !== ''\"\n @send=\"generate\"\n />\n </div>\n </div>\n </div>\n </template>\n </UModal>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ImageStudioDialog.js","names":["_openBlock","_createBlock","_unref","_withCtx","_createElementVNode","_createCommentVNode","_toDisplayString","_createVNode","_createElementBlock","_Fragment","_renderList","ImageStudioLibraryPanel","_withDirectives","_withModifiers","ImageStudioTile","_vShow","ImageStudioChat"],"sources":["../../../../src/features/image-studio/components/ImageStudioDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, useId, useTemplateRef, watch } from 'vue'\nimport { fetchErrorMessage } from '../../../core'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport {\n addGeneration,\n addReference,\n adoptImage,\n type ImageStudioState,\n type ImageStudioTurn,\n removeImage,\n select,\n selectedImage,\n setDescription,\n toggleImage,\n togglePromptBlock,\n} from '../state'\nimport type { ImageStudioLibraryItem } from '../library'\nimport type { ImageStudioStore } from '../store'\nimport ImageStudioChat from './ImageStudioChat.vue'\nimport ImageStudioLibraryPanel from './ImageStudioLibraryPanel.vue'\nimport ImageStudioTile from './ImageStudioTile.vue'\n\n/**\n * The Bild-Studio: description and prompt blocks on the left, references and previous generations in the middle,\n * the block's image and the chat on the right. The dialog applies the reducers of `../state` to `store.state` and\n * hands every result to `store.update`; the store only generates, uploads and persists (see `ImageStudioStore`).\n *\n * With `store.library` the middle column has two tabs: „Dieser Block“ (the above) and „Galerie“ (the host's images to\n * use as the block's image, add as references, rename or delete). `store.placeholderUrl` is shown in the preview while\n * no image is selected.\n *\n * The description is committed 500 ms after the last keystroke and at once on blur, on „Erzeugen“ and on close, so the\n * host never builds a prompt from a stale text. Closing (or unmounting) aborts a running generation; an aborted\n * generation adds no turn, a failed one adds a turn with the problem.\n */\nconst props = defineProps<{ open: boolean, store: ImageStudioStore, title?: string }>()\nconst emit = defineEmits<{ 'update:open': [open: boolean] }>()\n\nconst { Alert: UAlert, Button: UButton, Modal: UModal, Switch: USwitch, Textarea: UTextarea } = useUiKit()\nconst { t } = useEditorI18n()\n\nconst uid = useId()\nconst state = computed(() => props.store.state.value)\nconst promptBlocks = computed(() => props.store.promptBlocks.value)\nconst problem = computed(() => props.store.problem.value)\nconst images = computed(() => [...state.value.generations, ...state.value.references])\nconst picture = computed(() => selectedImage(state.value))\n\n// --- Description ----------------------------------------------------------------------------------------------------\n\nconst COMMIT_DELAY_MS = 500\nconst draft = ref(state.value.description)\n/** The description as last committed or received: the host's echo of our own commit must not overwrite a newer draft. */\nlet known = state.value.description\nlet commitTimer: ReturnType<typeof setTimeout> | undefined\n\nfunction commitDescription() {\n clearTimeout(commitTimer)\n commitTimer = undefined\n if (draft.value === props.store.state.value.description) return\n known = draft.value\n props.store.update(setDescription(props.store.state.value, draft.value))\n}\n\nwatch(draft, (text) => {\n if (text === props.store.state.value.description) return\n clearTimeout(commitTimer)\n commitTimer = setTimeout(commitDescription, COMMIT_DELAY_MS)\n})\n// A change of the host (undo, another tab) shows unless the user has typed something that is not committed yet.\nwatch(() => state.value.description, (description) => {\n if (description === known) return\n known = description\n if (commitTimer === undefined) draft.value = description\n})\n\n// --- Reducers -------------------------------------------------------------------------------------------------------\n\nconst update = (next: ImageStudioState) => props.store.update(next)\nconst toggleBlock = (id: string) => update(togglePromptBlock(props.store.state.value, id))\nconst toggle = (id: string) => update(toggleImage(props.store.state.value, id))\nconst use = (id: string) => update(select(props.store.state.value, id))\nconst remove = (id: string) => update(removeImage(props.store.state.value, id))\n\n// --- Gallery --------------------------------------------------------------------------------------------------------\n\nconst tab = ref<'block' | 'library'>('block')\nconst library = computed(() => props.store.library)\nconst plain = ({ id, url, title }: ImageStudioLibraryItem) => ({ id, url, title, active: false })\nconst useFromLibrary = (item: ImageStudioLibraryItem) => update(adoptImage(props.store.state.value, plain(item)))\nconst referenceFromLibrary = (item: ImageStudioLibraryItem) => update(addReference(props.store.state.value, plain(item)))\nfunction removedFromLibrary(id: string) {\n const current = props.store.state.value\n if (current.references.some(image => image.id === id) || current.generations.some(image => image.id === id)) update(removeImage(current, id))\n}\n\n// --- References: upload and drop ------------------------------------------------------------------------------------\n\nconst fileInputRef = useTemplateRef<HTMLInputElement>('fileInput')\nconst uploading = ref(0)\nconst uploadProblem = ref<string>()\nconst dragging = ref(false)\n\nasync function upload(files: readonly File[]) {\n const pictures = files.filter(file => file.type.startsWith('image/'))\n if (!pictures.length) return\n uploadProblem.value = undefined\n uploading.value += pictures.length\n await Promise.all(pictures.map(async (file) => {\n try {\n const image = await props.store.upload(file)\n update(addReference(props.store.state.value, image))\n } catch (error) {\n uploadProblem.value = t('imageStudio.dialog.references.failed', { message: fetchErrorMessage(error) })\n } finally {\n uploading.value -= 1\n }\n }))\n}\n\nfunction onFilesChosen(event: Event) {\n const input = event.target as HTMLInputElement\n const files = [...(input.files ?? [])]\n input.value = '' // the same file can be chosen again\n void upload(files)\n}\n\nfunction onDrop(event: DragEvent) {\n dragging.value = false\n void upload([...(event.dataTransfer?.files ?? [])])\n}\n\n// --- Generation -----------------------------------------------------------------------------------------------------\n\nconst running = ref(false)\nlet controller: AbortController | undefined\n\nfunction abortGeneration() {\n controller?.abort()\n controller = undefined\n running.value = false\n}\n\nasync function generate(instruction: string) {\n if (controller) return\n commitDescription()\n const own = new AbortController()\n controller = own\n running.value = true\n const turn: ImageStudioTurn = { id: crypto.randomUUID(), instruction, imageId: null, createdAt: new Date().toISOString() }\n try {\n const image = await props.store.generate(instruction, own.signal)\n if (own.signal.aborted) return\n props.store.update(addGeneration(props.store.state.value, image, turn))\n } catch (error) {\n if (own.signal.aborted) return\n const current = props.store.state.value\n props.store.update({ ...current, turns: [...current.turns, { ...turn, problem: fetchErrorMessage(error) }] })\n } finally {\n if (controller === own) {\n controller = undefined\n running.value = false\n }\n }\n}\n\n// --- Closing --------------------------------------------------------------------------------------------------------\n\nfunction close() {\n commitDescription()\n abortGeneration()\n emit('update:open', false)\n}\n\nwatch(() => props.open, (open) => {\n if (open) return\n tab.value = 'block'\n commitDescription()\n abortGeneration()\n})\n\nonBeforeUnmount(() => {\n commitDescription()\n abortGeneration()\n})\n</script>\n\n<template>\n <UModal\n :open=\"open\"\n :title=\"title ?? t('imageStudio.dialog.title')\"\n :ui=\"{ content: 'sm:w-[72rem] sm:max-w-[calc(100vw-2rem)]' }\"\n @update:open=\"(value: boolean) => { if (!value) close() }\"\n >\n <template #body>\n <div class=\"flex flex-col gap-3\" data-image-studio-dialog>\n <UAlert v-if=\"problem\" color=\"error\" variant=\"subtle\" :title=\"problem\" />\n <div class=\"grid gap-4 lg:h-[68vh] lg:grid-cols-[17rem_minmax(0,1fr)_minmax(0,22rem)]\">\n <!-- Left: what the picture shows. -->\n <section class=\"flex min-h-0 flex-col gap-3 lg:overflow-y-auto\" :aria-labelledby=\"`${uid}-description`\">\n <h3 :id=\"`${uid}-description`\" class=\"text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.description.heading') }}\n </h3>\n <UTextarea\n v-model=\"draft\"\n :rows=\"6\"\n :maxrows=\"14\"\n autoresize\n :placeholder=\"t('imageStudio.dialog.description.placeholder')\"\n :aria-label=\"t('imageStudio.dialog.description.heading')\"\n @blur=\"commitDescription\"\n />\n <h3 :id=\"`${uid}-blocks`\" class=\"mt-2 text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.blocks.heading') }}\n </h3>\n <ul v-if=\"promptBlocks.length\" class=\"flex flex-col gap-3\" :aria-labelledby=\"`${uid}-blocks`\">\n <li v-for=\"block in promptBlocks\" :key=\"block.id\">\n <USwitch\n :model-value=\"state.promptBlockIds.includes(block.id)\"\n :label=\"block.label\"\n :description=\"block.text\"\n @update:model-value=\"toggleBlock(block.id)\"\n />\n </li>\n </ul>\n <p v-else class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.blocks.empty') }}\n </p>\n </section>\n\n <!-- Middle: references and previous generations. -->\n <div class=\"flex min-h-0 flex-col gap-4 lg:overflow-y-auto\">\n <div v-if=\"library\" class=\"flex gap-2\" role=\"tablist\" data-image-studio-tabs>\n <UButton\n v-for=\"entry in (['block', 'library'] as const)\"\n :key=\"entry\"\n :label=\"t(`imageStudio.dialog.tabs.${entry}`)\"\n size=\"xs\"\n color=\"neutral\"\n :variant=\"tab === entry ? 'solid' : 'ghost'\"\n role=\"tab\"\n :aria-selected=\"tab === entry\"\n :data-image-studio-tab=\"entry\"\n @click=\"tab = entry\"\n />\n </div>\n <ImageStudioLibraryPanel\n v-if=\"library && tab === 'library'\"\n :library=\"library\"\n :state=\"state\"\n @use=\"useFromLibrary\"\n @reference=\"referenceFromLibrary\"\n @removed=\"removedFromLibrary\"\n />\n <section\n v-show=\"tab === 'block' || !library\"\n class=\"flex flex-col gap-2 rounded-md border border-dashed border-transparent p-1 data-[dragging=true]:border-[var(--sk-primary)]\"\n :data-dragging=\"dragging\"\n :aria-labelledby=\"`${uid}-references`\"\n data-image-studio-drop\n @dragenter.prevent=\"dragging = true\"\n @dragover.prevent=\"dragging = true\"\n @dragleave=\"dragging = false\"\n @drop.prevent=\"onDrop\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <h3 :id=\"`${uid}-references`\" class=\"text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.references.heading') }}\n </h3>\n <UButton\n :label=\"t('imageStudio.dialog.references.upload')\"\n icon=\"i-lucide-upload\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"outline\"\n :loading=\"uploading > 0\"\n @click=\"fileInputRef?.click()\"\n />\n <input ref=\"fileInput\" type=\"file\" accept=\"image/*\" multiple hidden data-image-studio-file @change=\"onFilesChosen\">\n </div>\n <p v-if=\"uploading\" class=\"text-xs text-[var(--sk-muted)]\" role=\"status\">\n {{ t('imageStudio.dialog.references.uploading') }}\n </p>\n <UAlert v-if=\"uploadProblem\" color=\"error\" variant=\"subtle\" :title=\"uploadProblem\" />\n <ul v-if=\"state.references.length\" class=\"grid grid-cols-2 gap-2 sm:grid-cols-3\">\n <ImageStudioTile\n v-for=\"image in state.references\"\n :key=\"image.id\"\n :image=\"image\"\n :selected=\"state.selected === image.id\"\n @toggle=\"toggle(image.id)\"\n @select=\"use(image.id)\"\n @remove=\"remove(image.id)\"\n />\n </ul>\n <p v-else class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.references.drop') }}\n </p>\n </section>\n\n <section v-show=\"tab === 'block' || !library\" class=\"flex flex-col gap-2 p-1\" :aria-labelledby=\"`${uid}-generations`\">\n <h3 :id=\"`${uid}-generations`\" class=\"text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.generations.heading') }}\n </h3>\n <ul v-if=\"state.generations.length\" class=\"grid grid-cols-2 gap-2 sm:grid-cols-3\">\n <ImageStudioTile\n v-for=\"image in state.generations\"\n :key=\"image.id\"\n :image=\"image\"\n :selected=\"state.selected === image.id\"\n @toggle=\"toggle(image.id)\"\n @select=\"use(image.id)\"\n @remove=\"remove(image.id)\"\n />\n </ul>\n <p v-else class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.generations.empty') }}\n </p>\n </section>\n </div>\n\n <!-- Right: the block's image and the chat. -->\n <div class=\"flex min-h-0 flex-col gap-3\">\n <section class=\"flex flex-col gap-2\" :aria-labelledby=\"`${uid}-preview`\">\n <h3 :id=\"`${uid}-preview`\" class=\"text-sm font-medium text-[var(--sk-fg-highlighted)]\">\n {{ t('imageStudio.dialog.preview.heading') }}\n </h3>\n <div class=\"flex aspect-square items-center justify-center overflow-hidden rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg-elevated)]\">\n <img\n v-if=\"picture\"\n :src=\"picture.url\"\n :alt=\"picture.title || t('imageStudio.dialog.tile.untitled')\"\n class=\"size-full object-contain\"\n data-image-studio-preview\n >\n <img\n v-else-if=\"store.placeholderUrl\"\n :src=\"store.placeholderUrl\"\n alt=\"\"\n class=\"size-full object-contain opacity-60\"\n data-image-studio-placeholder\n >\n <p v-else class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.preview.empty') }}\n </p>\n </div>\n </section>\n <ImageStudioChat\n :turns=\"state.turns\"\n :images=\"images\"\n :running=\"running\"\n :empty-allowed=\"draft.trim() !== ''\"\n @send=\"generate\"\n />\n </div>\n </div>\n </div>\n </template>\n </UModal>\n</template>\n"],"mappings":"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"}
|
|
@@ -5,6 +5,10 @@ import { ImageStudioStore } from "../store.js";
|
|
|
5
5
|
* the block's image and the chat on the right. The dialog applies the reducers of `../state` to `store.state` and
|
|
6
6
|
* hands every result to `store.update`; the store only generates, uploads and persists (see `ImageStudioStore`).
|
|
7
7
|
*
|
|
8
|
+
* With `store.library` the middle column has two tabs: „Dieser Block“ (the above) and „Galerie“ (the host's images to
|
|
9
|
+
* use as the block's image, add as references, rename or delete). `store.placeholderUrl` is shown in the preview while
|
|
10
|
+
* no image is selected.
|
|
11
|
+
*
|
|
8
12
|
* The description is committed 500 ms after the last keystroke and at once on blur, on „Erzeugen“ and on close, so the
|
|
9
13
|
* host never builds a prompt from a stale text. Closing (or unmounting) aborts a running generation; an aborted
|
|
10
14
|
* generation adds no turn, a failed one adds a turn with the problem.
|