Changeset 0.17.2 (#77)

This commit is contained in:
2026-02-28 11:58:27 +00:00
parent 856dc9b0ac
commit a008dac488
26 changed files with 3018 additions and 116 deletions

54
src/editor/build.mjs Normal file
View File

@@ -0,0 +1,54 @@
// ==========================================
// Chat Switchboard — CM6 Build Script
// ==========================================
// Bundles CodeMirror 6 into a single IIFE that
// exposes window.CM. Run via scripts/build-editor.sh
// or directly: node build.mjs [--outdir=path]
//
// Output:
// codemirror.bundle.js (~295KB min, ~90KB gzip)
// codemirror.bundle.css (~5-10KB)
// ==========================================
import { build } from 'esbuild';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
// Parse --outdir=<path> from argv
let outdir = resolve(__dirname, 'dist');
for (const arg of process.argv.slice(2)) {
if (arg.startsWith('--outdir=')) {
outdir = resolve(arg.slice('--outdir='.length));
}
}
try {
const result = await build({
entryPoints: [resolve(__dirname, 'index.mjs')],
bundle: true,
minify: true,
format: 'iife',
target: ['es2020'],
outfile: resolve(outdir, 'codemirror.bundle.js'),
// Metafile for optional size analysis
metafile: true,
// Log level
logLevel: 'info',
});
// Print bundle size summary
if (result.metafile) {
const outputs = result.metafile.outputs;
for (const [file, info] of Object.entries(outputs)) {
const kb = (info.bytes / 1024).toFixed(1);
console.log(` ${file}: ${kb} KB`);
}
}
console.log(`✅ CM6 bundle → ${outdir}`);
} catch (err) {
console.error('❌ CM6 build failed:', err.message);
process.exit(1);
}

310
src/editor/chat-input.mjs Normal file
View File

@@ -0,0 +1,310 @@
// ==========================================
// Chat Switchboard — CM6 Chat Input Factory
// ==========================================
// Minimal markdown editor for the chat message
// input. No line numbers, no gutter. Enter sends,
// Shift+Enter inserts newline. Auto-growing height.
//
// Usage:
// const editor = CM.chatInput(container, {
// placeholder: 'Send a message...',
// onSubmit: (text) => sendMessage(text),
// onChange: (text) => updateInputTokens(text),
// maxHeight: 200,
// darkMode: true,
// });
//
// editor.getValue()
// editor.setValue(str)
// editor.focus()
// editor.destroy()
// ==========================================
import { EditorView, keymap, drawSelection, dropCursor,
highlightSpecialChars, placeholder as placeholderExt,
ViewPlugin, Decoration } from '@codemirror/view';
import { EditorState, Compartment, RangeSetBuilder } from '@codemirror/state';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import { syntaxHighlighting, defaultHighlightStyle, syntaxTree } from '@codemirror/language';
import { chatInputTheme } from './theme.mjs';
// ── Auto-Height Extension ───────────────────
/**
* Extension that auto-grows the editor height to fit content,
* up to maxHeight pixels. Mirrors the existing textarea behavior:
* this.style.height = 'auto';
* this.style.height = Math.min(this.scrollHeight, 200) + 'px';
*/
function autoHeight(maxHeight = 200) {
return EditorView.updateListener.of((update) => {
if (update.docChanged || update.geometryChanged) {
const dom = update.view.dom;
dom.style.height = 'auto';
const scrollH = update.view.contentDOM.scrollHeight;
// Account for .cm-editor padding (12px top + 12px bottom from wrapper CSS)
const padV = 24;
const targetH = Math.min(scrollH + padV, maxHeight);
dom.style.height = targetH + 'px';
dom.style.overflowY = scrollH + padV > maxHeight ? 'auto' : 'hidden';
}
});
}
// ── Fenced Code Block Decorations ───────────
//
// Scans the markdown syntax tree for FencedCode nodes
// and applies line decorations so they render as visual
// code blocks (dark background, monospace, rounded corners)
// inside the chat input — matching the claude.ai UX.
const codeBlockDeco = ViewPlugin.fromClass(class {
decorations;
constructor(view) {
this.decorations = this.buildDecorations(view);
}
update(update) {
// Rebuild when content changes, viewport shifts, or the parser
// finishes an incremental parse (tree reference changes)
if (update.docChanged || update.viewportChanged ||
syntaxTree(update.startState) !== syntaxTree(update.state)) {
this.decorations = this.buildDecorations(update.view);
}
}
buildDecorations(view) {
const builder = new RangeSetBuilder();
const tree = syntaxTree(view.state);
const doc = view.state.doc;
tree.iterate({
enter(node) {
if (node.name !== 'FencedCode') return;
const startLine = doc.lineAt(node.from);
const endLine = doc.lineAt(node.to);
for (let ln = startLine.number; ln <= endLine.number; ln++) {
const line = doc.line(ln);
let cls;
if (ln === startLine.number) {
cls = 'cm-codeblock cm-codeblock-open';
} else if (ln === endLine.number) {
cls = 'cm-codeblock cm-codeblock-close';
} else {
cls = 'cm-codeblock cm-codeblock-mid';
}
// Single-line code block (just ```)
if (startLine.number === endLine.number) {
cls = 'cm-codeblock cm-codeblock-open cm-codeblock-close';
}
builder.add(line.from, line.from, Decoration.line({ class: cls }));
}
},
});
return builder.finish();
}
}, {
decorations: (v) => v.decorations,
});
// ── Code Block Shortcuts ────────────────────
/**
* Input handler: triple backtick → fenced code block.
* Triggers when user types the third ` completing ```
* at the start of a line (possibly preceded only by whitespace/text).
*/
function tripleBacktickHandler() {
return EditorView.inputHandler.of((view, from, to, text) => {
if (text !== '`') return false;
const line = view.state.doc.lineAt(from);
const textBeforeCursor = view.state.doc.sliceString(line.from, from);
// Match `` at end of line (completing ```) — only at line start
if (/^``$/.test(textBeforeCursor.trimStart()) && textBeforeCursor.trimStart() === '``') {
const leadingWhitespace = textBeforeCursor.match(/^(\s*)/)[1];
const block = leadingWhitespace + '```\n' + leadingWhitespace + '\n' + leadingWhitespace + '```';
view.dispatch({
changes: { from: line.from, to, insert: block },
selection: { anchor: line.from + leadingWhitespace.length + 4 },
});
return true;
}
return false;
});
}
/**
* Keybinding: Ctrl/Cmd+E wraps selection in inline code backticks,
* or inserts a pair and places cursor between them.
*/
function inlineCodeKeymap() {
return keymap.of([{
key: 'Mod-e',
run: (view) => {
const { from, to } = view.state.selection.main;
if (from !== to) {
const text = view.state.doc.sliceString(from, to);
view.dispatch({
changes: { from, to, insert: '`' + text + '`' },
selection: { anchor: from + 1, head: to + 1 },
});
} else {
view.dispatch({
changes: { from, to: from, insert: '``' },
selection: { anchor: from + 1 },
});
}
return true;
},
}]);
}
// ── Chat Input Factory ──────────────────────
/**
* Create a chat message input with markdown highlighting.
*
* @param {HTMLElement} target - Container element
* @param {Object} opts
* @param {string} [opts.placeholder='Send a message...']
* @param {Function} [opts.onSubmit] - Called with text on Enter (without Shift)
* @param {Function} [opts.onChange] - Called with text on every edit
* @param {number} [opts.maxHeight=200] - Max height in pixels before scrolling
* @param {boolean} [opts.darkMode] - Auto-detected from body if omitted
* @returns {{ getValue, setValue, focus, getView, destroy }}
*/
export function chatInput(target, opts = {}) {
const {
placeholder: placeholderText = 'Send a message...',
onSubmit = null,
onChange = null,
maxHeight = 200,
darkMode = document.documentElement.getAttribute('data-theme') !== 'light',
} = opts;
// Build extensions
const extensions = [
// Markdown highlighting
markdown({ base: markdownLanguage }),
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
// Code block shortcuts
tripleBacktickHandler(), // ``` → fenced code block
inlineCodeKeymap(), // Ctrl/Cmd+E → inline code
// Code block visual decorations (WYSIWYG style)
codeBlockDeco,
// Base editing
highlightSpecialChars(),
history(),
drawSelection(),
dropCursor(),
closeBrackets(),
// Theme — minimal chrome, no gutters
chatInputTheme,
// Placeholder
placeholderExt(placeholderText),
// Auto-growing height
autoHeight(maxHeight),
// Keymaps — chat-specific: Enter sends, Shift+Enter newline
keymap.of([
// Enter to submit (must be before defaultKeymap)
{
key: 'Enter',
run: (view) => {
if (onSubmit) {
const text = view.state.doc.toString();
onSubmit(text);
return true;
}
return false;
},
},
// Shift+Enter to insert newline
{
key: 'Shift-Enter',
run: (view) => {
view.dispatch(
view.state.replaceSelection('\n')
);
return true;
},
},
...closeBracketsKeymap,
...defaultKeymap,
...historyKeymap,
]),
];
// Change listener
if (onChange) {
extensions.push(EditorView.updateListener.of((update) => {
if (update.docChanged) {
onChange(update.state.doc.toString());
}
}));
}
// Enable spell check and standard text input attributes
extensions.push(
EditorView.contentAttributes.of({
spellcheck: 'true',
autocapitalize: 'sentences',
autocorrect: 'on',
})
);
// Single-line display mode: wrap long lines
extensions.push(EditorView.lineWrapping);
const view = new EditorView({
state: EditorState.create({ doc: '', extensions }),
parent: target,
});
// ── Public API ──────────────────────────
return {
getValue() {
return view.state.doc.toString();
},
setValue(text) {
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: text || '' },
});
},
focus() {
view.focus();
},
/** Access the underlying EditorView */
getView() {
return view;
},
/** Clean up */
destroy() {
view.destroy();
},
};
}

