{"version":3,"sources":["../src/components/LumirEditor.tsx","../src/utils/cn.ts","../src/utils/s3-uploader.ts","../src/blocks/HtmlPreview.tsx","../src/blocks/LinkPreview.tsx","../src/blocks/defaultLogo.ts","../src/blocks/VideoBlock.tsx","../src/blocks/columns/ColumnList.ts","../src/blocks/columns/columnNormalization.ts","../src/blocks/columns/columnDnd.ts","../src/blocks/columns/mergeColumns.ts","../src/blocks/columns/Column.ts","../src/styles/FontSizeStyle.tsx","../src/components/FloatingMenu/index.tsx","../src/components/FloatingMenu/Icons.tsx","../src/components/FloatingMenu/components/ToolbarDivider.tsx","../src/components/FloatingMenu/components/UndoRedoButtons.tsx","../src/components/FloatingMenu/components/TextStyleButton.tsx","../src/components/FloatingMenu/components/AlignButton.tsx","../src/utils/prosemirror-table-utils.ts","../src/components/FloatingMenu/components/ListButton.tsx","../src/components/FloatingMenu/components/ImageButton.tsx","../src/components/FloatingMenu/components/ColorButton.tsx","../src/constants/colors.ts","../src/components/FloatingMenu/components/FontSizeButton.tsx","../src/components/FloatingMenu/components/LinkButton.tsx","../src/components/FloatingMenu/components/TableButton.tsx","../src/components/FloatingMenu/components/HTMLImportButton.tsx","../src/components/FloatingMenu/components/BlockTypeSelect.tsx","../src/errors/LumirEditorError.ts","../src/extensions/VerticalAlignmentExtension.ts","../src/extensions/RowHeightExtension.ts","../src/extensions/rowResizing.ts","../src/constants/limits.ts","../src/extensions/tableScaling.ts","../src/extensions/tableCellAttrPreserve.ts","../src/extensions/TableAlignmentExtension.ts","../src/extensions/TableSelectAllExtension.ts","../src/extensions/InactiveSelectionExtension.ts","../src/blocks/columns/insertColumns.ts","../src/components/CustomFormattingToolbar.tsx","../src/components/TextAlignButtonWithVA.tsx","../src/components/VerticalAlignButton.tsx","../src/components/TableAlignButton.tsx","../src/components/FontSizeButton.tsx","../src/components/color/LumirColorControls.tsx","../src/components/LumirDragHandleMenu.tsx","../src/components/LumirTableHandlesController.tsx","../src/components/hooks/useFocusedCellHandlePositioning.ts","../src/utils/table-vertical-alignment.ts","../src/utils/font-size-serialization.ts","../src/utils/table-delete.ts","../src/utils/excel-paste.ts","../src/utils/table-paste-fit.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useCallback, useState, useRef } from \"react\";\nimport {\n  useCreateBlockNote,\n  SideMenu as BlockSideMenu,\n  SideMenuController,\n  DragHandleButton,\n  SuggestionMenuController,\n  getDefaultReactSlashMenuItems,\n  LinkToolbarController,\n  FormattingToolbarController,\n  useBlockNoteEditor,\n  useComponentsContext,\n  EditLinkButton,\n  OpenLinkButton,\n  DeleteLinkButton,\n} from \"@blocknote/react\";\nimport { BlockNoteView } from \"@blocknote/mantine\";\nimport { insertOrUpdateBlock } from \"@blocknote/core\";\nimport { ko, en } from \"@blocknote/core/locales\";\nimport { cn } from \"../utils/cn\";\n\nimport type { DefaultPartialBlock, LumirEditorProps } from \"../types\";\n\nimport { createS3Uploader } from \"../utils/s3-uploader\";\nimport { schema } from \"../blocks/HtmlPreview\";\nimport { FloatingMenu } from \"./FloatingMenu\";\nimport { LumirEditorError } from \"../errors/LumirEditorError\";\nimport { VerticalAlignmentExtension } from \"../extensions/VerticalAlignmentExtension\";\nimport { RowHeightExtension } from \"../extensions/RowHeightExtension\";\nimport { TableAlignmentExtension } from \"../extensions/TableAlignmentExtension\";\nimport { TableSelectAllExtension } from \"../extensions/TableSelectAllExtension\";\nimport { InactiveSelectionExtension } from \"../extensions/InactiveSelectionExtension\";\nimport { insertTwoColumns } from \"../blocks/columns/insertColumns\";\nimport { CustomFormattingToolbar } from \"./CustomFormattingToolbar\";\nimport { LumirDragHandleMenu } from \"./LumirDragHandleMenu\";\nimport { LumirTableHandlesController } from \"./LumirTableHandlesController\";\nimport {\n  injectTableCellAttrs,\n  injectTableBlockAttrs,\n} from \"../utils/table-vertical-alignment\";\nimport {\n  liftFontSize,\n  lowerFontSize,\n} from \"../utils/font-size-serialization\";\nimport { isInTableCell } from \"../utils/prosemirror-table-utils\";\nimport { removeFocusedRowOrColumn } from \"../utils/table-delete\";\nimport { normalizeExcelTableHtml } from \"../utils/excel-paste\";\nimport {\n  computeFittedColumnWidthsPerTable,\n  collectTableBlocks,\n  applyFittedWidthsToNewTables,\n} from \"../utils/table-paste-fit\";\nimport {\n  MAX_FILE_SIZE,\n  MAX_VIDEO_FILE_SIZE,\n  BLOCKED_EXTENSIONS,\n  ALLOWED_VIDEO_MIME_TYPES,\n  ALLOWED_VIDEO_EXTENSIONS,\n} from \"../constants/limits\";\n\n// #region agent log\nconst DEBUG_LOG = (loc: string, msg: string, data: Record<string, unknown>) => {\n  const p = fetch(\"http://127.0.0.1:7686/ingest/1f8ee1c5-0cf0-4ae7-91ed-5ea7ed17130a\", {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application/json\", \"X-Debug-Session-Id\": \"b73262\" },\n    body: JSON.stringify({\n      sessionId: \"b73262\",\n      location: loc,\n      message: msg,\n      data,\n      timestamp: Date.now(),\n    }),\n  });\n  if (p && typeof (p as Promise<unknown>).catch === \"function\") (p as Promise<unknown>).catch(() => {});\n};\n// #endregion\n\n// ==========================================\n// 유틸리티 클래스들\n// ==========================================\n\n/**\n * 콘텐츠 관리 유틸리티\n * 기본 블록 생성 및 콘텐츠 검증 로직을 담당\n */\nexport class ContentUtils {\n  /**\n   * JSON 문자열의 유효성을 검증합니다\n   * @param jsonString 검증할 JSON 문자열\n   * @returns 유효한 JSON 문자열인지 여부\n   */\n  static isValidJSONString(jsonString: string): boolean {\n    try {\n      const parsed = JSON.parse(jsonString);\n      return Array.isArray(parsed);\n    } catch {\n      return false;\n    }\n  }\n\n  /**\n   * JSON 문자열을 DefaultPartialBlock 배열로 파싱합니다\n   * @param jsonString JSON 문자열\n   * @returns 파싱된 블록 배열 또는 null (파싱 실패 시)\n   */\n  static parseJSONContent(jsonString: string): DefaultPartialBlock[] | null {\n    try {\n      const parsed = JSON.parse(jsonString);\n      if (Array.isArray(parsed)) {\n        return parsed as DefaultPartialBlock[];\n      }\n      return null;\n    } catch {\n      return null;\n    }\n  }\n\n  /**\n   * 기본 paragraph 블록 생성\n   * @returns 기본 설정이 적용된 DefaultPartialBlock\n   */\n  static createDefaultBlock(): DefaultPartialBlock {\n    return {\n      type: \"paragraph\",\n      props: {\n        textColor: \"default\",\n        backgroundColor: \"default\",\n        textAlignment: \"left\",\n      },\n      content: [{ type: \"text\", text: \"\", styles: {} }],\n      children: [],\n    };\n  }\n\n  /**\n   * 콘텐츠 유효성 검증 및 기본값 설정\n   * @param content 사용자 제공 콘텐츠 (객체 배열 또는 JSON 문자열)\n   * @param emptyBlockCount 빈 블록 개수 (기본값: 3)\n   * @returns 검증된 콘텐츠 배열\n   */\n  static validateContent(\n    content?: DefaultPartialBlock[] | string,\n    emptyBlockCount: number = 3\n  ): DefaultPartialBlock[] {\n    // 1. 문자열인 경우 JSON 파싱 시도\n    if (typeof content === \"string\") {\n      if (content.trim() === \"\") {\n        return this.createEmptyBlocks(emptyBlockCount);\n      }\n\n      const parsedContent = this.parseJSONContent(content);\n      if (parsedContent && parsedContent.length > 0) {\n        return parsedContent;\n      }\n\n      // 파싱 실패 시 빈 블록 생성\n      return this.createEmptyBlocks(emptyBlockCount);\n    }\n\n    // 2. 배열인 경우 기존 로직\n    if (!content || content.length === 0) {\n      return this.createEmptyBlocks(emptyBlockCount);\n    }\n\n    return content;\n  }\n\n  /**\n   * 빈 블록들을 생성합니다\n   * @param emptyBlockCount 생성할 블록 개수\n   * @returns 생성된 빈 블록 배열\n   */\n  private static createEmptyBlocks(\n    emptyBlockCount: number\n  ): DefaultPartialBlock[] {\n    return Array.from({ length: emptyBlockCount }, () =>\n      this.createDefaultBlock()\n    );\n  }\n}\n\n/**\n * 에디터 설정 관리 유틸리티\n * 각종 설정의 기본값과 검증 로직을 담당\n */\nexport class EditorConfig {\n  /**\n   * 테이블 설정 기본값 적용\n   * @param userTables 사용자 테이블 설정\n   * @returns 기본값이 적용된 테이블 설정\n   */\n  static getDefaultTableConfig(userTables?: LumirEditorProps[\"tables\"]) {\n    return {\n      splitCells: userTables?.splitCells ?? true,\n      cellBackgroundColor: userTables?.cellBackgroundColor ?? true,\n      cellTextColor: userTables?.cellTextColor ?? true,\n      headers: userTables?.headers ?? true,\n    };\n  }\n\n  /**\n   * 헤딩 설정 기본값 적용\n   * @param userHeading 사용자 헤딩 설정\n   * @returns 기본값이 적용된 헤딩 설정\n   */\n  static getDefaultHeadingConfig(userHeading?: LumirEditorProps[\"heading\"]) {\n    return userHeading?.levels && userHeading.levels.length > 0\n      ? userHeading\n      : { levels: [1, 2, 3, 4, 5, 6] as (1 | 2 | 3 | 4 | 5 | 6)[] };\n  }\n\n  /**\n   * 비활성화할 확장 기능 목록 생성\n   * @param userExtensions 사용자 정의 비활성 확장\n   * @param allowVideo 비디오 업로드 허용 여부\n   * @param allowAudio 오디오 업로드 허용 여부\n   * @param allowFile 일반 파일 업로드 허용 여부\n   * @returns 비활성화할 확장 기능 목록\n   */\n  static getDisabledExtensions(\n    userExtensions?: string[],\n    allowVideo = false,\n    allowAudio = false,\n    allowFile = false\n  ): string[] {\n    const set = new Set<string>(userExtensions ?? []);\n    if (!allowVideo) set.add(\"video\");\n    if (!allowAudio) set.add(\"audio\");\n    if (!allowFile) set.add(\"file\");\n    return Array.from(set);\n  }\n}\n\n// 파일 타입 검증 함수\n/** @internal 테스트용 export */\nexport const isImageFile = (file: File, maxSize?: number): boolean => {\n  const limit = maxSize !== undefined ? maxSize : MAX_FILE_SIZE;\n  if (file.size === 0 || file.size > limit) {\n    return false;\n  }\n\n  // 🔒 보안: SVG 파일 차단 (XSS 방지)\n  const fileName = file.name?.toLowerCase() || \"\";\n  if (\n    file.type === \"image/svg+xml\" ||\n    BLOCKED_EXTENSIONS.some((ext) => fileName.endsWith(ext))\n  ) {\n    return false;\n  }\n\n  // 이미지 타입 검증\n  return (\n    file.type?.startsWith(\"image/\") ||\n    (!file.type && /\\.(png|jpe?g|gif|webp|bmp)$/i.test(fileName))\n  );\n};\n\n/** @internal 테스트용 export */\nexport const isVideoFile = (file: File, maxSize?: number): boolean => {\n  const limit = maxSize !== undefined ? maxSize : MAX_VIDEO_FILE_SIZE;\n  const sizeOk = file.size > 0 && file.size <= limit;\n  const fileName = file.name?.toLowerCase() || \"\";\n  const mimeMatch = ALLOWED_VIDEO_MIME_TYPES.has(file.type);\n  const videoPrefix = typeof file.type === \"string\" && file.type.startsWith(\"video/\");\n  const extMatch = !file.type && ALLOWED_VIDEO_EXTENSIONS.some((ext) => fileName.endsWith(ext));\n  const result = sizeOk && (mimeMatch || videoPrefix || extMatch);\n  // #region agent log\n  DEBUG_LOG(\"isVideoFile:check\", \"result\", {\n    fileName: file.name,\n    fileType: file.type,\n    fileSize: file.size,\n    sizeOk,\n    mimeMatch,\n    videoPrefix,\n    extMatch,\n    result,\n  });\n  // #endregion\n  return result;\n};\n\n/**\n * floatingMenu(상단 고정 툴바) 사용 시 팝업 포매팅 툴바를 숨길 선택인지 판별.\n *\n * - 일반 텍스트 선택 → true (상단 툴바가 모든 텍스트 도구를 제공하므로 팝업 중복.\n *   또한 상단 툴바 상호작용 직후 팝업이 잘못된 위치(0,0)로 재표시되는 문제 방지)\n * - 테이블 셀 컨텍스트(CellSelection·셀 내부 커서) → false\n *   (셀 병합·세로 정렬·셀 배경 등 상단 툴바에 없는 도구가 팝업에만 있음)\n * - NodeSelection(이미지/비디오 등) → false (캡션·교체·다운로드 등 파일 도구 유지)\n *\n * @internal 테스트용 export\n */\nexport const isSuppressiblePopupSelection = (\n  editor: any,\n  selection: any\n): boolean => {\n  if (!selection) return false;\n  if (selection.node) return false; // NodeSelection\n  if (isInTableCell(editor)) return false; // CellSelection 또는 셀 내부 커서\n  return true;\n};\n\n/** @internal 테스트용 export */\nexport const isHtmlFile = (file: File): boolean => {\n  return (\n    file.size > 0 &&\n    (file.type === \"text/html\" ||\n      file.name?.toLowerCase().endsWith(\".html\") ||\n      file.name?.toLowerCase().endsWith(\".htm\"))\n  );\n};\n\n// ============================================\n// 🔒 보안 유틸리티 함수\n// ============================================\n\n/**\n * HTML 특수문자 이스케이프 (XSS 방지)\n * URL이나 사용자 입력을 HTML에 삽입할 때 사용\n * @internal 테스트용 export\n */\nexport const escapeHtml = (str: string): string => {\n  const htmlEscapes: Record<string, string> = {\n    \"&\": \"&amp;\",\n    \"<\": \"&lt;\",\n    \">\": \"&gt;\",\n    '\"': \"&quot;\",\n    \"'\": \"&#39;\",\n  };\n  return str.replace(/[&<>\"']/g, (char) => htmlEscapes[char]);\n};\n\n/**\n * 블록 배열에서 모든 이미지 URL 추출\n * (중첩된 children도 재귀적으로 탐색)\n * @internal 테스트용 export\n */\nexport const extractImageUrls = (blocks: DefaultPartialBlock[]): Set<string> => {\n  const urls = new Set<string>();\n\n  const traverse = (blockList: DefaultPartialBlock[]) => {\n    for (const block of blockList) {\n      // image 블록에서 URL 추출\n      if (block.type === \"image\" && (block.props as any)?.url) {\n        const url = (block.props as any).url;\n        if (typeof url === \"string\" && url.trim()) {\n          urls.add(url);\n        }\n      }\n      // children이 있으면 재귀 탐색\n      if (block.children && Array.isArray(block.children)) {\n        traverse(block.children as DefaultPartialBlock[]);\n      }\n    }\n  };\n\n  traverse(blocks);\n  return urls;\n};\n\n/**\n * 블록 배열에서 이미지·비디오(미디어) URL 추출\n * (중첩된 children도 재귀적으로 탐색, onImageDelete 삭제 감지용)\n * @internal 테스트용 export\n */\nexport const extractMediaUrls = (blocks: DefaultPartialBlock[]): Set<string> => {\n  const urls = new Set<string>();\n\n  const traverse = (blockList: DefaultPartialBlock[]) => {\n    for (const block of blockList) {\n      if (block.type === \"image\" && (block.props as any)?.url) {\n        const url = (block.props as any).url;\n        if (typeof url === \"string\" && url.trim()) urls.add(url);\n      }\n      if (block.type === \"video\" && (block.props as any)?.url) {\n        const url = (block.props as any).url;\n        if (typeof url === \"string\" && url.trim()) urls.add(url);\n      }\n      if (block.children && Array.isArray(block.children)) {\n        traverse(block.children as DefaultPartialBlock[]);\n      }\n    }\n  };\n\n  traverse(blocks);\n  return urls;\n};\n\n/**\n * 삭제된 미디어(이미지·비디오) URL 찾기\n * (이전 블록에는 있었지만 현재 블록에는 없는 URL)\n * @internal 테스트용 export\n */\nexport const findDeletedMediaUrls = (\n  previousUrls: Set<string>,\n  currentUrls: Set<string>\n): string[] => {\n  const deleted: string[] = [];\n  previousUrls.forEach((url) => {\n    if (!currentUrls.has(url)) deleted.push(url);\n  });\n  return deleted;\n};\n\n/**\n * 삭제된 이미지 URL 찾기 (findDeletedMediaUrls와 동일 로직, 하위 호환용)\n * @internal 테스트용 export\n */\nexport const findDeletedImageUrls = (\n  previousUrls: Set<string>,\n  currentUrls: Set<string>\n): string[] => findDeletedMediaUrls(previousUrls, currentUrls);\n\nconst findBlockWithLink = (blocks: any[], targetUrl: string): any | null => {\n  for (const block of blocks) {\n    if (block.content) {\n      for (const item of block.content) {\n        if (item.type === \"link\" && item.href === targetUrl) return block;\n        if (item.content) {\n          for (const sub of item.content) {\n            if (sub.type === \"link\" && sub.href === targetUrl) return block;\n          }\n        }\n      }\n    }\n    if (block.children?.length) {\n      const found = findBlockWithLink(block.children, targetUrl);\n      if (found) return found;\n    }\n  }\n  return null;\n};\n\nconst ConvertToPreviewButton = ({ url }: { url: string }) => {\n  const editor = useBlockNoteEditor();\n  const Components = useComponentsContext()!;\n\n  return (\n    <Components.LinkToolbar.Button\n      className=\"bn-button\"\n      mainTooltip=\"링크 프리뷰로 전환\"\n      label=\"링크 프리뷰로 전환\"\n      isSelected={false}\n      onClick={() => {\n        try {\n          const allBlocks = (editor as any).document;\n          const targetBlock = findBlockWithLink(allBlocks, url)\n            || editor.getTextCursorPosition().block;\n          (editor as any).replaceBlocks(\n            [targetBlock],\n            [{ type: \"linkPreview\", props: { url } }]\n          );\n        } catch (err) {\n          console.error(\"Convert to link preview failed:\", err);\n        }\n      }}\n      icon={\n        <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n          <rect x=\"1\" y=\"3\" width=\"14\" height=\"10\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.5\" fill=\"none\" />\n          <line x1=\"1\" y1=\"9\" x2=\"15\" y2=\"9\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n          <circle cx=\"5\" cy=\"6.5\" r=\"1.5\" stroke=\"currentColor\" strokeWidth=\"1\" fill=\"none\" />\n        </svg>\n      }\n    />\n  );\n};\n\nconst CustomLinkToolbar = (props: any) => {\n  const editor = useBlockNoteEditor();\n  const Components = useComponentsContext()!;\n  const hasLinkPreview = !!(editor as any)?._linkPreviewApiEndpoint;\n\n  return (\n    <Components.LinkToolbar.Root\n      className=\"bn-toolbar bn-link-toolbar\"\n      onMouseEnter={props.stopHideTimer}\n      onMouseLeave={props.startHideTimer}\n    >\n      <EditLinkButton url={props.url} text={props.text} editLink={props.editLink} />\n      <OpenLinkButton url={props.url} />\n      <DeleteLinkButton deleteLink={props.deleteLink} />\n      {hasLinkPreview && (\n        <ConvertToPreviewButton url={props.url} />\n      )}\n    </Components.LinkToolbar.Root>\n  );\n};\n\nexport default function LumirEditor({\n  // editor options\n  initialContent,\n  initialEmptyBlocks = 3,\n  uploadFile,\n  s3Upload,\n  tables,\n  heading,\n  defaultStyles = true,\n  disableExtensions,\n  tabBehavior = \"prefer-navigate-ui\",\n  trailingBlock = true,\n  allowVideoUpload = false,\n  allowAudioUpload = false,\n  allowFileUpload = false,\n  maxImageFileSize,\n  maxVideoFileSize,\n  // link preview\n  linkPreview,\n  // view options\n  editable = true,\n  theme = \"light\",\n  formattingToolbar = true,\n  linkToolbar = true,\n  sideMenu = true,\n  emojiPicker = true,\n  filePanel = true,\n  tableHandles = true,\n  onSelectionChange,\n  className = \"\",\n  placeholder,\n  sideMenuAddButton = false,\n  columnDivider = false,\n  floatingMenu = false,\n  floatingMenuPosition = \"sticky\",\n  // callbacks / refs\n  onContentChange,\n  onError,\n  onImageDelete,\n}: LumirEditorProps) {\n  // 이미지 업로드 로딩 상태\n  const [isUploading, setIsUploading] = useState(false);\n  /** S3 업로드 진행률 0–100. null이면 진행률 미표시 */\n  const [uploadProgress, setUploadProgress] = useState<number | null>(null);\n  // 에러 상태 (사용자에게 표시할 에러 메시지)\n  const [errorMessage, setErrorMessage] = useState<string | null>(null);\n  // FloatingMenu: DOM에 항상 있는 file input (onchange 미발생 방지)\n  const floatingMenuFileInputRef = useRef<HTMLInputElement | null>(null);\n  const floatingMenuBlockRef = useRef<{ id: string } | null>(null);\n  const floatingMenuUploadStartTimeRef = useRef<number>(0);\n\n  // 에러 처리 핸들러\n  const handleError = useCallback(\n    (error: LumirEditorError) => {\n      // 콜백이 있으면 호출\n      onError?.(error);\n      // 사용자에게 에러 메시지 표시\n      setErrorMessage(error.getUserMessage());\n      // 3초 후 에러 메시지 자동 숨김\n      setTimeout(() => setErrorMessage(null), 3000);\n    },\n    [onError]\n  );\n  const validatedContent = useMemo<DefaultPartialBlock[]>(() => {\n    // 형제 키 fontSize → styles.fontSize 복원 (직렬화 호환 레이어)\n    return liftFontSize(\n      ContentUtils.validateContent(initialContent, initialEmptyBlocks)\n    );\n  }, [initialContent, initialEmptyBlocks]);\n\n  // 테이블 설정 메모이제이션\n  const tableConfig = useMemo(() => {\n    return EditorConfig.getDefaultTableConfig(tables);\n  }, [\n    tables?.splitCells,\n    tables?.cellBackgroundColor,\n    tables?.cellTextColor,\n    tables?.headers,\n  ]);\n\n  // 헤딩 설정 메모이제이션\n  const headingConfig = useMemo(() => {\n    return EditorConfig.getDefaultHeadingConfig(heading);\n  }, [heading?.levels?.join(\",\") ?? \"\"]);\n\n  // 비활성화 확장 메모이제이션\n  const disabledExtensions = useMemo(() => {\n    return EditorConfig.getDisabledExtensions(\n      disableExtensions,\n      allowVideoUpload,\n      allowAudioUpload,\n      allowFileUpload\n    );\n  }, [disableExtensions, allowVideoUpload, allowAudioUpload, allowFileUpload]);\n\n  // #region agent log\n  useEffect(() => {\n    DEBUG_LOG(\"LumirEditor:init:disabledExtensions\", \"snapshot\", {\n      allowVideoUpload,\n      hasVideoInDisabled: disabledExtensions.includes(\"video\"),\n      disabledList: disabledExtensions.slice(0, 15),\n    });\n  }, [allowVideoUpload, disabledExtensions]);\n  // #endregion\n\n  // fileNameTransform 콜백을 ref로 관리 (에디터 재생성 방지)\n  const fileNameTransformRef = useRef(s3Upload?.fileNameTransform);\n  useEffect(() => {\n    fileNameTransformRef.current = s3Upload?.fileNameTransform;\n  }, [s3Upload?.fileNameTransform]);\n\n  // S3 업로드 설정 메모이제이션 (객체 참조 안정화)\n  // 주의: fileNameTransform은 ref로 관리하므로 의존성에서 제외\n  const memoizedS3Upload = useMemo(() => {\n    if (!s3Upload) return undefined;\n    return {\n      apiEndpoint: s3Upload.apiEndpoint,\n      env: s3Upload.env,\n      path: s3Upload.path,\n      appendUUID: s3Upload.appendUUID,\n      preserveExtension: s3Upload.preserveExtension,\n      uploadTimeoutMs: s3Upload.uploadTimeoutMs,\n      maxRetries: s3Upload.maxRetries,\n      onProgress: (percent: number) => {\n        setUploadProgress(percent);\n        s3Upload.onProgress?.(percent);\n      },\n      // 최신 콜백을 항상 사용하도록 ref를 통해 접근\n      fileNameTransform: ((originalName: string, file: File) => {\n        return fileNameTransformRef.current\n          ? fileNameTransformRef.current(originalName, file)\n          : originalName;\n      }) as ((originalName: string, file: File) => string) | undefined,\n    };\n  }, [\n    s3Upload?.apiEndpoint,\n    s3Upload?.env,\n    s3Upload?.path,\n    s3Upload?.appendUUID,\n    s3Upload?.preserveExtension,\n    s3Upload?.uploadTimeoutMs,\n    s3Upload?.maxRetries,\n    s3Upload?.onProgress,\n  ]);\n\n  const editor = useCreateBlockNote(\n    {\n      // HTML 미리보기 블록이 포함된 커스텀 스키마 사용\n      schema,\n      // 모든 BlockNote UI 텍스트(테이블 드롭다운 등) 한글 적용 + \"색깔\"→\"색\"\n      dictionary: {\n        ...ko,\n        drag_handle: { ...ko.drag_handle, colors_menuitem: \"색\" },\n        formatting_toolbar: {\n          ...ko.formatting_toolbar,\n          colors: { ...ko.formatting_toolbar.colors, tooltip: \"색\" },\n        },\n      },\n      initialContent: validatedContent as any,\n      tables: tableConfig,\n      heading: headingConfig,\n      animations: false, // 기본적으로 애니메이션 비활성화\n      defaultStyles,\n      // 확장 비활성: 비디오/오디오/파일 제어\n      disableExtensions: disabledExtensions,\n      _tiptapOptions: {\n        extensions: [\n          VerticalAlignmentExtension,\n          // 행 높이 attr 등록은 항상(저장된 높이 렌더), 드래그 리사이즈 UI는\n          // tableHandles prop으로 게이트(기존 grip 컨트롤러와 동일 게이트).\n          RowHeightExtension.configure({ resizable: tableHandles }),\n          // 표 블록 정렬(좌/가운데/우) attr.\n          TableAlignmentExtension,\n          // 셀 포커스 시 Ctrl/Cmd+A → 표 전체 선택.\n          TableSelectAllExtension,\n          // blur 상태(툴바 드롭다운 조작 등)에서도 텍스트 선택 하이라이트 유지.\n          InactiveSelectionExtension,\n        ],\n      },\n      placeholders: placeholder\n        ? { default: placeholder, emptyDocument: placeholder }\n        : undefined,\n      tabBehavior,\n      trailingBlock,\n      uploadFile: async (file) => {\n        const allowedImage = isImageFile(file, maxImageFileSize);\n        const allowedVideo =\n          allowVideoUpload && isVideoFile(file, maxVideoFileSize);\n        // #region agent log\n        DEBUG_LOG(\"uploadFile:step1:entry\", \"editor uploadFile callback invoked\", {\n          fileName: file.name,\n          fileType: file.type,\n          fileSize: file.size,\n          allowVideoUpload,\n          allowedImage,\n          allowedVideo,\n        });\n        // #endregion\n        if (!allowedImage && !allowedVideo) {\n          const error = LumirEditorError.invalidFileType(\n            file.name,\n            allowVideoUpload\n          );\n          handleError(error);\n          throw error;\n        }\n\n        try {\n          setUploadProgress(0);\n          let fileUrl: string;\n          // #region agent log\n          const branch = uploadFile ? \"custom\" : memoizedS3Upload?.apiEndpoint ? \"s3\" : \"none\";\n          DEBUG_LOG(\"uploadFile:step2:branch\", \"upload path\", {\n            branch,\n            hasCustomUploadFile: !!uploadFile,\n            hasS3ApiEndpoint: !!memoizedS3Upload?.apiEndpoint,\n          });\n          // #endregion\n          // 1. 사용자 정의 uploadFile 우선\n          if (uploadFile) {\n            const t0 = Date.now();\n            DEBUG_LOG(\"uploadFile:step3a:custom\", \"calling custom uploadFile\", { fileName: file.name });\n            fileUrl = await uploadFile(file);\n            DEBUG_LOG(\"uploadFile:step3a:done\", \"custom uploadFile returned\", { urlLen: fileUrl?.length, elapsedMs: Date.now() - t0 });\n          }\n          // 2. S3 업로드 (uploadFile 없을 때)\n          else if (memoizedS3Upload?.apiEndpoint) {\n            const t0 = Date.now();\n            DEBUG_LOG(\"uploadFile:step3b:s3\", \"calling S3 uploader\", { fileName: file.name });\n            const s3Uploader = createS3Uploader(memoizedS3Upload);\n            fileUrl = await s3Uploader(file);\n            DEBUG_LOG(\"uploadFile:step3b:done\", \"S3 uploader returned\", { urlLen: fileUrl?.length, elapsedMs: Date.now() - t0 });\n          }\n          // 3. 업로드 방법이 없으면 에러\n          else {\n            const error = LumirEditorError.s3ConfigError(\n              \"No upload method available. Please provide uploadFile or s3Upload configuration.\"\n            );\n            handleError(error);\n            throw error;\n          }\n\n          // BlockNote가 파일 타입에 따라 이미지/비디오 블록을 생성하도록 URL만 반환\n          // #region agent log\n          DEBUG_LOG(\"uploadFile:step4:success\", \"returning URL\", {\n            fileName: file.name,\n            urlPrefix: fileUrl.slice(0, 80),\n          });\n          // #endregion\n          return fileUrl;\n        } catch (error) {\n          // #region agent log\n          DEBUG_LOG(\"uploadFile:step5:catch\", \"uploadFile threw\", {\n            fileName: file.name,\n            errorMessage: error instanceof Error ? error.message : String(error),\n          });\n          // #endregion\n          // 이미 LumirEditorError인 경우 다시 처리하지 않음\n          if (error instanceof LumirEditorError) {\n            throw error;\n          }\n          const lumirError = LumirEditorError.uploadFailed(\n            error instanceof Error ? error.message : String(error),\n            error instanceof Error ? error : undefined\n          );\n          handleError(lumirError);\n          throw lumirError;\n        } finally {\n          setUploadProgress(null);\n        }\n      },\n      pasteHandler: (ctx) => {\n        const { event, editor, defaultPasteHandler } = ctx as any;\n\n        // URL 붙여넣기 감지 → linkPreview 블록 생성\n        if (linkPreview?.apiEndpoint) {\n          const text = event?.clipboardData?.getData?.(\"text/plain\") || \"\";\n          const trimmed = text.trim();\n          if (\n            trimmed &&\n            /^https?:\\/\\/\\S+$/i.test(trimmed) &&\n            !event?.clipboardData?.files?.length\n          ) {\n            event.preventDefault();\n            const currentBlock = editor.getTextCursorPosition().block;\n            const blockText = currentBlock.content\n              ?.map((c: any) => c.text || \"\")\n              .join(\"\")\n              .trim();\n            if (!blockText && currentBlock.type === \"paragraph\") {\n              editor.updateBlock(currentBlock, {\n                type: \"linkPreview\",\n                props: { url: trimmed },\n              });\n            } else {\n              editor.insertBlocks(\n                [{ type: \"linkPreview\", props: { url: trimmed } }],\n                currentBlock,\n                \"after\"\n              );\n            }\n            return true;\n          }\n        }\n\n        // 엑셀/스프레드시트 표 붙여넣기 감지\n        // 엑셀은 클립보드에 표 HTML과 함께 셀 비트맵(image.png)을 올리므로,\n        // 아래 이미지 업로드 분기가 가로채기 전에 HTML 표를 우선 처리한다.\n        // BlockNote 기본 HTML 파서가 병합/들쑥날쑥 행을 안전하게 표로 변환한다.\n        const pastedHtml =\n          event?.clipboardData?.getData?.(\"text/html\") || \"\";\n        if (/<table[\\s>]/i.test(pastedHtml)) {\n          // #region agent log\n          DEBUG_LOG(\"paste:step0:table\", \"table HTML detected, using pasteHTML\", {\n            htmlLen: pastedHtml.length,\n            hasFiles: !!event?.clipboardData?.files?.length,\n          });\n          // #endregion\n          event.preventDefault();\n          // 에디터 너비보다 넓은 표는 비율 유지하며 축소(가로 스크롤 방지):\n          // 붙여넣기 전에 HTML에서 열 너비를 읽어 maxWidth에 맞춘 columnWidths를 계산하고,\n          // 붙여넣은 뒤 새 표 블록에 적용한다(파서가 셀 width를 colwidth로 읽지 않으므로).\n          const pmDom = (editor as any).prosemirrorView?.dom as HTMLElement | undefined;\n          const maxWidth = pmDom?.clientWidth ? pmDom.clientWidth - 8 : 0;\n          const fittedWidths = computeFittedColumnWidthsPerTable(pastedHtml, maxWidth);\n          const beforeTableIds = new Set<string>(\n            collectTableBlocks(editor.document).map((b: any) => b.id),\n          );\n          // 엑셀 CSS 서식(색/정렬/굵게)을 BlockNote가 인식하는 형태로 정규화 후 삽입.\n          editor.pasteHTML(normalizeExcelTableHtml(pastedHtml));\n          applyFittedWidthsToNewTables(editor, beforeTableIds, fittedWidths);\n          return true;\n        }\n\n        const fileList =\n          (event?.clipboardData?.files as FileList | null) ?? null;\n        const files: File[] = fileList ? Array.from(fileList) : [];\n        const acceptedFiles: File[] = files.filter(\n          (f) =>\n            isImageFile(f, maxImageFileSize) || (allowVideoUpload && isVideoFile(f, maxVideoFileSize))\n        );\n        // #region agent log\n        DEBUG_LOG(\"paste:step1:files\", \"paste clipboard files\", {\n          filesCount: files.length,\n          acceptedCount: acceptedFiles.length,\n          fileNames: files.map((f) => f.name),\n          acceptedNames: acceptedFiles.map((f) => f.name),\n        });\n        // #endregion\n        // 파일이 있지만 허용된 미디어가 없으면 기본 처리 막고 무시\n        if (files.length > 0 && acceptedFiles.length === 0) {\n          event.preventDefault();\n          return true;\n        }\n\n        if (acceptedFiles.length === 0) {\n          return defaultPasteHandler() ?? false;\n        }\n\n        event.preventDefault();\n        (async () => {\n          setIsUploading(true);\n          try {\n            for (const file of acceptedFiles) {\n              try {\n                // #region agent log\n                DEBUG_LOG(\"paste:step2:upload\", \"calling uploadFile for paste\", {\n                  fileName: file.name,\n                  fileType: file.type,\n                });\n                // #endregion\n                const url = await editor.uploadFile(file);\n                if (isImageFile(file, maxImageFileSize)) {\n                  editor.pasteHTML(\n                    `<img src=\"${escapeHtml(url)}\" alt=\"image\" />`\n                  );\n                } else if (isVideoFile(file, maxVideoFileSize)) {\n                  const currentBlock = editor.getTextCursorPosition().block;\n                  editor.insertBlocks(\n                    [{ type: \"video\", props: { url } }] as any,\n                    currentBlock,\n                    \"after\"\n                  );\n                }\n              } catch (err) {\n                console.warn(\n                  \"Upload failed, skipped:\",\n                  file.name || \"\",\n                  err\n                );\n              }\n            }\n          } finally {\n            setIsUploading(false);\n          }\n        })();\n        return true;\n      },\n    },\n    [\n      validatedContent,\n      tableConfig,\n      headingConfig,\n      defaultStyles,\n      disabledExtensions,\n      tabBehavior,\n      trailingBlock,\n      uploadFile,\n      memoizedS3Upload,\n      allowVideoUpload,\n      linkPreview?.apiEndpoint,\n      placeholder,\n      // tableHandles 변경 시 RowHeightExtension의 resizable 게이트가 반영되도록 재생성\n      tableHandles,\n    ]\n  );\n\n  // Link Preview API endpoint를 에디터 인스턴스에 동기적으로 연결\n  // (useEffect 사용 시 블록 렌더링보다 늦게 설정되어 첫 fetch가 실패할 수 있음)\n  if (editor && linkPreview?.apiEndpoint) {\n    (editor as any)._linkPreviewApiEndpoint = linkPreview.apiEndpoint;\n  }\n\n  // columnDnd 플러그인이 드롭 시 BlockNote 블록 API(getBlock/replaceBlocks)에 접근할 수\n  // 있도록, ProseMirror view에 에디터 참조를 심는다(플러그인은 view.__lumirEditor로 읽음).\n  if (editor) {\n    try {\n      const view = (editor as any).prosemirrorView;\n      if (view) {\n        view.__lumirEditor = editor;\n      }\n    } catch {\n      /* view 미준비 시 무시(다음 렌더에서 설정) */\n    }\n  }\n\n  // 편집 가능 여부 설정\n  useEffect(() => {\n    if (editor) {\n      editor.isEditable = editable;\n    }\n  }, [editor, editable]);\n\n  // 행/열 삭제 결정적 처리 래퍼.\n  // 코어의 tableHandles.removeRowOrColumn은 삭제 대상 표를 플러그인 내부 상태\n  // view.tablePos로 찾는데, 포커스 기반 커스텀 그립 컨트롤러에서는 이 값이 어긋날 수\n  // 있어 잘못된 CellSelection이 만들어지고 첫 열/행 삭제가 화면에 반영되지 않는다.\n  // 현재 selection(커서)이 위치한 실제 표 기준으로 직접 삭제하고, 표 컨텍스트가\n  // 아니면(=false) 코어 기본 동작으로 폴백한다.\n  useEffect(() => {\n    if (!editor) return;\n    const th: any = (editor as any).tableHandles;\n    if (!th || th.__lumirColDelPatched || typeof th.removeRowOrColumn !== \"function\")\n      return;\n    const orig = th.removeRowOrColumn.bind(th);\n    th.removeRowOrColumn = (index: number, dir: \"row\" | \"column\") => {\n      if (removeFocusedRowOrColumn(editor, index, dir)) return;\n      return orig(index, dir);\n    };\n    th.__lumirColDelPatched = true;\n  }, [editor]);\n\n  // floatingMenu(상단 고정 툴바) 사용 시: 일반 텍스트 선택에서는 팝업 포매팅\n  // 툴바를 띄우지 않는다 (중복 UI + 상단 툴바에서 스타일 적용 직후 팝업이\n  // (0,0) 위치로 잘못 재표시되는 문제 방지). 테이블 셀·노드 선택은 팝업 유지.\n  // show:true emit 직후 동기적으로 closeMenu()하면 React 배칭으로 화면에\n  // 그려지기 전에 show:false로 정리된다.\n  useEffect(() => {\n    if (!editor || !floatingMenu) return;\n\n    const ft = (editor as any).formattingToolbar;\n    if (!ft?.onUpdate) return;\n\n    const unsubscribe = ft.onUpdate((state: { show?: boolean }) => {\n      if (!state?.show) return;\n      const selection = (editor as any)._tiptapEditor?.state?.selection;\n      if (isSuppressiblePopupSelection(editor, selection)) {\n        ft.closeMenu();\n      }\n    });\n    return unsubscribe;\n  }, [editor, floatingMenu]);\n\n  // 콘텐츠 변경 감지\n  useEffect(() => {\n    if (!editor || !onContentChange) return;\n\n    const handleContentChange = () => {\n      const blocks = editor.topLevelBlocks as DefaultPartialBlock[];\n      // ⚠️ styles.fontSize는 구버전 SDK(fontSize 스펙 없음)를 크래시시키므로\n      // 외부로 나가는 JSON은 반드시 lowerFontSize(형제 키 변환)를 거친다.\n      // 표 커스텀 셀 attr(세로 정렬·행 높이)은 한 번의 순회로 함께 주입한다.\n      const patched = lowerFontSize(\n        injectTableBlockAttrs(\n          injectTableCellAttrs(blocks, editor, [\n            \"verticalAlignment\",\n            \"rowHeight\",\n          ]),\n          editor\n        )\n      );\n      onContentChange(patched);\n    };\n\n    return editor.onEditorContentChange(handleContentChange);\n  }, [editor, onContentChange]);\n\n  // 이미지·비디오 삭제 감지 (onImageDelete 콜백 — 미디어 URL 모두 전달)\n  const previousMediaUrlsRef = useRef<Set<string>>(new Set());\n\n  useEffect(() => {\n    if (!editor) return;\n\n    const initialBlocks = editor.topLevelBlocks as DefaultPartialBlock[];\n    previousMediaUrlsRef.current = extractMediaUrls(initialBlocks);\n  }, [editor]);\n\n  useEffect(() => {\n    if (!editor || !onImageDelete) return;\n\n    const handleMediaDeleteCheck = () => {\n      const currentBlocks = editor.topLevelBlocks as DefaultPartialBlock[];\n      const currentUrls = extractMediaUrls(currentBlocks);\n      const previousUrls = previousMediaUrlsRef.current;\n\n      const deletedUrls = findDeletedMediaUrls(previousUrls, currentUrls);\n      deletedUrls.forEach((url) => {\n        onImageDelete(url);\n      });\n\n      previousMediaUrlsRef.current = currentUrls;\n    };\n\n    return editor.onEditorContentChange(handleMediaDeleteCheck);\n  }, [editor, onImageDelete]);\n\n  // 드래그앤드롭 이미지/HTML 처리\n  useEffect(() => {\n    const el = editor?.domElement as HTMLElement | undefined;\n    if (!el) return;\n\n    const handleDragOver = (e: DragEvent) => {\n      if (e.defaultPrevented) return;\n      const hasFiles = (\n        e.dataTransfer?.types as unknown as string[] | undefined\n      )?.includes?.(\"Files\");\n      if (hasFiles) {\n        e.preventDefault();\n        e.stopPropagation();\n      }\n    };\n\n    const handleDrop = (e: DragEvent) => {\n      if (!e.dataTransfer) return;\n      const hasFiles = (\n        (e.dataTransfer.types as unknown as string[] | undefined) ?? []\n      ).includes(\"Files\");\n      if (!hasFiles) return;\n\n      e.preventDefault();\n      e.stopPropagation();\n\n      const items = Array.from(e.dataTransfer.items ?? []);\n      const files = items\n        .filter((it) => it.kind === \"file\")\n        .map((it) => it.getAsFile())\n        .filter((f): f is File => !!f);\n\n      // 이미지, 동영상, HTML 파일 분리\n      const imageFiles = files.filter((f) => isImageFile(f, maxImageFileSize));\n      const videoFiles = allowVideoUpload\n        ? files.filter((f) => isVideoFile(f, maxVideoFileSize))\n        : [];\n      const htmlFiles = files.filter(isHtmlFile);\n\n      // #region agent log\n      DEBUG_LOG(\"drop:step1:files\", \"drop received\", {\n        filesCount: files.length,\n        imageCount: imageFiles.length,\n        videoCount: videoFiles.length,\n        htmlCount: htmlFiles.length,\n        allowVideoUpload,\n        firstFile: files[0]\n          ? {\n              name: files[0].name,\n              type: files[0].type,\n              size: files[0].size,\n              isImage: isImageFile(files[0], maxImageFileSize),\n              isVideo: isVideoFile(files[0], maxVideoFileSize),\n            }\n          : null,\n      });\n      // #endregion\n\n      if (\n        imageFiles.length === 0 &&\n        htmlFiles.length === 0 &&\n        videoFiles.length === 0\n      )\n        return;\n\n      (async () => {\n        setIsUploading(true);\n        try {\n          // #region agent log\n          DEBUG_LOG(\"drop:step2:async\", \"drop async started\", {\n            imageCount: imageFiles.length,\n            videoCount: videoFiles.length,\n          });\n          // #endregion\n          // 이미지 파일 처리\n          for (const file of imageFiles) {\n            try {\n              if (editor?.uploadFile) {\n                const url = await editor.uploadFile(file);\n                if (url && typeof url === \"string\") {\n                  editor.pasteHTML(\n                    `<img src=\"${escapeHtml(url)}\" alt=\"image\" />`\n                  );\n                }\n              }\n            } catch (err) {\n              console.warn(\n                \"Image upload failed, skipped:\",\n                file.name || \"\",\n                err\n              );\n            }\n          }\n\n          // 동영상 파일 처리 - video 블록으로 삽입\n          // #region agent log\n          DEBUG_LOG(\"drop:step3:videoLoop\", \"video loop start\", {\n            videoCount: videoFiles.length,\n            names: videoFiles.map((f) => f.name),\n          });\n          // #endregion\n          for (const file of videoFiles) {\n            try {\n              if (editor?.uploadFile) {\n                // #region agent log\n                DEBUG_LOG(\"drop:step4:videoUpload\", \"calling uploadFile for video\", {\n                  fileName: file.name,\n                });\n                // #endregion\n                const url = await editor.uploadFile(file);\n                if (url && typeof url === \"string\") {\n                  const currentBlock = editor.getTextCursorPosition().block;\n                  editor.insertBlocks(\n                    [{ type: \"video\", props: { url } }] as any,\n                    currentBlock,\n                    \"after\"\n                  );\n                }\n              }\n            } catch (err) {\n              console.warn(\n                \"Video upload failed, skipped:\",\n                file.name || \"\",\n                err\n              );\n            }\n          }\n\n          // HTML 파일 처리 - htmlPreview 블록으로 삽입\n          for (const file of htmlFiles) {\n            try {\n              const htmlContent = await file.text();\n              const currentBlock = editor.getTextCursorPosition().block;\n\n              // htmlPreview 블록 삽입\n              editor.insertBlocks(\n                [\n                  {\n                    type: \"htmlPreview\",\n                    props: {\n                      htmlContent: htmlContent,\n                      fileName: file.name,\n                      height: \"400px\",\n                    },\n                  },\n                ],\n                currentBlock,\n                \"after\"\n              );\n            } catch (err) {\n              console.warn(\n                \"HTML file processing failed, skipped:\",\n                file.name || \"\",\n                err\n              );\n            }\n          }\n        } finally {\n          setIsUploading(false);\n        }\n      })();\n    };\n\n    el.addEventListener(\"dragover\", handleDragOver, { capture: true });\n    el.addEventListener(\"drop\", handleDrop, { capture: true });\n\n    return () => {\n      el.removeEventListener(\"dragover\", handleDragOver, {\n        capture: true,\n      } as any);\n      el.removeEventListener(\"drop\", handleDrop, { capture: true } as any);\n    };\n  }, [editor, allowVideoUpload]);\n\n  // SideMenu 설정 (Add 버튼 제어)\n  const computedSideMenu = useMemo(() => {\n    return sideMenuAddButton ? sideMenu : false;\n  }, [sideMenuAddButton, sideMenu]);\n\n  // Add 버튼 없는 사이드 메뉴 (드래그 핸들만) - 메모이제이션\n  // 드래그 핸들 드롭다운에 표 정렬(좌/가운데/우) 항목을 추가한 커스텀 메뉴 사용.\n  const DragHandleOnlySideMenu = useMemo(() => {\n    return (props: any) => (\n      <BlockSideMenu {...props}>\n        <DragHandleButton {...props} dragHandleMenu={LumirDragHandleMenu} />\n      </BlockSideMenu>\n    );\n  }, []);\n\n  return (\n    <div\n      className={cn(\n        \"lumirEditor\",\n        columnDivider && \"lumir-column-divider\",\n        className,\n      )}\n      style={{ position: \"relative\", display: \"flex\", flexDirection: \"column\" }}\n    >\n      {/* FloatingMenu를 BlockNoteView 외부로 이동 */}\n      {floatingMenu && editor && (\n        <>\n          <input\n            ref={floatingMenuFileInputRef}\n            type=\"file\"\n            accept={\n              allowVideoUpload\n                ? \"image/*,video/mp4,video/webm,video/ogg,video/quicktime,.mov\"\n                : \"image/*\"\n            }\n            style={{\n              position: \"absolute\",\n              left: \"-9999px\",\n              opacity: 0,\n              pointerEvents: \"none\",\n            }}\n            onChange={async (e) => {\n              const inputEl = e.target as HTMLInputElement;\n              const file = inputEl.files?.[0];\n              // #region agent log\n              DEBUG_LOG(\"FloatingMenu:step3:onchange\", \"file input onchange fired\", {\n                hasFile: !!file,\n                fileName: file?.name,\n                fileType: file?.type,\n                fileSize: file?.size,\n                hasUploadFile: !!editor?.uploadFile,\n              });\n              // #endregion\n              const blockToInsertAfter = floatingMenuBlockRef.current;\n              if (file && editor.uploadFile && blockToInsertAfter) {\n                const allowedImage = isImageFile(file, maxImageFileSize);\n                const allowedVideo = allowVideoUpload && isVideoFile(file, maxVideoFileSize);\n                // #region agent log\n                DEBUG_LOG(\"FloatingMenu:step4:fileCheck\", \"allowed check\", {\n                  fileName: file.name,\n                  allowedImage,\n                  allowedVideo,\n                });\n                // #endregion\n                if (allowedImage || allowedVideo) {\n                  try {\n                    setIsUploading(true);\n                    floatingMenuUploadStartTimeRef.current = Date.now();\n                    // #region agent log\n                    DEBUG_LOG(\"FloatingMenu:step5:uploadStart\", \"calling editor.uploadFile\", {\n                      fileName: file.name,\n                    });\n                    // #endregion\n                    const url = await editor.uploadFile(file);\n                    const blockType = allowedVideo ? \"video\" : \"image\";\n                    const elapsedMs = Date.now() - floatingMenuUploadStartTimeRef.current;\n                    // #region agent log\n                    DEBUG_LOG(\"FloatingMenu:step6:uploadDone\", \"upload returned, inserting block\", {\n                      blockType,\n                      blockId: blockToInsertAfter.id,\n                      urlLen: url?.length,\n                      elapsedMs,\n                    });\n                    // #endregion\n                    editor.insertBlocks(\n                      [\n                        {\n                          type: blockType,\n                          props: { url: url as string },\n                        },\n                      ] as any,\n                      blockToInsertAfter,\n                      \"after\"\n                    );\n                  } catch (err) {\n                    // #region agent log\n                    DEBUG_LOG(\"FloatingMenu:step7:catch\", \"upload or insert failed\", {\n                      errMsg: err instanceof Error ? err.message : String(err),\n                    });\n                    // #endregion\n                    console.error(\"Upload failed:\", err);\n                  } finally {\n                    setIsUploading(false);\n                  }\n                }\n              }\n              inputEl.value = \"\";\n            }}\n          />\n          <FloatingMenu\n            editor={editor as any}\n            position={floatingMenuPosition}\n            onImageUpload={() => {\n              // #region agent log\n              DEBUG_LOG(\"FloatingMenu:step1:click\", \"upload button clicked\", {\n                allowVideoUpload,\n              });\n              // #endregion\n              let blockToInsertAfter: { id: string };\n              try {\n                blockToInsertAfter = editor.getTextCursorPosition().block;\n              } catch (err) {\n                DEBUG_LOG(\"FloatingMenu:step1b:error\", \"getTextCursorPosition failed\", {\n                  err: err instanceof Error ? err.message : String(err),\n                });\n                return;\n              }\n              floatingMenuBlockRef.current = blockToInsertAfter;\n              const input = floatingMenuFileInputRef.current;\n              if (!input) return;\n              input.accept = allowVideoUpload\n                ? \"image/*,video/mp4,video/webm,video/ogg,video/quicktime,.mov\"\n                : \"image/*\";\n              input.value = \"\";\n              // #region agent log\n              DEBUG_LOG(\"FloatingMenu:step2:inputReady\", \"persistent input ref, about to click\", {\n                accept: input.accept,\n              });\n              DEBUG_LOG(\"FloatingMenu:step2b:click\", \"input.click() about to be called\", {});\n              // #endregion\n              input.click();\n            }}\n          />\n        </>\n      )}\n      <BlockNoteView\n        editor={editor}\n        editable={editable}\n        theme={theme}\n        formattingToolbar={false}\n        linkToolbar={false}\n        sideMenu={computedSideMenu}\n        slashMenu={false}\n        emojiPicker={emojiPicker}\n        filePanel={filePanel}\n        tableHandles={false}\n        onSelectionChange={onSelectionChange}\n      >\n        {/* Notion 스타일 focus 기반 테이블 grip (기본 hover 핸들 대체).\n            소비자 tableHandles prop을 게이트로 유지. */}\n        {tableHandles && <LumirTableHandlesController />}\n        {formattingToolbar && (\n          <FormattingToolbarController\n            formattingToolbar={CustomFormattingToolbar}\n          />\n        )}\n        {linkToolbar && (\n          linkPreview?.apiEndpoint\n            ? <LinkToolbarController linkToolbar={CustomLinkToolbar} />\n            : <LinkToolbarController />\n        )}\n        {\n          <SuggestionMenuController\n            triggerCharacter=\"/\"\n            getItems={useCallback(\n              async (query: string) => {\n                const items = getDefaultReactSlashMenuItems(editor);\n                // 오디오/파일 항목 제거; 비디오는 allowVideoUpload일 때만 표시\n                const filtered = items.filter((item: any) => {\n                  const key = (item?.key || \"\").toString().toLowerCase();\n                  const title = (item?.title || \"\").toString().toLowerCase();\n                  if (key === \"video\" || title.includes(\"video\"))\n                    return allowVideoUpload;\n                  if ([\"audio\", \"file\"].includes(key)) return false;\n                  if (title.includes(\"audio\") || title.includes(\"file\"))\n                    return false;\n                  return true;\n                });\n\n                // HTML 미리보기 슬래시 메뉴 항목 추가\n                const htmlPreviewItem = {\n                  title: \"HTML Preview\",\n                  onItemClick: () => {\n                    // 파일 선택 다이얼로그 열기\n                    const input = document.createElement(\"input\");\n                    input.type = \"file\";\n                    input.accept = \".html,.htm\";\n                    input.onchange = async (e) => {\n                      const file = (e.target as HTMLInputElement).files?.[0];\n                      if (file) {\n                        const htmlContent = await file.text();\n                        const currentBlock =\n                          editor.getTextCursorPosition().block;\n                        editor.insertBlocks(\n                          [\n                            {\n                              type: \"htmlPreview\",\n                              props: {\n                                htmlContent: htmlContent,\n                                fileName: file.name,\n                                height: \"400px\",\n                              },\n                            },\n                          ],\n                          currentBlock,\n                          \"after\"\n                        );\n                      }\n                    };\n                    input.click();\n                  },\n                  aliases: [\"html\", \"preview\", \"웹\", \"웹페이지\"],\n                  group: \"Embeds\",\n                  icon: (\n                    <svg\n                      width=\"18\"\n                      height=\"18\"\n                      viewBox=\"0 0 24 24\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                      strokeWidth=\"2\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    >\n                      <polyline points=\"16 18 22 12 16 6\"></polyline>\n                      <polyline points=\"8 6 2 12 8 18\"></polyline>\n                    </svg>\n                  ),\n                  subtext: \"HTML 파일을 미리보기로 삽입\",\n                };\n\n                // 2단 컬럼(다단 레이아웃) 슬래시 항목 — 생성 시 구분선 유무를 선택해 고정.\n                // columnList/column은 public blockSchema에 없어 insertBlocks가 거부 →\n                // PM 트랜잭션으로 직접 삽입(insertTwoColumns).\n                const columnIcon = (withDivider: boolean) => (\n                  <svg\n                    width=\"18\"\n                    height=\"18\"\n                    viewBox=\"0 0 24 24\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"2\"\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                  >\n                    <rect x=\"3\" y=\"4\" width=\"7\" height=\"16\" rx=\"1\" />\n                    <rect x=\"14\" y=\"4\" width=\"7\" height=\"16\" rx=\"1\" />\n                    {withDivider && (\n                      <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"21\" strokeDasharray=\"2 2\" />\n                    )}\n                  </svg>\n                );\n                const columnItem = {\n                  title: \"2단 컬럼\",\n                  onItemClick: () => insertTwoColumns(editor, false),\n                  aliases: [\"columns\", \"column\", \"2col\", \"단\", \"컬럼\", \"다단\", \"분할\"],\n                  group: \"Basic blocks\",\n                  icon: columnIcon(false),\n                  subtext: \"블록을 좌우 2단으로 배치\",\n                };\n                const columnDividerItem = {\n                  title: \"2단 컬럼 (구분선)\",\n                  onItemClick: () => insertTwoColumns(editor, true),\n                  aliases: [\n                    \"columns divider\",\n                    \"구분선\",\n                    \"컬럼 구분선\",\n                    \"2단 구분선\",\n                    \"divider\",\n                  ],\n                  group: \"Basic blocks\",\n                  icon: columnIcon(true),\n                  subtext: \"가운데 세로 구분선이 있는 2단 컬럼\",\n                };\n\n                const allItems = [\n                  ...filtered,\n                  htmlPreviewItem,\n                  columnItem,\n                  columnDividerItem,\n                ];\n\n                // Link Preview 슬래시 메뉴 항목 (linkPreview 설정이 있을 때만)\n                if (linkPreview?.apiEndpoint) {\n                  allItems.push({\n                    title: \"Link Preview\",\n                    onItemClick: () => {\n                      insertOrUpdateBlock(editor, {\n                        type: \"linkPreview\",\n                        props: { url: \"\" },\n                      });\n                    },\n                    aliases: [\n                      \"link\",\n                      \"preview\",\n                      \"url\",\n                      \"링크\",\n                      \"미리보기\",\n                      \"프리뷰\",\n                    ],\n                    group: \"Embeds\",\n                    icon: (\n                      <svg\n                        width=\"18\"\n                        height=\"18\"\n                        viewBox=\"0 0 24 24\"\n                        fill=\"none\"\n                        stroke=\"currentColor\"\n                        strokeWidth=\"2\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      >\n                        <path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\" />\n                        <path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\" />\n                      </svg>\n                    ),\n                    subtext: \"URL의 미리보기 카드를 삽입\",\n                  });\n                }\n\n                // 한글 제목은 유지하되, 영어로도 검색 가능하도록 각 항목에 영어\n                // 로케일(en)의 별칭 + 영어 제목을 별칭으로 병합한다(예: /table, /heading).\n                const enSlash = en.slash_menu as Record<\n                  string,\n                  { title?: string; aliases?: string[] }\n                >;\n                for (const it of allItems as any[]) {\n                  const enEntry = it.key ? enSlash[it.key] : undefined;\n                  if (!enEntry) continue;\n                  const extra = [...(enEntry.aliases ?? []), enEntry.title]\n                    .filter((s): s is string => Boolean(s))\n                    .map((s) => s.toLowerCase());\n                  it.aliases = Array.from(\n                    new Set([...(it.aliases ?? []), ...extra])\n                  );\n                }\n\n                // 같은 group 항목이 비연속으로 섞여 있으면 SuggestionMenu가 동일\n                // group 헤더를 중복 렌더해 React key 충돌이 난다(예: ko 로케일에서\n                // \"제목\" group이 분리됨). group을 안정 정렬해 연속되도록 만든다.\n                const groupOrder: string[] = [];\n                for (const it of allItems) {\n                  const g = (it as any).group ?? \"\";\n                  if (!groupOrder.includes(g)) groupOrder.push(g);\n                }\n                allItems.sort(\n                  (a: any, b: any) =>\n                    groupOrder.indexOf(a.group ?? \"\") -\n                    groupOrder.indexOf(b.group ?? \"\")\n                );\n\n                if (!query) return allItems;\n                const q = query.toLowerCase();\n                return allItems.filter(\n                  (item: any) =>\n                    item.title?.toLowerCase().includes(q) ||\n                    (item.aliases || []).some((a: string) =>\n                      a.toLowerCase().includes(q)\n                    )\n                );\n              },\n              [editor, allowVideoUpload, linkPreview?.apiEndpoint]\n            )}\n          />\n        }\n        {!sideMenuAddButton && (\n          <SideMenuController sideMenu={DragHandleOnlySideMenu} />\n        )}\n      </BlockNoteView>\n\n      {/* 이미지/비디오 업로드 로딩 스피너 */}\n      {isUploading && (\n        <div className=\"lumirEditor-upload-overlay\">\n          <div className=\"lumirEditor-spinner\" />\n          {uploadProgress !== null && (\n            <span className=\"lumirEditor-upload-progress\">{uploadProgress}%</span>\n          )}\n        </div>\n      )}\n\n      {/* 에러 메시지 토스트 */}\n      {errorMessage && (\n        <div className=\"lumirEditor-error-toast\">\n          <span className=\"lumirEditor-error-icon\">⚠️</span>\n          <span className=\"lumirEditor-error-message\">{errorMessage}</span>\n          <button\n            className=\"lumirEditor-error-close\"\n            onClick={() => setErrorMessage(null)}\n            type=\"button\"\n          >\n            ✕\n          </button>\n        </div>\n      )}\n    </div>\n  );\n}\n","// clsx와 tailwind-merge를 사용한 className 유틸리티\n// 사용자가 직접 설치하도록 권장하거나, 간단한 버전 제공\n\nexport function cn(...inputs: (string | undefined | null | false)[]) {\n  return inputs.filter(Boolean).join(' ');\n}\n","export interface S3UploaderConfig {\n  apiEndpoint: string; // '/api/s3/presigned'(필수)\n  env: \"production\" | \"development\"; // 환경 (필수)\n  path: string; // 파일 경로 (필수)\n  /** 파일명 변환 콜백 - 확장자를 제외한 파일명을 받아 변환합니다 */\n  fileNameTransform?: (nameWithoutExt: string, file: File) => string;\n  /** true일 경우 파일명 뒤에 UUID를 자동으로 추가합니다 (예: image_abc123.png) */\n  appendUUID?: boolean;\n  /** false로 설정하면 확장자를 자동으로 붙이지 않음 (기본: true) */\n  preserveExtension?: boolean;\n  /** 업로드 진행률(0–100) 콜백. S3 PUT 시에만 호출됨 */\n  onProgress?: (percent: number) => void;\n  /** PUT 요청 타임아웃(ms). 미설정 시 120000(120초). 대용량 비디오에 유리 */\n  uploadTimeoutMs?: number;\n  /** PUT 실패 시 재시도 횟수. 기본 2(최대 3회 시도) */\n  maxRetries?: number;\n}\n\n/**\n * 🔒 보안: S3 URL 검증\n * HTTPS 프로토콜 강제 및 URL 형식 검증\n */\nfunction validateS3Url(url: unknown, fieldName: string): string {\n  // 타입 검증\n  if (typeof url !== \"string\" || !url || url.trim() === \"\") {\n    throw new Error(\n      `${fieldName} is required and must be a non-empty string`\n    );\n  }\n\n  // HTTPS 프로토콜 강제 (SSRF 방지)\n  if (!url.startsWith(\"https://\")) {\n    throw new Error(`${fieldName} must use HTTPS protocol`);\n  }\n\n  // URL 형식 검증\n  try {\n    const urlObj = new URL(url);\n    // 추가 검증: localhost, private IP 차단\n    const hostname = urlObj.hostname.toLowerCase();\n    if (\n      hostname === \"localhost\" ||\n      hostname.startsWith(\"127.\") ||\n      hostname.startsWith(\"192.168.\") ||\n      hostname.startsWith(\"10.\") ||\n      hostname === \"169.254.169.254\" // AWS 메타데이터 서버\n    ) {\n      throw new Error(`${fieldName} cannot point to internal/private networks`);\n    }\n  } catch (error) {\n    if (error instanceof Error && error.message.includes(\"cannot point to\")) {\n      throw error;\n    }\n    throw new Error(`${fieldName} is not a valid URL format`);\n  }\n\n  return url;\n}\n\n// UUID 생성 함수 (crypto.randomUUID 또는 폴백)\nconst generateUUID = (): string => {\n  if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n    return crypto.randomUUID();\n  }\n  // 폴백: 간단한 UUID v4 형식 생성\n  return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n    const r = (Math.random() * 16) | 0;\n    const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n    return v.toString(16);\n  });\n};\n\nexport const createS3Uploader = (config: S3UploaderConfig) => {\n  const {\n    apiEndpoint,\n    env,\n    path,\n    fileNameTransform,\n    appendUUID,\n    preserveExtension = true,\n    onProgress,\n    uploadTimeoutMs = 120000,\n    maxRetries = 2,\n  } = config;\n\n  // 필수 파라미터 검증\n  if (!apiEndpoint || apiEndpoint.trim() === \"\") {\n    throw new Error(\n      \"apiEndpoint is required for S3 upload. Please provide a valid API endpoint.\"\n    );\n  }\n\n  if (!env) {\n    throw new Error(\"env is required. Must be 'development' or 'production'.\");\n  }\n\n  if (!path || path.trim() === \"\") {\n    throw new Error(\"path is required and cannot be empty.\");\n  }\n\n  // 파일명에 UUID 추가하는 함수\n  const appendUUIDToFileName = (filename: string): string => {\n    const lastDotIndex = filename.lastIndexOf(\".\");\n    if (lastDotIndex === -1) {\n      // 확장자가 없는 경우\n      return `${filename}_${generateUUID()}`;\n    }\n    const name = filename.substring(0, lastDotIndex);\n    const ext = filename.substring(lastDotIndex);\n    return `${name}_${generateUUID()}${ext}`;\n  };\n\n  // 계층 구조 파일명 생성 함수\n  const generateHierarchicalFileName = (file: File): string => {\n    // 0. 확장자 분리\n    const originalName = file.name;\n    const lastDotIndex = originalName.lastIndexOf(\".\");\n    const nameWithoutExt =\n      lastDotIndex === -1\n        ? originalName\n        : originalName.substring(0, lastDotIndex);\n    const extension =\n      lastDotIndex === -1 ? \"\" : originalName.substring(lastDotIndex);\n\n    let filename = nameWithoutExt;\n\n    // 1. 사용자 정의 파일명 변환 콜백 적용 (확장자 제외한 이름만)\n    if (fileNameTransform) {\n      filename = fileNameTransform(filename, file);\n    }\n\n    // 2. UUID 자동 추가 (appendUUID가 true인 경우)\n    if (appendUUID) {\n      filename = `${filename}_${generateUUID()}`;\n    }\n\n    // 3. 확장자 다시 붙이기 (preserveExtension이 true인 경우만)\n    if (preserveExtension) {\n      filename = `${filename}${extension}`;\n    }\n\n    // {env}/{path}/{filename}\n    return `${env}/${path}/${filename}`;\n  };\n\n  const debugLog = (loc: string, msg: string, data: Record<string, unknown>) => {\n    const p = fetch(\"http://127.0.0.1:7686/ingest/1f8ee1c5-0cf0-4ae7-91ed-5ea7ed17130a\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\", \"X-Debug-Session-Id\": \"b73262\" },\n      body: JSON.stringify({ sessionId: \"b73262\", location: loc, message: msg, data, timestamp: Date.now() }),\n    });\n    if (p && typeof (p as Promise<unknown>).catch === \"function\") (p as Promise<unknown>).catch(() => {});\n  };\n\n  return async (file: File): Promise<string> => {\n    try {\n      if (!apiEndpoint || apiEndpoint.trim() === \"\") {\n        throw new Error(\n          \"Invalid apiEndpoint: Cannot upload file without a valid API ENDPOINT\"\n        );\n      }\n\n      const fileName = generateHierarchicalFileName(file);\n      const contentType = file.type || \"application/octet-stream\";\n      const presignedUrlFull = `${apiEndpoint}?key=${encodeURIComponent(fileName)}&contentType=${encodeURIComponent(contentType)}`;\n      const tPresigned = Date.now();\n      // #region agent log\n      debugLog(\"s3:step1:presignedReq\", \"fetching presigned URL\", {\n        fileName,\n        contentType,\n        apiEndpoint,\n      });\n      // #endregion\n      const response = await fetch(presignedUrlFull);\n\n      // #region agent log\n      debugLog(\"s3:step2:presignedRes\", \"presigned response\", {\n        ok: response.ok,\n        status: response.status,\n        elapsedMs: Date.now() - tPresigned,\n      });\n      // #endregion\n      if (!response.ok) {\n        const errorText = (await response.text()) || \"\";\n        debugLog(\"s3:step2b:presignedErr\", \"presigned failed\", {\n          status: response.status,\n          errorText: errorText.slice(0, 200),\n        });\n        throw new Error(\n          `Failed to get presigned URL: ${response.statusText}, ${errorText}`\n        );\n      }\n\n      const responseData = await response.json();\n      const { presignedUrl, publicUrl } = responseData;\n      const validatedPresignedUrl = validateS3Url(presignedUrl, \"presignedUrl\");\n      const validatedPublicUrl = validateS3Url(publicUrl, \"publicUrl\");\n\n      const tPut = Date.now();\n      // #region agent log\n      debugLog(\"s3:step3:putReq\", \"S3 PUT request\", { publicUrlLen: validatedPublicUrl?.length });\n      // #endregion\n\n      let lastError: Error | undefined;\n      const attempts = maxRetries + 1;\n      for (let attempt = 0; attempt < attempts; attempt++) {\n        try {\n          if (onProgress && typeof XMLHttpRequest !== \"undefined\") {\n            await new Promise<void>((resolve, reject) => {\n              const xhr = new XMLHttpRequest();\n              xhr.timeout = uploadTimeoutMs;\n\n              // --- 진행률 보간용 상태 (progress 이벤트가 드물 때 중간 % 표시) ---\n              let lastReported = -1; // 마지막으로 콜백에 넘긴 퍼센트 (중복 호출 방지)\n              const REPORT_INTERVAL_MS = 100; // 보간 타이머 주기(ms)\n              const simulatedCap = 90; // 보간 진행률 상한(%). 90 이상은 실제 progress 값 사용\n              let simulatedPercent = 0;\n\n              // 보간 타이머: progress 이벤트가 잘 오지 않을 때 주기적으로 진행률 상승\n              const simId = setInterval(() => {\n                if (simulatedPercent >= simulatedCap) return;\n                simulatedPercent = Math.min(simulatedCap, simulatedPercent + 3);\n                const p = Math.min(100, simulatedPercent);\n                if (p > lastReported) {\n                  lastReported = p;\n                  onProgress(p);\n                }\n              }, REPORT_INTERVAL_MS);\n              const clearSim = () => {\n                clearInterval(simId);\n              };\n\n              // XHR 실제 업로드 진행률 (loaded/total)\n              xhr.upload.onprogress = (e) => {\n                if (e.lengthComputable) {\n                  const p = Math.min(100, Math.round((e.loaded / e.total) * 100));\n                  if (p >= simulatedCap) clearSim(); // 실제 진행이 90% 넘으면 보간 타이머 중단\n                  const toReport = Math.max(p, simulatedPercent); // 실제 vs 보간 중 큰 값 사용\n                  if (toReport > lastReported) {\n                    lastReported = toReport;\n                    onProgress(toReport);\n                  }\n                }\n              };\n              xhr.onload = () => {\n                clearSim();\n                if (xhr.status >= 200 && xhr.status < 300) {\n                  if (lastReported < 100) onProgress(100); // 완료 시 100% 한 번 더 보장\n                  resolve();\n                } else {\n                  reject(new Error(`Failed to upload file: ${xhr.statusText}`));\n                }\n              };\n              xhr.onerror = () => {\n                clearSim();\n                reject(new Error(\"Upload failed\"));\n              };\n              xhr.ontimeout = () => {\n                clearSim();\n                reject(new Error(\"Upload timeout\"));\n              };\n              xhr.open(\"PUT\", validatedPresignedUrl);\n              xhr.setRequestHeader(\"Content-Type\", file.type || \"application/octet-stream\");\n              onProgress(0); // 업로드 시작 직후 0% 알림\n              lastReported = 0;\n              xhr.send(file);\n            });\n          } else {\n            const controller = new AbortController();\n            const timeoutId = setTimeout(() => controller.abort(), uploadTimeoutMs);\n            const uploadResponse = await fetch(validatedPresignedUrl, {\n              method: \"PUT\",\n              headers: {\n                \"Content-Type\": file.type || \"application/octet-stream\",\n              },\n              body: file,\n              signal: controller.signal,\n            });\n            clearTimeout(timeoutId);\n\n            // #region agent log\n            debugLog(\"s3:step4:putRes\", \"S3 PUT response\", {\n              ok: uploadResponse.ok,\n              status: uploadResponse.status,\n              putElapsedMs: Date.now() - tPut,\n            });\n            // #endregion\n            if (!uploadResponse.ok) {\n              throw new Error(`Failed to upload file: ${uploadResponse.statusText}`);\n            }\n          }\n          debugLog(\"s3:step5:return\", \"returning publicUrl\", { urlPrefix: validatedPublicUrl.slice(0, 80) });\n          return validatedPublicUrl;\n        } catch (err) {\n          lastError = err instanceof Error ? err : new Error(String(err));\n          if (attempt < attempts - 1) {\n            debugLog(\"s3:putRetry\", \"PUT failed, retrying\", { attempt: attempt + 1, attempts });\n          } else {\n            throw lastError;\n          }\n        }\n      }\n\n      throw lastError ?? new Error(\"Upload failed\");\n    } catch (error) {\n      console.error(\"S3 upload failed:\", error);\n      throw error;\n    }\n  };\n};\n","import { createReactBlockSpec } from \"@blocknote/react\";\nimport {\n  defaultBlockSpecs,\n  BlockNoteSchema,\n  defaultInlineContentSpecs,\n  defaultStyleSpecs,\n  createBlockSpecFromStronglyTypedTiptapNode,\n} from \"@blocknote/core\";\nimport { LinkPreviewBlock } from \"./LinkPreview\";\nimport { VideoBlock } from \"./VideoBlock\";\nimport { ColumnList } from \"./columns/ColumnList\";\nimport { Column } from \"./columns/Column\";\nimport { FontSize } from \"../styles/FontSizeStyle\";\nimport { useState, useRef, useCallback, useEffect } from \"react\";\n\n// HTML 미리보기 블록 속성 타입\nexport interface HtmlPreviewProps {\n  htmlContent: string;\n  fileName?: string;\n  height?: string;\n}\n\n// 최소/최대 높이 상수\n/** @internal 테스트용 export */\nexport const MIN_HEIGHT = 100;\n/** @internal 테스트용 export */\nexport const MAX_HEIGHT = 1200;\n\n// ============================================\n// 보안 유틸리티 함수\n// ============================================\n\n/**\n * HTML에 charset이 없으면 UTF-8 meta 태그 추가\n * (원본 HTML을 최소한으로만 수정하여 인코딩 깨짐 방지)\n * @internal 테스트용 export\n */\nexport const ensureCharset = (html: string): string => {\n  // 이미 charset이 있으면 원본 그대로 반환\n  const hasCharset = /<meta[^>]+charset\\s*=/i.test(html);\n  if (hasCharset) {\n    return html;\n  }\n\n  // <head> 태그가 있으면 그 안에 추가\n  if (/<head[^>]*>/i.test(html)) {\n    return html.replace(/(<head[^>]*>)/i, '$1\\n<meta charset=\"UTF-8\">');\n  }\n\n  // <html> 태그만 있으면 <head> 추가\n  if (/<html[^>]*>/i.test(html)) {\n    return html.replace(\n      /(<html[^>]*>)/i,\n      '$1\\n<head><meta charset=\"UTF-8\"></head>'\n    );\n  }\n\n  // HTML fragment인 경우 최소한의 구조 추가\n  return `<!DOCTYPE html>\n<html>\n<head><meta charset=\"UTF-8\"></head>\n<body>\n${html}\n</body>\n</html>`;\n};\n\n/**\n * 파일명 새니타이제이션 (경로 조작 방지)\n * @internal 테스트용 export\n */\nexport const sanitizeFileName = (fileName: string): string => {\n  if (!fileName || typeof fileName !== \"string\") {\n    return `document_${Date.now()}.html`;\n  }\n\n  return (\n    fileName\n      .replace(/\\0/g, \"\") // Null byte 제거\n      .replace(/[\\/\\\\]/g, \"_\") // 경로 구분자 제거\n      .replace(/[<>:\"|?*\\x00-\\x1f]/g, \"\") // 위험한 문자 제거\n      .replace(/\\.{2,}/g, \".\") // 연속된 점 제거\n      .trim()\n      .replace(/^\\.+|\\.+$/g, \"\") || `document_${Date.now()}.html` // 앞뒤 점 제거\n  );\n};\n\n/**\n * Blob URL 생성 (UTF-8 인코딩 명시)\n * @internal 테스트용 export\n */\nexport const createSecureBlobUrl = (htmlContent: string): string => {\n  const htmlWithCharset = ensureCharset(htmlContent);\n\n  // UTF-8 인코딩 명시\n  const blob = new Blob([htmlWithCharset], {\n    type: \"text/html;charset=utf-8\",\n  });\n\n  return URL.createObjectURL(blob);\n};\n\n// ============================================\n// HTML 미리보기 블록 스펙\n// ============================================\n\nexport const HtmlPreviewBlock = createReactBlockSpec(\n  {\n    type: \"htmlPreview\",\n    propSchema: {\n      htmlContent: {\n        default: \"\",\n      },\n      fileName: {\n        default: \"\",\n      },\n      height: {\n        default: \"400px\",\n      },\n    },\n    content: \"none\",\n  },\n  {\n    render: (props) => {\n      const [isExpanded, setIsExpanded] = useState(true);\n      const [isResizing, setIsResizing] = useState(false);\n      const [blobUrl, setBlobUrl] = useState<string>(\"\");\n      const containerRef = useRef<HTMLDivElement>(null);\n\n      const htmlContent = props.block.props.htmlContent || \"\";\n      const fileName = props.block.props.fileName || \"HTML Document\";\n      const savedHeight = props.block.props.height || \"400px\";\n\n      // 현재 높이 (숫자로 파싱)\n      const currentHeight = parseInt(savedHeight, 10) || 400;\n\n      // UTF-8 인코딩 보장된 Blob URL 생성\n      useEffect(() => {\n        if (htmlContent) {\n          const url = createSecureBlobUrl(htmlContent);\n          setBlobUrl(url);\n\n          return () => {\n            URL.revokeObjectURL(url);\n          };\n        }\n      }, [htmlContent]);\n\n      // 리사이즈 시작\n      const handleResizeStart = useCallback(\n        (e: React.MouseEvent) => {\n          e.preventDefault();\n          e.stopPropagation();\n          setIsResizing(true);\n\n          const startY = e.clientY;\n          const startHeight = currentHeight;\n\n          const handleMouseMove = (moveEvent: MouseEvent) => {\n            const deltaY = moveEvent.clientY - startY;\n            const newHeight = Math.min(\n              MAX_HEIGHT,\n              Math.max(MIN_HEIGHT, startHeight + deltaY)\n            );\n\n            // 블록 props 업데이트 (저장됨)\n            props.editor.updateBlock(props.block, {\n              props: { height: `${newHeight}px` },\n            });\n          };\n\n          const handleMouseUp = () => {\n            setIsResizing(false);\n            document.removeEventListener(\"mousemove\", handleMouseMove);\n            document.removeEventListener(\"mouseup\", handleMouseUp);\n          };\n\n          document.addEventListener(\"mousemove\", handleMouseMove);\n          document.addEventListener(\"mouseup\", handleMouseUp);\n        },\n        [currentHeight, props.editor, props.block]\n      );\n\n      // HTML 파일 다운로드 (원본 그대로 + 인코딩 보장)\n      const handleExport = useCallback(\n        (e: React.MouseEvent) => {\n          e.stopPropagation();\n\n          // 파일명 새니타이제이션 (경로 조작 방지)\n          const safeFileName = sanitizeFileName(fileName);\n          const downloadName = safeFileName.endsWith(\".html\")\n            ? safeFileName\n            : `${safeFileName}.html`;\n\n          // UTF-8 인코딩 명시\n          const htmlWithCharset = ensureCharset(htmlContent);\n          const blob = new Blob([htmlWithCharset], {\n            type: \"text/html;charset=utf-8\",\n          });\n\n          const url = URL.createObjectURL(blob);\n          const a = document.createElement(\"a\");\n          a.href = url;\n          a.download = downloadName;\n          a.rel = \"noopener noreferrer\"; // 보안 속성 추가\n\n          document.body.appendChild(a);\n          a.click();\n          document.body.removeChild(a);\n          URL.revokeObjectURL(url);\n        },\n        [htmlContent, fileName]\n      );\n\n      // 새 창에서 열기 (Blob URL 방식 - XSS 방지)\n      const handleOpenNewWindow = useCallback(\n        (e: React.MouseEvent) => {\n          e.stopPropagation();\n\n          // 클라이언트 사이드에서만 실행\n          if (typeof window === \"undefined\") return;\n\n          // Blob URL 생성 (UTF-8 인코딩 보장)\n          const url = createSecureBlobUrl(htmlContent);\n\n          // noopener, noreferrer로 보안 강화\n          const newWindow = window.open(url, \"_blank\", \"noopener,noreferrer\");\n\n          // Blob URL 정리\n          if (newWindow) {\n            setTimeout(() => URL.revokeObjectURL(url), 1000);\n          } else {\n            URL.revokeObjectURL(url);\n          }\n        },\n        [htmlContent]\n      );\n\n      return (\n        <div\n          ref={containerRef}\n          style={{\n            border: \"1px solid #e0e0e0\",\n            borderRadius: \"8px\",\n            overflow: \"hidden\",\n            backgroundColor: \"#f9f9f9\",\n            marginBottom: \"2px\",\n            width: \"100%\",\n            userSelect: isResizing ? \"none\" : \"auto\",\n            outline: \"none\",\n            boxShadow: \"none\",\n          }}\n        >\n          {/* 헤더 */}\n          <div\n            style={{\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"space-between\",\n              padding: \"4px 16px\",\n              backgroundColor: \"#fff\",\n              borderBottom: isExpanded ? \"1px solid #e0e0e0\" : \"none\",\n            }}\n          >\n            <div\n              style={{\n                display: \"flex\",\n                alignItems: \"center\",\n                gap: \"8px\",\n                cursor: \"pointer\",\n                flex: 1,\n              }}\n              onClick={() => setIsExpanded(!isExpanded)}\n            >\n              <svg\n                width=\"16\"\n                height=\"16\"\n                viewBox=\"0 0 24 24\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                strokeWidth=\"2\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                style={{\n                  transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n                  transition: \"transform 0.2s\",\n                }}\n              >\n                <polyline points=\"6 9 12 15 18 9\"></polyline>\n              </svg>\n\n              <span style={{ fontWeight: 500, fontSize: \"14px\" }}>\n                {fileName}\n              </span>\n            </div>\n\n            {/* 액션 버튼들 */}\n            <div style={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}>\n              {/* 새 창에서 열기 버튼 */}\n              <button\n                onClick={handleOpenNewWindow}\n                style={{\n                  background: \"none\",\n                  border: \"none\",\n                  cursor: \"pointer\",\n                  padding: \"4px\",\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  justifyContent: \"center\",\n                  color: \"#666\",\n                  borderRadius: \"4px\",\n                }}\n                title=\"새 창에서 열기\"\n                type=\"button\"\n                onMouseEnter={(e) => {\n                  (e.currentTarget as HTMLButtonElement).style.backgroundColor =\n                    \"#f0f0f0\";\n                }}\n                onMouseLeave={(e) => {\n                  (e.currentTarget as HTMLButtonElement).style.backgroundColor =\n                    \"transparent\";\n                }}\n              >\n                <svg\n                  width=\"16\"\n                  height=\"16\"\n                  viewBox=\"0 0 24 24\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                  strokeWidth=\"2\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                >\n                  <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"></path>\n                  <polyline points=\"15 3 21 3 21 9\"></polyline>\n                  <line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"></line>\n                </svg>\n              </button>\n\n              {/* 다운로드 버튼 */}\n              <button\n                onClick={handleExport}\n                style={{\n                  background: \"none\",\n                  border: \"none\",\n                  cursor: \"pointer\",\n                  padding: \"4px\",\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  justifyContent: \"center\",\n                  color: \"#666\",\n                  borderRadius: \"4px\",\n                }}\n                title=\"HTML 다운로드\"\n                type=\"button\"\n                onMouseEnter={(e) => {\n                  (e.currentTarget as HTMLButtonElement).style.backgroundColor =\n                    \"#f0f0f0\";\n                }}\n                onMouseLeave={(e) => {\n                  (e.currentTarget as HTMLButtonElement).style.backgroundColor =\n                    \"transparent\";\n                }}\n              >\n                <svg\n                  width=\"16\"\n                  height=\"16\"\n                  viewBox=\"0 0 24 24\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                  strokeWidth=\"2\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                >\n                  <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"></path>\n                  <polyline points=\"7 10 12 15 17 10\"></polyline>\n                  <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"></line>\n                </svg>\n              </button>\n            </div>\n          </div>\n\n          {/* iframe 미리보기 */}\n          {isExpanded && (\n            <div\n              style={{\n                padding: \"0\",\n                backgroundColor: \"#fff\",\n                position: \"relative\",\n              }}\n            >\n              {/* 🔒 보안 강화: JavaScript 완전 차단 + 부모 페이지 접근 차단 */}\n              <iframe\n                src={blobUrl || \"about:blank\"}\n                style={{\n                  width: \"100%\",\n                  height: `${currentHeight}px`,\n                  border: \"none\",\n                  display: \"block\",\n                  pointerEvents: isResizing ? \"none\" : \"auto\",\n                }}\n                // 🔒 allow-scripts 제거 = JavaScript 실행 차단\n                // 🔒 allow-same-origin 제거 = 부모 페이지 접근 차단\n                // ✅ HTML + CSS만 렌더링 (안전)\n                sandbox=\"allow-popups allow-forms\"\n                title={fileName}\n                referrerPolicy=\"no-referrer\"\n                loading=\"lazy\"\n              />\n\n              {/* 리사이즈 핸들 */}\n              <div\n                onMouseDown={handleResizeStart}\n                style={{\n                  position: \"absolute\",\n                  bottom: 0,\n                  left: 0,\n                  right: 0,\n                  height: \"12px\",\n                  cursor: \"ns-resize\",\n                  backgroundColor: isResizing\n                    ? \"rgba(59, 130, 246, 0.3)\"\n                    : \"transparent\",\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  justifyContent: \"center\",\n                  transition: \"background-color 0.2s\",\n                }}\n                onMouseEnter={(e) => {\n                  (e.currentTarget as HTMLDivElement).style.backgroundColor =\n                    \"rgba(59, 130, 246, 0.2)\";\n                }}\n                onMouseLeave={(e) => {\n                  if (!isResizing) {\n                    (e.currentTarget as HTMLDivElement).style.backgroundColor =\n                      \"transparent\";\n                  }\n                }}\n              >\n                {/* 리사이즈 핸들 아이콘 */}\n                <div\n                  style={{\n                    width: \"40px\",\n                    height: \"4px\",\n                    backgroundColor: \"#ccc\",\n                    borderRadius: \"2px\",\n                  }}\n                />\n              </div>\n            </div>\n          )}\n        </div>\n      );\n    },\n  }\n);\n\n// 다단(컬럼) 블록 스펙 — column/columnList Tiptap 노드를 BlockNote 블록 스펙으로 등록.\n// content는 자동으로 \"none\"으로 매핑되고 children은 childContainer에서 읽힌다 → nodeToBlock\n// (직렬화)이 columnList/column을 인식해 라운드트립된다.\nconst ColumnListBlock = createBlockSpecFromStronglyTypedTiptapNode(\n  ColumnList,\n  // showDivider를 블록 prop으로 등록 → onContentChange JSON 직렬화 + 재로드 라운드트립.\n  { showDivider: { default: false } },\n);\nconst ColumnBlock = createBlockSpecFromStronglyTypedTiptapNode(Column, {});\n\n// 커스텀 블록이 포함된 스키마 생성\nexport const schema = BlockNoteSchema.create({\n  blockSpecs: {\n    ...defaultBlockSpecs,\n    htmlPreview: HtmlPreviewBlock,\n    linkPreview: LinkPreviewBlock,\n    video: VideoBlock,\n    columnList: ColumnListBlock,\n    column: ColumnBlock,\n  },\n  inlineContentSpecs: defaultInlineContentSpecs,\n  styleSpecs: {\n    ...defaultStyleSpecs,\n    // 인라인 글자 크기. 저장 JSON 직렬화는 형제 키 방식(font-size-serialization.ts) 사용.\n    fontSize: FontSize,\n  },\n});\n\n// 스키마 타입 export\nexport type HtmlPreviewSchema = typeof schema;\n","import { createReactBlockSpec } from \"@blocknote/react\";\nimport React, { useState, useEffect, useCallback, useRef } from \"react\";\nimport { DEFAULT_LINK_PREVIEW_IMAGE } from \"./defaultLogo\";\n\nexport interface LinkMetadata {\n  url: string;\n  title: string;\n  description?: string;\n  image?: string;\n  domain: string;\n}\n\nconst metadataCache = new Map<string, LinkMetadata>();\n\nexport async function fetchLinkMetadata(\n  url: string,\n  apiEndpoint: string\n): Promise<LinkMetadata> {\n  const cached = metadataCache.get(url);\n  if (cached) return cached;\n\n  const response = await fetch(\n    `${apiEndpoint}?url=${encodeURIComponent(url)}`\n  );\n  if (!response.ok) {\n    throw new Error(`Failed to fetch metadata: ${response.status}`);\n  }\n\n  const metadata: LinkMetadata = await response.json();\n  metadataCache.set(url, metadata);\n  return metadata;\n}\n\nexport function clearMetadataCache() {\n  metadataCache.clear();\n}\n\nfunction extractDomain(url: string): string {\n  try {\n    return new URL(url).hostname.replace(/^www\\./, \"\");\n  } catch {\n    return url;\n  }\n}\n\nconst LinkPreviewCard = ({\n  url,\n  title,\n  description,\n  image,\n  domain,\n  editable,\n  onDelete,\n  width,\n  onWidthChange,\n  height,\n  onHeightChange,\n}: {\n  url: string;\n  title: string;\n  description: string;\n  image: string;\n  domain: string;\n  editable: boolean;\n  onDelete?: () => void;\n  width?: number;\n  onWidthChange?: (width: number) => void;\n  height?: number;\n  onHeightChange?: (height: number) => void;\n}) => {\n  const [imgError, setImgError] = useState(false);\n  const [hovered, setHovered] = useState(false);\n\n  type ResizeParams = {\n    handleUsed: \"left\" | \"right\" | \"bottom\";\n    initialClientX: number;\n    initialClientY: number;\n    initialWidth: number;\n    initialHeight: number;\n  };\n  const [resizeParams, setResizeParams] = useState<ResizeParams | undefined>(undefined);\n  const [localWidth, setLocalWidth] = useState<number | undefined>(width);\n  const [localHeight, setLocalHeight] = useState<number | undefined>(height);\n  const cardRef = useRef<HTMLDivElement>(null);\n  const justResizedRef = useRef(false);\n\n  useEffect(() => { setLocalWidth(width); }, [width]);\n  useEffect(() => { setLocalHeight(height); }, [height]);\n\n  useEffect(() => {\n    if (!resizeParams) return;\n\n    const onMouseMove = (e: MouseEvent) => {\n      if (resizeParams.handleUsed === \"bottom\") {\n        const delta = e.clientY - resizeParams.initialClientY;\n        setLocalHeight(Math.min(Math.max(resizeParams.initialHeight + delta, 100), 600));\n      } else {\n        const delta = resizeParams.handleUsed === \"left\"\n          ? resizeParams.initialClientX - e.clientX\n          : e.clientX - resizeParams.initialClientX;\n        setLocalWidth(Math.min(\n          Math.max(resizeParams.initialWidth + delta, 200),\n          cardRef.current?.parentElement?.clientWidth || 800\n        ));\n      }\n    };\n\n    const onMouseUp = () => {\n      const handle = resizeParams.handleUsed;\n      setResizeParams(undefined);\n      justResizedRef.current = true;\n      setTimeout(() => { justResizedRef.current = false; }, 50);\n      if (handle === \"bottom\") {\n        if (localHeight && onHeightChange) onHeightChange(localHeight);\n      } else {\n        if (localWidth && onWidthChange) onWidthChange(localWidth);\n      }\n    };\n\n    window.addEventListener(\"mousemove\", onMouseMove);\n    window.addEventListener(\"mouseup\", onMouseUp);\n    return () => {\n      window.removeEventListener(\"mousemove\", onMouseMove);\n      window.removeEventListener(\"mouseup\", onMouseUp);\n    };\n  }, [resizeParams, localWidth, localHeight, onWidthChange, onHeightChange]);\n\n  const handleLeftDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n    e.stopPropagation();\n    setResizeParams({\n      handleUsed: \"left\",\n      initialWidth: cardRef.current!.clientWidth,\n      initialHeight: localHeight || 200,\n      initialClientX: e.clientX,\n      initialClientY: e.clientY,\n    });\n  }, [localHeight]);\n\n  const handleRightDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n    e.stopPropagation();\n    setResizeParams({\n      handleUsed: \"right\",\n      initialWidth: cardRef.current!.clientWidth,\n      initialHeight: localHeight || 200,\n      initialClientX: e.clientX,\n      initialClientY: e.clientY,\n    });\n  }, [localHeight]);\n\n  const handleBottomDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n    e.stopPropagation();\n    setResizeParams({\n      handleUsed: \"bottom\",\n      initialWidth: cardRef.current!.clientWidth,\n      initialHeight: localHeight || 200,\n      initialClientX: e.clientX,\n      initialClientY: e.clientY,\n    });\n  }, [localHeight]);\n\n  const handleClick = useCallback(() => {\n    if (url && !resizeParams && !justResizedRef.current) {\n      window.open(url, \"_blank\", \"noopener,noreferrer\");\n    }\n  }, [url, resizeParams]);\n\n  const resizeCursor = resizeParams\n    ? resizeParams.handleUsed === \"bottom\" ? \"ns-resize\" : \"ew-resize\"\n    : \"pointer\";\n\n  return (\n    <div\n      ref={cardRef}\n      className=\"lumir-link-preview-card\"\n      onClick={handleClick}\n      style={{\n        border: \"1px solid #e0e0e0\",\n        borderRadius: \"8px\",\n        overflow: \"hidden\",\n        cursor: resizeCursor,\n        width: localWidth ? `${localWidth}px` : undefined,\n        maxWidth: \"100%\",\n        backgroundColor: \"#fff\",\n        transition: resizeParams ? \"none\" : \"box-shadow 0.2s\",\n        position: \"relative\",\n      }}\n      onMouseEnter={() => {\n        if (!resizeParams) setHovered(true);\n      }}\n      onMouseLeave={() => {\n        if (!resizeParams) setHovered(false);\n      }}\n    >\n      {editable && onDelete && (\n        <button\n          type=\"button\"\n          onClick={(e) => {\n            e.stopPropagation();\n            onDelete();\n          }}\n          className=\"lumir-link-preview-delete\"\n          style={{\n            position: \"absolute\",\n            top: \"8px\",\n            right: \"8px\",\n            width: \"24px\",\n            height: \"24px\",\n            borderRadius: \"50%\",\n            border: \"none\",\n            backgroundColor: \"rgba(0,0,0,0.5)\",\n            color: \"#fff\",\n            cursor: \"pointer\",\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            fontSize: \"14px\",\n            lineHeight: 1,\n            zIndex: 2,\n            opacity: 0,\n            transition: \"opacity 0.2s\",\n          }}\n          title=\"삭제\"\n        >\n          ✕\n        </button>\n      )}\n\n      {editable && (hovered || resizeParams) && (\n        <>\n          <div\n            className=\"lumir-resize-handle\"\n            style={{ left: \"4px\" }}\n            onMouseDown={handleLeftDown}\n          />\n          <div\n            className=\"lumir-resize-handle\"\n            style={{ right: \"4px\" }}\n            onMouseDown={handleRightDown}\n          />\n        </>\n      )}\n\n      {image && !imgError && (\n        <div\n          style={{\n            width: \"100%\",\n            height: `${localHeight || 200}px`,\n            overflow: \"hidden\",\n            backgroundColor: \"#f0f0f0\",\n            position: \"relative\",\n          }}\n        >\n          <img\n            src={image}\n            alt={title || \"Link preview\"}\n            onError={() => setImgError(true)}\n            style={{\n              width: \"100%\",\n              height: \"100%\",\n              objectFit: \"cover\",\n              display: \"block\",\n            }}\n            referrerPolicy=\"no-referrer\"\n            loading=\"lazy\"\n          />\n          {editable && (hovered || resizeParams) && (\n            <div\n              className=\"lumir-resize-handle-bottom\"\n              onMouseDown={handleBottomDown}\n            />\n          )}\n        </div>\n      )}\n\n      <div style={{ padding: \"12px 16px\" }}>\n        <div\n          style={{\n            fontSize: \"14px\",\n            fontWeight: 600,\n            color: \"#1a1a1a\",\n            lineHeight: 1.4,\n            marginBottom: description ? \"4px\" : \"8px\",\n            overflow: \"hidden\",\n            textOverflow: \"ellipsis\",\n            whiteSpace: \"nowrap\",\n          }}\n        >\n          {title || domain}\n        </div>\n\n        {description && (\n          <div\n            style={{\n              fontSize: \"12px\",\n              color: \"#666\",\n              lineHeight: 1.4,\n              marginBottom: \"8px\",\n              overflow: \"hidden\",\n              textOverflow: \"ellipsis\",\n              display: \"-webkit-box\",\n              WebkitLineClamp: 2,\n              WebkitBoxOrient: \"vertical\",\n            }}\n          >\n            {description}\n          </div>\n        )}\n\n        <div\n          style={{\n            fontSize: \"12px\",\n            color: \"#999\",\n            overflow: \"hidden\",\n            textOverflow: \"ellipsis\",\n            whiteSpace: \"nowrap\",\n          }}\n        >\n          {domain}\n        </div>\n      </div>\n    </div>\n  );\n};\n\nconst LinkPreviewSkeleton = () => (\n  <div\n    className=\"lumir-link-preview-skeleton\"\n    style={{\n      border: \"1px solid #e0e0e0\",\n      borderRadius: \"8px\",\n      overflow: \"hidden\",\n      maxWidth: \"400px\",\n      backgroundColor: \"#fff\",\n    }}\n  >\n    <div\n      style={{\n        width: \"100%\",\n        height: \"200px\",\n        backgroundColor: \"#f0f0f0\",\n        animation: \"lumir-skeleton-pulse 1.5s ease-in-out infinite\",\n      }}\n    />\n    <div style={{ padding: \"12px 16px\" }}>\n      <div\n        style={{\n          height: \"16px\",\n          width: \"70%\",\n          backgroundColor: \"#f0f0f0\",\n          borderRadius: \"4px\",\n          marginBottom: \"8px\",\n          animation: \"lumir-skeleton-pulse 1.5s ease-in-out infinite\",\n        }}\n      />\n      <div\n        style={{\n          height: \"12px\",\n          width: \"90%\",\n          backgroundColor: \"#f0f0f0\",\n          borderRadius: \"4px\",\n          marginBottom: \"8px\",\n          animation: \"lumir-skeleton-pulse 1.5s ease-in-out infinite\",\n        }}\n      />\n      <div\n        style={{\n          height: \"12px\",\n          width: \"40%\",\n          backgroundColor: \"#f0f0f0\",\n          borderRadius: \"4px\",\n          animation: \"lumir-skeleton-pulse 1.5s ease-in-out infinite\",\n        }}\n      />\n    </div>\n  </div>\n);\n\nconst LinkPreviewError = ({\n  url,\n  onRetry,\n}: {\n  url: string;\n  onRetry?: () => void;\n}) => {\n  const domain = (() => {\n    try { return new URL(url).hostname; } catch { return url; }\n  })();\n\n  return (\n    <div\n      className=\"lumir-link-preview-error\"\n      style={{\n        border: \"1px solid #e0e0e0\",\n        borderRadius: \"8px\",\n        overflow: \"hidden\",\n        maxWidth: \"400px\",\n        backgroundColor: \"#fff\",\n        cursor: \"pointer\",\n        position: \"relative\",\n      }}\n      onClick={() => window.open(url, \"_blank\", \"noopener,noreferrer\")}\n    >\n      <div\n        style={{\n          width: \"100%\",\n          height: \"160px\",\n          overflow: \"hidden\",\n          backgroundColor: \"#f0f0f0\",\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n        }}\n      >\n        <img\n          src={DEFAULT_LINK_PREVIEW_IMAGE}\n          alt=\"Lumir\"\n          style={{\n            width: \"100%\",\n            height: \"100%\",\n            objectFit: \"contain\",\n            display: \"block\",\n            padding: \"20px\",\n            boxSizing: \"border-box\",\n          }}\n        />\n      </div>\n      <div style={{ padding: \"12px 16px\" }}>\n        <div\n          style={{\n            fontSize: \"13px\",\n            color: \"#999\",\n            marginBottom: \"4px\",\n          }}\n        >\n          미리보기를 불러올 수 없습니다\n        </div>\n        <div\n          style={{\n            fontSize: \"12px\",\n            color: \"#1a73e8\",\n            overflow: \"hidden\",\n            textOverflow: \"ellipsis\",\n            whiteSpace: \"nowrap\",\n          }}\n        >\n          {domain}\n        </div>\n      </div>\n      {onRetry && (\n        <button\n          type=\"button\"\n          onClick={(e) => { e.stopPropagation(); onRetry(); }}\n          style={{\n            position: \"absolute\",\n            top: \"8px\",\n            right: \"8px\",\n            background: \"rgba(0,0,0,0.5)\",\n            border: \"none\",\n            borderRadius: \"4px\",\n            padding: \"4px 8px\",\n            cursor: \"pointer\",\n            fontSize: \"12px\",\n            color: \"#fff\",\n            whiteSpace: \"nowrap\",\n          }}\n        >\n          재시도\n        </button>\n      )}\n    </div>\n  );\n};\n\nconst LinkPreviewUrlInput = ({\n  onSubmit,\n}: {\n  onSubmit: (url: string) => void;\n}) => {\n  const [inputUrl, setInputUrl] = useState(\"\");\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  useEffect(() => {\n    inputRef.current?.focus();\n  }, []);\n\n  const handleSubmit = useCallback(() => {\n    const trimmed = inputUrl.trim();\n    if (!trimmed) return;\n    const normalized = /^https?:\\/\\//i.test(trimmed)\n      ? trimmed\n      : `https://${trimmed}`;\n    onSubmit(normalized);\n  }, [inputUrl, onSubmit]);\n\n  return (\n    <div\n      className=\"lumir-link-preview-input\"\n      style={{\n        border: \"1px solid #e0e0e0\",\n        borderRadius: \"8px\",\n        padding: \"16px\",\n        maxWidth: \"400px\",\n        backgroundColor: \"#fafafa\",\n        display: \"flex\",\n        alignItems: \"center\",\n        gap: \"8px\",\n      }}\n    >\n      <svg\n        width=\"18\"\n        height=\"18\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"#999\"\n        strokeWidth=\"2\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        style={{ flexShrink: 0 }}\n      >\n        <path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\" />\n        <path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\" />\n      </svg>\n      <input\n        ref={inputRef}\n        type=\"text\"\n        value={inputUrl}\n        onChange={(e) => setInputUrl(e.target.value)}\n        onKeyDown={(e) => {\n          if (e.key === \"Enter\") {\n            e.preventDefault();\n            handleSubmit();\n          }\n        }}\n        placeholder=\"URL을 입력하세요 (예: https://example.com)\"\n        style={{\n          flex: 1,\n          border: \"none\",\n          outline: \"none\",\n          backgroundColor: \"transparent\",\n          fontSize: \"13px\",\n          color: \"#333\",\n          minWidth: 0,\n        }}\n      />\n      <button\n        type=\"button\"\n        onClick={handleSubmit}\n        disabled={!inputUrl.trim()}\n        style={{\n          flexShrink: 0,\n          padding: \"4px 12px\",\n          border: \"none\",\n          borderRadius: \"4px\",\n          backgroundColor: inputUrl.trim() ? \"#3b82f6\" : \"#d1d5db\",\n          color: \"#fff\",\n          fontSize: \"12px\",\n          fontWeight: 500,\n          cursor: inputUrl.trim() ? \"pointer\" : \"not-allowed\",\n          transition: \"background-color 0.15s\",\n        }}\n      >\n        확인\n      </button>\n    </div>\n  );\n};\n\nexport const LinkPreviewBlock = createReactBlockSpec(\n  {\n    type: \"linkPreview\",\n    propSchema: {\n      url: { default: \"\" },\n      title: { default: \"\" },\n      description: { default: \"\" },\n      image: { default: \"\" },\n      domain: { default: \"\" },\n      previewWidth: { default: 400 as const },\n      previewHeight: { default: 200 as const },\n    },\n    content: \"none\",\n  },\n  {\n    render: (props) => {\n      const { url, title, description, image, domain } =\n        props.block.props;\n      const [status, setStatus] = useState<\"idle\" | \"loading\" | \"error\">(\n        !url ? \"idle\" : title ? \"idle\" : \"loading\"\n      );\n      const fetchedRef = useRef(false);\n\n      const editable = props.editor.isEditable;\n\n      useEffect(() => {\n        if (!url || title || fetchedRef.current) {\n          if (!url) setStatus(\"idle\");\n          return;\n        }\n\n        const getApiEndpoint = () =>\n          (props.editor as any)._linkPreviewApiEndpoint as\n            | string\n            | undefined;\n\n        const doFetch = (endpoint: string) => {\n          fetchedRef.current = true;\n          setStatus(\"loading\");\n\n          fetchLinkMetadata(url, endpoint)\n            .then((metadata) => {\n              props.editor.updateBlock(props.block, {\n                props: {\n                  title: metadata.title || \"\",\n                  description: metadata.description || \"\",\n                  image: metadata.image || \"\",\n                  domain: metadata.domain || extractDomain(url),\n                },\n              });\n              setStatus(\"idle\");\n            })\n            .catch(() => {\n              props.editor.updateBlock(props.block, {\n                props: { domain: extractDomain(url) },\n              });\n              setStatus(\"error\");\n            });\n        };\n\n        const endpoint = getApiEndpoint();\n        if (endpoint) {\n          doFetch(endpoint);\n        } else {\n          // endpoint가 아직 설정되지 않았을 수 있으므로 잠시 대기 후 재시도\n          const timer = setTimeout(() => {\n            const retryEndpoint = getApiEndpoint();\n            if (retryEndpoint) {\n              doFetch(retryEndpoint);\n            } else {\n              setStatus(\"error\");\n            }\n          }, 100);\n          return () => clearTimeout(timer);\n        }\n      }, [url, title]);\n\n      const handleRetry = useCallback(() => {\n        const endpoint = (props.editor as any)._linkPreviewApiEndpoint as\n          | string\n          | undefined;\n        if (!url || !endpoint) return;\n        metadataCache.delete(url);\n        fetchedRef.current = false;\n        setStatus(\"loading\");\n\n        fetchLinkMetadata(url, endpoint)\n          .then((metadata) => {\n            props.editor.updateBlock(props.block, {\n              props: {\n                title: metadata.title || \"\",\n                description: metadata.description || \"\",\n                image: metadata.image || \"\",\n                domain: metadata.domain || extractDomain(url),\n              },\n            });\n            setStatus(\"idle\");\n          })\n          .catch(() => {\n            setStatus(\"error\");\n          });\n      }, [url, props.editor, props.block]);\n\n      const handleDelete = useCallback(() => {\n        props.editor.removeBlocks([props.block]);\n      }, [props.editor, props.block]);\n\n      if (!url) {\n        if (!editable) {\n          return null;\n        }\n        return (\n          <LinkPreviewUrlInput\n            onSubmit={(newUrl) => {\n              props.editor.updateBlock(props.block, {\n                props: { url: newUrl },\n              });\n            }}\n          />\n        );\n      }\n\n      if (status === \"loading\") {\n        return <LinkPreviewSkeleton />;\n      }\n\n      if (status === \"error\") {\n        return <LinkPreviewError url={url} onRetry={handleRetry} />;\n      }\n\n      return (\n        <LinkPreviewCard\n          url={url}\n          title={title}\n          description={description}\n          image={image}\n          domain={domain || extractDomain(url)}\n          editable={editable}\n          onDelete={handleDelete}\n          width={props.block.props.previewWidth}\n          onWidthChange={(newWidth) => {\n            props.editor.updateBlock(props.block, {\n              props: { previewWidth: newWidth },\n            });\n          }}\n          height={props.block.props.previewHeight}\n          onHeightChange={(newHeight) => {\n            props.editor.updateBlock(props.block, {\n              props: { previewHeight: newHeight },\n            });\n          }}\n        />\n      );\n    },\n  }\n);\n","export const DEFAULT_LINK_PREVIEW_IMAGE = \"data:image/png;base64,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em1RHrY9EhgB5JWPq9DabLnDWKK8KrQqj2dIoXVQj+a8flLfBJN6T2FxKe//POeH76ctPvCWE/otMJ1HREREREREREREJJlUannNa4uGYNuPAV0rjavQ2my5w1iivCq0Ko9nSKGVFQzw84U706NFvQQTe9fcpetK3v1h9h3/N7TrVaaziIiIiIiIiIiIiCSLSi2veG1Rc+BubPvEKstUaG223GEsUV4VWpXHM6TQAjilfxueOKJngon9YdJfC+d+8du8g64+ZvufTGcRERERERERERERqS2VWl7w2qJTiBdaTaosU6G12XKHsUR5VWhVHs+gQis/J8jkS3elXaM8/G7l2iL77e9iYz79Ze7xz1y8R7HpPCIiIiIiIiIiIiI1pVIrk722yAIeA4ZvU6GhQqvyGxVaniq0AC7bI8Rtw7skmNyffpu5bNWnv8w96pLDtvvQdBYRERERERERERGRmlCplYnGLA5g22cAdwMNVGhVZy6HsUR5VWhVHs+wQqtxnRymX7krjepkJ9iBf63bUMy7381655XPZxzx+nX7FprOIyIiIiIiIiIiIrItVGplmjGLO2LbTwFDgG0rNFRoVX6jQstzhRY23LF/Fy7ZI5RgBwLwxz9LV3/ww+xjLj+677ums4iIiIiIiIiIiIhUl0qtTDJm8WnY9n1AA0CFVrXmchhLlFeFVuXxDCy02jeqw+TLd6FOdqUl4mDdhmI++H7WW+9/N+uIJy7dU8/aEhEREREREREREddTqZUJxixuDjyBbR9SPqZCS4XWFrP6r9ACeOqonpy0Y5sEOxEnv85YsvzTH2fvf+m/tv/adBYRERERERERERGRLVGp5XZjFh8EPIlttygfU6GlQmuLWf1ZaPVqWZ+fL9qZgP5U22Yr1xTab07659GTh3U/z3QWERERERERERERkUR0+tetxizOB+4FzqpRMbWl11tarkJrC/tQoVWtdR23cd5tsgotgDdP7ssBPZon2JFUx4Rf58a++XPBblce32+W6SwiIiIiIiIiIiIim1Op5UZjFm8HvAr0UqFVzXVVaPm60NqtQwGfn90/wY5kW8xbsrb4va9nnnvGQb2eMJ1FREREREREREREpCKVWm4zZvFZwH1AHRVa1VxXhZavC60ANhPP3YmBVqMEO5NtVVRcyrtfRz/86NvogY9ePqTYdB4RERERERERERER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\";\n","import { createReactBlockSpec } from \"@blocknote/react\";\nimport React, { useState, useCallback, useRef, useEffect } from \"react\";\n\nconst MIN_VIDEO_WIDTH = 200;\nconst MAX_VIDEO_WIDTH = 1200;\nconst MIN_VIDEO_HEIGHT = 120;\nconst MAX_VIDEO_HEIGHT = 600;\nconst DEFAULT_VIDEO_WIDTH = 640;\nconst DEFAULT_VIDEO_HEIGHT = 360;\n\ntype ResizeParams = {\n  handleUsed: \"left\" | \"right\" | \"bottom\";\n  initialClientX: number;\n  initialClientY: number;\n  initialWidth: number;\n  initialHeight: number;\n};\n\n/**\n * 비디오 블록 카드: 우클릭 방지 + 다운로드 버튼 미노출\n * - controlsList=\"nodownload\": 브라우저 기본 컨트롤에서 다운로드 버튼 숨김\n * - onContextMenu preventDefault: 우클릭 컨텍스트 메뉴(동영상 저장 등) 차단\n */\nconst VideoBlockCard = ({\n  url,\n  editable,\n  width,\n  onWidthChange,\n  height,\n  onHeightChange,\n  onContextMenuBlock,\n}: {\n  url: string;\n  editable: boolean;\n  width?: number;\n  onWidthChange?: (width: number) => void;\n  height?: number;\n  onHeightChange?: (height: number) => void;\n  onContextMenuBlock: (e: React.MouseEvent) => void;\n}) => {\n  const [resizeParams, setResizeParams] = useState<ResizeParams | undefined>(\n    undefined,\n  );\n  const [localWidth, setLocalWidth] = useState<number | undefined>(width);\n  const [localHeight, setLocalHeight] = useState<number | undefined>(height);\n  const wrapperRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    setLocalWidth(width);\n  }, [width]);\n  useEffect(() => {\n    setLocalHeight(height);\n  }, [height]);\n\n  useEffect(() => {\n    if (!resizeParams) return;\n\n    const onMouseMove = (e: MouseEvent) => {\n      if (resizeParams.handleUsed === \"bottom\") {\n        const delta = e.clientY - resizeParams.initialClientY;\n        setLocalHeight(\n          Math.min(\n            Math.max(resizeParams.initialHeight + delta, MIN_VIDEO_HEIGHT),\n            MAX_VIDEO_HEIGHT,\n          ),\n        );\n      } else {\n        const delta =\n          resizeParams.handleUsed === \"left\"\n            ? resizeParams.initialClientX - e.clientX\n            : e.clientX - resizeParams.initialClientX;\n        setLocalWidth(\n          Math.min(\n            Math.max(resizeParams.initialWidth + delta, MIN_VIDEO_WIDTH),\n            wrapperRef.current?.parentElement?.clientWidth || MAX_VIDEO_WIDTH,\n          ),\n        );\n      }\n    };\n\n    const onMouseUp = () => {\n      const handle = resizeParams.handleUsed;\n      setResizeParams(undefined);\n      if (handle === \"bottom\") {\n        if (localHeight != null && onHeightChange) onHeightChange(localHeight);\n      } else {\n        if (localWidth != null && onWidthChange) onWidthChange(localWidth);\n      }\n    };\n\n    window.addEventListener(\"mousemove\", onMouseMove);\n    window.addEventListener(\"mouseup\", onMouseUp);\n    return () => {\n      window.removeEventListener(\"mousemove\", onMouseMove);\n      window.removeEventListener(\"mouseup\", onMouseUp);\n    };\n  }, [resizeParams, localWidth, localHeight, onWidthChange, onHeightChange]);\n\n  const handleLeftDown = useCallback(\n    (e: React.MouseEvent) => {\n      e.preventDefault();\n      e.stopPropagation();\n      setResizeParams({\n        handleUsed: \"left\",\n        initialWidth: wrapperRef.current?.clientWidth ?? DEFAULT_VIDEO_WIDTH,\n        initialHeight: localHeight ?? DEFAULT_VIDEO_HEIGHT,\n        initialClientX: e.clientX,\n        initialClientY: e.clientY,\n      });\n    },\n    [localHeight],\n  );\n\n  const handleRightDown = useCallback(\n    (e: React.MouseEvent) => {\n      e.preventDefault();\n      e.stopPropagation();\n      setResizeParams({\n        handleUsed: \"right\",\n        initialWidth: wrapperRef.current?.clientWidth ?? DEFAULT_VIDEO_WIDTH,\n        initialHeight: localHeight ?? DEFAULT_VIDEO_HEIGHT,\n        initialClientX: e.clientX,\n        initialClientY: e.clientY,\n      });\n    },\n    [localHeight],\n  );\n\n  const handleBottomDown = useCallback(\n    (e: React.MouseEvent) => {\n      e.preventDefault();\n      e.stopPropagation();\n      setResizeParams({\n        handleUsed: \"bottom\",\n        initialWidth: wrapperRef.current?.clientWidth ?? DEFAULT_VIDEO_WIDTH,\n        initialHeight: localHeight ?? DEFAULT_VIDEO_HEIGHT,\n        initialClientX: e.clientX,\n        initialClientY: e.clientY,\n      });\n    },\n    [localHeight],\n  );\n\n  const resizeCursor = resizeParams\n    ? resizeParams.handleUsed === \"bottom\"\n      ? \"ns-resize\"\n      : \"ew-resize\"\n    : \"default\";\n\n  const [hovered, setHovered] = useState(false);\n\n  return (\n    <div\n      ref={wrapperRef}\n      className=\"lumir-video-block-wrapper\"\n      onContextMenu={onContextMenuBlock}\n      style={{\n        position: \"relative\",\n        width: localWidth != null ? `${localWidth}px` : undefined,\n        maxWidth: \"100%\",\n        cursor: resizeCursor,\n        transition: resizeParams ? \"none\" : \"box-shadow 0.2s\",\n      }}\n      onMouseEnter={() => {\n        if (!resizeParams) setHovered(true);\n      }}\n      onMouseLeave={() => {\n        if (!resizeParams) setHovered(false);\n      }}\n    >\n      {editable && (hovered || resizeParams) && (\n        <>\n          <div\n            className=\"lumir-resize-handle\"\n            style={{ left: \"4px\" }}\n            onMouseDown={handleLeftDown}\n          />\n          <div\n            className=\"lumir-resize-handle\"\n            style={{ right: \"4px\" }}\n            onMouseDown={handleRightDown}\n          />\n        </>\n      )}\n\n      <div\n        style={{\n          width: \"100%\",\n          height: `${localHeight ?? DEFAULT_VIDEO_HEIGHT}px`,\n          overflow: \"hidden\",\n          backgroundColor: \"#000\",\n          borderRadius: \"6px\",\n        }}\n      >\n        <video\n          src={url}\n          controls\n          controlsList=\"nodownload\"\n          playsInline\n          style={{\n            width: \"100%\",\n            height: \"100%\",\n            objectFit: \"contain\",\n            display: \"block\",\n          }}\n          onContextMenu={(e) => e.preventDefault()}\n        />\n        {editable && (hovered || resizeParams) && (\n          <div\n            className=\"lumir-resize-handle-bottom\"\n            onMouseDown={handleBottomDown}\n          />\n        )}\n      </div>\n    </div>\n  );\n};\n\nexport const VideoBlock = createReactBlockSpec(\n  {\n    type: \"video\",\n    propSchema: {\n      url: { default: \"\" },\n      previewWidth: { default: 640 },\n      previewHeight: { default: 360 },\n    },\n    content: \"none\",\n  },\n  {\n    render: (props) => {\n      const url = props.block.props.url ?? \"\";\n      const editable = props.editor.isEditable;\n\n      const handleContextMenu = useCallback((e: React.MouseEvent) => {\n        e.preventDefault();\n        e.stopPropagation();\n      }, []);\n\n      if (!url) {\n        return (\n          <div\n            className=\"lumir-video-block-placeholder\"\n            onContextMenu={handleContextMenu}\n            style={{\n              width: \"100%\",\n              maxWidth: `${DEFAULT_VIDEO_WIDTH}px`,\n              height: `${DEFAULT_VIDEO_HEIGHT}px`,\n              backgroundColor: \"#1a1a1a\",\n              borderRadius: \"6px\",\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              color: \"rgba(255,255,255,0.5)\",\n              fontSize: \"14px\",\n            }}\n          >\n            비디오 URL 없음\n          </div>\n        );\n      }\n\n      return (\n        <VideoBlockCard\n          url={url}\n          editable={editable}\n          width={props.block.props.previewWidth}\n          onWidthChange={(newWidth) => {\n            props.editor.updateBlock(props.block, {\n              props: { previewWidth: newWidth },\n            });\n          }}\n          height={props.block.props.previewHeight}\n          onHeightChange={(newHeight) => {\n            props.editor.updateBlock(props.block, {\n              props: { previewHeight: newHeight },\n            });\n          }}\n          onContextMenuBlock={handleContextMenu}\n        />\n      );\n    },\n  },\n);\n","import { createStronglyTypedTiptapNode } from \"@blocknote/core\";\nimport { columnNormalization } from \"./columnNormalization\";\nimport { columnDnd } from \"./columnDnd\";\n\n/**\n * 다단(컬럼) 레이아웃의 컨테이너 노드 `columnList`.\n *\n * @blocknote/xl-multi-column(AGPL)을 쓰지 않고, 코어가 이미 인지하는 노드 구조\n * (core/src/pm-nodes/README.md, MIT)를 직접 정의한다(라이선스 안전).\n *  - 코어 UniqueID가 [\"blockContainer\",\"columnList\",\"column\"]에 id 부여,\n *    blockToNode가 bnBlock 그룹 노드를 {id,...props,children}로 변환,\n *    getBlockInfoFromPos가 그룹 기반 처리.\n *  - nodeToBlock(직렬화)은 blockSchema에 스펙이 있어야 인식하므로, 이 노드를\n *    createBlockSpecFromStronglyTypedTiptapNode로 감싸 schema에 등록한다(HtmlPreview.tsx).\n *\n * 스키마(README):\n *   group:   \"childContainer bnBlock blockGroupChild\"\n *   content: \"column column+\"  (최소 2단)\n */\nexport const ColumnList = createStronglyTypedTiptapNode({\n  name: \"columnList\",\n  group: \"childContainer bnBlock blockGroupChild\",\n  content: \"column column+\",\n\n  addAttributes() {\n    return {\n      // 블록별 중앙 세로 구분선 표시 여부(드래그핸들 메뉴에서 토글). data-divider로 렌더.\n      showDivider: {\n        default: false,\n        parseHTML: (element) => element.getAttribute(\"data-divider\") === \"true\",\n        renderHTML: (attributes) =>\n          attributes.showDivider ? { \"data-divider\": \"true\" } : {},\n      },\n    };\n  },\n\n  parseHTML() {\n    return [{ tag: 'div[data-node-type=\"columnList\"]' }];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    const dom = document.createElement(\"div\");\n    dom.className = \"bn-column-list\";\n    dom.setAttribute(\"data-node-type\", \"columnList\");\n    for (const [attribute, value] of Object.entries(HTMLAttributes)) {\n      if (attribute !== \"class\") {\n        dom.setAttribute(attribute, value as string);\n      }\n    }\n    return { dom, contentDOM: dom };\n  },\n\n  // columnNormalization: 빈 컬럼/1단 columnList 자동 복구(unwrap).\n  // columnDnd: 블록을 다른 블록 좌/우로 드롭하면 2단 컬럼 생성.\n  addProseMirrorPlugins() {\n    return [columnNormalization(), columnDnd()];\n  },\n});\n","import { Plugin, PluginKey } from \"prosemirror-state\";\n\nexport const columnNormalizationKey = new PluginKey(\"lumirColumnNormalization\");\n\n/**\n * columnList 불변식을 유지하는 정규화 플러그인.\n *\n * 문제: 코어 removeBlocks/일반 편집은 column/columnList의 최소 content 규칙을 강제하지\n * 않는다 → 컬럼의 마지막 블록을 지우면 **빈 column**(content \"blockContainer+\" 위반),\n * 컬럼을 통째로 지우면 **1단 columnList**(content \"column column+\" 위반)가 되어 문서가\n * 깨지고 직렬화(nodeToBlock)가 오류날 수 있다.\n *\n * 해결: appendTransaction에서 매 docChanged마다 columnList를 검사해,\n *  - 내용 있는 컬럼이 2개 미만이면 columnList를 **풀어서**(unwrap) 남은 컬럼들의\n *    blockContainer들을 부모 blockGroup에 직접 펼친다(blockContainer는 blockGroupChild라 유효).\n *  - 내용이 전혀 없으면 columnList를 제거한다.\n * 정규화 후에는 더 고칠 게 없어 self-terminating.\n */\nexport function columnNormalization(): Plugin {\n  return new Plugin({\n    key: columnNormalizationKey,\n    appendTransaction(transactions, _oldState, newState) {\n      if (!transactions.some((tr) => tr.docChanged)) {\n        return null;\n      }\n\n      const lists: { pos: number; node: any }[] = [];\n      newState.doc.descendants((node: any, pos: number) => {\n        if (node.type.name === \"columnList\") {\n          lists.push({ pos, node });\n          return false; // columnList는 중첩되지 않음 → 하위 순회 불필요\n        }\n        return undefined;\n      });\n      if (lists.length === 0) {\n        return null;\n      }\n\n      const tr = newState.tr;\n      let modified = false;\n\n      // 위치 큰 것부터 처리해 앞쪽 위치가 어긋나지 않게 한다.\n      for (let i = lists.length - 1; i >= 0; i--) {\n        const { pos, node } = lists[i];\n\n        const nonEmptyColumns: any[] = [];\n        node.forEach((col: any) => {\n          if (col.type.name === \"column\" && col.childCount > 0) {\n            nonEmptyColumns.push(col);\n          }\n        });\n\n        if (nonEmptyColumns.length >= 2) {\n          continue; // 유효한 다단 → 유지\n        }\n\n        const from = tr.mapping.map(pos);\n        const to = tr.mapping.map(pos + node.nodeSize);\n\n        // 남은 컬럼들의 blockContainer들을 모아 columnList를 대체(unwrap).\n        const blocks: any[] = [];\n        nonEmptyColumns.forEach((col: any) => {\n          col.forEach((bc: any) => blocks.push(bc));\n        });\n\n        if (blocks.length > 0) {\n          tr.replaceWith(from, to, blocks);\n        } else {\n          tr.delete(from, to);\n        }\n        modified = true;\n      }\n\n      return modified ? tr : null;\n    },\n  });\n}\n","import { Plugin, PluginKey } from \"prosemirror-state\";\nimport type { EditorView } from \"prosemirror-view\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport { mergeBlocksIntoColumns } from \"./mergeColumns\";\n\nexport const columnDndKey = new PluginKey<DndState>(\"lumirColumnDnd\");\n\ntype DndState = {\n  /** 대상 top-level blockContainer의 pos(없으면 null). */\n  targetPos: number | null;\n  side: \"left\" | \"right\" | null;\n};\n\n/** 가장자리로 인식할 폭(px). 블록 폭의 30%와 80px 중 작은 값. */\nfunction edgeThreshold(width: number): number {\n  return Math.min(80, width * 0.3);\n}\n\n/**\n * 좌표 아래의 **top-level** blockContainer(루트 blockGroup 직속)를 찾는다.\n * v1은 최상위 블록만 컬럼 생성 대상으로 허용(컬럼 내부/중첩 블록은 제외).\n */\nfunction topLevelBlockAtCoords(\n  view: EditorView,\n  x: number,\n  y: number,\n): { pos: number; nodeSize: number; id: string; dom: HTMLElement } | null {\n  const found = view.posAtCoords({ left: x, top: y });\n  if (!found) {\n    return null;\n  }\n  const $pos = view.state.doc.resolve(found.pos);\n  for (let d = $pos.depth; d > 0; d--) {\n    if ($pos.node(d).type.name === \"blockContainer\") {\n      // 루트 blockGroup(depth 1) 직속만 → blockContainer는 depth 2.\n      if (d !== 2 || $pos.node(d - 1).type.name !== \"blockGroup\") {\n        return null;\n      }\n      const pos = $pos.before(d);\n      const node = $pos.node(d);\n      const dom = view.nodeDOM(pos) as HTMLElement | null;\n      if (!dom || !node.attrs?.id) {\n        return null;\n      }\n      return { pos, nodeSize: node.nodeSize, id: node.attrs.id, dom };\n    }\n  }\n  return null;\n}\n\n/** NodeSelection이면 드래그된 단일 블록 id를 반환(다중 선택은 v1 미지원 → null). */\nfunction draggedBlockId(view: EditorView): string | null {\n  const sel: any = view.state.selection;\n  // MultipleNodeSelection 등은 node가 없음 → v1 단일만.\n  return sel?.node?.attrs?.id ?? null;\n}\n\nfunction setDnd(view: EditorView, next: DndState) {\n  const cur = columnDndKey.getState(view.state);\n  if (cur && cur.targetPos === next.targetPos && cur.side === next.side) {\n    return; // 변화 없음 → dispatch 생략(드래그오버 스팸 방지)\n  }\n  view.dispatch(view.state.tr.setMeta(columnDndKey, next));\n}\n\nconst CLEARED: DndState = { targetPos: null, side: null };\n\n/**\n * 블록을 다른 블록의 좌/우 가장자리로 드롭하면 2단 컬럼을 생성하는 DnD 플러그인.\n * - dragover: 대상 블록의 좌/우 가장자리 hit-test → 세로 인디케이터(데코) + 기본 드롭커서 숨김.\n * - drop(handleDrop): 가장자리 드롭이면 mergeBlocksIntoColumns로 래핑하고 기본 동작 취소.\n */\nexport function columnDnd(): Plugin<DndState> {\n  return new Plugin<DndState>({\n    key: columnDndKey,\n    state: {\n      init: () => CLEARED,\n      apply(tr, prev) {\n        const meta = tr.getMeta(columnDndKey);\n        if (meta) {\n          return meta as DndState;\n        }\n        // 문서가 바뀌면 좌표 기반 targetPos가 무효 → 초기화.\n        if (tr.docChanged && prev.targetPos !== null) {\n          return CLEARED;\n        }\n        return prev;\n      },\n    },\n    props: {\n      handleDOMEvents: {\n        dragover: (view, event) => {\n          if (!(view as any).dragging) {\n            return false;\n          }\n          const target = topLevelBlockAtCoords(\n            view,\n            event.clientX,\n            event.clientY,\n          );\n          const draggedId = draggedBlockId(view);\n          if (!target || !draggedId || target.id === draggedId) {\n            setDnd(view, CLEARED);\n            view.dom.classList.remove(\"lumir-col-dnd-active\");\n            return false;\n          }\n          const rect = target.dom.getBoundingClientRect();\n          const th = edgeThreshold(rect.width);\n          let side: \"left\" | \"right\" | null = null;\n          if (event.clientX - rect.left <= th) {\n            side = \"left\";\n          } else if (rect.right - event.clientX <= th) {\n            side = \"right\";\n          }\n          if (side) {\n            setDnd(view, { targetPos: target.pos, side });\n            view.dom.classList.add(\"lumir-col-dnd-active\");\n          } else {\n            setDnd(view, CLEARED);\n            view.dom.classList.remove(\"lumir-col-dnd-active\");\n          }\n          return false;\n        },\n        dragend: (view) => {\n          setDnd(view, CLEARED);\n          view.dom.classList.remove(\"lumir-col-dnd-active\");\n          return false;\n        },\n        dragleave: (view, event) => {\n          // 에디터 밖으로 나가면 정리(내부 이동은 무시).\n          if (!(event as DragEvent).relatedTarget) {\n            setDnd(view, CLEARED);\n            view.dom.classList.remove(\"lumir-col-dnd-active\");\n          }\n          return false;\n        },\n      },\n      handleDrop: (view, _event, _slice, _moved) => {\n        const st = columnDndKey.getState(view.state);\n        view.dom.classList.remove(\"lumir-col-dnd-active\");\n        if (!st || st.side === null || st.targetPos === null) {\n          return false; // 일반(상하) 드롭 → 기본 처리\n        }\n        const draggedId = draggedBlockId(view);\n        const targetNode = view.state.doc.nodeAt(st.targetPos);\n        const targetId = targetNode?.attrs?.id as string | undefined;\n        // 상태 초기화(드롭 후 잔여 인디케이터 제거)\n        view.dispatch(view.state.tr.setMeta(columnDndKey, CLEARED));\n        if (!draggedId || !targetId) {\n          return false;\n        }\n        const editor = (view as any).__lumirEditor;\n        if (!editor) {\n          return false;\n        }\n        const ok = mergeBlocksIntoColumns(editor, draggedId, targetId, st.side);\n        return ok; // 성공 시 기본 드롭 취소\n      },\n      decorations: (state) => {\n        const st = columnDndKey.getState(state);\n        if (!st || st.side === null || st.targetPos === null) {\n          return null;\n        }\n        const node = state.doc.nodeAt(st.targetPos);\n        if (!node) {\n          return null;\n        }\n        return DecorationSet.create(state.doc, [\n          Decoration.node(st.targetPos, st.targetPos + node.nodeSize, {\n            class:\n              st.side === \"left\"\n                ? \"lumir-col-drop-left\"\n                : \"lumir-col-drop-right\",\n          }),\n        ]);\n      },\n    },\n  });\n}\n","/**\n * 드래그된 블록과 대상 블록을 2단 columnList로 병합한다(DnD 컬럼 생성의 핵심 로직).\n *\n * 공개 블록 API(getBlock/removeBlocks/replaceBlocks)만 사용하므로 columnList/column이\n * 블록 스펙으로 등록돼 있어야 한다(이미 등록됨). 이벤트 처리(columnDnd 플러그인)와 분리해\n * 단독 테스트가 가능하다.\n *\n * @param side \"left\"  → [dragged | target], \"right\" → [target | dragged]\n * @returns 병합 성공 여부\n */\nexport function mergeBlocksIntoColumns(\n  editor: any,\n  draggedId: string,\n  targetId: string,\n  side: \"left\" | \"right\",\n): boolean {\n  if (!editor || !draggedId || !targetId || draggedId === targetId) {\n    return false;\n  }\n\n  const dragged = editor.getBlock?.(draggedId);\n  const target = editor.getBlock?.(targetId);\n  if (!dragged || !target) {\n    return false;\n  }\n  // 컬럼/컬럼리스트 자체는 드래그 대상에서 제외(중첩/해체 복잡도 회피, v1).\n  if (\n    dragged.type === \"columnList\" ||\n    dragged.type === \"column\" ||\n    target.type === \"columnList\" ||\n    target.type === \"column\"\n  ) {\n    return false;\n  }\n\n  const columnList = {\n    type: \"columnList\",\n    children:\n      side === \"left\"\n        ? [\n            { type: \"column\", children: [dragged] },\n            { type: \"column\", children: [target] },\n          ]\n        : [\n            { type: \"column\", children: [target] },\n            { type: \"column\", children: [dragged] },\n          ],\n  };\n\n  try {\n    const run = () => {\n      // 원본에서 드래그 블록 제거 후, 대상을 columnList로 교체(대상+드래그를 단으로).\n      editor.removeBlocks([draggedId]);\n      editor.replaceBlocks([targetId], [columnList]);\n    };\n    if (typeof editor.transact === \"function\") {\n      editor.transact(run);\n    } else {\n      run();\n    }\n    return true;\n  } catch {\n    return false;\n  }\n}\n","import { createStronglyTypedTiptapNode } from \"@blocknote/core\";\n\n/**\n * 다단 레이아웃의 단일 컬럼 노드 `column`.\n *\n * 스키마(core/src/pm-nodes/README.md, MIT):\n *   group:   \"bnBlock childContainer\"\n *   content: \"blockContainer+\"  (컬럼은 1개 이상의 블록을 담는다)\n *\n * MVP은 모든 컬럼을 균등 폭(flex:1)으로 렌더한다. 향후 너비 리사이즈를 위해 선택적\n * `width` attr 자리만 둔다(현재 CSS 미사용).\n */\nexport const Column = createStronglyTypedTiptapNode({\n  name: \"column\",\n  group: \"bnBlock childContainer\",\n  content: \"blockContainer+\",\n\n  addAttributes() {\n    return {\n      width: {\n        default: 1,\n        parseHTML: (element) => {\n          const w = parseFloat(element.getAttribute(\"data-column-width\") || \"\");\n          return Number.isFinite(w) && w > 0 ? w : 1;\n        },\n        renderHTML: (attributes) => {\n          if (!attributes.width || attributes.width === 1) {\n            return {};\n          }\n          return { \"data-column-width\": String(attributes.width) };\n        },\n      },\n    };\n  },\n\n  parseHTML() {\n    return [{ tag: 'div[data-node-type=\"column\"]' }];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    const dom = document.createElement(\"div\");\n    dom.className = \"bn-column\";\n    dom.setAttribute(\"data-node-type\", \"column\");\n    for (const [attribute, value] of Object.entries(HTMLAttributes)) {\n      if (attribute !== \"class\") {\n        dom.setAttribute(attribute, value as string);\n      }\n    }\n    return { dom, contentDOM: dom };\n  },\n});\n","\"use client\";\n\nimport { createStyleSpec } from \"@blocknote/core\";\n\n/**\n * 인라인 글자 크기 커스텀 스타일.\n *\n * - propSchema \"string\": 값으로 \"18px\" 같은 CSS 크기를 가진다.\n * - HTML 직렬화: `<span data-style-type=\"fontSize\" data-value=\"18px\" style=\"font-size:18px\">`\n *   (BlockNote가 data-* 속성과 파싱 규칙을 자동 생성 → 신버전 에디터 간 복사/붙여넣기 왕복 보장,\n *   구버전 에디터는 매칭 파싱 규칙이 없어 조용히 무시)\n *\n * ⚠️ 저장 JSON 하위호환: BlockNote는 styles 맵에 스키마에 없는 키가 있으면\n * `style ... not found in styleSchema` 예외를 던지므로, 저장 JSON에는\n * styles.fontSize 대신 형제 키 fontSize로 직렬화한다.\n * → src/utils/font-size-serialization.ts (liftFontSize/lowerFontSize) 참고.\n */\nexport const FontSize = createStyleSpec(\n  {\n    type: \"fontSize\",\n    propSchema: \"string\",\n  },\n  {\n    // 바닐라(동기) 스타일 스펙: DOM을 직접 반환한다.\n    // createReactStyleSpec는 span 내용이 비동기 렌더되어, 적용 직후 동기 시점에\n    // 선택 영역의 DOM 좌표가 (0,0)으로 잡혀 BlockNote 포매팅 툴바가 좌상단으로\n    // 튀는 문제가 있었다(굵게/기울임 등 네이티브 마크는 동기 렌더라 정상).\n    render: (value) => {\n      const span = document.createElement(\"span\");\n      span.style.fontSize = value;\n      return { dom: span, contentDOM: span };\n    },\n  },\n);\n\n/** 툴바 드롭다운에서 제공하는 프리셋 (본문 기본은 14px = \"기본\") */\nexport const FONT_SIZE_PRESETS = [\n  \"10px\",\n  \"12px\",\n  \"14px\",\n  \"16px\",\n  \"18px\",\n  \"20px\",\n  \"24px\",\n  \"28px\",\n] as const;\n\nexport type FontSizePreset = (typeof FONT_SIZE_PRESETS)[number];\n\n/** 글자 크기 1px 스테퍼의 허용 범위/기본값 (사용자 결정: 8~96px). */\nexport const FONT_SIZE_MIN = 8;\nexport const FONT_SIZE_MAX = 96;\n/** \"기본\"(명시 fontSize 없음)일 때 기준이 되는 본문 px. */\nexport const FONT_SIZE_DEFAULT_PX = 14;\nexport const FONT_SIZE_STEP = 1;\n\n/** \"18px\" → 18, \"\" / 파싱 실패 → 14(기본). */\nexport function parseFontSizePx(size: string): number {\n  const n = parseInt(size, 10);\n  return Number.isFinite(n) ? n : FONT_SIZE_DEFAULT_PX;\n}\n\n/** 8~96 범위로 보정 + 정수 반올림. */\nexport function clampFontSizePx(px: number): number {\n  return Math.min(FONT_SIZE_MAX, Math.max(FONT_SIZE_MIN, Math.round(px)));\n}\n\n/** 숫자 → \"Npx\" (clamp 포함). */\nexport function toFontSizeValue(px: number): string {\n  return `${clampFontSizePx(px)}px`;\n}\n\n/**\n * 현재 선택 영역의 글자 크기를 읽는다.\n *\n * BlockNote의 `getActiveStyles()`는 `selection.$to.marks()`(선택 끝 위치의 마크)만\n * 보기 때문에, **여러 블록(문단/리스트)에 걸친 선택**에서는 fontSize가 균일하게\n * 적용돼 있어도 `$to`가 블록 경계에 놓여 빈 값(\"\")을 돌려준다. 그 결과 스테퍼의\n * 기준값이 기본(14)에 고정돼 +/− 연속 클릭이 누적되지 않는 버그가 있었다.\n *\n * 여기서는 선택 범위 전체의 텍스트 노드를 스캔해 fontSize가 균일하면 그 값을,\n * 섞여 있거나 일부만 적용돼 있으면 \"\"(기본)을 돌려준다. (collapsed 커서는\n * 기존처럼 커서 위치의 마크를 읽는다.)\n */\nexport function readSelectionFontSize(editor: unknown): string {\n  const ed = editor as {\n    _tiptapEditor?: any;\n    getActiveStyles?: () => Record<string, string>;\n  };\n  // tiptap state에 접근할 수 없으면(예: 테스트 mock) getActiveStyles로 대체.\n  const fallback = () => {\n    try {\n      return ed?.getActiveStyles?.().fontSize || \"\";\n    } catch {\n      return \"\";\n    }\n  };\n  try {\n    const tt = ed._tiptapEditor;\n    const state = tt?.state;\n    if (!state) return fallback();\n    const sel = state.selection;\n\n    // collapsed 커서: 저장 마크 또는 커서 위치의 마크\n    if (sel.empty) {\n      const marks = state.storedMarks || sel.$to.marks();\n      const m = marks?.find?.((mk: any) => mk.type?.name === \"fontSize\");\n      return m?.attrs?.stringValue || \"\";\n    }\n\n    // 범위 선택: [from, to] 안의 모든 텍스트 노드를 스캔\n    let value: string | null = null; // null=아직 미관측\n    let mixed = false;\n    state.doc.nodesBetween(sel.from, sel.to, (node: any) => {\n      if (mixed || !node.isText) return !mixed;\n      const m = node.marks?.find?.((mk: any) => mk.type?.name === \"fontSize\");\n      const v: string = m?.attrs?.stringValue || \"\";\n      if (value === null) value = v;\n      else if (value !== v) mixed = true;\n      return !mixed;\n    });\n    return mixed ? \"\" : value || \"\";\n  } catch {\n    return fallback();\n  }\n}\n","\"use client\";\n\nimport React, { useState, useEffect, useRef } from \"react\";\nimport type { EditorType } from \"../../types\";\nimport { cn } from \"../../utils/cn\";\nimport { Icons } from \"./Icons\";\nimport {\n  ToolbarDivider,\n  UndoRedoButtons,\n  TextStyleButton,\n  AlignButton,\n  ListButton,\n  ImageButton,\n  ColorButton,\n  FontSizeButton,\n  LinkButton,\n  TableButton,\n  HTMLImportButton,\n  BlockTypeSelect,\n} from \"./components\";\n\n// 반응형 브레이크포인트 (px)\nconst COMPACT_BREAKPOINT = 700;\nconst MINIMIZED_BREAKPOINT = 400;\n\nexport interface FloatingMenuProps {\n  editor: EditorType | any;\n  position?: \"sticky\" | \"fixed\";\n  className?: string;\n  onImageUpload?: () => void;\n}\n\n/**\n * FloatingMenu - 에디터 상단 고정 툴바\n */\nexport const FloatingMenu: React.FC<FloatingMenuProps> = ({\n  editor,\n  position = \"sticky\",\n  className,\n  onImageUpload,\n}) => {\n  const wrapperRef = useRef<HTMLDivElement>(null);\n  const [isCompact, setIsCompact] = useState(false);\n  const [isMinimizable, setIsMinimizable] = useState(false);\n  const [isMinimized, setIsMinimized] = useState(false);\n  // 선택 변경 시 리렌더링을 위한 카운터\n  const [, setSelectionTick] = useState(0);\n\n  // 선택 변경 감지 - 스타일 버튼 상태 업데이트를 위해 (디바운싱 적용)\n  useEffect(() => {\n    if (!editor) return;\n\n    let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n    const DEBOUNCE_DELAY = 150;\n\n    const handleSelectionChange = () => {\n      if (debounceTimer) {\n        clearTimeout(debounceTimer);\n      }\n      debounceTimer = setTimeout(() => {\n        setSelectionTick((prev) => prev + 1);\n      }, DEBOUNCE_DELAY);\n    };\n\n    const unsubscribe = editor.onSelectionChange?.(handleSelectionChange);\n    const unsubscribeContent = editor.onEditorContentChange?.(() => {\n      setSelectionTick((prev) => prev + 1);\n    });\n\n    return () => {\n      if (debounceTimer) {\n        clearTimeout(debounceTimer);\n      }\n      unsubscribe?.();\n      unsubscribeContent?.();\n    };\n  }, [editor]);\n\n  // 컨테이너 너비 감지\n  useEffect(() => {\n    const checkWidth = () => {\n      if (wrapperRef.current) {\n        const width = wrapperRef.current.offsetWidth;\n        setIsCompact(width < COMPACT_BREAKPOINT);\n        setIsMinimizable(width < MINIMIZED_BREAKPOINT);\n      }\n    };\n\n    checkWidth();\n\n    const resizeObserver = new ResizeObserver(checkWidth);\n    if (wrapperRef.current) {\n      resizeObserver.observe(wrapperRef.current);\n    }\n\n    return () => resizeObserver.disconnect();\n  }, []);\n\n  // 최소화된 레이아웃 (400px 이하)\n  const MinimizedLayout = () => (\n    <>\n      <button\n        className=\"lumir-toolbar-button lumir-toggle-button\"\n        onClick={() => setIsMinimized(!isMinimized)}\n        onMouseDown={(e) => e.preventDefault()}\n        type=\"button\"\n        title={isMinimized ? \"메뉴 펼치기\" : \"메뉴 접기\"}\n      >\n        {isMinimized ? Icons.chevronRight : Icons.chevronLeft}\n      </button>\n      {!isMinimized && (\n        <>\n          <ToolbarDivider />\n          <UndoRedoButtons editor={editor} />\n          <ToolbarDivider />\n          <div className=\"lumir-toolbar-group\">\n            <BlockTypeSelect editor={editor} />\n          </div>\n          <ToolbarDivider />\n          <div className=\"lumir-toolbar-group\">\n            <TextStyleButton editor={editor} style=\"bold\" />\n            <TextStyleButton editor={editor} style=\"italic\" />\n            <TextStyleButton editor={editor} style=\"underline\" />\n            <TextStyleButton editor={editor} style=\"strike\" />\n          </div>\n          <ToolbarDivider />\n          <div className=\"lumir-toolbar-group\">\n            <AlignButton editor={editor} alignment=\"left\" />\n            <AlignButton editor={editor} alignment=\"center\" />\n            <AlignButton editor={editor} alignment=\"right\" />\n          </div>\n          <ToolbarDivider />\n          <div className=\"lumir-toolbar-group\">\n            <ListButton editor={editor} type=\"bullet\" />\n            <ListButton editor={editor} type=\"numbered\" />\n          </div>\n          <ToolbarDivider />\n          <div className=\"lumir-toolbar-group\">\n            <FontSizeButton editor={editor} />\n            <ColorButton editor={editor} type=\"text\" />\n            <ColorButton editor={editor} type=\"background\" />\n          </div>\n          <ToolbarDivider />\n          <div className=\"lumir-toolbar-group\">\n            <ImageButton editor={editor} onImageUpload={onImageUpload} />\n            <LinkButton editor={editor} />\n            <TableButton editor={editor} />\n            <HTMLImportButton editor={editor} />\n          </div>\n        </>\n      )}\n    </>\n  );\n\n  // 1단 레이아웃\n  const SingleRowLayout = () => (\n    <>\n      <UndoRedoButtons editor={editor} />\n      <ToolbarDivider />\n      <div className=\"lumir-toolbar-group\">\n        <BlockTypeSelect editor={editor} />\n      </div>\n      <ToolbarDivider />\n      <div className=\"lumir-toolbar-group\">\n        <TextStyleButton editor={editor} style=\"bold\" />\n        <TextStyleButton editor={editor} style=\"italic\" />\n        <TextStyleButton editor={editor} style=\"underline\" />\n        <TextStyleButton editor={editor} style=\"strike\" />\n      </div>\n      <ToolbarDivider />\n      <div className=\"lumir-toolbar-group\">\n        <AlignButton editor={editor} alignment=\"left\" />\n        <AlignButton editor={editor} alignment=\"center\" />\n        <AlignButton editor={editor} alignment=\"right\" />\n      </div>\n      <ToolbarDivider />\n      <div className=\"lumir-toolbar-group\">\n        <ListButton editor={editor} type=\"bullet\" />\n        <ListButton editor={editor} type=\"numbered\" />\n      </div>\n      <ToolbarDivider />\n      <div className=\"lumir-toolbar-group\">\n        <FontSizeButton editor={editor} />\n        <ColorButton editor={editor} type=\"text\" />\n        <ColorButton editor={editor} type=\"background\" />\n      </div>\n      <ToolbarDivider />\n      <div className=\"lumir-toolbar-group\">\n        <ImageButton editor={editor} onImageUpload={onImageUpload} />\n        <LinkButton editor={editor} />\n        <TableButton editor={editor} />\n        <HTMLImportButton editor={editor} />\n      </div>\n    </>\n  );\n\n  // 2단 레이아웃\n  const TwoRowLayout = () => (\n    <>\n      <div className=\"lumir-toolbar-row\">\n        <UndoRedoButtons editor={editor} />\n        <ToolbarDivider />\n        <div className=\"lumir-toolbar-group\">\n          <TextStyleButton editor={editor} style=\"bold\" />\n          <TextStyleButton editor={editor} style=\"italic\" />\n          <TextStyleButton editor={editor} style=\"underline\" />\n          <TextStyleButton editor={editor} style=\"strike\" />\n        </div>\n        <ToolbarDivider />\n        <div className=\"lumir-toolbar-group\">\n          <AlignButton editor={editor} alignment=\"left\" />\n          <AlignButton editor={editor} alignment=\"center\" />\n          <AlignButton editor={editor} alignment=\"right\" />\n        </div>\n        <ToolbarDivider />\n        <div className=\"lumir-toolbar-group\">\n          <ListButton editor={editor} type=\"bullet\" />\n          <ListButton editor={editor} type=\"numbered\" />\n        </div>\n      </div>\n      <div className=\"lumir-toolbar-row\">\n        <div className=\"lumir-toolbar-group\">\n          <BlockTypeSelect editor={editor} />\n        </div>\n        <ToolbarDivider />\n        <div className=\"lumir-toolbar-group\">\n          <FontSizeButton editor={editor} />\n          <ColorButton editor={editor} type=\"text\" />\n          <ColorButton editor={editor} type=\"background\" />\n        </div>\n        <ToolbarDivider />\n        <div className=\"lumir-toolbar-group\">\n          <ImageButton editor={editor} onImageUpload={onImageUpload} />\n          <LinkButton editor={editor} />\n          <TableButton editor={editor} />\n          <HTMLImportButton editor={editor} />\n        </div>\n      </div>\n    </>\n  );\n\n  return (\n    <div\n      ref={wrapperRef}\n      className={cn(\n        \"lumir-floating-toolbar-wrapper\",\n        isMinimizable && \"is-minimizable\",\n        className\n      )}\n      data-position={position}\n    >\n      <div\n        className={cn(\n          \"lumir-floating-toolbar\",\n          isCompact && \"is-compact\",\n          isMinimizable && \"is-minimizable\",\n          isMinimized && \"is-minimized\"\n        )}\n      >\n        {isMinimizable ? (\n          <MinimizedLayout />\n        ) : isCompact ? (\n          <TwoRowLayout />\n        ) : (\n          <SingleRowLayout />\n        )}\n      </div>\n    </div>\n  );\n};\n\n// 하위 호환성을 위한 default export\nexport default FloatingMenu;\n","\"use client\";\n\nimport React from \"react\";\n\n/**\n * FloatingMenu에서 사용하는 SVG 아이콘 컴포넌트들\n */\nexport const Icons = {\n  undo: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z\" />\n    </svg>\n  ),\n  redo: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z\" />\n    </svg>\n  ),\n  bold: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z\" />\n    </svg>\n  ),\n  italic: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z\" />\n    </svg>\n  ),\n  underline: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z\" />\n    </svg>\n  ),\n  strikethrough: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M10 19h4v-3h-4v3zM5 4v3h5v3h4V7h5V4H5zM3 14h18v-2H3v2z\" />\n    </svg>\n  ),\n  alignLeft: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z\" />\n    </svg>\n  ),\n  alignCenter: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z\" />\n    </svg>\n  ),\n  alignRight: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z\" />\n    </svg>\n  ),\n  bulletList: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z\" />\n    </svg>\n  ),\n  numberedList: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z\" />\n    </svg>\n  ),\n  image: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z\" />\n    </svg>\n  ),\n  expandMore: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n      <path d=\"M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z\" />\n    </svg>\n  ),\n  textColor: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M11 3L5.5 17h2.25l1.12-3h6.25l1.12 3h2.25L13 3h-2zm-1.38 9L12 5.67 14.38 12H9.62z\" />\n    </svg>\n  ),\n  bgColor: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M16.56 8.94L7.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 10L10 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\" />\n      <path fillOpacity=\".36\" d=\"M0 20h24v4H0z\" />\n    </svg>\n  ),\n  link: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z\" />\n    </svg>\n  ),\n  chevronRight: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\" />\n    </svg>\n  ),\n  chevronLeft: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\" />\n    </svg>\n  ),\n  table: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M20 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM10 17H5v-2h5v2zm0-4H5v-2h5v2zm0-4H5V7h5v2zm9 8h-7v-2h7v2zm0-4h-7v-2h7v2zm0-4h-7V7h7v2z\" />\n    </svg>\n  ),\n  htmlFile: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm-1 2v5h5l-5-5zm-4 14H7v-1h2v1zm0-2H7v-1h2v1zm-2-2h2v1H7v-1zm4 4h-2v-1h2v1zm0-2h-2v-1h2v1zm0-2h-2v-1h2v1zm6 4h-4v-1h4v1zm0-2h-4v-1h4v1zm0-2h-4v-1h4v1z\" />\n    </svg>\n  ),\n};\n\n/**\n * 블록 타입 아이콘들\n */\nexport const BlockTypeIcons: Record<string, React.ReactNode> = {\n  paragraph: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M5 5h14v2H5zM5 11h14v2H5zM5 17h10v2H5z\" />\n    </svg>\n  ),\n  h1: <span className=\"lumir-block-icon-text\">H1</span>,\n  h2: <span className=\"lumir-block-icon-text\">H2</span>,\n  h3: <span className=\"lumir-block-icon-text\">H3</span>,\n  h4: <span className=\"lumir-block-icon-text\">H4</span>,\n  h5: <span className=\"lumir-block-icon-text\">H5</span>,\n  h6: <span className=\"lumir-block-icon-text\">H6</span>,\n  toggleH1: (\n    <span className=\"lumir-block-icon-toggle\">\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"8\" height=\"8\">\n        <path d=\"M8 5v14l11-7z\" />\n      </svg>\n      <span>H1</span>\n    </span>\n  ),\n  toggleH2: (\n    <span className=\"lumir-block-icon-toggle\">\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"8\" height=\"8\">\n        <path d=\"M8 5v14l11-7z\" />\n      </svg>\n      <span>H2</span>\n    </span>\n  ),\n  toggleH3: (\n    <span className=\"lumir-block-icon-toggle\">\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"8\" height=\"8\">\n        <path d=\"M8 5v14l11-7z\" />\n      </svg>\n      <span>H3</span>\n    </span>\n  ),\n  quote: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z\" />\n    </svg>\n  ),\n  codeBlock: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z\" />\n    </svg>\n  ),\n  toggleList: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M10 6h10v2H10zM10 11h10v2H10zM10 16h10v2H10z\" />\n      <path\n        d=\"M4 8l4 4-4 4\"\n        stroke=\"currentColor\"\n        strokeWidth=\"2\"\n        fill=\"none\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  ),\n  bulletList: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <circle cx=\"4\" cy=\"6\" r=\"1.5\" />\n      <circle cx=\"4\" cy=\"12\" r=\"1.5\" />\n      <circle cx=\"4\" cy=\"18\" r=\"1.5\" />\n      <path d=\"M8 5h12v2H8zM8 11h12v2H8zM8 17h12v2H8z\" />\n    </svg>\n  ),\n  numberedList: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <path d=\"M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z\" />\n    </svg>\n  ),\n  checkList: (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n      <rect\n        x=\"3\"\n        y=\"4\"\n        width=\"6\"\n        height=\"6\"\n        rx=\"1\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n      />\n      <path\n        d=\"M4.5 7l1.5 1.5 3-3\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        fill=\"none\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path d=\"M12 6h8v2h-8z\" />\n      <rect\n        x=\"3\"\n        y=\"14\"\n        width=\"6\"\n        height=\"6\"\n        rx=\"1\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n      />\n      <path d=\"M12 16h8v2h-8z\" />\n    </svg>\n  ),\n};\n","\"use client\";\n\nimport React from \"react\";\n\n/**\n * 툴바 구분선 컴포넌트\n */\nexport const ToolbarDivider: React.FC = () => (\n  <div className=\"lumir-toolbar-divider\" />\n);\n","\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\n\ninterface UndoRedoButtonsProps {\n  editor: EditorType | any;\n}\n\n/**\n * 실행 취소 / 다시 실행 버튼 컴포넌트\n */\nexport const UndoRedoButtons: React.FC<UndoRedoButtonsProps> = ({ editor }) => {\n  const handleUndo = useCallback(() => {\n    try {\n      editor?.undo?.();\n    } catch (err) {\n      console.error(\"Undo failed:\", err);\n    }\n  }, [editor]);\n\n  const handleRedo = useCallback(() => {\n    try {\n      editor?.redo?.();\n    } catch (err) {\n      console.error(\"Redo failed:\", err);\n    }\n  }, [editor]);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <div className=\"lumir-toolbar-group\">\n      <button\n        className=\"lumir-toolbar-btn\"\n        onClick={handleUndo}\n        onMouseDown={handleMouseDown}\n        title=\"실행 취소\"\n        type=\"button\"\n      >\n        {Icons.undo}\n      </button>\n      <button\n        className=\"lumir-toolbar-btn\"\n        onClick={handleRedo}\n        onMouseDown={handleMouseDown}\n        title=\"다시 실행\"\n        type=\"button\"\n      >\n        {Icons.redo}\n      </button>\n    </div>\n  );\n};\n","\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\nimport { cn } from \"../../../utils/cn\";\n\ntype TextStyle = \"bold\" | \"italic\" | \"underline\" | \"strike\";\n\ninterface TextStyleButtonProps {\n  editor: EditorType | any;\n  style: TextStyle;\n}\n\nconst iconMap: Record<TextStyle, React.ReactNode> = {\n  bold: Icons.bold,\n  italic: Icons.italic,\n  underline: Icons.underline,\n  strike: Icons.strikethrough,\n};\n\nconst titleMap: Record<TextStyle, string> = {\n  bold: \"굵게\",\n  italic: \"기울임\",\n  underline: \"밑줄\",\n  strike: \"취소선\",\n};\n\n/**\n * 텍스트 스타일 버튼 (굵게, 기울임, 밑줄, 취소선)\n */\nexport const TextStyleButton: React.FC<TextStyleButtonProps> = ({\n  editor,\n  style,\n}) => {\n  // 현재 스타일 상태를 직접 계산\n  const getIsActive = (): boolean => {\n    try {\n      const activeStyles = editor?.getActiveStyles?.() || {};\n      return activeStyles[style] === true;\n    } catch {\n      return false;\n    }\n  };\n\n  const isActive = getIsActive();\n\n  const handleClick = useCallback(() => {\n    try {\n      editor?.toggleStyles?.({ [style]: true });\n    } catch (err) {\n      console.error(`Toggle ${style} failed:`, err);\n    }\n  }, [editor, style]);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <button\n      className={cn(\"lumir-toolbar-btn\", isActive && \"is-active\")}\n      onClick={handleClick}\n      onMouseDown={handleMouseDown}\n      title={titleMap[style]}\n      type=\"button\"\n    >\n      {iconMap[style]}\n    </button>\n  );\n};\n","\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\nimport { cn } from \"../../../utils/cn\";\nimport {\n  getFirstSelectedCellAttr,\n  isInTableCell,\n  setSelectedCellsAttr,\n} from \"../../../utils/prosemirror-table-utils\";\n\ntype Alignment = \"left\" | \"center\" | \"right\";\n\ninterface AlignButtonProps {\n  editor: EditorType | any;\n  alignment: Alignment;\n}\n\nconst iconMap: Record<Alignment, React.ReactNode> = {\n  left: Icons.alignLeft,\n  center: Icons.alignCenter,\n  right: Icons.alignRight,\n};\n\nconst titleMap: Record<Alignment, string> = {\n  left: \"왼쪽 정렬\",\n  center: \"가운데 정렬\",\n  right: \"오른쪽 정렬\",\n};\n\n/**\n * 텍스트 정렬 버튼 (왼쪽, 가운데, 오른쪽)\n */\nexport const AlignButton: React.FC<AlignButtonProps> = ({\n  editor,\n  alignment,\n}) => {\n  // 현재 정렬 상태를 직접 계산 (표 셀이면 셀 attr, 아니면 블록 prop)\n  const getCurrentAlignment = (): string => {\n    try {\n      if (isInTableCell(editor)) {\n        return (\n          (getFirstSelectedCellAttr(editor, \"textAlignment\") as string) ||\n          \"left\"\n        );\n      }\n      const block = editor?.getTextCursorPosition()?.block;\n      return block?.props?.textAlignment || \"left\";\n    } catch {\n      return \"left\";\n    }\n  };\n\n  const isActive = getCurrentAlignment() === alignment;\n\n  const handleClick = useCallback(() => {\n    try {\n      // 표 셀 안이면 셀 단위 정렬(선택 유지), 아니면 블록 정렬.\n      if (setSelectedCellsAttr(editor, \"textAlignment\", alignment)) {\n        return;\n      }\n      const block = editor?.getTextCursorPosition()?.block;\n      if (block && editor?.updateBlock) {\n        editor.updateBlock(block, { props: { textAlignment: alignment } });\n      }\n    } catch (err) {\n      console.error(`Set alignment ${alignment} failed:`, err);\n    }\n  }, [editor, alignment]);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <button\n      className={cn(\"lumir-toolbar-btn\", isActive && \"is-active\")}\n      onClick={handleClick}\n      onMouseDown={handleMouseDown}\n      title={titleMap[alignment]}\n      type=\"button\"\n    >\n      {iconMap[alignment]}\n    </button>\n  );\n};\n","/**\n * ProseMirror selection에서 현재 선택된 테이블 셀의 위치(pos)를 반환한다.\n * CellSelection이면 선택된 모든 셀, 일반 selection이면 커서가 있는 셀.\n * prosemirror-tables 직접 임포트 없이 duck-typing으로 CellSelection을 감지한다.\n */\nexport function getSelectedCellPositions(editor: any): number[] {\n  const tiptap = editor._tiptapEditor;\n  if (!tiptap) return [];\n\n  const { state } = tiptap;\n  const { selection } = state;\n\n  if (typeof selection.forEachCell === \"function\") {\n    const positions: number[] = [];\n    selection.forEachCell((_node: any, pos: number) => {\n      positions.push(pos);\n    });\n    return positions;\n  }\n\n  const $pos = selection.$from;\n  for (let depth = $pos.depth; depth > 0; depth--) {\n    const node = $pos.node(depth);\n    if (\n      node.type.name === \"tableCell\" ||\n      node.type.name === \"tableHeader\"\n    ) {\n      return [$pos.before(depth)];\n    }\n  }\n\n  return [];\n}\n\n/**\n * 주어진 셀 위치들에 노드 attr(예: backgroundColor/textColor/textAlignment)를\n * setNodeMarkup으로 일괄 설정한다. setNodeMarkup은 문서 구조를 바꾸지 않으므로\n * **CellSelection이 유지**된다(updateBlock은 내용 전체를 교체해 선택이 풀림).\n *\n * backgroundColor/textColor는 BlockNote의 글로벌 attribute(tableCell/tableHeader),\n * textAlignment도 셀 노드 attr이므로 setNodeMarkup에 유효하다.\n *\n * @returns 적용된 셀이 하나라도 있으면 true(= 표 셀 컨텍스트), 아니면 false.\n */\nexport function setCellAttrAtPositions(\n  editor: any,\n  positions: number[],\n  attr: string,\n  value: unknown,\n): boolean {\n  const tiptap = editor?._tiptapEditor;\n  if (!tiptap || positions.length === 0) return false;\n\n  let tr = tiptap.state.tr;\n  let changed = false;\n  for (const pos of positions) {\n    const node = tr.doc.nodeAt(pos);\n    if (\n      node &&\n      (node.type.name === \"tableCell\" || node.type.name === \"tableHeader\")\n    ) {\n      // setNodeMarkup은 attr만 바꾸고 위치/크기는 그대로 → 선택 매핑이 유지됨.\n      tr = tr.setNodeMarkup(pos, undefined, { ...node.attrs, [attr]: value });\n      changed = true;\n    }\n  }\n  if (changed) {\n    tiptap.view?.dispatch(tr);\n  }\n  return changed;\n}\n\n/**\n * 현재 selection의 셀(들)에 attr를 설정한다. 표 셀 컨텍스트가 아니면 false 반환\n * → 호출부가 블록/인라인 폴백을 적용할 수 있다.\n */\nexport function setSelectedCellsAttr(\n  editor: any,\n  attr: string,\n  value: unknown,\n): boolean {\n  return setCellAttrAtPositions(\n    editor,\n    getSelectedCellPositions(editor),\n    attr,\n    value,\n  );\n}\n\n/** 현재 selection이 표 셀 안인지(단일/다중 무관). */\nexport function isInTableCell(editor: any): boolean {\n  return getSelectedCellPositions(editor).length > 0;\n}\n\n/** 첫 선택 셀의 노드 attr 값을 읽는다(활성 상태 표시용). 없으면 undefined. */\nexport function getFirstSelectedCellAttr(\n  editor: any,\n  attr: string,\n): unknown {\n  const tiptap = editor?._tiptapEditor;\n  const positions = getSelectedCellPositions(editor);\n  if (!tiptap || positions.length === 0) return undefined;\n  const node = tiptap.state.doc.nodeAt(positions[0]);\n  return node?.attrs?.[attr];\n}\n\n/**\n * blockId로 식별되는 table 블록의 ProseMirror 노드 pos를 찾는다.\n * blockContainer(id 일치) > firstChild(table)의 pos. 없으면 -1.\n */\nexport function findTableNodePos(tiptap: any, blockId: string): number {\n  let tablePos = -1;\n  tiptap.state.doc.descendants((node: any, pos: number) => {\n    if (tablePos !== -1) return false;\n    if (\n      node.type.name === \"blockContainer\" &&\n      node.attrs?.id === blockId &&\n      node.firstChild?.type.name === \"table\"\n    ) {\n      // blockContainer content 시작(= table 노드 pos)\n      tablePos = pos + 1;\n      return false;\n    }\n    return undefined;\n  });\n  return tablePos;\n}\n\n/**\n * table 블록의 노드 attr `tableAlignment`를 설정한다(setNodeMarkup → 구조/선택 유지).\n * @returns 적용 성공 여부.\n */\nexport function setTableAlignment(\n  editor: any,\n  blockId: string,\n  alignment: \"left\" | \"center\" | \"right\",\n): boolean {\n  const tiptap = editor?._tiptapEditor;\n  if (!tiptap || !blockId) return false;\n  const tablePos = findTableNodePos(tiptap, blockId);\n  if (tablePos < 0) return false;\n  const node = tiptap.state.doc.nodeAt(tablePos);\n  if (!node || node.type.name !== \"table\") return false;\n  tiptap.view?.dispatch(\n    tiptap.state.tr.setNodeMarkup(tablePos, undefined, {\n      ...node.attrs,\n      tableAlignment: alignment,\n    }),\n  );\n  return true;\n}\n\n/** table 블록의 현재 정렬값을 읽는다(활성 상태 표시용). 기본 \"left\". */\nexport function getTableAlignment(editor: any, blockId: string): string {\n  const tiptap = editor?._tiptapEditor;\n  if (!tiptap || !blockId) return \"left\";\n  const tablePos = findTableNodePos(tiptap, blockId);\n  if (tablePos < 0) return \"left\";\n  const node = tiptap.state.doc.nodeAt(tablePos);\n  return (node?.attrs?.tableAlignment as string) || \"left\";\n}\n","\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\nimport { cn } from \"../../../utils/cn\";\n\ntype ListType = \"bullet\" | \"numbered\";\n\ninterface ListButtonProps {\n  editor: EditorType | any;\n  type: ListType;\n}\n\nconst iconMap: Record<ListType, React.ReactNode> = {\n  bullet: Icons.bulletList,\n  numbered: Icons.numberedList,\n};\n\nconst titleMap: Record<ListType, string> = {\n  bullet: \"글머리 기호 목록\",\n  numbered: \"번호 목록\",\n};\n\n/**\n * 리스트 버튼 (글머리 기호, 번호 목록)\n */\nexport const ListButton: React.FC<ListButtonProps> = ({ editor, type }) => {\n  // 현재 리스트 상태를 직접 계산\n  const getIsActive = (): boolean => {\n    try {\n      const block = editor?.getTextCursorPosition()?.block;\n      const blockType =\n        type === \"bullet\" ? \"bulletListItem\" : \"numberedListItem\";\n      return block?.type === blockType;\n    } catch {\n      return false;\n    }\n  };\n\n  const isActive = getIsActive();\n\n  const handleClick = useCallback(() => {\n    try {\n      const block = editor?.getTextCursorPosition()?.block;\n      if (block && editor?.updateBlock) {\n        const targetType =\n          type === \"bullet\" ? \"bulletListItem\" : \"numberedListItem\";\n        const newType = block.type === targetType ? \"paragraph\" : targetType;\n        editor.updateBlock(block, { type: newType as any });\n      }\n    } catch (err) {\n      console.error(`List toggle failed:`, err);\n    }\n  }, [editor, type]);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <button\n      className={cn(\"lumir-toolbar-btn\", isActive && \"is-active\")}\n      onClick={handleClick}\n      onMouseDown={handleMouseDown}\n      title={titleMap[type]}\n      type=\"button\"\n    >\n      {iconMap[type]}\n    </button>\n  );\n};\n","\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\n\ninterface ImageButtonProps {\n  editor: EditorType | any;\n  onImageUpload?: () => void;\n}\n\n/**\n * 이미지 업로드 버튼\n */\nexport const ImageButton: React.FC<ImageButtonProps> = ({\n  editor,\n  onImageUpload,\n}) => {\n  const handleClick = useCallback(() => {\n    if (onImageUpload) {\n      onImageUpload();\n    } else {\n      const input = document.createElement(\"input\");\n      input.type = \"file\";\n      input.accept = \"image/*\";\n      input.onchange = async (e) => {\n        const file = (e.target as HTMLInputElement).files?.[0];\n        if (file && editor?.uploadFile) {\n          try {\n            const url = await editor.uploadFile(file);\n            editor.insertBlocks(\n              [{ type: \"image\", props: { url: url as string } }] as any,\n              editor.getTextCursorPosition().block,\n              \"after\"\n            );\n          } catch (err) {\n            console.error(\"Image upload failed:\", err);\n          }\n        }\n      };\n      input.click();\n    }\n  }, [editor, onImageUpload]);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <button\n      className=\"lumir-toolbar-btn\"\n      onClick={handleClick}\n      onMouseDown={handleMouseDown}\n      title=\"이미지 삽입\"\n      type=\"button\"\n    >\n      {Icons.image}\n    </button>\n  );\n};\n","\"use client\";\n\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\nimport { cn } from \"../../../utils/cn\";\nimport {\n  TEXT_COLORS,\n  BACKGROUND_COLORS,\n  getHexFromColorValue,\n} from \"../../../constants/colors\";\nimport {\n  getFirstSelectedCellAttr,\n  isInTableCell,\n  setSelectedCellsAttr,\n} from \"../../../utils/prosemirror-table-utils\";\n\ntype ColorType = \"text\" | \"background\";\n\ninterface ColorButtonProps {\n  editor: EditorType | any;\n  type: ColorType;\n}\n\n/**\n * 색상 선택 버튼 (텍스트/배경 색상)\n */\nexport const ColorButton: React.FC<ColorButtonProps> = ({ editor, type }) => {\n  const [isOpen, setIsOpen] = useState(false);\n  const [currentColor, setCurrentColor] = useState(\"default\");\n  const dropdownRef = useRef<HTMLDivElement>(null);\n\n  const colors = type === \"text\" ? TEXT_COLORS : BACKGROUND_COLORS;\n\n  const getCurrentColor = useCallback((): string => {\n    try {\n      // 표 셀 안이면 셀의 색 attr을 읽는다(인라인 활성 스타일이 아님).\n      if (isInTableCell(editor)) {\n        const attr = type === \"text\" ? \"textColor\" : \"backgroundColor\";\n        return (getFirstSelectedCellAttr(editor, attr) as string) || \"default\";\n      }\n      const activeStyles = editor?.getActiveStyles?.() || {};\n      if (type === \"text\" && activeStyles.textColor) {\n        return activeStyles.textColor;\n      } else if (type === \"background\" && activeStyles.backgroundColor) {\n        return activeStyles.backgroundColor;\n      }\n    } catch {\n      // ignore\n    }\n    return \"default\";\n  }, [editor, type]);\n\n  // 드롭다운 열릴 때 현재 색상 업데이트\n  useEffect(() => {\n    if (isOpen) {\n      const color = getCurrentColor();\n      setCurrentColor(color);\n    }\n  }, [isOpen, getCurrentColor]);\n\n  // 외부 클릭 감지\n  useEffect(() => {\n    const handleClickOutside = (e: MouseEvent) => {\n      if (\n        dropdownRef.current &&\n        !dropdownRef.current.contains(e.target as Node)\n      ) {\n        setIsOpen(false);\n      }\n    };\n    document.addEventListener(\"mousedown\", handleClickOutside);\n    return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n  }, []);\n\n  const handleColorSelect = useCallback(\n    (color: string) => {\n      try {\n        if (!editor) return;\n\n        const attr = type === \"text\" ? \"textColor\" : \"backgroundColor\";\n        // 표 셀 안이면 셀 단위로 적용(선택 유지), 아니면 인라인 스타일.\n        if (!setSelectedCellsAttr(editor, attr, color)) {\n          (editor as any).toggleStyles(\n            type === \"text\"\n              ? { textColor: color }\n              : { backgroundColor: color },\n          );\n        }\n        setCurrentColor(color);\n        setIsOpen(false);\n        setTimeout(() => editor.focus?.());\n      } catch (err) {\n        console.error(`Color apply failed:`, err);\n      }\n    },\n    [editor, type]\n  );\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <div className=\"lumir-dropdown-wrapper\" ref={dropdownRef}>\n      <button\n        className=\"lumir-toolbar-btn lumir-color-btn\"\n        onClick={() => setIsOpen(!isOpen)}\n        onMouseDown={handleMouseDown}\n        title={type === \"text\" ? \"텍스트 색상\" : \"배경 색상\"}\n        type=\"button\"\n      >\n        {type === \"text\" ? Icons.textColor : Icons.bgColor}\n        <span\n          className=\"lumir-color-indicator\"\n          style={{\n            backgroundColor: getHexFromColorValue(currentColor, type),\n          }}\n        />\n      </button>\n      {isOpen && (\n        <div className=\"lumir-dropdown-menu lumir-color-menu\">\n          <div className=\"lumir-color-grid\">\n            {colors.map((color) => (\n              <button\n                key={color.value}\n                className={cn(\n                  \"lumir-color-swatch\",\n                  currentColor === color.value && \"is-active\"\n                )}\n                onClick={() => handleColorSelect(color.value)}\n                onMouseDown={handleMouseDown}\n                title={color.name}\n                style={{ backgroundColor: color.hex }}\n                type=\"button\"\n              />\n            ))}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n};\n","/**\n * 색상 팔레트 상수\n * BlockNote 기본 색상 팔레트와 일치\n */\n\nexport interface ColorItem {\n  name: string;\n  value: string;\n  hex: string;\n}\n\n/**\n * 텍스트 색상 팔레트\n */\nexport const TEXT_COLORS: ColorItem[] = [\n  { name: \"기본\", value: \"default\", hex: \"#3f3f3f\" },\n  { name: \"회색\", value: \"gray\", hex: \"#9b9a97\" },\n  { name: \"갈색\", value: \"brown\", hex: \"#64473a\" },\n  { name: \"빨간색\", value: \"red\", hex: \"#e03e3e\" },\n  { name: \"주황색\", value: \"orange\", hex: \"#d9730d\" },\n  { name: \"노란색\", value: \"yellow\", hex: \"#dfab01\" },\n  { name: \"초록색\", value: \"green\", hex: \"#4d6461\" },\n  { name: \"파란색\", value: \"blue\", hex: \"#0b6e99\" },\n  { name: \"보라색\", value: \"purple\", hex: \"#6940a5\" },\n  { name: \"분홍색\", value: \"pink\", hex: \"#ad1a72\" },\n];\n\n/**\n * 배경 색상 팔레트\n */\nexport const BACKGROUND_COLORS: ColorItem[] = [\n  { name: \"기본\", value: \"default\", hex: \"transparent\" },\n  { name: \"회색\", value: \"gray\", hex: \"#ebeced\" },\n  { name: \"갈색\", value: \"brown\", hex: \"#e9e5e3\" },\n  { name: \"빨간색\", value: \"red\", hex: \"#fbe4e4\" },\n  { name: \"주황색\", value: \"orange\", hex: \"#f6e9d9\" },\n  { name: \"노란색\", value: \"yellow\", hex: \"#fbf3db\" },\n  { name: \"초록색\", value: \"green\", hex: \"#ddedea\" },\n  { name: \"파란색\", value: \"blue\", hex: \"#ddebf1\" },\n  { name: \"보라색\", value: \"purple\", hex: \"#eae4f2\" },\n  { name: \"분홍색\", value: \"pink\", hex: \"#f4dfeb\" },\n];\n\n/**\n * 색상 값으로 hex 색상 코드 찾기\n */\nexport const getHexFromColorValue = (\n  value: string,\n  type: \"text\" | \"background\"\n): string => {\n  const colors = type === \"text\" ? TEXT_COLORS : BACKGROUND_COLORS;\n  const colorItem = colors.find((c) => c.value === value);\n  return colorItem?.hex || (type === \"text\" ? \"#000000\" : \"transparent\");\n};\n","\"use client\";\n\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\nimport { cn } from \"../../../utils/cn\";\nimport {\n  FONT_SIZE_MAX,\n  FONT_SIZE_MIN,\n  FONT_SIZE_PRESETS,\n  parseFontSizePx,\n  readSelectionFontSize,\n  toFontSizeValue,\n} from \"../../../styles/FontSizeStyle\";\n\ninterface FontSizeButtonProps {\n  editor: EditorType | any;\n}\n\nconst DEFAULT_LABEL = \"기본\";\n\n/** \"18px\" → \"18\" */\nconst toLabel = (size: string) => size.replace(/px$/, \"\");\n\n/**\n * 글자 크기 선택 드롭다운 (FloatingMenu용).\n *\n * 드롭다운 상단에 −/+ 스테퍼 + 직접 입력(1px 단위, 8~96px clamp)을 두고,\n * 그 아래 \"기본\" 리셋 + 프리셋 목록을 유지한다.\n *\n * ColorButton과 달리 테이블 셀 분기가 없다 — 셀 단위 font-size attr이 없으므로\n * 셀 내부에서도 항상 인라인 스타일(addStyles/removeStyles)로 적용한다.\n */\nexport const FontSizeButton: React.FC<FontSizeButtonProps> = ({ editor }) => {\n  const [isOpen, setIsOpen] = useState(false);\n  const dropdownRef = useRef<HTMLDivElement>(null);\n\n  // 현재 인라인 글자 크기 (선택 변경 시 FloatingMenu가 tick으로 리렌더).\n  // 다중 블록 선택에서 getActiveStyles($to.marks)가 빈 값을 돌려주는 문제 때문에\n  // 선택 범위 전체를 스캔하는 readSelectionFontSize를 사용한다.\n  const live = readSelectionFontSize(editor);\n\n  // 낙관적 갱신값: +/−·입력 적용 직후, tick 리렌더가 빈 값을 읽어도 누적되도록\n  // 직전 적용값을 우선한다. live(실측)가 바뀌면(선택 이동 등) 해제한다.\n  const [optimistic, setOptimistic] = useState<string | null>(null);\n  const lastLiveRef = useRef<string>(live);\n  useEffect(() => {\n    if (live !== lastLiveRef.current) {\n      lastLiveRef.current = live;\n      setOptimistic(null);\n    }\n  }, [live]);\n\n  const currentSize = optimistic ?? live;\n  // 명시 크기가 없으면(\"기본\") 14px를 기준으로 증감/표시한다.\n  const currentPx = parseFontSizePx(currentSize);\n\n  // 직접 입력 필드의 로컬 상태 (실제 크기 변경 시 동기화)\n  const [inputValue, setInputValue] = useState<string>(String(currentPx));\n  useEffect(() => {\n    setInputValue(String(currentPx));\n  }, [currentPx]);\n\n  // 외부 클릭 감지\n  useEffect(() => {\n    const handleClickOutside = (e: MouseEvent) => {\n      if (\n        dropdownRef.current &&\n        !dropdownRef.current.contains(e.target as Node)\n      ) {\n        setIsOpen(false);\n      }\n    };\n    document.addEventListener(\"mousedown\", handleClickOutside);\n    return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n  }, []);\n\n  const handleSizeSelect = useCallback(\n    (size: string) => {\n      try {\n        if (!editor) return;\n        if (size === \"\") {\n          (editor as any).removeStyles?.({ fontSize: \"\" });\n          setOptimistic(null);\n        } else {\n          (editor as any).addStyles?.({ fontSize: size });\n          setOptimistic(size);\n        }\n        setIsOpen(false);\n        setTimeout(() => editor.focus?.());\n      } catch (err) {\n        console.error(\"Font size apply failed:\", err);\n      }\n    },\n    [editor]\n  );\n\n  // −/+ 스테퍼: 메뉴를 닫지 않고 1px씩 적용 (선택 영역 유지).\n  const stepBy = useCallback(\n    (delta: number) => {\n      try {\n        const value = toFontSizeValue(currentPx + delta);\n        (editor as any)?.addStyles?.({ fontSize: value });\n        setOptimistic(value);\n      } catch (err) {\n        console.error(\"Font size step failed:\", err);\n      }\n    },\n    [editor, currentPx]\n  );\n\n  // 직접 입력 적용: 유효하면 clamp 후 적용, 무효/빈값이면 표시값 복원.\n  const applyInput = useCallback(() => {\n    const n = parseInt(inputValue, 10);\n    if (Number.isFinite(n)) {\n      try {\n        const value = toFontSizeValue(n);\n        (editor as any)?.addStyles?.({ fontSize: value });\n        setOptimistic(value);\n      } catch (err) {\n        console.error(\"Font size apply failed:\", err);\n      }\n    } else {\n      setInputValue(String(currentPx));\n    }\n  }, [editor, inputValue, currentPx]);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <div className=\"lumir-dropdown-wrapper\" ref={dropdownRef}>\n      <button\n        className=\"lumir-dropdown-btn lumir-font-size-btn\"\n        onClick={() => setIsOpen(!isOpen)}\n        onMouseDown={handleMouseDown}\n        title=\"글자 크기\"\n        type=\"button\"\n      >\n        <span className=\"lumir-font-size-label\">\n          {currentSize ? toLabel(currentSize) : DEFAULT_LABEL}\n        </span>\n        {Icons.expandMore}\n      </button>\n      {isOpen && (\n        <div className=\"lumir-dropdown-menu lumir-font-size-menu\">\n          {/* 1px 스테퍼 + 직접 입력 */}\n          <div className=\"lumir-fs-stepper\">\n            <button\n              type=\"button\"\n              className=\"lumir-fs-stepper-btn\"\n              aria-label=\"글자 크기 1px 줄이기\"\n              disabled={currentPx <= FONT_SIZE_MIN}\n              onMouseDown={handleMouseDown}\n              onClick={() => stepBy(-1)}\n            >\n              −\n            </button>\n            <input\n              className=\"lumir-fs-stepper-input\"\n              type=\"text\"\n              inputMode=\"numeric\"\n              aria-label=\"글자 크기 입력\"\n              value={inputValue}\n              onChange={(e) =>\n                setInputValue(e.target.value.replace(/[^0-9]/g, \"\"))\n              }\n              onKeyDown={(e) => {\n                e.stopPropagation();\n                if (e.key === \"Enter\") {\n                  e.preventDefault();\n                  applyInput();\n                } else if (e.key === \"ArrowUp\") {\n                  e.preventDefault();\n                  stepBy(1);\n                } else if (e.key === \"ArrowDown\") {\n                  e.preventDefault();\n                  stepBy(-1);\n                }\n              }}\n              onBlur={applyInput}\n            />\n            <button\n              type=\"button\"\n              className=\"lumir-fs-stepper-btn\"\n              aria-label=\"글자 크기 1px 늘리기\"\n              disabled={currentPx >= FONT_SIZE_MAX}\n              onMouseDown={handleMouseDown}\n              onClick={() => stepBy(1)}\n            >\n              +\n            </button>\n          </div>\n          <button\n            className={cn(\n              \"lumir-dropdown-item\",\n              currentSize === \"\" && \"is-active\"\n            )}\n            onClick={() => handleSizeSelect(\"\")}\n            onMouseDown={handleMouseDown}\n            type=\"button\"\n          >\n            {DEFAULT_LABEL}\n          </button>\n          {FONT_SIZE_PRESETS.map((size) => (\n            <button\n              key={size}\n              className={cn(\n                \"lumir-dropdown-item\",\n                currentSize === size && \"is-active\"\n              )}\n              onClick={() => handleSizeSelect(size)}\n              onMouseDown={handleMouseDown}\n              type=\"button\"\n            >\n              {toLabel(size)}\n            </button>\n          ))}\n        </div>\n      )}\n    </div>\n  );\n};\n","\"use client\";\n\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\n\ninterface LinkButtonProps {\n  editor: EditorType | any;\n}\n\n/**\n * 🔒 위험한 URL 프로토콜 검증\n * javascript:, data:, vbscript: 등 XSS 공격에 사용될 수 있는 프로토콜 차단\n */\nexport const isDangerousProtocol = (url: string): boolean => {\n  const trimmedUrl = url.trim().toLowerCase();\n  // 위험한 프로토콜 패턴\n  const dangerousPatterns = [\n    /^javascript:/i,\n    /^data:/i,\n    /^vbscript:/i,\n    /^file:/i,\n  ];\n  return dangerousPatterns.some((pattern) => pattern.test(trimmedUrl));\n};\n\n/**\n * URL 프로토콜 자동 추가 유틸리티 (보안 강화)\n */\nexport const normalizeUrl = (url: string): string | null => {\n  const trimmedUrl = url.trim();\n\n  // 🔒 위험한 프로토콜 차단\n  if (isDangerousProtocol(trimmedUrl)) {\n    console.warn(\"Blocked dangerous URL protocol:\", trimmedUrl);\n    return null;\n  }\n\n  // 이미 프로토콜이 있는 경우 그대로 반환\n  if (/^https?:\\/\\//i.test(trimmedUrl)) {\n    return trimmedUrl;\n  }\n\n  // mailto: 또는 tel: 링크인 경우 그대로 반환\n  if (/^(mailto:|tel:)/i.test(trimmedUrl)) {\n    return trimmedUrl;\n  }\n\n  // 프로토콜이 없는 경우 https:// 추가\n  return `https://${trimmedUrl}`;\n};\n\n/**\n * 링크 삽입 버튼\n */\nexport const LinkButton: React.FC<LinkButtonProps> = ({ editor }) => {\n  const [isOpen, setIsOpen] = useState(false);\n  const [linkUrl, setLinkUrl] = useState(\"\");\n  const [errorMsg, setErrorMsg] = useState<string | null>(null);\n  const dropdownRef = useRef<HTMLDivElement>(null);\n  const inputRef = useRef<HTMLInputElement>(null);\n  const hasSelectionRef = useRef(false);\n\n  // 외부 클릭 감지\n  useEffect(() => {\n    const handleClickOutside = (e: MouseEvent) => {\n      if (\n        dropdownRef.current &&\n        !dropdownRef.current.contains(e.target as Node)\n      ) {\n        setIsOpen(false);\n        setLinkUrl(\"\");\n        setErrorMsg(null);\n      }\n    };\n    document.addEventListener(\"mousedown\", handleClickOutside);\n    return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n  }, []);\n\n  // 드롭다운 열릴 때 선택 상태 저장 후 input에 포커스\n  useEffect(() => {\n    if (isOpen && inputRef.current) {\n      try {\n        const selectedText = editor?.getSelectedText?.() || \"\";\n        hasSelectionRef.current = selectedText.length > 0;\n      } catch {\n        hasSelectionRef.current = false;\n      }\n      setTimeout(() => inputRef.current?.focus(), 0);\n    }\n  }, [isOpen, editor]);\n\n  const handleSubmit = useCallback(\n    (e?: React.FormEvent) => {\n      e?.preventDefault();\n      setErrorMsg(null);\n\n      try {\n        if (linkUrl.trim() && editor?.createLink) {\n          const normalizedUrl = normalizeUrl(linkUrl);\n\n          if (normalizedUrl === null) {\n            setErrorMsg(\"허용되지 않는 URL 형식입니다.\");\n            return;\n          }\n\n          editor.focus();\n\n          if (hasSelectionRef.current) {\n            editor.createLink(normalizedUrl);\n          } else {\n            editor.createLink(normalizedUrl, normalizedUrl);\n          }\n\n          setIsOpen(false);\n          setLinkUrl(\"\");\n        }\n      } catch (err) {\n        console.error(\"Create link failed:\", err);\n        setErrorMsg(\"링크 생성에 실패했습니다.\");\n      }\n    },\n    [editor, linkUrl]\n  );\n\n  const handleCancel = useCallback(() => {\n    setIsOpen(false);\n    setLinkUrl(\"\");\n    setErrorMsg(null);\n  }, []);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  const handleKeyDown = useCallback(\n    (e: React.KeyboardEvent<HTMLInputElement>) => {\n      if (e.key === \"Enter\") {\n        handleSubmit();\n      } else if (e.key === \"Escape\") {\n        handleCancel();\n      }\n    },\n    [handleSubmit, handleCancel]\n  );\n\n  return (\n    <div className=\"lumir-dropdown-wrapper\" ref={dropdownRef}>\n      <button\n        className=\"lumir-toolbar-btn\"\n        onClick={() => setIsOpen(!isOpen)}\n        onMouseDown={handleMouseDown}\n        title=\"링크 삽입\"\n        type=\"button\"\n      >\n        {Icons.link}\n      </button>\n      {isOpen && (\n        <div className=\"lumir-dropdown-menu lumir-link-menu\">\n          <form onSubmit={handleSubmit} className=\"lumir-link-form\">\n            <input\n              ref={inputRef}\n              type=\"text\"\n              className=\"lumir-link-input\"\n              placeholder=\"링크 URL을 입력하세요\"\n              value={linkUrl}\n              onChange={(e) => {\n                setLinkUrl(e.target.value);\n                setErrorMsg(null);\n              }}\n              onKeyDown={handleKeyDown}\n              onMouseDown={handleMouseDown}\n            />\n            {/* 에러 메시지 표시 */}\n            {errorMsg && (\n              <div\n                style={{\n                  color: \"#dc3545\",\n                  fontSize: \"12px\",\n                  marginTop: \"4px\",\n                  padding: \"0 4px\",\n                }}\n              >\n                {errorMsg}\n              </div>\n            )}\n            <div className=\"lumir-link-actions\">\n              <button\n                type=\"button\"\n                className=\"lumir-link-btn lumir-link-cancel\"\n                onClick={handleCancel}\n                onMouseDown={handleMouseDown}\n              >\n                취소\n              </button>\n              <button\n                type=\"submit\"\n                className=\"lumir-link-btn lumir-link-submit\"\n                onMouseDown={handleMouseDown}\n                disabled={!linkUrl.trim()}\n              >\n                확인\n              </button>\n            </div>\n          </form>\n        </div>\n      )}\n    </div>\n  );\n};\n","\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\n\ninterface TableButtonProps {\n  editor: EditorType | any;\n}\n\n/**\n * 테이블 삽입 버튼\n */\nexport const TableButton: React.FC<TableButtonProps> = ({ editor }) => {\n  const handleClick = useCallback(() => {\n    try {\n      const block = editor?.getTextCursorPosition()?.block;\n      if (!block || !editor?.insertBlocks) return;\n\n      // 3x3 기본 테이블 생성\n      const defaultCell = [{ type: \"text\", text: \"\", styles: {} }];\n      const tableContent = {\n        type: \"tableContent\",\n        rows: [\n          { cells: [defaultCell, defaultCell, defaultCell] },\n          { cells: [defaultCell, defaultCell, defaultCell] },\n          { cells: [defaultCell, defaultCell, defaultCell] },\n        ],\n      };\n\n      editor.insertBlocks(\n        [{ type: \"table\", content: tableContent }] as any,\n        block,\n        \"after\"\n      );\n    } catch (err) {\n      console.error(\"Table insert failed:\", err);\n    }\n  }, [editor]);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <button\n      className=\"lumir-toolbar-btn\"\n      onClick={handleClick}\n      onMouseDown={handleMouseDown}\n      title=\"테이블 삽입\"\n      type=\"button\"\n    >\n      {Icons.table}\n    </button>\n  );\n};\n","\"use client\";\n\nimport React, { useCallback, useRef } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons } from \"../Icons\";\n\ninterface HTMLImportButtonProps {\n  editor: EditorType | any;\n}\n\n/**\n * HTML 파일 Import 버튼\n */\nexport const HTMLImportButton: React.FC<HTMLImportButtonProps> = ({\n  editor,\n}) => {\n  const fileInputRef = useRef<HTMLInputElement>(null);\n\n  const handleFileUpload = useCallback(\n    (e: React.ChangeEvent<HTMLInputElement>) => {\n      const file = e.target.files?.[0];\n      if (!file) return;\n\n      const reader = new FileReader();\n      reader.onload = (event) => {\n        const content = event.target?.result as string;\n\n        try {\n          if (!editor || !content.trim()) return;\n\n          const block = editor?.getTextCursorPosition()?.block;\n          if (!block || !editor?.insertBlocks) return;\n\n          // htmlPreview 블록 삽입\n          editor.insertBlocks(\n            [\n              {\n                type: \"htmlPreview\",\n                props: {\n                  htmlContent: content,\n                  fileName: file.name,\n                  height: \"400px\",\n                },\n              } as any,\n            ],\n            block,\n            \"after\"\n          );\n\n          // file input 초기화\n          if (fileInputRef.current) {\n            fileInputRef.current.value = \"\";\n          }\n        } catch (err) {\n          console.error(\"HTML insert failed:\", err);\n        }\n      };\n      reader.readAsText(file);\n    },\n    [editor]\n  );\n\n  const handleClick = useCallback(() => {\n    fileInputRef.current?.click();\n  }, []);\n\n  // 버튼 클릭 시 에디터 포커스/선택 영역 유지\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  return (\n    <>\n      <input\n        ref={fileInputRef}\n        type=\"file\"\n        accept=\".html,.htm\"\n        onChange={handleFileUpload}\n        style={{ display: \"none\" }}\n      />\n      <button\n        className=\"lumir-toolbar-btn\"\n        onClick={handleClick}\n        onMouseDown={handleMouseDown}\n        title=\"HTML Import\"\n        type=\"button\"\n      >\n        {Icons.htmlFile}\n      </button>\n    </>\n  );\n};\n","\"use client\";\n\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { EditorType } from \"../../../types\";\nimport { Icons, BlockTypeIcons } from \"../Icons\";\nimport { cn } from \"../../../utils/cn\";\n\ninterface BlockTypeSelectProps {\n  editor: EditorType | any;\n}\n\n// 블록 타입 정의\ntype BlockTypeItem = {\n  type: string;\n  label: string;\n  icon: string;\n  level?: number;\n  isToggle?: boolean;\n};\n\n// 카테고리별 블록 타입\nconst blockTypeCategories: { category: string; items: BlockTypeItem[] }[] = [\n  {\n    category: \"Headings\",\n    items: [\n      { type: \"heading\", label: \"Heading 1\", level: 1, icon: \"h1\", isToggle: false },\n      { type: \"heading\", label: \"Heading 2\", level: 2, icon: \"h2\", isToggle: false },\n      { type: \"heading\", label: \"Heading 3\", level: 3, icon: \"h3\", isToggle: false },\n      { type: \"heading\", label: \"Toggle Heading 1\", level: 1, icon: \"toggleH1\", isToggle: true },\n      { type: \"heading\", label: \"Toggle Heading 2\", level: 2, icon: \"toggleH2\", isToggle: true },\n      { type: \"heading\", label: \"Toggle Heading 3\", level: 3, icon: \"toggleH3\", isToggle: true },\n    ],\n  },\n  {\n    category: \"Basic blocks\",\n    items: [\n      { type: \"paragraph\", label: \"Paragraph\", icon: \"paragraph\" },\n      { type: \"quote\", label: \"Quote\", icon: \"quote\" },\n      { type: \"codeBlock\", label: \"Code Block\", icon: \"codeBlock\" },\n      { type: \"bulletListItem\", label: \"Bullet List\", icon: \"bulletList\" },\n      { type: \"numberedListItem\", label: \"Numbered List\", icon: \"numberedList\" },\n      { type: \"checkListItem\", label: \"Check List\", icon: \"checkList\" },\n      { type: \"toggleListItem\", label: \"Toggle List\", icon: \"toggleList\" },\n    ],\n  },\n];\n\n// 평탄화된 블록 타입 목록\nconst blockTypes: BlockTypeItem[] = blockTypeCategories.flatMap(\n  (cat) => cat.items\n);\n\n/**\n * 블록 타입 선택 드롭다운\n */\nexport const BlockTypeSelect: React.FC<BlockTypeSelectProps> = ({ editor }) => {\n  const [isOpen, setIsOpen] = useState(false);\n  const dropdownRef = useRef<HTMLDivElement>(null);\n\n  // 현재 블록 타입을 직접 계산\n  const getCurrentBlock = () => {\n    try {\n      return editor?.getTextCursorPosition()?.block;\n    } catch {\n      return null;\n    }\n  };\n\n  const currentBlock = getCurrentBlock();\n  const currentType = currentBlock?.type || \"paragraph\";\n  const currentLevel = currentBlock?.props?.level;\n  const isCurrentToggle =\n    currentType === \"heading\" && currentBlock?.props?.isToggleable === true;\n\n  useEffect(() => {\n    const handleClickOutside = (e: MouseEvent) => {\n      if (\n        dropdownRef.current &&\n        !dropdownRef.current.contains(e.target as Node)\n      ) {\n        setIsOpen(false);\n      }\n    };\n    document.addEventListener(\"mousedown\", handleClickOutside);\n    return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n  }, []);\n\n  const handleTypeChange = (\n    type: string,\n    level?: number,\n    isToggle?: boolean\n  ) => {\n    try {\n      const block = editor?.getTextCursorPosition()?.block;\n      if (!block || !editor) return;\n\n      const props: any = {};\n      if (level) props.level = level;\n\n      if (type === \"heading\" && isToggle !== undefined) {\n        props.isToggleable = isToggle;\n        editor.updateBlock(block, {\n          type: \"heading\" as any,\n          props,\n        });\n      } else {\n        editor.updateBlock(block, { type: type as any, props });\n      }\n\n      setIsOpen(false);\n    } catch (err) {\n      console.error(\"Block type change failed:\", err);\n    }\n  };\n\n  const handleMouseDown = useCallback((e: React.MouseEvent) => {\n    e.preventDefault();\n  }, []);\n\n  const getCurrentLabel = () => {\n    if (currentType === \"heading\" && currentLevel) {\n      const found = blockTypes.find(\n        (bt) =>\n          bt.type === \"heading\" &&\n          bt.level === currentLevel &&\n          bt.isToggle === isCurrentToggle\n      );\n      return found?.label || \"Heading\";\n    }\n    const found = blockTypes.find((bt) => bt.type === currentType);\n    return found?.label || \"Paragraph\";\n  };\n\n  const getCurrentIcon = () => {\n    if (currentType === \"heading\" && currentLevel) {\n      const found = blockTypes.find(\n        (bt) =>\n          bt.type === \"heading\" &&\n          bt.level === currentLevel &&\n          bt.isToggle === isCurrentToggle\n      );\n      return found?.icon || `h${currentLevel}`;\n    }\n    const found = blockTypes.find((bt) => bt.type === currentType);\n    return found?.icon || \"paragraph\";\n  };\n\n  const isActiveItem = (bt: BlockTypeItem) => {\n    if (bt.type === \"heading\" && bt.level) {\n      const isLevelMatch =\n        currentType === \"heading\" && currentLevel === bt.level;\n      const isToggleMatch = bt.isToggle === isCurrentToggle;\n      return isLevelMatch && isToggleMatch;\n    }\n    return currentType === bt.type;\n  };\n\n  return (\n    <div className=\"lumir-dropdown-wrapper\" ref={dropdownRef}>\n      <button\n        className=\"lumir-dropdown-btn lumir-block-type-btn\"\n        onClick={() => setIsOpen(!isOpen)}\n        onMouseDown={handleMouseDown}\n        type=\"button\"\n      >\n        <span className=\"lumir-block-icon\">\n          {BlockTypeIcons[getCurrentIcon()]}\n        </span>\n        <span className=\"lumir-block-label\">{getCurrentLabel()}</span>\n        {Icons.expandMore}\n      </button>\n      {isOpen && (\n        <div className=\"lumir-dropdown-menu lumir-block-menu\">\n          {blockTypeCategories.map((category) => (\n            <div key={category.category} className=\"lumir-block-category\">\n              <div className=\"lumir-block-category-title\">\n                {category.category}\n              </div>\n              {category.items.map((bt) => (\n                <button\n                  key={bt.icon}\n                  className={cn(\n                    \"lumir-dropdown-item lumir-block-item\",\n                    isActiveItem(bt) && \"is-active\"\n                  )}\n                  onClick={() => handleTypeChange(bt.type, bt.level, bt.isToggle)}\n                  onMouseDown={handleMouseDown}\n                >\n                  <span className=\"lumir-block-icon\">\n                    {BlockTypeIcons[bt.icon]}\n                  </span>\n                  <span className=\"lumir-block-item-title\">{bt.label}</span>\n                </button>\n              ))}\n            </div>\n          ))}\n        </div>\n      )}\n    </div>\n  );\n};\n","/**\n * LumirEditor 커스텀 에러 클래스\n */\n\nexport type LumirErrorCode =\n  | \"UPLOAD_FAILED\"\n  | \"INVALID_FILE_TYPE\"\n  | \"S3_CONFIG_ERROR\"\n  | \"PRESIGNED_URL_ERROR\"\n  | \"NETWORK_ERROR\"\n  | \"EDITOR_ERROR\"\n  | \"UNKNOWN_ERROR\";\n\nexport interface LumirErrorDetails {\n  code: LumirErrorCode;\n  originalError?: Error;\n  context?: Record<string, unknown>;\n}\n\n/**\n * LumirEditor에서 발생하는 에러를 위한 커스텀 에러 클래스\n */\nexport class LumirEditorError extends Error {\n  public readonly code: LumirErrorCode;\n  public readonly originalError?: Error;\n  public readonly context?: Record<string, unknown>;\n\n  constructor(message: string, details: Partial<LumirErrorDetails> = {}) {\n    super(message);\n    this.name = \"LumirEditorError\";\n    this.code = details.code || \"UNKNOWN_ERROR\";\n    this.originalError = details.originalError;\n    this.context = details.context;\n\n    // Error 클래스 확장 시 프로토타입 체인 유지\n    Object.setPrototypeOf(this, LumirEditorError.prototype);\n  }\n\n  /**\n   * 에러 정보를 JSON 형태로 반환\n   */\n  toJSON(): Record<string, unknown> {\n    return {\n      name: this.name,\n      message: this.message,\n      code: this.code,\n      context: this.context,\n      stack: this.stack,\n    };\n  }\n\n  /**\n   * 사용자 친화적 에러 메시지 반환\n   */\n  getUserMessage(): string {\n    switch (this.code) {\n      case \"UPLOAD_FAILED\":\n        return \"파일 업로드에 실패했습니다. 다시 시도해주세요.\";\n      case \"INVALID_FILE_TYPE\":\n        return \"지원하지 않는 파일 형식입니다. 이미지 파일만 업로드 가능합니다.\";\n      case \"S3_CONFIG_ERROR\":\n        return \"S3 설정이 올바르지 않습니다. 관리자에게 문의하세요.\";\n      case \"PRESIGNED_URL_ERROR\":\n        return \"업로드 URL 생성에 실패했습니다. 다시 시도해주세요.\";\n      case \"NETWORK_ERROR\":\n        return \"네트워크 연결을 확인해주세요.\";\n      case \"EDITOR_ERROR\":\n        return \"에디터 오류가 발생했습니다. 페이지를 새로고침해주세요.\";\n      default:\n        return \"알 수 없는 오류가 발생했습니다.\";\n    }\n  }\n\n  /**\n   * 일반 Error를 LumirEditorError로 변환\n   */\n  static fromError(\n    error: Error,\n    code: LumirErrorCode = \"UNKNOWN_ERROR\",\n    context?: Record<string, unknown>\n  ): LumirEditorError {\n    return new LumirEditorError(error.message, {\n      code,\n      originalError: error,\n      context,\n    });\n  }\n\n  /**\n   * 업로드 실패 에러 생성\n   */\n  static uploadFailed(\n    message: string,\n    originalError?: Error\n  ): LumirEditorError {\n    return new LumirEditorError(message, {\n      code: \"UPLOAD_FAILED\",\n      originalError,\n    });\n  }\n\n  /**\n   * 잘못된 파일 형식 에러 생성\n   * @param allowVideoUpload true이면 \"image and video\" 메시지 사용\n   */\n  static invalidFileType(\n    fileName: string,\n    allowVideoUpload?: boolean\n  ): LumirEditorError {\n    const message =\n      allowVideoUpload === true\n        ? `Invalid file type: ${fileName}. Only image and video files are allowed.`\n        : `Invalid file type: ${fileName}. Only image files are allowed.`;\n    return new LumirEditorError(message, {\n      code: \"INVALID_FILE_TYPE\",\n      context: { fileName },\n    });\n  }\n\n  /**\n   * S3 설정 에러 생성\n   */\n  static s3ConfigError(message: string): LumirEditorError {\n    return new LumirEditorError(message, {\n      code: \"S3_CONFIG_ERROR\",\n    });\n  }\n\n  /**\n   * 네트워크 에러 생성\n   */\n  static networkError(originalError?: Error): LumirEditorError {\n    return new LumirEditorError(\"Network request failed\", {\n      code: \"NETWORK_ERROR\",\n      originalError,\n    });\n  }\n}\n","import { Extension } from \"@tiptap/core\";\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\n\nexport const VerticalAlignmentExtension = Extension.create({\n  name: \"verticalAlignment\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"tableCell\", \"tableHeader\"],\n        attributes: {\n          verticalAlignment: {\n            default: \"top\",\n            parseHTML: (element) => {\n              return (\n                element.getAttribute(\"data-vertical-alignment\") || \"top\"\n              );\n            },\n            renderHTML: (attributes) => {\n              if (\n                !attributes.verticalAlignment ||\n                attributes.verticalAlignment === \"top\"\n              ) {\n                return {};\n              }\n              return {\n                \"data-vertical-alignment\": attributes.verticalAlignment,\n              };\n            },\n          },\n        },\n      },\n    ];\n  },\n\n  addProseMirrorPlugins() {\n    return [];\n  },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { rowResizing } from \"./rowResizing\";\nimport { tableScaling } from \"./tableScaling\";\nimport { tableCellAttrPreserve } from \"./tableCellAttrPreserve\";\n\nexport interface RowHeightOptions {\n  /**\n   * 행 경계 드래그 리사이즈(rowResizing 플러그인) 활성화 여부.\n   * false면 속성 등록/렌더링은 유지하되 드래그 UI만 비활성화한다\n   * (= 저장된 행 높이는 그대로 표시되지만 사용자가 조절할 수 없음).\n   * LumirEditor에서 `tableHandles` prop으로 게이트한다.\n   */\n  resizable: boolean;\n}\n\n/**\n * 표 셀에 `rowHeight`(px) 노드 attr을 추가하는 Tiptap 확장.\n *\n * BlockNote 0.35는 행 높이를 모델링하지 않으므로(`TableContent`에 `columnWidths`만 존재),\n * `verticalAlignment`와 동일한 패턴으로 셀 단위 글로벌 attribute를 등록한다:\n *  - renderHTML이 inline `style=\"height:Npx\"`를 출력 → HTML 복사/내보내기/Excel 붙여넣기에 라운드트립\n *  - parseHTML이 `style.height` 또는 `data-row-height`를 역파싱\n *  - 저장(블록 JSON 주입)은 utils/table-cell-props.ts의 injectTableCellAttrs가 담당\n *  - 로드는 BlockNote blockToNode가 cell.props를 노드 attr로 펼쳐 자동 복원\n *\n * 드래그 리사이즈 UI는 addProseMirrorPlugins에서 rowResizing 플러그인으로 제공하며,\n * resizable 옵션으로 게이트한다.\n */\nexport const RowHeightExtension = Extension.create<RowHeightOptions>({\n  name: \"rowHeight\",\n\n  addOptions() {\n    return { resizable: true };\n  },\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"tableCell\", \"tableHeader\"],\n        attributes: {\n          rowHeight: {\n            default: null,\n            parseHTML: (element) => {\n              const fromStyle = parseInt(element.style?.height ?? \"\", 10);\n              if (Number.isFinite(fromStyle) && fromStyle > 0) {\n                return fromStyle;\n              }\n              const fromAttr = parseInt(\n                element.getAttribute(\"data-row-height\") ?? \"\",\n                10,\n              );\n              return Number.isFinite(fromAttr) && fromAttr > 0\n                ? fromAttr\n                : null;\n            },\n            renderHTML: (attributes) => {\n              const h = attributes.rowHeight;\n              if (!h || typeof h !== \"number\") {\n                return {};\n              }\n              return { style: `height: ${h}px` };\n            },\n          },\n        },\n      },\n    ];\n  },\n\n  addProseMirrorPlugins() {\n    // 커스텀 셀 attr 보존은 항상(드래그 비활성이어도 행/열 추가 등으로 유실 방지).\n    // 드래그 리사이즈 UI만 resizable로 게이트.\n    const plugins = [tableCellAttrPreserve()];\n    if (this.options.resizable) {\n      plugins.push(rowResizing());\n      // 표 전체 종횡비 고정 스케일(우하단 코너 드래그).\n      plugins.push(tableScaling());\n    }\n    return plugins;\n  },\n});\n","/**\n * 표 **행 높이(row height)** 드래그 리사이즈 ProseMirror 플러그인.\n *\n * prosemirror-tables의 `columnResizing`(열 너비)을 **수직축으로 미러링**한 구현이다.\n * columnResizing이 없는 행 버전을 동일한 구조로 직접 작성한다:\n *\n *  - 아무 행의 **아래 경계**(또는 위 경계)에 hover → `row-resize` 커서 + 핸들 노출\n *  - 경계를 드래그 → 대상 행 높이를 **실시간 프리뷰**(셀에 height 데코레이션)\n *  - 드롭 → 해당 행의 모든 셀에 `rowHeight` 노드 attr을 `setNodeMarkup`으로 일괄 커밋\n *\n * 저장은 셀 단위 `rowHeight` 속성(RowHeightExtension의 글로벌 attribute)으로 이뤄지며,\n * 이는 BlockNote가 행 props를 모델링하지 않기 때문이다(행 높이 = 행 셀들의 max height).\n *\n * 라이브 프리뷰 방식(중요):\n *  열 리사이즈는 `<colgroup>`을 직접 조작해 프리뷰한다(BlockNoteTableView.ignoreMutation이\n *  colgroup 변경을 무시하므로 PM이 되돌리지 않음). 행에는 colgroup 등가물이 없고, 셀/행의\n *  style을 직접 바꾸면 PM의 MutationObserver가 노드를 즉시 다시 그려 초기화한다.\n *  → 그래서 드래그 중 높이를 **Decoration.node({style:'height:Npx'})**로 입힌다(PM이 직접\n *    렌더하므로 mutation 충돌 없음). 높이 갱신은 **meta-only 트랜잭션**으로 하여 doc을 바꾸지\n *    않으므로 onContentChange/undo 스팸이 없다. 드롭 시 한 번만 실제 커밋(단일 undo 스텝).\n *\n * ⚠️ prosemirror-state/view/tables는 반드시 에디터(@blocknote)와 **동일한 인스턴스**를\n * 써야 한다(Decoration/Plugin instanceof). 따라서 raw `prosemirror-*`에서 import하고\n * tsup 빌드에서 external 처리한다(@blocknote가 같은 패키지를 런타임 의존성으로 쓰므로\n * 소비자 트리에서 단일 복사본으로 dedup된다).\n */\n\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport type { EditorState } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport type { EditorView } from \"prosemirror-view\";\nimport { TableMap, cellAround, pointsAtCell } from \"prosemirror-tables\";\nimport {\n  ROW_RESIZE_HANDLE_WIDTH,\n  ROW_RESIZE_MIN_HEIGHT,\n} from \"../constants/limits\";\n\nexport const rowResizingPluginKey = new PluginKey<RowResizeState>(\n  \"lumirRowResizing\",\n);\n\ntype Dragging = {\n  startY: number;\n  startHeight: number;\n  /** 현재 드래그 위치 기준 프리뷰 높이(px). 데코레이션이 이 값을 렌더한다. */\n  currentHeight: number;\n};\n\n/**\n * 플러그인 상태. activeHandle = 리사이즈 대상 셀의 pos(없으면 -1),\n * dragging = 드래그 중이면 시작 좌표/높이/현재 프리뷰 높이, 아니면 null.\n */\nexport class RowResizeState {\n  constructor(\n    public activeHandle: number,\n    public dragging: Dragging | null,\n  ) {}\n\n  apply(tr: any): RowResizeState {\n    const action = tr.getMeta(rowResizingPluginKey);\n    if (action && action.setHandle != null) {\n      return new RowResizeState(action.setHandle, null);\n    }\n    if (action && action.setDragging !== undefined) {\n      return new RowResizeState(this.activeHandle, action.setDragging);\n    }\n    if (this.activeHandle > -1 && tr.docChanged) {\n      let handle = tr.mapping.map(this.activeHandle, -1);\n      if (!pointsAtCell(tr.doc.resolve(handle))) {\n        handle = -1;\n      }\n      return new RowResizeState(handle, this.dragging);\n    }\n    return this;\n  }\n}\n\nexport function rowResizing({\n  handleWidth = ROW_RESIZE_HANDLE_WIDTH,\n  minHeight = ROW_RESIZE_MIN_HEIGHT,\n}: { handleWidth?: number; minHeight?: number } = {}): Plugin {\n  return new Plugin<RowResizeState>({\n    key: rowResizingPluginKey,\n    state: {\n      init() {\n        return new RowResizeState(-1, null);\n      },\n      apply(tr, prev) {\n        return prev.apply(tr);\n      },\n    },\n    props: {\n      attributes: (state): Record<string, string> => {\n        const pluginState = rowResizingPluginKey.getState(state);\n        return pluginState && pluginState.activeHandle > -1\n          ? { class: \"row-resize-cursor\" }\n          : {};\n      },\n      handleDOMEvents: {\n        mousemove: (view, event) => {\n          handleMouseMove(view, event as MouseEvent, handleWidth);\n        },\n        mouseleave: (view) => {\n          handleMouseLeave(view);\n        },\n        mousedown: (view, event) => {\n          handleMouseDown(view, event as MouseEvent, minHeight);\n        },\n      },\n      decorations: (state) => {\n        const pluginState = rowResizingPluginKey.getState(state);\n        if (pluginState && pluginState.activeHandle > -1) {\n          return handleDecorations(state, pluginState);\n        }\n        return undefined;\n      },\n    },\n  });\n}\n\nfunction handleMouseMove(\n  view: EditorView,\n  event: MouseEvent,\n  handleWidth: number,\n) {\n  if (!view.editable) {\n    return;\n  }\n  const pluginState = rowResizingPluginKey.getState(view.state);\n  if (!pluginState) {\n    return;\n  }\n  if (!pluginState.dragging) {\n    const target = domCellAround(event.target as Node | null);\n    let cell = -1;\n    if (target) {\n      const { top, bottom } = (target as HTMLElement).getBoundingClientRect();\n      if (event.clientY - top <= handleWidth) {\n        cell = edgeCell(view, event, \"top\", handleWidth);\n      } else if (bottom - event.clientY <= handleWidth) {\n        cell = edgeCell(view, event, \"bottom\", handleWidth);\n      }\n    }\n    if (cell !== pluginState.activeHandle) {\n      updateHandle(view, cell);\n    }\n  }\n}\n\nfunction handleMouseLeave(view: EditorView) {\n  if (!view.editable) {\n    return;\n  }\n  const pluginState = rowResizingPluginKey.getState(view.state);\n  if (pluginState && pluginState.activeHandle > -1 && !pluginState.dragging) {\n    updateHandle(view, -1);\n  }\n}\n\nfunction handleMouseDown(\n  view: EditorView,\n  event: MouseEvent,\n  minHeight: number,\n): boolean {\n  if (!view.editable) {\n    return false;\n  }\n  const win = view.dom.ownerDocument.defaultView ?? window;\n  const pluginState = rowResizingPluginKey.getState(view.state);\n  if (!pluginState || pluginState.activeHandle === -1 || pluginState.dragging) {\n    return false;\n  }\n\n  const startHeight = currentRowHeight(view, pluginState.activeHandle);\n  setDragging(view, {\n    startY: event.clientY,\n    startHeight,\n    currentHeight: startHeight,\n  });\n\n  function finish(finishEvent: MouseEvent) {\n    win.removeEventListener(\"mouseup\", finish);\n    win.removeEventListener(\"mousemove\", move);\n    const ps = rowResizingPluginKey.getState(view.state);\n    if (ps?.dragging) {\n      const finalHeight = draggedHeight(ps.dragging, finishEvent, minHeight);\n      // ⚠️ 순서 중요: 프리뷰 데코(셀 style:height)를 먼저 제거한 뒤 커밋한다.\n      // 데코의 inline style과 커밋 후 renderHTML의 style이 같은 style 속성을 두고\n      // 충돌하는데, 커밋 후 데코를 제거하면 PM이 style을 비우고 renderHTML 값을\n      // 복원하지 않는다(빈 style). 데코를 먼저 없애면 커밋 시 renderHTML이 깨끗이\n      // 적용된다. 두 dispatch는 동기로 연달아 일어나 중간 페인트가 없어 깜빡임도 없다.\n      setDragging(view, null);\n      commitRowHeight(view, ps.activeHandle, finalHeight);\n    }\n  }\n\n  function move(moveEvent: MouseEvent) {\n    // 버튼이 떼어진 채 들어온 mousemove면(놓친 mouseup 안전장치) 즉시 마무리.\n    // 표준 `buttons`(0=눌림 없음)를 우선 보고, 레거시 `which`를 폴백으로 본다.\n    if (\n      moveEvent.buttons === 0 ||\n      (moveEvent.buttons === undefined && !moveEvent.which)\n    ) {\n      return finish(moveEvent);\n    }\n    const ps = rowResizingPluginKey.getState(view.state);\n    if (!ps?.dragging) {\n      return;\n    }\n    const h = draggedHeight(ps.dragging, moveEvent, minHeight);\n    if (h !== ps.dragging.currentHeight) {\n      // doc은 바꾸지 않고 프리뷰 높이만 갱신(meta-only) → 데코레이션이 즉시 반영.\n      setDragging(view, { ...ps.dragging, currentHeight: h });\n    }\n  }\n\n  win.addEventListener(\"mouseup\", finish);\n  win.addEventListener(\"mousemove\", move);\n  event.preventDefault();\n  return true;\n}\n\n/** 드래그 상태를 갱신하는 meta-only 트랜잭션(doc 미변경). */\nfunction setDragging(view: EditorView, dragging: Dragging | null) {\n  view.dispatch(\n    view.state.tr.setMeta(rowResizingPluginKey, { setDragging: dragging }),\n  );\n}\n\n/** 렌더된 `<tr>` 높이를 드래그 시작 높이로 쓴다(미설정 행도 자연스러운 시작점). */\nfunction currentRowHeight(view: EditorView, cellPos: number): number {\n  const info = targetRowInfo(view, cellPos);\n  const tr = rowTrElement(view, cellPos, info.row);\n  return tr ? tr.offsetHeight : ROW_RESIZE_MIN_HEIGHT;\n}\n\n/** 드롭 시 커밋: 대상 행에서 **끝나는** 모든 셀에 rowHeight attr 일괄 적용(기록 O). */\nfunction commitRowHeight(view: EditorView, cellPos: number, height: number) {\n  const { table, map, start, row } = targetRowInfo(view, cellPos);\n  const tr = view.state.tr;\n  const seen = new Set<number>();\n  for (let col = 0; col < map.width; col++) {\n    const cellRelPos = map.map[row * map.width + col];\n    if (seen.has(cellRelPos)) {\n      continue; // colspan: 같은 셀 중복 스킵\n    }\n    seen.add(cellRelPos);\n    const rect = map.findCell(cellRelPos);\n    // rowspan 셀이 이 행에서 끝나지 않으면(아래로 더 뻗으면) origin 행 높이를 유지\n    if (rect.bottom - 1 !== row) {\n      continue;\n    }\n    const node = table.nodeAt(cellRelPos);\n    if (!node || node.attrs.rowHeight === height) {\n      continue;\n    }\n    tr.setNodeMarkup(start + cellRelPos, undefined, {\n      ...node.attrs,\n      rowHeight: height,\n    });\n  }\n  if (tr.docChanged) {\n    view.dispatch(tr);\n  }\n}\n\n/** activeHandle 셀이 속한 테이블/맵/대상 행 인덱스(이 셀의 아래 경계가 위치한 행)를 구한다. */\nfunction targetRowInfo(view: EditorView, cellPos: number) {\n  const $cell = view.state.doc.resolve(cellPos);\n  const table = $cell.node(-1);\n  const map = TableMap.get(table);\n  const start = $cell.start(-1);\n  const rect = map.findCell($cell.pos - start);\n  return { table, map, start, row: rect.bottom - 1, $cell };\n}\n\n/** activeHandle 셀이 속한 `<table>`을 찾아 그 안의 row번째 `<tr>`을 반환. */\nfunction rowTrElement(\n  view: EditorView,\n  cellPos: number,\n  row: number,\n): HTMLTableRowElement | null {\n  let dom: Node | null = view.nodeDOM(cellPos) as Node | null;\n  while (dom && dom.nodeName !== \"TABLE\") {\n    dom = dom.parentNode;\n  }\n  if (!dom) {\n    return null;\n  }\n  return (dom as HTMLTableElement).rows[row] ?? null;\n}\n\nfunction domCellAround(target: Node | null): HTMLElement | null {\n  let node: any = target;\n  while (node && node.nodeName !== \"TD\" && node.nodeName !== \"TH\") {\n    node = node.classList?.contains(\"ProseMirror\") ? null : node.parentNode;\n  }\n  return node as HTMLElement | null;\n}\n\n/**\n * 경계에 인접한 셀 pos를 구한다.\n * - \"bottom\": 커서가 닿은 셀(이 셀의 아래 경계를 드래그)\n * - \"top\":    윗 행의 셀(그 셀의 아래 경계 = 이 경계). map index - width.\n */\nfunction edgeCell(\n  view: EditorView,\n  event: MouseEvent,\n  side: \"top\" | \"bottom\",\n  handleWidth: number,\n): number {\n  const offset = side === \"bottom\" ? -handleWidth : handleWidth;\n  const found = view.posAtCoords({\n    left: event.clientX,\n    top: event.clientY + offset,\n  });\n  if (!found) {\n    return -1;\n  }\n  const $cell = cellAround(view.state.doc.resolve(found.pos));\n  if (!$cell) {\n    return -1;\n  }\n  if (side === \"bottom\") {\n    return $cell.pos;\n  }\n  const map = TableMap.get($cell.node(-1));\n  const start = $cell.start(-1);\n  const index = map.map.indexOf($cell.pos - start);\n  return index < map.width ? -1 : start + map.map[index - map.width];\n}\n\nfunction updateHandle(view: EditorView, value: number) {\n  view.dispatch(\n    view.state.tr.setMeta(rowResizingPluginKey, { setHandle: value }),\n  );\n}\n\nfunction draggedHeight(\n  dragging: Dragging,\n  event: MouseEvent,\n  minHeight: number,\n): number {\n  const offset = event.clientY - dragging.startY;\n  return Math.max(minHeight, dragging.startHeight + offset);\n}\n\n/**\n * activeHandle이 가리키는 행의 각 셀에 데코레이션을 그린다.\n *  - 하단 경계 핸들 위젯(.row-resize-handle)\n *  - 드래그 중이면: 셀에 height 인라인 style 데코레이션(라이브 프리뷰) + dragging 클래스\n */\nfunction handleDecorations(\n  state: EditorState,\n  pluginState: RowResizeState,\n): DecorationSet {\n  const decorations: Decoration[] = [];\n  const $cell = state.doc.resolve(pluginState.activeHandle);\n  const table = $cell.node(-1);\n  if (!table) {\n    return DecorationSet.empty;\n  }\n  const map = TableMap.get(table);\n  const start = $cell.start(-1);\n  const row = map.findCell($cell.pos - start).bottom - 1;\n  const dragging = pluginState.dragging;\n  const seen = new Set<number>();\n\n  for (let col = 0; col < map.width; col++) {\n    const cellRelPos = map.map[row * map.width + col];\n    if (seen.has(cellRelPos)) {\n      continue;\n    }\n    seen.add(cellRelPos);\n    if (map.findCell(cellRelPos).bottom - 1 !== row) {\n      continue;\n    }\n    const node = table.nodeAt(cellRelPos);\n    if (!node) {\n      continue;\n    }\n    const from = start + cellRelPos;\n    const to = from + node.nodeSize;\n\n    if (dragging) {\n      // 라이브 프리뷰: 셀에 height를 입혀 마우스를 따라 즉시 커진다(PM이 직접 렌더).\n      decorations.push(\n        Decoration.node(from, to, {\n          class: \"row-resize-dragging\",\n          style: `height: ${dragging.currentHeight}px`,\n        }),\n      );\n    }\n\n    const handle = document.createElement(\"div\");\n    handle.className = \"row-resize-handle\";\n    decorations.push(Decoration.widget(to - 1, handle));\n  }\n\n  return DecorationSet.create(state.doc, decorations);\n}\n","/**\n * 보안 및 성능 제한 상수\n */\n\n/** 최대 파일 크기: 10MB (이미지) */\nexport const MAX_FILE_SIZE = 10 * 1024 * 1024;\n\n/** 최대 동영상 파일 크기: 100MB */\nexport const MAX_VIDEO_FILE_SIZE = 100 * 1024 * 1024;\n\n/** 업로드 타임아웃: 30초 (이미지 등) */\nexport const UPLOAD_TIMEOUT = 30000;\n\n/** 대용량 파일(비디오 등) 업로드 타임아웃: 120초 */\nexport const UPLOAD_TIMEOUT_VIDEO = 120000;\n\n/** 허용된 이미지 MIME 타입 (SVG 제외) */\nexport const ALLOWED_IMAGE_MIME_TYPES = new Set([\n  \"image/jpeg\",\n  \"image/png\",\n  \"image/gif\",\n  \"image/webp\",\n  \"image/bmp\",\n]);\n\n/** 차단된 파일 확장자 */\nexport const BLOCKED_EXTENSIONS = [\".svg\", \".svgz\"];\n\n/** 허용된 이미지 확장자 */\nexport const ALLOWED_IMAGE_EXTENSIONS = [\n  \".png\",\n  \".jpg\",\n  \".jpeg\",\n  \".gif\",\n  \".webp\",\n  \".bmp\",\n];\n\n/** 허용된 동영상 MIME 타입 */\nexport const ALLOWED_VIDEO_MIME_TYPES = new Set([\n  \"video/mp4\",\n  \"video/webm\",\n  \"video/ogg\",\n  \"video/quicktime\", // .mov\n]);\n\n/** 허용된 동영상 확장자 */\nexport const ALLOWED_VIDEO_EXTENSIONS = [\n  \".mp4\",\n  \".webm\",\n  \".ogg\",\n  \".mov\",\n];\n\n/** 표 행 높이 리사이즈: 최소 행 높이(px) */\nexport const ROW_RESIZE_MIN_HEIGHT = 24;\n\n/** 표 행 높이 리사이즈: 행 경계 hit-area 두께(px). columnResizing handleWidth(5)와 동일. */\nexport const ROW_RESIZE_HANDLE_WIDTH = 5;\n\n/** 표 전체 스케일(코너 드래그): 우하단 코너 hit-area 한 변(px). */\nexport const TABLE_SCALE_CORNER_SIZE = 16;\n\n/** 표 전체 스케일: 최소 열 너비(px). prosemirror-tables cellMinWidth(25) 근사. */\nexport const TABLE_SCALE_MIN_COL_WIDTH = 24;\n\n/** 표 전체 스케일: 최대 배율(런어웨이 방지). */\nexport const TABLE_SCALE_MAX = 6;\n","/**\n * 표 **전체 스케일**(우하단 코너 드래그)의 프리뷰/커밋 엔진 ProseMirror 플러그인.\n *\n * UI 핸들과 드래그 추적은 LumirTableHandlesController(에디터 DOM 밖 오버레이)가 담당한다.\n * 코너가 rowResizing(하단 경계)·columnResizing(우측 경계) 핸들과 겹쳐 mousedown이 그쪽으로\n * 새는 것을 피하기 위해, 핸들은 에디터 DOM 밖에 두고 자체 포인터 이벤트로 처리한다.\n * 이 플러그인은 컨트롤러가 dispatch한 프리뷰 상태를 받아:\n *  - 높이: 셀 `Decoration.node({style:height})` 라이브 프리뷰\n *  - 너비: 표 `<colgroup>` `<col>` width 직접 조작(BlockNoteTableView.ignoreMutation이 무시)\n * 로 그리고, 커밋 시 모든 셀의 colwidth/rowHeight를 일괄 setNodeMarkup(단일 undo)한다.\n * 갱신은 meta-only 트랜잭션이라 undo/onContentChange 스팸이 없다.\n *\n * ⚠️ prosemirror-* 는 @blocknote와 동일 인스턴스를 써야 한다(rowResizing와 동일 이유).\n */\n\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport type { EditorState } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport type { EditorView } from \"prosemirror-view\";\nimport { TableMap } from \"prosemirror-tables\";\n\nexport const tableScalingPluginKey = new PluginKey<TableScalePreview | null>(\n  \"lumirTableScaling\",\n);\n\nexport type TableScalePreview = {\n  /** 대상 table 노드 pos. */\n  tablePos: number;\n  /** 드래그 시작 시점의 열 너비/행 높이(px) — materialize 값. */\n  colWidths: number[];\n  rowHeights: number[];\n  /** 드래그 시작 시점의 표 렌더 W×H(px). */\n  origW: number;\n  origH: number;\n  /** 현재 배율(≥1 또는 <1). */\n  scale: number;\n};\n\nexport function tableScaling(): Plugin<TableScalePreview | null> {\n  return new Plugin<TableScalePreview | null>({\n    key: tableScalingPluginKey,\n    state: {\n      init: () => null,\n      apply(tr, prev) {\n        const meta = tr.getMeta(tableScalingPluginKey);\n        if (meta !== undefined) return meta.preview as TableScalePreview | null;\n        if (prev && tr.docChanged) {\n          return { ...prev, tablePos: tr.mapping.map(prev.tablePos, -1) };\n        }\n        return prev;\n      },\n    },\n    props: {\n      decorations(state) {\n        const p = tableScalingPluginKey.getState(state);\n        return p ? buildHeightDecorations(state, p) : null;\n      },\n    },\n    view: (view) => ({\n      update: () => {\n        const p = tableScalingPluginKey.getState(view.state);\n        if (p) applyColgroupPreview(view, p);\n      },\n    }),\n  });\n}\n\n/** 컨트롤러가 호출: 프리뷰 상태 설정/해제(meta-only). */\nexport function setTableScalePreview(\n  view: EditorView,\n  preview: TableScalePreview | null,\n): void {\n  view.dispatch(view.state.tr.setMeta(tableScalingPluginKey, { preview }));\n}\n\n/** 컨트롤러가 호출: 드래그 시작 시 대상 표의 현재 열너비/행높이/렌더크기를 측정. */\nexport function measureTableForScale(\n  view: EditorView,\n  tablePos: number,\n): TableScalePreview | null {\n  const tableEl = findTableEl(view.nodeDOM(tablePos));\n  const node = view.state.doc.nodeAt(tablePos);\n  if (!tableEl || !node || node.type.name !== \"table\") return null;\n  const map = TableMap.get(node);\n  const rect = tableEl.getBoundingClientRect();\n  const body = tableEl.tBodies[0];\n  const rowHeights = body\n    ? Array.from(body.rows).map((tr) => tr.getBoundingClientRect().height)\n    : [];\n  const colWidths = measureColWidths(tableEl, map.width);\n  if (rowHeights.length !== map.height || colWidths.length !== map.width) {\n    return null;\n  }\n  return {\n    tablePos,\n    colWidths,\n    rowHeights,\n    origW: rect.width,\n    origH: rect.height,\n    scale: 1,\n  };\n}\n\n/** 컨트롤러가 호출: 드롭 시 모든 셀의 colwidth/rowHeight를 ×scale로 일괄 커밋(단일 undo). */\nexport function commitTableScale(\n  view: EditorView,\n  preview: TableScalePreview,\n): void {\n  const { state } = view;\n  const { tablePos, colWidths, rowHeights, scale } = preview;\n  const table = state.doc.nodeAt(tablePos);\n  if (!table || table.type.name !== \"table\") return;\n  const map = TableMap.get(table);\n  const start = tablePos + 1;\n  const tr = state.tr;\n  const seen = new Set<number>();\n\n  for (const relPos of map.map) {\n    if (seen.has(relPos)) continue;\n    seen.add(relPos);\n    const node = table.nodeAt(relPos);\n    if (!node) continue;\n    const rect = map.findCell(relPos);\n    const colwidth: number[] = [];\n    for (let c = rect.left; c < rect.right; c++) {\n      colwidth.push(Math.round((colWidths[c] ?? 0) * scale));\n    }\n    let h = 0;\n    for (let r = rect.top; r < rect.bottom; r++) h += rowHeights[r] ?? 0;\n    const rowHeight = Math.round(h * scale);\n    tr.setNodeMarkup(start + relPos, undefined, {\n      ...node.attrs,\n      colwidth: colwidth.some((w) => w > 0) ? colwidth : null,\n      rowHeight: rowHeight > 0 ? rowHeight : null,\n    });\n  }\n  if (tr.docChanged) view.dispatch(tr);\n}\n\n// ── 측정 보조 ─────────────────────────────────────────────────\nfunction measureColWidths(tableEl: HTMLTableElement, width: number): number[] {\n  const widths = new Array(width).fill(0);\n  const colgroup = tableEl.querySelector(\"colgroup\");\n  if (colgroup && colgroup.children.length === width) {\n    let allSet = true;\n    for (let i = 0; i < width; i++) {\n      const w = parseFloat((colgroup.children[i] as HTMLElement).style.width);\n      if (Number.isFinite(w) && w > 0) widths[i] = w;\n      else allSet = false;\n    }\n    if (allSet) return widths;\n  }\n  const firstRow = tableEl.tBodies[0]?.rows[0];\n  if (firstRow) {\n    let col = 0;\n    for (const cell of Array.from(firstRow.cells)) {\n      const span = cell.colSpan || 1;\n      const w = cell.getBoundingClientRect().width / span;\n      for (let s = 0; s < span && col < width; s++) widths[col++] = w;\n    }\n  }\n  return widths;\n}\n\n// ── 프리뷰 렌더 ────────────────────────────────────────────────\nfunction applyColgroupPreview(view: EditorView, p: TableScalePreview) {\n  const tableEl = findTableEl(view.nodeDOM(p.tablePos));\n  const colgroup = tableEl?.querySelector(\"colgroup\");\n  if (!colgroup) return;\n  const cols = colgroup.children;\n  for (let i = 0; i < cols.length && i < p.colWidths.length; i++) {\n    (cols[i] as HTMLElement).style.width =\n      Math.round(p.colWidths[i] * p.scale) + \"px\";\n  }\n}\n\nfunction buildHeightDecorations(\n  state: EditorState,\n  p: TableScalePreview,\n): DecorationSet {\n  const table = state.doc.nodeAt(p.tablePos);\n  if (!table || table.type.name !== \"table\") return DecorationSet.empty;\n  const map = TableMap.get(table);\n  const start = p.tablePos + 1;\n  const decorations: Decoration[] = [];\n  const seen = new Set<number>();\n  for (const relPos of map.map) {\n    if (seen.has(relPos)) continue;\n    seen.add(relPos);\n    const node = table.nodeAt(relPos);\n    if (!node) continue;\n    const rect = map.findCell(relPos);\n    let h = 0;\n    for (let r = rect.top; r < rect.bottom; r++) h += p.rowHeights[r] ?? 0;\n    const from = start + relPos;\n    const to = from + node.nodeSize;\n    decorations.push(\n      Decoration.node(from, to, {\n        class: \"lumir-table-scale-dragging\",\n        style: `height: ${Math.round(h * p.scale)}px`,\n      }),\n    );\n  }\n  return DecorationSet.create(state.doc, decorations);\n}\n\n/** 임의 DOM에서 가장 가까운 <table>을 찾는다(nodeDOM이 wrapper일 수 있음). */\nfunction findTableEl(dom: Node | null): HTMLTableElement | null {\n  if (!dom) return null;\n  const el = dom as HTMLElement;\n  if (el.nodeName === \"TABLE\") return el as HTMLTableElement;\n  const inner = el.querySelector?.(\"table\");\n  if (inner) return inner as HTMLTableElement;\n  return (el.closest?.(\"table\") as HTMLTableElement) ?? null;\n}\n","/**\n * 표 구조 편집 시 커스텀 셀 attr(rowHeight, verticalAlignment) 유실을 막는 플러그인.\n *\n * 문제:\n *  BlockNote의 행/열 추가·삭제(ExtendButton)와 행/열 드래그 재정렬은 내부적으로\n *  `editor.updateBlock(block, ...)`로 표 블록 전체를 재작성한다. 이때 사용하는 block은\n *  `nodeToBlock`(contentNodeToTableContent)에서 온 것인데, 이 변환은 고정된 prop 집합\n *  (colspan/rowspan/backgroundColor/textColor/textAlignment)만 cell.props에 담는다.\n *  → rowHeight·verticalAlignment 같은 커스텀 글로벌 attribute는 그 시점에 누락되고,\n *    updateBlock→blockToNode 재작성으로 PM 노드에서도 사라진다(셀 기본값으로 초기화).\n *\n * 해결:\n *  appendTransaction에서 \"직전(old) 상태의 셀 attr\"과 \"현재(new) 상태\"를 비교해, 구조\n *  편집으로 기본값이 된 셀에 직전 값을 다시 채운다. (row, 행 내 cell) 인덱스로 매칭한다.\n *\n *  - 사용자가 직접 리사이즈/정렬한 경우: 새 값이 non-default라 복원이 끼어들지 않는다.\n *  - 새로 추가된 행/열의 셀: 직전 값이 없으므로 그대로 기본값(정상).\n *  - self-terminating: 복원 후 재실행 시 new 값이 채워져 더 복원할 게 없다.\n */\n\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport type { Node as PMNode } from \"prosemirror-model\";\n\nexport const tableCellAttrPreserveKey = new PluginKey(\n  \"lumirTableCellAttrPreserve\",\n);\n\n/** 보존 대상 attr과 \"기본값\"(이 값이면 의미 없음 = 보존/복원 제외). */\nconst PRESERVED_ATTRS: { name: string; default: unknown }[] = [\n  { name: \"rowHeight\", default: null },\n  { name: \"verticalAlignment\", default: \"top\" },\n];\n\nfunction isMeaningful(attrName: string, value: unknown, def: unknown): boolean {\n  if (value === null || value === undefined) {\n    return false;\n  }\n  return value !== def;\n}\n\n/** 셀의 보존 대상 attr 중 의미 있는 값만 추린다. 없으면 null. */\nfunction meaningfulAttrs(node: PMNode): Record<string, unknown> | null {\n  let out: Record<string, unknown> | null = null;\n  for (const { name, default: def } of PRESERVED_ATTRS) {\n    const v = node.attrs?.[name];\n    if (isMeaningful(name, v, def)) {\n      (out ??= {})[name] = v;\n    }\n  }\n  return out;\n}\n\n/**\n * 문서에서 table 블록 id → \"row:cell\" → {attr:value} 맵을 만든다.\n * id는 blockContainer의 id attr(편집을 가로질러 유지됨)로 매칭한다.\n */\nfunction collectCellAttrs(\n  doc: PMNode,\n): Map<string, Map<string, Record<string, unknown>>> {\n  const result = new Map<string, Map<string, Record<string, unknown>>>();\n\n  doc.descendants((node) => {\n    if (node.type.name !== \"blockContainer\") {\n      return undefined;\n    }\n    const id = node.attrs?.id as string | undefined;\n    const table = node.firstChild;\n    if (!id || table?.type.name !== \"table\") {\n      // 표가 아닌 블록도 children에 표를 가질 수 있으므로 계속 descend.\n      return undefined;\n    }\n    const cells = new Map<string, Record<string, unknown>>();\n    let rowIndex = 0;\n    table.forEach((rowNode) => {\n      if (rowNode.type.name === \"tableRow\") {\n        let cellIndex = 0;\n        rowNode.forEach((cellNode) => {\n          const attrs = meaningfulAttrs(cellNode);\n          if (attrs) {\n            cells.set(`${rowIndex}:${cellIndex}`, attrs);\n          }\n          cellIndex++;\n        });\n        rowIndex++;\n      }\n    });\n    if (cells.size > 0) {\n      result.set(id, cells);\n    }\n    // 표 셀 내부엔 더 깊은 표가 없으므로 하위 순회는 불필요.\n    return false;\n  });\n\n  return result;\n}\n\nexport function tableCellAttrPreserve(): Plugin {\n  return new Plugin({\n    key: tableCellAttrPreserveKey,\n    appendTransaction(transactions, oldState, newState) {\n      if (!transactions.some((tr) => tr.docChanged)) {\n        return null;\n      }\n      const oldMap = collectCellAttrs(oldState.doc);\n      if (oldMap.size === 0) {\n        return null;\n      }\n\n      const tr = newState.tr;\n      let changed = false;\n\n      // new 문서를 순회하며 구조 편집으로 기본값이 된 셀을 old 값으로 복원.\n      let curTableCells: Map<string, Record<string, unknown>> | null = null;\n      let curRowIndex = -1;\n      let curCellIndex = 0;\n\n      newState.doc.descendants((node, pos) => {\n        const name = node.type.name;\n        if (name === \"blockContainer\") {\n          const id = node.attrs?.id as string | undefined;\n          const table = node.firstChild;\n          if (id && table?.type.name === \"table\" && oldMap.has(id)) {\n            curTableCells = oldMap.get(id)!;\n            curRowIndex = -1;\n          } else {\n            curTableCells = null;\n          }\n          return undefined; // 항상 descend(중첩 표 대응)\n        }\n        if (!curTableCells) {\n          return undefined;\n        }\n        if (name === \"tableRow\") {\n          curRowIndex++;\n          curCellIndex = 0;\n          return undefined;\n        }\n        if (name === \"tableCell\" || name === \"tableHeader\") {\n          const wanted = curTableCells.get(`${curRowIndex}:${curCellIndex}`);\n          curCellIndex++;\n          if (wanted) {\n            const patch: Record<string, unknown> = {};\n            let needs = false;\n            for (const { name: attrName, default: def } of PRESERVED_ATTRS) {\n              if (!(attrName in wanted)) {\n                continue;\n              }\n              const cur = node.attrs?.[attrName];\n              // 현재 값이 기본값/빈값일 때만 복원(사용자가 새로 지정한 값은 보존).\n              if (cur === null || cur === undefined || cur === def) {\n                patch[attrName] = wanted[attrName];\n                needs = true;\n              }\n            }\n            if (needs) {\n              tr.setNodeMarkup(pos, undefined, { ...node.attrs, ...patch });\n              changed = true;\n            }\n          }\n          return false; // 셀 내부는 순회 불필요\n        }\n        return undefined;\n      });\n\n      return changed ? tr : null;\n    },\n  });\n}\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\n\nexport type TableAlignment = \"left\" | \"center\" | \"right\";\n\nconst tableAlignmentDecoKey = new PluginKey(\"lumirTableAlignmentDeco\");\n\n/**\n * table 노드의 tableAlignment attr을 읽어 `data-table-alignment`를 노드 DOM에 입히는 데코레이션.\n *\n * 왜 renderHTML(글로벌 attr)만으로 부족한가: table 노드는 커스텀 nodeView\n * (BlockNoteTableView)를 사용하는데, 이 nodeView는 생성 시점의 HTMLAttributes만 wrapper div에\n * 적용하고 attr 변경 시 갱신하지 않는다. 그래서 setNodeMarkup으로 attr을 바꿔도 DOM에 반영되지\n * 않는다. Decoration.node는 PM이 매 state 변경마다 nodeView의 outer DOM에 속성을 적용하므로\n * 정렬 변경이 즉시 화면에 반영된다. (renderHTML attr은 HTML 내보내기/복사용으로 계속 유지.)\n */\nfunction tableAlignmentDecorationPlugin(): Plugin {\n  return new Plugin({\n    key: tableAlignmentDecoKey,\n    props: {\n      decorations(state) {\n        const decorations: Decoration[] = [];\n        state.doc.descendants((node, pos) => {\n          if (node.type.name === \"table\") {\n            const align = node.attrs.tableAlignment;\n            if (align && align !== \"left\") {\n              decorations.push(\n                Decoration.node(pos, pos + node.nodeSize, {\n                  \"data-table-alignment\": align,\n                }),\n              );\n            }\n            return false; // 표 내부는 순회 불필요\n          }\n          return undefined;\n        });\n        return DecorationSet.create(state.doc, decorations);\n      },\n    },\n  });\n}\n\n/**\n * 표(table) 블록 전체를 에디터 영역 기준으로 좌/가운데/우 정렬하는 Tiptap 확장.\n *\n * BlockNote 0.35는 테이블 블록 레벨 정렬을 모델링하지 않으므로, verticalAlignment/\n * rowHeight와 동일한 패턴으로 `table` 노드에 글로벌 attribute `tableAlignment`를 추가한다.\n *  - renderHTML이 `data-table-alignment`를 출력(BlockNoteTableView가 HTMLAttributes를\n *    적용하는 .bn-block-content[data-content-type=table] div에 붙음) → CSS가 content-폭\n *    <table>에 margin을 적용해 정렬한다(좌측은 기본이라 attr 미출력).\n *  - parseHTML로 복사/붙여넣기 라운드트립.\n *  - 저장(블록 JSON 주입)은 utils/table-vertical-alignment.ts의 injectTableBlockAttrs가 담당.\n *  - 로드는 BlockNote blockToNode가 block.props를 table 노드 attr로 펼쳐 자동 복원.\n */\nexport const TableAlignmentExtension = Extension.create({\n  name: \"tableAlignment\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"table\"],\n        attributes: {\n          tableAlignment: {\n            default: \"left\",\n            parseHTML: (element) =>\n              element.getAttribute(\"data-table-alignment\") || \"left\",\n            renderHTML: (attributes) => {\n              if (\n                !attributes.tableAlignment ||\n                attributes.tableAlignment === \"left\"\n              ) {\n                return {};\n              }\n              return { \"data-table-alignment\": attributes.tableAlignment };\n            },\n          },\n        },\n      },\n    ];\n  },\n\n  addProseMirrorPlugins() {\n    return [tableAlignmentDecorationPlugin()];\n  },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { CellSelection, TableMap } from \"prosemirror-tables\";\n\n/**\n * 표 셀에 포커스(커서/선택)가 있을 때 **Ctrl/Cmd+A로 표 전체(모든 셀)를 선택**하는 확장.\n *\n * 기본 Mod-a는 문서 전체를 선택하지만, 표 안에서는 먼저 표 전체를 선택하는 편이\n * 사용성이 좋다(셀 일괄 색/정렬/삭제 등). 동작:\n *  - 셀 안인데 표 전체가 아직 선택되지 않음 → 표 전체 CellSelection (handled)\n *  - 이미 표 전체가 선택됨 → false 반환 → 기본 동작(문서 전체 선택)으로 단계적 확장\n *  - 표 밖 → false (기본 문서 전체 선택)\n *\n * priority를 높여 기본 selectAll보다 먼저 처리한다.\n */\nexport const TableSelectAllExtension = Extension.create({\n  name: \"lumirTableSelectAll\",\n  priority: 1000,\n\n  addKeyboardShortcuts() {\n    return {\n      \"Mod-a\": () => {\n        const view = this.editor.view;\n        const { state } = view;\n        const sel: any = state.selection;\n        const $from = sel.$from;\n\n        // 선택을 감싸는 table 노드의 depth를 찾는다.\n        let depth = -1;\n        for (let d = $from.depth; d > 0; d--) {\n          if ($from.node(d).type.name === \"table\") {\n            depth = d;\n            break;\n          }\n        }\n        if (depth < 0) return false; // 표 밖 → 기본 동작\n\n        const table = $from.node(depth);\n        const tableStart = $from.start(depth);\n        const map = TableMap.get(table);\n        const firstRel = map.map[0]; // 좌상단 셀\n        const lastRel = map.map[map.map.length - 1]; // 우하단 셀\n\n        // 이미 표 전체가 선택돼 있으면 기본 동작(문서 전체)로 넘긴다.\n        if (sel instanceof CellSelection) {\n          const a = sel.$anchorCell.pos - tableStart;\n          const h = sel.$headCell.pos - tableStart;\n          if (Math.min(a, h) === firstRel && Math.max(a, h) === lastRel) {\n            return false;\n          }\n        }\n\n        const tr = state.tr.setSelection(\n          CellSelection.create(\n            state.doc,\n            tableStart + firstRel,\n            tableStart + lastRel,\n          ),\n        );\n        view.dispatch(tr);\n        return true;\n      },\n    };\n  },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey, TextSelection } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\n\n/**\n * 에디터가 **포커스를 잃었을 때도 텍스트 선택 영역을 시각적으로 유지**하는 확장.\n *\n * 브라우저는 contenteditable이 blur되면 네이티브 선택 하이라이트를 그리지 않는다.\n * 그래서 텍스트를 범위 선택한 뒤 포매팅 툴바의 드롭다운(글자 크기 등, 포커스를\n * 가져감)을 열면 선택 하이라이트가 사라져 어디에 적용되는지 안 보였다.\n * (ProseMirror 선택 state는 유지되므로 글자 크기 적용 자체는 정상)\n *\n * 이 플러그인은 에디터가 blur 상태일 때 현재 TextSelection 범위에 인라인\n * 데코레이션(`.bn-inactive-selection`)을 입혀 선택을 계속 보이게 한다.\n * 포커스가 있을 때는 데코레이션을 비워 네이티브 선택을 그대로 사용한다.\n */\nconst inactiveSelectionKey = new PluginKey<boolean>(\"lumirInactiveSelection\");\n\nexport const InactiveSelectionExtension = Extension.create({\n  name: \"lumirInactiveSelection\",\n\n  addProseMirrorPlugins() {\n    return [\n      new Plugin<boolean>({\n        key: inactiveSelectionKey,\n        // 플러그인 state = 에디터 포커스 여부\n        state: {\n          init: () => false,\n          apply: (tr, value) => {\n            const meta = tr.getMeta(inactiveSelectionKey);\n            return typeof meta === \"boolean\" ? meta : value;\n          },\n        },\n        props: {\n          decorations(state) {\n            const hasFocus = inactiveSelectionKey.getState(state);\n            if (hasFocus) {\n              return null; // 포커스 있으면 네이티브 선택 사용\n            }\n            const { selection } = state;\n            // 비어있는 커서/비텍스트 선택은 제외(테이블 셀 등은 자체 표시)\n            if (selection.empty || !(selection instanceof TextSelection)) {\n              return null;\n            }\n            return DecorationSet.create(state.doc, [\n              Decoration.inline(selection.from, selection.to, {\n                class: \"bn-inactive-selection\",\n              }),\n            ]);\n          },\n        },\n        view(view) {\n          const sync = () => {\n            const has = view.hasFocus();\n            if (inactiveSelectionKey.getState(view.state) !== has) {\n              view.dispatch(view.state.tr.setMeta(inactiveSelectionKey, has));\n            }\n          };\n          view.dom.addEventListener(\"focus\", sync);\n          view.dom.addEventListener(\"blur\", sync);\n          // 초기 상태 반영\n          const raf = requestAnimationFrame(sync);\n          return {\n            destroy() {\n              cancelAnimationFrame(raf);\n              view.dom.removeEventListener(\"focus\", sync);\n              view.dom.removeEventListener(\"blur\", sync);\n            },\n          };\n        },\n      }),\n    ];\n  },\n});\n","import { TextSelection } from \"prosemirror-state\";\n\n/**\n * 현재 블록 뒤에 2단 컬럼(columnList > column×2 > 빈 단락)을 삽입한다.\n *\n * BlockNote의 editor.insertBlocks는 public blockSchema에 없는 타입(columnList/column)을\n * \"unrecognized type\"으로 거부하므로, ProseMirror 트랜잭션으로 직접 삽입한다.\n * id는 코어 UniqueID 확장이 누락 노드에 자동 부여한다(types에 columnList/column 포함).\n *\n * @returns 삽입 성공 여부.\n */\nexport function insertTwoColumns(\n  editor: any,\n  showDivider: boolean = false,\n): boolean {\n  const tiptap = editor?._tiptapEditor;\n  if (!tiptap) {\n    return false;\n  }\n  const { state, schema } = tiptap;\n  const { blockContainer, paragraph, column, columnList } = schema.nodes;\n  if (!blockContainer || !paragraph || !column || !columnList) {\n    return false;\n  }\n\n  const $from = state.selection.$from;\n\n  // 컬럼 중첩 금지: 조상에 column/columnList가 있으면 거부(컬럼 안에 columnList를\n  // 넣으면 column content \"blockContainer+\" 위반 → 삽입이 throw). 안전하게 no-op.\n  for (let d = $from.depth; d > 0; d--) {\n    const name = $from.node(d).type.name;\n    if (name === \"column\" || name === \"columnList\") {\n      return false;\n    }\n  }\n\n  // 현재 selection을 감싸는 blockContainer를 찾아 그 뒤(blockGroup 형제 위치)에 삽입.\n  let depth = $from.depth;\n  while (depth > 0 && $from.node(depth).type.name !== \"blockContainer\") {\n    depth--;\n  }\n  if (depth === 0) {\n    return false;\n  }\n  const insertPos = $from.after(depth);\n\n  const mkBlock = () => blockContainer.create(null, paragraph.create());\n  const mkColumn = () => column.create(null, mkBlock());\n  // showDivider는 생성 시 고정(가운데 세로 구분선 유무). id는 UniqueID가 자동 부여.\n  const list = columnList.create({ showDivider }, [mkColumn(), mkColumn()]);\n\n  // insert/dispatch는 위치가 어떤 이유로든 무효면 throw할 수 있으므로 가드한다.\n  try {\n    let tr = state.tr.insert(insertPos, list);\n    // 첫 컬럼의 단락으로 커서 이동(columnList+1, column+1, blockContainer+1, paragraph+1).\n    try {\n      tr = tr.setSelection(TextSelection.create(tr.doc, insertPos + 4));\n    } catch {\n      // 위치 매핑 실패 시 커서 이동은 생략(삽입 자체는 유효).\n    }\n    tiptap.view.dispatch(tr.scrollIntoView());\n    return true;\n  } catch {\n    return false;\n  }\n}\n","\"use client\";\n\nimport {\n  BasicTextStyleButton,\n  BlockTypeSelect,\n  CreateLinkButton,\n  FileCaptionButton,\n  FileDeleteButton,\n  FileRenameButton,\n  FileReplaceButton,\n  FileDownloadButton,\n  FilePreviewButton,\n  FormattingToolbar,\n  NestBlockButton,\n  UnnestBlockButton,\n  TableCellMergeButton,\n} from \"@blocknote/react\";\nimport { TextAlignButtonWithVA } from \"./TextAlignButtonWithVA\";\nimport { VerticalAlignButton } from \"./VerticalAlignButton\";\nimport { TableAlignButton } from \"./TableAlignButton\";\nimport { FontSizeButton } from \"./FontSizeButton\";\nimport {\n  LumirColorStyleButton,\n  LumirCellColorToolbarButton,\n} from \"./color/LumirColorControls\";\n\n/**\n * BlockNote 기본 FormattingToolbar + 세로 정렬 버튼(top/middle/bottom)\n *\n * @see https://www.blocknotejs.org/examples/ui-components/formatting-toolbar-buttons\n */\nexport const CustomFormattingToolbar = () => {\n  return (\n    <FormattingToolbar>\n      <BlockTypeSelect key={\"blockTypeSelect\"} />\n      <TableCellMergeButton key={\"tableCellMergeButton\"} />\n\n      <FileCaptionButton key={\"fileCaptionButton\"} />\n      <FileReplaceButton key={\"replaceFileButton\"} />\n      <FileRenameButton key={\"fileRenameButton\"} />\n      <FileDeleteButton key={\"fileDeleteButton\"} />\n      <FileDownloadButton key={\"fileDownloadButton\"} />\n      <FilePreviewButton key={\"filePreviewButton\"} />\n\n      <BasicTextStyleButton basicTextStyle={\"bold\"} key={\"boldStyleButton\"} />\n      <BasicTextStyleButton\n        basicTextStyle={\"italic\"}\n        key={\"italicStyleButton\"}\n      />\n      <BasicTextStyleButton\n        basicTextStyle={\"underline\"}\n        key={\"underlineStyleButton\"}\n      />\n      <BasicTextStyleButton\n        basicTextStyle={\"strike\"}\n        key={\"strikeStyleButton\"}\n      />\n\n      <TextAlignButtonWithVA textAlignment={\"left\"} key={\"textAlignLeftButton\"} />\n      <TextAlignButtonWithVA\n        textAlignment={\"center\"}\n        key={\"textAlignCenterButton\"}\n      />\n      <TextAlignButtonWithVA textAlignment={\"right\"} key={\"textAlignRightButton\"} />\n\n      {/* 세로 정렬 버튼 (테이블 셀 선택 시에만 표시) */}\n      <VerticalAlignButton\n        verticalAlignment=\"top\"\n        key={\"verticalAlignTop\"}\n      />\n      <VerticalAlignButton\n        verticalAlignment=\"middle\"\n        key={\"verticalAlignMiddle\"}\n      />\n      <VerticalAlignButton\n        verticalAlignment=\"bottom\"\n        key={\"verticalAlignBottom\"}\n      />\n\n      {/* 표 블록 정렬 버튼 (표 컨텍스트에서만 표시) */}\n      <TableAlignButton alignment=\"left\" key={\"tableAlignLeft\"} />\n      <TableAlignButton alignment=\"center\" key={\"tableAlignCenter\"} />\n      <TableAlignButton alignment=\"right\" key={\"tableAlignRight\"} />\n\n      <FontSizeButton key={\"fontSizeButton\"} />\n      <LumirColorStyleButton key={\"colorStyleButton\"} />\n      <LumirCellColorToolbarButton key={\"cellColorButton\"} />\n      <NestBlockButton key={\"nestBlockButton\"} />\n      <UnnestBlockButton key={\"unnestBlockButton\"} />\n      <CreateLinkButton key={\"createLinkButton\"} />\n    </FormattingToolbar>\n  );\n};\n","\"use client\";\n\n/**\n * BlockNote 기본 TextAlignButton의 커스텀 버전.\n * 테이블 셀에서 가로 정렬 변경 시 ProseMirror 트랜잭션을 직접 사용하여\n * selection이 유지되고 verticalAlignment가 초기화되지 않도록 한다.\n */\n\nimport {\n  BlockSchema,\n  checkBlockHasDefaultProp,\n  checkBlockTypeHasDefaultProp,\n  InlineContentSchema,\n  mapTableCell,\n  StyleSchema,\n  TableContent,\n} from \"@blocknote/core\";\nimport { useCallback, useMemo } from \"react\";\nimport {\n  useComponentsContext,\n  useBlockNoteEditor,\n  useSelectedBlocks,\n} from \"@blocknote/react\";\nimport { getSelectedCellPositions } from \"../utils/prosemirror-table-utils\";\n\ntype TextAlignment = \"left\" | \"center\" | \"right\" | \"justify\";\n\nconst icons: Record<TextAlignment, JSX.Element> = {\n  left: (\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n      <path d=\"M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z\" />\n    </svg>\n  ),\n  center: (\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n      <path d=\"M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z\" />\n    </svg>\n  ),\n  right: (\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n      <path d=\"M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z\" />\n    </svg>\n  ),\n  justify: (\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"18\" height=\"18\">\n      <path d=\"M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zM3 3v2h18V3H3z\" />\n    </svg>\n  ),\n};\n\nconst tooltipMap: Record<TextAlignment, string> = {\n  left: \"왼쪽 정렬\",\n  center: \"가운데 정렬\",\n  right: \"오른쪽 정렬\",\n  justify: \"양쪽 정렬\",\n};\n\nexport const TextAlignButtonWithVA = (props: {\n  textAlignment: TextAlignment;\n}) => {\n  const Components = useComponentsContext()!;\n  const editor = useBlockNoteEditor<\n    BlockSchema,\n    InlineContentSchema,\n    StyleSchema\n  >();\n\n  const selectedBlocks = useSelectedBlocks(editor);\n\n  const textAlignment = useMemo(() => {\n    const block = selectedBlocks[0];\n\n    if (checkBlockHasDefaultProp(\"textAlignment\", block, editor)) {\n      return block.props.textAlignment;\n    }\n    if (block.type === \"table\") {\n      const cellSelection = editor.tableHandles?.getCellSelection();\n      if (!cellSelection) {\n        return;\n      }\n      const allCellsInTable = cellSelection.cells.map(\n        ({ row, col }: { row: number; col: number }) =>\n          mapTableCell(\n            (block.content as TableContent<any, any>).rows[row].cells[col],\n          ).props.textAlignment,\n      );\n      const firstAlignment = allCellsInTable[0];\n\n      if (allCellsInTable.every((alignment: string) => alignment === firstAlignment)) {\n        return firstAlignment;\n      }\n    }\n\n    return;\n  }, [editor, selectedBlocks]);\n\n  const setTextAlignment = useCallback(\n    (newAlignment: TextAlignment) => {\n      editor.focus();\n\n      for (const block of selectedBlocks) {\n        if (block.type === \"table\") {\n          const tiptap = editor._tiptapEditor;\n          if (!tiptap) continue;\n\n          const positions = getSelectedCellPositions(editor);\n          if (positions.length === 0) continue;\n\n          const { state } = tiptap;\n          let tr = state.tr;\n\n          for (const pos of positions) {\n            const node = tr.doc.nodeAt(pos);\n            if (node) {\n              tr = tr.setNodeMarkup(pos, undefined, {\n                ...node.attrs,\n                textAlignment: newAlignment,\n              });\n            }\n          }\n\n          tiptap.view?.dispatch(tr);\n        } else if (checkBlockTypeHasDefaultProp(\"textAlignment\", block.type, editor)) {\n          editor.updateBlock(block, {\n            props: { textAlignment: newAlignment },\n          });\n        }\n      }\n    },\n    [editor, selectedBlocks],\n  );\n\n  const show = useMemo(() => {\n    return !!selectedBlocks.find(\n      (block) =>\n        \"textAlignment\" in block.props ||\n        (block.type === \"table\" && block.children),\n    );\n  }, [selectedBlocks]);\n\n  if (!show || !editor.isEditable) {\n    return null;\n  }\n\n  return (\n    <Components.FormattingToolbar.Button\n      className={\"bn-button\"}\n      data-test={`alignText${\n        props.textAlignment.slice(0, 1).toUpperCase() +\n        props.textAlignment.slice(1)\n      }`}\n      onClick={() => setTextAlignment(props.textAlignment)}\n      isSelected={textAlignment === props.textAlignment}\n      label={tooltipMap[props.textAlignment]}\n      mainTooltip={tooltipMap[props.textAlignment]}\n      icon={icons[props.textAlignment]}\n    />\n  );\n};\n","\"use client\";\n\nimport { useCallback, useMemo } from \"react\";\nimport {\n  useBlockNoteEditor,\n  useComponentsContext,\n  useSelectedBlocks,\n} from \"@blocknote/react\";\nimport type {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"@blocknote/core\";\nimport type { VerticalAlignment } from \"../extensions/VerticalAlignmentExtension\";\nimport { getSelectedCellPositions } from \"../utils/prosemirror-table-utils\";\n\nconst icons: Record<VerticalAlignment, JSX.Element> = {\n  top: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\">\n      <rect x=\"2\" y=\"2\" width=\"12\" height=\"12\" rx=\"1\" />\n      <line x1=\"5\" y1=\"5\" x2=\"11\" y2=\"5\" />\n    </svg>\n  ),\n  middle: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\">\n      <rect x=\"2\" y=\"2\" width=\"12\" height=\"12\" rx=\"1\" />\n      <line x1=\"5\" y1=\"8\" x2=\"11\" y2=\"8\" />\n    </svg>\n  ),\n  bottom: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\">\n      <rect x=\"2\" y=\"2\" width=\"12\" height=\"12\" rx=\"1\" />\n      <line x1=\"5\" y1=\"11\" x2=\"11\" y2=\"11\" />\n    </svg>\n  ),\n};\n\nconst tooltips: Record<VerticalAlignment, string> = {\n  top: \"위쪽 정렬\",\n  middle: \"세로 가운데 정렬\",\n  bottom: \"아래쪽 정렬\",\n};\n\nfunction getCurrentVerticalAlignment(editor: any): VerticalAlignment | undefined {\n  const tiptap = editor._tiptapEditor;\n  if (!tiptap) return undefined;\n\n  const positions = getSelectedCellPositions(editor);\n  if (positions.length === 0) return undefined;\n\n  const { state } = tiptap;\n  const alignments = positions.map((pos) => {\n    const node = state.doc.nodeAt(pos);\n    return (node?.attrs?.verticalAlignment as VerticalAlignment) || \"top\";\n  });\n\n  const first = alignments[0];\n  return alignments.every((a) => a === first) ? first : undefined;\n}\n\nexport const VerticalAlignButton = (props: {\n  verticalAlignment: VerticalAlignment;\n}) => {\n  const Components = useComponentsContext()!;\n  const editor = useBlockNoteEditor<\n    BlockSchema,\n    InlineContentSchema,\n    StyleSchema\n  >();\n\n  const selectedBlocks = useSelectedBlocks(editor);\n\n  const currentAlignment = useMemo(() => {\n    return getCurrentVerticalAlignment(editor);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [editor, selectedBlocks]);\n\n  const setVerticalAlignment = useCallback(\n    (alignment: VerticalAlignment) => {\n      const tiptap = editor._tiptapEditor;\n      if (!tiptap) return;\n\n      const positions = getSelectedCellPositions(editor);\n      if (positions.length === 0) return;\n\n      editor.focus();\n\n      const { state } = tiptap;\n      let tr = state.tr;\n\n      for (const pos of positions) {\n        const node = tr.doc.nodeAt(pos);\n        if (node) {\n          tr = tr.setNodeMarkup(pos, undefined, {\n            ...node.attrs,\n            verticalAlignment: alignment,\n          });\n        }\n      }\n\n      tiptap.view?.dispatch(tr);\n    },\n    [editor],\n  );\n\n  const isInTable = useMemo(() => {\n    return selectedBlocks.some((block) => block.type === \"table\");\n  }, [selectedBlocks]);\n\n  if (!isInTable || !editor.isEditable) {\n    return null;\n  }\n\n  return (\n    <Components.FormattingToolbar.Button\n      className={\"bn-button\"}\n      data-test={`verticalAlign${props.verticalAlignment.charAt(0).toUpperCase() + props.verticalAlignment.slice(1)}`}\n      onClick={() => setVerticalAlignment(props.verticalAlignment)}\n      isSelected={currentAlignment === props.verticalAlignment}\n      label={tooltips[props.verticalAlignment]}\n      mainTooltip={tooltips[props.verticalAlignment]}\n      icon={icons[props.verticalAlignment]}\n    />\n  );\n};\n","\"use client\";\n\n/**\n * 표(table) 블록 전체를 에디터 영역 기준으로 좌/가운데/우 정렬하는 툴바 버튼.\n * 셀 텍스트 정렬(TextAlignButtonWithVA)과 구분되며, 표 블록이 선택된 컨텍스트에서만 노출된다.\n * 정렬값은 table 노드 attr(tableAlignment)에 저장한다(setTableAlignment).\n */\n\nimport { useCallback, useMemo } from \"react\";\nimport {\n  useBlockNoteEditor,\n  useComponentsContext,\n  useSelectedBlocks,\n} from \"@blocknote/react\";\nimport type {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"@blocknote/core\";\nimport type { TableAlignment } from \"../extensions/TableAlignmentExtension\";\nimport {\n  getTableAlignment,\n  setTableAlignment,\n} from \"../utils/prosemirror-table-utils\";\n\n// 표(직사각형) 안에 정렬 위치를 나타내는 작은 막대를 둔 아이콘.\nconst icons: Record<TableAlignment, JSX.Element> = {\n  left: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.4\">\n      <rect x=\"1.5\" y=\"4\" width=\"7\" height=\"8\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" />\n      <line x1=\"1.5\" y1=\"1.5\" x2=\"14.5\" y2=\"1.5\" />\n      <line x1=\"1.5\" y1=\"14.5\" x2=\"14.5\" y2=\"14.5\" />\n    </svg>\n  ),\n  center: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.4\">\n      <rect x=\"4.5\" y=\"4\" width=\"7\" height=\"8\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" />\n      <line x1=\"1.5\" y1=\"1.5\" x2=\"14.5\" y2=\"1.5\" />\n      <line x1=\"1.5\" y1=\"14.5\" x2=\"14.5\" y2=\"14.5\" />\n    </svg>\n  ),\n  right: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.4\">\n      <rect x=\"7.5\" y=\"4\" width=\"7\" height=\"8\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" />\n      <line x1=\"1.5\" y1=\"1.5\" x2=\"14.5\" y2=\"1.5\" />\n      <line x1=\"1.5\" y1=\"14.5\" x2=\"14.5\" y2=\"14.5\" />\n    </svg>\n  ),\n};\n\nconst tooltips: Record<TableAlignment, string> = {\n  left: \"표 왼쪽 정렬\",\n  center: \"표 가운데 정렬\",\n  right: \"표 오른쪽 정렬\",\n};\n\nexport const TableAlignButton = (props: { alignment: TableAlignment }) => {\n  const Components = useComponentsContext()!;\n  const editor = useBlockNoteEditor<\n    BlockSchema,\n    InlineContentSchema,\n    StyleSchema\n  >();\n\n  const selectedBlocks = useSelectedBlocks(editor);\n\n  const tableBlock = useMemo(\n    () => selectedBlocks.find((block) => block.type === \"table\"),\n    [selectedBlocks],\n  );\n\n  const current = useMemo(() => {\n    if (!tableBlock?.id) return \"left\";\n    return getTableAlignment(editor, tableBlock.id);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [editor, tableBlock, selectedBlocks]);\n\n  const apply = useCallback(() => {\n    if (!tableBlock?.id) return;\n    editor.focus();\n    setTableAlignment(editor, tableBlock.id, props.alignment);\n  }, [editor, tableBlock, props.alignment]);\n\n  if (!tableBlock || !editor.isEditable) {\n    return null;\n  }\n\n  return (\n    <Components.FormattingToolbar.Button\n      className={\"bn-button\"}\n      data-test={`tableAlign${props.alignment.charAt(0).toUpperCase() + props.alignment.slice(1)}`}\n      onClick={apply}\n      isSelected={current === props.alignment}\n      label={tooltips[props.alignment]}\n      mainTooltip={tooltips[props.alignment]}\n      icon={icons[props.alignment]}\n    />\n  );\n};\n","\"use client\";\n\n/**\n * 포매팅 툴바의 \"글자 크기\" 드롭다운 버튼.\n * LumirColorStyleButton(color/LumirColorControls.tsx)과 동일한 패턴:\n * Generic.Menu 드롭다운 + addStyles/removeStyles + 다음 틱 focus 복원.\n *\n * 드롭다운 상단에 −/+ 스테퍼 + 직접 입력(1px 단위, 8~96px clamp)을 두고,\n * 그 아래 \"기본\" 리셋 + 프리셋 목록을 빠른 선택용으로 유지한다.\n */\n\nimport {\n  type BlockSchema,\n  type InlineContentSchema,\n  type StyleSchema,\n} from \"@blocknote/core\";\nimport {\n  useBlockNoteEditor,\n  useComponentsContext,\n  useEditorContentOrSelectionChange,\n  useSelectedBlocks,\n} from \"@blocknote/react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n  FONT_SIZE_MAX,\n  FONT_SIZE_MIN,\n  FONT_SIZE_PRESETS,\n  parseFontSizePx,\n  readSelectionFontSize,\n  toFontSizeValue,\n} from \"../styles/FontSizeStyle\";\n\nconst DEFAULT_LABEL = \"기본\";\n\n/** \"18px\" → \"18\" (드롭다운/트리거 표시용) */\nconst toLabel = (size: string) => size.replace(/px$/, \"\");\n\nfunction FontSizeIcon({ size }: { size: string }) {\n  return (\n    <span\n      style={{\n        pointerEvents: \"none\",\n        fontSize: \"12px\",\n        fontWeight: 600,\n        lineHeight: \"20px\",\n        whiteSpace: \"nowrap\",\n      }}\n    >\n      {size ? toLabel(size) : \"가A\"}\n    </span>\n  );\n}\n\nexport function FontSizeButton() {\n  const Components = useComponentsContext()!;\n  const editor = useBlockNoteEditor<\n    BlockSchema,\n    InlineContentSchema,\n    StyleSchema\n  >();\n\n  // 커스텀 스타일은 제네릭 StyleSchema에서 추론되지 않으므로 느슨한 타입으로 처리\n  // (LumirColorStyleButton과 동일한 관례)\n  const ed = editor as unknown as {\n    getActiveStyles: () => Record<string, string>;\n    addStyles: (styles: Record<string, string>) => void;\n    removeStyles: (styles: Record<string, string>) => void;\n  };\n\n  const styleSchema = editor.schema.styleSchema as Record<\n    string,\n    { type?: string; propSchema?: string }\n  >;\n  const fontSizeInSchema =\n    styleSchema.fontSize?.type === \"fontSize\" &&\n    styleSchema.fontSize?.propSchema === \"string\";\n\n  const selectedBlocks = useSelectedBlocks(editor);\n\n  const [currentSize, setCurrentSize] = useState<string>(\n    fontSizeInSchema ? readSelectionFontSize(editor) : \"\",\n  );\n\n  useEditorContentOrSelectionChange(() => {\n    if (fontSizeInSchema) {\n      setCurrentSize(readSelectionFontSize(editor));\n    }\n  }, editor);\n\n  // 명시 크기가 없으면(\"기본\") 14px를 기준으로 증감/표시한다.\n  const currentPx = parseFontSizePx(currentSize);\n\n  // 직접 입력 필드의 로컬 상태 (선택/적용으로 실제 크기가 바뀌면 동기화)\n  const [inputValue, setInputValue] = useState<string>(String(currentPx));\n  useEffect(() => {\n    setInputValue(String(currentPx));\n  }, [currentPx]);\n\n  const setFontSize = useCallback(\n    (size: string) => {\n      size === \"\"\n        ? ed.removeStyles({ fontSize: \"\" })\n        : ed.addStyles({ fontSize: size });\n      // Mantine Toolbar의 useFocusTrap 호환을 위해 다음 틱에 focus.\n      setTimeout(() => editor.focus());\n    },\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [editor],\n  );\n\n  // −/+ 스테퍼: 메뉴를 닫지 않고 선택 영역을 유지한 채 1px씩 적용한다.\n  const stepBy = useCallback(\n    (delta: number) => {\n      const value = toFontSizeValue(currentPx + delta);\n      ed.addStyles({ fontSize: value });\n      // 다중 블록 선택 등에서 getActiveStyles($to.marks)가 빈 값을 돌려줘\n      // currentSize가 갱신되지 않는 경우를 대비해 적용값으로 낙관적 갱신.\n      setCurrentSize(value);\n    },\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [currentPx],\n  );\n\n  // 직접 입력 적용: 유효하면 clamp 후 적용, 무효/빈값이면 표시값 복원.\n  const applyInput = useCallback(() => {\n    const n = parseInt(inputValue, 10);\n    if (Number.isFinite(n)) {\n      const value = toFontSizeValue(n);\n      ed.addStyles({ fontSize: value });\n      setCurrentSize(value);\n    } else {\n      setInputValue(String(currentPx));\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [inputValue, currentPx]);\n\n  const show = useMemo(() => {\n    if (!fontSizeInSchema) {\n      return false;\n    }\n    for (const block of selectedBlocks) {\n      if (block.content !== undefined) {\n        return true;\n      }\n    }\n    return false;\n  }, [fontSizeInSchema, selectedBlocks]);\n\n  if (!show || !editor.isEditable) {\n    return null;\n  }\n\n  const tooltip = \"글자 크기\";\n  const preventBlur = (e: { preventDefault: () => void }) => e.preventDefault();\n\n  return (\n    <Components.Generic.Menu.Root>\n      <Components.Generic.Menu.Trigger>\n        <Components.FormattingToolbar.Button\n          className={\"bn-button\"}\n          data-test=\"font-size\"\n          label={tooltip}\n          mainTooltip={tooltip}\n          icon={<FontSizeIcon size={currentSize} />}\n        />\n      </Components.Generic.Menu.Trigger>\n      <Components.Generic.Menu.Dropdown className={\"bn-menu-dropdown\"}>\n        <Components.Generic.Menu.Label>글자 크기</Components.Generic.Menu.Label>\n        {/* 1px 스테퍼 + 직접 입력 (Menu.Item이 아니므로 클릭해도 메뉴가 닫히지 않음) */}\n        <div className=\"lumir-fs-stepper\">\n          <button\n            type=\"button\"\n            className=\"lumir-fs-stepper-btn\"\n            aria-label=\"글자 크기 1px 줄이기\"\n            data-test=\"font-size-decrease\"\n            disabled={currentPx <= FONT_SIZE_MIN}\n            onMouseDown={preventBlur}\n            onClick={() => stepBy(-1)}\n          >\n            −\n          </button>\n          <input\n            className=\"lumir-fs-stepper-input\"\n            type=\"text\"\n            inputMode=\"numeric\"\n            aria-label=\"글자 크기 입력\"\n            data-test=\"font-size-input\"\n            value={inputValue}\n            onChange={(e) =>\n              setInputValue(e.target.value.replace(/[^0-9]/g, \"\"))\n            }\n            onClick={(e) => e.stopPropagation()}\n            onKeyDown={(e) => {\n              e.stopPropagation();\n              if (e.key === \"Enter\") {\n                e.preventDefault();\n                applyInput();\n              } else if (e.key === \"ArrowUp\") {\n                e.preventDefault();\n                stepBy(1);\n              } else if (e.key === \"ArrowDown\") {\n                e.preventDefault();\n                stepBy(-1);\n              }\n            }}\n            onBlur={applyInput}\n          />\n          <button\n            type=\"button\"\n            className=\"lumir-fs-stepper-btn\"\n            aria-label=\"글자 크기 1px 늘리기\"\n            data-test=\"font-size-increase\"\n            disabled={currentPx >= FONT_SIZE_MAX}\n            onMouseDown={preventBlur}\n            onClick={() => stepBy(1)}\n          >\n            +\n          </button>\n        </div>\n        <Components.Generic.Menu.Item\n          onClick={() => setFontSize(\"\")}\n          checked={currentSize === \"\"}\n          data-test={\"font-size-default\"}\n          key={\"font-size-default\"}\n        >\n          {DEFAULT_LABEL}\n        </Components.Generic.Menu.Item>\n        {FONT_SIZE_PRESETS.map((size) => (\n          <Components.Generic.Menu.Item\n            onClick={() => setFontSize(size)}\n            checked={currentSize === size}\n            data-test={\"font-size-\" + toLabel(size)}\n            key={\"font-size-\" + size}\n          >\n            {toLabel(size)}\n          </Components.Generic.Menu.Item>\n        ))}\n      </Components.Generic.Menu.Dropdown>\n    </Components.Generic.Menu.Root>\n  );\n}\n","\"use client\";\n\n/**\n * 색상 컨트롤(텍스트 색/배경) — 컨텍스트별로 그룹 제목·툴팁을 명확히 구분한다.\n *\n * 배경:\n *  - BlockNote의 공유 `ColorPicker`는 그룹 제목을 editor 전역 dictionary\n *    (`color_picker.text_title`/`background_title`)에서 읽으므로, \"글(텍스트) 배경\"과\n *    \"셀 배경\"을 같은 \"텍스트/배경\" 라벨로 표기해 어떤 배경을 바꾸는 버튼인지 모호했다.\n *  - 공유 `ColorPicker`/`ColorIcon`은 패키지 루트로 export되지 않아 dictionary를\n *    덮어쓸 수도, 직접 import할 수도 없다(dictionary는 editor 인스턴스에 묶임).\n *  → 그래서 `ColorPicker`를 제목 prop을 받도록 복제(`LumirColorPicker`)하고,\n *    툴바(인라인)·셀 메뉴용 버튼을 각각 만들어 컨텍스트별 라벨을 주입한다.\n *\n * 라벨 규칙:\n *  - 툴바(인라인 글자 색/배경): 툴팁 \"텍스트 색·배경\", 그룹 \"텍스트 색\" / \"텍스트 배경\"\n *  - 셀 메뉴(셀 글자 색/배경):  트리거 \"셀 색·배경\",  그룹 \"셀 글자색\" / \"셀 배경\"\n *  버튼은 색·배경을 모두 바꾸므로 툴팁을 \"배경\"만으로 적으면 색 기능이 가려지고\n *  (특히 셀 메뉴의 텍스트 색), 그룹 제목에 \"텍스트 배경\"/\"셀 배경\"을 명시해 두 배경\n *  기능을 분명히 구분한다.\n */\n\nimport {\n  type BlockSchema,\n  type DefaultBlockSchema,\n  type DefaultInlineContentSchema,\n  type DefaultStyleSchema,\n  type InlineContentSchema,\n  isTableCell,\n  mapTableCell,\n  type StyleSchema,\n} from \"@blocknote/core\";\nimport {\n  SplitButton,\n  type TableCellMenuProps,\n  useBlockNoteEditor,\n  useComponentsContext,\n  useDictionary,\n  useEditorContentOrSelectionChange,\n  useSelectedBlocks,\n} from \"@blocknote/react\";\nimport { type ReactNode, useCallback, useMemo, useRef, useState } from \"react\";\nimport {\n  getSelectedCellPositions,\n  setCellAttrAtPositions,\n} from \"../../utils/prosemirror-table-utils\";\n\nconst COLORS = [\n  \"default\",\n  \"gray\",\n  \"brown\",\n  \"red\",\n  \"orange\",\n  \"yellow\",\n  \"green\",\n  \"blue\",\n  \"purple\",\n  \"pink\",\n] as const;\n\n/** BlockNote 내부 ColorIcon이 export되지 않아 동일 구현(색 스와치 \"A\")을 복제. */\nfunction ColorIcon(props: {\n  textColor?: string;\n  backgroundColor?: string;\n  size?: number;\n}) {\n  const textColor = props.textColor || \"default\";\n  const backgroundColor = props.backgroundColor || \"default\";\n  const size = props.size || 16;\n  return (\n    <div\n      className={\"bn-color-icon\"}\n      data-background-color={backgroundColor}\n      data-text-color={textColor}\n      style={{\n        pointerEvents: \"none\",\n        fontSize: `${size * 0.75}px`,\n        height: `${size}px`,\n        lineHeight: `${size}px`,\n        textAlign: \"center\",\n        width: `${size}px`,\n      }}\n    >\n      A\n    </div>\n  );\n}\n\n/** 셀 채움(배경) 아이콘 — 인라인 \"A\" 아이콘과 구분되는 페인트통(fill) 모양. */\nfunction CellFillIcon({ size = 18 }: { size?: number }) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"currentColor\"\n      style={{ pointerEvents: \"none\" }}\n      aria-hidden=\"true\"\n    >\n      <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\" />\n    </svg>\n  );\n}\n\n/**\n * 공유 ColorPicker의 복제판. 그룹 제목을 `textTitle`/`backgroundTitle`로 주입할 수\n * 있다(미지정 시 dictionary 기본값). data-test는 원본과 동일하게 유지.\n */\nfunction LumirColorPicker(props: {\n  onClick?: () => void;\n  iconSize?: number;\n  textTitle?: string;\n  backgroundTitle?: string;\n  text?: { color: string; setColor: (color: string) => void };\n  background?: { color: string; setColor: (color: string) => void };\n}) {\n  const Components = useComponentsContext()!;\n  const dict = useDictionary();\n\n  return (\n    <>\n      {props.text ? (\n        <>\n          <Components.Generic.Menu.Label>\n            {props.textTitle ?? dict.color_picker.text_title}\n          </Components.Generic.Menu.Label>\n          {COLORS.map((color) => (\n            <Components.Generic.Menu.Item\n              onClick={() => {\n                props.onClick?.();\n                props.text!.setColor(color);\n              }}\n              data-test={\"text-color-\" + color}\n              icon={<ColorIcon textColor={color} size={props.iconSize} />}\n              checked={props.text!.color === color}\n              key={\"text-color-\" + color}\n            >\n              {dict.color_picker.colors[color]}\n            </Components.Generic.Menu.Item>\n          ))}\n        </>\n      ) : null}\n      {props.background ? (\n        <>\n          <Components.Generic.Menu.Label>\n            {props.backgroundTitle ?? dict.color_picker.background_title}\n          </Components.Generic.Menu.Label>\n          {COLORS.map((color) => (\n            <Components.Generic.Menu.Item\n              onClick={() => {\n                props.onClick?.();\n                props.background!.setColor(color);\n              }}\n              data-test={\"background-color-\" + color}\n              icon={\n                <ColorIcon backgroundColor={color} size={props.iconSize} />\n              }\n              checked={props.background!.color === color}\n              key={\"background-color-\" + color}\n            >\n              {dict.color_picker.colors[color]}\n            </Components.Generic.Menu.Item>\n          ))}\n        </>\n      ) : null}\n    </>\n  );\n}\n\n/**\n * 툴바(인라인 텍스트) 색상 버튼. 글자 색 + 글자 배경을 바꾼다.\n * BlockNote ColorStyleButton의 복제판이며 그룹 제목/툴팁만 \"텍스트\"로 명시한다.\n */\nexport function LumirColorStyleButton() {\n  const Components = useComponentsContext()!;\n  const editor = useBlockNoteEditor<\n    BlockSchema,\n    InlineContentSchema,\n    StyleSchema\n  >();\n\n  // 기본 스타일 스키마(textColor/backgroundColor)는 제네릭 StyleSchema에서 모두\n  // undefined로 추론되므로, 스타일 읽기/쓰기는 느슨한 타입 별칭으로 처리한다.\n  const ed = editor as unknown as {\n    getActiveStyles: () => Record<string, string>;\n    addStyles: (styles: Record<string, string>) => void;\n    removeStyles: (styles: Record<string, string>) => void;\n  };\n\n  const styleSchema = editor.schema.styleSchema as Record<\n    string,\n    { type?: string; propSchema?: string }\n  >;\n  const textColorInSchema =\n    styleSchema.textColor?.type === \"textColor\" &&\n    styleSchema.textColor?.propSchema === \"string\";\n  const backgroundColorInSchema =\n    styleSchema.backgroundColor?.type === \"backgroundColor\" &&\n    styleSchema.backgroundColor?.propSchema === \"string\";\n\n  const selectedBlocks = useSelectedBlocks(editor);\n\n  const [currentTextColor, setCurrentTextColor] = useState<string>(\n    textColorInSchema ? ed.getActiveStyles().textColor || \"default\" : \"default\",\n  );\n  const [currentBackgroundColor, setCurrentBackgroundColor] = useState<string>(\n    backgroundColorInSchema\n      ? ed.getActiveStyles().backgroundColor || \"default\"\n      : \"default\",\n  );\n\n  useEditorContentOrSelectionChange(() => {\n    const active = ed.getActiveStyles();\n    if (textColorInSchema) {\n      setCurrentTextColor(active.textColor || \"default\");\n    }\n    if (backgroundColorInSchema) {\n      setCurrentBackgroundColor(active.backgroundColor || \"default\");\n    }\n  }, editor);\n\n  const setTextColor = useCallback(\n    (color: string) => {\n      color === \"default\"\n        ? ed.removeStyles({ textColor: color })\n        : ed.addStyles({ textColor: color });\n      // Mantine Toolbar의 useFocusTrap 호환을 위해 다음 틱에 focus.\n      setTimeout(() => editor.focus());\n    },\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [editor],\n  );\n\n  const setBackgroundColor = useCallback(\n    (color: string) => {\n      color === \"default\"\n        ? ed.removeStyles({ backgroundColor: color })\n        : ed.addStyles({ backgroundColor: color });\n      setTimeout(() => editor.focus());\n    },\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [editor],\n  );\n\n  const show = useMemo(() => {\n    if (!textColorInSchema && !backgroundColorInSchema) {\n      return false;\n    }\n    for (const block of selectedBlocks) {\n      if (block.content !== undefined) {\n        return true;\n      }\n    }\n    return false;\n  }, [backgroundColorInSchema, selectedBlocks, textColorInSchema]);\n\n  if (!show || !editor.isEditable) {\n    return null;\n  }\n\n  const tooltip = \"텍스트 색·배경\";\n\n  return (\n    <Components.Generic.Menu.Root>\n      <Components.Generic.Menu.Trigger>\n        <Components.FormattingToolbar.Button\n          className={\"bn-button\"}\n          data-test=\"colors\"\n          label={tooltip}\n          mainTooltip={tooltip}\n          icon={\n            <ColorIcon\n              textColor={currentTextColor}\n              backgroundColor={currentBackgroundColor}\n              size={20}\n            />\n          }\n        />\n      </Components.Generic.Menu.Trigger>\n      <Components.Generic.Menu.Dropdown\n        className={\"bn-menu-dropdown bn-color-picker-dropdown\"}\n      >\n        <LumirColorPicker\n          textTitle=\"텍스트 색\"\n          backgroundTitle=\"텍스트 배경\"\n          text={\n            textColorInSchema\n              ? { color: currentTextColor, setColor: setTextColor }\n              : undefined\n          }\n          background={\n            backgroundColorInSchema\n              ? { color: currentBackgroundColor, setColor: setBackgroundColor }\n              : undefined\n          }\n        />\n      </Components.Generic.Menu.Dropdown>\n    </Components.Generic.Menu.Root>\n  );\n}\n\n/**\n * 포매팅 툴바의 \"셀 배경색\" 버튼 — 여러 셀을 드래그(DND)로 선택했을 때 선택된\n * 모든 셀의 **배경색**을 일괄 변경한다(병합 버튼과 동일하게 다중 셀 선택에서만\n * 노출). 단일 셀은 셀 우측 그립 메뉴가 담당하므로 여기서는 숨긴다.\n *\n * 적용은 `setNodeMarkup`(셀 노드 attr) 방식이라 **셀 선택이 유지**된다 → 연속\n * 재색칠 가능(Notion 동작). updateBlock과 달리 내용 전체를 교체하지 않는다.\n *\n * 주의: 색 스와치 클릭은 드롭다운 상호작용이므로, 메뉴가 열리는 순간 셀 위치를\n * 스냅샷해 둔다. 적용 시 라이브 선택을 우선 쓰되(선택 유지), 혹시 붕괴했으면\n * 스냅샷으로 폴백해 색은 정확히 적용한다.\n *\n * 한계: 병합 셀(colspan/rowspan)은 이번 범위 외(예외만 방지).\n */\nexport function LumirCellColorToolbarButton() {\n  const Components = useComponentsContext()!;\n  const editor = useBlockNoteEditor<\n    { table: DefaultBlockSchema[\"table\"] },\n    InlineContentSchema,\n    StyleSchema\n  >();\n  const selectedBlocks = useSelectedBlocks(editor);\n\n  // 다중 셀 선택 여부(병합 버튼과 동일 패턴: selectedBlocks 변화 시 재계산).\n  const isMultiCell = useMemo(() => {\n    if (selectedBlocks.length !== 1 || selectedBlocks[0].type !== \"table\") {\n      return false;\n    }\n    const cs = editor.tableHandles?.getCellSelection();\n    return !!cs && cs.cells.length > 1;\n  }, [editor, selectedBlocks]);\n\n  // 메뉴 열림 시점의 셀 위치(pos) 스냅샷.\n  const stashRef = useRef<number[]>([]);\n\n  const applyBackground = useCallback(\n    (color: string) => {\n      // 라이브 선택 우선(선택 유지), 비어 있으면 스냅샷 폴백(색은 정확히 적용).\n      const live = getSelectedCellPositions(editor);\n      const positions = live.length > 0 ? live : stashRef.current;\n      setCellAttrAtPositions(editor, positions, \"backgroundColor\", color);\n      // 셀 선택이 다시 보이도록 에디터로 포커스 복귀.\n      setTimeout(() => editor.focus());\n    },\n    [editor],\n  );\n\n  if (!editor.isEditable || !isMultiCell) {\n    return null;\n  }\n\n  const tooltip = \"셀 배경색\";\n\n  return (\n    <Components.Generic.Menu.Root\n      onOpenChange={(open: boolean) => {\n        // 드롭다운이 열리는 순간 셀 위치 스냅샷(상호작용 중 선택 붕괴 대비).\n        if (open) {\n          stashRef.current = getSelectedCellPositions(editor);\n        }\n      }}\n    >\n      <Components.Generic.Menu.Trigger>\n        <Components.FormattingToolbar.Button\n          className={\"bn-button\"}\n          data-test=\"cell-colors\"\n          label={tooltip}\n          mainTooltip={tooltip}\n          icon={<CellFillIcon size={18} />}\n        />\n      </Components.Generic.Menu.Trigger>\n      <Components.Generic.Menu.Dropdown\n        className={\"bn-menu-dropdown bn-color-picker-dropdown\"}\n      >\n        {/* 일괄 변경은 배경색만 제공(글자색 그룹 제거). */}\n        <LumirColorPicker\n          backgroundTitle=\"셀 배경\"\n          background={{ color: \"default\", setColor: applyBackground }}\n        />\n      </Components.Generic.Menu.Dropdown>\n    </Components.Generic.Menu.Root>\n  );\n}\n\n/**\n * 셀 메뉴의 색상 서브메뉴. 셀 글자 색 + 셀 배경을 바꾼다.\n * BlockNote 셀 ColorPickerButton의 복제판이며 트리거/그룹 제목만 \"셀\"로 명시한다.\n */\nfunction LumirCellColorPickerButton<\n  I extends InlineContentSchema = DefaultInlineContentSchema,\n  S extends StyleSchema = DefaultStyleSchema,\n>(props: TableCellMenuProps<I, S>) {\n  const Components = useComponentsContext()!;\n  const editor = useBlockNoteEditor<\n    { table: DefaultBlockSchema[\"table\"] },\n    I,\n    S\n  >();\n\n  const updateColor = (color: string, type: \"text\" | \"background\") => {\n    const newTable = props.block.content.rows.map((row) => ({\n      ...row,\n      cells: row.cells.map((cell) => mapTableCell(cell)),\n    }));\n\n    if (type === \"text\") {\n      newTable[props.rowIndex].cells[props.colIndex].props.textColor = color;\n    } else {\n      newTable[props.rowIndex].cells[props.colIndex].props.backgroundColor =\n        color;\n    }\n\n    editor.updateBlock(props.block, {\n      type: \"table\",\n      content: { ...props.block.content, rows: newTable },\n    });\n    // updateBlock이 선택을 블록 밖으로 옮기므로 커서를 블록으로 되돌린다.\n    editor.setTextCursorPosition(props.block);\n  };\n\n  const currentCell =\n    props.block.content.rows[props.rowIndex]?.cells?.[props.colIndex];\n\n  if (\n    !currentCell ||\n    (editor.settings.tables.cellTextColor === false &&\n      editor.settings.tables.cellBackgroundColor === false)\n  ) {\n    return null;\n  }\n\n  return (\n    <Components.Generic.Menu.Root position={\"right\"} sub={true}>\n      <Components.Generic.Menu.Trigger sub={true}>\n        <Components.Generic.Menu.Item className={\"bn-menu-item\"} subTrigger={true}>\n          셀 색·배경\n        </Components.Generic.Menu.Item>\n      </Components.Generic.Menu.Trigger>\n\n      <Components.Generic.Menu.Dropdown\n        sub={true}\n        className={\"bn-menu-dropdown bn-color-picker-dropdown\"}\n      >\n        <LumirColorPicker\n          iconSize={18}\n          textTitle=\"셀 글자색\"\n          backgroundTitle=\"셀 배경\"\n          text={\n            editor.settings.tables.cellTextColor\n              ? {\n                  color: isTableCell(currentCell)\n                    ? currentCell.props.textColor\n                    : \"default\",\n                  setColor: (color) => updateColor(color, \"text\"),\n                }\n              : undefined\n          }\n          background={\n            editor.settings.tables.cellBackgroundColor\n              ? {\n                  color: isTableCell(currentCell)\n                    ? currentCell.props.backgroundColor\n                    : \"default\",\n                  setColor: (color) => updateColor(color, \"background\"),\n                }\n              : undefined\n          }\n        />\n      </Components.Generic.Menu.Dropdown>\n    </Components.Generic.Menu.Root>\n  );\n}\n\n/**\n * 셀 핸들 드롭다운 메뉴. 기본 TableCellMenu 복제판으로, 색상 버튼만 \"셀\" 라벨이\n * 명시된 LumirCellColorPickerButton으로 교체한다(병합 버튼은 그대로).\n * TableCellButton의 `tableCellMenu` prop으로 주입한다.\n */\nexport function LumirTableCellMenu<\n  I extends InlineContentSchema = DefaultInlineContentSchema,\n  S extends StyleSchema = DefaultStyleSchema,\n>(props: TableCellMenuProps<I, S> & { children?: ReactNode }) {\n  const Components = useComponentsContext()!;\n\n  return (\n    <Components.Generic.Menu.Dropdown\n      className={\"bn-menu-dropdown bn-drag-handle-menu\"}\n    >\n      {props.children || (\n        <>\n          <SplitButton\n            block={props.block}\n            rowIndex={props.rowIndex}\n            colIndex={props.colIndex}\n          />\n          <LumirCellColorPickerButton\n            block={props.block}\n            rowIndex={props.rowIndex}\n            colIndex={props.colIndex}\n          />\n        </>\n      )}\n    </Components.Generic.Menu.Dropdown>\n  );\n}\n","\"use client\";\n\n/**\n * 블록 드래그 핸들(좌측 ⠿) 드롭다운 메뉴의 커스텀 버전.\n *\n * BlockNote 기본 DragHandleMenu(삭제/색/행 제목/열 제목)를 그대로 재현하고,\n * **표 블록일 때 \"표 정렬\"(좌/가운데/우) 항목을 추가**한다.\n * (표 정렬을 셀 grip 메뉴가 아니라 블록 메뉴로 노출하기 위함.)\n *\n * 기본 DragHandleMenu는 children을 주면 기본 항목 대신 children만 렌더하므로,\n * 기본 항목들을 명시적으로 재구성한 뒤 정렬 항목을 덧붙인다.\n * (TableRowHeaderItem/TableColumnHeaderItem은 비-표 블록에서 자동으로 null을 반환한다.)\n */\n\nimport {\n  DragHandleMenu,\n  RemoveBlockItem,\n  BlockColorsItem,\n  TableRowHeaderItem,\n  TableColumnHeaderItem,\n  useComponentsContext,\n  useBlockNoteEditor,\n  useDictionary,\n  type DragHandleMenuProps,\n} from \"@blocknote/react\";\nimport type { TableAlignment } from \"../extensions/TableAlignmentExtension\";\nimport {\n  getTableAlignment,\n  setTableAlignment,\n} from \"../utils/prosemirror-table-utils\";\n\nconst TABLE_ALIGN_ICONS: Record<TableAlignment, JSX.Element> = {\n  left: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.4\">\n      <rect x=\"1.5\" y=\"4\" width=\"7\" height=\"8\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" />\n      <line x1=\"1.5\" y1=\"1.5\" x2=\"14.5\" y2=\"1.5\" />\n      <line x1=\"1.5\" y1=\"14.5\" x2=\"14.5\" y2=\"14.5\" />\n    </svg>\n  ),\n  center: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.4\">\n      <rect x=\"4.5\" y=\"4\" width=\"7\" height=\"8\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" />\n      <line x1=\"1.5\" y1=\"1.5\" x2=\"14.5\" y2=\"1.5\" />\n      <line x1=\"1.5\" y1=\"14.5\" x2=\"14.5\" y2=\"14.5\" />\n    </svg>\n  ),\n  right: (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.4\">\n      <rect x=\"7.5\" y=\"4\" width=\"7\" height=\"8\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" />\n      <line x1=\"1.5\" y1=\"1.5\" x2=\"14.5\" y2=\"1.5\" />\n      <line x1=\"1.5\" y1=\"14.5\" x2=\"14.5\" y2=\"14.5\" />\n    </svg>\n  ),\n};\n\nconst TABLE_ALIGN_LABELS: Record<TableAlignment, string> = {\n  left: \"표 왼쪽 정렬\",\n  center: \"표 가운데 정렬\",\n  right: \"표 오른쪽 정렬\",\n};\n\n/** 표 블록일 때만 \"표 정렬\" 항목(좌/가운데/우)을 렌더. table 노드 attr(tableAlignment)에 적용. */\nfunction TableAlignmentItems(props: { block: any }) {\n  const Components = useComponentsContext()!;\n  const editor = useBlockNoteEditor();\n\n  if (props.block?.type !== \"table\" || !props.block?.id) {\n    return null;\n  }\n\n  const current = getTableAlignment(editor, props.block.id) as TableAlignment;\n\n  return (\n    <>\n      {([\"left\", \"center\", \"right\"] as TableAlignment[]).map((align) => (\n        <Components.Generic.Menu.Item\n          key={`tableAlign-${align}`}\n          icon={TABLE_ALIGN_ICONS[align]}\n          checked={current === align}\n          onClick={() => {\n            setTableAlignment(editor, props.block.id, align);\n            editor.setTextCursorPosition(props.block.id);\n          }}\n        >\n          {TABLE_ALIGN_LABELS[align]}\n        </Components.Generic.Menu.Item>\n      ))}\n    </>\n  );\n}\n\nexport const LumirDragHandleMenu = (props: DragHandleMenuProps) => {\n  const dict = useDictionary();\n\n  return (\n    <DragHandleMenu {...props}>\n      <RemoveBlockItem {...props}>\n        {dict.drag_handle.delete_menuitem}\n      </RemoveBlockItem>\n      <BlockColorsItem {...props}>\n        {dict.drag_handle.colors_menuitem}\n      </BlockColorsItem>\n      <TableRowHeaderItem {...(props as any)}>\n        {dict.drag_handle.header_row_menuitem}\n      </TableRowHeaderItem>\n      <TableColumnHeaderItem {...(props as any)}>\n        {dict.drag_handle.header_column_menuitem}\n      </TableColumnHeaderItem>\n      <TableAlignmentItems block={props.block} />\n    </DragHandleMenu>\n  );\n};\n","\"use client\";\n\n/**\n * Notion 스타일 FOCUS 기반 테이블 grip 컨트롤러.\n *\n * 동작(Notion 레퍼런스):\n *  1) 셀 focus 시 → 그 셀 테두리에 하이라이트(focus 보더) 표시\n *  2) 셀 focus 시 → 상(열)·좌(행)·우(셀) 보더에 \"짧고 두꺼운 gutter 바\"만 표시\n *     (발판 grip은 아직 노출하지 않음)\n *  3) gutter에 hover 시 → 그 자리에 발판 grip(드래그 점 / 드롭다운 화살표) 노출\n *  4) grip 클릭 시 → 드롭다운 메뉴\n *\n * BlockNoteView는 tableHandles={false}로 기본 hover 핸들을 끄고, 이 컨트롤러를\n * 자식으로 렌더한다. grip/메뉴/드래그는 BlockNote 공개 컴포넌트(TableHandle,\n * TableCellButton)와 코어 editor.tableHandles 메서드를 재사용한다.\n *\n * 핵심 사실:\n * - tableHandles={false}는 React 컨트롤러만 끄고 코어 플러그인은 유지된다 →\n *   colDragStart/rowDragStart/dragEnd/메뉴 동작 재사용 가능.\n * - 메뉴 동작(add/remove)은 우리가 넘기는 index prop으로 동작하므로 안전하다.\n * - 드래그만 코어 hover 상태에 의존 → gutter pointer-enter 시 합성 mousemove로\n *   코어 인덱스를 포커스 셀과 동기화한다(TS-private view 미접근).\n * - grip을 클릭/드래그하면 ProseMirror 선택이 셀 밖으로 바뀌어 focused가 즉시\n *   해제될 수 있다 → 상호작용(pointerdown/메뉴/드래그) 동안 recompute를 freeze.\n */\n\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"@blocknote/core\";\nimport {\n  ExtendButton,\n  TableCellButton,\n  TableHandle,\n  useBlockNoteEditor,\n  useEditorContentOrSelectionChange,\n  useExtendButtonsPositioning,\n  useUIPluginState,\n} from \"@blocknote/react\";\nimport { autoUpdate, FloatingPortal } from \"@floating-ui/react\";\nimport type { DragEvent as ReactDragEvent } from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n  useFocusedCellHandlePositioning,\n  useTableCornerPositioning,\n} from \"./hooks/useFocusedCellHandlePositioning\";\nimport { LumirTableCellMenu } from \"./color/LumirColorControls\";\nimport {\n  measureTableForScale,\n  setTableScalePreview,\n  commitTableScale,\n} from \"../extensions/tableScaling\";\nimport { findTableNodePos } from \"../utils/prosemirror-table-utils\";\nimport {\n  TABLE_SCALE_MIN_COL_WIDTH,\n  TABLE_SCALE_MAX,\n  ROW_RESIZE_MIN_HEIGHT,\n} from \"../constants/limits\";\n\ntype FocusedCell = {\n  block: any;\n  rowIndex: number;\n  colIndex: number;\n  cellEl: HTMLElement;\n  tbodyEl: HTMLElement; // 표(tbody) — gutter 표-가장자리 배치 + 열/행 하이라이트 범위용\n  widgetContainer: HTMLElement;\n};\n\n/**\n * 포커스된 셀 중앙에 합성 mousemove를 디스패치해 코어 TableHandles 플러그인의\n * hover 상태(rowIndex/colIndex/referencePosCell/tablePos/...)를 포커스 셀과\n * 동기화한다. 코어 리스너(pmView.dom)가 직접 받아 \"cell\" 분기를 실행한다.\n * 마우스 버튼이 눌리지 않은 상태(pointer-enter)에서만 호출해야 한다.\n */\nfunction syncCoreHoverToFocusedCell(cellEl: HTMLElement) {\n  const r = cellEl.getBoundingClientRect();\n  cellEl.dispatchEvent(\n    new MouseEvent(\"mousemove\", {\n      bubbles: true,\n      cancelable: true,\n      view: window,\n      clientX: r.left + r.width / 2,\n      clientY: r.top + r.height / 2,\n    }),\n  );\n}\n\nexport function LumirTableHandlesController() {\n  const editor = useBlockNoteEditor<\n    BlockSchema,\n    InlineContentSchema,\n    StyleSchema\n  >();\n\n  const [focused, setFocused] = useState<FocusedCell | null>(null);\n  const [menuContainerRef, setMenuContainerRef] =\n    useState<HTMLDivElement | null>(null);\n  // overlay 엘리먼트는 FloatingPortal로 한 틱 늦게 마운트되므로 state-backed ref로\n  // 잡아, 실제 마운트 시점에 사이징 effect가 재실행되도록 한다.\n  const [overlayEl, setOverlayEl] = useState<HTMLDivElement | null>(null);\n\n  // 어떤 gutter의 메뉴가 열려 있는지(활성 파란 발판 + 열림 동안 발판 유지용).\n  const [openMenu, setOpenMenu] = useState<\"col\" | \"row\" | \"cell\" | null>(null);\n\n  // 상호작용 중에는 selection 변경으로 focused가 해제되지 않도록 recompute를 멈춘다.\n  const frozenRef = useRef(false);\n  const menuOpenRef = useRef(false);\n  const draggingRef = useRef(false);\n\n  // 그립 메뉴의 행/열 삭제는 코어 removeRowOrColumn(= view.tablePos/selection 의존)을\n  // 쓰는데, grip 클릭으로 ProseMirror 선택이 표 밖으로 빠지면 그 경로가 실패한다(첫 열/행\n  // 삭제 안 됨). 포커스된 표 id를 에디터에 노출해, LumirEditor의 삭제 래퍼가 선택/hover와\n  // 무관하게 정확한 표를 찾도록 한다. focused는 상호작용 중 freeze되어 유지된다.\n  useEffect(() => {\n    (editor as any).__lumirActiveTableId = focused?.block?.id ?? null;\n  }, [editor, focused]);\n\n  const recompute = useCallback(() => {\n    if (frozenRef.current) {\n      return;\n    }\n\n    const th = editor.tableHandles;\n    const view = editor.prosemirrorView;\n    if (!th || !view) {\n      setFocused(null);\n      return;\n    }\n\n    let cellEl: HTMLElement | null = null;\n    try {\n      const { node } = view.domAtPos(view.state.selection.from);\n      const el =\n        node.nodeType === Node.TEXT_NODE\n          ? (node.parentElement as HTMLElement | null)\n          : (node as HTMLElement);\n      cellEl = (el?.closest?.(\"td, th\") as HTMLElement | null) ?? null;\n    } catch {\n      cellEl = null;\n    }\n    if (!cellEl || !cellEl.isConnected) {\n      setFocused(null);\n      return;\n    }\n\n    const blockId = cellEl.closest(\"[data-id]\")?.getAttribute(\"data-id\");\n    const block = blockId ? editor.getBlock(blockId) : undefined;\n    if (!block || block.type !== \"table\") {\n      setFocused(null);\n      return;\n    }\n\n    const cellSel = th.getCellSelection();\n    if (!cellSel) {\n      setFocused(null);\n      return;\n    }\n\n    const widgetContainer = cellEl\n      .closest(\".tableWrapper\")\n      ?.querySelector(\".table-widgets-container\") as HTMLElement | null;\n    const tbodyEl = cellEl.closest(\"tbody\") as HTMLElement | null;\n    if (!widgetContainer || !tbodyEl) {\n      setFocused(null);\n      return;\n    }\n\n    setFocused({\n      block,\n      rowIndex: cellSel.from.row,\n      colIndex: cellSel.from.col,\n      cellEl,\n      tbodyEl,\n      widgetContainer,\n    });\n  }, [editor]);\n\n  useEditorContentOrSelectionChange(recompute, editor);\n  useEffect(() => {\n    recompute();\n  }, [recompute]);\n\n  // pointerdown으로 freeze한 상호작용이 메뉴/드래그로 이어지지 않은 경우의 안전장치.\n  useEffect(() => {\n    const onUp = () => {\n      requestAnimationFrame(() => {\n        if (!menuOpenRef.current && !draggingRef.current && frozenRef.current) {\n          frozenRef.current = false;\n          recompute();\n        }\n      });\n    };\n    window.addEventListener(\"pointerup\", onUp);\n    return () => window.removeEventListener(\"pointerup\", onUp);\n  }, [recompute]);\n\n  // 활성 보더 오버레이 위치/크기 갱신(스크롤/리사이즈 자동 추적).\n  // openMenu에 따라 범위가 달라진다: 열 메뉴→열 전체, 행 메뉴→행 전체, 그 외→셀.\n  useEffect(() => {\n    const f = focused;\n    if (!f || !overlayEl) {\n      return;\n    }\n    // 보더 두께(2px)의 절반 = PAD(1px). 정수/DPR 반올림으로 거터와 정렬 유지.\n    const PAD = 1;\n    const update = () => {\n      const cr = f.cellEl.getBoundingClientRect();\n      const tr = f.tbodyEl.getBoundingClientRect();\n      const kr = f.widgetContainer.getBoundingClientRect();\n      // 활성 보더 범위: 열 메뉴=열 전체(표 상하), 행 메뉴=행 전체(표 좌우), 그 외=셀\n      const x1 = openMenu === \"row\" ? tr.left : cr.left;\n      const y1 = openMenu === \"col\" ? tr.top : cr.top;\n      const x2 = openMenu === \"row\" ? tr.right : cr.right;\n      const y2 = openMenu === \"col\" ? tr.bottom : cr.bottom;\n      const dpr = window.devicePixelRatio || 1;\n      const rd = (v: number) => Math.round(v * dpr) / dpr;\n      const left = rd(x1 - kr.left) - PAD;\n      const top = rd(y1 - kr.top) - PAD;\n      const right = rd(x2 - kr.left) + PAD;\n      const bottom = rd(y2 - kr.top) + PAD;\n      overlayEl.style.transform = `translate(${left}px, ${top}px)`;\n      overlayEl.style.width = `${right - left}px`;\n      overlayEl.style.height = `${bottom - top}px`;\n    };\n    update();\n    const stopAutoUpdate = autoUpdate(f.cellEl, overlayEl, update);\n    // 다른 행 높이 변경(행 리사이즈 등)으로 표가 reflow되면 focus 셀 자체는 크기가\n    // 안 변해 autoUpdate가 발화하지 않는다. 특히 \"아래\" 행이 커지면 widgetContainer가\n    // 내려가 오버레이가 셀 밖으로 드리프트한다 → tbody 크기를 직접 관찰해 재포지셔닝.\n    const ro = new ResizeObserver(update);\n    ro.observe(f.tbodyEl);\n    return () => {\n      stopAutoUpdate();\n      ro.disconnect();\n    };\n  }, [focused, overlayEl, openMenu]);\n\n  const cellEl = focused?.cellEl ?? null;\n  const tbodyEl = focused?.tbodyEl ?? null;\n  const show = focused !== null;\n  const rowHandle = useFocusedCellHandlePositioning(cellEl, tbodyEl, \"row\", show);\n  const colHandle = useFocusedCellHandlePositioning(cellEl, tbodyEl, \"col\", show);\n  const cellHandle = useFocusedCellHandlePositioning(\n    cellEl,\n    tbodyEl,\n    \"cell\",\n    show,\n  );\n  const th = editor.tableHandles;\n\n  // 표 우측/하단 hover 시 \"행/열 추가\" ExtendButton — 코어 hover 상태 기반(focus와 독립).\n  const coreState = useUIPluginState(\n    editor.tableHandles!.onUpdate.bind(editor.tableHandles!),\n  );\n  const { addOrRemoveColumnsButton, addOrRemoveRowsButton } =\n    useExtendButtonsPositioning(\n      coreState?.showAddOrRemoveColumnsButton || false,\n      coreState?.showAddOrRemoveRowsButton || false,\n      coreState?.referencePosTable || null,\n    );\n  const onStartExtend = useCallback(() => {\n    editor.tableHandles?.freezeHandles();\n  }, [editor]);\n  const onEndExtend = useCallback(() => {\n    editor.tableHandles?.unfreezeHandles();\n  }, [editor]);\n\n  // 표 우하단 코너 스케일 hit-zone(focus 독립, 코어 hover 기반 → 셀 클릭 없이도 노출).\n  const tableCorner = useTableCornerPositioning(\n    coreState?.referencePosTable ?? null,\n    !!coreState?.widgetContainer,\n  );\n\n  // 메뉴 열림/닫힘 핸들러(컨트롤별). 열림 동안 frozen 유지 + openMenu로 활성 표시.\n  const menuHandlers = useMemo(() => {\n    const mk = (kind: \"col\" | \"row\" | \"cell\") => ({\n      freeze: () => {\n        menuOpenRef.current = true;\n        frozenRef.current = true;\n        setOpenMenu(kind);\n        editor.tableHandles?.freezeHandles();\n      },\n      unfreeze: () => {\n        menuOpenRef.current = false;\n        frozenRef.current = false;\n        setOpenMenu(null);\n        editor.tableHandles?.unfreezeHandles();\n        recompute();\n      },\n    });\n    return { col: mk(\"col\"), row: mk(\"row\"), cell: mk(\"cell\") };\n  }, [editor, recompute]);\n\n  const onGutterPointerDown = useCallback(() => {\n    frozenRef.current = true;\n  }, []);\n\n  const onGutterPointerEnter = useCallback(\n    (e: { buttons: number }) => {\n      if (e.buttons === 0 && focused) {\n        syncCoreHoverToFocusedCell(focused.cellEl);\n      }\n    },\n    [focused],\n  );\n\n  const makeDragStart = useCallback(\n    (dir: \"row\" | \"col\") => (e: ReactDragEvent) => {\n      draggingRef.current = true;\n      frozenRef.current = true;\n      if (dir === \"row\") {\n        editor.tableHandles?.rowDragStart(e as unknown as DragEvent);\n      } else {\n        editor.tableHandles?.colDragStart(e as unknown as DragEvent);\n      }\n    },\n    [editor],\n  );\n\n  const onDragEnd = useCallback(() => {\n    editor.tableHandles?.dragEnd();\n    draggingRef.current = false;\n    frozenRef.current = false;\n    recompute();\n  }, [editor, recompute]);\n\n  const noop = useCallback(() => {}, []);\n\n  // 우하단 코너 드래그 → 표 전체 종횡비 고정 스케일.\n  // hit-zone은 에디터 DOM 밖(오버레이)이라 row/column 리사이즈와 충돌하지 않는다.\n  // 대상 표는 코어 hover 상태(coreState.block) → 셀 focus 없이도 동작.\n  // 드래그 동안 freezeHandles로 코어 hover 상태를 고정(마우스가 표를 벗어나도 유지).\n  const onScaleStart = useCallback(\n    (e: React.PointerEvent) => {\n      e.preventDefault();\n      e.stopPropagation();\n      const view = (editor as any).prosemirrorView;\n      const blockId = coreState?.block?.id;\n      if (!view || !blockId) return;\n      const tablePos = findTableNodePos((editor as any)._tiptapEditor, blockId);\n      if (tablePos < 0) return;\n      const base = measureTableForScale(view, tablePos);\n      if (!base) return;\n\n      const minScale = Math.max(\n        TABLE_SCALE_MIN_COL_WIDTH / Math.max(1, Math.min(...base.colWidths)),\n        ROW_RESIZE_MIN_HEIGHT / Math.max(1, Math.min(...base.rowHeights)),\n      );\n      const startX = e.clientX;\n      const startY = e.clientY;\n      let current = base;\n      editor.tableHandles?.freezeHandles();\n\n      const onMove = (me: PointerEvent) => {\n        if (me.buttons === 0) return onUp();\n        const newW = base.origW + (me.clientX - startX);\n        const newH = base.origH + (me.clientY - startY);\n        const scale = Math.min(\n          TABLE_SCALE_MAX,\n          Math.max(minScale, Math.max(newW / base.origW, newH / base.origH)),\n        );\n        current = { ...base, scale };\n        setTableScalePreview(view, current);\n      };\n      const onUp = () => {\n        window.removeEventListener(\"pointermove\", onMove);\n        window.removeEventListener(\"pointerup\", onUp);\n        // ⚠️ rowResizing 교훈: 프리뷰 제거 후 커밋(스타일 충돌 방지).\n        setTableScalePreview(view, null);\n        commitTableScale(view, current);\n        editor.tableHandles?.unfreezeHandles();\n      };\n      window.addEventListener(\"pointermove\", onMove);\n      window.addEventListener(\"pointerup\", onUp);\n    },\n    [editor, coreState],\n  );\n\n  return (\n    <>\n      {/* 메뉴 컨테이너: grip 메뉴가 createPortal로 그려지는 대상. portal 밖에 둔다. */}\n      <div ref={setMenuContainerRef} />\n      {th && focused && menuContainerRef && (\n        <FloatingPortal root={focused.widgetContainer}>\n          {/* focus 보더 오버레이 (셀을 덮는 비클릭 테두리) */}\n          <div ref={setOverlayEl} className=\"lumir-tbl-cell-focus\" />\n\n          {/* 상(top) gutter → 열(column) */}\n          {colHandle.isMounted && (\n            <div\n              ref={colHandle.ref}\n              style={colHandle.style}\n              className={\n                \"lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--col\" +\n                (openMenu === \"col\" ? \" lumir-tbl-gutter-wrap--active\" : \"\")\n              }\n              onPointerEnter={onGutterPointerEnter}\n              onPointerDown={onGutterPointerDown}\n            >\n              <span className=\"lumir-tbl-gutter\" />\n              <div className=\"lumir-tbl-grip\">\n                <TableHandle\n                  editor={editor as any}\n                  orientation=\"column\"\n                  index={focused.colIndex}\n                  block={focused.block}\n                  dragStart={makeDragStart(\"col\")}\n                  dragEnd={onDragEnd}\n                  freezeHandles={menuHandlers.col.freeze}\n                  unfreezeHandles={menuHandlers.col.unfreeze}\n                  menuContainer={menuContainerRef}\n                  showOtherSide={noop}\n                  hideOtherSide={noop}\n                />\n              </div>\n            </div>\n          )}\n\n          {/* 좌(left) gutter → 행(row) */}\n          {rowHandle.isMounted && (\n            <div\n              ref={rowHandle.ref}\n              style={rowHandle.style}\n              className={\n                \"lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--row\" +\n                (openMenu === \"row\" ? \" lumir-tbl-gutter-wrap--active\" : \"\")\n              }\n              onPointerEnter={onGutterPointerEnter}\n              onPointerDown={onGutterPointerDown}\n            >\n              <span className=\"lumir-tbl-gutter\" />\n              <div className=\"lumir-tbl-grip\">\n                <TableHandle\n                  editor={editor as any}\n                  orientation=\"row\"\n                  index={focused.rowIndex}\n                  block={focused.block}\n                  dragStart={makeDragStart(\"row\")}\n                  dragEnd={onDragEnd}\n                  freezeHandles={menuHandlers.row.freeze}\n                  unfreezeHandles={menuHandlers.row.unfreeze}\n                  menuContainer={menuContainerRef}\n                  showOtherSide={noop}\n                  hideOtherSide={noop}\n                />\n              </div>\n            </div>\n          )}\n\n          {/* 우(right) → 파란 선택 앵커(rest) / hover 시 셀(색상·분할) 메뉴.\n              행/열 grip 메뉴가 열려 범위 하이라이트 중에는 노출하지 않는다. */}\n          {cellHandle.isMounted && openMenu !== \"col\" && openMenu !== \"row\" && (\n            <div\n              ref={cellHandle.ref}\n              style={cellHandle.style}\n              className={\n                \"lumir-tbl-gutter-wrap lumir-tbl-gutter-wrap--cell\" +\n                (openMenu === \"cell\" ? \" lumir-tbl-gutter-wrap--active\" : \"\")\n              }\n              onPointerDown={onGutterPointerDown}\n            >\n              <span className=\"lumir-tbl-gutter\" />\n              <div className=\"lumir-tbl-grip\">\n                <TableCellButton\n                  editor={editor as any}\n                  rowIndex={focused.rowIndex}\n                  colIndex={focused.colIndex}\n                  block={focused.block}\n                  tableCellMenu={LumirTableCellMenu as any}\n                  menuContainer={menuContainerRef}\n                  freezeHandles={menuHandlers.cell.freeze}\n                  unfreezeHandles={menuHandlers.cell.unfreeze}\n                />\n              </div>\n            </div>\n          )}\n        </FloatingPortal>\n      )}\n\n      {/* 표 우측/하단 hover 시 행/열 추가(ExtendButton). focus와 무관하게 코어 hover 상태로 표시. */}\n      {th && coreState?.widgetContainer && (\n        <FloatingPortal root={coreState.widgetContainer}>\n          <div ref={addOrRemoveRowsButton.ref} style={addOrRemoveRowsButton.style}>\n            <ExtendButton\n              editor={editor as any}\n              orientation=\"addOrRemoveRows\"\n              block={coreState.block}\n              onMouseDown={onStartExtend}\n              onMouseUp={onEndExtend}\n            />\n          </div>\n          <div\n            ref={addOrRemoveColumnsButton.ref}\n            style={addOrRemoveColumnsButton.style}\n          >\n            <ExtendButton\n              editor={editor as any}\n              orientation=\"addOrRemoveColumns\"\n              block={coreState.block}\n              onMouseDown={onStartExtend}\n              onMouseUp={onEndExtend}\n            />\n          </div>\n\n          {/* 우하단 코너 → 표 전체 종횡비 고정 스케일(투명 hit-zone, focus 독립).\n              hover된 표 기준이라 셀 클릭 없이도 동작. */}\n          {tableCorner.isMounted && (\n            <div\n              ref={tableCorner.ref}\n              style={tableCorner.style}\n              className=\"lumir-tbl-scale-handle\"\n              title=\"표 크기 조절 (종횡비 고정)\"\n              onPointerDown={onScaleStart}\n            />\n          )}\n        </FloatingPortal>\n      )}\n    </>\n  );\n}\n","import {\n  autoUpdate,\n  offset,\n  useFloating,\n  useTransitionStyles,\n} from \"@floating-ui/react\";\nimport type { CSSProperties } from \"react\";\nimport { useEffect, useMemo } from \"react\";\n\n/**\n * Notion 스타일 gutter/grip 배치용 positioner (표 가장자리 기준).\n *\n * Notion 레퍼런스: 열 gutter는 표 **상단** 가장자리(focus 열 중앙), 행 gutter는 표\n * **좌측** 가장자리(focus 행 중앙), 셀 핸들은 셀 **우상단 모서리**에 놓인다.\n * 따라서 셀이 아니라 표(tbody) 가장자리를 기준으로 배치해야 하므로, floating-ui\n * **virtual element**(`{ getBoundingClientRect, contextElement }`)를 reference로 쓴다.\n *\n * - \"col\"  → 표 상단 + 열 중앙 (zero-height ref, placement \"top\")\n * - \"row\"  → 표 좌측 + 행 중앙 (zero-width ref, placement \"left\")\n * - \"cell\" → 셀 우측 보더 (cell rect, placement \"right\")\n *\n * `contextElement: cellEl`을 넣어야 `autoUpdate`가 관찰할 실제 엘리먼트를 가짐\n * (스크롤/리사이즈 시 gutter가 가장자리에 고정 유지됨).\n */\nexport function useFocusedCellHandlePositioning(\n  cellEl: HTMLElement | null,\n  tbodyEl: HTMLElement | null,\n  orientation: \"row\" | \"col\" | \"cell\" | \"corner\",\n  show: boolean,\n): {\n  isMounted: boolean;\n  ref: (node: HTMLElement | null) => void;\n  style: CSSProperties;\n} {\n  const { refs, floatingStyles, context, update } = useFloating({\n    open: show,\n    placement:\n      orientation === \"row\"\n        ? \"left\"\n        : orientation === \"col\"\n          ? \"top\"\n          : orientation === \"corner\"\n            ? \"bottom-start\"\n            : \"right\",\n    // col/row/cell: 가장자리에 14px hit-area 중앙 정렬(-7).\n    // corner: 18px hit-zone이 표 우하단 모서리에 걸치도록 위/좌로 살짝 당김(모서리 hover 자연).\n    middleware: [\n      offset(\n        orientation === \"corner\" ? { mainAxis: -6, crossAxis: -6 } : -7,\n      ),\n    ],\n    whileElementsMounted: autoUpdate,\n  });\n\n  const { isMounted, styles } = useTransitionStyles(context);\n\n  // 다른 행 높이 변경으로 표가 reflow되면(특히 focus 셀보다 아래 행이 커지면) focus 셀\n  // 자체는 크기가 안 변해 autoUpdate가 발화하지 않아 gutter/grip이 어긋난다.\n  // tbody 크기를 직접 관찰해 재포지셔닝한다.\n  useEffect(() => {\n    if (!show || !tbodyEl) {\n      return;\n    }\n    const ro = new ResizeObserver(() => update());\n    ro.observe(tbodyEl);\n    return () => ro.disconnect();\n  }, [show, tbodyEl, update]);\n\n  useEffect(() => {\n    if (!cellEl) {\n      refs.setReference(null);\n      return;\n    }\n    refs.setReference({\n      contextElement: cellEl,\n      getBoundingClientRect: () => {\n        const c = cellEl.getBoundingClientRect();\n        const t = tbodyEl?.getBoundingClientRect() ?? c;\n        if (orientation === \"col\") {\n          // 표 상단 가장자리 + focus 셀의 열 폭(zero-height)\n          return new DOMRect(c.left, t.top, c.width, 0);\n        }\n        if (orientation === \"row\") {\n          // 표 좌측 가장자리 + focus 셀의 행 높이(zero-width)\n          return new DOMRect(t.left, c.top, 0, c.height);\n        }\n        if (orientation === \"corner\") {\n          // 표 우하단 모서리의 영점(zero-size) → placement \"bottom-start\"로 핸들 좌상단을\n          // 모서리에 맞닿게(핸들이 모서리에서 우·하로 뻗음).\n          return new DOMRect(t.right, t.bottom, 0, 0);\n        }\n        // 셀 핸들: 셀 rect (placement \"right\" → 우측 보더)\n        return c;\n      },\n    });\n  }, [cellEl, tbodyEl, orientation, refs]);\n\n  return useMemo(\n    () => ({\n      isMounted,\n      ref: refs.setFloating,\n      // display는 CSS 클래스에서 제어한다(absolute 자식 stacking).\n      style: {\n        ...styles,\n        ...floatingStyles,\n      },\n    }),\n    [floatingStyles, isMounted, refs.setFloating, styles],\n  );\n}\n\n/**\n * 표 우하단 코너 스케일 hit-zone 배치(focus 독립, 코어 hover 상태 기반).\n *\n * focus가 아니라 `coreState.referencePosTable`(hover된 표의 DOMRect)을 기준으로 잡으므로\n * 셀을 클릭하지 않아도 표 hover만으로 코너 hit-zone이 노출된다(ExtendButton과 동일 패턴).\n * 코너에 살짝 안쪽으로 걸치도록 배치해, hover 시 마우스가 표 위에 남아 coreState가 유지된다.\n */\nexport function useTableCornerPositioning(\n  referencePosTable: DOMRect | null,\n  show: boolean,\n): {\n  isMounted: boolean;\n  ref: (node: HTMLElement | null) => void;\n  style: CSSProperties;\n} {\n  const { refs, floatingStyles, context } = useFloating({\n    open: show,\n    placement: \"bottom-start\",\n    // 18px hit-zone을 모서리에서 안쪽(위/좌)으로 당겨 표 위에 걸치게 한다.\n    middleware: [offset({ mainAxis: -12, crossAxis: -12 })],\n    whileElementsMounted: autoUpdate,\n  });\n  const { isMounted, styles } = useTransitionStyles(context);\n\n  useEffect(() => {\n    if (!referencePosTable) {\n      refs.setReference(null);\n      return;\n    }\n    refs.setReference({\n      getBoundingClientRect: () =>\n        new DOMRect(referencePosTable.right, referencePosTable.bottom, 0, 0),\n    });\n  }, [referencePosTable, refs]);\n\n  return useMemo(\n    () => ({\n      isMounted,\n      ref: refs.setFloating,\n      style: { ...styles, ...floatingStyles },\n    }),\n    [floatingStyles, isMounted, refs.setFloating, styles],\n  );\n}\n","import type { DefaultPartialBlock } from \"../types\";\n\n/**\n * BlockNote가 모델링하지 않는 표 셀 속성(verticalAlignment, rowHeight 등)을 블록 JSON에\n * 주입하는 직렬화 헬퍼.\n *\n * BlockNote의 nodeToBlock(contentNodeToTableContent)은 고정된 prop 집합\n * (colspan/rowspan/backgroundColor/textColor/textAlignment)만 cell.props에 담으므로,\n * 커스텀 글로벌 attribute는 블록 JSON에서 누락된다. 이 모듈은 ProseMirror 상태에서\n * 직접 읽어 cell.props에 다시 주입한다(onContentChange 직전).\n *\n * 로드(역방향)는 별도 코드가 필요 없다: BlockNote blockToNode가 `...cell.props`를 노드\n * attr로 그대로 펼치므로, 글로벌 attribute로 등록만 돼 있으면 자동 복원된다.\n */\n\n/**\n * 각 셀 속성의 \"기본값\"(= 주입에서 제외할 값). 기본값이거나 null/undefined면 주입하지 않아\n * 블록 JSON을 불필요하게 키우지 않는다.\n */\nconst CELL_ATTR_DEFAULTS: Record<string, unknown> = {\n  verticalAlignment: \"top\",\n  rowHeight: null,\n};\n\nfunction isMeaningful(attr: string, value: unknown): boolean {\n  if (value === null || value === undefined) {\n    return false;\n  }\n  return value !== CELL_ATTR_DEFAULTS[attr];\n}\n\ntype CellAttrs = {\n  row: number;\n  col: number;\n  props: Record<string, unknown>;\n};\n\n/**\n * ProseMirror 문서에서 각 table 블록 id → 셀별(주입 대상 attr만 담은) props 맵을 구축한다.\n */\nfunction buildTableCellAttrMap(\n  doc: any,\n  attrs: string[],\n): Map<string, CellAttrs[]> {\n  const result = new Map<string, CellAttrs[]>();\n\n  doc.descendants((node: any) => {\n    if (node.type.name === \"blockContainer\") {\n      const blockId = node.attrs?.id;\n      const contentNode = node.firstChild;\n      if (blockId && contentNode?.type.name === \"table\") {\n        const cells: CellAttrs[] = [];\n        let rowIndex = 0;\n        contentNode.forEach((rowNode: any) => {\n          if (rowNode.type.name === \"tableRow\") {\n            let colIndex = 0;\n            rowNode.forEach((cellNode: any) => {\n              const props: Record<string, unknown> = {};\n              for (const attr of attrs) {\n                const value = cellNode.attrs?.[attr];\n                if (isMeaningful(attr, value)) {\n                  props[attr] = value;\n                }\n              }\n              if (Object.keys(props).length > 0) {\n                cells.push({ row: rowIndex, col: colIndex, props });\n              }\n              colIndex++;\n            });\n            rowIndex++;\n          }\n        });\n        if (cells.length > 0) {\n          result.set(blockId, cells);\n        }\n      }\n      return false;\n    }\n  });\n\n  return result;\n}\n\nfunction patchBlocks(\n  blocks: DefaultPartialBlock[],\n  cellAttrMap: Map<string, CellAttrs[]>,\n): DefaultPartialBlock[] {\n  return blocks.map((block) => {\n    if (block.type !== \"table\" || !block.id || !block.content) {\n      if (block.children && block.children.length > 0) {\n        return {\n          ...block,\n          children: patchBlocks(\n            block.children as DefaultPartialBlock[],\n            cellAttrMap,\n          ),\n        };\n      }\n      return block;\n    }\n\n    const cells = cellAttrMap.get(block.id);\n    if (!cells || cells.length === 0) {\n      return block;\n    }\n\n    const content = block.content as any;\n    if (content.type !== \"tableContent\" || !content.rows) {\n      return block;\n    }\n\n    const newRows = content.rows.map((row: any, rowIndex: number) => {\n      const newCells = row.cells.map((cell: any, colIndex: number) => {\n        const match = cells.find(\n          (c) => c.row === rowIndex && c.col === colIndex,\n        );\n        if (!match) {\n          return cell;\n        }\n        if (cell && typeof cell === \"object\" && cell.type === \"tableCell\") {\n          return {\n            ...cell,\n            props: { ...cell.props, ...match.props },\n          };\n        }\n        return cell;\n      });\n      return { ...row, cells: newCells };\n    });\n\n    return {\n      ...block,\n      content: { ...content, rows: newRows },\n    };\n  });\n}\n\n/**\n * blocks 배열의 table 블록을 찾아, ProseMirror 상태에서 읽은 셀별 attr(`attrs`로 지정)을\n * cell.props에 주입한다. 한 번의 문서 순회로 여러 attr을 동시에 처리한다.\n *\n * @param attrs 주입할 셀 노드 attr 이름들(예: [\"verticalAlignment\", \"rowHeight\"])\n */\nexport function injectTableCellAttrs(\n  blocks: DefaultPartialBlock[],\n  editor: any,\n  attrs: string[],\n): DefaultPartialBlock[] {\n  const tiptap = editor?._tiptapEditor;\n  if (!tiptap) {\n    return blocks;\n  }\n\n  const { doc } = tiptap.state;\n  const cellAttrMap = buildTableCellAttrMap(doc, attrs);\n  if (cellAttrMap.size === 0) {\n    return blocks;\n  }\n\n  return patchBlocks(blocks, cellAttrMap);\n}\n\n/**\n * verticalAlignment만 주입하는 하위호환 래퍼. 기존 호출부 호환을 위해 유지.\n * @deprecated 신규 호출부는 injectTableCellAttrs를 직접 사용.\n */\nexport function injectVerticalAlignment(\n  blocks: DefaultPartialBlock[],\n  editor: any,\n): DefaultPartialBlock[] {\n  return injectTableCellAttrs(blocks, editor, [\"verticalAlignment\"]);\n}\n\n/** table 노드(블록 레벨) 보존 대상 attr과 기본값. */\nconst TABLE_BLOCK_ATTR_DEFAULTS: Record<string, unknown> = {\n  tableAlignment: \"left\",\n};\n\n/**\n * ProseMirror 문서에서 table 블록 id → 블록 레벨 attr(tableAlignment 등) 맵을 만든다.\n * 셀이 아니라 table 노드 자체의 attr을 읽는다.\n */\nfunction buildTableBlockAttrMap(\n  doc: any,\n  attrs: string[],\n): Map<string, Record<string, unknown>> {\n  const result = new Map<string, Record<string, unknown>>();\n\n  doc.descendants((node: any) => {\n    if (node.type.name === \"blockContainer\") {\n      const blockId = node.attrs?.id;\n      const contentNode = node.firstChild;\n      if (blockId && contentNode?.type.name === \"table\") {\n        const props: Record<string, unknown> = {};\n        for (const attr of attrs) {\n          const value = contentNode.attrs?.[attr];\n          if (\n            value !== null &&\n            value !== undefined &&\n            value !== TABLE_BLOCK_ATTR_DEFAULTS[attr]\n          ) {\n            props[attr] = value;\n          }\n        }\n        if (Object.keys(props).length > 0) {\n          result.set(blockId, props);\n        }\n      }\n      return false;\n    }\n    return undefined;\n  });\n\n  return result;\n}\n\nfunction patchTableBlockProps(\n  blocks: DefaultPartialBlock[],\n  attrMap: Map<string, Record<string, unknown>>,\n): DefaultPartialBlock[] {\n  return blocks.map((block) => {\n    if (block.type === \"table\" && block.id && attrMap.has(block.id)) {\n      return {\n        ...block,\n        props: { ...(block.props as any), ...attrMap.get(block.id) },\n      } as DefaultPartialBlock;\n    }\n    if (block.children && block.children.length > 0) {\n      return {\n        ...block,\n        children: patchTableBlockProps(\n          block.children as DefaultPartialBlock[],\n          attrMap,\n        ),\n      };\n    }\n    return block;\n  });\n}\n\n/**\n * blocks 배열의 table 블록에, ProseMirror 상태에서 읽은 table 노드 블록 레벨 attr\n * (예: tableAlignment)을 block.props에 주입한다. BlockNote nodeToBlock이 table 노드의\n * 커스텀 attr을 block.props로 추출하지 않으므로 직접 주입한다.\n */\nexport function injectTableBlockAttrs(\n  blocks: DefaultPartialBlock[],\n  editor: any,\n): DefaultPartialBlock[] {\n  const tiptap = editor?._tiptapEditor;\n  if (!tiptap) {\n    return blocks;\n  }\n  const { doc } = tiptap.state;\n  const attrMap = buildTableBlockAttrMap(doc, [\"tableAlignment\"]);\n  if (attrMap.size === 0) {\n    return blocks;\n  }\n  return patchTableBlockProps(blocks, attrMap);\n}\n","import type { DefaultPartialBlock } from \"../types\";\n\n/**\n * 글자 크기(fontSize) 직렬화 호환 레이어.\n *\n * 배경:\n *  - BlockNote는 인라인 `styles` 맵에 스키마에 없는 키가 있으면\n *    `style ${name} not found in styleSchema` 예외를 던진다(blockToNode →\n *    styledTextToNodes). 이미 배포된 구버전 SDK(≤0.4.15, fontSize 스펙 없음)가\n *    `styles.fontSize`가 포함된 JSON을 initialContent로 받으면 에디터 생성이\n *    실패하며 React 트리가 크래시한다.\n *  - 반면 styled-text 객체의 **형제(sibling) 키**는 BlockNote가 읽지 않으므로\n *    조용히 무시된다(크래시 없음).\n *\n * 전략:\n *  - 저장/전달용 JSON(onContentChange 출력)에서는 `styles.fontSize`를 형제 키\n *    `fontSize`로 내린다(lowerFontSize).\n *  - 로드 시(initialContent)에는 형제 키를 다시 `styles.fontSize`로 올려\n *    신버전 에디터가 렌더링하게 한다(liftFontSize).\n *\n * ⚠️ 불변 규칙: 에디터 외부로 블록 JSON을 내보내는 모든 경로는 반드시\n * lowerFontSize를 거쳐야 한다(현재 출력 표면은 onContentChange 유일).\n * styles.fontSize가 저장 JSON에 유출되면 구버전 소비 앱이 크래시한다.\n */\n\n/**\n * 직렬화(저장) 포맷의 styled-text 항목.\n * fontSize는 styles 맵이 아닌 형제 키로 존재한다 — 구버전 SDK(≤0.4.15)는\n * styles만 순회하므로 이 키를 조용히 무시한다(파싱 오류 없음).\n */\nexport type SerializedStyledText = {\n  type: \"text\";\n  text: string;\n  styles: Record<string, unknown>;\n  /** 인라인 글자 크기(예: \"18px\"). 구버전 SDK에서는 무시됨 */\n  fontSize?: string;\n};\n\ntype Dir = \"lower\" | \"lift\";\n\n/**\n * 배열 map 후 모든 요소가 원본과 동일하면 원본 배열 참조를 그대로 반환\n * (불필요한 재생성 방지 — table-vertical-alignment.ts의 불변 패턴 준수)\n */\nfunction mapPreservingRef<T>(arr: T[], fn: (item: T) => T): T[] {\n  let changed = false;\n  const next = arr.map((item) => {\n    const mapped = fn(item);\n    if (mapped !== item) changed = true;\n    return mapped;\n  });\n  return changed ? next : arr;\n}\n\n/** styled-text 한 항목의 fontSize를 styles ↔ 형제 키 간 이동 */\nfunction mapStyledItem(item: any, dir: Dir): any {\n  if (!item || typeof item !== \"object\" || item.type !== \"text\") {\n    return item;\n  }\n\n  if (dir === \"lower\") {\n    const fs = item.styles?.fontSize;\n    if (fs == null) return item;\n    const { fontSize: _removed, ...restStyles } = item.styles;\n    return { ...item, styles: restStyles, fontSize: fs };\n  }\n\n  // lift: 형제 키 → styles.fontSize (둘 다 있으면 형제 키 우선)\n  const fs = item.fontSize;\n  if (fs == null) return item;\n  const { fontSize: _lifted, ...rest } = item;\n  return { ...rest, styles: { ...(item.styles ?? {}), fontSize: fs } };\n}\n\n/** 인라인 콘텐츠 항목 처리 — link는 중첩 content(StyledText[])를 재귀 */\nfunction mapInlineItem(node: any, dir: Dir): any {\n  if (!node || typeof node !== \"object\") return node;\n  if (node.type === \"link\" && Array.isArray(node.content)) {\n    const content = mapPreservingRef(node.content, (n) =>\n      mapStyledItem(n, dir),\n    );\n    return content === node.content ? node : { ...node, content };\n  }\n  return mapStyledItem(node, dir);\n}\n\n/** 테이블 셀 — `{type:\"tableCell\", content}` 객체와 평문 인라인 배열 두 형태 모두 처리 */\nfunction mapCell(cell: any, dir: Dir): any {\n  if (cell && typeof cell === \"object\" && cell.type === \"tableCell\") {\n    if (!Array.isArray(cell.content)) return cell;\n    const content = mapPreservingRef(cell.content, (n) =>\n      mapInlineItem(n, dir),\n    );\n    return content === cell.content ? cell : { ...cell, content };\n  }\n  if (Array.isArray(cell)) {\n    return mapPreservingRef(cell, (n) => mapInlineItem(n, dir));\n  }\n  return cell;\n}\n\nfunction mapBlock(block: any, dir: Dir): any {\n  if (!block || typeof block !== \"object\") return block;\n\n  let next = block;\n  const content = block.content;\n\n  if (Array.isArray(content)) {\n    // paragraph/heading/list 계열 인라인 콘텐츠\n    const mapped = mapPreservingRef(content, (n) => mapInlineItem(n, dir));\n    if (mapped !== content) next = { ...next, content: mapped };\n  } else if (\n    content &&\n    typeof content === \"object\" &&\n    content.type === \"tableContent\" &&\n    Array.isArray(content.rows)\n  ) {\n    const rows = mapPreservingRef(content.rows, (row: any) => {\n      if (!row || !Array.isArray(row.cells)) return row;\n      const cells = mapPreservingRef(row.cells, (cell: any) =>\n        mapCell(cell, dir),\n      );\n      return cells === row.cells ? row : { ...row, cells };\n    });\n    if (rows !== content.rows) next = { ...next, content: { ...content, rows } };\n  }\n  // content === undefined (image/video/htmlPreview/linkPreview) → 그대로 통과\n\n  if (Array.isArray(block.children) && block.children.length > 0) {\n    const children = mapPreservingRef(block.children, (b: any) =>\n      mapBlock(b, dir),\n    );\n    if (children !== block.children) next = { ...next, children };\n  }\n\n  return next;\n}\n\n/**\n * 출력 방향: `styles.fontSize` → 형제 키 `fontSize`.\n * onContentChange로 내보내는 블록 JSON에 적용해 구버전 SDK 크래시를 방지한다.\n * 입력을 변형하지 않는다(불변).\n */\nexport function lowerFontSize(\n  blocks: DefaultPartialBlock[],\n): DefaultPartialBlock[] {\n  return mapPreservingRef(blocks as any[], (b) => mapBlock(b, \"lower\"));\n}\n\n/**\n * 입력 방향: 형제 키 `fontSize` → `styles.fontSize`.\n * initialContent를 에디터에 넘기기 전에 적용해 글자 크기를 복원한다.\n * 입력을 변형하지 않는다(불변).\n */\nexport function liftFontSize(\n  blocks: DefaultPartialBlock[],\n): DefaultPartialBlock[] {\n  return mapPreservingRef(blocks as any[], (b) => mapBlock(b, \"lift\"));\n}\n","import { CellSelection, TableMap, deleteRow } from \"prosemirror-tables\";\nimport { findTableNodePos } from \"./prosemirror-table-utils\";\n\n/**\n * 표(tablePos)의 각 행(tbody > tr)의 렌더 높이(px)를 PM 행 순서대로 측정한다.\n * 열 삭제로 rowspan 병합 셀이 collapse될 때 높이를 보존하기 위해 쓴다.\n * 측정 불가 시 null(→ 호출부는 높이 보존을 건너뜀).\n */\nfunction measureRowHeights(view: any, tablePos: number): number[] | null {\n  try {\n    const at = view?.domAtPos?.(tablePos + 1);\n    let el: any = at?.node;\n    if (el && el.nodeType === 3) el = el.parentElement;\n    const tableEl: HTMLTableElement | null = el?.closest?.(\"table\") ?? null;\n    const body = tableEl?.tBodies?.[0];\n    if (!body) return null;\n    return Array.from(body.rows).map((tr) =>\n      Math.round(tr.getBoundingClientRect().height),\n    );\n  } catch {\n    return null;\n  }\n}\n\n/**\n * 표에서 한 열(col)을 제거한 **새 table 노드를 재구성**해 교체하는 트랜잭션을 만든다.\n *\n * 왜 재구성인가:\n *  prosemirror-tables의 removeColumn(및 그를 쓰는 deleteColumn)은 열의 셀들을 개별\n *  tr.delete로 지운다. 그런데 BlockNote의 tableRow content는 `tableRow+`(셀 1개 이상\n *  필수)라, 어떤 행의 \"유일한 셀\"이 삭제 대상이면 ProseMirror가 행을 비울 수 없어 **빈\n *  셀 껍데기**를 남긴다. 인접 열이 rowspan 병합돼 있으면(예: 2·3열 세로병합 후 1열 삭제)\n *  이 빈 셀이 rowspan 셀과 충돌(collision)하고, tableEditing의 fixTables가 이를 원복해\n *  **열이 삭제되지 않는다**(코어 BlockNote에서도 재현되는 버그).\n *\n * 재구성 규칙:\n *  - col만 차지하는 셀(colspan 1) → 제거.\n *  - col을 가로질러 colspan>1로 걸친 셀 → colspan-1(및 colwidth 보정)로 유지.\n *  - 그 외 셀 → 그대로 유지.\n *  - 모든 원본 셀이 제거돼 **비게 되는 행**은 행 자체를 삭제하고, 그 행을 rowspan으로\n *    덮던 셀들의 rowspan을 줄인다.\n *  모든 셀/표 attr(병합, 배경색, verticalAlignment, rowHeight, tableAlignment 등)은\n *  노드를 그대로 재생성하므로 보존된다.\n *\n * @returns 적용할 transaction, 처리 불가/무변경 시 null.\n */\nfunction buildDeleteColumnTr(\n  state: any,\n  tablePos: number,\n  col: number,\n  rowPx: number[] | null,\n): any {\n  const table = state.doc.nodeAt(tablePos);\n  if (!table || table.type.name !== \"table\") return null;\n  const map = TableMap.get(table);\n  const W = map.width;\n  const H = map.height;\n  if (col < 0 || col >= W || W <= 1) return null;\n\n  // 1. 고유 셀 + 원점(top,left)·span 수집.\n  type CellInfo = {\n    node: any;\n    top: number;\n    left: number;\n    rowspan: number;\n    colspan: number;\n  };\n  const cells: CellInfo[] = [];\n  const seen = new Set<number>();\n  for (let r = 0; r < H; r++) {\n    for (let c = 0; c < W; c++) {\n      const pos = map.map[r * W + c];\n      if (seen.has(pos)) continue;\n      seen.add(pos);\n      const node = table.nodeAt(pos);\n      if (!node) continue;\n      cells.push({\n        node,\n        top: r,\n        left: c,\n        rowspan: node.attrs.rowspan ?? 1,\n        colspan: node.attrs.colspan ?? 1,\n      });\n    }\n  }\n\n  // 2. 행별 \"살아남는 원점 셀 수\" 계산 → 0이면 그 행은 죽는다(삭제).\n  const spansCol = (c: CellInfo) => c.left <= col && col < c.left + c.colspan;\n  const isDropped = (c: CellInfo) => spansCol(c) && c.colspan === 1;\n  const survivingByRow = new Array(H).fill(0);\n  for (const c of cells) if (!isDropped(c)) survivingByRow[c.top]++;\n  const rowDies = survivingByRow.map((n) => n === 0);\n  const newRowIndex: number[] = [];\n  let ni = 0;\n  for (let r = 0; r < H; r++) newRowIndex[r] = rowDies[r] ? -1 : ni++;\n  const newH = ni;\n  if (newH === 0) return null; // 표 전체가 비는 경우는 처리하지 않음.\n\n  // 3. 새 행 배열 구성(원점 셀을 left 순서대로 push).\n  const newRows: any[][] = Array.from({ length: newH }, () => []);\n  for (const c of cells) {\n    if (isDropped(c)) continue;\n    const newColspan = c.colspan - (spansCol(c) ? 1 : 0);\n    let dyingWithin = 0;\n    for (let r = c.top; r < c.top + c.rowspan; r++) if (rowDies[r]) dyingWithin++;\n    const newRowspan = Math.max(1, c.rowspan - dyingWithin);\n\n    const attrs: any = { ...c.node.attrs, colspan: newColspan, rowspan: newRowspan };\n    if (spansCol(c) && Array.isArray(attrs.colwidth) && attrs.colwidth.length) {\n      const cw = attrs.colwidth.slice();\n      cw.splice(col - c.left, 1);\n      attrs.colwidth = cw.some((w: number) => w > 0) ? cw : null;\n    }\n    // 높이 보존: rowspan이 줄어든(=빈 행이 제거된) 셀은 collapse로 키를 잃는다.\n    // 원래 차지하던 행들의 렌더 높이 합을 rowHeight로 박아 시각 높이를 유지한다.\n    if (dyingWithin > 0 && rowPx) {\n      let sum = 0;\n      let ok = true;\n      for (let r = c.top; r < c.top + c.rowspan; r++) {\n        if (typeof rowPx[r] !== \"number\") {\n          ok = false;\n          break;\n        }\n        sum += rowPx[r];\n      }\n      if (ok && sum > 0) attrs.rowHeight = Math.round(sum);\n    }\n    const target = newRowIndex[c.top];\n    if (target >= 0) newRows[target].push(c.node.type.create(attrs, c.node.content));\n  }\n\n  // 4. 새 table 노드 생성 후 교체.\n  const rowType = table.child(0)?.type;\n  if (!rowType) return null;\n  const rowNodes = newRows.map((rowCells) => rowType.create(null, rowCells));\n  const newTable = table.type.create(table.attrs, rowNodes);\n\n  const tr = state.tr;\n  tr.replaceWith(tablePos, tablePos + table.nodeSize, newTable);\n  return tr.docChanged ? tr : null;\n}\n\n/**\n * 행/열 삭제를 대상 표에서 직접(결정적으로) 수행한다.\n *\n * 배경(중요):\n *  - 커스텀 포커스 기반 그립 메뉴가 열리면 ProseMirror 선택이 셀 밖으로 빠질 수 있어,\n *    코어 `tableHandles.removeRowOrColumn`(선택/`view.tablePos` 의존)이 실패한다.\n *  - 인접 열에 rowspan 병합이 있으면 코어 deleteColumn이 첫 열을 못 지운다(위 참고).\n *\n * 삭제 대상 표는 (1) 컨트롤러가 노출한 포커스 표 id → (2) 선택 → (3) view.tablePos\n * 순으로 찾는다. 열 삭제는 위 재구성 방식(buildDeleteColumnTr)으로 병합 셀까지 안전하게\n * 처리하고, 행 삭제는 코어 deleteRow(행 전체를 한 번에 제거 → 빈-셀 문제 없음)를 쓴다.\n *\n * @returns 처리 성공 여부. 표를 못 찾으면 false(호출부가 코어 기본 동작으로 폴백).\n */\nexport function removeFocusedRowOrColumn(\n  editor: any,\n  index: number,\n  direction: \"row\" | \"column\",\n): boolean {\n  const tiptap = editor?._tiptapEditor;\n  if (!tiptap) return false;\n  const { state } = tiptap;\n\n  // 1) 컨트롤러가 노출한 포커스 표 id(가장 권위 있음).\n  let tablePos = -1;\n  const activeId = editor?.__lumirActiveTableId;\n  if (activeId) {\n    const p = findTableNodePos(tiptap, activeId);\n    if (p >= 0 && state.doc.nodeAt(p)?.type.name === \"table\") tablePos = p;\n  }\n  // 2) 현재 선택을 감싸는 table.\n  if (tablePos < 0) {\n    const $from = state.selection.$from;\n    for (let d = $from.depth; d > 0; d--) {\n      if ($from.node(d).type.name === \"table\") {\n        tablePos = $from.before(d);\n        break;\n      }\n    }\n  }\n  // 3) 코어 hover 추적 위치로 폴백.\n  if (tablePos < 0) {\n    const vp = editor?.tableHandles?.view?.tablePos;\n    if (typeof vp === \"number\" && state.doc.nodeAt(vp)?.type.name === \"table\") {\n      tablePos = vp;\n    }\n  }\n  if (tablePos < 0) return false;\n\n  try {\n    if (direction === \"column\") {\n      const rowPx = measureRowHeights(tiptap.view, tablePos);\n      const tr = buildDeleteColumnTr(state, tablePos, index, rowPx);\n      if (!tr) return false;\n      tiptap.view.dispatch(tr);\n      return true;\n    }\n    // 행 삭제: 대상 표 기준 CellSelection 구성 후 코어 deleteRow.\n    const tableInside = state.doc.resolve(tablePos + 1);\n    const rowStart = state.doc.resolve(tableInside.posAtIndex(index) + 1);\n    const cellPos = state.doc.resolve(rowStart.posAtIndex(0));\n    const selState = state.apply(\n      state.tr.setSelection(new CellSelection(cellPos)),\n    );\n    return deleteRow(selState, (tr: any) => tiptap.view.dispatch(tr));\n  } catch {\n    return false;\n  }\n}\n","/**\n * 엑셀/스프레드시트 표 붙여넣기 서식 정규화\n *\n * 엑셀은 셀 서식을 CSS 인라인 스타일(color/background/text-align 등)로 내보내지만,\n * BlockNote 테이블 셀은 자기 속성(data-text-color / data-background-color /\n * data-text-alignment)만 파싱하고 임의의 CSS 색은 무시한다.\n * 이 모듈은 엑셀 HTML을 BlockNote가 인식하는 형태로 변환한다:\n *  - CSS color/background → 가장 가까운 BlockNote 팔레트 색 → data-* 속성\n *  - text-align / align → data-text-alignment\n *  - font-weight/font-style/underline → <strong>/<em>/<u> 래핑 (마크 보존 강화)\n *\n * BlockNote 색은 고정 팔레트(10색)뿐이라 임의 hex는 \"근사치\"로 양자화된다.\n */\n\ntype RGB = [number, number, number];\n\nconst NAMED_COLORS: Record<string, string> = {\n  black: \"#000000\",\n  white: \"#ffffff\",\n  red: \"#ff0000\",\n  green: \"#008000\",\n  blue: \"#0000ff\",\n  yellow: \"#ffff00\",\n  orange: \"#ffa500\",\n  purple: \"#800080\",\n  gray: \"#808080\",\n  grey: \"#808080\",\n};\n\n/** CSS 색 문자열(#hex, rgb(), 일부 named)을 RGB로 파싱. 실패 시 null */\nexport function parseCssColorToRgb(input?: string | null): RGB | null {\n  if (!input) return null;\n  const s = input.trim().toLowerCase();\n  if (!s || s === \"transparent\" || s === \"none\" || s === \"inherit\") return null;\n\n  let m = s.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/);\n  if (m) {\n    let h = m[1];\n    if (h.length === 3)\n      h = h\n        .split(\"\")\n        .map((c) => c + c)\n        .join(\"\");\n    return [\n      parseInt(h.slice(0, 2), 16),\n      parseInt(h.slice(2, 4), 16),\n      parseInt(h.slice(4, 6), 16),\n    ];\n  }\n\n  m = s.match(/^rgba?\\(([^)]+)\\)$/);\n  if (m) {\n    const parts = m[1].split(\",\").map((x) => parseFloat(x.trim()));\n    if (parts.length >= 3 && parts.slice(0, 3).every((n) => !isNaN(n))) {\n      return [parts[0], parts[1], parts[2]] as RGB;\n    }\n    return null;\n  }\n\n  if (NAMED_COLORS[s]) return parseCssColorToRgb(NAMED_COLORS[s]);\n  return null;\n}\n\n/** RGB(0-255) → HSL. h:0-360, s:0-1, l:0-1 */\nfunction rgbToHsl([r, g, b]: RGB): [number, number, number] {\n  r /= 255;\n  g /= 255;\n  b /= 255;\n  const max = Math.max(r, g, b);\n  const min = Math.min(r, g, b);\n  const l = (max + min) / 2;\n  let h = 0;\n  let s = 0;\n  const d = max - min;\n  if (d !== 0) {\n    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n    switch (max) {\n      case r:\n        h = ((g - b) / d + (g < b ? 6 : 0)) * 60;\n        break;\n      case g:\n        h = ((b - r) / d + 2) * 60;\n        break;\n      default:\n        h = ((r - g) / d + 4) * 60;\n        break;\n    }\n  }\n  return [h, s, l];\n}\n\n// 유채색 팔레트의 기준 색상(hue). TEXT_COLORS의 채도 높은 hex 기준.\nconst HUE_REFERENCE: { value: string; hue: number }[] = [\n  { value: \"red\", hue: 0 },\n  { value: \"brown\", hue: 17 },\n  { value: \"orange\", hue: 30 },\n  { value: \"yellow\", hue: 46 },\n  { value: \"green\", hue: 150 },\n  { value: \"blue\", hue: 197 },\n  { value: \"purple\", hue: 262 },\n  { value: \"pink\", hue: 324 },\n];\n\nfunction hueDist(a: number, b: number): number {\n  const d = Math.abs(a - b) % 360;\n  return d > 180 ? 360 - d : d;\n}\n\n/**\n * 임의 색 → BlockNote 팔레트 value (hue 기반 매칭).\n * 무채색(낮은 채도)은 검정류=default, 그 외 회색=gray.\n */\nfunction paletteValueFromRgb(rgb: RGB): string {\n  const [h, s, l] = rgbToHsl(rgb);\n  if (s < 0.15) {\n    // 무채색\n    if (l < 0.35) return \"default\"; // 검정류\n    if (l > 0.85) return \"default\"; // 흰색류(무배경/기본 글자배경)\n    return \"gray\";\n  }\n  let best = \"gray\";\n  let bestDist = Infinity;\n  for (const ref of HUE_REFERENCE) {\n    const d = hueDist(h, ref.hue);\n    if (d < bestDist) {\n      bestDist = d;\n      best = ref.value;\n    }\n  }\n  return best;\n}\n\n/** 임의 색 → 가장 가까운 텍스트 팔레트 value (검정류는 default) */\nexport function nearestTextColorValue(rgb: RGB): string {\n  return paletteValueFromRgb(rgb);\n}\n\n/** 임의 색 → 가장 가까운 배경 팔레트 value (흰색/근사 흰색은 default=무배경) */\nexport function nearestBackgroundColorValue(rgb: RGB): string {\n  const [, s, l] = rgbToHsl(rgb);\n  // 흰색/근사 흰색은 배경 없음으로 간주\n  if (s < 0.12 && l > 0.85) return \"default\";\n  return paletteValueFromRgb(rgb);\n}\n\n/** \"k:v; k2:v2\" → { k: v } */\nfunction parseStyle(style: string): Record<string, string> {\n  const out: Record<string, string> = {};\n  style.split(\";\").forEach((decl) => {\n    const idx = decl.indexOf(\":\");\n    if (idx === -1) return;\n    const k = decl.slice(0, idx).trim().toLowerCase();\n    const v = decl.slice(idx + 1).trim();\n    if (k) out[k] = v;\n  });\n  return out;\n}\n\n/** background 단축 속성에서 색만 추출 시도 */\nfunction colorFromBackgroundShorthand(value: string): RGB | null {\n  if (!value) return null;\n  const direct = parseCssColorToRgb(value);\n  if (direct) return direct;\n  for (const token of value.split(/\\s+/)) {\n    const rgb = parseCssColorToRgb(token);\n    if (rgb) return rgb;\n  }\n  return null;\n}\n\n/** rgba alpha=0 / transparent 여부 */\nfunction isTransparentColor(css?: string | null): boolean {\n  if (!css) return true;\n  const s = css.trim().toLowerCase();\n  if (s === \"transparent\" || s === \"none\") return true;\n  const m = s.match(/^rgba?\\(([^)]+)\\)$/);\n  if (m) {\n    const p = m[1].split(\",\").map((x) => parseFloat(x.trim()));\n    if (p.length >= 4 && p[3] === 0) return true;\n  }\n  return false;\n}\n\n/** CSS text-align 값을 BlockNote 정렬 value로 (left/start는 기본이라 빈 문자열) */\nfunction normalizeAlign(ta?: string | null): string {\n  const v = (ta || \"\").trim().toLowerCase();\n  if (v === \"right\" || v === \"end\") return \"right\";\n  if (v === \"center\") return \"center\";\n  if (v === \"justify\") return \"justify\";\n  return \"\";\n}\n\n/** CSS vertical-align → BlockNote verticalAlignment (top/기본은 null로 스킵) */\nfunction mapVerticalAlign(v?: string | null): \"middle\" | \"bottom\" | null {\n  const s = (v || \"\").trim().toLowerCase();\n  if (s === \"middle\" || s === \"center\") return \"middle\";\n  if (s === \"bottom\") return \"bottom\";\n  return null;\n}\n\n/** font-size 문자열을 px로. computed는 px, inline은 pt/px. 실패 시 null. */\nfunction fontSizeToPx(raw?: string | null): number | null {\n  if (!raw) return null;\n  const m = String(raw).trim().match(/^([\\d.]+)\\s*(px|pt)?$/i);\n  if (!m) return null;\n  const v = parseFloat(m[1]);\n  if (!Number.isFinite(v) || v <= 0) return null;\n  return (m[2] || \"px\").toLowerCase() === \"pt\" ? v * (96 / 72) : v;\n}\n\ninterface CellFormat {\n  bgRgb: RGB | null;\n  bgTransparent: boolean;\n  colorRgb: RGB | null;\n  align: string;\n  bold: boolean;\n  italic: boolean;\n  underline: boolean;\n  /** 셀 글자 크기(px). 본문 기본(14px) 근처면 적용하지 않는다. */\n  fontSizePx: number | null;\n  /** 셀 세로 정렬(middle/bottom만; top/기본은 null). */\n  verticalAlign: \"middle\" | \"bottom\" | null;\n}\n\n/** 셀 서식을 BlockNote data-* 속성/마크로 적용 */\nfunction applyCellFormatting(el: HTMLElement, fmt: CellFormat): void {\n  if (fmt.bgRgb && !fmt.bgTransparent) {\n    const v = nearestBackgroundColorValue(fmt.bgRgb);\n    if (v !== \"default\") el.setAttribute(\"data-background-color\", v);\n  }\n  if (fmt.colorRgb) {\n    const v = nearestTextColorValue(fmt.colorRgb);\n    if (v !== \"default\") el.setAttribute(\"data-text-color\", v);\n  }\n  if ([\"right\", \"center\", \"justify\"].includes(fmt.align)) {\n    el.setAttribute(\"data-text-alignment\", fmt.align);\n  }\n  if ((fmt.bold || fmt.italic || fmt.underline) && el.innerHTML.trim()) {\n    let inner = el.innerHTML;\n    if (fmt.underline) inner = `<u>${inner}</u>`;\n    if (fmt.italic) inner = `<em>${inner}</em>`;\n    if (fmt.bold) inner = `<strong>${inner}</strong>`;\n    el.innerHTML = inner;\n  }\n  // 세로 정렬: VerticalAlignmentExtension parseHTML이 data-vertical-alignment를 읽는다.\n  if (fmt.verticalAlign) {\n    el.setAttribute(\"data-vertical-alignment\", fmt.verticalAlign);\n  }\n  // 글자 크기: 본문 기본(14px)과 1px 넘게 다르면 셀 내용을 FontSize 스타일 span으로 래핑.\n  // (FontSize 스타일은 data-style-type/data-value 형태로 붙여넣기 왕복됨)\n  if (\n    fmt.fontSizePx &&\n    Math.abs(fmt.fontSizePx - 14) > 1 &&\n    el.innerHTML.trim()\n  ) {\n    const v = `${Math.round(fmt.fontSizePx)}px`;\n    el.innerHTML =\n      `<span data-style-type=\"fontSize\" data-value=\"${v}\" style=\"font-size:${v}\">` +\n      el.innerHTML +\n      `</span>`;\n  }\n}\n\n/** getComputedStyle로 셀 서식 읽기 (클래스/<style>/inline 모두 반영) */\nfunction readComputedFormat(el: HTMLElement): CellFormat {\n  const cs = getComputedStyle(el);\n  const fw = cs.fontWeight;\n  const fwNum = parseInt(fw, 10);\n  const decoration = cs.textDecorationLine || cs.textDecoration || \"\";\n  return {\n    bgRgb: parseCssColorToRgb(cs.backgroundColor),\n    bgTransparent: isTransparentColor(cs.backgroundColor),\n    colorRgb: parseCssColorToRgb(cs.color),\n    align: normalizeAlign(cs.textAlign),\n    bold: fw === \"bold\" || fw === \"bolder\" || (!isNaN(fwNum) && fwNum >= 600),\n    italic: (cs.fontStyle || \"\").toLowerCase().includes(\"italic\"),\n    underline: decoration.toLowerCase().includes(\"underline\"),\n    fontSizePx: fontSizeToPx(cs.fontSize),\n    // ⚠️ computed vertical-align은 td 기본값이 \"middle\"이라 셀마다 잘못 붙는다.\n    // 명시적 inline style / valign 속성만 읽는다(기본값 노이즈 방지).\n    verticalAlign: mapVerticalAlign(\n      el.style?.verticalAlign || el.getAttribute(\"valign\"),\n    ),\n  };\n}\n\n/** inline style 속성만으로 셀 서식 읽기 (computed 불가 환경 폴백) */\nfunction readInlineFormat(el: HTMLElement): CellFormat {\n  const sm = parseStyle(el.getAttribute(\"style\") || \"\");\n  const bgRaw = sm[\"background-color\"] || sm[\"background\"] || \"\";\n  const bgRgb =\n    colorFromBackgroundShorthand(bgRaw) ||\n    parseCssColorToRgb(el.getAttribute(\"bgcolor\"));\n  const fw = (sm[\"font-weight\"] || \"\").toLowerCase();\n  const decoration = sm[\"text-decoration\"] || sm[\"text-decoration-line\"] || \"\";\n  return {\n    bgRgb,\n    bgTransparent: !bgRaw && !el.getAttribute(\"bgcolor\"),\n    colorRgb: parseCssColorToRgb(sm[\"color\"]),\n    align: normalizeAlign(sm[\"text-align\"] || el.getAttribute(\"align\")),\n    bold: fw === \"bold\" || fw === \"bolder\" || parseInt(fw, 10) >= 600,\n    italic: (sm[\"font-style\"] || \"\").toLowerCase().includes(\"italic\"),\n    underline: decoration.toLowerCase().includes(\"underline\"),\n    fontSizePx: fontSizeToPx(sm[\"font-size\"]),\n    verticalAlign: mapVerticalAlign(\n      sm[\"vertical-align\"] || el.getAttribute(\"valign\"),\n    ),\n  };\n}\n\n/**\n * 엑셀 표 HTML을 BlockNote가 인식하는 서식으로 정규화한다.\n *\n * 엑셀(데스크톱)은 셀 서식을 인라인 style이 아니라 <style> 블록의 클래스\n * (.xlNN { background:#FFFF00 })로 내보내는 경우가 많다. 따라서 표를 격리된\n * Shadow DOM(오프스크린)에 잠깐 넣고 getComputedStyle로 최종 서식을 읽어\n * 인라인·클래스·mso 형식을 모두 일괄 처리한다. Shadow DOM이라 엑셀 <style>이\n * 페이지로 누출되지 않는다. 실패/비브라우저 환경에서는 inline 파싱으로 폴백.\n */\nexport function normalizeExcelTableHtml(html: string): string {\n  if (!html || typeof DOMParser === \"undefined\") return html;\n  try {\n    const doc = new DOMParser().parseFromString(html, \"text/html\");\n    doc.querySelectorAll(\"script\").forEach((s) => s.remove());\n    if (!doc.querySelector(\"table\")) return html;\n\n    // 우선 경로: 실제 DOM + getComputedStyle\n    if (\n      typeof document !== \"undefined\" &&\n      document.body &&\n      typeof HTMLElement !== \"undefined\" &&\n      typeof HTMLElement.prototype.attachShadow === \"function\"\n    ) {\n      let host: HTMLElement | null = null;\n      try {\n        host = document.createElement(\"div\");\n        host.setAttribute(\"aria-hidden\", \"true\");\n        host.style.cssText =\n          \"position:absolute;left:-99999px;top:0;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;font-size:14px\";\n        const shadow = host.attachShadow({ mode: \"open\" });\n        const styles = Array.from(doc.querySelectorAll(\"style\"))\n          .map((s) => s.outerHTML)\n          .join(\"\");\n        shadow.innerHTML = styles + doc.body.innerHTML;\n        document.body.appendChild(host);\n\n        shadow.querySelectorAll(\"td, th\").forEach((node) => {\n          applyCellFormatting(node as HTMLElement, readComputedFormat(node as HTMLElement));\n        });\n\n        const out = Array.from(shadow.querySelectorAll(\"table\"))\n          .map((t) => t.outerHTML)\n          .join(\"\");\n        return out || html;\n      } finally {\n        if (host && host.parentNode) host.parentNode.removeChild(host);\n      }\n    }\n\n    // 폴백 경로: inline style만 파싱\n    doc.querySelectorAll(\"td, th\").forEach((node) => {\n      applyCellFormatting(node as HTMLElement, readInlineFormat(node as HTMLElement));\n    });\n    return (\n      Array.from(doc.querySelectorAll(\"table\"))\n        .map((t) => t.outerHTML)\n        .join(\"\") || html\n    );\n  } catch {\n    return html;\n  }\n}\n","/**\n * 붙여넣은 표를 **에디터 콘텐츠 너비에 맞춰** 각 열 비율을 유지한 채 축소한다.\n *\n * 배경: Word/docx 등에서 복사한 표는 열 너비(pt/px/%)가 에디터보다 넓어 가로 스크롤이\n * 생긴다. BlockNote 표는 열 너비를 블록 content의 `columnWidths` 배열로 모델링하며,\n * 붙여넣기 HTML 파서는 셀 width를 columnWidths로 읽지 않는다(붙여넣은 표는 기본폭).\n * 그래서 (1) 붙여넣기 전에 HTML에서 열별 원본 너비를 읽어 비율 유지로 maxWidth 이하로\n * 스케일하고, (2) 붙여넣은 뒤 새 표 블록의 `columnWidths`를 그 값으로 설정한다.\n * table-layout:fixed라 columnWidths 합이 에디터 너비 이하면 가로 스크롤이 없다.\n */\n\nconst MIN_COL_PX = 24;\n\nfunction toPx(raw: string | null | undefined, maxWidth: number): number | null {\n  if (!raw) return null;\n  const m = String(raw).trim().match(/^([\\d.]+)\\s*(pt|px|%)?$/i);\n  if (!m) return null;\n  const v = parseFloat(m[1]);\n  if (!Number.isFinite(v) || v <= 0) return null;\n  const unit = (m[2] || \"px\").toLowerCase();\n  if (unit === \"pt\") return v * (96 / 72);\n  if (unit === \"%\") return (v / 100) * maxWidth;\n  return v;\n}\n\nfunction elWidthPx(el: Element, maxWidth: number): number | null {\n  const styleW = (el as HTMLElement).style?.width;\n  return toPx(styleW, maxWidth) ?? toPx(el.getAttribute(\"width\"), maxWidth);\n}\n\n/** 표의 열별 원본 너비(px)를 colgroup<col> 또는 첫 행 셀에서 읽는다. 못 구하면 null. */\nfunction readColumnWidths(table: HTMLTableElement, maxWidth: number): number[] | null {\n  const colEls = table.querySelector(\"colgroup\")?.querySelectorAll(\"col\");\n  if (colEls && colEls.length > 0) {\n    const widths: number[] = [];\n    let ok = true;\n    colEls.forEach((c) => {\n      const span = parseInt(c.getAttribute(\"span\") || \"1\", 10) || 1;\n      const w = elWidthPx(c, maxWidth);\n      if (w == null) ok = false;\n      for (let i = 0; i < span; i++) widths.push(w ?? 0);\n    });\n    if (ok && widths.length > 0) return widths;\n  }\n  const firstRow = table.querySelector(\"tr\");\n  if (!firstRow) return null;\n  const widths: number[] = [];\n  let ok = true;\n  Array.from(firstRow.children).forEach((cell) => {\n    if (cell.tagName !== \"TD\" && cell.tagName !== \"TH\") return;\n    const span = parseInt(cell.getAttribute(\"colspan\") || \"1\", 10) || 1;\n    const w = elWidthPx(cell, maxWidth);\n    if (w == null) ok = false;\n    const per = (w ?? 0) / span;\n    for (let i = 0; i < span; i++) widths.push(per);\n  });\n  return ok && widths.length > 0 ? widths : null;\n}\n\n/** 원본 너비를 maxWidth 이하로 비율 유지 스케일한 px 배열. 넘지 않으면 원본 유지. */\nfunction fitWidths(widths: number[], maxWidth: number): number[] {\n  const total = widths.reduce((a, b) => a + b, 0);\n  if (total <= 0) return widths;\n  const scale = total > maxWidth ? maxWidth / total : 1;\n  return widths.map((w) => Math.max(MIN_COL_PX, Math.round(w * scale)));\n}\n\n/**\n * 붙여넣기 HTML의 각 `<table>`에 대해, 에디터 너비(maxWidth)에 맞춘 columnWidths 배열을\n * HTML 등장 순서로 반환한다. 너비를 못 읽은 표는 null.\n */\nexport function computeFittedColumnWidthsPerTable(\n  html: string,\n  maxWidth: number,\n): (number[] | null)[] {\n  if (!html || typeof DOMParser === \"undefined\" || !(maxWidth > 0)) return [];\n  let doc: Document;\n  try {\n    doc = new DOMParser().parseFromString(html, \"text/html\");\n  } catch {\n    return [];\n  }\n  return Array.from(doc.querySelectorAll(\"table\")).map((t) => {\n    const widths = readColumnWidths(t as HTMLTableElement, maxWidth);\n    return widths ? fitWidths(widths, maxWidth) : null;\n  });\n}\n\n/** blocks(및 children)에서 table 블록을 등장 순서로 모은다. */\nexport function collectTableBlocks(blocks: any[]): any[] {\n  const out: any[] = [];\n  const walk = (bs: any[]) => {\n    for (const b of bs) {\n      if (b?.type === \"table\") out.push(b);\n      if (b?.children?.length) walk(b.children);\n    }\n  };\n  walk(blocks || []);\n  return out;\n}\n\n/**\n * 붙여넣기 직후 호출: beforeIds에 없던 새 표 블록들에 perTable[i] columnWidths를 적용한다.\n * 열 개수가 맞을 때만 적용(불일치 시 건너뜀).\n */\nexport function applyFittedWidthsToNewTables(\n  editor: any,\n  beforeIds: Set<string>,\n  perTable: (number[] | null)[],\n): void {\n  if (!editor || perTable.length === 0) return;\n  const newTables = collectTableBlocks(editor.document).filter(\n    (b) => !beforeIds.has(b.id),\n  );\n  newTables.forEach((tb, i) => {\n    const widths = perTable[i];\n    const current = tb?.content?.columnWidths;\n    if (\n      widths &&\n      Array.isArray(current) &&\n      current.length === widths.length\n    ) {\n      try {\n        editor.updateBlock(tb, {\n          type: \"table\",\n          content: { ...tb.content, columnWidths: widths },\n        });\n      } catch {\n        /* 단일 표 실패가 전체 붙여넣기를 막지 않도록 무시 */\n      }\n    }\n  });\n}\n"],"mappings":"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","names":["useEffect","useMemo","useCallback","useState","useRef","useBlockNoteEditor","useComponentsContext","createReactBlockSpec","endpoint","createReactBlockSpec","useState","useCallback","useRef","useEffect","Fragment","jsx","jsxs","tr","Plugin","PluginKey","PluginKey","Plugin","createStronglyTypedTiptapNode","useState","useRef","useCallback","useEffect","jsx","jsxs","createReactBlockSpec","handleMouseMove","useState","useEffect","useRef","jsx","jsxs","jsx","useCallback","jsx","jsxs","useCallback","handleMouseDown","useCallback","jsx","useCallback","handleMouseDown","useCallback","jsx","iconMap","titleMap","useCallback","handleMouseDown","useCallback","jsx","iconMap","titleMap","useCallback","handleMouseDown","useCallback","jsx","useCallback","handleMouseDown","useState","useEffect","useRef","useCallback","jsx","jsxs","useState","useRef","useCallback","useEffect","handleMouseDown","useState","useEffect","useRef","useCallback","jsx","jsxs","useState","useRef","useEffect","useCallback","handleMouseDown","useState","useEffect","useRef","useCallback","jsx","jsxs","useState","useRef","useEffect","useCallback","handleMouseDown","useCallback","jsx","useCallback","handleMouseDown","useCallback","useRef","Fragment","jsx","jsxs","useRef","useCallback","handleMouseDown","useState","useEffect","useRef","useCallback","jsx","jsxs","useState","useRef","useEffect","handleMouseDown","useCallback","found","Fragment","jsx","jsxs","useRef","useState","useEffect","Extension","Plugin","PluginKey","Decoration","DecorationSet","PluginKey","Plugin","offset","DecorationSet","Decoration","Plugin","PluginKey","Decoration","DecorationSet","TableMap","Plugin","PluginKey","tr","Extension","Extension","Plugin","PluginKey","Decoration","DecorationSet","Extension","TableMap","Extension","Plugin","PluginKey","Decoration","DecorationSet","TextSelection","schema","BlockTypeSelect","useCallback","jsx","useCallback","useCallback","useMemo","useBlockNoteEditor","useComponentsContext","useSelectedBlocks","jsx","jsxs","icons","useComponentsContext","useBlockNoteEditor","useSelectedBlocks","useMemo","useCallback","useCallback","useMemo","useBlockNoteEditor","useComponentsContext","useSelectedBlocks","jsx","jsxs","icons","tooltips","useComponentsContext","useBlockNoteEditor","useSelectedBlocks","useMemo","useCallback","useBlockNoteEditor","useComponentsContext","useSelectedBlocks","useCallback","useEffect","useMemo","useState","jsx","jsxs","DEFAULT_LABEL","toLabel","FontSizeButton","useComponentsContext","useBlockNoteEditor","useSelectedBlocks","useState","useEffect","useCallback","useMemo","mapTableCell","useBlockNoteEditor","useComponentsContext","useEditorContentOrSelectionChange","useSelectedBlocks","useCallback","useMemo","useRef","useState","Fragment","jsx","jsxs","useComponentsContext","useBlockNoteEditor","useSelectedBlocks","useState","useEditorContentOrSelectionChange","useCallback","useMemo","useRef","mapTableCell","jsx","jsxs","BlockTypeSelect","FontSizeButton","useComponentsContext","useBlockNoteEditor","useDictionary","Fragment","jsx","jsxs","useComponentsContext","useBlockNoteEditor","useDictionary","useBlockNoteEditor","useEditorContentOrSelectionChange","autoUpdate","useCallback","useEffect","useMemo","useRef","useState","useEffect","useMemo","Fragment","jsx","jsxs","useBlockNoteEditor","useState","useRef","useEffect","useCallback","th","cellEl","tbodyEl","useEditorContentOrSelectionChange","autoUpdate","useMemo","isMeaningful","fs","CellSelection","TableMap","TableMap","CellSelection","widths","ok","Fragment","jsx","jsxs","useBlockNoteEditor","useComponentsContext","jsx","jsxs","useState","useRef","useCallback","useMemo","useEffect","editor","Fragment"]}