@lumir-company/editor 0.4.14 → 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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/components/LumirEditor.tsx
4
- import { useEffect as useEffect8, useMemo as useMemo3, useCallback as useCallback16, useState as useState8, useRef as useRef9 } from "react";
4
+ import { useEffect as useEffect10, useMemo as useMemo6, useCallback as useCallback18, useState as useState10, useRef as useRef11 } from "react";
5
5
  import {
6
6
  useCreateBlockNote,
7
7
  SideMenu as BlockSideMenu,
@@ -11,14 +11,15 @@ import {
11
11
  getDefaultReactSlashMenuItems,
12
12
  LinkToolbarController,
13
13
  FormattingToolbarController,
14
- useBlockNoteEditor as useBlockNoteEditor3,
15
- useComponentsContext as useComponentsContext3,
14
+ useBlockNoteEditor as useBlockNoteEditor5,
15
+ useComponentsContext as useComponentsContext4,
16
16
  EditLinkButton,
17
17
  OpenLinkButton,
18
18
  DeleteLinkButton
19
19
  } from "@blocknote/react";
20
20
  import { BlockNoteView } from "@blocknote/mantine";
21
21
  import { insertOrUpdateBlock } from "@blocknote/core";
22
+ import { ko, en } from "@blocknote/core/locales";
22
23
 
23
24
  // src/utils/cn.ts
24
25
  function cn(...inputs) {
@@ -1814,6 +1815,66 @@ var TextStyleButton = ({
1814
1815
 
1815
1816
  // src/components/FloatingMenu/components/AlignButton.tsx
1816
1817
  import { useCallback as useCallback6 } from "react";
1818
+
1819
+ // src/utils/prosemirror-table-utils.ts
1820
+ function getSelectedCellPositions(editor) {
1821
+ const tiptap = editor._tiptapEditor;
1822
+ if (!tiptap) return [];
1823
+ const { state } = tiptap;
1824
+ const { selection } = state;
1825
+ if (typeof selection.forEachCell === "function") {
1826
+ const positions = [];
1827
+ selection.forEachCell((_node, pos) => {
1828
+ positions.push(pos);
1829
+ });
1830
+ return positions;
1831
+ }
1832
+ const $pos = selection.$from;
1833
+ for (let depth = $pos.depth; depth > 0; depth--) {
1834
+ const node = $pos.node(depth);
1835
+ if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
1836
+ return [$pos.before(depth)];
1837
+ }
1838
+ }
1839
+ return [];
1840
+ }
1841
+ function setCellAttrAtPositions(editor, positions, attr, value) {
1842
+ const tiptap = editor?._tiptapEditor;
1843
+ if (!tiptap || positions.length === 0) return false;
1844
+ let tr = tiptap.state.tr;
1845
+ let changed = false;
1846
+ for (const pos of positions) {
1847
+ const node = tr.doc.nodeAt(pos);
1848
+ if (node && (node.type.name === "tableCell" || node.type.name === "tableHeader")) {
1849
+ tr = tr.setNodeMarkup(pos, void 0, { ...node.attrs, [attr]: value });
1850
+ changed = true;
1851
+ }
1852
+ }
1853
+ if (changed) {
1854
+ tiptap.view?.dispatch(tr);
1855
+ }
1856
+ return changed;
1857
+ }
1858
+ function setSelectedCellsAttr(editor, attr, value) {
1859
+ return setCellAttrAtPositions(
1860
+ editor,
1861
+ getSelectedCellPositions(editor),
1862
+ attr,
1863
+ value
1864
+ );
1865
+ }
1866
+ function isInTableCell(editor) {
1867
+ return getSelectedCellPositions(editor).length > 0;
1868
+ }
1869
+ function getFirstSelectedCellAttr(editor, attr) {
1870
+ const tiptap = editor?._tiptapEditor;
1871
+ const positions = getSelectedCellPositions(editor);
1872
+ if (!tiptap || positions.length === 0) return void 0;
1873
+ const node = tiptap.state.doc.nodeAt(positions[0]);
1874
+ return node?.attrs?.[attr];
1875
+ }
1876
+
1877
+ // src/components/FloatingMenu/components/AlignButton.tsx
1817
1878
  import { jsx as jsx8 } from "react/jsx-runtime";
1818
1879
  var iconMap2 = {
1819
1880
  left: Icons.alignLeft,
@@ -1831,6 +1892,9 @@ var AlignButton = ({
1831
1892
  }) => {
1832
1893
  const getCurrentAlignment = () => {
1833
1894
  try {
1895
+ if (isInTableCell(editor)) {
1896
+ return getFirstSelectedCellAttr(editor, "textAlignment") || "left";
1897
+ }
1834
1898
  const block = editor?.getTextCursorPosition()?.block;
1835
1899
  return block?.props?.textAlignment || "left";
1836
1900
  } catch {
@@ -1840,6 +1904,9 @@ var AlignButton = ({
1840
1904
  const isActive = getCurrentAlignment() === alignment;
1841
1905
  const handleClick = useCallback6(() => {
1842
1906
  try {
1907
+ if (setSelectedCellsAttr(editor, "textAlignment", alignment)) {
1908
+ return;
1909
+ }
1843
1910
  const block = editor?.getTextCursorPosition()?.block;
1844
1911
  if (block && editor?.updateBlock) {
1845
1912
  editor.updateBlock(block, { props: { textAlignment: alignment } });
@@ -2005,6 +2072,10 @@ var ColorButton = ({ editor, type }) => {
2005
2072
  const colors = type === "text" ? TEXT_COLORS : BACKGROUND_COLORS;
2006
2073
  const getCurrentColor = useCallback9(() => {
2007
2074
  try {
2075
+ if (isInTableCell(editor)) {
2076
+ const attr = type === "text" ? "textColor" : "backgroundColor";
2077
+ return getFirstSelectedCellAttr(editor, attr) || "default";
2078
+ }
2008
2079
  const activeStyles = editor?.getActiveStyles?.() || {};
2009
2080
  if (type === "text" && activeStyles.textColor) {
2010
2081
  return activeStyles.textColor;
@@ -2034,13 +2105,15 @@ var ColorButton = ({ editor, type }) => {
2034
2105
  (color) => {
2035
2106
  try {
2036
2107
  if (!editor) return;
2037
- if (type === "text") {
2038
- editor.toggleStyles({ textColor: color });
2039
- } else {
2040
- editor.toggleStyles({ backgroundColor: color });
2108
+ const attr = type === "text" ? "textColor" : "backgroundColor";
2109
+ if (!setSelectedCellsAttr(editor, attr, color)) {
2110
+ editor.toggleStyles(
2111
+ type === "text" ? { textColor: color } : { backgroundColor: color }
2112
+ );
2041
2113
  }
2042
2114
  setCurrentColor(color);
2043
2115
  setIsOpen(false);
2116
+ setTimeout(() => editor.focus?.());
2044
2117
  } catch (err) {
2045
2118
  console.error(`Color apply failed:`, err);
2046
2119
  }
@@ -2847,7 +2920,6 @@ var VerticalAlignmentExtension = Extension.create({
2847
2920
  import {
2848
2921
  BasicTextStyleButton,
2849
2922
  BlockTypeSelect as BlockTypeSelect2,
2850
- ColorStyleButton,
2851
2923
  CreateLinkButton,
2852
2924
  FileCaptionButton,
2853
2925
  FileDeleteButton,
@@ -2873,31 +2945,6 @@ import {
2873
2945
  useBlockNoteEditor,
2874
2946
  useSelectedBlocks
2875
2947
  } from "@blocknote/react";
2876
-
2877
- // src/utils/prosemirror-table-utils.ts
2878
- function getSelectedCellPositions(editor) {
2879
- const tiptap = editor._tiptapEditor;
2880
- if (!tiptap) return [];
2881
- const { state } = tiptap;
2882
- const { selection } = state;
2883
- if (typeof selection.forEachCell === "function") {
2884
- const positions = [];
2885
- selection.forEachCell((_node, pos) => {
2886
- positions.push(pos);
2887
- });
2888
- return positions;
2889
- }
2890
- const $pos = selection.$from;
2891
- for (let depth = $pos.depth; depth > 0; depth--) {
2892
- const node = $pos.node(depth);
2893
- if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
2894
- return [$pos.before(depth)];
2895
- }
2896
- }
2897
- return [];
2898
- }
2899
-
2900
- // src/components/TextAlignButtonWithVA.tsx
2901
2948
  import { jsx as jsx17 } from "react/jsx-runtime";
2902
2949
  var icons = {
2903
2950
  left: /* @__PURE__ */ jsx17("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", width: "18", height: "18", children: /* @__PURE__ */ jsx17("path", { d: "M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z" }) }),
@@ -3078,77 +3125,782 @@ var VerticalAlignButton = (props) => {
3078
3125
  );
3079
3126
  };
3080
3127
 
3128
+ // src/components/color/LumirColorControls.tsx
3129
+ import {
3130
+ isTableCell,
3131
+ mapTableCell as mapTableCell2
3132
+ } from "@blocknote/core";
3133
+ import {
3134
+ SplitButton,
3135
+ useBlockNoteEditor as useBlockNoteEditor3,
3136
+ useComponentsContext as useComponentsContext3,
3137
+ useDictionary,
3138
+ useEditorContentOrSelectionChange,
3139
+ useSelectedBlocks as useSelectedBlocks3
3140
+ } from "@blocknote/react";
3141
+ import { useCallback as useCallback16, useMemo as useMemo3, useRef as useRef9, useState as useState8 } from "react";
3142
+ import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
3143
+ var COLORS = [
3144
+ "default",
3145
+ "gray",
3146
+ "brown",
3147
+ "red",
3148
+ "orange",
3149
+ "yellow",
3150
+ "green",
3151
+ "blue",
3152
+ "purple",
3153
+ "pink"
3154
+ ];
3155
+ function ColorIcon(props) {
3156
+ const textColor = props.textColor || "default";
3157
+ const backgroundColor = props.backgroundColor || "default";
3158
+ const size = props.size || 16;
3159
+ return /* @__PURE__ */ jsx19(
3160
+ "div",
3161
+ {
3162
+ className: "bn-color-icon",
3163
+ "data-background-color": backgroundColor,
3164
+ "data-text-color": textColor,
3165
+ style: {
3166
+ pointerEvents: "none",
3167
+ fontSize: `${size * 0.75}px`,
3168
+ height: `${size}px`,
3169
+ lineHeight: `${size}px`,
3170
+ textAlign: "center",
3171
+ width: `${size}px`
3172
+ },
3173
+ children: "A"
3174
+ }
3175
+ );
3176
+ }
3177
+ function CellFillIcon({ size = 18 }) {
3178
+ return /* @__PURE__ */ jsx19(
3179
+ "svg",
3180
+ {
3181
+ width: size,
3182
+ height: size,
3183
+ viewBox: "0 0 24 24",
3184
+ fill: "currentColor",
3185
+ style: { pointerEvents: "none" },
3186
+ "aria-hidden": "true",
3187
+ children: /* @__PURE__ */ jsx19("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" })
3188
+ }
3189
+ );
3190
+ }
3191
+ function LumirColorPicker(props) {
3192
+ const Components = useComponentsContext3();
3193
+ const dict = useDictionary();
3194
+ return /* @__PURE__ */ jsxs12(Fragment5, { children: [
3195
+ props.text ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
3196
+ /* @__PURE__ */ jsx19(Components.Generic.Menu.Label, { children: props.textTitle ?? dict.color_picker.text_title }),
3197
+ COLORS.map((color) => /* @__PURE__ */ jsx19(
3198
+ Components.Generic.Menu.Item,
3199
+ {
3200
+ onClick: () => {
3201
+ props.onClick?.();
3202
+ props.text.setColor(color);
3203
+ },
3204
+ "data-test": "text-color-" + color,
3205
+ icon: /* @__PURE__ */ jsx19(ColorIcon, { textColor: color, size: props.iconSize }),
3206
+ checked: props.text.color === color,
3207
+ children: dict.color_picker.colors[color]
3208
+ },
3209
+ "text-color-" + color
3210
+ ))
3211
+ ] }) : null,
3212
+ props.background ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
3213
+ /* @__PURE__ */ jsx19(Components.Generic.Menu.Label, { children: props.backgroundTitle ?? dict.color_picker.background_title }),
3214
+ COLORS.map((color) => /* @__PURE__ */ jsx19(
3215
+ Components.Generic.Menu.Item,
3216
+ {
3217
+ onClick: () => {
3218
+ props.onClick?.();
3219
+ props.background.setColor(color);
3220
+ },
3221
+ "data-test": "background-color-" + color,
3222
+ icon: /* @__PURE__ */ jsx19(ColorIcon, { backgroundColor: color, size: props.iconSize }),
3223
+ checked: props.background.color === color,
3224
+ children: dict.color_picker.colors[color]
3225
+ },
3226
+ "background-color-" + color
3227
+ ))
3228
+ ] }) : null
3229
+ ] });
3230
+ }
3231
+ function LumirColorStyleButton() {
3232
+ const Components = useComponentsContext3();
3233
+ const editor = useBlockNoteEditor3();
3234
+ const ed = editor;
3235
+ const styleSchema = editor.schema.styleSchema;
3236
+ const textColorInSchema = styleSchema.textColor?.type === "textColor" && styleSchema.textColor?.propSchema === "string";
3237
+ const backgroundColorInSchema = styleSchema.backgroundColor?.type === "backgroundColor" && styleSchema.backgroundColor?.propSchema === "string";
3238
+ const selectedBlocks = useSelectedBlocks3(editor);
3239
+ const [currentTextColor, setCurrentTextColor] = useState8(
3240
+ textColorInSchema ? ed.getActiveStyles().textColor || "default" : "default"
3241
+ );
3242
+ const [currentBackgroundColor, setCurrentBackgroundColor] = useState8(
3243
+ backgroundColorInSchema ? ed.getActiveStyles().backgroundColor || "default" : "default"
3244
+ );
3245
+ useEditorContentOrSelectionChange(() => {
3246
+ const active = ed.getActiveStyles();
3247
+ if (textColorInSchema) {
3248
+ setCurrentTextColor(active.textColor || "default");
3249
+ }
3250
+ if (backgroundColorInSchema) {
3251
+ setCurrentBackgroundColor(active.backgroundColor || "default");
3252
+ }
3253
+ }, editor);
3254
+ const setTextColor = useCallback16(
3255
+ (color) => {
3256
+ color === "default" ? ed.removeStyles({ textColor: color }) : ed.addStyles({ textColor: color });
3257
+ setTimeout(() => editor.focus());
3258
+ },
3259
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3260
+ [editor]
3261
+ );
3262
+ const setBackgroundColor = useCallback16(
3263
+ (color) => {
3264
+ color === "default" ? ed.removeStyles({ backgroundColor: color }) : ed.addStyles({ backgroundColor: color });
3265
+ setTimeout(() => editor.focus());
3266
+ },
3267
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3268
+ [editor]
3269
+ );
3270
+ const show = useMemo3(() => {
3271
+ if (!textColorInSchema && !backgroundColorInSchema) {
3272
+ return false;
3273
+ }
3274
+ for (const block of selectedBlocks) {
3275
+ if (block.content !== void 0) {
3276
+ return true;
3277
+ }
3278
+ }
3279
+ return false;
3280
+ }, [backgroundColorInSchema, selectedBlocks, textColorInSchema]);
3281
+ if (!show || !editor.isEditable) {
3282
+ return null;
3283
+ }
3284
+ const tooltip = "\uD14D\uC2A4\uD2B8 \uC0C9\xB7\uBC30\uACBD";
3285
+ return /* @__PURE__ */ jsxs12(Components.Generic.Menu.Root, { children: [
3286
+ /* @__PURE__ */ jsx19(Components.Generic.Menu.Trigger, { children: /* @__PURE__ */ jsx19(
3287
+ Components.FormattingToolbar.Button,
3288
+ {
3289
+ className: "bn-button",
3290
+ "data-test": "colors",
3291
+ label: tooltip,
3292
+ mainTooltip: tooltip,
3293
+ icon: /* @__PURE__ */ jsx19(
3294
+ ColorIcon,
3295
+ {
3296
+ textColor: currentTextColor,
3297
+ backgroundColor: currentBackgroundColor,
3298
+ size: 20
3299
+ }
3300
+ )
3301
+ }
3302
+ ) }),
3303
+ /* @__PURE__ */ jsx19(
3304
+ Components.Generic.Menu.Dropdown,
3305
+ {
3306
+ className: "bn-menu-dropdown bn-color-picker-dropdown",
3307
+ children: /* @__PURE__ */ jsx19(
3308
+ LumirColorPicker,
3309
+ {
3310
+ textTitle: "\uD14D\uC2A4\uD2B8 \uC0C9",
3311
+ backgroundTitle: "\uD14D\uC2A4\uD2B8 \uBC30\uACBD",
3312
+ text: textColorInSchema ? { color: currentTextColor, setColor: setTextColor } : void 0,
3313
+ background: backgroundColorInSchema ? { color: currentBackgroundColor, setColor: setBackgroundColor } : void 0
3314
+ }
3315
+ )
3316
+ }
3317
+ )
3318
+ ] });
3319
+ }
3320
+ function LumirCellColorToolbarButton() {
3321
+ const Components = useComponentsContext3();
3322
+ const editor = useBlockNoteEditor3();
3323
+ const selectedBlocks = useSelectedBlocks3(editor);
3324
+ const isMultiCell = useMemo3(() => {
3325
+ if (selectedBlocks.length !== 1 || selectedBlocks[0].type !== "table") {
3326
+ return false;
3327
+ }
3328
+ const cs = editor.tableHandles?.getCellSelection();
3329
+ return !!cs && cs.cells.length > 1;
3330
+ }, [editor, selectedBlocks]);
3331
+ const stashRef = useRef9([]);
3332
+ const applyBackground = useCallback16(
3333
+ (color) => {
3334
+ const live = getSelectedCellPositions(editor);
3335
+ const positions = live.length > 0 ? live : stashRef.current;
3336
+ setCellAttrAtPositions(editor, positions, "backgroundColor", color);
3337
+ setTimeout(() => editor.focus());
3338
+ },
3339
+ [editor]
3340
+ );
3341
+ if (!editor.isEditable || !isMultiCell) {
3342
+ return null;
3343
+ }
3344
+ const tooltip = "\uC140 \uBC30\uACBD\uC0C9";
3345
+ return /* @__PURE__ */ jsxs12(
3346
+ Components.Generic.Menu.Root,
3347
+ {
3348
+ onOpenChange: (open) => {
3349
+ if (open) {
3350
+ stashRef.current = getSelectedCellPositions(editor);
3351
+ }
3352
+ },
3353
+ children: [
3354
+ /* @__PURE__ */ jsx19(Components.Generic.Menu.Trigger, { children: /* @__PURE__ */ jsx19(
3355
+ Components.FormattingToolbar.Button,
3356
+ {
3357
+ className: "bn-button",
3358
+ "data-test": "cell-colors",
3359
+ label: tooltip,
3360
+ mainTooltip: tooltip,
3361
+ icon: /* @__PURE__ */ jsx19(CellFillIcon, { size: 18 })
3362
+ }
3363
+ ) }),
3364
+ /* @__PURE__ */ jsx19(
3365
+ Components.Generic.Menu.Dropdown,
3366
+ {
3367
+ className: "bn-menu-dropdown bn-color-picker-dropdown",
3368
+ children: /* @__PURE__ */ jsx19(
3369
+ LumirColorPicker,
3370
+ {
3371
+ backgroundTitle: "\uC140 \uBC30\uACBD",
3372
+ background: { color: "default", setColor: applyBackground }
3373
+ }
3374
+ )
3375
+ }
3376
+ )
3377
+ ]
3378
+ }
3379
+ );
3380
+ }
3381
+ function LumirCellColorPickerButton(props) {
3382
+ const Components = useComponentsContext3();
3383
+ const editor = useBlockNoteEditor3();
3384
+ const updateColor = (color, type) => {
3385
+ const newTable = props.block.content.rows.map((row) => ({
3386
+ ...row,
3387
+ cells: row.cells.map((cell) => mapTableCell2(cell))
3388
+ }));
3389
+ if (type === "text") {
3390
+ newTable[props.rowIndex].cells[props.colIndex].props.textColor = color;
3391
+ } else {
3392
+ newTable[props.rowIndex].cells[props.colIndex].props.backgroundColor = color;
3393
+ }
3394
+ editor.updateBlock(props.block, {
3395
+ type: "table",
3396
+ content: { ...props.block.content, rows: newTable }
3397
+ });
3398
+ editor.setTextCursorPosition(props.block);
3399
+ };
3400
+ const currentCell = props.block.content.rows[props.rowIndex]?.cells?.[props.colIndex];
3401
+ if (!currentCell || editor.settings.tables.cellTextColor === false && editor.settings.tables.cellBackgroundColor === false) {
3402
+ return null;
3403
+ }
3404
+ return /* @__PURE__ */ jsxs12(Components.Generic.Menu.Root, { position: "right", sub: true, children: [
3405
+ /* @__PURE__ */ jsx19(Components.Generic.Menu.Trigger, { sub: true, children: /* @__PURE__ */ jsx19(Components.Generic.Menu.Item, { className: "bn-menu-item", subTrigger: true, children: "\uC140 \uC0C9\xB7\uBC30\uACBD" }) }),
3406
+ /* @__PURE__ */ jsx19(
3407
+ Components.Generic.Menu.Dropdown,
3408
+ {
3409
+ sub: true,
3410
+ className: "bn-menu-dropdown bn-color-picker-dropdown",
3411
+ children: /* @__PURE__ */ jsx19(
3412
+ LumirColorPicker,
3413
+ {
3414
+ iconSize: 18,
3415
+ textTitle: "\uC140 \uAE00\uC790\uC0C9",
3416
+ backgroundTitle: "\uC140 \uBC30\uACBD",
3417
+ text: editor.settings.tables.cellTextColor ? {
3418
+ color: isTableCell(currentCell) ? currentCell.props.textColor : "default",
3419
+ setColor: (color) => updateColor(color, "text")
3420
+ } : void 0,
3421
+ background: editor.settings.tables.cellBackgroundColor ? {
3422
+ color: isTableCell(currentCell) ? currentCell.props.backgroundColor : "default",
3423
+ setColor: (color) => updateColor(color, "background")
3424
+ } : void 0
3425
+ }
3426
+ )
3427
+ }
3428
+ )
3429
+ ] });
3430
+ }
3431
+ function LumirTableCellMenu(props) {
3432
+ const Components = useComponentsContext3();
3433
+ return /* @__PURE__ */ jsx19(
3434
+ Components.Generic.Menu.Dropdown,
3435
+ {
3436
+ className: "bn-menu-dropdown bn-drag-handle-menu",
3437
+ children: props.children || /* @__PURE__ */ jsxs12(Fragment5, { children: [
3438
+ /* @__PURE__ */ jsx19(
3439
+ SplitButton,
3440
+ {
3441
+ block: props.block,
3442
+ rowIndex: props.rowIndex,
3443
+ colIndex: props.colIndex
3444
+ }
3445
+ ),
3446
+ /* @__PURE__ */ jsx19(
3447
+ LumirCellColorPickerButton,
3448
+ {
3449
+ block: props.block,
3450
+ rowIndex: props.rowIndex,
3451
+ colIndex: props.colIndex
3452
+ }
3453
+ )
3454
+ ] })
3455
+ }
3456
+ );
3457
+ }
3458
+
3081
3459
  // src/components/CustomFormattingToolbar.tsx
3082
- import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
3460
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
3083
3461
  var CustomFormattingToolbar = () => {
3084
- return /* @__PURE__ */ jsxs12(FormattingToolbar, { children: [
3085
- /* @__PURE__ */ jsx19(BlockTypeSelect2, {}, "blockTypeSelect"),
3086
- /* @__PURE__ */ jsx19(TableCellMergeButton, {}, "tableCellMergeButton"),
3087
- /* @__PURE__ */ jsx19(FileCaptionButton, {}, "fileCaptionButton"),
3088
- /* @__PURE__ */ jsx19(FileReplaceButton, {}, "replaceFileButton"),
3089
- /* @__PURE__ */ jsx19(FileRenameButton, {}, "fileRenameButton"),
3090
- /* @__PURE__ */ jsx19(FileDeleteButton, {}, "fileDeleteButton"),
3091
- /* @__PURE__ */ jsx19(FileDownloadButton, {}, "fileDownloadButton"),
3092
- /* @__PURE__ */ jsx19(FilePreviewButton, {}, "filePreviewButton"),
3093
- /* @__PURE__ */ jsx19(BasicTextStyleButton, { basicTextStyle: "bold" }, "boldStyleButton"),
3094
- /* @__PURE__ */ jsx19(
3462
+ return /* @__PURE__ */ jsxs13(FormattingToolbar, { children: [
3463
+ /* @__PURE__ */ jsx20(BlockTypeSelect2, {}, "blockTypeSelect"),
3464
+ /* @__PURE__ */ jsx20(TableCellMergeButton, {}, "tableCellMergeButton"),
3465
+ /* @__PURE__ */ jsx20(FileCaptionButton, {}, "fileCaptionButton"),
3466
+ /* @__PURE__ */ jsx20(FileReplaceButton, {}, "replaceFileButton"),
3467
+ /* @__PURE__ */ jsx20(FileRenameButton, {}, "fileRenameButton"),
3468
+ /* @__PURE__ */ jsx20(FileDeleteButton, {}, "fileDeleteButton"),
3469
+ /* @__PURE__ */ jsx20(FileDownloadButton, {}, "fileDownloadButton"),
3470
+ /* @__PURE__ */ jsx20(FilePreviewButton, {}, "filePreviewButton"),
3471
+ /* @__PURE__ */ jsx20(BasicTextStyleButton, { basicTextStyle: "bold" }, "boldStyleButton"),
3472
+ /* @__PURE__ */ jsx20(
3095
3473
  BasicTextStyleButton,
3096
3474
  {
3097
3475
  basicTextStyle: "italic"
3098
3476
  },
3099
3477
  "italicStyleButton"
3100
3478
  ),
3101
- /* @__PURE__ */ jsx19(
3479
+ /* @__PURE__ */ jsx20(
3102
3480
  BasicTextStyleButton,
3103
3481
  {
3104
3482
  basicTextStyle: "underline"
3105
3483
  },
3106
3484
  "underlineStyleButton"
3107
3485
  ),
3108
- /* @__PURE__ */ jsx19(
3486
+ /* @__PURE__ */ jsx20(
3109
3487
  BasicTextStyleButton,
3110
3488
  {
3111
3489
  basicTextStyle: "strike"
3112
3490
  },
3113
3491
  "strikeStyleButton"
3114
3492
  ),
3115
- /* @__PURE__ */ jsx19(TextAlignButtonWithVA, { textAlignment: "left" }, "textAlignLeftButton"),
3116
- /* @__PURE__ */ jsx19(
3493
+ /* @__PURE__ */ jsx20(TextAlignButtonWithVA, { textAlignment: "left" }, "textAlignLeftButton"),
3494
+ /* @__PURE__ */ jsx20(
3117
3495
  TextAlignButtonWithVA,
3118
3496
  {
3119
3497
  textAlignment: "center"
3120
3498
  },
3121
3499
  "textAlignCenterButton"
3122
3500
  ),
3123
- /* @__PURE__ */ jsx19(TextAlignButtonWithVA, { textAlignment: "right" }, "textAlignRightButton"),
3124
- /* @__PURE__ */ jsx19(
3501
+ /* @__PURE__ */ jsx20(TextAlignButtonWithVA, { textAlignment: "right" }, "textAlignRightButton"),
3502
+ /* @__PURE__ */ jsx20(
3125
3503
  VerticalAlignButton,
3126
3504
  {
3127
3505
  verticalAlignment: "top"
3128
3506
  },
3129
3507
  "verticalAlignTop"
3130
3508
  ),
3131
- /* @__PURE__ */ jsx19(
3509
+ /* @__PURE__ */ jsx20(
3132
3510
  VerticalAlignButton,
3133
3511
  {
3134
3512
  verticalAlignment: "middle"
3135
3513
  },
3136
3514
  "verticalAlignMiddle"
3137
3515
  ),
3138
- /* @__PURE__ */ jsx19(
3516
+ /* @__PURE__ */ jsx20(
3139
3517
  VerticalAlignButton,
3140
3518
  {
3141
3519
  verticalAlignment: "bottom"
3142
3520
  },
3143
3521
  "verticalAlignBottom"
3144
3522
  ),
3145
- /* @__PURE__ */ jsx19(ColorStyleButton, {}, "colorStyleButton"),
3146
- /* @__PURE__ */ jsx19(NestBlockButton, {}, "nestBlockButton"),
3147
- /* @__PURE__ */ jsx19(UnnestBlockButton, {}, "unnestBlockButton"),
3148
- /* @__PURE__ */ jsx19(CreateLinkButton, {}, "createLinkButton")
3523
+ /* @__PURE__ */ jsx20(LumirColorStyleButton, {}, "colorStyleButton"),
3524
+ /* @__PURE__ */ jsx20(LumirCellColorToolbarButton, {}, "cellColorButton"),
3525
+ /* @__PURE__ */ jsx20(NestBlockButton, {}, "nestBlockButton"),
3526
+ /* @__PURE__ */ jsx20(UnnestBlockButton, {}, "unnestBlockButton"),
3527
+ /* @__PURE__ */ jsx20(CreateLinkButton, {}, "createLinkButton")
3149
3528
  ] });
3150
3529
  };
3151
3530
 
3531
+ // src/components/LumirTableHandlesController.tsx
3532
+ import {
3533
+ ExtendButton,
3534
+ TableCellButton,
3535
+ TableHandle,
3536
+ useBlockNoteEditor as useBlockNoteEditor4,
3537
+ useEditorContentOrSelectionChange as useEditorContentOrSelectionChange2,
3538
+ useExtendButtonsPositioning,
3539
+ useUIPluginState
3540
+ } from "@blocknote/react";
3541
+ import { autoUpdate as autoUpdate2, FloatingPortal } from "@floating-ui/react";
3542
+ import { useCallback as useCallback17, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef10, useState as useState9 } from "react";
3543
+
3544
+ // src/components/hooks/useFocusedCellHandlePositioning.ts
3545
+ import {
3546
+ autoUpdate,
3547
+ offset,
3548
+ useFloating,
3549
+ useTransitionStyles
3550
+ } from "@floating-ui/react";
3551
+ import { useEffect as useEffect8, useMemo as useMemo4 } from "react";
3552
+ function useFocusedCellHandlePositioning(cellEl, tbodyEl, orientation, show) {
3553
+ const { refs, floatingStyles, context } = useFloating({
3554
+ open: show,
3555
+ placement: orientation === "row" ? "left" : orientation === "col" ? "top" : "right",
3556
+ // col/row: 가장자리 선(zero-size)에, cell: 셀 우측 보더에 14px hit-area 중앙 정렬(-7).
3557
+ middleware: [offset(-7)],
3558
+ whileElementsMounted: autoUpdate
3559
+ });
3560
+ const { isMounted, styles } = useTransitionStyles(context);
3561
+ useEffect8(() => {
3562
+ if (!cellEl) {
3563
+ refs.setReference(null);
3564
+ return;
3565
+ }
3566
+ refs.setReference({
3567
+ contextElement: cellEl,
3568
+ getBoundingClientRect: () => {
3569
+ const c = cellEl.getBoundingClientRect();
3570
+ const t = tbodyEl?.getBoundingClientRect() ?? c;
3571
+ if (orientation === "col") {
3572
+ return new DOMRect(c.left, t.top, c.width, 0);
3573
+ }
3574
+ if (orientation === "row") {
3575
+ return new DOMRect(t.left, c.top, 0, c.height);
3576
+ }
3577
+ return c;
3578
+ }
3579
+ });
3580
+ }, [cellEl, tbodyEl, orientation, refs]);
3581
+ return useMemo4(
3582
+ () => ({
3583
+ isMounted,
3584
+ ref: refs.setFloating,
3585
+ // display는 CSS 클래스에서 제어한다(absolute 자식 stacking).
3586
+ style: {
3587
+ ...styles,
3588
+ ...floatingStyles
3589
+ }
3590
+ }),
3591
+ [floatingStyles, isMounted, refs.setFloating, styles]
3592
+ );
3593
+ }
3594
+
3595
+ // src/components/LumirTableHandlesController.tsx
3596
+ import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
3597
+ function syncCoreHoverToFocusedCell(cellEl) {
3598
+ const r = cellEl.getBoundingClientRect();
3599
+ cellEl.dispatchEvent(
3600
+ new MouseEvent("mousemove", {
3601
+ bubbles: true,
3602
+ cancelable: true,
3603
+ view: window,
3604
+ clientX: r.left + r.width / 2,
3605
+ clientY: r.top + r.height / 2
3606
+ })
3607
+ );
3608
+ }
3609
+ function LumirTableHandlesController() {
3610
+ const editor = useBlockNoteEditor4();
3611
+ const [focused, setFocused] = useState9(null);
3612
+ const [menuContainerRef, setMenuContainerRef] = useState9(null);
3613
+ const [overlayEl, setOverlayEl] = useState9(null);
3614
+ const [openMenu, setOpenMenu] = useState9(null);
3615
+ const frozenRef = useRef10(false);
3616
+ const menuOpenRef = useRef10(false);
3617
+ const draggingRef = useRef10(false);
3618
+ const recompute = useCallback17(() => {
3619
+ if (frozenRef.current) {
3620
+ return;
3621
+ }
3622
+ const th2 = editor.tableHandles;
3623
+ const view = editor.prosemirrorView;
3624
+ if (!th2 || !view) {
3625
+ setFocused(null);
3626
+ return;
3627
+ }
3628
+ let cellEl2 = null;
3629
+ try {
3630
+ const { node } = view.domAtPos(view.state.selection.from);
3631
+ const el = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
3632
+ cellEl2 = el?.closest?.("td, th") ?? null;
3633
+ } catch {
3634
+ cellEl2 = null;
3635
+ }
3636
+ if (!cellEl2 || !cellEl2.isConnected) {
3637
+ setFocused(null);
3638
+ return;
3639
+ }
3640
+ const blockId = cellEl2.closest("[data-id]")?.getAttribute("data-id");
3641
+ const block = blockId ? editor.getBlock(blockId) : void 0;
3642
+ if (!block || block.type !== "table") {
3643
+ setFocused(null);
3644
+ return;
3645
+ }
3646
+ const cellSel = th2.getCellSelection();
3647
+ if (!cellSel) {
3648
+ setFocused(null);
3649
+ return;
3650
+ }
3651
+ const widgetContainer = cellEl2.closest(".tableWrapper")?.querySelector(".table-widgets-container");
3652
+ const tbodyEl2 = cellEl2.closest("tbody");
3653
+ if (!widgetContainer || !tbodyEl2) {
3654
+ setFocused(null);
3655
+ return;
3656
+ }
3657
+ setFocused({
3658
+ block,
3659
+ rowIndex: cellSel.from.row,
3660
+ colIndex: cellSel.from.col,
3661
+ cellEl: cellEl2,
3662
+ tbodyEl: tbodyEl2,
3663
+ widgetContainer
3664
+ });
3665
+ }, [editor]);
3666
+ useEditorContentOrSelectionChange2(recompute, editor);
3667
+ useEffect9(() => {
3668
+ recompute();
3669
+ }, [recompute]);
3670
+ useEffect9(() => {
3671
+ const onUp = () => {
3672
+ requestAnimationFrame(() => {
3673
+ if (!menuOpenRef.current && !draggingRef.current && frozenRef.current) {
3674
+ frozenRef.current = false;
3675
+ recompute();
3676
+ }
3677
+ });
3678
+ };
3679
+ window.addEventListener("pointerup", onUp);
3680
+ return () => window.removeEventListener("pointerup", onUp);
3681
+ }, [recompute]);
3682
+ useEffect9(() => {
3683
+ const f = focused;
3684
+ if (!f || !overlayEl) {
3685
+ return;
3686
+ }
3687
+ const PAD = 1;
3688
+ const update = () => {
3689
+ const cr = f.cellEl.getBoundingClientRect();
3690
+ const tr = f.tbodyEl.getBoundingClientRect();
3691
+ const kr = f.widgetContainer.getBoundingClientRect();
3692
+ const x1 = openMenu === "row" ? tr.left : cr.left;
3693
+ const y1 = openMenu === "col" ? tr.top : cr.top;
3694
+ const x2 = openMenu === "row" ? tr.right : cr.right;
3695
+ const y2 = openMenu === "col" ? tr.bottom : cr.bottom;
3696
+ const dpr = window.devicePixelRatio || 1;
3697
+ const rd = (v) => Math.round(v * dpr) / dpr;
3698
+ const left = rd(x1 - kr.left) - PAD;
3699
+ const top = rd(y1 - kr.top) - PAD;
3700
+ const right = rd(x2 - kr.left) + PAD;
3701
+ const bottom = rd(y2 - kr.top) + PAD;
3702
+ overlayEl.style.transform = `translate(${left}px, ${top}px)`;
3703
+ overlayEl.style.width = `${right - left}px`;
3704
+ overlayEl.style.height = `${bottom - top}px`;
3705
+ };
3706
+ update();
3707
+ return autoUpdate2(f.cellEl, overlayEl, update);
3708
+ }, [focused, overlayEl, openMenu]);
3709
+ const cellEl = focused?.cellEl ?? null;
3710
+ const tbodyEl = focused?.tbodyEl ?? null;
3711
+ const show = focused !== null;
3712
+ const rowHandle = useFocusedCellHandlePositioning(cellEl, tbodyEl, "row", show);
3713
+ const colHandle = useFocusedCellHandlePositioning(cellEl, tbodyEl, "col", show);
3714
+ const cellHandle = useFocusedCellHandlePositioning(
3715
+ cellEl,
3716
+ tbodyEl,
3717
+ "cell",
3718
+ show
3719
+ );
3720
+ const th = editor.tableHandles;
3721
+ const coreState = useUIPluginState(
3722
+ editor.tableHandles.onUpdate.bind(editor.tableHandles)
3723
+ );
3724
+ const { addOrRemoveColumnsButton, addOrRemoveRowsButton } = useExtendButtonsPositioning(
3725
+ coreState?.showAddOrRemoveColumnsButton || false,
3726
+ coreState?.showAddOrRemoveRowsButton || false,
3727
+ coreState?.referencePosTable || null
3728
+ );
3729
+ const onStartExtend = useCallback17(() => {
3730
+ editor.tableHandles?.freezeHandles();
3731
+ }, [editor]);
3732
+ const onEndExtend = useCallback17(() => {
3733
+ editor.tableHandles?.unfreezeHandles();
3734
+ }, [editor]);
3735
+ const menuHandlers = useMemo5(() => {
3736
+ const mk = (kind) => ({
3737
+ freeze: () => {
3738
+ menuOpenRef.current = true;
3739
+ frozenRef.current = true;
3740
+ setOpenMenu(kind);
3741
+ editor.tableHandles?.freezeHandles();
3742
+ },
3743
+ unfreeze: () => {
3744
+ menuOpenRef.current = false;
3745
+ frozenRef.current = false;
3746
+ setOpenMenu(null);
3747
+ editor.tableHandles?.unfreezeHandles();
3748
+ recompute();
3749
+ }
3750
+ });
3751
+ return { col: mk("col"), row: mk("row"), cell: mk("cell") };
3752
+ }, [editor, recompute]);
3753
+ const onGutterPointerDown = useCallback17(() => {
3754
+ frozenRef.current = true;
3755
+ }, []);
3756
+ const onGutterPointerEnter = useCallback17(
3757
+ (e) => {
3758
+ if (e.buttons === 0 && focused) {
3759
+ syncCoreHoverToFocusedCell(focused.cellEl);
3760
+ }
3761
+ },
3762
+ [focused]
3763
+ );
3764
+ const makeDragStart = useCallback17(
3765
+ (dir) => (e) => {
3766
+ draggingRef.current = true;
3767
+ frozenRef.current = true;
3768
+ if (dir === "row") {
3769
+ editor.tableHandles?.rowDragStart(e);
3770
+ } else {
3771
+ editor.tableHandles?.colDragStart(e);
3772
+ }
3773
+ },
3774
+ [editor]
3775
+ );
3776
+ const onDragEnd = useCallback17(() => {
3777
+ editor.tableHandles?.dragEnd();
3778
+ draggingRef.current = false;
3779
+ frozenRef.current = false;
3780
+ recompute();
3781
+ }, [editor, recompute]);
3782
+ const noop = useCallback17(() => {
3783
+ }, []);
3784
+ return /* @__PURE__ */ jsxs14(Fragment6, { children: [
3785
+ /* @__PURE__ */ jsx21("div", { ref: setMenuContainerRef }),
3786
+ th && focused && menuContainerRef && /* @__PURE__ */ jsxs14(FloatingPortal, { root: focused.widgetContainer, children: [
3787
+ /* @__PURE__ */ jsx21("div", { ref: setOverlayEl, className: "lumir-tbl-cell-focus" }),
3788
+ colHandle.isMounted && /* @__PURE__ */ jsxs14(
3789
+ "div",
3790
+ {
3791
+ ref: colHandle.ref,
3792
+ style: colHandle.style,
3793
+ className: "lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--col" + (openMenu === "col" ? " lumir-tbl-gutter-wrap--active" : ""),
3794
+ onPointerEnter: onGutterPointerEnter,
3795
+ onPointerDown: onGutterPointerDown,
3796
+ children: [
3797
+ /* @__PURE__ */ jsx21("span", { className: "lumir-tbl-gutter" }),
3798
+ /* @__PURE__ */ jsx21("div", { className: "lumir-tbl-grip", children: /* @__PURE__ */ jsx21(
3799
+ TableHandle,
3800
+ {
3801
+ editor,
3802
+ orientation: "column",
3803
+ index: focused.colIndex,
3804
+ block: focused.block,
3805
+ dragStart: makeDragStart("col"),
3806
+ dragEnd: onDragEnd,
3807
+ freezeHandles: menuHandlers.col.freeze,
3808
+ unfreezeHandles: menuHandlers.col.unfreeze,
3809
+ menuContainer: menuContainerRef,
3810
+ showOtherSide: noop,
3811
+ hideOtherSide: noop
3812
+ }
3813
+ ) })
3814
+ ]
3815
+ }
3816
+ ),
3817
+ rowHandle.isMounted && /* @__PURE__ */ jsxs14(
3818
+ "div",
3819
+ {
3820
+ ref: rowHandle.ref,
3821
+ style: rowHandle.style,
3822
+ className: "lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--row" + (openMenu === "row" ? " lumir-tbl-gutter-wrap--active" : ""),
3823
+ onPointerEnter: onGutterPointerEnter,
3824
+ onPointerDown: onGutterPointerDown,
3825
+ children: [
3826
+ /* @__PURE__ */ jsx21("span", { className: "lumir-tbl-gutter" }),
3827
+ /* @__PURE__ */ jsx21("div", { className: "lumir-tbl-grip", children: /* @__PURE__ */ jsx21(
3828
+ TableHandle,
3829
+ {
3830
+ editor,
3831
+ orientation: "row",
3832
+ index: focused.rowIndex,
3833
+ block: focused.block,
3834
+ dragStart: makeDragStart("row"),
3835
+ dragEnd: onDragEnd,
3836
+ freezeHandles: menuHandlers.row.freeze,
3837
+ unfreezeHandles: menuHandlers.row.unfreeze,
3838
+ menuContainer: menuContainerRef,
3839
+ showOtherSide: noop,
3840
+ hideOtherSide: noop
3841
+ }
3842
+ ) })
3843
+ ]
3844
+ }
3845
+ ),
3846
+ cellHandle.isMounted && openMenu !== "col" && openMenu !== "row" && /* @__PURE__ */ jsxs14(
3847
+ "div",
3848
+ {
3849
+ ref: cellHandle.ref,
3850
+ style: cellHandle.style,
3851
+ className: "lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--cell" + (openMenu === "cell" ? " lumir-tbl-gutter-wrap--active" : ""),
3852
+ onPointerDown: onGutterPointerDown,
3853
+ children: [
3854
+ /* @__PURE__ */ jsx21("span", { className: "lumir-tbl-gutter" }),
3855
+ /* @__PURE__ */ jsx21("div", { className: "lumir-tbl-grip", children: /* @__PURE__ */ jsx21(
3856
+ TableCellButton,
3857
+ {
3858
+ editor,
3859
+ rowIndex: focused.rowIndex,
3860
+ colIndex: focused.colIndex,
3861
+ block: focused.block,
3862
+ tableCellMenu: LumirTableCellMenu,
3863
+ menuContainer: menuContainerRef,
3864
+ freezeHandles: menuHandlers.cell.freeze,
3865
+ unfreezeHandles: menuHandlers.cell.unfreeze
3866
+ }
3867
+ ) })
3868
+ ]
3869
+ }
3870
+ )
3871
+ ] }),
3872
+ th && coreState?.widgetContainer && /* @__PURE__ */ jsxs14(FloatingPortal, { root: coreState.widgetContainer, children: [
3873
+ /* @__PURE__ */ jsx21("div", { ref: addOrRemoveRowsButton.ref, style: addOrRemoveRowsButton.style, children: /* @__PURE__ */ jsx21(
3874
+ ExtendButton,
3875
+ {
3876
+ editor,
3877
+ orientation: "addOrRemoveRows",
3878
+ block: coreState.block,
3879
+ onMouseDown: onStartExtend,
3880
+ onMouseUp: onEndExtend
3881
+ }
3882
+ ) }),
3883
+ /* @__PURE__ */ jsx21(
3884
+ "div",
3885
+ {
3886
+ ref: addOrRemoveColumnsButton.ref,
3887
+ style: addOrRemoveColumnsButton.style,
3888
+ children: /* @__PURE__ */ jsx21(
3889
+ ExtendButton,
3890
+ {
3891
+ editor,
3892
+ orientation: "addOrRemoveColumns",
3893
+ block: coreState.block,
3894
+ onMouseDown: onStartExtend,
3895
+ onMouseUp: onEndExtend
3896
+ }
3897
+ )
3898
+ }
3899
+ )
3900
+ ] })
3901
+ ] });
3902
+ }
3903
+
3152
3904
  // src/utils/table-vertical-alignment.ts
3153
3905
  function injectVerticalAlignment(blocks, editor) {
3154
3906
  const tiptap = editor?._tiptapEditor;
@@ -3479,7 +4231,7 @@ var ALLOWED_VIDEO_EXTENSIONS = [
3479
4231
  ];
3480
4232
 
3481
4233
  // src/components/LumirEditor.tsx
3482
- import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
4234
+ import { Fragment as Fragment7, jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
3483
4235
  var DEBUG_LOG = (loc, msg, data) => {
3484
4236
  const p = fetch("http://127.0.0.1:7686/ingest/1f8ee1c5-0cf0-4ae7-91ed-5ea7ed17130a", {
3485
4237
  method: "POST",
@@ -3704,9 +4456,9 @@ var findBlockWithLink = (blocks, targetUrl) => {
3704
4456
  return null;
3705
4457
  };
3706
4458
  var ConvertToPreviewButton = ({ url }) => {
3707
- const editor = useBlockNoteEditor3();
3708
- const Components = useComponentsContext3();
3709
- return /* @__PURE__ */ jsx20(
4459
+ const editor = useBlockNoteEditor5();
4460
+ const Components = useComponentsContext4();
4461
+ return /* @__PURE__ */ jsx22(
3710
4462
  Components.LinkToolbar.Button,
3711
4463
  {
3712
4464
  className: "bn-button",
@@ -3725,29 +4477,29 @@ var ConvertToPreviewButton = ({ url }) => {
3725
4477
  console.error("Convert to link preview failed:", err);
3726
4478
  }
3727
4479
  },
3728
- icon: /* @__PURE__ */ jsxs13("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
3729
- /* @__PURE__ */ jsx20("rect", { x: "1", y: "3", width: "14", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }),
3730
- /* @__PURE__ */ jsx20("line", { x1: "1", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "1.5" }),
3731
- /* @__PURE__ */ jsx20("circle", { cx: "5", cy: "6.5", r: "1.5", stroke: "currentColor", strokeWidth: "1", fill: "none" })
4480
+ icon: /* @__PURE__ */ jsxs15("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
4481
+ /* @__PURE__ */ jsx22("rect", { x: "1", y: "3", width: "14", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }),
4482
+ /* @__PURE__ */ jsx22("line", { x1: "1", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "1.5" }),
4483
+ /* @__PURE__ */ jsx22("circle", { cx: "5", cy: "6.5", r: "1.5", stroke: "currentColor", strokeWidth: "1", fill: "none" })
3732
4484
  ] })
3733
4485
  }
3734
4486
  );
3735
4487
  };
3736
4488
  var CustomLinkToolbar = (props) => {
3737
- const editor = useBlockNoteEditor3();
3738
- const Components = useComponentsContext3();
4489
+ const editor = useBlockNoteEditor5();
4490
+ const Components = useComponentsContext4();
3739
4491
  const hasLinkPreview = !!editor?._linkPreviewApiEndpoint;
3740
- return /* @__PURE__ */ jsxs13(
4492
+ return /* @__PURE__ */ jsxs15(
3741
4493
  Components.LinkToolbar.Root,
3742
4494
  {
3743
4495
  className: "bn-toolbar bn-link-toolbar",
3744
4496
  onMouseEnter: props.stopHideTimer,
3745
4497
  onMouseLeave: props.startHideTimer,
3746
4498
  children: [
3747
- /* @__PURE__ */ jsx20(EditLinkButton, { url: props.url, text: props.text, editLink: props.editLink }),
3748
- /* @__PURE__ */ jsx20(OpenLinkButton, { url: props.url }),
3749
- /* @__PURE__ */ jsx20(DeleteLinkButton, { deleteLink: props.deleteLink }),
3750
- hasLinkPreview && /* @__PURE__ */ jsx20(ConvertToPreviewButton, { url: props.url })
4499
+ /* @__PURE__ */ jsx22(EditLinkButton, { url: props.url, text: props.text, editLink: props.editLink }),
4500
+ /* @__PURE__ */ jsx22(OpenLinkButton, { url: props.url }),
4501
+ /* @__PURE__ */ jsx22(DeleteLinkButton, { deleteLink: props.deleteLink }),
4502
+ hasLinkPreview && /* @__PURE__ */ jsx22(ConvertToPreviewButton, { url: props.url })
3751
4503
  ]
3752
4504
  }
3753
4505
  );
@@ -3791,13 +4543,13 @@ function LumirEditor({
3791
4543
  onError,
3792
4544
  onImageDelete
3793
4545
  }) {
3794
- const [isUploading, setIsUploading] = useState8(false);
3795
- const [uploadProgress, setUploadProgress] = useState8(null);
3796
- const [errorMessage, setErrorMessage] = useState8(null);
3797
- const floatingMenuFileInputRef = useRef9(null);
3798
- const floatingMenuBlockRef = useRef9(null);
3799
- const floatingMenuUploadStartTimeRef = useRef9(0);
3800
- const handleError = useCallback16(
4546
+ const [isUploading, setIsUploading] = useState10(false);
4547
+ const [uploadProgress, setUploadProgress] = useState10(null);
4548
+ const [errorMessage, setErrorMessage] = useState10(null);
4549
+ const floatingMenuFileInputRef = useRef11(null);
4550
+ const floatingMenuBlockRef = useRef11(null);
4551
+ const floatingMenuUploadStartTimeRef = useRef11(0);
4552
+ const handleError = useCallback18(
3801
4553
  (error) => {
3802
4554
  onError?.(error);
3803
4555
  setErrorMessage(error.getUserMessage());
@@ -3805,10 +4557,10 @@ function LumirEditor({
3805
4557
  },
3806
4558
  [onError]
3807
4559
  );
3808
- const validatedContent = useMemo3(() => {
4560
+ const validatedContent = useMemo6(() => {
3809
4561
  return ContentUtils.validateContent(initialContent, initialEmptyBlocks);
3810
4562
  }, [initialContent, initialEmptyBlocks]);
3811
- const tableConfig = useMemo3(() => {
4563
+ const tableConfig = useMemo6(() => {
3812
4564
  return EditorConfig.getDefaultTableConfig(tables);
3813
4565
  }, [
3814
4566
  tables?.splitCells,
@@ -3816,10 +4568,10 @@ function LumirEditor({
3816
4568
  tables?.cellTextColor,
3817
4569
  tables?.headers
3818
4570
  ]);
3819
- const headingConfig = useMemo3(() => {
4571
+ const headingConfig = useMemo6(() => {
3820
4572
  return EditorConfig.getDefaultHeadingConfig(heading);
3821
4573
  }, [heading?.levels?.join(",") ?? ""]);
3822
- const disabledExtensions = useMemo3(() => {
4574
+ const disabledExtensions = useMemo6(() => {
3823
4575
  return EditorConfig.getDisabledExtensions(
3824
4576
  disableExtensions,
3825
4577
  allowVideoUpload,
@@ -3827,18 +4579,18 @@ function LumirEditor({
3827
4579
  allowFileUpload
3828
4580
  );
3829
4581
  }, [disableExtensions, allowVideoUpload, allowAudioUpload, allowFileUpload]);
3830
- useEffect8(() => {
4582
+ useEffect10(() => {
3831
4583
  DEBUG_LOG("LumirEditor:init:disabledExtensions", "snapshot", {
3832
4584
  allowVideoUpload,
3833
4585
  hasVideoInDisabled: disabledExtensions.includes("video"),
3834
4586
  disabledList: disabledExtensions.slice(0, 15)
3835
4587
  });
3836
4588
  }, [allowVideoUpload, disabledExtensions]);
3837
- const fileNameTransformRef = useRef9(s3Upload?.fileNameTransform);
3838
- useEffect8(() => {
4589
+ const fileNameTransformRef = useRef11(s3Upload?.fileNameTransform);
4590
+ useEffect10(() => {
3839
4591
  fileNameTransformRef.current = s3Upload?.fileNameTransform;
3840
4592
  }, [s3Upload?.fileNameTransform]);
3841
- const memoizedS3Upload = useMemo3(() => {
4593
+ const memoizedS3Upload = useMemo6(() => {
3842
4594
  if (!s3Upload) return void 0;
3843
4595
  return {
3844
4596
  apiEndpoint: s3Upload.apiEndpoint,
@@ -3871,6 +4623,15 @@ function LumirEditor({
3871
4623
  {
3872
4624
  // HTML 미리보기 블록이 포함된 커스텀 스키마 사용
3873
4625
  schema,
4626
+ // 모든 BlockNote UI 텍스트(테이블 드롭다운 등) 한글 적용 + "색깔"→"색"
4627
+ dictionary: {
4628
+ ...ko,
4629
+ drag_handle: { ...ko.drag_handle, colors_menuitem: "\uC0C9" },
4630
+ formatting_toolbar: {
4631
+ ...ko.formatting_toolbar,
4632
+ colors: { ...ko.formatting_toolbar.colors, tooltip: "\uC0C9" }
4633
+ }
4634
+ },
3874
4635
  initialContent: validatedContent,
3875
4636
  tables: tableConfig,
3876
4637
  heading: headingConfig,
@@ -4062,12 +4823,12 @@ function LumirEditor({
4062
4823
  if (editor && linkPreview?.apiEndpoint) {
4063
4824
  editor._linkPreviewApiEndpoint = linkPreview.apiEndpoint;
4064
4825
  }
4065
- useEffect8(() => {
4826
+ useEffect10(() => {
4066
4827
  if (editor) {
4067
4828
  editor.isEditable = editable;
4068
4829
  }
4069
4830
  }, [editor, editable]);
4070
- useEffect8(() => {
4831
+ useEffect10(() => {
4071
4832
  if (!editor || !onContentChange) return;
4072
4833
  const handleContentChange = () => {
4073
4834
  const blocks = editor.topLevelBlocks;
@@ -4076,13 +4837,13 @@ function LumirEditor({
4076
4837
  };
4077
4838
  return editor.onEditorContentChange(handleContentChange);
4078
4839
  }, [editor, onContentChange]);
4079
- const previousMediaUrlsRef = useRef9(/* @__PURE__ */ new Set());
4080
- useEffect8(() => {
4840
+ const previousMediaUrlsRef = useRef11(/* @__PURE__ */ new Set());
4841
+ useEffect10(() => {
4081
4842
  if (!editor) return;
4082
4843
  const initialBlocks = editor.topLevelBlocks;
4083
4844
  previousMediaUrlsRef.current = extractMediaUrls(initialBlocks);
4084
4845
  }, [editor]);
4085
- useEffect8(() => {
4846
+ useEffect10(() => {
4086
4847
  if (!editor || !onImageDelete) return;
4087
4848
  const handleMediaDeleteCheck = () => {
4088
4849
  const currentBlocks = editor.topLevelBlocks;
@@ -4096,7 +4857,7 @@ function LumirEditor({
4096
4857
  };
4097
4858
  return editor.onEditorContentChange(handleMediaDeleteCheck);
4098
4859
  }, [editor, onImageDelete]);
4099
- useEffect8(() => {
4860
+ useEffect10(() => {
4100
4861
  const el = editor?.domElement;
4101
4862
  if (!el) return;
4102
4863
  const handleDragOver = (e) => {
@@ -4227,20 +4988,20 @@ function LumirEditor({
4227
4988
  el.removeEventListener("drop", handleDrop, { capture: true });
4228
4989
  };
4229
4990
  }, [editor, allowVideoUpload]);
4230
- const computedSideMenu = useMemo3(() => {
4991
+ const computedSideMenu = useMemo6(() => {
4231
4992
  return sideMenuAddButton ? sideMenu : false;
4232
4993
  }, [sideMenuAddButton, sideMenu]);
4233
- const DragHandleOnlySideMenu = useMemo3(() => {
4234
- return (props) => /* @__PURE__ */ jsx20(BlockSideMenu, { ...props, children: /* @__PURE__ */ jsx20(DragHandleButton, { ...props }) });
4994
+ const DragHandleOnlySideMenu = useMemo6(() => {
4995
+ return (props) => /* @__PURE__ */ jsx22(BlockSideMenu, { ...props, children: /* @__PURE__ */ jsx22(DragHandleButton, { ...props }) });
4235
4996
  }, []);
4236
- return /* @__PURE__ */ jsxs13(
4997
+ return /* @__PURE__ */ jsxs15(
4237
4998
  "div",
4238
4999
  {
4239
5000
  className: cn("lumirEditor", className),
4240
5001
  style: { position: "relative", display: "flex", flexDirection: "column" },
4241
5002
  children: [
4242
- floatingMenu && editor && /* @__PURE__ */ jsxs13(Fragment5, { children: [
4243
- /* @__PURE__ */ jsx20(
5003
+ floatingMenu && editor && /* @__PURE__ */ jsxs15(Fragment7, { children: [
5004
+ /* @__PURE__ */ jsx22(
4244
5005
  "input",
4245
5006
  {
4246
5007
  ref: floatingMenuFileInputRef,
@@ -4311,7 +5072,7 @@ function LumirEditor({
4311
5072
  }
4312
5073
  }
4313
5074
  ),
4314
- /* @__PURE__ */ jsx20(
5075
+ /* @__PURE__ */ jsx22(
4315
5076
  FloatingMenu,
4316
5077
  {
4317
5078
  editor,
@@ -4343,7 +5104,7 @@ function LumirEditor({
4343
5104
  }
4344
5105
  )
4345
5106
  ] }),
4346
- /* @__PURE__ */ jsxs13(
5107
+ /* @__PURE__ */ jsxs15(
4347
5108
  BlockNoteView,
4348
5109
  {
4349
5110
  editor,
@@ -4355,21 +5116,22 @@ function LumirEditor({
4355
5116
  slashMenu: false,
4356
5117
  emojiPicker,
4357
5118
  filePanel,
4358
- tableHandles,
5119
+ tableHandles: false,
4359
5120
  onSelectionChange,
4360
5121
  children: [
4361
- formattingToolbar && /* @__PURE__ */ jsx20(
5122
+ tableHandles && /* @__PURE__ */ jsx22(LumirTableHandlesController, {}),
5123
+ formattingToolbar && /* @__PURE__ */ jsx22(
4362
5124
  FormattingToolbarController,
4363
5125
  {
4364
5126
  formattingToolbar: CustomFormattingToolbar
4365
5127
  }
4366
5128
  ),
4367
- linkToolbar && (linkPreview?.apiEndpoint ? /* @__PURE__ */ jsx20(LinkToolbarController, { linkToolbar: CustomLinkToolbar }) : /* @__PURE__ */ jsx20(LinkToolbarController, {})),
4368
- /* @__PURE__ */ jsx20(
5129
+ linkToolbar && (linkPreview?.apiEndpoint ? /* @__PURE__ */ jsx22(LinkToolbarController, { linkToolbar: CustomLinkToolbar }) : /* @__PURE__ */ jsx22(LinkToolbarController, {})),
5130
+ /* @__PURE__ */ jsx22(
4369
5131
  SuggestionMenuController,
4370
5132
  {
4371
5133
  triggerCharacter: "/",
4372
- getItems: useCallback16(
5134
+ getItems: useCallback18(
4373
5135
  async (query) => {
4374
5136
  const items = getDefaultReactSlashMenuItems(editor);
4375
5137
  const filtered = items.filter((item) => {
@@ -4413,7 +5175,7 @@ function LumirEditor({
4413
5175
  },
4414
5176
  aliases: ["html", "preview", "\uC6F9", "\uC6F9\uD398\uC774\uC9C0"],
4415
5177
  group: "Embeds",
4416
- icon: /* @__PURE__ */ jsxs13(
5178
+ icon: /* @__PURE__ */ jsxs15(
4417
5179
  "svg",
4418
5180
  {
4419
5181
  width: "18",
@@ -4425,8 +5187,8 @@ function LumirEditor({
4425
5187
  strokeLinecap: "round",
4426
5188
  strokeLinejoin: "round",
4427
5189
  children: [
4428
- /* @__PURE__ */ jsx20("polyline", { points: "16 18 22 12 16 6" }),
4429
- /* @__PURE__ */ jsx20("polyline", { points: "8 6 2 12 8 18" })
5190
+ /* @__PURE__ */ jsx22("polyline", { points: "16 18 22 12 16 6" }),
5191
+ /* @__PURE__ */ jsx22("polyline", { points: "8 6 2 12 8 18" })
4430
5192
  ]
4431
5193
  }
4432
5194
  ),
@@ -4451,7 +5213,7 @@ function LumirEditor({
4451
5213
  "\uD504\uB9AC\uBDF0"
4452
5214
  ],
4453
5215
  group: "Embeds",
4454
- icon: /* @__PURE__ */ jsxs13(
5216
+ icon: /* @__PURE__ */ jsxs15(
4455
5217
  "svg",
4456
5218
  {
4457
5219
  width: "18",
@@ -4463,14 +5225,31 @@ function LumirEditor({
4463
5225
  strokeLinecap: "round",
4464
5226
  strokeLinejoin: "round",
4465
5227
  children: [
4466
- /* @__PURE__ */ jsx20("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
4467
- /* @__PURE__ */ jsx20("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
5228
+ /* @__PURE__ */ jsx22("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
5229
+ /* @__PURE__ */ jsx22("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
4468
5230
  ]
4469
5231
  }
4470
5232
  ),
4471
5233
  subtext: "URL\uC758 \uBBF8\uB9AC\uBCF4\uAE30 \uCE74\uB4DC\uB97C \uC0BD\uC785"
4472
5234
  });
4473
5235
  }
5236
+ const enSlash = en.slash_menu;
5237
+ for (const it of allItems) {
5238
+ const enEntry = it.key ? enSlash[it.key] : void 0;
5239
+ if (!enEntry) continue;
5240
+ const extra = [...enEntry.aliases ?? [], enEntry.title].filter((s) => Boolean(s)).map((s) => s.toLowerCase());
5241
+ it.aliases = Array.from(
5242
+ /* @__PURE__ */ new Set([...it.aliases ?? [], ...extra])
5243
+ );
5244
+ }
5245
+ const groupOrder = [];
5246
+ for (const it of allItems) {
5247
+ const g = it.group ?? "";
5248
+ if (!groupOrder.includes(g)) groupOrder.push(g);
5249
+ }
5250
+ allItems.sort(
5251
+ (a, b) => groupOrder.indexOf(a.group ?? "") - groupOrder.indexOf(b.group ?? "")
5252
+ );
4474
5253
  if (!query) return allItems;
4475
5254
  const q = query.toLowerCase();
4476
5255
  return allItems.filter(
@@ -4483,21 +5262,21 @@ function LumirEditor({
4483
5262
  )
4484
5263
  }
4485
5264
  ),
4486
- !sideMenuAddButton && /* @__PURE__ */ jsx20(SideMenuController, { sideMenu: DragHandleOnlySideMenu })
5265
+ !sideMenuAddButton && /* @__PURE__ */ jsx22(SideMenuController, { sideMenu: DragHandleOnlySideMenu })
4487
5266
  ]
4488
5267
  }
4489
5268
  ),
4490
- isUploading && /* @__PURE__ */ jsxs13("div", { className: "lumirEditor-upload-overlay", children: [
4491
- /* @__PURE__ */ jsx20("div", { className: "lumirEditor-spinner" }),
4492
- uploadProgress !== null && /* @__PURE__ */ jsxs13("span", { className: "lumirEditor-upload-progress", children: [
5269
+ isUploading && /* @__PURE__ */ jsxs15("div", { className: "lumirEditor-upload-overlay", children: [
5270
+ /* @__PURE__ */ jsx22("div", { className: "lumirEditor-spinner" }),
5271
+ uploadProgress !== null && /* @__PURE__ */ jsxs15("span", { className: "lumirEditor-upload-progress", children: [
4493
5272
  uploadProgress,
4494
5273
  "%"
4495
5274
  ] })
4496
5275
  ] }),
4497
- errorMessage && /* @__PURE__ */ jsxs13("div", { className: "lumirEditor-error-toast", children: [
4498
- /* @__PURE__ */ jsx20("span", { className: "lumirEditor-error-icon", children: "\u26A0\uFE0F" }),
4499
- /* @__PURE__ */ jsx20("span", { className: "lumirEditor-error-message", children: errorMessage }),
4500
- /* @__PURE__ */ jsx20(
5276
+ errorMessage && /* @__PURE__ */ jsxs15("div", { className: "lumirEditor-error-toast", children: [
5277
+ /* @__PURE__ */ jsx22("span", { className: "lumirEditor-error-icon", children: "\u26A0\uFE0F" }),
5278
+ /* @__PURE__ */ jsx22("span", { className: "lumirEditor-error-message", children: errorMessage }),
5279
+ /* @__PURE__ */ jsx22(
4501
5280
  "button",
4502
5281
  {
4503
5282
  className: "lumirEditor-error-close",