241
src/editor/code-editor.mjs Normal file
View File

@@ -0,0 +1,241 @@
// ==========================================
// Chat Switchboard — CM6 Code Editor Factory
// ==========================================
// Full-featured code editor: line numbers, bracket
// matching, search/replace, auto-indent, syntax
// highlighting. Used for extension editing and
// future code surfaces.
//
// Usage:
// const editor = CM.codeEditor(container, {
// language: 'javascript',
// value: 'const x = 1;',
// lineNumbers: true,
// darkMode: true,
// readOnly: false,
// keymap: 'standard', // 'standard' | 'vim' | 'emacs'
// onChange: (value) => {},
// });
//
// editor.getValue()
// editor.setValue(str)
// editor.focus()
// editor.destroy()
// ==========================================
import { EditorView, keymap, lineNumbers, highlightActiveLine,
highlightActiveLineGutter, drawSelection, dropCursor,
rectangularSelection, crosshairCursor,
highlightSpecialChars, placeholder as placeholderExt } from '@codemirror/view';
import { EditorState, Compartment } from '@codemirror/state';
import { defaultKeymap, indentWithTab, history, historyKeymap,
undo, redo } from '@codemirror/commands';
import { bracketMatching, indentOnInput, foldGutter, foldKeymap,
syntaxHighlighting, defaultHighlightStyle,
HighlightStyle } from '@codemirror/language';
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
import { autocompletion, completionKeymap, closeBrackets,
closeBracketsKeymap } from '@codemirror/autocomplete';
import { oneDark } from '@codemirror/theme-one-dark';
import { javascript } from '@codemirror/lang-javascript';
import { json } from '@codemirror/lang-json';
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import { sql } from '@codemirror/lang-sql';
import { html } from '@codemirror/lang-html';
import { css } from '@codemirror/lang-css';
import { yaml } from '@codemirror/lang-yaml';
import { go } from '@codemirror/lang-go';
import { python } from '@codemirror/lang-python';
import { rust } from '@codemirror/lang-rust';
import { vim } from '@replit/codemirror-vim';
import { emacs } from '@replit/codemirror-emacs';
import { switchboardTheme } from './theme.mjs';
// ── Language Registry ────────────────────────
const LANGUAGES = {
javascript, js: javascript,
json,
markdown, md: markdown,
sql,
html,
css,
yaml, yml: yaml,
go,
python, py: python,
rust, rs: rust,
};
/**
* Resolve a language name to a CM6 LanguageSupport instance.
* Returns null if the language isn't bundled.
*/
function resolveLanguage(name) {
if (!name) return null;
const factory = LANGUAGES[name.toLowerCase()];
return factory ? factory() : null;
}
// ── Code Editor Factory ─────────────────────
/**
* Create a full-featured code editor.
*
* @param {HTMLElement} target - Container element (editor replaces its content)
* @param {Object} opts
* @param {string} [opts.language] - Language mode name
* @param {string} [opts.value=''] - Initial content
* @param {boolean} [opts.lineNumbers=true]
* @param {boolean} [opts.darkMode] - Auto-detected from body if omitted
* @param {boolean} [opts.readOnly=false]
* @param {string} [opts.keymap='standard'] - 'standard' | 'vim' | 'emacs'
* @param {string} [opts.placeholder] - Placeholder text
* @param {Function} [opts.onChange] - Called with new value on every edit
* @returns {{ getValue, setValue, focus, getView, destroy }}
*/
export function codeEditor(target, opts = {}) {
const {
language = null,
value = '',
lineNumbers: showLineNumbers = true,
darkMode = document.documentElement.getAttribute('data-theme') !== 'light',
readOnly = false,
keymap: keymapMode = 'standard',
placeholder: placeholderText = '',
onChange = null,
} = opts;
// Compartments for reconfigurable extensions
const langCompartment = new Compartment();
const keymapCompartment = new Compartment();
const readOnlyCompartment = new Compartment();
const themeCompartment = new Compartment();
// Build extensions list
const extensions = [
// Base editing
highlightSpecialChars(),
history(),
drawSelection(),
dropCursor(),
EditorState.allowMultipleSelections.of(true),
indentOnInput(),
bracketMatching(),
closeBrackets(),
autocompletion(),
rectangularSelection(),
crosshairCursor(),
highlightActiveLine(),
highlightSelectionMatches(),
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
// Keymaps
keymap.of([
...closeBracketsKeymap,
...defaultKeymap,
...searchKeymap,
...historyKeymap,
...foldKeymap,
...completionKeymap,
indentWithTab,
]),
// Theme
switchboardTheme,
themeCompartment.of(darkMode ? [oneDark] : []),
// Configurable compartments
langCompartment.of(resolveLanguage(language) || []),
keymapCompartment.of(_resolveKeymapExt(keymapMode)),
readOnlyCompartment.of(EditorState.readOnly.of(readOnly)),
// Optional features
...(showLineNumbers ? [lineNumbers(), highlightActiveLineGutter(), foldGutter()] : []),
...(placeholderText ? [placeholderExt(placeholderText)] : []),
];
// Change listener
if (onChange) {
extensions.push(EditorView.updateListener.of((update) => {
if (update.docChanged) {
onChange(update.state.doc.toString());
}
}));
}
// Create the editor
const view = new EditorView({
state: EditorState.create({ doc: value, extensions }),
parent: target,
});
// ── Public API ──────────────────────────
return {
getValue() {
return view.state.doc.toString();
},
setValue(text) {
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: text },
});
},
focus() {
view.focus();
},
/** Access the underlying EditorView for advanced use */
getView() {
return view;
},
/** Switch language mode at runtime */
setLanguage(name) {
const lang = resolveLanguage(name);
view.dispatch({
effects: langCompartment.reconfigure(lang || []),
});
},
/** Switch keymap mode at runtime */
setKeymap(mode) {
view.dispatch({
effects: keymapCompartment.reconfigure(_resolveKeymapExt(mode)),
});
},
/** Toggle read-only state */
setReadOnly(ro) {
view.dispatch({
effects: readOnlyCompartment.reconfigure(
EditorState.readOnly.of(ro)
),
});
},
/** Toggle dark/light mode (controls oneDark syntax theme) */
setDarkMode(dark) {
view.dispatch({
effects: themeCompartment.reconfigure(dark ? [oneDark] : []),
});
},
/** Clean up — removes the editor from the DOM */
destroy() {
view.destroy();
},
};
}
// ── Keybinding Resolution ───────────────────
function _resolveKeymapExt(mode) {
if (mode === 'vim') return vim();
if (mode === 'emacs') return emacs();
return [];
}

