@lumir-company/editor 0.4.13 → 0.4.15

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.js CHANGED
@@ -40,10 +40,11 @@ __export(index_exports, {
40
40
  module.exports = __toCommonJS(index_exports);
41
41
 
42
42
  // src/components/LumirEditor.tsx
43
- var import_react23 = require("react");
44
- var import_react24 = require("@blocknote/react");
43
+ var import_react30 = require("react");
44
+ var import_react31 = require("@blocknote/react");
45
45
  var import_mantine = require("@blocknote/mantine");
46
- var import_core4 = require("@blocknote/core");
46
+ var import_core5 = require("@blocknote/core");
47
+ var import_locales = require("@blocknote/core/locales");
47
48
 
48
49
  // src/utils/cn.ts
49
50
  function cn(...inputs) {
@@ -1834,6 +1835,66 @@ var TextStyleButton = ({
1834
1835
 
1835
1836
  // src/components/FloatingMenu/components/AlignButton.tsx
1836
1837
  var import_react9 = require("react");
1838
+
1839
+ // src/utils/prosemirror-table-utils.ts
1840
+ function getSelectedCellPositions(editor) {
1841
+ const tiptap = editor._tiptapEditor;
1842
+ if (!tiptap) return [];
1843
+ const { state } = tiptap;
1844
+ const { selection } = state;
1845
+ if (typeof selection.forEachCell === "function") {
1846
+ const positions = [];
1847
+ selection.forEachCell((_node, pos) => {
1848
+ positions.push(pos);
1849
+ });
1850
+ return positions;
1851
+ }
1852
+ const $pos = selection.$from;
1853
+ for (let depth = $pos.depth; depth > 0; depth--) {
1854
+ const node = $pos.node(depth);
1855
+ if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
1856
+ return [$pos.before(depth)];
1857
+ }
1858
+ }
1859
+ return [];
1860
+ }
1861
+ function setCellAttrAtPositions(editor, positions, attr, value) {
1862
+ const tiptap = editor?._tiptapEditor;
1863
+ if (!tiptap || positions.length === 0) return false;
1864
+ let tr = tiptap.state.tr;
1865
+ let changed = false;
1866
+ for (const pos of positions) {
1867
+ const node = tr.doc.nodeAt(pos);
1868
+ if (node && (node.type.name === "tableCell" || node.type.name === "tableHeader")) {
1869
+ tr = tr.setNodeMarkup(pos, void 0, { ...node.attrs, [attr]: value });
1870
+ changed = true;
1871
+ }
1872
+ }
1873
+ if (changed) {
1874
+ tiptap.view?.dispatch(tr);
1875
+ }
1876
+ return changed;
1877
+ }
1878
+ function setSelectedCellsAttr(editor, attr, value) {
1879
+ return setCellAttrAtPositions(
1880
+ editor,
1881
+ getSelectedCellPositions(editor),
1882
+ attr,
1883
+ value
1884
+ );
1885
+ }
1886
+ function isInTableCell(editor) {
1887
+ return getSelectedCellPositions(editor).length > 0;
1888
+ }
1889
+ function getFirstSelectedCellAttr(editor, attr) {
1890
+ const tiptap = editor?._tiptapEditor;
1891
+ const positions = getSelectedCellPositions(editor);
1892
+ if (!tiptap || positions.length === 0) return void 0;
1893
+ const node = tiptap.state.doc.nodeAt(positions[0]);
1894
+ return node?.attrs?.[attr];
1895
+ }
1896
+
1897
+ // src/components/FloatingMenu/components/AlignButton.tsx
1837
1898
  var import_jsx_runtime8 = require("react/jsx-runtime");
1838
1899
  var iconMap2 = {
1839
1900
  left: Icons.alignLeft,
@@ -1851,6 +1912,9 @@ var AlignButton = ({
1851
1912
  }) => {
1852
1913
  const getCurrentAlignment = () => {
1853
1914
  try {
1915
+ if (isInTableCell(editor)) {
1916
+ return getFirstSelectedCellAttr(editor, "textAlignment") || "left";
1917
+ }
1854
1918
  const block = editor?.getTextCursorPosition()?.block;
1855
1919
  return block?.props?.textAlignment || "left";
1856
1920
  } catch {
@@ -1860,6 +1924,9 @@ var AlignButton = ({
1860
1924
  const isActive = getCurrentAlignment() === alignment;
1861
1925
  const handleClick = (0, import_react9.useCallback)(() => {
1862
1926
  try {
1927
+ if (setSelectedCellsAttr(editor, "textAlignment", alignment)) {
1928
+ return;
1929
+ }
1863
1930
  const block = editor?.getTextCursorPosition()?.block;
1864
1931
  if (block && editor?.updateBlock) {
1865
1932
  editor.updateBlock(block, { props: { textAlignment: alignment } });
@@ -2025,6 +2092,10 @@ var ColorButton = ({ editor, type }) => {
2025
2092
  const colors = type === "text" ? TEXT_COLORS : BACKGROUND_COLORS;
2026
2093
  const getCurrentColor = (0, import_react12.useCallback)(() => {
2027
2094
  try {
2095
+ if (isInTableCell(editor)) {
2096
+ const attr = type === "text" ? "textColor" : "backgroundColor";
2097
+ return getFirstSelectedCellAttr(editor, attr) || "default";
2098
+ }
2028
2099
  const activeStyles = editor?.getActiveStyles?.() || {};
2029
2100
  if (type === "text" && activeStyles.textColor) {
2030
2101
  return activeStyles.textColor;
@@ -2054,13 +2125,15 @@ var ColorButton = ({ editor, type }) => {
2054
2125
  (color) => {
2055
2126
  try {
2056
2127
  if (!editor) return;
2057
- if (type === "text") {
2058
- editor.toggleStyles({ textColor: color });
2059
- } else {
2060
- editor.toggleStyles({ backgroundColor: color });
2128
+ const attr = type === "text" ? "textColor" : "backgroundColor";
2129
+ if (!setSelectedCellsAttr(editor, attr, color)) {
2130
+ editor.toggleStyles(
2131
+ type === "text" ? { textColor: color } : { backgroundColor: color }
2132
+ );
2061
2133
  }
2062
2134
  setCurrentColor(color);
2063
2135
  setIsOpen(false);
2136
+ setTimeout(() => editor.focus?.());
2064
2137
  } catch (err) {
2065
2138
  console.error(`Color apply failed:`, err);
2066
2139
  }
@@ -2864,37 +2937,12 @@ var VerticalAlignmentExtension = import_core2.Extension.create({
2864
2937
  });
2865
2938
 
2866
2939
  // src/components/CustomFormattingToolbar.tsx
2867
- var import_react22 = require("@blocknote/react");
2940
+ var import_react24 = require("@blocknote/react");
2868
2941
 
2869
2942
  // src/components/TextAlignButtonWithVA.tsx
2870
2943
  var import_core3 = require("@blocknote/core");
2871
2944
  var import_react18 = require("react");
2872
2945
  var import_react19 = require("@blocknote/react");
2873
-
2874
- // src/utils/prosemirror-table-utils.ts
2875
- function getSelectedCellPositions(editor) {
2876
- const tiptap = editor._tiptapEditor;
2877
- if (!tiptap) return [];
2878
- const { state } = tiptap;
2879
- const { selection } = state;
2880
- if (typeof selection.forEachCell === "function") {
2881
- const positions = [];
2882
- selection.forEachCell((_node, pos) => {
2883
- positions.push(pos);
2884
- });
2885
- return positions;
2886
- }
2887
- const $pos = selection.$from;
2888
- for (let depth = $pos.depth; depth > 0; depth--) {
2889
- const node = $pos.node(depth);
2890
- if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
2891
- return [$pos.before(depth)];
2892
- }
2893
- }
2894
- return [];
2895
- }
2896
-
2897
- // src/components/TextAlignButtonWithVA.tsx
2898
2946
  var import_jsx_runtime17 = require("react/jsx-runtime");
2899
2947
  var icons = {
2900
2948
  left: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z" }) }),
@@ -3071,77 +3119,759 @@ var VerticalAlignButton = (props) => {
3071
3119
  );
3072
3120
  };
3073
3121
 
3074
- // src/components/CustomFormattingToolbar.tsx
3122
+ // src/components/color/LumirColorControls.tsx
3123
+ var import_core4 = require("@blocknote/core");
3124
+ var import_react22 = require("@blocknote/react");
3125
+ var import_react23 = require("react");
3075
3126
  var import_jsx_runtime19 = require("react/jsx-runtime");
3076
- var CustomFormattingToolbar = () => {
3077
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react22.FormattingToolbar, { children: [
3078
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.BlockTypeSelect, {}, "blockTypeSelect"),
3079
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.TableCellMergeButton, {}, "tableCellMergeButton"),
3080
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.FileCaptionButton, {}, "fileCaptionButton"),
3081
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.FileReplaceButton, {}, "replaceFileButton"),
3082
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.FileRenameButton, {}, "fileRenameButton"),
3083
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.FileDeleteButton, {}, "fileDeleteButton"),
3084
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.FileDownloadButton, {}, "fileDownloadButton"),
3085
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.FilePreviewButton, {}, "filePreviewButton"),
3086
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.BasicTextStyleButton, { basicTextStyle: "bold" }, "boldStyleButton"),
3127
+ var COLORS = [
3128
+ "default",
3129
+ "gray",
3130
+ "brown",
3131
+ "red",
3132
+ "orange",
3133
+ "yellow",
3134
+ "green",
3135
+ "blue",
3136
+ "purple",
3137
+ "pink"
3138
+ ];
3139
+ function ColorIcon(props) {
3140
+ const textColor = props.textColor || "default";
3141
+ const backgroundColor = props.backgroundColor || "default";
3142
+ const size = props.size || 16;
3143
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3144
+ "div",
3145
+ {
3146
+ className: "bn-color-icon",
3147
+ "data-background-color": backgroundColor,
3148
+ "data-text-color": textColor,
3149
+ style: {
3150
+ pointerEvents: "none",
3151
+ fontSize: `${size * 0.75}px`,
3152
+ height: `${size}px`,
3153
+ lineHeight: `${size}px`,
3154
+ textAlign: "center",
3155
+ width: `${size}px`
3156
+ },
3157
+ children: "A"
3158
+ }
3159
+ );
3160
+ }
3161
+ function CellFillIcon({ size = 18 }) {
3162
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3163
+ "svg",
3164
+ {
3165
+ width: size,
3166
+ height: size,
3167
+ viewBox: "0 0 24 24",
3168
+ fill: "currentColor",
3169
+ style: { pointerEvents: "none" },
3170
+ "aria-hidden": "true",
3171
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M16.56 8.94 7.62 0 6.21 1.41l2.38 2.38-5.15 5.15c-.59.59-.59 1.54 0 2.12l5.5 5.5c.29.29.68.44 1.06.44s.77-.15 1.06-.44l5.5-5.5c.59-.58.59-1.53 0-2.12zM5.21 10 10 5.21 14.79 10H5.21zM19 11.5s-2 2.17-2 3.5c0 1.1.9 2 2 2s2-.9 2-2c0-1.33-2-3.5-2-3.5z" })
3172
+ }
3173
+ );
3174
+ }
3175
+ function LumirColorPicker(props) {
3176
+ const Components = (0, import_react22.useComponentsContext)();
3177
+ const dict = (0, import_react22.useDictionary)();
3178
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
3179
+ props.text ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
3180
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Components.Generic.Menu.Label, { children: props.textTitle ?? dict.color_picker.text_title }),
3181
+ COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3182
+ Components.Generic.Menu.Item,
3183
+ {
3184
+ onClick: () => {
3185
+ props.onClick?.();
3186
+ props.text.setColor(color);
3187
+ },
3188
+ "data-test": "text-color-" + color,
3189
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ColorIcon, { textColor: color, size: props.iconSize }),
3190
+ checked: props.text.color === color,
3191
+ children: dict.color_picker.colors[color]
3192
+ },
3193
+ "text-color-" + color
3194
+ ))
3195
+ ] }) : null,
3196
+ props.background ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
3197
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Components.Generic.Menu.Label, { children: props.backgroundTitle ?? dict.color_picker.background_title }),
3198
+ COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3199
+ Components.Generic.Menu.Item,
3200
+ {
3201
+ onClick: () => {
3202
+ props.onClick?.();
3203
+ props.background.setColor(color);
3204
+ },
3205
+ "data-test": "background-color-" + color,
3206
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ColorIcon, { backgroundColor: color, size: props.iconSize }),
3207
+ checked: props.background.color === color,
3208
+ children: dict.color_picker.colors[color]
3209
+ },
3210
+ "background-color-" + color
3211
+ ))
3212
+ ] }) : null
3213
+ ] });
3214
+ }
3215
+ function LumirColorStyleButton() {
3216
+ const Components = (0, import_react22.useComponentsContext)();
3217
+ const editor = (0, import_react22.useBlockNoteEditor)();
3218
+ const ed = editor;
3219
+ const styleSchema = editor.schema.styleSchema;
3220
+ const textColorInSchema = styleSchema.textColor?.type === "textColor" && styleSchema.textColor?.propSchema === "string";
3221
+ const backgroundColorInSchema = styleSchema.backgroundColor?.type === "backgroundColor" && styleSchema.backgroundColor?.propSchema === "string";
3222
+ const selectedBlocks = (0, import_react22.useSelectedBlocks)(editor);
3223
+ const [currentTextColor, setCurrentTextColor] = (0, import_react23.useState)(
3224
+ textColorInSchema ? ed.getActiveStyles().textColor || "default" : "default"
3225
+ );
3226
+ const [currentBackgroundColor, setCurrentBackgroundColor] = (0, import_react23.useState)(
3227
+ backgroundColorInSchema ? ed.getActiveStyles().backgroundColor || "default" : "default"
3228
+ );
3229
+ (0, import_react22.useEditorContentOrSelectionChange)(() => {
3230
+ const active = ed.getActiveStyles();
3231
+ if (textColorInSchema) {
3232
+ setCurrentTextColor(active.textColor || "default");
3233
+ }
3234
+ if (backgroundColorInSchema) {
3235
+ setCurrentBackgroundColor(active.backgroundColor || "default");
3236
+ }
3237
+ }, editor);
3238
+ const setTextColor = (0, import_react23.useCallback)(
3239
+ (color) => {
3240
+ color === "default" ? ed.removeStyles({ textColor: color }) : ed.addStyles({ textColor: color });
3241
+ setTimeout(() => editor.focus());
3242
+ },
3243
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3244
+ [editor]
3245
+ );
3246
+ const setBackgroundColor = (0, import_react23.useCallback)(
3247
+ (color) => {
3248
+ color === "default" ? ed.removeStyles({ backgroundColor: color }) : ed.addStyles({ backgroundColor: color });
3249
+ setTimeout(() => editor.focus());
3250
+ },
3251
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3252
+ [editor]
3253
+ );
3254
+ const show = (0, import_react23.useMemo)(() => {
3255
+ if (!textColorInSchema && !backgroundColorInSchema) {
3256
+ return false;
3257
+ }
3258
+ for (const block of selectedBlocks) {
3259
+ if (block.content !== void 0) {
3260
+ return true;
3261
+ }
3262
+ }
3263
+ return false;
3264
+ }, [backgroundColorInSchema, selectedBlocks, textColorInSchema]);
3265
+ if (!show || !editor.isEditable) {
3266
+ return null;
3267
+ }
3268
+ const tooltip = "\uD14D\uC2A4\uD2B8 \uC0C9\xB7\uBC30\uACBD";
3269
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Components.Generic.Menu.Root, { children: [
3270
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Components.Generic.Menu.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3271
+ Components.FormattingToolbar.Button,
3272
+ {
3273
+ className: "bn-button",
3274
+ "data-test": "colors",
3275
+ label: tooltip,
3276
+ mainTooltip: tooltip,
3277
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3278
+ ColorIcon,
3279
+ {
3280
+ textColor: currentTextColor,
3281
+ backgroundColor: currentBackgroundColor,
3282
+ size: 20
3283
+ }
3284
+ )
3285
+ }
3286
+ ) }),
3087
3287
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3088
- import_react22.BasicTextStyleButton,
3288
+ Components.Generic.Menu.Dropdown,
3289
+ {
3290
+ className: "bn-menu-dropdown bn-color-picker-dropdown",
3291
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3292
+ LumirColorPicker,
3293
+ {
3294
+ textTitle: "\uD14D\uC2A4\uD2B8 \uC0C9",
3295
+ backgroundTitle: "\uD14D\uC2A4\uD2B8 \uBC30\uACBD",
3296
+ text: textColorInSchema ? { color: currentTextColor, setColor: setTextColor } : void 0,
3297
+ background: backgroundColorInSchema ? { color: currentBackgroundColor, setColor: setBackgroundColor } : void 0
3298
+ }
3299
+ )
3300
+ }
3301
+ )
3302
+ ] });
3303
+ }
3304
+ function LumirCellColorToolbarButton() {
3305
+ const Components = (0, import_react22.useComponentsContext)();
3306
+ const editor = (0, import_react22.useBlockNoteEditor)();
3307
+ const selectedBlocks = (0, import_react22.useSelectedBlocks)(editor);
3308
+ const isMultiCell = (0, import_react23.useMemo)(() => {
3309
+ if (selectedBlocks.length !== 1 || selectedBlocks[0].type !== "table") {
3310
+ return false;
3311
+ }
3312
+ const cs = editor.tableHandles?.getCellSelection();
3313
+ return !!cs && cs.cells.length > 1;
3314
+ }, [editor, selectedBlocks]);
3315
+ const stashRef = (0, import_react23.useRef)([]);
3316
+ const applyBackground = (0, import_react23.useCallback)(
3317
+ (color) => {
3318
+ const live = getSelectedCellPositions(editor);
3319
+ const positions = live.length > 0 ? live : stashRef.current;
3320
+ setCellAttrAtPositions(editor, positions, "backgroundColor", color);
3321
+ setTimeout(() => editor.focus());
3322
+ },
3323
+ [editor]
3324
+ );
3325
+ if (!editor.isEditable || !isMultiCell) {
3326
+ return null;
3327
+ }
3328
+ const tooltip = "\uC140 \uBC30\uACBD\uC0C9";
3329
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
3330
+ Components.Generic.Menu.Root,
3331
+ {
3332
+ onOpenChange: (open) => {
3333
+ if (open) {
3334
+ stashRef.current = getSelectedCellPositions(editor);
3335
+ }
3336
+ },
3337
+ children: [
3338
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Components.Generic.Menu.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3339
+ Components.FormattingToolbar.Button,
3340
+ {
3341
+ className: "bn-button",
3342
+ "data-test": "cell-colors",
3343
+ label: tooltip,
3344
+ mainTooltip: tooltip,
3345
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CellFillIcon, { size: 18 })
3346
+ }
3347
+ ) }),
3348
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3349
+ Components.Generic.Menu.Dropdown,
3350
+ {
3351
+ className: "bn-menu-dropdown bn-color-picker-dropdown",
3352
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3353
+ LumirColorPicker,
3354
+ {
3355
+ backgroundTitle: "\uC140 \uBC30\uACBD",
3356
+ background: { color: "default", setColor: applyBackground }
3357
+ }
3358
+ )
3359
+ }
3360
+ )
3361
+ ]
3362
+ }
3363
+ );
3364
+ }
3365
+ function LumirCellColorPickerButton(props) {
3366
+ const Components = (0, import_react22.useComponentsContext)();
3367
+ const editor = (0, import_react22.useBlockNoteEditor)();
3368
+ const updateColor = (color, type) => {
3369
+ const newTable = props.block.content.rows.map((row) => ({
3370
+ ...row,
3371
+ cells: row.cells.map((cell) => (0, import_core4.mapTableCell)(cell))
3372
+ }));
3373
+ if (type === "text") {
3374
+ newTable[props.rowIndex].cells[props.colIndex].props.textColor = color;
3375
+ } else {
3376
+ newTable[props.rowIndex].cells[props.colIndex].props.backgroundColor = color;
3377
+ }
3378
+ editor.updateBlock(props.block, {
3379
+ type: "table",
3380
+ content: { ...props.block.content, rows: newTable }
3381
+ });
3382
+ editor.setTextCursorPosition(props.block);
3383
+ };
3384
+ const currentCell = props.block.content.rows[props.rowIndex]?.cells?.[props.colIndex];
3385
+ if (!currentCell || editor.settings.tables.cellTextColor === false && editor.settings.tables.cellBackgroundColor === false) {
3386
+ return null;
3387
+ }
3388
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Components.Generic.Menu.Root, { position: "right", sub: true, children: [
3389
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Components.Generic.Menu.Trigger, { sub: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Components.Generic.Menu.Item, { className: "bn-menu-item", subTrigger: true, children: "\uC140 \uC0C9\xB7\uBC30\uACBD" }) }),
3390
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3391
+ Components.Generic.Menu.Dropdown,
3392
+ {
3393
+ sub: true,
3394
+ className: "bn-menu-dropdown bn-color-picker-dropdown",
3395
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3396
+ LumirColorPicker,
3397
+ {
3398
+ iconSize: 18,
3399
+ textTitle: "\uC140 \uAE00\uC790\uC0C9",
3400
+ backgroundTitle: "\uC140 \uBC30\uACBD",
3401
+ text: editor.settings.tables.cellTextColor ? {
3402
+ color: (0, import_core4.isTableCell)(currentCell) ? currentCell.props.textColor : "default",
3403
+ setColor: (color) => updateColor(color, "text")
3404
+ } : void 0,
3405
+ background: editor.settings.tables.cellBackgroundColor ? {
3406
+ color: (0, import_core4.isTableCell)(currentCell) ? currentCell.props.backgroundColor : "default",
3407
+ setColor: (color) => updateColor(color, "background")
3408
+ } : void 0
3409
+ }
3410
+ )
3411
+ }
3412
+ )
3413
+ ] });
3414
+ }
3415
+ function LumirTableCellMenu(props) {
3416
+ const Components = (0, import_react22.useComponentsContext)();
3417
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3418
+ Components.Generic.Menu.Dropdown,
3419
+ {
3420
+ className: "bn-menu-dropdown bn-drag-handle-menu",
3421
+ children: props.children || /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
3422
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3423
+ import_react22.SplitButton,
3424
+ {
3425
+ block: props.block,
3426
+ rowIndex: props.rowIndex,
3427
+ colIndex: props.colIndex
3428
+ }
3429
+ ),
3430
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3431
+ LumirCellColorPickerButton,
3432
+ {
3433
+ block: props.block,
3434
+ rowIndex: props.rowIndex,
3435
+ colIndex: props.colIndex
3436
+ }
3437
+ )
3438
+ ] })
3439
+ }
3440
+ );
3441
+ }
3442
+
3443
+ // src/components/CustomFormattingToolbar.tsx
3444
+ var import_jsx_runtime20 = require("react/jsx-runtime");
3445
+ var CustomFormattingToolbar = () => {
3446
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react24.FormattingToolbar, { children: [
3447
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.BlockTypeSelect, {}, "blockTypeSelect"),
3448
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.TableCellMergeButton, {}, "tableCellMergeButton"),
3449
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.FileCaptionButton, {}, "fileCaptionButton"),
3450
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.FileReplaceButton, {}, "replaceFileButton"),
3451
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.FileRenameButton, {}, "fileRenameButton"),
3452
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.FileDeleteButton, {}, "fileDeleteButton"),
3453
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.FileDownloadButton, {}, "fileDownloadButton"),
3454
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.FilePreviewButton, {}, "filePreviewButton"),
3455
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.BasicTextStyleButton, { basicTextStyle: "bold" }, "boldStyleButton"),
3456
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3457
+ import_react24.BasicTextStyleButton,
3089
3458
  {
3090
3459
  basicTextStyle: "italic"
3091
3460
  },
