@godxjp/block-editor 33.24.0 → 33.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,16 +44,16 @@ import { BlockEditor } from "@godxjp/block-editor";
44
44
 
45
45
  ## Contract
46
46
 
47
- | prop | |
48
- | ------------------------------------------------------------------------ | -------------------------------------------------------- |
49
- | `value` / `defaultValue` / `onValueChange` | Markdown in, canonical Markdown out |
50
- | `upload(file, { signal })` | `→ { url, name? }` — the same shape as `MarkdownEditor` |
51
- | `pickMedia()` | `→ { url, name? } \| null` — the host's library |
52
- | `suggestWikilinks(query)` | `→ { target, label?, heading?, description? }[]` |
53
- | `renderEmbed(target)` | a block embed `![[target]]` |
54
- | `resolveUrl(url)` | display URL for a stored one (`asset:<id>`) |
55
- | `directives` | `{ [name]: (block) => ReactNode \| undefined }` previews |
56
- | `actions`, `labels`, `disabled`, `readOnly`, `autoFocus`, `aria-*`, `id` | |
47
+ | prop | |
48
+ | ------------------------------------------------------------------------ | ------------------------------------------------------------------ |
49
+ | `value` / `defaultValue` / `onValueChange` | Markdown in, canonical Markdown out |
50
+ | `upload(file, { signal })` | `→ { url, name? }` — the same shape as `MarkdownEditor` |
51
+ | `pickMedia()` | `→ { url, name? } \| null` — the host's library |
52
+ | `suggestWikilinks(query)` | `→ { target, label?, heading?, description? }[]` |
53
+ | `renderEmbed(target)` | a block embed `![[target]]` |
54
+ | `resolveUrl(url)` | display URL for a stored one (`asset:<id>`) |
55
+ | `directives` | `{ [name]: (block, controls) => ReactNode \| undefined }` previews |
56
+ | `actions`, `labels`, `disabled`, `readOnly`, `autoFocus`, `aria-*`, `id` | |
57
57
 
58
58
  ```tsx
59
59
  // A poll is a block: the host previews it and inserts its skeleton from the `/` menu.
@@ -75,6 +75,19 @@ import { BlockEditor } from "@godxjp/block-editor";
75
75
  />
76
76
  ```
77
77
 
