Add strudel codemirror package

This commit is contained in:
Anton Duda 2025-10-22 17:43:51 +03:00
parent 5f405467ec
commit 2f74420965
57 changed files with 4033 additions and 0 deletions

View file

@ -0,0 +1,3 @@
# @strudel/codemirror
This package contains helpers and extensions to use codemirror6. See [vite-vanilla-repl-cm6](../core/examples/vite-vanilla-repl-cm6/main.js) as an example of using it.

View file

@ -0,0 +1,460 @@
import jsdoc from '../../doc.json';
import { autocompletion } from '@codemirror/autocomplete';
import { h } from './html';
import { Scale } from '@tonaljs/tonal';
import { soundMap } from 'superdough';
import { complex } from '@strudel/tonal';
const escapeHtml = (str) => {
const div = document.createElement('div');
div.innerText = str;
return div.innerHTML;
};
const stripHtml = (html) => {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent || div.innerText || '';
};
const getDocLabel = (doc) => doc.name || doc.longname;
const buildParamsList = (params) =>
params?.length
? `
<div class="autocomplete-info-params-section">
<h4 class="autocomplete-info-section-title">Parameters</h4>
<ul class="autocomplete-info-params-list">
${params
.map(
({ name, type, description }) => `
<li class="autocomplete-info-param-item">
<span class="autocomplete-info-param-name">${name}</span>
<span class="autocomplete-info-param-type">${type.names?.join(' | ')}</span>
${description ? `<div class="autocomplete-info-param-desc">${stripHtml(description)}</div>` : ''}
</li>
`,
)
.join('')}
</ul>
</div>
`
: '';
const buildExamples = (examples) =>
examples?.length
? `
<div class="autocomplete-info-examples-section">
<h4 class="autocomplete-info-section-title">Examples</h4>
${examples
.map(
(example) => `
<pre class="autocomplete-info-example-code">${escapeHtml(example)}</pre>
`,
)
.join('')}
</div>
`
: '';
export const Autocomplete = (doc) =>
h`
<div class="autocomplete-info-container">
<div class="autocomplete-info-tooltip">
<h3 class="autocomplete-info-function-name">${getDocLabel(doc)}</h3>
${doc.synonyms_text ? `<div class="autocomplete-info-function-synonyms">Synonyms: ${doc.synonyms_text}</div>` : ''}
${doc.description ? `<div class="autocomplete-info-function-description">${doc.description}</div>` : ''}
${buildParamsList(doc.params)}
${buildExamples(doc.examples)}
</div>
</div>
`[0];
const isValidDoc = (doc) => {
const label = getDocLabel(doc);
return label && !label.startsWith('_') && !['package'].includes(doc.kind);
};
const hasExcludedTags = (doc) =>
['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag));
export function bankCompletions() {
const soundDict = soundMap.get();
const banks = new Set();
for (const key of Object.keys(soundDict)) {
const [bank, suffix] = key.split('_');
if (suffix && bank) banks.add(bank);
}
return Array.from(banks)
.sort()
.map((name) => ({ label: name, type: 'bank' }));
}
// Attempt to get all scale names from Tonal
let scaleCompletions = [];
try {
scaleCompletions = (Scale.names ? Scale.names() : []).map((name) => ({ label: name, type: 'scale' }));
} catch (e) {
console.warn('[autocomplete] Could not load scale names from Tonal:', e);
}
// Valid mode values for voicing
const modeCompletions = [
{ label: 'below', type: 'mode' },
{ label: 'above', type: 'mode' },
{ label: 'duck', type: 'mode' },
{ label: 'root', type: 'mode' },
];
// Valid chord symbols from ireal dictionary plus empty string for major triads
const chordSymbols = ['', ...Object.keys(complex)].sort();
const chordSymbolCompletions = chordSymbols.map((symbol) => {
if (symbol === '') {
return {
label: 'major',
apply: '',
type: 'chord-symbol',
};
}
return {
label: symbol,
apply: symbol,
type: 'chord-symbol',
};
});
export const getSynonymDoc = (doc, synonym) => {
const synonyms = doc.synonyms || [];
const docLabel = getDocLabel(doc);
// Swap `doc.name` in for `s` in the list of synonyms
const synonymsWithDoc = [docLabel, ...synonyms].filter((x) => x && x !== synonym);
return {
...doc,
name: synonym,
longname: synonym,
synonyms: synonymsWithDoc,
synonyms_text: synonymsWithDoc.join(', '),
};
};
const jsdocCompletions = (() => {
const seen = new Set(); // avoid repetition
const completions = [];
for (const doc of jsdoc.docs) {
if (!isValidDoc(doc) || hasExcludedTags(doc)) continue;
const docLabel = getDocLabel(doc);
// Remove duplicates
const synonyms = doc.synonyms || [];
let labels = [docLabel, ...synonyms];
for (const label of labels) {
// https://codemirror.net/docs/ref/#autocomplete.Completion
if (label && !seen.has(label)) {
seen.add(label);
completions.push({
label,
info: () => Autocomplete(getSynonymDoc(doc, label)),
type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type
});
}
}
}
return completions;
})();
// --- Handler functions for each context ---
const pitchNames = [
'C',
'C#',
'Db',
'D',
'D#',
'Eb',
'E',
'E#',
'Fb',
'F',
'F#',
'Gb',
'G',
'G#',
'Ab',
'A',
'A#',
'Bb',
'B',
'B#',
'Cb',
];
// Cached regex patterns for scaleHandler
const SCALE_NO_QUOTES_REGEX = /scale\(\s*$/;
const SCALE_AFTER_COLON_REGEX = /scale\(\s*['"][^'"]*:[^'"]*$/;
const SCALE_PRE_COLON_REGEX = /scale\(\s*['"][^'"]*$/;
const SCALE_PITCH_MATCH_REGEX = /([A-Ga-g][#b]*)?$/;
const SCALE_SPACES_TO_COLON_REGEX = /\s+/g;
function scaleHandler(context) {
// First check for scale context without quotes - block with empty completions
let scaleNoQuotesContext = context.matchBefore(SCALE_NO_QUOTES_REGEX);
if (scaleNoQuotesContext) {
return {
from: scaleNoQuotesContext.to,
options: [],
};
}
// Check for after-colon context first (more specific)
let scaleAfterColonContext = context.matchBefore(SCALE_AFTER_COLON_REGEX);
if (scaleAfterColonContext) {
const text = scaleAfterColonContext.text;
const colonIdx = text.lastIndexOf(':');
if (colonIdx !== -1) {
const fragment = text.slice(colonIdx + 1);
const filteredScales = scaleCompletions.filter((s) => s.label.startsWith(fragment));
const options = filteredScales.map((s) => ({
...s,
apply: s.label.replace(SCALE_SPACES_TO_COLON_REGEX, ':'),
}));
const from = scaleAfterColonContext.from + colonIdx + 1;
return {
from,
options,
};
}
}
// Then check for pre-colon context
let scalePreColonContext = context.matchBefore(SCALE_PRE_COLON_REGEX);
if (scalePreColonContext) {
if (!scalePreColonContext.text.includes(':')) {
if (context.explicit) {
const text = scalePreColonContext.text;
const match = text.match(SCALE_PITCH_MATCH_REGEX);
const fragment = match ? match[0] : '';
const filtered = pitchNames.filter((p) => p.toLowerCase().startsWith(fragment.toLowerCase()));
const from = scalePreColonContext.to - fragment.length;
const options = filtered.map((p) => ({ label: p, type: 'pitch' }));
return { from, options };
} else {
return { from: scalePreColonContext.to, options: [] };
}
}
}
return null;
}
// Cached regex patterns for soundHandler
const SOUND_NO_QUOTES_REGEX = /(s|sound)\(\s*$/;
const SOUND_WITH_QUOTES_REGEX = /(s|sound)\(\s*['"][^'"]*$/;
const SOUND_FRAGMENT_MATCH_REGEX = /(?:[\s[{(<])([\w]*)$/;
function soundHandler(context) {
// First check for sound context without quotes - block with empty completions
let soundNoQuotesContext = context.matchBefore(SOUND_NO_QUOTES_REGEX);
if (soundNoQuotesContext) {
return {
from: soundNoQuotesContext.to,
options: [],
};
}
// Then check for sound context with quotes - provide completions
let soundContext = context.matchBefore(SOUND_WITH_QUOTES_REGEX);
if (!soundContext) return null;
const text = soundContext.text;
const quoteIdx = Math.max(text.lastIndexOf('"'), text.lastIndexOf("'"));
if (quoteIdx === -1) return null;
const inside = text.slice(quoteIdx + 1);
const fragMatch = inside.match(SOUND_FRAGMENT_MATCH_REGEX);
const fragment = fragMatch ? fragMatch[1] : inside;
const soundNames = Object.keys(soundMap.get()).sort();
const filteredSounds = soundNames.filter((name) => name.includes(fragment));
let options = filteredSounds.map((name) => ({ label: name, type: 'sound' }));
const from = soundContext.to - fragment.length;
return {
from,
options,
};
}
// Cached regex patterns for bankHandler
const BANK_NO_QUOTES_REGEX = /bank\(\s*$/;
const BANK_WITH_QUOTES_REGEX = /bank\(\s*['"][^'"]*$/;
function bankHandler(context) {
// First check for bank context without quotes - block with empty completions
let bankNoQuotesContext = context.matchBefore(BANK_NO_QUOTES_REGEX);
if (bankNoQuotesContext) {
return {
from: bankNoQuotesContext.to,
options: [],
};
}
// Then check for bank context with quotes - provide completions
let bankMatch = context.matchBefore(BANK_WITH_QUOTES_REGEX);
if (!bankMatch) return null;
const text = bankMatch.text;
const quoteIdx = Math.max(text.lastIndexOf('"'), text.lastIndexOf("'"));
if (quoteIdx === -1) return null;
const inside = text.slice(quoteIdx + 1);
const fragment = inside;
let banks = bankCompletions();
const filteredBanks = banks.filter((b) => b.label.startsWith(fragment));
const from = bankMatch.to - fragment.length;
return {
from,
options: filteredBanks,
};
}
// Cached regex patterns for modeHandler
const MODE_NO_QUOTES_REGEX = /mode\(\s*$/;
const MODE_AFTER_COLON_REGEX = /mode\(\s*['"][^'"]*:[^'"]*$/;
const MODE_PRE_COLON_REGEX = /mode\(\s*['"][^'"]*$/;
const MODE_FRAGMENT_MATCH_REGEX = /(?:[\s[{(<])([\w:]*)$/;
function modeHandler(context) {
// First check for mode context without quotes - block with empty completions
let modeNoQuotesContext = context.matchBefore(MODE_NO_QUOTES_REGEX);
if (modeNoQuotesContext) {
return {
from: modeNoQuotesContext.to,
options: [],
};
}
// Check for after-colon context first (more specific)
let modeAfterColonContext = context.matchBefore(MODE_AFTER_COLON_REGEX);
if (modeAfterColonContext) {
const text = modeAfterColonContext.text;
const colonIdx = text.lastIndexOf(':');
if (colonIdx !== -1) {
const fragment = text.slice(colonIdx + 1);
// For anchor after colon, we can suggest pitch names
const filtered = pitchNames.filter((p) => p.toLowerCase().startsWith(fragment.toLowerCase()));
const options = filtered.map((p) => ({ label: p, type: 'pitch' }));
const from = modeAfterColonContext.from + colonIdx + 1;
return {
from,
options,
};
}
}
// Then check for pre-colon context
let modeContext = context.matchBefore(MODE_PRE_COLON_REGEX);
if (!modeContext) return null;
const text = modeContext.text;
const quoteIdx = Math.max(text.lastIndexOf('"'), text.lastIndexOf("'"));
if (quoteIdx === -1) return null;
const inside = text.slice(quoteIdx + 1);
const fragMatch = inside.match(MODE_FRAGMENT_MATCH_REGEX);
const fragment = fragMatch ? fragMatch[1] : inside;
const filteredModes = modeCompletions.filter((m) => m.label.startsWith(fragment));
const from = modeContext.to - fragment.length;
return {
from,
options: filteredModes,
};
}
// Cached regex patterns for chordHandler
const CHORD_NO_QUOTES_REGEX = /chord\(\s*$/;
const CHORD_WITH_QUOTES_REGEX = /chord\(\s*['"][^'"]*$/;
const CHORD_FRAGMENT_MATCH_REGEX = /(?:[\s[{(<])([\w#b+^:-]*)$/;
function chordHandler(context) {
// First check for chord context without quotes - block with empty completions
let chordNoQuotesContext = context.matchBefore(CHORD_NO_QUOTES_REGEX);
if (chordNoQuotesContext) {
return {
from: chordNoQuotesContext.to,
options: [],
};
}
// Then check for chord context with quotes - provide completions
let chordContext = context.matchBefore(CHORD_WITH_QUOTES_REGEX);
if (!chordContext) return null;
const text = chordContext.text;
const quoteIdx = Math.max(text.lastIndexOf('"'), text.lastIndexOf("'"));
if (quoteIdx === -1) return null;
const inside = text.slice(quoteIdx + 1);
// Use same fragment matching as sound/mode for expressions like "<G Am>"
const fragMatch = inside.match(CHORD_FRAGMENT_MATCH_REGEX);
const fragment = fragMatch ? fragMatch[1] : inside;
// Check if fragment contains any pitch name at start (for root + symbol)
let rootMatch = null;
let symbolFragment = fragment;
for (const pitch of pitchNames) {
if (fragment.toLowerCase().startsWith(pitch.toLowerCase())) {
rootMatch = pitch;
symbolFragment = fragment.slice(pitch.length);
break;
}
}
if (rootMatch) {
// We have a root, now complete chord symbols
const filteredSymbols = chordSymbolCompletions.filter((s) =>
s.label.toLowerCase().startsWith(symbolFragment.toLowerCase()),
);
// Create completions that replace the entire chord, not just the symbol part
const options = filteredSymbols;
const from = chordContext.to - symbolFragment.length;
return { from, options };
} else {
// No root yet, complete with pitch names
const filteredPitches = pitchNames.filter((p) => p.toLowerCase().startsWith(fragment.toLowerCase()));
const options = filteredPitches.map((p) => ({ label: p, type: 'pitch' }));
const from = chordContext.to - fragment.length;
return { from, options };
}
}
// Cached regex patterns for fallbackHandler
const FALLBACK_WORD_REGEX = /\w*/;
function fallbackHandler(context) {
const word = context.matchBefore(FALLBACK_WORD_REGEX);
if (word && word.from === word.to && !context.explicit) return null;
if (word) {
return {
from: word.from,
options: jsdocCompletions,
};
}
return null;
}
const handlers = [
soundHandler,
bankHandler,
chordHandler,
scaleHandler,
modeHandler,
// this handler *must* be last
fallbackHandler,
];
export const strudelAutocomplete = (context) => {
for (const handler of handlers) {
const result = handler(context);
if (result) {
return result;
}
}
return null;
};
export const isAutoCompletionEnabled = (enabled) =>
enabled ? [autocompletion({ override: [strudelAutocomplete], closeOnBlur: false })] : [];

View file

@ -0,0 +1,63 @@
import {
keymap,
highlightSpecialChars,
drawSelection,
highlightActiveLine,
dropCursor,
rectangularSelection,
crosshairCursor,
lineNumbers,
highlightActiveLineGutter,
} from '@codemirror/view';
import {
defaultHighlightStyle,
syntaxHighlighting,
bracketMatching,
foldGutter,
foldKeymap,
} from '@codemirror/language';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
import { completionKeymap, closeBracketsKeymap } from '@codemirror/autocomplete';
// Taken + slightly modified from https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
export const basicSetup = (() => [
// lineNumbers(),
// highlightActiveLineGutter(),
highlightSpecialChars(),
history(),
// foldGutter(),
// drawSelection(),
dropCursor(),
// EditorState.allowMultipleSelections.of(true),
// indentOnInput(),
// syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
// autocompletion(),
rectangularSelection(),
crosshairCursor(),
// highlightActiveLine(),
// highlightSelectionMatches(),
keymap.of([
...closeBracketsKeymap,
...defaultKeymap,
// ...searchKeymap,
...historyKeymap,
// ...foldKeymap,
// ...completionKeymap,
]),
])();
/// A minimal set of extensions to create a functional editor. Only
/// includes [the default keymap](#commands.defaultKeymap), [undo
/// history](#commands.history), [special character
/// highlighting](#view.highlightSpecialChars), [custom selection
/// drawing](#view.drawSelection), and [default highlight
/// style](#language.defaultHighlightStyle).
export const minimalSetup = (() => [
highlightSpecialChars(),
history(),
drawSelection(),
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
keymap.of([...defaultKeymap, ...historyKeymap]),
])();

View file

@ -0,0 +1,386 @@
import { closeBrackets } from '@codemirror/autocomplete';
export { toggleComment, toggleBlockComment, toggleLineComment, toggleBlockCommentByLine } from '@codemirror/commands';
// import { search, highlightSelectionMatches } from '@codemirror/search';
import { indentWithTab } from '@codemirror/commands';
import { javascript, javascriptLanguage } from '@codemirror/lang-javascript';
import { defaultHighlightStyle, syntaxHighlighting, bracketMatching } from '@codemirror/language';
import { Compartment, EditorState, Prec } from '@codemirror/state';
import {
EditorView,
highlightActiveLineGutter,
highlightActiveLine,
keymap,
lineNumbers,
drawSelection,
} from '@codemirror/view';
import { repl, registerControl } from '@strudel/core';
import { Drawer, cleanupDraw } from '@strudel/draw';
import { isAutoCompletionEnabled } from './autocomplete.mjs';
import { isTooltipEnabled } from './tooltip.mjs';
import { flash, isFlashEnabled } from './flash.mjs';
import { highlightMiniLocations, isPatternHighlightingEnabled, updateMiniLocations } from './highlight.mjs';
import { keybindings } from './keybindings.mjs';
import { initTheme, activateTheme, theme } from './themes.mjs';
import { sliderPlugin, updateSliderWidgets } from './slider.mjs';
import { widgetPlugin, updateWidgets } from './widget.mjs';
import { persistentAtom } from '@nanostores/persistent';
import { basicSetup } from './basicSetup.mjs';
const extensions = {
isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []),
isBracketMatchingEnabled: (on) => (on ? bracketMatching({ brackets: '()[]{}<>' }) : []),
isBracketClosingEnabled: (on) => (on ? closeBrackets() : []),
isLineNumbersDisplayed: (on) => (on ? lineNumbers() : []),
theme,
isAutoCompletionEnabled,
isTooltipEnabled,
isPatternHighlightingEnabled,
isActiveLineHighlighted: (on) => (on ? [highlightActiveLine(), highlightActiveLineGutter()] : []),
isFlashEnabled,
keybindings,
isTabIndentationEnabled: (on) => (on ? keymap.of([indentWithTab]) : []),
isMultiCursorEnabled: (on) =>
on
? [
EditorState.allowMultipleSelections.of(true),
EditorView.clickAddsSelectionRange.of((ev) => ev.metaKey || ev.ctrlKey),
]
: [],
};
const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()]));
export const defaultSettings = {
keybindings: 'codemirror',
isBracketMatchingEnabled: false,
isBracketClosingEnabled: true,
isLineNumbersDisplayed: true,
isActiveLineHighlighted: false,
isAutoCompletionEnabled: false,
isPatternHighlightingEnabled: true,
isFlashEnabled: true,
isTooltipEnabled: false,
isLineWrappingEnabled: false,
isTabIndentationEnabled: false,
isMultiCursorEnabled: false,
theme: 'strudelTheme',
fontFamily: 'monospace',
fontSize: 18,
};
export const codemirrorSettings = persistentAtom('codemirror-settings', defaultSettings, {
encode: JSON.stringify,
decode: JSON.parse,
});
// https://codemirror.net/docs/guide/
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root, mondo }) {
const settings = codemirrorSettings.get();
const initialSettings = Object.keys(compartments).map((key) =>
compartments[key].of(extensions[key](parseBooleans(settings[key]))),
);
initTheme(settings.theme);
let state = EditorState.create({
doc: initialCode,
extensions: [
/* search(),
highlightSelectionMatches(), */
...initialSettings,
basicSetup,
mondo ? [] : javascript(),
javascriptLanguage.data.of({
closeBrackets: { brackets: ['(', '[', '{', "'", '"', '<'] },
bracketMatching: { brackets: ['(', '[', '{', "'", '"', '<'] },
}),
sliderPlugin,
widgetPlugin,
// indentOnInput(), // works without. already brought with javascript extension?
// bracketMatching(), // does not do anything
syntaxHighlighting(defaultHighlightStyle),
EditorView.updateListener.of((v) => onChange(v)),
drawSelection({ cursorBlinkRate: 0 }),
Prec.highest(
keymap.of([
{
key: 'Ctrl-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Alt-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Ctrl-.',
run: () => onStop?.(),
},
{
key: 'Alt-.',
preventDefault: true,
run: () => onStop?.(),
},
/* {
key: 'Ctrl-Shift-.',
run: () => (onPanic ? onPanic() : onStop?.()),
},
{
key: 'Ctrl-Shift-Enter',
run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()),
}, */
]),
),
],
});
return new EditorView({
state,
parent: root,
});
}
export class StrudelMirror {
constructor(options) {
const {
root,
id,
initialCode = '',
onDraw,
drawContext,
drawTime = [0, 0],
autodraw,
prebake,
bgFill = true,
solo = true,
...replOptions
} = options;
this.code = initialCode;
this.root = root;
this.miniLocations = [];
this.widgets = [];
this.drawTime = drawTime;
this.drawContext = drawContext;
this.onDraw = onDraw || this.draw;
this.id = id || s4();
this.solo = solo;
this.drawer = new Drawer((haps, time, _, painters) => {
const currentFrame = haps.filter((hap) => hap.isActive(time));
this.highlight(currentFrame, time);
this.onDraw(haps, time, painters);
}, drawTime);
this.prebaked = prebake();
autodraw && this.drawFirstFrame();
this.repl = repl({
...replOptions,
id,
onToggle: (started) => {
replOptions?.onToggle?.(started);
if (started) {
this.drawer.start(this.repl.scheduler);
if (this.solo) {
// stop other repls when this one is started
document.dispatchEvent(
new CustomEvent('start-repl', {
detail: this.id,
}),
);
}
} else {
this.drawer.stop();
updateMiniLocations(this.editor, []);
cleanupDraw(true, id);
}
},
beforeEval: async () => {
cleanupDraw(true, id);
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;
const sliders = this.widgets.filter((w) => w.type === 'slider');
updateSliderWidgets(this.editor, sliders);
const widgets = this.widgets.filter((w) => w.type !== 'slider');
updateWidgets(this.editor, widgets);
updateMiniLocations(this.editor, this.miniLocations);
replOptions?.afterEval?.(options);
// if no painters are set (.onPaint was not called), then we only need the present moment (for highlighting)
const drawTime = options.pattern.getPainters().length ? this.drawTime : [0, 0];
this.drawer.setDrawTime(drawTime);
// invalidate drawer after we've set the appropriate drawTime
this.drawer.invalidate(this.repl.scheduler);
},
});
this.editor = initEditor({
root,
initialCode,
onChange: (v) => {
if (v.docChanged) {
this.code = v.state.doc.toString();
this.repl.setCode?.(this.code);
}
},
onEvaluate: () => this.evaluate(),
onStop: () => this.stop(),
mondo: replOptions.mondo,
});
const cmEditor = this.root.querySelector('.cm-editor');
if (cmEditor) {
this.root.style.display = 'block';
if (bgFill) {
this.root.style.backgroundColor = 'var(--background)';
}
cmEditor.style.backgroundColor = 'transparent';
}
const settings = codemirrorSettings.get();
this.setFontSize(settings.fontSize);
this.setFontFamily(settings.fontFamily);
// stop this repl when another repl is started
this.onStartRepl = (e) => {
if (this.solo && e.detail !== this.id) {
this.stop();
}
};
document.addEventListener('start-repl', this.onStartRepl);
}
draw(haps, time, painters) {
painters?.forEach((painter) => painter(this.drawContext, time, haps, this.drawTime));
}
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, -0.001);
// draw at -0.001 to avoid haps at 0 to be visualized as active
this.onDraw?.(this.drawer.visibleHaps, -0.001, this.drawer.painters);
} catch (err) {
console.warn('first frame could not be painted');
}
}
async evaluate() {
this.flash();
await this.repl.evaluate(this.code);
}
async stop() {
this.repl.scheduler.stop();
}
async toggle() {
if (this.repl.scheduler.started) {
this.repl.stop();
} else {
this.evaluate();
}
}
flash(ms) {
flash(this.editor, ms);
}
highlight(haps, time) {
highlightMiniLocations(this.editor, time, haps);
}
setFontSize(size) {
this.root.style.fontSize = size + 'px';
}
setFontFamily(family) {
this.root.style.fontFamily = family;
const scroller = this.root.querySelector('.cm-scroller');
if (scroller) {
scroller.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),
});
if (key === 'theme') {
activateTheme(value);
}
}
setLineWrappingEnabled(enabled) {
this.reconfigureExtension('isLineWrappingEnabled', enabled);
}
setBracketMatchingEnabled(enabled) {
this.reconfigureExtension('isBracketMatchingEnabled', enabled);
}
setLineNumbersDisplayed(enabled) {
this.reconfigureExtension('isLineNumbersDisplayed', enabled);
}
setBracketClosingEnabled(enabled) {
this.reconfigureExtension('isBracketClosingEnabled', 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]);
}
const updated = { ...codemirrorSettings.get(), ...settings };
codemirrorSettings.set(updated);
}
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 });
}
clear() {
this.onStartRepl && document.removeEventListener('start-repl', this.onStartRepl);
}
getCursorLocation() {
return this.editor.state.selection.main.head;
}
setCursorLocation(col) {
return this.editor.dispatch({ selection: { anchor: col } });
}
appendCode(code) {
const cursor = this.getCursorLocation();
this.setCode(this.code + code);
this.setCursorLocation(cursor);
}
}
function parseBooleans(value) {
return { true: true, false: false }[value] ?? value;
}
// helper function to generate repl ids
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
/**
* Overrides the css of highlighted events. Make sure to use single quotes!
* @name markcss
* @example
* note("c a f e")
* .markcss('text-decoration:underline')
*/
export const markcss = registerControl('markcss');

View file

@ -0,0 +1,39 @@
import { StateEffect, StateField } from '@codemirror/state';
import { Decoration, EditorView } from '@codemirror/view';
export const setFlash = StateEffect.define();
export const flashField = StateField.define({
create() {
return Decoration.none;
},
update(flash, tr) {
try {
for (let e of tr.effects) {
if (e.is(setFlash)) {
if (e.value && tr.newDoc.length > 0) {
const mark = Decoration.mark({
attributes: { style: `background-color: rgba(255,255,255, .4); filter: invert(10%)` },
});
flash = Decoration.set([mark.range(0, tr.newDoc.length)]);
} else {
flash = Decoration.set([]);
}
}
}
return flash;
} catch (err) {
console.warn('flash error', err);
return flash;
}
},
provide: (f) => EditorView.decorations.from(f),
});
export const flash = (view, ms = 200) => {
view.dispatch({ effects: setFlash.of(true) });
setTimeout(() => {
view.dispatch({ effects: setFlash.of(false) });
}, ms);
};
export const isFlashEnabled = (on) => (on ? flashField : []);

View file

@ -0,0 +1,138 @@
import { RangeSetBuilder, StateEffect, StateField, Prec } from '@codemirror/state';
import { Decoration, EditorView } from '@codemirror/view';
export const setMiniLocations = StateEffect.define();
export const showMiniLocations = StateEffect.define();
export const updateMiniLocations = (view, locations) => {
view.dispatch({ effects: setMiniLocations.of(locations) });
};
export const highlightMiniLocations = (view, atTime, haps) => {
view.dispatch({ effects: showMiniLocations.of({ atTime, haps }) });
};
const miniLocations = StateField.define({
create() {
return Decoration.none;
},
update(locations, tr) {
if (tr.docChanged) {
locations = locations.map(tr.changes);
}
for (let e of tr.effects) {
if (e.is(setMiniLocations)) {
// this is called on eval, with the mini locations obtained from the transpiler
// codemirror will automatically remap the marks when the document is edited
// create a mark for each mini location, adding the range to the spec to find it later
const marks = e.value
.filter(([from]) => from < tr.newDoc.length)
.map(([from, to]) => [from, Math.min(to, tr.newDoc.length)])
.map(
(range) =>
Decoration.mark({
id: range.join(':'),
// this green is only to verify that the decoration moves when the document is edited
// it will be removed later, so the mark is not visible by default
attributes: { style: `background-color: #00CA2880` },
}).range(...range), // -> Decoration
);
locations = Decoration.set(marks, true); // -> DecorationSet === RangeSet<Decoration>
}
}
return locations;
},
});
const visibleMiniLocations = StateField.define({
create() {
return { atTime: 0, haps: new Map() };
},
update(visible, tr) {
for (let e of tr.effects) {
if (e.is(showMiniLocations)) {
// this is called every frame to show the locations that are currently active
// we can NOT create new marks because the context.locations haven't changed since eval time
// this is why we need to find a way to update the existing decorations, showing the ones that have an active range
const haps = new Map();
for (let hap of e.value.haps) {
if (!hap.context?.locations || !hap.whole) {
continue;
}
for (let { start, end } of hap.context.locations) {
let id = `${start}:${end}`;
if (!haps.has(id) || haps.get(id).whole.begin.lt(hap.whole.begin)) {
haps.set(id, hap);
}
}
}
visible = { atTime: e.value.atTime, haps };
}
}
return visible;
},
});
// // Derive the set of decorations from the miniLocations and visibleLocations
const miniLocationHighlights = EditorView.decorations.compute([miniLocations, visibleMiniLocations], (state) => {
const iterator = state.field(miniLocations).iter();
const { haps } = state.field(visibleMiniLocations);
const builder = new RangeSetBuilder();
while (iterator.value) {
const {
from,
to,
value: {
spec: { id },
},
} = iterator;
if (haps.has(id)) {
const hap = haps.get(id);
const color = hap.value?.color ?? 'var(--foreground)';
const style = hap.value?.markcss || `outline: solid 2px ${color}`;
// Get explicit channels for color values
/*
const swatch = document.createElement('div');
swatch.style.color = color;
document.body.appendChild(swatch);
let channels = getComputedStyle(swatch)
.color.match(/^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*(\d*(?:\.\d+)?))?\)$/)
.slice(1)
.map((c) => parseFloat(c || 1));
document.body.removeChild(swatch);
// Get percentage of event
const percent = 1 - (atTime - hap.whole.begin) / hap.whole.duration;
channels[3] *= percent;
*/
builder.add(
from,
to,
Decoration.mark({
// attributes: { style: `outline: solid 2px rgba(${channels.join(', ')})` },
attributes: { style },
}),
);
}
iterator.next();
}
return builder.finish();
});
export const highlightExtension = [miniLocations, visibleMiniLocations, miniLocationHighlights];
export const isPatternHighlightingEnabled = (on, config) => {
on &&
config &&
setTimeout(() => {
updateMiniLocations(config.editor, config.miniLocations);
}, 100);
return on ? Prec.highest(highlightExtension) : [];
};

View file

@ -0,0 +1,18 @@
export let html = (string) => {
const template = document.createElement('template');
template.innerHTML = string.trim();
return template.content.childNodes;
};
let parseChunk = (chunk) => {
if (Array.isArray(chunk)) return chunk.flat().join('');
if (chunk === undefined) return '';
return chunk;
};
export let h = (strings, ...vars) => {
let string = '';
for (let i in strings) {
string += parseChunk(strings[i]);
string += parseChunk(vars[i]);
}
return html(string);
};

View file

@ -0,0 +1,6 @@
export * from './codemirror.mjs';
export * from './highlight.mjs';
export * from './flash.mjs';
export * from './slider.mjs';
export * from './themes.mjs';
export * from './widget.mjs';

View file

@ -0,0 +1,32 @@
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 { vim } from './vim_test.mjs';
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
import { defaultKeymap } 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,
codemirror: () => keymap.of(defaultKeymap),
vscode: vscodeExtension,
};
export function keybindings(name) {
const active = keymaps[name];
return [active ? Prec.high(active()) : []];
}

View file

@ -0,0 +1,58 @@
{
"name": "@strudel/codemirror",
"version": "1.2.5",
"description": "Codemirror Extensions for Strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"prepublishOnly": "npm run build"
},
"type": "module",
"repository": {
"type": "git",
"url": "git+https://codeberg.org/uzu/strudel.git"
},
"keywords": [
"tidalcycles",
"strudel",
"pattern",
"livecoding",
"algorave"
],
"author": "Felix Roos <flix91@gmail.com>",
"contributors": [
"Alex McLean <alex@slab.org>"
],
"license": "AGPL-3.0-or-later",
"bugs": {
"url": "https://codeberg.org/uzu/strudel/issues"
},
"homepage": "https://codeberg.org/uzu/strudel#readme",
"dependencies": {
"@codemirror/autocomplete": "^6.18.4",
"@codemirror/commands": "^6.8.0",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/language": "^6.10.8",
"@codemirror/search": "^6.5.8",
"@codemirror/state": "^6.5.1",
"@codemirror/view": "^6.36.2",
"@lezer/highlight": "^1.2.1",
"@nanostores/persistent": "^0.10.2",
"@replit/codemirror-emacs": "^6.1.0",
"@replit/codemirror-vim": "^6.3.0",
"@replit/codemirror-vscode-keymap": "^6.0.2",
"@strudel/core": "workspace:*",
"@strudel/draw": "workspace:*",
"@strudel/tonal": "workspace:*",
"@strudel/transpiler": "workspace:*",
"@tonaljs/tonal": "^4.10.0",
"superdough": "workspace:*",
"nanostores": "^0.11.3"
},
"devDependencies": {
"vite": "^6.0.11"
}
}

View file

@ -0,0 +1,146 @@
import { ref, pure } from '@strudel/core';
import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view';
import { StateEffect } from '@codemirror/state';
export let sliderValues = {};
const getSliderID = (from) => `slider_${from}`;
export class SliderWidget extends WidgetType {
constructor(value, min, max, from, to, step, view) {
super();
this.value = value;
this.min = min;
this.max = max;
this.from = from;
this.originalFrom = from;
this.to = to;
this.step = step;
this.view = view;
}
eq() {
return false;
}
toDOM() {
let wrap = document.createElement('span');
wrap.setAttribute('aria-hidden', 'true');
wrap.className = 'cm-slider'; // inline-flex items-center
let slider = wrap.appendChild(document.createElement('input'));
slider.type = 'range';
slider.min = this.min;
slider.max = this.max;
slider.step = this.step ?? (this.max - this.min) / 1000;
slider.originalValue = this.value;
// to make sure the code stays in sync, let's save the original value
// becuase .value automatically clamps values so it'll desync with the code
slider.value = slider.originalValue;
slider.from = this.from;
slider.originalFrom = this.originalFrom;
slider.to = this.to;
slider.style = 'width:64px;margin-right:4px;transform:translateY(4px)';
this.slider = slider;
slider.addEventListener('input', (e) => {
const next = e.target.value;
let insert = next;
//let insert = next.toFixed(2);
const to = slider.from + slider.originalValue.length;
let change = { from: slider.from, to, insert };
slider.originalValue = insert;
slider.value = insert;
this.view.dispatch({ changes: change });
const id = getSliderID(slider.originalFrom); // matches id generated in transpiler
window.postMessage({ type: 'cm-slider', value: Number(next), id });
});
return wrap;
}
ignoreEvent(e) {
return true;
}
}
export const setSliderWidgets = StateEffect.define();
export const updateSliderWidgets = (view, widgets) => {
view.dispatch({ effects: setSliderWidgets.of(widgets) });
};
function getSliders(widgetConfigs, view) {
return widgetConfigs
.filter((w) => w.type === 'slider')
.map(({ from, to, value, min, max, step }) => {
return Decoration.widget({
widget: new SliderWidget(value, min, max, from, to, step, view),
side: 0,
}).range(from /* , to */);
});
}
export const sliderPlugin = ViewPlugin.fromClass(
class {
decorations; //: DecorationSet
constructor(view /* : EditorView */) {
this.decorations = Decoration.set([]);
}
update(update /* : ViewUpdate */) {
update.transactions.forEach((tr) => {
if (tr.docChanged) {
this.decorations = this.decorations.map(tr.changes);
const iterator = this.decorations.iter();
while (iterator.value) {
// when the widgets are moved, we need to tell the dom node the current position
// this is important because the updateSliderValue function has to work with the dom node
if (iterator.value?.widget?.slider) {
iterator.value.widget.slider.from = iterator.from;
iterator.value.widget.slider.to = iterator.to;
}
iterator.next();
}
}
for (let e of tr.effects) {
if (e.is(setSliderWidgets)) {
this.decorations = Decoration.set(getSliders(e.value, update.view));
}
}
});
}
},
{
decorations: (v) => v.decorations,
},
);
/**
* Displays a slider widget to allow the user manipulate a value
*
* @name slider
* @param {number} value Initial value
* @param {number} min Minimum value - optional, defaults to 0
* @param {number} max Maximum value - optional, defaults to 1
* @param {number} step Step size - optional
*/
export let slider = (value) => {
console.warn('slider will only work when the transpiler is used... passing value as is');
return pure(value);
};
// function transpiled from slider = (value, min, max)
export let sliderWithID = (id, value, min, max) => {
sliderValues[id] = value; // sync state at eval time (code -> state)
return ref(() => sliderValues[id]); // use state at query time
};
// update state when sliders are moved
if (typeof window !== 'undefined') {
window.addEventListener('message', (e) => {
if (e.data.type === 'cm-slider') {
if (sliderValues[e.data.id] !== undefined) {
// update state when slider is moved
sliderValues[e.data.id] = e.data.value;
} else {
console.warn(`slider with id "${e.data.id}" is not registered. Only ${Object.keys(sliderValues)}`);
}
}
});
}

218
src/strudel/codemirror/themes.mjs vendored Normal file
View file

@ -0,0 +1,218 @@
import strudelTheme, { settings as strudelThemeSettings } from './themes/strudel-theme.mjs';
import bluescreen, { settings as bluescreenSettings } from './themes/bluescreen.mjs';
import blackscreen, { settings as blackscreenSettings } from './themes/blackscreen.mjs';
import whitescreen, { settings as whitescreenSettings } from './themes/whitescreen.mjs';
import teletext, { settings as teletextSettings } from './themes/teletext.mjs';
import algoboy, { settings as algoboySettings } from './themes/algoboy.mjs';
import CutiePi, { settings as CutiePiSettings } from './themes/CutiePi.mjs';
import sonicPink, { settings as sonicPinkSettings } from './themes/sonic-pink.mjs';
import redText, { settings as redTextSettings } from './themes/red-text.mjs';
import greenText, { settings as greenTextSettings } from './themes/green-text.mjs';
import archBtw, { settings as archBtwSettings } from './themes/archBtw.mjs';
import fruitDaw, { settings as fruitDawSettings } from './themes/fruitDaw.mjs';
import bluescreenlight, { settings as bluescreenlightsettings } from './themes/bluescreenlight.mjs';
import androidstudio, { settings as androidstudioSettings } from './themes/androidstudio.mjs';
import atomone, { settings as atomOneSettings } from './themes/atomone.mjs';
import aura, { settings as auraSettings } from './themes/aura.mjs';
import darcula, { settings as darculaSettings } from './themes/darcula.mjs';
import dracula, { settings as draculaSettings } from './themes/dracula.mjs';
import duotoneDark, { settings as duotoneDarkSettings } from './themes/duotoneDark.mjs';
import eclipse, { settings as eclipseSettings } from './themes/eclipse.mjs';
import githubDark, { settings as githubDarkSettings } from './themes/githubDark.mjs';
import githubLight, { settings as githubLightSettings } from './themes/githubLight.mjs';
import gruvboxDark, { settings as gruvboxDarkSettings } from './themes/gruvboxDark.mjs';
import gruvboxLight, { settings as gruvboxLightSettings } from './themes/gruvboxLight.mjs';
import materialDark, { settings as materialDarkSettings } from './themes/materialDark.mjs';
import materialLight, { settings as materialLightSettings } from './themes/materialLight.mjs';
import nord, { settings as nordSettings } from './themes/nord.mjs';
import monokai, { settings as monokaiSettings } from './themes/monokai.mjs';
import solarizedDark, { settings as solarizedDarkSettings } from './themes/solarizedDark.mjs';
import solarizedLight, { settings as solarizedLightSettings } from './themes/solarizedLight.mjs';
import sublime, { settings as sublimeSettings } from './themes/sublime.mjs';
import tokyoNight, { settings as tokyoNightSettings } from './themes/tokyoNight.mjs';
import tokyoNightStorm, { settings as tokyoNightStormSettings } from './themes/tokioNightStorm.mjs';
import tokyoNightDay, { settings as tokyoNightDaySettings } from './themes/tokyoNightDay.mjs';
import vscodeDark, { settings as vscodeDarkSettings } from './themes/vscodeDark.mjs';
import vscodeLight, { settings as vscodeLightSettings } from './themes/vscodeLight.mjs';
import xcodeLight, { settings as xcodeLightSettings } from './themes/xcodeLight.mjs';
import bbedit, { settings as bbeditSettings } from './themes/bbedit.mjs';
import noctisLilac, { settings as noctisLilacSettings } from './themes/noctisLilac.mjs';
import { setTheme } from '@strudel/draw';
export const themes = {
strudelTheme,
algoboy,
archBtw,
androidstudio,
atomone,
aura,
bbedit,
blackscreen,
bluescreen,
bluescreenlight,
CutiePi,
darcula,
dracula,
duotoneDark,
eclipse,
fruitDaw,
githubDark,
githubLight,
greenText,
gruvboxDark,
gruvboxLight,
sonicPink,
materialDark,
materialLight,
monokai,
noctisLilac,
nord,
redText,
solarizedDark,
solarizedLight,
sublime,
teletext,
tokyoNight,
tokyoNightDay,
tokyoNightStorm,
vscodeDark,
vscodeLight,
whitescreen,
xcodeLight,
};
export const settings = {
strudelTheme: strudelThemeSettings,
bluescreen: bluescreenSettings,
bluescreenlight: bluescreenlightsettings,
blackscreen: blackscreenSettings,
whitescreen: whitescreenSettings,
teletext: teletextSettings,
algoboy: algoboySettings,
archBtw: archBtwSettings,
androidstudio: androidstudioSettings,
atomone: atomOneSettings,
aura: auraSettings,
bbedit: bbeditSettings,
darcula: darculaSettings,
dracula: draculaSettings,
duotoneDark: duotoneDarkSettings,
eclipse: eclipseSettings,
CutiePi: CutiePiSettings,
sonicPink: sonicPinkSettings,
fruitDaw: fruitDawSettings,
githubLight: githubLightSettings,
githubDark: githubDarkSettings,
greenText: greenTextSettings,
gruvboxDark: gruvboxDarkSettings,
gruvboxLight: gruvboxLightSettings,
materialDark: materialDarkSettings,
materialLight: materialLightSettings,
noctisLilac: noctisLilacSettings,
nord: nordSettings,
monokai: monokaiSettings,
redText: redTextSettings,
solarizedLight: solarizedLightSettings,
solarizedDark: solarizedDarkSettings,
sublime: sublimeSettings,
tokyoNight: tokyoNightSettings,
tokyoNightStorm: tokyoNightStormSettings,
vscodeDark: vscodeDarkSettings,
vscodeLight: vscodeLightSettings,
xcodeLight: xcodeLightSettings,
tokyoNightDay: tokyoNightDaySettings,
};
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 const theme = (theme) => themes[theme] || themes.strudelTheme;
// css style injection helpers
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);
}
let currentTheme,
resetThemeStyle,
themeStyle,
styleID = 'strudel-theme-vars';
export function initTheme(theme) {
if (!document.getElementById(styleID)) {
themeStyle = document.createElement('style');
themeStyle.id = styleID;
document.head.append(themeStyle);
}
activateTheme(theme);
}
export function activateTheme(name) {
if (currentTheme === name) {
return;
}
currentTheme = name;
if (!settings[name]) {
console.warn('theme', name, 'has no settings.. defaulting to strudelTheme settings');
}
const themeSettings = settings[name] || settings.strudelTheme;
// set css variables
themeStyle.innerHTML = `:root {
${Object.entries(themeSettings)
// important to override fallback
.map(([key, value]) => `--${key}: ${value} !important;`)
.join('\n')}
}`;
setTheme(themeSettings);
// tailwind dark mode
if (themeSettings.light) {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
resetThemeStyle?.();
resetThemeStyle = undefined;
if (themeSettings.customStyle) {
resetThemeStyle = injectStyle(themeSettings.customStyle);
}
}

View file

@ -0,0 +1,45 @@
/*
* Cutie Pi
* by Switch Angel
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
const deepPurple = '#5c019a';
const yellowPink = '#fbeffc';
const grey = '#272C35';
const pinkAccent = '#fee1ff';
const lightGrey = '#465063';
const bratGreen = '#9acd3f';
const lighterGrey = '#97a1b7';
const pink = '#f6a6fd';
export const settings = {
background: 'white',
lineBackground: 'transparent',
foreground: deepPurple,
caret: '#797977',
selection: yellowPink,
selectionMatch: '#2B323D',
gutterBackground: grey,
gutterForeground: lightGrey,
gutterBorder: 'transparent',
lineHighlight: pinkAccent,
};
export default createTheme({
theme: 'light',
settings,
styles: [
{
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
color: deepPurple,
},
{ tag: [t.tagName, t.heading], color: settings.foreground },
{ tag: t.comment, color: lighterGrey },
{ tag: [t.variableName, t.propertyName, t.labelName], color: pink },
{ tag: [t.attributeName, t.number], color: '#d19a66' },
{ tag: t.className, color: grey },
{ tag: t.keyword, color: deepPurple },
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: bratGreen },
],
});

View file

@ -0,0 +1,61 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
const palettes = {
// https://www.deviantart.com/advancedfan2020/art/Game-Boy-Palette-Set-Color-HEX-Part-09-920495662
'Central Florida A': ['#FFF630', '#B3AC22', '#666213', '#191905'],
'Central Florida B': ['#38CEBA', '#279082', '#16524A', '#061513'],
'Central Florida C': ['#FF8836', '#B35F26', '#663616', '#190E05'],
'Central Florida D': ['#E07070', '#9D4E4E', '#5A2D2D', '#160B0B'],
'Central Florida E': ['#7AA4CB', '#55738E', '#314251', '#0C1014'],
'Feminine Energy A': ['#DC5686', '#9A415E', '#582536', '#16090D'],
'Feminine Energy B': ['#D0463C', '#92312A', '#531c18', '#150706'],
'Feminine Energy C': ['#D86918', '#974A11', '#562A0A', '#160A02'],
'Feminine Energy D': ['#EFC54F', '#A78A36', '#604F20', '#181408'],
'Feminine Energy E': ['#866399', '#5e456b', '#36283d', '#0d0a0f'],
'Sour Watermelon A': ['#993366', '#6B2447', '#3D1429', '#0F050A'],
'Sour Watermelon B': ['#996666', '#6B4747', '#3D2929', '#0F0A0A'],
'Sour Watermelon C': ['#999966', '#686B47', '#3d3d29', '#0f0f0A'],
'Sour Watermelon D': ['#99cc66', '#6b8f47', '#3d5229', '#0f140a'],
'Sour Watermelon E': ['#99ff66', '#6bb347', '#3d6629', '#0f190a'],
//https://www.deviantart.com/advancedfan2020/art/Game-Boy-Palette-Set-Color-HEX-Part-02-920073260
'Peri Peaceful A': ['#909BE9', '#656DA3', '#3A3E5D', '#0e0f17'],
'Peri Peaceful B': ['#68628d', '#494563', '#2a2738', '#0a0a0e'], // pretty dim
'Peri Peaceful E': ['#b5a0a9', '#7f7076', '#484044', '#121011'],
'Hichem Palette B': ['#4fa3a5', '#377273', '#204142', '#081010'],
'Hichem Palette C': ['#Fe6f9b', '#b24e6d', '#662c3e', '#190b0f'],
'Hichem Palette D': ['#ffbb5a', '#b3833f', '#664b24', '#191309'],
'JSR2 A': ['#E0EFC0', '#9da786', '#5a604d', '#161813'],
};
const palette = palettes['Sour Watermelon B'];
export const settings = {
background: palette[3],
foreground: palette[1],
caret: palette[0],
selection: palette[0],
selectionMatch: palette[1],
lineHighlight: palette[3],
lineBackground: palette[3] + '90',
//lineBackground: 'transparent',
gutterBackground: 'transparent',
gutterForeground: palette[0],
light: false,
// customStyle: '.cm-line { line-height: 1 }',
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{ tag: t.comment, color: palette[2] },
{ tag: t.string, color: palette[1] },
{ tag: [t.atom, t.number], color: palette[1] },
{ tag: [t.meta, t.labelName, t.variableName], color: palette[0] },
{
tag: [t.keyword, t.tagName, t.arithmeticOperator],
color: palette[1],
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: palette[0] },
{ tag: [t.function(t.variableName), t.propertyName], color: palette[0] },
{ tag: t.atom, color: palette[1] },
],
});

View file

@ -0,0 +1,43 @@
/*
* androidstudio
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#282b2e',
lineBackground: '#282b2e99',
foreground: '#a9b7c6',
caret: '#00FF00',
selection: '#343739',
selectionMatch: '#343739',
lineHighlight: '#343739',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#282b2e',
foreground: '#a9b7c6',
caret: '#00FF00',
selection: '#4e5254',
selectionMatch: '#4e5254',
gutterForeground: '#cccccc50',
lineHighlight: '#7f85891f',
},
styles: [
{ tag: t.labelName, color: 'inherit' },
{ tag: [t.keyword, t.deleted, t.className], color: '#a9b7c6' },
{ tag: [t.number, t.literal], color: '#6897bb' },
//{ tag: [t.link, t.variableName], color: '#629755' },
{ tag: [t.link, t.variableName], color: '#a9b7c6' },
{ tag: [t.comment, t.quote], color: 'grey' },
{ tag: [t.meta, t.documentMeta], color: '#bbb529' },
//{ tag: [t.string, t.propertyName, t.attributeValue], color: '#6a8759' },
{ tag: [t.propertyName, t.attributeValue], color: '#a9b7c6' },
{ tag: [t.string], color: '#6a8759' },
{ tag: [t.heading, t.typeName], color: '#ffc66d' },
{ tag: [t.attributeName], color: '#a9b7c6' },
{ tag: [t.emphasis], fontStyle: 'italic' },
],
});

View file

@ -0,0 +1,38 @@
/*
* Arch Btw
* Modern terminal inspired theme
* made by Jade
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
const hex = ['rgb(0, 0, 0)', 'rgb(82, 208, 250)', 'rgba(113, 208, 250, .4)', 'rgba(113, 208, 250, .15)'];
export const settings = {
background: hex[0],
lineBackground: 'transparent',
foreground: hex[1],
selection: hex[2],
selectionMatch: hex[0],
gutterBackground: hex[0],
gutterForeground: hex[2],
gutterBorder: 'transparent',
lineHighlight: hex[0],
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
color: hex[1],
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
{ tag: [t.comment, t.brace, t.bracket], color: hex[2] },
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[1] },
{ tag: [t.attributeName, t.number], color: hex[1] },
{ tag: t.keyword, color: hex[1] },
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
],
});

View file

@ -0,0 +1,51 @@
/*
* Atom One
* Atom One dark syntax theme
*
* https://github.com/atom/one-dark-syntax
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#272C35',
lineBackground: '#272C3599',
foreground: 'hsl(220, 14%, 71%)',
caret: '#797977',
selection: '#ffffff30',
selectionMatch: '#2B323D',
gutterBackground: '#272C35',
gutterForeground: '#465063',
gutterBorder: 'transparent',
lineHighlight: '#2B323D',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#272C35',
foreground: '#9d9b97',
caret: '#797977',
selection: '#3d4c64',
selectionMatch: '#3d4c64',
gutterBackground: '#272C35',
gutterForeground: '#465063',
gutterBorder: 'transparent',
lineHighlight: '#2e3f5940',
},
styles: [
{
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
color: 'hsl(207, 82%, 66%)',
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: 'hsl( 29, 54%, 61%)' },
{ tag: [t.tagName, t.heading], color: '#e06c75' },
{ tag: t.comment, color: '#54636D' },
{ tag: [t.variableName, t.propertyName, t.labelName], color: 'hsl(220, 14%, 71%)' },
{ tag: [t.attributeName, t.number], color: 'hsl( 29, 54%, 61%)' },
{ tag: t.className, color: 'hsl( 39, 67%, 69%)' },
{ tag: t.keyword, color: 'hsl(286, 60%, 67%)' },
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: '#98c379' },
],
});

51
src/strudel/codemirror/themes/aura.mjs vendored Normal file
View file

@ -0,0 +1,51 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#21202e',
lineBackground: '#21202e99',
foreground: '#edecee',
caret: '#a277ff',
selection: '#3d375e7f',
selectionMatch: '#3d375e7f',
gutterBackground: '#21202e',
gutterForeground: '#edecee',
gutterBorder: 'transparent',
lineHighlight: '#a394f033',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#21202e',
foreground: '#edecee',
caret: '#a277ff',
selection: '#5a51898f',
selectionMatch: '#5a51898f',
gutterBackground: '#21202e',
gutterForeground: '#edecee',
gutterBorder: 'transparent',
lineHighlight: '#a394f033',
},
styles: [
{ tag: t.keyword, color: '#a277ff' },
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#edecee' },
{ tag: [t.propertyName], color: '#ffca85' },
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#61ffca' },
{ tag: [t.function(t.variableName), t.labelName], color: '#ffca85' },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#61ffca' },
{ tag: [t.definition(t.name), t.separator], color: '#edecee' },
{ tag: [t.className], color: '#82e2ff' },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#61ffca' },
{ tag: [t.typeName], color: '#82e2ff' },
{ tag: [t.operator, t.operatorKeyword], color: '#a277ff' },
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#61ffca' },
{ tag: [t.meta, t.comment], color: '#6d6d6d' },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: '#a277ff' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#edecee' },
{ tag: t.invalid, color: '#ff6767' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
],
});

View file

@ -0,0 +1,46 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#FFFFFF',
lineBackground: '#FFFFFF99',
foreground: '#000000',
caret: '#FBAC52',
selection: '#FFD420',
selectionMatch: '#FFD420',
gutterBackground: '#f5f5f5',
gutterForeground: '#4D4D4C',
gutterBorder: 'transparent',
lineHighlight: '#00000012',
};
export default createTheme({
theme: 'light',
settings: {
background: '#FFFFFF',
foreground: '#000000',
caret: '#FBAC52',
selection: '#FFD420',
selectionMatch: '#FFD420',
gutterBackground: '#f5f5f5',
gutterForeground: '#4D4D4C',
gutterBorder: 'transparent',
lineHighlight: '#00000012',
},
styles: [
{ tag: [t.meta, t.comment], color: '#804000' },
{ tag: [t.keyword, t.strong], color: '#0000FF' },
{ tag: [t.number], color: '#FF0080' },
{ tag: [t.string], color: '#FF0080' },
{ tag: [t.variableName], color: '#006600' },
{ tag: [t.escape], color: '#33CC33' },
{ tag: [t.tagName], color: '#1C02FF' },
{ tag: [t.heading], color: '#0C07FF' },
{ tag: [t.quote], color: '#000000' },
{ tag: [t.list], color: '#B90690' },
{ tag: [t.documentMeta], color: '#888888' },
{ tag: [t.function(t.variableName)], color: '#0000A2' },
{ tag: [t.definition(t.typeName), t.typeName], color: '#6D79DE' },
],
});

View file

@ -0,0 +1,39 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
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.labelName, color: 'inherit' },
{ tag: t.keyword, color: 'inherit' },
{ tag: t.operator, color: 'inherit' },
{ tag: t.special(t.variableName), color: 'inherit' },
{ tag: t.typeName, color: 'inherit' },
{ tag: t.atom, color: 'inherit' },
{ tag: t.number, color: 'inherit' },
{ tag: t.definition(t.variableName), color: 'inherit' },
{ tag: t.string, color: 'inherit' },
{ tag: t.special(t.string), color: 'inherit' },
{ tag: t.comment, color: 'inherit' },
{ tag: t.variableName, color: 'inherit' },
{ tag: t.tagName, color: 'inherit' },
{ tag: t.bracket, color: 'inherit' },
{ tag: t.meta, color: 'inherit' },
{ tag: t.attributeName, color: 'inherit' },
{ tag: t.propertyName, color: 'inherit' },
{ tag: t.className, color: 'inherit' },
{ tag: t.invalid, color: 'inherit' },
{ tag: [t.unit, t.punctuation], color: 'inherit' },
],
});

View file

@ -0,0 +1,42 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
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.labelName, color: 'inherit' },
{ tag: t.keyword, color: 'inherit' },
{ tag: t.operator, color: 'inherit' },
{ tag: t.special(t.variableName), color: 'inherit' },
{ tag: t.typeName, color: 'inherit' },
{ tag: t.atom, color: 'inherit' },
{ tag: t.number, color: 'inherit' },
{ tag: t.definition(t.variableName), color: 'inherit' },
{ tag: t.string, color: 'inherit' },
{ tag: t.special(t.string), color: 'inherit' },
{ tag: t.comment, color: 'inherit' },
{ tag: t.variableName, color: 'inherit' },
{ tag: t.tagName, color: 'inherit' },
{ tag: t.bracket, color: 'inherit' },
{ tag: t.meta, color: 'inherit' },
{ tag: t.attributeName, color: 'inherit' },
{ tag: t.propertyName, color: 'inherit' },
{ tag: t.className, color: 'inherit' },
{ tag: t.invalid, color: 'inherit' },
{ tag: [t.unit, t.punctuation], color: 'inherit' },
],
});

View file

@ -0,0 +1,37 @@
/*
* A lighter blue screen theme
* made by Jade
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
const hex = ['rgb(75, 130, 247)', 'rgb(47, 108, 246)', 'rgb(255, 255, 255)', 'rgba(255, 255, 255,.3)'];
export const settings = {
background: hex[0],
lineBackground: 'transparent',
foreground: hex[2],
selection: hex[3],
selectionMatch: hex[0],
gutterBackground: hex[0],
gutterForeground: hex[2],
gutterBorder: 'transparent',
lineHighlight: hex[1],
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
color: hex[2],
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[2] },
{ tag: [t.comment, t.bracket, t.brace, t.compareOperator], color: hex[3] },
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
{ tag: [t.attributeName, t.number], color: hex[2] },
{ tag: t.keyword, color: hex[2] },
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[2] },
],
});

View file

@ -0,0 +1,47 @@
/*
* darcula
* Name: IntelliJ IDEA darcula theme
* From IntelliJ IDEA by JetBrains
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#242424',
lineBackground: '#24242499',
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)',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#242424',
foreground: '#f8f8f2',
caret: '#FFFFFF',
selection: 'rgba(255, 255, 255, 0.1)',
selectionMatch: 'rgba(255, 255, 255, 0.2)',
gutterBackground: 'transparent',
gutterForeground: '#999',
gutterBorder: 'transparent',
lineHighlight: 'rgba(255, 255, 255, 0.1)',
},
styles: [
{ tag: t.labelName, color: '#CCCCCC' },
{ tag: [t.atom, t.number], color: '#7A9EC2' },
{ tag: [t.comment], color: '#707070' },
{ tag: [t.string], color: '#6A8759' },
{ tag: [t.variableName, t.operator], color: '#CCCCCC' },
{ tag: [t.function(t.variableName), t.propertyName], color: '#FFC66D' },
{ tag: [t.meta, t.className], color: '#FFC66D' },
{ tag: [t.propertyName], color: '#FFC66D' },
{ tag: [t.keyword], color: '#CC7832' },
{ tag: [t.tagName], color: '#ff79c6' },
{ tag: [t.typeName], color: '#ffb86c' },
],
});

View file

@ -0,0 +1,50 @@
/*
* @name dracula
* Michael Kaminsky (http://github.com/mkaminsky11)
* Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)
*/
// this is different from https://thememirror.net/dracula
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
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: '#6272a4',
gutterBorder: 'transparent',
lineHighlight: 'rgba(255, 255, 255, 0.1)',
};
const purple = '#BD93F9';
export default createTheme({
theme: 'dark',
settings: {
background: '#282a36',
foreground: '#f8f8f2',
caret: '#f8f8f0',
selection: 'rgba(255, 255, 255, 0.1)',
selectionMatch: 'rgba(255, 255, 255, 0.2)',
gutterBackground: '#282a36',
gutterForeground: '#6272a4',
gutterBorder: 'transparent',
lineHighlight: 'rgba(255, 255, 255, 0.1)',
},
styles: [
{ tag: t.comment, color: '#6272a4' },
{ tag: t.string, color: '#f1fa8c' },
{ tag: [t.atom, t.number], color: purple },
{ tag: [t.meta, t.labelName, t.variableName], color: '#f8f8f2' },
{
tag: [t.keyword, t.tagName, t.arithmeticOperator],
color: '#ff79c6',
},
{ tag: [t.function(t.variableName), t.propertyName], color: '#50fa7b' },
{ tag: t.atom, color: '#bd93f9' },
],
});

View file

@ -0,0 +1,42 @@
/*
* duotone
* author Bram de Haan
* by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#2a2734',
lineBackground: '#2a273499',
foreground: '#eeebff',
caret: '#ffad5c',
selection: 'rgba(255, 255, 255, 0.1)',
gutterBackground: '#2a2734',
gutterForeground: '#545167',
lineHighlight: '#36334280',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#2a2734',
foreground: '#6c6783',
caret: '#ffad5c',
selection: '#9a86fd',
selectionMatch: '#9a86fd',
gutterBackground: '#2a2734',
gutterForeground: '#545167',
lineHighlight: '#36334280',
},
styles: [
{ tag: [t.comment, t.bracket, t.operator], color: '#6c6783' },
{ tag: [t.atom, t.number, t.keyword, t.link, t.attributeName, t.quote], color: '#ffcc99' },
{ tag: [t.emphasis, t.heading, t.tagName, t.propertyName, t.className, t.variableName], color: '#eeebff' },
{ tag: [t.typeName, t.url], color: '#eeebff' },
{ tag: t.string, color: '#ffb870' },
/* { tag: [t.propertyName], color: '#9a86fd' }, */
{ tag: [t.propertyName], color: '#eeebff' },
{ tag: t.labelName, color: '#eeebff' },
],
});

View file

@ -0,0 +1,45 @@
/*
* duotone
* author Bram de Haan
* by Bram de Haan, adapted from DuoTone themes by Simurai (http://simurai.com/projects/2016/01/01/duotone-themes)
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#faf8f5',
lineBackground: '#faf8f599',
foreground: '#b29762',
caret: '#93abdc',
selection: '#e3dcce',
selectionMatch: '#e3dcce',
gutterBackground: '#faf8f5',
gutterForeground: '#cdc4b1',
gutterBorder: 'transparent',
lineHighlight: '#EFEFEF',
};
export default createTheme({
theme: 'light',
settings: {
background: '#faf8f5',
foreground: '#b29762',
caret: '#93abdc',
selection: '#e3dcce',
selectionMatch: '#e3dcce',
gutterBackground: '#faf8f5',
gutterForeground: '#cdc4b1',
gutterBorder: 'transparent',
lineHighlight: '#ddceb154',
},
styles: [
{ tag: [t.comment, t.bracket], color: '#b6ad9a' },
{ tag: [t.atom, t.number, t.keyword, t.link, t.attributeName, t.quote], color: '#063289' },
{ tag: [t.emphasis, t.heading, t.tagName, t.propertyName, t.variableName], color: '#2d2006' },
{ tag: [t.typeName, t.url, t.string], color: '#896724' },
{ tag: [t.operator, t.string], color: '#1659df' },
{ tag: [t.propertyName], color: '#b29762' },
{ tag: [t.unit, t.punctuation], color: '#063289' },
],
});

View file

@ -0,0 +1,46 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#fff',
lineBackground: '#ffffff99',
foreground: '#000',
caret: '#FFFFFF',
selection: '#d7d4f0',
selectionMatch: '#d7d4f0',
gutterBackground: '#f7f7f7',
gutterForeground: '#999',
lineHighlight: '#e8f2ff',
gutterBorder: 'transparent',
};
export default createTheme({
theme: 'light',
settings: {
background: '#fff',
foreground: '#000',
caret: '#FFFFFF',
selection: '#d7d4f0',
selectionMatch: '#d7d4f0',
gutterBackground: '#f7f7f7',
gutterForeground: '#999',
lineHighlight: '#006fff1c',
gutterBorder: 'transparent',
},
styles: [
{ tag: [t.comment], color: '#3F7F5F' },
{ tag: [t.documentMeta], color: '#FF1717' },
{ tag: t.keyword, color: '#7F0055', fontWeight: 'bold' },
{ tag: t.atom, color: '#00f' },
{ tag: t.number, color: '#164' },
{ tag: t.propertyName, color: '#164' },
{ tag: [t.variableName, t.definition(t.variableName)], color: '#0000C0' },
{ tag: t.function(t.variableName), color: '#0000C0' },
{ tag: t.string, color: '#2A00FF' },
{ tag: t.operator, color: 'black' },
{ tag: t.tagName, color: '#170' },
{ tag: t.attributeName, color: '#00c' },
{ tag: t.link, color: '#219' },
],
});

View file

@ -0,0 +1,50 @@
/*
* Fruit Daw
* made by Jade
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
const hex = [
'rgb(84, 93, 98)',
'rgb(255, 255, 255)',
'rgba(255, 255, 255, .25)',
'rgb(67, 76, 81)',
'rgb(186, 230, 115)',
'rgb(252, 184, 67)',
'rgb(124, 206, 254)',
'rgb(83, 101, 102)',
'rgba(46, 62, 72,.5)',
'rgb(94, 100, 108)',
'rgb(167, 216, 177)',
];
export const settings = {
background: hex[0],
lineBackground: 'transparent',
foreground: hex[10],
selection: hex[8],
selectionMatch: hex[0],
gutterBackground: hex[3],
gutterForeground: hex[2],
gutterBorder: 'transparent',
lineHighlight: hex[3],
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
color: hex[1],
},
{ tag: [t.bool, t.special(t.variableName)], color: hex[1] },
{ tag: [t.comment, t.brace, t.bracket], color: hex[2] },
{ tag: [t.variableName], color: hex[1] },
{ tag: [t.labelName, t.propertyName, t.self, t.atom], color: hex[5] },
{ tag: [t.attributeName, t.number], color: hex[6] },
{ tag: t.keyword, color: hex[5] },
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[4] },
],
});

View file

@ -0,0 +1,42 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#0d1117',
lineBackground: '#0d111799',
foreground: '#c9d1d9',
caret: '#c9d1d9',
selection: '#003d73',
selectionMatch: '#003d73',
lineHighlight: '#36334280',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#0d1117',
foreground: '#c9d1d9',
caret: '#c9d1d9',
selection: '#003d73',
selectionMatch: '#003d73',
lineHighlight: '#36334280',
},
styles: [
{ tag: t.labelName, color: '#d2a8ff' },
{ tag: [t.standard(t.tagName), t.tagName], color: '#7ee787' },
{ tag: [t.comment, t.bracket], color: '#8b949e' },
{ tag: [t.className, t.propertyName], color: '#d2a8ff' },
{ tag: [t.variableName, t.attributeName], color: '#d2a8ff' },
{ tag: [t.number, t.operator], color: '#79c0ff' },
{ tag: [t.keyword, t.typeName, t.typeOperator, t.typeName], color: '#ff7b72' },
{ tag: [t.string, t.meta, t.regexp], color: '#a5d6ff' },
{ tag: [t.name, t.quote], color: '#7ee787' },
{ tag: [t.heading, t.strong], color: '#d2a8ff', fontWeight: 'bold' },
{ tag: [t.emphasis], color: '#d2a8ff', fontStyle: 'italic' },
{ tag: [t.deleted], color: '#ffdcd7', backgroundColor: 'ffeef0' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#ffab70' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.invalid, color: '#f97583' },
],
});

View file

@ -0,0 +1,42 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#fff',
lineBackground: '#ffffff99',
foreground: '#24292e',
selection: '#BBDFFF',
selectionMatch: '#BBDFFF',
gutterBackground: '#fff',
gutterForeground: '#6e7781',
};
export default createTheme({
theme: 'light',
settings: {
background: '#fff',
foreground: '#24292e',
selection: '#BBDFFF',
selectionMatch: '#BBDFFF',
gutterBackground: '#fff',
gutterForeground: '#6e7781',
},
styles: [
{ tag: [t.standard(t.tagName), t.tagName], color: '#116329' },
{ tag: [t.comment, t.bracket], color: '#6a737d' },
{ tag: [t.className, t.propertyName], color: '#6f42c1' },
{ tag: [t.variableName, t.attributeName, t.number, t.operator], color: '#005cc5' },
{ tag: [t.keyword, t.typeName, t.typeOperator, t.typeName], color: '#d73a49' },
{ tag: [t.string, t.meta, t.regexp], color: '#032f62' },
{ tag: [t.name, t.quote], color: '#22863a' },
{ tag: [t.heading, t.strong], color: '#24292e', fontWeight: 'bold' },
{ tag: [t.emphasis], color: '#24292e', fontStyle: 'italic' },
{ tag: [t.deleted], color: '#b31d28', backgroundColor: 'ffeef0' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#e36209' },
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#032f62' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.invalid, color: '#cb2431' },
],
});

View file

@ -0,0 +1,39 @@
/*
* Atom One
* Atom One dark syntax theme
*
* https://github.com/atom/one-dark-syntax
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
const hex = ['#000000', '#8ed675', '#56bd2a', '#54636D', '#171717'];
export const settings = {
background: hex[0],
lineBackground: 'transparent',
foreground: hex[2],
selection: hex[4],
selectionMatch: hex[0],
gutterBackground: hex[0],
gutterForeground: hex[3],
gutterBorder: 'transparent',
lineHighlight: hex[0],
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
color: hex[2],
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
{ tag: t.comment, color: hex[3] },
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
{ tag: [t.attributeName, t.number], color: hex[1] },
{ tag: t.keyword, color: hex[2] },
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
],
});

View file

@ -0,0 +1,82 @@
/*
* gruvbox-dark
* author morhetz
* From github.com/codemirror/codemirror5/blob/master/theme/gruvbox-dark.css
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#282828',
lineBackground: '#28282899',
foreground: '#ebdbb2',
caret: '#ebdbb2',
selection: '#bdae93',
selectionMatch: '#bdae93',
lineHighlight: '#3c3836',
gutterBackground: '#282828',
gutterForeground: '#7c6f64',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#282828',
foreground: '#ebdbb2',
caret: '#ebdbb2',
selection: '#b99d555c',
selectionMatch: '#b99d555c',
lineHighlight: '#baa1602b',
gutterBackground: '#282828',
gutterForeground: '#7c6f64',
},
styles: [
{ tag: t.keyword, color: '#fb4934' },
{ tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: '#8ec07c' },
{ tag: [t.variableName], color: '#83a598' },
{ tag: [t.function(t.variableName)], color: '#8ec07c', fontStyle: 'bold' },
{ tag: [t.labelName], color: '#ebdbb2' },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#d3869b' },
{ tag: [t.definition(t.name), t.separator], color: '#ebdbb2' },
{ tag: [t.brace], color: '#ebdbb2' },
{ tag: [t.annotation], color: '#fb4934d' },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#d3869b' },
{ tag: [t.typeName, t.className], color: '#fabd2f' },
{ tag: [t.operatorKeyword], color: '#fb4934' },
{
tag: [t.tagName],
color: '#8ec07c',
fontStyle: 'bold',
},
{ tag: [t.squareBracket], color: '#fe8019' },
{ tag: [t.angleBracket], color: '#83a598' },
{ tag: [t.attributeName], color: '#8ec07c' },
{ tag: [t.regexp], color: '#8ec07c' },
{ tag: [t.quote], color: '#928374' },
{ tag: [t.string], color: '#ebdbb2' },
{
tag: t.link,
color: '#a89984',
textDecoration: 'underline',
textUnderlinePosition: 'under',
},
{ tag: [t.url, t.escape, t.special(t.string)], color: '#d3869b' },
{ tag: [t.meta], color: '#fabd2f' },
{ tag: [t.comment], color: '#928374', fontStyle: 'italic' },
{ tag: t.strong, fontWeight: 'bold', color: '#fe8019' },
{ tag: t.emphasis, fontStyle: 'italic', color: '#b8bb26' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.heading, fontWeight: 'bold', color: '#b8bb26' },
{ tag: [t.heading1, t.heading2], fontWeight: 'bold', color: '#b8bb26' },
{
tag: [t.heading3, t.heading4],
fontWeight: 'bold',
color: '#fabd2f',
},
{ tag: [t.heading5, t.heading6], color: '#fabd2f' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#d3869b' },
{ tag: [t.processingInstruction, t.inserted], color: '#83a598' },
{ tag: [t.contentSeparator], color: '#fb4934' },
{ tag: t.invalid, color: '#fe8019', borderBottom: `1px dotted #fb4934d` },
],
});

View file

@ -0,0 +1,130 @@
/*
* gruvbox-light
* author morhetz
* From github.com/codemirror/codemirror5/blob/master/theme/gruvbox-light.css
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#fbf1c7',
lineBackground: '#fbf1c799',
foreground: '#3c3836',
caret: '#af3a03',
selection: '#ebdbb2',
selectionMatch: '#bdae93',
lineHighlight: '#ebdbb2',
gutterBackground: '#ebdbb2',
gutterForeground: '#665c54',
gutterBorder: 'transparent',
};
export default createTheme({
theme: 'light',
settings: {
background: '#fbf1c7',
foreground: '#3c3836',
caret: '#af3a03',
selection: '#bdae9391',
selectionMatch: '#bdae9391',
lineHighlight: '#a37f2238',
gutterBackground: '#ebdbb2',
gutterForeground: '#665c54',
gutterBorder: 'transparent',
},
styles: [
{ tag: t.keyword, color: '#9d0006' },
{
tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName],
color: '#427b58',
},
{ tag: [t.variableName], color: '#076678' },
{ tag: [t.function(t.variableName)], color: '#79740e', fontStyle: 'bold' },
{ tag: [t.labelName], color: '#3c3836' },
{
tag: [t.color, t.constant(t.name), t.standard(t.name)],
color: '#8f3f71',
},
{ tag: [t.definition(t.name), t.separator], color: '#3c3836' },
{ tag: [t.brace], color: '#3c3836' },
{
tag: [t.annotation],
color: '#9d0006',
},
{
tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],
color: '#8f3f71',
},
{
tag: [t.typeName, t.className],
color: '#b57614',
},
{
tag: [t.operator, t.operatorKeyword],
color: '#9d0006',
},
{
tag: [t.tagName],
color: '#427b58',
fontStyle: 'bold',
},
{
tag: [t.squareBracket],
color: '#af3a03',
},
{
tag: [t.angleBracket],
color: '#076678',
},
{
tag: [t.attributeName],
color: '#427b58',
},
{
tag: [t.regexp],
color: '#427b58',
},
{
tag: [t.quote],
color: '#928374',
},
{ tag: [t.string], color: '#3c3836' },
{
tag: t.link,
color: '#7c6f64',
textDecoration: 'underline',
textUnderlinePosition: 'under',
},
{
tag: [t.url, t.escape, t.special(t.string)],
color: '#8f3f71',
},
{ tag: [t.meta], color: '#b57614' },
{ tag: [t.comment], color: '#928374', fontStyle: 'italic' },
{ tag: t.strong, fontWeight: 'bold', color: '#af3a03' },
{ tag: t.emphasis, fontStyle: 'italic', color: '#79740e' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.heading, fontWeight: 'bold', color: '#79740e' },
{ tag: [t.heading1, t.heading2], fontWeight: 'bold', color: '#79740e' },
{
tag: [t.heading3, t.heading4],
fontWeight: 'bold',
color: '#b57614',
},
{
tag: [t.heading5, t.heading6],
color: '#b57614',
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#8f3f71' },
{
tag: [t.processingInstruction, t.inserted],
color: '#076678',
},
{
tag: [t.contentSeparator],
color: '#9d0006',
},
{ tag: t.invalid, color: '#af3a03', borderBottom: `1px dotted #9d0006` },
],
});

View file

@ -0,0 +1,77 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#212121',
lineBackground: '#21212199',
foreground: '#bdbdbd',
caret: '#a0a4ae',
selection: '#d7d4f0',
selectionMatch: '#d7d4f0',
gutterBackground: '#212121',
gutterForeground: '#999',
gutterActiveForeground: '#4f5b66',
lineHighlight: '#111111',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#212121',
foreground: '#bdbdbd',
caret: '#a0a4ae',
selection: '#d7d4f063',
selectionMatch: '#d7d4f063',
gutterBackground: '#212121',
gutterForeground: '#999',
gutterActiveForeground: '#4f5b66',
lineHighlight: '#333333',
},
styles: [
{ tag: t.keyword, color: '#cf6edf' },
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#56c8d8' },
{ tag: [t.propertyName], color: '#82AAFF' },
{ tag: [t.variableName], color: '#bdbdbd' },
{ tag: [t.function(t.variableName)], color: '#82AAFF' },
{ tag: [t.labelName], color: '#cf6edf' },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#facf4e' },
{ tag: [t.definition(t.name), t.separator], color: '#56c8d8' },
{ tag: [t.brace], color: '#cf6edf' },
{ tag: [t.annotation], color: '#f07178' },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#f07178' },
{ tag: [t.typeName, t.className], color: '#f07178' },
{ tag: [t.operator, t.operatorKeyword], color: '#82AAFF' },
{ tag: [t.tagName], color: '#99d066' },
{ tag: [t.squareBracket], color: '#f07178' },
{ tag: [t.angleBracket], color: '#606f7a' },
{ tag: [t.attributeName], color: '#bdbdbd' },
{ tag: [t.regexp], color: '#f07178' },
{ tag: [t.quote], color: '#6abf69' },
{ tag: [t.string], color: '#99d066' },
{
tag: t.link,
color: '#56c8d8',
textDecoration: 'underline',
textUnderlinePosition: 'under',
},
{ tag: [t.url, t.escape, t.special(t.string)], color: '#facf4e' },
{ tag: [t.meta], color: '#707d8b' },
{ tag: [t.comment], color: '#707d8b', fontStyle: 'italic' },
{ tag: t.monospace, color: '#bdbdbd' },
{ tag: t.strong, fontWeight: 'bold', color: '#f07178' },
{ tag: t.emphasis, fontStyle: 'italic', color: '#99d066' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.heading, fontWeight: 'bold', color: '#facf4e' },
{ tag: t.heading1, fontWeight: 'bold', color: '#facf4e' },
{
tag: [t.heading2, t.heading3, t.heading4],
fontWeight: 'bold',
color: '#facf4e',
},
{ tag: [t.heading5, t.heading6], color: '#facf4e' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#56c8d8' },
{ tag: [t.processingInstruction, t.inserted], color: '#f07178' },
{ tag: [t.contentSeparator], color: '#56c8d8' },
{ tag: t.invalid, color: '#606f7a', borderBottom: `1px dotted #f07178` },
],
});

View file

@ -0,0 +1,52 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#FAFAFA',
lineBackground: '#FAFAFA99',
foreground: '#90A4AE',
caret: '#272727',
selection: '#80CBC440',
selectionMatch: '#FAFAFA',
gutterBackground: '#FAFAFA',
gutterForeground: '#90A4AE',
gutterBorder: 'transparent',
lineHighlight: '#CCD7DA50',
};
export default createTheme({
theme: 'light',
settings: {
background: '#FAFAFA',
foreground: '#90A4AE',
caret: '#272727',
selection: '#80CBC440',
selectionMatch: '#80CBC440',
gutterBackground: '#FAFAFA',
gutterForeground: '#90A4AE',
gutterBorder: 'transparent',
lineHighlight: '#CCD7DA50',
},
styles: [
{ tag: t.keyword, color: '#39ADB5' },
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#90A4AE' },
{ tag: [t.propertyName], color: '#6182B8' },
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#91B859' },
{ tag: [t.function(t.variableName), t.labelName], color: '#6182B8' },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#39ADB5' },
{ tag: [t.definition(t.name), t.separator], color: '#90A4AE' },
{ tag: [t.className], color: '#E2931D' },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#F76D47' },
{ tag: [t.typeName], color: '#E2931D', fontStyle: '#E2931D' },
{ tag: [t.operator, t.operatorKeyword], color: '#39ADB5' },
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#91B859' },
{ tag: [t.meta, t.comment], color: '#90A4AE' },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: '#39ADB5' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#90A4AE' },
{ tag: t.invalid, color: '#E5393570' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
],
});

View file

@ -0,0 +1,45 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#272822',
lineBackground: '#27282299',
foreground: '#FFFFFF',
caret: '#FFFFFF',
selection: '#49483E',
selectionMatch: '#49483E',
gutterBackground: '#272822',
gutterForeground: '#FFFFFF70',
lineHighlight: '#00000059',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#272822',
foreground: '#FFFFFF',
caret: '#FFFFFF',
selection: '#49483E',
selectionMatch: '#49483E',
gutterBackground: '#272822',
gutterForeground: '#FFFFFF70',
lineHighlight: '#0000003b',
},
styles: [
{ tag: t.labelName, color: '#bababa' },
{ tag: [t.comment, t.documentMeta], color: '#8292a2' },
{ tag: [t.number, t.bool, t.null, t.atom], color: '#ae81ff' },
{ tag: [t.attributeValue, t.className, t.name], color: '#e6db74' },
{ tag: [t.propertyName, t.attributeName], color: '#a6e22e' },
{ tag: [t.variableName], color: '#9effff' },
{ tag: [t.squareBracket], color: '#bababa' },
{ tag: [t.string, t.special(t.brace)], color: '#e6db74' },
{ tag: [t.regexp, t.className, t.typeName, t.definition(t.typeName)], color: '#66d9ef' },
{
tag: [t.definition(t.variableName), t.definition(t.propertyName), t.function(t.variableName)],
color: '#a6e22e',
},
// { tag: t.keyword, color: '#f92672' },
{ tag: [t.keyword, t.definitionKeyword, t.modifier, t.tagName, t.angleBracket], color: '#f92672' },
],
});

View file

@ -0,0 +1,50 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#f2f1f8',
lineBackground: '#f2f1f899',
foreground: '#0c006b',
caret: '#5c49e9',
selection: '#d5d1f2',
selectionMatch: '#d5d1f2',
gutterBackground: '#f2f1f8',
gutterForeground: '#0c006b70',
lineHighlight: '#e1def3',
};
export default createTheme({
theme: 'light',
settings: {
background: '#f2f1f8',
foreground: '#0c006b',
caret: '#5c49e9',
selection: '#d5d1f2',
selectionMatch: '#d5d1f2',
gutterBackground: '#f2f1f8',
gutterForeground: '#0c006b70',
lineHighlight: '#16067911',
},
styles: [
{ tag: t.comment, color: '#9995b7' },
{
tag: t.keyword,
color: '#ff5792',
fontWeight: 'bold',
},
{ tag: [t.definitionKeyword, t.modifier], color: '#ff5792' },
{ tag: [t.className, t.tagName, t.definition(t.typeName)], color: '#0094f0' },
{ tag: [t.number, t.bool, t.null, t.special(t.brace)], color: '#5842ff' },
{ tag: [t.definition(t.propertyName), t.function(t.variableName)], color: '#0095a8' },
{ tag: t.typeName, color: '#b3694d' },
{ tag: [t.propertyName, t.variableName], color: '#fa8900' },
{ tag: t.operator, color: '#ff5792' },
{ tag: t.self, color: '#e64100' },
{ tag: [t.string, t.regexp], color: '#00b368' },
{ tag: [t.paren, t.bracket], color: '#0431fa' },
{ tag: t.labelName, color: '#00bdd6' },
{ tag: t.attributeName, color: '#e64100' },
{ tag: t.angleBracket, color: '#9995b7' },
],
});

78
src/strudel/codemirror/themes/nord.mjs vendored Normal file
View file

@ -0,0 +1,78 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#2e3440',
lineBackground: '#2e344099',
foreground: '#FFFFFF',
caret: '#FFFFFF',
selection: '#3b4252',
selectionMatch: '#e5e9f0',
gutterBackground: '#2e3440',
gutterForeground: '#4c566a',
gutterActiveForeground: '#d8dee9',
lineHighlight: '#4c566a',
};
// Colors from https://www.nordtheme.com/docs/colors-and-palettes
export default createTheme({
theme: 'dark',
settings: {
background: '#2e3440',
foreground: '#FFFFFF',
caret: '#FFFFFF',
selection: '#00000073',
selectionMatch: '#00000073',
gutterBackground: '#2e3440',
gutterForeground: '#4c566a',
gutterActiveForeground: '#d8dee9',
lineHighlight: '#4c566a29',
},
styles: [
{ tag: t.keyword, color: '#5e81ac' },
{ tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: '#88c0d0' },
{ tag: [t.variableName], color: '#8fbcbb' },
{ tag: [t.function(t.variableName)], color: '#8fbcbb' },
{ tag: [t.labelName], color: '#81a1c1' },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#5e81ac' },
{ tag: [t.definition(t.name), t.separator], color: '#a3be8c' },
{ tag: [t.brace], color: '#8fbcbb' },
{ tag: [t.annotation], color: '#d30102' },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#b48ead' },
{ tag: [t.typeName, t.className], color: '#ebcb8b' },
{ tag: [t.operator, t.operatorKeyword], color: '#a3be8c' },
{ tag: [t.tagName], color: '#b48ead' },
{ tag: [t.squareBracket], color: '#bf616a' },
{ tag: [t.angleBracket], color: '#d08770' },
{ tag: [t.attributeName], color: '#ebcb8b' },
{ tag: [t.regexp], color: '#5e81ac' },
{ tag: [t.quote], color: '#b48ead' },
{ tag: [t.string], color: '#a3be8c' },
{
tag: t.link,
color: '#a3be8c',
textDecoration: 'underline',
textUnderlinePosition: 'under',
},
{ tag: [t.url, t.escape, t.special(t.string)], color: '#8fbcbb' },
{ tag: [t.meta], color: '#88c0d0' },
{ tag: [t.monospace], color: '#d8dee9', fontStyle: 'italic' },
{ tag: [t.comment], color: '#4c566a', fontStyle: 'italic' },
{ tag: t.strong, fontWeight: 'bold', color: '#5e81ac' },
{ tag: t.emphasis, fontStyle: 'italic', color: '#5e81ac' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.heading, fontWeight: 'bold', color: '#5e81ac' },
{ tag: t.special(t.heading1), fontWeight: 'bold', color: '#5e81ac' },
{ tag: t.heading1, fontWeight: 'bold', color: '#5e81ac' },
{
tag: [t.heading2, t.heading3, t.heading4],
fontWeight: 'bold',
color: '#5e81ac',
},
{ tag: [t.heading5, t.heading6], color: '#5e81ac' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#d08770' },
{ tag: [t.processingInstruction, t.inserted], color: '#8fbcbb' },
{ tag: [t.contentSeparator], color: '#ebcb8b' },
{ tag: t.invalid, color: '#434c5e', borderBottom: `1px dotted #d30102` },
],
});

View file

@ -0,0 +1,39 @@
/*
* Atom One
* Atom One dark syntax theme
*
* https://github.com/atom/one-dark-syntax
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
const hex = ['#000000', '#ff5356', '#bd312a', '#54636D', '#171717'];
export const settings = {
background: hex[0],
lineBackground: 'transparent',
foreground: hex[2],
selection: hex[4],
selectionMatch: hex[0],
gutterBackground: hex[0],
gutterForeground: hex[3],
gutterBorder: 'transparent',
lineHighlight: hex[0],
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
color: hex[2],
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
{ tag: t.comment, color: hex[3] },
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
{ tag: [t.attributeName, t.number], color: hex[1] },
{ tag: t.keyword, color: hex[2] },
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
],
});

View file

@ -0,0 +1,79 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#002b36',
lineBackground: '#002b3699',
foreground: '#93a1a1',
caret: '#839496',
selection: '#173541',
selectionMatch: '#aafe661a',
gutterBackground: '#00252f',
gutterForeground: '#839496',
lineHighlight: '#173541',
};
const c = {
background: '#002B36',
foreground: '#839496',
selection: '#004454AA',
selectionMatch: '#005A6FAA',
cursor: '#D30102',
dropdownBackground: '#00212B',
dropdownBorder: '#2AA19899',
activeLine: '#00cafe11',
matchingBracket: '#073642',
keyword: '#859900',
storage: '#93A1A1',
variable: '#268BD2',
parameter: '#268BD2',
function: '#268BD2',
string: '#2AA198',
constant: '#CB4B16',
type: '#859900',
class: '#268BD2',
number: '#D33682',
comment: '#586E75',
heading: '#268BD2',
invalid: '#DC322F',
regexp: '#DC322F',
tag: '#268BD2',
};
export default createTheme({
theme: 'dark',
settings: {
background: c.background,
foreground: c.foreground,
caret: c.cursor,
selection: c.selection,
selectionMatch: c.selection,
gutterBackground: c.background,
gutterForeground: c.foreground,
gutterBorder: 'transparent',
lineHighlight: c.activeLine,
},
styles: [
{ tag: t.keyword, color: c.keyword },
{ tag: [t.name, t.deleted, t.character, t.macroName], color: c.variable },
{ tag: [t.propertyName], color: c.function },
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: c.string },
{ tag: [t.function(t.variableName), t.labelName], color: c.function },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: c.constant },
{ tag: [t.definition(t.name), t.separator], color: c.variable },
{ tag: [t.className], color: c.class },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: c.number },
{ tag: [t.typeName], color: c.type, fontStyle: c.type },
{ tag: [t.operator, t.operatorKeyword], color: c.keyword },
{ tag: [t.url, t.escape, t.regexp, t.link], color: c.regexp },
{ tag: [t.meta, t.comment], color: c.comment },
{ tag: t.tagName, color: c.tag },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: c.heading },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: c.variable },
{ tag: t.invalid, color: c.invalid },
{ tag: t.strikethrough, textDecoration: 'line-through' },
],
});

View file

@ -0,0 +1,82 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
// this is slightly different from https://thememirror.net/solarized-light
export const settings = {
light: true,
background: '#fdf6e3',
lineBackground: '#fdf6e399',
foreground: '#657b83',
caret: '#586e75',
selection: '#dfd9c8',
selectionMatch: '#dfd9c8',
gutterBackground: '#00000010',
gutterForeground: '#657b83',
lineHighlight: '#dfd9c8',
};
const c = {
background: '#FDF6E3',
foreground: '#657B83',
selection: '#EEE8D5',
selectionMatch: '#EEE8D5',
cursor: '#657B83',
dropdownBackground: '#EEE8D5',
dropdownBorder: '#D3AF86',
activeLine: '#3d392d11',
matchingBracket: '#EEE8D5',
keyword: '#859900',
storage: '#586E75',
variable: '#268BD2',
parameter: '#268BD2',
function: '#268BD2',
string: '#2AA198',
constant: '#CB4B16',
type: '#859900',
class: '#268BD2',
number: '#D33682',
comment: '#93A1A1',
heading: '#268BD2',
invalid: '#DC322F',
regexp: '#DC322F',
tag: '#268BD2',
};
export default createTheme({
theme: 'light',
settings: {
background: c.background,
foreground: c.foreground,
caret: c.cursor,
selection: c.selection,
selectionMatch: c.selectionMatch,
gutterBackground: c.background,
gutterForeground: c.foreground,
gutterBorder: 'transparent',
lineHighlight: c.activeLine,
},
styles: [
{ tag: t.keyword, color: c.keyword },
{ tag: [t.name, t.deleted, t.character, t.macroName], color: c.variable },
{ tag: [t.propertyName], color: c.function },
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: c.string },
{ tag: [t.function(t.variableName), t.labelName], color: c.function },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: c.constant },
{ tag: [t.definition(t.name), t.separator], color: c.variable },
{ tag: [t.className], color: c.class },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: c.number },
{ tag: [t.typeName], color: c.type, fontStyle: c.type },
{ tag: [t.operator, t.operatorKeyword], color: c.keyword },
{ tag: [t.url, t.escape, t.regexp, t.link], color: c.regexp },
{ tag: [t.meta, t.comment], color: c.comment },
{ tag: t.tagName, color: c.tag },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: c.heading },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: c.variable },
{ tag: t.invalid, color: c.invalid },
{ tag: t.strikethrough, textDecoration: 'line-through' },
],
});

View file

@ -0,0 +1,39 @@
/*
* Atom One
* Atom One dark syntax theme
*
* https://github.com/atom/one-dark-syntax
*/
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
const hex = ['#1e1e1e', '#fbde2d', '#ff1493', '#4c83ff', '#ededed', '#cccccc', '#ffffff30', '#dc2f8c'];
export const settings = {
background: '#000000',
lineBackground: 'transparent',
foreground: hex[4],
selection: hex[6],
gutterBackground: hex[0],
gutterForeground: hex[5],
gutterBorder: 'transparent',
lineHighlight: hex[0],
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
color: hex[4],
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[3] },
{ tag: t.comment, color: '#54636D' },
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[4] },
{ tag: [t.attributeName, t.number], color: hex[3] },
{ tag: t.keyword, color: hex[1] },
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[2] },
],
});

View file

@ -0,0 +1,49 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#222',
lineBackground: '#22222299',
foreground: '#fff',
caret: '#ffcc00',
selection: 'rgba(128, 203, 196, 0.5)',
selectionMatch: '#036dd626',
lineHighlight: '#00000050',
gutterBackground: 'transparent',
gutterForeground: '#8a919966',
};
export default createTheme({
theme: 'dark',
settings,
styles: [
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#89ddff' },
{ tag: t.labelName, color: '#89ddff' },
{ 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' },
],
});

View file

@ -0,0 +1,43 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#303841',
lineBackground: '#30384199',
foreground: '#FFFFFF',
caret: '#FBAC52',
selection: '#4C5964',
selectionMatch: '#3A546E',
gutterBackground: '#303841',
gutterForeground: '#FFFFFF70',
lineHighlight: '#00000059',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#303841',
foreground: '#FFFFFF',
caret: '#FBAC52',
selection: '#4C5964',
selectionMatch: '#3A546E',
gutterBackground: '#303841',
gutterForeground: '#FFFFFF70',
lineHighlight: '#00000059',
},
styles: [
{ tag: t.labelName, color: '#A2A9B5' },
{ tag: [t.meta, t.comment], color: '#A2A9B5' },
{ tag: [t.attributeName, t.keyword], color: '#B78FBA' },
{ tag: t.function(t.variableName), color: '#5AB0B0' },
{ tag: [t.string, t.regexp, t.attributeValue], color: '#99C592' },
{ tag: t.operator, color: '#f47954' },
// { tag: t.moduleKeyword, color: 'red' },
{ tag: [t.tagName, t.modifier], color: '#E35F63' },
{ tag: [t.number, t.definition(t.tagName), t.className, t.definition(t.variableName)], color: '#fbac52' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#E35F63' },
{ tag: t.variableName, color: '#539ac4' },
{ tag: [t.propertyName, t.typeName], color: '#629ccd' },
{ tag: t.propertyName, color: '#36b7b5' },
],
});

View file

@ -0,0 +1,51 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
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.labelName, color: colorB },
{ 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 },
],
});

