@webx-ui/core 0.41.0 → 0.43.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RichText-BJK1BVlM.js","sources":["../src/components/RichText/icons.ts","../src/components/RichText/ToolbarButton.vue","../src/components/RichText/source.ts","../src/components/RichText/tools.ts","../src/components/RichText/RichText.vue"],"sourcesContent":["/**\n * Toolbar glyphs as raw path data, drawn on a 24×24 grid with a 1.7 stroke.\n * Inline rather than an icon package: it is a dozen paths, and a dependency for\n * that would be worse than the duplication.\n */\nexport const richTextIcons: Record<string, string> = {\n bold: 'M7 5h6a3.5 3.5 0 0 1 0 7H7zm0 7h7a3.5 3.5 0 0 1 0 7H7z',\n italic: 'M15 5h-5M14 19H9M13.5 5 10.5 19',\n accent: 'M5 19 12 5l7 14M8 13h8M18 4.5h.01',\n strike:\n 'M5 12h14M8 8.5A3 3 0 0 1 11 6h2a3 3 0 0 1 3 2.5M16 15.5A3 3 0 0 1 13 18h-2a3 3 0 0 1-3-2.5',\n code: 'm9 8-5 4 5 4M15 8l5 4-5 4',\n bulletList: 'M9 6h11M9 12h11M9 18h11M4.5 6h.01M4.5 12h.01M4.5 18h.01',\n orderedList: 'M10 6h10M10 12h10M10 18h10M4 6h1v4M4 10h2M4 14h2v2H4v2h2',\n blockquote: 'M7 15V9h4v6H7l-2 3M15 15V9h4v6h-4l-2 3',\n hr: 'M4 12h16',\n link: 'M10 13a4 4 0 0 0 5.7.4l2.6-2.6a4 4 0 0 0-5.7-5.7l-1.5 1.5M14 11a4 4 0 0 0-5.7-.4l-2.6 2.6a4 4 0 0 0 5.7 5.7l1.5-1.5',\n table: 'M4 6h16v12H4zM4 10h16M10 10v8M4 14h16',\n image: 'M4 6h16v12H4zM4 15l4-4 4 4 3-3 5 5M9 9.5h.01',\n youtube:\n 'M3 8.5A2.5 2.5 0 0 1 5.5 6h13A2.5 2.5 0 0 1 21 8.5v7a2.5 2.5 0 0 1-2.5 2.5h-13A2.5 2.5 0 0 1 3 15.5zM10.5 9.5l4 2.5-4 2.5z',\n undo: 'M4 9h11a4.5 4.5 0 0 1 0 9h-4M4 9l4-4M4 9l4 4',\n redo: 'M20 9H9a4.5 4.5 0 0 0 0 9h4M20 9l-4-4M20 9l-4 4',\n source: 'm8 8-4 4 4 4M16 8l4 4-4 4M13.5 5l-3 14',\n rowAfter: 'M4 5h16M4 11h16M8 15v6M5 18h6',\n rowBefore: 'M4 19h16M4 13h16M8 3v6M5 6h6',\n columnAfter: 'M5 4v16M11 4v16M15 8h6M18 5v6',\n columnBefore: 'M19 4v16M13 4v16M3 8h6M6 5v6',\n deleteRow: 'M4 8h16M4 16h16M9 12h6',\n deleteColumn: 'M8 4v16M16 4v16M12 9v6',\n deleteTable: 'M5 7h14M9 7V5h6v2M7 7l1 12h8l1-12M10 11v4M14 11v4',\n mergeCells: 'M4 6h16v12H4zM12 6v3M12 15v3M9 12h6M9 12l2-2M9 12l2 2',\n token: 'M9 4H6v16h3M15 4h3v16h-3',\n check: 'm5 13 4 4L19 7',\n close: 'M6 6l12 12M18 6 6 18',\n}\n","<script setup lang=\"ts\">\nimport { richTextIcons } from './icons'\n\ndefineOptions({ name: 'WxRichTextToolbarButton' })\n\nwithDefaults(\n defineProps<{\n /** Key into the icon set. Omit it and the `text` shows instead. */\n icon?: string\n /** Short label used for headings, where a glyph reads worse than \"H2\". */\n text?: string\n label: string\n active?: boolean\n disabled?: boolean\n }>(),\n { icon: undefined, text: undefined, active: false, disabled: false },\n)\n\ndefineEmits<{ click: [] }>()\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"wx-rich-text__tool\"\n :class=\"{ 'is-active': active }\"\n :title=\"label\"\n :aria-label=\"label\"\n :aria-pressed=\"active\"\n :disabled=\"disabled\"\n @click=\"$emit('click')\"\n >\n <svg v-if=\"icon\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n :d=\"richTextIcons[icon]\"\n stroke=\"currentColor\"\n stroke-width=\"1.7\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <span v-else class=\"wx-rich-text__tool-text\">{{ text }}</span>\n </button>\n</template>\n\n<style scoped>\n.wx-rich-text__tool {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 30px;\n height: 30px;\n padding: 0;\n background: transparent;\n border: 1px solid transparent;\n border-radius: var(--wx-radius-xs);\n color: var(--wx-text-default);\n cursor: pointer;\n transition:\n background-color var(--wx-duration-fast) var(--wx-easing-standard),\n color var(--wx-duration-fast) var(--wx-easing-standard);\n}\n\n.wx-rich-text__tool svg {\n width: 18px;\n height: 18px;\n}\n\n.wx-rich-text__tool-text {\n font-size: var(--wx-font-size-sm);\n font-weight: var(--wx-font-weight-semibold);\n}\n\n.wx-rich-text__tool:hover:not(:disabled) {\n background: var(--wx-bg-fill);\n}\n\n.wx-rich-text__tool.is-active {\n background: var(--wx-color-primary-soft);\n color: var(--wx-color-primary);\n}\n\n.wx-rich-text__tool:focus-visible {\n outline: none;\n box-shadow: var(--wx-ring-focus);\n}\n\n.wx-rich-text__tool:disabled {\n color: var(--wx-text-disabled);\n cursor: not-allowed;\n}\n</style>\n","/**\n * The HTML behind the editor, as a person reads and edits it.\n *\n * Tiptap writes the document as one line, which is unreadable past a paragraph. Blocks go on\n * lines of their own here, and whatever holds other blocks indents them. Whitespace between\n * blocks means nothing to the parser, so the document that comes back is the one that went in.\n */\n\n/** Tags whose children are blocks: each child gets a line, one step further in. */\nconst CONTAINERS = new Set([\n 'ul',\n 'ol',\n 'li',\n 'blockquote',\n 'table',\n 'colgroup',\n 'thead',\n 'tbody',\n 'tfoot',\n 'tr',\n 'th',\n 'td',\n 'div',\n 'figure',\n])\n\n/** Tags that start a line and keep their content on it. */\nconst LINES = new Set([\n 'p',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'pre',\n 'hr',\n 'img',\n 'iframe',\n 'col',\n 'figcaption',\n])\n\nconst VOID = new Set(['hr', 'img', 'br', 'col', 'wbr', 'source', 'input'])\n\nconst TAG = /<(\\/?)([a-zA-Z][a-zA-Z0-9-]*)\\b[^>]*?(\\/?)>/g\n\nexport function formatHtml(html: string, indent = ' '): string {\n let out = ''\n let depth = 0\n /** Inside a line block nothing breaks: a newline there would be a space in the text. */\n let inLine = 0\n let inPre = 0\n let last = 0\n\n const breakLine = (): void => {\n out += `\\n${indent.repeat(depth)}`\n }\n\n for (const match of html.matchAll(TAG)) {\n const [token, closing, rawName, selfClosing] = match\n const name = rawName.toLowerCase()\n out += html.slice(last, match.index)\n last = match.index + token.length\n\n if (inPre > 0 && name !== 'pre') {\n out += token\n continue\n }\n\n const free = inLine === 0\n const isVoid = VOID.has(name) || selfClosing === '/'\n\n if (CONTAINERS.has(name) && free) {\n if (closing) {\n depth = Math.max(0, depth - 1)\n breakLine()\n out += token\n } else {\n breakLine()\n out += token\n if (!isVoid) depth += 1\n }\n continue\n }\n\n if (LINES.has(name)) {\n if (!closing && free) breakLine()\n out += token\n if (name === 'pre') inPre += closing ? -1 : 1\n if (!isVoid) inLine += closing ? -1 : 1\n inLine = Math.max(0, inLine)\n continue\n }\n\n out += token\n }\n\n out += html.slice(last)\n\n return out.replace(/^\\n/, '')\n}\n\n/** Tags the editor writes under another name: not a loss, only a spelling. */\nconst SPELLINGS: Record<string, string> = { b: 'strong', i: 'em', del: 's', strike: 's' }\n\nfunction census(html: string): Map<string, number> {\n const counts = new Map<string, number>()\n const add = (key: string): void => {\n counts.set(key, (counts.get(key) ?? 0) + 1)\n }\n const template = document.createElement('template')\n template.innerHTML = html\n\n for (const element of template.content.querySelectorAll('*')) {\n const tag = element.tagName.toLowerCase()\n const name = SPELLINGS[tag] ?? tag\n add(`<${name}>`)\n for (const attribute of element.getAttributeNames()) add(`${attribute} on <${name}>`)\n }\n\n return counts\n}\n\n/**\n * What a document loses on its way into the editor: the tags and attributes the editor's schema\n * does not know, each named once. ProseMirror drops them without a word, which is fine for a\n * paste and not for markup a person has just typed by hand.\n *\n * Counted rather than merely listed, so that one `<span>` the editor keeps does not vouch for\n * the nine it drops. What the editor adds — a paragraph around bare text, its own `rel` on a\n * link — is not a loss and is not reported.\n */\nexport function lostMarkup(before: string, after: string): string[] {\n const had = census(before)\n const kept = census(after)\n const lost: string[] = []\n\n for (const [key, count] of had) {\n if ((kept.get(key) ?? 0) < count) lost.push(key)\n }\n\n // A dropped tag takes its attributes with it; naming both says the same thing twice.\n const droppedTags = new Set(lost.filter((key) => key.startsWith('<')))\n\n return lost.filter((key) => {\n const on = / on (<[^>]+>)$/.exec(key)\n return !on || !droppedTags.has(on[1])\n })\n}\n","import type { RichTextTableTool, RichTextTool } from './types'\n\n/**\n * Kept out of the component because `withDefaults` cannot reference a binding\n * declared inside `<script setup>` — it is hoisted out of `setup()`.\n */\nexport const DEFAULT_TOOLS: RichTextTool[] = [\n 'bold',\n 'italic',\n 'strike',\n 'code',\n 'divider',\n 'h2',\n 'h3',\n 'h4',\n 'divider',\n 'bulletList',\n 'orderedList',\n 'blockquote',\n 'hr',\n 'divider',\n 'link',\n 'table',\n 'image',\n 'youtube',\n 'divider',\n 'undo',\n 'redo',\n 'divider',\n 'source',\n]\n\n/** What an inline field offers: the marks a line of a heading is ever given. */\nexport const INLINE_TOOLS: RichTextTool[] = ['bold', 'italic', 'accent']\n\nexport const DEFAULT_ACCEPT = [\n 'image/png',\n 'image/jpeg',\n 'image/gif',\n 'image/webp',\n 'image/svg+xml',\n]\n\nexport interface ToolMeta {\n label: string\n icon?: string\n text?: string\n}\n\nexport const TOOL_META: Record<Exclude<RichTextTool, 'divider'>, ToolMeta> = {\n bold: { label: 'Bold', icon: 'bold' },\n italic: { label: 'Italic', icon: 'italic' },\n accent: { label: 'Accent', icon: 'accent' },\n strike: { label: 'Strikethrough', icon: 'strike' },\n code: { label: 'Inline code', icon: 'code' },\n h2: { label: 'Heading 2', text: 'H2' },\n h3: { label: 'Heading 3', text: 'H3' },\n h4: { label: 'Heading 4', text: 'H4' },\n bulletList: { label: 'Bulleted list', icon: 'bulletList' },\n orderedList: { label: 'Numbered list', icon: 'orderedList' },\n blockquote: { label: 'Quote', icon: 'blockquote' },\n hr: { label: 'Divider', icon: 'hr' },\n link: { label: 'Link', icon: 'link' },\n table: { label: 'Table', icon: 'table' },\n image: { label: 'Image', icon: 'image' },\n youtube: { label: 'YouTube video', icon: 'youtube' },\n undo: { label: 'Undo', icon: 'undo' },\n redo: { label: 'Redo', icon: 'redo' },\n source: { label: 'HTML source', icon: 'source' },\n}\n\n/** Shown as a second row while the caret sits inside a table. */\nexport const TABLE_TOOLS: { key: RichTextTableTool; label: string; icon: string }[] = [\n { key: 'addRowAfter', label: 'Row below', icon: 'rowAfter' },\n { key: 'addRowBefore', label: 'Row above', icon: 'rowBefore' },\n { key: 'addColumnAfter', label: 'Column after', icon: 'columnAfter' },\n { key: 'addColumnBefore', label: 'Column before', icon: 'columnBefore' },\n { key: 'deleteRow', label: 'Delete row', icon: 'deleteRow' },\n { key: 'deleteColumn', label: 'Delete column', icon: 'deleteColumn' },\n { key: 'mergeOrSplit', label: 'Merge or split cells', icon: 'mergeCells' },\n { key: 'deleteTable', label: 'Delete table', icon: 'deleteTable' },\n]\n\nexport type TableToolKey = RichTextTableTool\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\nimport {\n Extension,\n Mark,\n Node,\n createDocument,\n getHTMLFromFragment,\n type Extensions,\n} from '@tiptap/core'\nimport { EditorContent, useEditor } from '@tiptap/vue-3'\nimport StarterKit from '@tiptap/starter-kit'\nimport { TableKit } from '@tiptap/extension-table'\nimport Image from '@tiptap/extension-image'\nimport Youtube from '@tiptap/extension-youtube'\nimport FileHandler from '@tiptap/extension-file-handler'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\nimport { useFormField } from '../../composables/useFormField'\nimport { useLocalized } from '../../composables/useLocalized'\nimport LocalePicker from '../Locales/LocalePicker.vue'\nimport WxTokenMenu from '../../internal/TokenMenu.vue'\nimport {\n fieldAnchor,\n rectOf,\n tokenAnchor,\n tokenSpans,\n tokenText,\n useTokenMenu,\n type TokenAnchor,\n type TokenOption,\n type TokenRange,\n} from '../../composables/useTokens'\nimport WxButton from '../Button/Button.vue'\n// The source view only: CodeMirror comes when somebody opens it, not with every text field.\nimport { WxCodeEditor } from '../CodeEditor'\nimport WxRichTextToolbarButton from './ToolbarButton.vue'\nimport { formatHtml, lostMarkup } from './source'\nimport {\n DEFAULT_ACCEPT,\n DEFAULT_TOOLS,\n INLINE_TOOLS,\n TABLE_TOOLS,\n TOOL_META,\n type TableToolKey,\n} from './tools'\nimport type {\n RichTextEmits,\n RichTextImage,\n RichTextLabelKey,\n RichTextLabels,\n RichTextModelValue,\n RichTextProps,\n RichTextTool,\n} from './types'\n\ndefineOptions({ name: 'WxRichText', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<RichTextProps>(), {\n placeholder: undefined,\n disabled: undefined,\n readonly: false,\n size: undefined,\n status: undefined,\n id: undefined,\n ariaLabel: undefined,\n inline: false,\n // Both left unset rather than defaulted: what they default to depends on `inline`, and\n // `withDefaults` cannot see the other props.\n minHeight: undefined,\n tools: undefined,\n upload: undefined,\n pickImage: undefined,\n accept: () => DEFAULT_ACCEPT,\n labels: undefined,\n localized: false,\n tokens: undefined,\n tokensTitle: 'Placeholders',\n tokensLabel: 'Insert a placeholder',\n})\n\n/**\n * What the editor says when nobody told it otherwise. English is the floor rather than the\n * source of truth: a panel running in ten languages hands its own words in, and a key it\n * forgot stays readable instead of blank.\n */\nconst DEFAULT_LABELS: RichTextLabels = {\n toolbar: 'Text formatting',\n linkAddress: 'Link address',\n youtubeAddress: 'YouTube URL',\n apply: 'Apply',\n cancel: 'Cancel',\n uploading: 'Uploading…',\n sourceLoss: 'The editor does not keep this markup and will remove it:',\n sourceDrop: 'Remove it',\n sourceKeep: 'Keep editing',\n}\n\nfunction label(key: RichTextLabelKey): string {\n const given = props.labels?.[key] ?? DEFAULT_LABELS[key]\n\n if (given !== undefined) return given\n if (key in TOOL_META) return TOOL_META[key as Exclude<RichTextTool, 'divider'>].label\n\n return TABLE_TOOLS.find((tool) => tool.key === key)?.label ?? key\n}\n\nconst emit = defineEmits<RichTextEmits>()\n\n/**\n * The picture node, taught one attribute of its own.\n *\n * ProseMirror keeps only the attributes a node declares — anything else is dropped the first\n * time the document is parsed, silently and in both directions — so the library key has to be\n * a declared attribute rather than something written onto the tag.\n */\nconst LibraryImage = Image.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n path: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute('data-wx-path'),\n renderHTML: (attributes: Record<string, unknown>) =>\n attributes.path === null || attributes.path === undefined\n ? {}\n : { 'data-wx-path': String(attributes.path) },\n },\n }\n },\n})\n\n/**\n * The accent of a line: a bare `<span>`, because that is what a heading written by hand already\n * holds — `Deeply heard<span>.</span>` — and what a site's styles colour. No class: a class would\n * be one more thing to agree on between the panel and every theme.\n */\nconst Accent = Mark.create({\n name: 'accent',\n parseHTML: () => [{ tag: 'span' }],\n renderHTML: () => ['span', 0],\n})\n\n/**\n * The document of an inline field is the line itself — text and its marks, no paragraph around\n * it — so `getHTML()` answers with exactly what a template prints inside its `<h1>`.\n */\nconst LineDocument = Node.create({ name: 'doc', topNode: true, content: 'text*' })\n\n/**\n * Enter has nowhere to go in a line. Swallowed rather than left to ProseMirror, which would try\n * to split a block the schema does not have.\n */\nconst OneLine = Extension.create({\n name: 'wxOneLine',\n addKeyboardShortcuts: () => ({\n Enter: () => true,\n 'Shift-Enter': () => true,\n 'Mod-Enter': () => true,\n }),\n})\n\nconst tokens = useTokenMenu(() => props.tokens)\nconst hasTokens = computed(() => tokens.all.value.length > 0)\n\nconst tokenChipsKey = new PluginKey('wxTokenChips')\n\n/**\n * The known placeholders drawn as chips: a decoration over the text `[name]`, not a node.\n *\n * A node would have to be written back as something, and what the site reads is the brackets\n * typed as text — so the document keeps them as text, `getHTML()` answers with them as text,\n * and the chip exists only on screen. A name nobody registered stays plain, which is the hint\n * that it will print as typed.\n */\nconst TokenChips = Extension.create({\n name: 'wxTokenChips',\n addProseMirrorPlugins: () => [\n new Plugin({\n key: tokenChipsKey,\n props: {\n decorations: (state) => {\n const known = tokens.known.value\n if (known.size === 0) return DecorationSet.empty\n\n const chips: Decoration[] = []\n\n state.doc.descendants((node, pos) => {\n if (!node.isText || !node.text) return\n\n for (const span of tokenSpans(node.text, known)) {\n chips.push(\n Decoration.inline(pos + span.from, pos + span.to, {\n class: 'wx-token',\n 'data-token': span.name,\n }),\n )\n }\n })\n\n return DecorationSet.create(state.doc, chips)\n },\n },\n }),\n ],\n})\n\nfunction inlineExtensions(): Extensions {\n return [\n StarterKit.configure({\n document: false,\n paragraph: false,\n heading: false,\n blockquote: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n listKeymap: false,\n codeBlock: false,\n code: false,\n horizontalRule: false,\n hardBreak: false,\n strike: false,\n underline: false,\n link: false,\n trailingNode: false,\n gapcursor: false,\n }),\n LineDocument,\n Accent,\n OneLine,\n TokenChips,\n ]\n}\n\nfunction documentExtensions(): Extensions {\n return [\n StarterKit.configure({\n link: {\n openOnClick: false,\n autolink: true,\n HTMLAttributes: { rel: 'noopener noreferrer nofollow', target: '_blank' },\n },\n }),\n TableKit.configure({ table: { resizable: true } }),\n LibraryImage.configure({ HTMLAttributes: { class: 'wx-rich-text__image' } }),\n Youtube.configure({ nocookie: true, width: 640, height: 360 }),\n FileHandler.configure({\n allowedMimeTypes: props.accept,\n onDrop: (_editor, files, pos) => void uploadFiles(files, pos),\n onPaste: (_editor, files) => void uploadFiles(files),\n }),\n TokenChips,\n ]\n}\n\nconst toolList = computed(() => props.tools ?? (props.inline ? INLINE_TOOLS : DEFAULT_TOOLS))\n\n/** A line is as tall as a line; a document starts tall enough to look like one. */\nconst bodyHeight = computed(() => props.minHeight ?? (props.inline ? undefined : '220px'))\n\nconst model = defineModel<RichTextModelValue>({ default: '' })\n\nconst field = useFormField(props)\n\nconst locales = useLocalized(props, model)\n\n/** The language on screen, or nothing at all when the field is plain. */\nconst editing = computed(() => (locales.on.value ? locales.active.value : undefined))\n\n/*\n * One editor, one language at a time. The other languages are not rendered anywhere — an\n * editor is a document, not a line, and four of them stacked is four documents to scroll past\n * to reach the next field. What the chip does is swap the document in this one.\n */\nconst currentValue = computed(() => locales.read(editing.value))\nconst focused = ref(false)\nconst uploading = ref(0)\nconst fileInput = ref<HTMLInputElement | null>(null)\n\n/** The link and YouTube buttons open one shared bar rather than a `prompt()`. */\nconst prompt = ref<{ kind: 'link' | 'youtube'; value: string } | null>(null)\n\nconst editable = computed(() => !field.disabled.value && !props.readonly)\n\n/** The HTML on screen while the source view is open; `null` while the editor is. */\nconst source = ref<string | null>(null)\n\n/** What leaving the source would cost, while the editor waits to hear whether that is fine. */\nconst losing = ref<string[] | null>(null)\n\n/** The source as it was opened: leaving it untouched has nothing to check and nothing to apply. */\nlet opened = ''\n\n/** Tiptap renders `<p></p>` for an empty document; a backend wants an empty string. */\nfunction readHtml(): string {\n const instance = editor.value\n if (!instance || instance.isEmpty) return ''\n return instance.getHTML()\n}\n\nconst editor = useEditor({\n content: currentValue.value,\n editable: editable.value,\n extensions: props.inline ? inlineExtensions() : documentExtensions(),\n editorProps: {\n // The list's keys first: Enter picks a placeholder rather than splitting the paragraph.\n handleKeyDown: (_view, event) => tokens.keydown(event, insertToken),\n ...(props.inline\n ? {\n // Pasted lines become one line: the breaks would otherwise vanish between the words\n // and glue the last of one line to the first of the next.\n transformPastedText: (text: string) => text.replace(/\\s*[\\r\\n]+\\s*/g, ' '),\n }\n : {}),\n },\n onUpdate: () => {\n const html = readHtml()\n // Tiptap raises an update for things that are not edits too. Writing the same words back\n // still changes the model's shape — an empty `[]` from the server becomes `{ en: '' }` —\n // and a form that compares snapshots reads that as a change and autosaves a draft.\n if (html === currentValue.value) return\n locales.write(editing.value, html)\n emit('change', html)\n suggestTokens()\n },\n onSelectionUpdate: () => {\n // A caret moved by hand: an open list follows it, or goes.\n if (tokens.open.value) suggestTokens()\n },\n onFocus: () => {\n focused.value = true\n emit('focus')\n },\n onBlur: () => {\n focused.value = false\n tokens.close()\n emit('blur')\n },\n})\n\n/**\n * Only replaces the document when the value really differs, or the caret jumps. Watching the\n * language being edited rather than the model: with a localized field those are different\n * things, and switching the chip has to bring the other language's document in.\n */\nwatch(currentValue, (value) => {\n const instance = editor.value\n if (!instance) return\n // The source view owns the value while it is open; another language brings its own source.\n if (source.value !== null) {\n if (value !== source.value) openSource(value)\n return\n }\n if (value === readHtml()) return\n instance.commands.setContent(value, { emitUpdate: false })\n})\n\n/*\n * Without the update Tiptap sends by default: a form locked for the length of a request and\n * unlocked after it would otherwise hear every editor on it \"change\" at once.\n */\nwatch(editable, (value) => editor.value?.setEditable(value, false))\n\n/*\n * Placeholders arrive after the editor — a panel fetches them — and decorations are only drawn\n * again on a transaction, so the arrival is one: empty, and changing nothing in the document.\n */\nwatch(tokens.known, () => {\n const instance = editor.value\n if (instance) instance.view.dispatch(instance.state.tr.setMeta(tokenChipsKey, true))\n})\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst tokenListId = computed(() => `${field.id.value}-tokens`)\n\n/** Under the line at a position of the document; under the field where nothing is measured. */\nfunction caretAnchor(pos: number): TokenAnchor | undefined {\n try {\n const at = editor.value?.view.coordsAtPos(pos)\n if (at && at.bottom > at.top) {\n return tokenAnchor(rectOf(at.left, at.top, 0, at.bottom - at.top), rootRef.value)\n }\n } catch {\n // No layout to ask (a test), or a position the view has not drawn yet.\n }\n return fieldAnchor(rootRef.value, null)\n}\n\n/** After a keystroke or a caret move: open, narrow or close the list by what is before it. */\nfunction suggestTokens(): void {\n const instance = editor.value\n if (!instance || !hasTokens.value || !editable.value) return\n\n const { selection } = instance.state\n const { $from } = selection\n\n if (!selection.empty || !$from.parent.isTextblock) return tokens.close()\n\n // Every leaf inline node counts as one character, so offsets stay document positions.\n const before = $from.parent.textBetween(0, $from.parentOffset, undefined, '')\n tokens.suggest(before, $from.pos, () =>\n caretAnchor($from.pos - before.length + before.lastIndexOf('[')),\n )\n}\n\nfunction insertToken(token: TokenOption, at: TokenRange): void {\n const instance = editor.value\n if (!instance) return\n\n instance\n .chain()\n .focus()\n .command(({ tr }) => {\n tr.insertText(tokenText(token), at.from, at.to)\n return true\n })\n .run()\n}\n\n/** The toolbar's button: every placeholder, going where the caret is or over the selection. */\nfunction browseTokens(): void {\n const instance = editor.value\n if (!instance) return\n if (tokens.open.value && tokens.browsing.value) return tokens.close()\n\n const { from, to } = instance.state.selection\n instance.commands.focus()\n tokens.browse({ from, to }, caretAnchor(from))\n}\n\nconst isEmpty = computed(() => editor.value?.isEmpty ?? true)\nconst inTable = computed(() => editor.value?.isActive('table') ?? false)\n\nconst classes = computed(() => [\n 'wx-rich-text',\n `wx-rich-text--${field.size.value}`,\n {\n [`wx-rich-text--${field.status.value}`]: field.status.value !== 'default',\n 'is-focused': focused.value,\n 'is-disabled': field.disabled.value,\n 'is-readonly': props.readonly,\n 'is-localized': locales.on.value,\n 'is-inline': props.inline,\n },\n])\n\n/** The caret follows the language into the document it just swapped in. */\nfunction chooseLocale(code: string): void {\n locales.active.value = code\n void nextTick(() => editor.value?.commands.focus())\n}\n\nfunction insertImage(image: RichTextImage, pos?: number) {\n const instance = editor.value\n if (!instance) return\n const at = pos ?? instance.state.selection.anchor\n instance\n .chain()\n .focus()\n .insertContentAt(at, {\n type: 'image',\n attrs: { src: image.url, alt: image.alt, path: image.path ?? null },\n })\n .run()\n}\n\n/**\n * Uploads happen one file at a time and insert only on success — a failed upload\n * leaves nothing behind to clean up.\n */\nasync function uploadFiles(files: File[], pos?: number) {\n if (!props.upload) return\n for (const file of files) {\n uploading.value += 1\n try {\n insertImage(await props.upload(file), pos)\n } catch (error) {\n emit('uploadError', error, file)\n } finally {\n uploading.value -= 1\n }\n }\n}\n\nasync function onImageButton() {\n if (props.pickImage) {\n const chosen = await props.pickImage()\n\n if (chosen) insertImage(typeof chosen === 'string' ? { url: chosen } : chosen)\n\n return\n }\n fileInput.value?.click()\n}\n\nfunction onFilePicked(event: Event) {\n const input = event.target as HTMLInputElement\n const files = Array.from(input.files ?? [])\n input.value = ''\n void uploadFiles(files)\n}\n\nfunction openPrompt(kind: 'link' | 'youtube') {\n const current = kind === 'link' ? (editor.value?.getAttributes('link').href ?? '') : ''\n prompt.value = { kind, value: String(current) }\n}\n\nfunction applyPrompt() {\n const instance = editor.value\n const current = prompt.value\n if (!instance || !current) return\n const value = current.value.trim()\n\n if (current.kind === 'link') {\n if (value) instance.chain().focus().extendMarkRange('link').setLink({ href: value }).run()\n else instance.chain().focus().extendMarkRange('link').unsetLink().run()\n } else if (value) {\n instance.commands.setYoutubeVideo({ src: value })\n }\n\n prompt.value = null\n}\n\nfunction openSource(html = readHtml()) {\n prompt.value = null\n losing.value = null\n opened = formatHtml(html)\n source.value = opened\n}\n\n/**\n * The source goes to the model as it is typed, not when the view closes: a form saved with the\n * source still open saves what is on screen. The server's allowlist still applies to it.\n */\nfunction onSourceInput(html: string) {\n source.value = html\n losing.value = null\n if (html === currentValue.value) return\n locales.write(editing.value, html)\n emit('change', html)\n}\n\n/**\n * Back to the editor, which parses the source against its schema. Markup the schema does not\n * know would vanish without a word, so when a hand edit is about to lose some the view stays\n * open and says what, until it is told to go ahead.\n */\nfunction closeSource(force = false) {\n const instance = editor.value\n const html = source.value\n if (!instance || html === null) return\n\n const edited = html !== opened\n\n if (edited && !force) {\n const parsed = createDocument(html, instance.schema)\n const lost = lostMarkup(html, getHTMLFromFragment(parsed.content, instance.schema))\n\n if (lost.length > 0) {\n losing.value = lost\n return\n }\n }\n\n losing.value = null\n source.value = null\n\n if (edited) instance.commands.setContent(html, { emitUpdate: false })\n\n // Written here rather than left to `onUpdate`: a source typed and typed back changes no node,\n // raises no update, and would leave the model holding the indented copy.\n const normalised = readHtml()\n if (normalised !== currentValue.value) {\n locales.write(editing.value, normalised)\n emit('change', normalised)\n }\n\n void nextTick(() => instance.commands.focus())\n}\n\nfunction onSourceFocus(on: boolean) {\n focused.value = on\n if (on) emit('focus')\n else emit('blur')\n}\n\nfunction toggleSource() {\n if (source.value === null) openSource()\n else closeSource()\n}\n\n/**\n * The source button stays usable on a read-only field — reading the markup is half of what it\n * is for — while every other button waits for the editor to come back.\n */\nfunction toolDisabled(tool: RichTextTool): boolean {\n if (tool === 'source') return field.disabled.value\n return !editable.value || source.value !== null\n}\n\n/** Whether a tool should render at all — the image button needs somewhere to get a file. */\nfunction toolVisible(tool: RichTextTool): boolean {\n if (tool === 'image') return Boolean(props.upload || props.pickImage)\n return true\n}\n\nconst visibleTools = computed(() => toolList.value.filter(toolVisible))\n\nfunction isActive(tool: RichTextTool): boolean {\n if (tool === 'source') return source.value !== null\n const instance = editor.value\n if (!instance) return false\n switch (tool) {\n case 'h2':\n return instance.isActive('heading', { level: 2 })\n case 'h3':\n return instance.isActive('heading', { level: 3 })\n case 'h4':\n return instance.isActive('heading', { level: 4 })\n default:\n return instance.isActive(tool)\n }\n}\n\nfunction run(tool: RichTextTool) {\n if (tool === 'source') return toggleSource()\n const instance = editor.value\n if (!instance) return\n const chain = instance.chain().focus()\n\n switch (tool) {\n case 'bold':\n chain.toggleBold().run()\n break\n case 'italic':\n chain.toggleItalic().run()\n break\n case 'accent':\n chain.toggleMark('accent').run()\n break\n case 'strike':\n chain.toggleStrike().run()\n break\n case 'code':\n chain.toggleCode().run()\n break\n case 'h2':\n chain.toggleHeading({ level: 2 }).run()\n break\n case 'h3':\n chain.toggleHeading({ level: 3 }).run()\n break\n case 'h4':\n chain.toggleHeading({ level: 4 }).run()\n break\n case 'bulletList':\n chain.toggleBulletList().run()\n break\n case 'orderedList':\n chain.toggleOrderedList().run()\n break\n case 'blockquote':\n chain.toggleBlockquote().run()\n break\n case 'hr':\n chain.setHorizontalRule().run()\n break\n case 'undo':\n chain.undo().run()\n break\n case 'redo':\n chain.redo().run()\n break\n case 'table':\n chain.insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()\n break\n case 'link':\n openPrompt('link')\n break\n case 'youtube':\n openPrompt('youtube')\n break\n case 'image':\n void onImageButton()\n break\n }\n}\n\nfunction runTableCommand(key: TableToolKey) {\n const instance = editor.value\n if (!instance) return\n const chain = instance.chain().focus() as unknown as Record<string, () => { run: () => void }>\n chain[key]().run()\n}\n\ndefineExpose({\n editor,\n focus: () => editor.value?.commands.focus(),\n clear: () => editor.value?.commands.clearContent(true),\n})\n</script>\n\n<template>\n <div ref=\"rootRef\" :class=\"classes\">\n <div class=\"wx-rich-text__toolbar\" role=\"toolbar\" :aria-label=\"ariaLabel ?? label('toolbar')\">\n <template v-for=\"(tool, index) in visibleTools\" :key=\"`${tool}-${index}`\">\n <span v-if=\"tool === 'divider'\" class=\"wx-rich-text__divider\" aria-hidden=\"true\" />\n <wx-rich-text-toolbar-button\n v-else\n :icon=\"TOOL_META[tool].icon\"\n :text=\"TOOL_META[tool].text\"\n :label=\"label(tool)\"\n :active=\"isActive(tool)\"\n :disabled=\"toolDisabled(tool)\"\n @click=\"run(tool)\"\n />\n </template>\n\n <wx-rich-text-toolbar-button\n v-if=\"hasTokens\"\n class=\"wx-rich-text__tokens\"\n icon=\"token\"\n :label=\"tokensLabel\"\n :active=\"tokens.open.value && tokens.browsing.value\"\n :disabled=\"!editable || source !== null\"\n @mousedown.prevent\n @click=\"browseTokens\"\n />\n\n <span v-if=\"uploading > 0\" class=\"wx-rich-text__uploading\">{{ label('uploading') }}</span>\n </div>\n\n <wx-token-menu\n v-if=\"hasTokens\"\n :open=\"tokens.open.value\"\n :items=\"tokens.items.value\"\n :active=\"tokens.active.value\"\n :anchor=\"tokens.anchor.value\"\n :list-id=\"tokenListId\"\n :title=\"tokens.browsing.value ? tokensTitle : undefined\"\n :owner=\"rootRef\"\n @choose=\"(token) => tokens.choose(token, insertToken)\"\n @hover=\"(index) => (tokens.active.value = index)\"\n @close=\"tokens.close()\"\n />\n\n <locale-picker\n v-if=\"locales.on.value\"\n :locales=\"locales.list.value\"\n :active=\"locales.active.value\"\n @choose=\"chooseLocale\"\n />\n\n <div\n v-if=\"inTable && editable && source === null\"\n class=\"wx-rich-text__toolbar wx-rich-text__toolbar--table\"\n >\n <wx-rich-text-toolbar-button\n v-for=\"tool in TABLE_TOOLS\"\n :key=\"tool.key\"\n :icon=\"tool.icon\"\n :label=\"label(tool.key)\"\n @click=\"runTableCommand(tool.key)\"\n />\n </div>\n\n <form v-if=\"prompt\" class=\"wx-rich-text__prompt\" @submit.prevent=\"applyPrompt\">\n <label class=\"wx-rich-text__prompt-label\" :for=\"`${field.id.value}-prompt`\">\n {{ label(prompt.kind === 'link' ? 'linkAddress' : 'youtubeAddress') }}\n </label>\n <input\n :id=\"`${field.id.value}-prompt`\"\n v-model=\"prompt.value\"\n class=\"wx-rich-text__prompt-input\"\n type=\"url\"\n :placeholder=\"prompt.kind === 'link' ? 'https://example.com' : 'https://youtu.be/…'\"\n @keydown.esc=\"prompt = null\"\n />\n <wx-rich-text-toolbar-button icon=\"check\" :label=\"label('apply')\" @click=\"applyPrompt\" />\n <wx-rich-text-toolbar-button icon=\"close\" :label=\"label('cancel')\" @click=\"prompt = null\" />\n </form>\n\n <div v-if=\"losing\" class=\"wx-rich-text__prompt wx-rich-text__loss\" role=\"alert\">\n <p class=\"wx-rich-text__loss-text\">\n {{ label('sourceLoss') }}\n <template v-for=\"(item, index) in losing\" :key=\"item\">\n <code>{{ item }}</code\n ><template v-if=\"index < losing.length - 1\">, </template>\n </template>\n </p>\n <wx-button size=\"sm\" type=\"danger\" variant=\"outline\" @click=\"closeSource(true)\">\n {{ label('sourceDrop') }}\n </wx-button>\n <wx-button size=\"sm\" variant=\"text\" @click=\"losing = null\">{{\n label('sourceKeep')\n }}</wx-button>\n </div>\n\n <div v-if=\"source !== null\" class=\"wx-rich-text__source\">\n <wx-code-editor\n :model-value=\"source\"\n language=\"html\"\n line-wrapping\n :min-height=\"bodyHeight\"\n :readonly=\"readonly\"\n :disabled=\"field.disabled.value\"\n :aria-label=\"label('source')\"\n @update:model-value=\"onSourceInput\"\n @focus=\"onSourceFocus(true)\"\n @blur=\"onSourceFocus(false)\"\n />\n </div>\n\n <!-- Hidden rather than unmounted: ProseMirror keeps its view, history and selection. -->\n <div v-show=\"source === null\" class=\"wx-rich-text__body\" :style=\"{ minHeight: bodyHeight }\">\n <span v-if=\"placeholder && isEmpty\" class=\"wx-rich-text__placeholder\">{{ placeholder }}</span>\n <editor-content\n :editor=\"editor\"\n class=\"wx-rich-text__content\"\n :aria-describedby=\"field.describedBy.value\"\n />\n </div>\n\n <input\n v-if=\"upload && !pickImage\"\n ref=\"fileInput\"\n class=\"wx-rich-text__file\"\n type=\"file\"\n :accept=\"accept.join(',')\"\n multiple\n @change=\"onFilePicked\"\n />\n </div>\n</template>\n\n<style scoped>\n.wx-rich-text {\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: 100%;\n background: var(--wx-bg-surface);\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-control);\n color: var(--wx-text-default);\n transition: border-color var(--wx-duration-normal) var(--wx-easing-standard);\n}\n\n.wx-rich-text.is-focused {\n border-color: var(--wx-border-focus);\n}\n\n.wx-rich-text--error,\n.wx-rich-text--error.is-focused {\n border-color: var(--wx-color-danger);\n}\n\n.wx-rich-text--success {\n border-color: var(--wx-color-success);\n}\n\n.wx-rich-text--warning {\n border-color: var(--wx-color-warning);\n}\n\n/*\n * The chip sits in the top corner, over the end of the toolbar rather than over the text: a\n * document is written from the top left and that corner has to stay clear. The toolbar keeps\n * its distance so the last button is not underneath it.\n */\n.wx-rich-text.is-localized {\n --wx-locale-picker-height: 20px;\n --wx-locale-picker-top: var(--wx-space-4);\n --wx-locale-picker-shift: 0;\n}\n\n.wx-rich-text.is-localized .wx-rich-text__toolbar:first-child {\n padding-right: var(--wx-space-40);\n}\n\n.wx-rich-text.is-disabled {\n background: var(--wx-bg-disabled);\n color: var(--wx-text-disabled);\n}\n\n.wx-rich-text__toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-2);\n padding: var(--wx-space-6);\n border-bottom: 1px solid var(--wx-border-muted);\n}\n\n.wx-rich-text__toolbar--table {\n background: var(--wx-bg-subtle);\n}\n\n.wx-rich-text__divider {\n width: 1px;\n height: 18px;\n margin: 0 var(--wx-space-4);\n background: var(--wx-border-default);\n}\n\n.wx-rich-text__uploading {\n margin-left: auto;\n padding: 0 var(--wx-space-8);\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-rich-text__prompt {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n padding: var(--wx-space-8);\n background: var(--wx-bg-subtle);\n border-bottom: 1px solid var(--wx-border-muted);\n}\n\n.wx-rich-text__prompt-label {\n flex: 0 0 auto;\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-rich-text__prompt-input {\n flex: 1 1 auto;\n min-width: 0;\n height: var(--wx-size-control-sm);\n padding: 0 var(--wx-space-10);\n background: var(--wx-bg-surface);\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-xs);\n outline: none;\n color: inherit;\n font-family: inherit;\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-rich-text__prompt-input:focus {\n border-color: var(--wx-border-focus);\n}\n\n.wx-rich-text__loss {\n flex-wrap: wrap;\n background: var(--wx-color-danger-soft);\n}\n\n.wx-rich-text__loss-text {\n flex: 1 1 240px;\n margin: 0;\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-rich-text__loss-text code {\n font-family: var(--wx-font-family-mono);\n white-space: nowrap;\n}\n\n/* The field draws the frame; the code editor inside it is only the surface. */\n.wx-rich-text__source {\n flex: 1 1 auto;\n}\n\n.wx-rich-text__source :deep(.wx-code-editor) {\n border: 0;\n border-radius: 0 0 var(--wx-radius-control) var(--wx-radius-control);\n}\n\n.wx-rich-text__body {\n position: relative;\n flex: 1 1 auto;\n padding: var(--wx-space-12) var(--wx-space-16);\n}\n\n.wx-rich-text__placeholder {\n position: absolute;\n top: var(--wx-space-12);\n left: var(--wx-space-16);\n color: var(--wx-text-placeholder);\n pointer-events: none;\n}\n\n/*\n * A line: the toolbar shrinks to a strip of three buttons and the text sits where an input's\n * would, so a form mixing the two does not jump between them.\n */\n.wx-rich-text.is-inline .wx-rich-text__toolbar {\n padding: var(--wx-space-2) var(--wx-space-4);\n}\n\n.wx-rich-text.is-inline .wx-rich-text__body {\n padding: var(--wx-space-6) var(--wx-space-12);\n}\n\n.wx-rich-text.is-inline .wx-rich-text__placeholder {\n top: var(--wx-space-6);\n left: var(--wx-space-12);\n}\n\n/* The accent shows as one: the editor sees which words a site will colour. */\n.wx-rich-text.is-inline .wx-rich-text__content :deep(span) {\n color: var(--wx-color-primary);\n}\n\n.wx-rich-text__file {\n display: none;\n}\n\n/* The editing surface itself is rendered by ProseMirror. */\n.wx-rich-text__content :deep(.ProseMirror) {\n outline: none;\n min-height: inherit;\n line-height: var(--wx-font-line-height-relaxed);\n}\n\n/*\n * Host applications decorate content tags globally — VitePress, which renders these\n * docs, gives every h2 a top border and 24px of padding, and turns tables into\n * `display: block`. The editing surface has to look the same wherever it is\n * embedded, so anything a host is likely to set is stated here instead of inherited.\n */\n.wx-rich-text__content :deep(h1),\n.wx-rich-text__content :deep(h2),\n.wx-rich-text__content :deep(h3),\n.wx-rich-text__content :deep(h4),\n.wx-rich-text__content :deep(h5),\n.wx-rich-text__content :deep(h6) {\n margin: var(--wx-space-18) 0 var(--wx-space-8);\n padding: 0;\n border: 0;\n color: var(--wx-text-strong);\n font-weight: var(--wx-font-weight-semibold);\n line-height: var(--wx-font-line-height-tight);\n}\n\n.wx-rich-text__content :deep(p) {\n margin: 0 0 var(--wx-space-12);\n}\n\n/* Nothing pushes the document away from the top or bottom edge of the field. */\n.wx-rich-text__content :deep(.ProseMirror > :first-child) {\n margin-top: 0;\n}\n\n.wx-rich-text__content :deep(.ProseMirror > :last-child) {\n margin-bottom: 0;\n}\n\n.wx-rich-text__content :deep(h2) {\n font-size: var(--wx-font-size-2xl);\n}\n\n.wx-rich-text__content :deep(h3) {\n font-size: var(--wx-font-size-xl);\n}\n\n.wx-rich-text__content :deep(h4) {\n font-size: var(--wx-font-size-lg);\n}\n\n.wx-rich-text__content :deep(ul),\n.wx-rich-text__content :deep(ol) {\n margin: 0 0 var(--wx-space-12);\n padding-left: var(--wx-space-24);\n}\n\n.wx-rich-text__content :deep(blockquote) {\n margin: 0 0 var(--wx-space-12);\n padding-left: var(--wx-space-16);\n border-left: 3px solid var(--wx-color-primary-soft);\n color: var(--wx-text-muted);\n}\n\n.wx-rich-text__content :deep(a) {\n color: var(--wx-text-link);\n text-decoration: underline;\n}\n\n.wx-rich-text__content :deep(code) {\n padding: 2px var(--wx-space-4);\n background: var(--wx-bg-fill);\n border-radius: var(--wx-radius-xs);\n font-family: var(--wx-font-family-mono);\n font-size: 0.9em;\n}\n\n.wx-rich-text__content :deep(pre) {\n margin: 0 0 var(--wx-space-12);\n padding: var(--wx-space-12);\n background: var(--wx-bg-inverse);\n border-radius: var(--wx-radius-xs);\n color: var(--wx-text-inverse);\n overflow-x: auto;\n}\n\n.wx-rich-text__content :deep(hr) {\n margin: var(--wx-space-18) 0;\n border: none;\n border-top: 1px solid var(--wx-border-default);\n}\n\n.wx-rich-text__content :deep(img),\n.wx-rich-text__content :deep(iframe) {\n max-width: 100%;\n border-radius: var(--wx-radius-xs);\n}\n\n.wx-rich-text__content :deep(img.ProseMirror-selectednode) {\n outline: 2px solid var(--wx-color-primary);\n}\n\n.wx-rich-text__content :deep(table) {\n /* Stated because hosts turn tables into `display: block` for horizontal scrolling. */\n display: table;\n width: 100%;\n margin: 0 0 var(--wx-space-12);\n border-collapse: collapse;\n table-layout: fixed;\n overflow: hidden;\n}\n\n.wx-rich-text__content :deep(th),\n.wx-rich-text__content :deep(td) {\n position: relative;\n padding: var(--wx-space-6) var(--wx-space-10);\n border: 1px solid var(--wx-border-default);\n vertical-align: top;\n}\n\n.wx-rich-text__content :deep(th) {\n background: var(--wx-bg-fill);\n font-weight: var(--wx-font-weight-semibold);\n text-align: left;\n}\n\n.wx-rich-text__content :deep(.selectedCell::after) {\n content: '';\n position: absolute;\n inset: 0;\n background: var(--wx-color-primary-soft);\n pointer-events: none;\n}\n\n.wx-rich-text__content :deep(.column-resize-handle) {\n position: absolute;\n top: 0;\n right: -2px;\n bottom: 0;\n width: 4px;\n background: var(--wx-color-primary);\n cursor: col-resize;\n}\n</style>\n"],"names":["richTextIcons","_createElementBlock","_normalizeClass","__props","$emit","_openBlock","_hoisted_2","_createElementVNode","_unref","_hoisted_4","_toDisplayString","CONTAINERS","LINES","VOID","TAG","formatHtml","html","indent","out","depth","inLine","inPre","last","breakLine","match","token","closing","rawName","selfClosing","name","free","isVoid","SPELLINGS","census","counts","add","key","template","element","tag","attribute","lostMarkup","before","after","had","kept","lost","count","droppedTags","on","DEFAULT_TOOLS","INLINE_TOOLS","DEFAULT_ACCEPT","TOOL_META","TABLE_TOOLS","props","DEFAULT_LABELS","label","given","tool","emit","__emit","LibraryImage","Image","attributes","Accent","Mark","LineDocument","Node","OneLine","Extension","tokens","useTokenMenu","hasTokens","computed","tokenChipsKey","PluginKey","TokenChips","Plugin","state","known","DecorationSet","chips","node","pos","span","tokenSpans","Decoration","inlineExtensions","StarterKit","documentExtensions","TableKit","Youtube","FileHandler","_editor","files","uploadFiles","toolList","bodyHeight","model","_useModel","field","useFormField","locales","useLocalized","editing","currentValue","focused","ref","uploading","fileInput","prompt","editable","source","losing","opened","readHtml","instance","editor","useEditor","_view","event","insertToken","text","suggestTokens","watch","value","openSource","rootRef","tokenListId","caretAnchor","at","tokenAnchor","rectOf","fieldAnchor","selection","$from","tr","tokenText","browseTokens","from","to","isEmpty","inTable","classes","chooseLocale","code","nextTick","insertImage","image","file","error","onImageButton","chosen","onFilePicked","input","openPrompt","kind","current","applyPrompt","onSourceInput","closeSource","force","edited","parsed","createDocument","getHTMLFromFragment","normalised","onSourceFocus","toggleSource","toolDisabled","toolVisible","visibleTools","isActive","run","chain","runTableCommand","__expose","_Fragment","_renderList","index","_createBlock","WxRichTextToolbarButton","$event","_hoisted_3","WxTokenMenu","_cache","LocalePicker","_hoisted_5","_vModelText","_createVNode","_hoisted_7","_hoisted_8","_createTextVNode","item","WxButton","_hoisted_9","WxCodeEditor","_hoisted_10","EditorContent"],"mappings":"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"}
@@ -1,44 +1,27 @@
1
- import { Extension } from '@codemirror/state';
2
1
  import { EditorView } from '@codemirror/view';
