@openeditor/react 0.0.27 → 0.0.28

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 CHANGED
@@ -9,6 +9,7 @@ Pass `attachmentRuntime` to `useOpenEditorController` for editable attachments a
9
9
  - `useOpenEditorController` for editor state, commands, document exports, and selection state
10
10
  - `OpenEditorContent` for the editable Tiptap-backed content surface
11
11
  - `OpenEditorViewer` for read-only React rendering
12
+ - `OpenEditorPageHeader` for host-backed page title and icon editing
12
13
  - `getDefaultBlockPickerItems` for host-owned block rails, pickers, and command palettes
13
14
  - `getDefaultSlashMenuItems` for UI packages that want the built-in insert actions
14
15
  - stable-identity block targeting and transaction-backed copy, duplicate, move, and delete commands
@@ -120,12 +121,24 @@ themeable SVG rendering and retain their canonical Mermaid source in the documen
120
121
  import { OpenEditorContent, useOpenEditorController } from "@openeditor/react";
121
122
 
122
123
  export function Example() {
123
- const controller = useOpenEditorController();
124
+ const controller = useOpenEditorController({ initialDocument });
124
125
 
125
126
  return <OpenEditorContent controller={controller} />;
126
127
  }
127
128
  ```
128
129
 
130
+ Editors are uncontrolled on both web and native. `initialDocument` is read once;
131
+ use `controller.setContent(document)` for an undoable programmatic replacement.
132
+ It emits `onChange` and preserves the current selection where possible. Runtime
133
+ changes to callbacks, `editable`, and `placeholder` are applied without remounting.
134
+
135
+ Exports are lazy. `controller.export("html")` returns publishing-safe built-in
136
+ HTML. `controller.exportUnsafe("html")` opts into trusted custom extension HTML.
137
+
138
+ Pass `enabledBlocks` to restrict authoring without removing nodes from the
139
+ schema. Existing disabled blocks remain readable. Runtime-dependent insertion
140
+ is automatically disabled unless page creation or attachment upload is wired.
141
+
129
142
  ## Custom blocks
130
143
 
131
144
  Custom blocks belong to the consuming product. OpenEditor hosts them without
@@ -215,8 +228,9 @@ export function ProductEditor() {
215
228
  }
216
229
  ```
217
230
 
218
- Pass the same extension array to `OpenEditorViewer`. The controller automatically
219
- uses registered exporters for `controller.exports` and registered blocks appear
231
+ Pass the same extension array to `OpenEditorViewer`. The controller uses
232
+ registered exporters only through the explicit `controller.exportUnsafe(...)`
233
+ path, and registered blocks appear
220
234
  in `getDefaultSlashMenuItems`, which powers `@openeditor/ui`.
221
235
 
222
236
  Insert-menu entries accept an optional React `icon` component and numeric `order`.
@@ -225,8 +239,7 @@ blocks can be placed between them (for example, `order: 250` appears between
225
239
  Heading 1 and Heading 2). Entries with the same order retain their declaration
226
240
  order, and entries without an order appear after ordered entries. This metadata
227
241
  is shared by slash menus and `getDefaultBlockPickerItems`; use `insertMenu: false`
228
- to keep a block out of both discovery surfaces. The legacy `slashMenu` extension
229
- field remains supported. The same fields are available on direct
242
+ to keep a block out of both discovery surfaces. The same fields are available on direct
230
243
  `slashMenuItems`; a slash menu's explicit `items` prop is also sorted by this rule.
231
244
 
232
245
  Block `name` is the stable public identifier. Namespace consumer blocks to avoid
@@ -238,19 +251,23 @@ Duplicate names and node types fail immediately during editor construction.
238
251
 
239
252
  The first-party `page` block models a Notion-style child page as a reference to
240
253
  a separately persisted document. Supply `pageRuntime` to connect creation,
241
- resolution, renaming, and navigation to your application:
254
+ resolution, metadata updates, and navigation to your application:
242
255
 
243
256
  ```tsx
244
257
  const controller = useOpenEditorController({
245
258
  pageRuntime: {
246
259
  createPage: async ({ title, icon }) => api.pages.create({ title, icon }),
247
260
  resolvePage: async (pageId) => api.pages.get(pageId),
248
- renamePage: async (pageId, title) => api.pages.rename(pageId, title),
261
+ updatePage: async (pageId, update) => api.pages.update(pageId, update),
249
262
  openPage: (page) => router.push(`/pages/${page.pageId}`),
250
263
  },
251
264
  });
252
265
  ```
253
266
 
