@lumir-company/editor 0.4.11 → 0.4.13

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, useCallback as useCallback14, useState as useState8, useRef as useRef9 } from "react";
4
+ import { useEffect as useEffect8, useMemo as useMemo3, useCallback as useCallback16, useState as useState8, useRef as useRef9 } from "react";
5
5
  import {
6
6
  useCreateBlockNote,
7
7
  SideMenu as BlockSideMenu,
@@ -10,8 +10,9 @@ import {
10
10
  SuggestionMenuController,
11
11
  getDefaultReactSlashMenuItems,
12
12
  LinkToolbarController,
13
- useBlockNoteEditor,
14
- useComponentsContext,
13
+ FormattingToolbarController,
14
+ useBlockNoteEditor as useBlockNoteEditor3,
15
+ useComponentsContext as useComponentsContext3,
15
16
  EditLinkButton,
16
17
  OpenLinkButton,
17
18
  DeleteLinkButton
@@ -2810,6 +2811,426 @@ var LumirEditorError = class _LumirEditorError extends Error {
2810
2811
  }
2811
2812
  };
2812
2813
 
2814
+ // src/extensions/VerticalAlignmentExtension.ts
2815
+ import { Extension } from "@tiptap/core";
2816
+ var VerticalAlignmentExtension = Extension.create({
2817
+ name: "verticalAlignment",
2818
+ addGlobalAttributes() {
2819
+ return [
2820
+ {
2821
+ types: ["tableCell", "tableHeader"],
2822
+ attributes: {
2823
+ verticalAlignment: {
2824
+ default: "top",
2825
+ parseHTML: (element) => {
2826
+ return element.getAttribute("data-vertical-alignment") || "top";
2827
+ },
2828
+ renderHTML: (attributes) => {
2829
+ if (!attributes.verticalAlignment || attributes.verticalAlignment === "top") {
2830
+ return {};
2831
+ }
2832
+ return {
2833
+ "data-vertical-alignment": attributes.verticalAlignment
2834
+ };
2835
+ }
2836
+ }
2837
+ }
2838
+ }
2839
+ ];
2840
+ },
2841
+ addProseMirrorPlugins() {
2842
+ return [];
2843
+ }
2844
+ });
2845
+
2846
+ // src/components/CustomFormattingToolbar.tsx
2847
+ import {
2848
+ BasicTextStyleButton,
2849
+ BlockTypeSelect as BlockTypeSelect2,
2850
+ ColorStyleButton,
2851
+ CreateLinkButton,
2852
+ FileCaptionButton,
2853
+ FileDeleteButton,
2854
+ FileRenameButton,
2855
+ FileReplaceButton,
2856
+ FileDownloadButton,
2857
+ FilePreviewButton,
2858
+ FormattingToolbar,
2859
+ NestBlockButton,
2860
+ UnnestBlockButton,
2861
+ TableCellMergeButton
2862
+ } from "@blocknote/react";
2863
+
2864
+ // src/components/TextAlignButtonWithVA.tsx
2865
+ import {
2866
+ checkBlockHasDefaultProp,
2867
+ checkBlockTypeHasDefaultProp,
2868
+ mapTableCell
2869
+ } from "@blocknote/core";
2870
+ import { useCallback as useCallback14, useMemo } from "react";
2871
+ import {
2872
+ useComponentsContext,
2873
+ useBlockNoteEditor,
2874
+ useSelectedBlocks
2875
+ } 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
+ import { jsx as jsx17 } from "react/jsx-runtime";
2902
+ var icons = {
2903
+ 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" }) }),
2904
+ center: /* @__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: "M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z" }) }),
2905
+ right: /* @__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: "M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z" }) }),
2906
+ justify: /* @__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: "M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zM3 3v2h18V3H3z" }) })
2907
+ };
2908
+ var tooltipMap = {
2909
+ left: "\uC67C\uCABD \uC815\uB82C",
2910
+ center: "\uAC00\uC6B4\uB370 \uC815\uB82C",
2911
+ right: "\uC624\uB978\uCABD \uC815\uB82C",
2912
+ justify: "\uC591\uCABD \uC815\uB82C"
2913
+ };
2914
+ var TextAlignButtonWithVA = (props) => {
2915
+ const Components = useComponentsContext();
2916
+ const editor = useBlockNoteEditor();
2917
+ const selectedBlocks = useSelectedBlocks(editor);
2918
+ const textAlignment = useMemo(() => {
2919
+ const block = selectedBlocks[0];
2920
+ if (checkBlockHasDefaultProp("textAlignment", block, editor)) {
2921
+ return block.props.textAlignment;
2922
+ }
2923
+ if (block.type === "table") {
2924
+ const cellSelection = editor.tableHandles?.getCellSelection();
2925
+ if (!cellSelection) {
2926
+ return;
2927
+ }
2928
+ const allCellsInTable = cellSelection.cells.map(
2929
+ ({ row, col }) => mapTableCell(
2930
+ block.content.rows[row].cells[col]
2931
+ ).props.textAlignment
2932
+ );
2933
+ const firstAlignment = allCellsInTable[0];
2934
+ if (allCellsInTable.every((alignment) => alignment === firstAlignment)) {
2935
+ return firstAlignment;
2936
+ }
2937
+ }
2938
+ return;
2939
+ }, [editor, selectedBlocks]);
2940
+ const setTextAlignment = useCallback14(
2941
+ (newAlignment) => {
2942
+ editor.focus();
2943
+ for (const block of selectedBlocks) {
2944
+ if (block.type === "table") {
2945
+ const tiptap = editor._tiptapEditor;
2946
+ if (!tiptap) continue;
2947
+ const positions = getSelectedCellPositions(editor);
2948
+ if (positions.length === 0) continue;
2949
+ const { state } = tiptap;
2950
+ let tr = state.tr;
2951
+ for (const pos of positions) {
2952
+ const node = tr.doc.nodeAt(pos);
2953
+ if (node) {
2954
+ tr = tr.setNodeMarkup(pos, void 0, {
2955
+ ...node.attrs,
2956
+ textAlignment: newAlignment
2957
+ });
2958
+ }
2959
+ }
2960
+ tiptap.view?.dispatch(tr);
2961
+ } else if (checkBlockTypeHasDefaultProp("textAlignment", block.type, editor)) {
2962
+ editor.updateBlock(block, {
2963
+ props: { textAlignment: newAlignment }
2964
+ });
2965
+ }
2966
+ }
2967
+ },
2968
+ [editor, selectedBlocks]
2969
+ );
2970
+ const show = useMemo(() => {
2971
+ return !!selectedBlocks.find(
2972
+ (block) => "textAlignment" in block.props || block.type === "table" && block.children
2973
+ );
2974
+ }, [selectedBlocks]);
2975
+ if (!show || !editor.isEditable) {
2976
+ return null;
2977
+ }
2978
+ return /* @__PURE__ */ jsx17(
2979
+ Components.FormattingToolbar.Button,
2980
+ {
2981
+ className: "bn-button",
2982
+ "data-test": `alignText${props.textAlignment.slice(0, 1).toUpperCase() + props.textAlignment.slice(1)}`,
2983
+ onClick: () => setTextAlignment(props.textAlignment),
2984
+ isSelected: textAlignment === props.textAlignment,
2985
+ label: tooltipMap[props.textAlignment],
2986
+ mainTooltip: tooltipMap[props.textAlignment],
2987
+ icon: icons[props.textAlignment]
2988
+ }
2989
+ );
2990
+ };
2991
+
2992
+ // src/components/VerticalAlignButton.tsx
2993
+ import { useCallback as useCallback15, useMemo as useMemo2 } from "react";
2994
+ import {
2995
+ useBlockNoteEditor as useBlockNoteEditor2,
2996
+ useComponentsContext as useComponentsContext2,
2997
+ useSelectedBlocks as useSelectedBlocks2
2998
+ } from "@blocknote/react";
2999
+ import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
3000
+ var icons2 = {
3001
+ top: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
3002
+ /* @__PURE__ */ jsx18("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1" }),
3003
+ /* @__PURE__ */ jsx18("line", { x1: "5", y1: "5", x2: "11", y2: "5" })
3004
+ ] }),
3005
+ middle: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
3006
+ /* @__PURE__ */ jsx18("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1" }),
3007
+ /* @__PURE__ */ jsx18("line", { x1: "5", y1: "8", x2: "11", y2: "8" })
3008
+ ] }),
3009
+ bottom: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
3010
+ /* @__PURE__ */ jsx18("rect", { x: "2", y: "2", width: "12", height: "12", rx: "1" }),
3011
+ /* @__PURE__ */ jsx18("line", { x1: "5", y1: "11", x2: "11", y2: "11" })
3012
+ ] })
3013
+ };
3014
+ var tooltips = {
3015
+ top: "\uC704\uCABD \uC815\uB82C",
3016
+ middle: "\uC138\uB85C \uAC00\uC6B4\uB370 \uC815\uB82C",
3017
+ bottom: "\uC544\uB798\uCABD \uC815\uB82C"
3018
+ };
3019
+ function getCurrentVerticalAlignment(editor) {
3020
+ const tiptap = editor._tiptapEditor;
3021
+ if (!tiptap) return void 0;
3022
+ const positions = getSelectedCellPositions(editor);
3023
+ if (positions.length === 0) return void 0;
3024
+ const { state } = tiptap;
3025
+ const alignments = positions.map((pos) => {
3026
+ const node = state.doc.nodeAt(pos);
3027
+ return node?.attrs?.verticalAlignment || "top";
3028
+ });
3029
+ const first = alignments[0];
3030
+ return alignments.every((a) => a === first) ? first : void 0;
3031
+ }
3032
+ var VerticalAlignButton = (props) => {
3033
+ const Components = useComponentsContext2();
3034
+ const editor = useBlockNoteEditor2();
3035
+ const selectedBlocks = useSelectedBlocks2(editor);
3036
+ const currentAlignment = useMemo2(() => {
3037
+ return getCurrentVerticalAlignment(editor);
3038
+ }, [editor, selectedBlocks]);
3039
+ const setVerticalAlignment = useCallback15(
3040
+ (alignment) => {
3041
+ const tiptap = editor._tiptapEditor;
3042
+ if (!tiptap) return;
3043
+ const positions = getSelectedCellPositions(editor);
3044
+ if (positions.length === 0) return;
3045
+ editor.focus();
3046
+ const { state } = tiptap;
3047
+ let tr = state.tr;
3048
+ for (const pos of positions) {
3049
+ const node = tr.doc.nodeAt(pos);
3050
+ if (node) {
3051
+ tr = tr.setNodeMarkup(pos, void 0, {
3052
+ ...node.attrs,
3053
+ verticalAlignment: alignment
3054
+ });
3055
+ }
3056
+ }
3057
+ tiptap.view?.dispatch(tr);
3058
+ },
3059
+ [editor]
3060
+ );
3061
+ const isInTable = useMemo2(() => {
3062
+ return selectedBlocks.some((block) => block.type === "table");
3063
+ }, [selectedBlocks]);
3064
+ if (!isInTable || !editor.isEditable) {
3065
+ return null;
3066
+ }
3067
+ return /* @__PURE__ */ jsx18(
3068
+ Components.FormattingToolbar.Button,
3069
+ {
3070
+ className: "bn-button",
3071
+ "data-test": `verticalAlign${props.verticalAlignment.charAt(0).toUpperCase() + props.verticalAlignment.slice(1)}`,
3072
+ onClick: () => setVerticalAlignment(props.verticalAlignment),
3073
+ isSelected: currentAlignment === props.verticalAlignment,
3074
+ label: tooltips[props.verticalAlignment],
3075
+ mainTooltip: tooltips[props.verticalAlignment],
3076
+ icon: icons2[props.verticalAlignment]
3077
+ }
3078
+ );
3079
+ };
3080
+
3081
+ // src/components/CustomFormattingToolbar.tsx
3082
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
3083
+ 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(
3095
+ BasicTextStyleButton,
3096
+ {
3097
+ basicTextStyle: "italic"
3098
+ },
3099
+ "italicStyleButton"
3100
+ ),
3101
+ /* @__PURE__ */ jsx19(
3102
+ BasicTextStyleButton,
3103
+ {
3104
+ basicTextStyle: "underline"
3105
+ },
3106
+ "underlineStyleButton"
3107
+ ),
3108
+ /* @__PURE__ */ jsx19(
3109
+ BasicTextStyleButton,
3110
+ {
3111
+ basicTextStyle: "strike"
3112
+ },
3113
+ "strikeStyleButton"
3114
+ ),
3115
+ /* @__PURE__ */ jsx19(TextAlignButtonWithVA, { textAlignment: "left" }, "textAlignLeftButton"),
3116
+ /* @__PURE__ */ jsx19(
3117
+ TextAlignButtonWithVA,
3118
+ {
3119
+ textAlignment: "center"
3120
+ },
3121
+ "textAlignCenterButton"
3122
+ ),
3123
+ /* @__PURE__ */ jsx19(TextAlignButtonWithVA, { textAlignment: "right" }, "textAlignRightButton"),
3124
+ /* @__PURE__ */ jsx19(
3125
+ VerticalAlignButton,
3126
+ {
3127
+ verticalAlignment: "top"
3128
+ },
3129
+ "verticalAlignTop"
3130
+ ),
3131
+ /* @__PURE__ */ jsx19(
3132
+ VerticalAlignButton,
3133
+ {
3134
+ verticalAlignment: "middle"
3135
+ },
3136
+ "verticalAlignMiddle"
3137
+ ),
3138
+ /* @__PURE__ */ jsx19(
3139
+ VerticalAlignButton,
3140
+ {
3141
+ verticalAlignment: "bottom"
3142
+ },
3143
+ "verticalAlignBottom"
3144
+ ),
3145
+ /* @__PURE__ */ jsx19(ColorStyleButton, {}, "colorStyleButton"),
3146
+ /* @__PURE__ */ jsx19(NestBlockButton, {}, "nestBlockButton"),
3147
+ /* @__PURE__ */ jsx19(UnnestBlockButton, {}, "unnestBlockButton"),
3148
+ /* @__PURE__ */ jsx19(CreateLinkButton, {}, "createLinkButton")
3149
+ ] });
3150
+ };
3151
+
3152
+ // src/utils/table-vertical-alignment.ts
3153
+ function injectVerticalAlignment(blocks, editor) {
3154
+ const tiptap = editor?._tiptapEditor;
3155
+ if (!tiptap) return blocks;
3156
+ const { doc } = tiptap.state;
3157
+ const tableVAMap = buildTableVerticalAlignmentMap(doc);
3158
+ if (tableVAMap.size === 0) return blocks;
3159
+ return patchBlocks(blocks, tableVAMap);
3160
+ }
3161
+ function buildTableVerticalAlignmentMap(doc) {
3162
+ const result = /* @__PURE__ */ new Map();
3163
+ doc.descendants((node) => {
3164
+ if (node.type.name === "blockContainer") {
3165
+ const blockId = node.attrs?.id;
3166
+ const contentNode = node.firstChild;
3167
+ if (blockId && contentNode?.type.name === "table") {
3168
+ const cells = [];
3169
+ let rowIndex = 0;
3170
+ contentNode.forEach((rowNode) => {
3171
+ if (rowNode.type.name === "tableRow") {
3172
+ let colIndex = 0;
3173
+ rowNode.forEach((cellNode) => {
3174
+ const va = cellNode.attrs?.verticalAlignment;
3175
+ if (va && va !== "top") {
3176
+ cells.push({ row: rowIndex, col: colIndex, va });
3177
+ }
3178
+ colIndex++;
3179
+ });
3180
+ rowIndex++;
3181
+ }
3182
+ });
3183
+ if (cells.length > 0) {
3184
+ result.set(blockId, cells);
3185
+ }
3186
+ }
3187
+ return false;
3188
+ }
3189
+ });
3190
+ return result;
3191
+ }
3192
+ function patchBlocks(blocks, tableVAMap) {
3193
+ return blocks.map((block) => {
3194
+ if (block.type !== "table" || !block.id || !block.content) {
3195
+ if (block.children && block.children.length > 0) {
3196
+ return {
3197
+ ...block,
3198
+ children: patchBlocks(block.children, tableVAMap)
3199
+ };
3200
+ }
3201
+ return block;
3202
+ }
3203
+ const cells = tableVAMap.get(block.id);
3204
+ if (!cells || cells.length === 0) {
3205
+ return block;
3206
+ }
3207
+ const content = block.content;
3208
+ if (content.type !== "tableContent" || !content.rows) {
3209
+ return block;
3210
+ }
3211
+ const newRows = content.rows.map((row, rowIndex) => {
3212
+ const newCells = row.cells.map((cell, colIndex) => {
3213
+ const match = cells.find(
3214
+ (c) => c.row === rowIndex && c.col === colIndex
3215
+ );
3216
+ if (!match) return cell;
3217
+ if (cell && typeof cell === "object" && cell.type === "tableCell") {
3218
+ return {
3219
+ ...cell,
3220
+ props: { ...cell.props, verticalAlignment: match.va }
3221
+ };
3222
+ }
3223
+ return cell;
3224
+ });
3225
+ return { ...row, cells: newCells };
3226
+ });
3227
+ return {
3228
+ ...block,
3229
+ content: { ...content, rows: newRows }
3230
+ };
3231
+ });
3232
+ }
3233
+
2813
3234
  // src/constants/limits.ts