View file

@ -0,0 +1,37 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
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.labelName, color: 'inherit' },
{ tag: t.keyword, color: 'inherit' },
{ tag: t.operator, color: 'inherit' },
{ tag: t.special(t.variableName), color: 'inherit' },
{ tag: t.typeName, color: 'inherit' },
{ tag: t.atom, color: 'inherit' },
{ tag: t.number, color: 'inherit' },
{ tag: t.definition(t.variableName), color: 'inherit' },
{ tag: t.string, color: 'inherit' },
{ tag: t.special(t.string), color: 'inherit' },
{ tag: t.comment, color: 'inherit' },
{ tag: t.variableName, color: 'inherit' },
{ tag: t.tagName, color: 'inherit' },
{ tag: t.bracket, color: 'inherit' },
{ tag: t.meta, color: 'inherit' },
{ tag: t.attributeName, color: 'inherit' },
{ tag: t.propertyName, color: 'inherit' },
{ tag: t.className, color: 'inherit' },
{ tag: t.invalid, color: 'inherit' },
],
});

View file

@ -0,0 +1,42 @@
import { EditorView } from '@codemirror/view';
import { syntaxHighlighting } from '@codemirror/language';
import { HighlightStyle } from '@codemirror/language';
export const createTheme = ({ theme, settings, styles }) => {
const _theme = EditorView.theme(
{
'&': {
color: settings.foreground,
backgroundColor: settings.background,
},
'.cm-gutters': {
backgroundColor: settings.gutterBackground,
color: settings.gutterForeground,
//borderRightColor: settings.gutterBorder
},
'.cm-content': {
caretColor: settings.caret,
},
'.cm-cursor, .cm-dropCursor': {
borderLeftColor: settings.caret,
},
'.cm-activeLineGutter': {
// color: settings.gutterActiveForeground
backgroundColor: settings.lineHighlight,
},
'.cm-activeLine': {
backgroundColor: settings.lineHighlight,
},
'&.cm-focused .cm-selectionBackground, & .cm-line::selection, & .cm-selectionLayer .cm-selectionBackground, .cm-content ::selection':
{
background: settings.selection + ' !important',
},
'& .cm-selectionMatch': {
backgroundColor: settings.selectionMatch,
},
},
{ dark: theme === 'dark' },
);
const highlightStyle = HighlightStyle.define(styles);
return [_theme, syntaxHighlighting(highlightStyle)];
};