3092
3461
  "italicStyleButton"
3093
3462
  ),
3094
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3095
- import_react22.BasicTextStyleButton,
3463
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3464
+ import_react24.BasicTextStyleButton,
3096
3465
  {
3097
3466
  basicTextStyle: "underline"
3098
3467
  },
3099
3468
  "underlineStyleButton"
3100
3469
  ),
3101
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3102
- import_react22.BasicTextStyleButton,
3470
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3471
+ import_react24.BasicTextStyleButton,
3103
3472
  {
3104
3473
  basicTextStyle: "strike"
3105
3474
  },
3106
3475
  "strikeStyleButton"
3107
3476
  ),
3108
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TextAlignButtonWithVA, { textAlignment: "left" }, "textAlignLeftButton"),
3109
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3477
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TextAlignButtonWithVA, { textAlignment: "left" }, "textAlignLeftButton"),
3478
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3110
3479
  TextAlignButtonWithVA,
3111
3480
  {
3112
3481
  textAlignment: "center"
3113
3482
  },
3114
3483
  "textAlignCenterButton"
3115
3484
  ),
3116
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TextAlignButtonWithVA, { textAlignment: "right" }, "textAlignRightButton"),
3117
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3485
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TextAlignButtonWithVA, { textAlignment: "right" }, "textAlignRightButton"),
3486
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3118
3487
  VerticalAlignButton,
