From 87feac447b6488621ea98f2abb050e3398c8c741 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Dec 2023 09:42:25 +0100 Subject: [PATCH] - make codemirror package more capable - add themes from react package - add Autocomplete from react package - handle dynamic extensions - handle keyboard shortcuts - make StrudelMirror as capable as Repl --- packages/codemirror/Autocomplete.jsx | 88 ++++ packages/codemirror/codemirror.mjs | 209 ++++++-- packages/codemirror/flash.mjs | 2 + packages/codemirror/highlight.mjs | 9 + packages/codemirror/keybindings.mjs | 31 ++ packages/codemirror/package.json | 10 +- packages/codemirror/themes.mjs | 484 +++++++++++++++++++ packages/codemirror/themes/algoboy.mjs | 41 ++ packages/codemirror/themes/blackscreen.mjs | 38 ++ packages/codemirror/themes/bluescreen.mjs | 41 ++ packages/codemirror/themes/one-dark.mjs | 139 ------ packages/codemirror/themes/strudel-theme.mjs | 45 ++ packages/codemirror/themes/teletext.mjs | 50 ++ packages/codemirror/themes/terminal.mjs | 36 ++ packages/codemirror/themes/whitescreen.mjs | 38 ++ pnpm-lock.yaml | 39 ++ 16 files changed, 1120 insertions(+), 180 deletions(-) create mode 100644 packages/codemirror/Autocomplete.jsx create mode 100644 packages/codemirror/keybindings.mjs create mode 100644 packages/codemirror/themes.mjs create mode 100644 packages/codemirror/themes/algoboy.mjs create mode 100644 packages/codemirror/themes/blackscreen.mjs create mode 100644 packages/codemirror/themes/bluescreen.mjs delete mode 100644 packages/codemirror/themes/one-dark.mjs create mode 100644 packages/codemirror/themes/strudel-theme.mjs create mode 100644 packages/codemirror/themes/teletext.mjs create mode 100644 packages/codemirror/themes/terminal.mjs create mode 100644 packages/codemirror/themes/whitescreen.mjs diff --git a/packages/codemirror/Autocomplete.jsx b/packages/codemirror/Autocomplete.jsx new file mode 100644 index 00000000..18f172ee --- /dev/null +++ b/packages/codemirror/Autocomplete.jsx @@ -0,0 +1,88 @@ +import { createRoot } from 'react-dom/client'; +import jsdoc from '../../doc.json'; +// import { javascriptLanguage } from '@codemirror/lang-javascript'; +import { autocompletion } from '@codemirror/autocomplete'; + +const getDocLabel = (doc) => doc.name || doc.longname; +const getInnerText = (html) => { + var div = document.createElement('div'); + div.innerHTML = html; + return div.textContent || div.innerText || ''; +}; + +export function Autocomplete({ doc }) { + return ( +
+

{getDocLabel(doc)}

+
+
    + {doc.params?.map(({ name, type, description }, i) => ( +
  • + {name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)} : ''} +
  • + ))} +