267
+ Render `OpenEditorPageHeader` on the opened page surface to provide the canonical
268
+ editable title and icon. Both fields call `pageRuntime.updatePage`; page references
269
+ resolve the same page entity instead of owning independent metadata.
270
+
254
271
  The document stores only the stable reference and cached presentation. Page
255
272
  contents, hierarchy, permissions, routing, and deletion remain host-owned.
256
273
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, CSSProperties, RefObject, ComponentType } from 'react';
3
- import { OpenEditorBlockRef, OpenEditorDocument, BlockSpec, ProseMirrorNode, OpenEditorAttachmentRuntime } from '@openeditor/core';
4
- export { OpenEditorBlockRef } from '@openeditor/core';
5
- import { toExportBundle, OpenEditorExporters } from '@openeditor/exporters';
3
+ import { OpenEditorBlockRef, OpenEditorDocument, BlockSpec, ProseMirrorNode, OpenEditorPageRuntime, OpenEditorAttachmentRuntime, OpenEditorPageSnapshot, OpenEditorAuthoringCapabilities } from '@openeditor/core';
4
+ export { OpenEditorBlockRef, OpenEditorPageRuntime, OpenEditorPageSnapshot } from '@openeditor/core';
5
+ import { ExportFormat, OpenEditorExporters } from '@openeditor/exporters';
6
6
  export { seedDocument } from '@openeditor/extensions';
7
7
  import { AnyExtension, NodeConfig } from '@tiptap/core';
8
8
  import { ReactNodeViewProps } from '@tiptap/react';
@@ -29,23 +29,7 @@ declare function OpenEditorThemeProvider({ children, className, theme }: {
29
29
  theme: OpenEditorTheme;
30
30
  }): react.JSX.Element;
31
31
 
