From d35bf9591c5b5f8021c8f5505db9701ebba4f81e Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 5 May 2023 11:54:09 +0200 Subject: [PATCH] vanilla-repl-cm6: better theme --- .../vite-vanilla-repl-cm6/codemirror.js | 3 +- .../examples/vite-vanilla-repl-cm6/main.js | 13 +- .../vite-vanilla-repl-cm6/one-dark.js | 139 ++++++++++++++++++ .../vite-vanilla-repl-cm6/package.json | 5 +- .../examples/vite-vanilla-repl-cm6/style.css | 2 + .../examples/vite-vanilla-repl-cm6/tunes.mjs | 80 ++++++++++ pnpm-lock.yaml | 3 + 7 files changed, 237 insertions(+), 8 deletions(-) create mode 100644 packages/core/examples/vite-vanilla-repl-cm6/one-dark.js diff --git a/packages/core/examples/vite-vanilla-repl-cm6/codemirror.js b/packages/core/examples/vite-vanilla-repl-cm6/codemirror.js index a79aaf4f..103edd92 100644 --- a/packages/core/examples/vite-vanilla-repl-cm6/codemirror.js +++ b/packages/core/examples/vite-vanilla-repl-cm6/codemirror.js @@ -4,13 +4,14 @@ import { defaultKeymap } from '@codemirror/commands'; import { syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language'; import { javascript } from '@codemirror/lang-javascript'; import { StateField, StateEffect } from '@codemirror/state'; -import './style.css'; +import { oneDark } from './one-dark'; // https://codemirror.net/docs/guide/ export function initEditor({ initialCode, onChange, onEvaluate, onStop }) { let state = EditorState.create({ doc: initialCode, extensions: [ + oneDark, javascript(), lineNumbers(), highlightField, diff --git a/packages/core/examples/vite-vanilla-repl-cm6/main.js b/packages/core/examples/vite-vanilla-repl-cm6/main.js index 6e0ed677..a73ccff3 100644 --- a/packages/core/examples/vite-vanilla-repl-cm6/main.js +++ b/packages/core/examples/vite-vanilla-repl-cm6/main.js @@ -3,12 +3,15 @@ import { initEditor, highlightHaps, flash } from './codemirror'; import { initStrudel } from './strudel'; import { Drawer } from './drawer'; -import { bumpStreet } from './tunes'; +import { bumpStreet, trafficFlam, funk42 } from './tunes'; import { pianoroll, getDrawOptions } from '@strudel.cycles/core'; +import './style.css'; -let code = bumpStreet; +let code = funk42; const repl = initStrudel(); -const roll = document.getElementById('roll'); +const canvas = document.getElementById('roll'); +canvas.width = canvas.width * 2; +canvas.height = canvas.height * 2; const view = initEditor({ initialCode: code, @@ -37,12 +40,12 @@ async function onStop() { scheduler.stop(); drawer.stop(); } -const ctx = roll.getContext('2d'); +const ctx = canvas.getContext('2d'); let drawer = new Drawer( (haps, time, { drawTime }) => { const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.whole.end); highlightHaps(view, currentFrame); - pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { fold: 1 }) }); + pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { fold: 0 }) }); }, [-2, 2], ); diff --git a/packages/core/examples/vite-vanilla-repl-cm6/one-dark.js b/packages/core/examples/vite-vanilla-repl-cm6/one-dark.js new file mode 100644 index 00000000..cce83699 --- /dev/null +++ b/packages/core/examples/vite-vanilla-repl-cm6/one-dark.js @@ -0,0 +1,139 @@ +import { EditorView } from '@codemirror/view'; +import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'; +import { tags as t } from '@lezer/highlight'; + +// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors + +const chalky = '#e5c07b', + coral = '#e06c75', + cyan = '#56b6c2', + invalid = '#ffffff', + ivory = '#abb2bf', + stone = '#7d8799', // Brightened compared to original to increase contrast + malibu = '#61afef', + sage = '#98c379', + whiskey = '#d19a66', + violet = '#c678dd', + darkBackground = '#21252b', + highlightBackground = '#2c313a', + background = '#282c34', + tooltipBackground = '#353a42', + selection = '#3E4451', + cursor = '#528bff'; + +/// The colors used in the theme, as CSS color strings. +export const color = { + chalky, + coral, + cyan, + invalid, + ivory, + stone, + malibu, + sage, + whiskey, + violet, + darkBackground, + highlightBackground, + background, + tooltipBackground, + selection, + cursor, +}; + +/// The editor theme styles for One Dark. +export const oneDarkTheme = EditorView.theme( + { + '&': { + color: ivory, + backgroundColor: background, + }, + + '.cm-content': { + caretColor: cursor, + }, + + '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor }, + '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': + { backgroundColor: selection }, + + '.cm-panels': { backgroundColor: darkBackground, color: ivory }, + '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' }, + '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' }, + + '.cm-searchMatch': { + backgroundColor: '#72a1ff59', + outline: '1px solid #457dff', + }, + '.cm-searchMatch.cm-searchMatch-selected': { + backgroundColor: '#6199ff2f', + }, + + '.cm-activeLine': { backgroundColor: '#6699ff0b' }, + '.cm-selectionMatch': { backgroundColor: '#aafe661a' }, + + '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': { + backgroundColor: '#bad0f847', + }, + + '.cm-gutters': { + backgroundColor: background, + color: stone, + border: 'none', + }, + + '.cm-activeLineGutter': { + backgroundColor: highlightBackground, + }, + + '.cm-foldPlaceholder': { + backgroundColor: 'transparent', + border: 'none', + color: '#ddd', + }, + + '.cm-tooltip': { + border: 'none', + backgroundColor: tooltipBackground, + }, + '.cm-tooltip .cm-tooltip-arrow:before': { + borderTopColor: 'transparent', + borderBottomColor: 'transparent', + }, + '.cm-tooltip .cm-tooltip-arrow:after': { + borderTopColor: tooltipBackground, + borderBottomColor: tooltipBackground, + }, + '.cm-tooltip-autocomplete': { + '& > ul > li[aria-selected]': { + backgroundColor: highlightBackground, + color: ivory, + }, + }, + }, + { dark: true }, +); + +/// The highlighting style for code in the One Dark theme. +export const oneDarkHighlightStyle = HighlightStyle.define([ + { tag: t.keyword, color: violet }, + { tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: coral }, + { tag: [t.function(t.variableName), t.labelName], color: malibu }, + { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey }, + { tag: [t.definition(t.name), t.separator], color: ivory }, + { tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: chalky }, + { tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], color: cyan }, + { tag: [t.meta, t.comment], color: stone }, + { tag: t.strong, fontWeight: 'bold' }, + { tag: t.emphasis, fontStyle: 'italic' }, + { tag: t.strikethrough, textDecoration: 'line-through' }, + { tag: t.link, color: stone, textDecoration: 'underline' }, + { tag: t.heading, fontWeight: 'bold', color: coral }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey }, + { tag: [t.processingInstruction, t.string, t.inserted], color: sage }, + { tag: t.invalid, color: invalid }, +]); + +/// Extension to enable the One Dark theme (both the editor theme and +/// the highlight style). +export const oneDark = [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)]; diff --git a/packages/core/examples/vite-vanilla-repl-cm6/package.json b/packages/core/examples/vite-vanilla-repl-cm6/package.json index f84b23ed..0251bf96 100644 --- a/packages/core/examples/vite-vanilla-repl-cm6/package.json +++ b/packages/core/examples/vite-vanilla-repl-cm6/package.json @@ -17,11 +17,12 @@ "@codemirror/language": "^6.6.0", "@codemirror/state": "^6.2.0", "@codemirror/view": "^6.10.0", + "@lezer/highlight": "^1.1.4", "@strudel.cycles/core": "workspace:*", "@strudel.cycles/mini": "workspace:*", + "@strudel.cycles/soundfonts": "workspace:*", "@strudel.cycles/tonal": "workspace:*", "@strudel.cycles/transpiler": "workspace:*", - "@strudel.cycles/webaudio": "workspace:*", - "@strudel.cycles/soundfonts": "workspace:*" + "@strudel.cycles/webaudio": "workspace:*" } } diff --git a/packages/core/examples/vite-vanilla-repl-cm6/style.css b/packages/core/examples/vite-vanilla-repl-cm6/style.css index 21f011a0..5a95388a 100644 --- a/packages/core/examples/vite-vanilla-repl-cm6/style.css +++ b/packages/core/examples/vite-vanilla-repl-cm6/style.css @@ -2,6 +2,7 @@ body, html { margin: 0; height: 100%; + background: #282c34; } main { @@ -14,6 +15,7 @@ main { flex-grow: 1; max-height: 100%; position: relative; + overflow:auto; } #editor { diff --git a/packages/core/examples/vite-vanilla-repl-cm6/tunes.mjs b/packages/core/examples/vite-vanilla-repl-cm6/tunes.mjs index 4b0155e4..b235aa95 100644 --- a/packages/core/examples/vite-vanilla-repl-cm6/tunes.mjs +++ b/packages/core/examples/vite-vanilla-repl-cm6/tunes.mjs @@ -30,3 +30,83 @@ await samples('https://strudel.tidalcycles.org/tidal-drum-machines.json', 'githu // wait for it... ).fast(2/3) //.crush(6) // remove "//" if you dare`; + +export const trafficFlam = `// froos - "traffic flam", licensed with CC BY-NC-SA 4.0 + +await samples('github:felixroos/samples/main') +await samples('https://strudel.tidalcycles.org/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/') + +addVoicings('hip', { + m11: ['2M 3m 4P 7m'], + '^7#11': ['3M 4A 5P 7M'], +}, ['C4', 'C6']) + +stack( + stack( + "/2".voicings('hip').note() + .s("gm_epiano1:2") + .arp("[<[0 1 2 3] [3 2 1 0]> ~@5]/2") + .release(2).late(.25).lpf(2000), + "/2".note().s('gm_acoustic_bass'), + n("<0 2 3>(3,8)".off(1/8, add(4))) + .scale("/2") + .s('gm_electric_guitar_jazz') + .decay(sine.range(.05, .2).slow(32)).sustain(0) + .delay(.5).lpf(sine.range(100,5000).slow(64)) + .gain(.7).room(.5).pan(sine.range(0,1).slow(11)) + ).add(perlin.range(0,.25).note()), + stack( + s("bd:1(3,8) rim").bank('RolandTR707').slow(2).room("<0 <.1 .6>>") + .when("<0@7 1>",x=>x.echoWith(3, .0625, (x,i) => x.speed(1+i*.24))), + s("rim*4").end(.05).bank('RolandTR808').speed(.8).room(.2) + ) +) + .late("[0 .05]*2").late(12) + +`; + +export const funk42 = `// froos - how to funk in 42 lines of code +// adapted from "how to funk in two minutes" by marc rebillet https://www.youtube.com/watch?v=3vBwRfQbXkg +// thanks to peach for the transcription: https://www.youtube.com/watch?v=8eiPXvIgda4 + +await samples('github:felixroos/samples/main') +await samples('https://strudel.tidalcycles.org/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/') + +setcps(.5) + +let drums = stack( + s("bd*2, ~ sd").bank('RolandTR707').room("0 .1"), + s("hh*4").begin(.2).release(.02).end(.25).release(.02) + .gain(.3).bank('RolandTR707').late(.02).room(.5), + s("shaker_small").struct("[x x*2]*2").speed(".8,.9").release(.02) +).fast(2) + +let wurli = note(\`< +[[a2,g3,[b3 c4],e4] ~ [g3,c4,e4](3,8)@4 ~@2]!3 +[[e2,e3,a3,b3,e4]@3 [e2,e3,ab3,b3,e4]@5]>\`) + .s("gm_epiano1:5").decay(.2).sustain("<[1 0@7]!3 1>") + .gain("<[.8@2 .4@14]!3 .7>").room(.3) + +let organ = note("<[~@3 [a3,d4,f#4]@2 [[a3,c4,e4]@2 ~] ~@2]!3 ~>".add(12)) + .s("gm_percussive_organ:2").gain(.6).lpf(1800).pan(.2).room(.3); + +let clav = note(\`< +[~@3 a2 [g3,[b3 c4],e4]@2 ~ a2 [g3,b3,e4] ~@2 [g3,c4,e4] ~@4]!3 +[~@3 e3 [[a3 b3],c3,e3]@2 ~ e2 [e3,a3]@3 [b3,e3] ~@2 [b3,e3]@2]>\`) + .s("gm_clavinet:1").decay("<.25!3 [.25 .4]>").sustain(0) + .gain(.7).pan(.8).room(.2); + +let bass = note(\`< +[a1 [~ [g2 a2]] [g1 g#1] [a1 [g2 a2]]] +[a1 [~ [g2 a2]] [e3 d3] [c3 [g3 a3]]] +[a1 [~ [g2 a2]] [g1 g#1] [a1 [g2 a2]]] +[e2@6 e1@5 e1 [[d2 e3] g1]@4] +>\`).s("gm_electric_bass_pick:1").release(.1) + +stack( + drums + ,wurli + ,organ + ,clav + ,bass +)`; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40a6442f..a2e65f7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,6 +118,9 @@ importers: '@codemirror/view': specifier: ^6.10.0 version: 6.10.0 + '@lezer/highlight': + specifier: ^1.1.4 + version: 1.1.4 '@strudel.cycles/core': specifier: workspace:* version: link:../..