View file

@ -0,0 +1,52 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#24283b',
lineBackground: '#24283b99',
foreground: '#7982a9',
caret: '#c0caf5',
selection: '#6f7bb630',
selectionMatch: '#1f2335',
gutterBackground: '#24283b',
gutterForeground: '#7982a9',
gutterBorder: 'transparent',
lineHighlight: '#292e42',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#24283b',
foreground: '#7982a9',
caret: '#c0caf5',
selection: '#6f7bb630',
selectionMatch: '#343b5f',
gutterBackground: '#24283b',
gutterForeground: '#7982a9',
gutterBorder: 'transparent',
lineHighlight: '#292e427a',
},
styles: [
{ tag: t.keyword, color: '#bb9af7' },
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#c0caf5' },
{ tag: [t.propertyName], color: '#7aa2f7' },
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#9ece6a' },
{ tag: [t.function(t.variableName), t.labelName], color: '#7aa2f7' },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#bb9af7' },
{ tag: [t.definition(t.name), t.separator], color: '#c0caf5' },
{ tag: [t.className], color: '#c0caf5' },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#ff9e64' },
{ tag: [t.typeName], color: '#2ac3de', fontStyle: '#2ac3de' },
{ tag: [t.operator, t.operatorKeyword], color: '#bb9af7' },
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#b4f9f8' },
{ tag: [t.meta, t.comment], color: '#565f89' },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: '#89ddff' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#c0caf5' },
{ tag: t.invalid, color: '#ff5370' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
],
});

