@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.
- package/CHANGELOG.md +8 -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 +7 -7
- package/dist/nuxt/module.d.ts +18 -0
- package/dist/presets/compose.js +1 -1
- package/dist/react/compose.js +1 -1
- package/dist/server/handlers/ai.d.ts +2 -2
- package/dist/tree/tree-host.js +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
|
@@ -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
|
-
|
|
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__ */
|
|
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")
|