@kedataindo/docflow-plugins 0.0.51 → 0.0.52

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/index.cjs CHANGED
@@ -84,6 +84,7 @@ __export(index_exports, {
84
84
  slashMenuPlugin: () => slashMenuPlugin,
85
85
  slashState: () => slashState,
86
86
  smartElementsPlugin: () => smartElementsPlugin,
87
+ tablePageSplitPlugin: () => tablePageSplitPlugin,
87
88
  tablePlugin: () => tablePlugin,
88
89
  textColorPlugin: () => textColorPlugin,
89
90
  tocPlugin: () => tocPlugin
@@ -274,6 +275,20 @@ var import_extension_table_row = __toESM(require("@tiptap/extension-table-row"),
274
275
  var import_extension_table_cell = __toESM(require("@tiptap/extension-table-cell"), 1);
275
276
  var import_extension_table_header = __toESM(require("@tiptap/extension-table-header"), 1);
276
277
  var CustomTable = import_extension_table.default.extend({
278
+ addAttributes() {
279
+ return {
280
+ ...this.parent?.(),
281
+ // Marker used by the experimental `tablePageSplitPlugin` to tag the
282
+ // head of a split pair. Backwards-compatible (default null) so docs
283
+ // without this attr round-trip cleanly. See
284
+ // packages/plugins/src/tablePageSplit.ts.
285
+ tablePageSplit: {
286
+ default: null,
287
+ parseHTML: () => null,
288
+ renderHTML: () => ({})
289
+ }
290
+ };
291
+ },
277
292
  renderHTML({ node, HTMLAttributes }) {
278
293
  let colCount = 0;
279
294
  const firstRow = node.firstChild;
@@ -316,9 +331,11 @@ var CustomTable = import_extension_table.default.extend({
316
331
  }
317
332
  }
318
333
  const tableAttrs = (0, import_core.mergeAttributes)(HTMLAttributes);
334
+ tableAttrs["data-tps-splittable"] = "";
319
335
  if (hasExplicitWidths && totalWidth > 0) {
320
336
  const existingStyle = tableAttrs.style || "";
321
337
  tableAttrs.style = existingStyle ? `${existingStyle}; width: ${totalWidth}px !important` : `width: ${totalWidth}px !important`;
338
+ tableAttrs["data-colwidth"] = "explicit";
322
339
  }
323
340
  return [
324
341
  "table",
@@ -3282,10 +3299,236 @@ var smartElementsPlugin = (0, import_docflow_core19.definePlugin)({
3282
3299
  }
3283
3300
  });
3284
3301
 
3285
- // src/slashMenu.ts
3302
+ // src/tablePageSplit.ts
3303
+ var import_docflow_core20 = require("@kedataindo/docflow-core");
3286
3304
  var import_core12 = require("@tiptap/core");
3287
3305
  var import_state3 = require("@tiptap/pm/state");
3288
- var import_docflow_core20 = require("@kedataindo/docflow-core");
3306
+ var PLUGIN_KEY = new import_state3.PluginKey("docflow/table-page-split");
3307
+ var SPLIT_MARK = "head";
3308
+ var MAX_SPLIT_DEPTH = 16;
3309
+ function readPageContentHeightPx(view, editor) {
3310
+ const storage = editor?.storage?.PaginationPlus;
3311
+ if (storage && typeof storage.pageHeight === "number") {
3312
+ const h1 = storage.headerHeight?.get?.(1) ?? 0;
3313
+ const f1 = storage.footerHeight?.get?.(1) ?? 0;
3314
+ const contentPx = storage.pageHeight - (storage.marginTop ?? 0) - (storage.marginBottom ?? 0) - (storage.contentMarginTop ?? 0) - (storage.contentMarginBottom ?? 0) - h1 - f1;
3315
+ if (Number.isFinite(contentPx) && contentPx > 0) return contentPx;
3316
+ }
3317
+ const raw = view.dom.style.getPropertyValue("--rm-max-content-child-height");
3318
+ if (raw) {
3319
+ const px = parseFloat(raw);
3320
+ if (Number.isFinite(px) && px > 0) return px;
3321
+ }
3322
+ return null;
3323
+ }
3324
+ function measureElementPx(el) {
3325
+ if (!el || !(el instanceof HTMLElement)) return 0;
3326
+ return el.getBoundingClientRect().height;
3327
+ }
3328
+ function availablePxAt(view, pos, defaultPageContentPx) {
3329
+ try {
3330
+ const tableCoords = view.coordsAtPos(pos + 1);
3331
+ const tableTop = tableCoords.top;
3332
+ const paginationWrapper = view.dom.querySelector("[data-rm-pagination]");
3333
+ if (!paginationWrapper) return defaultPageContentPx;
3334
+ const breakers = paginationWrapper.querySelectorAll(".rm-page-break .breaker");
3335
+ for (let i = 0; i < breakers.length; i++) {
3336
+ const br = breakers[i];
3337
+ if (!(br instanceof HTMLElement)) continue;
3338
+ const breakTop = br.getBoundingClientRect().top;
3339
+ if (breakTop > tableTop) {
3340
+ return Math.max(1, Math.floor(breakTop - tableTop));
3341
+ }
3342
+ }
3343
+ return defaultPageContentPx;
3344
+ } catch {
3345
+ return null;
3346
+ }
3347
+ }
3348
+ function measureRows(tableNode, tableDom) {
3349
+ const expectedRows = tableNode.childCount;
3350
+ const trs = tableDom.querySelectorAll(":scope > tbody > tr");
3351
+ if (trs.length !== expectedRows) return null;
3352
+ const measurements = [];
3353
+ let cumulative = 0;
3354
+ for (let i = 0; i < trs.length; i++) {
3355
+ const h = measureElementPx(trs[i]);
3356
+ cumulative += h;
3357
+ measurements.push({ rowIndex: i, heightPx: h, cumulativePx: cumulative });
3358
+ }
3359
+ return measurements;
3360
+ }
3361
+ function findSplitRow(measurements, pageContentPx) {
3362
+ if (measurements.length < 2) return -1;
3363
+ if (measurements[measurements.length - 1].cumulativePx <= pageContentPx) return -1;
3364
+ let lo = 0;
3365
+ let hi = measurements.length - 2;
3366
+ let best = -1;
3367
+ while (lo <= hi) {
3368
+ const mid = lo + hi >> 1;
3369
+ if (measurements[mid].cumulativePx <= pageContentPx) {
3370
+ best = mid;
3371
+ lo = mid + 1;
3372
+ } else {
3373
+ hi = mid - 1;
3374
+ }
3375
+ }
3376
+ return best;
3377
+ }
3378
+ function rowBoundaryOffset(tableNode, splitRow) {
3379
+ if (splitRow < 0 || tableNode.childCount === 0) return 0;
3380
+ let boundary = 0;
3381
+ tableNode.forEach((row, offset, index) => {
3382
+ if (index <= splitRow) boundary = offset + row.nodeSize;
3383
+ });
3384
+ return boundary;
3385
+ }
3386
+ function tailMeasurements(measurements, splitRow) {
3387
+ const tail = [];
3388
+ let cumulative = 0;
3389
+ for (let i = splitRow + 1; i < measurements.length; i++) {
3390
+ cumulative += measurements[i].heightPx;
3391
+ tail.push({ rowIndex: i, heightPx: measurements[i].heightPx, cumulativePx: cumulative });
3392
+ }
3393
+ return tail;
3394
+ }
3395
+ function buildSplitChain(tableNode, measurements, headBudgetPx, pageContentPx, schema, tableType, depth) {
3396
+ const budget = depth === 0 ? headBudgetPx : pageContentPx;
3397
+ const splitRow = findSplitRow(measurements, budget);
3398
+ if (splitRow < 0) return [tableNode];
3399
+ if (depth >= MAX_SPLIT_DEPTH) return [tableNode];
3400
+ const splitPos = rowBoundaryOffset(tableNode, splitRow);
3401
+ const content = tableNode.content;
3402
+ const leftContent = content.cut(0, splitPos);
3403
+ const rightContent = content.cut(splitPos);
3404
+ const leftAttrs = { ...tableNode.attrs, tablePageSplit: SPLIT_MARK };
3405
+ const rightAttrs = { ...tableNode.attrs, tablePageSplit: null };
3406
+ const leftTable = tableType.create(leftAttrs, leftContent, tableNode.marks);
3407
+ const rightTable = tableType.create(rightAttrs, rightContent, tableNode.marks);
3408
+ const expectedRows = tableNode.childCount;
3409
+ const leftRows = leftTable.childCount;
3410
+ const rightRows = rightTable.childCount;
3411
+ if (leftRows + rightRows !== expectedRows) {
3412
+ throw new Error(
3413
+ `[tablePageSplitPlugin] row-count invariant violated: expected ${expectedRows}, got ${leftRows} + ${rightRows} = ${leftRows + rightRows}`
3414
+ );
3415
+ }
3416
+ const tail = tailMeasurements(measurements, splitRow);
3417
+ const tailChain = buildSplitChain(rightTable, tail, headBudgetPx, pageContentPx, schema, tableType, depth + 1);
3418
+ if (tailChain.length === 1) {
3419
+ return [leftTable, rightTable];
3420
+ }
3421
+ return [leftTable, ...tailChain];
3422
+ }
3423
+ function lastChunkHeightPx(measurements, headBudgetPx, pageContentPx) {
3424
+ let current = measurements;
3425
+ let budget = headBudgetPx;
3426
+ let splitCount = 0;
3427
+ for (let depth = 0; depth < MAX_SPLIT_DEPTH; depth++) {
3428
+ const splitRow = findSplitRow(current, budget);
3429
+ if (splitRow < 0) {
3430
+ return splitCount === 0 ? null : current[current.length - 1]?.cumulativePx ?? 0;
3431
+ }
3432
+ splitCount++;
3433
+ current = tailMeasurements(current, splitRow);
3434
+ budget = pageContentPx;
3435
+ }
3436
+ return current[current.length - 1]?.cumulativePx ?? 0;
3437
+ }
3438
+ function runSplitPass(view, editor) {
3439
+ if (!view || view.isDestroyed) return;
3440
+ const v = view;
3441
+ const state = v.state;
3442
+ const { schema } = state;
3443
+ const tableType = schema.nodes.table;
3444
+ if (!tableType) return;
3445
+ const pageContentPx = readPageContentHeightPx(v, editor);
3446
+ if (pageContentPx == null) return;
3447
+ const tables = [];
3448
+ state.doc.descendants((node, pos) => {
3449
+ if (node.type !== tableType) return true;
3450
+ if (node.attrs.tablePageSplit === SPLIT_MARK) return true;
3451
+ const domAt = v.nodeDOM(pos);
3452
+ if (!(domAt instanceof HTMLElement)) return true;
3453
+ const measurements = measureRows(node, domAt);
3454
+ if (!measurements) return true;
3455
+ tables.push({ pos, node, measurements });
3456
+ return true;
3457
+ });
3458
+ if (tables.length === 0) return;
3459
+ const pending = [];
3460
+ let cursorAvailablePx = null;
3461
+ for (let i = 0; i < tables.length; i++) {
3462
+ const { pos, node, measurements } = tables[i];
3463
+ const availablePx = i === 0 ? availablePxAt(v, pos, pageContentPx) ?? pageContentPx : cursorAvailablePx ?? pageContentPx;
3464
+ const chain = buildSplitChain(node, measurements, availablePx, pageContentPx, schema, tableType, 0);
3465
+ if (chain.length > 1) pending.push({ pos, node, chain });
3466
+ const lastChunkPx = lastChunkHeightPx(measurements, availablePx, pageContentPx);
3467
+ if (lastChunkPx === null) {
3468
+ const totalPx = measurements[measurements.length - 1]?.cumulativePx ?? 0;
3469
+ cursorAvailablePx = Math.max(0, availablePx - totalPx);
3470
+ } else {
3471
+ cursorAvailablePx = Math.max(0, pageContentPx - lastChunkPx);
3472
+ }
3473
+ }
3474
+ if (pending.length === 0) return;
3475
+ const tr = state.tr;
3476
+ pending.sort((a, b) => b.pos - a.pos);
3477
+ for (const { pos, node, chain } of pending) {
3478
+ tr.replaceWith(pos, pos + node.nodeSize, chain);
3479
+ }
3480
+ tr.setMeta(PLUGIN_KEY, { splitAtRow: -1 });
3481
+ v.dispatch(tr);
3482
+ }
3483
+ var TablePageSplitExtension = import_core12.Extension.create({
3484
+ name: "tablePageSplit",
3485
+ addProseMirrorPlugins() {
3486
+ let viewRef = null;
3487
+ let pendingRun = false;
3488
+ const ext = this;
3489
+ const editorRef = ext.editor ?? null;
3490
+ return [
3491
+ new import_state3.Plugin({
3492
+ key: PLUGIN_KEY,
3493
+ view(_editorView) {
3494
+ viewRef = _editorView;
3495
+ return {
3496
+ destroy() {
3497
+ viewRef = null;
3498
+ }
3499
+ };
3500
+ },
3501
+ appendTransaction(transactions, _oldState, _newState) {
3502
+ if (transactions.some((t) => t.getMeta("PAGE_COUNT_META_KEY") !== void 0)) {
3503
+ return null;
3504
+ }
3505
+ if (transactions.some((t) => {
3506
+ const m = t.getMeta(PLUGIN_KEY);
3507
+ return m !== void 0 && m.splitAtRow !== void 0;
3508
+ })) return null;
3509
+ if (!viewRef || viewRef.isDestroyed) return null;
3510
+ if (pendingRun) return null;
3511
+ pendingRun = true;
3512
+ queueMicrotask(() => {
3513
+ pendingRun = false;
3514
+ if (!viewRef || viewRef.isDestroyed) return;
3515
+ runSplitPass(viewRef, editorRef);
3516
+ });
3517
+ return null;
3518
+ }
3519
+ })
3520
+ ];
3521
+ }
3522
+ });
3523
+ var tablePageSplitPlugin = (0, import_docflow_core20.definePlugin)({
3524
+ id: "tablePageSplit",
3525
+ tiptapExtensions: [TablePageSplitExtension]
3526
+ });
3527
+
3528
+ // src/slashMenu.ts
3529
+ var import_core13 = require("@tiptap/core");
3530
+ var import_state4 = require("@tiptap/pm/state");
3531
+ var import_docflow_core21 = require("@kedataindo/docflow-core");
3289
3532
  var slashState = {
3290
3533
  open: false,
3291
3534
  query: "",
@@ -3339,12 +3582,12 @@ function getRegisteredCommands() {
3339
3582
  return true;
3340
3583
  });
3341
3584
  }
3342
- var SlashMenuExtension = import_core12.Extension.create({
3585
+ var SlashMenuExtension = import_core13.Extension.create({
3343
3586
  name: "slashMenu",
3344
3587
  addProseMirrorPlugins() {
3345
3588
  return [
3346
- new import_state3.Plugin({
3347
- key: new import_state3.PluginKey("slashMenu"),
3589
+ new import_state4.Plugin({
3590
+ key: new import_state4.PluginKey("slashMenu"),
3348
3591
  props: {
3349
3592
  handleTextInput(view, from, _to, text) {
3350
3593
  if (text === "/") {
@@ -3430,7 +3673,7 @@ var SlashMenuExtension = import_core12.Extension.create({
3430
3673
  ];
3431
3674
  }
3432
3675
  });
3433
- var slashMenuPlugin = (0, import_docflow_core20.definePlugin)({
3676
+ var slashMenuPlugin = (0, import_docflow_core21.definePlugin)({
3434
3677
  id: "slash-menu",
3435
3678
  tiptapExtensions: [SlashMenuExtension],
3436
3679
  hooks: {
@@ -3450,6 +3693,7 @@ var defaultPlugins = [
3450
3693
  linkPlugin,
3451
3694
  imagePlugin,
3452
3695
  tablePlugin,
3696
+ tablePageSplitPlugin,
3453
3697
  blockquotePlugin,
3454
3698
  codeBlockPlugin,
3455
3699
  placeholderPlugin,
@@ -3520,6 +3764,7 @@ var defaultPlugins = [
3520
3764
  slashMenuPlugin,
3521
3765
  slashState,
3522
3766
  smartElementsPlugin,
3767
+ tablePageSplitPlugin,
3523
3768
  tablePlugin,
3524
3769
  textColorPlugin,
3525
3770
  tocPlugin
package/dist/index.d.cts CHANGED
@@ -281,6 +281,8 @@ declare const smartElementsPlugin: _kedata_indonesia_docflow_core.DocsEditorPlug
281
281
  */
282
282
  declare const BibliographyNode: Node<any, any>;
283
283
 
284
+ declare const tablePageSplitPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
285
+
284
286
  interface SlashState {
285
287
  open: boolean;
286
288
  query: string;
@@ -306,4 +308,4 @@ declare const slashMenuPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
306
308
 
307
309
  declare const defaultPlugins: _kedata_indonesia_docflow_core.DocsEditorPlugin[];
308
310
 
309
- export { AIExtension, type AIPreviewState, BibliographyNode, CitationEngineExtension, CitationNode, CiteEngine, type CommentMarkAttrs, CommentMark as CommentMarkExtension, DateChipNode, DropdownChipNode, FileChipNode, FootnoteNode, LocationChipNode, PageBreak, PeopleChipNode, type PlaceholderPluginOptions, SlashMenuExtension, TocEntryNode, TocNode, TocPageNumNode, aiPlugin, aiPluginKey, alignmentPlugin, blockquotePlugin, citationPlugin, codeBlockPlugin, collectHeadings, commentPlugin, createPlaceholderPlugin, defaultPlugins, fontSizePlugin, footnotePlugin, formattingPlugin, getAIPreview, getCitationEngine, headingsPlugin, highlightPlugin, imagePlugin, insertMarkdownBlock, linkPlugin, listsPlugin, markdownToFragment, onSlashStateChange, pageBreakPlugin, placeholderPlugin, regenerateToc, registerSlashCommands, slashMenuPlugin, slashState, smartElementsPlugin, tablePlugin, textColorPlugin, tocPlugin };
311
+ export { AIExtension, type AIPreviewState, BibliographyNode, CitationEngineExtension, CitationNode, CiteEngine, type CommentMarkAttrs, CommentMark as CommentMarkExtension, DateChipNode, DropdownChipNode, FileChipNode, FootnoteNode, LocationChipNode, PageBreak, PeopleChipNode, type PlaceholderPluginOptions, SlashMenuExtension, TocEntryNode, TocNode, TocPageNumNode, aiPlugin, aiPluginKey, alignmentPlugin, blockquotePlugin, citationPlugin, codeBlockPlugin, collectHeadings, commentPlugin, createPlaceholderPlugin, defaultPlugins, fontSizePlugin, footnotePlugin, formattingPlugin, getAIPreview, getCitationEngine, headingsPlugin, highlightPlugin, imagePlugin, insertMarkdownBlock, linkPlugin, listsPlugin, markdownToFragment, onSlashStateChange, pageBreakPlugin, placeholderPlugin, regenerateToc, registerSlashCommands, slashMenuPlugin, slashState, smartElementsPlugin, tablePageSplitPlugin, tablePlugin, textColorPlugin, tocPlugin };
package/dist/index.d.ts CHANGED
@@ -281,6 +281,8 @@ declare const smartElementsPlugin: _kedata_indonesia_docflow_core.DocsEditorPlug
281
281
  */
282
282
  declare const BibliographyNode: Node<any, any>;
283
283
 
284
+ declare const tablePageSplitPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
285
+
284
286
  interface SlashState {
285
287
  open: boolean;
286
288
  query: string;
@@ -306,4 +308,4 @@ declare const slashMenuPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
306
308
 
307
309
  declare const defaultPlugins: _kedata_indonesia_docflow_core.DocsEditorPlugin[];
308
310
 
309
- export { AIExtension, type AIPreviewState, BibliographyNode, CitationEngineExtension, CitationNode, CiteEngine, type CommentMarkAttrs, CommentMark as CommentMarkExtension, DateChipNode, DropdownChipNode, FileChipNode, FootnoteNode, LocationChipNode, PageBreak, PeopleChipNode, type PlaceholderPluginOptions, SlashMenuExtension, TocEntryNode, TocNode, TocPageNumNode, aiPlugin, aiPluginKey, alignmentPlugin, blockquotePlugin, citationPlugin, codeBlockPlugin, collectHeadings, commentPlugin, createPlaceholderPlugin, defaultPlugins, fontSizePlugin, footnotePlugin, formattingPlugin, getAIPreview, getCitationEngine, headingsPlugin, highlightPlugin, imagePlugin, insertMarkdownBlock, linkPlugin, listsPlugin, markdownToFragment, onSlashStateChange, pageBreakPlugin, placeholderPlugin, regenerateToc, registerSlashCommands, slashMenuPlugin, slashState, smartElementsPlugin, tablePlugin, textColorPlugin, tocPlugin };
311
+ export { AIExtension, type AIPreviewState, BibliographyNode, CitationEngineExtension, CitationNode, CiteEngine, type CommentMarkAttrs, CommentMark as CommentMarkExtension, DateChipNode, DropdownChipNode, FileChipNode, FootnoteNode, LocationChipNode, PageBreak, PeopleChipNode, type PlaceholderPluginOptions, SlashMenuExtension, TocEntryNode, TocNode, TocPageNumNode, aiPlugin, aiPluginKey, alignmentPlugin, blockquotePlugin, citationPlugin, codeBlockPlugin, collectHeadings, commentPlugin, createPlaceholderPlugin, defaultPlugins, fontSizePlugin, footnotePlugin, formattingPlugin, getAIPreview, getCitationEngine, headingsPlugin, highlightPlugin, imagePlugin, insertMarkdownBlock, linkPlugin, listsPlugin, markdownToFragment, onSlashStateChange, pageBreakPlugin, placeholderPlugin, regenerateToc, registerSlashCommands, slashMenuPlugin, slashState, smartElementsPlugin, tablePageSplitPlugin, tablePlugin, textColorPlugin, tocPlugin };
package/dist/index.js CHANGED
@@ -182,6 +182,20 @@ import TableRow from "@tiptap/extension-table-row";
182
182
  import TableCell from "@tiptap/extension-table-cell";
183
183
  import TableHeader from "@tiptap/extension-table-header";
184
184
  var CustomTable = Table.extend({
185
+ addAttributes() {
186
+ return {
187
+ ...this.parent?.(),
188
+ // Marker used by the experimental `tablePageSplitPlugin` to tag the
189
+ // head of a split pair. Backwards-compatible (default null) so docs
190
+ // without this attr round-trip cleanly. See
191
+ // packages/plugins/src/tablePageSplit.ts.
192
+ tablePageSplit: {
193
+ default: null,
194
+ parseHTML: () => null,
195
+ renderHTML: () => ({})
196
+ }
197
+ };
198
+ },
185
199
  renderHTML({ node, HTMLAttributes }) {
186
200
  let colCount = 0;
187
201
  const firstRow = node.firstChild;
@@ -224,9 +238,11 @@ var CustomTable = Table.extend({
224
238
  }
225
239
  }
226
240
  const tableAttrs = mergeAttributes(HTMLAttributes);
241
+ tableAttrs["data-tps-splittable"] = "";
227
242
  if (hasExplicitWidths && totalWidth > 0) {
228
243
  const existingStyle = tableAttrs.style || "";
229
244
  tableAttrs.style = existingStyle ? `${existingStyle}; width: ${totalWidth}px !important` : `width: ${totalWidth}px !important`;
245
+ tableAttrs["data-colwidth"] = "explicit";
230
246
  }
231
247
  return [
232
248
  "table",
@@ -3190,10 +3206,236 @@ var smartElementsPlugin = definePlugin19({
3190
3206
  }
3191
3207
  });
3192
3208
 
3193
- // src/slashMenu.ts
3209
+ // src/tablePageSplit.ts
3210
+ import { definePlugin as definePlugin20 } from "@kedataindo/docflow-core";
3194
3211
  import { Extension as Extension4 } from "@tiptap/core";
3195
3212
  import { Plugin as Plugin2, PluginKey as PluginKey2 } from "@tiptap/pm/state";
3196
- import { definePlugin as definePlugin20 } from "@kedataindo/docflow-core";
3213
+ var PLUGIN_KEY = new PluginKey2("docflow/table-page-split");
3214
+ var SPLIT_MARK = "head";
3215
+ var MAX_SPLIT_DEPTH = 16;
3216
+ function readPageContentHeightPx(view, editor) {
3217
+ const storage = editor?.storage?.PaginationPlus;
3218
+ if (storage && typeof storage.pageHeight === "number") {
3219
+ const h1 = storage.headerHeight?.get?.(1) ?? 0;
3220
+ const f1 = storage.footerHeight?.get?.(1) ?? 0;
3221
+ const contentPx = storage.pageHeight - (storage.marginTop ?? 0) - (storage.marginBottom ?? 0) - (storage.contentMarginTop ?? 0) - (storage.contentMarginBottom ?? 0) - h1 - f1;
3222
+ if (Number.isFinite(contentPx) && contentPx > 0) return contentPx;
3223
+ }
3224
+ const raw = view.dom.style.getPropertyValue("--rm-max-content-child-height");
3225
+ if (raw) {
3226
+ const px = parseFloat(raw);
3227
+ if (Number.isFinite(px) && px > 0) return px;
3228
+ }
3229
+ return null;
3230
+ }
3231
+ function measureElementPx(el) {
3232
+ if (!el || !(el instanceof HTMLElement)) return 0;
3233
+ return el.getBoundingClientRect().height;
3234
+ }
3235
+ function availablePxAt(view, pos, defaultPageContentPx) {
3236
+ try {
3237
+ const tableCoords = view.coordsAtPos(pos + 1);
3238
+ const tableTop = tableCoords.top;
3239
+ const paginationWrapper = view.dom.querySelector("[data-rm-pagination]");
3240
+ if (!paginationWrapper) return defaultPageContentPx;
3241
+ const breakers = paginationWrapper.querySelectorAll(".rm-page-break .breaker");
3242
+ for (let i = 0; i < breakers.length; i++) {
3243
+ const br = breakers[i];
3244
+ if (!(br instanceof HTMLElement)) continue;
3245
+ const breakTop = br.getBoundingClientRect().top;
3246
+ if (breakTop > tableTop) {
3247
+ return Math.max(1, Math.floor(breakTop - tableTop));
3248
+ }
3249
+ }
3250
+ return defaultPageContentPx;
3251
+ } catch {
3252
+ return null;
3253
+ }
3254
+ }
3255
+ function measureRows(tableNode, tableDom) {
3256
+ const expectedRows = tableNode.childCount;
3257
+ const trs = tableDom.querySelectorAll(":scope > tbody > tr");
3258
+ if (trs.length !== expectedRows) return null;
3259
+ const measurements = [];
3260
+ let cumulative = 0;
3261
+ for (let i = 0; i < trs.length; i++) {
3262
+ const h = measureElementPx(trs[i]);
3263
+ cumulative += h;
3264
+ measurements.push({ rowIndex: i, heightPx: h, cumulativePx: cumulative });
3265
+ }
3266
+ return measurements;
3267
+ }
3268
+ function findSplitRow(measurements, pageContentPx) {
3269
+ if (measurements.length < 2) return -1;
3270
+ if (measurements[measurements.length - 1].cumulativePx <= pageContentPx) return -1;
3271
+ let lo = 0;
3272
+ let hi = measurements.length - 2;
3273
+ let best = -1;
3274
+ while (lo <= hi) {
3275
+ const mid = lo + hi >> 1;
3276
+ if (measurements[mid].cumulativePx <= pageContentPx) {
3277
+ best = mid;
3278
+ lo = mid + 1;
3279
+ } else {
3280
+ hi = mid - 1;
3281
+ }
3282
+ }
3283
+ return best;
3284
+ }
3285
+ function rowBoundaryOffset(tableNode, splitRow) {
3286
+ if (splitRow < 0 || tableNode.childCount === 0) return 0;
3287
+ let boundary = 0;
3288
+ tableNode.forEach((row, offset, index) => {
3289
+ if (index <= splitRow) boundary = offset + row.nodeSize;
3290
+ });
3291
+ return boundary;
3292
+ }
3293
+ function tailMeasurements(measurements, splitRow) {
3294
+ const tail = [];
3295
+ let cumulative = 0;
3296
+ for (let i = splitRow + 1; i < measurements.length; i++) {
3297
+ cumulative += measurements[i].heightPx;
3298
+ tail.push({ rowIndex: i, heightPx: measurements[i].heightPx, cumulativePx: cumulative });
3299
+ }
3300
+ return tail;
3301
+ }
3302
+ function buildSplitChain(tableNode, measurements, headBudgetPx, pageContentPx, schema, tableType, depth) {
3303
+ const budget = depth === 0 ? headBudgetPx : pageContentPx;
3304
+ const splitRow = findSplitRow(measurements, budget);
3305
+ if (splitRow < 0) return [tableNode];
3306
+ if (depth >= MAX_SPLIT_DEPTH) return [tableNode];
3307
+ const splitPos = rowBoundaryOffset(tableNode, splitRow);
3308
+ const content = tableNode.content;
3309
+ const leftContent = content.cut(0, splitPos);
3310
+ const rightContent = content.cut(splitPos);
3311
+ const leftAttrs = { ...tableNode.attrs, tablePageSplit: SPLIT_MARK };
3312
+ const rightAttrs = { ...tableNode.attrs, tablePageSplit: null };
3313
+ const leftTable = tableType.create(leftAttrs, leftContent, tableNode.marks);
3314
+ const rightTable = tableType.create(rightAttrs, rightContent, tableNode.marks);
3315
+ const expectedRows = tableNode.childCount;
3316
+ const leftRows = leftTable.childCount;
3317
+ const rightRows = rightTable.childCount;
3318
+ if (leftRows + rightRows !== expectedRows) {
3319
+ throw new Error(
3320
+ `[tablePageSplitPlugin] row-count invariant violated: expected ${expectedRows}, got ${leftRows} + ${rightRows} = ${leftRows + rightRows}`
3321
+ );
3322
+ }
3323
+ const tail = tailMeasurements(measurements, splitRow);
3324
+ const tailChain = buildSplitChain(rightTable, tail, headBudgetPx, pageContentPx, schema, tableType, depth + 1);
3325
+ if (tailChain.length === 1) {
3326
+ return [leftTable, rightTable];
3327
+ }
3328
+ return [leftTable, ...tailChain];
3329
+ }
3330
+ function lastChunkHeightPx(measurements, headBudgetPx, pageContentPx) {
3331
+ let current = measurements;
3332
+ let budget = headBudgetPx;
3333
+ let splitCount = 0;
3334
+ for (let depth = 0; depth < MAX_SPLIT_DEPTH; depth++) {
3335
+ const splitRow = findSplitRow(current, budget);
3336
+ if (splitRow < 0) {
3337
+ return splitCount === 0 ? null : current[current.length - 1]?.cumulativePx ?? 0;
3338
+ }
3339
+ splitCount++;
3340
+ current = tailMeasurements(current, splitRow);
3341
+ budget = pageContentPx;
3342
+ }
3343
+ return current[current.length - 1]?.cumulativePx ?? 0;
3344
+ }
3345
+ function runSplitPass(view, editor) {
3346
+ if (!view || view.isDestroyed) return;
3347
+ const v = view;
3348
+ const state = v.state;
3349
+ const { schema } = state;
3350
+ const tableType = schema.nodes.table;
3351
+ if (!tableType) return;
3352
+ const pageContentPx = readPageContentHeightPx(v, editor);
3353
+ if (pageContentPx == null) return;
3354
+ const tables = [];
3355
+ state.doc.descendants((node, pos) => {
3356
+ if (node.type !== tableType) return true;
3357
+ if (node.attrs.tablePageSplit === SPLIT_MARK) return true;
3358
+ const domAt = v.nodeDOM(pos);
3359
+ if (!(domAt instanceof HTMLElement)) return true;
3360
+ const measurements = measureRows(node, domAt);
3361
+ if (!measurements) return true;
3362
+ tables.push({ pos, node, measurements });
3363
+ return true;
3364
+ });
3365
+ if (tables.length === 0) return;
3366
+ const pending = [];
3367
+ let cursorAvailablePx = null;
3368
+ for (let i = 0; i < tables.length; i++) {
3369
+ const { pos, node, measurements } = tables[i];
3370
+ const availablePx = i === 0 ? availablePxAt(v, pos, pageContentPx) ?? pageContentPx : cursorAvailablePx ?? pageContentPx;
3371
+ const chain = buildSplitChain(node, measurements, availablePx, pageContentPx, schema, tableType, 0);
3372
+ if (chain.length > 1) pending.push({ pos, node, chain });
3373
+ const lastChunkPx = lastChunkHeightPx(measurements, availablePx, pageContentPx);
3374
+ if (lastChunkPx === null) {
3375
+ const totalPx = measurements[measurements.length - 1]?.cumulativePx ?? 0;
3376
+ cursorAvailablePx = Math.max(0, availablePx - totalPx);
3377
+ } else {
3378
+ cursorAvailablePx = Math.max(0, pageContentPx - lastChunkPx);
3379
+ }
3380
+ }
3381
+ if (pending.length === 0) return;
3382
+ const tr = state.tr;
3383
+ pending.sort((a, b) => b.pos - a.pos);
3384
+ for (const { pos, node, chain } of pending) {
3385
+ tr.replaceWith(pos, pos + node.nodeSize, chain);
3386
+ }
3387
+ tr.setMeta(PLUGIN_KEY, { splitAtRow: -1 });
3388
+ v.dispatch(tr);
3389
+ }
3390
+ var TablePageSplitExtension = Extension4.create({
3391
+ name: "tablePageSplit",
3392
+ addProseMirrorPlugins() {
3393
+ let viewRef = null;
3394
+ let pendingRun = false;
3395
+ const ext = this;
3396
+ const editorRef = ext.editor ?? null;
3397
+ return [
3398
+ new Plugin2({
3399
+ key: PLUGIN_KEY,
3400
+ view(_editorView) {
3401
+ viewRef = _editorView;
3402
+ return {
3403
+ destroy() {
3404
+ viewRef = null;
3405
+ }
3406
+ };
3407
+ },
3408
+ appendTransaction(transactions, _oldState, _newState) {
3409
+ if (transactions.some((t) => t.getMeta("PAGE_COUNT_META_KEY") !== void 0)) {
3410
+ return null;
3411
+ }
3412
+ if (transactions.some((t) => {
3413
+ const m = t.getMeta(PLUGIN_KEY);
3414
+ return m !== void 0 && m.splitAtRow !== void 0;
3415
+ })) return null;
3416
+ if (!viewRef || viewRef.isDestroyed) return null;
3417
+ if (pendingRun) return null;
3418
+ pendingRun = true;
3419
+ queueMicrotask(() => {
3420
+ pendingRun = false;
3421
+ if (!viewRef || viewRef.isDestroyed) return;
3422
+ runSplitPass(viewRef, editorRef);
3423
+ });
3424
+ return null;
3425
+ }
3426
+ })
3427
+ ];
3428
+ }
3429
+ });
3430
+ var tablePageSplitPlugin = definePlugin20({
3431
+ id: "tablePageSplit",
3432
+ tiptapExtensions: [TablePageSplitExtension]
3433
+ });
3434
+
3435
+ // src/slashMenu.ts
3436
+ import { Extension as Extension5 } from "@tiptap/core";
3437
+ import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state";
3438
+ import { definePlugin as definePlugin21 } from "@kedataindo/docflow-core";
3197
3439
  var slashState = {
3198
3440
  open: false,
3199
3441
  query: "",
@@ -3247,12 +3489,12 @@ function getRegisteredCommands() {
3247
3489
  return true;
3248
3490
  });
3249
3491
  }
3250
- var SlashMenuExtension = Extension4.create({
3492
+ var SlashMenuExtension = Extension5.create({
3251
3493
  name: "slashMenu",
3252
3494
  addProseMirrorPlugins() {
3253
3495
  return [
3254
- new Plugin2({
3255
- key: new PluginKey2("slashMenu"),
3496
+ new Plugin3({
3497
+ key: new PluginKey3("slashMenu"),
3256
3498
  props: {
3257
3499
  handleTextInput(view, from, _to, text) {
3258
3500
  if (text === "/") {
@@ -3338,7 +3580,7 @@ var SlashMenuExtension = Extension4.create({
3338
3580
  ];
3339
3581
  }
3340
3582
  });
3341
- var slashMenuPlugin = definePlugin20({
3583
+ var slashMenuPlugin = definePlugin21({
3342
3584
  id: "slash-menu",
3343
3585
  tiptapExtensions: [SlashMenuExtension],
3344
3586
  hooks: {
@@ -3358,6 +3600,7 @@ var defaultPlugins = [
3358
3600
  linkPlugin,
3359
3601
  imagePlugin,
3360
3602
  tablePlugin,
3603
+ tablePageSplitPlugin,
3361
3604
  blockquotePlugin,
3362
3605
  codeBlockPlugin,
3363
3606
  placeholderPlugin,
@@ -3427,6 +3670,7 @@ export {
3427
3670
  slashMenuPlugin,
3428
3671
  slashState,
3429
3672
  smartElementsPlugin,
3673
+ tablePageSplitPlugin,
3430
3674
  tablePlugin,
3431
3675
  textColorPlugin,
3432
3676
  tocPlugin
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kedataindo/docflow-plugins",
3
3
  "license": "UNLICENSED",
4
- "version": "0.0.51",
4
+ "version": "0.0.52",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "@tiptap/pm": "^2.11.0",
32
32
  "citeproc": "^2.4.63",
33
33
  "tiptap-markdown": "0.8.10",
34
- "@kedataindo/docflow-core": "0.0.49"
34
+ "@kedataindo/docflow-core": "0.0.50"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@tiptap/core": "^2.11.0",