@lumir-company/editor 0.4.18 → 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/README.md CHANGED
@@ -1261,6 +1261,7 @@ interface LumirEditorProps {
1261
1261
  tableHandles?: boolean; // 테이블 핸들 표시 (기본: true)
1262
1262
  floatingMenu?: boolean; // 상단 고정 플로팅 메뉴 표시 (기본: false)
1263
1263
  floatingMenuPosition?: "sticky" | "fixed"; // 플로팅 메뉴 위치 (기본: "sticky")
1264
+ columnDivider?: boolean; // 2단(다단) 컬럼 사이 중앙 세로 구분선 표시 (기본: false)
1264
1265
  className?: string; // 컨테이너 CSS 클래스
1265
1266
 
1266
1267
  // === 링크 프리뷰 설정 ===
@@ -1478,6 +1479,18 @@ const url = await uploader(imageFile);
1478
1479
 
1479
1480
  ## 변경 로그
1480
1481
 
1482
+ ### v0.4.19
1483
+
1484
+ - **2단 컬럼 중앙 세로 구분선 (`columnDivider` 옵션)** *(신규)*
1485
+ - `columnDivider` prop(기본 `false`)으로 2단(다단) 컬럼 사이 중앙에 세로 구분선 표시
1486
+ - 구분선 양쪽에 드래그 핸들(grip) 너비만큼 여백을 둬 핸들과 겹치지 않음
1487
+ - 색·여백을 CSS 변수로 조절: `--lumir-column-divider-color`(기본 `#e5e7eb`), `--lumir-column-grip-space`(기본 28px)
1488
+ - **표 전체 종횡비 고정 스케일 (코너 드래그)** *(신규)*
1489
+ - 표 우하단 모서리 hover 시 대각 리사이즈 커서 → 드래그로 표 전체를 종횡비 고정 균일 배율로 확대/축소
1490
+ - 모든 열 너비(`colwidth`)·행 높이(`rowHeight`)에 동일 배율 적용 → 행·열 상대 비율과 표 종횡비 유지
1491
+ - 셀 focus 없이 표 hover만으로 동작(코어 hover 상태 기반), 기존 행/열 개별 리사이즈와 충돌 없음
1492
+ - `tableHandles` prop으로 게이트(행/열 리사이즈와 동일)
1493
+
1481
1494
  ### v0.4.18
1482
1495
 
1483
1496
  - **표 열 삭제 버그 수정 (병합 셀 포함)**
package/dist/index.d.mts CHANGED
@@ -119,6 +119,7 @@ interface LumirEditorProps {
119
119
  onSelectionChange?: () => void;
120
120
  className?: string;
121
121
  sideMenuAddButton?: boolean;
122
+ columnDivider?: boolean;
122
123
  floatingMenu?: boolean;
123
124
  floatingMenuPosition?: "sticky" | "fixed";
124
125
  linkPreview?: {
@@ -255,7 +256,7 @@ declare class EditorConfig {
255
256
  */
256
257
  static getDisabledExtensions(userExtensions?: string[], allowVideo?: boolean, allowAudio?: boolean, allowFile?: boolean): string[];
257
258
  }
258
- declare function LumirEditor({ initialContent, initialEmptyBlocks, uploadFile, s3Upload, tables, heading, defaultStyles, disableExtensions, tabBehavior, trailingBlock, allowVideoUpload, allowAudioUpload, allowFileUpload, maxImageFileSize, maxVideoFileSize, linkPreview, editable, theme, formattingToolbar, linkToolbar, sideMenu, emojiPicker, filePanel, tableHandles, onSelectionChange, className, placeholder, sideMenuAddButton, floatingMenu, floatingMenuPosition, onContentChange, onError, onImageDelete, }: LumirEditorProps): react_jsx_runtime.JSX.Element;
259
+ declare function LumirEditor({ initialContent, initialEmptyBlocks, uploadFile, s3Upload, tables, heading, defaultStyles, disableExtensions, tabBehavior, trailingBlock, allowVideoUpload, allowAudioUpload, allowFileUpload, maxImageFileSize, maxVideoFileSize, linkPreview, editable, theme, formattingToolbar, linkToolbar, sideMenu, emojiPicker, filePanel, tableHandles, onSelectionChange, className, placeholder, sideMenuAddButton, columnDivider, floatingMenu, floatingMenuPosition, onContentChange, onError, onImageDelete, }: LumirEditorProps): react_jsx_runtime.JSX.Element;
259
260
 
260
261
  declare function cn(...inputs: (string | undefined | null | false)[]): string;
261
262
 
package/dist/index.d.ts CHANGED
@@ -119,6 +119,7 @@ interface LumirEditorProps {
119
119
  onSelectionChange?: () => void;
120
120
  className?: string;
121
121
  sideMenuAddButton?: boolean;
122
+ columnDivider?: boolean;
122
123
  floatingMenu?: boolean;
123
124
  floatingMenuPosition?: "sticky" | "fixed";
124
125
  linkPreview?: {
@@ -255,7 +256,7 @@ declare class EditorConfig {
255
256
  */
256
257
  static getDisabledExtensions(userExtensions?: string[], allowVideo?: boolean, allowAudio?: boolean, allowFile?: boolean): string[];
257
258
  }
258
- declare function LumirEditor({ initialContent, initialEmptyBlocks, uploadFile, s3Upload, tables, heading, defaultStyles, disableExtensions, tabBehavior, trailingBlock, allowVideoUpload, allowAudioUpload, allowFileUpload, maxImageFileSize, maxVideoFileSize, linkPreview, editable, theme, formattingToolbar, linkToolbar, sideMenu, emojiPicker, filePanel, tableHandles, onSelectionChange, className, placeholder, sideMenuAddButton, floatingMenu, floatingMenuPosition, onContentChange, onError, onImageDelete, }: LumirEditorProps): react_jsx_runtime.JSX.Element;
259
+ declare function LumirEditor({ initialContent, initialEmptyBlocks, uploadFile, s3Upload, tables, heading, defaultStyles, disableExtensions, tabBehavior, trailingBlock, allowVideoUpload, allowAudioUpload, allowFileUpload, maxImageFileSize, maxVideoFileSize, linkPreview, editable, theme, formattingToolbar, linkToolbar, sideMenu, emojiPicker, filePanel, tableHandles, onSelectionChange, className, placeholder, sideMenuAddButton, columnDivider, floatingMenu, floatingMenuPosition, onContentChange, onError, onImageDelete, }: LumirEditorProps): react_jsx_runtime.JSX.Element;
259
260
 
260
261
  declare function cn(...inputs: (string | undefined | null | false)[]): string;
261
262
 
package/dist/index.js CHANGED
@@ -3436,6 +3436,8 @@ var ALLOWED_VIDEO_EXTENSIONS = [
3436
3436
  ];
3437
3437
  var ROW_RESIZE_MIN_HEIGHT = 24;
3438
3438
  var ROW_RESIZE_HANDLE_WIDTH = 5;
3439
+ var TABLE_SCALE_MIN_COL_WIDTH = 24;
3440
+ var TABLE_SCALE_MAX = 6;
3439
3441
 
3440
3442
  // src/extensions/rowResizing.ts
3441
3443
  var rowResizingPluginKey = new import_prosemirror_state3.PluginKey(
@@ -3714,9 +3716,165 @@ function handleDecorations(state, pluginState) {
3714
3716
  return import_prosemirror_view2.DecorationSet.create(state.doc, decorations);
3715
3717
  }
3716
3718
 
3717
- // src/extensions/tableCellAttrPreserve.ts
3719
+ // src/extensions/tableScaling.ts
3718
3720
  var import_prosemirror_state4 = require("prosemirror-state");
3719
- var tableCellAttrPreserveKey = new import_prosemirror_state4.PluginKey(
3721
+ var import_prosemirror_view3 = require("prosemirror-view");
3722
+ var import_prosemirror_tables2 = require("prosemirror-tables");
3723
+ var tableScalingPluginKey = new import_prosemirror_state4.PluginKey(
3724
+ "lumirTableScaling"
3725
+ );
3726
+ function tableScaling() {
3727
+ return new import_prosemirror_state4.Plugin({
3728
+ key: tableScalingPluginKey,
3729
+ state: {
3730
+ init: () => null,
3731
+ apply(tr, prev) {
3732
+ const meta = tr.getMeta(tableScalingPluginKey);
3733
+ if (meta !== void 0) return meta.preview;
3734
+ if (prev && tr.docChanged) {
3735
+ return { ...prev, tablePos: tr.mapping.map(prev.tablePos, -1) };
3736
+ }
3737
+ return prev;
3738
+ }
3739
+ },
3740
+ props: {
3741
+ decorations(state) {
3742
+ const p = tableScalingPluginKey.getState(state);
3743
+ return p ? buildHeightDecorations(state, p) : null;
3744
+ }
3745
+ },
3746
+ view: (view) => ({
3747
+ update: () => {
3748
+ const p = tableScalingPluginKey.getState(view.state);
3749
+ if (p) applyColgroupPreview(view, p);
3750
+ }
3751
+ })
3752
+ });
3753
+ }
3754
+ function setTableScalePreview(view, preview) {
3755
+ view.dispatch(view.state.tr.setMeta(tableScalingPluginKey, { preview }));
3756
+ }
3757
+ function measureTableForScale(view, tablePos) {
3758
+ const tableEl = findTableEl(view.nodeDOM(tablePos));
3759
+ const node = view.state.doc.nodeAt(tablePos);
3760
+ if (!tableEl || !node || node.type.name !== "table") return null;
3761
+ const map = import_prosemirror_tables2.TableMap.get(node);
3762
+ const rect = tableEl.getBoundingClientRect();
3763
+ const body = tableEl.tBodies[0];
3764
+ const rowHeights = body ? Array.from(body.rows).map((tr) => tr.getBoundingClientRect().height) : [];
3765
+ const colWidths = measureColWidths(tableEl, map.width);
3766
+ if (rowHeights.length !== map.height || colWidths.length !== map.width) {
3767
+ return null;
3768
+ }
3769
+ return {
3770
+ tablePos,
3771
+ colWidths,
3772
+ rowHeights,
3773
+ origW: rect.width,
3774
+ origH: rect.height,
3775
+ scale: 1
3776
+ };
3777
+ }
3778
+ function commitTableScale(view, preview) {
3779
+ const { state } = view;
3780
+ const { tablePos, colWidths, rowHeights, scale } = preview;
3781
+ const table = state.doc.nodeAt(tablePos);
3782
+ if (!table || table.type.name !== "table") return;
3783
+ const map = import_prosemirror_tables2.TableMap.get(table);
3784
+ const start = tablePos + 1;
3785
+ const tr = state.tr;
3786
+ const seen = /* @__PURE__ */ new Set();
3787
+ for (const relPos of map.map) {
3788
+ if (seen.has(relPos)) continue;
3789
+ seen.add(relPos);
3790
+ const node = table.nodeAt(relPos);
3791
+ if (!node) continue;
3792
+ const rect = map.findCell(relPos);
3793
+ const colwidth = [];
3794
+ for (let c = rect.left; c < rect.right; c++) {
3795
+ colwidth.push(Math.round((colWidths[c] ?? 0) * scale));
3796
+ }
3797
+ let h = 0;
3798
+ for (let r = rect.top; r < rect.bottom; r++) h += rowHeights[r] ?? 0;
3799
+ const rowHeight = Math.round(h * scale);
3800
+ tr.setNodeMarkup(start + relPos, void 0, {
3801
+ ...node.attrs,
3802
+ colwidth: colwidth.some((w) => w > 0) ? colwidth : null,
3803
+ rowHeight: rowHeight > 0 ? rowHeight : null
3804
+ });
3805
+ }
3806
+ if (tr.docChanged) view.dispatch(tr);
3807
+ }
3808
+ function measureColWidths(tableEl, width) {
3809
+ const widths = new Array(width).fill(0);
3810
+ const colgroup = tableEl.querySelector("colgroup");
3811
+ if (colgroup && colgroup.children.length === width) {
3812
+ let allSet = true;
3813
+ for (let i = 0; i < width; i++) {
3814
+ const w = parseFloat(colgroup.children[i].style.width);
3815
+ if (Number.isFinite(w) && w > 0) widths[i] = w;
3816
+ else allSet = false;
3817
+ }
3818
+ if (allSet) return widths;
3819
+ }
3820
+ const firstRow = tableEl.tBodies[0]?.rows[0];
3821
+ if (firstRow) {
3822
+ let col = 0;
3823
+ for (const cell of Array.from(firstRow.cells)) {
3824
+ const span = cell.colSpan || 1;
3825
+ const w = cell.getBoundingClientRect().width / span;
3826
+ for (let s = 0; s < span && col < width; s++) widths[col++] = w;
3827
+ }
3828
+ }
3829
+ return widths;
3830
+ }
3831
+ function applyColgroupPreview(view, p) {
3832
+ const tableEl = findTableEl(view.nodeDOM(p.tablePos));
3833
+ const colgroup = tableEl?.querySelector("colgroup");
3834
+ if (!colgroup) return;
3835
+ const cols = colgroup.children;
3836
+ for (let i = 0; i < cols.length && i < p.colWidths.length; i++) {
3837
+ cols[i].style.width = Math.round(p.colWidths[i] * p.scale) + "px";
3838
+ }
3839
+ }
3840
+ function buildHeightDecorations(state, p) {
3841
+ const table = state.doc.nodeAt(p.tablePos);
3842
+ if (!table || table.type.name !== "table") return import_prosemirror_view3.DecorationSet.empty;
3843
+ const map = import_prosemirror_tables2.TableMap.get(table);
3844
+ const start = p.tablePos + 1;
3845
+ const decorations = [];
3846
+ const seen = /* @__PURE__ */ new Set();
3847
+ for (const relPos of map.map) {
3848
+ if (seen.has(relPos)) continue;
3849
+ seen.add(relPos);
3850
+ const node = table.nodeAt(relPos);
3851
+ if (!node) continue;
3852
+ const rect = map.findCell(relPos);
3853
+ let h = 0;
3854
+ for (let r = rect.top; r < rect.bottom; r++) h += p.rowHeights[r] ?? 0;
3855
+ const from = start + relPos;
3856
+ const to = from + node.nodeSize;
3857
+ decorations.push(
3858
+ import_prosemirror_view3.Decoration.node(from, to, {
3859
+ class: "lumir-table-scale-dragging",
3860
+ style: `height: ${Math.round(h * p.scale)}px`
3861
+ })
3862
+ );
3863
+ }
3864
+ return import_prosemirror_view3.DecorationSet.create(state.doc, decorations);
3865
+ }
3866
+ function findTableEl(dom) {
3867
+ if (!dom) return null;
3868
+ const el = dom;
3869
+ if (el.nodeName === "TABLE") return el;
3870
+ const inner = el.querySelector?.("table");
3871
+ if (inner) return inner;
3872
+ return el.closest?.("table") ?? null;
3873
+ }
3874
+
3875
+ // src/extensions/tableCellAttrPreserve.ts
3876
+ var import_prosemirror_state5 = require("prosemirror-state");
3877
+ var tableCellAttrPreserveKey = new import_prosemirror_state5.PluginKey(
3720
3878
  "lumirTableCellAttrPreserve"
3721
3879
  );
3722
3880
  var PRESERVED_ATTRS = [
@@ -3773,7 +3931,7 @@ function collectCellAttrs(doc) {
3773
3931
  return result;
3774
3932
  }
3775
3933
  function tableCellAttrPreserve() {
3776
- return new import_prosemirror_state4.Plugin({
3934
+ return new import_prosemirror_state5.Plugin({
3777
3935
  key: tableCellAttrPreserveKey,
3778
3936
  appendTransaction(transactions, oldState, newState) {
3779
3937
  if (!transactions.some((tr2) => tr2.docChanged)) {
@@ -3879,6 +4037,7 @@ var RowHeightExtension = import_core5.Extension.create({
3879
4037
  const plugins = [tableCellAttrPreserve()];
3880
4038
  if (this.options.resizable) {
3881
4039
  plugins.push(rowResizing());
4040
+ plugins.push(tableScaling());
3882
4041
  }
3883
4042
  return plugins;
3884
4043
  }
@@ -3886,11 +4045,11 @@ var RowHeightExtension = import_core5.Extension.create({
3886
4045
 
3887
4046
  // src/extensions/TableAlignmentExtension.ts
3888
4047
  var import_core6 = require("@tiptap/core");
3889
- var import_prosemirror_state5 = require("prosemirror-state");
3890
- var import_prosemirror_view3 = require("prosemirror-view");
3891
- var tableAlignmentDecoKey = new import_prosemirror_state5.PluginKey("lumirTableAlignmentDeco");
4048
+ var import_prosemirror_state6 = require("prosemirror-state");
4049
+ var import_prosemirror_view4 = require("prosemirror-view");
4050
+ var tableAlignmentDecoKey = new import_prosemirror_state6.PluginKey("lumirTableAlignmentDeco");
3892
4051
  function tableAlignmentDecorationPlugin() {
3893
- return new import_prosemirror_state5.Plugin({
4052
+ return new import_prosemirror_state6.Plugin({
3894
4053
  key: tableAlignmentDecoKey,
3895
4054
  props: {
3896
4055
  decorations(state) {
@@ -3900,7 +4059,7 @@ function tableAlignmentDecorationPlugin() {
3900
4059
  const align = node.attrs.tableAlignment;
3901
4060
  if (align && align !== "left") {
3902
4061
  decorations.push(
3903
- import_prosemirror_view3.Decoration.node(pos, pos + node.nodeSize, {
4062
+ import_prosemirror_view4.Decoration.node(pos, pos + node.nodeSize, {
3904
4063
  "data-table-alignment": align
3905
4064
  })
3906
4065
  );
@@ -3909,7 +4068,7 @@ function tableAlignmentDecorationPlugin() {
3909
4068
  }
3910
4069
  return void 0;
3911
4070
  });
3912
- return import_prosemirror_view3.DecorationSet.create(state.doc, decorations);
4071
+ return import_prosemirror_view4.DecorationSet.create(state.doc, decorations);
3913
4072
  }
3914
4073
  }
3915
4074
  });
@@ -3941,7 +4100,7 @@ var TableAlignmentExtension = import_core6.Extension.create({
3941
4100
  });
3942
4101
 
3943
4102
  // src/blocks/columns/insertColumns.ts
3944
- var import_prosemirror_state6 = require("prosemirror-state");
4103
+ var import_prosemirror_state7 = require("prosemirror-state");
3945
4104
  function insertTwoColumns(editor) {
3946
4105
  const tiptap = editor?._tiptapEditor;
3947
4106
  if (!tiptap) {
@@ -3973,7 +4132,7 @@ function insertTwoColumns(editor) {
3973
4132
  try {
3974
4133
  let tr = state.tr.insert(insertPos, list);
3975
4134
  try {
3976
- tr = tr.setSelection(import_prosemirror_state6.TextSelection.create(tr.doc, insertPos + 4));
4135
+ tr = tr.setSelection(import_prosemirror_state7.TextSelection.create(tr.doc, insertPos + 4));
3977
4136
  } catch {
3978
4137
  }
3979
4138
  tiptap.view.dispatch(tr.scrollIntoView());
@@ -4787,9 +4946,14 @@ var import_react33 = require("react");
4787
4946
  function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
4788
4947
  const { refs, floatingStyles, context, update } = (0, import_react32.useFloating)({
4789
4948
  open: show,
4790
- placement: orientation === "row" ? "left" : orientation === "col" ? "top" : "right",
4791
- // col/row: 가장자리 선(zero-size)에, cell: 우측 보더에 14px hit-area 중앙 정렬(-7).
4792
- middleware: [(0, import_react32.offset)(-7)],
4949
+ placement: orientation === "row" ? "left" : orientation === "col" ? "top" : orientation === "corner" ? "bottom-start" : "right",
4950
+ // col/row/cell: 가장자리에 14px hit-area 중앙 정렬(-7).
4951
+ // corner: 18px hit-zone이 표 우하단 모서리에 걸치도록 위/좌로 살짝 당김(모서리 hover 자연).
4952
+ middleware: [
4953
+ (0, import_react32.offset)(
4954
+ orientation === "corner" ? { mainAxis: -6, crossAxis: -6 } : -7
4955
+ )
4956
+ ],
4793
4957
  whileElementsMounted: import_react32.autoUpdate
4794
4958
  });
4795
4959
  const { isMounted, styles } = (0, import_react32.useTransitionStyles)(context);
@@ -4817,6 +4981,9 @@ function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
4817
4981
  if (orientation === "row") {
4818
4982
  return new DOMRect(t.left, c.top, 0, c.height);
4819
4983
  }
4984
+ if (orientation === "corner") {
4985
+ return new DOMRect(t.right, t.bottom, 0, 0);
4986
+ }
4820
4987
  return c;
4821
4988
  }
4822
4989
  });
@@ -4834,6 +5001,33 @@ function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
4834
5001
  [floatingStyles, isMounted, refs.setFloating, styles]
4835
5002
  );
4836
5003
  }
5004
+ function useTableCornerPositioning(referencePosTable, show) {
5005
+ const { refs, floatingStyles, context } = (0, import_react32.useFloating)({
5006
+ open: show,
5007
+ placement: "bottom-start",
5008
+ // 18px hit-zone을 모서리에서 안쪽(위/좌)으로 당겨 표 위에 걸치게 한다.
5009
+ middleware: [(0, import_react32.offset)({ mainAxis: -12, crossAxis: -12 })],
5010
+ whileElementsMounted: import_react32.autoUpdate
5011
+ });
5012
+ const { isMounted, styles } = (0, import_react32.useTransitionStyles)(context);
5013
+ (0, import_react33.useEffect)(() => {
5014
+ if (!referencePosTable) {
5015
+ refs.setReference(null);
5016
+ return;
5017
+ }
5018
+ refs.setReference({
5019
+ getBoundingClientRect: () => new DOMRect(referencePosTable.right, referencePosTable.bottom, 0, 0)
5020
+ });
5021
+ }, [referencePosTable, refs]);
5022
+ return (0, import_react33.useMemo)(
5023
+ () => ({
5024
+ isMounted,
5025
+ ref: refs.setFloating,
5026
+ style: { ...styles, ...floatingStyles }
5027
+ }),
5028
+ [floatingStyles, isMounted, refs.setFloating, styles]
5029
+ );
5030
+ }
4837
5031
 
4838
5032
  // src/components/LumirTableHandlesController.tsx
4839
5033
  var import_jsx_runtime26 = require("react/jsx-runtime");
@@ -4984,6 +5178,10 @@ function LumirTableHandlesController() {
4984
5178
  const onEndExtend = (0, import_react36.useCallback)(() => {
4985
5179
  editor.tableHandles?.unfreezeHandles();
4986
5180
  }, [editor]);
5181
+ const tableCorner = useTableCornerPositioning(
5182
+ coreState?.referencePosTable ?? null,
5183
+ !!coreState?.widgetContainer
5184
+ );
4987
5185
  const menuHandlers = (0, import_react36.useMemo)(() => {
4988
5186
  const mk = (kind) => ({
4989
5187
  freeze: () => {
@@ -5033,6 +5231,48 @@ function LumirTableHandlesController() {
5033
5231
  }, [editor, recompute]);
5034
5232
  const noop = (0, import_react36.useCallback)(() => {
5035
5233
  }, []);
5234
+ const onScaleStart = (0, import_react36.useCallback)(
5235
+ (e) => {
5236
+ e.preventDefault();
5237
+ e.stopPropagation();
5238
+ const view = editor.prosemirrorView;
5239
+ const blockId = coreState?.block?.id;
5240
+ if (!view || !blockId) return;
5241
+ const tablePos = findTableNodePos(editor._tiptapEditor, blockId);
5242
+ if (tablePos < 0) return;
5243
+ const base = measureTableForScale(view, tablePos);
5244
+ if (!base) return;
5245
+ const minScale = Math.max(
5246
+ TABLE_SCALE_MIN_COL_WIDTH / Math.max(1, Math.min(...base.colWidths)),
5247
+ ROW_RESIZE_MIN_HEIGHT / Math.max(1, Math.min(...base.rowHeights))
5248
+ );
5249
+ const startX = e.clientX;
5250
+ const startY = e.clientY;
5251
+ let current = base;
5252
+ editor.tableHandles?.freezeHandles();
5253
+ const onMove = (me) => {
5254
+ if (me.buttons === 0) return onUp();
5255
+ const newW = base.origW + (me.clientX - startX);
5256
+ const newH = base.origH + (me.clientY - startY);
5257
+ const scale = Math.min(
5258
+ TABLE_SCALE_MAX,
5259
+ Math.max(minScale, Math.max(newW / base.origW, newH / base.origH))
5260
+ );
5261
+ current = { ...base, scale };
5262
+ setTableScalePreview(view, current);
5263
+ };
5264
+ const onUp = () => {
5265
+ window.removeEventListener("pointermove", onMove);
5266
+ window.removeEventListener("pointerup", onUp);
5267
+ setTableScalePreview(view, null);
5268
+ commitTableScale(view, current);
5269
+ editor.tableHandles?.unfreezeHandles();
5270
+ };
5271
+ window.addEventListener("pointermove", onMove);
5272
+ window.addEventListener("pointerup", onUp);
5273
+ },
5274
+ [editor, coreState]
5275
+ );
5036
5276
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
5037
5277
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { ref: setMenuContainerRef }),
5038
5278
  th && focused && menuContainerRef && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react35.FloatingPortal, { root: focused.widgetContainer, children: [
@@ -5148,6 +5388,16 @@ function LumirTableHandlesController() {
5148
5388
  }
5149
5389
  )
5150
5390
  }
5391
+ ),
5392
+ tableCorner.isMounted && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5393
+ "div",
5394
+ {
5395
+ ref: tableCorner.ref,
5396
+ style: tableCorner.style,
5397
+ className: "lumir-tbl-scale-handle",
5398
+ title: "\uD45C \uD06C\uAE30 \uC870\uC808 (\uC885\uD6A1\uBE44 \uACE0\uC815)",
5399
+ onPointerDown: onScaleStart
5400
+ }
5151
5401
  )
5152
5402
  ] })
5153
5403
  ] });
@@ -5404,7 +5654,7 @@ function liftFontSize(blocks) {
5404
5654
  }
5405
5655
 
5406
5656
  // src/utils/table-delete.ts
5407
- var import_prosemirror_tables2 = require("prosemirror-tables");
5657
+ var import_prosemirror_tables3 = require("prosemirror-tables");
5408
5658
  function measureRowHeights(view, tablePos) {
5409
5659
  try {
5410
5660
  const at = view?.domAtPos?.(tablePos + 1);
@@ -5423,7 +5673,7 @@ function measureRowHeights(view, tablePos) {
5423
5673
  function buildDeleteColumnTr(state, tablePos, col, rowPx) {
5424
5674
  const table = state.doc.nodeAt(tablePos);
5425
5675
  if (!table || table.type.name !== "table") return null;
5426
- const map = import_prosemirror_tables2.TableMap.get(table);
5676
+ const map = import_prosemirror_tables3.TableMap.get(table);
5427
5677
  const W = map.width;
5428
5678
  const H = map.height;
5429
5679
  if (col < 0 || col >= W || W <= 1) return null;
@@ -5529,9 +5779,9 @@ function removeFocusedRowOrColumn(editor, index, direction) {
5529
5779
  const rowStart = state.doc.resolve(tableInside.posAtIndex(index) + 1);
5530
5780
  const cellPos = state.doc.resolve(rowStart.posAtIndex(0));
5531
5781
  const selState = state.apply(
5532
- state.tr.setSelection(new import_prosemirror_tables2.CellSelection(cellPos))
5782
+ state.tr.setSelection(new import_prosemirror_tables3.CellSelection(cellPos))
5533
5783
  );
5534
- return (0, import_prosemirror_tables2.deleteRow)(selState, (tr) => tiptap.view.dispatch(tr));
5784
+ return (0, import_prosemirror_tables3.deleteRow)(selState, (tr) => tiptap.view.dispatch(tr));
5535
5785
  } catch {
5536
5786
  return false;
5537
5787
  }
@@ -6078,6 +6328,7 @@ function LumirEditor({
6078
6328
  className = "",
6079
6329
  placeholder,
6080
6330
  sideMenuAddButton = false,
6331
+ columnDivider = false,
6081
6332
  floatingMenu = false,
6082
6333
  floatingMenuPosition = "sticky",
6083
6334
  // callbacks / refs
@@ -6592,7 +6843,11 @@ function LumirEditor({
6592
6843
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
6593
6844
  "div",
6594
6845
  {
6595
- className: cn("lumirEditor", className),
6846
+ className: cn(
6847
+ "lumirEditor",
6848
+ columnDivider && "lumir-column-divider",
6849
+ className
6850
+ ),
6596
6851
  style: { position: "relative", display: "flex", flexDirection: "column" },
6597
6852
  children: [
6598
6853
  floatingMenu && editor && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [