<script setup>
import {
  escapeHtml,
  getEditableCaretOffset,
  markdownInlineToHtml,
  readEditableTextFromElement,
  setEditableCaretOffset
} from '../../lib/markdown-inline.js'
import { parseSlashInput } from '../../lib/markdown-slash-commands.js'

const props = defineProps({
  /** 인라인 마크다운 원문 */
  modelValue: {
    type: String,
    default: ''
  },
  /**
   * Enter 동작
   * - split-paragraph: 문단 분리
   * - insert-below: 블록 아래 새 줄 삽입
   * - none: 기본(제목 등 단일 줄 blur)
   * - focus-next: Enter 시 blur 없이 enter-advance 발생
   * - multiline: Enter 기본 동작(코드·콜아웃 본문 등)
   */
  enterMode: {
    type: String,
    default: 'none'
  },
  /**
   * ↑↓ 이동 범위
   * - document: 렌더러 전체 블록
   * - parent: 가장 가까운 data-editable-scope 컨테이너 안
   */
  navigationScope: {
    type: String,
    default: 'document'
  },
  /** @deprecated enterMode 사용 */
  splitOnEnter: {
    type: Boolean,
    default: false
  },
  /** 원본 마크다운 줄 번호(0-based) */
  sourceLine: {
    type: Number,
    default: null
  },
  /** 루트 요소 태그 */
  tag: {
    type: String,
    default: 'div'
  },
  /** 추가 클래스 */
  blockClass: {
    type: String,
    default: ''
  },
  /** 마크다운 원문 한 줄(백스페이스 시 표시) */
  rawLine: {
    type: String,
    default: ''
  },
  /** 맨 앞 백스페이스로 원문 토글 허용 */
  allowRawToggle: {
    type: Boolean,
    default: false
  },
  /** 슬래시 메뉴가 열려 키보드 탐색 중인지 */
  slashMenuActive: {
    type: Boolean,
    default: false
  },
  /** ESC 등으로 이 줄의 슬래시 메뉴를 닫은 상태(문자 `/`는 유지) */
  slashCommandSuppressed: {
    type: Boolean,
    default: false
  },
  /** true면 인라인 마크다운 변환 없이 줄바꿈 유지(코드 블록 등) */
  plainText: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits([
  'commit',
  'input',
  'split',
  'insert-below',
  'delete-line',
  'merge-with-previous',
  'raw-mode',
  'slash-update',
  'slash-end',
  'slash-apply',
  'enter-advance',
  'leave-block'
])

const rootRef = ref(null)
const isFocused = ref(false)
const suppressBlurCommit = ref(false)
const splitLock = ref(false)
/** 조합 중 Enter 후 compositionend에서 분리할지 */
const pendingSplitAfterComposition = ref(false)
/** 조합 종료 직후 중복 Enter를 무시할 기준 시각 */
const suppressComposedEnterUntil = ref(0)
const showingRaw = ref(false)

/** @returns {string} Enter 동작 모드 */
const resolvedEnterMode = computed(() => {
  if (props.enterMode !== 'none') {
    return props.enterMode
  }

  return props.splitOnEnter ? 'split-paragraph' : 'none'
})

/**
 * modelValue를 편집용 HTML로 변환한다.
 * @returns {string} HTML
 */
const toEditorHtml = () => markdownInlineToHtml(props.modelValue.replace(/\n/g, ' '))

/**
 * plainText 모드용 편집 HTML을 만든다.
 * @param {string} value - 본문
 * @returns {string} HTML
 */
const plainTextToEditorHtml = (value) => escapeHtml(String(value ?? '')).replace(/\n/g, '<br>')

/**
 * 편집 영역 HTML을 동기화한다.
 * @returns {void}
 */
const syncEditorHtml = () => {
  if (!rootRef.value || isFocused.value) {
    return
  }

  if (showingRaw.value && props.rawLine) {
    rootRef.value.textContent = props.rawLine
    return
  }

  if (props.plainText) {
    rootRef.value.innerHTML = plainTextToEditorHtml(props.modelValue)
    return
  }

  rootRef.value.innerHTML = toEditorHtml()
}

watch(() => [props.modelValue, props.rawLine], () => {
  if (!isFocused.value) {
    showingRaw.value = false
    syncEditorHtml()
    return
  }

  if (showingRaw.value) {
    if (props.rawLine && rootRef.value && rootRef.value.textContent !== props.rawLine) {
      rootRef.value.textContent = props.rawLine
    }

    return
  }

  const current = readEditorValue()

  if (current !== props.modelValue) {
    if (props.plainText) {
      rootRef.value.innerHTML = plainTextToEditorHtml(props.modelValue)
    } else {
      rootRef.value.innerHTML = toEditorHtml()
    }
  }
})

onMounted(() => {
  syncEditorHtml()
})

/**
 * 포커스 시 편집 상태를 표시한다.
 * @returns {void}
 */
const onFocus = () => {
  isFocused.value = true
  syncSlashState()
}

/**
 * 슬래시 명령 입력 상태를 부모에 알린다.
 * @returns {void}
 */
const syncSlashState = () => {
  if (props.sourceLine === null || showingRaw.value) {
    emit('slash-end')
    return
  }

  if (props.slashCommandSuppressed) {
    return
  }

  const parsed = parseSlashInput(readEditorValue())

  if (!parsed) {
    emit('slash-end', { sourceLine: props.sourceLine })
    return
  }

  emit('slash-update', {
    sourceLine: props.sourceLine,
    query: parsed.query,
    value: parsed.raw
  })
}

/**
 * 입력 시 슬래시 상태를 갱신한다.
 * @returns {void}
 */
const onEditorInput = () => {
  syncSlashState()
}

/**
 * blur 시 마크다운을 반영한다.
 * @returns {void}
 */
/**
 * 편집 영역 값을 읽는다.
 * @returns {string} 텍스트
 */
const readEditorValue = () => {
  if (!rootRef.value) {
    return ''
  }

  if (showingRaw.value) {
    return rootRef.value.textContent ?? ''
  }

  return readEditableTextFromElement(rootRef.value)
}

const onBlur = () => {
  isFocused.value = false

  if (!rootRef.value || suppressBlurCommit.value || splitLock.value) {
    return
  }

  const nextValue = readEditorValue()
  const changed = showingRaw.value
    ? nextValue !== props.rawLine
    : nextValue !== props.modelValue

  if (changed) {
    emit('commit', showingRaw.value ? { value: nextValue, raw: true } : nextValue)
  }

  if (showingRaw.value) {
    showingRaw.value = false
    notifyRawMode(false)
  }

  syncEditorHtml()
}

/**
 * 붙여넣기 시 서식 없는 텍스트만 삽입한다.
 * @param {ClipboardEvent} event - 붙여넣기 이벤트
 * @returns {void}
 */
const onPaste = (event) => {
  event.preventDefault()
  const text = event.clipboardData?.getData('text/plain') || ''

  if (!text || !import.meta.client) {
    return
  }

  document.execCommand('insertText', false, text)
}

/**
 * 커서가 블록 맨 앞/맨 끝인지 확인한다.
 * @param {'start'|'end'} edge - 확인할 위치
 * @returns {boolean}
 */
const isCaretAtEdge = (edge) => {
  if (!import.meta.client || !rootRef.value) {
    return false
  }

  const selection = window.getSelection()

  if (!selection || selection.rangeCount === 0) {
    return false
  }

  const range = selection.getRangeAt(0)

  if (!rootRef.value.contains(range.commonAncestorContainer)) {
    return false
  }

  const testRange = document.createRange()
  testRange.selectNodeContents(rootRef.value)

  if (edge === 'start') {
    testRange.setEnd(range.startContainer, range.startOffset)
    return testRange.toString().length === 0
  }

  testRange.setStart(range.endContainer, range.endOffset)
  return testRange.toString().length === 0
}

/**
 * 커서의 텍스트 오프셋을 반환한다.
 * @returns {number} 루트 기준 오프셋
 */
const getCaretTextOffset = () => {
  if (!import.meta.client || !rootRef.value) {
    return 0
  }

  const selection = window.getSelection()

  if (!selection || selection.rangeCount === 0) {
    return 0
  }

  const range = selection.getRangeAt(0)

  if (!rootRef.value.contains(range.commonAncestorContainer)) {
    return 0
  }

  const testRange = document.createRange()
  testRange.selectNodeContents(rootRef.value)
  testRange.setEnd(range.startContainer, range.startOffset)

  return testRange.toString().length
}

/**
 * 원문 모드 접두사 길이를 반환한다.
 * @returns {number} 접두사 길이
 */
const getRawPrefixLength = () => {
  if (!showingRaw.value || !rootRef.value) {
    return 0
  }

  const text = rootRef.value.textContent ?? ''
  const match = text.match(/^(\s*(?:#{1,6}\s+|>\s*|[-*+]\s+|\d+\.\s+))/)

  return match ? match[1].length : 0
}

/**
 * 편집 영역에 접힙지 않은 텍스트 선택이 있는지 확인한다.
 * @returns {boolean}
 */
const hasNonCollapsedSelection = () => {
  if (!import.meta.client || !rootRef.value) {
    return false
  }

  const selection = window.getSelection()

  if (!selection || selection.rangeCount === 0) {
    return false
  }

  const range = selection.getRangeAt(0)

  if (!rootRef.value.contains(range.commonAncestorContainer)) {
    return false
  }

  return !range.collapsed
}

/**
 * 커서가 줄의 논리적 맨 앞인지 확인한다(원문 접두사 직후 포함).
 * @returns {boolean}
 */
const isCaretAtLogicalStart = () => {
  if (isCaretAtEdge('start')) {
    return true
  }

  const prefixLength = getRawPrefixLength()

  if (!prefixLength) {
    return false
  }

  return getCaretTextOffset() <= prefixLength
}

/**
 * 커서가 줄의 논리적 맨 끝인지 확인한다.
 * @returns {boolean}
 */
const isCaretAtLogicalEnd = () => isCaretAtEdge('end')

/**
 * 커서가 위치한 시각 줄 정보를 반환한다.
 * @returns {{ column: number, lineIndex: number, lines: string[], isFirstLine: boolean, isLastLine: boolean }}
 */
const getCaretLineContext = () => {
  const fallback = {
    column: 0,
    lineIndex: 0,
    lines: [''],
    isFirstLine: true,
    isLastLine: true
  }

  if (!import.meta.client || !rootRef.value) {
    return fallback
  }

  const selection = window.getSelection()

  if (!selection || selection.rangeCount === 0) {
    return fallback
  }

  const range = selection.getRangeAt(0)

  if (!rootRef.value.contains(range.commonAncestorContainer)) {
    return fallback
  }

  const full = readEditorValue()
  const offset = getEditableCaretOffset(rootRef.value, range)
  const lines = full.length ? full.split('\n') : ['']
  const before = full.slice(0, offset)
  const lineIndex = before.split('\n').length - 1
  const lineStart = before.lastIndexOf('\n') + 1
  const column = offset - lineStart

  return {
    column,
    lineIndex,
    lines,
    isFirstLine: lineIndex === 0,
    isLastLine: lineIndex >= lines.length - 1
  }
}

/**
 * 텍스트 줄 배열에서 지정 줄·열의 오프셋을 계산한다.
 * @param {string[]} lines - 줄 목록
 * @param {number} lineIndex - 줄 인덱스
 * @param {number} column - 열
 * @returns {number} 오프셋
 */
const getOffsetForLineColumn = (lines, lineIndex, column) => {
  let offset = 0

  for (let index = 0; index < lineIndex; index += 1) {
    offset += (lines[index] ?? '').length + 1
  }

  const line = lines[lineIndex] ?? ''

  return offset + Math.min(Math.max(column, 0), line.length)
}

/**
 * 인접 편집 블록으로 포커스를 옮긴다.
 * @param {number} direction - -1 이전, 1 다음
 * @param {number} column - 유지할 열(column 모드)
 * @param {'column'|'block-start'|'block-end'} [caretMode='column'] - 커서 배치
 * @returns {void}
 */
/**
 * ↑↓ 이동 대상 편집 요소 목록을 반환한다.
 * @returns {HTMLElement[]}
 */
const getNavigationElements = () => {
  if (!import.meta.client || !rootRef.value) {
    return []
  }

  const root = props.navigationScope === 'parent'
    ? rootRef.value.closest('[data-editable-scope]')
    : rootRef.value.closest('.content-markdown-renderer')

  if (!root) {
    return []
  }

  return [...root.querySelectorAll('[data-source-line]')]
    .filter((element) => element.getAttribute('contenteditable') === 'true'
      || element.getAttribute('data-preview-card-block') === 'true')
    .sort((left, right) => Number(left.dataset.sourceLine) - Number(right.dataset.sourceLine))
}

const navigateToAdjacentBlock = (direction, column, caretMode = 'column') => {
  if (!import.meta.client || props.sourceLine === null) {
    return
  }

  const elements = getNavigationElements()

  if (!elements.length) {
    return
  }

  const currentIndex = elements.findIndex(
    (element) => Number(element.dataset.sourceLine) === props.sourceLine
  )

  if (currentIndex < 0) {
    return
  }

  const target = elements[currentIndex + direction]

  if (!target) {
    if (['insert-below', 'multiline'].includes(resolvedEnterMode.value) && direction === 1) {
      emit('insert-below', buildInsertBelowPayload())
    }

    return
  }

  if (props.navigationScope === 'parent') {
    emit('leave-block', { direction })
  }

  target.focus({ preventScroll: true })

  const scopedCaretMode = props.navigationScope === 'parent'
    ? (direction === 1 ? 'block-start' : 'block-end')
    : caretMode

  const targetText = readEditableTextFromElement(target)
  const targetLines = targetText.length ? targetText.split('\n') : ['']
  let targetLineIndex = direction === -1 ? targetLines.length - 1 : 0
  let targetColumn = column

  if (scopedCaretMode === 'block-start') {
    targetLineIndex = 0
    targetColumn = 0
  } else if (scopedCaretMode === 'block-end') {
    targetLineIndex = targetLines.length - 1
    targetColumn = (targetLines[targetLineIndex] ?? '').length
  } else {
    targetColumn = Math.min(column, (targetLines[targetLineIndex] ?? '').length)
  }

  const targetOffset = getOffsetForLineColumn(targetLines, targetLineIndex, targetColumn)

  setEditableCaretOffset(target, targetOffset)
}

/**
 * 현재 블록의 맨 앞·맨 끝으로 커서를 옮긴다.
 * @param {'start'|'end'} edge - 위치
 * @returns {void}
 */
const setCaretAtBlockEdge = (edge) => {
  if (!rootRef.value) {
    return
  }

  if (edge === 'start') {
    if (showingRaw.value) {
      placeCursorAfterPrefix()
      return
    }

    setEditableCaretOffset(rootRef.value, 0)
    return
  }

  const full = readEditorValue()

  setEditableCaretOffset(rootRef.value, full.length)
}

/**
 * 커서 기준 앞·뒤 텍스트를 읽는다.
 * @returns {{ before: string, after: string }}
 */
const readCaretSplit = () => {
  if (!import.meta.client || !rootRef.value) {
    return { before: props.modelValue, after: '' }
  }

  const selection = window.getSelection()

  if (!selection || selection.rangeCount === 0) {
    return { before: props.modelValue, after: '' }
  }

  const range = selection.getRangeAt(0)

  if (!rootRef.value.contains(range.commonAncestorContainer)) {
    return { before: props.modelValue, after: '' }
  }

  const full = readEditorValue()
  const offset = getEditableCaretOffset(rootRef.value, range)

  return {
    before: full.slice(0, offset),
    after: full.slice(offset)
  }
}

/**
 * insert-below 이벤트 페이로드를 만든다.
 * @returns {{ value: string, raw: boolean, before: string, after: string, caretAtStart: boolean, caretAtEnd: boolean }}
 */
const buildInsertBelowPayload = () => {
  const { before, after } = readCaretSplit()

  return {
    value: readEditorValue(),
    raw: showingRaw.value,
    before,
    after,
    caretAtStart: isCaretAtLogicalStart(),
    caretAtEnd: isCaretAtLogicalEnd()
  }
}

/**
 * 커서 위치에서 문단을 분리한다.
 * @returns {void}
 */
const splitAtCaret = () => {
  emit('split', readCaretSplit())
}

/**
 * Enter 처리(분리·아래 삽입)를 한 번만 실행한다.
 * @param {'split'|'insert-below'} action - 동작
 * @returns {void}
 */
const scheduleEnterAction = (action) => {
  if (splitLock.value) {
    return
  }

  splitLock.value = true
  suppressBlurCommit.value = true

  if (action === 'split') {
    splitAtCaret()
  } else {
    emit('insert-below', buildInsertBelowPayload())
  }

  nextTick(() => {
    splitLock.value = false
    window.setTimeout(() => {
      suppressBlurCommit.value = false
    }, 180)
  })
}

/**
 * 원문 모드 상태를 부모에 알린다.
 * @param {boolean} active - 활성 여부
 * @returns {void}
 */
const notifyRawMode = (active) => {
  if (props.sourceLine === null) {
    return
  }

  emit('raw-mode', { sourceLine: props.sourceLine, active })
}

/**
 * 원문 모드 커서를 접두사 뒤에 둔다.
 * @returns {void}
 */
const placeCursorAfterPrefix = () => {
  if (!rootRef.value) {
    return
  }

  const text = rootRef.value.textContent ?? ''
  const match = text.match(/^(\s*(?:#{1,6}\s+|>\s*|[-*+]\s+|\d+\.\s+))/)

  if (!match) {
    return
  }

  const offset = match[1].length
  const walker = document.createTreeWalker(rootRef.value, NodeFilter.SHOW_TEXT)
  let node = walker.nextNode()
  let remaining = offset

  while (node) {
    const length = node.textContent?.length ?? 0

    if (remaining <= length) {
      const range = document.createRange()
      range.setStart(node, remaining)
      range.collapse(true)
      const selection = window.getSelection()
      selection?.removeAllRanges()
      selection?.addRange(range)
      return
    }

    remaining -= length
    node = walker.nextNode()
  }
}

/**
 * 키보드 입력 처리
 * @param {KeyboardEvent} event - 키보드 이벤트
 * @returns {void}
 */
const onKeydown = (event) => {
  const isCmdE = (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'e'

  if (isCmdE) {
    return
  }

  if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key.toLowerCase() === 'k') {
    if (props.sourceLine !== null) {
      const lineContext = getCaretLineContext()
      const sourceLine = resolvedEnterMode.value === 'multiline'
        ? props.sourceLine + lineContext.lineIndex
        : props.sourceLine

      event.preventDefault()
      event.stopPropagation()
      emit('delete-line', sourceLine)
    }

    return
  }

  if (
    event.key === 'Backspace'
    && !hasNonCollapsedSelection()
    && isCaretAtLogicalStart()
    && !showingRaw.value
    && props.sourceLine !== null
    && readEditorValue().trim()
  ) {
    event.preventDefault()
    event.stopPropagation()
    emit('merge-with-previous', buildInsertBelowPayload())
    return
  }

  if (
    event.key === 'Backspace'
    && !hasNonCollapsedSelection()
    && isCaretAtLogicalStart()
    && !showingRaw.value
    && props.sourceLine !== null
    && !readEditorValue().trim()
  ) {
    const lineContext = getCaretLineContext()
    const sourceLine = resolvedEnterMode.value === 'multiline'
      ? props.sourceLine + lineContext.lineIndex
      : props.sourceLine

    event.preventDefault()
    event.stopPropagation()
    emit('delete-line', sourceLine)
    return
  }

  if (
    event.key === 'Backspace'
    && !hasNonCollapsedSelection()
    && props.allowRawToggle
    && props.rawLine
    && !showingRaw.value
    && isCaretAtEdge('start')
  ) {
    event.preventDefault()
    event.stopPropagation()
    showingRaw.value = true
    notifyRawMode(true)
    nextTick(() => {
      if (!rootRef.value) {
        return
      }

      rootRef.value.textContent = props.rawLine
      placeCursorAfterPrefix()
    })
    return
  }

  const hasCommandModifier = event.metaKey || event.ctrlKey

  if (hasCommandModifier && !event.shiftKey && !event.altKey) {
    if (event.key === 'ArrowLeft') {
      event.preventDefault()
      event.stopPropagation()
      setCaretAtBlockEdge('start')
      return
    }

    if (event.key === 'ArrowRight') {
      event.preventDefault()
      event.stopPropagation()
      setCaretAtBlockEdge('end')
      return
    }
  }

  if (props.slashMenuActive) {
    return
  }

  if (!hasCommandModifier && !event.altKey && (event.key === 'ArrowUp' || event.key === 'ArrowDown')) {
    if (event.isComposing || event.keyCode === 229) {
      return
    }

    const lineContext = getCaretLineContext()
    const direction = event.key === 'ArrowUp' ? -1 : 1
    const isMultilineEditor = resolvedEnterMode.value === 'multiline'

    if (isMultilineEditor) {
      if (direction === -1 && !lineContext.isFirstLine) {
        return
      }

      if (direction === 1 && !lineContext.isLastLine) {
        return
      }
    }

    event.preventDefault()
    event.stopPropagation()
    navigateToAdjacentBlock(direction, lineContext.column)
    return
  }

  if (!hasCommandModifier && !event.altKey && event.key === 'ArrowLeft' && isCaretAtLogicalStart()) {
    event.preventDefault()
    event.stopPropagation()
    navigateToAdjacentBlock(-1, 0, 'block-end')
    return
  }

  if (!hasCommandModifier && !event.altKey && event.key === 'ArrowRight' && isCaretAtLogicalEnd()) {
    event.preventDefault()
    event.stopPropagation()
    navigateToAdjacentBlock(1, 0, 'block-start')
    return
  }

  const enterMode = showingRaw.value ? 'insert-below' : resolvedEnterMode.value

  if (
    event.key === 'Enter'
    && !event.shiftKey
    && suppressComposedEnterUntil.value
    && Date.now() < suppressComposedEnterUntil.value
  ) {
    event.preventDefault()
    event.stopPropagation()
    return
  }

  if (event.key === 'Enter' && !event.shiftKey && parseSlashInput(readEditorValue())) {
    event.preventDefault()
    event.stopPropagation()
    event.stopImmediatePropagation?.()
    emit('slash-apply', {
      sourceLine: props.sourceLine,
      value: readEditorValue()
    })
    return
  }

  if (event.key === 'Enter' && (enterMode === 'split-paragraph' || enterMode === 'insert-below')) {
    event.preventDefault()
    event.stopPropagation()

    if (event.isComposing || event.keyCode === 229) {
      pendingSplitAfterComposition.value = true
      suppressComposedEnterUntil.value = Date.now() + 240
      return
    }

    pendingSplitAfterComposition.value = false
    scheduleEnterAction(enterMode === 'split-paragraph' ? 'split' : 'insert-below')
    return
  }

  if (event.key === 'Enter' && enterMode === 'focus-next') {
    event.preventDefault()
    event.stopPropagation()

    if (event.isComposing || event.keyCode === 229) {
      return
    }

    emit('enter-advance')
    return
  }

  if (event.key === 'Enter' && enterMode === 'none') {
    event.preventDefault()
    event.stopPropagation()
    rootRef.value?.blur()
  }
}

/**
 * 한글 등 IME 조합 종료 후 Enter 처리
 * @returns {void}
 */
const onCompositionEnd = () => {
  if (!pendingSplitAfterComposition.value) {
    return
  }

  pendingSplitAfterComposition.value = false
  suppressComposedEnterUntil.value = Date.now() + 240
  const enterMode = showingRaw.value ? 'insert-below' : resolvedEnterMode.value

  if (enterMode !== 'split-paragraph' && enterMode !== 'insert-below') {
    return
  }

  nextTick(() => {
    scheduleEnterAction(enterMode === 'split-paragraph' ? 'split' : 'insert-below')
  })
}

/**
 * 외부에서 포커스·커서를 둔다.
 * @param {'start'|'end'} position - 커서 위치
 * @returns {void}
 */
const focusEditor = (position = 'end') => {
  if (!import.meta.client || !rootRef.value) {
    return
  }

  rootRef.value.focus({ preventScroll: true })
  const textLength = readEditorValue().length
  const offset = position === 'start' ? 0 : textLength
  setEditableCaretOffset(rootRef.value, offset)
}

defineExpose({ focusEditor, readEditorValue })
</script>

<template>
  <component
    :is="tag"
    ref="rootRef"
    class="content-markdown-editable-inline min-w-0 outline-none"
    :class="[
      blockClass,
      isFocused ? 'content-markdown-editable-inline--focused' : 'content-markdown-editable-inline--idle',
      showingRaw ? 'content-markdown-editable-inline--raw' : ''
    ]"
    :data-source-line="sourceLine ?? undefined"
    contenteditable="true"
    spellcheck="true"
    @focus="onFocus"
    @blur="onBlur"
    @input="onEditorInput"
    @paste="onPaste"
    @keydown="onKeydown"
    @compositionend="onCompositionEnd"
  />
</template>

<style scoped>
.content-markdown-editable-inline {
  user-select: text;
}

.content-markdown-editable-inline--idle {
  cursor: text;
  border-radius: 4px;
}

.content-markdown-editable-inline--focused {
  background-color: transparent;
}

.content-markdown-editable-inline--raw {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 0.92em;
  color: #5c6570;
}

.content-markdown-editable-inline :deep(a) {
  color: var(--site-accent);
  text-decoration: underline;
  text-underline-offset: 4px;
}

.content-markdown-editable-inline :deep(code) {
  border-radius: 0.25rem;
  background: #252525;
  padding: 0.125rem 0.375rem;
  font-size: 0.9em;
  color: #fff;
}
</style>