3
- import { CodeEditorDiagnostic, CodeEditorProps } from './types';
4
- type __VLS_Props = CodeEditorProps;
5
- declare function format(): boolean;
6
- type __VLS_PublicProps = {
7
- modelValue?: string;
8
- } & __VLS_Props;
9
- declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {
10
- view: import('vue').ShallowRef<EditorView | null, EditorView | null>;
11
- focus: () => void | undefined;
12
- format: typeof format;
13
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
14
- change: (value: string) => any;
15
- "update:modelValue": (value: string) => any;
16
- blur: () => any;
17
- focus: () => any;
18
- lint: (diagnostics: CodeEditorDiagnostic[]) => any;
19
- }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
20
- onChange?: ((value: string) => any) | undefined;
21
- "onUpdate:modelValue"?: ((value: string) => any) | undefined;
22
- onBlur?: (() => any) | undefined;
23
- onFocus?: (() => any) | undefined;
24
- onLint?: ((diagnostics: CodeEditorDiagnostic[]) => any) | undefined;
25
- }>, {
26
- size: import('../..').ControlSize;
27
- disabled: boolean;
28
- placeholder: string;
29
- ariaLabel: string;
30
- status: import('../..').ControlStatus;
31
- id: string;
32
- language: import('./types').CodeEditorLanguage;
33
- readonly: boolean;
34
- minHeight: string;
35
- maxHeight: string;
36
- lineNumbers: boolean;
37
- lineWrapping: boolean;
38
- tabSize: number;
39
- lint: boolean;
40
- extensions: Extension[];
41
- }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
42
- host: HTMLDivElement;
43
- }, any>;
2
+ import { CodeEditorDiagnostic, CodeEditorModelValue, CodeEditorProps } from './types';
3
+ declare const _default: <T extends CodeEditorModelValue = string>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
4
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
5
+ readonly onChange?: ((value: string) => any) | undefined;
6
+ readonly "onUpdate:modelValue"?: ((value: T) => any) | undefined;
7
+ readonly onBlur?: (() => any) | undefined;
8
+ readonly onFocus?: (() => any) | undefined;
9
+ readonly onLint?: ((diagnostics: CodeEditorDiagnostic[]) => any) | undefined;
10
+ } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onChange" | "onUpdate:modelValue" | "onFocus" | "onBlur" | "onLint"> & ({
11
+ modelValue?: T;
12
+ } & CodeEditorProps) & Partial<{}>> & import('vue').PublicProps;
13
+ expose(exposed: import('vue').ShallowUnwrapRef<{
14
+ view: import('vue').ShallowRef<EditorView | null, EditorView | null>;
15
+ focus: () => void | undefined;
16
+ format: () => boolean;
17
+ }>): void;
18
+ attrs: any;
19
+ slots: {};
20
+ emit: (((evt: "change", value: string) => void) & ((evt: "blur") => void) & ((evt: "focus") => void) & ((evt: "lint", diagnostics: CodeEditorDiagnostic[]) => void)) & ((evt: "update:modelValue", value: T) => void);
21
+ }>) => import('vue').VNode & {
22
+ __ctx?: Awaited<typeof __VLS_setup>;
23
+ };
44
24
  export default _default;
