Use CodeMirror editor
This commit is contained in:
@@ -8,3 +8,6 @@ datascape
|
|||||||
*.exe
|
*.exe
|
||||||
bin/
|
bin/
|
||||||
companion/datascape-companion-*
|
companion/datascape-companion-*
|
||||||
|
|
||||||
|
# Editor build tooling deps (the built bundle is committed; node_modules is not)
|
||||||
|
editor-build/node_modules/
|
||||||
|
|||||||
@@ -16,12 +16,30 @@ go build .
|
|||||||
make deploy
|
make deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Editor bundle (the one build-pipeline exception)
|
||||||
|
|
||||||
|
The page editor uses CodeMirror 6, vendored as a single pre-built IIFE at
|
||||||
|
`assets/editor/vendor/codemirror.bundle.js` and embedded via `embed.FS`. This is
|
||||||
|
the **only** deliberate exception to the "no build pipeline" rule below — it is a
|
||||||
|
one-time, committed artifact, not a runtime build. `go build` / `make deploy`
|
||||||
|
never touch Node and only consume the committed bundle.
|
||||||
|
|
||||||
|
Regenerate the bundle **only** when upgrading the `@codemirror/*` versions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# bump versions in editor-build/package.json first, then:
|
||||||
|
make editor # runs `npm ci && npm run build` in editor-build/, rewrites the vendored bundle
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit the regenerated `codemirror.bundle.js` and the updated
|
||||||
|
`editor-build/package-lock.json`. `editor-build/node_modules/` is gitignored.
|
||||||
|
|
||||||
## HTTP API Surface
|
## HTTP API Surface
|
||||||
|
|
||||||
| Method | Path | Behaviour |
|
| Method | Path | Behaviour |
|
||||||
|--------|------|-----------|
|
|--------|------|-----------|
|
||||||
| GET | `/{path}/` | If folder exists: render `index.md` + list contents. If not: show empty create prompt. |
|
| GET | `/{path}/` | If folder exists: render `index.md` + list contents. If not: show empty create prompt. |
|
||||||
| GET | `/{path}/?edit` | Mobile-friendly editor with `index.md` content in a textarea |
|
| GET | `/{path}/?edit` | CodeMirror 6 editor initialized with `index.md` content |
|
||||||
| POST | `/{path}` | Write `index.md` to disk; creates the folder if it does not exist yet |
|
| POST | `/{path}` | Write `index.md` to disk; creates the folder if it does not exist yet |
|
||||||
|
|
||||||
Non-existent paths without a trailing slash redirect to the slash form (GET only — POSTs
|
Non-existent paths without a trailing slash redirect to the slash form (GET only — POSTs
|
||||||
@@ -49,7 +67,7 @@ Prefer separate, human-readable `.html` files over inlined HTML strings in Go. E
|
|||||||
|
|
||||||
## Frontend Rules
|
## Frontend Rules
|
||||||
|
|
||||||
- Vanilla JS only — no frameworks, no build pipeline
|
- Vanilla JS only — no frameworks, no build pipeline (the single exception is the vendored CodeMirror editor bundle; see Build & Deploy)
|
||||||
- Each feature gets its own JS file; global behaviour goes in `global-shortcuts.js`
|
- Each feature gets its own JS file; global behaviour goes in `global-shortcuts.js`
|
||||||
- Do not inline JS in templates or merge unrelated features into one file
|
- Do not inline JS in templates or merge unrelated features into one file
|
||||||
- `ALT+SHIFT` is the modifier for all keyboard shortcuts — do not introduce others
|
- `ALT+SHIFT` is the modifier for all keyboard shortcuts — do not introduce others
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ COMPANION_WIN := companion/datascape-companion-windows-amd64.exe
|
|||||||
COMPANION_LIN := companion/datascape-companion-linux-amd64
|
COMPANION_LIN := companion/datascape-companion-linux-amd64
|
||||||
COMPANION_SRCS := $(wildcard cmd/companion/*.go) $(wildcard cmd/companion/*.html) go.mod go.sum
|
COMPANION_SRCS := $(wildcard cmd/companion/*.go) $(wildcard cmd/companion/*.html) go.mod go.sum
|
||||||
|
|
||||||
.PHONY: deploy companion companion-windows companion-linux companion-release
|
EDITOR_BUNDLE := assets/editor/vendor/codemirror.bundle.js
|
||||||
|
EDITOR_SRCS := $(wildcard editor-build/*.js) editor-build/package.json editor-build/package-lock.json
|
||||||
|
|
||||||
|
.PHONY: deploy companion companion-windows companion-linux companion-release editor
|
||||||
|
|
||||||
# Cross-compiled companion artifacts the wiki binary embeds. Both must exist
|
# Cross-compiled companion artifacts the wiki binary embeds. Both must exist
|
||||||
# before `go build .` so embed.FS picks them up.
|
# before `go build .` so embed.FS picks them up.
|
||||||
@@ -24,6 +27,15 @@ companion:
|
|||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
go build -o bin/ ./cmd/companion
|
go build -o bin/ ./cmd/companion
|
||||||
|
|
||||||
|
# Regenerate the vendored CodeMirror bundle. One-time/dev-only step: run after
|
||||||
|
# upgrading the @codemirror/* versions in editor-build/package.json. The built
|
||||||
|
# artifact ($(EDITOR_BUNDLE)) is committed; `go build` only consumes it and
|
||||||
|
# never runs Node.
|
||||||
|
editor: $(EDITOR_BUNDLE)
|
||||||
|
|
||||||
|
$(EDITOR_BUNDLE): $(EDITOR_SRCS)
|
||||||
|
cd editor-build && npm ci && npm run build
|
||||||
|
|
||||||
deploy: companion-release
|
deploy: companion-release
|
||||||
GOOS=linux GOARCH=arm GOARM=7 go build -o datascape-arm .
|
GOOS=linux GOARCH=arm GOARM=7 go build -o datascape-arm .
|
||||||
ssh $(NAS) 'kill $$(cat /share/homes/luxick/.local/bin/datascape.pid) 2>/dev/null; rm -f /share/homes/luxick/.local/bin/datascape.pid'
|
ssh $(NAS) 'kill $$(cat /share/homes/luxick/.local/bin/datascape.pid) 2>/dev/null; rm -f /share/homes/luxick/.local/bin/datascape.pid'
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
window.EditorLists = (function () {
|
|
||||||
|
|
||||||
function detectListPrefix(lineText) {
|
|
||||||
var m;
|
|
||||||
m = lineText.match(/^(\s*)(- \[[ x]\] )/);
|
|
||||||
if (m) return { indent: m[1], prefix: m[2], type: 'task' };
|
|
||||||
m = lineText.match(/^(\s*)([-*+] )/);
|
|
||||||
if (m) return { indent: m[1], prefix: m[2], type: 'unordered' };
|
|
||||||
m = lineText.match(/^(\s*)(\d+)\. /);
|
|
||||||
if (m) return { indent: m[1], prefix: m[2] + '. ', type: 'ordered', num: parseInt(m[2], 10) };
|
|
||||||
m = lineText.match(/^(\s*)(> )/);
|
|
||||||
if (m) return { indent: m[1], prefix: m[2], type: 'blockquote' };
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function continuationPrefix(info) {
|
|
||||||
if (info.type === 'task') return info.indent + '- [ ] ';
|
|
||||||
if (info.type === 'ordered') return info.indent + (info.num + 1) + '. ';
|
|
||||||
return info.indent + info.prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renumberOrderedList(text, fromLineIndex, indent, startNum) {
|
|
||||||
var lines = text.split('\n');
|
|
||||||
var re = new RegExp('^' + indent.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '(\\d+)\\. ');
|
|
||||||
var num = startNum !== undefined ? startNum : null;
|
|
||||||
for (var i = fromLineIndex; i < lines.length; i++) {
|
|
||||||
var m = lines[i].match(re);
|
|
||||||
if (!m) break;
|
|
||||||
if (num === null) num = parseInt(m[1], 10);
|
|
||||||
var newNumStr = String(num);
|
|
||||||
if (m[1] !== newNumStr) {
|
|
||||||
lines[i] = indent + newNumStr + '. ' + lines[i].slice(m[0].length);
|
|
||||||
}
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
return lines.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleEnterKey(text, cursorPos) {
|
|
||||||
var before = text.slice(0, cursorPos);
|
|
||||||
var after = text.slice(cursorPos);
|
|
||||||
var lineStart = before.lastIndexOf('\n') + 1;
|
|
||||||
var lineEnd = text.indexOf('\n', cursorPos);
|
|
||||||
if (lineEnd === -1) lineEnd = text.length;
|
|
||||||
var fullLine = text.slice(lineStart, lineEnd);
|
|
||||||
var info = detectListPrefix(fullLine);
|
|
||||||
if (!info) return null;
|
|
||||||
|
|
||||||
var contentAfterPrefix = fullLine.slice(info.indent.length + info.prefix.length);
|
|
||||||
if (contentAfterPrefix.trim() === '') {
|
|
||||||
var newText = text.slice(0, lineStart) + '\n' + after;
|
|
||||||
var newCursor = lineStart + 1;
|
|
||||||
if (info.type === 'ordered') {
|
|
||||||
var lineIndex = text.slice(0, lineStart).split('\n').length;
|
|
||||||
newText = renumberOrderedList(newText, lineIndex, info.indent);
|
|
||||||
}
|
|
||||||
return { text: newText, cursor: newCursor };
|
|
||||||
}
|
|
||||||
|
|
||||||
var cont = continuationPrefix(info);
|
|
||||||
var newText = before + '\n' + cont + after;
|
|
||||||
var newCursor = cursorPos + 1 + cont.length;
|
|
||||||
if (info.type === 'ordered') {
|
|
||||||
var insertedLineIndex = before.split('\n').length;
|
|
||||||
newText = renumberOrderedList(newText, insertedLineIndex, info.indent);
|
|
||||||
}
|
|
||||||
return { text: newText, cursor: newCursor };
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteOrderedLine(text, cursorPos) {
|
|
||||||
var lineStart = text.lastIndexOf('\n', cursorPos - 1) + 1;
|
|
||||||
var lineEnd = text.indexOf('\n', cursorPos);
|
|
||||||
if (lineEnd === -1) lineEnd = text.length;
|
|
||||||
var fullLine = text.slice(lineStart, lineEnd);
|
|
||||||
var info = detectListPrefix(fullLine);
|
|
||||||
if (!info || info.type !== 'ordered') return null;
|
|
||||||
|
|
||||||
var newText = text.slice(0, lineStart) + text.slice(lineEnd === text.length ? lineEnd : lineEnd + 1);
|
|
||||||
var newCursor = lineStart;
|
|
||||||
var fromLineIndex = text.slice(0, lineStart).split('\n').length - 1;
|
|
||||||
newText = renumberOrderedList(newText, fromLineIndex, info.indent, info.num);
|
|
||||||
return { text: newText, cursor: Math.min(newCursor, newText.length) };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { handleEnterKey: handleEnterKey, deleteOrderedLine: deleteOrderedLine };
|
|
||||||
})();
|
|
||||||
@@ -71,11 +71,13 @@
|
|||||||
<span class="toolbar-sep"></span>
|
<span class="toolbar-sep"></span>
|
||||||
<button type="button" class="btn btn-tool" data-action="wide" data-key="Z" title="Toggle wide mode (Z)">⇔</button>
|
<button type="button" class="btn btn-tool" data-action="wide" data-key="Z" title="Toggle wide mode (Z)">⇔</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea class="input editor-textarea" name="content" id="editor" autofocus>{{.RawContent}}</textarea>
|
<div id="editor" class="editor-cm"></div>
|
||||||
|
<textarea name="content" id="editor-content" hidden>{{.RawContent}}</textarea>
|
||||||
</form>
|
</form>
|
||||||
<script src="/_/editor/lists.js"></script>
|
<script src="/_/editor/vendor/codemirror.bundle.js"></script>
|
||||||
<script src="/_/editor/tables.js"></script>
|
<script src="/_/editor/tables.js"></script>
|
||||||
<script src="/_/editor/dates.js"></script>
|
<script src="/_/editor/dates.js"></script>
|
||||||
<script src="/_/editor/movie.js"></script>
|
<script src="/_/editor/movie.js"></script>
|
||||||
|
<script src="/_/editor/wikicomplete.js"></script>
|
||||||
<script src="/_/editor/main.js"></script>
|
<script src="/_/editor/main.js"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
+150
-160
@@ -1,145 +1,147 @@
|
|||||||
(function () {
|
(function () {
|
||||||
var textarea = document.getElementById('editor');
|
var mount = document.getElementById('editor');
|
||||||
if (!textarea) return;
|
var hidden = document.getElementById('editor-content');
|
||||||
|
if (!mount || !hidden || !window.CM) return;
|
||||||
|
|
||||||
var form = textarea.closest('form');
|
var form = hidden.closest('form');
|
||||||
|
|
||||||
// --- DOM helpers ---
|
|
||||||
|
|
||||||
// Route every edit through execCommand so the browser's native undo/redo
|
|
||||||
// stack is preserved. Direct assignment to textarea.value would wipe it.
|
|
||||||
function replaceRange(start, end, text) {
|
|
||||||
textarea.focus();
|
|
||||||
textarea.selectionStart = start;
|
|
||||||
textarea.selectionEnd = end;
|
|
||||||
document.execCommand('insertText', false, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrap(before, after, placeholder) {
|
|
||||||
var start = textarea.selectionStart;
|
|
||||||
var end = textarea.selectionEnd;
|
|
||||||
var hadSelection = end > start;
|
|
||||||
var selected = hadSelection ? textarea.value.slice(start, end) : placeholder;
|
|
||||||
replaceRange(start, end, before + selected + after);
|
|
||||||
if (!hadSelection) {
|
|
||||||
textarea.selectionStart = start + before.length;
|
|
||||||
textarea.selectionEnd = start + before.length + placeholder.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function linePrefix(prefix) {
|
|
||||||
var start = textarea.selectionStart;
|
|
||||||
var lineStart = textarea.value.lastIndexOf('\n', start - 1) + 1;
|
|
||||||
replaceRange(lineStart, lineStart, prefix);
|
|
||||||
textarea.selectionStart = textarea.selectionEnd = start + prefix.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertAtCursor(s) {
|
|
||||||
replaceRange(textarea.selectionStart, textarea.selectionEnd, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyResult(result) {
|
|
||||||
var oldText = textarea.value;
|
|
||||||
var newText = result.text;
|
|
||||||
var prefixLen = 0;
|
|
||||||
var maxPrefix = Math.min(oldText.length, newText.length);
|
|
||||||
while (prefixLen < maxPrefix && oldText.charCodeAt(prefixLen) === newText.charCodeAt(prefixLen)) {
|
|
||||||
prefixLen++;
|
|
||||||
}
|
|
||||||
var oldEnd = oldText.length;
|
|
||||||
var newEnd = newText.length;
|
|
||||||
while (oldEnd > prefixLen && newEnd > prefixLen
|
|
||||||
&& oldText.charCodeAt(oldEnd - 1) === newText.charCodeAt(newEnd - 1)) {
|
|
||||||
oldEnd--;
|
|
||||||
newEnd--;
|
|
||||||
}
|
|
||||||
replaceRange(prefixLen, oldEnd, newText.slice(prefixLen, newEnd));
|
|
||||||
textarea.selectionStart = textarea.selectionEnd = result.cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyTableOp(fn, arg) {
|
|
||||||
var result = arg !== undefined
|
|
||||||
? fn(textarea.value, textarea.selectionStart, arg)
|
|
||||||
: fn(textarea.value, textarea.selectionStart);
|
|
||||||
if (result) applyResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// isValidWikiTarget mirrors the Go validator in wikilinks.go — absolute
|
|
||||||
// path, no empty/dot segments. Used to gate the INSERT confirm button.
|
|
||||||
function isValidWikiTarget(p) {
|
|
||||||
if (!p || p[0] !== '/') return false;
|
|
||||||
var trimmed = p.replace(/^\/+|\/+$/g, '');
|
|
||||||
if (trimmed === '') return true;
|
|
||||||
var segs = trimmed.split('/');
|
|
||||||
for (var i = 0; i < segs.length; i++) {
|
|
||||||
if (segs[i] === '' || segs[i] === '.' || segs[i] === '..') return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertWikilink() {
|
|
||||||
var sel = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
||||||
|
|
||||||
var container = document.createElement('div');
|
|
||||||
|
|
||||||
var targetWrap = document.createElement('div');
|
|
||||||
var targetInput = document.createElement('input');
|
|
||||||
targetInput.type = 'text';
|
|
||||||
targetInput.className = 'input';
|
|
||||||
targetInput.placeholder = 'Page path or search…';
|
|
||||||
targetWrap.appendChild(targetInput);
|
|
||||||
|
|
||||||
var displayInput = document.createElement('input');
|
|
||||||
displayInput.type = 'text';
|
|
||||||
displayInput.className = 'input';
|
|
||||||
displayInput.placeholder = 'Display text (optional)';
|
|
||||||
if (sel) displayInput.value = sel;
|
|
||||||
|
|
||||||
container.appendChild(targetWrap);
|
|
||||||
container.appendChild(displayInput);
|
|
||||||
|
|
||||||
var handle = openModal({
|
|
||||||
title: 'Insert link',
|
|
||||||
body: container,
|
|
||||||
confirm: {
|
|
||||||
label: 'INSERT',
|
|
||||||
initiallyDisabled: true,
|
|
||||||
onConfirm: function () {
|
|
||||||
var target = targetInput.value.trim();
|
|
||||||
if (!isValidWikiTarget(target)) return;
|
|
||||||
var display = displayInput.value.trim();
|
|
||||||
handle.close();
|
|
||||||
insertAtCursor(display ? '[[' + target + '::' + display + ']]' : '[[' + target + ']]');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateConfirm() {
|
|
||||||
handle.setConfirmDisabled(!isValidWikiTarget(targetInput.value.trim()));
|
|
||||||
}
|
|
||||||
targetInput.addEventListener('input', updateConfirm);
|
|
||||||
|
|
||||||
window.attachSuggestions(targetInput, {
|
|
||||||
showFooter: false,
|
|
||||||
container: targetWrap,
|
|
||||||
onPick: function (r) {
|
|
||||||
targetInput.value = '/' + r.path;
|
|
||||||
updateConfirm();
|
|
||||||
displayInput.focus();
|
|
||||||
displayInput.select();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Actions ---
|
|
||||||
|
|
||||||
var T = EditorTables;
|
var T = EditorTables;
|
||||||
var L = EditorLists;
|
|
||||||
var D = EditorDates;
|
var D = EditorDates;
|
||||||
var M = EditorMovie;
|
var M = EditorMovie;
|
||||||
|
|
||||||
|
// --- CodeMirror setup ---
|
||||||
|
|
||||||
|
// Shift+Enter (new table row below) / Shift+Delete (delete table row) run at
|
||||||
|
// highest precedence so they win over CM's default newline/forward-delete.
|
||||||
|
// Returning false (no table at cursor) lets CM fall back to its default.
|
||||||
|
function tableKey(fn) {
|
||||||
|
return function (view) {
|
||||||
|
var result = fn(view.state.doc.toString(), view.state.selection.main.head);
|
||||||
|
if (!result) return false;
|
||||||
|
dispatchFullReplace(result);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableKeymap = [
|
||||||
|
{ key: 'Shift-Enter', run: tableKey(T.insertRowBelow) },
|
||||||
|
{ key: 'Shift-Delete', run: tableKey(T.deleteRow) },
|
||||||
|
];
|
||||||
|
|
||||||
|
var state = CM.EditorState.create({
|
||||||
|
doc: hidden.value,
|
||||||
|
extensions: [
|
||||||
|
CM.history(),
|
||||||
|
CM.drawSelection(),
|
||||||
|
CM.indentOnInput(),
|
||||||
|
CM.EditorView.lineWrapping,
|
||||||
|
CM.markdown({ base: CM.markdownLanguage }),
|
||||||
|
CM.syntaxHighlighting(CM.highlightStyle),
|
||||||
|
CM.closeBrackets(),
|
||||||
|
CM.autocompletion({ override: [WikiComplete.source] }),
|
||||||
|
CM.theme,
|
||||||
|
CM.Prec.highest(CM.keymap.of(tableKeymap)),
|
||||||
|
CM.keymap.of([].concat(
|
||||||
|
CM.closeBracketsKeymap,
|
||||||
|
CM.completionKeymap,
|
||||||
|
CM.markdownKeymap,
|
||||||
|
CM.defaultKeymap,
|
||||||
|
CM.historyKeymap,
|
||||||
|
[CM.indentWithTab]
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
var view = new CM.EditorView({ state: state, parent: mount });
|
||||||
|
view.focus();
|
||||||
|
|
||||||
|
// --- CM document helpers ---
|
||||||
|
|
||||||
|
function dispatchFullReplace(result) {
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: 0, to: view.state.doc.length, insert: result.text },
|
||||||
|
selection: { anchor: result.cursor },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
view.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertAtCursor(s) {
|
||||||
|
var sel = view.state.selection.main;
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: sel.from, to: sel.to, insert: s },
|
||||||
|
selection: { anchor: sel.from + s.length },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
view.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrap(before, after, placeholder) {
|
||||||
|
var sel = view.state.selection.main;
|
||||||
|
var hadSelection = sel.to > sel.from;
|
||||||
|
var selected = hadSelection ? view.state.sliceDoc(sel.from, sel.to) : placeholder;
|
||||||
|
var insert = before + selected + after;
|
||||||
|
var anchor, head;
|
||||||
|
if (hadSelection) {
|
||||||
|
anchor = head = sel.from + insert.length;
|
||||||
|
} else {
|
||||||
|
anchor = sel.from + before.length;
|
||||||
|
head = anchor + placeholder.length;
|
||||||
|
}
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: sel.from, to: sel.to, insert: insert },
|
||||||
|
selection: { anchor: anchor, head: head },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
view.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function linePrefix(prefix) {
|
||||||
|
var sel = view.state.selection.main;
|
||||||
|
var line = view.state.doc.lineAt(sel.from);
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: line.from, to: line.from, insert: prefix },
|
||||||
|
selection: { anchor: sel.from + prefix.length },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
view.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTableOp(fn, arg) {
|
||||||
|
var text = view.state.doc.toString();
|
||||||
|
var pos = view.state.selection.main.head;
|
||||||
|
var result = arg !== undefined ? fn(text, pos, arg) : fn(text, pos);
|
||||||
|
if (result) dispatchFullReplace(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapter passed to movie.js so it reads/writes the CM document instead of a
|
||||||
|
// textarea (replace() dispatches a transaction; cursor lands after the block).
|
||||||
|
var movieCtx = {
|
||||||
|
getValue: function () { return view.state.doc.toString(); },
|
||||||
|
replace: function (start, end, text) {
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: start, to: end, insert: text },
|
||||||
|
selection: { anchor: start + text.length },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
view.focus();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Content sync ---
|
||||||
|
|
||||||
|
// Serialize the CM document into the hidden textarea on submit only — Save
|
||||||
|
// must not depend on CM focus or async state (decision 8). requestSubmit
|
||||||
|
// fires this listener even for the ALT+SHIFT+S path.
|
||||||
|
function syncContent() {
|
||||||
|
hidden.value = view.state.doc.toString();
|
||||||
|
}
|
||||||
|
form.addEventListener('submit', syncContent);
|
||||||
|
|
||||||
|
// --- Actions ---
|
||||||
|
|
||||||
var actions = {
|
var actions = {
|
||||||
save: function () { form.submit(); },
|
save: function () { form.requestSubmit(); },
|
||||||
bold: function () { wrap('**', '**', 'bold text'); },
|
bold: function () { wrap('**', '**', 'bold text'); },
|
||||||
italic: function () { wrap('*', '*', 'italic text'); },
|
italic: function () { wrap('*', '*', 'italic text'); },
|
||||||
h1: function () { linePrefix('# '); },
|
h1: function () { linePrefix('# '); },
|
||||||
@@ -164,7 +166,7 @@
|
|||||||
tbldeleterow: function () { applyTableOp(T.deleteRow); },
|
tbldeleterow: function () { applyTableOp(T.deleteRow); },
|
||||||
dateiso: function () { insertAtCursor(D.isoDate()); },
|
dateiso: function () { insertAtCursor(D.isoDate()); },
|
||||||
datelong: function () { insertAtCursor(D.longDate()); },
|
datelong: function () { insertAtCursor(D.longDate()); },
|
||||||
movie: function () { M.run(textarea); },
|
movie: function () { M.run(movieCtx); },
|
||||||
wide: function () {
|
wide: function () {
|
||||||
var enabled = !document.body.classList.contains('editor-wide');
|
var enabled = !document.body.classList.contains('editor-wide');
|
||||||
document.body.classList.toggle('editor-wide', enabled);
|
document.body.classList.toggle('editor-wide', enabled);
|
||||||
@@ -172,6 +174,19 @@
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Wiki link button: drop an empty [[]] at the cursor and open the `[[`
|
||||||
|
// completion popup so the same autocomplete flow handles target selection.
|
||||||
|
function insertWikilink() {
|
||||||
|
var sel = view.state.selection.main;
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: sel.from, to: sel.to, insert: '[[]]' },
|
||||||
|
selection: { anchor: sel.from + 2 },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
view.focus();
|
||||||
|
CM.startCompletion(view);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Keyboard shortcut registration ---
|
// --- Keyboard shortcut registration ---
|
||||||
|
|
||||||
var keyMap = {};
|
var keyMap = {};
|
||||||
@@ -197,31 +212,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Textarea key handling ---
|
|
||||||
|
|
||||||
textarea.addEventListener('keydown', function (e) {
|
|
||||||
if (e.key === 'Delete' && e.shiftKey) {
|
|
||||||
var result = T.deleteRow(textarea.value, textarea.selectionStart)
|
|
||||||
|| L.deleteOrderedLine(textarea.value, textarea.selectionStart);
|
|
||||||
if (!result) return;
|
|
||||||
e.preventDefault();
|
|
||||||
applyResult(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e.key === 'Enter' && e.shiftKey) {
|
|
||||||
var result = T.insertRowBelow(textarea.value, textarea.selectionStart);
|
|
||||||
if (!result) return;
|
|
||||||
e.preventDefault();
|
|
||||||
applyResult(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e.key !== 'Enter') return;
|
|
||||||
var result = L.handleEnterKey(textarea.value, textarea.selectionStart);
|
|
||||||
if (!result) return;
|
|
||||||
e.preventDefault();
|
|
||||||
applyResult(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- Dropdowns ---
|
// --- Dropdowns ---
|
||||||
|
|
||||||
document.querySelectorAll('.dropdown-toggle').forEach(wireDropdown);
|
document.querySelectorAll('.dropdown-toggle').forEach(wireDropdown);
|
||||||
|
|||||||
+13
-19
@@ -48,26 +48,20 @@ window.EditorMovie = (function () {
|
|||||||
return out.join('\n');
|
return out.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceRange(ta, start, end, text) {
|
// ctx is the CM adapter from main.js: { getValue(), replace(start,end,text) }.
|
||||||
ta.focus();
|
function insertOrReplace(ctx, markup) {
|
||||||
ta.selectionStart = start;
|
var t = ctx.getValue() || '';
|
||||||
ta.selectionEnd = end;
|
|
||||||
document.execCommand('insertText', false, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertOrReplace(ta, markup) {
|
|
||||||
var t = ta.value || '';
|
|
||||||
var b = t.indexOf(BEGIN);
|
var b = t.indexOf(BEGIN);
|
||||||
var e = t.indexOf(END);
|
var e = t.indexOf(END);
|
||||||
if (b !== -1 && e !== -1 && e > b) {
|
if (b !== -1 && e !== -1 && e > b) {
|
||||||
replaceRange(ta, b, e + END.length, markup);
|
ctx.replace(b, e + END.length, markup);
|
||||||
} else {
|
} else {
|
||||||
var h = t.match(/^#{1,6}\s+.+?\s*$/m);
|
var h = t.match(/^#{1,6}\s+.+?\s*$/m);
|
||||||
if (h) {
|
if (h) {
|
||||||
var idx = t.indexOf(h[0]) + h[0].length;
|
var idx = t.indexOf(h[0]) + h[0].length;
|
||||||
replaceRange(ta, idx, idx, '\n\n' + markup);
|
ctx.replace(idx, idx, '\n\n' + markup);
|
||||||
} else {
|
} else {
|
||||||
replaceRange(ta, 0, 0, t ? markup + '\n\n' : markup);
|
ctx.replace(0, 0, t ? markup + '\n\n' : markup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +119,7 @@ window.EditorMovie = (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function importWithKey(textarea, key, initialTitle) {
|
function importWithKey(ctx, key, initialTitle) {
|
||||||
var input = document.createElement('input');
|
var input = document.createElement('input');
|
||||||
input.type = 'text';
|
input.type = 'text';
|
||||||
input.className = 'input';
|
input.className = 'input';
|
||||||
@@ -148,7 +142,7 @@ window.EditorMovie = (function () {
|
|||||||
data.Error === 'Invalid API key!') {
|
data.Error === 'Invalid API key!') {
|
||||||
localStorage.removeItem(STORAGE_KEY);
|
localStorage.removeItem(STORAGE_KEY);
|
||||||
promptForKey(true, function (newKey) {
|
promptForKey(true, function (newKey) {
|
||||||
importWithKey(textarea, newKey, raw);
|
importWithKey(ctx, newKey, raw);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -157,7 +151,7 @@ window.EditorMovie = (function () {
|
|||||||
(data && data.Error) || 'Movie not found.');
|
(data && data.Error) || 'Movie not found.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
insertOrReplace(textarea, buildBlock(data));
|
insertOrReplace(ctx, buildBlock(data));
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
showMessage('Import failed', 'OMDb lookup failed.');
|
showMessage('Import failed', 'OMDb lookup failed.');
|
||||||
@@ -167,16 +161,16 @@ window.EditorMovie = (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function run(textarea) {
|
function run(ctx) {
|
||||||
var initialTitle = firstHeading(textarea.value || '');
|
var initialTitle = firstHeading(ctx.getValue() || '');
|
||||||
var key = localStorage.getItem(STORAGE_KEY);
|
var key = localStorage.getItem(STORAGE_KEY);
|
||||||
if (!key) {
|
if (!key) {
|
||||||
promptForKey(false, function (newKey) {
|
promptForKey(false, function (newKey) {
|
||||||
importWithKey(textarea, newKey, initialTitle);
|
importWithKey(ctx, newKey, initialTitle);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
importWithKey(textarea, key, initialTitle);
|
importWithKey(ctx, key, initialTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { run: run };
|
return { run: run };
|
||||||
|
|||||||
+27
File diff suppressed because one or more lines are too long
@@ -0,0 +1,93 @@
|
|||||||
|
// wikicomplete.js — the `[[` wikilink autocomplete source for CodeMirror.
|
||||||
|
//
|
||||||
|
// Triggers when the cursor sits in a `[[…` token (freshly typed or re-edited
|
||||||
|
// inside an existing `[[…]]`), queries the existing /_search JSON endpoint
|
||||||
|
// (debounced), and offers matching page paths. Selecting a result inserts the
|
||||||
|
// absolute path and auto-closes `]]`, leaving the cursor before the close so a
|
||||||
|
// `::display` alias can be typed (decision 7). Exposes window.WikiComplete.source
|
||||||
|
// for main.js to register via CM's autocompletion().
|
||||||
|
window.WikiComplete = (function () {
|
||||||
|
var DEBOUNCE_MS = 100;
|
||||||
|
var MIN_QUERY_LEN = 2;
|
||||||
|
|
||||||
|
// Mirrors the Go validator (wikilinks.go isValidWikiTarget) and the old
|
||||||
|
// client check: absolute path, no empty/'.'/'..' segments. Server results
|
||||||
|
// are wiki paths so this is mostly a guard against odd index entries.
|
||||||
|
function isValidWikiTarget(p) {
|
||||||
|
if (!p || p[0] !== '/') return false;
|
||||||
|
var trimmed = p.replace(/^\/+|\/+$/g, '');
|
||||||
|
if (trimmed === '') return true;
|
||||||
|
var segs = trimmed.split('/');
|
||||||
|
for (var i = 0; i < segs.length; i++) {
|
||||||
|
if (segs[i] === '' || segs[i] === '.' || segs[i] === '..') return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the apply() for a chosen result. Inserts the path over the typed
|
||||||
|
// query and ensures a single trailing `]]`, cursor parked before it. If the
|
||||||
|
// user (or closeBrackets) already produced `]]`, we don't double it up.
|
||||||
|
function makeApply(path) {
|
||||||
|
return function (view, completion, from, to) {
|
||||||
|
var closed = view.state.sliceDoc(to, to + 2) === ']]';
|
||||||
|
var insert = closed ? path : path + ']]';
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: from, to: to, insert: insert },
|
||||||
|
selection: { anchor: from + path.length },
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchSuggest(query) {
|
||||||
|
return fetch('/_search?q=' + encodeURIComponent(query), {
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: { 'Accept': 'application/json' },
|
||||||
|
}).then(function (r) {
|
||||||
|
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||||
|
return r.json();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// CM completion source. `[[` plus any run of non-`]`, non-newline chars
|
||||||
|
// before the cursor is the trigger; the query is the text after `[[`. The
|
||||||
|
// replaced range extends past the cursor to the end of the inner token (up
|
||||||
|
// to `]]`/EOL) so re-editing inside an existing `[[…]]` replaces the whole
|
||||||
|
// target instead of duplicating the trailing text.
|
||||||
|
function source(context) {
|
||||||
|
var match = context.matchBefore(/\[\[[^\]\n]*/);
|
||||||
|
if (!match) return null;
|
||||||
|
var query = context.state.sliceDoc(match.from + 2, context.pos);
|
||||||
|
if (query.length < MIN_QUERY_LEN && !context.explicit) return null;
|
||||||
|
|
||||||
|
var line = context.state.doc.lineAt(context.pos);
|
||||||
|
var to = context.pos;
|
||||||
|
while (to < line.to && context.state.sliceDoc(to, to + 1) !== ']') to++;
|
||||||
|
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
var timer = setTimeout(function () {
|
||||||
|
if (context.aborted) { resolve(null); return; }
|
||||||
|
fetchSuggest(query).then(function (resp) {
|
||||||
|
if (context.aborted) { resolve(null); return; }
|
||||||
|
var options = (resp.results || []).reduce(function (acc, r) {
|
||||||
|
var path = '/' + r.path;
|
||||||
|
if (isValidWikiTarget(path)) {
|
||||||
|
acc.push({ label: path, detail: r.name, apply: makeApply(path) });
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
resolve({ from: match.from + 2, to: to, options: options });
|
||||||
|
}).catch(function () {
|
||||||
|
resolve(null);
|
||||||
|
});
|
||||||
|
}, DEBOUNCE_MS);
|
||||||
|
// CM doesn't cancel pending promises, but it sets context.aborted;
|
||||||
|
// clear the timer too if the doc moved on before it fired.
|
||||||
|
if (context.addEventListener) {
|
||||||
|
context.addEventListener('abort', function () { clearTimeout(timer); });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { source: source, isValidWikiTarget: isValidWikiTarget };
|
||||||
|
})();
|
||||||
+7
-12
@@ -167,7 +167,7 @@ footer {
|
|||||||
.btn::after { content: "]"; color: var(--secondary); }
|
.btn::after { content: "]"; color: var(--secondary); }
|
||||||
.btn:hover { color: var(--primary-hover); }
|
.btn:hover { color: var(--primary-hover); }
|
||||||
.btn-small { font-size: 0.65rem; font-weight: normal; vertical-align: middle; }
|
.btn-small { font-size: 0.65rem; font-weight: normal; vertical-align: middle; }
|
||||||
.btn-tool { font-size: var(--font-sm); padding: 0 0.15rem; }
|
.btn-tool { padding: 0 0.15rem; }
|
||||||
.btn-block {
|
.btn-block {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -372,16 +372,11 @@ main > h2 {
|
|||||||
/* === Edit form === */
|
/* === Edit form === */
|
||||||
.edit-form { display: flex; flex-direction: column; }
|
.edit-form { display: flex; flex-direction: column; }
|
||||||
body.editor-wide .page-wrap { max-width: none; }
|
body.editor-wide .page-wrap { max-width: none; }
|
||||||
.editor-textarea {
|
/* CodeMirror mount. The .cm-editor visual treatment (border, bg, font, padding)
|
||||||
min-height: 60vh;
|
lives in the CM theme (editor-build/entry.js), keyed off the same :root
|
||||||
background: var(--bg);
|
variables; this only sizes the container. */
|
||||||
border-top: none;
|
.editor-cm { min-height: 60vh; }
|
||||||
font-family: "Iosevka Slab", monospace;
|
.editor-cm .cm-editor { height: 100%; }
|
||||||
font-size: 0.9rem;
|
|
||||||
line-height: 1.6;
|
|
||||||
padding: var(--space-4);
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* === Search === */
|
/* === Search === */
|
||||||
.search-form {
|
.search-form {
|
||||||
@@ -678,7 +673,7 @@ aside.sidebar:empty { display: none; }
|
|||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
header, footer { padding: var(--space-2) var(--space-3); }
|
header, footer { padding: var(--space-2) var(--space-3); }
|
||||||
main { padding: var(--space-4) var(--space-3); }
|
main { padding: var(--space-4) var(--space-3); }
|
||||||
.editor-textarea { min-height: 50vh; }
|
.editor-cm { min-height: 50vh; }
|
||||||
.sidebar { width: calc(100% - 1.5rem); }
|
.sidebar { width: calc(100% - 1.5rem); }
|
||||||
.modal-backdrop { padding: var(--space-2); align-items: flex-start; }
|
.modal-backdrop { padding: var(--space-2); align-items: flex-start; }
|
||||||
.modal { max-width: none; margin-top: var(--space-4); }
|
.modal { max-width: none; margin-top: var(--space-4); }
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Builds the vendored CodeMirror 6 bundle consumed by the page editor.
|
||||||
|
//
|
||||||
|
// Output is an IIFE that assigns the CM primitives the editor scripts need
|
||||||
|
// onto window.CM (see entry.js). The editor JS is loaded as plain global
|
||||||
|
// scripts, not ES modules, so there is no runtime module loader.
|
||||||
|
//
|
||||||
|
// Run via `make editor` (or `npm run build` here) after changing CM versions.
|
||||||
|
// The committed artifact at assets/editor/vendor/codemirror.bundle.js is the
|
||||||
|
// only thing `go build` ever sees.
|
||||||
|
import * as esbuild from "esbuild";
|
||||||
|
|
||||||
|
await esbuild.build({
|
||||||
|
entryPoints: ["entry.js"],
|
||||||
|
bundle: true,
|
||||||
|
format: "iife",
|
||||||
|
minify: true,
|
||||||
|
target: ["es2018"],
|
||||||
|
legalComments: "none",
|
||||||
|
outfile: "../assets/editor/vendor/codemirror.bundle.js",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("built assets/editor/vendor/codemirror.bundle.js");
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// CodeMirror 6 bundle entry point.
|
||||||
|
//
|
||||||
|
// Imports only the CM packages the editor needs (keep the bundle small for the
|
||||||
|
// mobile/VPN path) and exposes them on window.CM for the global-style editor
|
||||||
|
// scripts. The editor theme and markdown highlight palette draw from the app's
|
||||||
|
// :root CSS variables so there are no hardcoded colors/spacing here.
|
||||||
|
import { EditorState, EditorSelection, Compartment, Prec } from "@codemirror/state";
|
||||||
|
import { EditorView, keymap, drawSelection } from "@codemirror/view";
|
||||||
|
import { history, historyKeymap, defaultKeymap, indentWithTab } from "@codemirror/commands";
|
||||||
|
import { markdown, markdownLanguage, markdownKeymap } from "@codemirror/lang-markdown";
|
||||||
|
import { syntaxHighlighting, HighlightStyle, indentOnInput } from "@codemirror/language";
|
||||||
|
import {
|
||||||
|
autocompletion,
|
||||||
|
closeBrackets,
|
||||||
|
closeBracketsKeymap,
|
||||||
|
completionKeymap,
|
||||||
|
startCompletion,
|
||||||
|
} from "@codemirror/autocomplete";
|
||||||
|
import { tags } from "@lezer/highlight";
|
||||||
|
|
||||||
|
// Editor chrome. Colors/spacing/fonts come from :root variables; var() works
|
||||||
|
// here because HighlightStyle/theme emit real CSS rules.
|
||||||
|
const theme = EditorView.theme(
|
||||||
|
{
|
||||||
|
"&": {
|
||||||
|
backgroundColor: "var(--bg)",
|
||||||
|
color: "var(--text)",
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
border: "var(--border)",
|
||||||
|
borderTop: "none",
|
||||||
|
},
|
||||||
|
"&.cm-focused": { outline: "none" },
|
||||||
|
".cm-scroller": {
|
||||||
|
fontFamily: '"Iosevka Slab", monospace',
|
||||||
|
lineHeight: "1.6",
|
||||||
|
minHeight: "60vh",
|
||||||
|
},
|
||||||
|
".cm-content": {
|
||||||
|
padding: "var(--space-4)",
|
||||||
|
caretColor: "var(--text)",
|
||||||
|
},
|
||||||
|
".cm-cursor, .cm-dropCursor": { borderLeftColor: "var(--text)" },
|
||||||
|
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
|
||||||
|
{ backgroundColor: "var(--bg-panel-hover)" },
|
||||||
|
".cm-activeLine": { backgroundColor: "transparent" },
|
||||||
|
".cm-tooltip": {
|
||||||
|
backgroundColor: "var(--bg-panel)",
|
||||||
|
border: "var(--border-dashed)",
|
||||||
|
color: "var(--text)",
|
||||||
|
},
|
||||||
|
".cm-tooltip.cm-tooltip-autocomplete > ul": {
|
||||||
|
fontFamily: '"Iosevka Slab", monospace',
|
||||||
|
fontSize: "var(--font-sm)",
|
||||||
|
},
|
||||||
|
".cm-tooltip-autocomplete ul li[aria-selected]": {
|
||||||
|
backgroundColor: "var(--bg-panel-hover)",
|
||||||
|
color: "var(--text)",
|
||||||
|
},
|
||||||
|
".cm-completionDetail": {
|
||||||
|
color: "var(--text-muted)",
|
||||||
|
fontStyle: "normal",
|
||||||
|
marginLeft: "var(--space-3)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ dark: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const highlightStyle = HighlightStyle.define([
|
||||||
|
{ tag: [tags.heading1, tags.heading2, tags.heading3, tags.heading4, tags.heading5, tags.heading6], color: "var(--secondary)", fontWeight: "bold" },
|
||||||
|
{ tag: tags.strong, fontWeight: "bold", color: "var(--text)" },
|
||||||
|
{ tag: tags.emphasis, fontStyle: "italic" },
|
||||||
|
{ tag: tags.strikethrough, textDecoration: "line-through" },
|
||||||
|
{ tag: [tags.link, tags.url], color: "var(--link)" },
|
||||||
|
{ tag: tags.monospace, color: "var(--primary-hover)" },
|
||||||
|
{ tag: tags.quote, color: "var(--text-muted)", fontStyle: "italic" },
|
||||||
|
{ tag: [tags.list, tags.contentSeparator], color: "var(--secondary)" },
|
||||||
|
{ tag: [tags.processingInstruction, tags.meta], color: "var(--text-muted)" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
window.CM = {
|
||||||
|
EditorState,
|
||||||
|
EditorSelection,
|
||||||
|
Compartment,
|
||||||
|
Prec,
|
||||||
|
EditorView,
|
||||||
|
keymap,
|
||||||
|
drawSelection,
|
||||||
|
history,
|
||||||
|
historyKeymap,
|
||||||
|
defaultKeymap,
|
||||||
|
indentWithTab,
|
||||||
|
markdown,
|
||||||
|
markdownLanguage,
|
||||||
|
markdownKeymap,
|
||||||
|
syntaxHighlighting,
|
||||||
|
indentOnInput,
|
||||||
|
autocompletion,
|
||||||
|
closeBrackets,
|
||||||
|
closeBracketsKeymap,
|
||||||
|
completionKeymap,
|
||||||
|
startCompletion,
|
||||||
|
theme,
|
||||||
|
highlightStyle,
|
||||||
|
};
|
||||||
Generated
+730
@@ -0,0 +1,730 @@
|
|||||||
|
{
|
||||||
|
"name": "datascape-editor-build",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "datascape-editor-build",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.18.0",
|
||||||
|
"@codemirror/commands": "^6.7.0",
|
||||||
|
"@codemirror/lang-markdown": "^6.3.0",
|
||||||
|
"@codemirror/language": "^6.10.0",
|
||||||
|
"@codemirror/state": "^6.4.0",
|
||||||
|
"@codemirror/view": "^6.34.0",
|
||||||
|
"@lezer/highlight": "^1.2.0",
|
||||||
|
"esbuild": "^0.24.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/autocomplete": {
|
||||||
|
"version": "6.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz",
|
||||||
|
"integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.17.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/commands": {
|
||||||
|
"version": "6.10.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz",
|
||||||
|
"integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.6.0",
|
||||||
|
"@codemirror/view": "^6.27.0",
|
||||||
|
"@lezer/common": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-css": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.2",
|
||||||
|
"@lezer/css": "^1.1.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-html": {
|
||||||
|
"version": "6.4.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
|
||||||
|
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/lang-css": "^6.0.0",
|
||||||
|
"@codemirror/lang-javascript": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.4.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.17.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/css": "^1.1.0",
|
||||||
|
"@lezer/html": "^1.3.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-javascript": {
|
||||||
|
"version": "6.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz",
|
||||||
|
"integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.6.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.17.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/javascript": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-markdown": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.7.1",
|
||||||
|
"@codemirror/lang-html": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.3.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.2.1",
|
||||||
|
"@lezer/markdown": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/language": {
|
||||||
|
"version": "6.12.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz",
|
||||||
|
"integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.23.0",
|
||||||
|
"@lezer/common": "^1.5.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0",
|
||||||
|
"style-mod": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lint": {
|
||||||
|
"version": "6.9.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.6.tgz",
|
||||||
|
"integrity": "sha512-6Kp7r6XfCi/D/5sdXieMfg9pJU1bUEx96WITuLU6ESaKizCz0QHFMjY/TaFSbigDdEAIgi93itLBIUETP4oK+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.42.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/state": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/view": {
|
||||||
|
"version": "6.43.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz",
|
||||||
|
"integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.6.0",
|
||||||
|
"crelt": "^1.0.6",
|
||||||
|
"style-mod": "^4.1.0",
|
||||||
|
"w3c-keyname": "^2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"aix"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-x64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-x64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/common": {
|
||||||
|
"version": "1.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz",
|
||||||
|
"integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/css": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.2.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/highlight": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/html": {
|
||||||
|
"version": "1.3.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz",
|
||||||
|
"integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.2.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/javascript": {
|
||||||
|
"version": "1.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz",
|
||||||
|
"integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.2.0",
|
||||||
|
"@lezer/highlight": "^1.1.3",
|
||||||
|
"@lezer/lr": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/lr": {
|
||||||
|
"version": "1.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz",
|
||||||
|
"integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/markdown": {
|
||||||
|
"version": "1.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.4.tgz",
|
||||||
|
"integrity": "sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.5.0",
|
||||||
|
"@lezer/highlight": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@marijn/find-cluster-break": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/crelt": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.24.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
|
||||||
|
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.24.2",
|
||||||
|
"@esbuild/android-arm": "0.24.2",
|
||||||
|
"@esbuild/android-arm64": "0.24.2",
|
||||||
|
"@esbuild/android-x64": "0.24.2",
|
||||||
|
"@esbuild/darwin-arm64": "0.24.2",
|
||||||
|
"@esbuild/darwin-x64": "0.24.2",
|
||||||
|
"@esbuild/freebsd-arm64": "0.24.2",
|
||||||
|
"@esbuild/freebsd-x64": "0.24.2",
|
||||||
|
"@esbuild/linux-arm": "0.24.2",
|
||||||
|
"@esbuild/linux-arm64": "0.24.2",
|
||||||
|
"@esbuild/linux-ia32": "0.24.2",
|
||||||
|
"@esbuild/linux-loong64": "0.24.2",
|
||||||
|
"@esbuild/linux-mips64el": "0.24.2",
|
||||||
|
"@esbuild/linux-ppc64": "0.24.2",
|
||||||
|
"@esbuild/linux-riscv64": "0.24.2",
|
||||||
|
"@esbuild/linux-s390x": "0.24.2",
|
||||||
|
"@esbuild/linux-x64": "0.24.2",
|
||||||
|
"@esbuild/netbsd-arm64": "0.24.2",
|
||||||
|
"@esbuild/netbsd-x64": "0.24.2",
|
||||||
|
"@esbuild/openbsd-arm64": "0.24.2",
|
||||||
|
"@esbuild/openbsd-x64": "0.24.2",
|
||||||
|
"@esbuild/sunos-x64": "0.24.2",
|
||||||
|
"@esbuild/win32-arm64": "0.24.2",
|
||||||
|
"@esbuild/win32-ia32": "0.24.2",
|
||||||
|
"@esbuild/win32-x64": "0.24.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/style-mod": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/w3c-keyname": {
|
||||||
|
"version": "2.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||||
|
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "datascape-editor-build",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "One-time build tooling for the vendored CodeMirror 6 editor bundle. Dev-only: `go build` never runs Node, it only consumes the committed bundle artifact.",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "node build.mjs"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.18.0",
|
||||||
|
"@codemirror/commands": "^6.7.0",
|
||||||
|
"@codemirror/lang-markdown": "^6.3.0",
|
||||||
|
"@codemirror/language": "^6.10.0",
|
||||||
|
"@codemirror/state": "^6.4.0",
|
||||||
|
"@codemirror/view": "^6.34.0",
|
||||||
|
"@lezer/highlight": "^1.2.0",
|
||||||
|
"esbuild": "^0.24.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,7 +90,7 @@ func main() {
|
|||||||
staticFS, _ := fs.Sub(assets, "assets")
|
staticFS, _ := fs.Sub(assets, "assets")
|
||||||
static := http.StripPrefix("/_/", http.FileServer(http.FS(staticFS)))
|
static := http.StripPrefix("/_/", http.FileServer(http.FS(staticFS)))
|
||||||
http.Handle("/_/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
http.Handle("/_/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if strings.HasPrefix(r.URL.Path, "/_/fonts/") {
|
if strings.HasPrefix(r.URL.Path, "/_/fonts/") || strings.HasPrefix(r.URL.Path, "/_/editor/vendor/") {
|
||||||
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
|
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
|
||||||
}
|
}
|
||||||
static.ServeHTTP(w, r)
|
static.ServeHTTP(w, r)
|
||||||
|
|||||||
Reference in New Issue
Block a user