3119
3488
  {
3120
3489
  verticalAlignment: "top"
3121
3490
  },
3122
3491
  "verticalAlignTop"
3123
3492
  ),
3124
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3493
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3125
3494
  VerticalAlignButton,
3126
3495
  {
3127
3496
  verticalAlignment: "middle"
3128
3497
  },
3129
3498
  "verticalAlignMiddle"
3130
3499
  ),
3131
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3500
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3132
3501
  VerticalAlignButton,
3133
3502
  {
3134
3503
  verticalAlignment: "bottom"
3135
3504
  },
3136
3505
  "verticalAlignBottom"
3137
3506
  ),
3138
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.ColorStyleButton, {}, "colorStyleButton"),
3139
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.NestBlockButton, {}, "nestBlockButton"),
3140
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.UnnestBlockButton, {}, "unnestBlockButton"),
3141
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.CreateLinkButton, {}, "createLinkButton")
3507
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LumirColorStyleButton, {}, "colorStyleButton"),
3508
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LumirCellColorToolbarButton, {}, "cellColorButton"),
3509
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.NestBlockButton, {}, "nestBlockButton"),
3510
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.UnnestBlockButton, {}, "unnestBlockButton"),
3511
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.CreateLinkButton, {}, "createLinkButton")
3142
3512
  ] });
3143
3513
  };
3144
3514
 
