mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 13:48:34 +00:00
+ pass hap to logger + add editPattern hook to repl + useStrudel + do not throw when webaudio gets plain values
402 lines
12 KiB
JavaScript
402 lines
12 KiB
JavaScript
import l, { useCallback as N, useRef as k, useEffect as _, useMemo as K, useState as w, useLayoutEffect as G } from "react";
|
|
import Z from "@uiw/react-codemirror";
|
|
import { Decoration as y, EditorView as J } from "@codemirror/view";
|
|
import { StateEffect as Q, StateField as X } from "@codemirror/state";
|
|
import { javascript as ee } from "@codemirror/lang-javascript";
|
|
import { tags as s } from "@lezer/highlight";
|
|
import { createTheme as te } from "@uiw/codemirror-themes";
|
|
import { repl as re, logger as ne, pianoroll as oe } from "@strudel.cycles/core";
|
|
import { webaudioOutput as ae, getAudioContext as ce } from "@strudel.cycles/webaudio";
|
|
import { useInView as se } from "react-hook-inview";
|
|
import { transpiler as ie } from "@strudel.cycles/transpiler";
|
|
const le = te({
|
|
theme: "dark",
|
|
settings: {
|
|
background: "#222",
|
|
foreground: "#75baff",
|
|
caret: "#ffcc00",
|
|
selection: "rgba(128, 203, 196, 0.5)",
|
|
selectionMatch: "#036dd626",
|
|
lineHighlight: "#00000050",
|
|
gutterBackground: "transparent",
|
|
gutterForeground: "#8a919966"
|
|
},
|
|
styles: [
|
|
{ tag: s.keyword, color: "#c792ea" },
|
|
{ tag: s.operator, color: "#89ddff" },
|
|
{ tag: s.special(s.variableName), color: "#eeffff" },
|
|
{ tag: s.typeName, color: "#c3e88d" },
|
|
{ tag: s.atom, color: "#f78c6c" },
|
|
{ tag: s.number, color: "#c3e88d" },
|
|
{ tag: s.definition(s.variableName), color: "#82aaff" },
|
|
{ tag: s.string, color: "#c3e88d" },
|
|
{ tag: s.special(s.string), color: "#c3e88d" },
|
|
{ tag: s.comment, color: "#7d8799" },
|
|
{ tag: s.variableName, color: "#c792ea" },
|
|
{ tag: s.tagName, color: "#c3e88d" },
|
|
{ tag: s.bracket, color: "#525154" },
|
|
{ tag: s.meta, color: "#ffcb6b" },
|
|
{ tag: s.attributeName, color: "#c792ea" },
|
|
{ tag: s.propertyName, color: "#c792ea" },
|
|
{ tag: s.className, color: "#decb6b" },
|
|
{ tag: s.invalid, color: "#ffffff" }
|
|
]
|
|
});
|
|
const j = Q.define(), ue = X.define({
|
|
create() {
|
|
return y.none;
|
|
},
|
|
update(e, r) {
|
|
try {
|
|
for (let t of r.effects)
|
|
if (t.is(j))
|
|
if (t.value) {
|
|
const a = y.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
|
e = y.set([a.range(0, r.newDoc.length)]);
|
|
} else
|
|
e = y.set([]);
|
|
return e;
|
|
} catch (t) {
|
|
return console.warn("flash error", t), e;
|
|
}
|
|
},
|
|
provide: (e) => J.decorations.from(e)
|
|
}), de = (e) => {
|
|
e.dispatch({ effects: j.of(!0) }), setTimeout(() => {
|
|
e.dispatch({ effects: j.of(!1) });
|
|
}, 200);
|
|
}, z = Q.define(), fe = X.define({
|
|
create() {
|
|
return y.none;
|
|
},
|
|
update(e, r) {
|
|
try {
|
|
for (let t of r.effects)
|
|
if (t.is(z)) {
|
|
const a = t.value.map(
|
|
(o) => (o.context.locations || []).map(({ start: i, end: u }) => {
|
|
const m = o.context.color || "#FFCA28";
|
|
let n = r.newDoc.line(i.line).from + i.column, d = r.newDoc.line(u.line).from + u.column;
|
|
const v = r.newDoc.length;
|
|
return n > v || d > v ? void 0 : y.mark({ attributes: { style: `outline: 1.5px solid ${m};` } }).range(n, d);
|
|
})
|
|
).flat().filter(Boolean) || [];
|
|
e = y.set(a, !0);
|
|
}
|
|
return e;
|
|
} catch {
|
|
return y.set([]);
|
|
}
|
|
},
|
|
provide: (e) => J.decorations.from(e)
|
|
}), me = [ee(), le, fe, ue];
|
|
function ge({ value: e, onChange: r, onViewChanged: t, onSelectionChange: a, options: o, editorDidMount: i }) {
|
|
const u = N(
|
|
(d) => {
|
|
r?.(d);
|
|
},
|
|
[r]
|
|
), m = N(
|
|
(d) => {
|
|
t?.(d);
|
|
},
|
|
[t]
|
|
), n = N(
|
|
(d) => {
|
|
d.selectionSet && a && a?.(d.state.selection);
|
|
},
|
|
[a]
|
|
);
|
|
return /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(Z, {
|
|
value: e,
|
|
onChange: u,
|
|
onCreateEditor: m,
|
|
onUpdate: n,
|
|
extensions: me
|
|
}));
|
|
}
|
|
function W(...e) {
|
|
return e.filter(Boolean).join(" ");
|
|
}
|
|
function pe({ view: e, pattern: r, active: t, getTime: a }) {
|
|
const o = k([]), i = k();
|
|
_(() => {
|
|
if (e)
|
|
if (r && t) {
|
|
let u = requestAnimationFrame(function m() {
|
|
try {
|
|
const n = a(), v = [Math.max(i.current || n, n - 1 / 10, 0), n + 1 / 60];
|
|
i.current = v[1], o.current = o.current.filter((p) => p.whole.end > n);
|
|
const h = r.queryArc(...v).filter((p) => p.hasOnset());
|
|
o.current = o.current.concat(h), e.dispatch({ effects: z.of(o.current) });
|
|
} catch {
|
|
e.dispatch({ effects: z.of([]) });
|
|
}
|
|
u = requestAnimationFrame(m);
|
|
});
|
|
return () => {
|
|
cancelAnimationFrame(u);
|
|
};
|
|
} else
|
|
o.current = [], e.dispatch({ effects: z.of([]) });
|
|
}, [r, t, e]);
|
|
}
|
|
function he(e, r = !1) {
|
|
const t = k(), a = k(), o = (m) => {
|
|
if (a.current !== void 0) {
|
|
const n = m - a.current;
|
|
e(m, n);
|
|
}
|
|
a.current = m, t.current = requestAnimationFrame(o);
|
|
}, i = () => {
|
|
t.current = requestAnimationFrame(o);
|
|
}, u = () => {
|
|
t.current && cancelAnimationFrame(t.current), delete t.current;
|
|
};
|
|
return _(() => {
|
|
t.current && (u(), i());
|
|
}, [e]), _(() => (r && i(), u), []), {
|
|
start: i,
|
|
stop: u
|
|
};
|
|
}
|
|
function ve({ pattern: e, started: r, getTime: t, onDraw: a }) {
|
|
let o = k([]), i = k(null);
|
|
const { start: u, stop: m } = he(
|
|
N(() => {
|
|
const n = t();
|
|
if (i.current === null) {
|
|
i.current = n;
|
|
return;
|
|
}
|
|
const d = e.queryArc(Math.max(i.current, n - 1 / 10), n), v = 4;
|
|
i.current = n, o.current = (o.current || []).filter((h) => h.whole.end > n - v).concat(d.filter((h) => h.hasOnset())), a(n, o.current);
|
|
}, [e])
|
|
);
|
|
_(() => {
|
|
r ? u() : (o.current = [], m());
|
|
}, [r]);
|
|
}
|
|
function be(e) {
|
|
return _(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((r) => window.postMessage(r, "*"), []);
|
|
}
|
|
function Ee({
|
|
defaultOutput: e,
|
|
interval: r,
|
|
getTime: t,
|
|
evalOnMount: a = !1,
|
|
initialCode: o = "",
|
|
autolink: i = !1,
|
|
beforeEval: u,
|
|
afterEval: m,
|
|
editPattern: n,
|
|
onEvalError: d,
|
|
onToggle: v,
|
|
canvasId: h
|
|
}) {
|
|
const p = K(() => we(), []);
|
|
h = h || `canvas-${p}`;
|
|
const [F, A] = w(), [P, D] = w(), [b, q] = w(o), [x, V] = w(), [I, R] = w(), [L, B] = w(!1), H = b !== x, { scheduler: M, evaluate: c, start: f, stop: C, pause: O } = K(
|
|
() => re({
|
|
interval: r,
|
|
defaultOutput: e,
|
|
onSchedulerError: A,
|
|
onEvalError: (g) => {
|
|
D(g), d?.(g);
|
|
},
|
|
getTime: t,
|
|
transpiler: ie,
|
|
beforeEval: ({ code: g }) => {
|
|
q(g), u?.();
|
|
},
|
|
editPattern: n ? (g) => n(g, p) : void 0,
|
|
afterEval: ({ pattern: g, code: T }) => {
|
|
V(T), R(g), D(), A(), i && (window.location.hash = "#" + encodeURIComponent(btoa(T))), m?.();
|
|
},
|
|
onToggle: (g) => {
|
|
B(g), v?.(g);
|
|
}
|
|
}),
|
|
[e, r, t]
|
|
), Y = be(({ data: { from: g, type: T } }) => {
|
|
T === "start" && g !== p && C();
|
|
}), S = N(
|
|
async (g = !0) => {
|
|
await c(b, g), Y({ type: "start", from: p });
|
|
},
|
|
[c, b]
|
|
), U = k();
|
|
return _(() => {
|
|
!U.current && a && b && (U.current = !0, S());
|
|
}, [S, a, b]), _(() => () => {
|
|
M.stop();
|
|
}, [M]), {
|
|
id: p,
|
|
canvasId: h,
|
|
code: b,
|
|
setCode: q,
|
|
error: F || P,
|
|
schedulerError: F,
|
|
scheduler: M,
|
|
evalError: P,
|
|
evaluate: c,
|
|
activateCode: S,
|
|
activeCode: x,
|
|
isDirty: H,
|
|
pattern: I,
|
|
started: L,
|
|
start: f,
|
|
stop: C,
|
|
pause: O,
|
|
togglePlay: async () => {
|
|
L ? M.pause() : await S();
|
|
}
|
|
};
|
|
}
|
|
function we() {
|
|
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
|
}
|
|
function $({ type: e }) {
|
|
return /* @__PURE__ */ l.createElement("svg", {
|
|
xmlns: "http://www.w3.org/2000/svg",
|
|
className: "sc-h-5 sc-w-5",
|
|
viewBox: "0 0 20 20",
|
|
fill: "currentColor"
|
|
}, {
|
|
refresh: /* @__PURE__ */ l.createElement("path", {
|
|
fillRule: "evenodd",
|
|
d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z",
|
|
clipRule: "evenodd"
|
|
}),
|
|
play: /* @__PURE__ */ l.createElement("path", {
|
|
fillRule: "evenodd",
|
|
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z",
|
|
clipRule: "evenodd"
|
|
}),
|
|
pause: /* @__PURE__ */ l.createElement("path", {
|
|
fillRule: "evenodd",
|
|
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z",
|
|
clipRule: "evenodd"
|
|
})
|
|
}[e]);
|
|
}
|
|
const ye = "_container_3i85k_1", ke = "_header_3i85k_5", _e = "_buttons_3i85k_9", Fe = "_button_3i85k_9", Ce = "_buttonDisabled_3i85k_17", Ne = "_error_3i85k_21", xe = "_body_3i85k_25", E = {
|
|
container: ye,
|
|
header: ke,
|
|
buttons: _e,
|
|
button: Fe,
|
|
buttonDisabled: Ce,
|
|
error: Ne,
|
|
body: xe
|
|
}, Me = () => ce().currentTime;
|
|
function je({ tune: e, hideOutsideView: r = !1, enableKeyboard: t, withCanvas: a = !1, canvasHeight: o = 200 }) {
|
|
const {
|
|
code: i,
|
|
setCode: u,
|
|
evaluate: m,
|
|
activateCode: n,
|
|
error: d,
|
|
isDirty: v,
|
|
activeCode: h,
|
|
pattern: p,
|
|
started: F,
|
|
scheduler: A,
|
|
togglePlay: P,
|
|
stop: D,
|
|
canvasId: b,
|
|
id: q
|
|
} = Ee({
|
|
initialCode: e,
|
|
defaultOutput: ae,
|
|
getTime: Me,
|
|
editPattern: (c, f) => c.withContext((C) => ({ ...C, id: f }))
|
|
});
|
|
ve({
|
|
pattern: p,
|
|
started: a && F,
|
|
getTime: () => A.now(),
|
|
onDraw: (c, f) => {
|
|
const C = document.querySelector("#" + b).getContext("2d");
|
|
oe({ ctx: C, time: c, haps: f, autorange: 1, fold: 1, playhead: 1 });
|
|
}
|
|
});
|
|
const [x, V] = w(), [I, R] = se({
|
|
threshold: 0.01
|
|
}), L = k(), B = K(() => ((R || !r) && (L.current = !0), R || L.current), [R, r]);
|
|
pe({
|
|
view: x,
|
|
pattern: p,
|
|
active: F && !h?.includes("strudel disable-highlighting"),
|
|
getTime: () => A.getPhase()
|
|
}), G(() => {
|
|
if (t) {
|
|
const c = async (f) => {
|
|
(f.ctrlKey || f.altKey) && (f.code === "Enter" ? (f.preventDefault(), de(x), await n()) : f.code === "Period" && (D(), f.preventDefault()));
|
|
};
|
|
return window.addEventListener("keydown", c, !0), () => window.removeEventListener("keydown", c, !0);
|
|
}
|
|
}, [t, p, i, m, D, x]);
|
|
const [H, M] = w([]);
|
|
return Ae(
|
|
N((c) => {
|
|
const { data: f } = c.detail;
|
|
f?.hap?.context?.id === q && M((O) => O.concat([c.detail]).slice(-10));
|
|
}, [])
|
|
), /* @__PURE__ */ l.createElement("div", {
|
|
className: E.container,
|
|
ref: I
|
|
}, /* @__PURE__ */ l.createElement("div", {
|
|
className: E.header
|
|
}, /* @__PURE__ */ l.createElement("div", {
|
|
className: E.buttons
|
|
}, /* @__PURE__ */ l.createElement("button", {
|
|
className: W(E.button, F ? "sc-animate-pulse" : ""),
|
|
onClick: () => P()
|
|
}, /* @__PURE__ */ l.createElement($, {
|
|
type: F ? "pause" : "play"
|
|
})), /* @__PURE__ */ l.createElement("button", {
|
|
className: W(v ? E.button : E.buttonDisabled),
|
|
onClick: () => n()
|
|
}, /* @__PURE__ */ l.createElement($, {
|
|
type: "refresh"
|
|
}))), d && /* @__PURE__ */ l.createElement("div", {
|
|
className: E.error
|
|
}, d.message)), /* @__PURE__ */ l.createElement("div", {
|
|
className: E.body
|
|
}, B && /* @__PURE__ */ l.createElement(ge, {
|
|
value: i,
|
|
onChange: u,
|
|
onViewChanged: V
|
|
})), a && /* @__PURE__ */ l.createElement("canvas", {
|
|
id: b,
|
|
className: "w-full pointer-events-none",
|
|
height: o,
|
|
ref: (c) => {
|
|
c && c.width !== c.clientWidth && (c.width = c.clientWidth);
|
|
}
|
|
}), !!H.length && /* @__PURE__ */ l.createElement("div", {
|
|
className: "sc-bg-gray-800 sc-rounded-md sc-p-2"
|
|
}, H.map(({ message: c }, f) => /* @__PURE__ */ l.createElement("div", {
|
|
key: f
|
|
}, c))));
|
|
}
|
|
function Ae(e) {
|
|
De(ne.key, e);
|
|
}
|
|
function De(e, r, t = !1) {
|
|
_(() => (document.addEventListener(e, r, t), () => {
|
|
document.removeEventListener(e, r, t);
|
|
}), [r]);
|
|
}
|
|
const Ue = (e) => G(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
|
|
export {
|
|
ge as CodeMirror,
|
|
je as MiniRepl,
|
|
W as cx,
|
|
de as flash,
|
|
pe as useHighlighting,
|
|
Ue as useKeydown,
|
|
be as usePostMessage,
|
|
Ee as useStrudel
|
|
};
|