@ecsplico/editor 3.3.0 → 3.5.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 (94) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/core/abilities.js +1 -0
  3. package/dist/core/abilities.js.map +1 -1
  4. package/dist/core/backend-capability.d.ts +1 -1
  5. package/dist/core/backend-capability.js +2 -1
  6. package/dist/core/backend-capability.js.map +1 -1
  7. package/dist/core/backend-types.d.ts +25 -0
  8. package/dist/core/backend-types.js.map +1 -1
  9. package/dist/core/backend.d.ts +9 -1
  10. package/dist/core/backend.js.map +1 -1
  11. package/dist/core/http-backend.js +17 -0
  12. package/dist/core/http-backend.js.map +1 -1
  13. package/dist/core/schema/abilities.d.ts +1 -0
  14. package/dist/core/schema/abilities.js +4 -0
  15. package/dist/core/schema/abilities.js.map +1 -1
  16. package/dist/features/ai/protocol/generate.d.ts +1 -1
  17. package/dist/features/composites/components/CompositeChrome.js +10 -9
  18. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  19. package/dist/features/composites/kinds.d.ts +4 -0
  20. package/dist/features/composites/kinds.js.map +1 -1
  21. package/dist/features/composites/merge.d.ts +1 -1
  22. package/dist/features/composites/merge.js +5 -2
  23. package/dist/features/composites/merge.js.map +1 -1
  24. package/dist/features/composites/nodes.js +1 -0
  25. package/dist/features/composites/nodes.js.map +1 -1
  26. package/dist/features/composites/outline.d.ts +9 -2
  27. package/dist/features/composites/outline.js +41 -3
  28. package/dist/features/composites/outline.js.map +1 -1
  29. package/dist/features/composites/react/CompositeChrome.js +11 -9
  30. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  31. package/dist/features/composites/rows.d.ts +2 -0
  32. package/dist/features/composites/rows.js +1 -0
  33. package/dist/features/composites/rows.js.map +1 -1
  34. package/dist/features/image-studio/components/ImageStudioChat.js +121 -0
  35. package/dist/features/image-studio/components/ImageStudioChat.js.map +1 -0
  36. package/dist/features/image-studio/components/ImageStudioDialog.js +371 -0
  37. package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -0
  38. package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +25 -0
  39. package/dist/features/image-studio/components/ImageStudioTile.js +74 -0
  40. package/dist/features/image-studio/components/ImageStudioTile.js.map +1 -0
  41. package/dist/features/image-studio/components/ImageStudioView.js +117 -0
  42. package/dist/features/image-studio/components/ImageStudioView.js.map +1 -0
  43. package/dist/features/image-studio/feature.d.ts +24 -0
  44. package/dist/features/image-studio/feature.js +53 -0
  45. package/dist/features/image-studio/feature.js.map +1 -0
  46. package/dist/features/image-studio/index.d.ts +5 -0
  47. package/dist/features/image-studio/index.js +4 -0
  48. package/dist/features/image-studio/node-store.d.ts +27 -0
  49. package/dist/features/image-studio/node-store.js +64 -0
  50. package/dist/features/image-studio/node-store.js.map +1 -0
  51. package/dist/features/image-studio/schema.d.ts +29 -0
  52. package/dist/features/image-studio/schema.js +86 -0
  53. package/dist/features/image-studio/schema.js.map +1 -0
  54. package/dist/features/image-studio/state.d.ts +52 -0
  55. package/dist/features/image-studio/state.js +147 -0
  56. package/dist/features/image-studio/state.js.map +1 -0
  57. package/dist/features/image-studio/store.d.ts +29 -0
  58. package/dist/features/image-studio/vue.d.ts +17 -0
  59. package/dist/features/image-studio/vue.js +20 -0
  60. package/dist/features/image-studio/vue.js.map +1 -0
  61. package/dist/features/media/schema.d.ts +5 -1
  62. package/dist/features/media/schema.js +3 -3
  63. package/dist/features/media/schema.js.map +1 -1
  64. package/dist/features/transcript/segment-node.js +1 -0
  65. package/dist/features/transcript/segment-node.js.map +1 -1
  66. package/dist/i18n/de/image-studio.js +39 -0
  67. package/dist/i18n/de/image-studio.js.map +1 -0
  68. package/dist/i18n/de.d.ts +32 -0
  69. package/dist/i18n/de.js +13 -11
  70. package/dist/i18n/de.js.map +1 -1
  71. package/dist/i18n/en/image-studio.js +39 -0
  72. package/dist/i18n/en/image-studio.js.map +1 -0
  73. package/dist/i18n/en.js +13 -11
  74. package/dist/i18n/en.js.map +1 -1
  75. package/dist/nuxt/module.d.ts +64 -0
  76. package/dist/server/handlers/images.d.ts +22 -0
  77. package/dist/server/handlers/images.js +46 -0
  78. package/dist/server/handlers/images.js.map +1 -0
  79. package/dist/server/handlers/index.d.ts +2 -1
  80. package/dist/server/handlers/index.js +2 -1
  81. package/dist/server/index.d.ts +2 -2
  82. package/dist/server/ports.d.ts +11 -1
  83. package/dist/server/ports.js +2 -1
  84. package/dist/server/ports.js.map +1 -1
  85. package/dist/server/routes.js +19 -0
  86. package/dist/server/routes.js.map +1 -1
  87. package/dist/tree/DocumentTree.js +5 -4
  88. package/dist/tree/DocumentTree.js.map +1 -1
  89. package/dist/tree/react/DocumentTree.js +2 -1
  90. package/dist/tree/react/DocumentTree.js.map +1 -1
  91. package/dist/tree/tree-items.js +1 -0
  92. package/dist/tree/tree-items.js.map +1 -1
  93. package/dist/vue/AdvancedEditor.vue.d.ts +1 -1
  94. package/package.json +16 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageStudioView.js","names":["selected","node","editor","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createElementBlock","_createVNode","_toDisplayString","_createCommentVNode","ImageStudioDialog"],"sources":["../../../../src/features/image-studio/components/ImageStudioView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { computed, onBeforeUnmount, ref, shallowRef, toRaw } from 'vue'\nimport { editorEntryOf } from '../../../core'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { useNotifier } from '../../../vue/use-notifier'\nimport { SCRATCH_DOCUMENT_ID } from '../../media/assets'\nimport { createImageStudioNodeStore, type ImageStudioNodeStore } from '../node-store'\nimport { normalizeImageStudioState } from '../state'\nimport ImageStudioDialog from './ImageStudioDialog.vue'\n\nconst props = defineProps(nodeViewProps)\n\nconst { Button: UButton, Icon: UIcon } = useUiKit()\nconst { t } = useEditorI18n()\nconst notify = useNotifier(() => props.editor)\n\nconst studio = computed(() => normalizeImageStudioState(props.node.attrs.studio))\nconst description = computed(() => studio.value.description.trim())\n\n// The store (and with it the Prompt-Bausteine list) is built when the studio opens first and lives as long as the view.\nconst open = ref(false)\nconst store = shallowRef<ImageStudioNodeStore>()\n\n/** Opens the studio dialog; a read-only editor opens none, a backend without image generation explains itself. */\nfunction openStudio() {\n if (!props.editor.isEditable) return\n const ctx = editorEntryOf(toRaw(props.editor))?.ctx\n const backend = ctx?.backend.images\n if (!ctx || !backend) {\n notify({ id: 'image-studio.unavailable', title: t('imageStudio.action.notConfigured'), kind: 'warning' })\n return\n }\n store.value ??= createImageStudioNodeStore({\n editor: toRaw(props.editor),\n getPos: props.getPos,\n backend,\n documentId: ctx.documentId ?? SCRATCH_DOCUMENT_ID,\n })\n open.value = true\n}\n\nonBeforeUnmount(() => store.value?.dispose())\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"figure\"\n data-image-studio-block\n :class=\"['my-3 flex flex-col gap-2 rounded-md', { 'outline-2 outline-offset-2 outline-[var(--sk-primary)]': selected }]\"\n @dblclick=\"openStudio\"\n @sk-image-studio-open=\"openStudio\"\n >\n <div class=\"flex flex-col gap-2\" contenteditable=\"false\">\n <img\n v-if=\"node.attrs.src\"\n :src=\"node.attrs.src\"\n :alt=\"node.attrs.alt || description\"\n class=\"m-0 max-w-full rounded-md\"\n >\n <div\n v-else\n class=\"flex min-h-32 flex-col items-center justify-center gap-2 rounded-md border border-dashed border-[var(--sk-border)] p-4 text-center text-sm\"\n >\n <UIcon name=\"i-lucide-image-plus\" class=\"size-6 shrink-0 text-[var(--sk-fg-dimmed)]\" />\n <span v-if=\"description\" class=\"whitespace-pre-wrap text-[var(--sk-muted)]\">{{ description }}</span>\n <span v-else class=\"text-[var(--sk-fg-dimmed)]\">{{ t('imageStudio.node.noDescription') }}</span>\n </div>\n <figcaption v-if=\"node.attrs.src && description\" class=\"whitespace-pre-wrap px-1 text-sm text-[var(--sk-muted)]\">\n {{ description }}\n </figcaption>\n <div v-if=\"editor.isEditable\" class=\"flex\">\n <UButton\n :label=\"t('imageStudio.node.open')\"\n icon=\"i-lucide-wand-sparkles\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"soft\"\n @click=\"openStudio\"\n />\n </div>\n </div>\n <ImageStudioDialog\n v-if=\"store\"\n v-model:open=\"open\"\n :store=\"store\"\n :title=\"t('imageStudio.node.dialogTitle')\"\n />\n </NodeViewWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYA,MAAM,QAAQ;EAEd,MAAM,EAAE,QAAQ,SAAS,MAAM,UAAU,SAAS;EAClD,MAAM,EAAE,MAAM,cAAc;EAC5B,MAAM,SAAS,kBAAkB,MAAM,MAAM;EAE7C,MAAM,SAAS,eAAe,0BAA0B,MAAM,KAAK,MAAM,MAAM,CAAC;EAChF,MAAM,cAAc,eAAe,OAAO,MAAM,YAAY,KAAK,CAAC;EAGlE,MAAM,OAAO,IAAI,KAAK;EACtB,MAAM,QAAQ,WAAiC;;EAG/C,SAAS,aAAa;GACpB,IAAI,CAAC,MAAM,OAAO,YAAY;GAC9B,MAAM,MAAM,cAAc,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE;GAChD,MAAM,UAAU,KAAK,QAAQ;GAC7B,IAAI,CAAC,OAAO,CAAC,SAAS;IACpB,OAAO;KAAE,IAAI;KAA4B,OAAO,EAAE,kCAAkC;KAAG,MAAM;IAAU,CAAC;IACxG;GACF;GACA,MAAM,UAAU,2BAA2B;IACzC,QAAQ,MAAM,MAAM,MAAM;IAC1B,QAAQ,MAAM;IACd;IACA,YAAY,IAAI,cAAA;GAClB,CAAC;GACD,KAAK,QAAQ;EACf;EAEA,sBAAsB,MAAM,OAAO,QAAQ,CAAC;;GAI1C,OAAAG,UAAA,GAAAC,YA0CkBC,MAAA,eAAA,GAAA;IAzChB,IAAG;IACH,2BAAA;IACC,OAAKC,eAAA,CAAA,uCAAA,EAAA,0DAAsGN,KAAAA,SAAQ,CAAA,CAAA;IACnH,YAAU;IACV,qBAAsB;;IAEvB,SAAAO,cA4BM,CA5BNC,mBA4BM,OA5BN,YA4BM;KA1BIP,KAAAA,KAAK,MAAM,OADnBE,UAAA,GAAAM,mBAKC,OAAA;;MAHE,KAAKR,KAAAA,KAAK,MAAM;MAChB,KAAKA,KAAAA,KAAK,MAAM,OAAO,YAAA;MACxB,OAAM;KAER,GAAA,MAAA,GAAA,UAAA,MAAAE,UAAA,GAAAM,mBAOM,OAPN,YAOM,CAHJC,YAAuFL,MAAA,KAAA,GAAA;MAAhF,MAAK;MAAsB,OAAM;KAC5B,CAAA,GAAA,YAAA,SAAZF,UAAA,GAAAM,mBAAoG,QAApG,YAAoGE,gBAArB,YAAA,KAAW,GAAA,CAAA,MAC1FR,UAAA,GAAAM,mBAAgG,QAAhG,YAAgGE,gBAA7CN,MAAA,CAAA,CAAC,CAAA,gCAAA,CAAA,GAAA,CAAA,EAAA,CAAA;KAEpCJ,KAAAA,KAAK,MAAM,OAAO,YAAA,SAApCE,UAAA,GAAAM,mBAEa,cAFb,YAEaE,gBADR,YAAA,KAAW,GAAA,CAAA,KAAAC,mBAAA,QAAA,IAAA;KAELV,KAAAA,OAAO,cAAlBC,UAAA,GAAAM,mBASM,OATN,YASM,CARJC,YAOEL,MAAA,OAAA,GAAA;MANC,OAAOA,MAAA,CAAA,CAAC,CAAA,uBAAA;MACT,MAAK;MACL,MAAK;MACL,OAAM;MACN,SAAQ;MACP,SAAO;;IAKN,CAAA,GAAA,MAAA,SADRF,UAAA,GAAAC,YAKES,aAAA;;KAHQ,MAAM,KAAA;KAAA,iBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,KAAI,QAAA;KACjB,OAAO,MAAA;KACP,OAAOR,MAAA,CAAA,CAAC,CAAA,8BAAA"}