3515
+ // src/components/LumirTableHandlesController.tsx
3516
+ var import_react27 = require("@blocknote/react");
3517
+ var import_react28 = require("@floating-ui/react");
3518
+ var import_react29 = require("react");
3519
+
3520
+ // src/components/hooks/useFocusedCellHandlePositioning.ts
3521
+ var import_react25 = require("@floating-ui/react");
3522
+ var import_react26 = require("react");
3523
+ function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
3524
+ const { refs, floatingStyles, context } = (0, import_react25.useFloating)({
3525
+ open: show,
3526
+ placement: orientation === "row" ? "left" : orientation === "col" ? "top" : "right",
3527
+ // col/row: 가장자리 선(zero-size)에, cell: 셀 우측 보더에 14px hit-area 중앙 정렬(-7).
3528
+ middleware: [(0, import_react25.offset)(-7)],
3529
+ whileElementsMounted: import_react25.autoUpdate
3530
+ });
3531
+ const { isMounted, styles } = (0, import_react25.useTransitionStyles)(context);
3532
+ (0, import_react26.useEffect)(() => {
3533
+ if (!cellEl) {
3534
+ refs.setReference(null);
3535
+ return;
3536
+ }
3537
+ refs.setReference({
3538
+ contextElement: cellEl,
3539
+ getBoundingClientRect: () => {
3540
+ const c = cellEl.getBoundingClientRect();
3541
+ const t = tbodyEl?.getBoundingClientRect() ?? c;
3542
+ if (orientation === "col") {
3543
+ return new DOMRect(c.left, t.top, c.width, 0);
3544
+ }
3545
+ if (orientation === "row") {
3546
+ return new DOMRect(t.left, c.top, 0, c.height);
3547
+ }
3548
+ return c;
3549
+ }
3550
+ });
3551
+ }, [cellEl, tbodyEl, orientation, refs]);
3552
+ return (0, import_react26.useMemo)(
3553
+ () => ({
3554
+ isMounted,
3555
+ ref: refs.setFloating,
3556
+ // display는 CSS 클래스에서 제어한다(absolute 자식 stacking).
3557
+ style: {
3558
+ ...styles,
3559
+ ...floatingStyles
3560
+ }
3561
+ }),
3562
+ [floatingStyles, isMounted, refs.setFloating, styles]
3563
+ );
3564
+ }
3565
+
3566
+ // src/components/LumirTableHandlesController.tsx
3567
+ var import_jsx_runtime21 = require("react/jsx-runtime");
3568
+ function syncCoreHoverToFocusedCell(cellEl) {
3569
+ const r = cellEl.getBoundingClientRect();
3570
+ cellEl.dispatchEvent(
3571
+ new MouseEvent("mousemove", {
3572
+ bubbles: true,
3573
+ cancelable: true,
3574
+ view: window,
3575
+ clientX: r.left + r.width / 2,
3576
+ clientY: r.top + r.height / 2
3577
+ })
3578
+ );
3579
+ }
3580
+ function LumirTableHandlesController() {
3581
+ const editor = (0, import_react27.useBlockNoteEditor)();
3582
+ const [focused, setFocused] = (0, import_react29.useState)(null);
3583
+ const [menuContainerRef, setMenuContainerRef] = (0, import_react29.useState)(null);
3584
+ const [overlayEl, setOverlayEl] = (0, import_react29.useState)(null);
3585
+ const [openMenu, setOpenMenu] = (0, import_react29.useState)(null);
3586
+ const frozenRef = (0, import_react29.useRef)(false);
3587
+ const menuOpenRef = (0, import_react29.useRef)(false);
3588
+ const draggingRef = (0, import_react29.useRef)(false);
3589
+ const recompute = (0, import_react29.useCallback)(() => {
3590
+ if (frozenRef.current) {
3591
+ return;
3592
+ }
3593
+ const th2 = editor.tableHandles;
3594
+ const view = editor.prosemirrorView;
3595
+ if (!th2 || !view) {
3596
+ setFocused(null);
3597
+ return;
3598
+ }
3599
+ let cellEl2 = null;
3600
+ try {
3601
+ const { node } = view.domAtPos(view.state.selection.from);
3602
+ const el = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
3603
+ cellEl2 = el?.closest?.("td, th") ?? null;
3604
+ } catch {
3605
+ cellEl2 = null;
3606
+ }
3607
+ if (!cellEl2 || !cellEl2.isConnected) {
3608
+ setFocused(null);
3609
+ return;
3610
+ }
3611
+ const blockId = cellEl2.closest("[data-id]")?.getAttribute("data-id");
3612
+ const block = blockId ? editor.getBlock(blockId) : void 0;
3613
+ if (!block || block.type !== "table") {
3614
+ setFocused(null);
3615
+ return;
3616
+ }
3617
+ const cellSel = th2.getCellSelection();
3618
+ if (!cellSel) {
3619
+ setFocused(null);
3620
+ return;
3621
+ }
3622
+ const widgetContainer = cellEl2.closest(".tableWrapper")?.querySelector(".table-widgets-container");
3623
+ const tbodyEl2 = cellEl2.closest("tbody");
3624
+ if (!widgetContainer || !tbodyEl2) {
3625
+ setFocused(null);
3626
+ return;
3627
+ }
3628
+ setFocused({
3629
+ block,
3630
+ rowIndex: cellSel.from.row,
3631
+ colIndex: cellSel.from.col,
3632
+ cellEl: cellEl2,
3633
+ tbodyEl: tbodyEl2,
3634
+ widgetContainer
3635
+ });
3636
+ }, [editor]);
3637
+ (0, import_react27.useEditorContentOrSelectionChange)(recompute, editor);
3638
+ (0, import_react29.useEffect)(() => {
3639
+ recompute();
3640
+ }, [recompute]);
3641
+ (0, import_react29.useEffect)(() => {
3642
+ const onUp = () => {
3643
+ requestAnimationFrame(() => {
3644
+ if (!menuOpenRef.current && !draggingRef.current && frozenRef.current) {
3645
+ frozenRef.current = false;
3646
+ recompute();
3647
+ }
3648
+ });
3649
+ };
3650
+ window.addEventListener("pointerup", onUp);
3651
+ return () => window.removeEventListener("pointerup", onUp);
3652
+ }, [recompute]);
3653
+ (0, import_react29.useEffect)(() => {
3654
+ const f = focused;
3655
+ if (!f || !overlayEl) {
3656
+ return;
3657
+ }
3658
+ const PAD = 1;
3659
+ const update = () => {
3660
+ const cr = f.cellEl.getBoundingClientRect();
3661
+ const tr = f.tbodyEl.getBoundingClientRect();
3662
+ const kr = f.widgetContainer.getBoundingClientRect();
3663
+ const x1 = openMenu === "row" ? tr.left : cr.left;
3664
+ const y1 = openMenu === "col" ? tr.top : cr.top;
3665
+ const x2 = openMenu === "row" ? tr.right : cr.right;
3666
+ const y2 = openMenu === "col" ? tr.bottom : cr.bottom;
3667
+ const dpr = window.devicePixelRatio || 1;
3668
+ const rd = (v) => Math.round(v * dpr) / dpr;
3669
+ const left = rd(x1 - kr.left) - PAD;
3670
+ const top = rd(y1 - kr.top) - PAD;
3671
+ const right = rd(x2 - kr.left) + PAD;
3672
+ const bottom = rd(y2 - kr.top) + PAD;
3673
+ overlayEl.style.transform = `translate(${left}px, ${top}px)`;
3674
+ overlayEl.style.width = `${right - left}px`;
3675
+ overlayEl.style.height = `${bottom - top}px`;
3676
+ };
3677
+ update();
3678
+ return (0, import_react28.autoUpdate)(f.cellEl, overlayEl, update);
3679
+ }, [focused, overlayEl, openMenu]);
3680
+ const cellEl = focused?.cellEl ?? null;
3681
+ const tbodyEl = focused?.tbodyEl ?? null;
3682
+ const show = focused !== null;
3683
+ const rowHandle = useFocusedCellHandlePositioning(cellEl, tbodyEl, "row", show);
3684
+ const colHandle = useFocusedCellHandlePositioning(cellEl, tbodyEl, "col", show);
3685
+ const cellHandle = useFocusedCellHandlePositioning(
3686
+ cellEl,
3687
+ tbodyEl,
3688
+ "cell",
3689
+ show
3690
+ );
3691
+ const th = editor.tableHandles;
3692
+ const coreState = (0, import_react27.useUIPluginState)(
3693
+ editor.tableHandles.onUpdate.bind(editor.tableHandles)
3694
+ );
3695
+ const { addOrRemoveColumnsButton, addOrRemoveRowsButton } = (0, import_react27.useExtendButtonsPositioning)(
3696
+ coreState?.showAddOrRemoveColumnsButton || false,
3697
+ coreState?.showAddOrRemoveRowsButton || false,
3698
+ coreState?.referencePosTable || null
3699
+ );
3700
+ const onStartExtend = (0, import_react29.useCallback)(() => {
3701
+ editor.tableHandles?.freezeHandles();
3702
+ }, [editor]);
3703
+ const onEndExtend = (0, import_react29.useCallback)(() => {
3704
+ editor.tableHandles?.unfreezeHandles();
3705
+ }, [editor]);
3706
+ const menuHandlers = (0, import_react29.useMemo)(() => {
3707
+ const mk = (kind) => ({
3708
+ freeze: () => {
3709
+ menuOpenRef.current = true;
3710
+ frozenRef.current = true;
3711
+ setOpenMenu(kind);
3712
+ editor.tableHandles?.freezeHandles();
3713
+ },
3714
+ unfreeze: () => {
3715
+ menuOpenRef.current = false;
3716
+ frozenRef.current = false;
3717
+ setOpenMenu(null);
3718
+ editor.tableHandles?.unfreezeHandles();
3719
+ recompute();
3720
+ }
3721
+ });
3722
+ return { col: mk("col"), row: mk("row"), cell: mk("cell") };
3723
+ }, [editor, recompute]);
3724
+ const onGutterPointerDown = (0, import_react29.useCallback)(() => {
3725
+ frozenRef.current = true;
3726
+ }, []);
3727
+ const onGutterPointerEnter = (0, import_react29.useCallback)(
3728
+ (e) => {
3729
+ if (e.buttons === 0 && focused) {
3730
+ syncCoreHoverToFocusedCell(focused.cellEl);
3731
+ }
3732
+ },
3733
+ [focused]
3734
+ );
3735
+ const makeDragStart = (0, import_react29.useCallback)(
3736
+ (dir) => (e) => {
3737
+ draggingRef.current = true;
3738
+ frozenRef.current = true;
3739
+ if (dir === "row") {
3740
+ editor.tableHandles?.rowDragStart(e);
3741
+ } else {
3742
+ editor.tableHandles?.colDragStart(e);
3743
+ }
3744
+ },
3745
+ [editor]
3746
+ );
3747
+ const onDragEnd = (0, import_react29.useCallback)(() => {
3748
+ editor.tableHandles?.dragEnd();
3749
+ draggingRef.current = false;
3750
+ frozenRef.current = false;
3751
+ recompute();
3752
+ }, [editor, recompute]);
3753
+ const noop = (0, import_react29.useCallback)(() => {
3754
+ }, []);
3755
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
3756
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { ref: setMenuContainerRef }),
3757
+ th && focused && menuContainerRef && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react28.FloatingPortal, { root: focused.widgetContainer, children: [
3758
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { ref: setOverlayEl, className: "lumir-tbl-cell-focus" }),
3759
+ colHandle.isMounted && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3760
+ "div",
3761
+ {
3762
+ ref: colHandle.ref,
3763
+ style: colHandle.style,
3764
+ className: "lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--col" + (openMenu === "col" ? " lumir-tbl-gutter-wrap--active" : ""),
3765
+ onPointerEnter: onGutterPointerEnter,
3766
+ onPointerDown: onGutterPointerDown,
3767
+ children: [
3768
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "lumir-tbl-gutter" }),
3769
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "lumir-tbl-grip", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3770
+ import_react27.TableHandle,
3771
+ {
3772
+ editor,
3773
+ orientation: "column",
3774
+ index: focused.colIndex,
3775
+ block: focused.block,
3776
+ dragStart: makeDragStart("col"),
3777
+ dragEnd: onDragEnd,
3778
+ freezeHandles: menuHandlers.col.freeze,
3779
+ unfreezeHandles: menuHandlers.col.unfreeze,
3780
+ menuContainer: menuContainerRef,
3781
+ showOtherSide: noop,
3782
+ hideOtherSide: noop
3783
+ }
3784
+ ) })
3785
+ ]
3786
+ }
3787
+ ),
3788
+ rowHandle.isMounted && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3789
+ "div",
3790
+ {
3791
+ ref: rowHandle.ref,
3792
+ style: rowHandle.style,
3793
+ className: "lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--row" + (openMenu === "row" ? " lumir-tbl-gutter-wrap--active" : ""),
3794
+ onPointerEnter: onGutterPointerEnter,
3795
+ onPointerDown: onGutterPointerDown,
3796
+ children: [
3797
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "lumir-tbl-gutter" }),
3798
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "lumir-tbl-grip", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3799
+ import_react27.TableHandle,
3800
+ {
3801
+ editor,
3802
+ orientation: "row",
3803
+ index: focused.rowIndex,
3804
+ block: focused.block,
3805
+ dragStart: makeDragStart("row"),
3806
+ dragEnd: onDragEnd,
3807
+ freezeHandles: menuHandlers.row.freeze,
3808
+ unfreezeHandles: menuHandlers.row.unfreeze,
3809
+ menuContainer: menuContainerRef,
3810
+ showOtherSide: noop,
3811
+ hideOtherSide: noop
3812
+ }
3813
+ ) })
3814
+ ]
3815
+ }
3816
+ ),
3817
+ cellHandle.isMounted && openMenu !== "col" && openMenu !== "row" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3818
+ "div",
3819
+ {
3820
+ ref: cellHandle.ref,
3821
+ style: cellHandle.style,
3822
+ className: "lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--cell" + (openMenu === "cell" ? " lumir-tbl-gutter-wrap--active" : ""),
3823
+ onPointerDown: onGutterPointerDown,
3824
+ children: [
3825
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "lumir-tbl-gutter" }),
3826
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "lumir-tbl-grip", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3827
+ import_react27.TableCellButton,
3828
+ {
3829
+ editor,
3830
+ rowIndex: focused.rowIndex,
3831
+ colIndex: focused.colIndex,
3832
+ block: focused.block,
3833
+ tableCellMenu: LumirTableCellMenu,
3834
+ menuContainer: menuContainerRef,
3835
+ freezeHandles: menuHandlers.cell.freeze,
3836
+ unfreezeHandles: menuHandlers.cell.unfreeze
3837
+ }
3838
+ ) })
3839
+ ]
3840
+ }
3841
+ )
3842
+ ] }),
3843
+ th && coreState?.widgetContainer && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react28.FloatingPortal, { root: coreState.widgetContainer, children: [
3844
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { ref: addOrRemoveRowsButton.ref, style: addOrRemoveRowsButton.style, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3845
+ import_react27.ExtendButton,
3846
+ {
3847
+ editor,
3848
+ orientation: "addOrRemoveRows",
3849
+ block: coreState.block,
3850
+ onMouseDown: onStartExtend,
3851
+ onMouseUp: onEndExtend
3852
+ }
3853
+ ) }),
3854
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3855
+ "div",
3856
+ {
3857
+ ref: addOrRemoveColumnsButton.ref,
3858
+ style: addOrRemoveColumnsButton.style,
3859
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3860
+ import_react27.ExtendButton,
3861
+ {
3862
+ editor,
3863
+ orientation: "addOrRemoveColumns",
3864
+ block: coreState.block,
3865
+ onMouseDown: onStartExtend,
3866
+ onMouseUp: onEndExtend
3867
+ }
3868
+ )
3869
+ }
3870
+ )
3871
+ ] })
3872
+ ] });
3873
+ }
3874
+
3145
3875
  // src/utils/table-vertical-alignment.ts
