@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
@@ -0,0 +1,186 @@
1
+ import { fetchErrorMessage } from "../../../core/fetch-error.js";
2
+ import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
3
+ import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
4
+ import _sfc_main$1 from "./ImageStudioLibraryTile.js";
5
+ import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, onBeforeUnmount, onMounted, openBlock, reactive, ref, renderList, toDisplayString, unref, watch } from "vue";
6
+ //#region src/features/image-studio/components/ImageStudioLibraryPanel.vue
7
+ const _hoisted_1 = {
8
+ class: "flex flex-col gap-3 p-1",
9
+ "data-image-studio-library": ""
10
+ };
11
+ const _hoisted_2 = { class: "flex flex-wrap items-center gap-2" };
12
+ const _hoisted_3 = {
13
+ key: 1,
14
+ class: "text-sm text-[var(--sk-muted)]",
15
+ role: "status"
16
+ };
17
+ const _hoisted_4 = {
18
+ key: 2,
19
+ class: "text-sm text-[var(--sk-muted)]"
20
+ };
21
+ const _hoisted_5 = {
22
+ key: 3,
23
+ class: "grid grid-cols-2 gap-2 sm:grid-cols-3"
24
+ };
25
+ /**
26
+ * The gallery tab of the Bild-Studio: search, one select per facet of the host and the host's images as tiles. The
27
+ * list is read on mount (the facets with their first option chosen), after a facet change and 300 ms after the last
28
+ * keystroke of the search; a newer read aborts the running one. Picking, referencing, renaming and deleting go to the
29
+ * host (`library`) or up to the dialog (`use`, `reference`, `removed`), which applies the reducers.
30
+ */
31
+ const SEARCH_DELAY_MS = 300;
32
+ const _sfc_main = /*@__PURE__*/ defineComponent({
33
+ __name: "ImageStudioLibraryPanel",
34
+ props: {
35
+ library: {},
36
+ state: {}
37
+ },
38
+ emits: [
39
+ "use",
40
+ "reference",
41
+ "removed"
42
+ ],
43
+ setup(__props, { emit: __emit }) {
44
+ const props = __props;
45
+ const emit = __emit;
46
+ const { Alert: UAlert, Input: UInput, Select: USelect } = useUiKit();
47
+ const { t } = useEditorI18n();
48
+ const facets = props.library.facets();
49
+ const values = reactive(Object.fromEntries(facets.map((facet) => [facet.id, facet.options[0]?.value ?? ""])));
50
+ const search = ref("");
51
+ const items = ref([]);
52
+ const busy = ref(false);
53
+ const problem = ref();
54
+ let controller;
55
+ let searchTimer;
56
+ async function load() {
57
+ clearTimeout(searchTimer);
58
+ searchTimer = void 0;
59
+ controller?.abort();
60
+ const own = new AbortController();
61
+ controller = own;
62
+ busy.value = true;
63
+ problem.value = void 0;
64
+ try {
65
+ const found = await props.library.list({
66
+ search: search.value,
67
+ facets: { ...values }
68
+ }, own.signal);
69
+ if (own.signal.aborted) return;
70
+ items.value = found;
71
+ } catch (error) {
72
+ if (own.signal.aborted) return;
73
+ problem.value = t("imageStudio.dialog.library.failed", { message: fetchErrorMessage(error) });
74
+ } finally {
75
+ if (controller === own) {
76
+ controller = void 0;
77
+ busy.value = false;
78
+ }
79
+ }
80
+ }
81
+ onMounted(() => void load());
82
+ watch(values, () => void load());
83
+ watch(search, () => {
84
+ clearTimeout(searchTimer);
85
+ searchTimer = setTimeout(() => void load(), SEARCH_DELAY_MS);
86
+ });
87
+ onBeforeUnmount(() => {
88
+ clearTimeout(searchTimer);
89
+ controller?.abort();
90
+ });
91
+ const held = (id) => props.state.references.some((image) => image.id === id) || props.state.generations.some((image) => image.id === id);
92
+ async function rename(item, title) {
93
+ if (!props.library.rename) return;
94
+ problem.value = void 0;
95
+ try {
96
+ const renamed = await props.library.rename(item.id, title);
97
+ items.value = items.value.map((other) => other.id === item.id ? renamed : other);
98
+ } catch (error) {
99
+ problem.value = fetchErrorMessage(error);
100
+ }
101
+ }
102
+ async function remove(item) {
103
+ if (!props.library.remove) return;
104
+ problem.value = void 0;
105
+ try {
106
+ await props.library.remove(item.id);
107
+ items.value = items.value.filter((other) => other.id !== item.id);
108
+ emit("removed", item.id);
109
+ } catch (error) {
110
+ problem.value = fetchErrorMessage(error);
111
+ }
112
+ }
113
+ return (_ctx, _cache) => {
114
+ return openBlock(), createElementBlock("section", _hoisted_1, [
115
+ createElementVNode("div", _hoisted_2, [createVNode(unref(UInput), {
116
+ modelValue: search.value,
117
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
118
+ class: "min-w-40 flex-1",
119
+ icon: "i-lucide-search",
120
+ size: "xs",
121
+ placeholder: unref(t)("imageStudio.dialog.library.search"),
122
+ "aria-label": unref(t)("imageStudio.dialog.library.search"),
123
+ "data-image-studio-library-search": ""
124
+ }, null, 8, [
125
+ "modelValue",
126
+ "placeholder",
127
+ "aria-label"
128
+ ]), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(facets), (facet) => {
129
+ return openBlock(), createBlock(unref(USelect), {
130
+ key: facet.id,
131
+ modelValue: values[facet.id],
132
+ "onUpdate:modelValue": ($event) => values[facet.id] = $event,
133
+ items: facet.options.map((option) => ({
134
+ label: option.label,
135
+ value: option.value
136
+ })),
137
+ size: "xs",
138
+ "aria-label": facet.label,
139
+ "data-image-studio-facet": facet.id
140
+ }, null, 8, [
141
+ "modelValue",
142
+ "onUpdate:modelValue",
143
+ "items",
144
+ "aria-label",
145
+ "data-image-studio-facet"
146
+ ]);
147
+ }), 128))]),
148
+ problem.value ? (openBlock(), createBlock(unref(UAlert), {
149
+ key: 0,
150
+ color: "error",
151
+ variant: "subtle",
152
+ title: problem.value
153
+ }, null, 8, ["title"])) : createCommentVNode("v-if", true),
154
+ busy.value ? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString(unref(t)("imageStudio.dialog.library.loading")), 1)) : !items.value.length && !problem.value ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString(unref(t)("imageStudio.dialog.library.empty")), 1)) : createCommentVNode("v-if", true),
155
+ items.value.length ? (openBlock(), createElementBlock("ul", _hoisted_5, [(openBlock(true), createElementBlock(Fragment, null, renderList(items.value, (item) => {
156
+ return openBlock(), createBlock(_sfc_main$1, {
157
+ key: item.id,
158
+ item,
159
+ selected: __props.state.selected === item.id,
160
+ held: held(item.id),
161
+ renamable: Boolean(__props.library.rename),
162
+ removable: Boolean(__props.library.remove),
163
+ onUse: ($event) => emit("use", item),
164
+ onReference: ($event) => emit("reference", item),
165
+ onRename: (title) => rename(item, title),
166
+ onRemove: ($event) => remove(item)
167
+ }, null, 8, [
168
+ "item",
169
+ "selected",
170
+ "held",
171
+ "renamable",
172
+ "removable",
173
+ "onUse",
174
+ "onReference",
175
+ "onRename",
176
+ "onRemove"
177
+ ]);
178
+ }), 128))])) : createCommentVNode("v-if", true)
179
+ ]);
180
+ };
181
+ }
182
+ });
183
+ //#endregion
184
+ export { _sfc_main as default };
185
+
186
+ //# sourceMappingURL=ImageStudioLibraryPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageStudioLibraryPanel.js","names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_Fragment","_renderList","_createBlock","_createCommentVNode","_toDisplayString","ImageStudioLibraryTile"],"sources":["../../../../src/features/image-studio/components/ImageStudioLibraryPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'\nimport { fetchErrorMessage } from '../../../core'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { ImageStudioLibrary, ImageStudioLibraryItem } from '../library'\nimport type { ImageStudioState } from '../state'\nimport ImageStudioLibraryTile from './ImageStudioLibraryTile.vue'\n\n/**\n * The gallery tab of the Bild-Studio: search, one select per facet of the host and the host's images as tiles. The\n * list is read on mount (the facets with their first option chosen), after a facet change and 300 ms after the last\n * keystroke of the search; a newer read aborts the running one. Picking, referencing, renaming and deleting go to the\n * host (`library`) or up to the dialog (`use`, `reference`, `removed`), which applies the reducers.\n */\nconst props = defineProps<{ library: ImageStudioLibrary, state: ImageStudioState }>()\nconst emit = defineEmits<{ use: [item: ImageStudioLibraryItem], reference: [item: ImageStudioLibraryItem], removed: [id: string] }>()\n\nconst { Alert: UAlert, Input: UInput, Select: USelect } = useUiKit()\nconst { t } = useEditorI18n()\n\nconst SEARCH_DELAY_MS = 300\n\nconst facets = props.library.facets()\nconst values = reactive<Record<string, string>>(Object.fromEntries(facets.map(facet => [facet.id, facet.options[0]?.value ?? ''])))\nconst search = ref('')\nconst items = ref<readonly ImageStudioLibraryItem[]>([])\nconst busy = ref(false)\nconst problem = ref<string>()\n\nlet controller: AbortController | undefined\nlet searchTimer: ReturnType<typeof setTimeout> | undefined\n\nasync function load() {\n clearTimeout(searchTimer)\n searchTimer = undefined\n controller?.abort()\n const own = new AbortController()\n controller = own\n busy.value = true\n problem.value = undefined\n try {\n const found = await props.library.list({ search: search.value, facets: { ...values } }, own.signal)\n if (own.signal.aborted) return\n items.value = found\n } catch (error) {\n if (own.signal.aborted) return\n problem.value = t('imageStudio.dialog.library.failed', { message: fetchErrorMessage(error) })\n } finally {\n if (controller === own) {\n controller = undefined\n busy.value = false\n }\n }\n}\n\nonMounted(() => void load())\nwatch(values, () => void load())\nwatch(search, () => {\n clearTimeout(searchTimer)\n searchTimer = setTimeout(() => void load(), SEARCH_DELAY_MS)\n})\nonBeforeUnmount(() => {\n clearTimeout(searchTimer)\n controller?.abort()\n})\n\nconst held = (id: string) => props.state.references.some(image => image.id === id) || props.state.generations.some(image => image.id === id)\n\nasync function rename(item: ImageStudioLibraryItem, title: string) {\n if (!props.library.rename) return\n problem.value = undefined\n try {\n const renamed = await props.library.rename(item.id, title)\n items.value = items.value.map(other => other.id === item.id ? renamed : other)\n } catch (error) {\n problem.value = fetchErrorMessage(error)\n }\n}\n\nasync function remove(item: ImageStudioLibraryItem) {\n if (!props.library.remove) return\n problem.value = undefined\n try {\n await props.library.remove(item.id)\n items.value = items.value.filter(other => other.id !== item.id)\n emit('removed', item.id)\n } catch (error) {\n problem.value = fetchErrorMessage(error)\n }\n}\n</script>\n\n<template>\n <section class=\"flex flex-col gap-3 p-1\" data-image-studio-library>\n <div class=\"flex flex-wrap items-center gap-2\">\n <UInput\n v-model=\"search\"\n class=\"min-w-40 flex-1\"\n icon=\"i-lucide-search\"\n size=\"xs\"\n :placeholder=\"t('imageStudio.dialog.library.search')\"\n :aria-label=\"t('imageStudio.dialog.library.search')\"\n data-image-studio-library-search\n />\n <USelect\n v-for=\"facet in facets\"\n :key=\"facet.id\"\n v-model=\"values[facet.id]\"\n :items=\"facet.options.map(option => ({ label: option.label, value: option.value }))\"\n size=\"xs\"\n :aria-label=\"facet.label\"\n :data-image-studio-facet=\"facet.id\"\n />\n </div>\n <UAlert v-if=\"problem\" color=\"error\" variant=\"subtle\" :title=\"problem\" />\n <p v-if=\"busy\" class=\"text-sm text-[var(--sk-muted)]\" role=\"status\">\n {{ t('imageStudio.dialog.library.loading') }}\n </p>\n <p v-else-if=\"!items.length && !problem\" class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('imageStudio.dialog.library.empty') }}\n </p>\n <ul v-if=\"items.length\" class=\"grid grid-cols-2 gap-2 sm:grid-cols-3\">\n <ImageStudioLibraryTile\n v-for=\"item in items\"\n :key=\"item.id\"\n :item=\"item\"\n :selected=\"state.selected === item.id\"\n :held=\"held(item.id)\"\n :renamable=\"Boolean(library.rename)\"\n :removable=\"Boolean(library.remove)\"\n @use=\"emit('use', item)\"\n @reference=\"emit('reference', item)\"\n @rename=\"title => rename(item, title)\"\n @remove=\"remove(item)\"\n />\n </ul>\n </section>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
2
+ import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
3
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, openBlock, ref, toDisplayString, unref, withKeys, withModifiers } from "vue";
4
+ //#region src/features/image-studio/components/ImageStudioLibraryTile.vue
5
+ const _hoisted_1 = ["data-selected"];
6
+ const _hoisted_2 = { class: "block overflow-hidden rounded bg-[var(--sk-bg-elevated)]" };
7
+ const _hoisted_3 = ["src", "alt"];
8
+ const _hoisted_4 = { class: "flex min-w-0 flex-col" };
9
+ const _hoisted_5 = ["title"];
10
+ const _hoisted_6 = ["title"];
11
+ const _hoisted_7 = { class: "flex flex-wrap items-center justify-between gap-1" };
12
+ const _hoisted_8 = { class: "flex items-center" };
13
+ /**
14
+ * One image of the host's gallery. „Als Bild verwenden“ makes it the block's image, „Als Referenz“ adds it to the
15
+ * references (disabled once the block holds it), the pencil renames it in place (Enter commits, Escape or blur cancels)
16
+ * and the trash asks the host to delete it. The host's `subtitle` shows under the title.
17
+ */
18
+ const _sfc_main = /*@__PURE__*/ defineComponent({
19
+ __name: "ImageStudioLibraryTile",
20
+ props: {
21
+ item: {},
22
+ selected: { type: Boolean },
23
+ held: { type: Boolean },
24
+ renamable: { type: Boolean },
25
+ removable: { type: Boolean }
26
+ },
27
+ emits: [
28
+ "use",
29
+ "reference",
30
+ "rename",
31
+ "remove"
32
+ ],
33
+ setup(__props, { emit: __emit }) {
34
+ const props = __props;
35
+ const emit = __emit;
36
+ const { Button: UButton, Input: UInput } = useUiKit();
37
+ const { t } = useEditorI18n();
38
+ const title = computed(() => props.item.title || t("imageStudio.dialog.tile.untitled"));
39
+ const editing = ref(false);
40
+ const draft = ref("");
41
+ function startRename() {
42
+ draft.value = props.item.title;
43
+ editing.value = true;
44
+ }
45
+ function commitRename() {
46
+ if (!editing.value) return;
47
+ editing.value = false;
48
+ const next = draft.value.trim();
49
+ if (next !== "" && next !== props.item.title) emit("rename", next);
50
+ }
51
+ return (_ctx, _cache) => {
52
+ return openBlock(), createElementBlock("li", {
53
+ class: "flex flex-col gap-1.5 rounded-md border border-[var(--sk-border)] p-1.5 data-[selected=true]:border-[var(--sk-primary)]",
54
+ "data-selected": __props.selected,
55
+ "data-image-studio-library-tile": ""
56
+ }, [
57
+ createElementVNode("div", _hoisted_2, [createElementVNode("img", {
58
+ src: __props.item.url,
59
+ alt: title.value,
60
+ class: "aspect-square w-full object-cover",
61
+ draggable: "false"
62
+ }, null, 8, _hoisted_3)]),
63
+ createElementVNode("div", _hoisted_4, [editing.value ? (openBlock(), createBlock(unref(UInput), {
64
+ key: 0,
65
+ modelValue: draft.value,
66
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => draft.value = $event),
67
+ size: "xs",
68
+ autofocus: "",
69
+ "aria-label": unref(t)("imageStudio.dialog.library.rename"),
70
+ "data-image-studio-library-rename": "",
71
+ onKeydown: [withKeys(withModifiers(commitRename, ["prevent"]), ["enter"]), _cache[1] || (_cache[1] = withKeys(withModifiers(($event) => editing.value = false, ["prevent", "stop"]), ["esc"]))],
72
+ onBlur: _cache[2] || (_cache[2] = ($event) => editing.value = false)
73
+ }, null, 8, [
74
+ "modelValue",
75
+ "aria-label",
76
+ "onKeydown"
77
+ ])) : (openBlock(), createElementBlock("span", {
78
+ key: 1,
79
+ class: "truncate text-xs font-medium text-[var(--sk-fg-highlighted)]",
80
+ title: title.value
81
+ }, toDisplayString(title.value), 9, _hoisted_5)), __props.item.subtitle ? (openBlock(), createElementBlock("span", {
82
+ key: 2,
83
+ class: "truncate text-xs text-[var(--sk-muted)]",
84
+ title: __props.item.subtitle
85
+ }, toDisplayString(__props.item.subtitle), 9, _hoisted_6)) : createCommentVNode("v-if", true)]),
86
+ createElementVNode("div", _hoisted_7, [
87
+ createVNode(unref(UButton), {
88
+ label: unref(t)("imageStudio.dialog.tile.use"),
89
+ icon: __props.selected ? "i-lucide-check" : void 0,
90
+ size: "xs",
91
+ color: "primary",
92
+ variant: __props.selected ? "solid" : "outline",
93
+ "aria-pressed": __props.selected,
94
+ onClick: _cache[3] || (_cache[3] = ($event) => emit("use"))
95
+ }, null, 8, [
96
+ "label",
97
+ "icon",
98
+ "variant",
99
+ "aria-pressed"
100
+ ]),
101
+ createVNode(unref(UButton), {
102
+ label: unref(t)("imageStudio.dialog.library.reference"),
103
+ size: "xs",
104
+ color: "neutral",
105
+ variant: "outline",
106
+ disabled: __props.held,
107
+ onClick: _cache[4] || (_cache[4] = ($event) => emit("reference"))
108
+ }, null, 8, ["label", "disabled"]),
109
+ createElementVNode("span", _hoisted_8, [__props.renamable ? (openBlock(), createBlock(unref(UButton), {
110
+ key: 0,
111
+ icon: "i-lucide-pencil",
112
+ size: "xs",
113
+ color: "neutral",
114
+ variant: "ghost",
115
+ "aria-label": unref(t)("imageStudio.dialog.library.rename"),
116
+ onClick: startRename
117
+ }, null, 8, ["aria-label"])) : createCommentVNode("v-if", true), __props.removable ? (openBlock(), createBlock(unref(UButton), {
118
+ key: 1,
119
+ icon: "i-lucide-trash-2",
120
+ size: "xs",
121
+ color: "neutral",
122
+ variant: "ghost",
123
+ "aria-label": unref(t)("imageStudio.dialog.library.remove"),
124
+ onClick: _cache[5] || (_cache[5] = ($event) => emit("remove"))
125
+ }, null, 8, ["aria-label"])) : createCommentVNode("v-if", true)])
126
+ ])
127
+ ], 8, _hoisted_1);
128
+ };
129
+ }
130
+ });
131
+ //#endregion
132
+ export { _sfc_main as default };
133
+
134
+ //# sourceMappingURL=ImageStudioLibraryTile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageStudioLibraryTile.js","names":["_openBlock","_createElementBlock","_createElementVNode","_createBlock","_unref","_withKeys","_withModifiers","_toDisplayString","_createCommentVNode","_createVNode"],"sources":["../../../../src/features/image-studio/components/ImageStudioLibraryTile.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { ImageStudioLibraryItem } from '../library'\n\n/**\n * One image of the host's gallery. „Als Bild verwenden“ makes it the block's image, „Als Referenz“ adds it to the\n * references (disabled once the block holds it), the pencil renames it in place (Enter commits, Escape or blur cancels)\n * and the trash asks the host to delete it. The host's `subtitle` shows under the title.\n */\nconst props = defineProps<{ item: ImageStudioLibraryItem, selected: boolean, held: boolean, renamable: boolean, removable: boolean }>()\nconst emit = defineEmits<{ use: [], reference: [], rename: [title: string], remove: [] }>()\n\nconst { Button: UButton, Input: UInput } = useUiKit()\nconst { t } = useEditorI18n()\n\nconst title = computed(() => props.item.title || t('imageStudio.dialog.tile.untitled'))\n\nconst editing = ref(false)\nconst draft = ref('')\n\nfunction startRename() {\n draft.value = props.item.title\n editing.value = true\n}\n\nfunction commitRename() {\n if (!editing.value) return\n editing.value = false\n const next = draft.value.trim()\n if (next !== '' && next !== props.item.title) emit('rename', next)\n}\n</script>\n\n<template>\n <li\n class=\"flex flex-col gap-1.5 rounded-md border border-[var(--sk-border)] p-1.5 data-[selected=true]:border-[var(--sk-primary)]\"\n :data-selected=\"selected\"\n data-image-studio-library-tile\n >\n <div class=\"block overflow-hidden rounded bg-[var(--sk-bg-elevated)]\">\n <img :src=\"item.url\" :alt=\"title\" class=\"aspect-square w-full object-cover\" draggable=\"false\">\n </div>\n <div class=\"flex min-w-0 flex-col\">\n <UInput\n v-if=\"editing\"\n v-model=\"draft\"\n size=\"xs\"\n autofocus\n :aria-label=\"t('imageStudio.dialog.library.rename')\"\n data-image-studio-library-rename\n @keydown.enter.prevent=\"commitRename\"\n @keydown.esc.prevent.stop=\"editing = false\"\n @blur=\"editing = false\"\n />\n <span v-else class=\"truncate text-xs font-medium text-[var(--sk-fg-highlighted)]\" :title=\"title\">{{ title }}</span>\n <span v-if=\"item.subtitle\" class=\"truncate text-xs text-[var(--sk-muted)]\" :title=\"item.subtitle\">{{ item.subtitle }}</span>\n </div>\n <div class=\"flex flex-wrap items-center justify-between gap-1\">\n <UButton\n :label=\"t('imageStudio.dialog.tile.use')\"\n :icon=\"selected ? 'i-lucide-check' : undefined\"\n size=\"xs\"\n color=\"primary\"\n :variant=\"selected ? 'solid' : 'outline'\"\n :aria-pressed=\"selected\"\n @click=\"emit('use')\"\n />\n <UButton\n :label=\"t('imageStudio.dialog.library.reference')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"outline\"\n :disabled=\"held\"\n @click=\"emit('reference')\"\n />\n <span class=\"flex items-center\">\n <UButton\n v-if=\"renamable\"\n icon=\"i-lucide-pencil\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :aria-label=\"t('imageStudio.dialog.library.rename')\"\n @click=\"startRename\"\n />\n <UButton\n v-if=\"removable\"\n icon=\"i-lucide-trash-2\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :aria-label=\"t('imageStudio.dialog.library.remove')\"\n @click=\"emit('remove')\"\n />\n </span>\n </div>\n </li>\n</template>\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { ImageStudioImage, ImageStudioPromptBlock } from "../../core/backend-types.js";
2
2
  import { IMAGE_STUDIO_OPEN, ImageStudioUi, imageStudioFeatureCore } from "./feature.js";
