@ecsplico/editor 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/code/CodeSurface.vue.d.ts +3 -3
  3. package/dist/document/DocumentSurface.vue.d.ts +3 -3
  4. package/dist/features/image-studio/components/ImageStudioDialog.js +149 -86
  5. package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
  6. package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +4 -0
  7. package/dist/features/image-studio/components/ImageStudioLibraryPanel.js +186 -0
  8. package/dist/features/image-studio/components/ImageStudioLibraryPanel.js.map +1 -0
  9. package/dist/features/image-studio/components/ImageStudioLibraryTile.js +134 -0
  10. package/dist/features/image-studio/components/ImageStudioLibraryTile.js.map +1 -0
  11. package/dist/features/image-studio/index.d.ts +3 -2
  12. package/dist/features/image-studio/index.js +2 -2
  13. package/dist/features/image-studio/library.d.ts +35 -0
  14. package/dist/features/image-studio/react/ImageStudioDialog.js +45 -4
  15. package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
  16. package/dist/features/image-studio/react/ImageStudioLibraryPanel.js +141 -0
  17. package/dist/features/image-studio/react/ImageStudioLibraryPanel.js.map +1 -0
  18. package/dist/features/image-studio/react/ImageStudioLibraryTile.js +105 -0
  19. package/dist/features/image-studio/react/ImageStudioLibraryTile.js.map +1 -0
  20. package/dist/features/image-studio/react/store.d.ts +5 -0
  21. package/dist/features/image-studio/react/store.js.map +1 -1
  22. package/dist/features/image-studio/react.d.ts +3 -2
  23. package/dist/features/image-studio/react.js +2 -2
  24. package/dist/features/image-studio/react.js.map +1 -1
  25. package/dist/features/image-studio/state.d.ts +5 -0
  26. package/dist/features/image-studio/state.js +9 -1
  27. package/dist/features/image-studio/state.js.map +1 -1
  28. package/dist/features/image-studio/store.d.ts +5 -0
  29. package/dist/features/image-studio/vue.d.ts +3 -2
  30. package/dist/features/image-studio/vue.js +2 -2
  31. package/dist/features/image-studio/vue.js.map +1 -1
  32. package/dist/features/media/index.js +1 -1
  33. package/dist/features/media/react/AudioRecorderModal.js +1 -1
  34. package/dist/features/media/react.js +1 -1
  35. package/dist/i18n/de/image-studio.js +9 -0
  36. package/dist/i18n/de/image-studio.js.map +1 -1
  37. package/dist/i18n/de.d.ts +9 -0
  38. package/dist/i18n/en/image-studio.js +9 -0
  39. package/dist/i18n/en/image-studio.js.map +1 -1
  40. package/dist/integrations/composite-ai/transfer-transforms.js +1 -1
  41. package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
  42. package/dist/nuxt/module.d.ts +18 -0
  43. package/dist/presets/compose.js +1 -1
  44. package/dist/react/compose.js +1 -1
  45. package/dist/server/handlers/ai.d.ts +2 -2
  46. package/dist/tree/tree-host.js +1 -1
  47. package/dist/vue/AdvancedEditor.vue.d.ts +4 -4
  48. package/dist/vue/SimpleEditor.vue.d.ts +4 -4
  49. package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
  50. package/dist/vue/compose.js +1 -1
  51. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.1.0 - 2026-10-22
4
+
5
+ ### Added
6
+
7
+ - 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.
8
+ - `placeholderUrl?: string` on `ImageStudioStore` and `ImageStudioReactStore`: shown in the preview while no image is selected (instead of the «Noch kein Bild» text).
9
+ - `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.
10
+
3
11
  ## 4.0.0 - 2026-10-21
4
12
 
5
13
  ### Added
@@ -11,16 +11,16 @@ type __VLS_Props = {
11
11
  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
- "update:modelValue": (text: string) => any;
15
14
  ready: (view: EditorView) => any;
15
+ "update:modelValue": (text: string) => any;
16
16
  save: (text: string) => any;
17
17
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
18
- "onUpdate:modelValue"?: ((text: string) => any) | undefined;
19
18
  onReady?: ((view: EditorView) => any) | undefined;
19
+ "onUpdate:modelValue"?: ((text: string) => any) | undefined;
20
20
  onSave?: ((text: string) => any) | undefined;
21
21
  }>, {
22
- modelValue: string;
23
22
  editable: boolean;
23
+ modelValue: string;
24
24
  wordWrap: boolean;
25
25
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
26
  declare const _default: typeof __VLS_export;
@@ -32,16 +32,16 @@ type __VLS_Slots = {} & { [K in NonNullable<typeof __VLS_13>]?: (props: typeof _
32
32
  binary?: (props: typeof __VLS_26) => any;
33
33
  };
34
34
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
35
- "update:modelValue": (text: string) => any;
36
35
  ready: (target: DocumentSurfaceTarget) => any;
36
+ "update:modelValue": (text: string) => any;
37
37
  save: (text: string) => any;
38
38
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
39
- "onUpdate:modelValue"?: ((text: string) => any) | undefined;
40
39
  onReady?: ((target: DocumentSurfaceTarget) => any) | undefined;
40
+ "onUpdate:modelValue"?: ((text: string) => any) | undefined;
41
41
  onSave?: ((text: string) => any) | undefined;
42
42
  }>, {
43
- modelValue: string;
44
43
  editable: boolean;
44
+ modelValue: string;
45
45
  wordWrap: boolean;
46
46
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
47
47
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -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 "./ImageStudioTile.js";
7
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, onBeforeUnmount, openBlock, ref, renderList, toDisplayString, unref, useId, useTemplateRef, watch, withCtx, withModifiers } from "vue";
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 = ["data-dragging", "aria-labelledby"];
24
- const _hoisted_10 = { class: "flex items-center justify-between gap-2" };
25
- const _hoisted_11 = ["id"];
26
- const _hoisted_12 = {
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 _hoisted_13 = {
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 _hoisted_14 = {
42
+ const _hoisted_15 = {
36
43
  key: 3,
37
44
  class: "text-sm text-[var(--sk-muted)]"
38
45
  };
39
- const _hoisted_15 = ["aria-labelledby"];
40
- const _hoisted_16 = ["id"];
41
- const _hoisted_17 = {
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 _hoisted_18 = {
52
+ const _hoisted_19 = {
46
53
  key: 1,
47
54
  class: "text-sm text-[var(--sk-muted)]"
48
55
  };
49
- const _hoisted_19 = { class: "flex min-h-0 flex-col gap-3" };
50
- const _hoisted_20 = ["aria-labelledby"];
51
- const _hoisted_21 = ["id"];
52
- const _hoisted_22 = { class: "flex aspect-square items-center justify-center overflow-hidden rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg-elevated)]" };
53
- const _hoisted_23 = ["src", "alt"];
54
- const _hoisted_24 = {
55
- key: 1,
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, [createElementVNode("section", {
259
- class: "flex flex-col gap-2 rounded-md border border-dashed border-transparent p-1 data-[dragging=true]:border-[var(--sk-primary)]",
260
- "data-dragging": dragging.value,
261
- "aria-labelledby": `${unref(uid)}-references`,
262
- "data-image-studio-drop": "",
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: "outline",
279
- loading: uploading.value > 0,
280
- onClick: _cache[1] || (_cache[1] = ($event) => fileInputRef.value?.click())
281
- }, null, 8, ["label", "loading"]),
282
- createElementVNode("input", {
283
- ref: "fileInput",
284
- type: "file",
285
- accept: "image/*",
286
- multiple: "",
287
- hidden: "",
288
- "data-image-studio-file": "",
289
- onChange: onFilesChosen
290
- }, null, 544)
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
- color: "error",
296
- variant: "subtle",
297
- title: uploadProblem.value
298
- }, null, 8, ["title"])) : createCommentVNode("v-if", true),
299
- state.value.references.length ? (openBlock(), createElementBlock("ul", _hoisted_13, [(openBlock(true), createElementBlock(Fragment, null, renderList(state.value.references, (image) => {
300
- return openBlock(), createBlock(_sfc_main$2, {
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", _hoisted_14, toDisplayString(unref(t)("imageStudio.dialog.references.drop")), 1))
315
- ], 40, _hoisted_9), createElementVNode("section", {
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", _hoisted_19, [createElementVNode("section", {
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, _hoisted_21), createElementVNode("div", _hoisted_22, [picture.value ? (openBlock(), createElementBlock("img", {
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, _hoisted_23)) : (openBlock(), createElementBlock("p", _hoisted_24, toDisplayString(unref(t)("imageStudio.dialog.preview.empty")), 1))])], 8, _hoisted_20), createVNode(_sfc_main$1, {
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.