3146
3876
  function injectVerticalAlignment(blocks, editor) {
3147
3877
  const tiptap = editor?._tiptapEditor;
@@ -3224,6 +3954,235 @@ function patchBlocks(blocks, tableVAMap) {
3224
3954
  });
3225
3955
  }
3226
3956
 
3957
+ // src/utils/excel-paste.ts
3958
+ var NAMED_COLORS = {
3959
+ black: "#000000",
3960
+ white: "#ffffff",
3961
+ red: "#ff0000",
3962
+ green: "#008000",
3963
+ blue: "#0000ff",
3964
+ yellow: "#ffff00",
3965
+ orange: "#ffa500",
3966
+ purple: "#800080",
3967
+ gray: "#808080",
3968
+ grey: "#808080"
3969
+ };
3970
+ function parseCssColorToRgb(input) {
3971
+ if (!input) return null;
3972
+ const s = input.trim().toLowerCase();
3973
+ if (!s || s === "transparent" || s === "none" || s === "inherit") return null;
3974
+ let m = s.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/);
3975
+ if (m) {
3976
+ let h = m[1];
3977
+ if (h.length === 3)
3978
+ h = h.split("").map((c) => c + c).join("");
3979
+ return [
3980
+ parseInt(h.slice(0, 2), 16),
3981
+ parseInt(h.slice(2, 4), 16),
3982
+ parseInt(h.slice(4, 6), 16)
3983
+ ];
3984
+ }
3985
+ m = s.match(/^rgba?\(([^)]+)\)$/);
3986
+ if (m) {
3987
+ const parts = m[1].split(",").map((x) => parseFloat(x.trim()));
3988
+ if (parts.length >= 3 && parts.slice(0, 3).every((n) => !isNaN(n))) {
3989
+ return [parts[0], parts[1], parts[2]];
3990
+ }
3991
+ return null;
3992
+ }
3993
+ if (NAMED_COLORS[s]) return parseCssColorToRgb(NAMED_COLORS[s]);
3994
+ return null;
3995
+ }
3996
+ function rgbToHsl([r, g, b]) {
3997
+ r /= 255;
3998
+ g /= 255;
3999
+ b /= 255;
4000
+ const max = Math.max(r, g, b);
4001
+ const min = Math.min(r, g, b);
4002
+ const l = (max + min) / 2;
4003
+ let h = 0;
4004
+ let s = 0;
4005
+ const d = max - min;
4006
+ if (d !== 0) {
4007
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
4008
+ switch (max) {
4009
+ case r:
4010
+ h = ((g - b) / d + (g < b ? 6 : 0)) * 60;
4011
+ break;
4012
+ case g:
4013
+ h = ((b - r) / d + 2) * 60;
4014
+ break;
4015
+ default:
4016
+ h = ((r - g) / d + 4) * 60;
4017
+ break;
4018
+ }
4019
+ }
4020
+ return [h, s, l];
4021
+ }
4022
+ var HUE_REFERENCE = [
4023
+ { value: "red", hue: 0 },
4024
+ { value: "brown", hue: 17 },
4025
+ { value: "orange", hue: 30 },
4026
+ { value: "yellow", hue: 46 },
4027
+ { value: "green", hue: 150 },
4028
+ { value: "blue", hue: 197 },
4029
+ { value: "purple", hue: 262 },
4030
+ { value: "pink", hue: 324 }
4031
+ ];
4032
+ function hueDist(a, b) {
4033
+ const d = Math.abs(a - b) % 360;
4034
+ return d > 180 ? 360 - d : d;
4035
+ }
4036
+ function paletteValueFromRgb(rgb) {
4037
+ const [h, s, l] = rgbToHsl(rgb);
4038
+ if (s < 0.15) {
4039
+ if (l < 0.35) return "default";
4040
+ if (l > 0.85) return "default";
4041
+ return "gray";
4042
+ }
4043
+ let best = "gray";
4044
+ let bestDist = Infinity;
4045
+ for (const ref of HUE_REFERENCE) {
4046
+ const d = hueDist(h, ref.hue);
4047
+ if (d < bestDist) {
4048
+ bestDist = d;
4049
+ best = ref.value;
4050
+ }
4051
+ }
4052
+ return best;
4053
+ }
4054
+ function nearestTextColorValue(rgb) {
4055
+ return paletteValueFromRgb(rgb);
4056
+ }
4057
+ function nearestBackgroundColorValue(rgb) {
4058
+ const [, s, l] = rgbToHsl(rgb);
4059
+ if (s < 0.12 && l > 0.85) return "default";
4060
+ return paletteValueFromRgb(rgb);
4061
+ }
4062
+ function parseStyle(style) {
4063
+ const out = {};
4064
+ style.split(";").forEach((decl) => {
4065
+ const idx = decl.indexOf(":");
4066
+ if (idx === -1) return;
4067
+ const k = decl.slice(0, idx).trim().toLowerCase();
4068
+ const v = decl.slice(idx + 1).trim();
4069
+ if (k) out[k] = v;
4070
+ });
4071
+ return out;
4072
+ }
4073
+ function colorFromBackgroundShorthand(value) {
4074
+ if (!value) return null;
4075
+ const direct = parseCssColorToRgb(value);
4076
+ if (direct) return direct;
4077
+ for (const token of value.split(/\s+/)) {
4078
+ const rgb = parseCssColorToRgb(token);
4079
+ if (rgb) return rgb;
4080
+ }
4081
+ return null;
4082
+ }
4083
+ function isTransparentColor(css) {
4084
+ if (!css) return true;
4085
+ const s = css.trim().toLowerCase();
4086
+ if (s === "transparent" || s === "none") return true;
4087
+ const m = s.match(/^rgba?\(([^)]+)\)$/);
4088
+ if (m) {
4089
+ const p = m[1].split(",").map((x) => parseFloat(x.trim()));
4090
+ if (p.length >= 4 && p[3] === 0) return true;
4091
+ }
4092
+ return false;
4093
+ }
4094
+ function normalizeAlign(ta) {
4095
+ const v = (ta || "").trim().toLowerCase();
4096
+ if (v === "right" || v === "end") return "right";
4097
+ if (v === "center") return "center";
4098
+ if (v === "justify") return "justify";
4099
+ return "";
4100
+ }
4101
+ function applyCellFormatting(el, fmt) {
4102
+ if (fmt.bgRgb && !fmt.bgTransparent) {
4103
+ const v = nearestBackgroundColorValue(fmt.bgRgb);
4104
+ if (v !== "default") el.setAttribute("data-background-color", v);
4105
+ }
4106
+ if (fmt.colorRgb) {
4107
+ const v = nearestTextColorValue(fmt.colorRgb);
4108
+ if (v !== "default") el.setAttribute("data-text-color", v);
4109
+ }
4110
+ if (["right", "center", "justify"].includes(fmt.align)) {
4111
+ el.setAttribute("data-text-alignment", fmt.align);
4112
+ }
4113
+ if ((fmt.bold || fmt.italic || fmt.underline) && el.innerHTML.trim()) {
4114
+ let inner = el.innerHTML;
4115
+ if (fmt.underline) inner = `<u>${inner}</u>`;
4116
+ if (fmt.italic) inner = `<em>${inner}</em>`;
4117
+ if (fmt.bold) inner = `<strong>${inner}</strong>`;
4118
+ el.innerHTML = inner;
4119
+ }
4120
+ }
4121
+ function readComputedFormat(el) {
4122
+ const cs = getComputedStyle(el);
4123
+ const fw = cs.fontWeight;
4124
+ const fwNum = parseInt(fw, 10);
4125
+ const decoration = cs.textDecorationLine || cs.textDecoration || "";
4126
+ return {
4127
+ bgRgb: parseCssColorToRgb(cs.backgroundColor),
4128
+ bgTransparent: isTransparentColor(cs.backgroundColor),
4129
+ colorRgb: parseCssColorToRgb(cs.color),
4130
+ align: normalizeAlign(cs.textAlign),
4131
+ bold: fw === "bold" || fw === "bolder" || !isNaN(fwNum) && fwNum >= 600,
4132
+ italic: (cs.fontStyle || "").toLowerCase().includes("italic"),
4133
+ underline: decoration.toLowerCase().includes("underline")
4134
+ };
4135
+ }
4136
+ function readInlineFormat(el) {
4137
+ const sm = parseStyle(el.getAttribute("style") || "");
4138
+ const bgRaw = sm["background-color"] || sm["background"] || "";
4139
+ const bgRgb = colorFromBackgroundShorthand(bgRaw) || parseCssColorToRgb(el.getAttribute("bgcolor"));
4140
+ const fw = (sm["font-weight"] || "").toLowerCase();
4141
+ const decoration = sm["text-decoration"] || sm["text-decoration-line"] || "";
4142
+ return {
4143
+ bgRgb,
4144
+ bgTransparent: !bgRaw && !el.getAttribute("bgcolor"),
4145
+ colorRgb: parseCssColorToRgb(sm["color"]),
4146
+ align: normalizeAlign(sm["text-align"] || el.getAttribute("align")),
4147
+ bold: fw === "bold" || fw === "bolder" || parseInt(fw, 10) >= 600,
4148
+ italic: (sm["font-style"] || "").toLowerCase().includes("italic"),
4149
+ underline: decoration.toLowerCase().includes("underline")
4150
+ };
4151
+ }
4152
+ function normalizeExcelTableHtml(html) {
4153
+ if (!html || typeof DOMParser === "undefined") return html;
4154
+ try {
4155
+ const doc = new DOMParser().parseFromString(html, "text/html");
4156
+ doc.querySelectorAll("script").forEach((s) => s.remove());
4157
+ if (!doc.querySelector("table")) return html;
4158
+ if (typeof document !== "undefined" && document.body && typeof HTMLElement !== "undefined" && typeof HTMLElement.prototype.attachShadow === "function") {
4159
+ let host = null;
4160
+ try {
4161
+ host = document.createElement("div");
4162
+ host.setAttribute("aria-hidden", "true");
4163
+ host.style.cssText = "position:absolute;left:-99999px;top:0;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none";
4164
+ const shadow = host.attachShadow({ mode: "open" });
4165
+ const styles = Array.from(doc.querySelectorAll("style")).map((s) => s.outerHTML).join("");
4166
+ shadow.innerHTML = styles + doc.body.innerHTML;
4167
+ document.body.appendChild(host);
4168
+ shadow.querySelectorAll("td, th").forEach((node) => {
4169
+ applyCellFormatting(node, readComputedFormat(node));
4170
+ });
4171
+ const out = Array.from(shadow.querySelectorAll("table")).map((t) => t.outerHTML).join("");
4172
+ return out || html;
4173
+ } finally {
4174
+ if (host && host.parentNode) host.parentNode.removeChild(host);
4175
+ }
4176
+ }
4177
+ doc.querySelectorAll("td, th").forEach((node) => {
4178
+ applyCellFormatting(node, readInlineFormat(node));
4179
+ });
4180
+ return Array.from(doc.querySelectorAll("table")).map((t) => t.outerHTML).join("") || html;
4181
+ } catch {
4182
+ return html;
4183
+ }
4184
+ }
4185
+
3227
4186
  // src/constants/limits.ts