78
+ A preview can change its own block (gh#1479): `controls.update(markdown)` replaces the block's
79
+ source exactly like "Edit source" → Done (still one directive: the same block, one undo step;
80
+ anything else takes its place), `controls.remove()` deletes it, and `controls.editable` is false in a
81
+ read-only or disabled editor. A host's picker or config form writes its result back this way:
82
+
83
+ ```tsx
84
+ directives={{
85
+ card: (block, { update, editable }) => (
86
+ <CardPreview id={block.attributes.card} onPick={editable ? (id) => update(`:::card{card=${id}}\n:::`) : undefined} />
87
+ ),
88
+ }}
89
+ ```
90
+
78
91
  The grammar it reads and writes is specified in `@godxjp/markdown`'s README ("Grammar v1 and the
79
92
  codec"). The editor's schema is tested to round-trip every fixture exactly:
80
93
  `serialize(editor.getJSON()) === normalize(markdown)`.
package/dist/index.d.ts CHANGED
@@ -248,8 +248,23 @@ type BlockEditorLabels = {
248
248
  [K in keyof (typeof BLOCK_EDITOR_MESSAGES)["en"]]: string;
249
249
  };
250
250
 
251
+ /**
252
+ * What a directive preview can do to its own block (gh#1479): a host's picker or config form writes
253
+ * the result back without the "Edit source" textarea.
254
+ */
255
+ type BlockEditorDirectiveControls = {
256
+ /**
257
+ * Replace the block's Markdown. Parsed like "Edit source" → Done: still one directive, it stays
258
+ * this block with the new source; anything else (blocks, raw Markdown, nothing) takes its place.
259
+ */
260
+ update: (source: string) => void;
261
+ /** Remove the block. */
262
+ remove: () => void;
263
+ /** False in a read-only or disabled editor: the preview should offer no edits. */
264
+ editable: boolean;
265
+ };
251
266
  /** Draws a directive block's preview in the editor; `undefined` shows its source instead. */
252
- type BlockEditorDirectiveRenderer = (block: DirectiveBlock) => React.ReactNode | undefined;
267
+ type BlockEditorDirectiveRenderer = (block: DirectiveBlock, controls: BlockEditorDirectiveControls) => React.ReactNode | undefined;
253
268
 
254
269
  /** What the host's storage returns for one file — the same shape as MarkdownEditor's. */
255
270
  type BlockEditorUploadResult = {
@@ -465,4 +480,4 @@ declare const TranslatableRichText: React.ForwardRefExoticComponent<Omit<RichTex
465
480
  size?: "sm" | "md" | "lg";
466
481
  } & React.RefAttributes<RichTextEditorHandle>>;
467
482
 
468
- export { BLOCK_EDITOR_MESSAGES, BlockEditor, type BlockEditorAction, type BlockEditorApi, type BlockEditorDirectiveRenderer, type BlockEditorHandle, type BlockEditorLabels, type BlockEditorProps, type BlockEditorUploadResult, type CalloutTitles, RICH_TEXT_ALLOWED_TAGS, RichTextEditor, type RichTextEditorHandle, type RichTextEditorLabels, type RichTextEditorProps, TranslatableRichText, type TranslatableRichTextProps, type WikilinkSuggestion, schemaExtensions };
483
+ export { BLOCK_EDITOR_MESSAGES, BlockEditor, type BlockEditorAction, type BlockEditorApi, type BlockEditorDirectiveControls, type BlockEditorDirectiveRenderer, type BlockEditorHandle, type BlockEditorLabels, type BlockEditorProps, type BlockEditorUploadResult, type CalloutTitles, RICH_TEXT_ALLOWED_TAGS, RichTextEditor, type RichTextEditorHandle, type RichTextEditorLabels, type RichTextEditorProps, TranslatableRichText, type TranslatableRichTextProps, type WikilinkSuggestion, schemaExtensions };
package/dist/index.js CHANGED
@@ -1548,10 +1548,8 @@ function DirectiveView({ node, editor, getPos, updateAttributes, selected }) {
1548
1548
  const [draft, setDraft] = React5.useState(source);
1549
1549
  const render = previews && Object.hasOwn(previews, name) ? previews[name] : void 0;
1550
1550
  const block = React5.useMemo(() => render ? readDirective(source) : null, [render, source]);
1551
- const preview = render && block ? render(block) : void 0;
1552
- const commit = () => {
1553
- setEditing(false);
1554
- const doc = parse(draft);
1551
+ const replace = (markdown) => {
1552
+ const doc = parse(markdown);
1555
1553
  const pos = getPos();
1556
1554
  const [only] = doc.content;
1557
1555
  if (doc.content.length === 1 && only.type === "directive") {
@@ -1561,6 +1559,22 @@ function DirectiveView({ node, editor, getPos, updateAttributes, selected }) {
1561
1559
  if (typeof pos !== "number") return;
1562
1560
  editor.chain().focus().insertContentAt({ from: pos, to: pos + node.nodeSize }, doc.content).run();
1563
1561
  };
1562
+ const controls = {
1563
+ update: (markdown) => {
1564
+ if (editor.isEditable) replace(markdown);
1565
+ },
1566
+ remove: () => {
1567
+ const pos = getPos();
1568
+ if (!editor.isEditable || typeof pos !== "number") return;
1569
+ editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).run();
1570
+ },
1571
+ editable: editor.isEditable
1572
+ };
1573
+ const preview = render && block ? render(block, controls) : void 0;
1574
+ const commit = () => {
1575
+ setEditing(false);
1576
+ replace(draft);
1577
+ };
1564
1578
  return /* @__PURE__ */ jsxs5(
1565
1579
  NodeViewWrapper,
1566
1580
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@godxjp/block-editor",
3
- "version": "33.24.0",
3
+ "version": "33.25.0",
4
4
  "description": "Block editor for GoDX apps (Notion / note.com style) on Tiptap: slash menu, block handles, floating format toolbar; reads and writes Markdown through @godxjp/markdown/codec, IME-safe, ja / en / vi.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,8 +45,8 @@
45
45
  "lucide-react": "^1.37.0"
46
46
  },
47
47
  "peerDependencies": {
48
- "@godxjp/markdown": "~33.24.0",
49
- "@godxjp/ui": "~33.24.0",
48
+ "@godxjp/markdown": "~33.25.0",
49
+ "@godxjp/ui": "~33.25.0",
50
50
  "react": ">=19.0.0",
51
51
  "react-dom": ">=19.0.0"
52
52
  },