View file

@ -0,0 +1,52 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#1a1b26',
lineBackground: '#1a1b2699',
foreground: '#787c99',
caret: '#c0caf5',
selection: '#515c7e40',
selectionMatch: '#16161e',
gutterBackground: '#1a1b26',
gutterForeground: '#787c99',
gutterBorder: 'transparent',
lineHighlight: '#1e202e',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#1a1b26',
foreground: '#787c99',
caret: '#c0caf5',
selection: '#515c7e40',
selectionMatch: '#16161e',
gutterBackground: '#1a1b26',
gutterForeground: '#787c99',
gutterBorder: 'transparent',
lineHighlight: '#474b6611',
},
styles: [
{ tag: t.keyword, color: '#bb9af7' },
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#c0caf5' },
{ tag: [t.propertyName], color: '#7aa2f7' },
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#9ece6a' },
{ tag: [t.function(t.variableName), t.labelName], color: '#7aa2f7' },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#bb9af7' },
{ tag: [t.definition(t.name), t.separator], color: '#c0caf5' },
{ tag: [t.className], color: '#c0caf5' },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#ff9e64' },
{ tag: [t.typeName], color: '#0db9d7' },
{ tag: [t.operator, t.operatorKeyword], color: '#bb9af7' },
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#b4f9f8' },
{ tag: [t.meta, t.comment], color: '#444b6a' },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: '#89ddff' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#c0caf5' },
{ tag: t.invalid, color: '#ff5370' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
],
});

