@lumir-company/editor 0.4.17 → 0.4.19

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.mjs CHANGED
@@ -3412,6 +3412,8 @@ var ALLOWED_VIDEO_EXTENSIONS = [
3412
3412
  ];
3413
3413
  var ROW_RESIZE_MIN_HEIGHT = 24;
3414
3414
  var ROW_RESIZE_HANDLE_WIDTH = 5;
3415
+ var TABLE_SCALE_MIN_COL_WIDTH = 24;
3416
+ var TABLE_SCALE_MAX = 6;
3415
3417
 
3416
3418
  // src/extensions/rowResizing.ts
3417
3419
  var rowResizingPluginKey = new PluginKey3(
@@ -3690,9 +3692,165 @@ function handleDecorations(state, pluginState) {
3690
3692
  return DecorationSet2.create(state.doc, decorations);
3691
3693
  }
3692
3694
 
3693
- // src/extensions/tableCellAttrPreserve.ts
3695
+ // src/extensions/tableScaling.ts
3694
3696
  import { Plugin as Plugin4, PluginKey as PluginKey4 } from "prosemirror-state";
3695
- var tableCellAttrPreserveKey = new PluginKey4(
3697
+ import { Decoration as Decoration3, DecorationSet as DecorationSet3 } from "prosemirror-view";
3698
+ import { TableMap as TableMap2 } from "prosemirror-tables";
3699
+ var tableScalingPluginKey = new PluginKey4(
3700
+ "lumirTableScaling"
3701
+ );
3702
+ function tableScaling() {
3703
+ return new Plugin4({
3704
+ key: tableScalingPluginKey,
3705
+ state: {
3706
+ init: () => null,
3707
+ apply(tr, prev) {
3708
+ const meta = tr.getMeta(tableScalingPluginKey);
3709
+ if (meta !== void 0) return meta.preview;
3710
+ if (prev && tr.docChanged) {
3711
+ return { ...prev, tablePos: tr.mapping.map(prev.tablePos, -1) };
3712
+ }
3713
+ return prev;
3714
+ }
3715
+ },
3716
+ props: {
3717
+ decorations(state) {
3718
+ const p = tableScalingPluginKey.getState(state);
3719
+ return p ? buildHeightDecorations(state, p) : null;
3720
+ }
3721
+ },
3722
+ view: (view) => ({
3723
+ update: () => {
3724
+ const p = tableScalingPluginKey.getState(view.state);
3725
+ if (p) applyColgroupPreview(view, p);
3726
+ }
3727
+ })
3728
+ });
3729
+ }
3730
+ function setTableScalePreview(view, preview) {
3731
+ view.dispatch(view.state.tr.setMeta(tableScalingPluginKey, { preview }));
3732
+ }
3733
+ function measureTableForScale(view, tablePos) {
3734
+ const tableEl = findTableEl(view.nodeDOM(tablePos));
3735
+ const node = view.state.doc.nodeAt(tablePos);
3736
+ if (!tableEl || !node || node.type.name !== "table") return null;
3737
+ const map = TableMap2.get(node);
3738
+ const rect = tableEl.getBoundingClientRect();
3739
+ const body = tableEl.tBodies[0];
3740
+ const rowHeights = body ? Array.from(body.rows).map((tr) => tr.getBoundingClientRect().height) : [];
3741
+ const colWidths = measureColWidths(tableEl, map.width);
3742
+ if (rowHeights.length !== map.height || colWidths.length !== map.width) {
3743
+ return null;
3744
+ }
3745
+ return {
3746
+ tablePos,
3747
+ colWidths,
3748
+ rowHeights,
3749
+ origW: rect.width,
3750
+ origH: rect.height,
3751
+ scale: 1
3752
+ };
3753
+ }
3754
+ function commitTableScale(view, preview) {
3755
+ const { state } = view;
3756
+ const { tablePos, colWidths, rowHeights, scale } = preview;
3757
+ const table = state.doc.nodeAt(tablePos);
3758
+ if (!table || table.type.name !== "table") return;
3759
+ const map = TableMap2.get(table);
3760
+ const start = tablePos + 1;
3761
+ const tr = state.tr;
3762
+ const seen = /* @__PURE__ */ new Set();
3763
+ for (const relPos of map.map) {
3764
+ if (seen.has(relPos)) continue;
3765
+ seen.add(relPos);
3766
+ const node = table.nodeAt(relPos);
3767
+ if (!node) continue;
3768
+ const rect = map.findCell(relPos);
3769
+ const colwidth = [];
3770
+ for (let c = rect.left; c < rect.right; c++) {
3771
+ colwidth.push(Math.round((colWidths[c] ?? 0) * scale));
3772
+ }
3773
+ let h = 0;
3774
+ for (let r = rect.top; r < rect.bottom; r++) h += rowHeights[r] ?? 0;
3775
+ const rowHeight = Math.round(h * scale);
3776
+ tr.setNodeMarkup(start + relPos, void 0, {
3777
+ ...node.attrs,
3778
+ colwidth: colwidth.some((w) => w > 0) ? colwidth : null,
3779
+ rowHeight: rowHeight > 0 ? rowHeight : null
3780
+ });
3781
+ }
3782
+ if (tr.docChanged) view.dispatch(tr);
3783
+ }
3784
+ function measureColWidths(tableEl, width) {
3785
+ const widths = new Array(width).fill(0);
3786
+ const colgroup = tableEl.querySelector("colgroup");
3787
+ if (colgroup && colgroup.children.length === width) {
3788
+ let allSet = true;
3789
+ for (let i = 0; i < width; i++) {
3790
+ const w = parseFloat(colgroup.children[i].style.width);
3791
+ if (Number.isFinite(w) && w > 0) widths[i] = w;
3792
+ else allSet = false;
3793
+ }
3794
+ if (allSet) return widths;
3795
+ }
3796
+ const firstRow = tableEl.tBodies[0]?.rows[0];
3797
+ if (firstRow) {
3798
+ let col = 0;
3799
+ for (const cell of Array.from(firstRow.cells)) {
3800
+ const span = cell.colSpan || 1;
3801
+ const w = cell.getBoundingClientRect().width / span;
3802
+ for (let s = 0; s < span && col < width; s++) widths[col++] = w;
3803
+ }
3804
+ }
3805
+ return widths;
3806
+ }
3807
+ function applyColgroupPreview(view, p) {
3808
+ const tableEl = findTableEl(view.nodeDOM(p.tablePos));
3809
+ const colgroup = tableEl?.querySelector("colgroup");
3810
+ if (!colgroup) return;
3811
+ const cols = colgroup.children;
3812
+ for (let i = 0; i < cols.length && i < p.colWidths.length; i++) {
3813
+ cols[i].style.width = Math.round(p.colWidths[i] * p.scale) + "px";
3814
+ }
3815
+ }
3816
+ function buildHeightDecorations(state, p) {
3817
+ const table = state.doc.nodeAt(p.tablePos);
3818
+ if (!table || table.type.name !== "table") return DecorationSet3.empty;
3819
+ const map = TableMap2.get(table);
3820
+ const start = p.tablePos + 1;
3821
+ const decorations = [];
3822
+ const seen = /* @__PURE__ */ new Set();
3823
+ for (const relPos of map.map) {
3824
+ if (seen.has(relPos)) continue;
3825
+ seen.add(relPos);
3826
+ const node = table.nodeAt(relPos);
3827
+ if (!node) continue;
3828
+ const rect = map.findCell(relPos);
3829
+ let h = 0;
3830
+ for (let r = rect.top; r < rect.bottom; r++) h += p.rowHeights[r] ?? 0;
3831
+ const from = start + relPos;
3832
+ const to = from + node.nodeSize;
3833
+ decorations.push(
3834
+ Decoration3.node(from, to, {
3835
+ class: "lumir-table-scale-dragging",
3836
+ style: `height: ${Math.round(h * p.scale)}px`
3837
+ })
3838
+ );
3839
+ }
3840
+ return DecorationSet3.create(state.doc, decorations);
3841
+ }
3842
+ function findTableEl(dom) {
3843
+ if (!dom) return null;
3844
+ const el = dom;
3845
+ if (el.nodeName === "TABLE") return el;
3846
+ const inner = el.querySelector?.("table");
3847
+ if (inner) return inner;
3848
+ return el.closest?.("table") ?? null;
3849
+ }
3850
+
3851
+ // src/extensions/tableCellAttrPreserve.ts
3852
+ import { Plugin as Plugin5, PluginKey as PluginKey5 } from "prosemirror-state";
3853
+ var tableCellAttrPreserveKey = new PluginKey5(
3696
3854
  "lumirTableCellAttrPreserve"
3697
3855
  );
3698
3856
  var PRESERVED_ATTRS = [
@@ -3749,7 +3907,7 @@ function collectCellAttrs(doc) {
3749
3907
  return result;
3750
3908
  }
3751
3909
  function tableCellAttrPreserve() {
3752
- return new Plugin4({
3910
+ return new Plugin5({
3753
3911
  key: tableCellAttrPreserveKey,
3754
3912
  appendTransaction(transactions, oldState, newState) {
3755
3913
  if (!transactions.some((tr2) => tr2.docChanged)) {
@@ -3855,6 +4013,7 @@ var RowHeightExtension = Extension2.create({
3855
4013
  const plugins = [tableCellAttrPreserve()];
3856
4014
  if (this.options.resizable) {
3857
4015
  plugins.push(rowResizing());
4016
+ plugins.push(tableScaling());
3858
4017
  }
3859
4018
  return plugins;
3860
4019
  }
@@ -3862,11 +4021,11 @@ var RowHeightExtension = Extension2.create({
3862
4021
 
3863
4022
  // src/extensions/TableAlignmentExtension.ts
3864
4023
  import { Extension as Extension3 } from "@tiptap/core";
3865
- import { Plugin as Plugin5, PluginKey as PluginKey5 } from "prosemirror-state";
3866
- import { Decoration as Decoration3, DecorationSet as DecorationSet3 } from "prosemirror-view";
3867
- var tableAlignmentDecoKey = new PluginKey5("lumirTableAlignmentDeco");
4024
+ import { Plugin as Plugin6, PluginKey as PluginKey6 } from "prosemirror-state";
4025
+ import { Decoration as Decoration4, DecorationSet as DecorationSet4 } from "prosemirror-view";
4026
+ var tableAlignmentDecoKey = new PluginKey6("lumirTableAlignmentDeco");
3868
4027
  function tableAlignmentDecorationPlugin() {
3869
- return new Plugin5({
4028
+ return new Plugin6({
3870
4029
  key: tableAlignmentDecoKey,
3871
4030
  props: {
3872
4031
  decorations(state) {
@@ -3876,7 +4035,7 @@ function tableAlignmentDecorationPlugin() {
3876
4035
  const align = node.attrs.tableAlignment;
3877
4036
  if (align && align !== "left") {
3878
4037
  decorations.push(
3879
- Decoration3.node(pos, pos + node.nodeSize, {
4038
+ Decoration4.node(pos, pos + node.nodeSize, {
3880
4039
  "data-table-alignment": align
3881
4040
  })
3882
4041
  );
@@ -3885,7 +4044,7 @@ function tableAlignmentDecorationPlugin() {
3885
4044
  }
3886
4045
  return void 0;
3887
4046
  });
3888
- return DecorationSet3.create(state.doc, decorations);
4047
+ return DecorationSet4.create(state.doc, decorations);
3889
4048
  }
3890
4049
  }
3891
4050
  });
@@ -4830,9 +4989,14 @@ import { useEffect as useEffect9, useMemo as useMemo6 } from "react";
4830
4989
  function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
4831
4990
  const { refs, floatingStyles, context, update } = useFloating({
4832
4991
  open: show,
4833
- placement: orientation === "row" ? "left" : orientation === "col" ? "top" : "right",
4834
- // col/row: 가장자리 선(zero-size)에, cell: 우측 보더에 14px hit-area 중앙 정렬(-7).
4835
- middleware: [offset(-7)],
4992
+ placement: orientation === "row" ? "left" : orientation === "col" ? "top" : orientation === "corner" ? "bottom-start" : "right",
4993
+ // col/row/cell: 가장자리에 14px hit-area 중앙 정렬(-7).
4994
+ // corner: 18px hit-zone이 표 우하단 모서리에 걸치도록 위/좌로 살짝 당김(모서리 hover 자연).
4995
+ middleware: [
4996
+ offset(
4997
+ orientation === "corner" ? { mainAxis: -6, crossAxis: -6 } : -7
4998
+ )
4999
+ ],
4836
5000
  whileElementsMounted: autoUpdate
4837
5001
  });
4838
5002
  const { isMounted, styles } = useTransitionStyles(context);
@@ -4860,6 +5024,9 @@ function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
4860
5024
  if (orientation === "row") {
4861
5025
  return new DOMRect(t.left, c.top, 0, c.height);
4862
5026
  }
5027
+ if (orientation === "corner") {
5028
+ return new DOMRect(t.right, t.bottom, 0, 0);
5029
+ }
4863
5030
  return c;
4864
5031
  }
4865
5032
  });
@@ -4877,6 +5044,33 @@ function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
4877
5044
  [floatingStyles, isMounted, refs.setFloating, styles]
4878
5045
  );
4879
5046
  }
5047
+ function useTableCornerPositioning(referencePosTable, show) {
5048
+ const { refs, floatingStyles, context } = useFloating({
5049
+ open: show,
5050
+ placement: "bottom-start",
5051
+ // 18px hit-zone을 모서리에서 안쪽(위/좌)으로 당겨 표 위에 걸치게 한다.
5052
+ middleware: [offset({ mainAxis: -12, crossAxis: -12 })],
5053
+ whileElementsMounted: autoUpdate
5054
+ });
5055
+ const { isMounted, styles } = useTransitionStyles(context);
5056
+ useEffect9(() => {
5057
+ if (!referencePosTable) {
5058
+ refs.setReference(null);
5059
+ return;
5060
+ }
5061
+ refs.setReference({
5062
+ getBoundingClientRect: () => new DOMRect(referencePosTable.right, referencePosTable.bottom, 0, 0)
5063
+ });
5064
+ }, [referencePosTable, refs]);
5065
+ return useMemo6(
5066
+ () => ({
5067
+ isMounted,
5068
+ ref: refs.setFloating,
5069
+ style: { ...styles, ...floatingStyles }
5070
+ }),
5071
+ [floatingStyles, isMounted, refs.setFloating, styles]
5072
+ );
5073
+ }
4880
5074
 
4881
5075
  // src/components/LumirTableHandlesController.tsx
4882
5076
  import { Fragment as Fragment7, jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
@@ -4901,6 +5095,9 @@ function LumirTableHandlesController() {
4901
5095
  const frozenRef = useRef11(false);
4902
5096
  const menuOpenRef = useRef11(false);
4903
5097
  const draggingRef = useRef11(false);
5098
+ useEffect10(() => {
5099
+ editor.__lumirActiveTableId = focused?.block?.id ?? null;
5100
+ }, [editor, focused]);
4904
5101
  const recompute = useCallback20(() => {
4905
5102
  if (frozenRef.current) {
4906
5103
  return;
@@ -5024,6 +5221,10 @@ function LumirTableHandlesController() {
5024
5221
  const onEndExtend = useCallback20(() => {
5025
5222
  editor.tableHandles?.unfreezeHandles();
5026
5223
  }, [editor]);
5224
+ const tableCorner = useTableCornerPositioning(
5225
+ coreState?.referencePosTable ?? null,
5226
+ !!coreState?.widgetContainer
5227
+ );
5027
5228
  const menuHandlers = useMemo7(() => {
5028
5229
  const mk = (kind) => ({
5029
5230
  freeze: () => {
@@ -5073,6 +5274,48 @@ function LumirTableHandlesController() {
5073
5274
  }, [editor, recompute]);
5074
5275
  const noop = useCallback20(() => {
5075
5276
  }, []);
5277
+ const onScaleStart = useCallback20(
5278
+ (e) => {
5279
+ e.preventDefault();
5280
+ e.stopPropagation();
5281
+ const view = editor.prosemirrorView;
5282
+ const blockId = coreState?.block?.id;
5283
+ if (!view || !blockId) return;
5284
+ const tablePos = findTableNodePos(editor._tiptapEditor, blockId);
5285
+ if (tablePos < 0) return;
5286
+ const base = measureTableForScale(view, tablePos);
5287
+ if (!base) return;
5288
+ const minScale = Math.max(
5289
+ TABLE_SCALE_MIN_COL_WIDTH / Math.max(1, Math.min(...base.colWidths)),
5290
+ ROW_RESIZE_MIN_HEIGHT / Math.max(1, Math.min(...base.rowHeights))
5291
+ );
5292
+ const startX = e.clientX;
5293
+ const startY = e.clientY;
5294
+ let current = base;
5295
+ editor.tableHandles?.freezeHandles();
5296
+ const onMove = (me) => {
5297
+ if (me.buttons === 0) return onUp();
5298
+ const newW = base.origW + (me.clientX - startX);
5299
+ const newH = base.origH + (me.clientY - startY);
5300
+ const scale = Math.min(
5301
+ TABLE_SCALE_MAX,
5302
+ Math.max(minScale, Math.max(newW / base.origW, newH / base.origH))
5303
+ );
5304
+ current = { ...base, scale };
5305
+ setTableScalePreview(view, current);
5306
+ };
5307
+ const onUp = () => {
5308
+ window.removeEventListener("pointermove", onMove);
5309
+ window.removeEventListener("pointerup", onUp);
5310
+ setTableScalePreview(view, null);
5311
+ commitTableScale(view, current);
5312
+ editor.tableHandles?.unfreezeHandles();
5313
+ };
5314
+ window.addEventListener("pointermove", onMove);
5315
+ window.addEventListener("pointerup", onUp);
5316
+ },
5317
+ [editor, coreState]
5318
+ );
5076
5319
  return /* @__PURE__ */ jsxs18(Fragment7, { children: [
5077
5320
  /* @__PURE__ */ jsx26("div", { ref: setMenuContainerRef }),
5078
5321
  th && focused && menuContainerRef && /* @__PURE__ */ jsxs18(FloatingPortal, { root: focused.widgetContainer, children: [
@@ -5188,6 +5431,16 @@ function LumirTableHandlesController() {
5188
5431
  }
5189
5432
  )
5190
5433
  }
5434
+ ),
5435
+ tableCorner.isMounted && /* @__PURE__ */ jsx26(
5436
+ "div",
5437
+ {
5438
+ ref: tableCorner.ref,
5439
+ style: tableCorner.style,
5440
+ className: "lumir-tbl-scale-handle",
5441
+ title: "\uD45C \uD06C\uAE30 \uC870\uC808 (\uC885\uD6A1\uBE44 \uACE0\uC815)",
5442
+ onPointerDown: onScaleStart
5443
+ }
5191
5444
  )
5192
5445
  ] })
5193
5446
  ] });
@@ -5443,6 +5696,140 @@ function liftFontSize(blocks) {
5443
5696
  return mapPreservingRef(blocks, (b) => mapBlock(b, "lift"));
5444
5697
  }
5445
5698
 
5699
+ // src/utils/table-delete.ts
5700
+ import { CellSelection, TableMap as TableMap3, deleteRow } from "prosemirror-tables";
5701
+ function measureRowHeights(view, tablePos) {
5702
+ try {
5703
+ const at = view?.domAtPos?.(tablePos + 1);
5704
+ let el = at?.node;
5705
+ if (el && el.nodeType === 3) el = el.parentElement;
5706
+ const tableEl = el?.closest?.("table") ?? null;
5707
+ const body = tableEl?.tBodies?.[0];
5708
+ if (!body) return null;
5709
+ return Array.from(body.rows).map(
5710
+ (tr) => Math.round(tr.getBoundingClientRect().height)
5711
+ );
5712
+ } catch {
5713
+ return null;
5714
+ }
5715
+ }
5716
+ function buildDeleteColumnTr(state, tablePos, col, rowPx) {
5717
+ const table = state.doc.nodeAt(tablePos);
5718
+ if (!table || table.type.name !== "table") return null;
5719
+ const map = TableMap3.get(table);
5720
+ const W = map.width;
5721
+ const H = map.height;
5722
+ if (col < 0 || col >= W || W <= 1) return null;
5723
+ const cells = [];
5724
+ const seen = /* @__PURE__ */ new Set();
5725
+ for (let r = 0; r < H; r++) {
5726
+ for (let c = 0; c < W; c++) {
5727
+ const pos = map.map[r * W + c];
5728
+ if (seen.has(pos)) continue;
5729
+ seen.add(pos);
5730
+ const node = table.nodeAt(pos);
5731
+ if (!node) continue;
5732
+ cells.push({
5733
+ node,
5734
+ top: r,
5735
+ left: c,
5736
+ rowspan: node.attrs.rowspan ?? 1,
5737
+ colspan: node.attrs.colspan ?? 1
5738
+ });
5739
+ }
5740
+ }
5741
+ const spansCol = (c) => c.left <= col && col < c.left + c.colspan;
5742
+ const isDropped = (c) => spansCol(c) && c.colspan === 1;
5743
+ const survivingByRow = new Array(H).fill(0);
5744
+ for (const c of cells) if (!isDropped(c)) survivingByRow[c.top]++;
5745
+ const rowDies = survivingByRow.map((n) => n === 0);
5746
+ const newRowIndex = [];
5747
+ let ni = 0;
5748
+ for (let r = 0; r < H; r++) newRowIndex[r] = rowDies[r] ? -1 : ni++;
5749
+ const newH = ni;
5750
+ if (newH === 0) return null;
5751
+ const newRows = Array.from({ length: newH }, () => []);
5752
+ for (const c of cells) {
5753
+ if (isDropped(c)) continue;
5754
+ const newColspan = c.colspan - (spansCol(c) ? 1 : 0);
5755
+ let dyingWithin = 0;
5756
+ for (let r = c.top; r < c.top + c.rowspan; r++) if (rowDies[r]) dyingWithin++;
5757
+ const newRowspan = Math.max(1, c.rowspan - dyingWithin);
5758
+ const attrs = { ...c.node.attrs, colspan: newColspan, rowspan: newRowspan };
5759
+ if (spansCol(c) && Array.isArray(attrs.colwidth) && attrs.colwidth.length) {
5760
+ const cw = attrs.colwidth.slice();
5761
+ cw.splice(col - c.left, 1);
5762
+ attrs.colwidth = cw.some((w) => w > 0) ? cw : null;
5763
+ }
5764
+ if (dyingWithin > 0 && rowPx) {
5765
+ let sum = 0;
5766
+ let ok = true;
5767
+ for (let r = c.top; r < c.top + c.rowspan; r++) {
5768
+ if (typeof rowPx[r] !== "number") {
5769
+ ok = false;
5770
+ break;
5771
+ }
5772
+ sum += rowPx[r];
5773
+ }
5774
+ if (ok && sum > 0) attrs.rowHeight = Math.round(sum);
5775
+ }
5776
+ const target = newRowIndex[c.top];
5777
+ if (target >= 0) newRows[target].push(c.node.type.create(attrs, c.node.content));
5778
+ }
5779
+ const rowType = table.child(0)?.type;
5780
+ if (!rowType) return null;
5781
+ const rowNodes = newRows.map((rowCells) => rowType.create(null, rowCells));
5782
+ const newTable = table.type.create(table.attrs, rowNodes);
5783
+ const tr = state.tr;
5784
+ tr.replaceWith(tablePos, tablePos + table.nodeSize, newTable);
5785
+ return tr.docChanged ? tr : null;
5786
+ }
5787
+ function removeFocusedRowOrColumn(editor, index, direction) {
5788
+ const tiptap = editor?._tiptapEditor;
5789
+ if (!tiptap) return false;
5790
+ const { state } = tiptap;
5791
+ let tablePos = -1;
5792
+ const activeId = editor?.__lumirActiveTableId;
5793
+ if (activeId) {
5794
+ const p = findTableNodePos(tiptap, activeId);
5795
+ if (p >= 0 && state.doc.nodeAt(p)?.type.name === "table") tablePos = p;
5796
+ }
5797
+ if (tablePos < 0) {
5798
+ const $from = state.selection.$from;
5799
+ for (let d = $from.depth; d > 0; d--) {
5800
+ if ($from.node(d).type.name === "table") {
5801
+ tablePos = $from.before(d);
5802
+ break;
5803
+ }
5804
+ }
5805
+ }
5806
+ if (tablePos < 0) {
5807
+ const vp = editor?.tableHandles?.view?.tablePos;
5808
+ if (typeof vp === "number" && state.doc.nodeAt(vp)?.type.name === "table") {
5809
+ tablePos = vp;
5810
+ }
5811
+ }
5812
+ if (tablePos < 0) return false;
5813
+ try {
5814
+ if (direction === "column") {
5815
+ const rowPx = measureRowHeights(tiptap.view, tablePos);
5816
+ const tr = buildDeleteColumnTr(state, tablePos, index, rowPx);
5817
+ if (!tr) return false;
5818
+ tiptap.view.dispatch(tr);
5819
+ return true;
5820
+ }
5821
+ const tableInside = state.doc.resolve(tablePos + 1);
5822
+ const rowStart = state.doc.resolve(tableInside.posAtIndex(index) + 1);
5823
+ const cellPos = state.doc.resolve(rowStart.posAtIndex(0));
5824
+ const selState = state.apply(
5825
+ state.tr.setSelection(new CellSelection(cellPos))
5826
+ );
5827
+ return deleteRow(selState, (tr) => tiptap.view.dispatch(tr));
5828
+ } catch {
5829
+ return false;
5830
+ }
5831
+ }
5832
+
5446
5833
  // src/utils/excel-paste.ts
5447
5834
  var NAMED_COLORS = {
5448
5835
  black: "#000000",
@@ -5984,6 +6371,7 @@ function LumirEditor({
5984
6371
  className = "",
5985
6372
  placeholder,
5986
6373
  sideMenuAddButton = false,
6374
+ columnDivider = false,
5987
6375
  floatingMenu = false,
5988
6376
  floatingMenuPosition = "sticky",
5989
6377
  // callbacks / refs
@@ -6296,6 +6684,18 @@ function LumirEditor({
6296
6684
  editor.isEditable = editable;
6297
6685
  }
6298
6686
  }, [editor, editable]);
6687
+ useEffect11(() => {
6688
+ if (!editor) return;
6689
+ const th = editor.tableHandles;
6690
+ if (!th || th.__lumirColDelPatched || typeof th.removeRowOrColumn !== "function")
6691
+ return;
6692
+ const orig = th.removeRowOrColumn.bind(th);
6693
+ th.removeRowOrColumn = (index, dir) => {
6694
+ if (removeFocusedRowOrColumn(editor, index, dir)) return;
6695
+ return orig(index, dir);
6696
+ };
6697
+ th.__lumirColDelPatched = true;
6698
+ }, [editor]);
6299
6699
  useEffect11(() => {
6300
6700
  if (!editor || !floatingMenu) return;
6301
6701
  const ft = editor.formattingToolbar;
@@ -6486,7 +6886,11 @@ function LumirEditor({
6486
6886
  return /* @__PURE__ */ jsxs19(
6487
6887
  "div",
6488
6888
  {
6489
- className: cn("lumirEditor", className),
6889
+ className: cn(
6890
+ "lumirEditor",
6891
+ columnDivider && "lumir-column-divider",
6892
+ className
6893
+ ),
6490
6894
  style: { position: "relative", display: "flex", flexDirection: "column" },
6491
6895
  children: [
6492
6896
  floatingMenu && editor && /* @__PURE__ */ jsxs19(Fragment8, { children: [