mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-14 07:08:30 +00:00
435 lines
13 KiB
JavaScript
435 lines
13 KiB
JavaScript
import l, { useCallback as w, useRef as A, useEffect as k, useMemo as $, useState as _, useLayoutEffect as Y } from "react";
|
|
import ie from "@uiw/react-codemirror";
|
|
import { Decoration as M, EditorView as Z } from "@codemirror/view";
|
|
import { StateEffect as ee, StateField as te } from "@codemirror/state";
|
|
import { javascript as le } from "@codemirror/lang-javascript";
|
|
import { tags as i } from "@lezer/highlight";
|
|
import { createTheme as ue } from "@uiw/codemirror-themes";
|
|
import { webaudioOutput as de, getAudioContext as fe } from "@strudel.cycles/webaudio";
|
|
import { useInView as me } from "react-hook-inview";
|
|
import { repl as he, logger as ge } from "@strudel.cycles/core";
|
|
import { transpiler as pe } from "@strudel.cycles/transpiler";
|
|
const ve = ue({
|
|
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: i.keyword, color: "#c792ea" },
|
|
{ tag: i.operator, color: "#89ddff" },
|
|
{ tag: i.special(i.variableName), color: "#eeffff" },
|
|
{ tag: i.typeName, color: "#c3e88d" },
|
|
{ tag: i.atom, color: "#f78c6c" },
|
|
{ tag: i.number, color: "#c3e88d" },
|
|
{ tag: i.definition(i.variableName), color: "#82aaff" },
|
|
{ tag: i.string, color: "#c3e88d" },
|
|
{ tag: i.special(i.string), color: "#c3e88d" },
|
|
{ tag: i.comment, color: "#7d8799" },
|
|
{ tag: i.variableName, color: "#c792ea" },
|
|
{ tag: i.tagName, color: "#c3e88d" },
|
|
{ tag: i.bracket, color: "#525154" },
|
|
{ tag: i.meta, color: "#ffcb6b" },
|
|
{ tag: i.attributeName, color: "#c792ea" },
|
|
{ tag: i.propertyName, color: "#c792ea" },
|
|
{ tag: i.className, color: "#decb6b" },
|
|
{ tag: i.invalid, color: "#ffffff" }
|
|
]
|
|
});
|
|
const G = ee.define(), be = te.define({
|
|
create() {
|
|
return M.none;
|
|
},
|
|
update(e, r) {
|
|
try {
|
|
for (let t of r.effects)
|
|
if (t.is(G))
|
|
if (t.value) {
|
|
const o = M.mark({ attributes: { style: "background-color: #FFCA2880" } });
|
|
e = M.set([o.range(0, r.newDoc.length)]);
|
|
} else
|
|
e = M.set([]);
|
|
return e;
|
|
} catch (t) {
|
|
return console.warn("flash error", t), e;
|
|
}
|
|
},
|
|
provide: (e) => Z.decorations.from(e)
|
|
}), Ee = (e) => {
|
|
e.dispatch({ effects: G.of(!0) }), setTimeout(() => {
|
|
e.dispatch({ effects: G.of(!1) });
|
|
}, 200);
|
|
}, O = ee.define(), ye = te.define({
|
|
create() {
|
|
return M.none;
|
|
},
|
|
update(e, r) {
|
|
try {
|
|
for (let t of r.effects)
|
|
if (t.is(O)) {
|
|
const o = t.value.map(
|
|
(u) => (u.context.locations || []).map(({ start: m, end: d }) => {
|
|
const a = u.context.color || "#FFCA28";
|
|
let c = r.newDoc.line(m.line).from + m.column, h = r.newDoc.line(d.line).from + d.column;
|
|
const g = r.newDoc.length;
|
|
return c > g || h > g ? void 0 : M.mark({ attributes: { style: `outline: 1.5px solid ${a};` } }).range(c, h);
|
|
})
|
|
).flat().filter(Boolean) || [];
|
|
e = M.set(o, !0);
|
|
}
|
|
return e;
|
|
} catch {
|
|
return M.set([]);
|
|
}
|
|
},
|
|
provide: (e) => Z.decorations.from(e)
|
|
}), we = [le(), ve, ye, be];
|
|
function ke({ value: e, onChange: r, onViewChanged: t, onSelectionChange: o, options: u, editorDidMount: m }) {
|
|
const d = w(
|
|
(h) => {
|
|
r?.(h);
|
|
},
|
|
[r]
|
|
), a = w(
|
|
(h) => {
|
|
t?.(h);
|
|
},
|
|
[t]
|
|
), c = w(
|
|
(h) => {
|
|
h.selectionSet && o && o?.(h.state.selection);
|
|
},
|
|
[o]
|
|
);
|
|
return /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(ie, {
|
|
value: e,
|
|
onChange: d,
|
|
onCreateEditor: a,
|
|
onUpdate: c,
|
|
extensions: we
|
|
}));
|
|
}
|
|
function T(...e) {
|
|
return e.filter(Boolean).join(" ");
|
|
}
|
|
function Fe({ view: e, pattern: r, active: t, getTime: o }) {
|
|
const u = A([]), m = A();
|
|
k(() => {
|
|
if (e)
|
|
if (r && t) {
|
|
let d = requestAnimationFrame(function a() {
|
|
try {
|
|
const c = o(), g = [Math.max(m.current || c, c - 1 / 10, 0), c + 1 / 60];
|
|
m.current = g[1], u.current = u.current.filter((p) => p.whole.end > c);
|
|
const n = r.queryArc(...g).filter((p) => p.hasOnset());
|
|
u.current = u.current.concat(n), e.dispatch({ effects: O.of(u.current) });
|
|
} catch {
|
|
e.dispatch({ effects: O.of([]) });
|
|
}
|
|
d = requestAnimationFrame(a);
|
|
});
|
|
return () => {
|
|
cancelAnimationFrame(d);
|
|
};
|
|
} else
|
|
u.current = [], e.dispatch({ effects: O.of([]) });
|
|
}, [r, t, e]);
|
|
}
|
|
function _e(e, r = !1) {
|
|
const t = A(), o = A(), u = (a) => {
|
|
if (o.current !== void 0) {
|
|
const c = a - o.current;
|
|
e(a, c);
|
|
}
|
|
o.current = a, t.current = requestAnimationFrame(u);
|
|
}, m = () => {
|
|
t.current = requestAnimationFrame(u);
|
|
}, d = () => {
|
|
t.current && cancelAnimationFrame(t.current), delete t.current;
|
|
};
|
|
return k(() => {
|
|
t.current && (d(), m());
|
|
}, [e]), k(() => (r && m(), d), []), {
|
|
start: m,
|
|
stop: d
|
|
};
|
|
}
|
|
function Me({ pattern: e, started: r, getTime: t, onDraw: o, drawTime: u = [-2, 2] }) {
|
|
let [m, d] = u;
|
|
m = Math.abs(m);
|
|
let a = A([]), c = A(null);
|
|
k(() => {
|
|
if (e) {
|
|
const n = t(), p = e.queryArc(n, n + d);
|
|
a.current = a.current.filter((b) => b.whole.begin < n), a.current = a.current.concat(p);
|
|
}
|
|
}, [e]);
|
|
const { start: h, stop: g } = _e(
|
|
w(() => {
|
|
const n = t() + d;
|
|
if (c.current === null) {
|
|
c.current = n;
|
|
return;
|
|
}
|
|
const p = e.queryArc(Math.max(c.current, n - 1 / 10), n);
|
|
c.current = n, a.current = (a.current || []).filter((b) => b.whole.end >= n - m - d).concat(p.filter((b) => b.hasOnset())), o(e, n - d, a.current, u);
|
|
}, [e])
|
|
);
|
|
return k(() => {
|
|
r ? h() : (a.current = [], g());
|
|
}, [r]), {
|
|
clear: () => {
|
|
a.current = [];
|
|
}
|
|
};
|
|
}
|
|
function Ae(e) {
|
|
return k(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), w((r) => window.postMessage(r, "*"), []);
|
|
}
|
|
function Ne({
|
|
defaultOutput: e,
|
|
interval: r,
|
|
getTime: t,
|
|
evalOnMount: o = !1,
|
|
initialCode: u = "",
|
|
autolink: m = !1,
|
|
beforeEval: d,
|
|
afterEval: a,
|
|
onEvalError: c,
|
|
onToggle: h,
|
|
canvasId: g,
|
|
drawContext: n,
|
|
drawTime: p = [-2, 2]
|
|
}) {
|
|
const b = $(() => De(), []);
|
|
g = g || `canvas-${b}`;
|
|
const [q, x] = _(), [C, z] = _(), [E, D] = _(u), [H, B] = _(), [N, P] = _(), [R, S] = _(!1), I = E !== H, { scheduler: s, evaluate: v, start: J, stop: V, pause: re } = $(
|
|
() => he({
|
|
interval: r,
|
|
defaultOutput: e,
|
|
onSchedulerError: x,
|
|
onEvalError: (f) => {
|
|
z(f), c?.(f);
|
|
},
|
|
getTime: t,
|
|
drawContext: n,
|
|
transpiler: pe,
|
|
beforeEval: ({ code: f }) => {
|
|
D(f), d?.();
|
|
},
|
|
afterEval: ({ pattern: f, code: y }) => {
|
|
B(y), P(f), z(), x(), m && (window.location.hash = "#" + encodeURIComponent(btoa(y))), a?.();
|
|
},
|
|
onToggle: (f) => {
|
|
S(f), h?.(f);
|
|
}
|
|
}),
|
|
[e, r, t]
|
|
), ne = Ae(({ data: { from: f, type: y } }) => {
|
|
y === "start" && f !== b && V();
|
|
}), K = w(
|
|
async (f = !0) => {
|
|
const y = await v(E, f);
|
|
return ne({ type: "start", from: b }), y;
|
|
},
|
|
[v, E]
|
|
), L = w(
|
|
(f, y, U, W) => {
|
|
const { onPaint: ce } = f.context || {}, se = typeof n == "function" ? n(g) : n;
|
|
ce?.(se, y, U, W);
|
|
},
|
|
[n, g]
|
|
), j = w(
|
|
(f) => {
|
|
if (n && L) {
|
|
const [y, U] = p, W = f.queryArc(0, U);
|
|
L(f, 0, W, p);
|
|
}
|
|
},
|
|
[p, L]
|
|
), Q = A();
|
|
k(() => {
|
|
!Q.current && n && L && o && E && (Q.current = !0, v(E, !1).then((f) => j(f)));
|
|
}, [K, o, E, j]), k(() => () => {
|
|
s.stop();
|
|
}, [s]);
|
|
const oe = async () => {
|
|
R ? (s.stop(), j(N)) : await K();
|
|
}, ae = q || C;
|
|
return Me({
|
|
pattern: N,
|
|
started: n && R,
|
|
getTime: () => s.now(),
|
|
drawTime: p,
|
|
onDraw: L
|
|
}), {
|
|
id: b,
|
|
canvasId: g,
|
|
code: E,
|
|
setCode: D,
|
|
error: ae,
|
|
schedulerError: q,
|
|
scheduler: s,
|
|
evalError: C,
|
|
evaluate: v,
|
|
activateCode: K,
|
|
activeCode: H,
|
|
isDirty: I,
|
|
pattern: N,
|
|
started: R,
|
|
start: J,
|
|
stop: V,
|
|
pause: re,
|
|
togglePlay: oe
|
|
};
|
|
}
|
|
function De() {
|
|
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
|
}
|
|
function X({ 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"
|
|
}),
|
|
stop: /* @__PURE__ */ l.createElement("path", {
|
|
fillRule: "evenodd",
|
|
d: "M2 10a8 8 0 1116 0 8 8 0 01-16 0zm5-2.25A.75.75 0 017.75 7h4.5a.75.75 0 01.75.75v4.5a.75.75 0 01-.75.75h-4.5a.75.75 0 01-.75-.75v-4.5z",
|
|
clipRule: "evenodd"
|
|
})
|
|
}[e]);
|
|
}
|
|
const Ce = "_container_3i85k_1", Re = "_header_3i85k_5", Le = "_buttons_3i85k_9", qe = "_button_3i85k_9", xe = "_buttonDisabled_3i85k_17", ze = "_error_3i85k_21", He = "_body_3i85k_25", F = {
|
|
container: Ce,
|
|
header: Re,
|
|
buttons: Le,
|
|
button: qe,
|
|
buttonDisabled: xe,
|
|
error: ze,
|
|
body: He
|
|
}, Pe = () => fe().currentTime;
|
|
function Te({ tune: e, hideOutsideView: r = !1, enableKeyboard: t, drawTime: o, canvasHeight: u = 200 }) {
|
|
const {
|
|
code: m,
|
|
setCode: d,
|
|
evaluate: a,
|
|
activateCode: c,
|
|
error: h,
|
|
isDirty: g,
|
|
activeCode: n,
|
|
pattern: p,
|
|
started: b,
|
|
scheduler: q,
|
|
togglePlay: x,
|
|
stop: C,
|
|
canvasId: z,
|
|
id: E
|
|
} = Ne({
|
|
initialCode: e,
|
|
defaultOutput: de,
|
|
getTime: Pe,
|
|
evalOnMount: !!o,
|
|
drawContext: o ? (s) => document.querySelector("#" + s)?.getContext("2d") : null,
|
|
drawTime: o
|
|
}), [D, H] = _(), [B, N] = me({
|
|
threshold: 0.01
|
|
}), P = A(), R = $(() => ((N || !r) && (P.current = !0), N || P.current), [N, r]);
|
|
Fe({
|
|
view: D,
|
|
pattern: p,
|
|
active: b && !n?.includes("strudel disable-highlighting"),
|
|
getTime: () => q.now()
|
|
}), Y(() => {
|
|
if (t) {
|
|
const s = async (v) => {
|
|
(v.ctrlKey || v.altKey) && (v.code === "Enter" ? (v.preventDefault(), Ee(D), await c()) : v.code === "Period" && (C(), v.preventDefault()));
|
|
};
|
|
return window.addEventListener("keydown", s, !0), () => window.removeEventListener("keydown", s, !0);
|
|
}
|
|
}, [t, p, m, a, C, D]);
|
|
const [S, I] = _([]);
|
|
return Se(
|
|
w((s) => {
|
|
const { data: v } = s.detail;
|
|
v?.hap?.context?.id === E && I((V) => V.concat([s.detail]).slice(-10));
|
|
}, [])
|
|
), /* @__PURE__ */ l.createElement("div", {
|
|
className: F.container,
|
|
ref: B
|
|
}, /* @__PURE__ */ l.createElement("div", {
|
|
className: F.header
|
|
}, /* @__PURE__ */ l.createElement("div", {
|
|
className: F.buttons
|
|
}, /* @__PURE__ */ l.createElement("button", {
|
|
className: T(F.button, b ? "sc-animate-pulse" : ""),
|
|
onClick: () => x()
|
|
}, /* @__PURE__ */ l.createElement(X, {
|
|
type: b ? "stop" : "play"
|
|
})), /* @__PURE__ */ l.createElement("button", {
|
|
className: T(g ? F.button : F.buttonDisabled),
|
|
onClick: () => c()
|
|
}, /* @__PURE__ */ l.createElement(X, {
|
|
type: "refresh"
|
|
}))), h && /* @__PURE__ */ l.createElement("div", {
|
|
className: F.error
|
|
}, h.message)), /* @__PURE__ */ l.createElement("div", {
|
|
className: F.body
|
|
}, R && /* @__PURE__ */ l.createElement(ke, {
|
|
value: m,
|
|
onChange: d,
|
|
onViewChanged: H
|
|
})), o && /* @__PURE__ */ l.createElement("canvas", {
|
|
id: z,
|
|
className: "w-full pointer-events-none",
|
|
height: u,
|
|
ref: (s) => {
|
|
s && s.width !== s.clientWidth && (s.width = s.clientWidth);
|
|
}
|
|
}), !!S.length && /* @__PURE__ */ l.createElement("div", {
|
|
className: "sc-bg-gray-800 sc-rounded-md sc-p-2"
|
|
}, S.map(({ message: s }, v) => /* @__PURE__ */ l.createElement("div", {
|
|
key: v
|
|
}, s))));
|
|
}
|
|
function Se(e) {
|
|
Ve(ge.key, e);
|
|
}
|
|
function Ve(e, r, t = !1) {
|
|
k(() => (document.addEventListener(e, r, t), () => {
|
|
document.removeEventListener(e, r, t);
|
|
}), [r]);
|
|
}
|
|
const Xe = (e) => Y(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
|
|
export {
|
|
ke as CodeMirror,
|
|
Te as MiniRepl,
|
|
T as cx,
|
|
Ee as flash,
|
|
Fe as useHighlighting,
|
|
Xe as useKeydown,
|
|
Ae as usePostMessage,
|
|
Ne as useStrudel
|
|
};
|