View file

@ -0,0 +1,53 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#e1e2e7',
lineBackground: '#e1e2e799',
foreground: '#3760bf',
caret: '#3760bf',
selection: '#99a7df',
selectionMatch: '#99a7df',
gutterBackground: '#e1e2e7',
gutterForeground: '#3760bf',
gutterBorder: 'transparent',
lineHighlight: '#5f5faf11',
};
export default createTheme({
theme: 'light',
settings: {
background: '#e1e2e7',
foreground: '#3760bf',
caret: '#3760bf',
selection: '#99a7df',
selectionMatch: '#99a7df',
gutterBackground: '#e1e2e7',
gutterForeground: '#3760bf',
gutterBorder: 'transparent',
lineHighlight: '#5f5faf11',
},
styles: [
{ tag: t.keyword, color: '#007197' },
{ tag: [t.name, t.deleted, t.character, t.macroName], color: '#3760bf' },
{ tag: [t.propertyName], color: '#3760bf' },
{ tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: '#587539' },
{ tag: [t.function(t.variableName), t.labelName], color: '#3760bf' },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#3760bf' },
{ tag: [t.definition(t.name), t.separator], color: '#3760bf' },
{ tag: [t.className], color: '#3760bf' },
{ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#b15c00' },
{ tag: [t.typeName], color: '#007197', fontStyle: '#007197' },
{ tag: [t.operator, t.operatorKeyword], color: '#007197' },
{ tag: [t.url, t.escape, t.regexp, t.link], color: '#587539' },
{ tag: [t.meta, t.comment], color: '#848cb5' },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.link, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: '#b15c00' },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#3760bf' },
{ tag: t.invalid, color: '#f52a65' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
],
});

View file

@ -0,0 +1,80 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#1e1e1e',
lineBackground: '#1e1e1e99',
foreground: '#fff',
caret: '#c6c6c6',
selection: '#6199ff2f',
selectionMatch: '#72a1ff59',
lineHighlight: '#ffffff0f',
gutterBackground: '#1e1e1e',
gutterForeground: '#838383',
gutterActiveForeground: '#fff',
};
export default createTheme({
theme: 'dark',
settings: {
background: '#1e1e1e',
foreground: '#fff',
caret: '#c6c6c6',
selection: '#6199ff2f',
selectionMatch: '#72a1ff59',
lineHighlight: '#ffffff0f',
gutterBackground: '#1e1e1e',
gutterForeground: '#838383',
gutterActiveForeground: '#fff',
fontFamily: 'Menlo, Monaco, Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
},
styles: [
{
tag: [
t.keyword,
t.operatorKeyword,
t.modifier,
t.color,
t.constant(t.name),
t.standard(t.name),
t.standard(t.tagName),
t.special(t.brace),
t.atom,
t.bool,
t.special(t.variableName),
],
color: '#569cd6',
},
{ tag: [t.controlKeyword, t.moduleKeyword], color: '#c586c0' },
{
tag: [
t.name,
t.deleted,
t.character,
t.macroName,
t.propertyName,
t.variableName,
t.labelName,
t.definition(t.name),
],
color: '#9cdcfe',
},
{ tag: t.heading, fontWeight: 'bold', color: '#9cdcfe' },
{
tag: [t.typeName, t.className, t.tagName, t.number, t.changed, t.annotation, t.self, t.namespace],
color: '#4ec9b0',
},
{ tag: [t.function(t.variableName), t.function(t.propertyName)], color: '#dcdcaa' },
{ tag: [t.number], color: '#b5cea8' },
{ tag: [t.operator, t.punctuation, t.separator, t.url, t.escape, t.regexp], color: '#d4d4d4' },
{ tag: [t.regexp], color: '#d16969' },
{ tag: [t.special(t.string), t.processingInstruction, t.string, t.inserted], color: '#ce9178' },
{ tag: [t.angleBracket], color: '#808080' },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: [t.meta, t.comment], color: '#6a9955' },
{ tag: t.link, color: '#6a9955', textDecoration: 'underline' },
{ tag: t.invalid, color: '#ff0000' },
],
});

