Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more work on vanilla repl: repl web component + package + MicroRepl #866

Merged
merged 35 commits into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d4afbc6
strudel web component
felixroos Dec 15, 2023
f0c3d38
move repl web component to new repl package
felixroos Dec 15, 2023
f6b1e13
fix: lint errors + wrong color
felixroos Dec 15, 2023
46bfd74
repl build config without externals
felixroos Dec 15, 2023
2cec7bc
fix: rollup warning
felixroos Dec 15, 2023
cf44563
bundle as iife instead of cjs
felixroos Dec 15, 2023
9872a89
bump repl to 0.9.1
felixroos Dec 15, 2023
be1e5a7
remove onReady callback
felixroos Dec 15, 2023
034df01
better theme style handling
felixroos Dec 15, 2023
1293270
bump repl to 0.9.2
felixroos Dec 15, 2023
505ad17
fix: process is not defined
felixroos Dec 15, 2023
9fac2c3
band aid fix for process is not defined
felixroos Dec 15, 2023
9b58cf9
bump repl to 0.9.3
felixroos Dec 15, 2023
372e93e
repl now accepts code in innerHTML + bump
felixroos Dec 15, 2023
8e35079
more code in example
felixroos Dec 15, 2023
93e8186
MicroRepl poc
felixroos Dec 15, 2023
b5edcde
cleanup
felixroos Dec 15, 2023
9c13f6b
fix: mini repl play toggle button
felixroos Dec 15, 2023
89c0604
fix: repl package import on server
felixroos Dec 15, 2023
20e8a30
copy recipes page to test new MicroRepl
felixroos Dec 15, 2023
48e06bd
fix: redundant style injections for multiple repls
felixroos Dec 16, 2023
fc03483
use StrudelMirror directly in MicroRepl
felixroos Dec 16, 2023
7fcd9d8
approaching proper draw logic in microrepl
felixroos Dec 16, 2023
c29d032
add autodraw flag
felixroos Dec 16, 2023
53d6ef0
fix: drawTime + canvasHeight
felixroos Dec 16, 2023
40f3212
fix: first frame active state
felixroos Dec 16, 2023
592e54a
delete strudelmirror example
felixroos Dec 17, 2023
1cdb596
settings sync
felixroos Dec 17, 2023
b462f19
fix: boolean settings
felixroos Dec 17, 2023
9519d28
remove web component attribute support
felixroos Dec 17, 2023
e17df79
microrepl: ssr static code
felixroos Dec 17, 2023
ed8f5bf
fix: first frame
felixroos Dec 17, 2023
e8e8f88
fix: stop other repls on start
felixroos Dec 25, 2023
0ed615a
fix: add .piano function
felixroos Dec 25, 2023
de4460a
remove redundant test pages
felixroos Dec 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 63 additions & 11 deletions packages/codemirror/codemirror.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core';
import { flash, isFlashEnabled } from './flash.mjs';
import { highlightMiniLocations, isPatternHighlightingEnabled, updateMiniLocations } from './highlight.mjs';
import { keybindings } from './keybindings.mjs';
import { theme } from './themes.mjs';
import { initTheme, activateTheme, theme } from './themes.mjs';
import { updateWidgets, sliderPlugin } from './slider.mjs';
import { persistentAtom } from '@nanostores/persistent';

const extensions = {
isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []),
Expand All @@ -25,11 +26,32 @@ const extensions = {
};
const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()]));

