@jackuait/blok 0.10.6 → 0.10.8
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/dist/blok.mjs +2 -2
- package/dist/chunks/{blok-B0k4z2h1.mjs → blok-ClCrnWuI.mjs} +1510 -1460
- package/dist/chunks/{constants-lOEJu8bC.mjs → constants-BoE5frJm.mjs} +1 -1
- package/dist/chunks/{core-C942GvJO.mjs → core-B7mxBIHA.mjs} +1 -1
- package/dist/chunks/{engine-javascript-Dd6ViPCH.mjs → engine-javascript-Bmmg8uL9.mjs} +1 -1
- package/dist/chunks/{i18next-loader-CIXsptng.mjs → i18next-loader-453gJdot.mjs} +1 -1
- package/dist/chunks/{tools-FmtWXVgt.mjs → tools-HQPJLj5m.mjs} +3 -3
- package/dist/full.mjs +3 -3
- package/dist/react.mjs +3 -3
- package/dist/tools.mjs +2 -2
- package/package.json +1 -1
- package/src/components/modules/api/blocks.ts +14 -1
- package/src/components/modules/paste/handlers/blok-data-handler.ts +93 -1
- package/src/components/modules/renderer.ts +9 -1
- package/src/components/modules/themeManager.ts +13 -3
- package/src/components/utils/data-model-transform.ts +166 -28
- package/src/markdown/markdown-handler.ts +9 -2
- package/src/tools/table/table-operations.ts +9 -4
- /package/dist/chunks/{lightweight-i18n-DTYoSr_o.mjs → lightweight-i18n-DSjG0iTr.mjs} +0 -0
- /package/dist/chunks/{objectWithoutProperties-D0XxKB4n.mjs → objectWithoutProperties-Dci1-l7D.mjs} +0 -0
|
@@ -130,7 +130,7 @@ var a = {
|
|
|
130
130
|
RIGHT: 2,
|
|
131
131
|
BACKWARD: 3,
|
|
132
132
|
FORWARD: 4
|
|
133
|
-
}, l = () => "0.10.
|
|
133
|
+
}, l = () => "0.10.8", u = /* @__PURE__ */ function(e) {
|
|
134
134
|
return e.VERBOSE = "VERBOSE", e.INFO = "INFO", e.WARN = "WARN", e.ERROR = "ERROR", e;
|
|
135
135
|
}({}), d = (e, t, n = "log", r, i = "color: inherit") => {
|
|
136
136
|
let a = typeof console > "u" ? void 0 : console;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as e } from "./chunk-D6kmoKXy.mjs";
|
|
2
2
|
import { n as t, t as n } from "./objectSpread2-CWwMYL_U.mjs";
|
|
3
|
-
import { t as r } from "./objectWithoutProperties-
|
|
3
|
+
import { t as r } from "./objectWithoutProperties-Dci1-l7D.mjs";
|
|
4
4
|
import { t as i } from "./ccount-C9Y7nqDe.mjs";
|
|
5
5
|
//#region node_modules/zwitch/index.js
|
|
6
6
|
var a = {}.hasOwnProperty;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as e, t } from "./objectSpread2-CWwMYL_U.mjs";
|
|
2
|
-
import { t as n } from "./objectWithoutProperties-
|
|
2
|
+
import { t as n } from "./objectWithoutProperties-Dci1-l7D.mjs";
|
|
3
3
|
//#region node_modules/@shikijs/engine-javascript/dist/scanner-BFcBmQR1.mjs
|
|
4
4
|
var r = 4294967295, i = class {
|
|
5
5
|
constructor(t, n = {}) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e } from "./lightweight-i18n-
|
|
1
|
+
import { n as e } from "./lightweight-i18n-DSjG0iTr.mjs";
|
|
2
2
|
//#region src/components/i18n/i18next-loader.ts
|
|
3
3
|
var t = async (t, n) => {
|
|
4
4
|
let r = (await import("./i18next-CLUkHqmV.mjs")).default.createInstance(), i = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as e, $t as t, A as n, At as r, B as i, Bt as a, Ct as o, D as s, Et as c, F as l, Ft as u, G as d, Gt as f, H as p, Ht as m, I as h, It as g, J as _, Jt as v, K as y, Kt as b, L as ee, Lt as x, M as te, Mt as S, Nn as ne, Nt as re, O as ie, Ot as C, P as ae, Pt as oe, Q as se, Qt as ce, R as le, Rn as w, Rt as ue, St as de, Tt as fe, U as pe, Ut as me, V as he, W as ge, Wt as _e, X as ve, Xt as ye, Y as be, Yt as xe, Z as Se, Zt as Ce, _n as we, _t as Te, a as Ee, at as De, bt as Oe, c as ke, cn as T, ct as Ae, d as je, dr as E, dt as Me, en as Ne, et as Pe, f as Fe, ft as Ie, g as Le, gt as Re, h as ze, ht as Be, i as Ve, ir as He, it as Ue, j as We, jt as Ge, k as Ke, kt as qe, l as Je, ln as Ye, lt as Xe, mn as Ze, mt as Qe, n as $e, nt as et, o as tt, ot as nt, p as rt, pn as D, pt as it, q as at, qt as ot, r as st, rn as ct, rt as lt, s as ut, sn as dt, st as ft, t as pt, tt as mt, u as O, un as ht, ur as k, ut as gt, v as _t, vn as vt, vt as yt, wt as bt, xt, z as St, zt as Ct } from "./constants-
|
|
1
|
+
import { $ as e, $t as t, A as n, At as r, B as i, Bt as a, Ct as o, D as s, Et as c, F as l, Ft as u, G as d, Gt as f, H as p, Ht as m, I as h, It as g, J as _, Jt as v, K as y, Kt as b, L as ee, Lt as x, M as te, Mt as S, Nn as ne, Nt as re, O as ie, Ot as C, P as ae, Pt as oe, Q as se, Qt as ce, R as le, Rn as w, Rt as ue, St as de, Tt as fe, U as pe, Ut as me, V as he, W as ge, Wt as _e, X as ve, Xt as ye, Y as be, Yt as xe, Z as Se, Zt as Ce, _n as we, _t as Te, a as Ee, at as De, bt as Oe, c as ke, cn as T, ct as Ae, d as je, dr as E, dt as Me, en as Ne, et as Pe, f as Fe, ft as Ie, g as Le, gt as Re, h as ze, ht as Be, i as Ve, ir as He, it as Ue, j as We, jt as Ge, k as Ke, kt as qe, l as Je, ln as Ye, lt as Xe, mn as Ze, mt as Qe, n as $e, nt as et, o as tt, ot as nt, p as rt, pn as D, pt as it, q as at, qt as ot, r as st, rn as ct, rt as lt, s as ut, sn as dt, st as ft, t as pt, tt as mt, u as O, un as ht, ur as k, ut as gt, v as _t, vn as vt, vt as yt, wt as bt, xt, z as St, zt as Ct } from "./constants-BoE5frJm.mjs";
|
|
2
2
|
import { t as A } from "./objectSpread2-CWwMYL_U.mjs";
|
|
3
3
|
import { n as j } from "./tw-CqxBf-1Y.mjs";
|
|
4
4
|
//#region src/components/utils/html.ts
|
|
@@ -3075,7 +3075,7 @@ var Ir = (e) => {
|
|
|
3075
3075
|
let e = n.blocks.getBlockIndex(t);
|
|
3076
3076
|
if (e === void 0) continue;
|
|
3077
3077
|
let i = n.blocks.getBlockByIndex(e);
|
|
3078
|
-
if (i && i.parentId
|
|
3078
|
+
if (i && !(i.parentId !== null && i.parentId !== void 0 && i.parentId !== r)) {
|
|
3079
3079
|
if (i.holder.closest(`[${k.nestedBlocks}]`)) {
|
|
3080
3080
|
s.appendChild(i.holder.cloneNode(!0));
|
|
3081
3081
|
continue;
|
|
@@ -9051,7 +9051,7 @@ function ec(e) {
|
|
|
9051
9051
|
}
|
|
9052
9052
|
async function tc() {
|
|
9053
9053
|
return $s.highlighterPromise || ($s.highlighterPromise = (async () => {
|
|
9054
|
-
let { createHighlighterCore: e } = await import("./core-
|
|
9054
|
+
let { createHighlighterCore: e } = await import("./core-B7mxBIHA.mjs"), { createJavaScriptRegexEngine: t } = await import("./engine-javascript-Bmmg8uL9.mjs"), n = await e({
|
|
9055
9055
|
themes: [import("./one-light-Di_o5Kb7.mjs"), import("./vitesse-dark-B5oAIYZ5.mjs")],
|
|
9056
9056
|
langs: [],
|
|
9057
9057
|
engine: t()
|
package/dist/full.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as e, t } from "./chunks/blok-
|
|
2
|
-
import { ur as n } from "./chunks/constants-
|
|
1
|
+
import { n as e, t } from "./chunks/blok-ClCrnWuI.mjs";
|
|
2
|
+
import { ur as n } from "./chunks/constants-BoE5frJm.mjs";
|
|
3
3
|
import { t as r } from "./chunks/objectSpread2-CWwMYL_U.mjs";
|
|
4
|
-
import { a as i, b as a, c as o, g as s, i as c, l, n as u, o as d, s as f, t as p, v as m, y as h } from "./chunks/tools-
|
|
4
|
+
import { a as i, b as a, c as o, g as s, i as c, l, n as u, o as d, s as f, t as p, v as m, y as h } from "./chunks/tools-HQPJLj5m.mjs";
|
|
5
5
|
//#region src/full.ts
|
|
6
6
|
var g = {
|
|
7
7
|
paragraph: {
|
package/dist/react.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as e } from "./chunks/blok-
|
|
2
|
-
import "./chunks/constants-
|
|
1
|
+
import { t as e } from "./chunks/blok-ClCrnWuI.mjs";
|
|
2
|
+
import "./chunks/constants-BoE5frJm.mjs";
|
|
3
3
|
import { t } from "./chunks/objectSpread2-CWwMYL_U.mjs";
|
|
4
|
-
import { t as n } from "./chunks/objectWithoutProperties-
|
|
4
|
+
import { t as n } from "./chunks/objectWithoutProperties-Dci1-l7D.mjs";
|
|
5
5
|
import { forwardRef as r, useEffect as i, useMemo as a, useRef as o, useState as s } from "react";
|
|
6
6
|
import { jsx as c } from "react/jsx-runtime";
|
|
7
7
|
//#region src/react/holder-map.ts
|
package/dist/tools.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { m as e } from "./chunks/constants-
|
|
2
|
-
import { _ as t, a as n, b as r, c as i, d as a, f as o, g as s, h as c, i as l, l as u, m as d, n as f, o as p, p as m, r as h, s as g, t as _, u as v, v as y, y as b } from "./chunks/tools-
|
|
1
|
+
import { m as e } from "./chunks/constants-BoE5frJm.mjs";
|
|
2
|
+
import { _ as t, a as n, b as r, c as i, d as a, f as o, g as s, h as c, i as l, l as u, m as d, n as f, o as p, p as m, r as h, s as g, t as _, u as v, v as y, y as b } from "./chunks/tools-HQPJLj5m.mjs";
|
|
3
3
|
export { u as Bold, c as Callout, v as Code, e as Convert, d as Database, m as DatabaseRow, o as Divider, b as Header, h as InlineCode, i as Italic, g as Link, y as List, p as Marker, r as Paragraph, a as Quote, l as Strikethrough, t as Table, s as Toggle, n as Underline, _ as defaultBlockTools, f as defaultInlineTools };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jackuait/blok",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.8",
|
|
4
4
|
"description": "Blok — headless, highly extensible rich text editor built for developers who need to implement a block-based editing experience (similar to Notion) without building it from scratch",
|
|
5
5
|
"module": "dist/blok.mjs",
|
|
6
6
|
"types": "./types/index.d.ts",
|
|
@@ -7,6 +7,7 @@ import { Module } from '../../__module';
|
|
|
7
7
|
import { Block } from '../../block';
|
|
8
8
|
import { BlockAPI } from '../../block/api';
|
|
9
9
|
import { capitalize } from '../../utils';
|
|
10
|
+
import { normalizeTableChildParents } from '../../utils/data-model-transform';
|
|
10
11
|
|
|
11
12
|
import { logLabeled } from './../../utils';
|
|
12
13
|
|
|
@@ -401,11 +402,23 @@ export class BlocksAPI extends Module {
|
|
|
401
402
|
): BlockAPIInterface[] => {
|
|
402
403
|
this.validateIndex(index);
|
|
403
404
|
|
|
404
|
-
|
|
405
|
+
// Backfill `parent` on children referenced by table cells so that
|
|
406
|
+
// alternative load paths (any consumer of the public API) get the
|
|
407
|
+
// same hierarchical correctness as Renderer.render(). Without this,
|
|
408
|
+
// flat-array article shapes lose their cell→child relationship and
|
|
409
|
+
// children render as detached top-level blocks.
|
|
410
|
+
const normalizedBlocks = normalizeTableChildParents(blocks);
|
|
411
|
+
|
|
412
|
+
const blocksToInsert = normalizedBlocks.map(({ id, type, data, tunes, parent, content, lastEditedAt, lastEditedBy }) => {
|
|
405
413
|
return this.Blok.BlockManager.composeBlock({
|
|
406
414
|
id,
|
|
407
415
|
tool: type || (this.config.defaultBlock as string),
|
|
408
416
|
data: data as BlockToolData,
|
|
417
|
+
tunes,
|
|
418
|
+
parentId: parent,
|
|
419
|
+
contentIds: content,
|
|
420
|
+
lastEditedAt,
|
|
421
|
+
lastEditedBy,
|
|
409
422
|
});
|
|
410
423
|
});
|
|
411
424
|
|
|
@@ -142,10 +142,17 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
|
|
|
142
142
|
* accumulated old→new ID mapping.
|
|
143
143
|
*/
|
|
144
144
|
private insertBlokBlocks(
|
|
145
|
-
|
|
145
|
+
rawBlocks: BlokClipboardBlock[],
|
|
146
146
|
canReplace: boolean
|
|
147
147
|
): void {
|
|
148
148
|
const { BlockManager, Caret, Tools } = this.Blok;
|
|
149
|
+
|
|
150
|
+
// Some article shapes (e.g. flat-array exports) reference table children
|
|
151
|
+
// ONLY via `data.content[r][c].blocks = [<id>]` and never set parentId
|
|
152
|
+
// on the children themselves. Backfill parentId before classification so
|
|
153
|
+
// those children get adopted by the table during the two-pass insert
|
|
154
|
+
// instead of becoming detached top-level paragraphs.
|
|
155
|
+
const blocks = backfillTableChildParents(rawBlocks);
|
|
149
156
|
const sanitizedBlocks = sanitizeBlocks(
|
|
150
157
|
blocks,
|
|
151
158
|
(name) => Tools.blockTools.get(name)?.sanitizeConfig ?? {},
|
|
@@ -243,6 +250,91 @@ export class BlokDataHandler extends BasePasteHandler implements PasteHandler {
|
|
|
243
250
|
}
|
|
244
251
|
}
|
|
245
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Records each cell-referenced child id under its owning table.
|
|
255
|
+
*/
|
|
256
|
+
function collectCellChildIds(
|
|
257
|
+
cell: unknown,
|
|
258
|
+
tableId: string,
|
|
259
|
+
childToTable: Map<string, string>
|
|
260
|
+
): void {
|
|
261
|
+
if (
|
|
262
|
+
typeof cell !== 'object' ||
|
|
263
|
+
cell === null ||
|
|
264
|
+
!Array.isArray((cell as { blocks?: unknown }).blocks)
|
|
265
|
+
) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const ids = (cell as { blocks: unknown[] }).blocks;
|
|
269
|
+
|
|
270
|
+
ids.forEach(childId => {
|
|
271
|
+
if (typeof childId === 'string' && !childToTable.has(childId)) {
|
|
272
|
+
childToTable.set(childId, tableId);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Walks a clipboard table block's `data.content[r][c]` cells and records
|
|
279
|
+
* every child id referenced by `cell.blocks` under its owning table.
|
|
280
|
+
*/
|
|
281
|
+
function collectTableCellRefs(
|
|
282
|
+
block: BlokClipboardBlock,
|
|
283
|
+
childToTable: Map<string, string>
|
|
284
|
+
): void {
|
|
285
|
+
if (block.tool !== 'table' || block.id === undefined) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const data = block.data as { content?: unknown } | undefined;
|
|
289
|
+
const content = data?.content;
|
|
290
|
+
|
|
291
|
+
if (!Array.isArray(content)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const tableId = block.id;
|
|
296
|
+
|
|
297
|
+
content.forEach(row => {
|
|
298
|
+
if (!Array.isArray(row)) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
row.forEach(cell => collectCellChildIds(cell, tableId, childToTable));
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Backfills `parentId` on clipboard blocks that are referenced by a sibling
|
|
307
|
+
* table's `data.content[r][c].blocks` array but never declare a parent of
|
|
308
|
+
* their own. Idempotent — never overwrites an explicit parentId.
|
|
309
|
+
*/
|
|
310
|
+
function backfillTableChildParents(
|
|
311
|
+
blocks: BlokClipboardBlock[]
|
|
312
|
+
): BlokClipboardBlock[] {
|
|
313
|
+
const childToTable = new Map<string, string>();
|
|
314
|
+
|
|
315
|
+
blocks.forEach(block => collectTableCellRefs(block, childToTable));
|
|
316
|
+
|
|
317
|
+
if (childToTable.size === 0) {
|
|
318
|
+
return blocks;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return blocks.map(block => {
|
|
322
|
+
if (block.id === undefined) {
|
|
323
|
+
return block;
|
|
324
|
+
}
|
|
325
|
+
const tableId = childToTable.get(block.id);
|
|
326
|
+
|
|
327
|
+
if (tableId === undefined) {
|
|
328
|
+
return block;
|
|
329
|
+
}
|
|
330
|
+
if (block.parentId !== undefined && block.parentId !== null) {
|
|
331
|
+
return block;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return { ...block, parentId: tableId };
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
246
338
|
/**
|
|
247
339
|
* Recursively walks `value` and replaces any string found as a key in `idMap`
|
|
248
340
|
* with its mapped value. Used to remap old block IDs to new IDs within a
|
|
@@ -7,6 +7,7 @@ import { generateBlockId, log, logLabeled } from '../utils';
|
|
|
7
7
|
import {
|
|
8
8
|
analyzeDataFormat,
|
|
9
9
|
expandToHierarchical,
|
|
10
|
+
normalizeTableChildParents,
|
|
10
11
|
shouldExpandToHierarchical,
|
|
11
12
|
type DataFormatAnalysis,
|
|
12
13
|
} from '../utils/data-model-transform';
|
|
@@ -88,10 +89,17 @@ export class Renderer extends Module {
|
|
|
88
89
|
this.detectedInputFormat = analysis.format;
|
|
89
90
|
|
|
90
91
|
// Transform to hierarchical if config requires it
|
|
91
|
-
const
|
|
92
|
+
const expandedBlocks = shouldExpandToHierarchical(dataModelConfig, analysis.format)
|
|
92
93
|
? expandToHierarchical(blocksData)
|
|
93
94
|
: blocksData;
|
|
94
95
|
|
|
96
|
+
// Tables persist child references via `data.content[r][c].blocks = [<id>]`
|
|
97
|
+
// rather than an explicit `parent` field on each child. Pre-normalize
|
|
98
|
+
// those parent references so downstream code that gates on parentId
|
|
99
|
+
// (read-only cell mounter, saver filter, hierarchy queries) correctly
|
|
100
|
+
// recognizes the children as belonging to their table.
|
|
101
|
+
const processedBlocks = normalizeTableChildParents(expandedBlocks);
|
|
102
|
+
|
|
95
103
|
// Note: Yjs data layer is loaded via BlockManager.insertMany() with the correct block IDs
|
|
96
104
|
|
|
97
105
|
/**
|
|
@@ -42,7 +42,7 @@ export class ThemeManager extends Module {
|
|
|
42
42
|
public prepare(): void {
|
|
43
43
|
state.activeInstances++;
|
|
44
44
|
this.mode = this.config.theme ?? 'auto';
|
|
45
|
-
this.applyAttribute();
|
|
45
|
+
this.applyAttribute(false);
|
|
46
46
|
this.resolved = this.deriveResolved();
|
|
47
47
|
|
|
48
48
|
if (this.mode === 'auto') {
|
|
@@ -75,7 +75,7 @@ export class ThemeManager extends Module {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
this.mode = mode;
|
|
78
|
-
this.applyAttribute();
|
|
78
|
+
this.applyAttribute(true);
|
|
79
79
|
this.resolved = this.deriveResolved();
|
|
80
80
|
|
|
81
81
|
if (this.mode === 'auto') {
|
|
@@ -102,7 +102,14 @@ export class ThemeManager extends Module {
|
|
|
102
102
|
|
|
103
103
|
// ─── Private ───────────────────────────────────────────────────────────────
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
/**
|
|
106
|
+
* @param explicit - true when called from setMode() (consumer API);
|
|
107
|
+
* false when called from prepare() (initial construction).
|
|
108
|
+
* During prepare(), skip setting the attribute if another
|
|
109
|
+
* instance already controls it — prevents nested editors
|
|
110
|
+
* (e.g. inside Columns) from overriding the parent theme.
|
|
111
|
+
*/
|
|
112
|
+
private applyAttribute(explicit: boolean): void {
|
|
106
113
|
if (typeof document === 'undefined') {
|
|
107
114
|
return;
|
|
108
115
|
}
|
|
@@ -112,6 +119,9 @@ export class ThemeManager extends Module {
|
|
|
112
119
|
this.removeAttribute();
|
|
113
120
|
}
|
|
114
121
|
} else {
|
|
122
|
+
if (!explicit && state.activeInstances > 1 && document.documentElement.hasAttribute(ATTR)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
115
125
|
document.documentElement.setAttribute(ATTR, this.mode);
|
|
116
126
|
}
|
|
117
127
|
}
|
|
@@ -368,6 +368,45 @@ const expandListToHierarchical = (
|
|
|
368
368
|
return blocks;
|
|
369
369
|
};
|
|
370
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Recursively expand a list of legacy body blocks into hierarchical flat blocks.
|
|
373
|
+
* Each body block is routed through expandToHierarchical so that nested legacy
|
|
374
|
+
* toggleList/callout/list structures are fully flattened instead of passing
|
|
375
|
+
* through with their legacy type (which would hit Renderer's "unknown tool"
|
|
376
|
+
* fallback and render as a stub).
|
|
377
|
+
*
|
|
378
|
+
* Returns both the direct-child IDs (for the parent's `content` array) and
|
|
379
|
+
* the flattened descendant blocks in document order.
|
|
380
|
+
* @param bodyBlocks - legacy body blocks to expand
|
|
381
|
+
* @param parentId - id of the parent block (toggle/callout) that owns them
|
|
382
|
+
*/
|
|
383
|
+
const expandLegacyBodyBlocks = (
|
|
384
|
+
bodyBlocks: OutputBlockData[],
|
|
385
|
+
parentId: BlockId
|
|
386
|
+
): { childIds: BlockId[]; childBlocks: OutputBlockData[] } => {
|
|
387
|
+
const childIds: BlockId[] = [];
|
|
388
|
+
const childBlocks: OutputBlockData[] = [];
|
|
389
|
+
|
|
390
|
+
for (const childBlock of bodyBlocks) {
|
|
391
|
+
const expanded = expandToHierarchical([childBlock]);
|
|
392
|
+
|
|
393
|
+
if (expanded.length === 0) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// The first emitted block corresponds to the original input block.
|
|
398
|
+
// Re-parent it to the legacy container; descendants already carry the
|
|
399
|
+
// correct parent refs assigned during their own recursive expansion.
|
|
400
|
+
const [first, ...rest] = expanded;
|
|
401
|
+
const childId = first.id ?? generateBlockId();
|
|
402
|
+
|
|
403
|
+
childIds.push(childId);
|
|
404
|
+
childBlocks.push({ ...first, id: childId, parent: parentId }, ...rest);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return { childIds, childBlocks };
|
|
408
|
+
};
|
|
409
|
+
|
|
371
410
|
/**
|
|
372
411
|
* Expand a legacy toggleList block into flat toggle block + child blocks
|
|
373
412
|
*/
|
|
@@ -378,20 +417,7 @@ const expandToggleListToHierarchical = (
|
|
|
378
417
|
const toggleId = block.id ?? generateBlockId();
|
|
379
418
|
const bodyBlocks = block.data.body?.blocks ?? [];
|
|
380
419
|
|
|
381
|
-
|
|
382
|
-
const childIds: BlockId[] = [];
|
|
383
|
-
const childBlocks: OutputBlockData[] = [];
|
|
384
|
-
|
|
385
|
-
for (const childBlock of bodyBlocks) {
|
|
386
|
-
const childId = childBlock.id ?? generateBlockId();
|
|
387
|
-
|
|
388
|
-
childIds.push(childId);
|
|
389
|
-
childBlocks.push({
|
|
390
|
-
...childBlock,
|
|
391
|
-
id: childId,
|
|
392
|
-
parent: toggleId,
|
|
393
|
-
});
|
|
394
|
-
}
|
|
420
|
+
const { childIds, childBlocks } = expandLegacyBodyBlocks(bodyBlocks, toggleId);
|
|
395
421
|
|
|
396
422
|
const sharedFields = {
|
|
397
423
|
id: toggleId,
|
|
@@ -439,20 +465,7 @@ const expandCalloutToHierarchical = (
|
|
|
439
465
|
const calloutId = block.id ?? generateBlockId();
|
|
440
466
|
const bodyBlocks = block.data.body?.blocks ?? [];
|
|
441
467
|
|
|
442
|
-
|
|
443
|
-
const childIds: BlockId[] = [];
|
|
444
|
-
const childBlocks: OutputBlockData[] = [];
|
|
445
|
-
|
|
446
|
-
for (const childBlock of bodyBlocks) {
|
|
447
|
-
const childId = childBlock.id ?? generateBlockId();
|
|
448
|
-
|
|
449
|
-
childIds.push(childId);
|
|
450
|
-
childBlocks.push({
|
|
451
|
-
...childBlock,
|
|
452
|
-
id: childId,
|
|
453
|
-
parent: calloutId,
|
|
454
|
-
});
|
|
455
|
-
}
|
|
468
|
+
const { childIds, childBlocks } = expandLegacyBodyBlocks(bodyBlocks, calloutId);
|
|
456
469
|
|
|
457
470
|
// Map variant → backgroundColor preset
|
|
458
471
|
const variant = block.data.variant ?? 'general';
|
|
@@ -905,6 +918,131 @@ export const collapseToLegacy = (blocks: OutputBlockData[]): OutputBlockData[] =
|
|
|
905
918
|
return result;
|
|
906
919
|
};
|
|
907
920
|
|
|
921
|
+
/**
|
|
922
|
+
* A table cell that references its content blocks by id.
|
|
923
|
+
* Tables persist their child blocks via `data.content[row][col].blocks = [<id>, ...]`
|
|
924
|
+
* rather than nesting block payloads inline, so the parent/content relationship
|
|
925
|
+
* is implicit in the table data instead of explicit on each child block.
|
|
926
|
+
*/
|
|
927
|
+
interface CellWithBlockRefs {
|
|
928
|
+
blocks: string[];
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
const isCellWithBlockRefs = (cell: unknown): cell is CellWithBlockRefs => {
|
|
932
|
+
return (
|
|
933
|
+
typeof cell === 'object' &&
|
|
934
|
+
cell !== null &&
|
|
935
|
+
Array.isArray((cell as { blocks?: unknown }).blocks)
|
|
936
|
+
);
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
interface TableDataShape {
|
|
940
|
+
content?: unknown;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const getTableContentRows = (data: unknown): unknown[][] | null => {
|
|
944
|
+
if (typeof data !== 'object' || data === null) {
|
|
945
|
+
return null;
|
|
946
|
+
}
|
|
947
|
+
const content = (data as TableDataShape).content;
|
|
948
|
+
|
|
949
|
+
if (!Array.isArray(content)) {
|
|
950
|
+
return null;
|
|
951
|
+
}
|
|
952
|
+
return content as unknown[][];
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* When a flat block array contains `table` blocks that reference child blocks
|
|
957
|
+
* via `data.content[row][col].blocks = [<id>, ...]`, ensure each referenced
|
|
958
|
+
* child carries `parent: <tableId>`. This makes the parent/content invariant
|
|
959
|
+
* explicit even for externally-authored data shapes that omit the `parent`
|
|
960
|
+
* field on children.
|
|
961
|
+
*
|
|
962
|
+
* Without this normalization, downstream readers that key on parentId
|
|
963
|
+
* (`mountCellBlocksReadOnly`'s cross-table guard, the table saver's
|
|
964
|
+
* own-child filter, hierarchy queries, drag-and-drop) skip those children
|
|
965
|
+
* and leak them out of the table, rendering them at the bottom of the page
|
|
966
|
+
* instead of inside the cells.
|
|
967
|
+
*
|
|
968
|
+
* The function is idempotent, never mutates the input array, and leaves
|
|
969
|
+
* pre-existing `parent` fields unchanged. Children referenced by multiple
|
|
970
|
+
* tables get assigned to the first table that lists them (first-writer-wins);
|
|
971
|
+
* corrupted cross-table references are preserved as-is so defensive guards
|
|
972
|
+
* downstream can still reject them.
|
|
973
|
+
* @param blocks - flat block array potentially containing tables with cell refs
|
|
974
|
+
*/
|
|
975
|
+
const collectCellChildRefs = (
|
|
976
|
+
cell: unknown,
|
|
977
|
+
tableId: BlockId,
|
|
978
|
+
childToTable: Map<BlockId, BlockId>
|
|
979
|
+
): void => {
|
|
980
|
+
if (!isCellWithBlockRefs(cell)) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
for (const childId of cell.blocks) {
|
|
984
|
+
if (typeof childId !== 'string' || childToTable.has(childId)) {
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
childToTable.set(childId, tableId);
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
const collectRowChildRefs = (
|
|
992
|
+
row: unknown,
|
|
993
|
+
tableId: BlockId,
|
|
994
|
+
childToTable: Map<BlockId, BlockId>
|
|
995
|
+
): void => {
|
|
996
|
+
if (!Array.isArray(row)) {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
row.forEach(cell => collectCellChildRefs(cell, tableId, childToTable));
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
const collectTableChildRefs = (
|
|
1003
|
+
tableBlock: OutputBlockData,
|
|
1004
|
+
childToTable: Map<BlockId, BlockId>
|
|
1005
|
+
): void => {
|
|
1006
|
+
if (tableBlock.id === undefined || tableBlock.id === null) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
const rows = getTableContentRows(tableBlock.data);
|
|
1010
|
+
|
|
1011
|
+
if (rows === null) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const tableId = tableBlock.id;
|
|
1015
|
+
|
|
1016
|
+
rows.forEach(row => collectRowChildRefs(row, tableId, childToTable));
|
|
1017
|
+
};
|
|
1018
|
+
|
|
1019
|
+
export const normalizeTableChildParents = (blocks: OutputBlockData[]): OutputBlockData[] => {
|
|
1020
|
+
const childToTable = new Map<BlockId, BlockId>();
|
|
1021
|
+
|
|
1022
|
+
blocks
|
|
1023
|
+
.filter(block => block.type === 'table')
|
|
1024
|
+
.forEach(tableBlock => collectTableChildRefs(tableBlock, childToTable));
|
|
1025
|
+
|
|
1026
|
+
if (childToTable.size === 0) {
|
|
1027
|
+
return blocks;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
return blocks.map(block => {
|
|
1031
|
+
if (block.id === undefined || block.id === null) {
|
|
1032
|
+
return block;
|
|
1033
|
+
}
|
|
1034
|
+
const tableId = childToTable.get(block.id);
|
|
1035
|
+
|
|
1036
|
+
if (tableId === undefined) {
|
|
1037
|
+
return block;
|
|
1038
|
+
}
|
|
1039
|
+
if (block.parent !== undefined && block.parent !== null) {
|
|
1040
|
+
return block;
|
|
1041
|
+
}
|
|
1042
|
+
return { ...block, parent: tableId };
|
|
1043
|
+
});
|
|
1044
|
+
};
|
|
1045
|
+
|
|
908
1046
|
/**
|
|
909
1047
|
* Check if transformation is needed based on config and detected format
|
|
910
1048
|
*/
|
|
@@ -6,6 +6,7 @@ import type { HandlerContext } from '../components/modules/paste/types';
|
|
|
6
6
|
import type { PasteHandler } from '../components/modules/paste/handlers/base';
|
|
7
7
|
import { BasePasteHandler } from '../components/modules/paste/handlers/base';
|
|
8
8
|
import { Block } from '../components/block';
|
|
9
|
+
import { normalizeTableChildParents } from '../components/utils/data-model-transform';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Patterns that indicate text is likely Markdown rather than plain text.
|
|
@@ -66,12 +67,18 @@ export class MarkdownHandler extends BasePasteHandler implements PasteHandler {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
const { markdownToBlocks } = await import('./index');
|
|
69
|
-
const
|
|
70
|
+
const rawOutputBlocks = await markdownToBlocks(data);
|
|
70
71
|
|
|
71
|
-
if (!
|
|
72
|
+
if (!rawOutputBlocks.length) {
|
|
72
73
|
return false;
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
// Defense-in-depth: backfill `parent` on table cell children so that any
|
|
77
|
+
// future regression in mdast-to-blocks (or external converter) cannot
|
|
78
|
+
// produce the dodopizza shape (children referenced by table cells but
|
|
79
|
+
// lacking explicit parent), which would render them at page bottom.
|
|
80
|
+
const outputBlocks = normalizeTableChildParents(rawOutputBlocks);
|
|
81
|
+
|
|
75
82
|
const { BlockManager, Caret } = this.Blok;
|
|
76
83
|
|
|
77
84
|
// Replace empty default block if present
|
|
@@ -326,10 +326,15 @@ export const mountCellBlocksReadOnly = (
|
|
|
326
326
|
continue;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
// Skip blocks
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
|
|
329
|
+
// Skip blocks whose parentId explicitly points to a DIFFERENT table
|
|
330
|
+
// (corrupted cross-table references). Blocks with null/undefined
|
|
331
|
+
// parentId are accepted: legitimate flat-array data shapes (e.g. the
|
|
332
|
+
// dodopizza article format) reference children by id from
|
|
333
|
+
// `cell.blocks` without setting `parent` on each child, and the
|
|
334
|
+
// Renderer's normalizeTableChildParents pre-step is the primary
|
|
335
|
+
// place that backfills parentId. This guard is defense-in-depth for
|
|
336
|
+
// load paths that bypass the Renderer (Yjs sync, direct insertion).
|
|
337
|
+
if (block.parentId !== null && block.parentId !== undefined && block.parentId !== _tableBlockId) {
|
|
333
338
|
continue;
|
|
334
339
|
}
|
|
335
340
|
|
|
File without changes
|
/package/dist/chunks/{objectWithoutProperties-D0XxKB4n.mjs → objectWithoutProperties-Dci1-l7D.mjs}
RENAMED
|
File without changes
|