View file

@ -0,0 +1,81 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
background: '#ffffff',
lineBackground: '#ffffff50',
foreground: '#383a42',
caret: '#000',
selection: '#add6ff',
selectionMatch: '#a8ac94',
lineHighlight: '#99999926',
gutterBackground: '#fff',
gutterForeground: '#237893',
gutterActiveForeground: '#0b216f',
fontFamily: 'Menlo, Monaco, Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
};
export default createTheme({
theme: 'light',
settings: {
background: '#ffffff',
foreground: '#383a42',
caret: '#000',
selection: '#add6ff',
selectionMatch: '#a8ac94',
lineHighlight: '#99999926',
gutterBackground: '#fff',
gutterForeground: '#237893',
gutterActiveForeground: '#0b216f',
fontFamily: 'Menlo, Monaco, Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
},
styles: [
{
tag: [
t.keyword,
t.operatorKeyword,
t.modifier,
t.color,
t.constant(t.name),
t.standard(t.name),
t.standard(t.tagName),
t.special(t.brace),
t.atom,
t.bool,
t.special(t.variableName),
],
color: '#0000ff',
},
{ tag: [t.moduleKeyword, t.controlKeyword], color: '#af00db' },
{
tag: [
t.name,
t.deleted,
t.character,
t.macroName,
t.propertyName,
t.variableName,
t.labelName,
t.definition(t.name),
],
color: '#0070c1',
},
{ tag: t.heading, fontWeight: 'bold', color: '#0070c1' },
{
tag: [t.typeName, t.className, t.tagName, t.number, t.changed, t.annotation, t.self, t.namespace],
color: '#267f99',
},
{ tag: [t.function(t.variableName), t.function(t.propertyName)], color: '#795e26' },
{ tag: [t.number], color: '#098658' },
{ tag: [t.operator, t.punctuation, t.separator, t.url, t.escape, t.regexp], color: '#383a42' },
{ tag: [t.regexp], color: '#af00db' },
{ tag: [t.special(t.string), t.processingInstruction, t.string, t.inserted], color: '#a31515' },
{ tag: [t.angleBracket], color: '#383a42' },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: [t.meta, t.comment], color: '#008000' },
{ tag: t.link, color: '#4078f2', textDecoration: 'underline' },
{ tag: t.invalid, color: '#e45649' },
],
});