export const defaultSettings = {
keybindings: 'codemirror',
isLineNumbersDisplayed: true,
isActiveLineHighlighted: false,
isAutoCompletionEnabled: false,
isPatternHighlightingEnabled: true,
isFlashEnabled: true,
isTooltipEnabled: false,
isLineWrappingEnabled: 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, settings, root }) {
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root }) {
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: [
Expand Down Expand Up @@ -86,36 +108,44 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, set

export class StrudelMirror {
constructor(options) {
const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, settings, ...replOptions } = options;
const { root, id, initialCode = '', onDraw, drawTime = [-2, 2], autodraw, prebake, ...replOptions } = options;
this.code = initialCode;
this.root = root;
this.miniLocations = [];
this.widgets = [];
this.painters = [];
this.onDraw = onDraw;
const self = this;
this.id = id || s4();

this.drawer = new Drawer((haps, time) => {
const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.endClipped);
this.highlight(currentFrame, time);
this.onDraw?.(haps, time, currentFrame, this.painters);
}, drawTime);

// this approach might not work with multiple repls on screen..
// this approach does not work with multiple repls on screen
// TODO: refactor onPaint usages + find fix, maybe remove painters here?
Pattern.prototype.onPaint = function (onPaint) {
self.painters.push(onPaint);
return this;
};

this.prebaked = prebake();
// this.drawFirstFrame();
autodraw && this.drawFirstFrame();

this.repl = repl({
...replOptions,
onToggle: (started) => {
replOptions?.onToggle?.(started);
if (started) {
this.drawer.start(this.repl.scheduler);
// stop other repls when this one is started
document.dispatchEvent(
new CustomEvent('start-repl', {
detail: this.id,
}),
);
} else {
this.drawer.stop();
updateMiniLocations(this.editor, []);
Expand All @@ -140,23 +170,29 @@ export class StrudelMirror {
});
this.editor = initEditor({
root,
settings,
initialCode,
onChange: (v) => {
if (v.docChanged) {
this.code = v.state.doc.toString();
// TODO: repl is still untouched to make sure the old Repl.jsx stays untouched..
// this.repl.setCode(this.code);
this.repl.setCode?.(this.code);
}
},
onEvaluate: () => this.evaluate(),
onStop: () => this.stop(),
});
const cmEditor = this.root.querySelector('.cm-editor');
if (cmEditor) {
this.root.style.display = 'block';
this.root.style.backgroundColor = 'var(--background)';
cmEditor.style.backgroundColor = 'transparent';
}
// stop this repl when another repl is started
this.onStartRepl = (e) => {
if (e.detail !== this.id) {
this.stop();
}
};
document.addEventListener('start-repl', this.onStartRepl);
}
async drawFirstFrame() {
if (!this.onDraw) {
Expand All @@ -166,8 +202,9 @@ export class StrudelMirror {
await this.prebaked;
try {
await this.repl.evaluate(this.code, false);
this.drawer.invalidate(this.repl.scheduler);
this.onDraw?.(this.drawer.visibleHaps, 0, []);
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.painters);
} catch (err) {
console.warn('first frame could not be painted');
}
Expand All @@ -181,7 +218,7 @@ export class StrudelMirror {
}
async toggle() {
if (this.repl.scheduler.started) {
this.repl.scheduler.stop();
this.repl.stop();
} else {
this.evaluate();
}
Expand Down Expand Up @@ -212,6 +249,9 @@ export class StrudelMirror {
this.editor.dispatch({
effects: compartments[key].reconfigure(newValue),
});
if (key === 'theme') {
activateTheme(value);
}
}
setLineWrappingEnabled(enabled) {
this.reconfigureExtension('isLineWrappingEnabled', enabled);
Expand All @@ -231,6 +271,8 @@ export class StrudelMirror {
for (let key in extensions) {
this.reconfigureExtension(key, settings[key]);
}
const updated = { ...codemirrorSettings.get(), ...settings };
codemirrorSettings.set(updated);
}
changeSetting(key, value) {
if (extensions[key]) {
Expand All @@ -246,8 +288,18 @@ export class StrudelMirror {
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);
}
}

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);
}
24 changes: 0 additions & 24 deletions packages/codemirror/examples/strudelmirror/.gitignore

This file was deleted.

87 changes: 0 additions & 87 deletions packages/codemirror/examples/strudelmirror/index.html

This file was deleted.

Loading