25
+ type __VLS_PrettifyLocal<T> = {
26
+ [K in keyof T]: T[K];
27
+ } & {};
@@ -1,2 +1,46 @@
1
- export { default as WxCodeEditor } from './CodeEditor';
1
+ export declare const WxCodeEditor: (<T extends import('./types').CodeEditorModelValue = string>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: {
2
+ attrs: any;
3
+ emit: (((evt: "change", value: string) => void) & ((evt: "blur") => void) & ((evt: "focus") => void) & ((evt: "lint", diagnostics: import('./types').CodeEditorDiagnostic[]) => void)) & ((evt: "update:modelValue", value: T) => void);
4
+ slots: {};
5
+ }, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
6
+ props: {
7
+ readonly onChange?: ((value: string) => any) | undefined;
8
+ readonly "onUpdate:modelValue"?: ((value: T) => any) | undefined;
9
+ readonly onFocus?: (() => any) | undefined;
10
+ readonly onBlur?: (() => any) | undefined;
11
+ readonly onLint?: ((diagnostics: import('./types').CodeEditorDiagnostic[]) => any) | undefined;
12
+ modelValue?: T | undefined;
13
+ language?: import('./types').CodeEditorLanguage | undefined;
14
+ placeholder?: string | undefined;
15
+ disabled?: boolean | undefined;
16
+ readonly?: boolean | undefined;
17
+ size?: import('../..').ControlSize | undefined;
18
+ status?: import('../..').ControlStatus | undefined;
19
+ id?: string | undefined;
20
+ ariaLabel?: string | undefined;
21
+ minHeight?: string | undefined;
22
+ maxHeight?: string | undefined;
23
+ lineNumbers?: boolean | undefined;
24
+ lineWrapping?: boolean | undefined;
25
+ tabSize?: number | undefined;
26
+ lint?: boolean | undefined;
27
+ completions?: import('./types').CodeEditorCompletions | undefined;
28
+ localized?: boolean | undefined;
29
+ extensions?: import('@codemirror/state').Extension[] | undefined;
30
+ } & import('vue').PublicProps;
31
+ expose(exposed: import('vue').ShallowUnwrapRef<{
32
+ view: import('vue').ShallowRef<import('@codemirror/view').EditorView | null, import('@codemirror/view').EditorView | null>;
33
+ focus: () => void | undefined;
34
+ format: () => boolean;
35
+ }>): void;
36
+ attrs: any;
37
+ slots: {};
38
+ emit: (((evt: "change", value: string) => void) & ((evt: "blur") => void) & ((evt: "focus") => void) & ((evt: "lint", diagnostics: import('./types').CodeEditorDiagnostic[]) => void)) & ((evt: "update:modelValue", value: T) => void);
39
+ }>) => import('vue').VNode & {
40
+ __ctx?: Awaited<typeof __VLS_setup>;
41
+ }) & {
42
+ name: string;
43
+ };
2
44
  export type * from './types';