View file

@ -0,0 +1,39 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
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.labelName, color: 'inherit' },
{ tag: t.keyword, color: 'inherit' },
{ tag: t.operator, color: 'inherit' },
{ tag: t.special(t.variableName), color: 'inherit' },
{ tag: t.typeName, color: 'inherit' },
{ tag: t.atom, color: 'inherit' },
{ tag: t.number, color: 'inherit' },
{ tag: t.definition(t.variableName), color: 'inherit' },
{ tag: t.string, color: 'inherit' },
{ tag: t.special(t.string), color: 'inherit' },
{ tag: t.comment, color: 'inherit' },
{ tag: t.variableName, color: 'inherit' },
{ tag: t.tagName, color: 'inherit' },
{ tag: t.bracket, color: 'inherit' },
{ tag: t.meta, color: 'inherit' },
{ tag: t.attributeName, color: 'inherit' },
{ tag: t.propertyName, color: 'inherit' },
{ tag: t.className, color: 'inherit' },
{ tag: t.invalid, color: 'inherit' },
],
});

View file

@ -0,0 +1,38 @@
import { tags as t } from '@lezer/highlight';
import { createTheme } from './theme-helper.mjs';
export const settings = {
light: true,
background: '#fff',
lineBackground: '#ffffff99',
foreground: '#3D3D3D',
selection: '#BBDFFF',
selectionMatch: '#BBDFFF',
gutterBackground: '#fff',
gutterForeground: '#AFAFAF',
lineHighlight: '#EDF4FF',
};
export default createTheme({
theme: 'light',
settings: {
background: '#fff',
foreground: '#3D3D3D',
selection: '#BBDFFF',
selectionMatch: '#BBDFFF',
gutterBackground: '#fff',
gutterForeground: '#AFAFAF',
lineHighlight: '#d5e6ff69',
},
styles: [
{ tag: [t.comment, t.quote], color: '#707F8D' },
{ tag: [t.typeName, t.typeOperator], color: '#aa0d91' },
{ tag: [t.keyword], color: '#aa0d91', fontWeight: 'bold' },
{ tag: [t.string, t.meta], color: '#D23423' },
{ tag: [t.name], color: '#032f62' },
{ tag: [t.typeName], color: '#522BB2' },
{ tag: [t.variableName], color: '#23575C' },
{ tag: [t.definition(t.variableName)], color: '#327A9E' },
{ tag: [t.regexp, t.link], color: '#0e0eff' },
],
});