3228
4187
  var MAX_FILE_SIZE = 10 * 1024 * 1024;
3229
4188
  var MAX_VIDEO_FILE_SIZE = 100 * 1024 * 1024;
@@ -3243,7 +4202,7 @@ var ALLOWED_VIDEO_EXTENSIONS = [
3243
4202
  ];
3244
4203
 
3245
4204
  // src/components/LumirEditor.tsx
3246
- var import_jsx_runtime20 = require("react/jsx-runtime");
4205
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3247
4206
  var DEBUG_LOG = (loc, msg, data) => {
3248
4207
  const p = fetch("http://127.0.0.1:7686/ingest/1f8ee1c5-0cf0-4ae7-91ed-5ea7ed17130a", {
3249
4208
  method: "POST",
@@ -3468,9 +4427,9 @@ var findBlockWithLink = (blocks, targetUrl) => {
3468
4427
  return null;
3469
4428
  };
3470
4429
  var ConvertToPreviewButton = ({ url }) => {
3471
- const editor = (0, import_react24.useBlockNoteEditor)();
3472
- const Components = (0, import_react24.useComponentsContext)();
3473
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4430
+ const editor = (0, import_react31.useBlockNoteEditor)();
4431
+ const Components = (0, import_react31.useComponentsContext)();
4432
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3474
4433
  Components.LinkToolbar.Button,
3475
4434
  {
3476
4435
  className: "bn-button",
@@ -3489,29 +4448,29 @@ var ConvertToPreviewButton = ({ url }) => {
3489
4448
  console.error("Convert to link preview failed:", err);
3490
4449
  }
3491
4450
  },
3492
- icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
3493
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("rect", { x: "1", y: "3", width: "14", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }),
3494
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("line", { x1: "1", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "1.5" }),
3495
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: "5", cy: "6.5", r: "1.5", stroke: "currentColor", strokeWidth: "1", fill: "none" })
4451
+ icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
4452
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "1", y: "3", width: "14", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }),
4453
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("line", { x1: "1", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "1.5" }),
4454
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "5", cy: "6.5", r: "1.5", stroke: "currentColor", strokeWidth: "1", fill: "none" })
3496
4455
  ] })
3497
4456
  }
3498
4457
  );
3499
4458
  };
