@inkandswitch/patchwork-pkg-base 0.0.127 → 0.0.129
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/account/dist/{AccountPicker-TCOUU4YK.js → AccountPicker-SBCGXHRS.js} +13 -44
- package/packages/account/dist/AccountPicker-SBCGXHRS.js.map +7 -0
- package/packages/account/dist/{ContactAvatar-MKDWQC6G.js → ContactAvatar-IVYTJ7M4.js} +5 -2
- package/packages/account/dist/ContactAvatar-IVYTJ7M4.js.map +7 -0
- package/packages/account/dist/{ContactCursor-4FYCATG5.js → ContactCursor-2FVKF5QA.js} +5 -2
- package/packages/account/dist/ContactCursor-2FVKF5QA.js.map +7 -0
- package/packages/account/dist/chunk-L6JWPAD3.js +52 -0
- package/packages/account/dist/chunk-L6JWPAD3.js.map +7 -0
- package/packages/account/dist/index.js +3 -3
- package/packages/codemirror-base/dist/{chunk-A2WBDY3J.js → chunk-REXVGJ53.js} +27 -2
- package/packages/codemirror-base/dist/{chunk-A2WBDY3J.js.map → chunk-REXVGJ53.js.map} +2 -2
- package/packages/codemirror-base/dist/lib/codemirror.js +1 -1
- package/packages/codemirror-base/dist/tool.js +1 -1
- package/packages/codemirror-markdown/dist/extensions/links.js +16 -11
- package/packages/codemirror-markdown/dist/extensions/links.js.map +2 -2
- package/packages/codemirror-markdown/dist/extensions/markdown.js +404 -63
- package/packages/codemirror-markdown/dist/extensions/markdown.js.map +3 -3
- package/packages/theming/dist/{active-theme-6H5UANUR.js → active-theme-WA6XMDTP.js} +2 -2
- package/packages/theming/dist/{chunk-HHCCYVOT.js → chunk-4GOF67HA.js} +2 -2
- package/packages/theming/dist/{chunk-VJZ4NPK4.js → chunk-T7VWPJQG.js} +26 -1
- package/packages/theming/dist/chunk-T7VWPJQG.js.map +7 -0
- package/packages/theming/dist/main.js +4 -4
- package/packages/theming/dist/{theme-HE7ECLIW.css → theme-HV4IDVYM.css} +44 -0
- package/packages/theming/dist/{theme-editor-datatype-TJ6LYT2U.js → theme-editor-datatype-DZFQK7HV.js} +2 -2
- package/packages/theming/dist/theme-editor.js +1 -1
- package/packages/theming/dist/{theme-titlebar-UX4BND5Z.js → theme-titlebar-LG6PFBKR.js} +2 -2
- package/packages/theming/dist/{theme-tray-B477G2WR.js → theme-tray-R3PJDYCL.js} +2 -2
- package/packages/theming/dist/tool.js +1 -1
- package/packages/threepane/dist/IsolatedDocumentArea-JJMLEL4O.js +2 -0
- package/packages/threepane/dist/{PatchworkFrame-22ABHJVG.js → PatchworkFrame-HMA5KZLY.js} +2 -2
- package/packages/threepane/dist/chunk-UOTRB26X.js +846 -0
- package/packages/threepane/dist/chunk-UOTRB26X.js.map +7 -0
- package/packages/threepane/dist/index.js +1 -1
- package/plugin-registry-url +1 -1
- package/packages/account/dist/AccountPicker-TCOUU4YK.js.map +0 -7
- package/packages/account/dist/ContactAvatar-MKDWQC6G.js.map +0 -7
- package/packages/account/dist/ContactCursor-4FYCATG5.js.map +0 -7
- package/packages/theming/dist/chunk-VJZ4NPK4.js.map +0 -7
- package/packages/threepane/dist/IsolatedDocumentArea-3HGASUGM.js +0 -2
- package/packages/threepane/dist/chunk-34WWZW2V.js +0 -1215
- package/packages/threepane/dist/chunk-34WWZW2V.js.map +0 -7
- /package/packages/theming/dist/{active-theme-6H5UANUR.js.map → active-theme-WA6XMDTP.js.map} +0 -0
- /package/packages/theming/dist/{chunk-HHCCYVOT.js.map → chunk-4GOF67HA.js.map} +0 -0
- /package/packages/theming/dist/{theme-editor-datatype-TJ6LYT2U.js.map → theme-editor-datatype-DZFQK7HV.js.map} +0 -0
- /package/packages/theming/dist/{theme-titlebar-UX4BND5Z.js.map → theme-titlebar-LG6PFBKR.js.map} +0 -0
- /package/packages/theming/dist/{theme-tray-B477G2WR.js.map → theme-tray-R3PJDYCL.js.map} +0 -0
- /package/packages/threepane/dist/{IsolatedDocumentArea-3HGASUGM.js.map → IsolatedDocumentArea-JJMLEL4O.js.map} +0 -0
- /package/packages/threepane/dist/{PatchworkFrame-22ABHJVG.js.map → PatchworkFrame-HMA5KZLY.js.map} +0 -0
|
@@ -17,7 +17,7 @@ import { EditorView } from "@codemirror/view";
|
|
|
17
17
|
var linkTheme = EditorView.baseTheme({
|
|
18
18
|
".cm-link": {
|
|
19
19
|
textDecoration: "underline",
|
|
20
|
-
|
|
20
|
+
// a plain click edits; it's a pointer only while Mod is held (below).
|
|
21
21
|
// the widget replaces the link's text, so the theme's `t.link` highlight
|
|
22
22
|
// (theming/src/codemirror-theme.ts) never reaches it: read the same tokens.
|
|
23
23
|
fontFamily: "var(--markdown-body-family, var(--text-editor-family-sans))",
|
|
@@ -67,14 +67,11 @@ var LinkWidget = class extends WidgetType {
|
|
|
67
67
|
link.textContent = this.text;
|
|
68
68
|
link.className = "cm-link";
|
|
69
69
|
link.title = this.url;
|
|
70
|
-
link.
|
|
71
|
-
e.preventDefault();
|
|
72
|
-
openUrl(this.url, link);
|
|
73
|
-
};
|
|
70
|
+
link.addEventListener("click", (e) => e.preventDefault());
|
|
74
71
|
return link;
|
|
75
72
|
}
|
|
76
|
-
ignoreEvent(
|
|
77
|
-
return
|
|
73
|
+
ignoreEvent() {
|
|
74
|
+
return false;
|
|
78
75
|
}
|
|
79
76
|
};
|
|
80
77
|
function getLinks(view) {
|
|
@@ -156,7 +153,10 @@ function linkAt(state, pos) {
|
|
|
156
153
|
return null;
|
|
157
154
|
}
|
|
158
155
|
var urlAt = (state, pos) => linkAt(state, pos)?.url ?? null;
|
|
159
|
-
function linkUnderPointer(view, x, y) {
|
|
156
|
+
function linkUnderPointer(view, x, y, target) {
|
|
157
|
+
const widget = target instanceof Element ? target.closest(".cm-link") : null;
|
|
158
|
+
if (widget && view.contentDOM.contains(widget))
|
|
159
|
+
return linkAt(view.state, view.posAtDOM(widget));
|
|
160
160
|
const hit = view.posAndSideAtCoords({ x, y });
|
|
161
161
|
if (!hit) return null;
|
|
162
162
|
const at = view.coordsAtPos(hit.pos, hit.assoc);
|
|
@@ -190,7 +190,7 @@ var modClick = ViewPlugin.fromClass(
|
|
|
190
190
|
mod = false;
|
|
191
191
|
refresh() {
|
|
192
192
|
const { view } = this;
|
|
193
|
-
const link = this.mod && this.pointer ? linkUnderPointer(view, this.pointer.x, this.pointer.y) : null;
|
|
193
|
+
const link = this.mod && this.pointer ? linkUnderPointer(view, this.pointer.x, this.pointer.y, this.pointer.target) : null;
|
|
194
194
|
let current = null;
|
|
195
195
|
view.state.field(hoverField).between(0, view.state.doc.length, (from, to) => {
|
|
196
196
|
current = { from, to };
|
|
@@ -204,14 +204,19 @@ var modClick = ViewPlugin.fromClass(
|
|
|
204
204
|
eventHandlers: {
|
|
205
205
|
mousedown(event, view) {
|
|
206
206
|
if (event.button !== 0 || !modHeld(event)) return false;
|
|
207
|
-
const link = linkUnderPointer(
|
|
207
|
+
const link = linkUnderPointer(
|
|
208
|
+
view,
|
|
209
|
+
event.clientX,
|
|
210
|
+
event.clientY,
|
|
211
|
+
event.target
|
|
212
|
+
);
|
|
208
213
|
if (!link) return false;
|
|
209
214
|
event.preventDefault();
|
|
210
215
|
openUrl(link.url, view.dom);
|
|
211
216
|
return true;
|
|
212
217
|
},
|
|
213
218
|
mousemove(event) {
|
|
214
|
-
this.pointer = { x: event.clientX, y: event.clientY };
|
|
219
|
+
this.pointer = { x: event.clientX, y: event.clientY, target: event.target };
|
|
215
220
|
this.mod = modHeld(event);
|
|
216
221
|
this.refresh();
|
|
217
222
|
},
|
|
@@ -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 // `<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": ";AAAA;AAAA,EACE,cAAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EAGE;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;;;ACfpC,SAAS,kBAAkB;AAEpB,IAAM,YAAY,WAAW,UAAU;AAAA,EAC5C,YAAY;AAAA,IACV,gBAAgB;AAAA
|
|
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 // Clicks work as on any other link in the editor: a plain click puts\n // the caret there (which shows the markup), and Mod-click opens it \u2014 see\n // modClick below. The browser mustn't follow the href itself.\n link.addEventListener(\"click\", (e) => e.preventDefault());\n\n return link;\n }\n\n ignoreEvent() {\n // hand every event to the editor, so it places the caret and modClick\n // sees the Mod-click.\n return false;\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(\n view: EditorView,\n x: number,\n y: number,\n target?: EventTarget | null\n) {\n // over a rendered link: its whole width is the link, not just near an edge.\n const widget =\n target instanceof Element ? target.closest(\".cm-link\") : null;\n if (widget && view.contentDOM.contains(widget))\n return linkAt(view.state, view.posAtDOM(widget));\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; target: EventTarget | null } | 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, this.pointer.target)\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(\n view,\n event.clientX,\n event.clientY,\n event.target\n );\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, target: event.target };\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 // a plain click edits; it's a pointer only while Mod is held (below).\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
|
}
|