View file

@ -0,0 +1,79 @@
import { hoverTooltip } from '@codemirror/view';
import jsdoc from '../../doc.json';
import { Autocomplete, getSynonymDoc } from './autocomplete.mjs';
const getDocLabel = (doc) => doc.name || doc.longname;
let ctrlDown = false;
if (typeof window !== 'undefined') {
// Record Control key event to trigger or block the tooltip depending on the state
window.addEventListener(
'keyup',
function (e) {
if (e.key == 'Control') {
ctrlDown = false;
}
},
true,
);
window.addEventListener(
'keydown',
function (e) {
if (e.key == 'Control') {
ctrlDown = true;
}
},
true,
);
}
export const strudelTooltip = hoverTooltip(
(view, pos, side) => {
// Word selection from CodeMirror Hover Tooltip example https://codemirror.net/examples/tooltip/#hover-tooltips
if (!ctrlDown) {
return null;
}
let { from, to, text } = view.state.doc.lineAt(pos);
let start = pos,
end = pos;
while (start > from && /\w/.test(text[start - from - 1])) {
start--;
}
while (end < to && /\w/.test(text[end - from])) {
end++;
}
if ((start == pos && side < 0) || (end == pos && side > 0)) {
return null;
}
let word = text.slice(start - from, end - from);
// Get entry from Strudel documentation
let entry = jsdoc.docs.filter((doc) => getDocLabel(doc) === word)[0];
if (!entry) {
// Try for synonyms
const doc = jsdoc.docs.filter((doc) => doc.synonyms && doc.synonyms.includes(word))[0];
if (!doc) {
return null;
}
entry = getSynonymDoc(doc, word);
}
return {
pos: start,
end,
above: false,
arrow: true,
create(view) {
let dom = document.createElement('div');
dom.className = 'strudel-tooltip';
const ac = Autocomplete(entry);
dom.appendChild(ac);
return { dom };
},
};
},
{ hoverTime: 10 },
);
export const isTooltipEnabled = (on) => (on ? strudelTooltip : []);

View file

@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});

View file

@ -0,0 +1,142 @@
import { StateEffect, StateField } from '@codemirror/state';
import { Decoration, EditorView, WidgetType } from '@codemirror/view';
import { getWidgetID, registerWidgetType } from '@strudel/transpiler';
import { Pattern } from '@strudel/core';
export const addWidget = StateEffect.define({
map: ({ from, to }, change) => {
return { from: change.mapPos(from), to: change.mapPos(to) };
},
});
export const updateWidgets = (view, widgets) => {
view.dispatch({ effects: addWidget.of(widgets) });
};
function getWidgets(widgetConfigs) {
return (
widgetConfigs
// codemirror throws an error if we don't sort
.sort((a, b) => a.to - b.to)
.map((widgetConfig) => {
return Decoration.widget({
widget: new BlockWidget(widgetConfig),
side: 0,
block: true,
}).range(widgetConfig.to);
})
);
}
const widgetField = StateField.define(
/* <DecorationSet> */ {
create() {
return Decoration.none;
},
update(widgets, tr) {
widgets = widgets.map(tr.changes);
for (let e of tr.effects) {
if (e.is(addWidget)) {
try {
widgets = widgets.update({
filter: () => false,
add: getWidgets(e.value),
});
} catch (error) {
console.log('err', error);
}
}
}
return widgets;
},
provide: (f) => EditorView.decorations.from(f),
},
);
const widgetElements = {};
export function setWidget(id, el) {
widgetElements[id] = el;
el.id = id;
}
export class BlockWidget extends WidgetType {
constructor(widgetConfig) {
super();
this.widgetConfig = widgetConfig;
}
eq() {
return true;
}
toDOM() {
const id = getWidgetID(this.widgetConfig);
const el = widgetElements[id];
return el;
}
ignoreEvent(e) {
return true;
}
}
export const widgetPlugin = [widgetField];
// widget implementer API to create a new widget type
export function registerWidget(type, fn) {
registerWidgetType(type);
if (fn) {
Pattern.prototype[type] = function (id, options = { fold: 1 }) {
// fn is expected to create a dom element and call setWidget(id, el);
// fn should also return the pattern
return fn(id, options, this);
};
}
}
// wire up @strudel/draw functions
function getCanvasWidget(id, options = {}) {
const { width = 500, height = 60, pixelRatio = window.devicePixelRatio } = options;
let canvas = document.getElementById(id) || document.createElement('canvas');
canvas.width = width * pixelRatio;
canvas.height = height * pixelRatio;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
setWidget(id, canvas);
return canvas;
}
registerWidget('_pianoroll', (id, options = {}, pat) => {
const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.tag(id).pianoroll({ fold: 1, ...options, ctx, id });
});
registerWidget('_punchcard', (id, options = {}, pat) => {
const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.tag(id).punchcard({ fold: 1, ...options, ctx, id });
});
registerWidget('_spiral', (id, options = {}, pat) => {
let _size = options.size || 275;
options = { width: _size, height: _size, ...options, size: _size / 5 };
const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.tag(id).spiral({ ...options, ctx, id });
});
registerWidget('_scope', (id, options = {}, pat) => {
options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options };
const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.tag(id).scope({ ...options, ctx, id });
});
registerWidget('_pitchwheel', (id, options = {}, pat) => {
let _size = options.size || 200;
options = { width: _size, height: _size, ...options, size: _size / 5 };
const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.pitchwheel({ ...options, ctx, id });
});
registerWidget('_spectrum', (id, options = {}, pat) => {
let _size = options.size || 200;
options = { width: _size, height: _size, ...options, size: _size / 5 };
const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.spectrum({ ...options, ctx, id });
});