proper draw cleanup

This commit is contained in:
Felix Roos 2022-11-13 13:58:36 +01:00
parent aa76419eda
commit 8f31fbe275
5 changed files with 157 additions and 154 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,15 +1,15 @@
import n, { useCallback as C, useRef as P, useEffect as z, useState as E, useMemo as q, useLayoutEffect as I } from "react"; import n, { useCallback as N, useRef as x, useEffect as R, useState as y, useMemo as q, useLayoutEffect as I } from "react";
import G from "@uiw/react-codemirror"; import J from "@uiw/react-codemirror";
import { Decoration as b, EditorView as K } from "@codemirror/view"; import { Decoration as E, EditorView as K } from "@codemirror/view";
import { StateEffect as O, StateField as j } from "@codemirror/state"; import { StateEffect as O, StateField as j } from "@codemirror/state";
import { javascript as J } from "@codemirror/lang-javascript"; import { javascript as Q } from "@codemirror/lang-javascript";
import { tags as r } from "@lezer/highlight"; import { tags as r } from "@lezer/highlight";
import { createTheme as Q } from "@uiw/codemirror-themes"; import { createTheme as W } from "@uiw/codemirror-themes";
import { useInView as W } from "react-hook-inview"; import { useInView as X } from "react-hook-inview";
import { webaudioOutput as X, getAudioContext as Y } from "@strudel.cycles/webaudio"; import { webaudioOutput as Y, getAudioContext as Z } from "@strudel.cycles/webaudio";
import { repl as Z } from "@strudel.cycles/core"; import { repl as ee } from "@strudel.cycles/core";
import { transpiler as ee } from "@strudel.cycles/transpiler"; import { transpiler as te } from "@strudel.cycles/transpiler";
const te = Q({ const re = W({
theme: "dark", theme: "dark",
settings: { settings: {
background: "#222", background: "#222",
@ -42,113 +42,113 @@ const te = Q({
{ tag: r.invalid, color: "#ffffff" } { tag: r.invalid, color: "#ffffff" }
] ]
}); });
const L = O.define(), re = j.define({ const L = O.define(), oe = j.define({
create() { create() {
return b.none; return E.none;
}, },
update(e, t) { update(e, t) {
try { try {
for (let o of t.effects) for (let o of t.effects)
if (o.is(L)) if (o.is(L))
if (o.value) { if (o.value) {
const s = b.mark({ attributes: { style: "background-color: #FFCA2880" } }); const a = E.mark({ attributes: { style: "background-color: #FFCA2880" } });
e = b.set([s.range(0, t.newDoc.length)]); e = E.set([a.range(0, t.newDoc.length)]);
} else } else
e = b.set([]); e = E.set([]);
return e; return e;
} catch (o) { } catch (o) {
return console.warn("flash error", o), e; return console.warn("flash error", o), e;
} }
}, },
provide: (e) => K.decorations.from(e) provide: (e) => K.decorations.from(e)
}), oe = (e) => { }), ne = (e) => {
e.dispatch({ effects: L.of(!0) }), setTimeout(() => { e.dispatch({ effects: L.of(!0) }), setTimeout(() => {
e.dispatch({ effects: L.of(!1) }); e.dispatch({ effects: L.of(!1) });
}, 200); }, 200);
}, R = O.define(), ne = j.define({ }, A = O.define(), ae = j.define({
create() { create() {
return b.none; return E.none;
}, },
update(e, t) { update(e, t) {
try { try {
for (let o of t.effects) for (let o of t.effects)
if (o.is(R)) { if (o.is(A)) {
const s = o.value.map( const a = o.value.map(
(c) => (c.context.locations || []).map(({ start: f, end: d }) => { (s) => (s.context.locations || []).map(({ start: u, end: l }) => {
const u = c.context.color || "#FFCA28"; const d = s.context.color || "#FFCA28";
let a = t.newDoc.line(f.line).from + f.column, i = t.newDoc.line(d.line).from + d.column; let c = t.newDoc.line(u.line).from + u.column, i = t.newDoc.line(l.line).from + l.column;
const m = t.newDoc.length; const f = t.newDoc.length;
return a > m || i > m ? void 0 : b.mark({ attributes: { style: `outline: 1.5px solid ${u};` } }).range(a, i); return c > f || i > f ? void 0 : E.mark({ attributes: { style: `outline: 1.5px solid ${d};` } }).range(c, i);
}) })
).flat().filter(Boolean) || []; ).flat().filter(Boolean) || [];
e = b.set(s, !0); e = E.set(a, !0);
} }
return e; return e;
} catch { } catch {
return b.set([]); return E.set([]);
} }
}, },
provide: (e) => K.decorations.from(e) provide: (e) => K.decorations.from(e)
}), ae = [J(), te, ne, re]; }), se = [Q(), re, ae, oe];
function se({ value: e, onChange: t, onViewChanged: o, onSelectionChange: s, options: c, editorDidMount: f }) { function ce({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, options: s, editorDidMount: u }) {
const d = C( const l = N(
(i) => { (i) => {
t?.(i); t?.(i);
}, },
[t] [t]
), u = C( ), d = N(
(i) => { (i) => {
o?.(i); o?.(i);
}, },
[o] [o]
), a = C( ), c = N(
(i) => { (i) => {
i.selectionSet && s && s?.(i.state.selection); i.selectionSet && a && a?.(i.state.selection);
}, },
[s] [a]
); );
return /* @__PURE__ */ n.createElement(n.Fragment, null, /* @__PURE__ */ n.createElement(G, { return /* @__PURE__ */ n.createElement(n.Fragment, null, /* @__PURE__ */ n.createElement(J, {
value: e, value: e,
onChange: d, onChange: l,
onCreateEditor: u, onCreateEditor: d,
onUpdate: a, onUpdate: c,
extensions: ae extensions: se
})); }));
} }
function S(...e) { function S(...e) {
return e.filter(Boolean).join(" "); return e.filter(Boolean).join(" ");
} }
function ce({ view: e, pattern: t, active: o, getTime: s }) { function ie({ view: e, pattern: t, active: o, getTime: a }) {
const c = P([]), f = P(); const s = x([]), u = x();
z(() => { R(() => {
if (e) if (e)
if (t && o) { if (t && o) {
let u = function() { let d = function() {
try { try {
const a = s(), m = [Math.max(f.current || a, a - 1 / 10, 0), a + 1 / 60]; const c = a(), f = [Math.max(u.current || c, c - 1 / 10, 0), c + 1 / 60];
f.current = m[1], c.current = c.current.filter((l) => l.whole.end > a); u.current = f[1], s.current = s.current.filter((m) => m.whole.end > c);
const p = t.queryArc(...m).filter((l) => l.hasOnset()); const v = t.queryArc(...f).filter((m) => m.hasOnset());
c.current = c.current.concat(p), e.dispatch({ effects: R.of(c.current) }); s.current = s.current.concat(v), e.dispatch({ effects: A.of(s.current) });
} catch { } catch {
e.dispatch({ effects: R.of([]) }); e.dispatch({ effects: A.of([]) });
} }
d = requestAnimationFrame(u); l = requestAnimationFrame(d);
}, d = requestAnimationFrame(u); }, l = requestAnimationFrame(d);
return () => { return () => {
cancelAnimationFrame(d); cancelAnimationFrame(l);
}; };
} else } else
c.current = [], e.dispatch({ effects: R.of([]) }); s.current = [], e.dispatch({ effects: A.of([]) });
}, [t, o, e]); }, [t, o, e]);
} }
const ie = "_container_3i85k_1", le = "_header_3i85k_5", de = "_buttons_3i85k_9", ue = "_button_3i85k_9", fe = "_buttonDisabled_3i85k_17", me = "_error_3i85k_21", ge = "_body_3i85k_25", v = { const le = "_container_3i85k_1", de = "_header_3i85k_5", ue = "_buttons_3i85k_9", fe = "_button_3i85k_9", me = "_buttonDisabled_3i85k_17", ge = "_error_3i85k_21", pe = "_body_3i85k_25", b = {
container: ie, container: le,
header: le, header: de,
buttons: de, buttons: ue,
button: ue, button: fe,
buttonDisabled: fe, buttonDisabled: me,
error: me, error: ge,
body: ge body: pe
}; };
function B({ type: e }) { function B({ type: e }) {
return /* @__PURE__ */ n.createElement("svg", { return /* @__PURE__ */ n.createElement("svg", {
@ -174,133 +174,134 @@ function B({ type: e }) {
}) })
}[e]); }[e]);
} }
function pe({ function he({
defaultOutput: e, defaultOutput: e,
interval: t, interval: t,
getTime: o, getTime: o,
evalOnMount: s = !1, evalOnMount: a = !1,
initialCode: c = "", initialCode: s = "",
autolink: f = !1, autolink: u = !1,
beforeEval: l,
afterEval: d, afterEval: d,
onEvalError: u onEvalError: c
}) { }) {
const [a, i] = E(), [m, p] = E(), [l, N] = E(c), [D, F] = E(l), [k, H] = E(), [M, _] = E(!1), A = l !== D, { scheduler: w, evaluate: y, start: h, stop: U, pause: $ } = q( const [i, f] = y(), [v, m] = y(), [h, D] = y(s), [_, C] = y(h), [P, z] = y(), [k, F] = y(!1), H = h !== _, { scheduler: w, evaluate: g, start: U, stop: $, pause: G } = q(
() => Z({ () => ee({
interval: t, interval: t,
defaultOutput: e, defaultOutput: e,
onSchedulerError: i, onSchedulerError: f,
onEvalError: (g) => { onEvalError: (p) => {
p(g), u?.(g); m(p), c?.(p);
}, },
getTime: o, getTime: o,
transpiler: ee, transpiler: te,
beforeEval: ({ code: g }) => { beforeEval: ({ code: p }) => {
N(g); D(p), l?.();
}, },
afterEval: ({ pattern: g, code: V }) => { afterEval: ({ pattern: p, code: V }) => {
F(V), H(g), p(), i(), f && (window.location.hash = "#" + encodeURIComponent(btoa(V))), d?.(); C(V), z(p), m(), f(), u && (window.location.hash = "#" + encodeURIComponent(btoa(V))), d?.();
}, },
onToggle: (g) => _(g) onToggle: (p) => F(p)
}), }),
[e, t, o] [e, t, o]
), x = C(async (g = !0) => y(l, g), [y, l]), T = P(); ), M = N(async (p = !0) => g(h, p), [g, h]), T = x();
return z(() => { return R(() => {
!T.current && s && l && (T.current = !0, x()); !T.current && a && h && (T.current = !0, M());
}, [x, s, l]), z(() => () => { }, [M, a, h]), R(() => () => {
w.stop(); w.stop();
}, [w]), { }, [w]), {
code: l, code: h,
setCode: N, setCode: D,
error: a || m, error: i || v,
schedulerError: a, schedulerError: i,
scheduler: w, scheduler: w,
evalError: m, evalError: v,
evaluate: y, evaluate: g,
activateCode: x, activateCode: M,
activeCode: D, activeCode: _,
isDirty: A, isDirty: H,
pattern: k, pattern: P,
started: M, started: k,
start: h, start: U,
stop: U, stop: $,
pause: $, pause: G,
togglePlay: async () => { togglePlay: async () => {
M ? w.pause() : await x(); k ? w.pause() : await M();
} }
}; };
} }
const he = () => Y().currentTime; const ve = () => Z().currentTime;
function xe({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: s }) { function Re({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) {
const { const {
code: c, code: s,
setCode: f, setCode: u,
evaluate: d, evaluate: l,
activateCode: u, activateCode: d,
error: a, error: c,
isDirty: i, isDirty: i,
activeCode: m, activeCode: f,
pattern: p, pattern: v,
started: l, started: m,
scheduler: N, scheduler: h,
togglePlay: D, togglePlay: D,
stop: F stop: _
} = pe({ } = he({
initialCode: e, initialCode: e,
defaultOutput: X, defaultOutput: Y,
getTime: he getTime: ve
}), [k, H] = E(), [M, _] = W({ }), [C, P] = y(), [z, k] = X({
threshold: 0.01 threshold: 0.01
}), A = P(), w = q(() => ((_ || !t) && (A.current = !0), _ || A.current), [_, t]); }), F = x(), H = q(() => ((k || !t) && (F.current = !0), k || F.current), [k, t]);
return ce({ return ie({
view: k, view: C,
pattern: p, pattern: v,
active: l && !m?.includes("strudel disable-highlighting"), active: m && !f?.includes("strudel disable-highlighting"),
getTime: () => N.getPhase() getTime: () => h.getPhase()
}), I(() => { }), I(() => {
if (s) { if (a) {
const y = async (h) => { const w = async (g) => {
(h.ctrlKey || h.altKey) && (h.code === "Enter" ? (h.preventDefault(), oe(k), await u()) : h.code === "Period" && (F(), h.preventDefault())); (g.ctrlKey || g.altKey) && (g.code === "Enter" ? (g.preventDefault(), ne(C), await d()) : g.code === "Period" && (_(), g.preventDefault()));
}; };
return window.addEventListener("keydown", y, !0), () => window.removeEventListener("keydown", y, !0); return window.addEventListener("keydown", w, !0), () => window.removeEventListener("keydown", w, !0);
} }
}, [s, p, c, d, F, k]), /* @__PURE__ */ n.createElement("div", { }, [a, v, s, l, _, C]), /* @__PURE__ */ n.createElement("div", {
className: v.container, className: b.container,
ref: M ref: z
}, /* @__PURE__ */ n.createElement("div", { }, /* @__PURE__ */ n.createElement("div", {
className: v.header className: b.header
}, /* @__PURE__ */ n.createElement("div", { }, /* @__PURE__ */ n.createElement("div", {
className: v.buttons className: b.buttons
}, /* @__PURE__ */ n.createElement("button", { }, /* @__PURE__ */ n.createElement("button", {
className: S(v.button, l ? "sc-animate-pulse" : ""), className: S(b.button, m ? "sc-animate-pulse" : ""),
onClick: () => D() onClick: () => D()
}, /* @__PURE__ */ n.createElement(B, { }, /* @__PURE__ */ n.createElement(B, {
type: l ? "pause" : "play" type: m ? "pause" : "play"
})), /* @__PURE__ */ n.createElement("button", { })), /* @__PURE__ */ n.createElement("button", {
className: S(i ? v.button : v.buttonDisabled), className: S(i ? b.button : b.buttonDisabled),
onClick: () => u() onClick: () => d()
}, /* @__PURE__ */ n.createElement(B, { }, /* @__PURE__ */ n.createElement(B, {
type: "refresh" type: "refresh"
}))), a && /* @__PURE__ */ n.createElement("div", { }))), c && /* @__PURE__ */ n.createElement("div", {
className: v.error className: b.error
}, a.message)), /* @__PURE__ */ n.createElement("div", { }, c.message)), /* @__PURE__ */ n.createElement("div", {
className: v.body className: b.body
}, w && /* @__PURE__ */ n.createElement(se, { }, H && /* @__PURE__ */ n.createElement(ce, {
value: c, value: s,
onChange: f, onChange: u,
onViewChanged: H onViewChanged: P
}))); })));
} }
function Re(e) { function Pe(e) {
return z(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), C((t) => window.postMessage(t, "*"), []); return R(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((t) => window.postMessage(t, "*"), []);
} }
const Pe = (e) => I(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]); const ze = (e) => I(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
export { export {
se as CodeMirror, ce as CodeMirror,
xe as MiniRepl, Re as MiniRepl,
S as cx, S as cx,
oe as flash, ne as flash,
ce as useHighlighting, ie as useHighlighting,
Pe as useKeydown, ze as useKeydown,
Re as usePostMessage, Pe as usePostMessage,
pe as useStrudel he as useStrudel
}; };

View file

@ -9,6 +9,7 @@ function useStrudel({
evalOnMount = false, evalOnMount = false,
initialCode = '', initialCode = '',
autolink = false, autolink = false,
beforeEval,
afterEval, afterEval,
onEvalError, onEvalError,
}) { }) {
@ -36,6 +37,7 @@ function useStrudel({
transpiler, transpiler,
beforeEval: ({ code }) => { beforeEval: ({ code }) => {
setCode(code); setCode(code);
beforeEval?.();
}, },
afterEval: ({ pattern: _pattern, code }) => { afterEval: ({ pattern: _pattern, code }) => {
setActiveCode(code); setActiveCode(code);

View file

@ -114,6 +114,10 @@ function App() {
defaultOutput: webaudioOutput, defaultOutput: webaudioOutput,
getTime, getTime,
autolink: true, autolink: true,
beforeEval: () => {
cleanupUi();
cleanupDraw();
},
}); });
// init code // init code
@ -175,7 +179,6 @@ function App() {
} else { } else {
logger('[repl] stopped. tip: you can also stop by pressing ctrl+dot', 'highlight'); logger('[repl] stopped. tip: you can also stop by pressing ctrl+dot', 'highlight');
stop(); stop();
// cleanupDraw();
} }
}; };
const handleUpdate = () => { const handleUpdate = () => {
@ -186,9 +189,6 @@ function App() {
const handleShuffle = async () => { const handleShuffle = async () => {
const { code, name } = getRandomTune(); const { code, name } = getRandomTune();
logger(`[repl] ✨ loading random tune "${name}"`); logger(`[repl] ✨ loading random tune "${name}"`);
cleanupDraw();
cleanupUi();
resetLoadedSamples(); resetLoadedSamples();
await prebake(); // declare default samples await prebake(); // declare default samples
await evaluate(code, false); await evaluate(code, false);