3500
4459
  var CustomLinkToolbar = (props) => {
3501
- const editor = (0, import_react24.useBlockNoteEditor)();
3502
- const Components = (0, import_react24.useComponentsContext)();
4460
+ const editor = (0, import_react31.useBlockNoteEditor)();
4461
+ const Components = (0, import_react31.useComponentsContext)();
3503
4462
  const hasLinkPreview = !!editor?._linkPreviewApiEndpoint;
3504
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4463
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3505
4464
  Components.LinkToolbar.Root,
3506
4465
  {
3507
4466
  className: "bn-toolbar bn-link-toolbar",
3508
4467
  onMouseEnter: props.stopHideTimer,
3509
4468
  onMouseLeave: props.startHideTimer,
3510
4469
  children: [
3511
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.EditLinkButton, { url: props.url, text: props.text, editLink: props.editLink }),
3512
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.OpenLinkButton, { url: props.url }),
3513
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.DeleteLinkButton, { deleteLink: props.deleteLink }),
3514
- hasLinkPreview && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ConvertToPreviewButton, { url: props.url })
4470
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react31.EditLinkButton, { url: props.url, text: props.text, editLink: props.editLink }),
4471
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react31.OpenLinkButton, { url: props.url }),
4472
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react31.DeleteLinkButton, { deleteLink: props.deleteLink }),
4473
+ hasLinkPreview && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ConvertToPreviewButton, { url: props.url })
3515
4474
  ]
3516
4475
  }
3517
4476
  );