3
- import { EMPTY_IMAGE_STUDIO_STATE, ImageStudioState, ImageStudioTurn, addGeneration, addReference, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "./state.js";
3
+ import { EMPTY_IMAGE_STUDIO_STATE, ImageStudioState, ImageStudioTurn, addGeneration, addReference, adoptImage, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "./state.js";
4
4
  import { ImageStudioAttrs, ImageStudioNode, imageStudioSchemaExtensions } from "./schema.js";
5
- export { EMPTY_IMAGE_STUDIO_STATE, IMAGE_STUDIO_OPEN, type ImageStudioAttrs, type ImageStudioImage, ImageStudioNode, type ImageStudioPromptBlock, type ImageStudioState, type ImageStudioTurn, type ImageStudioUi, addGeneration, addReference, imageStudioFeatureCore, imageStudioSchemaExtensions, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock };
5
+ import { ImageStudioLibrary, ImageStudioLibraryFacet, ImageStudioLibraryItem, ImageStudioLibraryQuery } from "./library.js";
6
+ export { EMPTY_IMAGE_STUDIO_STATE, IMAGE_STUDIO_OPEN, type ImageStudioAttrs, type ImageStudioImage, type ImageStudioLibrary, type ImageStudioLibraryFacet, type ImageStudioLibraryItem, type ImageStudioLibraryQuery, ImageStudioNode, type ImageStudioPromptBlock, type ImageStudioState, type ImageStudioTurn, type ImageStudioUi, addGeneration, addReference, adoptImage, imageStudioFeatureCore, imageStudioSchemaExtensions, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock };
@@ -1,4 +1,4 @@
1
- import { EMPTY_IMAGE_STUDIO_STATE, addGeneration, addReference, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "./state.js";
1
+ import { EMPTY_IMAGE_STUDIO_STATE, addGeneration, addReference, adoptImage, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "./state.js";
2
2
  import { ImageStudioNode, imageStudioSchemaExtensions } from "./schema.js";
3
3
  import { IMAGE_STUDIO_OPEN, imageStudioFeatureCore } from "./feature.js";
4
- export { EMPTY_IMAGE_STUDIO_STATE, IMAGE_STUDIO_OPEN, ImageStudioNode, addGeneration, addReference, imageStudioFeatureCore, imageStudioSchemaExtensions, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock };
4
+ export { EMPTY_IMAGE_STUDIO_STATE, IMAGE_STUDIO_OPEN, ImageStudioNode, addGeneration, addReference, adoptImage, imageStudioFeatureCore, imageStudioSchemaExtensions, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock };
@@ -0,0 +1,35 @@
1
+ import { ImageStudioImage } from "../../core/backend-types.js";
2
+ import "./state.js";
3
+ //#region src/features/image-studio/library.d.ts
4
+ /**
5
+ * One filter of the gallery (a Kapitel select, say); the dialog renders it as a select. Option values must be
6
+ * non-empty: select primitives reserve the empty string, so an «all» option needs a value of its own (`'alle'`).
7
+ */
8
+ export interface ImageStudioLibraryFacet {
9
+ readonly id: string;
10
+ readonly label: string;
11
+ readonly options: readonly {
12
+ readonly value: string;
13
+ readonly label: string;
14
+ }[];
15
+ }
16
+ /** What the gallery shows: the search text and the chosen option (`value`) of each facet, by facet id. */
17
+ export interface ImageStudioLibraryQuery {
18
+ readonly search: string;
19
+ readonly facets: Readonly<Record<string, string>>;
20
+ }
21
+ /** An image of the gallery; `subtitle` is a host line under the title (origin, usage, Kapitel). */
22
+ export interface ImageStudioLibraryItem extends ImageStudioImage {
23
+ readonly subtitle?: string;
24
+ }
25
+ export interface ImageStudioLibrary {
26
+ /** The host's filter facets; the first option of each is the default. Read each time the gallery tab opens. */
27
+ facets: () => readonly ImageStudioLibraryFacet[];
28
+ list: (query: ImageStudioLibraryQuery, signal: AbortSignal) => Promise<readonly ImageStudioLibraryItem[]>;
29
+ /** Resolves with the renamed item; absent = no renaming. */
30
+ rename?: (id: string, title: string) => Promise<ImageStudioLibraryItem>;
31
+ /** Rejects with an Error whose message the dialog shows (asset in use, say); absent = no deleting. */
32
+ remove?: (id: string) => Promise<void>;
33
+ }
34
+ //#endregion
35
+ //# sourceMappingURL=library.d.ts.map
@@ -7,11 +7,12 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../../../react
7
7
  import { Textarea } from "../../../react/ui/textarea.js";
8
8
  import { Alert, AlertTitle } from "../../../react/ui/alert.js";
9
9
  import { Switch } from "../../../react/ui/switch.js";
10
- import { addGeneration, addReference, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "../state.js";
10
+ import { addGeneration, addReference, adoptImage, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "../state.js";
11
11
  import { ImageStudioChat } from "./ImageStudioChat.js";
12
+ import { ImageStudioLibraryPanel } from "./ImageStudioLibraryPanel.js";
12
13
  import { ImageStudioTile } from "./ImageStudioTile.js";
13
14
  import { useEffect, useId, useRef, useState } from "react";
14
- import { jsx, jsxs } from "react/jsx-runtime";
15
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
15
16
  //#region src/features/image-studio/react/ImageStudioDialog.tsx
16
17
  const COMMIT_DELAY_MS = 500;
17
18
  /**
@@ -139,6 +140,8 @@ function ImageStudioDialog({ open, onOpenChange, store, title }) {
139
140
  }
140
141
  }
141
142
  }
143
+ const { library } = store;
144
+ const [tab, setTab] = useState("block");
142
145
  function close() {
143
146
  commitDescription();
144
147
  abortGeneration();
@@ -146,6 +149,7 @@ function ImageStudioDialog({ open, onOpenChange, store, title }) {
146
149
  }
147
150
  useEffect(() => {
148
151
  if (open) return;
152
+ setTab("block");
149
153
  commitDescription();
150
154
  abortGeneration();
151
155
  }, [open]);
@@ -154,6 +158,13 @@ function ImageStudioDialog({ open, onOpenChange, store, title }) {
154
158
  abortGeneration();
155
159
  }, []);
156
160
  const heading = "text-sm font-medium text-foreground";
161
+ /** A gallery item as the state holds it: without the host's `subtitle`, not active. */
162
+ const pick = ({ id, url, title }) => ({
163
+ id,
164
+ url,
165
+ title,
166
+ active: false
167
+ });
157
168
  return /* @__PURE__ */ jsx(Dialog, {
158
169
  open,
159
170
  onOpenChange: (value) => {
@@ -162,6 +173,9 @@ function ImageStudioDialog({ open, onOpenChange, store, title }) {
162
173
  children: /* @__PURE__ */ jsxs(DialogContent, {
163
174
  className: "max-h-[calc(100vh-2rem)] overflow-y-auto sm:max-w-[min(72rem,calc(100vw-2rem))]",
164
175
  "aria-describedby": void 0,
176
+ onEscapeKeyDown: (event) => {
177
+ if (event.target instanceof Element && event.target.closest("[data-image-studio-library-rename]")) event.preventDefault();
178
+ },
165
179
  children: [/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: title ?? t("imageStudio.dialog.title") }) }), /* @__PURE__ */ jsxs("div", {
166
180
  className: "flex flex-col gap-3",
167
181
  "data-image-studio-dialog": true,
@@ -226,7 +240,29 @@ function ImageStudioDialog({ open, onOpenChange, store, title }) {
226
240
  }),
227
241
  /* @__PURE__ */ jsxs("div", {
228
242
  className: "flex min-h-0 flex-col gap-4 lg:overflow-y-auto",
229
- children: [/* @__PURE__ */ jsxs("section", {
243
+ children: [library && /* @__PURE__ */ jsx("div", {
244
+ className: "flex gap-1",
245
+ role: "tablist",
246
+ "data-image-studio-tabs": true,
247
+ children: ["block", "library"].map((name) => /* @__PURE__ */ jsx(Button, {
248
+ size: "sm",
249
+ variant: tab === name ? "default" : "ghost",
250
+ role: "tab",
251
+ "aria-selected": tab === name,
252
+ "data-image-studio-tab": name,
253
+ onClick: () => setTab(name),
254
+ children: t(name === "block" ? "imageStudio.dialog.tabs.block" : "imageStudio.dialog.tabs.library")
255
+ }, name))
256
+ }), library && tab === "library" ? /* @__PURE__ */ jsx(ImageStudioLibraryPanel, {
257
+ library,
258
+ state,
259
+ onUse: (item) => apply(adoptImage(latest.current, pick(item))),
260
+ onReference: (item) => apply(addReference(latest.current, pick(item))),
261
+ onRemoved: (id) => {
262
+ const current = latest.current;
263
+ if (current.references.some((image) => image.id === id) || current.generations.some((image) => image.id === id)) apply(removeImage(current, id));
264
+ }
265
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("section", {
230
266
  className: "flex flex-col gap-2 rounded-md border border-dashed border-transparent p-1 data-[dragging=true]:border-primary",
231
267
  "data-dragging": dragging,
232
268
  "aria-labelledby": `${uid}-references`,
@@ -314,7 +350,7 @@ function ImageStudioDialog({ open, onOpenChange, store, title }) {
314
350
  className: "text-sm text-muted-foreground",
315
351
  children: t("imageStudio.dialog.generations.empty")
316
352
  })]
317
- })]
353
+ })] })]
318
354
  }),
319
355
  /* @__PURE__ */ jsxs("div", {
320
356
  className: "flex min-h-0 flex-col gap-3",
@@ -332,6 +368,11 @@ function ImageStudioDialog({ open, onOpenChange, store, title }) {
332
368
  alt: picture.title || t("imageStudio.dialog.tile.untitled"),
333
369
  className: "size-full object-contain",
334
370
  "data-image-studio-preview": true
371
+ }) : store.placeholderUrl ? /* @__PURE__ */ jsx("img", {
372
+ src: store.placeholderUrl,
373
+ alt: "",
374
+ className: "size-full object-contain opacity-60",
375
+ "data-image-studio-placeholder": true
335
376
  }) : /* @__PURE__ */ jsx("p", {
336
377
  className: "text-sm text-muted-foreground",
337
378
  children: t("imageStudio.dialog.preview.empty")