+
+ {doc.examples?.map((example, i) => ( +
+
 {
+                console.log('ola!');
+                navigator.clipboard.writeText(example);
+                e.stopPropagation();
+              }}
+            >
+              {example}
+            
+
+ ))} +
+
+ ); +} + +const jsdocCompletions = jsdoc.docs + .filter( + (doc) => + getDocLabel(doc) && + !getDocLabel(doc).startsWith('_') && + !['package'].includes(doc.kind) && + !['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)), + ) + // https://codemirror.net/docs/ref/#autocomplete.Completion + .map((doc) /*: Completion */ => ({ + label: getDocLabel(doc), + // detail: 'xxx', // An optional short piece of information to show (with a different style) after the label. + info: () => { + const node = document.createElement('div'); + // if Autocomplete is non-interactive, it could also be rendered at build time.. + // .. using renderToStaticMarkup + createRoot(node).render(); + return node; + }, + type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type + })); + +export const strudelAutocomplete = (context /* : CompletionContext */) => { + let word = context.matchBefore(/\w*/); + if (word.from == word.to && !context.explicit) return null; + return { + from: word.from, + options: jsdocCompletions, + /* options: [ + { label: 'match', type: 'keyword' }, + { label: 'hello', type: 'variable', info: '(World)' }, + { label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' }, + ], */ + }; +}; + +export function isAutoCompletionEnabled(on) { + return on + ? [ + autocompletion({ override: [strudelAutocomplete] }), + //javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }), + ] + : []; // autocompletion({ override: [] }) +} diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 2094416e..8c64372d 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -1,36 +1,78 @@ -import { defaultKeymap } from '@codemirror/commands'; +import { closeBrackets } from '@codemirror/autocomplete'; +// import { search, highlightSelectionMatches } from '@codemirror/search'; +import { history } from '@codemirror/commands'; import { javascript } from '@codemirror/lang-javascript'; import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language'; -import { EditorState } from '@codemirror/state'; -import { EditorView, highlightActiveLineGutter, keymap, lineNumbers } from '@codemirror/view'; -import { Drawer, repl } from '@strudel.cycles/core'; -import { flashField, flash } from './flash.mjs'; -import { highlightExtension, highlightMiniLocations } from './highlight.mjs'; -import { oneDark } from './themes/one-dark'; +import { Compartment, EditorState } from '@codemirror/state'; +import { EditorView, highlightActiveLineGutter, highlightActiveLine, keymap, lineNumbers } from '@codemirror/view'; +import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core'; +import { isAutoCompletionEnabled } from './Autocomplete'; +import { flash, isFlashEnabled } from './flash.mjs'; +import { highlightMiniLocations, isPatternHighlightingEnabled, updateMiniLocations } from './highlight.mjs'; +import { keybindings } from './keybindings.mjs'; +import { theme } from './themes.mjs'; +import { updateWidgets, sliderPlugin } from './slider.mjs'; + +const extensions = { + isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []), + isLineNumbersDisplayed: (on) => (on ? lineNumbers() : []), + theme, + isAutoCompletionEnabled, + isPatternHighlightingEnabled, + isFlashEnabled, + keybindings, +}; +const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()])); // https://codemirror.net/docs/guide/ -export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, theme = oneDark, root }) { +export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, settings, root }) { + const initialSettings = Object.keys(compartments).map((key) => + compartments[key].of(extensions[key](parseBooleans(settings[key]))), + ); let state = EditorState.create({ doc: initialCode, extensions: [ - theme, + /* search(), + highlightSelectionMatches(), */ + ...initialSettings, javascript(), - lineNumbers(), - highlightExtension, + sliderPlugin, + // indentOnInput(), // works without. already brought with javascript extension? + // bracketMatching(), // does not do anything + closeBrackets(), highlightActiveLineGutter(), + highlightActiveLine(), syntaxHighlighting(defaultHighlightStyle), - keymap.of(defaultKeymap), - flashField, + history(), EditorView.updateListener.of((v) => onChange(v)), keymap.of([ { key: 'Ctrl-Enter', - run: () => onEvaluate(), + run: () => onEvaluate?.(), + }, + { + key: 'Alt-Enter', + run: () => onEvaluate?.(), }, { key: 'Ctrl-.', - run: () => onStop(), + run: () => onStop?.(), }, + { + key: 'Alt-.', + run: (_, e) => { + e.preventDefault(); + onStop?.(); + }, + }, + /* { + key: 'Ctrl-Shift-.', + run: () => (onPanic ? onPanic() : onStop?.()), + }, + { + key: 'Ctrl-Shift-Enter', + run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()), + }, */ ]), ], }); @@ -43,71 +85,158 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, the export class StrudelMirror { constructor(options) { - const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, ...replOptions } = options; + const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, settings, ...replOptions } = options; this.code = initialCode; + this.root = root; + this.miniLocations = []; + this.widgets = []; + this.painters = []; + this.onDraw = onDraw; + const self = this; this.drawer = new Drawer((haps, time) => { const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.endClipped); this.highlight(currentFrame, time); - onDraw?.(haps, time, currentFrame); + this.onDraw?.(haps, time, currentFrame, this.painters); }, drawTime); - const prebaked = prebake(); - prebaked.then(async () => { - if (!onDraw) { - return; - } - const { scheduler, evaluate } = await this.repl; - // draw first frame instantly - prebaked.then(async () => { - await evaluate(this.code, false); - this.drawer.invalidate(scheduler); - onDraw?.(this.drawer.visibleHaps, 0, []); - }); - }); + // this approach might not work with multiple repls on screen.. + Pattern.prototype.onPaint = function (onPaint) { + self.painters.push(onPaint); + return this; + }; + + this.prebaked = prebake(); + // this.drawFirstFrame(); this.repl = repl({ ...replOptions, - onToggle: async (started) => { + onToggle: (started) => { replOptions?.onToggle?.(started); - const { scheduler } = await this.repl; if (started) { - this.drawer.start(scheduler); + this.drawer.start(this.repl.scheduler); } else { this.drawer.stop(); + updateMiniLocations(this.editor, []); + cleanupDraw(false); } }, beforeEval: async () => { - await prebaked; + cleanupDraw(); + this.painters = []; + await this.prebaked; + await replOptions?.beforeEval?.(); }, afterEval: (options) => { + // remember for when highlighting is toggled on + this.miniLocations = options.meta?.miniLocations; + this.widgets = options.meta?.widgets; + updateWidgets(this.editor, this.widgets); + updateMiniLocations(this.editor, this.miniLocations); replOptions?.afterEval?.(options); this.drawer.invalidate(); }, }); this.editor = initEditor({ root, + settings, initialCode, onChange: (v) => { - this.code = v.state.doc.toString(); + if (v.docChanged) { + this.code = v.state.doc.toString(); + this.repl.setCode(this.code); + } }, onEvaluate: () => this.evaluate(), onStop: () => this.stop(), }); } + async drawFirstFrame() { + if (!this.onDraw) { + return; + } + // draw first frame instantly + await this.prebaked; + try { + await this.repl.evaluate(this.code, false); + this.drawer.invalidate(this.repl.scheduler); + this.onDraw?.(this.drawer.visibleHaps, 0, []); + } catch (err) { + console.warn('first frame could not be painted'); + } + } async evaluate() { - const { evaluate } = await this.repl; this.flash(); - await evaluate(this.code); + await this.repl.evaluate(this.code); } async stop() { - const { scheduler } = await this.repl; - scheduler.stop(); + this.repl.scheduler.stop(); + } + async toggle() { + if (this.repl.scheduler.started) { + this.repl.scheduler.stop(); + } else { + this.evaluate(); + } } flash(ms) { flash(this.editor, ms); } highlight(haps, time) { - highlightMiniLocations(this.editor.view, time, haps); + highlightMiniLocations(this.editor, time, haps); + } + setFontSize(size) { + this.root.style.fontSize = size + 'px'; + } + setFontFamily(family) { + this.root.style.fontFamily = family; + } + reconfigureExtension(key, value) { + if (!extensions[key]) { + console.warn(`extension ${key} is not known`); + return; + } + value = parseBooleans(value); + const newValue = extensions[key](value, this); + this.editor.dispatch({ + effects: compartments[key].reconfigure(newValue), + }); + } + setLineWrappingEnabled(enabled) { + this.reconfigureExtension('isLineWrappingEnabled', enabled); + } + setLineNumbersDisplayed(enabled) { + this.reconfigureExtension('isLineNumbersDisplayed', enabled); + } + setTheme(theme) { + this.reconfigureExtension('theme', theme); + } + setAutocompletionEnabled(enabled) { + this.reconfigureExtension('isAutoCompletionEnabled', enabled); + } + updateSettings(settings) { + this.setFontSize(settings.fontSize); + this.setFontFamily(settings.fontFamily); + for (let key in extensions) { + this.reconfigureExtension(key, settings[key]); + } + } + changeSetting(key, value) { + if (extensions[key]) { + this.reconfigureExtension(key, value); + return; + } else if (key === 'fontFamily') { + this.setFontFamily(value); + } else if (key === 'fontSize') { + this.setFontSize(value); + } + } + setCode(code) { + const changes = { from: 0, to: this.editor.state.doc.length, insert: code }; + this.editor.dispatch({ changes }); } } + +function parseBooleans(value) { + return { true: true, false: false }[value] ?? value; +} diff --git a/packages/codemirror/flash.mjs b/packages/codemirror/flash.mjs index 9bc5c593..6b37038f 100644 --- a/packages/codemirror/flash.mjs +++ b/packages/codemirror/flash.mjs @@ -33,3 +33,5 @@ export const flash = (view, ms = 200) => { view.dispatch({ effects: setFlash.of(false) }); }, ms); }; + +export const isFlashEnabled = (on) => (on ? flashField : []); diff --git a/packages/codemirror/highlight.mjs b/packages/codemirror/highlight.mjs index 317c5fdf..79724f8f 100644 --- a/packages/codemirror/highlight.mjs +++ b/packages/codemirror/highlight.mjs @@ -124,3 +124,12 @@ const miniLocationHighlights = EditorView.decorations.compute([miniLocations, vi }); export const highlightExtension = [miniLocations, visibleMiniLocations, miniLocationHighlights]; + +export const isPatternHighlightingEnabled = (on, config) => { + on && + config && + setTimeout(() => { + updateMiniLocations(config.editor, config.miniLocations); + }, 100); + return on ? highlightExtension : []; +}; diff --git a/packages/codemirror/keybindings.mjs b/packages/codemirror/keybindings.mjs new file mode 100644 index 00000000..6fe00eda --- /dev/null +++ b/packages/codemirror/keybindings.mjs @@ -0,0 +1,31 @@ +import { Prec } from '@codemirror/state'; +import { keymap, ViewPlugin } from '@codemirror/view'; +// import { searchKeymap } from '@codemirror/search'; +import { emacs } from '@replit/codemirror-emacs'; +import { vim } from '@replit/codemirror-vim'; +import { vscodeKeymap } from '@replit/codemirror-vscode-keymap'; +import { defaultKeymap, historyKeymap } from '@codemirror/commands'; + +const vscodePlugin = ViewPlugin.fromClass( + class { + constructor() {} + }, + { + provide: () => { + return Prec.highest(keymap.of([...vscodeKeymap])); + }, + }, +); +const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []); + +const keymaps = { + vim, + emacs, + vscode: vscodeExtension, +}; + +export function keybindings(name) { + const active = keymaps[name]; + return [keymap.of(defaultKeymap), keymap.of(historyKeymap), active ? active() : []]; + // keymap.of(searchKeymap), +} diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index 4e443648..a309efa2 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -33,13 +33,21 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { + "@codemirror/autocomplete": "^6.6.0", "@codemirror/commands": "^6.2.4", "@codemirror/lang-javascript": "^6.1.7", "@codemirror/language": "^6.6.0", + "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.2.0", "@codemirror/view": "^6.10.0", "@lezer/highlight": "^1.1.4", - "@strudel.cycles/core": "workspace:*" + "@replit/codemirror-emacs": "^6.0.1", + "@replit/codemirror-vim": "^6.0.14", + "@replit/codemirror-vscode-keymap": "^6.0.2", + "@strudel.cycles/core": "workspace:*", + "@uiw/codemirror-themes": "^4.19.16", + "@uiw/codemirror-themes-all": "^4.19.16", + "react-dom": "^18.2.0" }, "devDependencies": { "vite": "^4.3.3" diff --git a/packages/codemirror/themes.mjs b/packages/codemirror/themes.mjs new file mode 100644 index 00000000..1f520623 --- /dev/null +++ b/packages/codemirror/themes.mjs @@ -0,0 +1,484 @@ +import { + abcdef, + androidstudio, + atomone, + aura, + bespin, + darcula, + dracula, + duotoneDark, + eclipse, + githubDark, + gruvboxDark, + materialDark, + nord, + okaidia, + solarizedDark, + sublime, + tokyoNight, + tokyoNightStorm, + vscodeDark, + xcodeDark, + bbedit, + duotoneLight, + githubLight, + gruvboxLight, + materialLight, + noctisLilac, + solarizedLight, + tokyoNightDay, + xcodeLight, +} from '@uiw/codemirror-themes-all'; + +import strudelTheme from './themes/strudel-theme'; +import bluescreen, { settings as bluescreenSettings } from './themes/bluescreen'; +import blackscreen, { settings as blackscreenSettings } from './themes/blackscreen'; +import whitescreen, { settings as whitescreenSettings } from './themes/whitescreen'; +import teletext, { settings as teletextSettings } from './themes/teletext'; +import algoboy, { settings as algoboySettings } from './themes/algoboy'; +import terminal, { settings as terminalSettings } from './themes/terminal'; + +export const themes = { + strudelTheme, + bluescreen, + blackscreen, + whitescreen, + teletext, + algoboy, + terminal, + abcdef, + androidstudio, + atomone, + aura, + bespin, + darcula, + dracula, + duotoneDark, + eclipse, + githubDark, + gruvboxDark, + materialDark, + nord, + okaidia, + solarizedDark, + sublime, + tokyoNight, + tokyoNightStorm, + vscodeDark, + xcodeDark, + bbedit, + duotoneLight, + githubLight, + gruvboxLight, + materialLight, + noctisLilac, + solarizedLight, + tokyoNightDay, + xcodeLight, +}; + +// lineBackground is background with 50% opacity, to make sure the selection below is visible + +export const settings = { + strudelTheme: { + background: '#222', + lineBackground: '#22222299', + foreground: '#fff', + // foreground: '#75baff', + caret: '#ffcc00', + selection: 'rgba(128, 203, 196, 0.5)', + selectionMatch: '#036dd626', + // lineHighlight: '#8a91991a', // original + lineHighlight: '#00000050', + gutterBackground: 'transparent', + // gutterForeground: '#8a919966', + gutterForeground: '#8a919966', + }, + bluescreen: bluescreenSettings, + blackscreen: blackscreenSettings, + whitescreen: whitescreenSettings, + teletext: teletextSettings, + algoboy: algoboySettings, + terminal: terminalSettings, + abcdef: { + background: '#0f0f0f', + lineBackground: '#0f0f0f99', + foreground: '#defdef', + caret: '#00FF00', + selection: '#515151', + selectionMatch: '#515151', + gutterBackground: '#555', + gutterForeground: '#FFFFFF', + lineHighlight: '#314151', + }, + androidstudio: { + background: '#282b2e', + lineBackground: '#282b2e99', + foreground: '#a9b7c6', + caret: '#00FF00', + selection: '#343739', + selectionMatch: '#343739', + lineHighlight: '#343739', + }, + atomone: { + background: '#272C35', + lineBackground: '#272C3599', + foreground: '#9d9b97', + caret: '#797977', + selection: '#ffffff30', + selectionMatch: '#2B323D', + gutterBackground: '#272C35', + gutterForeground: '#465063', + gutterBorder: 'transparent', + lineHighlight: '#2B323D', + }, + aura: { + background: '#21202e', + lineBackground: '#21202e99', + foreground: '#edecee', + caret: '#a277ff', + selection: '#3d375e7f', + selectionMatch: '#3d375e7f', + gutterBackground: '#21202e', + gutterForeground: '#edecee', + gutterBorder: 'transparent', + lineHighlight: '#a394f033', + }, + bbedit: { + light: true, + background: '#FFFFFF', + lineBackground: '#FFFFFF99', + foreground: '#000000', + caret: '#FBAC52', + selection: '#FFD420', + selectionMatch: '#FFD420', + gutterBackground: '#f5f5f5', + gutterForeground: '#4D4D4C', + gutterBorder: 'transparent', + lineHighlight: '#00000012', + }, + bespin: { + background: '#28211c', + lineBackground: '#28211c99', + foreground: '#9d9b97', + caret: '#797977', + selection: '#36312e', + selectionMatch: '#4f382b', + gutterBackground: '#28211c', + gutterForeground: '#666666', + lineHighlight: 'rgba(255, 255, 255, 0.1)', + }, + darcula: { + background: '#2B2B2B', + lineBackground: '#2B2B2B99', + foreground: '#f8f8f2', + caret: '#FFFFFF', + selection: 'rgba(255, 255, 255, 0.1)', + selectionMatch: 'rgba(255, 255, 255, 0.2)', + gutterBackground: 'rgba(255, 255, 255, 0.1)', + gutterForeground: '#999', + gutterBorder: 'transparent', + lineHighlight: 'rgba(255, 255, 255, 0.1)', + }, + dracula: { + background: '#282a36', + lineBackground: '#282a3699', + foreground: '#f8f8f2', + caret: '#f8f8f0', + selection: 'rgba(255, 255, 255, 0.1)', + selectionMatch: 'rgba(255, 255, 255, 0.2)', + gutterBackground: '#282a36', + gutterForeground: '#6D8A88', + gutterBorder: 'transparent', + lineHighlight: 'rgba(255, 255, 255, 0.1)', + }, + duotoneLight: { + light: true, + background: '#faf8f5', + lineBackground: '#faf8f599', + foreground: '#b29762', + caret: '#93abdc', + selection: '#e3dcce', + selectionMatch: '#e3dcce', + gutterBackground: '#faf8f5', + gutterForeground: '#cdc4b1', + gutterBorder: 'transparent', + lineHighlight: '#EFEFEF', + }, + duotoneDark: { + background: '#2a2734', + lineBackground: '#2a273499', + foreground: '#6c6783', + caret: '#ffad5c', + selection: 'rgba(255, 255, 255, 0.1)', + gutterBackground: '#2a2734', + gutterForeground: '#545167', + lineHighlight: '#36334280', + }, + eclipse: { + light: true, + background: '#fff', + lineBackground: '#ffffff99', + foreground: '#000', + caret: '#FFFFFF', + selection: '#d7d4f0', + selectionMatch: '#d7d4f0', + gutterBackground: '#f7f7f7', + gutterForeground: '#999', + lineHighlight: '#e8f2ff', + gutterBorder: 'transparent', + }, + githubLight: { + light: true, + background: '#fff', + lineBackground: '#ffffff99', + foreground: '#24292e', + selection: '#BBDFFF', + selectionMatch: '#BBDFFF', + gutterBackground: '#fff', + gutterForeground: '#6e7781', + }, + githubDark: { + background: '#0d1117', + lineBackground: '#0d111799', + foreground: '#c9d1d9', + caret: '#c9d1d9', + selection: '#003d73', + selectionMatch: '#003d73', + lineHighlight: '#36334280', + }, + gruvboxDark: { + background: '#282828', + lineBackground: '#28282899', + foreground: '#ebdbb2', + caret: '#ebdbb2', + selection: '#bdae93', + selectionMatch: '#bdae93', + lineHighlight: '#3c3836', + gutterBackground: '#282828', + gutterForeground: '#7c6f64', + }, + gruvboxLight: { + light: true, + background: '#fbf1c7', + lineBackground: '#fbf1c799', + foreground: '#3c3836', + caret: '#af3a03', + selection: '#ebdbb2', + selectionMatch: '#bdae93', + lineHighlight: '#ebdbb2', + gutterBackground: '#ebdbb2', + gutterForeground: '#665c54', + gutterBorder: 'transparent', + }, + materialDark: { + background: '#2e3235', + lineBackground: '#2e323599', + foreground: '#bdbdbd', + caret: '#a0a4ae', + selection: '#d7d4f0', + selectionMatch: '#d7d4f0', + gutterBackground: '#2e3235', + gutterForeground: '#999', + gutterActiveForeground: '#4f5b66', + lineHighlight: '#545b61', + }, + materialLight: { + light: true, + background: '#FAFAFA', + lineBackground: '#FAFAFA99', + foreground: '#90A4AE', + caret: '#272727', + selection: '#80CBC440', + selectionMatch: '#FAFAFA', + gutterBackground: '#FAFAFA', + gutterForeground: '#90A4AE', + gutterBorder: 'transparent', + lineHighlight: '#CCD7DA50', + }, + noctisLilac: { + light: true, + background: '#f2f1f8', + lineBackground: '#f2f1f899', + foreground: '#0c006b', + caret: '#5c49e9', + selection: '#d5d1f2', + selectionMatch: '#d5d1f2', + gutterBackground: '#f2f1f8', + gutterForeground: '#0c006b70', + lineHighlight: '#e1def3', + }, + nord: { + background: '#2e3440', + lineBackground: '#2e344099', + foreground: '#FFFFFF', + caret: '#FFFFFF', + selection: '#3b4252', + selectionMatch: '#e5e9f0', + gutterBackground: '#2e3440', + gutterForeground: '#4c566a', + gutterActiveForeground: '#d8dee9', + lineHighlight: '#4c566a', + }, + okaidia: { + background: '#272822', + lineBackground: '#27282299', + foreground: '#FFFFFF', + caret: '#FFFFFF', + selection: '#49483E', + selectionMatch: '#49483E', + gutterBackground: '#272822', + gutterForeground: '#FFFFFF70', + lineHighlight: '#00000059', + }, + solarizedLight: { + light: true, + background: '#fdf6e3', + lineBackground: '#fdf6e399', + foreground: '#657b83', + caret: '#586e75', + selection: '#dfd9c8', + selectionMatch: '#dfd9c8', + gutterBackground: '#00000010', + gutterForeground: '#657b83', + lineHighlight: '#dfd9c8', + }, + solarizedDark: { + background: '#002b36', + lineBackground: '#002b3699', + foreground: '#93a1a1', + caret: '#839496', + selection: '#173541', + selectionMatch: '#aafe661a', + gutterBackground: '#00252f', + gutterForeground: '#839496', + lineHighlight: '#173541', + }, + sublime: { + background: '#303841', + lineBackground: '#30384199', + foreground: '#FFFFFF', + caret: '#FBAC52', + selection: '#4C5964', + selectionMatch: '#3A546E', + gutterBackground: '#303841', + gutterForeground: '#FFFFFF70', + lineHighlight: '#00000059', + }, + tokyoNightDay: { + light: true, + background: '#e1e2e7', + lineBackground: '#e1e2e799', + foreground: '#3760bf', + caret: '#3760bf', + selection: '#99a7df', + selectionMatch: '#99a7df', + gutterBackground: '#e1e2e7', + gutterForeground: '#3760bf', + gutterBorder: 'transparent', + lineHighlight: '#5f5faf11', + }, + tokyoNightStorm: { + background: '#24283b', + lineBackground: '#24283b99', + foreground: '#7982a9', + caret: '#c0caf5', + selection: '#6f7bb630', + selectionMatch: '#1f2335', + gutterBackground: '#24283b', + gutterForeground: '#7982a9', + gutterBorder: 'transparent', + lineHighlight: '#292e42', + }, + tokyoNight: { + background: '#1a1b26', + lineBackground: '#1a1b2699', + foreground: '#787c99', + caret: '#c0caf5', + selection: '#515c7e40', + selectionMatch: '#16161e', + gutterBackground: '#1a1b26', + gutterForeground: '#787c99', + gutterBorder: 'transparent', + lineHighlight: '#1e202e', + }, + vscodeDark: { + background: '#1e1e1e', + lineBackground: '#1e1e1e99', + foreground: '#9cdcfe', + caret: '#c6c6c6', + selection: '#6199ff2f', + selectionMatch: '#72a1ff59', + lineHighlight: '#ffffff0f', + gutterBackground: '#1e1e1e', + gutterForeground: '#838383', + gutterActiveForeground: '#fff', + }, + xcodeLight: { + light: true, + background: '#fff', + lineBackground: '#ffffff99', + foreground: '#3D3D3D', + selection: '#BBDFFF', + selectionMatch: '#BBDFFF', + gutterBackground: '#fff', + gutterForeground: '#AFAFAF', + lineHighlight: '#EDF4FF', + }, + xcodeDark: { + background: '#292A30', + lineBackground: '#292A3099', + foreground: '#CECFD0', + caret: '#fff', + selection: '#727377', + selectionMatch: '#727377', + lineHighlight: '#2F3239', + }, +}; + +function getColors(str) { + const colorRegex = /#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})/g; + const colors = []; + + let match; + while ((match = colorRegex.exec(str)) !== null) { + const color = match[0]; + if (!colors.includes(color)) { + colors.push(color); + } + } + + return colors; +} + +// TODO: remove +export function themeColors(theme) { + return getColors(stringifySafe(theme)); +} + +function getCircularReplacer() { + const seen = new WeakSet(); + return (key, value) => { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return; + } + seen.add(value); + } + return value; + }; +} + +function stringifySafe(json) { + return JSON.stringify(json, getCircularReplacer()); +} + +export function injectStyle(rule) { + const newStyle = document.createElement('style'); + document.head.appendChild(newStyle); + const styleSheet = newStyle.sheet; + const ruleIndex = styleSheet.insertRule(rule, 0); + return () => styleSheet.deleteRule(ruleIndex); +} + +export const theme = (theme) => themes[theme] || themes.strudelTheme; diff --git a/packages/codemirror/themes/algoboy.mjs b/packages/codemirror/themes/algoboy.mjs new file mode 100644 index 00000000..399370e1 --- /dev/null +++ b/packages/codemirror/themes/algoboy.mjs @@ -0,0 +1,41 @@ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from '@uiw/codemirror-themes'; +export const settings = { + background: '#9bbc0f', + foreground: '#0f380f', // whats that? + caret: '#0f380f', + selection: '#306230', + selectionMatch: '#ffffff26', + lineHighlight: '#8bac0f', + lineBackground: '#9bbc0f50', + //lineBackground: 'transparent', + gutterBackground: 'transparent', + gutterForeground: '#0f380f', + light: true, + customStyle: '.cm-line { line-height: 1 }', +}; +export default createTheme({ + theme: 'light', + settings, + styles: [ + { tag: t.keyword, color: '#0f380f' }, + { tag: t.operator, color: '#0f380f' }, + { tag: t.special(t.variableName), color: '#0f380f' }, + { tag: t.typeName, color: '#0f380f' }, + { tag: t.atom, color: '#0f380f' }, + { tag: t.number, color: '#0f380f' }, + { tag: t.definition(t.variableName), color: '#0f380f' }, + { tag: t.string, color: '#0f380f' }, + { tag: t.special(t.string), color: '#0f380f' }, + { tag: t.comment, color: '#0f380f' }, + { tag: t.variableName, color: '#0f380f' }, + { tag: t.tagName, color: '#0f380f' }, + { tag: t.bracket, color: '#0f380f' }, + { tag: t.meta, color: '#0f380f' }, + { tag: t.attributeName, color: '#0f380f' }, + { tag: t.propertyName, color: '#0f380f' }, + { tag: t.className, color: '#0f380f' }, + { tag: t.invalid, color: '#0f380f' }, + { tag: [t.unit, t.punctuation], color: '#0f380f' }, + ], +}); diff --git a/packages/codemirror/themes/blackscreen.mjs b/packages/codemirror/themes/blackscreen.mjs new file mode 100644 index 00000000..135285a3 --- /dev/null +++ b/packages/codemirror/themes/blackscreen.mjs @@ -0,0 +1,38 @@ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from '@uiw/codemirror-themes'; +export const settings = { + background: 'black', + foreground: 'white', // whats that? + caret: 'white', + selection: '#ffffff20', + selectionMatch: '#036dd626', + lineHighlight: '#ffffff10', + lineBackground: '#00000050', + gutterBackground: 'transparent', + gutterForeground: '#8a919966', +}; +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { tag: t.keyword, color: 'white' }, + { tag: t.operator, color: 'white' }, + { tag: t.special(t.variableName), color: 'white' }, + { tag: t.typeName, color: 'white' }, + { tag: t.atom, color: 'white' }, + { tag: t.number, color: 'white' }, + { tag: t.definition(t.variableName), color: 'white' }, + { tag: t.string, color: 'white' }, + { tag: t.special(t.string), color: 'white' }, + { tag: t.comment, color: 'white' }, + { tag: t.variableName, color: 'white' }, + { tag: t.tagName, color: 'white' }, + { tag: t.bracket, color: 'white' }, + { tag: t.meta, color: 'white' }, + { tag: t.attributeName, color: 'white' }, + { tag: t.propertyName, color: 'white' }, + { tag: t.className, color: 'white' }, + { tag: t.invalid, color: 'white' }, + { tag: [t.unit, t.punctuation], color: 'white' }, + ], +}); diff --git a/packages/codemirror/themes/bluescreen.mjs b/packages/codemirror/themes/bluescreen.mjs new file mode 100644 index 00000000..aa6489d6 --- /dev/null +++ b/packages/codemirror/themes/bluescreen.mjs @@ -0,0 +1,41 @@ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from '@uiw/codemirror-themes'; +export const settings = { + background: '#051DB5', + lineBackground: '#051DB550', + foreground: 'white', // whats that? + caret: 'white', + selection: 'rgba(128, 203, 196, 0.5)', + selectionMatch: '#036dd626', + // lineHighlight: '#8a91991a', // original + lineHighlight: '#00000050', + gutterBackground: 'transparent', + // gutterForeground: '#8a919966', + gutterForeground: '#8a919966', +}; + +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { tag: t.keyword, color: 'white' }, + { tag: t.operator, color: 'white' }, + { tag: t.special(t.variableName), color: 'white' }, + { tag: t.typeName, color: 'white' }, + { tag: t.atom, color: 'white' }, + { tag: t.number, color: 'white' }, + { tag: t.definition(t.variableName), color: 'white' }, + { tag: t.string, color: 'white' }, + { tag: t.special(t.string), color: 'white' }, + { tag: t.comment, color: 'white' }, + { tag: t.variableName, color: 'white' }, + { tag: t.tagName, color: 'white' }, + { tag: t.bracket, color: 'white' }, + { tag: t.meta, color: 'white' }, + { tag: t.attributeName, color: 'white' }, + { tag: t.propertyName, color: 'white' }, + { tag: t.className, color: 'white' }, + { tag: t.invalid, color: 'white' }, + { tag: [t.unit, t.punctuation], color: 'white' }, + ], +}); diff --git a/packages/codemirror/themes/one-dark.mjs b/packages/codemirror/themes/one-dark.mjs deleted file mode 100644 index cce83699..00000000 --- a/packages/codemirror/themes/one-dark.mjs +++ /dev/null @@ -1,139 +0,0 @@ -import { EditorView } from '@codemirror/view'; -import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'; -import { tags as t } from '@lezer/highlight'; - -// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors - -const chalky = '#e5c07b', - coral = '#e06c75', - cyan = '#56b6c2', - invalid = '#ffffff', - ivory = '#abb2bf', - stone = '#7d8799', // Brightened compared to original to increase contrast - malibu = '#61afef', - sage = '#98c379', - whiskey = '#d19a66', - violet = '#c678dd', - darkBackground = '#21252b', - highlightBackground = '#2c313a', - background = '#282c34', - tooltipBackground = '#353a42', - selection = '#3E4451', - cursor = '#528bff'; - -/// The colors used in the theme, as CSS color strings. -export const color = { - chalky, - coral, - cyan, - invalid, - ivory, - stone, - malibu, - sage, - whiskey, - violet, - darkBackground, - highlightBackground, - background, - tooltipBackground, - selection, - cursor, -}; - -/// The editor theme styles for One Dark. -export const oneDarkTheme = EditorView.theme( - { - '&': { - color: ivory, - backgroundColor: background, - }, - - '.cm-content': { - caretColor: cursor, - }, - - '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor }, - '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': - { backgroundColor: selection }, - - '.cm-panels': { backgroundColor: darkBackground, color: ivory }, - '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' }, - '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' }, - - '.cm-searchMatch': { - backgroundColor: '#72a1ff59', - outline: '1px solid #457dff', - }, - '.cm-searchMatch.cm-searchMatch-selected': { - backgroundColor: '#6199ff2f', - }, - - '.cm-activeLine': { backgroundColor: '#6699ff0b' }, - '.cm-selectionMatch': { backgroundColor: '#aafe661a' }, - - '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': { - backgroundColor: '#bad0f847', - }, - - '.cm-gutters': { - backgroundColor: background, - color: stone, - border: 'none', - }, - - '.cm-activeLineGutter': { - backgroundColor: highlightBackground, - }, - - '.cm-foldPlaceholder': { - backgroundColor: 'transparent', - border: 'none', - color: '#ddd', - }, - - '.cm-tooltip': { - border: 'none', - backgroundColor: tooltipBackground, - }, - '.cm-tooltip .cm-tooltip-arrow:before': { - borderTopColor: 'transparent', - borderBottomColor: 'transparent', - }, - '.cm-tooltip .cm-tooltip-arrow:after': { - borderTopColor: tooltipBackground, - borderBottomColor: tooltipBackground, - }, - '.cm-tooltip-autocomplete': { - '& > ul > li[aria-selected]': { - backgroundColor: highlightBackground, - color: ivory, - }, - }, - }, - { dark: true }, -); - -/// The highlighting style for code in the One Dark theme. -export const oneDarkHighlightStyle = HighlightStyle.define([ - { tag: t.keyword, color: violet }, - { tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: coral }, - { tag: [t.function(t.variableName), t.labelName], color: malibu }, - { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey }, - { tag: [t.definition(t.name), t.separator], color: ivory }, - { tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: chalky }, - { tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], color: cyan }, - { tag: [t.meta, t.comment], color: stone }, - { tag: t.strong, fontWeight: 'bold' }, - { tag: t.emphasis, fontStyle: 'italic' }, - { tag: t.strikethrough, textDecoration: 'line-through' }, - { tag: t.link, color: stone, textDecoration: 'underline' }, - { tag: t.heading, fontWeight: 'bold', color: coral }, - { tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey }, - { tag: [t.processingInstruction, t.string, t.inserted], color: sage }, - { tag: t.invalid, color: invalid }, -]); - -/// Extension to enable the One Dark theme (both the editor theme and -/// the highlight style). -export const oneDark = [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)]; diff --git a/packages/codemirror/themes/strudel-theme.mjs b/packages/codemirror/themes/strudel-theme.mjs new file mode 100644 index 00000000..4ae31060 --- /dev/null +++ b/packages/codemirror/themes/strudel-theme.mjs @@ -0,0 +1,45 @@ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from '@uiw/codemirror-themes'; +export default createTheme({ + theme: 'dark', + settings: { + background: '#222', + foreground: '#75baff', // whats that? + caret: '#ffcc00', + selection: 'rgba(128, 203, 196, 0.5)', + selectionMatch: '#036dd626', + // lineHighlight: '#8a91991a', // original + lineHighlight: '#00000050', + gutterBackground: 'transparent', + // gutterForeground: '#8a919966', + gutterForeground: '#8a919966', + }, + styles: [ + { tag: t.keyword, color: '#c792ea' }, + { tag: t.operator, color: '#89ddff' }, + { tag: t.special(t.variableName), color: '#eeffff' }, + // { tag: t.typeName, color: '#f07178' }, // original + { tag: t.typeName, color: '#c3e88d' }, + { tag: t.atom, color: '#f78c6c' }, + // { tag: t.number, color: '#ff5370' }, // original + { tag: t.number, color: '#c3e88d' }, + { tag: t.definition(t.variableName), color: '#82aaff' }, + { tag: t.string, color: '#c3e88d' }, + // { tag: t.special(t.string), color: '#f07178' }, // original + { tag: t.special(t.string), color: '#c3e88d' }, + { tag: t.comment, color: '#7d8799' }, + // { tag: t.variableName, color: '#f07178' }, // original + { tag: t.variableName, color: '#c792ea' }, + // { tag: t.tagName, color: '#ff5370' }, // original + { tag: t.tagName, color: '#c3e88d' }, + { tag: t.bracket, color: '#525154' }, + // { tag: t.bracket, color: '#a2a1a4' }, // original + { tag: t.meta, color: '#ffcb6b' }, + { tag: t.attributeName, color: '#c792ea' }, + { tag: t.propertyName, color: '#c792ea' }, + + { tag: t.className, color: '#decb6b' }, + { tag: t.invalid, color: '#ffffff' }, + { tag: [t.unit, t.punctuation], color: '#82aaff' }, + ], +}); diff --git a/packages/codemirror/themes/teletext.mjs b/packages/codemirror/themes/teletext.mjs new file mode 100644 index 00000000..5fd9a557 --- /dev/null +++ b/packages/codemirror/themes/teletext.mjs @@ -0,0 +1,50 @@ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from '@uiw/codemirror-themes'; + +let colorA = '#6edee4'; +//let colorB = 'magenta'; +let colorB = 'white'; +let colorC = 'red'; +let colorD = '#f8fc55'; + +export const settings = { + background: '#000000', + foreground: colorA, // whats that? + caret: colorC, + selection: colorD, + selectionMatch: colorA, + lineHighlight: '#6edee440', // panel bg + lineBackground: '#00000040', + gutterBackground: 'transparent', + gutterForeground: '#8a919966', + customStyle: '.cm-line { line-height: 1 }', +}; + +let punctuation = colorD; +let mini = colorB; + +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { tag: t.keyword, color: colorA }, + { tag: t.operator, color: mini }, + { tag: t.special(t.variableName), color: colorA }, + { tag: t.typeName, color: colorA }, + { tag: t.atom, color: colorA }, + { tag: t.number, color: mini }, + { tag: t.definition(t.variableName), color: colorA }, + { tag: t.string, color: mini }, + { tag: t.special(t.string), color: mini }, + { tag: t.comment, color: punctuation }, + { tag: t.variableName, color: colorA }, + { tag: t.tagName, color: colorA }, + { tag: t.bracket, color: punctuation }, + { tag: t.meta, color: colorA }, + { tag: t.attributeName, color: colorA }, + { tag: t.propertyName, color: colorA }, // methods + { tag: t.className, color: colorA }, + { tag: t.invalid, color: colorC }, + { tag: [t.unit, t.punctuation], color: punctuation }, + ], +}); diff --git a/packages/codemirror/themes/terminal.mjs b/packages/codemirror/themes/terminal.mjs new file mode 100644 index 00000000..1374bb86 --- /dev/null +++ b/packages/codemirror/themes/terminal.mjs @@ -0,0 +1,36 @@ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from '@uiw/codemirror-themes'; +export const settings = { + background: 'black', + foreground: '#41FF00', // whats that? + caret: '#41FF00', + selection: '#ffffff20', + selectionMatch: '#036dd626', + lineHighlight: '#ffffff10', + gutterBackground: 'transparent', + gutterForeground: '#8a919966', +}; +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { tag: t.keyword, color: '#41FF00' }, + { tag: t.operator, color: '#41FF00' }, + { tag: t.special(t.variableName), color: '#41FF00' }, + { tag: t.typeName, color: '#41FF00' }, + { tag: t.atom, color: '#41FF00' }, + { tag: t.number, color: '#41FF00' }, + { tag: t.definition(t.variableName), color: '#41FF00' }, + { tag: t.string, color: '#41FF00' }, + { tag: t.special(t.string), color: '#41FF00' }, + { tag: t.comment, color: '#41FF00' }, + { tag: t.variableName, color: '#41FF00' }, + { tag: t.tagName, color: '#41FF00' }, + { tag: t.bracket, color: '#41FF00' }, + { tag: t.meta, color: '#41FF00' }, + { tag: t.attributeName, color: '#41FF00' }, + { tag: t.propertyName, color: '#41FF00' }, + { tag: t.className, color: '#41FF00' }, + { tag: t.invalid, color: '#41FF00' }, + ], +}); diff --git a/packages/codemirror/themes/whitescreen.mjs b/packages/codemirror/themes/whitescreen.mjs new file mode 100644 index 00000000..22abad9e --- /dev/null +++ b/packages/codemirror/themes/whitescreen.mjs @@ -0,0 +1,38 @@ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from '@uiw/codemirror-themes'; +export const settings = { + background: 'white', + foreground: 'black', // whats that? + caret: 'black', + selection: 'rgba(128, 203, 196, 0.5)', + selectionMatch: '#ffffff26', + lineHighlight: '#cccccc50', + lineBackground: '#ffffff50', + gutterBackground: 'transparent', + gutterForeground: 'black', + light: true, +}; +export default createTheme({ + theme: 'light', + settings, + styles: [ + { tag: t.keyword, color: 'black' }, + { tag: t.operator, color: 'black' }, + { tag: t.special(t.variableName), color: 'black' }, + { tag: t.typeName, color: 'black' }, + { tag: t.atom, color: 'black' }, + { tag: t.number, color: 'black' }, + { tag: t.definition(t.variableName), color: 'black' }, + { tag: t.string, color: 'black' }, + { tag: t.special(t.string), color: 'black' }, + { tag: t.comment, color: 'black' }, + { tag: t.variableName, color: 'black' }, + { tag: t.tagName, color: 'black' }, + { tag: t.bracket, color: 'black' }, + { tag: t.meta, color: 'black' }, + { tag: t.attributeName, color: 'black' }, + { tag: t.propertyName, color: 'black' }, + { tag: t.className, color: 'black' }, + { tag: t.invalid, color: 'black' }, + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 98f6441f..f6f446e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + importers: .: @@ -71,6 +75,9 @@ importers: packages/codemirror: dependencies: + '@codemirror/autocomplete': + specifier: ^6.6.0 + version: 6.6.0(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0)(@lezer/common@1.0.2) '@codemirror/commands': specifier: ^6.2.4 version: 6.2.4 @@ -80,6 +87,9 @@ importers: '@codemirror/language': specifier: ^6.6.0 version: 6.6.0 + '@codemirror/search': + specifier: ^6.0.0 + version: 6.2.3 '@codemirror/state': specifier: ^6.2.0 version: 6.2.0 @@ -89,9 +99,27 @@ importers: '@lezer/highlight': specifier: ^1.1.4 version: 1.1.4 + '@replit/codemirror-emacs': + specifier: ^6.0.1 + version: 6.0.1(@codemirror/autocomplete@6.6.0)(@codemirror/commands@6.2.4)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + '@replit/codemirror-vim': + specifier: ^6.0.14 + version: 6.0.14(@codemirror/commands@6.2.4)(@codemirror/language@6.6.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + '@replit/codemirror-vscode-keymap': + specifier: ^6.0.2 + version: 6.0.2(@codemirror/autocomplete@6.6.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.6.0)(@codemirror/lint@6.1.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) '@strudel.cycles/core': specifier: workspace:* version: link:../core + '@uiw/codemirror-themes': + specifier: ^4.19.16 + version: 4.19.16(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + '@uiw/codemirror-themes-all': + specifier: ^4.19.16 + version: 4.19.16(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) devDependencies: vite: specifier: ^4.3.3 @@ -5572,6 +5600,7 @@ packages: /b4a@1.6.4: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + requiresBuild: true optional: true /babel-plugin-add-module-exports@0.2.1: @@ -6154,6 +6183,7 @@ packages: /color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + requiresBuild: true dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 @@ -6166,6 +6196,7 @@ packages: /color@4.2.3: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + requiresBuild: true dependencies: color-convert: 2.0.1 color-string: 1.9.1 @@ -6629,6 +6660,7 @@ packages: /detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} + requiresBuild: true optional: true /detective-amd@4.0.1: @@ -7472,6 +7504,7 @@ packages: /fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + requiresBuild: true optional: true /fast-glob@3.2.12: @@ -10579,6 +10612,7 @@ packages: /node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + requiresBuild: true optional: true /node-domexception@1.0.0: @@ -11875,6 +11909,7 @@ packages: /queue-tick@1.0.1: resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + requiresBuild: true optional: true /quick-lru@4.0.1: @@ -12730,6 +12765,7 @@ packages: /simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + requiresBuild: true dependencies: is-arrayish: 0.3.2 optional: true @@ -12943,6 +12979,7 @@ packages: /streamx@2.15.2: resolution: {integrity: sha512-b62pAV/aeMjUoRN2C/9F0n+G8AfcJjNC0zw/ZmOHeFsIe4m4GzjVW9m6VHXVjk536NbdU9JRwKMJRfkc+zUFTg==} + requiresBuild: true dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 @@ -13220,6 +13257,7 @@ packages: /tar-fs@3.0.4: resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} + requiresBuild: true dependencies: mkdirp-classic: 0.5.3 pump: 3.0.0 @@ -13238,6 +13276,7 @@ packages: /tar-stream@3.1.6: resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + requiresBuild: true dependencies: b4a: 1.6.4 fast-fifo: 1.3.2