@@ -3555,13 +4514,13 @@ function LumirEditor({
3555
4514
  onError,
3556
4515
  onImageDelete
3557
4516
  }) {
3558
- const [isUploading, setIsUploading] = (0, import_react23.useState)(false);
3559
- const [uploadProgress, setUploadProgress] = (0, import_react23.useState)(null);
3560
- const [errorMessage, setErrorMessage] = (0, import_react23.useState)(null);
3561
- const floatingMenuFileInputRef = (0, import_react23.useRef)(null);
3562
- const floatingMenuBlockRef = (0, import_react23.useRef)(null);
3563
- const floatingMenuUploadStartTimeRef = (0, import_react23.useRef)(0);
3564
- const handleError = (0, import_react23.useCallback)(
4517
+ const [isUploading, setIsUploading] = (0, import_react30.useState)(false);
4518
+ const [uploadProgress, setUploadProgress] = (0, import_react30.useState)(null);
4519
+ const [errorMessage, setErrorMessage] = (0, import_react30.useState)(null);
4520
+ const floatingMenuFileInputRef = (0, import_react30.useRef)(null);
4521
+ const floatingMenuBlockRef = (0, import_react30.useRef)(null);
4522
+ const floatingMenuUploadStartTimeRef = (0, import_react30.useRef)(0);
4523
+ const handleError = (0, import_react30.useCallback)(
3565
4524
  (error) => {
3566
4525
  onError?.(error);
3567
4526
  setErrorMessage(error.getUserMessage());
@@ -3569,10 +4528,10 @@ function LumirEditor({
3569
4528
  },
3570
4529
  [onError]
3571
4530
  );
3572
- const validatedContent = (0, import_react23.useMemo)(() => {
4531
+ const validatedContent = (0, import_react30.useMemo)(() => {
3573
4532
  return ContentUtils.validateContent(initialContent, initialEmptyBlocks);
3574
4533
  }, [initialContent, initialEmptyBlocks]);
3575
- const tableConfig = (0, import_react23.useMemo)(() => {
4534
+ const tableConfig = (0, import_react30.useMemo)(() => {
3576
4535
  return EditorConfig.getDefaultTableConfig(tables);
3577
4536
  }, [
3578
4537
  tables?.splitCells,
@@ -3580,10 +4539,10 @@ function LumirEditor({
3580
4539
  tables?.cellTextColor,
3581
4540
  tables?.headers
3582
4541
  ]);
3583
- const headingConfig = (0, import_react23.useMemo)(() => {
4542
+ const headingConfig = (0, import_react30.useMemo)(() => {
3584
4543
  return EditorConfig.getDefaultHeadingConfig(heading);
3585
4544
  }, [heading?.levels?.join(",") ?? ""]);
3586
- const disabledExtensions = (0, import_react23.useMemo)(() => {
4545
+ const disabledExtensions = (0, import_react30.useMemo)(() => {
3587
4546
  return EditorConfig.getDisabledExtensions(
3588
4547
  disableExtensions,
3589
4548
  allowVideoUpload,
@@ -3591,18 +4550,18 @@ function LumirEditor({
3591
4550
  allowFileUpload
3592
4551
  );
3593
4552
  }, [disableExtensions, allowVideoUpload, allowAudioUpload, allowFileUpload]);
3594
- (0, import_react23.useEffect)(() => {
4553
+ (0, import_react30.useEffect)(() => {
3595
4554
  DEBUG_LOG("LumirEditor:init:disabledExtensions", "snapshot", {
3596
4555
  allowVideoUpload,
3597
4556
  hasVideoInDisabled: disabledExtensions.includes("video"),
3598
4557
  disabledList: disabledExtensions.slice(0, 15)
3599
4558
  });
3600
4559
  }, [allowVideoUpload, disabledExtensions]);
3601
- const fileNameTransformRef = (0, import_react23.useRef)(s3Upload?.fileNameTransform);
3602
- (0, import_react23.useEffect)(() => {
4560
+ const fileNameTransformRef = (0, import_react30.useRef)(s3Upload?.fileNameTransform);
4561
+ (0, import_react30.useEffect)(() => {
3603
4562
  fileNameTransformRef.current = s3Upload?.fileNameTransform;
3604
4563
  }, [s3Upload?.fileNameTransform]);
3605
- const memoizedS3Upload = (0, import_react23.useMemo)(() => {
4564
+ const memoizedS3Upload = (0, import_react30.useMemo)(() => {
3606
4565
  if (!s3Upload) return void 0;
3607
4566
  return {
3608
4567
  apiEndpoint: s3Upload.apiEndpoint,
@@ -3631,10 +4590,19 @@ function LumirEditor({
3631
4590
  s3Upload?.maxRetries,
3632
4591
  s3Upload?.onProgress
3633
4592
  ]);
3634
- const editor = (0, import_react24.useCreateBlockNote)(
4593
+ const editor = (0, import_react31.useCreateBlockNote)(
3635
4594
  {
3636
4595
  // HTML 미리보기 블록이 포함된 커스텀 스키마 사용
3637
4596
  schema,
4597
+ // 모든 BlockNote UI 텍스트(테이블 드롭다운 등) 한글 적용 + "색깔"→"색"
4598
+ dictionary: {
4599
+ ...import_locales.ko,
4600
+ drag_handle: { ...import_locales.ko.drag_handle, colors_menuitem: "\uC0C9" },
4601
+ formatting_toolbar: {
4602
+ ...import_locales.ko.formatting_toolbar,
4603
+ colors: { ...import_locales.ko.formatting_toolbar.colors, tooltip: "\uC0C9" }
4604
+ }
4605
+ },
3638
4606
  initialContent: validatedContent,
3639
4607
  tables: tableConfig,
3640
4608
  heading: headingConfig,
@@ -3742,6 +4710,16 @@ function LumirEditor({
3742
4710
  return true;
3743
4711
  }
3744
4712
  }
4713
+ const pastedHtml = event?.clipboardData?.getData?.("text/html") || "";
4714
+ if (/<table[\s>]/i.test(pastedHtml)) {
4715
+ DEBUG_LOG("paste:step0:table", "table HTML detected, using pasteHTML", {
4716
+ htmlLen: pastedHtml.length,
4717
+ hasFiles: !!event?.clipboardData?.files?.length
4718
+ });
4719
+ event.preventDefault();
4720
+ editor2.pasteHTML(normalizeExcelTableHtml(pastedHtml));
4721
+ return true;
4722
+ }
3745
4723
  const fileList = event?.clipboardData?.files ?? null;
3746
4724
  const files = fileList ? Array.from(fileList) : [];
3747
4725
  const acceptedFiles = files.filter(
@@ -3816,12 +4794,12 @@ function LumirEditor({
3816
4794
  if (editor && linkPreview?.apiEndpoint) {
3817
4795
  editor._linkPreviewApiEndpoint = linkPreview.apiEndpoint;
3818
4796
  }
3819
- (0, import_react23.useEffect)(() => {
4797
+ (0, import_react30.useEffect)(() => {
3820
4798
  if (editor) {
3821
4799
  editor.isEditable = editable;
3822
4800
  }
3823
4801
  }, [editor, editable]);
3824
- (0, import_react23.useEffect)(() => {
4802
+ (0, import_react30.useEffect)(() => {
3825
4803
  if (!editor || !onContentChange) return;
3826
4804
  const handleContentChange = () => {
3827
4805
  const blocks = editor.topLevelBlocks;
@@ -3830,13 +4808,13 @@ function LumirEditor({
3830
4808
  };
3831
4809
  return editor.onEditorContentChange(handleContentChange);
3832
4810
  }, [editor, onContentChange]);
3833
- const previousMediaUrlsRef = (0, import_react23.useRef)(/* @__PURE__ */ new Set());
3834
- (0, import_react23.useEffect)(() => {
4811
+ const previousMediaUrlsRef = (0, import_react30.useRef)(/* @__PURE__ */ new Set());
4812
+ (0, import_react30.useEffect)(() => {
3835
4813
  if (!editor) return;
3836
4814
  const initialBlocks = editor.topLevelBlocks;
3837
4815
  previousMediaUrlsRef.current = extractMediaUrls(initialBlocks);
3838
4816
  }, [editor]);
3839
- (0, import_react23.useEffect)(() => {
4817
+ (0, import_react30.useEffect)(() => {
3840
4818
  if (!editor || !onImageDelete) return;
3841
4819
  const handleMediaDeleteCheck = () => {
3842
4820
  const currentBlocks = editor.topLevelBlocks;
@@ -3850,7 +4828,7 @@ function LumirEditor({
3850
4828
  };
3851
4829
  return editor.onEditorContentChange(handleMediaDeleteCheck);
3852
4830
  }, [editor, onImageDelete]);
3853
- (0, import_react23.useEffect)(() => {
4831
+ (0, import_react30.useEffect)(() => {
3854
4832
  const el = editor?.domElement;
3855
4833
  if (!el) return;
3856
4834
  const handleDragOver = (e) => {
@@ -3981,20 +4959,20 @@ function LumirEditor({
3981
4959
  el.removeEventListener("drop", handleDrop, { capture: true });
3982
4960
  };
3983
4961
  }, [editor, allowVideoUpload]);
3984
- const computedSideMenu = (0, import_react23.useMemo)(() => {
4962
+ const computedSideMenu = (0, import_react30.useMemo)(() => {
3985
4963
  return sideMenuAddButton ? sideMenu : false;
3986
4964
  }, [sideMenuAddButton, sideMenu]);
3987
- const DragHandleOnlySideMenu = (0, import_react23.useMemo)(() => {
3988
- return (props) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.SideMenu, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.DragHandleButton, { ...props }) });
4965
+ const DragHandleOnlySideMenu = (0, import_react30.useMemo)(() => {
4966
+ return (props) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react31.SideMenu, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react31.DragHandleButton, { ...props }) });
3989
4967
  }, []);
3990
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4968
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3991
4969
  "div",
3992
4970
  {
3993
4971
  className: cn("lumirEditor", className),
3994
4972
  style: { position: "relative", display: "flex", flexDirection: "column" },
3995
4973
  children: [
3996
- floatingMenu && editor && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
3997
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4974
+ floatingMenu && editor && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
4975
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3998
4976
  "input",
3999
4977
  {
4000
4978
  ref: floatingMenuFileInputRef,
@@ -4065,7 +5043,7 @@ function LumirEditor({
4065
5043
  }
4066
5044
  }
4067
5045
  ),
4068
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5046
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4069
5047
  FloatingMenu,
4070
5048
  {
4071
5049
  editor,
@@ -4097,7 +5075,7 @@ function LumirEditor({
4097
5075
  }
4098
5076
  )
4099
5077
  ] }),
4100
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5078
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4101
5079
  import_mantine.BlockNoteView,
4102
5080
  {
4103
5081
  editor,
@@ -4109,23 +5087,24 @@ function LumirEditor({
4109
5087
  slashMenu: false,
4110
5088
  emojiPicker,
4111
5089
  filePanel,
4112
- tableHandles,
5090
+ tableHandles: false,
4113
5091
  onSelectionChange,
4114
5092
  children: [
4115
- formattingToolbar && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4116
- import_react24.FormattingToolbarController,
5093
+ tableHandles && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(LumirTableHandlesController, {}),
5094
+ formattingToolbar && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5095
+ import_react31.FormattingToolbarController,
4117
5096
  {
4118
5097
  formattingToolbar: CustomFormattingToolbar
4119
5098
  }
4120
5099
  ),
4121
- linkToolbar && (linkPreview?.apiEndpoint ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.LinkToolbarController, { linkToolbar: CustomLinkToolbar }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.LinkToolbarController, {})),
4122
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4123
- import_react24.SuggestionMenuController,
5100
+ linkToolbar && (linkPreview?.apiEndpoint ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react31.LinkToolbarController, { linkToolbar: CustomLinkToolbar }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react31.LinkToolbarController, {})),
5101
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5102
+ import_react31.SuggestionMenuController,
4124
5103
  {
4125
5104
  triggerCharacter: "/",
4126
- getItems: (0, import_react23.useCallback)(
5105
+ getItems: (0, import_react30.useCallback)(
4127
5106
  async (query) => {
4128
- const items = (0, import_react24.getDefaultReactSlashMenuItems)(editor);
5107
+ const items = (0, import_react31.getDefaultReactSlashMenuItems)(editor);
4129
5108
  const filtered = items.filter((item) => {
4130
5109
  const key = (item?.key || "").toString().toLowerCase();
4131
5110
  const title = (item?.title || "").toString().toLowerCase();
@@ -4167,7 +5146,7 @@ function LumirEditor({
4167
5146
  },
4168
5147
  aliases: ["html", "preview", "\uC6F9", "\uC6F9\uD398\uC774\uC9C0"],
4169
5148
  group: "Embeds",
4170
- icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5149
+ icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4171
5150
  "svg",
4172
5151
  {
4173
5152
  width: "18",
@@ -4179,8 +5158,8 @@ function LumirEditor({
4179
5158
  strokeLinecap: "round",
4180
5159
  strokeLinejoin: "round",
4181
5160
  children: [
4182
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("polyline", { points: "16 18 22 12 16 6" }),
4183
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("polyline", { points: "8 6 2 12 8 18" })
5161
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("polyline", { points: "16 18 22 12 16 6" }),
5162
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("polyline", { points: "8 6 2 12 8 18" })
4184
5163
  ]
4185
5164
  }
4186
5165
  ),
@@ -4191,7 +5170,7 @@ function LumirEditor({
4191
5170
  allItems.push({
4192
5171
  title: "Link Preview",
4193
5172
  onItemClick: () => {
4194
- (0, import_core4.insertOrUpdateBlock)(editor, {
5173
+ (0, import_core5.insertOrUpdateBlock)(editor, {
4195
5174
  type: "linkPreview",
4196
5175
  props: { url: "" }
4197
5176
  });
@@ -4205,7 +5184,7 @@ function LumirEditor({
4205
5184
  "\uD504\uB9AC\uBDF0"
4206
5185
  ],
4207
5186
  group: "Embeds",
4208
- icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5187
+ icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4209
5188
  "svg",
4210
5189
  {
4211
5190
  width: "18",
@@ -4217,14 +5196,31 @@ function LumirEditor({
4217
5196
  strokeLinecap: "round",
4218
5197
  strokeLinejoin: "round",
4219
5198
  children: [
4220
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
4221
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
5199
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
5200
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
4222
5201
  ]
4223
5202
  }
4224
5203
  ),
4225
5204
  subtext: "URL\uC758 \uBBF8\uB9AC\uBCF4\uAE30 \uCE74\uB4DC\uB97C \uC0BD\uC785"
4226
5205
  });
4227
5206
  }
5207
+ const enSlash = import_locales.en.slash_menu;
5208
+ for (const it of allItems) {
5209
+ const enEntry = it.key ? enSlash[it.key] : void 0;
5210
+ if (!enEntry) continue;
5211
+ const extra = [...enEntry.aliases ?? [], enEntry.title].filter((s) => Boolean(s)).map((s) => s.toLowerCase());
5212
+ it.aliases = Array.from(
5213
+ /* @__PURE__ */ new Set([...it.aliases ?? [], ...extra])
5214
+ );
5215
+ }
5216
+ const groupOrder = [];
5217
+ for (const it of allItems) {
5218
+ const g = it.group ?? "";
5219
+ if (!groupOrder.includes(g)) groupOrder.push(g);
5220
+ }
5221
+ allItems.sort(
5222
+ (a, b) => groupOrder.indexOf(a.group ?? "") - groupOrder.indexOf(b.group ?? "")
5223
+ );
4228
5224
  if (!query) return allItems;
4229
5225
  const q = query.toLowerCase();
4230
5226
  return allItems.filter(
@@ -4237,21 +5233,21 @@ function LumirEditor({
4237
5233
  )
4238
5234
  }
4239
5235
  ),
4240
- !sideMenuAddButton && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react24.SideMenuController, { sideMenu: DragHandleOnlySideMenu })
5236
+ !sideMenuAddButton && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react31.SideMenuController, { sideMenu: DragHandleOnlySideMenu })
4241
5237
  ]
4242
5238
  }
4243
5239
  ),
4244
- isUploading && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "lumirEditor-upload-overlay", children: [
4245
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "lumirEditor-spinner" }),
4246
- uploadProgress !== null && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "lumirEditor-upload-progress", children: [
5240
+ isUploading && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "lumirEditor-upload-overlay", children: [
5241
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "lumirEditor-spinner" }),
5242
+ uploadProgress !== null && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "lumirEditor-upload-progress", children: [
4247
5243
  uploadProgress,
4248
5244
  "%"
4249
5245
  ] })
4250
5246
  ] }),
4251
- errorMessage && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "lumirEditor-error-toast", children: [
4252
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "lumirEditor-error-icon", children: "\u26A0\uFE0F" }),
4253
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "lumirEditor-error-message", children: errorMessage }),
4254
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5247
+ errorMessage && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "lumirEditor-error-toast", children: [
5248
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "lumirEditor-error-icon", children: "\u26A0\uFE0F" }),
5249
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "lumirEditor-error-message", children: errorMessage }),
5250
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4255
5251
  "button",
4256
5252
  {
4257
5253
  className: "lumirEditor-error-close",