{"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","detectFormat","marked","sanitizeHtml","useControllableState","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","ContentRenderer"],"mappings":"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","file":"chunk-HIOMHITU.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n  contentRef: RefObject<HTMLDivElement | null>;\n  exec: (command: string, arg?: string) => void;\n  insertText: (text: string) => void;\n  wrapSelection: (before: string, after: string) => void;\n  outputFormat: \"html\" | \"markdown\";\n  lineSpacing: number;\n  placeholder?: string;\n  /** Merged render extensions (global + instance). */\n  extensions: RenderExtension[];\n  /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n  showSource: boolean;\n  /** Toggle/set the raw-source view. */\n  setShowSource: (showSource: boolean) => void;\n  /** Initial HTML content to load into the editor on mount */\n  _initialHtml: string;\n  /** @internal Called by EditorContent on input events */\n  _onInput: () => void;\n  /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n  _sourceValue: string;\n  /** @internal Commit an edit made directly in the source textarea. */\n  _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n  const ctx = useContext(EditorContext);\n  if (!ctx) {\n    throw new Error(\"useEditor must be used within an <Editor> component\");\n  }\n  return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n  className,\n  icon = \"</>\",\n  title = \"Source\",\n  activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n  const { showSource, setShowSource } = useEditor();\n\n  return (\n    <button\n      type=\"button\"\n      onClick={() => setShowSource(!showSource)}\n      title={showSource ? activeTitle : title}\n      aria-pressed={showSource}\n      data-react-fancy-editor-source-toggle=\"\"\n      className={cn(\n        \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n        showSource\n          ? \"bg-zinc-200 dark:bg-zinc-700\"\n          : \"hover:bg-zinc-100 dark:hover:bg-zinc-800\",\n        className,\n      )}\n    >\n      {icon}\n    </button>\n  );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n  { icon: \"B\", label: \"Bold\", command: \"bold\" },\n  { icon: \"I\", label: \"Italic\", command: \"italic\" },\n  { icon: \"U\", label: \"Underline\", command: \"underline\" },\n  { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n  actions = DEFAULT_ACTIONS,\n  onAction,\n  children,\n  className,\n  showSourceToggle = true,\n}: EditorToolbarProps) {\n  const { exec, showSource } = useEditor();\n\n  return (\n    <div\n      data-react-fancy-editor-toolbar=\"\"\n      className={cn(\n        \"flex shrink-0 items-center gap-0.5 border-b border-zinc-200 px-2 py-1.5 dark:border-zinc-700\",\n        className,\n      )}\n    >\n      {children ?? (\n        <>\n          {actions.map((action) => (\n            <button\n              key={`${action.command}-${action.commandArg ?? \"\"}`}\n              type=\"button\"\n              // Rich-text commands don't apply to the raw-source textarea.\n              disabled={showSource}\n              onClick={() => {\n                if (onAction) {\n                  onAction(action.command);\n                } else {\n                  exec(action.command, action.commandArg);\n                }\n              }}\n              title={action.label}\n              className={cn(\n                \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n                action.active\n                  ? \"bg-zinc-200 dark:bg-zinc-700\"\n                  : \"hover:bg-zinc-100 dark:hover:bg-zinc-800\",\n                showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n              )}\n            >\n              {action.icon}\n            </button>\n          ))}\n          {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n        </>\n      )}\n    </div>\n  );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n  return (\n    <span\n      data-react-fancy-editor-toolbar-separator=\"\"\n      className=\"mx-1 h-5 w-px bg-zinc-200 dark:bg-zinc-700\"\n    />\n  );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight, sourceClassName }: EditorContentProps) {\n  const {\n    contentRef,\n    lineSpacing,\n    placeholder,\n    extensions,\n    outputFormat,\n    showSource,\n    _initialHtml,\n    _onInput,\n    _sourceValue,\n    _onSourceInput,\n  } = useEditor();\n  // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n  // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n  // `value`, while still never re-seeding on plain keystrokes — typing leaves\n  // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n  const seededHtml = useRef<string | null>(null);\n\n  useEffect(() => {\n    // Source view replaces the contentEditable in the tree, so it unmounts.\n    // Forget what we seeded — the div that returns is a fresh, empty one and\n    // must always be re-seeded, even if `value` was left untouched.\n    if (showSource) {\n      seededHtml.current = null;\n      return;\n    }\n    const el = contentRef.current;\n    if (el && seededHtml.current !== _initialHtml) {\n      el.innerHTML = _initialHtml;\n      seededHtml.current = _initialHtml;\n    }\n  }, [contentRef, _initialHtml, showSource]);\n\n  const extClasses = useMemo(\n    () => extensionEditorClasses(extensions),\n    [extensions],\n  );\n\n  if (showSource) {\n    // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n    // markdown/plain un-highlighted), built on the shared fancy-file-commons\n    // primitives. It's a DIRECT child of the flex-column editor root (no\n    // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n    // fill the editor body when height-constrained and grow with content\n    // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n    return (\n      <CodeView\n        value={_sourceValue}\n        onChange={_onSourceInput}\n        language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n        placeholder={placeholder}\n        maxHeight={maxHeight}\n        className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n      />\n    );\n  }\n\n  return (\n    <div\n      ref={contentRef}\n      contentEditable\n      suppressContentEditableWarning\n      data-react-fancy-editor-content=\"\"\n      data-placeholder={placeholder}\n      onInput={_onInput}\n      style={{\n        lineHeight: lineSpacing,\n        maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n      }}\n      className={cn(\n        \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n        \"focus:outline-none\",\n        proseClasses,\n        extClasses,\n        \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n        className,\n      )}\n    />\n  );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { htmlToMarkdown, detectFormat } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\nfunction toHtml(\n  value: string,\n  outputFormat: \"html\" | \"markdown\",\n  unsafe: boolean,\n  valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): string {\n  if (!value) return \"\";\n  const raw = (() => {\n    // An explicit valueFormat is authoritative — the sniff misclassifies\n    // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n    // collapsing it into an innerHTML wall of text (#10).\n    const format =\n      valueFormat !== \"auto\"\n        ? valueFormat\n        : outputFormat === \"html\"\n          ? \"html\"\n          : detectFormat(value);\n    if (format === \"html\") return value;\n    return (marked.parse(value, { async: false }) as string).trim();\n  })();\n  return unsafe ? raw : sanitizeHtml(raw);\n}\n\nfunction EditorRoot({\n  children,\n  className,\n  value: controlledValue,\n  defaultValue = \"\",\n  onChange,\n  outputFormat = \"html\",\n  valueFormat = \"auto\",\n  lineSpacing = 1.6,\n  placeholder,\n  mode: modeProp,\n  extensions: instanceExtensions,\n  unsafe = false,\n  showSource: showSourceProp,\n  defaultShowSource = false,\n  onShowSourceChange,\n}: EditorProps) {\n  const contentRef = useRef<HTMLDivElement | null>(null);\n  const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n  const [showSource, setShowSource] = useControllableState(\n    showSourceProp,\n    defaultShowSource,\n    onShowSourceChange,\n  );\n  // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n  const mode = useFieldMode(modeProp);\n  const isView = mode === \"view\";\n\n  // The value this editor last emitted through `onChange`. An incoming `value`\n  // equal to it is our own keystroke echoing back; anything else came from\n  // OUTSIDE — an async load, a host reset, a form repopulating.\n  const lastEmitted = useRef<string | null>(null);\n\n  // Bumped only on an external `value` change, so the seed memo below recomputes\n  // for those and stays put while the user types. Without this, `value` was\n  // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n  // value arriving after mount was silently dropped and the editor stayed blank\n  // forever (#15).\n  const [externalSeed, setExternalSeed] = useState(0);\n  useEffect(() => {\n    if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n  }, [value]);\n\n  const initialHtml = useMemo(\n    () => toHtml(value, outputFormat, unsafe, valueFormat),\n    // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n    // and when returning from the raw-source view, which may have rewritten the\n    // value out from under the rich-text surface. `value` itself stays out of\n    // the deps so typing never re-seeds (which would fight the caret);\n    // `externalSeed` covers the externally-driven case instead.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n  );\n\n  const extensions = useMemo(\n    () => mergeExtensions(instanceExtensions),\n    [instanceExtensions],\n  );\n\n  const getOutputValue = useCallback(\n    (html: string): string => {\n      return outputFormat === \"markdown\" ? htmlToMarkdown(html) : html;\n    },\n    [outputFormat],\n  );\n\n  const handleInput = useCallback(() => {\n    const el = contentRef.current;\n    if (!el) return;\n    const next = getOutputValue(el.innerHTML);\n    // Record what we emitted BEFORE publishing it, so the value coming back in\n    // is recognised as our own and does not trigger a re-seed mid-keystroke.\n    lastEmitted.current = next;\n    setValue(next);\n  }, [setValue, getOutputValue]);\n\n  const exec = useCallback(\n    (command: string, arg?: string) => {\n      const el = contentRef.current;\n      if (!el) return;\n      el.focus();\n      document.execCommand(command, false, arg);\n      handleInput();\n    },\n    [handleInput],\n  );\n\n  const insertText = useCallback(\n    (text: string) => {\n      const el = contentRef.current;\n      if (!el) return;\n      el.focus();\n      document.execCommand(\"insertText\", false, text);\n      handleInput();\n    },\n    [handleInput],\n  );\n\n  // The source textarea edits `value` directly — it is already the canonical\n  // source in `outputFormat` (html or markdown), so no conversion round-trip.\n  const handleSourceInput = useCallback(\n    (next: string) => {\n      // Also an internal emission — record it so typing in the source view is\n      // not mistaken for an external change on every keystroke.\n      lastEmitted.current = next;\n      setValue(next);\n    },\n    [setValue],\n  );\n\n  const wrapSelection = useCallback(\n    (before: string, after: string) => {\n      const sel = window.getSelection();\n      if (!sel || sel.rangeCount === 0) return;\n      const range = sel.getRangeAt(0);\n      const selectedText = range.toString();\n      const el = contentRef.current;\n      if (!el) return;\n      el.focus();\n      document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n      handleInput();\n    },\n    [handleInput],\n  );\n\n  const contextValue = {\n    contentRef,\n    exec,\n    insertText,\n    wrapSelection,\n    outputFormat,\n    lineSpacing,\n    placeholder,\n    extensions,\n    showSource,\n    setShowSource,\n    _initialHtml: initialHtml,\n    _onInput: handleInput,\n    _sourceValue: value,\n    _onSourceInput: handleSourceInput,\n  };\n\n  // View mode — render the value read-only through ContentRenderer, matching the\n  // editor's own format (markdown/html), instead of the editable toolbar+content\n  // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n  if (isView) {\n    return (\n      <div\n        data-react-fancy-editor=\"\"\n        data-mode=\"view\"\n        className={cn(\n          \"overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900\",\n          className,\n        )}\n      >\n        <ContentRenderer\n          value={value}\n          format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n          lineSpacing={lineSpacing}\n          extensions={instanceExtensions}\n          unsafe={unsafe}\n          className=\"px-4 py-3\"\n        />\n      </div>\n    );\n  }\n\n  return (\n    <EditorContext.Provider value={contextValue}>\n      <div\n        data-react-fancy-editor=\"\"\n        data-mode=\"edit\"\n        className={cn(\n          \"flex flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900\",\n          className,\n        )}\n      >\n        {children}\n      </div>\n    </EditorContext.Provider>\n  );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n  Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n  Toolbar: ToolbarWithSeparator,\n  Content: EditorContent,\n  SourceToggle: EditorSourceToggle,\n});\n"]}