45
+ export { mustacheCompletions, mustacheHighlighting, mustacheProblems, scanMustache, } from './mustache';
46
+ export type { MustacheProblems, MustacheTag, MustacheTagKind } from './mustache';
@@ -0,0 +1,66 @@
1
+ import { Extension } from '@codemirror/state';
2
+ import { CodeEditorCompleter, MustacheVariable, MustacheWords } from './types';
3
+ /**
4
+ * Mustache over text or HTML: `{{name}}`, sections `{{#list}}…{{/list}}`, `{{^name}}`, comments.
5
+ *
6
+ * Neither CodeMirror nor a maintained package carries Mustache for CodeMirror 6 (the legacy modes
7
+ * have Jinja and Velocity, not Mustache), and a template of a notification is a dozen lines — so
8
+ * the tags are found by one scan of the document and painted as marks over whatever language is
9
+ * underneath. The same scan answers the completion source and the checks a form runs on a
10
+ * template, so the three never disagree about where a tag begins.
11
+ */
12
+ /** What a tag is, by its first character. */
13
+ export type MustacheTagKind = 'variable' | 'section' | 'inverted' | 'close' | 'comment'
14
+ /** `{{{name}}}` and `{{&name}}`: printed raw by Mustache itself. */
15
+ | 'raw'
16
+ /** `{{>partial}}`, `{{=<% %>=}}`, `{{%pragma}}`, `{{$block}}`, `{{<parent}}`. */
17
+ | 'other'
18
+ /** `{{` with no `}}` after it. */
19
+ | 'unclosed';
20
+ export interface MustacheTag {
21
+ kind: MustacheTagKind;
22
+ /** The name inside, trimmed; empty for a comment and for a tag still being typed. */
23
+ name: string;
24
+ /** Offset of the first `{`. */
25
+ from: number;
26
+ /** Offset just past the last `}` — the end of the document for an unclosed tag. */
27
+ to: number;
28
+ }
29
+ /** Every tag of a template, in order. */
30
+ export declare function scanMustache(text: string): MustacheTag[];
31
+ /** The sections open at an offset, the innermost last. */
32
+ export declare function openSections(tags: MustacheTag[], before: number): string[];
33
+ export interface MustacheProblems {
34
+ /** Sections opened and never closed. */
35
+ unclosed: string[];
36
+ /** `{{/name}}` with nothing of that name open. */
37
+ stray: string[];
38
+ /** Names no variable and no field of an open list has — only when variables were given. */
39
+ unknown: string[];
40
+ /** Tags Mustache would read and a template here may not use: partials, delimiters, pragmas. */
41
+ refused: string[];
42
+ /** `{{` that never ends. */
43
+ broken: boolean;
44
+ }
45
+ /**
46
+ * What is wrong with a template as it stands, by the same rules the completion knows: the open
47
+ * list's fields inside its section, the variables everywhere. With no variables the names are not
48
+ * judged — a template for any event may say anything.
49
+ */
50
+ export declare function mustacheProblems(text: string, variables?: MustacheVariable[]): MustacheProblems;
51
+ /** The tags painted as marks over the language underneath. */
52
+ export declare function mustacheHighlighting(): Extension;
53
+ /**
54
+ * Mustache's own suggestions after `{{`: the variables and the fields of the list being looped
55
+ * over, a list as a whole section, `{{#…}}` / `{{^…}}` for "has a value" / "is empty", and after
56
+ * `{{/` the section open nearest. With no variables only the syntax is offered — a template for
57
+ * any event knows no names.
58
+ *
59
+ * Every suggestion ends the tag: the `}}` that closing brackets typed is taken over rather than
60
+ * doubled, and a section is written whole with the caret inside.
61
+ *
62
+ * ```ts
63
+ * <wx-code-editor language="mustache" :completions="mustacheCompletions(variables)" />
64
+ * ```
65
+ */
66
+ export declare function mustacheCompletions(variables?: MustacheVariable[], words?: Partial<MustacheWords>): CodeEditorCompleter;
@@ -1,8 +1,79 @@
1
1
  import { Extension } from '@codemirror/state';
