@threadlabs/looma 0.28.14 → 0.28.15
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/editor/image-delivery.d.ts +10 -1
- package/package.json +1 -1
- package/vue/editor/LoomaEditor.d.ts +10 -1
- package/vue/editor/index.d.ts +1 -1
- package/vue/editor/index.js +25 -11
|
@@ -23,12 +23,20 @@ export interface LoomaImageRenditionAttributes {
|
|
|
23
23
|
decoding?: "async" | "auto" | "sync";
|
|
24
24
|
fetchPriority?: "auto" | "high" | "low";
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Host policy for resolving a browser-only master URL from durable image data.
|
|
28
|
+
* @ownership The host validates and authorizes the URL; Looma never persists it.
|
|
29
|
+
* @failure Empty results, `undefined` and exceptions keep the durable source.
|
|
30
|
+
*/
|
|
31
|
+
export type LoomaImageSourceResolver = (image: LoomaImageDescriptor) => string | undefined;
|
|
26
32
|
/**
|
|
27
33
|
* Host policy for deriving transient browser-delivery attributes.
|
|
28
34
|
*
|
|
29
35
|
* @ownership The application owns the resolver and any URL/signing policy it
|
|
30
36
|
* consults; Looma reads its result without persisting it into editor JSON.
|
|
31
|
-
* @
|
|
37
|
+
* @contract The descriptor's source is the resolved master URL when a source
|
|
38
|
+
* resolver is supplied. Image events and serialized content retain durable data.
|
|
39
|
+
* @failure Throwing or returning `undefined` falls back to the resolved master
|
|
32
40
|
* instead of interrupting rendering or mutating the document.
|
|
33
41
|
*/
|
|
34
42
|
export type LoomaImageAttributeResolver = (image: LoomaImageDescriptor) => LoomaImageRenditionAttributes | undefined;
|
|
@@ -49,6 +57,7 @@ export interface LoomaImageRenditionErrorDetail extends LoomaImageDescriptor {
|
|
|
49
57
|
trigger: "programmatic";
|
|
50
58
|
}
|
|
51
59
|
interface LoomaImageDeliveryExtensionOptions {
|
|
60
|
+
resolveSource?: () => LoomaImageSourceResolver | undefined;
|
|
52
61
|
resolveAttributes: () => LoomaImageAttributeResolver | undefined;
|
|
53
62
|
isEditable: () => boolean;
|
|
54
63
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import "./looma-editor.css";
|
|
|
3
3
|
import { type AnyExtension, type Editor, type JSONContent } from "@tiptap/core";
|
|
4
4
|
import { type PropType, type VNode } from "vue";
|
|
5
5
|
import { type LoomaSlashCommand, type LoomaMentionItem, type LoomaMentionProvider, type LoomaCodeLanguages } from "@threadlabs/looma/editor";
|
|
6
|
-
import { type LoomaImageActivationDetail, type LoomaImageAttributeResolver, type LoomaImageDescriptor, type LoomaImageRenditionErrorDetail } from "../../editor/image-delivery.js";
|
|
6
|
+
import { type LoomaImageActivationDetail, type LoomaImageAttributeResolver, type LoomaImageSourceResolver, type LoomaImageDescriptor, type LoomaImageRenditionErrorDetail } from "../../editor/image-delivery.js";
|
|
7
7
|
import { type LoomaLinkResolve, type LoomaLinkSearch } from "../../editor/link-editing.js";
|
|
8
8
|
/**
|
|
9
9
|
* Host upload result normalized into the editor's durable image descriptor.
|
|
@@ -162,6 +162,10 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
162
162
|
type: PropType<LoomaImageAttributeResolver | undefined>;
|
|
163
163
|
default: undefined;
|
|
164
164
|
};
|
|
165
|
+
resolveImageSource: {
|
|
166
|
+
type: PropType<LoomaImageSourceResolver | undefined>;
|
|
167
|
+
default: undefined;
|
|
168
|
+
};
|
|
165
169
|
toolbarMode: {
|
|
166
170
|
type: PropType<LoomaEditorToolbarMode>;
|
|
167
171
|
default: string;
|
|
@@ -300,6 +304,10 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
300
304
|
type: PropType<LoomaImageAttributeResolver | undefined>;
|
|
301
305
|
default: undefined;
|
|
302
306
|
};
|
|
307
|
+
resolveImageSource: {
|
|
308
|
+
type: PropType<LoomaImageSourceResolver | undefined>;
|
|
309
|
+
default: undefined;
|
|
310
|
+
};
|
|
303
311
|
toolbarMode: {
|
|
304
312
|
type: PropType<LoomaEditorToolbarMode>;
|
|
305
313
|
default: string;
|
|
@@ -369,6 +377,7 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
369
377
|
linkTargetLabel: string;
|
|
370
378
|
uploadImage: LoomaImageUploader | undefined;
|
|
371
379
|
resolveImageAttributes: LoomaImageAttributeResolver | undefined;
|
|
380
|
+
resolveImageSource: LoomaImageSourceResolver | undefined;
|
|
372
381
|
toolbarMode: LoomaEditorToolbarMode;
|
|
373
382
|
compactToolbar: boolean;
|
|
374
383
|
selectionActions: LoomaEditorSelectionAction[];
|
package/vue/editor/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from "@threadlabs/looma/editor";
|
|
2
2
|
export * from "./primitives.js";
|
|
3
3
|
export { LoomaEditor, type LoomaEditorToolbarMode, type LoomaEditorSelectionAction, type LoomaEditorSelectionActionDetail, type LoomaEditorRangeAction, type LoomaEditorRangeActionDetail, type LoomaEditorSlashCommands, type LoomaLinkTarget, type LoomaLinkSearch, type LoomaLinkResolve, type LoomaImageUploader, type LoomaImageUploadResult, } from "./LoomaEditor.js";
|
|
4
|
-
export type { LoomaImageActivationDetail, LoomaImageActivationTrigger, LoomaImageAttributeResolver, LoomaImageDescriptor, LoomaImageRenditionErrorDetail, LoomaImageRenditionAttributes, } from "../../editor/image-delivery.js";
|
|
4
|
+
export type { LoomaImageActivationDetail, LoomaImageActivationTrigger, LoomaImageAttributeResolver, LoomaImageSourceResolver, LoomaImageDescriptor, LoomaImageRenditionErrorDetail, LoomaImageRenditionAttributes, } from "../../editor/image-delivery.js";
|
package/vue/editor/index.js
CHANGED
|
@@ -705,20 +705,27 @@ function imageDescriptorFromAttrs(attrs) {
|
|
|
705
705
|
...attrs.responsive === true ? { responsive: true } : {}
|
|
706
706
|
};
|
|
707
707
|
}
|
|
708
|
-
function transientAttributes(resolver, image, editable) {
|
|
708
|
+
function transientAttributes(resolveSource, resolver, image, editable) {
|
|
709
|
+
let masterSource = image.src;
|
|
710
|
+
try {
|
|
711
|
+
masterSource = optionalString(resolveSource?.({ ...image })) ?? masterSource;
|
|
712
|
+
} catch {}
|
|
709
713
|
const attributes = {
|
|
710
714
|
...editable ? {} : {
|
|
711
715
|
"aria-label": image.alt ? `Open image: ${image.alt}` : "Open image",
|
|
712
716
|
role: "button"
|
|
713
717
|
},
|
|
714
718
|
"data-looma-image": "",
|
|
715
|
-
src:
|
|
719
|
+
src: masterSource,
|
|
716
720
|
tabindex: editable ? "-1" : "0"
|
|
717
721
|
};
|
|
718
722
|
if (!image.responsive || !resolver) return attributes;
|
|
719
723
|
let rendition;
|
|
720
724
|
try {
|
|
721
|
-
rendition = resolver(
|
|
725
|
+
rendition = resolver({
|
|
726
|
+
...image,
|
|
727
|
+
src: masterSource
|
|
728
|
+
});
|
|
722
729
|
} catch {
|
|
723
730
|
return attributes;
|
|
724
731
|
}
|
|
@@ -740,10 +747,10 @@ function transientAttributes(resolver, image, editable) {
|
|
|
740
747
|
"high",
|
|
741
748
|
"low"
|
|
742
749
|
].includes(rendition.fetchPriority ?? "")) attributes.fetchpriority = rendition.fetchPriority;
|
|
743
|
-
if (src && src !==
|
|
750
|
+
if (src && src !== masterSource || srcset) attributes["data-looma-rendition"] = "true";
|
|
744
751
|
return attributes;
|
|
745
752
|
}
|
|
746
|
-
function createDecorations(doc, resolver, failedRenditions, editable) {
|
|
753
|
+
function createDecorations(doc, resolveSource, resolver, failedRenditions, editable) {
|
|
747
754
|
const decorations = [];
|
|
748
755
|
const currentSources = /* @__PURE__ */ new Set();
|
|
749
756
|
doc.descendants((node, position) => {
|
|
@@ -751,7 +758,7 @@ function createDecorations(doc, resolver, failedRenditions, editable) {
|
|
|
751
758
|
const image = imageDescriptorFromAttrs(node.attrs);
|
|
752
759
|
if (!image) return;
|
|
753
760
|
currentSources.add(image.src);
|
|
754
|
-
decorations.push(Decoration.node(position, position + node.nodeSize, {}, { loomaImageAttributes: transientAttributes(failedRenditions.has(image.src) ? void 0 : resolver, image, editable) }));
|
|
761
|
+
decorations.push(Decoration.node(position, position + node.nodeSize, {}, { loomaImageAttributes: transientAttributes(resolveSource, failedRenditions.has(image.src) ? void 0 : resolver, image, editable) }));
|
|
755
762
|
});
|
|
756
763
|
for (const src of failedRenditions) if (!currentSources.has(src)) failedRenditions.delete(src);
|
|
757
764
|
return DecorationSet.create(doc, decorations);
|
|
@@ -789,10 +796,10 @@ function createLoomaImageDeliveryController(options) {
|
|
|
789
796
|
return [new Plugin({
|
|
790
797
|
key: pluginKey,
|
|
791
798
|
state: {
|
|
792
|
-
init: (_, state) => createDecorations(state.doc, options.resolveAttributes(), failedRenditions, options.isEditable()),
|
|
799
|
+
init: (_, state) => createDecorations(state.doc, options.resolveSource?.(), options.resolveAttributes(), failedRenditions, options.isEditable()),
|
|
793
800
|
apply: (transaction, previous) => {
|
|
794
801
|
if (!transaction.getMeta(pluginKey) && !transactionChangesImages(transaction)) return previous.map(transaction.mapping, transaction.doc);
|
|
795
|
-
return createDecorations(transaction.doc, options.resolveAttributes(), failedRenditions, options.isEditable());
|
|
802
|
+
return createDecorations(transaction.doc, options.resolveSource?.(), options.resolveAttributes(), failedRenditions, options.isEditable());
|
|
796
803
|
}
|
|
797
804
|
},
|
|
798
805
|
props: { decorations: (state) => pluginKey.getState(state) }
|
|
@@ -2643,7 +2650,7 @@ var imageNodeView = ({ editor, node, getPos, decorations }) => {
|
|
|
2643
2650
|
const attrs = current.value.attrs;
|
|
2644
2651
|
const transient = Object.assign({}, ...delivery.value.map((decoration) => decoration.spec.loomaImageAttributes ?? {}));
|
|
2645
2652
|
return h(Image, {
|
|
2646
|
-
src: attrs.src,
|
|
2653
|
+
src: transient.src ?? attrs.src,
|
|
2647
2654
|
alt: attrs.alt ?? "",
|
|
2648
2655
|
width: attrs.width,
|
|
2649
2656
|
height: attrs.height,
|
|
@@ -2857,6 +2864,10 @@ var LoomaEditor = defineComponent({
|
|
|
2857
2864
|
type: Function,
|
|
2858
2865
|
default: void 0
|
|
2859
2866
|
},
|
|
2867
|
+
resolveImageSource: {
|
|
2868
|
+
type: Function,
|
|
2869
|
+
default: void 0
|
|
2870
|
+
},
|
|
2860
2871
|
toolbarMode: {
|
|
2861
2872
|
type: String,
|
|
2862
2873
|
default: "bubble"
|
|
@@ -3094,6 +3105,7 @@ var LoomaEditor = defineComponent({
|
|
|
3094
3105
|
}
|
|
3095
3106
|
});
|
|
3096
3107
|
const imageDelivery = createLoomaImageDeliveryController({
|
|
3108
|
+
resolveSource: () => props.resolveImageSource,
|
|
3097
3109
|
resolveAttributes: () => props.resolveImageAttributes,
|
|
3098
3110
|
isEditable: () => props.editable
|
|
3099
3111
|
});
|
|
@@ -3142,6 +3154,7 @@ var LoomaEditor = defineComponent({
|
|
|
3142
3154
|
content: props.modelValue,
|
|
3143
3155
|
editable: props.editable,
|
|
3144
3156
|
editorProps: {
|
|
3157
|
+
nodeViews: { image: () => ({ dom: document.createElement("span") }) },
|
|
3145
3158
|
attributes: {
|
|
3146
3159
|
role: "textbox",
|
|
3147
3160
|
"aria-multiline": "true",
|
|
@@ -3193,6 +3206,7 @@ var LoomaEditor = defineComponent({
|
|
|
3193
3206
|
emit("update", value);
|
|
3194
3207
|
}
|
|
3195
3208
|
}, Editor$1);
|
|
3209
|
+
delete session.editor.options.editorProps.nodeViews;
|
|
3196
3210
|
session.subscribe(() => {
|
|
3197
3211
|
editorStateVersion.value += 1;
|
|
3198
3212
|
});
|
|
@@ -3318,7 +3332,7 @@ var LoomaEditor = defineComponent({
|
|
|
3318
3332
|
watch(() => props.editable, (editable) => {
|
|
3319
3333
|
formatting?.press.cancel();
|
|
3320
3334
|
formatting?.release();
|
|
3321
|
-
editor.value?.setEditable(editable);
|
|
3335
|
+
editor.value?.setEditable(editable, false);
|
|
3322
3336
|
if (!editable) formattingPopup?.hide();
|
|
3323
3337
|
if (editor.value) imageDelivery.reset(editor.value);
|
|
3324
3338
|
root.value?.querySelectorAll("[data-looma-chip]").forEach((chip) => {
|
|
@@ -3346,7 +3360,7 @@ var LoomaEditor = defineComponent({
|
|
|
3346
3360
|
"aria-label": label
|
|
3347
3361
|
} } });
|
|
3348
3362
|
});
|
|
3349
|
-
watch(() => props.resolveImageAttributes, () => {
|
|
3363
|
+
watch([() => props.resolveImageAttributes, () => props.resolveImageSource], () => {
|
|
3350
3364
|
const instance = editor.value;
|
|
3351
3365
|
if (instance) imageDelivery.reset(instance);
|
|
3352
3366
|
});
|