@openeditor/react 0.0.30 → 0.0.32
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/README.md +30 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +356 -31
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -2,7 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
Headless React bindings for OpenEditor's web editor runtime.
|
|
4
4
|
|
|
5
|
-
Pass `
|
|
5
|
+
Pass `imageRuntime` and `attachmentRuntime` to `useOpenEditorController` for
|
|
6
|
+
host-backed media and to `OpenEditorViewer` for identity-based URL resolution.
|
|
7
|
+
Progress, cancellation, retry input, and errors remain transient node-view state
|
|
8
|
+
and are never emitted through document JSON.
|
|
9
|
+
|
|
10
|
+
An inserted image starts as an empty image node—never a fake placeholder URL.
|
|
11
|
+
The built-in node view offers upload, drag/drop, URL embedding, replacement,
|
|
12
|
+
alternative-text editing, progress, retry, and removal. The upload button is
|
|
13
|
+
shown only when the host supplies `selectImage` and `uploadImage`:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
const imageRuntime = {
|
|
17
|
+
selectImage: () => chooseImageFromYourPicker(),
|
|
18
|
+
validateImage: (input) => input.size && input.size > 15_000_000
|
|
19
|
+
? { accepted: false, message: "Image is too large." }
|
|
20
|
+
: { accepted: true },
|
|
21
|
+
uploadImage: (input, { signal, onProgress } = {}) =>
|
|
22
|
+
uploadToYourStorage(input.source, { signal, onProgress }),
|
|
23
|
+
resolveImage: (imageId, { signal } = {}) =>
|
|
24
|
+
resolveImageFromYourStorage(imageId, { signal }),
|
|
25
|
+
replaceImage: (imageId, input, options) =>
|
|
26
|
+
replaceImageInYourStorage(imageId, input.source, options),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const controller = useOpenEditorController({ imageRuntime });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`uploadImage` and `replaceImage` return an `OpenEditorImageSnapshot`. Store a
|
|
33
|
+
durable `imageId`, public `src`, or both; `resolveImage` may return a temporary
|
|
34
|
+
owner-preview URL such as a `blob:` URL without serializing it.
|
|
6
35
|
|
|
7
36
|
## Public surface
|
|
8
37
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, CSSProperties, RefObject, ComponentType } from 'react';
|
|
3
|
-
import { OpenEditorBlockRef, OpenEditorDocument, BlockSpec, ProseMirrorNode, OpenEditorUrlPolicy, OpenEditorUrlContext, OpenEditorPageRuntime, OpenEditorAttachmentRuntime, OpenEditorPageSnapshot, OpenEditorAuthoringCapabilities } from '@openeditor/core';
|
|
4
|
-
export { OpenEditorBlockRef, OpenEditorPageRuntime, OpenEditorPageSnapshot, OpenEditorUrlContext, OpenEditorUrlPolicy, openEditorPublicUrlPolicy, openEditorUnsafeUrlPolicy } from '@openeditor/core';
|
|
3
|
+
import { OpenEditorBlockRef, OpenEditorDocument, BlockSpec, ProseMirrorNode, OpenEditorUrlPolicy, OpenEditorUrlContext, OpenEditorPageRuntime, OpenEditorAttachmentRuntime, OpenEditorImageRuntime, OpenEditorPageSnapshot, OpenEditorAuthoringCapabilities } from '@openeditor/core';
|
|
4
|
+
export { OpenEditorBlockRef, OpenEditorImageRuntime, OpenEditorImageSnapshot, OpenEditorImageUploadInput, OpenEditorPageRuntime, OpenEditorPageSnapshot, OpenEditorUrlContext, OpenEditorUrlPolicy, openEditorPublicUrlPolicy, openEditorUnsafeUrlPolicy } from '@openeditor/core';
|
|
5
5
|
import { ExportFormat, OpenEditorExporters } from '@openeditor/exporters';
|
|
6
6
|
export { seedDocument } from '@openeditor/extensions';
|
|
7
7
|
import { Editor, AnyExtension, NodeConfig } from '@tiptap/core';
|
|
@@ -19,7 +19,9 @@ type OpenEditorBlockInteractionStore = {
|
|
|
19
19
|
|
|
20
20
|
declare const openEditorThemeTokenNames: readonly ["surface", "surfaceRaised", "surfaceMuted", "blockSurface", "text", "textSoft", "heading", "muted", "placeholder", "border", "borderStrong", "structuralLine", "accent", "accentText", "accentStrong", "buttonBackground", "buttonText", "codeBackground", "codeText", "link", "linkHover", "shadow", "fontSans", "fontMono", "radiusSmall", "radiusMedium", "radiusLarge", "spaceBlock", "spaceInline", "bodyFontSize", "bodyLineHeight", "headingFont", "headingLineHeight", "headingWeight", "heading1Size", "heading2Size", "heading3Size", "heading4Size", "heading5Size", "heading6Size"];
|
|
21
21
|
type OpenEditorThemeToken = (typeof openEditorThemeTokenNames)[number];
|
|
22
|
-
type OpenEditorTheme =
|
|
22
|
+
type OpenEditorTheme = {
|
|
23
|
+
surfaceRaised: string;
|
|
24
|
+
} & Partial<Record<Exclude<OpenEditorThemeToken, "surfaceRaised">, string>>;
|
|
23
25
|
type OpenEditorThemeStyle = CSSProperties & Record<`--oe-${string}`, string | undefined>;
|
|
24
26
|
declare const createOpenEditorThemeStyle: (theme: OpenEditorTheme) => OpenEditorThemeStyle;
|
|
25
27
|
declare const useOpenEditorThemeStyle: () => OpenEditorThemeStyle | undefined;
|
|
@@ -58,6 +60,7 @@ type OpenEditorReactProps = OpenEditorAuthoringCapabilities & {
|
|
|
58
60
|
onFilePasteDrop?: (file: File) => boolean;
|
|
59
61
|
pageRuntime?: OpenEditorPageRuntime;
|
|
60
62
|
attachmentRuntime?: OpenEditorAttachmentRuntime<any>;
|
|
63
|
+
imageRuntime?: OpenEditorImageRuntime<any>;
|
|
61
64
|
slashMenuItems?: readonly OpenEditorSlashMenuItem[];
|
|
62
65
|
/** Consumer-owned blocks. Extension names and node types must be unique. */
|
|
63
66
|
extensions?: readonly OpenEditorReactExtension[];
|
|
@@ -122,6 +125,7 @@ type OpenEditorController = {
|
|
|
122
125
|
extensions: readonly OpenEditorReactExtension[];
|
|
123
126
|
pageRuntime?: OpenEditorPageRuntime;
|
|
124
127
|
attachmentRuntime?: OpenEditorAttachmentRuntime<any>;
|
|
128
|
+
imageRuntime?: OpenEditorImageRuntime<any>;
|
|
125
129
|
enabledBlocks?: readonly string[];
|
|
126
130
|
slashState: SlashState | null;
|
|
127
131
|
dismissSlashMenu: () => void;
|
|
@@ -232,6 +236,7 @@ type OpenEditorViewerProps = {
|
|
|
232
236
|
extensions?: readonly OpenEditorReactExtension[];
|
|
233
237
|
pageRuntime?: OpenEditorPageRuntime;
|
|
234
238
|
attachmentRuntime?: OpenEditorAttachmentRuntime<any>;
|
|
239
|
+
imageRuntime?: OpenEditorImageRuntime<any>;
|
|
235
240
|
/** Safe by default. Supply an explicit host policy for trusted preview URLs such as `blob:`. */
|
|
236
241
|
urlPolicy?: OpenEditorUrlPolicy;
|
|
237
242
|
};
|
|
@@ -251,7 +256,7 @@ type OpenEditorPageHeaderProps = {
|
|
|
251
256
|
};
|
|
252
257
|
/** Canonical Notion-style page metadata editor shared by host page surfaces. */
|
|
253
258
|
declare const OpenEditorPageHeader: ({ page, runtime, className, onPageChange, }: OpenEditorPageHeaderProps) => react.JSX.Element;
|
|
254
|
-
declare const useOpenEditorController: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, enabledBlocks, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
|
|
259
|
+
declare const useOpenEditorController: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, imageRuntime, enabledBlocks, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
|
|
255
260
|
type OpenEditorBubbleMenuProps = {
|
|
256
261
|
controller: OpenEditorController;
|
|
257
262
|
children: ReactNode;
|
|
@@ -293,7 +298,7 @@ declare const sortBlockPickerItems: (items: readonly OpenEditorBlockPickerItem[]
|
|
|
293
298
|
declare const getDefaultBlockPickerItems: (controller: OpenEditorController) => OpenEditorBlockPickerItem[];
|
|
294
299
|
declare const getDefaultSlashMenuItems: (controller: OpenEditorController) => OpenEditorSlashMenuItem[];
|
|
295
300
|
declare const OpenEditorContent: ({ controller, className, }: OpenEditorContentProps) => react.JSX.Element;
|
|
296
|
-
declare const OpenEditorViewer: ({ document, className, renderers, extensions, pageRuntime, attachmentRuntime, urlPolicy, }: OpenEditorViewerProps) => react.JSX.Element;
|
|
297
|
-
declare const useOpenEditor: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, enabledBlocks, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
|
|
301
|
+
declare const OpenEditorViewer: ({ document, className, renderers, extensions, pageRuntime, attachmentRuntime, imageRuntime, urlPolicy, }: OpenEditorViewerProps) => react.JSX.Element;
|
|
302
|
+
declare const useOpenEditor: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, imageRuntime, enabledBlocks, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
|
|
298
303
|
|
|
299
304
|
export { type InsertRange, type OpenEditorBlockAction, type OpenEditorBlockActionContext, OpenEditorBlockDragHandle, type OpenEditorBlockDragHandleProps, type OpenEditorBlockPickerItem, OpenEditorBubbleMenu, type OpenEditorBubbleMenuProps, OpenEditorContent, type OpenEditorContentProps, type OpenEditorController, type OpenEditorExtensionMenu, type OpenEditorOverlayRect, OpenEditorPageHeader, type OpenEditorPageHeaderProps, type OpenEditorReactConfig, type OpenEditorReactExtension, type OpenEditorReactNodeExtension, type OpenEditorReactProps, type OpenEditorSlashMenuIcon, type OpenEditorSlashMenuIconProps, type OpenEditorSlashMenuItem, OpenEditorTableBubbleMenu, type OpenEditorTableBubbleMenuProps, type OpenEditorTableCommand, type OpenEditorTableController, type OpenEditorTableState, type OpenEditorTheme, OpenEditorThemeProvider, type OpenEditorThemeStyle, type OpenEditorThemeToken, OpenEditorViewer, type OpenEditorViewerProps, type OpenEditorViewerRenderer, type SlashState, canRunOpenEditorTableCommand, createOpenEditorThemeStyle, defineOpenEditorReactExtension, defineOpenEditorReactNode, formatAttachmentSize, getDefaultBlockPickerItems, getDefaultSlashMenuItems, getOpenEditorTableState, openEditorTableCommands, openEditorThemeTokenNames, runOpenEditorTableCommand, sortBlockPickerItems, sortSlashMenuItems, useOpenEditor, useOpenEditorBlockInteraction, useOpenEditorController, useOpenEditorThemeStyle };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { openEditorPublicUrlPolicy, openEditorUnsafeUrlPolicy } from '@openedito
|
|
|
3
3
|
import { Checkbox } from '@base-ui/react/checkbox';
|
|
4
4
|
import { shift as shift$1, flip, offset, arrow as arrow$1, size, autoPlacement, hide, inline, computePosition, autoUpdate } from '@floating-ui/dom';
|
|
5
5
|
import { OpenEditorEmojiPicker } from '@openeditor/emoji';
|
|
6
|
+
import { RefreshCw, Link2, Pencil, Trash2, Image as Image$1, Upload, X } from 'lucide-react';
|
|
6
7
|
import React, { createContext, forwardRef, useRef, useState, useEffect, useContext, useMemo, useLayoutEffect, createRef, memo, createElement, version, useSyncExternalStore as useSyncExternalStore$1, useCallback, use, useDebugValue } from 'react';
|
|
7
8
|
import { jsxs, Fragment as Fragment$1, jsx } from 'react/jsx-runtime';
|
|
8
9
|
import { renderMermaidSVG } from 'beautiful-mermaid';
|
|
@@ -33094,6 +33095,7 @@ var createOpenEditorTableController = (getEditor) => ({
|
|
|
33094
33095
|
});
|
|
33095
33096
|
var OpenEditorPageRuntimeContext = createContext(void 0);
|
|
33096
33097
|
var OpenEditorAttachmentRuntimeContext = createContext(void 0);
|
|
33098
|
+
var OpenEditorImageRuntimeContext = createContext(void 0);
|
|
33097
33099
|
var defineOpenEditorReactExtension = (extension) => extension;
|
|
33098
33100
|
var defineOpenEditorReactNode = ({
|
|
33099
33101
|
block,
|
|
@@ -33421,6 +33423,262 @@ var Callout = Node3.create({
|
|
|
33421
33423
|
return ReactNodeViewRenderer(OpenEditorCalloutNodeView);
|
|
33422
33424
|
}
|
|
33423
33425
|
});
|
|
33426
|
+
var imageSnapshotFromNode = (node) => ({
|
|
33427
|
+
imageId: typeof node.attrs?.imageId === "string" ? node.attrs.imageId : null,
|
|
33428
|
+
src: typeof node.attrs?.src === "string" && node.attrs.src.trim() ? node.attrs.src : null,
|
|
33429
|
+
alt: typeof node.attrs?.alt === "string" ? node.attrs.alt : "",
|
|
33430
|
+
width: typeof node.attrs?.width === "number" ? node.attrs.width : null,
|
|
33431
|
+
height: typeof node.attrs?.height === "number" ? node.attrs.height : null
|
|
33432
|
+
});
|
|
33433
|
+
var resolvedImageSrc = (snapshot, runtimeResolved = false) => {
|
|
33434
|
+
if (!snapshot?.src) return null;
|
|
33435
|
+
const safe = openEditorPublicUrlPolicy(snapshot.src, "image");
|
|
33436
|
+
if (safe) return safe;
|
|
33437
|
+
const normalized = snapshot.src.trim();
|
|
33438
|
+
return runtimeResolved && normalized.startsWith("blob:") ? normalized : null;
|
|
33439
|
+
};
|
|
33440
|
+
var webImageInput = (file) => ({
|
|
33441
|
+
name: file.name,
|
|
33442
|
+
mimeType: file.type || null,
|
|
33443
|
+
size: file.size,
|
|
33444
|
+
source: file
|
|
33445
|
+
});
|
|
33446
|
+
var OpenEditorImageNodeView = ({ editor, node, updateAttributes: updateAttributes2, deleteNode: deleteNode2, selected }) => {
|
|
33447
|
+
const runtime = use(OpenEditorImageRuntimeContext);
|
|
33448
|
+
const durable = imageSnapshotFromNode(node);
|
|
33449
|
+
const hasImage = Boolean(durable.imageId || durable.src);
|
|
33450
|
+
const [phase, setPhase] = useState(hasImage ? "ready" : "idle");
|
|
33451
|
+
const [progress, setProgress] = useState(0);
|
|
33452
|
+
const [error, setError] = useState(null);
|
|
33453
|
+
const [input, setInput] = useState(null);
|
|
33454
|
+
const [resolved, setResolved] = useState(null);
|
|
33455
|
+
const [showUrl, setShowUrl] = useState(false);
|
|
33456
|
+
const [url, setUrl] = useState("");
|
|
33457
|
+
const [editingAlt, setEditingAlt] = useState(false);
|
|
33458
|
+
const [alt, setAlt] = useState(durable.alt);
|
|
33459
|
+
const operationRef = useRef(0);
|
|
33460
|
+
const abortRef = useRef(null);
|
|
33461
|
+
const stopCurrent = () => {
|
|
33462
|
+
operationRef.current += 1;
|
|
33463
|
+
abortRef.current?.abort();
|
|
33464
|
+
abortRef.current = null;
|
|
33465
|
+
};
|
|
33466
|
+
const isCurrent = (operation) => operationRef.current === operation;
|
|
33467
|
+
useEffect(() => () => stopCurrent(), []);
|
|
33468
|
+
useEffect(() => {
|
|
33469
|
+
setAlt(durable.alt);
|
|
33470
|
+
}, [durable.alt]);
|
|
33471
|
+
useEffect(() => {
|
|
33472
|
+
if (!durable.imageId || !runtime?.resolveImage) {
|
|
33473
|
+
setResolved(null);
|
|
33474
|
+
return;
|
|
33475
|
+
}
|
|
33476
|
+
const controller = new AbortController();
|
|
33477
|
+
void runtime.resolveImage(durable.imageId, { signal: controller.signal }).then((snapshot) => {
|
|
33478
|
+
if (!controller.signal.aborted) setResolved(snapshot);
|
|
33479
|
+
}).catch(() => {
|
|
33480
|
+
if (!controller.signal.aborted) setResolved(null);
|
|
33481
|
+
});
|
|
33482
|
+
return () => controller.abort();
|
|
33483
|
+
}, [durable.imageId, runtime]);
|
|
33484
|
+
const upload = async (selectedInput, replacing) => {
|
|
33485
|
+
if (!runtime?.uploadImage) return;
|
|
33486
|
+
stopCurrent();
|
|
33487
|
+
const operation = operationRef.current;
|
|
33488
|
+
const controller = new AbortController();
|
|
33489
|
+
abortRef.current = controller;
|
|
33490
|
+
setInput(selectedInput);
|
|
33491
|
+
setProgress(0);
|
|
33492
|
+
setError(null);
|
|
33493
|
+
setShowUrl(false);
|
|
33494
|
+
setPhase("uploading");
|
|
33495
|
+
try {
|
|
33496
|
+
const validation = await runtime.validateImage?.(selectedInput);
|
|
33497
|
+
if (!isCurrent(operation)) return;
|
|
33498
|
+
if (validation && !validation.accepted) throw new Error(validation.message);
|
|
33499
|
+
const callbacks = {
|
|
33500
|
+
signal: controller.signal,
|
|
33501
|
+
onProgress: (value) => {
|
|
33502
|
+
if (isCurrent(operation)) setProgress(Math.max(0, Math.min(1, value)));
|
|
33503
|
+
}
|
|
33504
|
+
};
|
|
33505
|
+
const uploaded = replacing && durable.imageId && runtime.replaceImage ? await runtime.replaceImage(durable.imageId, selectedInput, callbacks) : await runtime.uploadImage(selectedInput, callbacks);
|
|
33506
|
+
if (!isCurrent(operation)) return;
|
|
33507
|
+
updateAttributes2({ ...uploaded, alt: uploaded.alt || durable.alt });
|
|
33508
|
+
if (uploaded.imageId && runtime.resolveImage) {
|
|
33509
|
+
const nextResolved = await runtime.resolveImage(uploaded.imageId, { signal: controller.signal });
|
|
33510
|
+
if (!isCurrent(operation)) return;
|
|
33511
|
+
setResolved(nextResolved);
|
|
33512
|
+
} else {
|
|
33513
|
+
setResolved(uploaded);
|
|
33514
|
+
}
|
|
33515
|
+
setInput(null);
|
|
33516
|
+
setProgress(1);
|
|
33517
|
+
setPhase("ready");
|
|
33518
|
+
} catch (cause) {
|
|
33519
|
+
if (!isCurrent(operation) || controller.signal.aborted) return;
|
|
33520
|
+
setError(cause instanceof Error ? cause.message : "Image upload failed.");
|
|
33521
|
+
setPhase("error");
|
|
33522
|
+
}
|
|
33523
|
+
};
|
|
33524
|
+
const selectAndUpload = async (replacing = false) => {
|
|
33525
|
+
if (!runtime?.selectImage || !runtime.uploadImage) return;
|
|
33526
|
+
stopCurrent();
|
|
33527
|
+
const operation = operationRef.current;
|
|
33528
|
+
const controller = new AbortController();
|
|
33529
|
+
abortRef.current = controller;
|
|
33530
|
+
setError(null);
|
|
33531
|
+
setPhase("selecting");
|
|
33532
|
+
try {
|
|
33533
|
+
const selectedInput = await runtime.selectImage({ signal: controller.signal });
|
|
33534
|
+
if (!isCurrent(operation)) return;
|
|
33535
|
+
if (!selectedInput) {
|
|
33536
|
+
setPhase(hasImage ? "ready" : "idle");
|
|
33537
|
+
return;
|
|
33538
|
+
}
|
|
33539
|
+
await upload(selectedInput, replacing);
|
|
33540
|
+
} catch (cause) {
|
|
33541
|
+
if (!isCurrent(operation) || controller.signal.aborted) return;
|
|
33542
|
+
setError(cause instanceof Error ? cause.message : "Could not select an image.");
|
|
33543
|
+
setPhase("error");
|
|
33544
|
+
}
|
|
33545
|
+
};
|
|
33546
|
+
const submitUrl = (event) => {
|
|
33547
|
+
event.preventDefault();
|
|
33548
|
+
const safe = openEditorPublicUrlPolicy(url.trim(), "image");
|
|
33549
|
+
if (!safe) {
|
|
33550
|
+
setError("Enter a valid http or https image URL.");
|
|
33551
|
+
return;
|
|
33552
|
+
}
|
|
33553
|
+
stopCurrent();
|
|
33554
|
+
updateAttributes2({ imageId: null, src: safe, width: null, height: null });
|
|
33555
|
+
setResolved(null);
|
|
33556
|
+
setError(null);
|
|
33557
|
+
setShowUrl(false);
|
|
33558
|
+
setUrl("");
|
|
33559
|
+
setPhase("ready");
|
|
33560
|
+
};
|
|
33561
|
+
const dropImage = (event) => {
|
|
33562
|
+
if (!editor.isEditable || !runtime?.uploadImage) return;
|
|
33563
|
+
const file = event.dataTransfer.files?.[0];
|
|
33564
|
+
if (!file) return;
|
|
33565
|
+
event.preventDefault();
|
|
33566
|
+
event.stopPropagation();
|
|
33567
|
+
void upload(webImageInput(file), hasImage);
|
|
33568
|
+
};
|
|
33569
|
+
const cancel = () => {
|
|
33570
|
+
stopCurrent();
|
|
33571
|
+
setInput(null);
|
|
33572
|
+
setError(null);
|
|
33573
|
+
setPhase(hasImage ? "ready" : "idle");
|
|
33574
|
+
};
|
|
33575
|
+
const preview = resolvedImageSrc(resolved, true) ?? resolvedImageSrc(durable);
|
|
33576
|
+
const canUpload = Boolean(runtime?.selectImage && runtime.uploadImage);
|
|
33577
|
+
const busy = phase === "selecting" || phase === "uploading";
|
|
33578
|
+
return /* @__PURE__ */ jsxs(
|
|
33579
|
+
NodeViewWrapper,
|
|
33580
|
+
{
|
|
33581
|
+
as: "figure",
|
|
33582
|
+
className: "oe-image",
|
|
33583
|
+
contentEditable: false,
|
|
33584
|
+
"data-selected": selected ? "true" : void 0,
|
|
33585
|
+
"data-state": phase,
|
|
33586
|
+
onDragOver: (event) => {
|
|
33587
|
+
if (canUpload) event.preventDefault();
|
|
33588
|
+
},
|
|
33589
|
+
onDrop: dropImage,
|
|
33590
|
+
children: [
|
|
33591
|
+
preview ? /* @__PURE__ */ jsxs("div", { className: "oe-image-preview", children: [
|
|
33592
|
+
/* @__PURE__ */ jsx("img", { alt: durable.alt, draggable: false, height: durable.height ?? void 0, src: preview, width: durable.width ?? void 0 }),
|
|
33593
|
+
editor.isEditable ? /* @__PURE__ */ jsxs("div", { "aria-label": "Image actions", className: "oe-image-actions", role: "toolbar", children: [
|
|
33594
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => {
|
|
33595
|
+
if (canUpload) void selectAndUpload(true);
|
|
33596
|
+
else setShowUrl(true);
|
|
33597
|
+
}, type: "button", children: [
|
|
33598
|
+
/* @__PURE__ */ jsx(RefreshCw, { "aria-hidden": "true" }),
|
|
33599
|
+
" Replace"
|
|
33600
|
+
] }),
|
|
33601
|
+
/* @__PURE__ */ jsx("button", { "aria-label": "Use image URL", onClick: () => {
|
|
33602
|
+
setError(null);
|
|
33603
|
+
setShowUrl(true);
|
|
33604
|
+
}, title: "Replace from a link", type: "button", children: /* @__PURE__ */ jsx(Link2, { "aria-hidden": "true" }) }),
|
|
33605
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => setEditingAlt(true), type: "button", children: [
|
|
33606
|
+
/* @__PURE__ */ jsx(Pencil, { "aria-hidden": "true" }),
|
|
33607
|
+
" Alt text"
|
|
33608
|
+
] }),
|
|
33609
|
+
/* @__PURE__ */ jsx("button", { "aria-label": "Remove image", className: "oe-image-remove", onClick: deleteNode2, title: "Remove image", type: "button", children: /* @__PURE__ */ jsx(Trash2, { "aria-hidden": "true" }) })
|
|
33610
|
+
] }) : null
|
|
33611
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "oe-image-empty", children: [
|
|
33612
|
+
/* @__PURE__ */ jsx("span", { className: "oe-image-empty-icon", children: /* @__PURE__ */ jsx(Image$1, { "aria-hidden": "true" }) }),
|
|
33613
|
+
/* @__PURE__ */ jsxs("div", { className: "oe-image-empty-copy", children: [
|
|
33614
|
+
/* @__PURE__ */ jsx("strong", { children: "Add an image" }),
|
|
33615
|
+
/* @__PURE__ */ jsx("span", { children: canUpload ? "Upload, drop, or embed from a link" : "Embed from a link" })
|
|
33616
|
+
] }),
|
|
33617
|
+
canUpload ? /* @__PURE__ */ jsx("button", { "aria-busy": phase === "selecting", "aria-label": "Upload image", className: "oe-image-empty-action", disabled: busy, onClick: () => void selectAndUpload(false), title: "Upload image", type: "button", children: /* @__PURE__ */ jsx(Upload, { "aria-hidden": "true" }) }) : null,
|
|
33618
|
+
/* @__PURE__ */ jsx("button", { "aria-label": "Use image URL", className: "oe-image-empty-action", disabled: busy, onClick: () => {
|
|
33619
|
+
setError(null);
|
|
33620
|
+
setShowUrl(true);
|
|
33621
|
+
}, title: "Use image URL", type: "button", children: /* @__PURE__ */ jsx(Link2, { "aria-hidden": "true" }) }),
|
|
33622
|
+
editor.isEditable ? /* @__PURE__ */ jsx("button", { "aria-label": "Remove empty image block", className: "oe-image-empty-action oe-image-remove", onClick: deleteNode2, title: "Remove image block", type: "button", children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" }) }) : null
|
|
33623
|
+
] }),
|
|
33624
|
+
phase === "uploading" ? /* @__PURE__ */ jsxs("div", { className: "oe-image-upload-status", children: [
|
|
33625
|
+
/* @__PURE__ */ jsx("div", { className: "oe-image-progress", role: "progressbar", "aria-label": `Uploading ${input?.name ?? "image"}`, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": Math.round(progress * 100), children: /* @__PURE__ */ jsx("span", { style: { width: `${progress * 100}%` } }) }),
|
|
33626
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
33627
|
+
Math.round(progress * 100),
|
|
33628
|
+
"%"
|
|
33629
|
+
] }),
|
|
33630
|
+
/* @__PURE__ */ jsx("button", { onClick: cancel, type: "button", children: "Cancel" })
|
|
33631
|
+
] }) : null,
|
|
33632
|
+
showUrl ? /* @__PURE__ */ jsxs("form", { className: "oe-image-panel", onSubmit: submitUrl, children: [
|
|
33633
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
33634
|
+
/* @__PURE__ */ jsx("span", { children: "Image URL" }),
|
|
33635
|
+
/* @__PURE__ */ jsx("input", { autoFocus: true, "aria-label": "Image URL", inputMode: "url", onChange: (event) => setUrl(event.target.value), placeholder: "https://example.com/image.jpg", value: url })
|
|
33636
|
+
] }),
|
|
33637
|
+
/* @__PURE__ */ jsx("button", { className: "oe-image-primary", type: "submit", children: "Use image" }),
|
|
33638
|
+
/* @__PURE__ */ jsx("button", { onClick: () => {
|
|
33639
|
+
setShowUrl(false);
|
|
33640
|
+
setError(null);
|
|
33641
|
+
}, type: "button", children: "Cancel" })
|
|
33642
|
+
] }) : null,
|
|
33643
|
+
editingAlt ? /* @__PURE__ */ jsxs("form", { className: "oe-image-panel", onSubmit: (event) => {
|
|
33644
|
+
event.preventDefault();
|
|
33645
|
+
updateAttributes2({ alt });
|
|
33646
|
+
setEditingAlt(false);
|
|
33647
|
+
}, children: [
|
|
33648
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
33649
|
+
/* @__PURE__ */ jsx("span", { children: "Alternative text" }),
|
|
33650
|
+
/* @__PURE__ */ jsx("input", { autoFocus: true, "aria-label": "Alternative text", onChange: (event) => setAlt(event.target.value), placeholder: "Describe this image", value: alt })
|
|
33651
|
+
] }),
|
|
33652
|
+
/* @__PURE__ */ jsx("button", { className: "oe-image-primary", type: "submit", children: "Save" }),
|
|
33653
|
+
/* @__PURE__ */ jsx("button", { onClick: () => {
|
|
33654
|
+
setAlt(durable.alt);
|
|
33655
|
+
setEditingAlt(false);
|
|
33656
|
+
}, type: "button", children: "Cancel" })
|
|
33657
|
+
] }) : null,
|
|
33658
|
+
error ? /* @__PURE__ */ jsxs("div", { className: "oe-image-error", role: "alert", children: [
|
|
33659
|
+
/* @__PURE__ */ jsx("span", { children: error }),
|
|
33660
|
+
input ? /* @__PURE__ */ jsx("button", { onClick: () => void upload(input, hasImage), type: "button", children: "Retry" }) : null
|
|
33661
|
+
] }) : null
|
|
33662
|
+
]
|
|
33663
|
+
}
|
|
33664
|
+
);
|
|
33665
|
+
};
|
|
33666
|
+
var OpenEditorImage = index_default2.extend({
|
|
33667
|
+
addAttributes() {
|
|
33668
|
+
return {
|
|
33669
|
+
...this.parent?.(),
|
|
33670
|
+
imageId: {
|
|
33671
|
+
default: null,
|
|
33672
|
+
parseHTML: (element) => element.getAttribute("data-openeditor-image-id"),
|
|
33673
|
+
renderHTML: (attributes) => attributes.imageId ? { "data-openeditor-image-id": attributes.imageId } : {}
|
|
33674
|
+
},
|
|
33675
|
+
src: { default: null }
|
|
33676
|
+
};
|
|
33677
|
+
},
|
|
33678
|
+
addNodeView() {
|
|
33679
|
+
return ReactNodeViewRenderer(OpenEditorImageNodeView);
|
|
33680
|
+
}
|
|
33681
|
+
});
|
|
33424
33682
|
var attachmentSnapshotFromNode = (node) => ({
|
|
33425
33683
|
attachmentId: typeof node.attrs?.attachmentId === "string" ? node.attrs.attachmentId : null,
|
|
33426
33684
|
name: typeof node.attrs?.name === "string" ? node.attrs.name : "",
|
|
@@ -33440,12 +33698,19 @@ var formatAttachmentSize = (size3) => {
|
|
|
33440
33698
|
}
|
|
33441
33699
|
return `${value >= 10 ? value.toFixed(0) : value.toFixed(1)} ${units[unit]}`;
|
|
33442
33700
|
};
|
|
33443
|
-
var
|
|
33444
|
-
|
|
33445
|
-
const
|
|
33446
|
-
|
|
33701
|
+
var IMAGE_ATTACHMENT_EXTENSION = /\.(?:avif|gif|jpe?g|png|svg|webp)$/i;
|
|
33702
|
+
var AttachmentPreview = ({ snapshot, url }) => {
|
|
33703
|
+
const isImage = snapshot.mimeType?.toLowerCase().startsWith("image/") || IMAGE_ATTACHMENT_EXTENSION.test(snapshot.name);
|
|
33704
|
+
if (!url || !isImage) return null;
|
|
33705
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "oe-attachment-thumbnail", children: /* @__PURE__ */ jsx("img", { alt: "", src: url }) });
|
|
33706
|
+
};
|
|
33707
|
+
var attachmentPreviewUrl = (url, allowBlob = false) => {
|
|
33708
|
+
if (!url) return null;
|
|
33709
|
+
const safeUrl = openEditorPublicUrlPolicy(url, "attachment");
|
|
33710
|
+
if (safeUrl) return safeUrl;
|
|
33711
|
+
const normalized = url.trim();
|
|
33712
|
+
return allowBlob && normalized.startsWith("blob:") ? normalized : null;
|
|
33447
33713
|
};
|
|
33448
|
-
var AttachmentIcon = ({ snapshot }) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "oe-attachment-icon", children: /* @__PURE__ */ jsx("span", { children: attachmentTypeLabel(snapshot).slice(0, 4) }) });
|
|
33449
33714
|
var OpenEditorAttachmentNodeView = ({ editor, node, updateAttributes: updateAttributes2, deleteNode: deleteNode2 }) => {
|
|
33450
33715
|
const runtime = use(OpenEditorAttachmentRuntimeContext);
|
|
33451
33716
|
const durable = attachmentSnapshotFromNode(node);
|
|
@@ -33453,6 +33718,9 @@ var OpenEditorAttachmentNodeView = ({ editor, node, updateAttributes: updateAttr
|
|
|
33453
33718
|
const [progress, setProgress] = useState(0);
|
|
33454
33719
|
const [error, setError] = useState(null);
|
|
33455
33720
|
const [input, setInput] = useState(null);
|
|
33721
|
+
const [resolved, setResolved] = useState(null);
|
|
33722
|
+
const [renaming, setRenaming] = useState(false);
|
|
33723
|
+
const [draftName, setDraftName] = useState("");
|
|
33456
33724
|
const operationRef = useRef(0);
|
|
33457
33725
|
const abortRef = useRef(null);
|
|
33458
33726
|
const isCurrent = (operation) => operationRef.current === operation;
|
|
@@ -33462,6 +33730,19 @@ var OpenEditorAttachmentNodeView = ({ editor, node, updateAttributes: updateAttr
|
|
|
33462
33730
|
abortRef.current = null;
|
|
33463
33731
|
};
|
|
33464
33732
|
useEffect(() => () => stopCurrent(), []);
|
|
33733
|
+
useEffect(() => {
|
|
33734
|
+
if (!durable.attachmentId || !runtime?.resolveAttachment) {
|
|
33735
|
+
setResolved(null);
|
|
33736
|
+
return;
|
|
33737
|
+
}
|
|
33738
|
+
const controller = new AbortController();
|
|
33739
|
+
void runtime.resolveAttachment(durable.attachmentId, { signal: controller.signal }).then((snapshot) => {
|
|
33740
|
+
if (!controller.signal.aborted) setResolved(snapshot);
|
|
33741
|
+
}).catch(() => {
|
|
33742
|
+
if (!controller.signal.aborted) setResolved(null);
|
|
33743
|
+
});
|
|
33744
|
+
return () => controller.abort();
|
|
33745
|
+
}, [durable.attachmentId, runtime]);
|
|
33465
33746
|
const upload = async (selected, replacing) => {
|
|
33466
33747
|
if (!runtime?.uploadAttachment) return;
|
|
33467
33748
|
stopCurrent();
|
|
@@ -33471,6 +33752,8 @@ var OpenEditorAttachmentNodeView = ({ editor, node, updateAttributes: updateAttr
|
|
|
33471
33752
|
setInput(selected);
|
|
33472
33753
|
setProgress(0);
|
|
33473
33754
|
setError(null);
|
|
33755
|
+
setResolved(null);
|
|
33756
|
+
setRenaming(false);
|
|
33474
33757
|
setPhase("uploading");
|
|
33475
33758
|
try {
|
|
33476
33759
|
const validation = await runtime.validateAttachment?.(selected);
|
|
@@ -33485,6 +33768,11 @@ var OpenEditorAttachmentNodeView = ({ editor, node, updateAttributes: updateAttr
|
|
|
33485
33768
|
setInput(null);
|
|
33486
33769
|
setProgress(1);
|
|
33487
33770
|
setPhase("ready");
|
|
33771
|
+
if (uploaded.attachmentId && runtime.resolveAttachment) {
|
|
33772
|
+
void runtime.resolveAttachment(uploaded.attachmentId, { signal: controller.signal }).then((snapshot) => {
|
|
33773
|
+
if (isCurrent(operation) && !controller.signal.aborted) setResolved(snapshot);
|
|
33774
|
+
}).catch(() => void 0);
|
|
33775
|
+
}
|
|
33488
33776
|
} catch (cause) {
|
|
33489
33777
|
if (!isCurrent(operation) || controller.signal.aborted) return;
|
|
33490
33778
|
setError(cause instanceof Error ? cause.message : "Upload failed.");
|
|
@@ -33528,26 +33816,46 @@ var OpenEditorAttachmentNodeView = ({ editor, node, updateAttributes: updateAttr
|
|
|
33528
33816
|
};
|
|
33529
33817
|
const name = durable.name || input?.name || "Choose a file";
|
|
33530
33818
|
const display = durable.name ? durable : { ...durable, name, mimeType: input?.mimeType ?? null, size: input?.size ?? null };
|
|
33819
|
+
const previewSnapshot = resolved ? { ...resolved, name } : display;
|
|
33820
|
+
const previewUrl = attachmentPreviewUrl(previewSnapshot.url, Boolean(resolved));
|
|
33821
|
+
const commitRename = () => {
|
|
33822
|
+
const nextName = draftName.trim() || name;
|
|
33823
|
+
setRenaming(false);
|
|
33824
|
+
if (nextName === name) return;
|
|
33825
|
+
updateAttributes2({ name: nextName });
|
|
33826
|
+
if (durable.attachmentId) void runtime?.renameAttachment?.(durable.attachmentId, nextName);
|
|
33827
|
+
};
|
|
33531
33828
|
const open = () => {
|
|
33532
|
-
|
|
33533
|
-
|
|
33829
|
+
const attachment = resolved ? { ...resolved, name } : durable;
|
|
33830
|
+
if (runtime?.openAttachment) void runtime.openAttachment(attachment);
|
|
33831
|
+
else if (attachment.url) window.open(attachment.url, "_blank", "noopener,noreferrer");
|
|
33534
33832
|
};
|
|
33535
33833
|
return /* @__PURE__ */ jsxs(NodeViewWrapper, { className: "oe-attachment", "data-state": phase, children: [
|
|
33536
|
-
/* @__PURE__ */ jsx(
|
|
33834
|
+
(durable.attachmentId || durable.url) && phase !== "uploading" ? /* @__PURE__ */ jsx("button", { "aria-label": `Open ${name}`, className: "oe-attachment-open-control", contentEditable: false, onClick: open, type: "button" }) : null,
|
|
33835
|
+
/* @__PURE__ */ jsx(AttachmentPreview, { snapshot: previewSnapshot, url: previewUrl }),
|
|
33537
33836
|
/* @__PURE__ */ jsxs("div", { className: "oe-attachment-body", children: [
|
|
33538
|
-
/* @__PURE__ */ jsx("input", { "aria-label": "
|
|
33539
|
-
if (
|
|
33540
|
-
|
|
33541
|
-
|
|
33837
|
+
renaming ? /* @__PURE__ */ jsx("input", { autoFocus: true, "aria-label": "Rename file", className: "oe-attachment-name oe-attachment-name-input", value: draftName, onBlur: commitRename, onChange: (event) => setDraftName(event.target.value), onKeyDown: (event) => {
|
|
33838
|
+
if (event.key === "Enter") {
|
|
33839
|
+
event.preventDefault();
|
|
33840
|
+
event.currentTarget.blur();
|
|
33841
|
+
} else if (event.key === "Escape") {
|
|
33842
|
+
event.preventDefault();
|
|
33843
|
+
setDraftName(name);
|
|
33844
|
+
setRenaming(false);
|
|
33845
|
+
}
|
|
33846
|
+
} }) : /* @__PURE__ */ jsx("span", { className: "oe-attachment-name", children: name }),
|
|
33542
33847
|
phase === "uploading" ? /* @__PURE__ */ jsx("div", { className: "oe-attachment-progress", role: "progressbar", "aria-label": `Uploading ${name}`, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": Math.round(progress * 100), children: /* @__PURE__ */ jsx("span", { style: { width: `${progress * 100}%` } }) }) : null,
|
|
33543
33848
|
error ? /* @__PURE__ */ jsx("div", { className: "oe-attachment-error", role: "alert", children: error }) : null
|
|
33544
33849
|
] }),
|
|
33545
33850
|
/* @__PURE__ */ jsxs("div", { className: "oe-attachment-actions", contentEditable: false, children: [
|
|
33546
33851
|
phase === "uploading" || phase === "selecting" ? /* @__PURE__ */ jsx("button", { onClick: cancel, type: "button", children: "Cancel" }) : null,
|
|
33547
33852
|
phase === "error" && input ? /* @__PURE__ */ jsx("button", { onClick: () => void upload(input, Boolean(durable.attachmentId)), type: "button", children: "Retry" }) : null,
|
|
33548
|
-
|
|
33549
|
-
|
|
33550
|
-
|
|
33853
|
+
editor.isEditable && durable.name && phase !== "uploading" && phase !== "selecting" ? /* @__PURE__ */ jsx("button", { "aria-label": `Rename ${name}`, onClick: () => {
|
|
33854
|
+
setDraftName(name);
|
|
33855
|
+
setRenaming(true);
|
|
33856
|
+
}, title: "Rename file", type: "button", children: /* @__PURE__ */ jsx(Pencil, { "aria-hidden": "true" }) }) : null,
|
|
33857
|
+
editor.isEditable && runtime?.selectAttachment && runtime.uploadAttachment && phase !== "uploading" ? /* @__PURE__ */ jsx("button", { "aria-label": `Replace ${name}`, onClick: () => void selectAndUpload(Boolean(durable.attachmentId)), title: "Replace file", type: "button", children: /* @__PURE__ */ jsx(RefreshCw, { "aria-hidden": "true" }) }) : null,
|
|
33858
|
+
editor.isEditable ? /* @__PURE__ */ jsx("button", { "aria-label": `Remove ${name}`, className: "oe-attachment-remove", onClick: remove, title: "Remove file", type: "button", children: /* @__PURE__ */ jsx(Trash2, { "aria-hidden": "true" }) }) : null
|
|
33551
33859
|
] })
|
|
33552
33860
|
] });
|
|
33553
33861
|
};
|
|
@@ -33583,12 +33891,10 @@ var OpenEditorAttachmentViewer = ({
|
|
|
33583
33891
|
return () => controller.abort();
|
|
33584
33892
|
}, [cached.attachmentId, cached.url, runtime]);
|
|
33585
33893
|
const snapshot = state.snapshot ?? cached;
|
|
33894
|
+
const previewUrl = snapshot.url ? urlPolicy(snapshot.url, "attachment") : null;
|
|
33586
33895
|
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
33587
|
-
/* @__PURE__ */ jsx(
|
|
33588
|
-
/* @__PURE__ */
|
|
33589
|
-
/* @__PURE__ */ jsx("strong", { className: "oe-attachment-name", children: snapshot.name || "Attachment unavailable" }),
|
|
33590
|
-
/* @__PURE__ */ jsx("span", { className: "oe-attachment-meta", children: state.phase === "resolving" ? "Loading\u2026" : state.phase === "missing" ? "Missing or inaccessible" : [formatAttachmentSize(snapshot.size), attachmentTypeLabel(snapshot)].filter(Boolean).join(" \xB7 ") })
|
|
33591
|
-
] })
|
|
33896
|
+
/* @__PURE__ */ jsx(AttachmentPreview, { snapshot, url: previewUrl }),
|
|
33897
|
+
/* @__PURE__ */ jsx("span", { className: "oe-attachment-body", children: /* @__PURE__ */ jsx("strong", { className: "oe-attachment-name", children: state.phase === "resolving" ? "Loading\u2026" : snapshot.name || "Attachment unavailable" }) })
|
|
33592
33898
|
] });
|
|
33593
33899
|
if (state.phase === "ready" && runtime?.openAttachment) return /* @__PURE__ */ jsx("button", { className: "oe-attachment oe-attachment-viewer", "data-openeditor-attachment": true, onClick: () => void runtime.openAttachment?.(snapshot), type: "button", children: content });
|
|
33594
33900
|
const href = snapshot.url ? urlPolicy(snapshot.url, "attachment") : null;
|
|
@@ -33846,7 +34152,7 @@ var ToggleListViewerItem = ({ children, initiallyOpen }) => {
|
|
|
33846
34152
|
className: "oe-toggle-list-trigger",
|
|
33847
34153
|
onClick: () => setOpen((current) => !current),
|
|
33848
34154
|
type: "button",
|
|
33849
|
-
children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "oe-toggle-list-chevron"
|
|
34155
|
+
children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "oe-toggle-list-chevron" })
|
|
33850
34156
|
}
|
|
33851
34157
|
),
|
|
33852
34158
|
/* @__PURE__ */ jsx("div", { className: "oe-toggle-list-content", children })
|
|
@@ -33929,7 +34235,7 @@ var createEditorExtensions = (getPlaceholder, customExtensions, slashSuggestion)
|
|
|
33929
34235
|
}),
|
|
33930
34236
|
index_default10.configure({ horizontalRule: false, link: false, underline: false }),
|
|
33931
34237
|
Divider,
|
|
33932
|
-
|
|
34238
|
+
OpenEditorImage.configure({ allowBase64: true }),
|
|
33933
34239
|
index_default3.configure({ openOnClick: false }),
|
|
33934
34240
|
index_default8,
|
|
33935
34241
|
index_default6,
|
|
@@ -33998,6 +34304,7 @@ var useOpenEditorController = ({
|
|
|
33998
34304
|
onFilePasteDrop,
|
|
33999
34305
|
pageRuntime,
|
|
34000
34306
|
attachmentRuntime,
|
|
34307
|
+
imageRuntime,
|
|
34001
34308
|
enabledBlocks,
|
|
34002
34309
|
slashMenuItems = [],
|
|
34003
34310
|
extensions = EMPTY_EXTENSIONS,
|
|
@@ -34345,6 +34652,7 @@ var useOpenEditorController = ({
|
|
|
34345
34652
|
extensions,
|
|
34346
34653
|
pageRuntime,
|
|
34347
34654
|
attachmentRuntime,
|
|
34655
|
+
imageRuntime,
|
|
34348
34656
|
enabledBlocks,
|
|
34349
34657
|
slashState,
|
|
34350
34658
|
dismissSlashMenu,
|
|
@@ -34790,6 +35098,27 @@ var renderHeadingViewer = (node, children) => {
|
|
|
34790
35098
|
return /* @__PURE__ */ jsx("h6", { ...props, children });
|
|
34791
35099
|
}
|
|
34792
35100
|
};
|
|
35101
|
+
var OpenEditorImageViewer = ({ node, resolveUrl }) => {
|
|
35102
|
+
const runtime = use(OpenEditorImageRuntimeContext);
|
|
35103
|
+
const durable = imageSnapshotFromNode(node);
|
|
35104
|
+
const [resolved, setResolved] = useState(null);
|
|
35105
|
+
useEffect(() => {
|
|
35106
|
+
if (!durable.imageId || !runtime?.resolveImage) {
|
|
35107
|
+
setResolved(null);
|
|
35108
|
+
return;
|
|
35109
|
+
}
|
|
35110
|
+
const controller = new AbortController();
|
|
35111
|
+
void runtime.resolveImage(durable.imageId, { signal: controller.signal }).then((snapshot) => {
|
|
35112
|
+
if (!controller.signal.aborted) setResolved(snapshot);
|
|
35113
|
+
}).catch(() => {
|
|
35114
|
+
if (!controller.signal.aborted) setResolved(null);
|
|
35115
|
+
});
|
|
35116
|
+
return () => controller.abort();
|
|
35117
|
+
}, [durable.imageId, runtime]);
|
|
35118
|
+
const src = resolvedImageSrc(resolved, true) ?? resolveUrl(durable.src, "image");
|
|
35119
|
+
if (!src) return null;
|
|
35120
|
+
return /* @__PURE__ */ jsx("img", { alt: durable.alt, height: durable.height ?? void 0, src, width: durable.width ?? void 0 });
|
|
35121
|
+
};
|
|
34793
35122
|
var BUILT_IN_VIEWER_RENDERERS = {
|
|
34794
35123
|
paragraph: ({ node, children }) => /* @__PURE__ */ jsx("p", { style: viewerTextAlignStyle(node), children: node.content?.length ? children : /* @__PURE__ */ jsx("br", {}) }),
|
|
34795
35124
|
heading: ({ node, children }) => renderHeadingViewer(node, children),
|
|
@@ -34802,12 +35131,7 @@ var BUILT_IN_VIEWER_RENDERERS = {
|
|
|
34802
35131
|
return /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { className: language ? `language-${language}` : void 0, children: getNodeText(node) }) });
|
|
34803
35132
|
},
|
|
34804
35133
|
divider: () => /* @__PURE__ */ jsx("hr", {}),
|
|
34805
|
-
image: ({ node, resolveUrl }) => {
|
|
34806
|
-
const src = resolveUrl(node.attrs?.src, "image");
|
|
34807
|
-
if (!src) return null;
|
|
34808
|
-
const dimension = (value) => typeof value === "string" || typeof value === "number" ? value : void 0;
|
|
34809
|
-
return /* @__PURE__ */ jsx("img", { alt: typeof node.attrs?.alt === "string" ? node.attrs.alt : "", height: dimension(node.attrs?.height), src, title: typeof node.attrs?.title === "string" ? node.attrs.title : void 0, width: dimension(node.attrs?.width) });
|
|
34810
|
-
},
|
|
35134
|
+
image: ({ node, resolveUrl }) => /* @__PURE__ */ jsx(OpenEditorImageViewer, { node, resolveUrl }),
|
|
34811
35135
|
columns: ({ node, children }) => {
|
|
34812
35136
|
const columnCount = Math.min(Math.max(node.content?.length ?? 0, 2), 4);
|
|
34813
35137
|
return /* @__PURE__ */ jsx("section", { className: "oe-columns", "data-openeditor-columns": node.content?.length ?? 0, style: { "--oe-column-count": columnCount }, children });
|
|
@@ -34864,7 +35188,7 @@ var OpenEditorContent = ({
|
|
|
34864
35188
|
className = "oe-canvas"
|
|
34865
35189
|
}) => {
|
|
34866
35190
|
const internalController = controller;
|
|
34867
|
-
return /* @__PURE__ */ jsx(OpenEditorAttachmentRuntimeContext.Provider, { value: internalController.attachmentRuntime, children: /* @__PURE__ */ jsx(OpenEditorPageRuntimeContext.Provider, { value: internalController.pageRuntime, children: /* @__PURE__ */ jsx(EditorContent, { editor: internalController.editor, className }) }) });
|
|
35191
|
+
return /* @__PURE__ */ jsx(OpenEditorImageRuntimeContext.Provider, { value: internalController.imageRuntime, children: /* @__PURE__ */ jsx(OpenEditorAttachmentRuntimeContext.Provider, { value: internalController.attachmentRuntime, children: /* @__PURE__ */ jsx(OpenEditorPageRuntimeContext.Provider, { value: internalController.pageRuntime, children: /* @__PURE__ */ jsx(EditorContent, { editor: internalController.editor, className }) }) }) });
|
|
34868
35192
|
};
|
|
34869
35193
|
var OpenEditorViewer = ({
|
|
34870
35194
|
document: document2,
|
|
@@ -34873,6 +35197,7 @@ var OpenEditorViewer = ({
|
|
|
34873
35197
|
extensions = EMPTY_EXTENSIONS,
|
|
34874
35198
|
pageRuntime,
|
|
34875
35199
|
attachmentRuntime,
|
|
35200
|
+
imageRuntime,
|
|
34876
35201
|
urlPolicy = openEditorPublicUrlPolicy
|
|
34877
35202
|
}) => {
|
|
34878
35203
|
const registry = useMemo(() => createReactBlockRegistry(extensions), [extensions]);
|
|
@@ -34880,9 +35205,9 @@ var OpenEditorViewer = ({
|
|
|
34880
35205
|
...Object.fromEntries(extensions.map((extension) => [extension.block.name, extension.viewer])),
|
|
34881
35206
|
...renderers
|
|
34882
35207
|
}), [extensions, renderers]);
|
|
34883
|
-
return /* @__PURE__ */ jsx(OpenEditorAttachmentRuntimeContext.Provider, { value: attachmentRuntime, children: /* @__PURE__ */ jsx(OpenEditorPageRuntimeContext.Provider, { value: pageRuntime, children: /* @__PURE__ */ jsx("div", { className, children: normalizeDocument(document2).content.map(
|
|
35208
|
+
return /* @__PURE__ */ jsx(OpenEditorImageRuntimeContext.Provider, { value: imageRuntime, children: /* @__PURE__ */ jsx(OpenEditorAttachmentRuntimeContext.Provider, { value: attachmentRuntime, children: /* @__PURE__ */ jsx(OpenEditorPageRuntimeContext.Provider, { value: pageRuntime, children: /* @__PURE__ */ jsx("div", { className, children: normalizeDocument(document2).content.map(
|
|
34884
35209
|
(node, index) => renderViewerNode(node, `viewer-${index}`, mergedRenderers, registry, urlPolicy)
|
|
34885
|
-
) }) }) });
|
|
35210
|
+
) }) }) }) });
|
|
34886
35211
|
};
|
|
34887
35212
|
var useOpenEditor = useOpenEditorController;
|
|
34888
35213
|
|