61
src/editor/index.mjs Normal file
View File

@@ -0,0 +1,61 @@
// ==========================================
// Chat Switchboard — CM6 Bundle Entrypoint
// ==========================================
// esbuild compiles this into an IIFE that sets
// window.CM with factory functions and utilities.
//
// Consumers:
// if (window.CM) {
// const editor = CM.chatInput(el, opts);
// const code = CM.codeEditor(el, opts);
// }
// ==========================================
import { EditorView } from '@codemirror/view';
import { EditorState } from '@codemirror/state';
import { javascript } from '@codemirror/lang-javascript';
import { json } from '@codemirror/lang-json';
import { markdown } from '@codemirror/lang-markdown';
import { sql } from '@codemirror/lang-sql';
import { html } from '@codemirror/lang-html';
import { css } from '@codemirror/lang-css';
import { yaml } from '@codemirror/lang-yaml';
import { go } from '@codemirror/lang-go';
import { python } from '@codemirror/lang-python';
import { rust } from '@codemirror/lang-rust';
import { vim } from '@replit/codemirror-vim';
import { emacs } from '@replit/codemirror-emacs';
import { codeEditor } from './code-editor.mjs';
import { chatInput } from './chat-input.mjs';
// ── Expose on window ────────────────────────
window.CM = {
// Version (injected by the build script or matched to app version)
version: '0.17.2',
// Low-level access (for advanced use / debug console)
EditorView,
EditorState,
// Factory: full-featured code editor
// CM.codeEditor(container, { language, value, lineNumbers, darkMode, keymap, onChange })
codeEditor,
// Factory: chat message input (markdown mode, minimal chrome)
// CM.chatInput(container, { placeholder, onSubmit, onChange, maxHeight, darkMode })
chatInput,
// Bundled language modes (for reference / dynamic use)
languages: {
javascript, json, markdown, sql, html, css, yaml, go, python, rust,
},
// Keybinding modes (for reference / settings UI)
keybindings: { vim, emacs },
};
console.log('[CM6] CodeMirror bundle loaded (v' + window.CM.version + ')');