2
2
  import { EditorView } from '@codemirror/view';
3
3
  import { ControlSize, ControlStatus } from '../../composables/useFormField';
4
- /** The languages the editor highlights out of the box. `plain` highlights nothing. */
5
- export type CodeEditorLanguage = 'json' | 'javascript' | 'typescript' | 'html' | 'css' | 'markdown' | 'yaml' | 'php' | 'plain';
4
+ import { LocalizedValue } from '../../composables/useLocalized';
5
+ /** The document, or under `localized` one per language. */
6
+ export type CodeEditorModelValue = string | LocalizedValue | null | undefined;
7
+ /**
8
+ * The languages the editor highlights out of the box. `plain` highlights nothing; `mustache` is
9
+ * Mustache tags over text, `mustache-html` over HTML.
10
+ */
11
+ export type CodeEditorLanguage = 'json' | 'javascript' | 'typescript' | 'html' | 'css' | 'markdown' | 'yaml' | 'php' | 'mustache' | 'mustache-html' | 'plain';
12
+ /** One suggestion of the completion list. */
13
+ export interface CodeEditorCompletion {
14
+ /** What the list shows, and what typing is matched against. */
15
+ label: string;
16
+ /**
17
+ * What goes into the document instead of the label. `${}` marks where the caret lands and
18
+ * `${name}` a field to type over, as in CodeMirror's snippets.
19
+ */
20
+ apply?: string;
21
+ /** A short word beside the label: a type, an example value. */
22
+ detail?: string;
23
+ /** A longer description, shown beside the list for the suggestion in focus. */
24
+ info?: string;
25
+ /** CodeMirror's kind of suggestion — `variable`, `keyword`, `property` and the like. */
26
+ type?: string;
27
+ /** Higher comes first among equally good matches; -99 … 99. */
28
+ boost?: number;
29
+ }
30
+ export interface CodeEditorCompletionContext {
31
+ /** The whole document. */
32
+ text: string;
33
+ /** Where the caret is. */
34
+ pos: number;
35
+ /** Asked for with Ctrl+Space rather than by typing. */
36
+ explicit: boolean;
37
+ }
38
+ export interface CodeEditorCompletionResult {
39
+ /** Where the text the suggestions replace starts. */
40
+ from: number;
41
+ /** Where it ends; the caret when left out. */
42
+ to?: number;
43
+ options: CodeEditorCompletion[];
44
+ }
45
+ /** Suggestions for a place in the document, or `null` for none there. */
46
+ export type CodeEditorCompleter = (context: CodeEditorCompletionContext) => CodeEditorCompletionResult | null;
47
+ /**
48
+ * A fixed vocabulary — completed from the word before the caret — or a function that decides
49
+ * by the document itself, like `mustacheCompletions()`.
50
+ */
51
+ export type CodeEditorCompletions = CodeEditorCompletion[] | CodeEditorCompleter;
52
+ /** A value a Mustache template may use, for `mustacheCompletions()` and `mustacheProblems()`. */
53
+ export interface MustacheVariable {
54
+ /** `name`, `field.phone`; `field.*` for a family of names. */
55
+ name: string;
56
+ /** What it is, in words. */
57
+ label?: string;
58
+ /** A value it may take. */
59
+ example?: unknown;
60
+ /** A list: the fields of each of its elements. */
61
+ fields?: {
62
+ name: string;
63
+ label?: string;
64
+ }[];
65
+ }
66
+ /** The words of the Mustache suggestions — English by default, `:name` filled in. */
67
+ export interface MustacheWords {
68
+ variable: string;
69
+ list: string;
70
+ field: string;
71
+ section: string;
72
+ inverted: string;
73
+ close: string;
74
+ comment: string;
75
+ example: string;
76
+ }
6
77
  export interface CodeEditorDiagnostic {
7
78
  /** Offset of the first character the problem covers. */
8
79
  from: number;
@@ -35,6 +106,16 @@ export interface CodeEditorProps {
35
106
  * Only JSON ships with one; the flag is ignored elsewhere.
36
107
  */
37
108
  lint?: boolean;
109
+ /**
110
+ * Suggestions while typing, and on Ctrl+Space: a list of words, or a function of the document
111
+ * and the caret — `mustacheCompletions(variables)` for a Mustache template.
112
+ */
113
+ completions?: CodeEditorCompletions;
114
+ /**
115
+ * Edits the document in every language the site publishes in, with a selector on the field;
116
+ * the model is then `{ en: '…', uk: '…' }`.
117
+ */
118
+ localized?: boolean;
38
119
  /** Extra CodeMirror extensions, for anything this component does not wrap. */
39
120
  extensions?: Extension[];
40
121
  }
@@ -33,9 +33,9 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_PublicProps,
33
33
  clearable: boolean;
34
34
  status: import('../..').ControlStatus;
35
35
  id: string;
36
- readonly: boolean;
37
- format: string;
38
36
  locale: string | import('../..').DateFnsLocale;
37
+ format: string;
38
+ readonly: boolean;
39
39
  valueFormat: string;
40
40
  minDate: string | Date;
41
41
  maxDate: string | Date;
@@ -32,9 +32,9 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_PublicProps,
32
32
  clearable: boolean;
33
33
  status: import('../..').ControlStatus;
34
34
  id: string;
35
- readonly: boolean;
36
- format: string;
37
35
  locale: string | import('../..').DateFnsLocale;
36
+ format: string;
37
+ readonly: boolean;
38
38
  valueFormat: string;
39
39
  minDate: string | Date;
40
40
  maxDate: string | Date;
@@ -31,9 +31,9 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_PublicProps,
31
31
  clearable: boolean;
32
32
  status: import('../..').ControlStatus;
33
33
  id: string;
34
- readonly: boolean;
35
- format: string;
36
34
  locale: string | import('../..').DateFnsLocale;
35
+ format: string;
36
+ readonly: boolean;
37
37
  valueFormat: string;
38
38
  minDate: string | Date;
39
39
  maxDate: string | Date;
@@ -97,10 +97,10 @@ declare function __VLS_template(): {
97
97
  status: import('..').InputStatus;
98
98
  id: string;
99
99
  readonly: boolean;
100
+ localized: boolean;
100
101
  autocomplete: string;
101
102
  maxlength: number;
102
103
  showCount: boolean;
103
- localized: boolean;
104
104
  tokens: import('../..').TokenOption[];
105
105
  tokensTitle: string;
106
106
  tokensLabel: string;
@@ -134,10 +134,10 @@ declare function __VLS_template(): {
134
134
  status: import('..').InputStatus;
135
135
  id: string;
136
136
  readonly: boolean;
137
+ localized: boolean;
137
138
  autocomplete: string;
138
139
  maxlength: number;
139
140
  showCount: boolean;
140
- localized: boolean;
141
141
  tokens: import('../..').TokenOption[];
142
142
  tokensTitle: string;
143
143
  tokensLabel: string;
@@ -150,7 +150,7 @@ declare function __VLS_template(): {
150
150
  onClear?: (() => any) | undefined;
151
151
  onBlur?: ((event: FocusEvent) => any) | undefined;
152
152
  onFocus?: ((event: FocusEvent) => any) | undefined;
153
- }>, "input" | "select" | "blur" | "focus" | ("size" | "disabled" | "type" | "placeholder" | "ariaLabel" | "clearable" | "status" | "id" | "readonly" | "autocomplete" | "maxlength" | "showCount" | "localized" | "tokens" | "tokensTitle" | "tokensLabel")> & {
153
+ }>, "input" | "select" | "blur" | "focus" | ("size" | "disabled" | "type" | "placeholder" | "ariaLabel" | "clearable" | "status" | "id" | "readonly" | "localized" | "autocomplete" | "maxlength" | "showCount" | "tokens" | "tokensTitle" | "tokensLabel")> & {
154
154
  focus: () => void | undefined;
155
155
  blur: () => void | undefined;
156
156
  select: () => void | undefined;
@@ -286,10 +286,10 @@ declare const __VLS_component: import('vue').DefineComponent<FileCardProps, {},
286
286
  status: import('..').InputStatus;
287
287
  id: string;
288
288
  readonly: boolean;
289
+ localized: boolean;
289
290
  autocomplete: string;
290
291
  maxlength: number;
291
292
  showCount: boolean;
292
- localized: boolean;
293
293
  tokens: import('../..').TokenOption[];
294
294
  tokensTitle: string;
295
295
  tokensLabel: string;
@@ -323,10 +323,10 @@ declare const __VLS_component: import('vue').DefineComponent<FileCardProps, {},
323
323
  status: import('..').InputStatus;
324
324
  id: string;
325
325
  readonly: boolean;
326
+ localized: boolean;
326
327
  autocomplete: string;
327
328
  maxlength: number;
328
329
  showCount: boolean;
329
- localized: boolean;
330
330
  tokens: import('../..').TokenOption[];
331
331
  tokensTitle: string;
332
332
  tokensLabel: string;
@@ -339,7 +339,7 @@ declare const __VLS_component: import('vue').DefineComponent<FileCardProps, {},
339
339
  onClear?: (() => any) | undefined;
340
340
  onBlur?: ((event: FocusEvent) => any) | undefined;
341
341
  onFocus?: ((event: FocusEvent) => any) | undefined;
342
- }>, "input" | "select" | "blur" | "focus" | ("size" | "disabled" | "type" | "placeholder" | "ariaLabel" | "clearable" | "status" | "id" | "readonly" | "autocomplete" | "maxlength" | "showCount" | "localized" | "tokens" | "tokensTitle" | "tokensLabel")> & {
342
+ }>, "input" | "select" | "blur" | "focus" | ("size" | "disabled" | "type" | "placeholder" | "ariaLabel" | "clearable" | "status" | "id" | "readonly" | "localized" | "autocomplete" | "maxlength" | "showCount" | "tokens" | "tokensTitle" | "tokensLabel")> & {
343
343
  focus: () => void | undefined;
344
344
  blur: () => void | undefined;
345
345
  select: () => void | undefined;
@@ -65,6 +65,7 @@ export declare const builtinIcons: {
65
65
  readonly calendar: "<rect x=\"3.5\" y=\"5.5\" width=\"17\" height=\"15\" rx=\"2.5\"/><path d=\"M3.5 10h17M8 3.5V7M16 3.5V7\"/>";
66
66
  readonly clock: "<circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M12 7.5V12l3 2\"/>";
67
67
  readonly mail: "<rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2.5\"/><path d=\"m4 7.5 6.9 5a2 2 0 0 0 2.2 0l6.9-5\"/>";
68
+ readonly send: "<path d=\"M20.5 3.5 3.8 10.4a.6.6 0 0 0 0 1.1l6.3 2.4 2.4 6.3a.6.6 0 0 0 1.1 0Z\"/><path d=\"m10.1 13.9 4.6-4.6\"/>";
68
69
  readonly phone: "<path d=\"M7 3.5 9.5 4l1.2 3.4-1.9 1.6a11 11 0 0 0 5.2 5.2l1.6-1.9 3.4 1.2.5 2.5a2 2 0 0 1-2 2.3A15.5 15.5 0 0 1 4.7 5.5a2 2 0 0 1 2.3-2Z\"/>";
69
70
  readonly bell: "<path d=\"M7 10a5 5 0 0 1 10 0c0 4 1.5 5.5 1.5 5.5h-13S7 14 7 10Z\"/><path d=\"M10.2 18.5a2 2 0 0 0 3.6 0\"/>";
70
71
  readonly 'map-pin': "<path d=\"M12 21s6.5-6 6.5-10.5a6.5 6.5 0 0 0-13 0C5.5 15 12 21 12 21Z\"/><circle cx=\"12\" cy=\"10.3\" r=\"2.4\"/>";
@@ -32,8 +32,8 @@ declare const _default: import('vue').DefineComponent<ImageEditorProps, {
32
32
  disabled: boolean;
33
33
  footer: boolean;
34
34
  maxWidth: number;
35
- maxHeight: number;
36
35
  format: "auto" | "image/jpeg" | "image/png" | "image/webp";
36
+ maxHeight: number;
37
37
  background: string;
38
38
  filters: boolean;
39
39
  resizable: boolean;
@@ -47,10 +47,10 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_PublicProps,
47
47
  status: import('./types').InputStatus;
48
48
  id: string;
49
49
  readonly: boolean;
50
+ localized: boolean;
50
51
  autocomplete: string;
51
52
  maxlength: number;
52
53
  showCount: boolean;
53
- localized: boolean;
54
54
  tokens: TokenOption[];
55
55
  tokensTitle: string;
56
56
  tokensLabel: string;