@moraya/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +344 -0
- package/LICENSE +85 -0
- package/README.md +82 -0
- package/dist/adapters/browser-media-resolver.d.ts +21 -0
- package/dist/adapters/browser-media-resolver.js +24 -0
- package/dist/adapters/browser-media-resolver.js.map +1 -0
- package/dist/commands.d.ts +35 -0
- package/dist/commands.js +976 -0
- package/dist/commands.js.map +1 -0
- package/dist/doc-cache.d.ts +29 -0
- package/dist/doc-cache.js +50 -0
- package/dist/doc-cache.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +4534 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +46 -0
- package/dist/markdown.js +1553 -0
- package/dist/markdown.js.map +1 -0
- package/dist/plugins/code-block-view.d.ts +52 -0
- package/dist/plugins/code-block-view.js +686 -0
- package/dist/plugins/code-block-view.js.map +1 -0
- package/dist/plugins/cursor-syntax.d.ts +27 -0
- package/dist/plugins/cursor-syntax.js +122 -0
- package/dist/plugins/cursor-syntax.js.map +1 -0
- package/dist/plugins/definition-list.d.ts +23 -0
- package/dist/plugins/definition-list.js +12 -0
- package/dist/plugins/definition-list.js.map +1 -0
- package/dist/plugins/editor-props-plugin.d.ts +36 -0
- package/dist/plugins/editor-props-plugin.js +1963 -0
- package/dist/plugins/editor-props-plugin.js.map +1 -0
- package/dist/plugins/emoji.d.ts +21 -0
- package/dist/plugins/emoji.js +42 -0
- package/dist/plugins/emoji.js.map +1 -0
- package/dist/plugins/enter-handler.d.ts +26 -0
- package/dist/plugins/enter-handler.js +193 -0
- package/dist/plugins/enter-handler.js.map +1 -0
- package/dist/plugins/highlight.d.ts +39 -0
- package/dist/plugins/highlight.js +283 -0
- package/dist/plugins/highlight.js.map +1 -0
- package/dist/plugins/inline-code-convert.d.ts +32 -0
- package/dist/plugins/inline-code-convert.js +173 -0
- package/dist/plugins/inline-code-convert.js.map +1 -0
- package/dist/plugins/link-text-plugin.d.ts +22 -0
- package/dist/plugins/link-text-plugin.js +194 -0
- package/dist/plugins/link-text-plugin.js.map +1 -0
- package/dist/plugins/mermaid-renderer.d.ts +24 -0
- package/dist/plugins/mermaid-renderer.js +80 -0
- package/dist/plugins/mermaid-renderer.js.map +1 -0
- package/dist/schema.d.ts +48 -0
- package/dist/schema.js +847 -0
- package/dist/schema.js.map +1 -0
- package/dist/setup.d.ts +104 -0
- package/dist/setup.js +4393 -0
- package/dist/setup.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +121 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands.ts","../src/schema.ts","../src/types.ts"],"sourcesContent":["import { type Command, type EditorState, type Transaction } from 'prosemirror-state'\nimport {\n toggleMark,\n setBlockType,\n wrapIn,\n lift,\n} from 'prosemirror-commands'\nimport { wrapInList, liftListItem } from 'prosemirror-schema-list'\nimport { type Node, type MarkType, type NodeType, type Attrs } from 'prosemirror-model'\nimport { defaultSchema } from './schema'\n\n/**\n * Core editing commands (§3.2).\n *\n * Pure ProseMirror Commands: `(state, dispatch?) => boolean`. No IO,\n * no async. Consumer extensions (findAndReplace / quickOpen / etc.) live\n * in their own repos and are NOT exported from this package.\n */\n\nconst schema = defaultSchema\n\nfunction markType(name: string): MarkType {\n const m = schema.marks[name]\n if (!m) throw new Error(`[@moraya/core] mark \"${name}\" not in schema`)\n return m\n}\n\nfunction nodeType(name: string): NodeType {\n const n = schema.nodes[name]\n if (!n) throw new Error(`[@moraya/core] node \"${name}\" not in schema`)\n return n\n}\n\nexport const toggleBold: Command = (state, dispatch) =>\n toggleMark(markType('strong'))(state, dispatch)\n\nexport const toggleItalic: Command = (state, dispatch) =>\n toggleMark(markType('em'))(state, dispatch)\n\nexport const toggleStrikethrough: Command = (state, dispatch) =>\n toggleMark(markType('strike_through'))(state, dispatch)\n\nexport const toggleCode: Command = (state, dispatch) =>\n toggleMark(markType('code'))(state, dispatch)\n\nexport function setHeading(level: 1 | 2 | 3 | 4 | 5 | 6): Command {\n return (state, dispatch) => setBlockType(nodeType('heading'), { level })(state, dispatch)\n}\n\nexport const toggleBlockquote: Command = (state, dispatch) => {\n // If already in blockquote, lift; else wrap.\n const $from = state.selection.$from\n for (let d = $from.depth; d > 0; d--) {\n if ($from.node(d).type.name === 'blockquote') {\n return lift(state, dispatch)\n }\n }\n return wrapIn(nodeType('blockquote'))(state, dispatch)\n}\n\nexport const toggleOrderedList: Command = (state, dispatch) =>\n wrapInList(nodeType('ordered_list'))(state, dispatch)\n\nexport const toggleBulletList: Command = (state, dispatch) =>\n wrapInList(nodeType('bullet_list'))(state, dispatch)\n\n/**\n * Toggle a list: wrap if not in this list type, lift out if already in it.\n * Schema-agnostic — uses `state.schema` rather than `defaultSchema`.\n */\nfunction makeToggleList(typeName: 'bullet_list' | 'ordered_list'): Command {\n return (state, dispatch, view) => {\n const listType = state.schema.nodes[typeName]\n const listItemType = state.schema.nodes.list_item\n if (!listType || !listItemType) return false\n const { $from } = state.selection\n for (let d = $from.depth; d >= 0; d--) {\n if ($from.node(d).type === listType) {\n return liftListItem(listItemType)(state, dispatch, view)\n }\n }\n return wrapInList(listType)(state, dispatch, view)\n }\n}\n\nexport const wrapInBulletList: Command = makeToggleList('bullet_list')\nexport const wrapInOrderedList: Command = makeToggleList('ordered_list')\n\n/**\n * Wrap current block(s) in a bullet list with task-list items (checked: false).\n * Two-step: first apply wrapInList against the bullet_list type, then\n * post-process newly-created list_item nodes within the affected range to\n * set `checked: false` so they render as task items.\n */\nexport const wrapInTaskList: Command = (state, dispatch) => {\n const bulletListType = state.schema.nodes.bullet_list\n const listItemType = state.schema.nodes.list_item\n if (!bulletListType || !listItemType) return false\n if (!wrapInList(bulletListType)(state)) return false\n if (!dispatch) return true\n\n let listTr: Transaction | undefined\n wrapInList(bulletListType)(state, (tr) => { listTr = tr })\n if (!listTr) return false\n\n const { from, to } = listTr.selection\n const updates: Array<{ pos: number; attrs: Attrs }> = []\n listTr.doc.nodesBetween(\n Math.max(0, from - 200),\n Math.min(listTr.doc.content.size, to + 200),\n (node, pos) => {\n if (node.type === listItemType && node.attrs.checked === null) {\n updates.push({ pos, attrs: { ...node.attrs, checked: false } })\n }\n },\n )\n for (let i = updates.length - 1; i >= 0; i--) {\n const u = updates[i]!\n listTr.setNodeMarkup(u.pos, undefined, u.attrs)\n }\n dispatch(listTr.scrollIntoView())\n return true\n}\n\nexport const toggleCodeBlock: Command = (state, dispatch) => {\n const cb = nodeType('code_block')\n if (state.selection.$from.parent.type === cb) {\n return setBlockType(nodeType('paragraph'))(state, dispatch)\n }\n return setBlockType(cb)(state, dispatch)\n}\n\nexport const insertHorizontalRule: Command = (state, dispatch) => {\n if (dispatch) {\n dispatch(state.tr.replaceSelectionWith(nodeType('horizontal_rule').create()))\n }\n return true\n}\n\n/**\n * Insert a 3×3 placeholder table. Note: full table support requires\n * prosemirror-tables setup at editor mount time; this command falls back\n * to inserting a markdown-style code block snippet if tables aren't\n * registered (true for this minimal v0.1.0 schema).\n */\nexport const insertTable: Command = (state, dispatch) => {\n const tableType = schema.nodes.table\n if (!tableType) {\n // Insert pipe-table markdown directly into a paragraph.\n if (dispatch) {\n const text = '\\n\\n| Col 1 | Col 2 | Col 3 |\\n|---|---|---|\\n| | | |\\n| | | |\\n\\n'\n dispatch(state.tr.insertText(text))\n }\n return true\n }\n return false\n}\n\nexport const insertMathBlock: Command = (state, dispatch) => {\n const mathBlock = schema.nodes.math_block\n if (!mathBlock) {\n if (dispatch) {\n dispatch(state.tr.insertText('\\n$$\\n\\\\\\n$$\\n'))\n }\n return true\n }\n if (dispatch) {\n const node = mathBlock.create({ value: '' })\n dispatch(state.tr.replaceSelectionWith(node))\n }\n return true\n}\n\nexport function toggleLink(href?: string): Command {\n return (state, dispatch) => {\n const link = markType('link')\n const { from, to } = state.selection\n if (state.doc.rangeHasMark(from, to, link)) {\n if (dispatch) dispatch(state.tr.removeMark(from, to, link))\n return true\n }\n if (!href) return false\n if (dispatch) {\n dispatch(state.tr.addMark(from, to, link.create({ href })))\n }\n return true\n }\n}\n\nexport function insertImage(src: string, alt?: string): Command {\n return (state, dispatch) => {\n const img = nodeType('image').create({ src, alt: alt ?? null })\n if (dispatch) {\n dispatch(state.tr.replaceSelectionWith(img))\n }\n return true\n }\n}\n\n// Re-export for ergonomic imports\nexport type { Command, EditorState, Transaction, Node }\n","/**\n * Unified ProseMirror Schema for `@moraya/core`.\n *\n * Faithful 1:1 migration from Moraya desktop `src/lib/editor/schema.ts`\n * with the following DI changes (v0.60.0-pre §F2.5):\n * - All Tauri IPC `read_file_binary` / `plugin-http` calls in image / media\n * loaders are replaced by consumer-injected `MediaResolver` methods.\n * - Schema NodeSpecs that depend on the resolver (image, html_inline) are\n * built inside `createSchema(config)` factory body, capturing `config`\n * in closures for `toDOM`. Other NodeSpecs are pure data.\n * - Module-level `documentBaseDir` + `setDocumentBaseDir` is preserved\n * (pure string state, not Tauri-coupled).\n * - Per §6.1.1: this module does NOT export the default schema. It is\n * used internally by parseMarkdown / serializeMarkdown only.\n *\n * Nodes (23): doc, text, paragraph, heading, blockquote, code_block,\n * horizontal_rule, bullet_list, ordered_list, list_item, image,\n * hardbreak, html_block, html_inline, table, table_header_row, table_row,\n * table_header, table_cell, math_inline, math_block,\n * defList, defListTerm, defListDescription\n *\n * Marks (6): html_mark, strong, em, code, link, strike_through\n */\n\nimport { Schema, Fragment } from 'prosemirror-model'\nimport type { NodeSpec, MarkSpec, Node as PmNode } from 'prosemirror-model'\nimport katex from 'katex'\nimport {\n type SchemaConfig,\n type MediaResolver,\n isNullMediaResolver,\n NULL_MEDIA_RESOLVER_SENTINEL,\n type NullMediaResolver,\n} from './types'\n\n// ── Helpers (pure DOM / string ops, no host coupling) ────────────\n\n/** Extract a quoted attribute value from an HTML tag string. */\nfunction extractHtmlAttr(html: string, name: string): string | null {\n const re = new RegExp(`${name}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`, 'i')\n const m = html.match(re)\n return m ? (m[1] ?? m[2] ?? m[3] ?? null) : null\n}\n\n/** Extract all attributes from an HTML tag string as key-value pairs. */\nfunction extractAllHtmlAttrs(html: string): Record<string, string> {\n const attrs: Record<string, string> = {}\n const re = /([a-zA-Z_][\\w:.-]*)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s>]+))/gi\n let m: RegExpExecArray | null\n while ((m = re.exec(html)) !== null) {\n const name = m[1]\n if (!name) continue\n attrs[name.toLowerCase()] = m[2] ?? m[3] ?? m[4] ?? ''\n }\n return attrs\n}\n\n/** Replace element content with broken-image icon + source code display. */\nfunction showBrokenImage(container: HTMLElement, sourceText: string): void {\n container.textContent = ''\n container.className = (container.className.replace(/\\bhtml-img-wrapper\\b|\\bimage-node\\b/, '').trim()\n + ' broken-image').trim()\n const icon = document.createElement('span')\n icon.className = 'broken-image-icon'\n container.appendChild(icon)\n const code = document.createElement('code')\n code.className = 'broken-image-src'\n code.textContent = sourceText\n container.appendChild(code)\n}\n\n/** Convert HTML tag attributes to CSS inline styles for visual rendering. */\nfunction htmlTagToStyle(openTag: string): string {\n const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/)\n if (!tagMatch || !tagMatch[1]) return ''\n const tagName = tagMatch[1].toLowerCase()\n switch (tagName) {\n case 'font': {\n const parts: string[] = []\n const color = extractHtmlAttr(openTag, 'color')\n if (color) parts.push(`color: ${color}`)\n const size = extractHtmlAttr(openTag, 'size')\n if (size) {\n const sizeMap: Record<string, string> = {\n '1': '0.63em', '2': '0.82em', '3': '1em', '4': '1.13em',\n '5': '1.5em', '6': '2em', '7': '3em',\n }\n parts.push(`font-size: ${sizeMap[size] || size}`)\n }\n const face = extractHtmlAttr(openTag, 'face')\n if (face) parts.push(`font-family: ${face}`)\n return parts.join('; ')\n }\n case 'span':\n case 'div':\n return extractHtmlAttr(openTag, 'style') || ''\n default:\n return ''\n }\n}\n\n/**\n * Base directory for resolving relative image paths. Set by the consumer when\n * a document is opened so `<img src=\"./foo.png\">` can be resolved. Pure\n * string state — not Tauri-coupled.\n */\nlet documentBaseDir = ''\n\n/** Update the base directory used to resolve relative image paths. */\nexport function setDocumentBaseDir(dir: string): void {\n documentBaseDir = dir\n}\n\n/** Read the current base dir. Exposed for consumers that need to coordinate (e.g. tests). */\nexport function getDocumentBaseDir(): string {\n return documentBaseDir\n}\n\n/** Check if a path is a local file path (absolute Unix or Windows path). */\nfunction isAbsoluteFilePath(src: string): boolean {\n if (!src) return false\n if (src.startsWith('/') && !src.startsWith('//')) return true\n if (/^[A-Z]:[\\\\/]/i.test(src)) return true\n return false\n}\n\n/** Check if a src is a relative file path (not a URL scheme). */\nfunction isRelativePath(src: string): boolean {\n if (!src) return false\n if (/^(https?:|data:|blob:|javascript:|vbscript:|tauri:|\\/\\/)/i.test(src)) return false\n if (src.startsWith('/') || /^[A-Z]:[\\\\/]/i.test(src)) return false\n return true\n}\n\n/** Resolve a relative path against documentBaseDir to an absolute path. */\nfunction resolveRelativePath(src: string): string {\n if (!documentBaseDir) return src\n let rel = src.replace(/^\\.\\//, '')\n const sep = documentBaseDir.includes('\\\\') ? '\\\\' : '/'\n let base = documentBaseDir.endsWith(sep) ? documentBaseDir.slice(0, -1) : documentBaseDir\n while (rel.startsWith('../') || rel.startsWith('..\\\\')) {\n rel = rel.slice(3)\n const lastSep = base.lastIndexOf(sep)\n if (lastSep > 0) base = base.slice(0, lastSep)\n }\n return `${base}${sep}${rel}`\n}\n\n// ── Image / media DI helpers ────────────────────────────────────\n\n/**\n * Apply MediaResolver-loaded URL to an <img> element. Decodes URL-encoded\n * paths first (markdown parsers URL-encode non-ASCII; filesystem expects\n * actual Unicode characters).\n */\nfunction loadLocalImageSrc(\n img: HTMLImageElement,\n src: string,\n mediaResolver: MediaResolver\n): void {\n let path: string\n try { path = decodeURIComponent(src) } catch { path = src }\n\n mediaResolver.loadLocalImage(path).then((url) => {\n if (url) img.src = url\n else img.dispatchEvent(new Event('error'))\n }).catch(() => {\n img.dispatchEvent(new Event('error'))\n })\n}\n\n/** Apply MediaResolver-loaded URL to a <video>/<audio>/<source> element. */\nfunction setMediaSrc(\n el: HTMLMediaElement | HTMLSourceElement,\n src: string,\n mediaResolver: MediaResolver\n): void {\n if (isAbsoluteFilePath(src)) {\n mediaResolver.loadLocalMedia(src).then((url) => {\n if (!url) return\n el.src = url\n if (el instanceof HTMLMediaElement) el.load()\n }).catch(() => { /* media load failed silently */ })\n } else if (isRelativePath(src)) {\n mediaResolver.loadLocalMedia(resolveRelativePath(src)).then((url) => {\n if (!url) return\n el.src = url\n if (el instanceof HTMLMediaElement) el.load()\n }).catch(() => { /* media load failed silently */ })\n } else if (/^https?:\\/\\//i.test(src)) {\n // For <video>, set src directly so the browser can issue HTTP range requests\n // and stream playback. Tauri-HTTP-to-blob proxy used for <audio> would\n // download entire file before any frame plays — fine for a few-MB audio,\n // fatal for 10s-of-MB to GB video.\n if (el instanceof HTMLVideoElement) {\n el.src = src\n el.load()\n } else {\n mediaResolver.loadRemoteMedia(src).then((url) => {\n if (!url) return\n el.src = url\n if (el instanceof HTMLMediaElement) el.load()\n }).catch(() => { /* fetch failed */ })\n }\n } else {\n el.src = src\n }\n}\n\n/**\n * Create a <video> or <audio> element from raw HTML. Attributes from the\n * original tag are preserved. Child <source> elements are extracted.\n * Event handler attributes (on*) are stripped for XSS prevention.\n */\nfunction createMediaElement(\n tagName: 'video' | 'audio',\n value: string,\n mediaResolver: MediaResolver\n): HTMLElement {\n const wrapper = document.createElement('span')\n wrapper.dataset.type = 'html-inline'\n wrapper.dataset.value = value\n wrapper.className = 'html-media-wrapper'\n wrapper.contentEditable = 'false'\n\n const el = document.createElement(tagName)\n // Stop ProseMirror from grabbing mousedown for atom-node selection — the\n // browser's native <audio>/<video> controls (play, scrub, volume) must\n // receive events directly, otherwise clicks select the node instead of\n // triggering playback.\n const stopForControls = (ev: Event) => ev.stopPropagation()\n el.addEventListener('mousedown', stopForControls)\n el.addEventListener('click', stopForControls)\n el.addEventListener('pointerdown', stopForControls)\n\n const openTagMatch = value.match(new RegExp(`^<${tagName}\\\\b[^>]*>`, 'i'))\n const openTag = openTagMatch ? openTagMatch[0] : ''\n const attrs = extractAllHtmlAttrs(openTag)\n\n for (const [key, val] of Object.entries(attrs)) {\n if (key === 'src') continue\n if (key.startsWith('on')) continue\n el.setAttribute(key, val)\n }\n\n const strippedTag = openTag.replace(/=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/g, '')\n const boolAttrs = ['controls', 'autoplay', 'loop', 'muted', 'playsinline']\n for (const attr of boolAttrs) {\n if (!(attr in attrs) && new RegExp(`\\\\b${attr}\\\\b`, 'i').test(strippedTag)) {\n el.setAttribute(attr, '')\n }\n }\n\n if (tagName === 'audio' && !attrs.preload) {\n el.setAttribute('preload', 'auto')\n }\n\n const sourceRe = /<source\\b[^>]*\\/?>/gi\n let srcMatch: RegExpExecArray | null\n while ((srcMatch = sourceRe.exec(value)) !== null) {\n const srcAttrs = extractAllHtmlAttrs(srcMatch[0])\n if (!srcAttrs.src) continue\n const source = document.createElement('source')\n if (srcAttrs.type) source.type = srcAttrs.type\n setMediaSrc(source, srcAttrs.src, mediaResolver)\n el.appendChild(source)\n }\n\n if (attrs.src) {\n setMediaSrc(el, attrs.src, mediaResolver)\n }\n\n wrapper.appendChild(el)\n return wrapper\n}\n\n// ── Pure NodeSpecs (no MediaResolver coupling) ─────────────────\n\nconst doc: NodeSpec = {\n content: 'block+',\n}\n\nconst text: NodeSpec = { group: 'inline' }\n\nconst paragraph: NodeSpec = {\n content: 'inline*',\n group: 'block',\n parseDOM: [{ tag: 'p' }],\n toDOM() { return ['p', 0] },\n}\n\nconst heading: NodeSpec = {\n attrs: {\n id: { default: '' },\n level: { default: 1 },\n },\n content: 'inline*',\n group: 'block',\n defining: true,\n parseDOM: [1, 2, 3, 4, 5, 6].map(level => ({\n tag: `h${level}`,\n getAttrs(dom: HTMLElement) {\n return { level, id: dom.getAttribute('id') || '' }\n },\n })),\n toDOM(node) {\n const attrs: Record<string, string> = {}\n if (node.attrs.id) attrs.id = node.attrs.id as string\n return [`h${node.attrs.level as number}`, attrs, 0]\n },\n}\n\nconst blockquote: NodeSpec = {\n content: 'block+',\n group: 'block',\n defining: true,\n parseDOM: [{ tag: 'blockquote' }],\n toDOM() { return ['blockquote', 0] },\n}\n\nconst code_block: NodeSpec = {\n content: 'text*',\n group: 'block',\n marks: '',\n defining: true,\n code: true,\n attrs: {\n language: { default: 'text' },\n },\n parseDOM: [{\n tag: 'pre',\n preserveWhitespace: 'full' as const,\n getAttrs(dom: HTMLElement) {\n return { language: dom.dataset.language || 'text' }\n },\n }],\n toDOM(node) {\n return ['pre', { 'data-language': (node.attrs.language as string) || undefined }, ['code', 0]]\n },\n}\n\nconst horizontal_rule: NodeSpec = {\n group: 'block',\n parseDOM: [{ tag: 'hr' }],\n toDOM() { return ['hr'] },\n}\n\nconst bullet_list: NodeSpec = {\n content: 'list_item+',\n group: 'block',\n parseDOM: [{ tag: 'ul' }],\n toDOM() { return ['ul', 0] },\n}\n\nconst ordered_list: NodeSpec = {\n content: 'list_item+',\n group: 'block',\n attrs: {\n order: { default: 1 },\n },\n parseDOM: [{\n tag: 'ol',\n getAttrs(dom: HTMLElement) {\n return { order: dom.hasAttribute('start') ? +(dom.getAttribute('start') || 1) : 1 }\n },\n }],\n toDOM(node) {\n return node.attrs.order === 1\n ? ['ol', 0]\n : ['ol', { start: node.attrs.order as number }, 0]\n },\n}\n\nconst list_item: NodeSpec = {\n content: 'paragraph block*',\n group: 'listItem',\n defining: true,\n attrs: {\n label: { default: '•' },\n listType: { default: 'bullet' },\n spread: { default: 'true' },\n checked: { default: null },\n },\n parseDOM: [\n {\n tag: 'li[data-item-type=\"task\"]',\n getAttrs(dom: HTMLElement) {\n return {\n label: dom.dataset.label,\n listType: dom.dataset.listType,\n spread: dom.dataset.spread,\n checked: dom.dataset.checked ? dom.dataset.checked === 'true' : null,\n }\n },\n },\n {\n tag: 'li',\n getAttrs(dom: HTMLElement) {\n return {\n label: dom.dataset.label || '•',\n listType: dom.dataset.listType || 'bullet',\n spread: dom.dataset.spread || 'true',\n }\n },\n },\n ],\n toDOM(node) {\n if (node.attrs.checked != null) {\n return ['li', {\n 'data-item-type': 'task',\n 'data-label': node.attrs.label as string,\n 'data-list-type': node.attrs.listType as string,\n 'data-spread': node.attrs.spread as string,\n 'data-checked': String(node.attrs.checked),\n }, 0]\n }\n return ['li', {\n 'data-label': node.attrs.label as string,\n 'data-list-type': node.attrs.listType as string,\n 'data-spread': node.attrs.spread as string,\n }, 0]\n },\n}\n\nconst hardbreak: NodeSpec = {\n inline: true,\n group: 'inline',\n selectable: false,\n attrs: {\n isInline: { default: false },\n },\n parseDOM: [\n { tag: 'br' },\n {\n tag: 'span[data-type=\"hardbreak\"]',\n getAttrs() { return { isInline: true } },\n },\n ],\n toDOM() {\n // Always render as span with newline to maintain consistent cursor size.\n // leafText() ensures it serializes correctly.\n return ['span', { 'data-type': 'hardbreak', 'class': 'hardbreak-marker' }, '\\n']\n },\n leafText() { return '\\n' },\n}\n\nconst html_block: NodeSpec = {\n content: 'text*',\n group: 'block',\n marks: '',\n code: true,\n defining: true,\n parseDOM: [{\n tag: 'div[data-type=\"html\"]',\n preserveWhitespace: 'full' as const,\n }],\n toDOM() {\n return ['div', { 'data-type': 'html' }, ['pre', 0]]\n },\n}\n\n// ── Table NodeSpecs ─────────────────────────────────────────────\n\nconst table: NodeSpec = {\n content: 'table_header_row table_row+',\n group: 'block',\n tableRole: 'table',\n isolating: true,\n parseDOM: [{ tag: 'table' }],\n toDOM() { return ['table', ['tbody', 0]] },\n}\n\nconst table_header_row: NodeSpec = {\n content: '(table_header)*',\n tableRole: 'row',\n parseDOM: [\n { tag: 'tr[data-is-header]' },\n {\n tag: 'tr',\n getAttrs(dom: HTMLElement) {\n const hasHeader = dom.querySelector('th')\n return hasHeader ? {} : false\n },\n },\n ],\n toDOM() { return ['tr', { 'data-is-header': 'true' }, 0] },\n}\n\nconst table_row: NodeSpec = {\n content: '(table_cell)*',\n tableRole: 'row',\n parseDOM: [{ tag: 'tr' }],\n toDOM() { return ['tr', 0] },\n}\n\nconst table_header: NodeSpec = {\n content: 'paragraph+',\n tableRole: 'header_cell',\n attrs: {\n alignment: { default: 'left' },\n colspan: { default: 1 },\n rowspan: { default: 1 },\n colwidth: { default: null },\n },\n isolating: true,\n parseDOM: [{\n tag: 'th',\n getAttrs(dom: HTMLElement) {\n return {\n alignment: dom.style.textAlign || 'left',\n colspan: Number(dom.getAttribute('colspan') || 1),\n rowspan: Number(dom.getAttribute('rowspan') || 1),\n colwidth: null,\n }\n },\n }],\n toDOM(node) {\n return ['th', { style: `text-align: ${(node.attrs.alignment as string) || 'left'}` }, 0]\n },\n}\n\nconst table_cell: NodeSpec = {\n content: 'paragraph+',\n tableRole: 'cell',\n attrs: {\n alignment: { default: 'left' },\n colspan: { default: 1 },\n rowspan: { default: 1 },\n colwidth: { default: null },\n },\n isolating: true,\n parseDOM: [{\n tag: 'td',\n getAttrs(dom: HTMLElement) {\n return {\n alignment: dom.style.textAlign || 'left',\n colspan: Number(dom.getAttribute('colspan') || 1),\n rowspan: Number(dom.getAttribute('rowspan') || 1),\n colwidth: null,\n }\n },\n }],\n toDOM(node) {\n return ['td', { style: `text-align: ${(node.attrs.alignment as string) || 'left'}` }, 0]\n },\n}\n\n// ── Math NodeSpecs (KaTeX) ──────────────────────────────────────\n\nconst math_inline: NodeSpec = {\n group: 'inline',\n content: 'text*',\n inline: true,\n atom: true,\n parseDOM: [{\n tag: 'span[data-type=\"math_inline\"]',\n getContent(dom: globalThis.Node, schema: Schema) {\n if (!(dom instanceof HTMLElement)) return Fragment.empty\n const value = dom.dataset.value ?? ''\n if (!value) return Fragment.empty\n return Fragment.from(schema.text(value))\n },\n }],\n toDOM(node) {\n const code = node.textContent\n const dom = document.createElement('span')\n dom.dataset.type = 'math_inline'\n dom.dataset.value = code\n try {\n katex.render(code, dom)\n } catch {\n // §4.4 KaTeX error contract: render fallback marker; serializer reads data-tex attr.\n dom.textContent = code\n dom.classList.add('math-error')\n dom.setAttribute('data-math-type', 'inline')\n }\n return dom\n },\n}\n\nconst math_block: NodeSpec = {\n content: 'text*',\n group: 'block',\n marks: '',\n defining: true,\n atom: true,\n isolating: true,\n attrs: {\n value: { default: '' },\n },\n parseDOM: [{\n tag: 'div[data-type=\"math_block\"]',\n preserveWhitespace: 'full' as const,\n getAttrs(dom: HTMLElement) {\n return { value: dom.dataset.value ?? '' }\n },\n }],\n toDOM(node) {\n const code = node.attrs.value as string\n const dom = document.createElement('div')\n dom.dataset.type = 'math_block'\n dom.dataset.value = code\n try {\n katex.render(code, dom, { displayMode: true })\n } catch {\n dom.textContent = code\n dom.classList.add('math-error')\n dom.setAttribute('data-math-type', 'block')\n }\n return dom\n },\n}\n\n// ── Definition List NodeSpecs ───────────────────────────────────\n\nconst defList: NodeSpec = {\n content: '(defListTerm | defListDescription)+',\n group: 'block',\n defining: true,\n parseDOM: [{ tag: 'dl' }],\n toDOM() { return ['dl', { class: 'definition-list' }, 0] },\n}\n\nconst defListTerm: NodeSpec = {\n content: 'inline*',\n group: 'block',\n defining: true,\n parseDOM: [{ tag: 'dt' }],\n toDOM() { return ['dt', 0] },\n}\n\nconst defListDescription: NodeSpec = {\n content: 'block+',\n group: 'block',\n defining: true,\n parseDOM: [{ tag: 'dd' }],\n toDOM() { return ['dd', 0] },\n}\n\n// ── Marks ───────────────────────────────────────────────────────\n\nconst strong: MarkSpec = {\n parseDOM: [\n {\n tag: 'b',\n getAttrs(dom: HTMLElement) {\n return dom.style.fontWeight !== 'normal' && null\n },\n },\n { tag: 'strong' },\n {\n style: 'font-weight',\n getAttrs(value: string) {\n return /^(bold(er)?|[5-9]\\d{2,})$/.test(value) && null\n },\n },\n ],\n toDOM() { return ['strong', 0] },\n}\n\nconst em: MarkSpec = {\n parseDOM: [\n { tag: 'i' },\n { tag: 'em' },\n {\n style: 'font-style',\n getAttrs(value: string) {\n return value === 'italic' && null\n },\n },\n ],\n toDOM() { return ['em', 0] },\n}\n\nconst code: MarkSpec = {\n priority: 100,\n code: true,\n inclusive: false,\n parseDOM: [{ tag: 'code' }],\n toDOM() { return ['code', 0] },\n}\n\nconst link: MarkSpec = {\n attrs: {\n href: {},\n title: { default: null },\n },\n inclusive: false,\n parseDOM: [{\n tag: 'a[href]',\n getAttrs(dom: HTMLElement) {\n return {\n href: dom.getAttribute('href'),\n title: dom.getAttribute('title'),\n }\n },\n }],\n toDOM(mark) {\n const attrs: Record<string, string> = { href: mark.attrs.href as string }\n if (mark.attrs.title) attrs.title = mark.attrs.title as string\n return ['a', attrs, 0]\n },\n}\n\nconst strike_through: MarkSpec = {\n parseDOM: [\n { tag: 'del' },\n { tag: 's' },\n {\n style: 'text-decoration',\n getAttrs(value: string) {\n return value === 'line-through' && null\n },\n },\n ],\n toDOM() { return ['del', 0] },\n}\n\nconst html_mark: MarkSpec = {\n attrs: {\n openTag: { default: '' },\n closeTag: { default: '' },\n },\n excludes: '', // Allow nesting multiple html_marks (e.g., <font><u>text</u></font>)\n parseDOM: [{\n tag: '[data-type=\"html-mark\"]',\n getAttrs(dom: HTMLElement) {\n return {\n openTag: dom.dataset.openTag ?? '',\n closeTag: dom.dataset.closeTag ?? '',\n }\n },\n }],\n toDOM(mark) {\n const openTag = mark.attrs.openTag as string\n const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/)\n const tagName = tagMatch && tagMatch[1] ? tagMatch[1].toLowerCase() : 'span'\n\n const attrs: Record<string, string> = {\n 'data-type': 'html-mark',\n 'data-open-tag': openTag,\n 'data-close-tag': mark.attrs.closeTag as string,\n }\n\n const semanticTags = ['sub', 'sup', 'u', 'ins', 'mark', 'small', 'big', 'kbd', 'abbr']\n if (semanticTags.includes(tagName)) {\n return [tagName, attrs, 0]\n }\n\n const style = htmlTagToStyle(openTag)\n if (style) attrs.style = style\n return ['span', attrs, 0]\n },\n}\n\n// ── Resolver-coupled NodeSpec builders (image, html_inline) ─────\n\n/**\n * Build the `image` NodeSpec with a closed-over MediaResolver. Local-path\n * images are loaded via `mediaResolver.loadLocalImage`; remote URLs are\n * applied directly so the browser can stream / cache normally.\n */\nfunction buildImageNodeSpec(mediaResolver: MediaResolver): NodeSpec {\n return {\n inline: true,\n group: 'inline',\n selectable: true,\n draggable: true,\n marks: '',\n atom: true,\n defining: true,\n isolating: true,\n attrs: {\n src: { default: '' },\n alt: { default: '' },\n title: { default: '' },\n },\n parseDOM: [{\n tag: 'img[src]',\n getAttrs(dom: HTMLElement) {\n return {\n src: dom.getAttribute('src') || '',\n alt: dom.getAttribute('alt') || '',\n title: dom.getAttribute('title') || dom.getAttribute('alt') || '',\n }\n },\n }],\n toDOM(node) {\n const container = document.createElement('span')\n container.className = 'image-node'\n\n const img = document.createElement('img')\n if (node.attrs.alt) img.alt = node.attrs.alt as string\n if (node.attrs.title) img.title = node.attrs.title as string\n\n // Apply width from title attr (e.g. title=\"width=70%\")\n const titleStr = (node.attrs.title || '') as string\n const widthMatch = titleStr.match(/^width=(\\d+%?)$/)\n const widthVal = widthMatch?.[1]\n if (widthVal) {\n img.style.width = widthVal.includes('%') ? widthVal : `${widthVal}px`\n img.style.maxWidth = 'none'\n }\n\n img.onerror = () => {\n const alt = node.attrs.alt ? `![${node.attrs.alt}]` : '![]'\n const title = node.attrs.title ? ` \"${node.attrs.title}\"` : ''\n showBrokenImage(container, `${alt}(${node.attrs.src}${title})`)\n }\n\n const src = node.attrs.src as string\n if (isAbsoluteFilePath(src)) {\n loadLocalImageSrc(img, src, mediaResolver)\n } else if (isRelativePath(src)) {\n loadLocalImageSrc(img, resolveRelativePath(src), mediaResolver)\n } else {\n img.src = src\n }\n\n container.appendChild(img)\n return container\n },\n }\n}\n\n/**\n * Build the `html_inline` NodeSpec with a closed-over MediaResolver. Inline\n * <img> / <video> / <audio> tags route their src through the resolver; other\n * inline HTML (<font>, <br>, etc.) renders as a plain span carrying its\n * verbatim source for byte-stable roundtrip.\n */\nfunction buildHtmlInlineNodeSpec(mediaResolver: MediaResolver): NodeSpec {\n return {\n group: 'inline',\n inline: true,\n atom: true,\n attrs: {\n value: { default: '' },\n },\n parseDOM: [{\n tag: 'span[data-type=\"html-inline\"]',\n getAttrs(dom: HTMLElement) {\n return { value: dom.dataset.value ?? '' }\n },\n }],\n toDOM(node) {\n const value = node.attrs.value as string\n\n if (/^<img\\s/i.test(value)) {\n const wrapper = document.createElement('span')\n wrapper.dataset.type = 'html-inline'\n wrapper.dataset.value = value\n wrapper.className = 'html-img-wrapper'\n\n const attrs = extractAllHtmlAttrs(value)\n const src = attrs.src || ''\n if (src) {\n const img = document.createElement('img')\n for (const [key, val] of Object.entries(attrs)) {\n if (key === 'src') continue\n if (key === 'onerror' || key === 'onload' || key.startsWith('on')) continue\n img.setAttribute(key, val)\n }\n img.onerror = () => {\n showBrokenImage(wrapper, value)\n }\n if (isAbsoluteFilePath(src)) {\n loadLocalImageSrc(img, src, mediaResolver)\n } else if (isRelativePath(src)) {\n loadLocalImageSrc(img, resolveRelativePath(src), mediaResolver)\n } else {\n img.src = src\n }\n wrapper.appendChild(img)\n } else {\n showBrokenImage(wrapper, value)\n }\n return wrapper\n }\n\n if (/^<video\\b/i.test(value)) return createMediaElement('video', value, mediaResolver)\n if (/^<audio\\b/i.test(value)) return createMediaElement('audio', value, mediaResolver)\n\n // Default: invisible span for other inline HTML (<font>, <br>, etc.)\n return ['span', { 'data-type': 'html-inline', 'data-value': value }]\n },\n }\n}\n\n// ── Schema assembly ─────────────────────────────────────────────\n\nfunction buildNodes(mediaResolver: MediaResolver): Record<string, NodeSpec> {\n return {\n doc,\n text,\n paragraph,\n heading,\n blockquote,\n code_block,\n horizontal_rule,\n bullet_list,\n ordered_list,\n list_item,\n image: buildImageNodeSpec(mediaResolver),\n hardbreak,\n html_block,\n html_inline: buildHtmlInlineNodeSpec(mediaResolver),\n table,\n table_header_row,\n table_row,\n table_header,\n table_cell,\n math_inline,\n math_block,\n defList,\n defListTerm,\n defListDescription,\n }\n}\n\nconst marks: Record<string, MarkSpec> = {\n html_mark,\n strong,\n em,\n code,\n link,\n strike_through,\n}\n\n// ── Internal default schema (parser/serializer fallback) ────────\n\nconst nullMediaResolver: NullMediaResolver = {\n [NULL_MEDIA_RESOLVER_SENTINEL]: true,\n async loadLocalImage() { return '' },\n async loadLocalMedia() { return '' },\n async loadRemoteMedia(url: string) { return url },\n}\n\n/**\n * Internal default schema (uses {@link nullMediaResolver}).\n * Used by parseMarkdown / serializeMarkdown when no real consumer schema\n * is available. Per §6.1.1 NOT exported via index.ts — consumers must call\n * createSchema(config) with a real MediaResolver.\n */\nexport const defaultSchema = new Schema({\n nodes: buildNodes(nullMediaResolver),\n marks,\n})\n\n// ── Public factory ──────────────────────────────────────────────\n\n/** Cached config-keyed schemas. Reuses Schema instances when consumers call createSchema with the same config. */\nconst schemaCache = new WeakMap<MediaResolver, Schema>()\n\n/**\n * Create a ProseMirror Schema with consumer-injected dependencies.\n *\n * @throws TypeError if `config.mediaResolver` is missing or is the internal\n * nullMediaResolver sentinel.\n */\nexport function createSchema(config: SchemaConfig): Schema {\n if (!config || typeof config !== 'object') {\n throw new TypeError('@moraya/core: createSchema() requires a config object with a MediaResolver')\n }\n if (!config.mediaResolver) {\n throw new TypeError('@moraya/core: createSchema() requires a MediaResolver')\n }\n if (isNullMediaResolver(config.mediaResolver)) {\n throw new TypeError(\n \"@moraya/core: do not pass nullMediaResolver to createSchema(). That instance is reserved for parseMarkdown/serializeMarkdown internal use only. Provide a real MediaResolver implementation (e.g. BrowserMediaResolver from '@moraya/core/adapters/browser-media-resolver').\"\n )\n }\n const cached = schemaCache.get(config.mediaResolver)\n if (cached) return cached\n const schema = new Schema({\n nodes: buildNodes(config.mediaResolver),\n marks,\n })\n schemaCache.set(config.mediaResolver, schema)\n return schema\n}\n\nexport type { SchemaConfig, PmNode }\n","/**\n * Dependency-injection interfaces for `@moraya/core`.\n *\n * These 4 interfaces are the **only** boundary between the core package\n * (host-agnostic, pure ESM) and the consumer environment (Tauri / browser /\n * mobile WebView). All Tauri / DOM-specific APIs that previously lived inside\n * the editor source are now accessed through these injected implementations.\n *\n * See iteration spec: `moraya/docs/iterations/v0.60.0-pre-shared-markdown-core.md` §3.3.\n */\n\n/**\n * Loads local / remote media as a URL usable in `img.src` / `video.src` / etc.\n * Typically returns blob: URLs for local files (cached by the implementation).\n */\nexport interface MediaResolver {\n /** Read a local image file by absolute path; return a blob: URL (implementation caches internally). */\n loadLocalImage(absolutePath: string): Promise<string>\n\n /** Read a local audio/video file by absolute path; return a blob: URL. */\n loadLocalMedia(absolutePath: string): Promise<string>\n\n /**\n * Fetch a remote media URL.\n * Browser implementations may return the original URL directly.\n * Tauri implementations proxy through plugin-http to bypass WKWebView mixed-content.\n */\n loadRemoteMedia(url: string): Promise<string>\n}\n\n/** Opens an external link (browser = `window.open`; Tauri = plugin-opener; mobile = bridge). */\nexport interface LinkOpener {\n open(url: string): void\n}\n\n/**\n * Custom code-block renderer plugin (WaveDrom / D2 / Excalidraw, etc.).\n * Loaded dynamically via {@link RendererRegistry}.\n */\nexport interface RendererPluginModule {\n /**\n * Render `source` into `container` (async supported).\n * The caller guarantees `container` is already mounted in the DOM.\n * The implementation must clear any old content of `container` before rendering.\n *\n * Error propagation: if this method throws or rejects, the core captures the error\n * inside the NodeView (no rethrow) and inserts a fallback DOM:\n * `<div class=\"renderer-error\" data-language=\"${lang}\" data-error=\"${msg}\">[Renderer ${lang} failed]</div>`\n * Roundtrip preservation: the serializer reads the original fenced source from\n * `node.attrs.source` (NOT from the fallback DOM), so the fenced code block\n * round-trips byte-stably even if rendering fails.\n */\n render(\n source: string,\n container: HTMLElement,\n options?: { theme?: string; baseUrl?: string }\n ): void | Promise<void>\n\n /**\n * Destroy the rendered content within `container` (optional).\n * Called from NodeView.destroy() to free canvas / SVG / event listeners.\n *\n * Error propagation: if this method throws, the core catches and `console.warn`s\n * (does not report to Sentry, does not rethrow). This is a cleanup path; throwing\n * here would block NodeView destruction and cause memory leaks.\n */\n destroy?(container: HTMLElement): void\n}\n\n/** Code-block custom renderer registry. Implemented by consumers (Moraya desktop / Web). */\nexport interface RendererRegistry {\n /** Whether a renderer is registered for the given language identifier. */\n has(language: string): boolean\n\n /** Asynchronously load the renderer module (consumer handles CDN / local import). */\n load(language: string): Promise<RendererPluginModule>\n\n /**\n * Snapshot of currently registered renderer versions (language → version string).\n * Used by code-block-view to invalidate cached renders when versions change.\n */\n readonly versions: Readonly<Record<string, string>>\n}\n\n/**\n * Platform behavior parameters (carries the editor-props-plugin DI from §F2.6).\n * Desktop injects Tauri / OS truth; Web uses browser detection; mobile bridges fill.\n */\nexport interface Platform {\n /**\n * Absolute path of the currently open document (used for relative-image-path\n * resolution). Returns `null` when no document is open.\n */\n getCurrentFilePath: () => string | null\n\n /**\n * Whether the user is on macOS. Affects Option-key handling, Cmd vs Ctrl,\n * emoji popup behavior, etc.\n */\n isMacOS: boolean\n}\n\n/** SchemaConfig (re-exported from schema.ts for convenience). */\nexport interface SchemaConfig {\n mediaResolver: MediaResolver\n rendererRegistry?: RendererRegistry\n linkOpener?: LinkOpener\n}\n\n// ===== Internal sentinel for misuse detection (v0.60.0-pre §6.1.1) =====\n\n/**\n * Internal symbol-tagged null MediaResolver used by core for parseMarkdown /\n * serializeMarkdown internal fallback. Consumers must NOT pass this to\n * createSchema(); doing so throws with a descriptive error.\n */\nexport const NULL_MEDIA_RESOLVER_SENTINEL = Symbol('@moraya/core:null-media-resolver')\n\nexport interface NullMediaResolver extends MediaResolver {\n readonly [NULL_MEDIA_RESOLVER_SENTINEL]: true\n}\n\nexport function isNullMediaResolver(r: MediaResolver): r is NullMediaResolver {\n return (r as NullMediaResolver)[NULL_MEDIA_RESOLVER_SENTINEL] === true\n}\n"],"mappings":"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","names":["code","schema","code","text","link"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Node } from 'prosemirror-model';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ProseMirror Doc LRU cache (v0.19.0 perf optimization, ported here).
|
|
5
|
+
*
|
|
6
|
+
* Per v0.60.0-pre §3 file tree note:
|
|
7
|
+
* - Factory `createDocCache(maxEntries?)` (no module-level singleton)
|
|
8
|
+
* - Consumers inject via `createEditor(opts.docCache)`; default = createDocCache(10)
|
|
9
|
+
* - Moraya desktop multi-tab: one app-level instance keyed by filePath hash
|
|
10
|
+
* - Moraya Web note list: shared app-level instance with createDocCache(50)
|
|
11
|
+
*/
|
|
12
|
+
interface DocCache {
|
|
13
|
+
get(hash: number): Node | undefined;
|
|
14
|
+
set(hash: number, doc: Node): void;
|
|
15
|
+
clear(): void;
|
|
16
|
+
/** Current entry count (read-only). */
|
|
17
|
+
readonly size: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create an LRU-bounded ProseMirror Doc cache.
|
|
21
|
+
*
|
|
22
|
+
* @param maxEntries Max docs to retain. Default 10 (matches Moraya desktop's
|
|
23
|
+
* per-Editor instance size; Moraya Web with note list typically uses 50).
|
|
24
|
+
*/
|
|
25
|
+
declare function createDocCache(maxEntries?: number): DocCache;
|
|
26
|
+
/** djb2 hash (matches Moraya desktop's existing hash to keep cache key compat). */
|
|
27
|
+
declare function djb2Hash(str: string): number;
|
|
28
|
+
|
|
29
|
+
export { type DocCache, createDocCache, djb2Hash };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/doc-cache.ts
|
|
2
|
+
var LRUDocCache = class {
|
|
3
|
+
constructor(maxEntries) {
|
|
4
|
+
this.maxEntries = maxEntries;
|
|
5
|
+
if (maxEntries < 1) throw new RangeError("docCache maxEntries must be \u2265 1");
|
|
6
|
+
}
|
|
7
|
+
maxEntries;
|
|
8
|
+
map = /* @__PURE__ */ new Map();
|
|
9
|
+
get size() {
|
|
10
|
+
return this.map.size;
|
|
11
|
+
}
|
|
12
|
+
get(hash) {
|
|
13
|
+
const v = this.map.get(hash);
|
|
14
|
+
if (v !== void 0) {
|
|
15
|
+
this.map.delete(hash);
|
|
16
|
+
this.map.set(hash, v);
|
|
17
|
+
}
|
|
18
|
+
return v;
|
|
19
|
+
}
|
|
20
|
+
set(hash, doc) {
|
|
21
|
+
if (this.map.has(hash)) {
|
|
22
|
+
this.map.delete(hash);
|
|
23
|
+
}
|
|
24
|
+
this.map.set(hash, doc);
|
|
25
|
+
if (this.map.size > this.maxEntries) {
|
|
26
|
+
const firstKey = this.map.keys().next().value;
|
|
27
|
+
if (firstKey !== void 0) {
|
|
28
|
+
this.map.delete(firstKey);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
clear() {
|
|
33
|
+
this.map.clear();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
function createDocCache(maxEntries = 10) {
|
|
37
|
+
return new LRUDocCache(maxEntries);
|
|
38
|
+
}
|
|
39
|
+
function djb2Hash(str) {
|
|
40
|
+
let hash = 5381;
|
|
41
|
+
for (let i = 0; i < str.length; i++) {
|
|
42
|
+
hash = hash * 33 ^ str.charCodeAt(i);
|
|
43
|
+
}
|
|
44
|
+
return hash >>> 0;
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
createDocCache,
|
|
48
|
+
djb2Hash
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=doc-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/doc-cache.ts"],"sourcesContent":["import type { Node } from 'prosemirror-model'\n\n/**\n * ProseMirror Doc LRU cache (v0.19.0 perf optimization, ported here).\n *\n * Per v0.60.0-pre §3 file tree note:\n * - Factory `createDocCache(maxEntries?)` (no module-level singleton)\n * - Consumers inject via `createEditor(opts.docCache)`; default = createDocCache(10)\n * - Moraya desktop multi-tab: one app-level instance keyed by filePath hash\n * - Moraya Web note list: shared app-level instance with createDocCache(50)\n */\n\nexport interface DocCache {\n get(hash: number): Node | undefined\n set(hash: number, doc: Node): void\n clear(): void\n /** Current entry count (read-only). */\n readonly size: number\n}\n\nclass LRUDocCache implements DocCache {\n private readonly map = new Map<number, Node>()\n constructor(private readonly maxEntries: number) {\n if (maxEntries < 1) throw new RangeError('docCache maxEntries must be ≥ 1')\n }\n\n get size(): number {\n return this.map.size\n }\n\n get(hash: number): Node | undefined {\n const v = this.map.get(hash)\n if (v !== undefined) {\n // LRU touch: re-insert to mark as most-recently-used\n this.map.delete(hash)\n this.map.set(hash, v)\n }\n return v\n }\n\n set(hash: number, doc: Node): void {\n if (this.map.has(hash)) {\n this.map.delete(hash)\n }\n this.map.set(hash, doc)\n if (this.map.size > this.maxEntries) {\n // Evict oldest\n const firstKey = this.map.keys().next().value\n if (firstKey !== undefined) {\n this.map.delete(firstKey)\n }\n }\n }\n\n clear(): void {\n this.map.clear()\n }\n}\n\n/**\n * Create an LRU-bounded ProseMirror Doc cache.\n *\n * @param maxEntries Max docs to retain. Default 10 (matches Moraya desktop's\n * per-Editor instance size; Moraya Web with note list typically uses 50).\n */\nexport function createDocCache(maxEntries = 10): DocCache {\n return new LRUDocCache(maxEntries)\n}\n\n/** djb2 hash (matches Moraya desktop's existing hash to keep cache key compat). */\nexport function djb2Hash(str: string): number {\n let hash = 5381\n for (let i = 0; i < str.length; i++) {\n hash = (hash * 33) ^ str.charCodeAt(i)\n }\n return hash >>> 0\n}\n"],"mappings":";AAoBA,IAAM,cAAN,MAAsC;AAAA,EAEpC,YAA6B,YAAoB;AAApB;AAC3B,QAAI,aAAa,EAAG,OAAM,IAAI,WAAW,sCAAiC;AAAA,EAC5E;AAAA,EAF6B;AAAA,EADZ,MAAM,oBAAI,IAAkB;AAAA,EAK7C,IAAI,OAAe;AACjB,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA,EAEA,IAAI,MAAgC;AAClC,UAAM,IAAI,KAAK,IAAI,IAAI,IAAI;AAC3B,QAAI,MAAM,QAAW;AAEnB,WAAK,IAAI,OAAO,IAAI;AACpB,WAAK,IAAI,IAAI,MAAM,CAAC;AAAA,IACtB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,MAAc,KAAiB;AACjC,QAAI,KAAK,IAAI,IAAI,IAAI,GAAG;AACtB,WAAK,IAAI,OAAO,IAAI;AAAA,IACtB;AACA,SAAK,IAAI,IAAI,MAAM,GAAG;AACtB,QAAI,KAAK,IAAI,OAAO,KAAK,YAAY;AAEnC,YAAM,WAAW,KAAK,IAAI,KAAK,EAAE,KAAK,EAAE;AACxC,UAAI,aAAa,QAAW;AAC1B,aAAK,IAAI,OAAO,QAAQ;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,IAAI,MAAM;AAAA,EACjB;AACF;AAQO,SAAS,eAAe,aAAa,IAAc;AACxD,SAAO,IAAI,YAAY,UAAU;AACnC;AAGO,SAAS,SAAS,KAAqB;AAC5C,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,WAAQ,OAAO,KAAM,IAAI,WAAW,CAAC;AAAA,EACvC;AACA,SAAO,SAAS;AAClB;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { createSchema, getDocumentBaseDir, setDocumentBaseDir } from './schema.js';
|
|
2
|
+
export { parseMarkdown, parseMarkdownAsync, serializeMarkdown } from './markdown.js';
|
|
3
|
+
export { CreateEditorOptions, EditorPluginOptions, MorayaEditorInstance, createEditor, createEditorPlugins, preloadEnhancementPlugins } from './setup.js';
|
|
4
|
+
export { insertHorizontalRule, insertImage, insertMathBlock, insertTable, setHeading, toggleBlockquote, toggleBold, toggleBulletList, toggleCode, toggleCodeBlock, toggleItalic, toggleLink, toggleOrderedList, toggleStrikethrough } from './commands.js';
|
|
5
|
+
export { DocCache, createDocCache, djb2Hash } from './doc-cache.js';
|
|
6
|
+
export { LinkOpener, MediaResolver, Platform, RendererPluginModule, RendererRegistry, SchemaConfig } from './types.js';
|
|
7
|
+
import 'prosemirror-model';
|
|
8
|
+
import 'prosemirror-state';
|
|
9
|
+
import 'prosemirror-view';
|
|
10
|
+
import 'prosemirror-inputrules';
|