@duskmoon-dev/el-markdown-input 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/css.ts", "../../src/highlight.ts", "../../src/upload.ts", "../../src/autocomplete.ts", "../../src/status-bar.ts", "../../src/element.ts", "../../src/register.ts"],
3
+ "sources": ["../../src/sanitize-schema.ts", "../../src/render.ts", "../../src/element.ts", "../../src/css.ts", "../../src/highlight.ts", "../../src/upload.ts", "../../src/autocomplete.ts", "../../src/pairs.ts", "../../src/status-bar.ts", "../../src/register.ts"],
4
4
  "sourcesContent": [
5
- "import { css } from '@duskmoon-dev/el-base';\n\n/**\n * Shadow DOM stylesheet for el-dm-markdown-input.\n *\n * Exposes --md-* custom properties as the external theming API.\n * Each --md-* variable falls back to the corresponding --color-* token\n * from @duskmoon-dev/core so the element automatically adopts the\n * active design-system theme without any consumer configuration.\n */\nexport const elementStyles = css`\n /* ── Custom property defaults with design-system fallbacks ─────────── */\n :host {\n --md-border: var(--color-outline, #d0d7de);\n --md-border-focus: var(--color-primary, #0969da);\n --md-bg: var(--color-surface, #ffffff);\n --md-bg-toolbar: var(--color-surface-variant, #f6f8fa);\n --md-bg-hover: var(--color-surface-container, #eaeef2);\n --md-text: var(--color-on-surface, #1f2328);\n --md-text-muted: var(--color-on-surface-variant, #656d76);\n --md-accent: var(--color-primary, #0969da);\n --md-radius: 6px;\n --md-upload-bar: var(--color-primary, #0969da);\n --md-color-warning: var(--color-warning, #d97706);\n --md-color-error: var(--color-error, #dc2626);\n\n display: block;\n position: relative; /* establishes containing block for the ac-dropdown portal */\n font-family: inherit;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n /* Dark-mode overrides (activated by [dark] attribute on host) */\n :host([dark]) {\n --md-border: #30363d;\n --md-border-focus: #58a6ff;\n --md-bg: #0d1117;\n --md-bg-toolbar: #161b22;\n --md-bg-hover: #21262d;\n --md-text: #e6edf3;\n --md-text-muted: #8b949e;\n --md-accent: #58a6ff;\n --md-upload-bar: #58a6ff;\n --md-color-warning: #f59e0b;\n --md-color-error: #fca5a5;\n }\n\n /* ── Editor chrome ──────────────────────────────────────────────────── */\n .editor {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--md-border);\n border-radius: var(--md-radius);\n background: var(--md-bg);\n color: var(--md-text);\n overflow: hidden;\n }\n\n .editor:focus-within {\n border-color: var(--md-border-focus);\n outline: 2px solid var(--md-border-focus);\n outline-offset: -1px;\n }\n\n /* ── Toolbar / tab bar ──────────────────────────────────────────────── */\n .toolbar {\n display: flex;\n gap: 0;\n background: var(--md-bg-toolbar);\n border-bottom: 1px solid var(--md-border);\n padding: 0 0.5rem;\n }\n\n .tab-btn {\n padding: 0.5rem 0.875rem;\n border: none;\n background: transparent;\n color: var(--md-text-muted);\n font-family: inherit;\n font-size: 0.875rem;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n transition:\n color 150ms ease,\n border-color 150ms ease;\n }\n\n .tab-btn:hover {\n color: var(--md-text);\n background: var(--md-bg-hover);\n }\n\n .tab-btn[aria-selected='true'] {\n color: var(--md-text);\n border-bottom-color: var(--md-accent);\n }\n\n .tab-btn:focus-visible {\n outline: 2px solid var(--md-accent);\n outline-offset: -2px;\n border-radius: 3px;\n }\n\n /* ── Write area (backdrop + textarea overlay) ───────────────────────── */\n .write-area {\n position: relative;\n min-height: 12rem;\n flex: 1;\n }\n\n /*\n * Backdrop: renders syntax-highlighted HTML behind the transparent textarea.\n * Must share IDENTICAL font metrics with the textarea to stay pixel-aligned.\n */\n .backdrop {\n position: absolute;\n inset: 0;\n pointer-events: none;\n /*\n * Use overflow: auto (not overflow: hidden) so the backdrop reserves\n * the same scrollbar gutter as the textarea when content overflows.\n * Without this, the textarea scrollbar narrows its text area but the\n * backdrop stays full-width lines wrap at different points causing\n * the cursor to appear misaligned with the highlighted text.\n */\n overflow: auto;\n scrollbar-width: none; /* Firefox */\n border: none;\n background: transparent;\n /*\n * Do NOT put white-space: pre-wrap here. The backdrop div contains a\n * backdrop-content child, and the HTML template has whitespace text\n * nodes (newline + indent) between them. With pre-wrap on the parent\n * those text nodes render as a visible leading newline, shifting all\n * content down by one line and misaligning the cursor vertically.\n * pre-wrap lives on .backdrop-content instead.\n */\n\n font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n font-size: 0.875rem;\n line-height: 1.6;\n padding: 0.75rem;\n color: var(--md-text);\n }\n\n .backdrop::-webkit-scrollbar {\n display: none; /* Chrome / Safari */\n }\n\n .backdrop-content {\n display: block;\n white-space: pre-wrap;\n word-wrap: break-word;\n overflow-wrap: break-word;\n /* Prism token colours are injected via a separate <style id=\"prism-theme\"> */\n }\n\n textarea {\n position: relative;\n display: block;\n width: 100%;\n min-height: 12rem;\n border: none;\n outline: none;\n resize: vertical;\n background: transparent;\n color: transparent;\n caret-color: var(--md-text);\n box-sizing: border-box;\n\n /* MUST match .backdrop exactly */\n font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n font-size: 0.875rem;\n line-height: 1.6;\n padding: 0.75rem;\n white-space: pre-wrap;\n word-wrap: break-word;\n overflow-wrap: break-word;\n }\n\n textarea::placeholder {\n color: var(--md-text-muted);\n }\n\n textarea:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n /* ── Preview panel ──────────────────────────────────────────────────── */\n .preview-body {\n padding: 0.75rem;\n min-height: 12rem;\n overflow-y: auto;\n color: var(--md-text);\n /* .markdown-body styles come from @duskmoon-dev/core via the element */\n }\n\n /* ── Status bar ─────────────────────────────────────────────────────── */\n .status-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.375rem 0.75rem;\n border-top: 1px solid var(--md-border);\n background: var(--md-bg-toolbar);\n font-size: 0.75rem;\n color: var(--md-text-muted);\n gap: 0.5rem;\n }\n\n .attach-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.5rem;\n border: none;\n background: transparent;\n color: var(--md-text-muted);\n font-family: inherit;\n font-size: 0.75rem;\n cursor: pointer;\n border-radius: 4px;\n transition:\n color 150ms ease,\n background 150ms ease;\n }\n\n .attach-btn:hover {\n color: var(--md-text);\n background: var(--md-bg-hover);\n }\n\n .attach-btn:focus-visible {\n outline: 2px solid var(--md-accent);\n outline-offset: 1px;\n }\n\n .status-bar-count {\n margin-left: auto;\n white-space: nowrap;\n }\n\n .status-bar-count .warning {\n color: var(--md-color-warning);\n }\n\n .status-bar-count .error {\n color: var(--md-color-error);\n }\n\n .file-input {\n display: none;\n }\n\n /* ── Autocomplete dropdown ──────────────────────────────────────────── */\n /*\n * The dropdown is a direct child of :host (outside .editor) so it is not\n * clipped by .editor's overflow: hidden. :host has position: relative which\n * establishes the containing block for this absolute positioning.\n */\n .ac-dropdown {\n position: absolute;\n z-index: 100;\n left: 0.75rem;\n /* Align to bottom of the editor chrome; the editor fills 100% of :host height */\n bottom: calc(var(--md-status-bar-height, 2rem) + 4px);\n min-width: 16rem;\n max-width: 28rem;\n max-height: 16rem;\n overflow-y: auto;\n margin: 0;\n padding: 0.25rem 0;\n list-style: none;\n background: var(--md-bg);\n border: 1px solid var(--md-border);\n border-radius: var(--md-radius);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n }\n\n .ac-item {\n display: flex;\n flex-direction: column;\n padding: 0.5rem 0.75rem;\n cursor: pointer;\n transition: background 100ms ease;\n }\n\n .ac-item:hover,\n .ac-item[aria-selected='true'] {\n background: var(--md-bg-hover);\n }\n\n .ac-item-label {\n font-size: 0.875rem;\n color: var(--md-text);\n font-weight: 500;\n }\n\n .ac-item-subtitle {\n font-size: 0.75rem;\n color: var(--md-text-muted);\n margin-top: 1px;\n }\n\n /* ── Upload progress rows ───────────────────────────────────────────── */\n .upload-list {\n display: flex;\n flex-direction: column;\n gap: 0;\n }\n\n .upload-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n border-top: 1px solid var(--md-border);\n background: var(--md-bg-toolbar);\n font-size: 0.75rem;\n color: var(--md-text-muted);\n }\n\n .upload-filename {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .upload-bar-track {\n width: 6rem;\n height: 3px;\n background: var(--md-border);\n border-radius: 2px;\n overflow: hidden;\n flex-shrink: 0;\n }\n\n .upload-bar {\n height: 100%;\n background: var(--md-upload-bar);\n border-radius: 2px;\n transition: width 150ms ease;\n }\n\n .upload-error-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n border-top: 1px solid var(--md-border);\n background: oklch(97% 0.02 25);\n color: var(--md-color-error);\n font-size: 0.75rem;\n }\n\n :host([dark]) .upload-error-row {\n background: oklch(20% 0.03 25);\n }\n\n .upload-error-msg {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n`;\n",
6
- "/**\n * Prism.js CDN loader + backdrop highlight utilities.\n *\n * Prism is loaded lazily as a UMD script from cdnjs. A module-level Promise\n * caches the load so multiple elements on the same page share one request.\n * If the CDN is unavailable the element degrades gracefully text entry and\n * form submission continue to work, just without syntax colouring.\n */\n\ndeclare global {\n interface Window {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Prism?: any;\n }\n}\n\nconst PRISM_BASE = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0';\nconst PRISM_CORE_URL = `${PRISM_BASE}/prism.min.js`;\nconst PRISM_AUTOLOADER_URL = `${PRISM_BASE}/plugins/autoloader/prism-autoloader.min.js`;\nconst PRISM_THEME_DARK_URL = `${PRISM_BASE}/themes/prism-tomorrow.min.css`;\nconst PRISM_THEME_LIGHT_URL = `${PRISM_BASE}/themes/prism-coy.min.css`;\n\n/** Cached load promise shared across all instances on the page. */\nlet _prismReady: Promise<void> | null = null;\n\n/** Inject a script tag into document.head and resolve when loaded. */\nfunction _loadScript(src: string): Promise<void> {\n return new Promise((resolve) => {\n const script = document.createElement('script');\n script.src = src;\n script.onload = () => resolve();\n script.onerror = () => resolve(); // resolve even on error (graceful degrade)\n document.head.appendChild(script);\n });\n}\n\n/**\n * Ensure Prism is loaded and ready. Returns a cached Promise after the first call.\n * Safe to call multiple times only one network request is ever made.\n */\nexport function ensurePrism(): Promise<void> {\n if (window.Prism) return Promise.resolve();\n if (_prismReady) return _prismReady;\n\n _prismReady = _loadScript(PRISM_CORE_URL).then(() => {\n if (!window.Prism) return;\n // Configure autoloader before loading it\n window.Prism.manual = true;\n return _loadScript(PRISM_AUTOLOADER_URL).then(() => {\n if (window.Prism?.plugins?.autoloader) {\n window.Prism.plugins.autoloader.languages_path = `${PRISM_BASE}/components/`;\n }\n });\n });\n\n return _prismReady;\n}\n\n/**\n * Escape HTML special characters in the given text.\n * Escapes & first to prevent double-escaping.\n */\nfunction escapeHtml(text: string): string {\n return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\n/**\n * Highlight markdown text using Prism and return an HTML string.\n * If Prism is not available, returns the HTML-escaped text unchanged.\n *\n * Appends a non-breaking space to prevent the backdrop div from collapsing\n * when the textarea value ends with a newline.\n */\nexport function highlightMarkdown(text: string): string {\n const escaped = escapeHtml(text);\n\n if (!window.Prism?.languages?.markdown) {\n // Prism not ready yet return escaped plain text\n return escaped + '\\u00a0';\n }\n\n try {\n const highlighted = window.Prism.highlight(text, window.Prism.languages.markdown, 'markdown');\n return highlighted + '\\u00a0';\n } catch {\n return escaped + '\\u00a0';\n }\n}\n\n/**\n * Inject or update a Prism syntax theme inside the given shadow root.\n * Uses a <style id=\"prism-theme\"> element with an @import so the browser\n * caches the CDN stylesheet normally.\n */\nexport function applyPrismTheme(shadowRoot: ShadowRoot, dark: boolean): void {\n const themeUrl = dark ? PRISM_THEME_DARK_URL : PRISM_THEME_LIGHT_URL;\n let styleEl = shadowRoot.getElementById('prism-theme') as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = 'prism-theme';\n shadowRoot.appendChild(styleEl);\n }\n\n const expected = `@import url(\"${themeUrl}\");`;\n if (styleEl.textContent !== expected) {\n styleEl.textContent = expected;\n }\n}\n",
7
- "/**\n * File upload utilities for el-dm-markdown-input.\n *\n * Handles XHR-based multipart/form-data uploads, markdown snippet generation,\n * and accepted file type validation.\n */\n\n/** Accepted MIME types and extensions (aligned with PRD). */\nconst ACCEPTED_MIME_PREFIXES = ['image/'];\nconst ACCEPTED_MIME_EXACT = ['application/pdf'];\nconst ACCEPTED_EXTENSIONS = ['.zip', '.txt', '.csv', '.json', '.md'];\n\n/**\n * Returns true if the file's MIME type or name extension is accepted.\n */\nexport function isAcceptedType(file: File): boolean {\n const type = file.type.toLowerCase();\n if (ACCEPTED_MIME_PREFIXES.some((p) => type.startsWith(p))) return true;\n if (ACCEPTED_MIME_EXACT.includes(type)) return true;\n const name = file.name.toLowerCase();\n if (ACCEPTED_EXTENSIONS.some((ext) => name.endsWith(ext))) return true;\n return false;\n}\n\n/**\n * Generate the markdown insertion string for an uploaded file.\n * Images use `![name](url)`, all other files use `[name](url)`.\n */\nexport function fileToMarkdown(file: File, url: string): string {\n if (file.type.startsWith('image/')) {\n return `![${file.name}](${url})`;\n }\n return `[${file.name}](${url})`;\n}\n\n/**\n * Upload a single file to the given URL via XHR POST multipart/form-data.\n *\n * @param file The file to upload\n * @param uploadUrl POST endpoint — must return `{ url: string }` on 2xx\n * @param onProgress Callback fired with progress 0–100 during upload\n * @returns Resolves with the URL from the server response\n * @throws Rejects with an error message string on failure\n */\nexport function uploadFile(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n const body = new FormData();\n body.append('file', file);\n\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) {\n onProgress(Math.round((e.loaded / e.total) * 100));\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const data = JSON.parse(xhr.responseText) as { url?: string };\n if (data.url) {\n resolve(data.url);\n } else {\n reject('Upload response missing url field');\n }\n } catch {\n reject('Upload response is not valid JSON');\n }\n } else {\n reject(`Upload failed with status ${xhr.status}`);\n }\n });\n\n xhr.addEventListener('error', () => reject('Network error during upload'));\n xhr.addEventListener('abort', () => reject('Upload aborted'));\n\n xhr.open('POST', uploadUrl);\n xhr.send(body);\n });\n}\n",
8
- "/**\n * Autocomplete utilities for @mention and #reference detection.\n */\n\nimport type { Suggestion } from './types.js';\n\n/**\n * Scan backward from cursorPos through value to detect an active @word or #word trigger.\n *\n * Rules:\n * - Scan character by character backward from cursor\n * - If a whitespace or newline is encountered before a trigger char → no trigger\n * - If @ or # is found → extract the query (text between trigger and cursor)\n * - The query must not contain any whitespace\n *\n * @returns Trigger info or null if no active trigger\n */\nexport function detectTrigger(\n value: string,\n cursorPos: number,\n): { trigger: '@' | '#'; query: string; triggerPos: number } | null {\n let i = cursorPos - 1;\n\n while (i >= 0) {\n const ch = value[i];\n\n if (ch === '@' || ch === '#') {\n const query = value.slice(i + 1, cursorPos);\n // Only activate if there's no whitespace in the query\n if (!/\\s/.test(query)) {\n // Make sure the char before the trigger is whitespace, start-of-string, or trigger is at position 0\n const before = i > 0 ? value[i - 1] : null;\n if (before === null || /[\\s\\n]/.test(before)) {\n return { trigger: ch as '@' | '#', query, triggerPos: i };\n }\n }\n return null;\n }\n\n if (/[\\s\\n]/.test(ch)) {\n // Hit whitespace before finding a trigger\n return null;\n }\n\n i--;\n }\n\n return null;\n}\n\n/**\n * Replace the trigger+query span in value with the confirmed replacement.\n *\n * For a mention: `@ali` → `@asmith` (trigger is preserved in replacement)\n * The replacement value should NOT include the trigger prefix — we add it.\n *\n * @param value Full textarea value\n * @param triggerPos Index of the @ or # character\n * @param cursorPos Current cursor position (end of query)\n * @param trigger The trigger character used\n * @param replacement The id from the selected suggestion\n * @returns New value and new cursor position\n */\nexport function confirmSuggestion(\n value: string,\n triggerPos: number,\n cursorPos: number,\n trigger: '@' | '#',\n replacement: string,\n): { newValue: string; newCursorPos: number } {\n const before = value.slice(0, triggerPos);\n const after = value.slice(cursorPos);\n const inserted = `${trigger}${replacement}`;\n const newValue = before + inserted + after;\n const newCursorPos = triggerPos + inserted.length;\n return { newValue, newCursorPos };\n}\n\n/**\n * Render the HTML for the autocomplete dropdown list.\n *\n * @param suggestions Current suggestion list\n * @param selectedIndex 0-based highlighted item (-1 = none)\n */\nexport function renderDropdown(suggestions: Suggestion[], selectedIndex: number): string {\n if (suggestions.length === 0) return '';\n\n const items = suggestions\n .map((s, i) => {\n const selected = i === selectedIndex ? ' aria-selected=\"true\"' : ' aria-selected=\"false\"';\n const subtitle = s.subtitle\n ? `<span class=\"ac-item-subtitle\">${escapeHtml(s.subtitle)}</span>`\n : '';\n return `<li id=\"ac-item-${i}\" class=\"ac-item\" role=\"option\" data-ac-index=\"${i}\"${selected}>\n <span class=\"ac-item-label\">${escapeHtml(s.label)}</span>${subtitle}\n </li>`;\n })\n .join('');\n\n return items;\n}\n\nfunction escapeHtml(text: string): string {\n return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n",
5
+ "/**\n * Extended sanitization schema for rehype-sanitize.\n *\n * Builds on the default GitHub schema to allow:\n * - KaTeX output (span className/style, math MathML elements)\n * - Mermaid placeholders (code className)\n * - Task list checkboxes (input type/checked/disabled)\n */\nimport { defaultSchema } from 'rehype-sanitize';\nimport type { Schema } from 'hast-util-sanitize';\n\nfunction deepMergeSchemas(base: Schema, extension: Partial<Schema>): Schema {\n const result: Schema = { ...base };\n\n if (extension.attributes) {\n result.attributes = { ...base.attributes };\n for (const [tag, attrs] of Object.entries(extension.attributes)) {\n const existing = result.attributes[tag];\n if (Array.isArray(existing)) {\n result.attributes[tag] = [...existing, ...(attrs as string[])];\n } else {\n result.attributes[tag] = attrs;\n }\n }\n }\n\n if (extension.tagNames) {\n result.tagNames = [...(base.tagNames ?? []), ...extension.tagNames];\n }\n\n return result;\n}\n\nexport const sanitizeSchema: Schema = deepMergeSchemas(defaultSchema, {\n attributes: {\n // KaTeX output`style` is required for KaTeX's inline math sizing.\n // Trade-off: allowing `style` on `span` is a potential injection vector if\n // a compromised KaTeX version emits user-controlled content. Acceptable here\n // because KaTeX only renders math expressions (inline $...$ and display $$...$$),\n // not arbitrary user HTML. Do not widen this allowlist without review.\n span: ['className', 'style'],\n // Mermaid placeholder\n code: ['className'],\n // Task list checkboxes (rendered as disabled no interactive toggling)\n input: ['type', 'checked', 'disabled'],\n // KaTeX MathML presentation attributes\n math: ['xmlns', 'display'],\n annotation: ['encoding'],\n mi: ['mathvariant'],\n mo: ['stretchy', 'lspace', 'rspace'],\n mpadded: ['height', 'depth', 'width', 'lspace', 'voffset'],\n mspace: ['height', 'depth', 'width'],\n mstyle: ['mathsize', 'mathcolor', 'mathbackground', 'displaystyle'],\n },\n tagNames: [\n // KaTeX MathML elements\n 'math',\n 'semantics',\n 'mrow',\n 'mi',\n 'mo',\n 'mn',\n 'msup',\n 'msub',\n 'mfrac',\n 'mover',\n 'munder',\n 'msqrt',\n 'mtable',\n 'mtr',\n 'mtd',\n 'annotation',\n // Additional KaTeX MathML elements emitted by KaTeX 0.16.x\n 'mtext',\n 'mpadded',\n 'mspace',\n 'merror',\n 'mstyle',\n 'ms',\n 'mphantom',\n 'mmultiscripts',\n 'mprescripts',\n ],\n});\n",
6
+ "/**\n * Render pipeline for the preview tab.\n *\n * Uses unified (remark rehype) to transform raw markdown into sanitized HTML.\n * All dependencies are lazy-loaded on first preview activation for tree-shaking.\n *\n * Mermaid diagrams are handled as a post-render step (not part of the unified pipeline).\n */\n\nimport { sanitizeSchema } from './sanitize-schema.js';\n\n// ── Lazy-loaded processor cache ─────────────────────────────────────────\n\ntype Processor = {\n process(source: string): Promise<{ toString(): string }>;\n};\n\n// Store the promise itself so concurrent calls share one build (no duplicate initialization)\nlet processorPromise: Promise<Processor> | null = null;\n\nasync function buildProcessor(): Promise<Processor> {\n const [\n { unified },\n { default: remarkParse },\n { default: remarkGfm },\n { default: remarkMath },\n { default: remarkRehype },\n { default: rehypeKatex },\n { default: rehypePrismPlus },\n { default: rehypeSanitize },\n { default: rehypeStringify },\n ] = await Promise.all([\n import('unified'),\n import('remark-parse'),\n import('remark-gfm'),\n import('remark-math'),\n import('remark-rehype'),\n import('rehype-katex'),\n import('rehype-prism-plus'),\n import('rehype-sanitize'),\n import('rehype-stringify'),\n ]);\n\n return unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkRehype, { allowDangerousHtml: false })\n .use(rehypeKatex)\n .use(rehypePrismPlus, { ignoreMissing: true })\n .use(rehypeSanitize, sanitizeSchema)\n .use(rehypeStringify) as unknown as Processor;\n}\n\nfunction getProcessor(): Promise<Processor> {\n if (!processorPromise) {\n processorPromise = buildProcessor().catch((err) => {\n // Clear cache on failure so subsequent calls retry the imports\n processorPromise = null;\n throw err;\n });\n }\n return processorPromise;\n}\n\n/**\n * Transform raw markdown into sanitized HTML using the unified pipeline.\n * The processor is lazy-loaded on first call and cached for subsequent renders.\n */\nexport async function renderMarkdown(source: string): Promise<string> {\n const proc = await getProcessor();\n const file = await proc.process(source);\n return String(file);\n}\n\n// ── Mermaid post-render ─────────────────────────────────────────────────\n\nlet mermaidIdCounter = 0;\n\n/**\n * Detect the current theme by checking `data-theme` on `<html>`.\n */\nfunction getCurrentTheme(): string {\n if (typeof document === 'undefined') return 'default';\n return document.documentElement.getAttribute('data-theme') ?? 'default';\n}\n\n/**\n * Post-render step: find all `pre > code.language-mermaid` blocks in the container,\n * lazy-load mermaid, and replace each `<pre>` with the rendered SVG.\n *\n * @param container The preview container element (inside shadow DOM)\n * @param mermaidSrc Optional override URL for the mermaid ESM bundle\n */\nexport async function renderMermaidBlocks(\n container: HTMLElement,\n mermaidSrc?: string,\n): Promise<void> {\n const blocks = container.querySelectorAll('pre > code.language-mermaid');\n if (blocks.length === 0) return;\n\n // Dynamic import either from provided URL or the npm package.\n // Only https: URLs are accepted to prevent arbitrary code execution via\n // a consumer-supplied mermaid-src attribute pointing to a malicious module.\n if (mermaidSrc !== undefined && !/^https:\\/\\//i.test(mermaidSrc)) {\n console.warn(\n `[el-dm-markdown-input] mermaid-src \"${mermaidSrc}\" rejected only https: URLs are allowed. Falling back to bundled mermaid.`,\n );\n mermaidSrc = undefined;\n }\n\n // Catch import failure separately so a CDN outage does not destroy the\n // already-rendered prose HTML. Blocks are marked as errored and we return\n // without throwing — the caller's prose render survives.\n let mermaidModule: { default?: unknown } | undefined;\n try {\n mermaidModule = mermaidSrc\n ? await import(/* @vite-ignore */ mermaidSrc)\n : await import('mermaid');\n } catch (err) {\n console.error('[el-dm-markdown-input] Failed to load mermaid: %o', err);\n blocks.forEach((block) => block.parentElement?.classList.add('mermaid-error'));\n return;\n }\n const mermaid = (mermaidModule!.default ?? mermaidModule) as {\n initialize(opts: Record<string, unknown>): void;\n render(id: string, src: string): Promise<{ svg: string }>;\n };\n\n mermaid.initialize({\n startOnLoad: false,\n theme: getCurrentTheme() === 'moonlight' ? 'dark' : 'default',\n fontFamily: 'inherit',\n });\n\n for (const [i, block] of [...blocks].entries()) {\n const pre = block.parentElement;\n if (!pre) continue;\n\n const id = `mermaid-${++mermaidIdCounter}-${i}`;\n try {\n const { svg } = await mermaid.render(id, block.textContent ?? '');\n const wrapper = document.createElement('div');\n wrapper.className = 'mermaid-diagram';\n // Safety: mermaid's rendered SVG is treated as trusted output from the\n // mermaid library (not user HTML). It bypasses rehype-sanitize intentionally.\n // Known risk: if mermaid emits user-controlled content in SVG attributes or\n // text nodes a XSS could occur. The mermaid-src URL is validated to https: only.\n // TODO: run mermaid output through DOMPurify for defense-in-depth once a\n // lightweight sanitizer integration is available without bloating the bundle.\n wrapper.innerHTML = svg;\n pre.replaceWith(wrapper);\n } catch (err) {\n console.error(\n '[el-dm-markdown-input] mermaid.render failed for block %s: %o\\nSource: %s',\n id,\n err,\n block.textContent?.slice(0, 200),\n );\n pre.classList.add('mermaid-error');\n }\n }\n}\n",
7
+ "/**\n * DuskMoon Markdown Input Element\n *\n * A form-associated custom element providing a markdown editor with:\n * - Write tab with syntax-highlighted render layer (Prism.js CDN)\n * - Preview tab with rendered HTML (.markdown-body from @duskmoon-dev/core)\n * - File upload via drag-and-drop, clipboard paste, or file picker\n * - @mention / #reference autocomplete dropdown\n * - Live word / character count status bar\n * - Phoenix LiveView hook (MarkdownInputHook, exported from index.ts)\n *\n * @element el-dm-markdown-input\n *\n * @attr {string} name Form field name\n * @attr {string} value Initial markdown content\n * @attr {string} placeholder Textarea placeholder (default: \"Write markdown…\")\n * @attr {boolean} disabled Disables editing\n * @attr {boolean} readonly Makes the editor read-only (value still submitted)\n * @attr {string} upload-url POST endpoint for file uploads\n * @attr {number} max-words Soft word cap shown in status bar\n * @attr {boolean} dark Activates dark Prism theme + dark CSS variable defaults\n *\n * @fires change `{ value: string }` — on every input\n * @fires upload-start `{ file: File }` — when a file is accepted\n * @fires upload-done `{ file: File, url: string, markdown: string }` — on success\n * @fires upload-error `{ file: File, error: string }` — on failure\n * @fires mention-query `{ trigger: \"@\", query, resolve }` — on @word input\n * @fires reference-query `{ trigger: \"#\", query, resolve }` — on #word input\n */\n\nimport { BaseElement } from '@duskmoon-dev/el-base';\nimport { css as markdownBodyCSS } from '@duskmoon-dev/core/components/markdown-body';\n\nimport { elementStyles } from './css.js';\nimport { ensurePrism, highlightMarkdown, applyPrismTheme } from './highlight.js';\nimport { uploadFile, fileToMarkdown, isAcceptedType } from './upload.js';\nimport { detectTrigger, confirmSuggestion, renderDropdown } from './autocomplete.js';\nimport { handlePairKey, handleEnterKey } from './pairs.js';\nimport { countWords, renderStatusCount } from './status-bar.js';\nimport type { Suggestion } from './types.js';\n\n// Render pipeline is lazy-loaded — only imported when preview tab is first activated\nimport type { renderMarkdown as RenderFn, renderMermaidBlocks as MermaidFn } from './render.js';\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreMarkdownStyles = markdownBodyCSS\n .replace(/@layer\\s+components\\s*\\{/, '')\n .replace(/\\}\\s*$/, '');\n\n// Inject core markdown-body styles as a constructable stylesheet\nimport { css } from '@duskmoon-dev/el-base';\nconst markdownBodySheet = css`\n ${coreMarkdownStyles}\n`;\n\nexport class ElDmMarkdownInput extends BaseElement {\n static formAssociated = true as const;\n\n static properties = {\n name: { type: String, reflect: true, default: '' },\n value: { type: String, default: '' },\n placeholder: { type: String, reflect: true, default: 'Write markdown\\u2026' },\n disabled: { type: Boolean, reflect: true },\n readonly: { type: Boolean, reflect: true },\n required: { type: Boolean, reflect: true },\n uploadUrl: { type: String, reflect: true, attribute: 'upload-url' },\n maxWords: { type: Number, reflect: true, attribute: 'max-words' },\n dark: { type: Boolean, reflect: true },\n livePreview: { type: Boolean, reflect: true, attribute: 'live-preview' },\n debounce: { type: Number, reflect: true, default: 300 },\n katexCssUrl: { type: String, reflect: true, attribute: 'katex-css-url' },\n mermaidSrc: { type: String, reflect: true, attribute: 'mermaid-src' },\n };\n\n declare name: string;\n declare value: string;\n declare placeholder: string;\n declare disabled: boolean;\n declare readonly: boolean;\n declare required: boolean;\n declare uploadUrl: string | undefined;\n declare maxWords: number | undefined;\n declare dark: boolean;\n declare livePreview: boolean;\n declare debounce: number;\n declare katexCssUrl: string | undefined;\n declare mermaidSrc: string | undefined;\n\n // ── ElementInternals for form association ────────────────────────────\n #internals!: ElementInternals;\n\n // ── Rendering state ──────────────────────────────────────────────────\n /** True after the first full render has populated the shadow DOM. */\n #initialized = false;\n\n /** Which tab is currently active. */\n #activeTab: 'write' | 'preview' = 'write';\n\n // ── Debounce timers ──────────────────────────────────────────────────\n #highlightTimer: ReturnType<typeof setTimeout> | null = null;\n #statusTimer: ReturnType<typeof setTimeout> | null = null;\n\n // ── DOM element refs (set after first render) ────────────────────────\n #textarea: HTMLTextAreaElement | null = null;\n #renderLayer: HTMLElement | null = null;\n #writeArea: HTMLElement | null = null;\n #previewBody: HTMLElement | null = null;\n #statusCount: HTMLElement | null = null;\n #acDropdown: HTMLElement | null = null;\n #uploadList: HTMLElement | null = null;\n #fileInput: HTMLInputElement | null = null;\n\n // ── Autocomplete state ───────────────────────────────────────────────\n #acSuggestions: Suggestion[] = [];\n #acSelectedIndex = -1;\n #acTriggerPos = -1;\n #acTrigger: '@' | '#' | null = null;\n /** Monotonically-increasing counter to discard out-of-order resolve() calls. */\n #acGeneration = 0;\n\n // ── Render pipeline (lazy-loaded) ───────────────────────────────────\n #prevDark = false;\n #renderFn: typeof RenderFn | null = null;\n #mermaidFn: typeof MermaidFn | null = null;\n #livePreviewTimer: ReturnType<typeof setTimeout> | null = null;\n #renderAbortController: AbortController | null = null;\n /** Source value from the last completed preview render — skip re-render if unchanged. */\n #lastRenderedSource: string | null = null;\n /** True once the KaTeX <link> stylesheet has been injected into the shadow root. */\n #katexCssInjected = false;\n\n // ── Upload state ─────────────────────────────────────────────────────\n #uploadIdCounter = 0;\n\n constructor() {\n super();\n // attachInternals() must be called in the constructor per HTML spec\n this.#internals = this.attachInternals();\n this.attachStyles([elementStyles, markdownBodySheet]);\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Lifecycle\n // ════════════════════════════════════════════════════════════════════\n\n connectedCallback(): void {\n super.connectedCallback(); // triggers initial update() → render()\n }\n\n disconnectedCallback(): void {\n this.#renderAbortController?.abort();\n if (this.#livePreviewTimer !== null) clearTimeout(this.#livePreviewTimer);\n super.disconnectedCallback();\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Rendering — override update() to protect the textarea after init\n // ════════════════════════════════════════════════════════════════════\n\n protected override update(): void {\n if (!this.#initialized) {\n // Full initial render — creates all DOM nodes\n super.update(); // calls render() → shadowRoot.innerHTML = content\n this.#initialized = true;\n this.#cacheDOMRefs();\n this.#attachEventHandlers();\n this.#initHighlight();\n\n // Restore initial value from reactive prop BEFORE updating status bar,\n // so that required+valueMissing validity is evaluated against the real\n // initial content rather than a transient empty textarea.\n const initVal = (this as unknown as { value: string }).value ?? '';\n if (this.#textarea) {\n this.#textarea.value = initVal;\n this.#syncFormValue();\n if (initVal) this.#scheduleHighlight();\n }\n this.#updateStatusBarNow();\n return;\n }\n\n // Incremental update — patch only specific DOM regions\n this.#patchDynamicRegions();\n }\n\n /**\n * Patch DOM regions that can change due to reactive property updates,\n * without replacing the textarea (which would lose state).\n */\n #patchDynamicRegions(): void {\n const ta = this.#textarea;\n if (!ta) return;\n\n // Sync simple attributes\n const placeholder =\n (this as unknown as { placeholder: string }).placeholder ?? 'Write markdown\\u2026';\n ta.placeholder = placeholder;\n ta.disabled = !!(this as unknown as { disabled: boolean }).disabled;\n ta.readOnly = !!(this as unknown as { readonly: boolean }).readonly;\n\n const attachBtn = this.shadowRoot.querySelector<HTMLButtonElement>('.attach-btn');\n if (attachBtn) {\n attachBtn.disabled = ta.disabled || ta.readOnly;\n }\n\n // Sync value if the reactive prop was updated externally (e.g. attributeChangedCallback)\n const propVal = (this as unknown as { value: string }).value ?? '';\n if (propVal !== ta.value) {\n ta.value = propVal;\n this.#syncFormValue();\n this.#scheduleHighlight();\n // Re-render preview if the value changed while the preview tab is active\n if (this.#activeTab === 'preview' && this.#previewBody) {\n this.#renderPreview(propVal);\n }\n }\n\n // Update Prism theme when dark attribute changes\n const dark = !!(this as unknown as { dark: boolean }).dark;\n applyPrismTheme(this.shadowRoot, dark);\n\n // Re-render preview if dark attribute changed while preview tab is active\n // (mermaid diagrams use theme-dependent SVGs, code blocks need matching Prism theme)\n // Force=true bypasses the source cache so theme-sensitive renders always refresh.\n if (dark !== this.#prevDark) {\n this.#prevDark = dark;\n if (this.#activeTab === 'preview' && this.#previewBody) {\n this.#lastRenderedSource = null; // invalidate cache on theme change\n this.#renderPreview(ta.value);\n }\n }\n\n // Re-render status bar (maxWords may have changed)\n this.#updateStatusBarNow();\n }\n\n protected override render(): string {\n const ph = (this as unknown as { placeholder: string }).placeholder ?? 'Write markdown\\u2026';\n const disabled = !!(this as unknown as { disabled: boolean }).disabled;\n const readonly = !!(this as unknown as { readonly: boolean }).readonly;\n\n return `\n <div class=\"editor\">\n <div class=\"toolbar\" role=\"tablist\" aria-label=\"Editor mode\">\n <button\n class=\"tab-btn\"\n id=\"tab-write\"\n data-tab=\"write\"\n role=\"tab\"\n aria-selected=\"true\"\n aria-controls=\"write-panel\"\n tabindex=\"0\"\n >Write</button>\n <button\n class=\"tab-btn\"\n id=\"tab-preview\"\n data-tab=\"preview\"\n role=\"tab\"\n aria-selected=\"false\"\n aria-controls=\"preview-panel\"\n tabindex=\"-1\"\n >Preview</button>\n </div>\n\n <div class=\"write-area\" id=\"write-panel\" role=\"tabpanel\" aria-labelledby=\"tab-write\">\n <div class=\"render-layer\" aria-hidden=\"true\"></div>\n <textarea\n aria-label=\"Markdown editor\"\n aria-haspopup=\"listbox\"\n aria-expanded=\"false\"\n aria-autocomplete=\"list\"\n aria-controls=\"ac-dropdown\"\n placeholder=\"${escapeHtmlStr(ph)}\"\n ${disabled ? 'disabled' : ''}\n ${readonly ? 'readonly' : ''}\n spellcheck=\"false\"\n autocomplete=\"off\"\n autocorrect=\"off\"\n autocapitalize=\"off\"\n ></textarea>\n </div>\n\n <div\n class=\"preview-body markdown-body\"\n id=\"preview-panel\"\n role=\"tabpanel\"\n aria-labelledby=\"tab-preview\"\n hidden\n ></div>\n\n <div class=\"status-bar\">\n <button class=\"attach-btn\" type=\"button\" aria-label=\"Attach files\" ${disabled || readonly ? 'disabled' : ''}>\n &#128206; Attach files\n </button>\n <span class=\"status-bar-count\" aria-live=\"polite\"></span>\n <input\n type=\"file\"\n class=\"file-input\"\n multiple\n accept=\"image/*,application/pdf,.zip,.txt,.csv,.json,.md\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n >\n </div>\n\n <div class=\"upload-list\"></div>\n </div>\n <ul id=\"ac-dropdown\" class=\"ac-dropdown\" role=\"listbox\" aria-label=\"Suggestions\" hidden></ul>\n `;\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Post-render setup\n // ════════════════════════════════════════════════════════════════════\n\n #cacheDOMRefs(): void {\n this.#textarea = this.shadowRoot.querySelector('textarea');\n this.#renderLayer = this.shadowRoot.querySelector('.render-layer');\n this.#writeArea = this.shadowRoot.querySelector('.write-area');\n this.#previewBody = this.shadowRoot.querySelector('.preview-body');\n this.#statusCount = this.shadowRoot.querySelector('.status-bar-count');\n this.#acDropdown = this.shadowRoot.querySelector('.ac-dropdown');\n this.#uploadList = this.shadowRoot.querySelector('.upload-list');\n this.#fileInput = this.shadowRoot.querySelector('.file-input');\n }\n\n #attachEventHandlers(): void {\n const ta = this.#textarea;\n if (!ta) return;\n\n // ── Textarea input ─────────────────────────────────────────────\n ta.addEventListener('input', () => {\n this.#syncFormValue();\n this.emit('change', { value: ta.value });\n this.#scheduleHighlight();\n this.#scheduleStatusUpdate();\n this.#handleAutocompleteInput();\n this.#scheduleLivePreview();\n });\n\n // ── Close dropdown when focus leaves the textarea ──────────────\n ta.addEventListener('blur', () => {\n // Delay to allow pointer events on dropdown items to fire first\n setTimeout(() => {\n if (!this.shadowRoot?.activeElement) {\n this.#closeDropdown();\n }\n }, 150);\n });\n\n // ── Keydown: autocomplete nav, smart pairs, list/heading continuation ──\n ta.addEventListener('keydown', (e) => {\n // Autocomplete dropdown takes priority when open\n if (this.#acSuggestions.length > 0 && !this.#acDropdown?.hidden) {\n this.#handleDropdownKeydown(e);\n if (e.defaultPrevented) return;\n }\n\n // Ctrl+Shift+P (Windows/Linux) or Cmd+Shift+P (Mac) → toggle preview\n if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'P') {\n e.preventDefault();\n this.#switchTab(this.#activeTab === 'write' ? 'preview' : 'write');\n return;\n }\n\n // Skip smart editing when the editor is read-only or disabled\n if (\n (this as unknown as { disabled: boolean }).disabled ||\n (this as unknown as { readonly: boolean }).readonly\n ) {\n return;\n }\n\n // Smart pair insertion (backtick pairing)\n if (!e.ctrlKey && !e.metaKey && !e.altKey && handlePairKey(ta, e.key)) {\n e.preventDefault();\n this.#syncFormValue();\n this.emit('change', { value: ta.value });\n this.#scheduleHighlight();\n return;\n }\n\n // List/heading Enter continuation\n if (e.key === 'Enter' && !e.ctrlKey && !e.metaKey && !e.altKey) {\n if (handleEnterKey(ta, e)) {\n this.#syncFormValue();\n this.emit('change', { value: ta.value });\n this.#scheduleHighlight();\n this.#scheduleStatusUpdate();\n }\n }\n });\n\n // ── Drag and drop ──────────────────────────────────────────────\n const writeArea = this.#writeArea;\n if (writeArea) {\n writeArea.addEventListener('dragover', (e) => {\n if ((this as unknown as { disabled: boolean }).disabled) return;\n if ((this as unknown as { readonly: boolean }).readonly) return;\n e.preventDefault();\n writeArea.style.opacity = '0.8';\n });\n writeArea.addEventListener('dragleave', () => {\n writeArea.style.opacity = '';\n });\n writeArea.addEventListener('drop', (e) => {\n e.preventDefault();\n writeArea.style.opacity = '';\n if ((this as unknown as { disabled: boolean }).disabled) return;\n if ((this as unknown as { readonly: boolean }).readonly) return;\n const files = Array.from(e.dataTransfer?.files ?? []).filter(isAcceptedType);\n files.forEach((f) => this.#startUpload(f));\n });\n }\n\n // ── Clipboard paste (images only) ─────────────────────────────\n ta.addEventListener('paste', (e) => {\n if ((this as unknown as { disabled: boolean }).disabled) return;\n if ((this as unknown as { readonly: boolean }).readonly) return;\n const imageFiles = Array.from(e.clipboardData?.files ?? []).filter((f) =>\n f.type.startsWith('image/'),\n );\n if (imageFiles.length > 0) {\n e.preventDefault();\n imageFiles.forEach((f) => this.#startUpload(f));\n }\n });\n\n // ── Tab buttons ────────────────────────────────────────────────\n const toolbar = this.shadowRoot.querySelector('.toolbar');\n toolbar?.addEventListener('click', (e) => {\n const btn = (e.target as HTMLElement).closest<HTMLElement>('.tab-btn');\n const tab = btn?.dataset.tab as 'write' | 'preview' | undefined;\n if (tab) this.#switchTab(tab);\n });\n\n // Arrow key navigation between tabs (WAI-ARIA tablist pattern)\n toolbar?.addEventListener('keydown', (e) => {\n const kev = e as KeyboardEvent;\n if (kev.key === 'ArrowLeft' || kev.key === 'ArrowRight') {\n kev.preventDefault();\n const nextTab = this.#activeTab === 'write' ? 'preview' : 'write';\n this.#switchTab(nextTab);\n const nextBtn = this.shadowRoot.querySelector<HTMLElement>(\n `.tab-btn[data-tab=\"${nextTab}\"]`,\n );\n nextBtn?.focus();\n }\n });\n\n // ── Attach button ──────────────────────────────────────────────\n const attachBtn = this.shadowRoot.querySelector('.attach-btn');\n attachBtn?.addEventListener('click', () => this.#fileInput?.click());\n\n this.#fileInput?.addEventListener('change', () => {\n const files = Array.from(this.#fileInput?.files ?? []).filter(isAcceptedType);\n files.forEach((f) => this.#startUpload(f));\n if (this.#fileInput) this.#fileInput.value = '';\n });\n\n // ── Autocomplete dropdown click delegation ─────────────────────\n this.#acDropdown?.addEventListener('click', (e) => {\n const item = (e.target as HTMLElement).closest<HTMLElement>('[data-ac-index]');\n if (item) {\n const idx = parseInt(item.dataset.acIndex ?? '-1', 10);\n if (idx >= 0) {\n this.#acSelectedIndex = idx;\n this.#confirmAutocomplete();\n }\n }\n });\n\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Highlight (Write tab render layer)\n // ════════════════════════════════════════════════════════════════════\n\n #initHighlight(): void {\n const dark = !!(this as unknown as { dark: boolean }).dark;\n applyPrismTheme(this.shadowRoot, dark);\n ensurePrism().then(() => {\n // Highlight immediately once Prism is ready\n if (this.#textarea && this.#renderLayer) {\n this.#renderLayer.innerHTML = highlightMarkdown(this.#textarea.value);\n }\n });\n }\n\n #scheduleHighlight(): void {\n if (this.#highlightTimer !== null) clearTimeout(this.#highlightTimer);\n this.#highlightTimer = setTimeout(() => {\n this.#highlightTimer = null;\n if (this.#renderLayer && this.#textarea) {\n this.#renderLayer.innerHTML = highlightMarkdown(this.#textarea.value);\n }\n }, 60);\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Tab switching (Write ↔ Preview)\n // ════════════════════════════════════════════════════════════════════\n\n #switchTab(tab: 'write' | 'preview'): void {\n if (tab === this.#activeTab) return;\n this.#activeTab = tab;\n\n const writeBtns = this.shadowRoot.querySelectorAll<HTMLElement>('.tab-btn');\n writeBtns.forEach((btn) => {\n const isActive = btn.dataset.tab === tab;\n btn.setAttribute('aria-selected', String(isActive));\n btn.setAttribute('tabindex', isActive ? '0' : '-1');\n });\n\n if (tab === 'preview') {\n this.#writeArea?.setAttribute('hidden', '');\n if (this.#previewBody) {\n this.#previewBody.removeAttribute('hidden');\n this.#renderPreview(this.#textarea?.value ?? '');\n }\n } else {\n this.#writeArea?.removeAttribute('hidden');\n this.#previewBody?.setAttribute('hidden', '');\n }\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Unified render pipeline (preview tab)\n // ════════════════════════════════════════════════════════════════════\n\n /**\n * Load the render pipeline lazily. Called on first preview activation.\n * Returns cached functions on subsequent calls.\n */\n async #loadRenderPipeline(): Promise<{\n renderMarkdown: typeof RenderFn;\n renderMermaidBlocks: typeof MermaidFn;\n }> {\n if (this.#renderFn && this.#mermaidFn) {\n return { renderMarkdown: this.#renderFn, renderMermaidBlocks: this.#mermaidFn };\n }\n\n const mod = await import('./render.js');\n this.#renderFn = mod.renderMarkdown;\n this.#mermaidFn = mod.renderMermaidBlocks;\n return { renderMarkdown: this.#renderFn, renderMermaidBlocks: this.#mermaidFn };\n }\n\n /**\n * Render markdown to the preview panel using the unified pipeline.\n * Shows a loading skeleton on first load, emits render events.\n * Skips re-render if the source and theme are unchanged from the last render.\n */\n async #renderPreview(source: string, force = false): Promise<void> {\n const preview = this.#previewBody;\n if (!preview) return;\n\n // Skip if source is identical to the last completed render (and not forced)\n if (!force && this.#lastRenderedSource === source && this.#renderFn !== null) {\n return;\n }\n\n // Cancel any in-flight render\n this.#renderAbortController?.abort();\n const controller = new AbortController();\n this.#renderAbortController = controller;\n\n this.emit('render-start', {});\n preview.setAttribute('aria-busy', 'true');\n\n // Show skeleton on first load while pipeline imports\n if (!this.#renderFn) {\n preview.innerHTML = `\n <div class=\"preview-skeleton\" aria-label=\"Loading preview…\">\n <div class=\"skeleton-line\" style=\"width:90%\"></div>\n <div class=\"skeleton-line\" style=\"width:75%\"></div>\n <div class=\"skeleton-line\" style=\"width:85%\"></div>\n <div class=\"skeleton-line\" style=\"width:60%\"></div>\n </div>`;\n }\n\n try {\n const { renderMarkdown, renderMermaidBlocks } = await this.#loadRenderPipeline();\n\n // If this render was aborted (new one started), bail out\n if (controller.signal.aborted) {\n preview.removeAttribute('aria-busy');\n return;\n }\n\n const html = await renderMarkdown(source);\n if (controller.signal.aborted) {\n preview.removeAttribute('aria-busy');\n return;\n }\n\n preview.innerHTML = html;\n preview.removeAttribute('aria-busy');\n\n // Inject KaTeX CSS into shadow DOM if not already present\n this.#ensureKatexCss();\n\n // Mermaid post-render step\n const mermaidSrc = (this as unknown as { mermaidSrc: string | undefined }).mermaidSrc;\n await renderMermaidBlocks(preview, mermaidSrc);\n if (controller.signal.aborted) {\n preview.removeAttribute('aria-busy');\n return;\n }\n\n // Update cache only after the full render pipeline (including mermaid)\n // completes successfully. Setting it earlier would cause cache hits\n // to skip mermaid post-processing on re-render.\n this.#lastRenderedSource = source;\n\n this.emit('render-done', { html });\n } catch (err) {\n if (controller.signal.aborted) {\n preview.removeAttribute('aria-busy');\n return;\n }\n\n // Fallback: show raw markdown as preformatted text\n preview.removeAttribute('aria-busy');\n preview.innerHTML = `<pre class=\"render-error-fallback\">${escapeHtmlStr(source)}</pre>`;\n this.emit('render-error', { error: err instanceof Error ? err : new Error(String(err)) });\n }\n }\n\n /**\n * Ensure KaTeX CSS is loaded in the shadow DOM.\n */\n #ensureKatexCss(): void {\n if (this.#katexCssInjected) return;\n this.#katexCssInjected = true;\n\n const rawUrl =\n (this as unknown as { katexCssUrl: string | undefined }).katexCssUrl ??\n 'https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css';\n\n // Only allow https: URLs to prevent data:/javascript: CSS injection.\n // katex-css-url is a trusted-author attribute but must not be user-controlled.\n const katexUrl = /^https:\\/\\//i.test(rawUrl)\n ? rawUrl\n : 'https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css';\n if (katexUrl !== rawUrl) {\n console.warn(\n `[el-dm-markdown-input] katex-css-url \"${rawUrl}\" rejected — only https: URLs are allowed.`,\n );\n }\n\n const link = document.createElement('link');\n link.id = 'katex-css';\n link.rel = 'stylesheet';\n link.href = katexUrl;\n this.shadowRoot.appendChild(link);\n }\n\n /**\n * Schedule a debounced live preview render (only when live-preview attribute is set).\n */\n #scheduleLivePreview(): void {\n if (!(this as unknown as { livePreview: boolean }).livePreview) return;\n if (this.#activeTab !== 'preview') return;\n\n if (this.#livePreviewTimer !== null) clearTimeout(this.#livePreviewTimer);\n const ms = (this as unknown as { debounce: number }).debounce ?? 300;\n this.#livePreviewTimer = setTimeout(() => {\n this.#livePreviewTimer = null;\n this.#renderPreview(this.#textarea?.value ?? '');\n }, ms);\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Form association\n // ════════════════════════════════════════════════════════════════════\n\n #syncFormValue(): void {\n this.#internals?.setFormValue(this.#textarea?.value ?? '');\n }\n\n // ════════════════════════════════════════════════════════════════════\n // File upload\n // ════════════════════════════════════════════════════════════════════\n\n #startUpload(file: File): void {\n this.emit('upload-start', { file });\n\n const id = `upload-${++this.#uploadIdCounter}`;\n const uploadUrl = (this as unknown as { uploadUrl: string | undefined }).uploadUrl;\n\n if (!uploadUrl) {\n this.emit('upload-error', { file, error: 'no upload-url set' });\n this.#showUploadError(file, 'no upload-url set');\n return;\n }\n\n // Create progress row\n this.#addProgressRow(id, file.name);\n\n uploadFile(file, uploadUrl, (pct) => {\n this.#updateProgressRow(id, pct);\n })\n .then((url) => {\n this.#removeUploadRow(id);\n const markdown = fileToMarkdown(file, url);\n this.insertText(markdown);\n this.emit('upload-done', { file, url, markdown });\n })\n .catch((err: unknown) => {\n this.#removeUploadRow(id);\n const errorMsg = err instanceof Error ? err.message : 'Upload failed';\n this.emit('upload-error', { file, error: errorMsg });\n this.#showUploadError(file, errorMsg);\n });\n }\n\n #addProgressRow(id: string, filename: string): void {\n if (!this.#uploadList) return;\n const row = document.createElement('div');\n row.className = 'upload-row';\n row.id = id;\n row.innerHTML = `\n <span class=\"upload-filename\">${escapeHtmlStr(filename)}</span>\n <div class=\"upload-bar-track\" role=\"progressbar\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-label=\"Uploading ${escapeHtmlStr(filename)}\">\n <div class=\"upload-bar\" style=\"width: 0%\"></div>\n </div>\n `;\n this.#uploadList.appendChild(row);\n }\n\n #updateProgressRow(id: string, pct: number): void {\n const track = this.#uploadList?.querySelector<HTMLElement>(`#${id} .upload-bar-track`);\n if (track) track.setAttribute('aria-valuenow', String(pct));\n const bar = this.#uploadList?.querySelector<HTMLElement>(`#${id} .upload-bar`);\n if (bar) bar.style.width = `${pct}%`;\n }\n\n #removeUploadRow(id: string): void {\n this.#uploadList?.querySelector(`#${id}`)?.remove();\n }\n\n #showUploadError(file: File, message: string): void {\n if (!this.#uploadList) return;\n const row = document.createElement('div');\n row.className = 'upload-error-row';\n row.setAttribute('role', 'alert');\n row.innerHTML = `\n <span class=\"upload-error-msg\">${escapeHtmlStr(file.name)}: ${escapeHtmlStr(message)}</span>\n `;\n this.#uploadList.appendChild(row);\n setTimeout(() => row.remove(), 4000);\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Autocomplete\n // ════════════════════════════════════════════════════════════════════\n\n #handleAutocompleteInput(): void {\n const ta = this.#textarea;\n if (!ta) return;\n\n const result = detectTrigger(ta.value, ta.selectionStart ?? 0);\n\n if (!result) {\n this.#closeDropdown();\n return;\n }\n\n const { trigger, query, triggerPos } = result;\n this.#acTrigger = trigger;\n this.#acTriggerPos = triggerPos;\n\n // Capture current generation so stale async resolutions are ignored\n const gen = ++this.#acGeneration;\n const resolve = (list: Suggestion[]) => {\n if (gen === this.#acGeneration) this.setSuggestions(list);\n };\n\n if (trigger === '@') {\n this.emit('mention-query', { trigger, query, resolve });\n } else {\n this.emit('reference-query', { trigger, query, resolve });\n }\n }\n\n #handleDropdownKeydown(e: KeyboardEvent): void {\n const len = this.#acSuggestions.length;\n if (len === 0) return;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this.#acSelectedIndex = (this.#acSelectedIndex + 1) % len;\n this.#updateDropdown();\n break;\n case 'ArrowUp':\n e.preventDefault();\n this.#acSelectedIndex = (this.#acSelectedIndex - 1 + len) % len;\n this.#updateDropdown();\n break;\n case 'Enter':\n case 'Tab':\n if (this.#acSelectedIndex >= 0) {\n e.preventDefault();\n this.#confirmAutocomplete();\n }\n break;\n case 'Escape':\n this.#closeDropdown();\n break;\n }\n }\n\n #confirmAutocomplete(): void {\n const ta = this.#textarea;\n if (!ta || this.#acSelectedIndex < 0 || !this.#acTrigger) return;\n\n const suggestion = this.#acSuggestions[this.#acSelectedIndex];\n if (!suggestion) return;\n\n const { newValue, newCursorPos } = confirmSuggestion(\n ta.value,\n this.#acTriggerPos,\n ta.selectionStart ?? ta.value.length,\n this.#acTrigger,\n suggestion.id,\n );\n\n ta.value = newValue;\n ta.setSelectionRange(newCursorPos, newCursorPos);\n this.#syncFormValue();\n this.emit('change', { value: ta.value });\n this.#scheduleHighlight();\n this.#scheduleStatusUpdate();\n this.#closeDropdown();\n }\n\n #closeDropdown(): void {\n this.#acGeneration++; // invalidate any pending resolve() callbacks\n this.#acSuggestions = [];\n this.#acSelectedIndex = -1;\n this.#acTrigger = null;\n this.#acTriggerPos = -1;\n if (this.#acDropdown) {\n this.#acDropdown.innerHTML = '';\n this.#acDropdown.hidden = true;\n }\n this.#textarea?.setAttribute('aria-expanded', 'false');\n this.#textarea?.removeAttribute('aria-activedescendant');\n }\n\n #updateDropdown(): void {\n if (!this.#acDropdown) return;\n if (this.#acSuggestions.length === 0) {\n this.#acDropdown.hidden = true;\n this.#textarea?.setAttribute('aria-expanded', 'false');\n this.#textarea?.removeAttribute('aria-activedescendant');\n return;\n }\n this.#acDropdown.innerHTML = renderDropdown(this.#acSuggestions, this.#acSelectedIndex);\n this.#acDropdown.hidden = false;\n this.#textarea?.setAttribute('aria-expanded', 'true');\n // Update aria-activedescendant so screen readers announce the highlighted item\n if (this.#acSelectedIndex >= 0) {\n this.#textarea?.setAttribute('aria-activedescendant', `ac-item-${this.#acSelectedIndex}`);\n } else {\n this.#textarea?.removeAttribute('aria-activedescendant');\n }\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Status bar\n // ════════════════════════════════════════════════════════════════════\n\n #scheduleStatusUpdate(): void {\n if (this.#statusTimer !== null) clearTimeout(this.#statusTimer);\n this.#statusTimer = setTimeout(() => {\n this.#statusTimer = null;\n this.#updateStatusBarNow();\n }, 100);\n }\n\n #updateStatusBarNow(): void {\n if (!this.#statusCount) return;\n const text = this.#textarea?.value ?? '';\n const words = countWords(text);\n const chars = text.length;\n const maxWords = (this as unknown as { maxWords: number | undefined }).maxWords ?? null;\n this.#statusCount.innerHTML = renderStatusCount(words, chars, maxWords);\n\n // Report form validity\n const isRequired = !!(this as unknown as { required: boolean }).required;\n if (maxWords && words > maxWords) {\n this.#internals?.setValidity(\n { customError: true },\n `Content exceeds ${maxWords} word limit (${words} words)`,\n this.#textarea ?? undefined,\n );\n } else if (isRequired && text.trim() === '') {\n this.#internals?.setValidity(\n { valueMissing: true },\n 'Please fill in this field.',\n this.#textarea ?? undefined,\n );\n } else {\n this.#internals?.setValidity({});\n }\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Public API\n // ════════════════════════════════════════════════════════════════════\n\n /** Returns the current markdown content. */\n getValue(): string {\n return this.#textarea?.value ?? '';\n }\n\n /**\n * Set the editor content programmatically.\n * Does NOT fire a change event. Updates the form value.\n */\n setValue(str: string): void {\n if (this.#textarea) {\n this.#textarea.value = str;\n this.#syncFormValue();\n this.#scheduleHighlight();\n this.#updateStatusBarNow();\n // Keep preview in sync when value is set programmatically\n if (this.#activeTab === 'preview' && this.#previewBody) {\n this.#renderPreview(str);\n }\n } else {\n // Called before element is connected — store in reactive property\n (this as unknown as { value: string }).value = str;\n }\n }\n\n /**\n * Insert text at the current cursor position, replacing any selection.\n * Fires a change event after insertion.\n */\n insertText(str: string): void {\n const ta = this.#textarea;\n if (!ta) return;\n\n const start = ta.selectionStart ?? ta.value.length;\n const end = ta.selectionEnd ?? ta.value.length;\n ta.value = ta.value.slice(0, start) + str + ta.value.slice(end);\n const newPos = start + str.length;\n ta.setSelectionRange(newPos, newPos);\n // Dispatch 'input' — the textarea's input listener handles syncFormValue(),\n // emit('change'), scheduleHighlight(), scheduleStatusUpdate(), and autocomplete.\n // bubbles: false keeps the synthetic event inside the shadow root so it is not\n // observable on the host element (preventing shadow DOM boundary leakage).\n ta.dispatchEvent(new Event('input', { bubbles: false }));\n }\n\n /**\n * Feed suggestions into the autocomplete dropdown.\n * Pass an empty array to close the dropdown.\n */\n setSuggestions(list: Suggestion[]): void {\n this.#acSuggestions = list;\n this.#acSelectedIndex = list.length > 0 ? 0 : -1;\n this.#updateDropdown();\n }\n}\n\n/** HTML-escape a string for safe insertion into innerHTML. */\nfunction escapeHtmlStr(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n",
8
+ "import { css } from '@duskmoon-dev/el-base';\n\n/**\n * Shadow DOM stylesheet for el-dm-markdown-input.\n *\n * Exposes --md-* custom properties as the external theming API.\n * Each --md-* variable falls back to the corresponding --color-* token\n * from @duskmoon-dev/core so the element automatically adopts the\n * active design-system theme without any consumer configuration.\n */\nexport const elementStyles = css`\n /* ── Custom property defaults with design-system fallbacks ─────────── */\n :host {\n --md-border: var(--color-outline, #d0d7de);\n --md-border-focus: var(--color-primary, #0969da);\n --md-bg: var(--color-surface, #ffffff);\n --md-bg-toolbar: var(--color-surface-variant, #f6f8fa);\n --md-bg-hover: var(--color-surface-container, #eaeef2);\n --md-text: var(--color-on-surface, #1f2328);\n --md-text-muted: var(--color-on-surface-variant, #656d76);\n --md-accent: var(--color-primary, #0969da);\n --md-radius: 6px;\n --md-upload-bar: var(--color-primary, #0969da);\n --md-color-warning: var(--color-warning, #d97706);\n --md-color-error: var(--color-error, #dc2626);\n\n display: block;\n position: relative; /* establishes containing block for the ac-dropdown portal */\n font-family: inherit;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n /* Dark-mode overrides (activated by [dark] attribute on host) */\n :host([dark]) {\n --md-border: #30363d;\n --md-border-focus: #58a6ff;\n --md-bg: #0d1117;\n --md-bg-toolbar: #161b22;\n --md-bg-hover: #21262d;\n --md-text: #e6edf3;\n --md-text-muted: #8b949e;\n --md-accent: #58a6ff;\n --md-upload-bar: #58a6ff;\n --md-color-warning: #f59e0b;\n --md-color-error: #fca5a5;\n }\n\n /* ── Editor chrome ──────────────────────────────────────────────────── */\n .editor {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--md-border);\n border-radius: var(--md-radius);\n background: var(--md-bg);\n color: var(--md-text);\n overflow: hidden;\n height: inherit;\n }\n\n .editor:focus-within {\n border-color: var(--md-border-focus);\n outline: 2px solid var(--md-border-focus);\n outline-offset: -1px;\n }\n\n /* ── Toolbar / tab bar ──────────────────────────────────────────────── */\n .toolbar {\n display: flex;\n gap: 0;\n background: var(--md-bg-toolbar);\n border-bottom: 1px solid var(--md-border);\n padding: 0 0.5rem;\n }\n\n .tab-btn {\n padding: 0.5rem 0.875rem;\n border: none;\n background: transparent;\n color: var(--md-text-muted);\n font-family: inherit;\n font-size: 0.875rem;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n transition:\n color 150ms ease,\n border-color 150ms ease;\n }\n\n .tab-btn:hover {\n color: var(--md-text);\n background: var(--md-bg-hover);\n }\n\n .tab-btn[aria-selected='true'] {\n color: var(--md-text);\n border-bottom-color: var(--md-accent);\n }\n\n .tab-btn:focus-visible {\n outline: 2px solid var(--md-accent);\n outline-offset: -2px;\n border-radius: 3px;\n }\n\n /* ── Write area (render-layer + textarea overlay) ──────────────────── */\n /*\n * CodeMirror-style render model: .render-layer sits in normal flow and\n * drives the container height; the textarea is absolutely positioned on\n * top. No scroll sync required — both layers always share the same size.\n */\n .write-area {\n position: relative;\n min-height: 12rem;\n flex: 1 1 auto;\n }\n\n .write-area[hidden] {\n display: none;\n }\n\n /*\n * Render layer: highlighted HTML in normal flow. Drives container height.\n * pointer-events: none lets clicks pass through to the textarea underneath.\n * Font metrics MUST match the textarea exactly for pixel-aligned overlay.\n */\n .render-layer {\n position: relative;\n z-index: 1;\n pointer-events: none;\n min-height: 12rem;\n font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n font-size: 0.875rem;\n line-height: 1.6;\n padding: 0.75rem;\n white-space: pre-wrap;\n word-wrap: break-word;\n overflow-wrap: break-word;\n color: var(--md-text);\n }\n\n /*\n * Textarea: absolute overlay on top of the render layer. Transparent text\n * lets highlighted content show through; caret-color keeps cursor visible.\n * overflow: hidden — the render layer drives height, not the textarea.\n */\n textarea {\n position: absolute;\n inset: 0;\n z-index: 2;\n display: block;\n width: 100%;\n height: 100%;\n border: none;\n outline: none;\n resize: none;\n background: transparent;\n color: transparent;\n caret-color: var(--md-text);\n box-sizing: border-box;\n overflow: hidden;\n font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n font-size: 0.875rem;\n line-height: 1.6;\n padding: 0.75rem;\n white-space: pre-wrap;\n word-wrap: break-word;\n overflow-wrap: break-word;\n }\n\n textarea::placeholder {\n color: var(--md-text-muted);\n }\n\n textarea:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n /* ── Preview panel ──────────────────────────────────────────────────── */\n .preview-body {\n padding: 0.75rem;\n min-height: 12rem;\n height: stretch;\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n color: var(--md-text);\n /* .markdown-body styles come from @duskmoon-dev/core via the element */\n }\n\n .preview-body[hidden] {\n display: none;\n }\n\n /* ── Preview skeleton (shown while render pipeline loads) ──────────── */\n .preview-skeleton {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n padding: 0.5rem 0;\n }\n\n .skeleton-line {\n height: 0.875rem;\n background: linear-gradient(\n 90deg,\n var(--md-bg-toolbar) 25%,\n var(--md-bg-hover) 50%,\n var(--md-bg-toolbar) 75%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: skeleton-shimmer 1.5s ease-in-out infinite;\n }\n\n @keyframes skeleton-shimmer {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .skeleton-line {\n animation: none;\n background: var(--md-bg-hover);\n }\n }\n\n /* ── Mermaid diagram blocks ────────────────────────────────────────── */\n .mermaid-diagram {\n display: flex;\n justify-content: center;\n margin: 1rem 0;\n overflow-x: auto;\n }\n\n .mermaid-error {\n border-left: 3px solid var(--md-color-error);\n opacity: 0.7;\n position: relative;\n }\n\n .mermaid-error::before {\n content: 'Mermaid render failed';\n display: block;\n font-size: 0.75rem;\n color: var(--md-color-error);\n font-family: inherit;\n margin-bottom: 0.25rem;\n padding-left: 0.5rem;\n }\n\n /* ── Render error fallback ──────────────────────────────────────────── */\n .render-error-fallback {\n white-space: pre-wrap;\n word-wrap: break-word;\n font-size: 0.875rem;\n opacity: 0.8;\n border-left: 3px solid var(--md-color-error);\n padding-left: 0.75rem;\n color: var(--md-text-muted);\n }\n\n .render-error-fallback::before {\n content: 'Preview render failed — showing raw markdown';\n display: block;\n font-size: 0.75rem;\n color: var(--md-color-error);\n font-family: inherit;\n margin-bottom: 0.5rem;\n }\n\n /* ── Status bar ─────────────────────────────────────────────────────── */\n .status-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.375rem 0.75rem;\n border-top: 1px solid var(--md-border);\n background: var(--md-bg-toolbar);\n font-size: 0.75rem;\n color: var(--md-text-muted);\n gap: 0.5rem;\n }\n\n .attach-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.5rem;\n border: none;\n background: transparent;\n color: var(--md-text-muted);\n font-family: inherit;\n font-size: 0.75rem;\n cursor: pointer;\n border-radius: 4px;\n transition:\n color 150ms ease,\n background 150ms ease;\n }\n\n .attach-btn:hover {\n color: var(--md-text);\n background: var(--md-bg-hover);\n }\n\n .attach-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n pointer-events: none;\n }\n\n .attach-btn:focus-visible {\n outline: 2px solid var(--md-accent);\n outline-offset: 1px;\n }\n\n .status-bar-count {\n margin-left: auto;\n white-space: nowrap;\n }\n\n .status-bar-count .warning {\n color: var(--md-color-warning);\n }\n\n .status-bar-count .error {\n color: var(--md-color-error);\n }\n\n .file-input {\n display: none;\n }\n\n /* ── Autocomplete dropdown ──────────────────────────────────────────── */\n /*\n * The dropdown is a direct child of :host (outside .editor) so it is not\n * clipped by .editor's overflow: hidden. :host has position: relative which\n * establishes the containing block for this absolute positioning.\n */\n .ac-dropdown {\n position: absolute;\n z-index: 100;\n left: 0.75rem;\n /* Align to bottom of the editor chrome; the editor fills 100% of :host height */\n bottom: calc(var(--md-status-bar-height, 2rem) + 4px);\n min-width: 16rem;\n max-width: 28rem;\n max-height: 16rem;\n overflow-y: auto;\n margin: 0;\n padding: 0.25rem 0;\n list-style: none;\n background: var(--md-bg);\n border: 1px solid var(--md-border);\n border-radius: var(--md-radius);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n }\n\n .ac-item {\n display: flex;\n flex-direction: column;\n padding: 0.5rem 0.75rem;\n cursor: pointer;\n transition: background 100ms ease;\n }\n\n .ac-item:hover,\n .ac-item[aria-selected='true'] {\n background: var(--md-bg-hover);\n }\n\n .ac-item-label {\n font-size: 0.875rem;\n color: var(--md-text);\n font-weight: 500;\n }\n\n .ac-item-subtitle {\n font-size: 0.75rem;\n color: var(--md-text-muted);\n margin-top: 1px;\n }\n\n /* ── Upload progress rows ───────────────────────────────────────────── */\n .upload-list {\n display: flex;\n flex-direction: column;\n gap: 0;\n }\n\n .upload-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n border-top: 1px solid var(--md-border);\n background: var(--md-bg-toolbar);\n font-size: 0.75rem;\n color: var(--md-text-muted);\n }\n\n .upload-filename {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .upload-bar-track {\n width: 6rem;\n height: 3px;\n background: var(--md-border);\n border-radius: 2px;\n overflow: hidden;\n flex-shrink: 0;\n }\n\n .upload-bar {\n height: 100%;\n background: var(--md-upload-bar);\n border-radius: 2px;\n transition: width 150ms ease;\n }\n\n .upload-error-row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.375rem 0.75rem;\n border-top: 1px solid var(--md-border);\n background: oklch(97% 0.02 25);\n color: var(--md-color-error);\n font-size: 0.75rem;\n }\n\n :host([dark]) .upload-error-row {\n background: oklch(20% 0.03 25);\n }\n\n .upload-error-msg {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ── Reduced motion: disable all transitions and animations ──────── */\n @media (prefers-reduced-motion: reduce) {\n .tab-btn,\n .attach-btn,\n .ac-item,\n .upload-bar {\n transition: none;\n }\n }\n`;\n",
9
+ "/**\n * Prism.js CDN loader + backdrop highlight utilities.\n *\n * Prism is loaded lazily as a UMD script from cdnjs. A module-level Promise\n * caches the load so multiple elements on the same page share one request.\n * If the CDN is unavailable the element degrades gracefully — text entry and\n * form submission continue to work, just without syntax colouring.\n */\n\ndeclare global {\n interface Window {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Prism?: any;\n }\n}\n\nconst PRISM_BASE = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0';\nconst PRISM_CORE_URL = `${PRISM_BASE}/prism.min.js`;\nconst PRISM_AUTOLOADER_URL = `${PRISM_BASE}/plugins/autoloader/prism-autoloader.min.js`;\n\n// Subresource Integrity hashes for Prism 1.29.0 from cdnjs.\n// Update these whenever the CDN URL or version changes.\nconst PRISM_SRI: Record<string, string> = {\n [PRISM_CORE_URL]:\n 'sha512-7Z9J3l1+EYfeaPKcGXu3MS/7T+w19WtKQY/n+xzmw4hZhJ9tyYmcUS+4QqAlzhicE5LAfMQSF3iFTK9bQdTxXg==',\n [PRISM_AUTOLOADER_URL]:\n 'sha512-SkmBfuA2hqjzEVpmnMt/LINrjop3GKWqsuLSSB3e7iBmYK7JuWw4ldmmxwD9mdm2IRTTi0OxSAfEGvgEi0i2Kw==',\n};\nconst PRISM_THEME_DARK_URL = `${PRISM_BASE}/themes/prism-tomorrow.min.css`;\nconst PRISM_THEME_LIGHT_URL = `${PRISM_BASE}/themes/prism-coy.min.css`;\n\n/** Cached load promise — shared across all instances on the page. */\nlet _prismReady: Promise<void> | null = null;\n\n/** Inject a script tag into document.head and resolve when loaded. */\nfunction _loadScript(src: string): Promise<void> {\n return new Promise((resolve) => {\n const script = document.createElement('script');\n script.src = src;\n // Apply Subresource Integrity if available, to guard against CDN compromise.\n const integrity = PRISM_SRI[src];\n if (integrity) {\n script.integrity = integrity;\n script.crossOrigin = 'anonymous';\n }\n script.onload = () => resolve();\n script.onerror = () => resolve(); // resolve even on error (graceful degrade)\n document.head.appendChild(script);\n });\n}\n\n/**\n * Ensure Prism is loaded and ready. Returns a cached Promise after the first call.\n * Safe to call multiple times — only one network request is ever made.\n * If loading fails (e.g. network error), the cache is cleared so the next call retries.\n */\nexport function ensurePrism(): Promise<void> {\n if (window.Prism) return Promise.resolve();\n if (_prismReady) return _prismReady;\n\n _prismReady = _loadScript(PRISM_CORE_URL).then(() => {\n if (!window.Prism) {\n // Script failed to load — clear cache so next call retries\n _prismReady = null;\n return;\n }\n // Configure autoloader before loading it\n window.Prism.manual = true;\n return _loadScript(PRISM_AUTOLOADER_URL).then(() => {\n if (window.Prism?.plugins?.autoloader) {\n // Security note: individual language grammar scripts (e.g. prism-python.min.js)\n // loaded by the autoloader are fetched from the same cdnjs base URL but without\n // per-file SRI hashes. A compromised CDN could serve malicious grammar scripts.\n // Acceptable trade-off for syntax highlighting; mitigated by SRI on core + autoloader.\n // TODO: consider bundling commonly-used grammars statically to remove this gap.\n window.Prism.plugins.autoloader.languages_path = `${PRISM_BASE}/components/`;\n }\n // Pre-load the markdown grammar so highlightMarkdown() works immediately\n // when ensurePrism() resolves. The autoloader only fetches grammars on\n // demand (when it sees a language-xxx class), so without this the first\n // call to highlightMarkdown() always falls back to plain text.\n return _loadScript(`${PRISM_BASE}/components/prism-markdown.min.js`);\n });\n });\n\n return _prismReady;\n}\n\n/**\n * Escape HTML special characters in the given text.\n * Escapes & first to prevent double-escaping.\n */\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\n/**\n * Highlight markdown text using Prism and return an HTML string.\n * If Prism is not available, returns the HTML-escaped text unchanged.\n *\n * Appends a non-breaking space to prevent the backdrop div from collapsing\n * when the textarea value ends with a newline.\n */\nexport function highlightMarkdown(text: string): string {\n const escaped = escapeHtml(text);\n\n if (!window.Prism?.languages?.markdown) {\n // Prism not ready yet — return escaped plain text\n return escaped + '\\u00a0';\n }\n\n try {\n const highlighted = window.Prism.highlight(text, window.Prism.languages.markdown, 'markdown');\n return highlighted + '\\u00a0';\n } catch {\n return escaped + '\\u00a0';\n }\n}\n\n/**\n * Inject or update a Prism syntax theme inside the given shadow root.\n * Uses a <style id=\"prism-theme\"> element with an @import so the browser\n * caches the CDN stylesheet normally.\n */\nexport function applyPrismTheme(shadowRoot: ShadowRoot, dark: boolean): void {\n const themeUrl = dark ? PRISM_THEME_DARK_URL : PRISM_THEME_LIGHT_URL;\n let styleEl = shadowRoot.getElementById('prism-theme') as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = 'prism-theme';\n shadowRoot.appendChild(styleEl);\n }\n\n // Rules after @import beat the imported stylesheet for equal specificity,\n // so these overrides neutralise prism-coy / prism-tomorrow's hardcoded\n // pre/code backgrounds and borders inside the preview panel, letting the\n // markdown-body styles from @duskmoon-dev/core control the visual frame.\n // Only Prism token colours (.token.*) are preserved.\n const previewOverrides = `\n.preview-body pre[class*=\"language-\"] {\n background: transparent;\n margin: 0;\n padding: 0;\n overflow: visible;\n position: static;\n}\n.preview-body pre[class*=\"language-\"] > code {\n background: transparent;\n border: none;\n box-shadow: none;\n padding: 0;\n background-image: none;\n display: block;\n overflow: auto;\n max-height: none;\n height: auto;\n}`;\n\n const expected = `@import url(\"${themeUrl}\");${previewOverrides}`;\n if (styleEl.textContent !== expected) {\n styleEl.textContent = expected;\n }\n}\n",
10
+ "/**\n * File upload utilities for el-dm-markdown-input.\n *\n * Handles XHR-based multipart/form-data uploads, markdown snippet generation,\n * and accepted file type validation.\n */\n\n/** Accepted MIME types and extensions (aligned with PRD). */\nconst ACCEPTED_MIME_PREFIXES = ['image/'];\nconst ACCEPTED_MIME_EXACT = ['application/pdf'];\nconst ACCEPTED_EXTENSIONS = ['.zip', '.txt', '.csv', '.json', '.md'];\n\n/**\n * Returns true if the file's MIME type or name extension is accepted.\n */\nexport function isAcceptedType(file: File): boolean {\n const type = file.type.toLowerCase();\n if (ACCEPTED_MIME_PREFIXES.some((p) => type.startsWith(p))) return true;\n if (ACCEPTED_MIME_EXACT.includes(type)) return true;\n const name = file.name.toLowerCase();\n if (ACCEPTED_EXTENSIONS.some((ext) => name.endsWith(ext))) return true;\n return false;\n}\n\n/**\n * Generate the markdown insertion string for an uploaded file.\n * Images use `![name](url)`, all other files use `[name](url)`.\n *\n * Filenames and URLs are escaped to prevent markdown syntax injection:\n * - `[` and `]` in filenames are backslash-escaped\n * - `(` and `)` in URLs are percent-encoded\n *\n * Only `https:` and relative URLs are accepted. Dangerous schemes like\n * `javascript:` or `data:` from a compromised upload endpoint are rejected\n * and replaced with `#unsafe-url` so the markdown is never persisted with\n * an executable URL.\n */\nexport function fileToMarkdown(file: File, url: string): string {\n // Reject non-https absolute URLs (e.g. javascript:, data:, file:)\n const isSafeUrl = /^https:\\/\\//i.test(url) || /^\\//.test(url) || /^\\.\\.?\\//.test(url);\n const safeUrl = isSafeUrl ? url.replace(/\\(/g, '%28').replace(/\\)/g, '%29') : '#unsafe-url';\n\n const safeName = file.name.replace(/[[\\]]/g, '\\\\$&');\n if (file.type.startsWith('image/')) {\n return `![${safeName}](${safeUrl})`;\n }\n return `[${safeName}](${safeUrl})`;\n}\n\n/**\n * Upload a single file to the given URL via XHR POST multipart/form-data.\n *\n * @param file The file to upload\n * @param uploadUrl POST endpoint — must be an https: or relative URL.\n * Must return `{ url: string }` on 2xx.\n * @param onProgress Callback fired with progress 0–100 during upload\n * @returns Resolves with the URL from the server response\n * @throws Rejects with an Error on failure or invalid URL\n */\nexport function uploadFile(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<string> {\n // Validate the upload URL scheme to prevent SSRF and accidental data exfiltration.\n // Only https: and relative URLs are accepted (same policy as fileToMarkdown).\n const isSafeUploadUrl =\n /^https:\\/\\//i.test(uploadUrl) || /^\\//.test(uploadUrl) || /^\\.\\.?\\//.test(uploadUrl);\n if (!isSafeUploadUrl) {\n return Promise.reject(\n new Error(\n `[el-dm-markdown-input] upload-url \"${uploadUrl}\" rejected — only https: and relative URLs are allowed.`,\n ),\n );\n }\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n const body = new FormData();\n body.append('file', file);\n\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) {\n onProgress(Math.round((e.loaded / e.total) * 100));\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const data = JSON.parse(xhr.responseText) as { url?: string };\n if (data.url) {\n resolve(data.url);\n } else {\n reject(new Error('Upload response missing url field'));\n }\n } catch {\n reject(new Error('Upload response is not valid JSON'));\n }\n } else {\n reject(new Error(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener('error', () => reject(new Error('Network error during upload')));\n xhr.addEventListener('abort', () => reject(new Error('Upload aborted')));\n\n xhr.open('POST', uploadUrl);\n xhr.send(body);\n });\n}\n",
11
+ "/**\n * Autocomplete utilities for @mention and #reference detection.\n */\n\nimport type { Suggestion } from './types.js';\n\n/**\n * Scan backward from cursorPos through value to detect an active @word or #word trigger.\n *\n * Rules:\n * - Scan character by character backward from cursor\n * - If a whitespace or newline is encountered before a trigger char → no trigger\n * - If @ or # is found → extract the query (text between trigger and cursor)\n * - The query must not contain any whitespace\n *\n * @returns Trigger info or null if no active trigger\n */\nexport function detectTrigger(\n value: string,\n cursorPos: number,\n): { trigger: '@' | '#'; query: string; triggerPos: number } | null {\n let i = cursorPos - 1;\n\n while (i >= 0) {\n const ch = value[i];\n\n if (ch === '@' || ch === '#') {\n const query = value.slice(i + 1, cursorPos);\n // Only activate if there's no whitespace in the query\n if (!/\\s/.test(query)) {\n // Make sure the char before the trigger is whitespace, start-of-string, or trigger is at position 0\n const before = i > 0 ? value[i - 1] : null;\n if (before === null || /\\s/.test(before)) {\n return { trigger: ch as '@' | '#', query, triggerPos: i };\n }\n }\n return null;\n }\n\n if (/\\s/.test(ch)) {\n // Hit whitespace before finding a trigger\n return null;\n }\n\n i--;\n }\n\n return null;\n}\n\n/**\n * Replace the trigger+query span in value with the confirmed replacement.\n *\n * For a mention: `@ali` → `@asmith` (trigger is preserved in replacement)\n * The replacement value should NOT include the trigger prefix — we add it.\n *\n * @param value Full textarea value\n * @param triggerPos Index of the @ or # character\n * @param cursorPos Current cursor position (end of query)\n * @param trigger The trigger character used\n * @param replacement The id from the selected suggestion\n * @returns New value and new cursor position\n */\nexport function confirmSuggestion(\n value: string,\n triggerPos: number,\n cursorPos: number,\n trigger: '@' | '#',\n replacement: string,\n): { newValue: string; newCursorPos: number } {\n const before = value.slice(0, triggerPos);\n const after = value.slice(cursorPos);\n const inserted = `${trigger}${replacement}`;\n // Add a trailing space so the user can continue typing immediately,\n // unless the next character is already a space or we're at end of line\n const needsSpace = after.length === 0 || (after[0] !== ' ' && after[0] !== '\\n');\n const suffix = needsSpace ? ' ' : '';\n const newValue = before + inserted + suffix + after;\n const newCursorPos = triggerPos + inserted.length + suffix.length;\n return { newValue, newCursorPos };\n}\n\n/**\n * Render the HTML for the autocomplete dropdown list.\n *\n * @param suggestions Current suggestion list\n * @param selectedIndex 0-based highlighted item (-1 = none)\n */\nexport function renderDropdown(suggestions: Suggestion[], selectedIndex: number): string {\n if (suggestions.length === 0) return '';\n\n const items = suggestions\n .map((s, i) => {\n const selected = i === selectedIndex ? ' aria-selected=\"true\"' : ' aria-selected=\"false\"';\n const subtitle = s.subtitle\n ? `<span class=\"ac-item-subtitle\">${escapeHtml(s.subtitle)}</span>`\n : '';\n return `<li id=\"ac-item-${i}\" class=\"ac-item\" role=\"option\" data-ac-index=\"${i}\"${selected}>\n <span class=\"ac-item-label\">${escapeHtml(s.label)}</span>${subtitle}\n </li>`;\n })\n .join('');\n\n return items;\n}\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n",
12
+ "/**\n * Smart pair insertion and list/heading continuation for the markdown editor.\n *\n * Two behaviors:\n * 1. Backtick pairing — ` → `|` or wraps selection; ``` → fenced block\n * 2. Enter continuation — carries list bullets (*) or breaks out of headings\n */\n\n// ─── Backtick pairing ────────────────────────────────────────────────────────\n\n/**\n * Handle a keypress that may trigger smart pair insertion.\n * Currently only `` ` `` is paired; returns false for all other keys.\n *\n * @returns true if the event was handled (caller should preventDefault)\n */\nexport function handlePairKey(ta: HTMLTextAreaElement, key: string): boolean {\n if (key !== '`') return false;\n\n const start = ta.selectionStart;\n const end = ta.selectionEnd;\n const value = ta.value;\n\n // Selection: wrap the selected text in backticks\n if (start !== end) {\n const selected = value.slice(start, end);\n ta.value = value.slice(0, start) + '`' + selected + '`' + value.slice(end);\n ta.setSelectionRange(start + 1, end + 1);\n return true;\n }\n\n // Triple-backtick: two `` already before cursor → create fenced code block\n if (start >= 2 && value.slice(start - 2, start) === '``') {\n // Result: ```\\n<cursor>\\n```\n ta.value = value.slice(0, start) + '`\\n\\n```' + value.slice(end);\n ta.setSelectionRange(start + 2, start + 2);\n return true;\n }\n\n // Default: insert a closing backtick and leave cursor between them\n ta.value = value.slice(0, start) + '``' + value.slice(end);\n ta.setSelectionRange(start + 1, start + 1);\n return true;\n}\n\n// ─── Enter continuation ───────────────────────────────────────────────────────\n\n/**\n * Handle the Enter key with smart list/heading continuation.\n *\n * @returns true if the event was handled (caller should preventDefault)\n */\nexport function handleEnterKey(ta: HTMLTextAreaElement, e: KeyboardEvent): boolean {\n if (e.key !== 'Enter') return false;\n\n const pos = ta.selectionStart;\n const value = ta.value;\n\n // Only act on collapsed cursor (no selection)\n if (ta.selectionEnd !== pos) return false;\n\n // Determine the content of the current line up to the cursor\n const lineStart = value.lastIndexOf('\\n', pos - 1) + 1;\n const currentLine = value.slice(lineStart, pos);\n\n const result = getLineContinuation(currentLine);\n if (result === null) return false;\n\n e.preventDefault();\n\n if (result.eraseCurrentLine) {\n // Remove the current line's prefix — don't add '\\n'; value.slice(pos)\n // already starts with it (if content follows) or the line is at EOF.\n const newValue = value.slice(0, lineStart) + value.slice(pos);\n ta.value = newValue;\n ta.setSelectionRange(lineStart, lineStart);\n } else {\n // Insert newline + continuation prefix\n const newValue = value.slice(0, pos) + '\\n' + result.prefix + value.slice(ta.selectionEnd);\n const newPos = pos + 1 + result.prefix.length;\n ta.value = newValue;\n ta.setSelectionRange(newPos, newPos);\n }\n\n return true;\n}\n\n// ─── Continuation policy ─────────────────────────────────────────────────────\n\ntype ContinuationResult = { prefix: string; eraseCurrentLine: false } | { eraseCurrentLine: true };\n\n/**\n * Given the text of the current line (from line-start to cursor), decide what\n * to insert when the user presses Enter.\n *\n * Return value:\n * - null → do nothing (fall through to native Enter)\n * - { prefix, eraseCurrentLine: false } → insert \"\\n\" + prefix\n * - { eraseCurrentLine: true } → erase the current line's bullet prefix (no \"\\n\" inserted)\n *\n * Rules to implement:\n * - `* text` → continue with `* ` on the next line\n * - `* ` → empty bullet: erase the `* ` and break out of the list\n * - `# text`, `## text`, `### text` (heading with content) → plain newline (no heading prefix)\n * - anything else → null (native Enter)\n *\n * Constraints:\n * - Ordered lists (`1. `, `2. `) are out of scope for now.\n * - Blockquote (`> `) continuation is a bonus if you want it.\n */\nexport function getLineContinuation(line: string): ContinuationResult | null {\n // Empty unordered bullet → break out of list\n if (line === '* ') return { eraseCurrentLine: true };\n\n // Unordered list with content → continue bullet on next line\n if (/^\\* ./.test(line)) return { prefix: '* ', eraseCurrentLine: false };\n\n // ATX heading (any level) → plain newline, no heading continuation\n if (/^#{1,6} ./.test(line)) return { prefix: '', eraseCurrentLine: false };\n\n return null;\n}\n",
9
13
  "/**\n * Status bar utilities: word/character counting and colour thresholds.\n */\n\n/**\n * Count words in a string using the algorithm specified in the PRD:\n * trim, split on whitespace, filter empty strings.\n */\nexport function countWords(text: string): number {\n if (!text.trim()) return 0;\n return text.trim().split(/\\s+/).filter(Boolean).length;\n}\n\n/**\n * Determine the CSS colour class for the word count display.\n *\n * @param wordCount Current word count\n * @param maxWords The configured maximum, or null if uncapped\n * @returns 'normal' | 'warning' | 'error'\n */\nexport function countColour(\n wordCount: number,\n maxWords: number | null,\n): 'normal' | 'warning' | 'error' {\n if (!maxWords) return 'normal';\n const pct = (wordCount / maxWords) * 100;\n if (pct >= 100) return 'error';\n if (pct >= 90) return 'warning';\n return 'normal';\n}\n\n/**\n * Render the HTML content for the .status-bar-count span.\n *\n * @param wordCount Current word count\n * @param charCount Current character count\n * @param maxWords Configured cap, or null/undefined if uncapped\n */\nexport function renderStatusCount(\n wordCount: number,\n charCount: number,\n maxWords: number | null | undefined,\n): string {\n const cap = maxWords ?? null;\n const colour = countColour(wordCount, cap);\n const colourClass = colour !== 'normal' ? ` class=\"${colour}\"` : '';\n\n if (cap) {\n return `<span${colourClass}>${wordCount} / ${cap} words · ${charCount} chars</span>`;\n }\n return `<span>${wordCount} words · ${charCount} chars</span>`;\n}\n",
10
- "/**\n * DuskMoon Markdown Input Element\n *\n * A form-associated custom element providing a markdown editor with:\n * - Write tab with syntax-highlighted backdrop (Prism.js CDN)\n * - Preview tab with rendered HTML (.markdown-body from @duskmoon-dev/core)\n * - File upload via drag-and-drop, clipboard paste, or file picker\n * - @mention / #reference autocomplete dropdown\n * - Live word / character count status bar\n * - Phoenix LiveView hook (MarkdownInputHook, exported from index.ts)\n *\n * @element el-dm-markdown-input\n *\n * @attr {string} name Form field name\n * @attr {string} value Initial markdown content\n * @attr {string} placeholder Textarea placeholder (default: \"Write markdown…\")\n * @attr {boolean} disabled Disables editing\n * @attr {boolean} readonly Makes the editor read-only (value still submitted)\n * @attr {string} upload-url POST endpoint for file uploads\n * @attr {number} max-words Soft word cap shown in status bar\n * @attr {boolean} dark Activates dark Prism theme + dark CSS variable defaults\n *\n * @fires change `{ value: string }` — on every input\n * @fires upload-start `{ file: File }` — when a file is accepted\n * @fires upload-done `{ file: File, url: string, markdown: string }` — on success\n * @fires upload-error `{ file: File, error: string }` — on failure\n * @fires mention-query `{ trigger: \"@\", query, resolve }` — on @word input\n * @fires reference-query `{ trigger: \"#\", query, resolve }` — on #word input\n */\n\nimport { BaseElement } from '@duskmoon-dev/el-base';\nimport { css as markdownBodyCSS } from '@duskmoon-dev/core/components/markdown-body';\n\nimport { elementStyles } from './css.js';\nimport { ensurePrism, highlightMarkdown, applyPrismTheme } from './highlight.js';\nimport { uploadFile, fileToMarkdown, isAcceptedType } from './upload.js';\nimport { detectTrigger, confirmSuggestion, renderDropdown } from './autocomplete.js';\nimport { countWords, renderStatusCount } from './status-bar.js';\nimport type { Suggestion } from './types.js';\n\n// Strip @layer wrapper for Shadow DOM compatibility\nconst coreMarkdownStyles = markdownBodyCSS\n .replace(/@layer\\s+components\\s*\\{/, '')\n .replace(/\\}\\s*$/, '');\n\n// Inject core markdown-body styles as a constructable stylesheet\nimport { css } from '@duskmoon-dev/el-base';\nconst markdownBodySheet = css`\n ${coreMarkdownStyles}\n`;\n\nexport class ElDmMarkdownInput extends BaseElement {\n static formAssociated = true as const;\n\n static properties = {\n name: { type: String, reflect: true, default: '' },\n value: { type: String, default: '' },\n placeholder: { type: String, reflect: true, default: 'Write markdown\\u2026' },\n disabled: { type: Boolean, reflect: true },\n readonly: { type: Boolean, reflect: true },\n uploadUrl: { type: String, reflect: true, attribute: 'upload-url' },\n maxWords: { type: Number, reflect: true, attribute: 'max-words' },\n dark: { type: Boolean, reflect: true },\n };\n\n declare name: string;\n declare value: string;\n declare placeholder: string;\n declare disabled: boolean;\n declare readonly: boolean;\n declare uploadUrl: string | undefined;\n declare maxWords: number | undefined;\n declare dark: boolean;\n\n // ── ElementInternals for form association ────────────────────────────\n #internals!: ElementInternals;\n\n // ── Rendering state ──────────────────────────────────────────────────\n /** True after the first full render has populated the shadow DOM. */\n #initialized = false;\n\n /** Which tab is currently active. */\n #activeTab: 'write' | 'preview' = 'write';\n\n // ── Debounce timers ──────────────────────────────────────────────────\n #highlightTimer: ReturnType<typeof setTimeout> | null = null;\n #statusTimer: ReturnType<typeof setTimeout> | null = null;\n\n // ── DOM element refs (set after first render) ────────────────────────\n #textarea: HTMLTextAreaElement | null = null;\n #backdrop: HTMLElement | null = null;\n #backdropContent: HTMLElement | null = null;\n #writeArea: HTMLElement | null = null;\n #previewBody: HTMLElement | null = null;\n #statusCount: HTMLElement | null = null;\n #acDropdown: HTMLElement | null = null;\n #uploadList: HTMLElement | null = null;\n #fileInput: HTMLInputElement | null = null;\n\n // ── Resize observer ──────────────────────────────────────────────────\n #resizeObserver: ResizeObserver | null = null;\n\n // ── Autocomplete state ───────────────────────────────────────────────\n #acSuggestions: Suggestion[] = [];\n #acSelectedIndex = -1;\n #acTriggerPos = -1;\n #acTrigger: '@' | '#' | null = null;\n\n // ── Upload state ─────────────────────────────────────────────────────\n #uploadIdCounter = 0;\n\n constructor() {\n super();\n // attachInternals() must be called in the constructor per HTML spec\n this.#internals = this.attachInternals();\n this.attachStyles([elementStyles, markdownBodySheet]);\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Lifecycle\n // ════════════════════════════════════════════════════════════════════\n\n connectedCallback(): void {\n super.connectedCallback(); // triggers initial update() → render()\n\n // Set initial form value from the reactive `value` property\n const initial = (this as unknown as { value: string }).value ?? '';\n if (initial && this.#textarea) {\n this.#textarea.value = initial;\n this.#syncFormValue();\n }\n }\n\n disconnectedCallback(): void {\n this.#resizeObserver?.disconnect();\n super.disconnectedCallback();\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Rendering — override update() to protect the textarea after init\n // ════════════════════════════════════════════════════════════════════\n\n protected override update(): void {\n if (!this.#initialized) {\n // Full initial render — creates all DOM nodes\n super.update(); // calls render() → shadowRoot.innerHTML = content\n this.#initialized = true;\n this.#cacheDOMRefs();\n this.#attachEventHandlers();\n this.#initHighlight();\n this.#updateStatusBarNow();\n\n // Restore initial value from reactive prop\n const initVal = (this as unknown as { value: string }).value ?? '';\n if (initVal && this.#textarea) {\n this.#textarea.value = initVal;\n this.#syncFormValue();\n this.#scheduleHighlight();\n }\n return;\n }\n\n // Incremental update — patch only specific DOM regions\n this.#patchDynamicRegions();\n }\n\n /**\n * Patch DOM regions that can change due to reactive property updates,\n * without replacing the textarea (which would lose state).\n */\n #patchDynamicRegions(): void {\n const ta = this.#textarea;\n if (!ta) return;\n\n // Sync simple attributes\n const placeholder =\n (this as unknown as { placeholder: string }).placeholder ?? 'Write markdown\\u2026';\n ta.placeholder = placeholder;\n ta.disabled = !!(this as unknown as { disabled: boolean }).disabled;\n ta.readOnly = !!(this as unknown as { readonly: boolean }).readonly;\n\n const attachBtn = this.shadowRoot.querySelector<HTMLButtonElement>('.attach-btn');\n if (attachBtn) {\n attachBtn.disabled = ta.disabled || ta.readOnly;\n }\n\n // Sync value if the reactive prop was updated externally (e.g. attributeChangedCallback)\n const propVal = (this as unknown as { value: string }).value ?? '';\n if (propVal !== ta.value) {\n ta.value = propVal;\n this.#syncFormValue();\n this.#scheduleHighlight();\n }\n\n // Update Prism theme when dark attribute changes\n const dark = !!(this as unknown as { dark: boolean }).dark;\n applyPrismTheme(this.shadowRoot, dark);\n\n // Re-render status bar (maxWords may have changed)\n this.#updateStatusBarNow();\n }\n\n protected override render(): string {\n const ph = (this as unknown as { placeholder: string }).placeholder ?? 'Write markdown\\u2026';\n const disabled = !!(this as unknown as { disabled: boolean }).disabled;\n const readonly = !!(this as unknown as { readonly: boolean }).readonly;\n\n return `\n <div class=\"editor\">\n <div class=\"toolbar\" role=\"tablist\" aria-label=\"Editor mode\">\n <button\n class=\"tab-btn\"\n data-tab=\"write\"\n role=\"tab\"\n aria-selected=\"true\"\n aria-controls=\"write-panel\"\n >Write</button>\n <button\n class=\"tab-btn\"\n data-tab=\"preview\"\n role=\"tab\"\n aria-selected=\"false\"\n aria-controls=\"preview-panel\"\n >Preview</button>\n </div>\n\n <div class=\"write-area\" id=\"write-panel\" role=\"tabpanel\" aria-label=\"Markdown editor\">\n <div class=\"backdrop\" aria-hidden=\"true\">\n <div class=\"backdrop-content\"></div>\n </div>\n <textarea\n aria-label=\"Markdown editor\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n aria-controls=\"ac-dropdown\"\n placeholder=\"${ph}\"\n ${disabled ? 'disabled' : ''}\n ${readonly ? 'readonly' : ''}\n spellcheck=\"false\"\n autocomplete=\"off\"\n autocorrect=\"off\"\n autocapitalize=\"off\"\n ></textarea>\n </div>\n\n <div\n class=\"preview-body markdown-body\"\n id=\"preview-panel\"\n role=\"tabpanel\"\n aria-label=\"Markdown preview\"\n hidden\n ></div>\n\n <div class=\"status-bar\">\n <button class=\"attach-btn\" type=\"button\" aria-label=\"Attach files\" ${disabled || readonly ? 'disabled' : ''}>\n &#128206; Attach files\n </button>\n <span class=\"status-bar-count\" aria-live=\"polite\"></span>\n <input\n type=\"file\"\n class=\"file-input\"\n multiple\n accept=\"image/*,application/pdf,.zip,.txt,.csv,.json,.md\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n >\n </div>\n\n <div class=\"upload-list\"></div>\n </div>\n <ul id=\"ac-dropdown\" class=\"ac-dropdown\" role=\"listbox\" aria-label=\"Suggestions\" hidden></ul>\n `;\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Post-render setup\n // ════════════════════════════════════════════════════════════════════\n\n #cacheDOMRefs(): void {\n this.#textarea = this.shadowRoot.querySelector('textarea');\n this.#backdrop = this.shadowRoot.querySelector('.backdrop');\n this.#backdropContent = this.shadowRoot.querySelector('.backdrop-content');\n this.#writeArea = this.shadowRoot.querySelector('.write-area');\n this.#previewBody = this.shadowRoot.querySelector('.preview-body');\n this.#statusCount = this.shadowRoot.querySelector('.status-bar-count');\n this.#acDropdown = this.shadowRoot.querySelector('.ac-dropdown');\n this.#uploadList = this.shadowRoot.querySelector('.upload-list');\n this.#fileInput = this.shadowRoot.querySelector('.file-input');\n }\n\n #attachEventHandlers(): void {\n const ta = this.#textarea;\n if (!ta) return;\n\n // ── Textarea input ─────────────────────────────────────────────\n ta.addEventListener('input', () => {\n this.#syncFormValue();\n this.emit('change', { value: ta.value });\n this.#scheduleHighlight();\n this.#scheduleStatusUpdate();\n this.#handleAutocompleteInput();\n });\n\n // ── Scroll sync (backdrop must follow textarea scroll) ─────────\n ta.addEventListener('scroll', () => {\n if (this.#backdrop) {\n this.#backdrop.scrollTop = ta.scrollTop;\n this.#backdrop.scrollLeft = ta.scrollLeft;\n }\n });\n\n // ── Close dropdown when focus leaves the textarea ──────────────\n ta.addEventListener('blur', () => {\n // Delay to allow pointer events on dropdown items to fire first\n setTimeout(() => {\n if (!this.shadowRoot?.activeElement) {\n this.#closeDropdown();\n }\n }, 150);\n });\n\n // ── Tab key for autocomplete (prevent default only when dropdown open) ──\n ta.addEventListener('keydown', (e) => {\n if (this.#acSuggestions.length > 0 && !this.#acDropdown?.hidden) {\n this.#handleDropdownKeydown(e);\n }\n // Ctrl+Shift+P → toggle preview (T023)\n if (e.ctrlKey && e.shiftKey && e.key === 'P') {\n e.preventDefault();\n this.#switchTab(this.#activeTab === 'write' ? 'preview' : 'write');\n }\n });\n\n // ── Drag and drop ──────────────────────────────────────────────\n const writeArea = this.#writeArea;\n if (writeArea) {\n writeArea.addEventListener('dragover', (e) => {\n e.preventDefault();\n writeArea.style.opacity = '0.8';\n });\n writeArea.addEventListener('dragleave', () => {\n writeArea.style.opacity = '';\n });\n writeArea.addEventListener('drop', (e) => {\n e.preventDefault();\n writeArea.style.opacity = '';\n if ((this as unknown as { readonly: boolean }).readonly) return;\n const files = Array.from(e.dataTransfer?.files ?? []).filter(isAcceptedType);\n files.forEach((f) => this.#startUpload(f));\n });\n }\n\n // ── Clipboard paste (images only) ─────────────────────────────\n ta.addEventListener('paste', (e) => {\n if ((this as unknown as { readonly: boolean }).readonly) return;\n const imageFiles = Array.from(e.clipboardData?.files ?? []).filter((f) =>\n f.type.startsWith('image/'),\n );\n if (imageFiles.length > 0) {\n e.preventDefault();\n imageFiles.forEach((f) => this.#startUpload(f));\n }\n });\n\n // ── Tab buttons ────────────────────────────────────────────────\n const toolbar = this.shadowRoot.querySelector('.toolbar');\n toolbar?.addEventListener('click', (e) => {\n const btn = (e.target as HTMLElement).closest<HTMLElement>('.tab-btn');\n const tab = btn?.dataset.tab as 'write' | 'preview' | undefined;\n if (tab) this.#switchTab(tab);\n });\n\n // ── Attach button ──────────────────────────────────────────────\n const attachBtn = this.shadowRoot.querySelector('.attach-btn');\n attachBtn?.addEventListener('click', () => this.#fileInput?.click());\n\n this.#fileInput?.addEventListener('change', () => {\n const files = Array.from(this.#fileInput?.files ?? []).filter(isAcceptedType);\n files.forEach((f) => this.#startUpload(f));\n if (this.#fileInput) this.#fileInput.value = '';\n });\n\n // ── Autocomplete dropdown click delegation ─────────────────────\n this.#acDropdown?.addEventListener('click', (e) => {\n const item = (e.target as HTMLElement).closest<HTMLElement>('[data-ac-index]');\n if (item) {\n const idx = parseInt(item.dataset.acIndex ?? '-1', 10);\n if (idx >= 0) {\n this.#acSelectedIndex = idx;\n this.#confirmAutocomplete();\n }\n }\n });\n\n // ── ResizeObserver: mirror textarea dimensions to backdrop ─────\n if (typeof ResizeObserver !== 'undefined') {\n this.#resizeObserver = new ResizeObserver(() => {\n if (this.#backdrop && this.#textarea) {\n this.#backdrop.style.height = `${this.#textarea.offsetHeight}px`;\n }\n });\n this.#resizeObserver.observe(ta);\n }\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Highlight (Write tab backdrop)\n // ════════════════════════════════════════════════════════════════════\n\n #initHighlight(): void {\n const dark = !!(this as unknown as { dark: boolean }).dark;\n applyPrismTheme(this.shadowRoot, dark);\n ensurePrism().then(() => {\n // Highlight immediately once Prism is ready\n if (this.#textarea && this.#backdropContent) {\n this.#backdropContent.innerHTML = highlightMarkdown(this.#textarea.value);\n }\n });\n }\n\n #scheduleHighlight(): void {\n if (this.#highlightTimer !== null) clearTimeout(this.#highlightTimer);\n this.#highlightTimer = setTimeout(() => {\n this.#highlightTimer = null;\n if (this.#backdropContent && this.#textarea) {\n this.#backdropContent.innerHTML = highlightMarkdown(this.#textarea.value);\n }\n // Sync scroll after highlight (content size may change)\n if (this.#backdrop && this.#textarea) {\n this.#backdrop.scrollTop = this.#textarea.scrollTop;\n }\n }, 60);\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Tab switching (Write ↔ Preview)\n // ════════════════════════════════════════════════════════════════════\n\n #switchTab(tab: 'write' | 'preview'): void {\n if (tab === this.#activeTab) return;\n this.#activeTab = tab;\n\n const writeBtns = this.shadowRoot.querySelectorAll<HTMLElement>('.tab-btn');\n writeBtns.forEach((btn) => {\n const isActive = btn.dataset.tab === tab;\n btn.setAttribute('aria-selected', String(isActive));\n });\n\n if (tab === 'preview') {\n this.#writeArea?.setAttribute('hidden', '');\n if (this.#previewBody) {\n this.#previewBody.removeAttribute('hidden');\n this.#previewBody.innerHTML = this.#renderMarkdown(this.#textarea?.value ?? '');\n }\n } else {\n this.#writeArea?.removeAttribute('hidden');\n this.#previewBody?.setAttribute('hidden', '');\n }\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Minimal markdown renderer (preview tab)\n // ════════════════════════════════════════════════════════════════════\n\n #renderMarkdown(md: string): string {\n if (!md.trim()) return '<p></p>';\n\n // Step 1: Extract fenced code blocks\n // Use a placeholder string that won't appear in markdown content\n const CB_PH = '\\u2060CB';\n const IC_PH = '\\u2060IC';\n const codeBlocks: string[] = [];\n let html = md.replace(/```(\\w*)\\n?([\\s\\S]*?)```/g, (_, lang, code) => {\n const escaped = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n const idx =\n codeBlocks.push(`<pre><code class=\"language-${lang || 'text'}\">${escaped}</code></pre>`) -\n 1;\n return `${CB_PH}${idx}${CB_PH}`;\n });\n\n // Step 2: Extract inline code\n const inlineCodes: string[] = [];\n html = html.replace(/`([^`\\n]+)`/g, (_, code) => {\n const escaped = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n const idx = inlineCodes.push(`<code>${escaped}</code>`) - 1;\n return `${IC_PH}${idx}${IC_PH}`;\n });\n\n // Step 3: HTML-escape remaining content\n html = html.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n\n // Step 4: Markdown transformations\n // Images before links — sanitize URLs to block javascript: and other unsafe protocols\n html = html.replace(\n /!\\[([^\\]]*)\\]\\(([^)]+)\\)/g,\n (_, alt, url) => `<img src=\"${sanitizeUrl(url)}\" alt=\"${alt}\">`,\n );\n html = html.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n (_, text, url) => `<a href=\"${sanitizeUrl(url)}\">${text}</a>`,\n );\n\n // Headings\n html = html\n .replace(/^###### (.+)$/gm, '<h6>$1</h6>')\n .replace(/^##### (.+)$/gm, '<h5>$1</h5>')\n .replace(/^#### (.+)$/gm, '<h4>$1</h4>')\n .replace(/^### (.+)$/gm, '<h3>$1</h3>')\n .replace(/^## (.+)$/gm, '<h2>$1</h2>')\n .replace(/^# (.+)$/gm, '<h1>$1</h1>');\n\n // Horizontal rules\n html = html.replace(/^[-*_]{3,}$/gm, '<hr>');\n\n // Blockquotes (note: > is escaped to &gt; above)\n html = html.replace(/^&gt; (.+)$/gm, '<blockquote>$1</blockquote>');\n\n // Bold + italic (order matters: *** before ** before *)\n html = html\n .replace(/\\*\\*\\*(.+?)\\*\\*\\*/g, '<strong><em>$1</em></strong>')\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\*(.+?)\\*/g, '<em>$1</em>')\n .replace(/___(.+?)___/g, '<strong><em>$1</em></strong>')\n .replace(/__(.+?)__/g, '<strong>$1</strong>')\n .replace(/_(.+?)_/g, '<em>$1</em>');\n\n // Strikethrough\n html = html.replace(/~~(.+?)~~/g, '<del>$1</del>');\n\n // Lists (single-level)\n // Tag items with a type marker to distinguish ul from ol, then group consecutive runs\n html = html.replace(/^[ \\t]*[-*+] (.+)$/gm, '<li data-list=\"ul\">$1</li>');\n html = html.replace(/^[ \\t]*\\d+\\. (.+)$/gm, '<li data-list=\"ol\">$1</li>');\n // Group consecutive unordered items into <ul>\n html = html.replace(\n /(<li data-list=\"ul\">[^\\n]*<\\/li>(?:\\n<li data-list=\"ul\">[^\\n]*<\\/li>)*)/g,\n (match) => '<ul>' + match.replace(/ data-list=\"ul\"/g, '') + '</ul>',\n );\n // Group consecutive ordered items into <ol>\n html = html.replace(\n /(<li data-list=\"ol\">[^\\n]*<\\/li>(?:\\n<li data-list=\"ol\">[^\\n]*<\\/li>)*)/g,\n (match) => '<ol>' + match.replace(/ data-list=\"ol\"/g, '') + '</ol>',\n );\n\n // Paragraphs\n const lines = html.split('\\n\\n');\n html = lines\n .map((block) => {\n const t = block.trim();\n if (!t) return '';\n if (/^<(h[1-6]|ul|ol|li|blockquote|hr|pre|img)/.test(t) || t.startsWith(CB_PH)) return t;\n return `<p>${t.replace(/\\n/g, '<br>')}</p>`;\n })\n .filter(Boolean)\n .join('\\n');\n\n // Step 5: Restore placeholders\n html = html.replace(\n new RegExp(`${CB_PH}(\\\\d+)${CB_PH}`, 'g'),\n (_, i) => codeBlocks[parseInt(i, 10)] ?? '',\n );\n html = html.replace(\n new RegExp(`${IC_PH}(\\\\d+)${IC_PH}`, 'g'),\n (_, i) => inlineCodes[parseInt(i, 10)] ?? '',\n );\n\n return html;\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Form association\n // ════════════════════════════════════════════════════════════════════\n\n #syncFormValue(): void {\n this.#internals?.setFormValue(this.#textarea?.value ?? '');\n }\n\n // ════════════════════════════════════════════════════════════════════\n // File upload\n // ════════════════════════════════════════════════════════════════════\n\n #startUpload(file: File): void {\n this.emit('upload-start', { file });\n\n const id = `upload-${++this.#uploadIdCounter}`;\n const uploadUrl = (this as unknown as { uploadUrl: string | undefined }).uploadUrl;\n\n if (!uploadUrl) {\n this.emit('upload-error', { file, error: 'no upload-url set' });\n this.#showUploadError(file, 'no upload-url set');\n return;\n }\n\n // Create progress row\n this.#addProgressRow(id, file.name);\n\n uploadFile(file, uploadUrl, (pct) => {\n this.#updateProgressRow(id, pct);\n })\n .then((url) => {\n this.#removeUploadRow(id);\n const markdown = fileToMarkdown(file, url);\n this.insertText(markdown);\n this.emit('upload-done', { file, url, markdown });\n })\n .catch((err: string) => {\n this.#removeUploadRow(id);\n const errorMsg = typeof err === 'string' ? err : 'Upload failed';\n this.emit('upload-error', { file, error: errorMsg });\n this.#showUploadError(file, errorMsg);\n });\n }\n\n #addProgressRow(id: string, filename: string): void {\n if (!this.#uploadList) return;\n const row = document.createElement('div');\n row.className = 'upload-row';\n row.id = id;\n row.innerHTML = `\n <span class=\"upload-filename\">${escapeHtmlStr(filename)}</span>\n <div class=\"upload-bar-track\">\n <div class=\"upload-bar\" style=\"width: 0%\"></div>\n </div>\n `;\n this.#uploadList.appendChild(row);\n }\n\n #updateProgressRow(id: string, pct: number): void {\n const bar = this.#uploadList?.querySelector<HTMLElement>(`#${id} .upload-bar`);\n if (bar) bar.style.width = `${pct}%`;\n }\n\n #removeUploadRow(id: string): void {\n this.#uploadList?.querySelector(`#${id}`)?.remove();\n }\n\n #showUploadError(file: File, message: string): void {\n if (!this.#uploadList) return;\n const row = document.createElement('div');\n row.className = 'upload-error-row';\n row.innerHTML = `\n <span class=\"upload-error-msg\">${escapeHtmlStr(file.name)}: ${escapeHtmlStr(message)}</span>\n `;\n this.#uploadList.appendChild(row);\n setTimeout(() => row.remove(), 4000);\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Autocomplete\n // ════════════════════════════════════════════════════════════════════\n\n #handleAutocompleteInput(): void {\n const ta = this.#textarea;\n if (!ta) return;\n\n const result = detectTrigger(ta.value, ta.selectionStart ?? 0);\n\n if (!result) {\n this.#closeDropdown();\n return;\n }\n\n const { trigger, query, triggerPos } = result;\n this.#acTrigger = trigger;\n this.#acTriggerPos = triggerPos;\n\n const resolve = (list: Suggestion[]) => this.setSuggestions(list);\n\n if (trigger === '@') {\n this.emit('mention-query', { trigger, query, resolve });\n } else {\n this.emit('reference-query', { trigger, query, resolve });\n }\n }\n\n #handleDropdownKeydown(e: KeyboardEvent): void {\n const len = this.#acSuggestions.length;\n if (len === 0) return;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this.#acSelectedIndex = (this.#acSelectedIndex + 1) % len;\n this.#updateDropdown();\n break;\n case 'ArrowUp':\n e.preventDefault();\n this.#acSelectedIndex = (this.#acSelectedIndex - 1 + len) % len;\n this.#updateDropdown();\n break;\n case 'Enter':\n case 'Tab':\n if (this.#acSelectedIndex >= 0) {\n e.preventDefault();\n this.#confirmAutocomplete();\n }\n break;\n case 'Escape':\n this.#closeDropdown();\n break;\n }\n }\n\n #confirmAutocomplete(): void {\n const ta = this.#textarea;\n if (!ta || this.#acSelectedIndex < 0 || !this.#acTrigger) return;\n\n const suggestion = this.#acSuggestions[this.#acSelectedIndex];\n if (!suggestion) return;\n\n const { newValue, newCursorPos } = confirmSuggestion(\n ta.value,\n this.#acTriggerPos,\n ta.selectionStart ?? ta.value.length,\n this.#acTrigger,\n suggestion.id,\n );\n\n ta.value = newValue;\n ta.setSelectionRange(newCursorPos, newCursorPos);\n this.#syncFormValue();\n this.emit('change', { value: ta.value });\n this.#scheduleHighlight();\n this.#scheduleStatusUpdate();\n this.#closeDropdown();\n }\n\n #closeDropdown(): void {\n this.#acSuggestions = [];\n this.#acSelectedIndex = -1;\n this.#acTrigger = null;\n this.#acTriggerPos = -1;\n if (this.#acDropdown) {\n this.#acDropdown.innerHTML = '';\n this.#acDropdown.hidden = true;\n }\n }\n\n #updateDropdown(): void {\n if (!this.#acDropdown) return;\n if (this.#acSuggestions.length === 0) {\n this.#acDropdown.hidden = true;\n this.#textarea?.removeAttribute('aria-activedescendant');\n return;\n }\n this.#acDropdown.innerHTML = renderDropdown(this.#acSuggestions, this.#acSelectedIndex);\n this.#acDropdown.hidden = false;\n // Update aria-activedescendant so screen readers announce the highlighted item\n if (this.#acSelectedIndex >= 0) {\n this.#textarea?.setAttribute('aria-activedescendant', `ac-item-${this.#acSelectedIndex}`);\n } else {\n this.#textarea?.removeAttribute('aria-activedescendant');\n }\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Status bar\n // ════════════════════════════════════════════════════════════════════\n\n #scheduleStatusUpdate(): void {\n if (this.#statusTimer !== null) clearTimeout(this.#statusTimer);\n this.#statusTimer = setTimeout(() => {\n this.#statusTimer = null;\n this.#updateStatusBarNow();\n }, 100);\n }\n\n #updateStatusBarNow(): void {\n if (!this.#statusCount) return;\n const text = this.#textarea?.value ?? '';\n const words = countWords(text);\n const chars = text.length;\n const maxWords = (this as unknown as { maxWords: number | undefined }).maxWords ?? null;\n this.#statusCount.innerHTML = renderStatusCount(words, chars, maxWords);\n }\n\n // ════════════════════════════════════════════════════════════════════\n // Public API\n // ════════════════════════════════════════════════════════════════════\n\n /** Returns the current markdown content. */\n getValue(): string {\n return this.#textarea?.value ?? '';\n }\n\n /**\n * Set the editor content programmatically.\n * Does NOT fire a change event. Updates the form value.\n */\n setValue(str: string): void {\n if (this.#textarea) {\n this.#textarea.value = str;\n this.#syncFormValue();\n this.#scheduleHighlight();\n this.#updateStatusBarNow();\n } else {\n // Called before element is connected — store in reactive property\n (this as unknown as { value: string }).value = str;\n }\n }\n\n /**\n * Insert text at the current cursor position, replacing any selection.\n * Fires a change event after insertion.\n */\n insertText(str: string): void {\n const ta = this.#textarea;\n if (!ta) return;\n\n const start = ta.selectionStart ?? ta.value.length;\n const end = ta.selectionEnd ?? ta.value.length;\n ta.value = ta.value.slice(0, start) + str + ta.value.slice(end);\n const newPos = start + str.length;\n ta.setSelectionRange(newPos, newPos);\n // Dispatch 'input' — the textarea's input listener handles syncFormValue(),\n // emit('change'), scheduleHighlight(), scheduleStatusUpdate(), and autocomplete\n ta.dispatchEvent(new Event('input', { bubbles: true }));\n }\n\n /**\n * Feed suggestions into the autocomplete dropdown.\n * Pass an empty array to close the dropdown.\n */\n setSuggestions(list: Suggestion[]): void {\n this.#acSuggestions = list;\n this.#acSelectedIndex = list.length > 0 ? 0 : -1;\n this.#updateDropdown();\n }\n}\n\n/**\n * Sanitize a URL for use in rendered HTML (preview tab).\n * Only allows https://, http://, relative paths, and anchor fragments.\n * Rejects javascript:, data:, vbscript:, and other unsafe protocols.\n */\nfunction sanitizeUrl(url: string): string {\n const trimmed = url.trim();\n // Has no protocol → relative URL, safe\n if (!/^[a-z][a-z\\d+\\-.]*:/i.test(trimmed)) return trimmed;\n // Allow http and https only\n if (/^https?:/i.test(trimmed)) return trimmed;\n // All other protocols (javascript:, data:, vbscript:, …) → neutralise\n return '#';\n}\n\n/** HTML-escape a string for safe insertion into innerHTML. */\nfunction escapeHtmlStr(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n",
11
14
  "/**\n * Auto-register el-dm-markdown-input custom element.\n *\n * @example\n * ```ts\n * import '@duskmoon-dev/el-markdown-input/register';\n * // <el-dm-markdown-input> is now available in HTML\n * ```\n */\nimport { ElDmMarkdownInput } from './element.js';\n\nif (!customElements.get('el-dm-markdown-input')) {\n customElements.define('el-dm-markdown-input', ElDmMarkdownInput);\n}\n"
12
15
  ],
13
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAA,gBAUa;AAAA;AAAA,EAVb;AAAA,EAUa,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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",
14
- "debugId": "05F4B33017FA700264756E2164756E21",
16
+ "mappings": "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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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",
17
+ "debugId": "CBBAC14DC8E0779464756E2164756E21",
15
18
  "names": []
16
19
  }