917
src/editor/package-lock.json generated Normal file
View File

@@ -0,0 +1,917 @@
{
"name": "editor",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"@codemirror/autocomplete": "^6",
"@codemirror/commands": "^6",
"@codemirror/lang-css": "^6",
"@codemirror/lang-go": "^6",
"@codemirror/lang-html": "^6",
"@codemirror/lang-javascript": "^6",
"@codemirror/lang-json": "^6",
"@codemirror/lang-markdown": "^6",
"@codemirror/lang-python": "^6",
"@codemirror/lang-rust": "^6",
"@codemirror/lang-sql": "^6",
"@codemirror/lang-yaml": "^6",
"@codemirror/language": "^6",
"@codemirror/search": "^6",
"@codemirror/state": "^6",
"@codemirror/theme-one-dark": "^6",
"@codemirror/view": "^6",
"@replit/codemirror-emacs": "^6",
"@replit/codemirror-vim": "^6"
},
"devDependencies": {
"esbuild": "^0.25.0"
}
},
"node_modules/@codemirror/autocomplete": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz",
"integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==",
"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.2",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.2.tgz",
"integrity": "sha512-vvX1fsih9HledO1c9zdotZYUZnE4xV0m6i3m25s5DIfXofuprk6cRcLUZvSk3CASUbwjQX21tOGbkY2BH8TpnQ==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.4.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==",
"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-go": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@codemirror/lang-go/-/lang-go-6.0.1.tgz",
"integrity": "sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.6.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.0.0",
"@lezer/go": "^1.0.0"
}
},
"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==",
"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.4",
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
"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-json": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@lezer/json": "^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==",
"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/lang-python": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.2.1.tgz",
"integrity": "sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.3.2",
"@codemirror/language": "^6.8.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.2.1",
"@lezer/python": "^1.1.4"
}
},
"node_modules/@codemirror/lang-rust": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.2.tgz",
"integrity": "sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@lezer/rust": "^1.0.0"
}
},
"node_modules/@codemirror/lang-sql": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz",
"integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@codemirror/lang-yaml": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.2.tgz",
"integrity": "sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.2.0",
"@lezer/lr": "^1.0.0",
"@lezer/yaml": "^1.0.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.12.2",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.2.tgz",
"integrity": "sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg==",
"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.4",
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.4.tgz",
"integrity": "sha512-ABc9vJ8DEmvOWuH26P3i8FpMWPQkduD9Rvba5iwb6O3hxASgclm3T3krGo8NASXkHCidz6b++LWlzWIUfEPSWw==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.35.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/search": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.6.0.tgz",
"integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.37.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/state": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz",
"integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==",
"license": "MIT",
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
},
"node_modules/@codemirror/theme-one-dark": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/highlight": "^1.0.0"
}
},
"node_modules/@codemirror/view": {
"version": "6.39.15",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.15.tgz",
"integrity": "sha512-aCWjgweIIXLBHh7bY6cACvXuyrZ0xGafjQ2VInjp4RM4gMfscK5uESiNdrH0pE+e1lZr2B4ONGsjchl2KsKZzg==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.5.0",
"crelt": "^1.0.6",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@lezer/common": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.1.tgz",
"integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==",
"license": "MIT"
},
"node_modules/@lezer/css": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.1.tgz",
"integrity": "sha512-PYAKeUVBo3HFThruRyp/iK91SwiZJnzXh8QzkQlwijB5y+N5iB28+iLk78o2zmKqqV0uolNhCwFqB8LA7b0Svg==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.3.0"
}
},
"node_modules/@lezer/go": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.1.tgz",
"integrity": "sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==",
"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==",
"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==",
"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==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.1.3",
"@lezer/lr": "^1.3.0"
}
},
"node_modules/@lezer/json": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz",
"integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@lezer/lr": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz",
"integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lezer/markdown": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.3.tgz",
"integrity": "sha512-jpGm5Ps+XErS+xA4urw7ogEGkeZOahVQF21Z6oECF0sj+2liwZopd2+I8uH5I/vZsRuuze3OxBREIANLf6KKUw==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.5.0",
"@lezer/highlight": "^1.0.0"
}
},
"node_modules/@lezer/python": {
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.18.tgz",
"integrity": "sha512-31FiUrU7z9+d/ElGQLJFXl+dKOdx0jALlP3KEOsGTex8mvj+SoE1FgItcHWK/axkxCHGUSpqIHt6JAWfWu9Rhg==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@lezer/rust": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz",
"integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@lezer/yaml": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.4.tgz",
"integrity": "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.4.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==",
"license": "MIT"
},
"node_modules/@replit/codemirror-emacs": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz",
"integrity": "sha512-74DITnht6Cs6sHg02PQ169IKb1XgtyhI9sLD0JeOFco6Ds18PT+dkD8+DgXBDokne9UIFKsBbKPnpFRAz60/Lw==",
"license": "MIT",
"peerDependencies": {
"@codemirror/autocomplete": "^6.0.2",
"@codemirror/commands": "^6.0.0",
"@codemirror/search": "^6.0.0",
"@codemirror/state": "^6.0.1",
"@codemirror/view": "^6.3.0"
}
},
"node_modules/@replit/codemirror-vim": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.3.0.tgz",
"integrity": "sha512-aTx931ULAMuJx6xLf7KQDOL7CxD+Sa05FktTDrtLaSy53uj01ll3Zf17JdKsriER248oS55GBzg0CfCTjEneAQ==",
"license": "MIT",
"peerDependencies": {
"@codemirror/commands": "6.x.x",
"@codemirror/language": "6.x.x",
"@codemirror/search": "6.x.x",
"@codemirror/state": "6.x.x",
"@codemirror/view": "6.x.x"
}
},
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
"license": "MIT"
},
"node_modules/esbuild": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.12",
"@esbuild/android-arm": "0.25.12",
"@esbuild/android-arm64": "0.25.12",
"@esbuild/android-x64": "0.25.12",
"@esbuild/darwin-arm64": "0.25.12",
"@esbuild/darwin-x64": "0.25.12",
"@esbuild/freebsd-arm64": "0.25.12",
"@esbuild/freebsd-x64": "0.25.12",
"@esbuild/linux-arm": "0.25.12",
"@esbuild/linux-arm64": "0.25.12",
"@esbuild/linux-ia32": "0.25.12",
"@esbuild/linux-loong64": "0.25.12",
"@esbuild/linux-mips64el": "0.25.12",
"@esbuild/linux-ppc64": "0.25.12",
"@esbuild/linux-riscv64": "0.25.12",
"@esbuild/linux-s390x": "0.25.12",
"@esbuild/linux-x64": "0.25.12",
"@esbuild/netbsd-arm64": "0.25.12",
"@esbuild/netbsd-x64": "0.25.12",
"@esbuild/openbsd-arm64": "0.25.12",
"@esbuild/openbsd-x64": "0.25.12",
"@esbuild/openharmony-arm64": "0.25.12",
"@esbuild/sunos-x64": "0.25.12",
"@esbuild/win32-arm64": "0.25.12",
"@esbuild/win32-ia32": "0.25.12",
"@esbuild/win32-x64": "0.25.12"
}
},
"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==",
"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==",
"license": "MIT"
}
}
}