32
- type OpenEditorPageSnapshot = {
33
- pageId: string;
34
- title: string;
35
- icon?: string | null;
36
- href?: string | null;
37
- };
38
- type OpenEditorPageRuntime = {
39
- createPage?: (input: {
40
- title: string;
41
- icon?: string | null;
42
- }) => Promise<OpenEditorPageSnapshot>;
43
- resolvePage?: (pageId: string) => Promise<OpenEditorPageSnapshot | null>;
44
- renamePage?: (pageId: string, title: string) => Promise<void> | void;
45
- updatePageIcon?: (pageId: string, icon: string) => Promise<void> | void;
46
- openPage?: (page: OpenEditorPageSnapshot) => Promise<void> | void;
47
- };
48
- type OpenEditorReactProps = {
32
+ type OpenEditorReactProps = OpenEditorAuthoringCapabilities & {
49
33
  initialDocument?: OpenEditorDocument;
50
34
  editable?: boolean;
51
35
  placeholder?: string;
@@ -72,7 +56,8 @@ type OpenEditorBlockAction = {
72
56
  type OpenEditorReactConfig = OpenEditorReactProps;
73
57
  type OpenEditorController = {
74
58
  document: OpenEditorDocument;
75
- exports: ReturnType<typeof toExportBundle>;
59
+ export: (format: ExportFormat) => string;
60
+ exportUnsafe: (format: ExportFormat) => string;
76
61
  ready: boolean;
77
62
  editable: boolean;
78
63
  placeholder: string;
@@ -95,6 +80,7 @@ type OpenEditorController = {
95
80
  undo: () => void;
96
81
  redo: () => void;
97
82
  insertBlock: (blockName?: string, range?: InsertRange) => boolean;
83
+ isBlockEnabled: (blockName: string) => boolean;
98
84
  setLink: (url?: string | null) => void;
99
85
  getActiveLink: () => string | undefined;
100
86
  selectBlock: (block: OpenEditorBlockRef) => boolean;
@@ -112,6 +98,7 @@ type OpenEditorController = {
112
98
  extensions: readonly OpenEditorReactExtension[];
113
99
  pageRuntime?: OpenEditorPageRuntime;
114
100
  attachmentRuntime?: OpenEditorAttachmentRuntime<any>;
101
+ enabledBlocks?: readonly string[];
115
102
  slashState: SlashState | null;
116
103
  selectionBubbleState: SelectionBubbleState | null;
117
104
  };
@@ -206,8 +193,6 @@ type OpenEditorReactExtension = {
206
193
  blockHandle?: "self" | "nested";
207
194
  /** Discovery metadata shared by slash menus and host-owned block pickers. */
208
195
  insertMenu?: false | OpenEditorExtensionMenu;
209
- /** @deprecated Use `insertMenu`. */
210
- slashMenu?: false | OpenEditorExtensionMenu;
211
196
  viewer: OpenEditorViewerRenderer;
212
197
  exporters?: OpenEditorExporters;
213
198
  };
@@ -234,7 +219,15 @@ declare module "@tiptap/core" {
234
219
  }
235
220
  }
236
221
  declare const formatAttachmentSize: (size: number | null) => string;
237
- declare const useOpenEditorController: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
222
+ type OpenEditorPageHeaderProps = {
223
+ page: OpenEditorPageSnapshot;
224
+ runtime: OpenEditorPageRuntime;
225
+ className?: string;
226
+ onPageChange?: (page: OpenEditorPageSnapshot) => void;
227
+ };
228
+ /** Canonical Notion-style page metadata editor shared by host page surfaces. */
229
+ declare const OpenEditorPageHeader: ({ page, runtime, className, onPageChange, }: OpenEditorPageHeaderProps) => react.JSX.Element;
230
+ declare const useOpenEditorController: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, enabledBlocks, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
238
231
  declare const useOpenEditorBlockInteraction: (controller: OpenEditorController) => OpenEditorBlockInteractionSnapshot;
239
232
  type OpenEditorBlockDragHandleProps = {
240
233
  controller: OpenEditorController;
@@ -258,6 +251,6 @@ declare const getDefaultBlockPickerItems: (controller: OpenEditorController) =>
258
251
  declare const getDefaultSlashMenuItems: (controller: OpenEditorController) => OpenEditorSlashMenuItem[];
259
252
  declare const OpenEditorContent: ({ controller, className, }: OpenEditorContentProps) => react.JSX.Element;
260
253
  declare const OpenEditorViewer: ({ document, className, renderers, extensions, pageRuntime, attachmentRuntime, }: OpenEditorViewerProps) => react.JSX.Element;
261
- declare const useOpenEditor: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
254
+ declare const useOpenEditor: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, enabledBlocks, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
262
255
 
263
- export { type InsertRange, type OpenEditorBlockAction, type OpenEditorBlockActionContext, OpenEditorBlockDragHandle, type OpenEditorBlockDragHandleProps, type OpenEditorBlockPickerItem, OpenEditorContent, type OpenEditorContentProps, type OpenEditorController, type OpenEditorExtensionMenu, type OpenEditorOverlayRect, type OpenEditorPageRuntime, type OpenEditorPageSnapshot, type OpenEditorReactConfig, type OpenEditorReactExtension, type OpenEditorReactNodeExtension, type OpenEditorReactProps, type OpenEditorSlashMenuIcon, type OpenEditorSlashMenuIconProps, type OpenEditorSlashMenuItem, type OpenEditorTheme, OpenEditorThemeProvider, type OpenEditorThemeStyle, type OpenEditorThemeToken, OpenEditorViewer, type OpenEditorViewerProps, type OpenEditorViewerRenderer, type SelectionBubbleState, type SlashState, createOpenEditorThemeStyle, defineOpenEditorReactExtension, defineOpenEditorReactNode, formatAttachmentSize, getDefaultBlockPickerItems, getDefaultSlashMenuItems, openEditorThemeTokenNames, sortBlockPickerItems, sortSlashMenuItems, useOpenEditor, useOpenEditorBlockInteraction, useOpenEditorController, useOpenEditorThemeStyle };
256
+ export { type InsertRange, type OpenEditorBlockAction, type OpenEditorBlockActionContext, OpenEditorBlockDragHandle, type OpenEditorBlockDragHandleProps, type OpenEditorBlockPickerItem, 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, type OpenEditorTheme, OpenEditorThemeProvider, type OpenEditorThemeStyle, type OpenEditorThemeToken, OpenEditorViewer, type OpenEditorViewerProps, type OpenEditorViewerRenderer, type SelectionBubbleState, type SlashState, createOpenEditorThemeStyle, defineOpenEditorReactExtension, defineOpenEditorReactNode, formatAttachmentSize, getDefaultBlockPickerItems, getDefaultSlashMenuItems, openEditorThemeTokenNames, sortBlockPickerItems, sortSlashMenuItems, useOpenEditor, useOpenEditorBlockInteraction, useOpenEditorController, useOpenEditorThemeStyle };
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { DEFAULT_CALLOUT_EMOJI, normalizeEmoji, DEFAULT_PAGE_EMOJI, normalizeDocument, toProseMirrorDocument, createDocument as createDocument$1, createBlockRegistry, OPENEDITOR_BLOCK_ID_ATTR, createBlockId, fromProseMirrorDocument, findBlockSpecForNode } from '@openeditor/core';
1
+ import { DEFAULT_CALLOUT_EMOJI, normalizeEmoji, DEFAULT_PAGE_EMOJI, normalizeDocument, toProseMirrorDocument, createDocument as createDocument$1, isOpenEditorBlockEnabled, createBlockRegistry, OPENEDITOR_BLOCK_ID_ATTR, createBlockId, fromProseMirrorDocument, findBlockSpecForNode } from '@openeditor/core';
2
2
  import { Checkbox } from '@base-ui/react/checkbox';
3
3
  import { shift as shift$1, autoUpdate, computePosition } from '@floating-ui/dom';
4
4
  import { OpenEditorEmojiPicker } from '@openeditor/emoji';
5
- import React, { createContext, forwardRef, useContext, useMemo, createRef, memo, createElement, version, useState, useRef, useEffect, useSyncExternalStore as useSyncExternalStore$1, useCallback, use, useDebugValue, Fragment as Fragment$2, useLayoutEffect } from 'react';
5
+ import React, { createContext, forwardRef, useContext, useMemo, createRef, memo, createElement, version, useState, useEffect, useRef, useSyncExternalStore as useSyncExternalStore$1, useCallback, use, useDebugValue, Fragment as Fragment$2, useLayoutEffect } from 'react';
6
6
  import { jsxs, Fragment as Fragment$1, jsx } from 'react/jsx-runtime';
7
7
  import { renderMermaidSVG } from 'beautiful-mermaid';
8
- import { toExportBundle } from '@openeditor/exporters';
8
+ import { exportDocumentUnsafe, exportDocument } from '@openeditor/exporters';
9
9
  import { openEditorInsertPresets, resolveOpenEditorInsertBlock, defaultBlockRegistry } from '@openeditor/extensions';
10
10
  export { seedDocument } from '@openeditor/extensions';
11
11
  import ReactDOM, { flushSync, createPortal } from 'react-dom';
@@ -31524,11 +31524,12 @@ var OpenEditorPageNodeView = ({ editor, node, updateAttributes: updateAttributes
31524
31524
  };
31525
31525
  const updateIcon = async (nextIcon) => {
31526
31526
  updateAttributes2({ icon: nextIcon });
31527
- if (!page?.pageId || !runtime?.updatePageIcon) return;
31527
+ if (!page?.pageId || !runtime) return;
31528
31528
  setResolvedPage({ ...page, icon: nextIcon });
31529
31529
  setError(null);
31530
31530
  try {
31531
- await runtime.updatePageIcon(page.pageId, nextIcon);
31531
+ const updated = await runtime.updatePage(page.pageId, { icon: nextIcon });
31532
+ if (updated) setResolvedPage(updated);
31532
31533
  } catch (cause) {
31533
31534
  setError(cause instanceof Error ? cause.message : "Could not update the page icon.");
31534
31535
  }
@@ -31557,7 +31558,7 @@ var OpenEditorPageNodeView = ({ editor, node, updateAttributes: updateAttributes
31557
31558
  OpenEditorEmojiPicker,
31558
31559
  {
31559
31560
  className: "oe-page-icon-trigger",
31560
- disabled: !editor.isEditable,
31561
+ disabled: !editor.isEditable || !runtime,
31561
31562
  emoji: icon,
31562
31563
  label: "Change page emoji",
31563
31564
  popupStyle: themeStyle,
@@ -31626,6 +31627,61 @@ var OpenEditorPageViewer = ({ node }) => {
31626
31627
  }
31627
31628
  return /* @__PURE__ */ jsx("div", { className: "oe-page oe-page-viewer", children: content });
31628
31629
  };
31630
+ var OpenEditorPageHeader = ({
31631
+ page,
31632
+ runtime,
31633
+ className = "oe-page-header",
31634
+ onPageChange
31635
+ }) => {
31636
+ const [title, setTitle] = useState(page.title);
31637
+ const [icon, setIcon] = useState(normalizeEmoji(page.icon, DEFAULT_PAGE_EMOJI));
31638
+ const [error, setError] = useState(null);
31639
+ useEffect(() => setTitle(page.title), [page.pageId, page.title]);
31640
+ useEffect(() => setIcon(normalizeEmoji(page.icon, DEFAULT_PAGE_EMOJI)), [page.icon, page.pageId]);
31641
+ const updatePage = async (update) => {
31642
+ setError(null);
31643
+ try {
31644
+ const resolved = await runtime.updatePage(page.pageId, update);
31645
+ const next = resolved ?? { ...page, ...update };
31646
+ setTitle(next.title);
31647
+ setIcon(normalizeEmoji(next.icon, DEFAULT_PAGE_EMOJI));
31648
+ onPageChange?.(next);
31649
+ } catch (cause) {
31650
+ setError(cause instanceof Error ? cause.message : "Could not update the page.");
31651
+ }
31652
+ };
31653
+ return /* @__PURE__ */ jsxs("header", { className: `oe-page-header${className === "oe-page-header" ? "" : ` ${className}`}`, children: [
31654
+ /* @__PURE__ */ jsx(
31655
+ OpenEditorEmojiPicker,
31656
+ {
31657
+ emoji: icon,
31658
+ label: "Change page icon",
31659
+ onEmojiSelect: (nextIcon) => {
31660
+ setIcon(nextIcon);
31661
+ void updatePage({ icon: nextIcon });
31662
+ }
31663
+ }
31664
+ ),
31665
+ /* @__PURE__ */ jsx(
31666
+ "input",
31667
+ {
31668
+ "aria-label": "Page title",
31669
+ className: "oe-page-header-title",
31670
+ onBlur: () => {
31671
+ const nextTitle = title.trim() || "Untitled";
31672
+ setTitle(nextTitle);
31673
+ if (nextTitle !== page.title) void updatePage({ title: nextTitle });
31674
+ },
31675
+ onChange: (event) => setTitle(event.target.value),
31676
+ onKeyDown: (event) => {
31677
+ if (event.key === "Enter") event.currentTarget.blur();
31678
+ },
31679
+ value: title
31680
+ }
31681
+ ),
31682
+ error ? /* @__PURE__ */ jsx("span", { className: "oe-page-error", role: "alert", children: error }) : null
31683
+ ] });
31684
+ };
31629
31685
  var MermaidPreview = ({ code }) => {
31630
31686
  const rendered = useMemo(() => {
31631
31687
  try {
@@ -31698,7 +31754,7 @@ var MermaidDiagram = Node3.create({
31698
31754
  return ReactNodeViewRenderer(OpenEditorDiagramNodeView);
31699
31755
  }
31700
31756
  });
31701
- var createEditorExtensions = (placeholder, customExtensions) => [
31757
+ var createEditorExtensions = (getPlaceholder, customExtensions) => [
31702
31758
  index_default12.configure({
31703
31759
  attributeName: OPENEDITOR_BLOCK_ID_ATTR,
31704
31760
  types: "all",
@@ -31719,7 +31775,7 @@ var createEditorExtensions = (placeholder, customExtensions) => [
31719
31775
  index_default6,
31720
31776
  index_default5,
31721
31777
  index_default10.configure({ types: ["heading", "paragraph"] }),
31722
- index_default4.configure({ placeholder, showOnlyCurrent: true }),
31778
+ index_default4.configure({ placeholder: () => getPlaceholder(), showOnlyCurrent: true }),
31723
31779
  Columns,
31724
31780
  Column,
31725
31781
  ToggleList,
@@ -31825,12 +31881,12 @@ var useOpenEditorController = ({
31825
31881
  onFilePasteDrop,
31826
31882
  pageRuntime,
31827
31883
  attachmentRuntime,
31884
+ enabledBlocks,
31828
31885
  slashMenuItems = [],
31829
31886
  extensions = EMPTY_EXTENSIONS,
31830
31887
  blockActions = []
31831
31888
  } = {}) => {
31832
31889
  const [document2, setDocument] = useState(() => normalizeDocument(initialDocument));
31833
- const [initialEditorContent] = useState(() => toProseMirrorDocument(document2));
31834
31890
  const [selectedBlock, setSelectedBlock] = useState("paragraph");
31835
31891
  const [slashState, setSlashState] = useState(null);
31836
31892
  const [selectionBubbleState, setSelectionBubbleState] = useState(null);
@@ -31839,12 +31895,29 @@ var useOpenEditorController = ({
31839
31895
  const slashStateRef = useRef(null);
31840
31896
  const selectionBubbleStateRef = useRef(null);
31841
31897
  const documentMetaRef = useRef(document2.meta);
31898
+ const placeholderRef = useRef(placeholder);
31899
+ placeholderRef.current = placeholder;
31900
+ const onChangeRef = useRef(onChange);
31901
+ onChangeRef.current = onChange;
31902
+ const onFilePasteDropRef = useRef(onFilePasteDrop);
31903
+ onFilePasteDropRef.current = onFilePasteDrop;
31904
+ const isBlockEnabled = (blockName) => {
31905
+ const canonicalName = /^heading[1-6]$/.test(blockName) ? "heading" : blockName;
31906
+ if (!isOpenEditorBlockEnabled(canonicalName, { enabledBlocks })) return false;
31907
+ if (canonicalName === "attachment") {
31908
+ return Boolean(attachmentRuntime?.selectAttachment && attachmentRuntime.uploadAttachment);
31909
+ }
31910
+ if (canonicalName === "page") return Boolean(pageRuntime?.createPage);
31911
+ return true;
31912
+ };
31913
+ const isBlockEnabledRef = useRef(isBlockEnabled);
31914
+ isBlockEnabledRef.current = isBlockEnabled;
31842
31915
  const registry = useMemo(() => createReactBlockRegistry(extensions), [extensions]);
31843
31916
  const exporters = useMemo(() => createReactExporters(extensions), [extensions]);
31844
31917
  const editor = useEditor({
31845
31918
  immediatelyRender: false,
31846
- extensions: createEditorExtensions(placeholder, extensions),
31847
- content: initialEditorContent,
31919
+ extensions: createEditorExtensions(() => placeholderRef.current, extensions),
31920
+ content: toProseMirrorDocument(document2),
31848
31921
  editable,
31849
31922
  editorProps: {
31850
31923
  attributes: {
@@ -31861,6 +31934,12 @@ var useOpenEditorController = ({
31861
31934
  try {
31862
31935
  const payload = JSON.parse(encoded);
31863
31936
  if (payload.format === "openeditor-blocks" && payload.version === 1 && Array.isArray(payload.blocks)) {
31937
+ const firstBlock = payload.blocks[0];
31938
+ const blockName = firstBlock ? findBlockSpecForNode(registry, firstBlock)?.name : void 0;
31939
+ if (blockName && !isBlockEnabledRef.current(blockName)) {
31940
+ event.preventDefault();
31941
+ return true;
31942
+ }
31864
31943
  const nodes = payload.blocks.map(
31865
31944
  (node) => view.state.schema.nodeFromJSON(regenerateClipboardNodeIds(node))
31866
31945
  );
@@ -31877,7 +31956,7 @@ var useOpenEditorController = ({
31877
31956
  }
31878
31957
  }
31879
31958
  const file = event.clipboardData?.files?.[0];
31880
- if (!file || !onFilePasteDrop?.(file)) {
31959
+ if (!file || !isBlockEnabledRef.current("attachment") || !onFilePasteDropRef.current?.(file)) {
31881
31960
  return false;
31882
31961
  }
31883
31962
  event.preventDefault();
@@ -31885,7 +31964,7 @@ var useOpenEditorController = ({
31885
31964
  },
31886
31965
  handleDrop: (_view, event, _slice, moved) => {
31887
31966
  const file = event.dataTransfer?.files?.[0];
31888
- if (moved || !file || !onFilePasteDrop?.(file)) {
31967
+ if (moved || !file || !isBlockEnabledRef.current("attachment") || !onFilePasteDropRef.current?.(file)) {
31889
31968
  return false;
31890
31969
  }
31891
31970
  event.preventDefault();
@@ -31907,7 +31986,7 @@ var useOpenEditorController = ({
31907
31986
  documentMetaRef.current
31908
31987
  );
31909
31988
  setDocument(nextDocument);
31910
- onChange?.(nextDocument);
31989
+ onChangeRef.current?.(nextDocument);
31911
31990
  setSlashState(getSlashState(currentEditor));
31912
31991
  setSelectionBubbleState(
31913
31992
  isPointerSelectingRef.current ? null : getSelectionBubbleState(currentEditor)
@@ -31923,10 +32002,12 @@ var useOpenEditorController = ({
31923
32002
  }
31924
32003
  }
31925
32004
  }, [extensions]);
31926
- const exports = useMemo(
31927
- () => toExportBundle(document2, exporters),
31928
- [document2, exporters]
31929
- );
32005
+ useEffect(() => {
32006
+ editor?.setEditable(editable, false);
32007
+ }, [editable, editor]);
32008
+ useEffect(() => {
32009
+ if (editor && !editor.isDestroyed) editor.view.dispatch(editor.state.tr);
32010
+ }, [editor, placeholder]);
31930
32011
  useEffect(() => {
31931
32012
  slashStateRef.current = slashState;
31932
32013
  }, [slashState]);
@@ -31975,7 +32056,7 @@ var useOpenEditorController = ({
31975
32056
  };
31976
32057
  }, [editor]);
31977
32058
  const insertBlock = (blockName = selectedBlock, range) => {
31978
- if (!editor) {
32059
+ if (!editor || !isBlockEnabled(blockName)) {
31979
32060
  return false;
31980
32061
  }
31981
32062
  const block = resolveOpenEditorInsertBlock(blockName, registry);
@@ -31985,14 +32066,14 @@ var useOpenEditorController = ({
31985
32066
  return range ? commandChain.deleteRange(range) : commandChain;
31986
32067
  };
31987
32068
  if (blockName === "page" && pageRuntime?.createPage) {
31988
- const prepared = chain().run();
31989
- if (!prepared) return false;
31990
32069
  setSlashState(null);
31991
32070
  void pageRuntime.createPage({
31992
32071
  title: "Untitled",
31993
32072
  icon: DEFAULT_PAGE_EMOJI
31994
32073
  }).then((created) => {
31995
- editor.chain().focus().insertContent({
32074
+ const insertion = editor.chain().focus();
32075
+ const prepared = range ? insertion.deleteRange(range) : insertion;
32076
+ prepared.insertContent({
31996
32077
  ...block,
31997
32078
  attrs: {
31998
32079
  ...block.attrs,
@@ -32002,9 +32083,7 @@ var useOpenEditorController = ({
32002
32083
  },
32003
32084
  content: [{ type: "text", text: created.title || "Untitled" }]
32004
32085
  }).run();
32005
- }).catch(() => {
32006
- editor.chain().focus().insertContent(block).run();
32007
- });
32086
+ }).catch(() => void 0);
32008
32087
  return true;
32009
32088
  }
32010
32089
  const inserted = chain().insertContent(block).run();
@@ -32017,7 +32096,9 @@ var useOpenEditorController = ({
32017
32096
  }
32018
32097
  const normalized = normalizeDocument(nextDocument);
32019
32098
  documentMetaRef.current = normalized.meta;
32020
- editor.commands.setContent(toProseMirrorDocument(normalized));
32099
+ editor.commands.setContent(toProseMirrorDocument(normalized), { emitUpdate: false });
32100
+ setDocument(normalized);
32101
+ onChangeRef.current?.(normalized);
32021
32102
  };
32022
32103
  const resolveBlock = (block) => block ?? (editor ? selectedBlockRef(editor, registry) : null);
32023
32104
  const resolveBlockAt = (position) => editor ? blockRefAtPosition(editor, position, registry) : null;
@@ -32091,10 +32172,10 @@ var useOpenEditorController = ({
32091
32172
  editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
32092
32173
  };
32093
32174
  const setParagraph = () => {
32094
- editor?.chain().focus().setParagraph().run();
32175
+ if (isBlockEnabled("paragraph")) editor?.chain().focus().setParagraph().run();
32095
32176
  };
32096
32177
  const toggleHeading = (level) => {
32097
- editor?.chain().focus().toggleHeading({ level }).run();
32178
+ if (isBlockEnabled("heading")) editor?.chain().focus().toggleHeading({ level }).run();
32098
32179
  };
32099
32180
  const toggleBold = () => {
32100
32181
  editor?.chain().focus().toggleBold().run();
@@ -32112,19 +32193,19 @@ var useOpenEditorController = ({
32112
32193
  editor?.chain().focus().toggleCode().run();
32113
32194
  };
32114
32195
  const toggleBulletList = () => {
32115
- editor?.chain().focus().toggleBulletList().run();
32196
+ if (isBlockEnabled("bulletList")) editor?.chain().focus().toggleBulletList().run();
32116
32197
  };
32117
32198
  const toggleOrderedList = () => {
32118
- editor?.chain().focus().toggleOrderedList().run();
32199
+ if (isBlockEnabled("orderedList")) editor?.chain().focus().toggleOrderedList().run();
32119
32200
  };
32120
32201
  const toggleTaskList = () => {
32121
- editor?.chain().focus().toggleTaskList().run();
32202
+ if (isBlockEnabled("taskList")) editor?.chain().focus().toggleTaskList().run();
32122
32203
  };
32123
32204
  const toggleBlockquote = () => {
32124
- editor?.chain().focus().toggleBlockquote().run();
32205
+ if (isBlockEnabled("blockquote")) editor?.chain().focus().toggleBlockquote().run();
32125
32206
  };
32126
32207
  const toggleCodeBlock = () => {
32127
- editor?.chain().focus().toggleCodeBlock().run();
32208
+ if (isBlockEnabled("codeBlock")) editor?.chain().focus().toggleCodeBlock().run();
32128
32209
  };
32129
32210
  const undo2 = () => {
32130
32211
  editor?.chain().focus().undo().run();
@@ -32135,7 +32216,8 @@ var useOpenEditorController = ({
32135
32216
  const controller = {
32136
32217
  document: document2,
32137
32218
  editor,
32138
- exports,
32219
+ export: (format) => exportDocument(document2, format),
32220
+ exportUnsafe: (format) => exportDocumentUnsafe(document2, format, exporters),
32139
32221
  ready: editor !== null,
32140
32222
  editable,
32141
32223
  placeholder,
@@ -32158,6 +32240,7 @@ var useOpenEditorController = ({
32158
32240
  undo: undo2,
32159
32241
  redo: redo2,
32160
32242
  insertBlock,
32243
+ isBlockEnabled,
32161
32244
  setLink,
32162
32245
  getActiveLink: () => editor?.getAttributes("link").href,
32163
32246
  selectBlock,
@@ -32178,6 +32261,7 @@ var useOpenEditorController = ({
32178
32261
  extensions,
32179
32262
  pageRuntime,
32180
32263
  attachmentRuntime,
32264
+ enabledBlocks,
32181
32265
  slashState,
32182
32266
  selectionBubbleState
32183
32267
  };
@@ -32402,9 +32486,9 @@ var sortMenuItems = (items) => [...items].sort((left, right) => {
32402
32486
  });
32403
32487
  var sortSlashMenuItems = (items) => sortMenuItems(items);
32404
32488
  var sortBlockPickerItems = (items) => sortMenuItems(items);
32405
- var getExtensionInsertMenu = (extension) => extension.insertMenu ?? extension.slashMenu;
32489
+ var getExtensionInsertMenu = (extension) => extension.insertMenu;
32406
32490
  var getDefaultBlockPickerItems = (controller) => sortBlockPickerItems([
32407
- ...openEditorInsertPresets.map((preset, index) => ({
32491
+ ...openEditorInsertPresets.filter((preset) => controller.isBlockEnabled(preset.key)).map((preset, index) => ({
32408
32492
  key: preset.key,
32409
32493
  label: preset.label,
32410
32494
  group: preset.group,
@@ -32412,7 +32496,7 @@ var getDefaultBlockPickerItems = (controller) => sortBlockPickerItems([
32412
32496
  order: (index + 1) * 100,
32413
32497
  insert: () => controller.insertBlock(preset.key)
32414
32498
  })),
32415
- ...controller.extensions.filter((extension) => getExtensionInsertMenu(extension) !== false).map((extension) => {
32499
+ ...controller.extensions.filter((extension) => getExtensionInsertMenu(extension) !== false && controller.isBlockEnabled(extension.block.name)).map((extension) => {
32416
32500
  const menu = getExtensionInsertMenu(extension) || void 0;
32417
32501
  return {
32418
32502
  key: extension.block.name,
@@ -32428,7 +32512,7 @@ var getDefaultBlockPickerItems = (controller) => sortBlockPickerItems([
32428
32512
  var getDefaultSlashMenuItems = (controller) => {
32429
32513
  const internalController = controller;
32430
32514
  return sortSlashMenuItems([
32431
- ...openEditorInsertPresets.map((preset, index) => ({
32515
+ ...openEditorInsertPresets.filter((preset) => controller.isBlockEnabled(preset.key)).map((preset, index) => ({
32432
32516
  key: preset.key,
32433
32517
  label: preset.label,
32434
32518
  group: preset.group,
@@ -32460,7 +32544,7 @@ var getDefaultSlashMenuItems = (controller) => {
32460
32544
  return true;
32461
32545
  }
32462
32546
  },
32463
- ...controller.extensions.filter((extension) => getExtensionInsertMenu(extension) !== false).map((extension) => {
32547
+ ...controller.extensions.filter((extension) => getExtensionInsertMenu(extension) !== false && controller.isBlockEnabled(extension.block.name)).map((extension) => {
32464
32548
  const menu = getExtensionInsertMenu(extension) || void 0;
32465
32549
  return {
32466
32550
  key: extension.block.name,
@@ -32615,6 +32699,6 @@ var OpenEditorViewer = ({
32615
32699
  };
32616
32700
  var useOpenEditor = useOpenEditorController;
32617
32701
 
32618
- export { NodeViewContent, NodeViewWrapper, OpenEditorBlockDragHandle, OpenEditorContent, OpenEditorThemeProvider, OpenEditorViewer, createOpenEditorThemeStyle, defineOpenEditorReactExtension, defineOpenEditorReactNode, formatAttachmentSize, getDefaultBlockPickerItems, getDefaultSlashMenuItems, openEditorThemeTokenNames, sortBlockPickerItems, sortSlashMenuItems, useOpenEditor, useOpenEditorBlockInteraction, useOpenEditorController, useOpenEditorThemeStyle };
32702
+ export { NodeViewContent, NodeViewWrapper, OpenEditorBlockDragHandle, OpenEditorContent, OpenEditorPageHeader, OpenEditorThemeProvider, OpenEditorViewer, createOpenEditorThemeStyle, defineOpenEditorReactExtension, defineOpenEditorReactNode, formatAttachmentSize, getDefaultBlockPickerItems, getDefaultSlashMenuItems, openEditorThemeTokenNames, sortBlockPickerItems, sortSlashMenuItems, useOpenEditor, useOpenEditorBlockInteraction, useOpenEditorController, useOpenEditorThemeStyle };
32619
32703
  //# sourceMappingURL=index.js.map
32620
32704
  //# sourceMappingURL=index.js.map