2814
3235
  var MAX_FILE_SIZE = 10 * 1024 * 1024;
2815
3236
  var MAX_VIDEO_FILE_SIZE = 100 * 1024 * 1024;
@@ -2829,7 +3250,7 @@ var ALLOWED_VIDEO_EXTENSIONS = [
2829
3250
  ];
2830
3251
 
2831
3252
  // src/components/LumirEditor.tsx
2832
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
3253
+ import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
2833
3254
  var DEBUG_LOG = (loc, msg, data) => {
2834
3255
  const p = fetch("http://127.0.0.1:7686/ingest/1f8ee1c5-0cf0-4ae7-91ed-5ea7ed17130a", {
2835
3256
  method: "POST",
@@ -3054,9 +3475,9 @@ var findBlockWithLink = (blocks, targetUrl) => {
3054
3475
  return null;
3055
3476
  };
3056
3477
  var ConvertToPreviewButton = ({ url }) => {
3057
- const editor = useBlockNoteEditor();
3058
- const Components = useComponentsContext();
3059
- return /* @__PURE__ */ jsx17(
3478
+ const editor = useBlockNoteEditor3();
3479
+ const Components = useComponentsContext3();
3480
+ return /* @__PURE__ */ jsx20(
3060
3481
  Components.LinkToolbar.Button,
3061
3482
  {
3062
3483
  className: "bn-button",
@@ -3075,29 +3496,29 @@ var ConvertToPreviewButton = ({ url }) => {
3075
3496
  console.error("Convert to link preview failed:", err);
3076
3497
  }
3077
3498
  },
3078
- icon: /* @__PURE__ */ jsxs11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
3079
- /* @__PURE__ */ jsx17("rect", { x: "1", y: "3", width: "14", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }),
3080
- /* @__PURE__ */ jsx17("line", { x1: "1", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "1.5" }),
3081
- /* @__PURE__ */ jsx17("circle", { cx: "5", cy: "6.5", r: "1.5", stroke: "currentColor", strokeWidth: "1", fill: "none" })
3499
+ icon: /* @__PURE__ */ jsxs13("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
3500
+ /* @__PURE__ */ jsx20("rect", { x: "1", y: "3", width: "14", height: "10", rx: "2", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }),
3501
+ /* @__PURE__ */ jsx20("line", { x1: "1", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "1.5" }),
3502
+ /* @__PURE__ */ jsx20("circle", { cx: "5", cy: "6.5", r: "1.5", stroke: "currentColor", strokeWidth: "1", fill: "none" })
3082
3503
  ] })
3083
3504
  }
3084
3505
  );
3085
3506
  };
3086
3507
  var CustomLinkToolbar = (props) => {
3087
- const editor = useBlockNoteEditor();
3088
- const Components = useComponentsContext();
3508
+ const editor = useBlockNoteEditor3();
3509
+ const Components = useComponentsContext3();
3089
3510
  const hasLinkPreview = !!editor?._linkPreviewApiEndpoint;
3090
- return /* @__PURE__ */ jsxs11(
3511
+ return /* @__PURE__ */ jsxs13(
3091
3512
  Components.LinkToolbar.Root,
3092
3513
  {
3093
3514
  className: "bn-toolbar bn-link-toolbar",
3094
3515
  onMouseEnter: props.stopHideTimer,
3095
3516
  onMouseLeave: props.startHideTimer,
3096
3517
  children: [
3097
- /* @__PURE__ */ jsx17(EditLinkButton, { url: props.url, text: props.text, editLink: props.editLink }),
3098
- /* @__PURE__ */ jsx17(OpenLinkButton, { url: props.url }),
3099
- /* @__PURE__ */ jsx17(DeleteLinkButton, { deleteLink: props.deleteLink }),
3100
- hasLinkPreview && /* @__PURE__ */ jsx17(ConvertToPreviewButton, { url: props.url })
3518
+ /* @__PURE__ */ jsx20(EditLinkButton, { url: props.url, text: props.text, editLink: props.editLink }),
3519
+ /* @__PURE__ */ jsx20(OpenLinkButton, { url: props.url }),
3520
+ /* @__PURE__ */ jsx20(DeleteLinkButton, { deleteLink: props.deleteLink }),
3521
+ hasLinkPreview && /* @__PURE__ */ jsx20(ConvertToPreviewButton, { url: props.url })
3101
3522
  ]
3102
3523
  }
3103
3524
  );
@@ -3147,7 +3568,7 @@ function LumirEditor({
3147
3568
  const floatingMenuFileInputRef = useRef9(null);
3148
3569
  const floatingMenuBlockRef = useRef9(null);
3149
3570
  const floatingMenuUploadStartTimeRef = useRef9(0);
3150
- const handleError = useCallback14(
3571
+ const handleError = useCallback16(
3151
3572
  (error) => {
3152
3573
  onError?.(error);
3153
3574
  setErrorMessage(error.getUserMessage());
@@ -3155,10 +3576,10 @@ function LumirEditor({
3155
3576
  },
3156
3577
  [onError]
3157
3578
  );
3158
- const validatedContent = useMemo(() => {
3579
+ const validatedContent = useMemo3(() => {
3159
3580
  return ContentUtils.validateContent(initialContent, initialEmptyBlocks);
3160
3581
  }, [initialContent, initialEmptyBlocks]);
3161
- const tableConfig = useMemo(() => {
3582
+ const tableConfig = useMemo3(() => {
3162
3583
  return EditorConfig.getDefaultTableConfig(tables);
3163
3584
  }, [
3164
3585
  tables?.splitCells,
@@ -3166,10 +3587,10 @@ function LumirEditor({
3166
3587
  tables?.cellTextColor,
3167
3588
  tables?.headers
3168
3589
  ]);
3169
- const headingConfig = useMemo(() => {
3590
+ const headingConfig = useMemo3(() => {
3170
3591
  return EditorConfig.getDefaultHeadingConfig(heading);
3171
3592
  }, [heading?.levels?.join(",") ?? ""]);
3172
- const disabledExtensions = useMemo(() => {
3593
+ const disabledExtensions = useMemo3(() => {
3173
3594
  return EditorConfig.getDisabledExtensions(
3174
3595
  disableExtensions,
3175
3596
  allowVideoUpload,
@@ -3188,7 +3609,7 @@ function LumirEditor({
3188
3609
  useEffect8(() => {
3189
3610
  fileNameTransformRef.current = s3Upload?.fileNameTransform;
3190
3611
  }, [s3Upload?.fileNameTransform]);
3191
- const memoizedS3Upload = useMemo(() => {
3612
+ const memoizedS3Upload = useMemo3(() => {
3192
3613
  if (!s3Upload) return void 0;
3193
3614
  return {
3194
3615
  apiEndpoint: s3Upload.apiEndpoint,
@@ -3229,6 +3650,9 @@ function LumirEditor({
3229
3650
  defaultStyles,
3230
3651
  // 확장 비활성: 비디오/오디오/파일 제어
3231
3652
  disableExtensions: disabledExtensions,
3653
+ _tiptapOptions: {
3654
+ extensions: [VerticalAlignmentExtension]
3655
+ },
3232
3656
  placeholders: placeholder ? { default: placeholder, emptyDocument: placeholder } : void 0,
3233
3657
  tabBehavior,
3234
3658
  trailingBlock,
@@ -3408,7 +3832,8 @@ function LumirEditor({
3408
3832
  if (!editor || !onContentChange) return;
3409
3833
  const handleContentChange = () => {
3410
3834
  const blocks = editor.topLevelBlocks;
3411
- onContentChange(blocks);
3835
+ const patched = injectVerticalAlignment(blocks, editor);
3836
+ onContentChange(patched);
3412
3837
  };
3413
3838
  return editor.onEditorContentChange(handleContentChange);
3414
3839
  }, [editor, onContentChange]);
@@ -3563,20 +3988,20 @@ function LumirEditor({
3563
3988
  el.removeEventListener("drop", handleDrop, { capture: true });
3564
3989
  };
3565
3990
  }, [editor, allowVideoUpload]);
3566
- const computedSideMenu = useMemo(() => {
3991
+ const computedSideMenu = useMemo3(() => {
3567
3992
  return sideMenuAddButton ? sideMenu : false;
3568
3993
  }, [sideMenuAddButton, sideMenu]);
3569
- const DragHandleOnlySideMenu = useMemo(() => {
3570
- return (props) => /* @__PURE__ */ jsx17(BlockSideMenu, { ...props, children: /* @__PURE__ */ jsx17(DragHandleButton, { ...props }) });
3994
+ const DragHandleOnlySideMenu = useMemo3(() => {
3995
+ return (props) => /* @__PURE__ */ jsx20(BlockSideMenu, { ...props, children: /* @__PURE__ */ jsx20(DragHandleButton, { ...props }) });
3571
3996
  }, []);
3572
- return /* @__PURE__ */ jsxs11(
3997
+ return /* @__PURE__ */ jsxs13(
3573
3998
  "div",
3574
3999
  {
3575
4000
  className: cn("lumirEditor", className),
3576
4001
  style: { position: "relative", display: "flex", flexDirection: "column" },
3577
4002
  children: [
3578
- floatingMenu && editor && /* @__PURE__ */ jsxs11(Fragment5, { children: [
3579
- /* @__PURE__ */ jsx17(
4003
+ floatingMenu && editor && /* @__PURE__ */ jsxs13(Fragment5, { children: [
4004
+ /* @__PURE__ */ jsx20(
3580
4005
  "input",
3581
4006
  {
3582
4007
  ref: floatingMenuFileInputRef,
@@ -3647,7 +4072,7 @@ function LumirEditor({
3647
4072
  }
3648
4073
  }
3649
4074
  ),
3650
- /* @__PURE__ */ jsx17(
4075
+ /* @__PURE__ */ jsx20(
3651
4076
  FloatingMenu,
3652
4077
  {
3653
4078
  editor,
@@ -3679,13 +4104,13 @@ function LumirEditor({
3679
4104
  }
3680
4105
  )
3681
4106
  ] }),
3682
- /* @__PURE__ */ jsxs11(
4107
+ /* @__PURE__ */ jsxs13(
3683
4108
  BlockNoteView,
3684
4109
  {
3685
4110
  editor,
3686
4111
  editable,
3687
4112
  theme,
3688
- formattingToolbar,
4113
+ formattingToolbar: false,
3689
4114
  linkToolbar: false,
3690
4115
  sideMenu: computedSideMenu,
3691
4116
  slashMenu: false,
@@ -3694,12 +4119,18 @@ function LumirEditor({
3694
4119
  tableHandles,
3695
4120
  onSelectionChange,
3696
4121
  children: [
3697
- linkToolbar && (linkPreview?.apiEndpoint ? /* @__PURE__ */ jsx17(LinkToolbarController, { linkToolbar: CustomLinkToolbar }) : /* @__PURE__ */ jsx17(LinkToolbarController, {})),
3698
- /* @__PURE__ */ jsx17(
4122
+ formattingToolbar && /* @__PURE__ */ jsx20(
4123
+ FormattingToolbarController,
4124
+ {
4125
+ formattingToolbar: CustomFormattingToolbar
4126
+ }
4127
+ ),
4128
+ linkToolbar && (linkPreview?.apiEndpoint ? /* @__PURE__ */ jsx20(LinkToolbarController, { linkToolbar: CustomLinkToolbar }) : /* @__PURE__ */ jsx20(LinkToolbarController, {})),
4129
+ /* @__PURE__ */ jsx20(
3699
4130
  SuggestionMenuController,
3700
4131
  {
3701
4132
  triggerCharacter: "/",
3702
- getItems: useCallback14(
4133
+ getItems: useCallback16(
3703
4134
  async (query) => {
3704
4135
  const items = getDefaultReactSlashMenuItems(editor);
3705
4136
  const filtered = items.filter((item) => {
@@ -3743,7 +4174,7 @@ function LumirEditor({
3743
4174
  },
3744
4175
  aliases: ["html", "preview", "\uC6F9", "\uC6F9\uD398\uC774\uC9C0"],
3745
4176
  group: "Embeds",
3746
- icon: /* @__PURE__ */ jsxs11(
4177
+ icon: /* @__PURE__ */ jsxs13(
3747
4178
  "svg",
3748
4179
  {
3749
4180
  width: "18",
@@ -3755,8 +4186,8 @@ function LumirEditor({
3755
4186
  strokeLinecap: "round",
3756
4187
  strokeLinejoin: "round",
3757
4188
  children: [
3758
- /* @__PURE__ */ jsx17("polyline", { points: "16 18 22 12 16 6" }),
3759
- /* @__PURE__ */ jsx17("polyline", { points: "8 6 2 12 8 18" })
4189
+ /* @__PURE__ */ jsx20("polyline", { points: "16 18 22 12 16 6" }),
4190
+ /* @__PURE__ */ jsx20("polyline", { points: "8 6 2 12 8 18" })
3760
4191
  ]
3761
4192
  }
3762
4193
  ),
@@ -3781,7 +4212,7 @@ function LumirEditor({
3781
4212
  "\uD504\uB9AC\uBDF0"
3782
4213
  ],
3783
4214
  group: "Embeds",
3784
- icon: /* @__PURE__ */ jsxs11(
4215
+ icon: /* @__PURE__ */ jsxs13(
3785
4216
  "svg",
3786
4217
  {
3787
4218
  width: "18",
@@ -3793,8 +4224,8 @@ function LumirEditor({
3793
4224
  strokeLinecap: "round",
3794
4225
  strokeLinejoin: "round",
3795
4226
  children: [
3796
- /* @__PURE__ */ jsx17("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
3797
- /* @__PURE__ */ jsx17("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
4227
+ /* @__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" }),
4228
+ /* @__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" })
3798
4229
  ]
3799
4230
  }
3800
4231
  ),
@@ -3813,21 +4244,21 @@ function LumirEditor({
3813
4244
  )
3814
4245
  }
3815
4246
  ),
3816
- !sideMenuAddButton && /* @__PURE__ */ jsx17(SideMenuController, { sideMenu: DragHandleOnlySideMenu })
4247
+ !sideMenuAddButton && /* @__PURE__ */ jsx20(SideMenuController, { sideMenu: DragHandleOnlySideMenu })
3817
4248
  ]
3818
4249
  }
3819
4250
  ),
3820
- isUploading && /* @__PURE__ */ jsxs11("div", { className: "lumirEditor-upload-overlay", children: [
3821
- /* @__PURE__ */ jsx17("div", { className: "lumirEditor-spinner" }),
3822
- uploadProgress !== null && /* @__PURE__ */ jsxs11("span", { className: "lumirEditor-upload-progress", children: [
4251
+ isUploading && /* @__PURE__ */ jsxs13("div", { className: "lumirEditor-upload-overlay", children: [
4252
+ /* @__PURE__ */ jsx20("div", { className: "lumirEditor-spinner" }),
4253
+ uploadProgress !== null && /* @__PURE__ */ jsxs13("span", { className: "lumirEditor-upload-progress", children: [
3823
4254
  uploadProgress,
3824
4255
  "%"
3825
4256
  ] })
3826
4257
  ] }),
3827
- errorMessage && /* @__PURE__ */ jsxs11("div", { className: "lumirEditor-error-toast", children: [
3828
- /* @__PURE__ */ jsx17("span", { className: "lumirEditor-error-icon", children: "\u26A0\uFE0F" }),
3829
- /* @__PURE__ */ jsx17("span", { className: "lumirEditor-error-message", children: errorMessage }),
3830
- /* @__PURE__ */ jsx17(
4258
+ errorMessage && /* @__PURE__ */ jsxs13("div", { className: "lumirEditor-error-toast", children: [
4259
+ /* @__PURE__ */ jsx20("span", { className: "lumirEditor-error-icon", children: "\u26A0\uFE0F" }),
4260
+ /* @__PURE__ */ jsx20("span", { className: "lumirEditor-error-message", children: errorMessage }),
4261
+ /* @__PURE__ */ jsx20(
3831
4262
  "button",
3832
4263
  {
3833
4264
  className: "lumirEditor-error-close",