28
src/editor/package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"private": true,
"type": "module",
"devDependencies": {
"esbuild": "^0.25.0"
},
"dependencies": {
"@codemirror/autocomplete": "^6",
"@codemirror/commands": "^6",
"@codemirror/lang-css": "^6",
"@codemirror/lang-go": "^6",
"@codemirror/lang-html": "^6",
"@codemirror/lang-javascript": "^6",
"@codemirror/lang-json": "^6",
"@codemirror/lang-markdown": "^6",
"@codemirror/lang-python": "^6",
"@codemirror/lang-rust": "^6",
"@codemirror/lang-sql": "^6",
"@codemirror/lang-yaml": "^6",
"@codemirror/language": "^6",
"@codemirror/search": "^6",
"@codemirror/state": "^6",
"@codemirror/theme-one-dark": "^6",
"@codemirror/view": "^6",
"@replit/codemirror-vim": "^6",
"@replit/codemirror-emacs": "^6"
}
}

186
src/editor/theme.mjs Normal file
View File

@@ -0,0 +1,186 @@
// ==========================================
// Chat Switchboard — CM6 Theme
// ==========================================
// Maps the app's CSS custom properties to CM6
// theme slots. Works in both light and dark mode.
//
// The theme is applied via EditorView.theme()
// which generates scoped CSS classes. Dark mode
// detection uses body.dark-theme (existing pattern).
// ==========================================
import { EditorView } from '@codemirror/view';
/**
* Switchboard base theme — uses CSS variables so it
* adapts automatically when the theme toggle fires.
*/
export const switchboardTheme = EditorView.theme({
'&': {
backgroundColor: 'var(--bg-surface, var(--bg))',
color: 'var(--text)',
fontSize: '14px',
},
'.cm-content': {
fontFamily: 'var(--mono, monospace)',
caretColor: 'var(--accent)',
padding: '4px 0',
},
'&.cm-focused': {
outline: 'none',
},
'.cm-cursor, .cm-dropCursor': {
borderLeftColor: 'var(--accent)',
borderLeftWidth: '2px',
},
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground': {
backgroundColor: 'var(--accent-muted, rgba(99, 102, 241, 0.2))',
},
'.cm-activeLine': {
backgroundColor: 'var(--bg-hover, rgba(255, 255, 255, 0.03))',
},
'.cm-gutters': {
backgroundColor: 'var(--bg-2, var(--bg))',
color: 'var(--text-3, #666)',
borderRight: '1px solid var(--border)',
minWidth: '40px',
},
'.cm-activeLineGutter': {
backgroundColor: 'var(--bg-hover, rgba(255, 255, 255, 0.05))',
color: 'var(--text-2)',
},
'.cm-foldPlaceholder': {
backgroundColor: 'var(--bg-2)',
color: 'var(--text-3)',
border: '1px solid var(--border)',
borderRadius: '3px',
padding: '0 4px',
},
// Search panel
'.cm-panels': {
backgroundColor: 'var(--bg-2)',
color: 'var(--text)',
borderBottom: '1px solid var(--border)',
},
'.cm-panels.cm-panels-top': {
borderBottom: '1px solid var(--border)',
},
'.cm-panels.cm-panels-bottom': {
borderTop: '1px solid var(--border)',
},
'.cm-search input, .cm-search button': {
backgroundColor: 'var(--bg-surface)',
color: 'var(--text)',
border: '1px solid var(--border)',
borderRadius: 'var(--radius, 4px)',
fontSize: '13px',
},
'.cm-search input:focus': {
borderColor: 'var(--accent)',
outline: 'none',
},
// Autocomplete
'.cm-tooltip': {
backgroundColor: 'var(--bg-2)',
color: 'var(--text)',
border: '1px solid var(--border)',
borderRadius: 'var(--radius, 4px)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
},
'.cm-tooltip-autocomplete > ul > li': {
padding: '4px 8px',
},
'.cm-tooltip-autocomplete > ul > li[aria-selected]': {
backgroundColor: 'var(--accent)',
color: '#fff',
},
// Matching brackets
'.cm-matchingBracket': {
backgroundColor: 'var(--accent-muted, rgba(99, 102, 241, 0.3))',
outline: '1px solid var(--accent)',
},
'.cm-nonmatchingBracket': {
color: 'var(--danger, #e74c3c)',
},
// Scrollbar
'.cm-scroller': {
fontFamily: 'var(--mono, monospace)',
overflow: 'auto',
},
});
/**
* Minimal theme for the chat input — removes gutters,
* line numbers, and active line highlighting.
*/
export const chatInputTheme = EditorView.theme({
'&': {
backgroundColor: 'transparent',
color: 'var(--text)',
fontSize: 'var(--msg-font, 14px)',
},
'.cm-content': {
fontFamily: 'var(--font, system-ui, sans-serif)',
caretColor: 'var(--text)',
padding: '0',
minHeight: '20px',
},
'&.cm-focused': {
outline: 'none',
},
// Cursor: high-contrast, always visible in both themes
'.cm-cursor, .cm-dropCursor': {
borderLeftColor: 'var(--text)',
borderLeftWidth: '2px',
},
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground': {
backgroundColor: 'var(--accent-dim, rgba(99, 102, 241, 0.2))',
},
'.cm-line': {
padding: '0',
},
'.cm-scroller': {
fontFamily: 'var(--font, system-ui, sans-serif)',
overflow: 'auto',
},
// Hide scrollbar but keep scrollable for auto-grow
'.cm-scroller::-webkit-scrollbar': {
display: 'none',
},
// Placeholder — vertically aligned with first line of input
'.cm-placeholder': {
color: 'var(--text-3, #888)',
fontStyle: 'normal',
lineHeight: 'inherit',
pointerEvents: 'none',
},
// Tooltip/autocomplete inherits from base
'.cm-tooltip': {
backgroundColor: 'var(--bg-2)',
color: 'var(--text)',
border: '1px solid var(--border)',
borderRadius: 'var(--radius, 4px)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
},
// ── Fenced code block decorations (WYSIWYG) ──
// Lines inside fenced code blocks get a visual container
'.cm-codeblock': {
backgroundColor: 'var(--bg-surface, #18181b)',
fontFamily: 'var(--mono, monospace)',
fontSize: '13px',
padding: '0 10px',
borderLeft: '2px solid var(--accent, #6c9fff)',
},
'.cm-codeblock.cm-codeblock-open': {
borderTopLeftRadius: 'var(--radius, 6px)',
borderTopRightRadius: 'var(--radius, 6px)',
paddingTop: '6px',
color: 'var(--text-3, #6b6b7b)',
},
'.cm-codeblock.cm-codeblock-close': {
borderBottomLeftRadius: 'var(--radius, 6px)',
borderBottomRightRadius: 'var(--radius, 6px)',
paddingBottom: '6px',
color: 'var(--text-3, #6b6b7b)',
},
});