Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol diffdown.com

feat: add dark mode with system preference + manual toggle

- CSS custom properties for dark theme on [data-theme="dark"] and prefers-color-scheme
- Theme toggle button (☀/🌙) in navbar, persisted to localStorage
- Flash-free theme application via inline script in <head>
- CodeMirror oneDark theme switches dynamically via Compartment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+523 -280
+13
package-lock.json
··· 11 11 "dependencies": { 12 12 "@codemirror/lang-markdown": "^6.5.0", 13 13 "@codemirror/state": "^6.5.4", 14 + "@codemirror/theme-one-dark": "^6.1.3", 14 15 "@codemirror/view": "^6.39.16", 15 16 "codemirror": "^6.0.2" 16 17 } ··· 142 143 "license": "MIT", 143 144 "dependencies": { 144 145 "@marijn/find-cluster-break": "^1.0.0" 146 + } 147 + }, 148 + "node_modules/@codemirror/theme-one-dark": { 149 + "version": "6.1.3", 150 + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", 151 + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", 152 + "license": "MIT", 153 + "dependencies": { 154 + "@codemirror/language": "^6.0.0", 155 + "@codemirror/state": "^6.0.0", 156 + "@codemirror/view": "^6.0.0", 157 + "@lezer/highlight": "^1.0.0" 145 158 } 146 159 }, 147 160 "node_modules/@codemirror/view": {
+1
package.json
··· 13 13 "dependencies": { 14 14 "@codemirror/lang-markdown": "^6.5.0", 15 15 "@codemirror/state": "^6.5.4", 16 + "@codemirror/theme-one-dark": "^6.1.3", 16 17 "@codemirror/view": "^6.39.16", 17 18 "codemirror": "^6.0.2" 18 19 }
+8
static/css/diff.css
··· 50 50 51 51 .diff-delete { background: #fef2f2; } 52 52 .diff-delete .diff-sign { color: var(--danger); } 53 + 54 + [data-theme="dark"] .diff-insert { background: #0d2d17; } 55 + [data-theme="dark"] .diff-delete { background: #2d0d0d; } 56 + 57 + @media (prefers-color-scheme: dark) { 58 + :root:not([data-theme="light"]) .diff-insert { background: #0d2d17; } 59 + :root:not([data-theme="light"]) .diff-delete { background: #2d0d0d; } 60 + }
+1 -1
static/css/markdown.css
··· 18 18 .markdown-body p { margin-bottom: 1em; } 19 19 20 20 .markdown-body code { 21 - background: #f3f4f6; 21 + background: var(--border); 22 22 padding: 0.15em 0.4em; 23 23 border-radius: 3px; 24 24 font-family: var(--font-mono);
+34 -2
static/css/style.css
··· 14 14 --primary-hover: #1d4ed8; 15 15 --danger: #dc2626; 16 16 --success: #16a34a; 17 + --alert-error-bg: #fef2f2; 18 + --alert-error-border: #fecaca; 17 19 --radius: 6px; 18 20 --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 19 21 --font-mono: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace; 22 + } 23 + 24 + [data-theme="dark"] { 25 + --bg: #0d1117; 26 + --bg-card: #161b22; 27 + --text: #e6edf3; 28 + --text-muted: #8b949e; 29 + --border: #30363d; 30 + --primary: #388bfd; 31 + --primary-hover: #58a6ff; 32 + --danger: #f85149; 33 + --success: #3fb950; 34 + --alert-error-bg: #1c0608; 35 + --alert-error-border: #6e1c20; 36 + } 37 + 38 + @media (prefers-color-scheme: dark) { 39 + :root:not([data-theme="light"]) { 40 + --bg: #0d1117; 41 + --bg-card: #161b22; 42 + --text: #e6edf3; 43 + --text-muted: #8b949e; 44 + --border: #30363d; 45 + --primary: #388bfd; 46 + --primary-hover: #58a6ff; 47 + --danger: #f85149; 48 + --success: #3fb950; 49 + --alert-error-bg: #1c0608; 50 + --alert-error-border: #6e1c20; 51 + } 20 52 } 21 53 22 54 body { ··· 109 141 } 110 142 111 143 .alert-error { 112 - background: #fef2f2; 144 + background: var(--alert-error-bg); 113 145 color: var(--danger); 114 - border: 1px solid #fecaca; 146 + border: 1px solid var(--alert-error-border); 115 147 } 116 148 117 149 /* Auth pages */
+417 -271
static/vendor/editor.js
··· 1473 1473 return new _EditorSelection(ranges, mainIndex); 1474 1474 } 1475 1475 }; 1476 - function checkSelection(selection, docLength) { 1477 - for (let range of selection.ranges) 1476 + function checkSelection(selection2, docLength) { 1477 + for (let range of selection2.ranges) 1478 1478 if (range.to > docLength) 1479 1479 throw new RangeError("Selection points outside of document"); 1480 1480 } ··· 2032 2032 StateEffect.reconfigure = /* @__PURE__ */ StateEffect.define(); 2033 2033 StateEffect.appendConfig = /* @__PURE__ */ StateEffect.define(); 2034 2034 var Transaction = class _Transaction { 2035 - constructor(startState, changes, selection, effects, annotations, scrollIntoView3) { 2035 + constructor(startState, changes, selection2, effects, annotations, scrollIntoView3) { 2036 2036 this.startState = startState; 2037 2037 this.changes = changes; 2038 - this.selection = selection; 2038 + this.selection = selection2; 2039 2039 this.effects = effects; 2040 2040 this.annotations = annotations; 2041 2041 this.scrollIntoView = scrollIntoView3; 2042 2042 this._doc = null; 2043 2043 this._state = null; 2044 - if (selection) 2045 - checkSelection(selection, changes.newLength); 2044 + if (selection2) 2045 + checkSelection(selection2, changes.newLength); 2046 2046 if (!annotations.some((a) => a.type == _Transaction.time)) 2047 2047 this.annotations = annotations.concat(_Transaction.time.of(Date.now())); 2048 2048 } 2049 2049 /** 2050 2050 @internal 2051 2051 */ 2052 - static create(startState, changes, selection, effects, annotations, scrollIntoView3) { 2053 - return new _Transaction(startState, changes, selection, effects, annotations, scrollIntoView3); 2052 + static create(startState, changes, selection2, effects, annotations, scrollIntoView3) { 2053 + return new _Transaction(startState, changes, selection2, effects, annotations, scrollIntoView3); 2054 2054 } 2055 2055 /** 2056 2056 The new document produced by the transaction. Contrary to ··· 2270 2270 }; 2271 2271 } 2272 2272 var EditorState = class _EditorState { 2273 - constructor(config2, doc2, selection, values2, computeSlot, tr) { 2273 + constructor(config2, doc2, selection2, values2, computeSlot, tr) { 2274 2274 this.config = config2; 2275 2275 this.doc = doc2; 2276 - this.selection = selection; 2276 + this.selection = selection2; 2277 2277 this.values = values2; 2278 2278 this.status = config2.statusTemplate.slice(); 2279 2279 this.computeSlot = computeSlot; ··· 2340 2340 } else { 2341 2341 startValues = tr.startState.values.slice(); 2342 2342 } 2343 - let selection = tr.startState.facet(allowMultipleSelections) ? tr.newSelection : tr.newSelection.asSingle(); 2344 - new _EditorState(conf, tr.newDoc, selection, startValues, (state, slot) => slot.update(state, tr), tr); 2343 + let selection2 = tr.startState.facet(allowMultipleSelections) ? tr.newSelection : tr.newSelection.asSingle(); 2344 + new _EditorState(conf, tr.newDoc, selection2, startValues, (state, slot) => slot.update(state, tr), tr); 2345 2345 } 2346 2346 /** 2347 2347 Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that ··· 2471 2471 static create(config2 = {}) { 2472 2472 let configuration = Configuration.resolve(config2.extensions || [], /* @__PURE__ */ new Map()); 2473 2473 let doc2 = config2.doc instanceof Text ? config2.doc : Text.of((config2.doc || "").split(configuration.staticFacet(_EditorState.lineSeparator) || DefaultSplit)); 2474 - let selection = !config2.selection ? EditorSelection.single(0) : config2.selection instanceof EditorSelection ? config2.selection : EditorSelection.single(config2.selection.anchor, config2.selection.head); 2475 - checkSelection(selection, doc2.length); 2474 + let selection2 = !config2.selection ? EditorSelection.single(0) : config2.selection instanceof EditorSelection ? config2.selection : EditorSelection.single(config2.selection.anchor, config2.selection.head); 2475 + checkSelection(selection2, doc2.length); 2476 2476 if (!configuration.staticFacet(allowMultipleSelections)) 2477 - selection = selection.asSingle(); 2478 - return new _EditorState(configuration, doc2, selection, configuration.dynamicSlots.map(() => null), (state, slot) => slot.create(state), null); 2477 + selection2 = selection2.asSingle(); 2478 + return new _EditorState(configuration, doc2, selection2, configuration.dynamicSlots.map(() => null), (state, slot) => slot.create(state), null); 2479 2479 } 2480 2480 /** 2481 2481 The size (in columns) of a tab in the document, determined by ··· 2911 2911 of the iteration. 2912 2912 */ 2913 2913 static spans(sets, from, to, iterator, minPointSize = -1) { 2914 - let cursor = new SpanCursor(sets, null, minPointSize).goto(from), pos = from; 2915 - let openRanges = cursor.openStart; 2914 + let cursor2 = new SpanCursor(sets, null, minPointSize).goto(from), pos = from; 2915 + let openRanges = cursor2.openStart; 2916 2916 for (; ; ) { 2917 - let curTo = Math.min(cursor.to, to); 2918 - if (cursor.point) { 2919 - let active = cursor.activeForPoint(cursor.to); 2920 - let openCount = cursor.pointFrom < from ? active.length + 1 : cursor.point.startSide < 0 ? active.length : Math.min(active.length, openRanges); 2921 - iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank); 2922 - openRanges = Math.min(cursor.openEnd(curTo), active.length); 2917 + let curTo = Math.min(cursor2.to, to); 2918 + if (cursor2.point) { 2919 + let active = cursor2.activeForPoint(cursor2.to); 2920 + let openCount = cursor2.pointFrom < from ? active.length + 1 : cursor2.point.startSide < 0 ? active.length : Math.min(active.length, openRanges); 2921 + iterator.point(pos, curTo, cursor2.point, active, openCount, cursor2.pointRank); 2922 + openRanges = Math.min(cursor2.openEnd(curTo), active.length); 2923 2923 } else if (curTo > pos) { 2924 - iterator.span(pos, curTo, cursor.active, openRanges); 2925 - openRanges = cursor.openEnd(curTo); 2924 + iterator.span(pos, curTo, cursor2.active, openRanges); 2925 + openRanges = cursor2.openEnd(curTo); 2926 2926 } 2927 - if (cursor.to > to) 2928 - return openRanges + (cursor.point && cursor.to > to ? 1 : 0); 2929 - pos = cursor.to; 2930 - cursor.next(); 2927 + if (cursor2.to > to) 2928 + return openRanges + (cursor2.point && cursor2.to > to ? 1 : 0); 2929 + pos = cursor2.to; 2930 + cursor2.next(); 2931 2931 } 2932 2932 } 2933 2933 /** ··· 4070 4070 function contains(dom, node) { 4071 4071 return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false; 4072 4072 } 4073 - function hasSelection(dom, selection) { 4074 - if (!selection.anchorNode) 4073 + function hasSelection(dom, selection2) { 4074 + if (!selection2.anchorNode) 4075 4075 return false; 4076 4076 try { 4077 - return contains(dom, selection.anchorNode); 4077 + return contains(dom, selection2.anchorNode); 4078 4078 } catch (_) { 4079 4079 return false; 4080 4080 } ··· 4350 4350 } 4351 4351 return null; 4352 4352 } 4353 - function atElementStart(doc2, selection) { 4354 - let node = selection.focusNode, offset = selection.focusOffset; 4355 - if (!node || selection.anchorNode != node || selection.anchorOffset != offset) 4353 + function atElementStart(doc2, selection2) { 4354 + let node = selection2.focusNode, offset = selection2.focusOffset; 4355 + if (!node || selection2.anchorNode != node || selection2.anchorOffset != offset) 4356 4356 return false; 4357 4357 offset = Math.min(offset, maxOffset(node)); 4358 4358 for (; ; ) { ··· 6517 6517 // If a zero-length widget is inserted next to the cursor during 6518 6518 // composition, avoid moving it across it and disrupting the 6519 6519 // composition. 6520 - suppressWidgetCursorChange(sel, cursor) { 6521 - return this.hasComposition && cursor.empty && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) && this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor.head; 6520 + suppressWidgetCursorChange(sel, cursor2) { 6521 + return this.hasComposition && cursor2.empty && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) && this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor2.head; 6522 6522 } 6523 6523 enforceCursorAssoc() { 6524 6524 if (this.hasComposition) 6525 6525 return; 6526 - let { view } = this, cursor = view.state.selection.main; 6526 + let { view } = this, cursor2 = view.state.selection.main; 6527 6527 let sel = getSelection(view.root); 6528 6528 let { anchorNode, anchorOffset } = view.observer.selectionRange; 6529 - if (!sel || !cursor.empty || !cursor.assoc || !sel.modify) 6529 + if (!sel || !cursor2.empty || !cursor2.assoc || !sel.modify) 6530 6530 return; 6531 - let line = this.lineAt(cursor.head, cursor.assoc); 6531 + let line = this.lineAt(cursor2.head, cursor2.assoc); 6532 6532 if (!line) 6533 6533 return; 6534 6534 let lineStart = line.posAtStart; 6535 - if (cursor.head == lineStart || cursor.head == lineStart + line.length) 6535 + if (cursor2.head == lineStart || cursor2.head == lineStart + line.length) 6536 6536 return; 6537 - let before = this.coordsAt(cursor.head, -1), after = this.coordsAt(cursor.head, 1); 6537 + let before = this.coordsAt(cursor2.head, -1), after = this.coordsAt(cursor2.head, 1); 6538 6538 if (!before || !after || before.bottom > after.top) 6539 6539 return; 6540 - let dom = this.domAtPos(cursor.head + cursor.assoc, cursor.assoc); 6540 + let dom = this.domAtPos(cursor2.head + cursor2.assoc, cursor2.assoc); 6541 6541 sel.collapse(dom.node, dom.offset); 6542 - sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary"); 6542 + sel.modify("move", cursor2.assoc < 0 ? "forward" : "backward", "lineboundary"); 6543 6543 view.observer.readSelectionRange(); 6544 6544 let newRange = view.observer.selectionRange; 6545 - if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from) 6545 + if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor2.from) 6546 6546 sel.collapse(anchorNode, anchorOffset); 6547 6547 } 6548 6548 posFromDOM(node, offset) { ··· 7726 7726 let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor; 7727 7727 return anchor > -1 && head > -1 ? EditorSelection.single(anchor + base2, head + base2) : null; 7728 7728 } 7729 - function sameSelPos(selection, range) { 7730 - return range.head == selection.main.head && range.anchor == selection.main.anchor; 7729 + function sameSelPos(selection2, range) { 7730 + return range.head == selection2.main.head && range.anchor == selection2.main.anchor; 7731 7731 } 7732 7732 var InputState = class { 7733 7733 setSelectionOrigin(origin) { ··· 8004 8004 this.select(this.lastEvent); 8005 8005 } 8006 8006 select(event) { 8007 - let { view } = this, selection = skipAtomsForSelection(this.atoms, this.style.get(event, this.extend, this.multiple)); 8008 - if (this.mustSelect || !selection.eq(view.state.selection, this.dragging === false)) 8007 + let { view } = this, selection2 = skipAtomsForSelection(this.atoms, this.style.get(event, this.extend, this.multiple)); 8008 + if (this.mustSelect || !selection2.eq(view.state.selection, this.dragging === false)) 8009 8009 this.view.dispatch({ 8010 - selection, 8010 + selection: selection2, 8011 8011 userEvent: "select.pointer" 8012 8012 }); 8013 8013 this.mustSelect = false; ··· 10266 10266 } 10267 10267 readSelectionRange() { 10268 10268 let { view } = this; 10269 - let selection = getSelection(view.root); 10270 - if (!selection) 10269 + let selection2 = getSelection(view.root); 10270 + if (!selection2) 10271 10271 return false; 10272 - let range = browser.safari && view.root.nodeType == 11 && view.root.activeElement == this.dom && safariSelectionRangeHack(this.view, selection) || selection; 10272 + let range = browser.safari && view.root.nodeType == 11 && view.root.activeElement == this.dom && safariSelectionRangeHack(this.view, selection2) || selection2; 10273 10273 if (!range || this.selectionRange.eq(range)) 10274 10274 return false; 10275 10275 let local = hasSelection(this.dom, range); ··· 10553 10553 [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset]; 10554 10554 return { anchorNode, anchorOffset, focusNode, focusOffset }; 10555 10555 } 10556 - function safariSelectionRangeHack(view, selection) { 10557 - if (selection.getComposedRanges) { 10558 - let range = selection.getComposedRanges(view.root)[0]; 10556 + function safariSelectionRangeHack(view, selection2) { 10557 + if (selection2.getComposedRanges) { 10558 + let range = selection2.getComposedRanges(view.root)[0]; 10559 10559 if (range) 10560 10560 return buildSelectionRangeFromRange(view, range); 10561 10561 } ··· 12205 12205 let prim = r == state.selection.main; 12206 12206 if (r.empty || conf.drawRangeCursor) { 12207 12207 let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary"; 12208 - let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1); 12209 - for (let piece of RectangleMarker.forRange(view, className, cursor)) 12208 + let cursor2 = r.empty ? r : EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1); 12209 + for (let piece of RectangleMarker.forRange(view, className, cursor2)) 12210 12210 cursors.push(piece); 12211 12211 } 12212 12212 } ··· 12346 12346 } 12347 12347 function iterMatches(doc2, re, from, to, f) { 12348 12348 re.lastIndex = 0; 12349 - for (let cursor = doc2.iterRange(from, to), pos = from, m; !cursor.next().done; pos += cursor.value.length) { 12350 - if (!cursor.lineBreak) 12351 - while (m = re.exec(cursor.value)) 12349 + for (let cursor2 = doc2.iterRange(from, to), pos = from, m; !cursor2.next().done; pos += cursor2.value.length) { 12350 + if (!cursor2.lineBreak) 12351 + while (m = re.exec(cursor2.value)) 12352 12352 f(pos + m.index, m); 12353 12353 } 12354 12354 } ··· 13855 13855 function asArray2(val) { 13856 13856 return Array.isArray(val) ? val : [val]; 13857 13857 } 13858 - function advanceCursor(cursor, collect, pos) { 13859 - while (cursor.value && cursor.from <= pos) { 13860 - if (cursor.from == pos) 13861 - collect.push(cursor.value); 13862 - cursor.next(); 13858 + function advanceCursor(cursor2, collect, pos) { 13859 + while (cursor2.value && cursor2.from <= pos) { 13860 + if (cursor2.from == pos) 13861 + collect.push(cursor2.value); 13862 + cursor2.next(); 13863 13863 } 13864 13864 } 13865 13865 var UpdateContext = class { ··· 14391 14391 */ 14392 14392 cursorAt(pos, side = 0, mode = 0) { 14393 14393 let scope = CachedNode.get(this) || this.topNode; 14394 - let cursor = new TreeCursor(scope); 14395 - cursor.moveTo(pos, side); 14396 - CachedNode.set(this, cursor._tree); 14397 - return cursor; 14394 + let cursor2 = new TreeCursor(scope); 14395 + cursor2.moveTo(pos, side); 14396 + CachedNode.set(this, cursor2._tree); 14397 + return cursor2; 14398 14398 } 14399 14399 /** 14400 14400 Get a [syntax node](#common.SyntaxNode) object for the top of the ··· 15371 15371 function buildTree(data2) { 15372 15372 var _a2; 15373 15373 let { buffer, nodeSet, maxBufferLength = DefaultBufferLength, reused = [], minRepeatType = nodeSet.types.length } = data2; 15374 - let cursor = Array.isArray(buffer) ? new FlatBufferCursor(buffer, buffer.length) : buffer; 15374 + let cursor2 = Array.isArray(buffer) ? new FlatBufferCursor(buffer, buffer.length) : buffer; 15375 15375 let types2 = nodeSet.types; 15376 15376 let contextHash = 0, lookAhead = 0; 15377 15377 function takeNode(parentStart, minPos, children2, positions2, inRepeat, depth) { 15378 - let { id: id2, start, end, size } = cursor; 15378 + let { id: id2, start, end, size } = cursor2; 15379 15379 let lookAheadAtStart = lookAhead, contextAtStart = contextHash; 15380 15380 if (size < 0) { 15381 - cursor.next(); 15381 + cursor2.next(); 15382 15382 if (size == -1) { 15383 15383 let node2 = reused[id2]; 15384 15384 children2.push(node2); ··· 15396 15396 } 15397 15397 let type = types2[id2], node, buffer2; 15398 15398 let startPos = start - parentStart; 15399 - if (end - start <= maxBufferLength && (buffer2 = findBufferSize(cursor.pos - minPos, inRepeat))) { 15399 + if (end - start <= maxBufferLength && (buffer2 = findBufferSize(cursor2.pos - minPos, inRepeat))) { 15400 15400 let data3 = new Uint16Array(buffer2.size - buffer2.skip); 15401 - let endPos = cursor.pos - buffer2.size, index = data3.length; 15402 - while (cursor.pos > endPos) 15401 + let endPos = cursor2.pos - buffer2.size, index = data3.length; 15402 + while (cursor2.pos > endPos) 15403 15403 index = copyToBuffer(buffer2.start, data3, index); 15404 15404 node = new TreeBuffer(data3, end - buffer2.start, nodeSet); 15405 15405 startPos = buffer2.start - parentStart; 15406 15406 } else { 15407 - let endPos = cursor.pos - size; 15408 - cursor.next(); 15407 + let endPos = cursor2.pos - size; 15408 + cursor2.next(); 15409 15409 let localChildren = [], localPositions = []; 15410 15410 let localInRepeat = id2 >= minRepeatType ? id2 : -1; 15411 15411 let lastGroup = 0, lastEnd = end; 15412 - while (cursor.pos > endPos) { 15413 - if (localInRepeat >= 0 && cursor.id == localInRepeat && cursor.size >= 0) { 15414 - if (cursor.end <= lastEnd - maxBufferLength) { 15415 - makeRepeatLeaf(localChildren, localPositions, start, lastGroup, cursor.end, lastEnd, localInRepeat, lookAheadAtStart, contextAtStart); 15412 + while (cursor2.pos > endPos) { 15413 + if (localInRepeat >= 0 && cursor2.id == localInRepeat && cursor2.size >= 0) { 15414 + if (cursor2.end <= lastEnd - maxBufferLength) { 15415 + makeRepeatLeaf(localChildren, localPositions, start, lastGroup, cursor2.end, lastEnd, localInRepeat, lookAheadAtStart, contextAtStart); 15416 15416 lastGroup = localChildren.length; 15417 - lastEnd = cursor.end; 15417 + lastEnd = cursor2.end; 15418 15418 } 15419 - cursor.next(); 15419 + cursor2.next(); 15420 15420 } else if (depth > 2500) { 15421 15421 takeFlatNode(start, endPos, localChildren, localPositions); 15422 15422 } else { ··· 15440 15440 function takeFlatNode(parentStart, minPos, children2, positions2) { 15441 15441 let nodes = []; 15442 15442 let nodeCount = 0, stopAt = -1; 15443 - while (cursor.pos > minPos) { 15444 - let { id: id2, start, end, size } = cursor; 15443 + while (cursor2.pos > minPos) { 15444 + let { id: id2, start, end, size } = cursor2; 15445 15445 if (size > 4) { 15446 - cursor.next(); 15446 + cursor2.next(); 15447 15447 } else if (stopAt > -1 && start < stopAt) { 15448 15448 break; 15449 15449 } else { ··· 15451 15451 stopAt = end - maxBufferLength; 15452 15452 nodes.push(id2, start, end); 15453 15453 nodeCount++; 15454 - cursor.next(); 15454 + cursor2.next(); 15455 15455 } 15456 15456 } 15457 15457 if (nodeCount) { ··· 15500 15500 return new Tree(type, children2, positions2, length2, props); 15501 15501 } 15502 15502 function findBufferSize(maxSize, inRepeat) { 15503 - let fork = cursor.fork(); 15503 + let fork = cursor2.fork(); 15504 15504 let size = 0, start = 0, skip = 0, minStart = fork.end - maxBufferLength; 15505 15505 let result = { size: 0, start: 0, skip: 0 }; 15506 15506 scan: for (let minPos = fork.pos - maxSize; fork.pos > minPos; ) { ··· 15543 15543 return result.size > 4 ? result : void 0; 15544 15544 } 15545 15545 function copyToBuffer(bufferStart, buffer2, index) { 15546 - let { id: id2, start, end, size } = cursor; 15547 - cursor.next(); 15546 + let { id: id2, start, end, size } = cursor2; 15547 + cursor2.next(); 15548 15548 if (size >= 0 && id2 < minRepeatType) { 15549 15549 let startIndex = index; 15550 15550 if (size > 4) { 15551 - let endPos = cursor.pos - (size - 4); 15552 - while (cursor.pos > endPos) 15551 + let endPos = cursor2.pos - (size - 4); 15552 + while (cursor2.pos > endPos) 15553 15553 index = copyToBuffer(bufferStart, buffer2, index); 15554 15554 } 15555 15555 buffer2[--index] = startIndex; ··· 15564 15564 return index; 15565 15565 } 15566 15566 let children = [], positions = []; 15567 - while (cursor.pos > 0) 15567 + while (cursor2.pos > 0) 15568 15568 takeNode(data2.start || 0, data2.bufferStart || 0, children, positions, -1, 0); 15569 15569 let length = (_a2 = data2.length) !== null && _a2 !== void 0 ? _a2 : children.length ? positions[0] + children[0].length : 0; 15570 15570 return new Tree(types2[data2.topID], children.reverse(), positions.reverse(), length); ··· 15655 15655 /** 15656 15656 Set the value for the node that a cursor currently points to. 15657 15657 */ 15658 - cursorSet(cursor, value) { 15659 - if (cursor.buffer) 15660 - this.setBuffer(cursor.buffer.buffer, cursor.index, value); 15658 + cursorSet(cursor2, value) { 15659 + if (cursor2.buffer) 15660 + this.setBuffer(cursor2.buffer.buffer, cursor2.index, value); 15661 15661 else 15662 - this.map.set(cursor.tree, value); 15662 + this.map.set(cursor2.tree, value); 15663 15663 } 15664 15664 /** 15665 15665 Retrieve the value for the node that a cursor currently points 15666 15666 to. 15667 15667 */ 15668 - cursorGet(cursor) { 15669 - return cursor.buffer ? this.getBuffer(cursor.buffer.buffer, cursor.index) : this.map.get(cursor.tree); 15668 + cursorGet(cursor2) { 15669 + return cursor2.buffer ? this.getBuffer(cursor2.buffer.buffer, cursor2.index) : this.map.get(cursor2.tree); 15670 15670 } 15671 15671 }; 15672 15672 var TreeFragment = class _TreeFragment { ··· 15887 15887 let fragmentCursor = new FragmentCursor(this.fragments); 15888 15888 let overlay = null; 15889 15889 let covered = null; 15890 - let cursor = new TreeCursor(new TreeNode(this.baseTree, this.ranges[0].from, 0, null), IterMode.IncludeAnonymous | IterMode.IgnoreMounts); 15890 + let cursor2 = new TreeCursor(new TreeNode(this.baseTree, this.ranges[0].from, 0, null), IterMode.IncludeAnonymous | IterMode.IgnoreMounts); 15891 15891 scan: for (let nest, isCovered; ; ) { 15892 15892 let enter = true, range; 15893 - if (this.stoppedAt != null && cursor.from >= this.stoppedAt) { 15893 + if (this.stoppedAt != null && cursor2.from >= this.stoppedAt) { 15894 15894 enter = false; 15895 - } else if (fragmentCursor.hasNode(cursor)) { 15895 + } else if (fragmentCursor.hasNode(cursor2)) { 15896 15896 if (overlay) { 15897 - let match = overlay.mounts.find((m) => m.frag.from <= cursor.from && m.frag.to >= cursor.to && m.mount.overlay); 15897 + let match = overlay.mounts.find((m) => m.frag.from <= cursor2.from && m.frag.to >= cursor2.to && m.mount.overlay); 15898 15898 if (match) 15899 15899 for (let r of match.mount.overlay) { 15900 15900 let from = r.from + match.pos, to = r.to + match.pos; 15901 - if (from >= cursor.from && to <= cursor.to && !overlay.ranges.some((r2) => r2.from < to && r2.to > from)) 15901 + if (from >= cursor2.from && to <= cursor2.to && !overlay.ranges.some((r2) => r2.from < to && r2.to > from)) 15902 15902 overlay.ranges.push({ from, to }); 15903 15903 } 15904 15904 } 15905 15905 enter = false; 15906 - } else if (covered && (isCovered = checkCover(covered.ranges, cursor.from, cursor.to))) { 15906 + } else if (covered && (isCovered = checkCover(covered.ranges, cursor2.from, cursor2.to))) { 15907 15907 enter = isCovered != 2; 15908 - } else if (!cursor.type.isAnonymous && (nest = this.nest(cursor, this.input)) && (cursor.from < cursor.to || !nest.overlay)) { 15909 - if (!cursor.tree) { 15910 - materialize(cursor); 15908 + } else if (!cursor2.type.isAnonymous && (nest = this.nest(cursor2, this.input)) && (cursor2.from < cursor2.to || !nest.overlay)) { 15909 + if (!cursor2.tree) { 15910 + materialize(cursor2); 15911 15911 if (overlay) 15912 15912 overlay.depth++; 15913 15913 if (covered) 15914 15914 covered.depth++; 15915 15915 } 15916 - let oldMounts = fragmentCursor.findMounts(cursor.from, nest.parser); 15916 + let oldMounts = fragmentCursor.findMounts(cursor2.from, nest.parser); 15917 15917 if (typeof nest.overlay == "function") { 15918 - overlay = new ActiveOverlay(nest.parser, nest.overlay, oldMounts, this.inner.length, cursor.from, !!nest.bracketed, cursor.tree, overlay); 15918 + overlay = new ActiveOverlay(nest.parser, nest.overlay, oldMounts, this.inner.length, cursor2.from, !!nest.bracketed, cursor2.tree, overlay); 15919 15919 } else { 15920 - let ranges = punchRanges(this.ranges, nest.overlay || (cursor.from < cursor.to ? [new Range2(cursor.from, cursor.to)] : [])); 15920 + let ranges = punchRanges(this.ranges, nest.overlay || (cursor2.from < cursor2.to ? [new Range2(cursor2.from, cursor2.to)] : [])); 15921 15921 if (ranges.length) 15922 15922 checkRanges(ranges); 15923 15923 if (ranges.length || !nest.overlay) 15924 - this.inner.push(new InnerParse(nest.parser, ranges.length ? nest.parser.startParse(this.input, enterFragments(oldMounts, ranges), ranges) : nest.parser.startParse(""), nest.overlay ? nest.overlay.map((r) => new Range2(r.from - cursor.from, r.to - cursor.from)) : null, !!nest.bracketed, cursor.tree, ranges.length ? ranges[0].from : cursor.from)); 15924 + this.inner.push(new InnerParse(nest.parser, ranges.length ? nest.parser.startParse(this.input, enterFragments(oldMounts, ranges), ranges) : nest.parser.startParse(""), nest.overlay ? nest.overlay.map((r) => new Range2(r.from - cursor2.from, r.to - cursor2.from)) : null, !!nest.bracketed, cursor2.tree, ranges.length ? ranges[0].from : cursor2.from)); 15925 15925 if (!nest.overlay) 15926 15926 enter = false; 15927 15927 else if (ranges.length) 15928 15928 covered = { ranges, depth: 0, prev: covered }; 15929 15929 } 15930 - } else if (overlay && (range = overlay.predicate(cursor))) { 15930 + } else if (overlay && (range = overlay.predicate(cursor2))) { 15931 15931 if (range === true) 15932 - range = new Range2(cursor.from, cursor.to); 15932 + range = new Range2(cursor2.from, cursor2.to); 15933 15933 if (range.from < range.to) { 15934 15934 let last = overlay.ranges.length - 1; 15935 15935 if (last >= 0 && overlay.ranges[last].to == range.from) ··· 15938 15938 overlay.ranges.push(range); 15939 15939 } 15940 15940 } 15941 - if (enter && cursor.firstChild()) { 15941 + if (enter && cursor2.firstChild()) { 15942 15942 if (overlay) 15943 15943 overlay.depth++; 15944 15944 if (covered) 15945 15945 covered.depth++; 15946 15946 } else { 15947 15947 for (; ; ) { 15948 - if (cursor.nextSibling()) 15948 + if (cursor2.nextSibling()) 15949 15949 break; 15950 - if (!cursor.parent()) 15950 + if (!cursor2.parent()) 15951 15951 break scan; 15952 15952 if (overlay && !--overlay.depth) { 15953 15953 let ranges = punchRanges(this.ranges, overlay.ranges); ··· 15980 15980 positions.push(from - off); 15981 15981 } 15982 15982 } 15983 - function materialize(cursor) { 15984 - let { node } = cursor, stack = []; 15983 + function materialize(cursor2) { 15984 + let { node } = cursor2, stack = []; 15985 15985 let buffer = node.context.buffer; 15986 15986 do { 15987 - stack.push(cursor.index); 15988 - cursor.parent(); 15989 - } while (!cursor.tree); 15990 - let base2 = cursor.tree, i = base2.children.indexOf(buffer); 15987 + stack.push(cursor2.index); 15988 + cursor2.parent(); 15989 + } while (!cursor2.tree); 15990 + let base2 = cursor2.tree, i = base2.children.indexOf(buffer); 15991 15991 let buf = base2.children[i], b = buf.buffer, newStack = [i]; 15992 15992 function split(startI, endI, type, innerOffset, length, stackPos) { 15993 15993 let targetI = stack[stackPos]; ··· 16003 16003 } 16004 16004 base2.children[i] = split(0, b.length, NodeType.none, 0, buf.length, stack.length - 1); 16005 16005 for (let index of newStack) { 16006 - let tree = cursor.tree.children[index], pos = cursor.tree.positions[index]; 16007 - cursor.yield(new TreeNode(tree, pos + cursor.from, index, cursor._tree)); 16006 + let tree = cursor2.tree.children[index], pos = cursor2.tree.positions[index]; 16007 + cursor2.yield(new TreeNode(tree, pos + cursor2.from, index, cursor2._tree)); 16008 16008 } 16009 16009 } 16010 16010 var StructureCursor = class { ··· 16015 16015 } 16016 16016 // Move to the first node (in pre-order) that starts at or after `pos`. 16017 16017 moveTo(pos) { 16018 - let { cursor } = this, p = pos - this.offset; 16019 - while (!this.done && cursor.from < p) { 16020 - if (cursor.to >= pos && cursor.enter(p, 1, IterMode.IgnoreOverlays | IterMode.ExcludeBuffers)) ; 16021 - else if (!cursor.next(false)) 16018 + let { cursor: cursor2 } = this, p = pos - this.offset; 16019 + while (!this.done && cursor2.from < p) { 16020 + if (cursor2.to >= pos && cursor2.enter(p, 1, IterMode.IgnoreOverlays | IterMode.ExcludeBuffers)) ; 16021 + else if (!cursor2.next(false)) 16022 16022 this.done = true; 16023 16023 } 16024 16024 } 16025 - hasNode(cursor) { 16026 - this.moveTo(cursor.from); 16027 - if (!this.done && this.cursor.from + this.offset == cursor.from && this.cursor.tree) { 16025 + hasNode(cursor2) { 16026 + this.moveTo(cursor2.from); 16027 + if (!this.done && this.cursor.from + this.offset == cursor2.from && this.cursor.tree) { 16028 16028 for (let tree = this.cursor.tree; ; ) { 16029 - if (tree == cursor.tree) 16029 + if (tree == cursor2.tree) 16030 16030 return true; 16031 16031 if (tree.children.length && tree.positions[0] == 0 && tree.children[0] instanceof Tree) 16032 16032 tree = tree.children[0]; ··· 16411 16411 if (to > this.at && this.class) 16412 16412 this.span(this.at, to, this.class); 16413 16413 } 16414 - highlightRange(cursor, from, to, inheritedClass, highlighters) { 16415 - let { type, from: start, to: end } = cursor; 16414 + highlightRange(cursor2, from, to, inheritedClass, highlighters) { 16415 + let { type, from: start, to: end } = cursor2; 16416 16416 if (start >= to || end <= from) 16417 16417 return; 16418 16418 if (type.isTop) 16419 16419 highlighters = this.highlighters.filter((h) => !h.scope || h.scope(type)); 16420 16420 let cls = inheritedClass; 16421 - let rule = getStyleTags(cursor) || Rule.empty; 16421 + let rule = getStyleTags(cursor2) || Rule.empty; 16422 16422 let tagCls = highlightTags(highlighters, rule.tags); 16423 16423 if (tagCls) { 16424 16424 if (cls) ··· 16430 16430 this.startSpan(Math.max(from, start), cls); 16431 16431 if (rule.opaque) 16432 16432 return; 16433 - let mounted = cursor.tree && cursor.tree.prop(NodeProp.mounted); 16433 + let mounted = cursor2.tree && cursor2.tree.prop(NodeProp.mounted); 16434 16434 if (mounted && mounted.overlay) { 16435 - let inner = cursor.node.enter(mounted.overlay[0].from + start, 1); 16435 + let inner = cursor2.node.enter(mounted.overlay[0].from + start, 1); 16436 16436 let innerHighlighters = this.highlighters.filter((h) => !h.scope || h.scope(mounted.tree.type)); 16437 - let hasChild2 = cursor.firstChild(); 16437 + let hasChild2 = cursor2.firstChild(); 16438 16438 for (let i = 0, pos = start; ; i++) { 16439 16439 let next = i < mounted.overlay.length ? mounted.overlay[i] : null; 16440 16440 let nextPos = next ? next.from + start : end; 16441 16441 let rangeFrom2 = Math.max(from, pos), rangeTo2 = Math.min(to, nextPos); 16442 16442 if (rangeFrom2 < rangeTo2 && hasChild2) { 16443 - while (cursor.from < rangeTo2) { 16444 - this.highlightRange(cursor, rangeFrom2, rangeTo2, inheritedClass, highlighters); 16445 - this.startSpan(Math.min(rangeTo2, cursor.to), cls); 16446 - if (cursor.to >= nextPos || !cursor.nextSibling()) 16443 + while (cursor2.from < rangeTo2) { 16444 + this.highlightRange(cursor2, rangeFrom2, rangeTo2, inheritedClass, highlighters); 16445 + this.startSpan(Math.min(rangeTo2, cursor2.to), cls); 16446 + if (cursor2.to >= nextPos || !cursor2.nextSibling()) 16447 16447 break; 16448 16448 } 16449 16449 } ··· 16456 16456 } 16457 16457 } 16458 16458 if (hasChild2) 16459 - cursor.parent(); 16460 - } else if (cursor.firstChild()) { 16459 + cursor2.parent(); 16460 + } else if (cursor2.firstChild()) { 16461 16461 if (mounted) 16462 16462 inheritedClass = ""; 16463 16463 do { 16464 - if (cursor.to <= from) 16464 + if (cursor2.to <= from) 16465 16465 continue; 16466 - if (cursor.from >= to) 16466 + if (cursor2.from >= to) 16467 16467 break; 16468 - this.highlightRange(cursor, from, to, inheritedClass, highlighters); 16469 - this.startSpan(Math.min(to, cursor.to), cls); 16470 - } while (cursor.nextSibling()); 16471 - cursor.parent(); 16468 + this.highlightRange(cursor2, from, to, inheritedClass, highlighters); 16469 + this.startSpan(Math.min(to, cursor2.to), cls); 16470 + } while (cursor2.nextSibling()); 16471 + cursor2.parent(); 16472 16472 } 16473 16473 } 16474 16474 }; ··· 18410 18410 } 18411 18411 function matchMarkedBrackets(_state, _pos, dir, token, handle, matching, brackets) { 18412 18412 let parent = token.parent, firstToken = { from: handle.from, to: handle.to }; 18413 - let depth = 0, cursor = parent === null || parent === void 0 ? void 0 : parent.cursor(); 18414 - if (cursor && (dir < 0 ? cursor.childBefore(token.from) : cursor.childAfter(token.to))) 18413 + let depth = 0, cursor2 = parent === null || parent === void 0 ? void 0 : parent.cursor(); 18414 + if (cursor2 && (dir < 0 ? cursor2.childBefore(token.from) : cursor2.childAfter(token.to))) 18415 18415 do { 18416 - if (dir < 0 ? cursor.to <= token.from : cursor.from >= token.to) { 18417 - if (depth == 0 && matching.indexOf(cursor.type.name) > -1 && cursor.from < cursor.to) { 18418 - let endHandle = findHandle(cursor); 18416 + if (dir < 0 ? cursor2.to <= token.from : cursor2.from >= token.to) { 18417 + if (depth == 0 && matching.indexOf(cursor2.type.name) > -1 && cursor2.from < cursor2.to) { 18418 + let endHandle = findHandle(cursor2); 18419 18419 return { start: firstToken, end: endHandle ? { from: endHandle.from, to: endHandle.to } : void 0, matched: true }; 18420 - } else if (matchingNodes(cursor.type, dir, brackets)) { 18420 + } else if (matchingNodes(cursor2.type, dir, brackets)) { 18421 18421 depth++; 18422 - } else if (matchingNodes(cursor.type, -dir, brackets)) { 18422 + } else if (matchingNodes(cursor2.type, -dir, brackets)) { 18423 18423 if (depth == 0) { 18424 - let endHandle = findHandle(cursor); 18424 + let endHandle = findHandle(cursor2); 18425 18425 return { 18426 18426 start: firstToken, 18427 18427 end: endHandle && endHandle.from < endHandle.to ? { from: endHandle.from, to: endHandle.to } : void 0, ··· 18431 18431 depth--; 18432 18432 } 18433 18433 } 18434 - } while (dir < 0 ? cursor.prevSibling() : cursor.nextSibling()); 18434 + } while (dir < 0 ? cursor2.prevSibling() : cursor2.nextSibling()); 18435 18435 return { start: firstToken, matched: false }; 18436 18436 } 18437 18437 function matchPlainBrackets(state, pos, dir, tree, tokenType, maxScanDistance, brackets) { ··· 18759 18759 }) 18760 18760 ]; 18761 18761 } 18762 - function cmd(side, selection) { 18762 + function cmd(side, selection2) { 18763 18763 return function({ state, dispatch }) { 18764 - if (!selection && state.readOnly) 18764 + if (!selection2 && state.readOnly) 18765 18765 return false; 18766 18766 let historyState = state.field(historyField_, false); 18767 18767 if (!historyState) 18768 18768 return false; 18769 - let tr = historyState.pop(side, state, selection); 18769 + let tr = historyState.pop(side, state, selection2); 18770 18770 if (!tr) 18771 18771 return false; 18772 18772 dispatch(tr); ··· 18803 18803 // This does not check `addToHistory` and such, it assumes the 18804 18804 // transaction needs to be converted to an item. Returns null when 18805 18805 // there are no changes or effects in the transaction. 18806 - static fromTransaction(tr, selection) { 18806 + static fromTransaction(tr, selection2) { 18807 18807 let effects = none2; 18808 18808 for (let invert of tr.startState.facet(invertedEffects)) { 18809 18809 let result = invert(tr); ··· 18812 18812 } 18813 18813 if (!effects.length && tr.changes.empty) 18814 18814 return null; 18815 - return new _HistEvent(tr.changes.invert(tr.startState.doc), effects, void 0, selection || tr.startState.selection, none2); 18815 + return new _HistEvent(tr.changes.invert(tr.startState.doc), effects, void 0, selection2 || tr.startState.selection, none2); 18816 18816 } 18817 18817 static selection(selections) { 18818 18818 return new _HistEvent(void 0, none2, void 0, void 0, selections); ··· 18844 18844 } 18845 18845 var none2 = []; 18846 18846 var MaxSelectionsPerEvent = 200; 18847 - function addSelection(branch, selection) { 18847 + function addSelection(branch, selection2) { 18848 18848 if (!branch.length) { 18849 - return [HistEvent.selection([selection])]; 18849 + return [HistEvent.selection([selection2])]; 18850 18850 } else { 18851 18851 let lastEvent = branch[branch.length - 1]; 18852 18852 let sels = lastEvent.selectionsAfter.slice(Math.max(0, lastEvent.selectionsAfter.length - MaxSelectionsPerEvent)); 18853 - if (sels.length && sels[sels.length - 1].eq(selection)) 18853 + if (sels.length && sels[sels.length - 1].eq(selection2)) 18854 18854 return branch; 18855 - sels.push(selection); 18855 + sels.push(selection2); 18856 18856 return updateBranch(branch, branch.length - 1, 1e9, lastEvent.setSelAfter(sels)); 18857 18857 } 18858 18858 } ··· 18909 18909 } 18910 18910 return new _HistoryState(done, none2, time, userEvent); 18911 18911 } 18912 - addSelection(selection, time, userEvent, newGroupDelay) { 18912 + addSelection(selection2, time, userEvent, newGroupDelay) { 18913 18913 let last = this.done.length ? this.done[this.done.length - 1].selectionsAfter : none2; 18914 - if (last.length > 0 && time - this.prevTime < newGroupDelay && userEvent == this.prevUserEvent && userEvent && /^select($|\.)/.test(userEvent) && eqSelectionShape(last[last.length - 1], selection)) 18914 + if (last.length > 0 && time - this.prevTime < newGroupDelay && userEvent == this.prevUserEvent && userEvent && /^select($|\.)/.test(userEvent) && eqSelectionShape(last[last.length - 1], selection2)) 18915 18915 return this; 18916 - return new _HistoryState(addSelection(this.done, selection), this.undone, time, userEvent); 18916 + return new _HistoryState(addSelection(this.done, selection2), this.undone, time, userEvent); 18917 18917 } 18918 18918 addMapping(mapping) { 18919 18919 return new _HistoryState(addMappingToBranch(this.done, mapping), addMappingToBranch(this.undone, mapping), this.prevTime, this.prevUserEvent); ··· 18922 18922 let branch = side == 0 ? this.done : this.undone; 18923 18923 if (branch.length == 0) 18924 18924 return null; 18925 - let event = branch[branch.length - 1], selection = event.selectionsAfter[0] || (event.startSelection ? event.startSelection.map(event.changes.invertedDesc, 1) : state.selection); 18925 + let event = branch[branch.length - 1], selection2 = event.selectionsAfter[0] || (event.startSelection ? event.startSelection.map(event.changes.invertedDesc, 1) : state.selection); 18926 18926 if (onlySelection && event.selectionsAfter.length) { 18927 18927 return state.update({ 18928 18928 selection: event.selectionsAfter[event.selectionsAfter.length - 1], 18929 - annotations: fromHistory.of({ side, rest: popSelection(branch), selection }), 18929 + annotations: fromHistory.of({ side, rest: popSelection(branch), selection: selection2 }), 18930 18930 userEvent: side == 0 ? "select.undo" : "select.redo", 18931 18931 scrollIntoView: true 18932 18932 }); ··· 18940 18940 changes: event.changes, 18941 18941 selection: event.startSelection, 18942 18942 effects: event.effects, 18943 - annotations: fromHistory.of({ side, rest, selection }), 18943 + annotations: fromHistory.of({ side, rest, selection: selection2 }), 18944 18944 filter: false, 18945 18945 userEvent: side == 0 ? "undo" : "redo", 18946 18946 scrollIntoView: true ··· 18959 18959 function updateSel(sel, by) { 18960 18960 return EditorSelection.create(sel.ranges.map(by), sel.mainIndex); 18961 18961 } 18962 - function setSel(state, selection) { 18963 - return state.update({ selection, scrollIntoView: true, userEvent: "select" }); 18962 + function setSel(state, selection2) { 18963 + return state.update({ selection: selection2, scrollIntoView: true, userEvent: "select" }); 18964 18964 } 18965 18965 function moveSel({ state, dispatch }, how) { 18966 - let selection = updateSel(state.selection, how); 18967 - if (selection.eq(state.selection, true)) 18966 + let selection2 = updateSel(state.selection, how); 18967 + if (selection2.eq(state.selection, true)) 18968 18968 return false; 18969 - dispatch(setSel(state, selection)); 18969 + dispatch(setSel(state, selection2)); 18970 18970 return true; 18971 18971 } 18972 18972 function rangeEnd(range, forward) { ··· 19047 19047 } 19048 19048 function cursorByPage(view, forward) { 19049 19049 let page = pageInfo(view); 19050 - let { state } = view, selection = updateSel(state.selection, (range) => { 19050 + let { state } = view, selection2 = updateSel(state.selection, (range) => { 19051 19051 return range.empty ? view.moveVertically(range, forward, page.height) : rangeEnd(range, forward); 19052 19052 }); 19053 - if (selection.eq(state.selection)) 19053 + if (selection2.eq(state.selection)) 19054 19054 return false; 19055 19055 let effect; 19056 19056 if (page.selfScroll) { ··· 19058 19058 let scrollRect = view.scrollDOM.getBoundingClientRect(); 19059 19059 let scrollTop = scrollRect.top + page.marginTop, scrollBottom = scrollRect.bottom - page.marginBottom; 19060 19060 if (startPos && startPos.top > scrollTop && startPos.bottom < scrollBottom) 19061 - effect = EditorView.scrollIntoView(selection.main.head, { y: "start", yMargin: startPos.top - scrollTop }); 19061 + effect = EditorView.scrollIntoView(selection2.main.head, { y: "start", yMargin: startPos.top - scrollTop }); 19062 19062 } 19063 - view.dispatch(setSel(state, selection), { effects: effect }); 19063 + view.dispatch(setSel(state, selection2), { effects: effect }); 19064 19064 return true; 19065 19065 } 19066 19066 var cursorPageUp = (view) => cursorByPage(view, false); ··· 19083 19083 var cursorLineStart = (view) => moveSel(view, (range) => EditorSelection.cursor(view.lineBlockAt(range.head).from, 1)); 19084 19084 var cursorLineEnd = (view) => moveSel(view, (range) => EditorSelection.cursor(view.lineBlockAt(range.head).to, -1)); 19085 19085 function toMatchingBracket(state, dispatch, extend) { 19086 - let found = false, selection = updateSel(state.selection, (range) => { 19086 + let found = false, selection2 = updateSel(state.selection, (range) => { 19087 19087 let matching = matchBrackets(state, range.head, -1) || matchBrackets(state, range.head, 1) || range.head > 0 && matchBrackets(state, range.head - 1, 1) || range.head < state.doc.length && matchBrackets(state, range.head + 1, -1); 19088 19088 if (!matching || !matching.end) 19089 19089 return range; ··· 19093 19093 }); 19094 19094 if (!found) 19095 19095 return false; 19096 - dispatch(setSel(state, selection)); 19096 + dispatch(setSel(state, selection2)); 19097 19097 return true; 19098 19098 } 19099 19099 var cursorMatchingBracket = ({ state, dispatch }) => toMatchingBracket(state, dispatch, false); 19100 19100 function extendSel(target, how) { 19101 - let selection = updateSel(target.state.selection, (range) => { 19101 + let selection2 = updateSel(target.state.selection, (range) => { 19102 19102 let head = how(range); 19103 19103 return EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || void 0); 19104 19104 }); 19105 - if (selection.eq(target.state.selection)) 19105 + if (selection2.eq(target.state.selection)) 19106 19106 return false; 19107 - target.dispatch(setSel(target.state, selection)); 19107 + target.dispatch(setSel(target.state, selection2)); 19108 19108 return true; 19109 19109 } 19110 19110 function selectByChar(view, forward) { ··· 19161 19161 return true; 19162 19162 }; 19163 19163 var selectParentSyntax = ({ state, dispatch }) => { 19164 - let selection = updateSel(state.selection, (range) => { 19164 + let selection2 = updateSel(state.selection, (range) => { 19165 19165 let tree = syntaxTree(state), stack = tree.resolveStack(range.from, 1); 19166 19166 if (range.empty) { 19167 19167 let stackBefore = tree.resolveStack(range.from, -1); ··· 19175 19175 } 19176 19176 return range; 19177 19177 }); 19178 - if (selection.eq(state.selection)) 19178 + if (selection2.eq(state.selection)) 19179 19179 return false; 19180 - dispatch(setSel(state, selection)); 19180 + dispatch(setSel(state, selection2)); 19181 19181 return true; 19182 19182 }; 19183 19183 function addCursorVertically(view, forward) { ··· 19206 19206 var addCursorAbove = (view) => addCursorVertically(view, false); 19207 19207 var addCursorBelow = (view) => addCursorVertically(view, true); 19208 19208 var simplifySelection = ({ state, dispatch }) => { 19209 - let cur2 = state.selection, selection = null; 19209 + let cur2 = state.selection, selection2 = null; 19210 19210 if (cur2.ranges.length > 1) 19211 - selection = EditorSelection.create([cur2.main]); 19211 + selection2 = EditorSelection.create([cur2.main]); 19212 19212 else if (!cur2.main.empty) 19213 - selection = EditorSelection.create([EditorSelection.cursor(cur2.main.head)]); 19214 - if (!selection) 19213 + selection2 = EditorSelection.create([EditorSelection.cursor(cur2.main.head)]); 19214 + if (!selection2) 19215 19215 return false; 19216 - dispatch(setSel(state, selection)); 19216 + dispatch(setSel(state, selection2)); 19217 19217 return true; 19218 19218 }; 19219 19219 function deleteBy(target, by) { ··· 19421 19421 to++; 19422 19422 return { from, to }; 19423 19423 })); 19424 - let selection = updateSel(state.selection, (range) => { 19424 + let selection2 = updateSel(state.selection, (range) => { 19425 19425 let dist2 = void 0; 19426 19426 if (view.lineWrapping) { 19427 19427 let block = view.lineBlockAt(range.head), pos = view.coordsAtPos(range.head, range.assoc || 1); ··· 19430 19430 } 19431 19431 return view.moveVertically(range, true, dist2); 19432 19432 }).map(changes); 19433 - view.dispatch({ changes, selection, scrollIntoView: true, userEvent: "delete.line" }); 19433 + view.dispatch({ changes, selection: selection2, scrollIntoView: true, userEvent: "delete.line" }); 19434 19434 return true; 19435 19435 }; 19436 19436 function isBetweenBrackets(state, pos) { ··· 19913 19913 line2 = line2 * (sign == "-" ? -1 : 1) + startLine.number; 19914 19914 } 19915 19915 let docLine = state.doc.line(Math.max(1, Math.min(state.doc.lines, line2))); 19916 - let selection = EditorSelection.cursor(docLine.from + Math.max(0, Math.min(col, docLine.length))); 19916 + let selection2 = EditorSelection.cursor(docLine.from + Math.max(0, Math.min(col, docLine.length))); 19917 19917 view.dispatch({ 19918 - effects: [close, EditorView.scrollIntoView(selection.from, { y: "center" })], 19919 - selection 19918 + effects: [close, EditorView.scrollIntoView(selection2.from, { y: "center" })], 19919 + selection: selection2 19920 19920 }); 19921 19921 }); 19922 19922 return true; ··· 19989 19989 } 19990 19990 let deco = []; 19991 19991 for (let part of view.visibleRanges) { 19992 - let cursor = new SearchCursor(state.doc, query, part.from, part.to); 19993 - while (!cursor.next().done) { 19994 - let { from, to } = cursor.value; 19992 + let cursor2 = new SearchCursor(state.doc, query, part.from, part.to); 19993 + while (!cursor2.next().done) { 19994 + let { from, to } = cursor2.value; 19995 19995 if (!check || insideWordBoundaries(check, state, from, to)) { 19996 19996 if (range.empty && from <= range.from && to >= range.to) 19997 19997 deco.push(mainMatchDeco.range(from, to)); ··· 20012 20012 ".cm-searchMatch .cm-selectionMatch": { backgroundColor: "transparent" } 20013 20013 }); 20014 20014 var selectWord = ({ state, dispatch }) => { 20015 - let { selection } = state; 20016 - let newSel = EditorSelection.create(selection.ranges.map((range) => state.wordAt(range.head) || EditorSelection.cursor(range.head)), selection.mainIndex); 20017 - if (newSel.eq(selection)) 20015 + let { selection: selection2 } = state; 20016 + let newSel = EditorSelection.create(selection2.ranges.map((range) => state.wordAt(range.head) || EditorSelection.cursor(range.head)), selection2.mainIndex); 20017 + if (newSel.eq(selection2)) 20018 20018 return false; 20019 20019 dispatch(state.update({ selection: newSel })); 20020 20020 return true; ··· 20022 20022 function findNextOccurrence(state, query) { 20023 20023 let { main, ranges } = state.selection; 20024 20024 let word = state.wordAt(main.head), fullWord = word && word.from == main.from && word.to == main.to; 20025 - for (let cycled = false, cursor = new SearchCursor(state.doc, query, ranges[ranges.length - 1].to); ; ) { 20026 - cursor.next(); 20027 - if (cursor.done) { 20025 + for (let cycled = false, cursor2 = new SearchCursor(state.doc, query, ranges[ranges.length - 1].to); ; ) { 20026 + cursor2.next(); 20027 + if (cursor2.done) { 20028 20028 if (cycled) 20029 20029 return null; 20030 - cursor = new SearchCursor(state.doc, query, 0, Math.max(0, ranges[ranges.length - 1].from - 1)); 20030 + cursor2 = new SearchCursor(state.doc, query, 0, Math.max(0, ranges[ranges.length - 1].from - 1)); 20031 20031 cycled = true; 20032 20032 } else { 20033 - if (cycled && ranges.some((r) => r.from == cursor.value.from)) 20033 + if (cycled && ranges.some((r) => r.from == cursor2.value.from)) 20034 20034 continue; 20035 20035 if (fullWord) { 20036 - let word2 = state.wordAt(cursor.value.from); 20037 - if (!word2 || word2.from != cursor.value.from || word2.to != cursor.value.to) 20036 + let word2 = state.wordAt(cursor2.value.from); 20037 + if (!word2 || word2.from != cursor2.value.from || word2.to != cursor2.value.to) 20038 20038 continue; 20039 20039 } 20040 - return cursor.value; 20040 + return cursor2.value; 20041 20041 } 20042 20042 } 20043 20043 } ··· 20149 20149 super(spec); 20150 20150 } 20151 20151 nextMatch(state, curFrom, curTo) { 20152 - let cursor = stringCursor(this.spec, state, curTo, state.doc.length).nextOverlapping(); 20153 - if (cursor.done) { 20152 + let cursor2 = stringCursor(this.spec, state, curTo, state.doc.length).nextOverlapping(); 20153 + if (cursor2.done) { 20154 20154 let end = Math.min(state.doc.length, curFrom + this.spec.unquoted.length); 20155 - cursor = stringCursor(this.spec, state, 0, end).nextOverlapping(); 20155 + cursor2 = stringCursor(this.spec, state, 0, end).nextOverlapping(); 20156 20156 } 20157 - return cursor.done || cursor.value.from == curFrom && cursor.value.to == curTo ? null : cursor.value; 20157 + return cursor2.done || cursor2.value.from == curFrom && cursor2.value.to == curTo ? null : cursor2.value; 20158 20158 } 20159 20159 // Searching in reverse is, rather than implementing an inverted search 20160 20160 // cursor, done by scanning chunk after chunk forward. 20161 20161 prevMatchInRange(state, from, to) { 20162 20162 for (let pos = to; ; ) { 20163 20163 let start = Math.max(from, pos - 1e4 - this.spec.unquoted.length); 20164 - let cursor = stringCursor(this.spec, state, start, pos), range = null; 20165 - while (!cursor.nextOverlapping().done) 20166 - range = cursor.value; 20164 + let cursor2 = stringCursor(this.spec, state, start, pos), range = null; 20165 + while (!cursor2.nextOverlapping().done) 20166 + range = cursor2.value; 20167 20167 if (range) 20168 20168 return range; 20169 20169 if (start == from) ··· 20181 20181 return this.spec.unquote(this.spec.replace); 20182 20182 } 20183 20183 matchAll(state, limit) { 20184 - let cursor = stringCursor(this.spec, state, 0, state.doc.length), ranges = []; 20185 - while (!cursor.next().done) { 20184 + let cursor2 = stringCursor(this.spec, state, 0, state.doc.length), ranges = []; 20185 + while (!cursor2.next().done) { 20186 20186 if (ranges.length >= limit) 20187 20187 return null; 20188 - ranges.push(cursor.value); 20188 + ranges.push(cursor2.value); 20189 20189 } 20190 20190 return ranges; 20191 20191 } 20192 20192 highlight(state, from, to, add2) { 20193 - let cursor = stringCursor(this.spec, state, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, state.doc.length)); 20194 - while (!cursor.next().done) 20195 - add2(cursor.value.from, cursor.value.to); 20193 + let cursor2 = stringCursor(this.spec, state, Math.max(0, from - this.spec.unquoted.length), Math.min(to + this.spec.unquoted.length, state.doc.length)); 20194 + while (!cursor2.next().done) 20195 + add2(cursor2.value.from, cursor2.value.to); 20196 20196 } 20197 20197 }; 20198 20198 function wrapRegexpTest(test, state, inner) { ··· 20219 20219 } 20220 20220 var RegExpQuery = class extends QueryType2 { 20221 20221 nextMatch(state, curFrom, curTo) { 20222 - let cursor = regexpCursor(this.spec, state, curTo, state.doc.length).next(); 20223 - if (cursor.done) 20224 - cursor = regexpCursor(this.spec, state, 0, curFrom).next(); 20225 - return cursor.done ? null : cursor.value; 20222 + let cursor2 = regexpCursor(this.spec, state, curTo, state.doc.length).next(); 20223 + if (cursor2.done) 20224 + cursor2 = regexpCursor(this.spec, state, 0, curFrom).next(); 20225 + return cursor2.done ? null : cursor2.value; 20226 20226 } 20227 20227 prevMatchInRange(state, from, to) { 20228 20228 for (let size = 1; ; size++) { ··· 20231 20231 to - size * 1e4 20232 20232 /* FindPrev.ChunkSize */ 20233 20233 ); 20234 - let cursor = regexpCursor(this.spec, state, start, to), range = null; 20235 - while (!cursor.next().done) 20236 - range = cursor.value; 20234 + let cursor2 = regexpCursor(this.spec, state, start, to), range = null; 20235 + while (!cursor2.next().done) 20236 + range = cursor2.value; 20237 20237 if (range && (start == from || range.from > start + 10)) 20238 20238 return range; 20239 20239 if (start == from) ··· 20258 20258 }); 20259 20259 } 20260 20260 matchAll(state, limit) { 20261 - let cursor = regexpCursor(this.spec, state, 0, state.doc.length), ranges = []; 20262 - while (!cursor.next().done) { 20261 + let cursor2 = regexpCursor(this.spec, state, 0, state.doc.length), ranges = []; 20262 + while (!cursor2.next().done) { 20263 20263 if (ranges.length >= limit) 20264 20264 return null; 20265 - ranges.push(cursor.value); 20265 + ranges.push(cursor2.value); 20266 20266 } 20267 20267 return ranges; 20268 20268 } 20269 20269 highlight(state, from, to, add2) { 20270 - let cursor = regexpCursor(this.spec, state, Math.max( 20270 + let cursor2 = regexpCursor(this.spec, state, Math.max( 20271 20271 0, 20272 20272 from - 250 20273 20273 /* RegExp.HighlightMargin */ 20274 20274 ), Math.min(to + 250, state.doc.length)); 20275 - while (!cursor.next().done) 20276 - add2(cursor.value.from, cursor.value.to); 20275 + while (!cursor2.next().done) 20276 + add2(cursor2.value.from, cursor2.value.to); 20277 20277 } 20278 20278 }; 20279 20279 var setSearchQuery = /* @__PURE__ */ StateEffect.define(); ··· 20341 20341 let next = query.nextMatch(view.state, to, to); 20342 20342 if (!next) 20343 20343 return false; 20344 - let selection = EditorSelection.single(next.from, next.to); 20344 + let selection2 = EditorSelection.single(next.from, next.to); 20345 20345 let config2 = view.state.facet(searchConfigFacet); 20346 20346 view.dispatch({ 20347 - selection, 20348 - effects: [announceMatch(view, next), config2.scrollToMatch(selection.main, view)], 20347 + selection: selection2, 20348 + effects: [announceMatch(view, next), config2.scrollToMatch(selection2.main, view)], 20349 20349 userEvent: "select.search" 20350 20350 }); 20351 20351 selectSearchInput(view); ··· 20356 20356 let prev = query.prevMatch(state, from, from); 20357 20357 if (!prev) 20358 20358 return false; 20359 - let selection = EditorSelection.single(prev.from, prev.to); 20359 + let selection2 = EditorSelection.single(prev.from, prev.to); 20360 20360 let config2 = view.state.facet(searchConfigFacet); 20361 20361 view.dispatch({ 20362 - selection, 20363 - effects: [announceMatch(view, prev), config2.scrollToMatch(selection.main, view)], 20362 + selection: selection2, 20363 + effects: [announceMatch(view, prev), config2.scrollToMatch(selection2.main, view)], 20364 20364 userEvent: "select.search" 20365 20365 }); 20366 20366 selectSearchInput(view); ··· 20403 20403 if (!match) 20404 20404 return false; 20405 20405 let next = match; 20406 - let changes = [], selection, replacement; 20406 + let changes = [], selection2, replacement; 20407 20407 let effects = []; 20408 20408 if (next.from == from && next.to == to) { 20409 20409 replacement = state.toText(query.getReplacement(next)); ··· 20413 20413 } 20414 20414 let changeSet = view.state.changes(changes); 20415 20415 if (next) { 20416 - selection = EditorSelection.single(next.from, next.to).map(changeSet); 20416 + selection2 = EditorSelection.single(next.from, next.to).map(changeSet); 20417 20417 effects.push(announceMatch(view, next)); 20418 - effects.push(state.facet(searchConfigFacet).scrollToMatch(selection.main, view)); 20418 + effects.push(state.facet(searchConfigFacet).scrollToMatch(selection2.main, view)); 20419 20419 } 20420 20420 view.dispatch({ 20421 20421 changes: changeSet, 20422 - selection, 20422 + selection: selection2, 20423 20423 effects, 20424 20424 userEvent: "input.replace" 20425 20425 }); ··· 22965 22965 if (this.selectedIndex < 0) 22966 22966 return; 22967 22967 let field = this.view.state.field(lintState); 22968 - let selection = findDiagnostic(field.diagnostics, this.items[selectedIndex].diagnostic); 22969 - if (!selection) 22968 + let selection2 = findDiagnostic(field.diagnostics, this.items[selectedIndex].diagnostic); 22969 + if (!selection2) 22970 22970 return; 22971 22971 this.view.dispatch({ 22972 - selection: { anchor: selection.from, head: selection.to }, 22972 + selection: { anchor: selection2.from, head: selection2.to }, 22973 22973 scrollIntoView: true, 22974 - effects: movePanelSelection.of(selection) 22974 + effects: movePanelSelection.of(selection2) 22975 22975 }); 22976 22976 } 22977 22977 static open(view) { ··· 26102 26102 var verbose = typeof process != "undefined" && process.env && /\bparse\b/.test(process.env.LOG); 26103 26103 var stackIDs = null; 26104 26104 function cutAt(tree, pos, side) { 26105 - let cursor = tree.cursor(IterMode.IncludeAnonymous); 26106 - cursor.moveTo(pos); 26105 + let cursor2 = tree.cursor(IterMode.IncludeAnonymous); 26106 + cursor2.moveTo(pos); 26107 26107 for (; ; ) { 26108 - if (!(side < 0 ? cursor.childBefore(pos) : cursor.childAfter(pos))) 26108 + if (!(side < 0 ? cursor2.childBefore(pos) : cursor2.childAfter(pos))) 26109 26109 for (; ; ) { 26110 - if ((side < 0 ? cursor.to < pos : cursor.from > pos) && !cursor.type.isError) 26110 + if ((side < 0 ? cursor2.to < pos : cursor2.from > pos) && !cursor2.type.isError) 26111 26111 return side < 0 ? Math.max(0, Math.min( 26112 - cursor.to - 1, 26112 + cursor2.to - 1, 26113 26113 pos - 25 26114 26114 /* Lookahead.Margin */ 26115 26115 )) : Math.min(tree.length, Math.max( 26116 - cursor.from + 1, 26116 + cursor2.from + 1, 26117 26117 pos + 25 26118 26118 /* Lookahead.Margin */ 26119 26119 )); 26120 - if (side < 0 ? cursor.prevSibling() : cursor.nextSibling()) 26120 + if (side < 0 ? cursor2.prevSibling() : cursor2.nextSibling()) 26121 26121 break; 26122 - if (!cursor.parent()) 26122 + if (!cursor2.parent()) 26123 26123 return side < 0 ? 0 : tree.length; 26124 26124 } 26125 26125 } ··· 28252 28252 let known = VariablesByNode.get(node); 28253 28253 if (known) 28254 28254 return known; 28255 - let result = [], seen = /* @__PURE__ */ new Set(), cursor = node.cursor(IterMode.IncludeAnonymous); 28256 - if (cursor.firstChild()) 28255 + let result = [], seen = /* @__PURE__ */ new Set(), cursor2 = node.cursor(IterMode.IncludeAnonymous); 28256 + if (cursor2.firstChild()) 28257 28257 do { 28258 - for (let option of variableNames(doc2, cursor.node, isVariable)) 28258 + for (let option of variableNames(doc2, cursor2.node, isVariable)) 28259 28259 if (!seen.has(option.label)) { 28260 28260 seen.add(option.label); 28261 28261 result.push(option); 28262 28262 } 28263 - } while (cursor.nextSibling()); 28263 + } while (cursor2.nextSibling()); 28264 28264 VariablesByNode.set(node, result); 28265 28265 return result; 28266 28266 } else { ··· 30006 30006 return true; 30007 30007 } 30008 30008 }); 30009 + 30010 + // node_modules/@codemirror/theme-one-dark/dist/index.js 30011 + var chalky = "#e5c07b"; 30012 + var coral = "#e06c75"; 30013 + var cyan = "#56b6c2"; 30014 + var invalid = "#ffffff"; 30015 + var ivory = "#abb2bf"; 30016 + var stone = "#7d8799"; 30017 + var malibu = "#61afef"; 30018 + var sage = "#98c379"; 30019 + var whiskey = "#d19a66"; 30020 + var violet = "#c678dd"; 30021 + var darkBackground = "#21252b"; 30022 + var highlightBackground = "#2c313a"; 30023 + var background = "#282c34"; 30024 + var tooltipBackground = "#353a42"; 30025 + var selection = "#3E4451"; 30026 + var cursor = "#528bff"; 30027 + var oneDarkTheme = /* @__PURE__ */ EditorView.theme({ 30028 + "&": { 30029 + color: ivory, 30030 + backgroundColor: background 30031 + }, 30032 + ".cm-content": { 30033 + caretColor: cursor 30034 + }, 30035 + ".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor }, 30036 + "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": { backgroundColor: selection }, 30037 + ".cm-panels": { backgroundColor: darkBackground, color: ivory }, 30038 + ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, 30039 + ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" }, 30040 + ".cm-searchMatch": { 30041 + backgroundColor: "#72a1ff59", 30042 + outline: "1px solid #457dff" 30043 + }, 30044 + ".cm-searchMatch.cm-searchMatch-selected": { 30045 + backgroundColor: "#6199ff2f" 30046 + }, 30047 + ".cm-activeLine": { backgroundColor: "#6699ff0b" }, 30048 + ".cm-selectionMatch": { backgroundColor: "#aafe661a" }, 30049 + "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { 30050 + backgroundColor: "#bad0f847" 30051 + }, 30052 + ".cm-gutters": { 30053 + backgroundColor: background, 30054 + color: stone, 30055 + border: "none" 30056 + }, 30057 + ".cm-activeLineGutter": { 30058 + backgroundColor: highlightBackground 30059 + }, 30060 + ".cm-foldPlaceholder": { 30061 + backgroundColor: "transparent", 30062 + border: "none", 30063 + color: "#ddd" 30064 + }, 30065 + ".cm-tooltip": { 30066 + border: "none", 30067 + backgroundColor: tooltipBackground 30068 + }, 30069 + ".cm-tooltip .cm-tooltip-arrow:before": { 30070 + borderTopColor: "transparent", 30071 + borderBottomColor: "transparent" 30072 + }, 30073 + ".cm-tooltip .cm-tooltip-arrow:after": { 30074 + borderTopColor: tooltipBackground, 30075 + borderBottomColor: tooltipBackground 30076 + }, 30077 + ".cm-tooltip-autocomplete": { 30078 + "& > ul > li[aria-selected]": { 30079 + backgroundColor: highlightBackground, 30080 + color: ivory 30081 + } 30082 + } 30083 + }, { dark: true }); 30084 + var oneDarkHighlightStyle = /* @__PURE__ */ HighlightStyle.define([ 30085 + { 30086 + tag: tags.keyword, 30087 + color: violet 30088 + }, 30089 + { 30090 + tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName], 30091 + color: coral 30092 + }, 30093 + { 30094 + tag: [/* @__PURE__ */ tags.function(tags.variableName), tags.labelName], 30095 + color: malibu 30096 + }, 30097 + { 30098 + tag: [tags.color, /* @__PURE__ */ tags.constant(tags.name), /* @__PURE__ */ tags.standard(tags.name)], 30099 + color: whiskey 30100 + }, 30101 + { 30102 + tag: [/* @__PURE__ */ tags.definition(tags.name), tags.separator], 30103 + color: ivory 30104 + }, 30105 + { 30106 + tag: [tags.typeName, tags.className, tags.number, tags.changed, tags.annotation, tags.modifier, tags.self, tags.namespace], 30107 + color: chalky 30108 + }, 30109 + { 30110 + tag: [tags.operator, tags.operatorKeyword, tags.url, tags.escape, tags.regexp, tags.link, /* @__PURE__ */ tags.special(tags.string)], 30111 + color: cyan 30112 + }, 30113 + { 30114 + tag: [tags.meta, tags.comment], 30115 + color: stone 30116 + }, 30117 + { 30118 + tag: tags.strong, 30119 + fontWeight: "bold" 30120 + }, 30121 + { 30122 + tag: tags.emphasis, 30123 + fontStyle: "italic" 30124 + }, 30125 + { 30126 + tag: tags.strikethrough, 30127 + textDecoration: "line-through" 30128 + }, 30129 + { 30130 + tag: tags.link, 30131 + color: stone, 30132 + textDecoration: "underline" 30133 + }, 30134 + { 30135 + tag: tags.heading, 30136 + fontWeight: "bold", 30137 + color: coral 30138 + }, 30139 + { 30140 + tag: [tags.atom, tags.bool, /* @__PURE__ */ tags.special(tags.variableName)], 30141 + color: whiskey 30142 + }, 30143 + { 30144 + tag: [tags.processingInstruction, tags.string, tags.inserted], 30145 + color: sage 30146 + }, 30147 + { 30148 + tag: tags.invalid, 30149 + color: invalid 30150 + } 30151 + ]); 30152 + var oneDark = [oneDarkTheme, /* @__PURE__ */ syntaxHighlighting(oneDarkHighlightStyle)]; 30009 30153 export { 30154 + Compartment, 30010 30155 EditorView, 30011 30156 basicSetup, 30012 - markdown 30157 + markdown, 30158 + oneDark 30013 30159 };
+26
templates/base.html
··· 7 7 <title>{{.Title}} — MarkdownHub</title> 8 8 <link rel="stylesheet" href="/static/css/style.css"> 9 9 {{block "head" .}}{{end}} 10 + <script> 11 + (function() { 12 + var stored = localStorage.getItem('theme'); 13 + if (stored) document.documentElement.setAttribute('data-theme', stored); 14 + })(); 15 + </script> 10 16 </head> 11 17 <body> 12 18 <nav class="navbar"> ··· 22 28 <a href="/auth/login">Log in</a> 23 29 <a href="/auth/register" class="btn btn-sm">Sign up</a> 24 30 {{end}} 31 + <button id="theme-toggle" class="btn-link" aria-label="Toggle dark mode" onclick="toggleTheme()" style="font-size:1.1rem;padding:0.25rem">☀</button> 25 32 </div> 26 33 </nav> 27 34 <main> ··· 31 38 {{block "content" .}}{{end}} 32 39 </main> 33 40 {{block "scripts" .}}{{end}} 41 + <script> 42 + function getEffectiveTheme() { 43 + var stored = localStorage.getItem('theme'); 44 + if (stored) return stored; 45 + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; 46 + } 47 + function applyThemeIcon(theme) { 48 + document.getElementById('theme-toggle').textContent = theme === 'dark' ? '☀' : '🌙'; 49 + } 50 + function toggleTheme() { 51 + var current = getEffectiveTheme(); 52 + var next = current === 'dark' ? 'light' : 'dark'; 53 + localStorage.setItem('theme', next); 54 + document.documentElement.setAttribute('data-theme', next); 55 + applyThemeIcon(next); 56 + if (window.__cmSetTheme) window.__cmSetTheme(next); 57 + } 58 + applyThemeIcon(getEffectiveTheme()); 59 + </script> 34 60 </body> 35 61 </html> 36 62 {{end}}
+23 -6
templates/editor.html
··· 35 35 36 36 {{define "scripts"}} 37 37 <script type="module"> 38 - import {EditorView, basicSetup, markdown} from '/static/vendor/editor.js'; 38 + import {EditorView, basicSetup, markdown, oneDark, Compartment} from '/static/vendor/editor.js'; 39 39 40 40 const textarea = document.getElementById('editor-textarea'); 41 41 const previewEl = document.getElementById('preview'); ··· 44 44 45 45 let autoSaveTimer = null; 46 46 47 + function isDark() { 48 + var stored = localStorage.getItem('theme'); 49 + if (stored) return stored === 'dark'; 50 + return window.matchMedia('(prefers-color-scheme: dark)').matches; 51 + } 52 + 53 + const baseTheme = EditorView.theme({ 54 + '&': {height: '100%', fontSize: '14px'}, 55 + '.cm-scroller': {overflow: 'auto'}, 56 + '.cm-content': {fontFamily: '"JetBrains Mono", "Fira Code", monospace'}, 57 + }); 58 + 59 + const darkCompartment = new Compartment(); 60 + 47 61 const view = new EditorView({ 48 62 doc: textarea.value, 49 63 extensions: [ 50 64 basicSetup, 51 65 markdown(), 66 + baseTheme, 67 + darkCompartment.of(isDark() ? oneDark : []), 52 68 EditorView.updateListener.of((update) => { 53 69 if (update.docChanged) { 54 70 updatePreview(update.state.doc.toString()); 55 71 scheduleAutoSave(update.state.doc.toString()); 56 72 } 57 73 }), 58 - EditorView.theme({ 59 - '&': {height: '100%', fontSize: '14px'}, 60 - '.cm-scroller': {overflow: 'auto'}, 61 - '.cm-content': {fontFamily: '"JetBrains Mono", "Fira Code", monospace'}, 62 - }), 63 74 ], 64 75 parent: document.getElementById('editor'), 65 76 }); 77 + 78 + window.__cmSetTheme = function(theme) { 79 + view.dispatch({ 80 + effects: darkCompartment.reconfigure(theme === 'dark' ? oneDark : []), 81 + }); 82 + }; 66 83 67 84 // Initial render 68 85 updatePreview(textarea.value);