@inkandswitch/patchwork-pkg-base 0.0.126 → 0.0.127
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/module-settings-url +1 -1
- package/package.json +1 -1
- package/packages/codemirror-markdown/dist/extensions/links.js +16 -0
- package/packages/codemirror-markdown/dist/extensions/links.js.map +2 -2
- package/packages/codemirror-markdown/dist/extensions/markdown.js +211 -113
- package/packages/codemirror-markdown/dist/extensions/markdown.js.map +4 -4
- package/plugin-registry-url +1 -1
package/module-settings-url
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
automerge:
|
|
1
|
+
automerge:2nY33XxqmEjeUiRH68vhpL8W9FrT
|
package/package.json
CHANGED
|
@@ -86,6 +86,22 @@ function getLinks(view) {
|
|
|
86
86
|
from,
|
|
87
87
|
to,
|
|
88
88
|
enter: (node) => {
|
|
89
|
+
if (node.name === "Autolink") {
|
|
90
|
+
const url = node.node.getChild("URL");
|
|
91
|
+
const inside = selection.from >= node.from && selection.from <= node.to;
|
|
92
|
+
const spans = selection.from < node.from && selection.to > node.to;
|
|
93
|
+
if (url && !inside && !spans) {
|
|
94
|
+
const text = state.doc.sliceString(url.from, url.to);
|
|
95
|
+
const href = /^[a-z][a-z0-9+.-]*:/i.test(text) || !text.includes("@") ? text : `mailto:${text}`;
|
|
96
|
+
widgets.push(
|
|
97
|
+
Decoration.replace({ widget: new LinkWidget(href, text) }).range(
|
|
98
|
+
node.from,
|
|
99
|
+
node.to
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
89
105
|
if (node.name === "Link") {
|
|
90
106
|
const linkFrom = node.from;
|
|
91
107
|
const linkTo = node.to;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/extensions/links.ts", "../../src/themes/links.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n EditorView,\n Decoration,\n WidgetType,\n ViewPlugin,\n ViewUpdate,\n type DecorationSet,\n} from \"@codemirror/view\";\nimport {\n type EditorState,\n Range,\n StateEffect,\n StateField,\n} from \"@codemirror/state\";\nimport { syntaxTree } from \"@codemirror/language\";\nimport { isValidAutomergeUrl } from \"@automerge/automerge-repo/slim\";\nimport { linkTheme } from \"../themes/links.ts\";\n\n/**\n * Open a link from the editor. Automerge urls open inside Patchwork rather\n * than as a browser URL (automerge: isn't navigable); bubbles/composed so the\n * event escapes the editor and any shadow root up to whoever drives selection.\n */\nexport function openUrl(url: string, from: Element) {\n if (isValidAutomergeUrl(url)) {\n from.dispatchEvent(\n new CustomEvent(\"patchwork:open-document\", {\n detail: { url },\n bubbles: true,\n composed: true,\n }),\n );\n } else {\n // GFM autolinks `www.example.com` with no scheme.\n const href = /^[a-z][a-z0-9+.-]*:/i.test(url) ? url : `https://${url}`;\n window.open(href, \"_blank\", \"noopener,noreferrer\");\n }\n}\n\n/**\n * Widget to render a clickable link in the editor\n */\nclass LinkWidget extends WidgetType {\n readonly url: string;\n readonly text: string;\n\n constructor(url: string, text: string) {\n super();\n this.url = url;\n this.text = text;\n }\n\n eq(other: LinkWidget) {\n return other.url === this.url && other.text === this.text;\n }\n\n toDOM() {\n const link = document.createElement(\"a\");\n link.href = this.url;\n link.textContent = this.text;\n link.className = \"cm-link\";\n link.title = this.url;\n\n link.onclick = (e) => {\n e.preventDefault();\n openUrl(this.url, link);\n };\n\n return link;\n }\n\n ignoreEvent(e: Event) {\n // Let click events through to our widget\n return e.type !== \"click\";\n }\n}\n\nfunction getLinks(view: EditorView) {\n const widgets: Range<Decoration>[] = [];\n const { state } = view;\n const selection = state.selection.main;\n\n for (let { from, to } of view.visibleRanges) {\n syntaxTree(state).iterate({\n from,\n to,\n enter: (node) => {\n // Match markdown link syntax\n if (node.name === \"Link\") {\n const linkFrom = node.from;\n const linkTo = node.to;\n\n // Check if cursor is inside this link\n const cursorInLink =\n selection.from >= linkFrom && selection.from <= linkTo;\n // Check if the link is inside the selection\n const selectionSpansLink =\n selection.from < linkFrom && selection.to > linkTo;\n\n // Replace if cursor is outside the link and there's no selection across the link\n if (!cursorInLink && !selectionSpansLink) {\n const linkText = state.doc.sliceString(linkFrom, linkTo);\n const match = linkText.match(/\\[([^\\]]+)\\]\\(([^)]+)\\)/);\n\n if (match) {\n const [, text, url] = match;\n\n // Replace the entire [text](url) with just the link widget\n const deco = Decoration.replace({\n widget: new LinkWidget(url, text),\n });\n\n widgets.push(deco.range(linkFrom, linkTo));\n }\n }\n }\n },\n });\n }\n\n return Decoration.set(widgets);\n}\n\nconst linkPlugin = ViewPlugin.fromClass(\n class {\n decorations: DecorationSet;\n\n constructor(view: EditorView) {\n this.decorations = getLinks(view);\n }\n\n update(update: ViewUpdate) {\n // Recompute when doc changes, selection moves, or viewport changes\n if (\n update.docChanged ||\n update.selectionSet ||\n update.viewportChanged ||\n syntaxTree(update.startState) !== syntaxTree(update.state)\n ) {\n this.decorations = getLinks(update.view);\n }\n }\n },\n {\n decorations: (v) => v.decorations,\n }\n);\n\ntype SyntaxNode = ReturnType<ReturnType<typeof syntaxTree>[\"resolveInner\"]>;\n\n/**\n * The link at `pos`, if any: an inline `[text](url)`, an autolink `<url>`, a\n * GFM bare url, or the url in a `[ref]: url` definition. `from`/`to` cover\n * the whole link as written. Reference-style `[text][ref]` links have no url\n * of their own and are skipped.\n */\nexport function linkAt(\n state: EditorState,\n pos: number\n): { from: number; to: number; url: string } | null {\n const slice = (n: SyntaxNode) => state.doc.sliceString(n.from, n.to);\n for (const side of [1, -1] as const) {\n for (\n let node: SyntaxNode | null = syntaxTree(state).resolveInner(pos, side);\n node;\n node = node.parent\n ) {\n const inLink = node.parent?.name === \"Link\" || node.parent?.name === \"Autolink\";\n if (node.name === \"URL\" && !inLink)\n return { from: node.from, to: node.to, url: slice(node) };\n if (node.name === \"Link\" || node.name === \"Autolink\") {\n const url = node.getChild(\"URL\");\n return url ? { from: node.from, to: node.to, url: slice(url) } : null;\n }\n }\n }\n return null;\n}\n\nexport const urlAt = (state: EditorState, pos: number) =>\n linkAt(state, pos)?.url ?? null;\n\n/**\n * The link actually under the pointer. `posAndSideAtCoords` snaps to the\n * nearest position, so without the distance check the blank space past the\n * end of a line would count as hovering a link that ends the line.\n */\nfunction linkUnderPointer(view: EditorView, x: number, y: number) {\n const hit = view.posAndSideAtCoords({ x, y });\n if (!hit) return null;\n const at = view.coordsAtPos(hit.pos, hit.assoc);\n if (\n !at ||\n y < at.top ||\n y > at.bottom ||\n Math.abs(x - at.left) > view.defaultCharacterWidth * 1.5\n )\n return null;\n return linkAt(view.state, hit.pos);\n}\n\nconst isMac =\n typeof navigator !== \"undefined\" &&\n /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent);\nconst modHeld = (e: KeyboardEvent | MouseEvent) =>\n isMac ? e.metaKey : e.ctrlKey;\n\n/** The one link to show as clickable: under the pointer, with Mod held. */\nconst setHover = StateEffect.define<{ from: number; to: number } | null>();\nconst hoverMark = Decoration.mark({ class: \"cm-md-link-hover\" });\n\nconst hoverField = StateField.define<DecorationSet>({\n create: () => Decoration.none,\n update(deco, tr) {\n deco = deco.map(tr.changes);\n for (const e of tr.effects) {\n if (e.is(setHover)) {\n deco = e.value\n ? Decoration.set(hoverMark.range(e.value.from, e.value.to))\n : Decoration.none;\n }\n }\n return deco;\n },\n provide: (f) => EditorView.decorations.from(f),\n});\n\n/**\n * Cmd-click (Ctrl off a Mac) opens the link under the pointer, even while the\n * cursor is inside it and it's showing as raw markdown. While the key is held\n * the hovered link gets `cm-md-link-hover`, so the theme can underline it.\n */\nconst modClick = ViewPlugin.fromClass(\n class {\n pointer: { x: number; y: number } | null = null;\n mod = false;\n\n constructor(readonly view: EditorView) {}\n\n refresh() {\n const { view } = this;\n const link =\n this.mod && this.pointer\n ? linkUnderPointer(view, this.pointer.x, this.pointer.y)\n : null;\n let current: { from: number; to: number } | null = null;\n view.state.field(hoverField).between(0, view.state.doc.length, (from, to) => {\n current = { from, to };\n });\n const same = (a: typeof current, b: typeof current) =>\n a?.from === b?.from && a?.to === b?.to;\n const next = link && { from: link.from, to: link.to };\n if (!same(current, next)) view.dispatch({ effects: setHover.of(next) });\n }\n },\n {\n eventHandlers: {\n mousedown(event, view) {\n if (event.button !== 0 || !modHeld(event)) return false;\n const link = linkUnderPointer(view, event.clientX, event.clientY);\n if (!link) return false;\n event.preventDefault();\n openUrl(link.url, view.dom);\n return true;\n },\n mousemove(event) {\n this.pointer = { x: event.clientX, y: event.clientY };\n this.mod = modHeld(event);\n this.refresh();\n },\n mouseleave() {\n this.pointer = null;\n this.refresh();\n },\n keydown(event) {\n this.mod = modHeld(event);\n this.refresh();\n },\n keyup(event) {\n this.mod = modHeld(event);\n this.refresh();\n },\n blur() {\n this.mod = false;\n this.refresh();\n },\n },\n }\n);\n\nexport function markdownLinks() {\n return [linkPlugin, hoverField, modClick, linkTheme];\n}\n", "import { EditorView } from \"@codemirror/view\";\n\nexport const linkTheme = EditorView.baseTheme({\n \".cm-link\": {\n textDecoration: \"underline\",\n cursor: \"var(--studio-cursor-pointer, pointer)\",\n // the widget replaces the link's text, so the theme's `t.link` highlight\n // (theming/src/codemirror-theme.ts) never reaches it: read the same tokens.\n fontFamily: \"var(--markdown-body-family, var(--text-editor-family-sans))\",\n color: \"var(--syntax-link, #0066cc)\",\n backgroundColor: \"var(--syntax-fill-link)\",\n fontStyle: \"var(--syntax-style-link)\",\n fontWeight: \"var(--syntax-weight-link)\",\n \"&:hover\": {\n color: \"inherit\",\n },\n },\n \".cm-md-link-hover, .cm-md-link-hover *\": {\n textDecoration: \"underline\",\n cursor: \"var(--studio-cursor-pointer, pointer)\",\n },\n});\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import {\n EditorView,\n Decoration,\n WidgetType,\n ViewPlugin,\n ViewUpdate,\n type DecorationSet,\n} from \"@codemirror/view\";\nimport {\n type EditorState,\n Range,\n StateEffect,\n StateField,\n} from \"@codemirror/state\";\nimport { syntaxTree } from \"@codemirror/language\";\nimport { isValidAutomergeUrl } from \"@automerge/automerge-repo/slim\";\nimport { linkTheme } from \"../themes/links.ts\";\n\n/**\n * Open a link from the editor. Automerge urls open inside Patchwork rather\n * than as a browser URL (automerge: isn't navigable); bubbles/composed so the\n * event escapes the editor and any shadow root up to whoever drives selection.\n */\nexport function openUrl(url: string, from: Element) {\n if (isValidAutomergeUrl(url)) {\n from.dispatchEvent(\n new CustomEvent(\"patchwork:open-document\", {\n detail: { url },\n bubbles: true,\n composed: true,\n }),\n );\n } else {\n // GFM autolinks `www.example.com` with no scheme.\n const href = /^[a-z][a-z0-9+.-]*:/i.test(url) ? url : `https://${url}`;\n window.open(href, \"_blank\", \"noopener,noreferrer\");\n }\n}\n\n/**\n * Widget to render a clickable link in the editor\n */\nclass LinkWidget extends WidgetType {\n readonly url: string;\n readonly text: string;\n\n constructor(url: string, text: string) {\n super();\n this.url = url;\n this.text = text;\n }\n\n eq(other: LinkWidget) {\n return other.url === this.url && other.text === this.text;\n }\n\n toDOM() {\n const link = document.createElement(\"a\");\n link.href = this.url;\n link.textContent = this.text;\n link.className = \"cm-link\";\n link.title = this.url;\n\n link.onclick = (e) => {\n e.preventDefault();\n openUrl(this.url, link);\n };\n\n return link;\n }\n\n ignoreEvent(e: Event) {\n // Let click events through to our widget\n return e.type !== \"click\";\n }\n}\n\nfunction getLinks(view: EditorView) {\n const widgets: Range<Decoration>[] = [];\n const { state } = view;\n const selection = state.selection.main;\n\n for (let { from, to } of view.visibleRanges) {\n syntaxTree(state).iterate({\n from,\n to,\n enter: (node) => {\n // `<https://\u2026>`, `<fax:\u2026>`, `<a@b.c>`: the url, without its brackets\n if (node.name === \"Autolink\") {\n const url = node.node.getChild(\"URL\");\n const inside =\n selection.from >= node.from && selection.from <= node.to;\n const spans = selection.from < node.from && selection.to > node.to;\n if (url && !inside && !spans) {\n const text = state.doc.sliceString(url.from, url.to);\n const href =\n /^[a-z][a-z0-9+.-]*:/i.test(text) || !text.includes(\"@\")\n ? text\n : `mailto:${text}`;\n widgets.push(\n Decoration.replace({ widget: new LinkWidget(href, text) }).range(\n node.from,\n node.to\n )\n );\n }\n return;\n }\n\n // Match markdown link syntax\n if (node.name === \"Link\") {\n const linkFrom = node.from;\n const linkTo = node.to;\n\n // Check if cursor is inside this link\n const cursorInLink =\n selection.from >= linkFrom && selection.from <= linkTo;\n // Check if the link is inside the selection\n const selectionSpansLink =\n selection.from < linkFrom && selection.to > linkTo;\n\n // Replace if cursor is outside the link and there's no selection across the link\n if (!cursorInLink && !selectionSpansLink) {\n const linkText = state.doc.sliceString(linkFrom, linkTo);\n const match = linkText.match(/\\[([^\\]]+)\\]\\(([^)]+)\\)/);\n\n if (match) {\n const [, text, url] = match;\n\n // Replace the entire [text](url) with just the link widget\n const deco = Decoration.replace({\n widget: new LinkWidget(url, text),\n });\n\n widgets.push(deco.range(linkFrom, linkTo));\n }\n }\n }\n },\n });\n }\n\n return Decoration.set(widgets);\n}\n\nconst linkPlugin = ViewPlugin.fromClass(\n class {\n decorations: DecorationSet;\n\n constructor(view: EditorView) {\n this.decorations = getLinks(view);\n }\n\n update(update: ViewUpdate) {\n // Recompute when doc changes, selection moves, or viewport changes\n if (\n update.docChanged ||\n update.selectionSet ||\n update.viewportChanged ||\n syntaxTree(update.startState) !== syntaxTree(update.state)\n ) {\n this.decorations = getLinks(update.view);\n }\n }\n },\n {\n decorations: (v) => v.decorations,\n }\n);\n\ntype SyntaxNode = ReturnType<ReturnType<typeof syntaxTree>[\"resolveInner\"]>;\n\n/**\n * The link at `pos`, if any: an inline `[text](url)`, an autolink `<url>`, a\n * GFM bare url, or the url in a `[ref]: url` definition. `from`/`to` cover\n * the whole link as written. Reference-style `[text][ref]` links have no url\n * of their own and are skipped.\n */\nexport function linkAt(\n state: EditorState,\n pos: number\n): { from: number; to: number; url: string } | null {\n const slice = (n: SyntaxNode) => state.doc.sliceString(n.from, n.to);\n for (const side of [1, -1] as const) {\n for (\n let node: SyntaxNode | null = syntaxTree(state).resolveInner(pos, side);\n node;\n node = node.parent\n ) {\n const inLink = node.parent?.name === \"Link\" || node.parent?.name === \"Autolink\";\n if (node.name === \"URL\" && !inLink)\n return { from: node.from, to: node.to, url: slice(node) };\n if (node.name === \"Link\" || node.name === \"Autolink\") {\n const url = node.getChild(\"URL\");\n return url ? { from: node.from, to: node.to, url: slice(url) } : null;\n }\n }\n }\n return null;\n}\n\nexport const urlAt = (state: EditorState, pos: number) =>\n linkAt(state, pos)?.url ?? null;\n\n/**\n * The link actually under the pointer. `posAndSideAtCoords` snaps to the\n * nearest position, so without the distance check the blank space past the\n * end of a line would count as hovering a link that ends the line.\n */\nfunction linkUnderPointer(view: EditorView, x: number, y: number) {\n const hit = view.posAndSideAtCoords({ x, y });\n if (!hit) return null;\n const at = view.coordsAtPos(hit.pos, hit.assoc);\n if (\n !at ||\n y < at.top ||\n y > at.bottom ||\n Math.abs(x - at.left) > view.defaultCharacterWidth * 1.5\n )\n return null;\n return linkAt(view.state, hit.pos);\n}\n\nconst isMac =\n typeof navigator !== \"undefined\" &&\n /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent);\nconst modHeld = (e: KeyboardEvent | MouseEvent) =>\n isMac ? e.metaKey : e.ctrlKey;\n\n/** The one link to show as clickable: under the pointer, with Mod held. */\nconst setHover = StateEffect.define<{ from: number; to: number } | null>();\nconst hoverMark = Decoration.mark({ class: \"cm-md-link-hover\" });\n\nconst hoverField = StateField.define<DecorationSet>({\n create: () => Decoration.none,\n update(deco, tr) {\n deco = deco.map(tr.changes);\n for (const e of tr.effects) {\n if (e.is(setHover)) {\n deco = e.value\n ? Decoration.set(hoverMark.range(e.value.from, e.value.to))\n : Decoration.none;\n }\n }\n return deco;\n },\n provide: (f) => EditorView.decorations.from(f),\n});\n\n/**\n * Cmd-click (Ctrl off a Mac) opens the link under the pointer, even while the\n * cursor is inside it and it's showing as raw markdown. While the key is held\n * the hovered link gets `cm-md-link-hover`, so the theme can underline it.\n */\nconst modClick = ViewPlugin.fromClass(\n class {\n pointer: { x: number; y: number } | null = null;\n mod = false;\n\n constructor(readonly view: EditorView) {}\n\n refresh() {\n const { view } = this;\n const link =\n this.mod && this.pointer\n ? linkUnderPointer(view, this.pointer.x, this.pointer.y)\n : null;\n let current: { from: number; to: number } | null = null;\n view.state.field(hoverField).between(0, view.state.doc.length, (from, to) => {\n current = { from, to };\n });\n const same = (a: typeof current, b: typeof current) =>\n a?.from === b?.from && a?.to === b?.to;\n const next = link && { from: link.from, to: link.to };\n if (!same(current, next)) view.dispatch({ effects: setHover.of(next) });\n }\n },\n {\n eventHandlers: {\n mousedown(event, view) {\n if (event.button !== 0 || !modHeld(event)) return false;\n const link = linkUnderPointer(view, event.clientX, event.clientY);\n if (!link) return false;\n event.preventDefault();\n openUrl(link.url, view.dom);\n return true;\n },\n mousemove(event) {\n this.pointer = { x: event.clientX, y: event.clientY };\n this.mod = modHeld(event);\n this.refresh();\n },\n mouseleave() {\n this.pointer = null;\n this.refresh();\n },\n keydown(event) {\n this.mod = modHeld(event);\n this.refresh();\n },\n keyup(event) {\n this.mod = modHeld(event);\n this.refresh();\n },\n blur() {\n this.mod = false;\n this.refresh();\n },\n },\n }\n);\n\nexport function markdownLinks() {\n return [linkPlugin, hoverField, modClick, linkTheme];\n}\n", "import { EditorView } from \"@codemirror/view\";\n\nexport const linkTheme = EditorView.baseTheme({\n \".cm-link\": {\n textDecoration: \"underline\",\n cursor: \"var(--studio-cursor-pointer, pointer)\",\n // the widget replaces the link's text, so the theme's `t.link` highlight\n // (theming/src/codemirror-theme.ts) never reaches it: read the same tokens.\n fontFamily: \"var(--markdown-body-family, var(--text-editor-family-sans))\",\n color: \"var(--syntax-link, #0066cc)\",\n backgroundColor: \"var(--syntax-fill-link)\",\n fontStyle: \"var(--syntax-style-link)\",\n fontWeight: \"var(--syntax-weight-link)\",\n \"&:hover\": {\n color: \"inherit\",\n },\n },\n \".cm-md-link-hover, .cm-md-link-hover *\": {\n textDecoration: \"underline\",\n cursor: \"var(--studio-cursor-pointer, pointer)\",\n },\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["EditorView", "EditorView"]
|
|
7
7
|
}
|
|
@@ -1269,23 +1269,23 @@ var FlattenedDoc = class _FlattenedDoc {
|
|
|
1269
1269
|
get to() {
|
|
1270
1270
|
return this.from + this.text.length;
|
|
1271
1271
|
}
|
|
1272
|
-
static get(
|
|
1273
|
-
let cached = flattened.get(
|
|
1272
|
+
static get(doc2, from, to) {
|
|
1273
|
+
let cached = flattened.get(doc2);
|
|
1274
1274
|
if (!cached || cached.from >= to || cached.to <= from) {
|
|
1275
|
-
let flat = new _FlattenedDoc(from,
|
|
1276
|
-
flattened.set(
|
|
1275
|
+
let flat = new _FlattenedDoc(from, doc2.sliceString(from, to));
|
|
1276
|
+
flattened.set(doc2, flat);
|
|
1277
1277
|
return flat;
|
|
1278
1278
|
}
|
|
1279
1279
|
if (cached.from == from && cached.to == to)
|
|
1280
1280
|
return cached;
|
|
1281
1281
|
let { text, from: cachedFrom } = cached;
|
|
1282
1282
|
if (cachedFrom > from) {
|
|
1283
|
-
text =
|
|
1283
|
+
text = doc2.sliceString(from, cachedFrom) + text;
|
|
1284
1284
|
cachedFrom = from;
|
|
1285
1285
|
}
|
|
1286
1286
|
if (cached.to < to)
|
|
1287
|
-
text +=
|
|
1288
|
-
flattened.set(
|
|
1287
|
+
text += doc2.sliceString(cached.to, to);
|
|
1288
|
+
flattened.set(doc2, new _FlattenedDoc(cachedFrom, text));
|
|
1289
1289
|
return new _FlattenedDoc(from, text.slice(from - cachedFrom, to - cachedFrom));
|
|
1290
1290
|
}
|
|
1291
1291
|
};
|
|
@@ -1511,11 +1511,11 @@ function stringCursor(spec, state, from, to) {
|
|
|
1511
1511
|
test = wrapStringTest(spec.test, state, test);
|
|
1512
1512
|
return new SearchCursor(state.doc, spec.unquoted, from, to, spec.caseSensitive ? void 0 : (x) => x.toLowerCase(), test);
|
|
1513
1513
|
}
|
|
1514
|
-
function stringWordTest(
|
|
1514
|
+
function stringWordTest(doc2, categorizer) {
|
|
1515
1515
|
return (from, to, buf, bufPos) => {
|
|
1516
1516
|
if (bufPos > from || bufPos + buf.length < to) {
|
|
1517
1517
|
bufPos = Math.max(0, from - 2);
|
|
1518
|
-
buf =
|
|
1518
|
+
buf = doc2.sliceString(bufPos, Math.min(doc2.length, to + 2));
|
|
1519
1519
|
}
|
|
1520
1520
|
return (categorizer(charBefore(buf, from - bufPos)) != CharCategory.Word || categorizer(charAfter(buf, from - bufPos)) != CharCategory.Word) && (categorizer(charAfter(buf, to - bufPos)) != CharCategory.Word || categorizer(charBefore(buf, to - bufPos)) != CharCategory.Word);
|
|
1521
1521
|
};
|
|
@@ -2066,7 +2066,7 @@ var searchExtensions = [
|
|
|
2066
2066
|
];
|
|
2067
2067
|
|
|
2068
2068
|
// src/extensions/markdown.ts
|
|
2069
|
-
import { EditorView as
|
|
2069
|
+
import { EditorView as EditorView9, keymap as keymap5 } from "@codemirror/view";
|
|
2070
2070
|
|
|
2071
2071
|
// src/extensions/formatting.ts
|
|
2072
2072
|
import { EditorSelection as EditorSelection2 } from "@codemirror/state";
|
|
@@ -2189,14 +2189,14 @@ function toggle(view, from) {
|
|
|
2189
2189
|
var doneMark = Decoration2.mark({ class: "cm-task-done" });
|
|
2190
2190
|
function build(view) {
|
|
2191
2191
|
const builder = new RangeSetBuilder2();
|
|
2192
|
-
const { doc } = view.state;
|
|
2192
|
+
const { doc: doc2 } = view.state;
|
|
2193
2193
|
for (const { from, to } of view.visibleRanges) {
|
|
2194
2194
|
syntaxTree(view.state).iterate({
|
|
2195
2195
|
from,
|
|
2196
2196
|
to,
|
|
2197
2197
|
enter: (node) => {
|
|
2198
2198
|
if (node.name !== "TaskMarker") return;
|
|
2199
|
-
const checked =
|
|
2199
|
+
const checked = doc2.sliceString(node.from + 1, node.to - 1) !== " ";
|
|
2200
2200
|
builder.add(
|
|
2201
2201
|
node.from,
|
|
2202
2202
|
node.to,
|
|
@@ -2234,8 +2234,6 @@ import {
|
|
|
2234
2234
|
ViewPlugin as ViewPlugin3,
|
|
2235
2235
|
WidgetType as WidgetType2
|
|
2236
2236
|
} from "@codemirror/view";
|
|
2237
|
-
var numberMark = Decoration3.mark({ class: "cm-md-hang cm-md-hang-number" });
|
|
2238
|
-
var quoteMark = Decoration3.mark({ class: "cm-md-hang cm-md-hang-quote" });
|
|
2239
2237
|
var hidden = Decoration3.replace({});
|
|
2240
2238
|
var taskMark = Decoration3.mark({ class: "cm-md-hang", inclusive: true });
|
|
2241
2239
|
var HeadingLabel = class extends WidgetType2 {
|
|
@@ -2301,15 +2299,6 @@ function renderedNumber(state, mark) {
|
|
|
2301
2299
|
const start = firstMark ? parseInt(state.doc.sliceString(firstMark.from, firstMark.to), 10) : 1;
|
|
2302
2300
|
return `${(Number.isNaN(start) ? 1 : start) + index}${delimiter}`;
|
|
2303
2301
|
}
|
|
2304
|
-
function caretLines(state) {
|
|
2305
|
-
const lines = /* @__PURE__ */ new Set();
|
|
2306
|
-
for (const r of state.selection.ranges) {
|
|
2307
|
-
const first = state.doc.lineAt(r.from).number;
|
|
2308
|
-
const last = state.doc.lineAt(r.to).number;
|
|
2309
|
-
for (let n = first; n <= last; n++) lines.add(n);
|
|
2310
|
-
}
|
|
2311
|
-
return lines;
|
|
2312
|
-
}
|
|
2313
2302
|
function skipSpaces(state, pos) {
|
|
2314
2303
|
const line = state.doc.lineAt(pos);
|
|
2315
2304
|
while (pos < line.to && state.doc.sliceString(pos, pos + 1) === " ") pos++;
|
|
@@ -2317,8 +2306,7 @@ function skipSpaces(state, pos) {
|
|
|
2317
2306
|
}
|
|
2318
2307
|
function build2(view) {
|
|
2319
2308
|
const { state } = view;
|
|
2320
|
-
const { doc } = state;
|
|
2321
|
-
const editing = caretLines(state);
|
|
2309
|
+
const { doc: doc2 } = state;
|
|
2322
2310
|
const decos = [];
|
|
2323
2311
|
for (const { from, to } of view.visibleRanges) {
|
|
2324
2312
|
syntaxTree2(state).iterate({
|
|
@@ -2329,21 +2317,20 @@ function build2(view) {
|
|
|
2329
2317
|
const heading = /^ATXHeading(\d)$/.exec(name);
|
|
2330
2318
|
if (heading) {
|
|
2331
2319
|
const mark = node.node.firstChild;
|
|
2332
|
-
if (!mark || mark.name !== "HeaderMark"
|
|
2333
|
-
return;
|
|
2320
|
+
if (!mark || mark.name !== "HeaderMark") return;
|
|
2334
2321
|
decos.push(
|
|
2335
2322
|
Decoration3.replace({
|
|
2336
2323
|
widget: new HeadingLabel(Number(heading[1]))
|
|
2337
|
-
}).range(
|
|
2324
|
+
}).range(doc2.lineAt(node.from).from, skipSpaces(state, mark.to))
|
|
2338
2325
|
);
|
|
2339
2326
|
return;
|
|
2340
2327
|
}
|
|
2341
2328
|
if (name === "ListMark") {
|
|
2342
2329
|
const end = skipSpaces(state, node.to);
|
|
2343
|
-
const char =
|
|
2330
|
+
const char = doc2.sliceString(node.from, node.to);
|
|
2344
2331
|
const bullet = ["-", "+", "*"].includes(char);
|
|
2345
2332
|
const task = node.node.nextSibling?.firstChild;
|
|
2346
|
-
const isTask = bullet && task?.name === "TaskMarker" &&
|
|
2333
|
+
const isTask = bullet && task?.name === "TaskMarker" && doc2.sliceString(end, task.from).trim() === "";
|
|
2347
2334
|
if (isTask && task) {
|
|
2348
2335
|
if (end > node.from) decos.push(hidden.range(node.from, end));
|
|
2349
2336
|
decos.push(taskMark.range(task.from, skipSpaces(state, task.to)));
|
|
@@ -2353,8 +2340,6 @@ function build2(view) {
|
|
|
2353
2340
|
decos.push(
|
|
2354
2341
|
Decoration3.replace({ widget: new Bullet() }).range(node.from, end)
|
|
2355
2342
|
);
|
|
2356
|
-
} else if (editing.has(doc.lineAt(node.from).number)) {
|
|
2357
|
-
decos.push(numberMark.range(node.from, end));
|
|
2358
2343
|
} else {
|
|
2359
2344
|
decos.push(
|
|
2360
2345
|
Decoration3.replace({
|
|
@@ -2367,10 +2352,10 @@ function build2(view) {
|
|
|
2367
2352
|
if (name === "Blockquote") {
|
|
2368
2353
|
for (let p = node.node.parent; p; p = p.parent)
|
|
2369
2354
|
if (p.name === "Blockquote") return;
|
|
2370
|
-
const first =
|
|
2371
|
-
const last =
|
|
2355
|
+
const first = doc2.lineAt(node.from).number;
|
|
2356
|
+
const last = doc2.lineAt(node.to).number;
|
|
2372
2357
|
for (let n = first; n <= last; n++) {
|
|
2373
|
-
const line =
|
|
2358
|
+
const line = doc2.line(n);
|
|
2374
2359
|
const classes = ["cm-md-quote"];
|
|
2375
2360
|
if (n === first) classes.push("cm-md-quote-first");
|
|
2376
2361
|
if (n === last) classes.push("cm-md-quote-last");
|
|
@@ -2381,22 +2366,17 @@ function build2(view) {
|
|
|
2381
2366
|
return;
|
|
2382
2367
|
}
|
|
2383
2368
|
if (name === "QuoteMark") {
|
|
2384
|
-
const line =
|
|
2385
|
-
const before =
|
|
2369
|
+
const line = doc2.lineAt(node.from);
|
|
2370
|
+
const before = doc2.sliceString(line.from, node.from);
|
|
2386
2371
|
if (before.trim() !== "") return;
|
|
2387
2372
|
let end = node.to;
|
|
2388
2373
|
for (; ; ) {
|
|
2389
2374
|
const next = skipSpaces(state, end);
|
|
2390
|
-
if (
|
|
2375
|
+
if (doc2.sliceString(next, next + 1) !== ">") break;
|
|
2391
2376
|
end = next + 1;
|
|
2392
2377
|
}
|
|
2393
2378
|
end = skipSpaces(state, end);
|
|
2394
|
-
decos.push(
|
|
2395
|
-
(editing.has(line.number) ? quoteMark : hidden).range(
|
|
2396
|
-
line.from,
|
|
2397
|
-
end
|
|
2398
|
-
)
|
|
2399
|
-
);
|
|
2379
|
+
decos.push(hidden.range(line.from, end));
|
|
2400
2380
|
}
|
|
2401
2381
|
}
|
|
2402
2382
|
});
|
|
@@ -2410,7 +2390,7 @@ var marginPlugin = ViewPlugin3.fromClass(
|
|
|
2410
2390
|
this.decorations = build2(view);
|
|
2411
2391
|
}
|
|
2412
2392
|
update(update) {
|
|
2413
|
-
if (update.docChanged || update.
|
|
2393
|
+
if (update.docChanged || update.viewportChanged || syntaxTree2(update.startState) !== syntaxTree2(update.state)) {
|
|
2414
2394
|
this.decorations = build2(update.view);
|
|
2415
2395
|
}
|
|
2416
2396
|
}
|
|
@@ -2500,7 +2480,6 @@ var margin = [marginPlugin, marginTheme];
|
|
|
2500
2480
|
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
2501
2481
|
import {
|
|
2502
2482
|
EditorSelection as EditorSelection3,
|
|
2503
|
-
EditorState as EditorState2,
|
|
2504
2483
|
Prec as Prec2
|
|
2505
2484
|
} from "@codemirror/state";
|
|
2506
2485
|
import { keymap } from "@codemirror/view";
|
|
@@ -2517,31 +2496,6 @@ function headingPrefix(state, line) {
|
|
|
2517
2496
|
}
|
|
2518
2497
|
return null;
|
|
2519
2498
|
}
|
|
2520
|
-
var keepOutOfPrefix = EditorState2.transactionFilter.of((tr) => {
|
|
2521
|
-
if (!tr.selection) return tr;
|
|
2522
|
-
const { state } = tr;
|
|
2523
|
-
const before = tr.startState.selection;
|
|
2524
|
-
let moved = false;
|
|
2525
|
-
const ranges = tr.selection.ranges.map((r, i) => {
|
|
2526
|
-
if (!r.empty) return r;
|
|
2527
|
-
const line = state.doc.lineAt(r.head);
|
|
2528
|
-
if (r.head >= line.to && line.length > 0) return r;
|
|
2529
|
-
const prefix = headingPrefix(state, line);
|
|
2530
|
-
if (!prefix || r.head >= prefix.to) return r;
|
|
2531
|
-
const old = before.ranges[i] ?? before.main;
|
|
2532
|
-
const leaving = !tr.docChanged && old.empty && old.head === prefix.to && line.from > 0;
|
|
2533
|
-
moved = true;
|
|
2534
|
-
return EditorSelection3.cursor(leaving ? line.from - 1 : prefix.to);
|
|
2535
|
-
});
|
|
2536
|
-
if (!moved) return tr;
|
|
2537
|
-
return [
|
|
2538
|
-
tr,
|
|
2539
|
-
{
|
|
2540
|
-
selection: EditorSelection3.create(ranges, tr.selection.mainIndex),
|
|
2541
|
-
sequential: true
|
|
2542
|
-
}
|
|
2543
|
-
];
|
|
2544
|
-
});
|
|
2545
2499
|
function selectedLines(state) {
|
|
2546
2500
|
const seen = /* @__PURE__ */ new Set();
|
|
2547
2501
|
const lines = [];
|
|
@@ -2556,12 +2510,12 @@ function selectedLines(state) {
|
|
|
2556
2510
|
}
|
|
2557
2511
|
var setHeading = (level) => ({ state, dispatch }) => {
|
|
2558
2512
|
const lines = selectedLines(state);
|
|
2559
|
-
const
|
|
2560
|
-
const already = level > 0 &&
|
|
2513
|
+
const prefixes2 = lines.map((line) => headingPrefix(state, line));
|
|
2514
|
+
const already = level > 0 && prefixes2.every((p) => p && p.level === level);
|
|
2561
2515
|
const target = already ? 0 : level;
|
|
2562
2516
|
const insert = target ? "#".repeat(target) + " " : "";
|
|
2563
2517
|
const changes = lines.map((line, i) => {
|
|
2564
|
-
const p =
|
|
2518
|
+
const p = prefixes2[i];
|
|
2565
2519
|
return { from: line.from, to: p ? p.to : line.from, insert };
|
|
2566
2520
|
});
|
|
2567
2521
|
dispatch(
|
|
@@ -2580,18 +2534,6 @@ function atHeadingStart(state) {
|
|
|
2580
2534
|
const prefix = headingPrefix(state, line);
|
|
2581
2535
|
return prefix && sel.main.head === prefix.to ? prefix : null;
|
|
2582
2536
|
}
|
|
2583
|
-
var backspace = ({ state, dispatch }) => {
|
|
2584
|
-
const prefix = atHeadingStart(state);
|
|
2585
|
-
if (!prefix) return false;
|
|
2586
|
-
dispatch(
|
|
2587
|
-
state.update({
|
|
2588
|
-
changes: { from: prefix.from, to: prefix.to },
|
|
2589
|
-
selection: EditorSelection3.cursor(prefix.from),
|
|
2590
|
-
userEvent: "delete.backward"
|
|
2591
|
-
})
|
|
2592
|
-
);
|
|
2593
|
-
return true;
|
|
2594
|
-
};
|
|
2595
2537
|
var enter = ({ state, dispatch }) => {
|
|
2596
2538
|
const prefix = atHeadingStart(state);
|
|
2597
2539
|
if (!prefix) return false;
|
|
@@ -2607,29 +2549,11 @@ var enter = ({ state, dispatch }) => {
|
|
|
2607
2549
|
);
|
|
2608
2550
|
return true;
|
|
2609
2551
|
};
|
|
2610
|
-
var deleteForward = ({ state, dispatch }) => {
|
|
2611
|
-
const sel = state.selection;
|
|
2612
|
-
if (sel.ranges.length !== 1 || !sel.main.empty) return false;
|
|
2613
|
-
const line = state.doc.lineAt(sel.main.head);
|
|
2614
|
-
if (sel.main.head !== line.to || line.number >= state.doc.lines) return false;
|
|
2615
|
-
const prefix = headingPrefix(state, state.doc.line(line.number + 1));
|
|
2616
|
-
if (!prefix) return false;
|
|
2617
|
-
dispatch(
|
|
2618
|
-
state.update({
|
|
2619
|
-
changes: { from: line.to, to: prefix.to },
|
|
2620
|
-
userEvent: "delete.forward"
|
|
2621
|
-
})
|
|
2622
|
-
);
|
|
2623
|
-
return true;
|
|
2624
|
-
};
|
|
2625
2552
|
var headings = [
|
|
2626
|
-
|
|
2627
|
-
// above lang-markdown's own (Prec.high) Enter and Backspace.
|
|
2553
|
+
// above lang-markdown's own (Prec.high) Enter.
|
|
2628
2554
|
Prec2.highest(
|
|
2629
2555
|
keymap.of([
|
|
2630
|
-
{ key: "Backspace", run: backspace },
|
|
2631
2556
|
{ key: "Enter", run: enter },
|
|
2632
|
-
{ key: "Delete", run: deleteForward },
|
|
2633
2557
|
{ key: "Mod-Shift-h", run: setHeading(2), preventDefault: true },
|
|
2634
2558
|
{ key: "Mod-Shift-j", run: setHeading(3), preventDefault: true },
|
|
2635
2559
|
{ key: "Mod-Shift-b", run: setHeading(0), preventDefault: true },
|
|
@@ -2960,9 +2884,181 @@ var listsTheme = EditorView4.baseTheme({
|
|
|
2960
2884
|
});
|
|
2961
2885
|
var lists = [hideDoneField, menuPlugin, listsTheme];
|
|
2962
2886
|
|
|
2887
|
+
// src/extensions/prefix.ts
|
|
2888
|
+
import { syntaxTree as syntaxTree5 } from "@codemirror/language";
|
|
2889
|
+
import {
|
|
2890
|
+
EditorSelection as EditorSelection4,
|
|
2891
|
+
EditorState as EditorState2,
|
|
2892
|
+
Prec as Prec3
|
|
2893
|
+
} from "@codemirror/state";
|
|
2894
|
+
import { EditorView as EditorView5, keymap as keymap2 } from "@codemirror/view";
|
|
2895
|
+
var MARKS2 = /* @__PURE__ */ new Set(["QuoteMark", "ListMark", "HeaderMark", "TaskMarker"]);
|
|
2896
|
+
function skipSpaces2(state, pos, end) {
|
|
2897
|
+
while (pos < end && /[ \t]/.test(state.doc.sliceString(pos, pos + 1))) pos++;
|
|
2898
|
+
return pos;
|
|
2899
|
+
}
|
|
2900
|
+
function blockPrefix(state, line) {
|
|
2901
|
+
const tree = syntaxTree5(state);
|
|
2902
|
+
const marks = [];
|
|
2903
|
+
const starts = [];
|
|
2904
|
+
let pos = line.from;
|
|
2905
|
+
for (; ; ) {
|
|
2906
|
+
const at = skipSpaces2(state, pos, line.to);
|
|
2907
|
+
const node = tree.resolveInner(at, 1);
|
|
2908
|
+
if (!MARKS2.has(node.name) || node.from !== at || node.to <= at) break;
|
|
2909
|
+
if (doc(state, pos, at).trim() !== "") break;
|
|
2910
|
+
marks.push(node.name);
|
|
2911
|
+
starts.push(at);
|
|
2912
|
+
pos = node.to;
|
|
2913
|
+
}
|
|
2914
|
+
if (!marks.length) return null;
|
|
2915
|
+
return { from: line.from, to: skipSpaces2(state, pos, line.to), marks, starts };
|
|
2916
|
+
}
|
|
2917
|
+
var doc = (state, from, to) => state.doc.sliceString(from, to);
|
|
2918
|
+
var keepOut = EditorState2.transactionFilter.of((tr) => {
|
|
2919
|
+
if (!tr.selection) return tr;
|
|
2920
|
+
const { state } = tr;
|
|
2921
|
+
const before = tr.startState.selection;
|
|
2922
|
+
let moved = false;
|
|
2923
|
+
const ranges = tr.selection.ranges.map((r, i) => {
|
|
2924
|
+
if (!r.empty) return r;
|
|
2925
|
+
const line = state.doc.lineAt(r.head);
|
|
2926
|
+
const prefix = blockPrefix(state, line);
|
|
2927
|
+
if (!prefix || r.head >= prefix.to) return r;
|
|
2928
|
+
const old = before.ranges[i] ?? before.main;
|
|
2929
|
+
const leaving = !tr.docChanged && old.empty && old.head === prefix.to && line.from > 0;
|
|
2930
|
+
moved = true;
|
|
2931
|
+
return EditorSelection4.cursor(leaving ? line.from - 1 : prefix.to);
|
|
2932
|
+
});
|
|
2933
|
+
if (!moved) return tr;
|
|
2934
|
+
return [
|
|
2935
|
+
tr,
|
|
2936
|
+
{
|
|
2937
|
+
selection: EditorSelection4.create(ranges, tr.selection.mainIndex),
|
|
2938
|
+
sequential: true
|
|
2939
|
+
}
|
|
2940
|
+
];
|
|
2941
|
+
});
|
|
2942
|
+
function atTextStart(state) {
|
|
2943
|
+
const sel = state.selection;
|
|
2944
|
+
if (sel.ranges.length !== 1 || !sel.main.empty) return null;
|
|
2945
|
+
const prefix = blockPrefix(state, state.doc.lineAt(sel.main.head));
|
|
2946
|
+
return prefix && sel.main.head === prefix.to ? prefix : null;
|
|
2947
|
+
}
|
|
2948
|
+
var backspace = ({ state, dispatch }) => {
|
|
2949
|
+
const prefix = atTextStart(state);
|
|
2950
|
+
if (!prefix) return false;
|
|
2951
|
+
dispatch(
|
|
2952
|
+
state.update({
|
|
2953
|
+
changes: { from: prefix.starts.at(-1), to: prefix.to },
|
|
2954
|
+
selection: EditorSelection4.cursor(prefix.starts.at(-1)),
|
|
2955
|
+
userEvent: "delete.backward"
|
|
2956
|
+
})
|
|
2957
|
+
);
|
|
2958
|
+
return true;
|
|
2959
|
+
};
|
|
2960
|
+
var deleteForward = ({ state, dispatch }) => {
|
|
2961
|
+
const sel = state.selection;
|
|
2962
|
+
if (sel.ranges.length !== 1 || !sel.main.empty) return false;
|
|
2963
|
+
const line = state.doc.lineAt(sel.main.head);
|
|
2964
|
+
if (sel.main.head !== line.to || line.number >= state.doc.lines) return false;
|
|
2965
|
+
const prefix = blockPrefix(state, state.doc.line(line.number + 1));
|
|
2966
|
+
if (!prefix) return false;
|
|
2967
|
+
dispatch(
|
|
2968
|
+
state.update({
|
|
2969
|
+
changes: { from: line.to, to: prefix.to },
|
|
2970
|
+
userEvent: "delete.forward"
|
|
2971
|
+
})
|
|
2972
|
+
);
|
|
2973
|
+
return true;
|
|
2974
|
+
};
|
|
2975
|
+
var inCode = (state, pos) => {
|
|
2976
|
+
for (let n = syntaxTree5(state).resolveInner(pos, -1); n; n = n.parent) {
|
|
2977
|
+
if (/^(FencedCode|CodeBlock|CodeText|InlineCode)$/.test(n.name)) return true;
|
|
2978
|
+
}
|
|
2979
|
+
return false;
|
|
2980
|
+
};
|
|
2981
|
+
var LIST_OR_HEADING = /^(\d{1,9}[.)]|[-*+]|#{1,6})$/;
|
|
2982
|
+
var HEADING = /^#{1,6}$/;
|
|
2983
|
+
var replaceMarker = EditorView5.inputHandler.of((view, from, to, text) => {
|
|
2984
|
+
if (text !== " " || from !== to) return false;
|
|
2985
|
+
const { state } = view;
|
|
2986
|
+
const line = state.doc.lineAt(from);
|
|
2987
|
+
const prefix = blockPrefix(state, line);
|
|
2988
|
+
if (!prefix || inCode(state, from)) return false;
|
|
2989
|
+
const { marks, starts } = prefix;
|
|
2990
|
+
let typed = doc(state, prefix.to, from);
|
|
2991
|
+
let mine = marks.length - 1;
|
|
2992
|
+
if (!typed && prefix.to === from && marks.length > 1) {
|
|
2993
|
+
typed = doc(state, starts[mine], from);
|
|
2994
|
+
mine--;
|
|
2995
|
+
}
|
|
2996
|
+
if (!typed || marks[mine] === "QuoteMark") return false;
|
|
2997
|
+
if (marks[mine] === "TaskMarker" && marks[mine - 1] === "ListMark") mine--;
|
|
2998
|
+
const ok = marks[mine] === "HeaderMark" ? HEADING : LIST_OR_HEADING;
|
|
2999
|
+
if (!ok.test(typed)) return false;
|
|
3000
|
+
view.dispatch({
|
|
3001
|
+
changes: { from: starts[mine], to: from, insert: typed + " " },
|
|
3002
|
+
selection: EditorSelection4.cursor(starts[mine] + typed.length + 1),
|
|
3003
|
+
userEvent: "input.type"
|
|
3004
|
+
});
|
|
3005
|
+
return true;
|
|
3006
|
+
});
|
|
3007
|
+
var prefixes = [
|
|
3008
|
+
keepOut,
|
|
3009
|
+
replaceMarker,
|
|
3010
|
+
// above lang-markdown's own (Prec.high) Backspace.
|
|
3011
|
+
Prec3.highest(
|
|
3012
|
+
keymap2.of([
|
|
3013
|
+
{ key: "Backspace", run: backspace },
|
|
3014
|
+
{ key: "Delete", run: deleteForward }
|
|
3015
|
+
])
|
|
3016
|
+
)
|
|
3017
|
+
];
|
|
3018
|
+
|
|
3019
|
+
// src/extensions/vertical.ts
|
|
3020
|
+
import { EditorSelection as EditorSelection5, Prec as Prec4 } from "@codemirror/state";
|
|
3021
|
+
import { cursorLineDown, cursorLineUp } from "@codemirror/commands";
|
|
3022
|
+
import { keymap as keymap3 } from "@codemirror/view";
|
|
3023
|
+
var streaks = /* @__PURE__ */ new WeakMap();
|
|
3024
|
+
var vertical = (move) => (view) => {
|
|
3025
|
+
const { state } = view;
|
|
3026
|
+
const sel = state.selection;
|
|
3027
|
+
if (sel.ranges.length !== 1 || !sel.main.empty) return move(view);
|
|
3028
|
+
let streak = streaks.get(view);
|
|
3029
|
+
if (!streak || streak.head !== sel.main.head || streak.doc !== state.doc) {
|
|
3030
|
+
streak = { visited: /* @__PURE__ */ new Map(), head: sel.main.head, doc: state.doc };
|
|
3031
|
+
}
|
|
3032
|
+
const from = state.doc.lineAt(sel.main.head).number;
|
|
3033
|
+
if (!streak.visited.has(from)) streak.visited.set(from, sel.main.head);
|
|
3034
|
+
if (!move(view)) return false;
|
|
3035
|
+
const now = view.state.selection.main;
|
|
3036
|
+
const to = view.state.doc.lineAt(now.head).number;
|
|
3037
|
+
const back = streak.visited.get(to);
|
|
3038
|
+
if (back !== void 0 && back !== now.head && view.state.doc === streak.doc) {
|
|
3039
|
+
view.dispatch({
|
|
3040
|
+
selection: EditorSelection5.cursor(back),
|
|
3041
|
+
scrollIntoView: true,
|
|
3042
|
+
userEvent: "select"
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
3045
|
+
streak.head = view.state.selection.main.head;
|
|
3046
|
+
streak.doc = view.state.doc;
|
|
3047
|
+
streaks.set(view, streak);
|
|
3048
|
+
return true;
|
|
3049
|
+
};
|
|
3050
|
+
var verticalMemory = Prec4.high(
|
|
3051
|
+
keymap3.of([
|
|
3052
|
+
{ key: "ArrowUp", run: vertical(cursorLineUp) },
|
|
3053
|
+
{ key: "ArrowDown", run: vertical(cursorLineDown) },
|
|
3054
|
+
{ mac: "Ctrl-p", run: vertical(cursorLineUp) },
|
|
3055
|
+
{ mac: "Ctrl-n", run: vertical(cursorLineDown) }
|
|
3056
|
+
])
|
|
3057
|
+
);
|
|
3058
|
+
|
|
2963
3059
|
// src/extensions/zoom.ts
|
|
2964
|
-
import { Prec as
|
|
2965
|
-
import { keymap as
|
|
3060
|
+
import { Prec as Prec5 } from "@codemirror/state";
|
|
3061
|
+
import { keymap as keymap4 } from "@codemirror/view";
|
|
2966
3062
|
var MIN_FONT_SIZE = 6;
|
|
2967
3063
|
var FALLBACK_FONT_SIZE = 16;
|
|
2968
3064
|
var px = (value) => parseFloat(value) || FALLBACK_FONT_SIZE;
|
|
@@ -2991,8 +3087,8 @@ function step(view, by) {
|
|
|
2991
3087
|
var zoomIn = (view) => step(view, 1);
|
|
2992
3088
|
var zoomOut = (view) => step(view, -1);
|
|
2993
3089
|
var zoomReset = (view) => step(view, 0);
|
|
2994
|
-
var markdownZoom =
|
|
2995
|
-
|
|
3090
|
+
var markdownZoom = Prec5.high(
|
|
3091
|
+
keymap4.of([
|
|
2996
3092
|
// `+` and `_` are the shifted keys, for keyboards that need Shift.
|
|
2997
3093
|
{ key: "Mod-=", run: zoomIn, preventDefault: true },
|
|
2998
3094
|
{ key: "Mod-+", run: zoomIn, preventDefault: true },
|
|
@@ -3004,7 +3100,7 @@ var markdownZoom = Prec3.high(
|
|
|
3004
3100
|
|
|
3005
3101
|
// src/themes/markdown.ts
|
|
3006
3102
|
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
3007
|
-
import { EditorView as
|
|
3103
|
+
import { EditorView as EditorView8 } from "@codemirror/view";
|
|
3008
3104
|
var bodyFont = (style) => style == "serif" ? 'var(--text-editor-family-serif, "Merriweather", serif)' : "var(--text-editor-family-sans)";
|
|
3009
3105
|
var zoomed2 = (length) => `calc(${length} * var(--markdown-zoom, 1))`;
|
|
3010
3106
|
var markdownStyles = (style) => ({
|
|
@@ -3219,7 +3315,7 @@ var markdownSyntaxHighlighting = (style) => HighlightStyle.define([
|
|
|
3219
3315
|
{ tag: tags.monospace, ...baseCodeStyles }
|
|
3220
3316
|
]);
|
|
3221
3317
|
var theme = (style) => [
|
|
3222
|
-
|
|
3318
|
+
EditorView8.theme(markdownStyles(style)),
|
|
3223
3319
|
syntaxHighlighting(markdownSyntaxHighlighting(style))
|
|
3224
3320
|
];
|
|
3225
3321
|
|
|
@@ -3229,7 +3325,7 @@ function markdownExtensions() {
|
|
|
3229
3325
|
...theme("sans"),
|
|
3230
3326
|
indentOnInput(),
|
|
3231
3327
|
markdownZoom,
|
|
3232
|
-
|
|
3328
|
+
keymap5.of([
|
|
3233
3329
|
...markdownFormattingKeymap,
|
|
3234
3330
|
...defaultKeymap,
|
|
3235
3331
|
...searchKeymap,
|
|
@@ -3237,7 +3333,7 @@ function markdownExtensions() {
|
|
|
3237
3333
|
...completionKeymap,
|
|
3238
3334
|
indentWithTab
|
|
3239
3335
|
]),
|
|
3240
|
-
|
|
3336
|
+
EditorView9.lineWrapping,
|
|
3241
3337
|
// GFM base: task lists, tables, strikethrough, autolinks.
|
|
3242
3338
|
markdown({
|
|
3243
3339
|
base: markdownLanguage,
|
|
@@ -3246,6 +3342,8 @@ function markdownExtensions() {
|
|
|
3246
3342
|
}),
|
|
3247
3343
|
taskCheckboxes,
|
|
3248
3344
|
margin,
|
|
3345
|
+
prefixes,
|
|
3346
|
+
verticalMemory,
|
|
3249
3347
|
headings,
|
|
3250
3348
|
lists,
|
|
3251
3349
|
indentUnit.of(" ")
|