@@ -0,0 +1,24 @@
1
+ import { EditorFeature } from "../../core/feature.js";
2
+ import "../../core/index.js";
3
+ import { NodeViewRenderer } from "@tiptap/core";
4
+ //#region src/features/image-studio/feature.d.ts
5
+ /** What a framework binding supplies to the Bild-Studio feature. */
6
+ export interface ImageStudioUi {
7
+ /** The block's view of the framework; without one the node renders as the plain `<img>` of its HTML form. */
8
+ nodeView?: NodeViewRenderer;
9
+ }
10
+ /**
11
+ * The event the `image-studio.open` block action fires on the block's node view element; the view answers it by opening
12
+ * the studio dialog. A node view that does not listen (a headless editor) ignores it.
13
+ */
14
+ export declare const IMAGE_STUDIO_OPEN = "sk-image-studio-open";
15
+ /**
16
+ * The Bild-Studio block (`imageStudio` node: a picture with its description, Prompt-Bausteine, references, previous
17
+ * generations and chat log in one `studio` attribute), the `/` entry `bild-studio` that inserts an empty one, offered
18
+ * where the caret has `insert.imageStudio`, and the block action `image-studio.open` («Im Bild-Studio öffnen») that asks
19
+ * the block's view to open the studio dialog. The node view and the dialog come from the framework binding (`ui`).
20
+ * Works without the `images` backend capability: the view then explains that generation is not set up instead of opening.
21
+ */
22
+ export declare function imageStudioFeatureCore(ui?: ImageStudioUi): EditorFeature<never>;
23
+ //#endregion
24
+ //# sourceMappingURL=feature.d.ts.map
@@ -0,0 +1,53 @@
1
+ import { ABILITY } from "../../core/schema/abilities.js";
2
+ import { command } from "../../core/commands.js";
3
+ import { ImageStudioNode, imageStudioSchemaExtensions } from "./schema.js";
4
+ //#region src/features/image-studio/feature.ts
5
+ /**
6
+ * The event the `image-studio.open` block action fires on the block's node view element; the view answers it by opening
7
+ * the studio dialog. A node view that does not listen (a headless editor) ignores it.
8
+ */
9
+ const IMAGE_STUDIO_OPEN = "sk-image-studio-open";
10
+ const commands = (ctx) => [command({
11
+ id: "imageStudio",
12
+ slash: { id: "bild-studio" },
13
+ creates: { type: "imageStudio" },
14
+ icon: "i-lucide-image-plus",
15
+ label: ctx.t("imageStudio.slash.insert"),
16
+ ability: ABILITY.imageStudio,
17
+ command: (c) => c.insertImageStudio()
18
+ })];
19
+ const blockActions = (ctx) => [{
20
+ id: "image-studio.open",
21
+ label: ctx.t("imageStudio.action.open"),
22
+ icon: "i-lucide-wand-sparkles",
23
+ isVisible: (block) => block.node.type.name === "imageStudio",
24
+ placement: "both",
25
+ run: (block) => {
26
+ const dom = block.editor.view.nodeDOM(block.pos);
27
+ if (dom instanceof HTMLElement) dom.dispatchEvent(new CustomEvent(IMAGE_STUDIO_OPEN));
28
+ }
29
+ }];
30
+ /**
31
+ * The Bild-Studio block (`imageStudio` node: a picture with its description, Prompt-Bausteine, references, previous
32
+ * generations and chat log in one `studio` attribute), the `/` entry `bild-studio` that inserts an empty one, offered
33
+ * where the caret has `insert.imageStudio`, and the block action `image-studio.open` («Im Bild-Studio öffnen») that asks
34
+ * the block's view to open the studio dialog. The node view and the dialog come from the framework binding (`ui`).
35
+ * Works without the `images` backend capability: the view then explains that generation is not set up instead of opening.
36
+ */
37
+ function imageStudioFeatureCore(ui = {}) {
38
+ const { nodeView } = ui;
39
+ return {
40
+ id: "image-studio",
41
+ dimension: "content",
42
+ schemaExtensions: imageStudioSchemaExtensions,
43
+ extensions: () => [nodeView ? ImageStudioNode.extend({ addNodeView() {
44
+ return nodeView;
45
+ } }) : ImageStudioNode],
46
+ commands,
47
+ blockActions
48
+ };
49
+ }
50
+ //#endregion
51
+ export { IMAGE_STUDIO_OPEN, imageStudioFeatureCore };
52
+
53
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/image-studio/feature.ts"],"sourcesContent":["import type { NodeViewRenderer } from '@tiptap/core'\nimport { type BlockAction, command, type CommandDef, type EditorFeature, type FeatureContext } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport { ImageStudioNode, imageStudioSchemaExtensions } from './schema'\n\n/** What a framework binding supplies to the Bild-Studio feature. */\nexport interface ImageStudioUi {\n /** The block's view of the framework; without one the node renders as the plain `<img>` of its HTML form. */\n nodeView?: NodeViewRenderer\n}\n\n/**\n * The event the `image-studio.open` block action fires on the block's node view element; the view answers it by opening\n * the studio dialog. A node view that does not listen (a headless editor) ignores it.\n */\nexport const IMAGE_STUDIO_OPEN = 'sk-image-studio-open'\n\nconst commands = (ctx: FeatureContext): CommandDef<never>[] => [\n command({\n id: 'imageStudio',\n slash: { id: 'bild-studio' },\n creates: { type: 'imageStudio' },\n icon: 'i-lucide-image-plus',\n label: ctx.t('imageStudio.slash.insert'),\n ability: ABILITY.imageStudio,\n command: c => c.insertImageStudio(),\n }),\n]\n\nconst blockActions = (ctx: FeatureContext): BlockAction[] => [{\n id: 'image-studio.open',\n label: ctx.t('imageStudio.action.open'),\n icon: 'i-lucide-wand-sparkles',\n isVisible: block => block.node.type.name === 'imageStudio',\n placement: 'both',\n run: (block) => {\n const dom = block.editor.view.nodeDOM(block.pos)\n if (dom instanceof HTMLElement) dom.dispatchEvent(new CustomEvent(IMAGE_STUDIO_OPEN))\n },\n}]\n\n/**\n * The Bild-Studio block (`imageStudio` node: a picture with its description, Prompt-Bausteine, references, previous\n * generations and chat log in one `studio` attribute), the `/` entry `bild-studio` that inserts an empty one, offered\n * where the caret has `insert.imageStudio`, and the block action `image-studio.open` («Im Bild-Studio öffnen») that asks\n * the block's view to open the studio dialog. The node view and the dialog come from the framework binding (`ui`).\n * Works without the `images` backend capability: the view then explains that generation is not set up instead of opening.\n */\nexport function imageStudioFeatureCore(ui: ImageStudioUi = {}): EditorFeature<never> {\n const { nodeView } = ui\n return {\n id: 'image-studio',\n dimension: 'content',\n schemaExtensions: imageStudioSchemaExtensions,\n extensions: () => [nodeView\n ? ImageStudioNode.extend({\n addNodeView(): NodeViewRenderer {\n return nodeView\n },\n })\n : ImageStudioNode],\n commands,\n blockActions,\n }\n}\n"],"mappings":";;;;;;;;AAeA,MAAa,oBAAoB;AAEjC,MAAM,YAAY,QAA6C,CAC7D,QAAQ;CACN,IAAI;CACJ,OAAO,EAAE,IAAI,cAAc;CAC3B,SAAS,EAAE,MAAM,cAAc;CAC/B,MAAM;CACN,OAAO,IAAI,EAAE,0BAA0B;CACvC,SAAS,QAAQ;CACjB,UAAS,MAAK,EAAE,kBAAkB;AACpC,CAAC,CACH;AAEA,MAAM,gBAAgB,QAAuC,CAAC;CAC5D,IAAI;CACJ,OAAO,IAAI,EAAE,yBAAyB;CACtC,MAAM;CACN,YAAW,UAAS,MAAM,KAAK,KAAK,SAAS;CAC7C,WAAW;CACX,MAAM,UAAU;EACd,MAAM,MAAM,MAAM,OAAO,KAAK,QAAQ,MAAM,GAAG;EAC/C,IAAI,eAAe,aAAa,IAAI,cAAc,IAAI,YAAY,iBAAiB,CAAC;CACtF;AACF,CAAC;;;;;;;;AASD,SAAgB,uBAAuB,KAAoB,CAAC,GAAyB;CACnF,MAAM,EAAE,aAAa;CACrB,OAAO;EACL,IAAI;EACJ,WAAW;EACX,kBAAkB;EAClB,kBAAkB,CAAC,WACf,gBAAgB,OAAO,EACrB,cAAgC;GAC9B,OAAO;EACT,EACF,CAAC,IACD,eAAe;EACnB;EACA;CACF;AACF"}
@@ -0,0 +1,5 @@
1
+ import { ImageStudioImage, ImageStudioPromptBlock } from "../../core/backend-types.js";
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";
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 };
@@ -0,0 +1,4 @@
1
+ import { EMPTY_IMAGE_STUDIO_STATE, addGeneration, addReference, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "./state.js";
2
+ import { ImageStudioNode, imageStudioSchemaExtensions } from "./schema.js";
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 };
@@ -0,0 +1,27 @@
1
+ import { EditorBackend } from "../../core/backend.js";
2
+ import { ImageStudioStore } from "./store.js";
3
+ import { Editor } from "@tiptap/core";
4
+ //#region src/features/image-studio/node-store.d.ts
5
+ export interface ImageStudioNodeStoreOptions {
6
+ editor: Editor;
7
+ /** Position of the `imageStudio` node (a node view's `getPos`); `undefined` once the node is gone. */
8
+ getPos: () => number | undefined;
9
+ /** The backend's image port; `undefined` (backend without the `images` capability) makes `generate` and `upload` reject. */
10
+ backend: EditorBackend['images'];
11
+ /** Document whose package stores the uploaded references. */
12
+ documentId: string;
13
+ }
14
+ /** A store over one node, with the function that detaches it from the editor. */
15
+ export interface ImageStudioNodeStore extends ImageStudioStore {
16
+ /** Stops following the editor and ignores the answers still outstanding; call when the node view unmounts. */
17
+ dispose: () => void;
18
+ }
19
+ /**
20
+ * The `ImageStudioStore` of one `imageStudio` node: `state` follows the node's `studio` attribute (re-read on every
21
+ * editor transaction, so undo, collaboration and other writers show), `update` writes it back through
22
+ * `setImageStudioState` as one transaction (one undo step; the node's `src` follows the selected image), and
23
+ * `generate`, `upload` and the Prompt-Bausteine go to `backend.images`. Call `dispose` when the view goes.
24
+ */
25
+ export declare function createImageStudioNodeStore(options: ImageStudioNodeStoreOptions): ImageStudioNodeStore;
26
+ //#endregion
27
+ //# sourceMappingURL=node-store.d.ts.map
@@ -0,0 +1,64 @@
1
+ import { EMPTY_IMAGE_STUDIO_STATE, normalizeImageStudioState } from "./state.js";
2
+ import { shallowRef } from "vue";
3
+ //#region src/features/image-studio/node-store.ts
4
+ const NO_IMAGES = "@ecsplico/editor: the backend has no images capability";
5
+ /**
6
+ * The `ImageStudioStore` of one `imageStudio` node: `state` follows the node's `studio` attribute (re-read on every
7
+ * editor transaction, so undo, collaboration and other writers show), `update` writes it back through
8
+ * `setImageStudioState` as one transaction (one undo step; the node's `src` follows the selected image), and
9
+ * `generate`, `upload` and the Prompt-Bausteine go to `backend.images`. Call `dispose` when the view goes.
10
+ */
11
+ function createImageStudioNodeStore(options) {
12
+ const { editor, getPos, backend, documentId } = options;
13
+ const state = shallowRef(EMPTY_IMAGE_STUDIO_STATE);
14
+ const promptBlocks = shallowRef([]);
15
+ const problem = shallowRef(void 0);
16
+ let disposed = false;
17
+ let source;
18
+ function sync() {
19
+ const pos = getPos();
20
+ const node = pos === void 0 || editor.isDestroyed ? null : editor.state.doc.nodeAt(pos);
21
+ if (node?.type.name !== "imageStudio" || node.attrs.studio === source) return;
22
+ source = node.attrs.studio;
23
+ state.value = normalizeImageStudioState(source);
24
+ }
25
+ editor.on("transaction", sync);
26
+ sync();
27
+ if (backend) backend.promptBlocks().then((blocks) => {
28
+ if (!disposed) promptBlocks.value = blocks;
29
+ }, (error) => {
30
+ if (!disposed) problem.value = error instanceof Error ? error.message : String(error);
31
+ });
32
+ return {
33
+ state,
34
+ promptBlocks,
35
+ problem,
36
+ update(next) {
37
+ const pos = getPos();
38
+ if (pos === void 0 || editor.isDestroyed) return;
39
+ editor.commands.setImageStudioState(pos, next);
40
+ },
41
+ generate(instruction, signal) {
42
+ if (!backend) return Promise.reject(/* @__PURE__ */ new Error(NO_IMAGES));
43
+ const { description, promptBlockIds, references, generations } = state.value;
44
+ const referenceUrls = [...references, ...generations].filter((image) => image.active).map((image) => image.url);
45
+ return backend.generate({
46
+ description,
47
+ instruction,
48
+ promptBlockIds: [...promptBlockIds],
49
+ referenceUrls
50
+ }, signal);
51
+ },
52
+ upload(file) {
53
+ return backend ? backend.upload(documentId, file) : Promise.reject(/* @__PURE__ */ new Error(NO_IMAGES));
54
+ },
55
+ dispose() {
56
+ disposed = true;
57
+ editor.off("transaction", sync);
58
+ }
59
+ };
60
+ }
61
+ //#endregion
62
+ export { createImageStudioNodeStore };
63
+
64
+ //# sourceMappingURL=node-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-store.js","names":[],"sources":["../../../src/features/image-studio/node-store.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { shallowRef } from 'vue'\nimport type { EditorBackend } from '../../core/backend'\nimport type { ImageStudioStore } from './store'\nimport { type ImageStudioImage, type ImageStudioPromptBlock, type ImageStudioState, EMPTY_IMAGE_STUDIO_STATE, normalizeImageStudioState } from './state'\n\nexport interface ImageStudioNodeStoreOptions {\n editor: Editor\n /** Position of the `imageStudio` node (a node view's `getPos`); `undefined` once the node is gone. */\n getPos: () => number | undefined\n /** The backend's image port; `undefined` (backend without the `images` capability) makes `generate` and `upload` reject. */\n backend: EditorBackend['images']\n /** Document whose package stores the uploaded references. */\n documentId: string\n}\n\n/** A store over one node, with the function that detaches it from the editor. */\nexport interface ImageStudioNodeStore extends ImageStudioStore {\n /** Stops following the editor and ignores the answers still outstanding; call when the node view unmounts. */\n dispose: () => void\n}\n\n// What a store without the backend's `images` port rejects with (the node view does not open one without the port).\nconst NO_IMAGES = '@ecsplico/editor: the backend has no images capability'\n\n/**\n * The `ImageStudioStore` of one `imageStudio` node: `state` follows the node's `studio` attribute (re-read on every\n * editor transaction, so undo, collaboration and other writers show), `update` writes it back through\n * `setImageStudioState` as one transaction (one undo step; the node's `src` follows the selected image), and\n * `generate`, `upload` and the Prompt-Bausteine go to `backend.images`. Call `dispose` when the view goes.\n */\nexport function createImageStudioNodeStore(options: ImageStudioNodeStoreOptions): ImageStudioNodeStore {\n const { editor, getPos, backend, documentId } = options\n const state = shallowRef<ImageStudioState>(EMPTY_IMAGE_STUDIO_STATE)\n const promptBlocks = shallowRef<readonly ImageStudioPromptBlock[]>([])\n const problem = shallowRef<string | undefined>(undefined)\n let disposed = false\n // The attribute value `state` was normalized from: a transaction that left it alone keeps `state` as it is.\n let source: unknown\n\n function sync(): void {\n const pos = getPos()\n const node = pos === undefined || editor.isDestroyed ? null : editor.state.doc.nodeAt(pos)\n if (node?.type.name !== 'imageStudio' || node.attrs.studio === source) return\n source = node.attrs.studio\n state.value = normalizeImageStudioState(source)\n }\n\n editor.on('transaction', sync)\n sync()\n\n if (backend) {\n backend.promptBlocks().then(\n (blocks) => { if (!disposed) promptBlocks.value = blocks },\n (error: unknown) => { if (!disposed) problem.value = error instanceof Error ? error.message : String(error) },\n )\n }\n\n return {\n state,\n promptBlocks,\n problem,\n update(next) {\n const pos = getPos()\n if (pos === undefined || editor.isDestroyed) return\n editor.commands.setImageStudioState(pos, next)\n },\n generate(instruction, signal) {\n if (!backend) return Promise.reject(new Error(NO_IMAGES))\n const { description, promptBlockIds, references, generations } = state.value\n const referenceUrls = [...references, ...generations].filter(image => image.active).map(image => image.url)\n return backend.generate({ description, instruction, promptBlockIds: [...promptBlockIds], referenceUrls }, signal)\n },\n upload(file): Promise<ImageStudioImage> {\n return backend ? backend.upload(documentId, file) : Promise.reject(new Error(NO_IMAGES))\n },\n dispose() {\n disposed = true\n editor.off('transaction', sync)\n },\n }\n}\n"],"mappings":";;;AAuBA,MAAM,YAAY;;;;;;;AAQlB,SAAgB,2BAA2B,SAA4D;CACrG,MAAM,EAAE,QAAQ,QAAQ,SAAS,eAAe;CAChD,MAAM,QAAQ,WAA6B,wBAAwB;CACnE,MAAM,eAAe,WAA8C,CAAC,CAAC;CACrE,MAAM,UAAU,WAA+B,KAAA,CAAS;CACxD,IAAI,WAAW;CAEf,IAAI;CAEJ,SAAS,OAAa;EACpB,MAAM,MAAM,OAAO;EACnB,MAAM,OAAO,QAAQ,KAAA,KAAa,OAAO,cAAc,OAAO,OAAO,MAAM,IAAI,OAAO,GAAG;EACzF,IAAI,MAAM,KAAK,SAAS,iBAAiB,KAAK,MAAM,WAAW,QAAQ;EACvE,SAAS,KAAK,MAAM;EACpB,MAAM,QAAQ,0BAA0B,MAAM;CAChD;CAEA,OAAO,GAAG,eAAe,IAAI;CAC7B,KAAK;CAEL,IAAI,SACF,QAAQ,aAAa,CAAC,CAAC,MACpB,WAAW;EAAE,IAAI,CAAC,UAAU,aAAa,QAAQ;CAAO,IACxD,UAAmB;EAAE,IAAI,CAAC,UAAU,QAAQ,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;CAAE,CAC9G;CAGF,OAAO;EACL;EACA;EACA;EACA,OAAO,MAAM;GACX,MAAM,MAAM,OAAO;GACnB,IAAI,QAAQ,KAAA,KAAa,OAAO,aAAa;GAC7C,OAAO,SAAS,oBAAoB,KAAK,IAAI;EAC/C;EACA,SAAS,aAAa,QAAQ;GAC5B,IAAI,CAAC,SAAS,OAAO,QAAQ,uBAAO,IAAI,MAAM,SAAS,CAAC;GACxD,MAAM,EAAE,aAAa,gBAAgB,YAAY,gBAAgB,MAAM;GACvE,MAAM,gBAAgB,CAAC,GAAG,YAAY,GAAG,WAAW,CAAC,CAAC,QAAO,UAAS,MAAM,MAAM,CAAC,CAAC,KAAI,UAAS,MAAM,GAAG;GAC1G,OAAO,QAAQ,SAAS;IAAE;IAAa;IAAa,gBAAgB,CAAC,GAAG,cAAc;IAAG;GAAc,GAAG,MAAM;EAClH;EACA,OAAO,MAAiC;GACtC,OAAO,UAAU,QAAQ,OAAO,YAAY,IAAI,IAAI,QAAQ,uBAAO,IAAI,MAAM,SAAS,CAAC;EACzF;EACA,UAAU;GACR,WAAW;GACX,OAAO,IAAI,eAAe,IAAI;EAChC;CACF;AACF"}
@@ -0,0 +1,29 @@
1
+ import { ImageStudioState } from "./state.js";
2
+ import { AnyExtension, Node } from "@tiptap/core";
3
+ //#region src/features/image-studio/schema.d.ts
4
+ /** What `insertImageStudio` takes: the description, chat and images live in `studio`, `src` follows its selected image. */
5
+ export interface ImageStudioAttrs {
6
+ alt?: string;
7
+ studio?: ImageStudioState;
8
+ }
9
+ declare module '@tiptap/core' {
10
+ interface Commands<ReturnType> {
11
+ imageStudio: {
12
+ /** Inserts a Bild-Studio block at the selection. */
13
+ insertImageStudio: (attrs?: ImageStudioAttrs) => ReturnType;
14
+ /** Replaces the studio state of the block at `pos` and mirrors the selected image into its `src`. */
15
+ setImageStudioState: (pos: number, state: ImageStudioState) => ReturnType;
16
+ };
17
+ }
18
+ }
19
+ /** The Bild-Studio node of the document schema. */
20
+ export declare const imageStudioSchemaExtensions: () => AnyExtension[];
21
+ /**
22
+ * The Bild-Studio block: a picture whose description, Prompt-Bausteine, references, previous generations and chat live in
23
+ * the `studio` attribute (`ImageStudioState`); `src` mirrors the selected image. HTML: `<img data-image-studio src alt
24
+ * data-studio="{json}">`. Markdown: `[Bild-Studio: alt](src) <!-- {"studio":{…}} -->` on its own line, the comment left
25
+ * out while the studio is empty. The label is the literal `Bild-Studio` in every UI language (it is what parsing looks for).
26
+ */
27
+ export declare const ImageStudioNode: Node<any, any>;
28
+ //#endregion
29
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1,86 @@
1
+ import { TEXT_ABILITIES } from "../../core/schema/abilities.js";
2
+ import { attrComment, mediaTokenizer } from "../media/schema.js";
3
+ import { EMPTY_IMAGE_STUDIO_STATE, isEmptyImageStudioState, normalizeImageStudioState, selectedImage } from "./state.js";
4
+ import { Node, mergeAttributes } from "@tiptap/core";
5
+ //#region src/features/image-studio/schema.ts
6
+ /** The Bild-Studio node of the document schema. */
7
+ const imageStudioSchemaExtensions = () => [ImageStudioNode];
8
+ /** The `src` a block shows for `studio`: the url of its selected image, `null` without one. */
9
+ const srcOf = (studio) => selectedImage(studio)?.url ?? null;
10
+ function parseStudioJson(json) {
11
+ if (!json) return void 0;
12
+ try {
13
+ return JSON.parse(json);
14
+ } catch {
15
+ return;
16
+ }
17
+ }
18
+ /**
19
+ * The Bild-Studio block: a picture whose description, Prompt-Bausteine, references, previous generations and chat live in
20
+ * the `studio` attribute (`ImageStudioState`); `src` mirrors the selected image. HTML: `<img data-image-studio src alt
21
+ * data-studio="{json}">`. Markdown: `[Bild-Studio: alt](src) <!-- {"studio":{…}} -->` on its own line, the comment left
22
+ * out while the studio is empty. The label is the literal `Bild-Studio` in every UI language (it is what parsing looks for).
23
+ */
24
+ const ImageStudioNode = Node.create({
25
+ name: "imageStudio",
26
+ group: "block",
27
+ atom: true,
28
+ draggable: true,
29
+ editorBlock: true,
30
+ abilities: { revokes: TEXT_ABILITIES },
31
+ addAttributes: () => ({
32
+ src: { default: null },
33
+ alt: { default: "" },
34
+ studio: {
35
+ default: EMPTY_IMAGE_STUDIO_STATE,
36
+ parseHTML: (el) => normalizeImageStudioState(parseStudioJson(el.getAttribute("data-studio"))),
37
+ renderHTML: (attrs) => ({ "data-studio": JSON.stringify(attrs.studio) })
38
+ }
39
+ }),
40
+ parseHTML: () => [{
41
+ tag: "img[data-image-studio]",
42
+ priority: 100
43
+ }],
44
+ renderHTML: ({ HTMLAttributes }) => ["img", mergeAttributes(HTMLAttributes, { "data-image-studio": "" })],
45
+ markdownTokenizer: mediaTokenizer("imageStudio", "Bild-Studio"),
46
+ parseMarkdown: (token, helpers) => helpers.createNode("imageStudio", {
47
+ src: token.href || null,
48
+ alt: token.title ?? "",
49
+ studio: normalizeImageStudioState(token.attrs?.studio)
50
+ }),
51
+ renderMarkdown: (node) => {
52
+ const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "";
53
+ const studio = normalizeImageStudioState(node.attrs?.studio);
54
+ return `[${alt ? `Bild-Studio: ${alt.replace(/[[\]\\]/g, "\\$&")}` : "Bild-Studio"}](${typeof node.attrs?.src === "string" ? node.attrs.src : ""})${attrComment({ studio: isEmptyImageStudioState(studio) ? void 0 : studio })}`;
55
+ },
56
+ addCommands() {
57
+ return {
58
+ insertImageStudio: (attrs) => ({ commands }) => {
59
+ const studio = normalizeImageStudioState(attrs?.studio);
60
+ return commands.insertContent({
61
+ type: this.name,
62
+ attrs: {
63
+ src: srcOf(studio),
64
+ alt: attrs?.alt ?? "",
65
+ studio
66
+ }
67
+ });
68
+ },
69
+ setImageStudioState: (pos, state) => ({ tr, dispatch }) => {
70
+ const node = tr.doc.nodeAt(pos);
71
+ if (node?.type.name !== this.name) return false;
72
+ const studio = normalizeImageStudioState(state);
73
+ if (dispatch) tr.setNodeMarkup(pos, void 0, {
74
+ ...node.attrs,
75
+ studio,
76
+ src: srcOf(studio)
77
+ });
78
+ return true;
79
+ }
80
+ };
81
+ }
82
+ });
83
+ //#endregion
84
+ export { ImageStudioNode, imageStudioSchemaExtensions };
85
+
86
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/image-studio/schema.ts"],"sourcesContent":["import { type AnyExtension, mergeAttributes, Node } from '@tiptap/core'\nimport { TEXT_ABILITIES } from '../../core/schema'\nimport { attrComment, mediaTokenizer } from '../media/schema'\nimport { EMPTY_IMAGE_STUDIO_STATE, type ImageStudioState, isEmptyImageStudioState, normalizeImageStudioState, selectedImage } from './state'\n\n// Vue-free and server-importable (`@ecsplico/editor/image-studio/schema`): the stored document may contain the node, so\n// the server schema has it. The client adds the node view and the dialog.\n\n/** What `insertImageStudio` takes: the description, chat and images live in `studio`, `src` follows its selected image. */\nexport interface ImageStudioAttrs {\n alt?: string\n studio?: ImageStudioState\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n imageStudio: {\n /** Inserts a Bild-Studio block at the selection. */\n insertImageStudio: (attrs?: ImageStudioAttrs) => ReturnType\n /** Replaces the studio state of the block at `pos` and mirrors the selected image into its `src`. */\n setImageStudioState: (pos: number, state: ImageStudioState) => ReturnType\n }\n }\n}\n\n/** The Bild-Studio node of the document schema. */\nexport const imageStudioSchemaExtensions = (): AnyExtension[] => [ImageStudioNode]\n\n/** The `src` a block shows for `studio`: the url of its selected image, `null` without one. */\nconst srcOf = (studio: ImageStudioState): string | null => selectedImage(studio)?.url ?? null\n\nfunction parseStudioJson(json: string | null): unknown {\n if (!json) return undefined\n try {\n return JSON.parse(json)\n }\n catch {\n return undefined\n }\n}\n\n/**\n * The Bild-Studio block: a picture whose description, Prompt-Bausteine, references, previous generations and chat live in\n * the `studio` attribute (`ImageStudioState`); `src` mirrors the selected image. HTML: `<img data-image-studio src alt\n * data-studio=\"{json}\">`. Markdown: `[Bild-Studio: alt](src) <!-- {\"studio\":{…}} -->` on its own line, the comment left\n * out while the studio is empty. The label is the literal `Bild-Studio` in every UI language (it is what parsing looks for).\n */\nexport const ImageStudioNode = Node.create({\n name: 'imageStudio',\n group: 'block',\n atom: true,\n draggable: true,\n editorBlock: true,\n // A picture block holds no text: nothing text-related applies inside it.\n abilities: { revokes: TEXT_ABILITIES },\n addAttributes: () => ({\n src: { default: null },\n alt: { default: '' },\n studio: {\n default: EMPTY_IMAGE_STUDIO_STATE,\n parseHTML: el => normalizeImageStudioState(parseStudioJson(el.getAttribute('data-studio'))),\n renderHTML: attrs => ({ 'data-studio': JSON.stringify(attrs.studio) }),\n },\n }),\n // Before the stock image rule, which also matches an `<img src>`.\n parseHTML: () => [{ tag: 'img[data-image-studio]', priority: 100 }],\n renderHTML: ({ HTMLAttributes }) => ['img', mergeAttributes(HTMLAttributes, { 'data-image-studio': '' })],\n markdownTokenizer: mediaTokenizer('imageStudio', 'Bild-Studio'),\n parseMarkdown: (token, helpers) => helpers.createNode('imageStudio', {\n src: token.href || null,\n alt: token.title ?? '',\n studio: normalizeImageStudioState(token.attrs?.studio),\n }),\n renderMarkdown: (node) => {\n const alt = typeof node.attrs?.alt === 'string' ? node.attrs.alt : ''\n const studio = normalizeImageStudioState(node.attrs?.studio)\n const label = alt ? `Bild-Studio: ${alt.replace(/[[\\]\\\\]/g, '\\\\$&')}` : 'Bild-Studio'\n const src = typeof node.attrs?.src === 'string' ? node.attrs.src : ''\n return `[${label}](${src})${attrComment({ studio: isEmptyImageStudioState(studio) ? undefined : studio })}`\n },\n addCommands() {\n return {\n insertImageStudio: attrs => ({ commands }) => {\n const studio = normalizeImageStudioState(attrs?.studio)\n return commands.insertContent({ type: this.name, attrs: { src: srcOf(studio), alt: attrs?.alt ?? '', studio } })\n },\n setImageStudioState: (pos, state) => ({ tr, dispatch }) => {\n const node = tr.doc.nodeAt(pos)\n if (node?.type.name !== this.name) return false\n const studio = normalizeImageStudioState(state)\n if (dispatch) tr.setNodeMarkup(pos, undefined, { ...node.attrs, studio, src: srcOf(studio) })\n return true\n },\n }\n },\n})\n"],"mappings":";;;;;;AA0BA,MAAa,oCAAoD,CAAC,eAAe;;AAGjF,MAAM,SAAS,WAA4C,cAAc,MAAM,CAAC,EAAE,OAAO;AAEzF,SAAS,gBAAgB,MAA8B;CACrD,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QACM;EACJ;CACF;AACF;;;;;;;AAQA,MAAa,kBAAkB,KAAK,OAAO;CACzC,MAAM;CACN,OAAO;CACP,MAAM;CACN,WAAW;CACX,aAAa;CAEb,WAAW,EAAE,SAAS,eAAe;CACrC,sBAAsB;EACpB,KAAK,EAAE,SAAS,KAAK;EACrB,KAAK,EAAE,SAAS,GAAG;EACnB,QAAQ;GACN,SAAS;GACT,YAAW,OAAM,0BAA0B,gBAAgB,GAAG,aAAa,aAAa,CAAC,CAAC;GAC1F,aAAY,WAAU,EAAE,eAAe,KAAK,UAAU,MAAM,MAAM,EAAE;EACtE;CACF;CAEA,iBAAiB,CAAC;EAAE,KAAK;EAA0B,UAAU;CAAI,CAAC;CAClE,aAAa,EAAE,qBAAqB,CAAC,OAAO,gBAAgB,gBAAgB,EAAE,qBAAqB,GAAG,CAAC,CAAC;CACxG,mBAAmB,eAAe,eAAe,aAAa;CAC9D,gBAAgB,OAAO,YAAY,QAAQ,WAAW,eAAe;EACnE,KAAK,MAAM,QAAQ;EACnB,KAAK,MAAM,SAAS;EACpB,QAAQ,0BAA0B,MAAM,OAAO,MAAM;CACvD,CAAC;CACD,iBAAiB,SAAS;EACxB,MAAM,MAAM,OAAO,KAAK,OAAO,QAAQ,WAAW,KAAK,MAAM,MAAM;EACnE,MAAM,SAAS,0BAA0B,KAAK,OAAO,MAAM;EAG3D,OAAO,IAFO,MAAM,gBAAgB,IAAI,QAAQ,YAAY,MAAM,MAAM,cAEvD,IADL,OAAO,KAAK,OAAO,QAAQ,WAAW,KAAK,MAAM,MAAM,GAC1C,GAAG,YAAY,EAAE,QAAQ,wBAAwB,MAAM,IAAI,KAAA,IAAY,OAAO,CAAC;CAC1G;CACA,cAAc;EACZ,OAAO;GACL,oBAAmB,WAAU,EAAE,eAAe;IAC5C,MAAM,SAAS,0BAA0B,OAAO,MAAM;IACtD,OAAO,SAAS,cAAc;KAAE,MAAM,KAAK;KAAM,OAAO;MAAE,KAAK,MAAM,MAAM;MAAG,KAAK,OAAO,OAAO;MAAI;KAAO;IAAE,CAAC;GACjH;GACA,sBAAsB,KAAK,WAAW,EAAE,IAAI,eAAe;IACzD,MAAM,OAAO,GAAG,IAAI,OAAO,GAAG;IAC9B,IAAI,MAAM,KAAK,SAAS,KAAK,MAAM,OAAO;IAC1C,MAAM,SAAS,0BAA0B,KAAK;IAC9C,IAAI,UAAU,GAAG,cAAc,KAAK,KAAA,GAAW;KAAE,GAAG,KAAK;KAAO;KAAQ,KAAK,MAAM,MAAM;IAAE,CAAC;IAC5F,OAAO;GACT;EACF;CACF;AACF,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { ImageStudioImage, ImageStudioPromptBlock } from "../../core/backend-types.js";
2
+ //#region src/features/image-studio/state.d.ts
3
+ /** One chat turn: the instruction and the generation it produced (or the problem). */
4
+ export interface ImageStudioTurn {
5
+ id: string;
6
+ instruction: string;
7
+ imageId: string | null;
8
+ createdAt: string;
9
+ problem?: string;
10
+ }
11
+ export interface ImageStudioState {
12
+ /** The text describing the image; '' shows the placeholder. */
13
+ description: string;
14
+ /** Active Prompt-Bausteine, by host id. */
15
+ promptBlockIds: string[];
16
+ /** Uploaded or picked references. */
17
+ references: ImageStudioImage[];
18
+ /** Previous generations; an active one goes into the next generation as a reference. */
19
+ generations: ImageStudioImage[];
20
+ /** Id of the generation (or reference) that is the block's image. */
21
+ selected: string | null;
22
+ /** The chat log: instruction → generation. */
23
+ turns: ImageStudioTurn[];
24
+ }
25
+ /** The state of a block that has no description, no images and no chat yet. */
26
+ export declare const EMPTY_IMAGE_STUDIO_STATE: ImageStudioState;
27
+ /** The image that is the block's image (a generation or a reference), if the selection still points at one. */
28
+ export declare function selectedImage(state: ImageStudioState): ImageStudioImage | undefined;
29
+ export declare function setDescription(state: ImageStudioState, description: string): ImageStudioState;
30
+ /** Adds the Prompt-Baustein `id` to the active ones, or removes it when it is active. */
31
+ export declare function togglePromptBlock(state: ImageStudioState, id: string): ImageStudioState;
32
+ /** Flips `active` of the reference or generation `id`; an unknown id changes nothing. */
33
+ export declare function toggleImage(state: ImageStudioState, id: string): ImageStudioState;
34
+ /** Makes the reference or generation `id` the block's image; `null` clears the choice. An unknown id changes nothing. */
35
+ export declare function select(state: ImageStudioState, id: string | null): ImageStudioState;
36
+ /** Drops the image `id` from the block (references and generations); the selection goes with it, the chat log keeps its turns. */
37
+ export declare function removeImage(state: ImageStudioState, id: string): ImageStudioState;
38
+ /** Appends a reference; one with the same id is replaced where it stands. */
39
+ export declare function addReference(state: ImageStudioState, image: ImageStudioImage): ImageStudioState;
40
+ /** A chat generate means "this is the picture now": appends the image and its turn (pointing at the image) and selects it. */
41
+ export declare function addGeneration(state: ImageStudioState, image: ImageStudioImage, turn: ImageStudioTurn): ImageStudioState;
42
+ /**
43
+ * The state `value` stands for, for parsed attributes and stored JSON: a missing field, a wrong type or an entry without
44
+ * an id falls back to the empty value of that field, and a `selected` that points at no image becomes `null`. A valid
45
+ * state comes back unchanged (as a copy).
46
+ */
47
+ export declare function normalizeImageStudioState(value: unknown): ImageStudioState;
48
+ /** Whether `state` holds nothing: no description, Prompt-Baustein, image, selection or chat turn. */
49
+ export declare function isEmptyImageStudioState(state: ImageStudioState): boolean;
50
+ //#endregion
51
+ export type { ImageStudioImage, ImageStudioPromptBlock };
52
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1,147 @@
1
+ import { z } from "zod";
2
+ //#region src/features/image-studio/state.ts
3
+ /**
4
+ * The Bild-Studio's state: what a block holds about its picture. Host-agnostic
5
+ * JSON; image ids are opaque to the library (a host uses its own asset ids).
6
+ */
7
+ /** The state of a block that has no description, no images and no chat yet. */
8
+ const EMPTY_IMAGE_STUDIO_STATE = {
9
+ description: "",
10
+ promptBlockIds: [],
11
+ references: [],
12
+ generations: [],
13
+ selected: null,
14
+ turns: []
15
+ };
16
+ const hasImage = (state, id) => state.references.some((image) => image.id === id) || state.generations.some((image) => image.id === id);
17
+ const toggled = (images, id) => images.map((image) => ({
18
+ ...image,
19
+ active: image.id === id ? !image.active : image.active
20
+ }));
21
+ /** The image that is the block's image (a generation or a reference), if the selection still points at one. */
22
+ function selectedImage(state) {
23
+ if (state.selected === null) return void 0;
24
+ return state.generations.find((image) => image.id === state.selected) ?? state.references.find((image) => image.id === state.selected);
25
+ }
26
+ function setDescription(state, description) {
27
+ return {
28
+ ...state,
29
+ description
30
+ };
31
+ }
32
+ /** Adds the Prompt-Baustein `id` to the active ones, or removes it when it is active. */
33
+ function togglePromptBlock(state, id) {
34
+ const active = state.promptBlockIds.includes(id);
35
+ return {
36
+ ...state,
37
+ promptBlockIds: active ? state.promptBlockIds.filter((other) => other !== id) : [...state.promptBlockIds, id]
38
+ };
39
+ }
40
+ /** Flips `active` of the reference or generation `id`; an unknown id changes nothing. */
41
+ function toggleImage(state, id) {
42
+ return {
43
+ ...state,
44
+ references: toggled(state.references, id),
45
+ generations: toggled(state.generations, id)
46
+ };
47
+ }
48
+ /** Makes the reference or generation `id` the block's image; `null` clears the choice. An unknown id changes nothing. */
49
+ function select(state, id) {
50
+ return {
51
+ ...state,
52
+ selected: id === null || hasImage(state, id) ? id : state.selected
53
+ };
54
+ }
55
+ /** Drops the image `id` from the block (references and generations); the selection goes with it, the chat log keeps its turns. */
56
+ function removeImage(state, id) {
57
+ return {
58
+ ...state,
59
+ references: state.references.filter((image) => image.id !== id),
60
+ generations: state.generations.filter((image) => image.id !== id),
61
+ selected: state.selected === id ? null : state.selected
62
+ };
63
+ }
64
+ /** Appends a reference; one with the same id is replaced where it stands. */
65
+ function addReference(state, image) {
66
+ const known = state.references.some((other) => other.id === image.id);
67
+ return {
68
+ ...state,
69
+ references: known ? state.references.map((other) => other.id === image.id ? { ...image } : other) : [...state.references, { ...image }]
70
+ };
71
+ }
72
+ /** A chat generate means "this is the picture now": appends the image and its turn (pointing at the image) and selects it. */
73
+ function addGeneration(state, image, turn) {
74
+ return {
75
+ ...state,
76
+ generations: [...state.generations, { ...image }],
77
+ turns: [...state.turns, {
78
+ ...turn,
79
+ imageId: image.id
80
+ }],
81
+ selected: image.id
82
+ };
83
+ }
84
+ const Fields = z.record(z.string(), z.unknown());
85
+ const textOf = (value) => typeof value === "string" ? value : "";
86
+ const idOf = (value) => typeof value === "string" && value !== "" ? value : null;
87
+ /** The entries of the array `value` that `parse` accepts (it answers `null` for a refused one); a non-array has none. */
88
+ function entriesOf(value, parse) {
89
+ return Array.isArray(value) ? value.flatMap((entry) => parse(entry) ?? []) : [];
90
+ }
91
+ function imageOf(entry) {
92
+ const fields = Fields.safeParse(entry);
93
+ const id = fields.success ? idOf(fields.data.id) : null;
94
+ if (!fields.success || id === null) return null;
95
+ return {
96
+ id,
97
+ url: textOf(fields.data.url),
98
+ title: textOf(fields.data.title),
99
+ active: fields.data.active === true
100
+ };
101
+ }
102
+ function turnOf(entry) {
103
+ const fields = Fields.safeParse(entry);
104
+ const id = fields.success ? idOf(fields.data.id) : null;
105
+ if (!fields.success || id === null) return null;
106
+ const turn = {
107
+ id,
108
+ instruction: textOf(fields.data.instruction),
109
+ imageId: idOf(fields.data.imageId),
110
+ createdAt: textOf(fields.data.createdAt)
111
+ };
112
+ const problem = idOf(fields.data.problem);
113
+ return problem === null ? turn : {
114
+ ...turn,
115
+ problem
116
+ };
117
+ }
118
+ /**
119
+ * The state `value` stands for, for parsed attributes and stored JSON: a missing field, a wrong type or an entry without
120
+ * an id falls back to the empty value of that field, and a `selected` that points at no image becomes `null`. A valid
121
+ * state comes back unchanged (as a copy).
122
+ */
123
+ function normalizeImageStudioState(value) {
124
+ const fields = Fields.safeParse(value);
125
+ const data = fields.success ? fields.data : {};
126
+ const state = {
127
+ description: textOf(data.description),
128
+ promptBlockIds: entriesOf(data.promptBlockIds, idOf),
129
+ references: entriesOf(data.references, imageOf),
130
+ generations: entriesOf(data.generations, imageOf),
131
+ selected: null,
132
+ turns: entriesOf(data.turns, turnOf)
133
+ };
134
+ const selected = idOf(data.selected);
135
+ return {
136
+ ...state,
137
+ selected: selected !== null && hasImage(state, selected) ? selected : null
138
+ };
139
+ }
140
+ /** Whether `state` holds nothing: no description, Prompt-Baustein, image, selection or chat turn. */
141
+ function isEmptyImageStudioState(state) {
142
+ return state.description === "" && state.promptBlockIds.length === 0 && state.references.length === 0 && state.generations.length === 0 && state.selected === null && state.turns.length === 0;
143
+ }
144
+ //#endregion
145
+ export { EMPTY_IMAGE_STUDIO_STATE, addGeneration, addReference, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock };
146
+
147
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","names":[],"sources":["../../../src/features/image-studio/state.ts"],"sourcesContent":["/**\n * The Bild-Studio's state: what a block holds about its picture. Host-agnostic\n * JSON; image ids are opaque to the library (a host uses its own asset ids).\n */\n\nimport { z } from 'zod'\nimport type { ImageStudioImage, ImageStudioPromptBlock } from '../../core/backend-types'\n\n// `ImageStudioImage` and `ImageStudioPromptBlock` are wire types of `EditorBackend.images`, so they live in\n// `core/backend-types.ts` (core never imports a feature); this module is their feature-side entry.\nexport type { ImageStudioImage, ImageStudioPromptBlock }\n\n/** One chat turn: the instruction and the generation it produced (or the problem). */\nexport interface ImageStudioTurn {\n id: string\n instruction: string\n imageId: string | null\n createdAt: string\n problem?: string\n}\n\nexport interface ImageStudioState {\n /** The text describing the image; '' shows the placeholder. */\n description: string\n /** Active Prompt-Bausteine, by host id. */\n promptBlockIds: string[]\n /** Uploaded or picked references. */\n references: ImageStudioImage[]\n /** Previous generations; an active one goes into the next generation as a reference. */\n generations: ImageStudioImage[]\n /** Id of the generation (or reference) that is the block's image. */\n selected: string | null\n /** The chat log: instruction → generation. */\n turns: ImageStudioTurn[]\n}\n\n/** The state of a block that has no description, no images and no chat yet. */\nexport const EMPTY_IMAGE_STUDIO_STATE: ImageStudioState = {\n description: '',\n promptBlockIds: [],\n references: [],\n generations: [],\n selected: null,\n turns: [],\n}\n\n// Reducers: pure, each returns a new state and never touches its argument. The host owns the files behind the images.\n\nconst hasImage = (state: ImageStudioState, id: string): boolean =>\n state.references.some(image => image.id === id) || state.generations.some(image => image.id === id)\n\nconst toggled = (images: readonly ImageStudioImage[], id: string): ImageStudioImage[] =>\n images.map(image => ({ ...image, active: image.id === id ? !image.active : image.active }))\n\n/** The image that is the block's image (a generation or a reference), if the selection still points at one. */\nexport function selectedImage(state: ImageStudioState): ImageStudioImage | undefined {\n if (state.selected === null) return undefined\n return state.generations.find(image => image.id === state.selected) ?? state.references.find(image => image.id === state.selected)\n}\n\nexport function setDescription(state: ImageStudioState, description: string): ImageStudioState {\n return { ...state, description }\n}\n\n/** Adds the Prompt-Baustein `id` to the active ones, or removes it when it is active. */\nexport function togglePromptBlock(state: ImageStudioState, id: string): ImageStudioState {\n const active = state.promptBlockIds.includes(id)\n return { ...state, promptBlockIds: active ? state.promptBlockIds.filter(other => other !== id) : [...state.promptBlockIds, id] }\n}\n\n/** Flips `active` of the reference or generation `id`; an unknown id changes nothing. */\nexport function toggleImage(state: ImageStudioState, id: string): ImageStudioState {\n return { ...state, references: toggled(state.references, id), generations: toggled(state.generations, id) }\n}\n\n/** Makes the reference or generation `id` the block's image; `null` clears the choice. An unknown id changes nothing. */\nexport function select(state: ImageStudioState, id: string | null): ImageStudioState {\n return { ...state, selected: id === null || hasImage(state, id) ? id : state.selected }\n}\n\n/** Drops the image `id` from the block (references and generations); the selection goes with it, the chat log keeps its turns. */\nexport function removeImage(state: ImageStudioState, id: string): ImageStudioState {\n return {\n ...state,\n references: state.references.filter(image => image.id !== id),\n generations: state.generations.filter(image => image.id !== id),\n selected: state.selected === id ? null : state.selected,\n }\n}\n\n/** Appends a reference; one with the same id is replaced where it stands. */\nexport function addReference(state: ImageStudioState, image: ImageStudioImage): ImageStudioState {\n const known = state.references.some(other => other.id === image.id)\n return { ...state, references: known ? state.references.map(other => other.id === image.id ? { ...image } : other) : [...state.references, { ...image }] }\n}\n\n/** A chat generate means \"this is the picture now\": appends the image and its turn (pointing at the image) and selects it. */\nexport function addGeneration(state: ImageStudioState, image: ImageStudioImage, turn: ImageStudioTurn): ImageStudioState {\n return {\n ...state,\n generations: [...state.generations, { ...image }],\n turns: [...state.turns, { ...turn, imageId: image.id }],\n selected: image.id,\n }\n}\n\n// Parsing: stored attributes and JSON from other tools are untrusted, a missing or mistyped field falls back to its empty value.\n// Fields are read one by one from a record: a zod object would reject an entry for a missing key, where this one defaults it.\nconst Fields = z.record(z.string(), z.unknown())\n\nconst textOf = (value: unknown): string => typeof value === 'string' ? value : ''\nconst idOf = (value: unknown): string | null => typeof value === 'string' && value !== '' ? value : null\n\n/** The entries of the array `value` that `parse` accepts (it answers `null` for a refused one); a non-array has none. */\nfunction entriesOf<T>(value: unknown, parse: (entry: unknown) => T | null): T[] {\n return Array.isArray(value) ? value.flatMap((entry: unknown) => parse(entry) ?? []) : []\n}\n\nfunction imageOf(entry: unknown): ImageStudioImage | null {\n const fields = Fields.safeParse(entry)\n const id = fields.success ? idOf(fields.data.id) : null\n if (!fields.success || id === null) return null\n return { id, url: textOf(fields.data.url), title: textOf(fields.data.title), active: fields.data.active === true }\n}\n\nfunction turnOf(entry: unknown): ImageStudioTurn | null {\n const fields = Fields.safeParse(entry)\n const id = fields.success ? idOf(fields.data.id) : null\n if (!fields.success || id === null) return null\n const turn: ImageStudioTurn = { id, instruction: textOf(fields.data.instruction), imageId: idOf(fields.data.imageId), createdAt: textOf(fields.data.createdAt) }\n const problem = idOf(fields.data.problem)\n return problem === null ? turn : { ...turn, problem }\n}\n\n/**\n * The state `value` stands for, for parsed attributes and stored JSON: a missing field, a wrong type or an entry without\n * an id falls back to the empty value of that field, and a `selected` that points at no image becomes `null`. A valid\n * state comes back unchanged (as a copy).\n */\nexport function normalizeImageStudioState(value: unknown): ImageStudioState {\n const fields = Fields.safeParse(value)\n const data = fields.success ? fields.data : {}\n const state: ImageStudioState = {\n description: textOf(data.description),\n promptBlockIds: entriesOf(data.promptBlockIds, idOf),\n references: entriesOf(data.references, imageOf),\n generations: entriesOf(data.generations, imageOf),\n selected: null,\n turns: entriesOf(data.turns, turnOf),\n }\n const selected = idOf(data.selected)\n return { ...state, selected: selected !== null && hasImage(state, selected) ? selected : null }\n}\n\n/** Whether `state` holds nothing: no description, Prompt-Baustein, image, selection or chat turn. */\nexport function isEmptyImageStudioState(state: ImageStudioState): boolean {\n return state.description === '' && state.promptBlockIds.length === 0 && state.references.length === 0 && state.generations.length === 0\n && state.selected === null && state.turns.length === 0\n}\n\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { ImageStudioImage, ImageStudioPromptBlock } from "../../core/backend-types.js";
2
+ import { ImageStudioState } from "./state.js";
3
+ import { Ref } from "vue";
4
+ //#region src/features/image-studio/store.d.ts
5
+ /**
6
+ * What the Bild-Studio dialog drives; a host supplies it (the node view builds one over the node's `studio` attribute and
7
+ * `EditorBackend.images`, an app with its own image store builds its own).
8
+ *
9
+ * The dialog applies the reducers of `./state` and hands the result to `update`; the store only persists it. The
10
+ * store's methods never touch `state` themselves:
11
+ * - `generate` produces one image from the block's `state.description`, the active Prompt-Bausteine and the active
12
+ * images (the host builds the prompt). When it resolves, the dialog calls `update(addGeneration(state, image, turn))`;
13
+ * when it rejects (and the signal was not aborted) the dialog appends a turn with `imageId: null` and the message as
14
+ * `problem`. An aborted generation adds nothing.
15
+ * - `upload` stores the file and returns the image; the dialog then calls `update(addReference(state, image))`.
16
+ * - `problem` is a problem of the store itself (the Prompt-Bausteine could not be loaded, say); the dialog shows it as a notice.
17
+ */
18
+ export interface ImageStudioStore {
19
+ readonly state: Ref<ImageStudioState>;
20
+ /** The managed list of Prompt-Bausteine, loaded by the host. */
21
+ readonly promptBlocks: Ref<readonly ImageStudioPromptBlock[]>;
22
+ /** Called with the state after a reducer; the host persists it. */
23
+ update: (next: ImageStudioState) => void;
24
+ generate: (instruction: string, signal: AbortSignal) => Promise<ImageStudioImage>;
25
+ upload: (file: File) => Promise<ImageStudioImage>;
26
+ readonly problem: Ref<string | undefined>;
27
+ }
28
+ //#endregion
29
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { ImageStudioImage, ImageStudioPromptBlock } from "../../core/backend-types.js";
2
+ import { VueFeature } from "../../vue/index.js";
3
+ import { EMPTY_IMAGE_STUDIO_STATE, ImageStudioState, ImageStudioTurn, addGeneration, addReference, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock } from "./state.js";
4
+ import { ImageStudioStore } from "./store.js";
5
+ import _default from "./components/ImageStudioDialog.vue.js";
6
+ import { ImageStudioNodeStore, ImageStudioNodeStoreOptions, createImageStudioNodeStore } from "./node-store.js";
7
+ //#region src/features/image-studio/vue.d.ts
8
+ /**
9
+ * The Vue Bild-Studio feature: the `imageStudio` block with its Vue view (picture or description placeholder, opens the
10
+ * studio dialog on double-click and through the block action `image-studio.open`), the `/` entry `bild-studio` and the
11
+ * action. Generation, upload and the Prompt-Bausteine need a backend with the `images` capability; without it the view
12
+ * says that generation is not set up.
13
+ */
14
+ export declare function imageStudioFeature(): VueFeature;
15
+ //#endregion
16
+ export { EMPTY_IMAGE_STUDIO_STATE, _default as ImageStudioDialog, type ImageStudioImage, type ImageStudioNodeStore, type ImageStudioNodeStoreOptions, type ImageStudioPromptBlock, type ImageStudioState, type ImageStudioStore, type ImageStudioTurn, addGeneration, addReference, createImageStudioNodeStore, isEmptyImageStudioState, normalizeImageStudioState, removeImage, select, selectedImage, setDescription, toggleImage, togglePromptBlock };
17
+ //# sourceMappingURL=vue.d.ts.map