From c92cb1c096880c8a20bd9abef735bd08f679c0b0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 13 Nov 2022 14:50:48 +0100 Subject: [PATCH] better draw cleanup + began reference tab --- jsdoc.config.json | 2 +- packages/core/draw.mjs | 4 +- packages/react/dist/index.cjs.js | 2 +- packages/react/dist/index.es.js | 231 ++++++++++++------------ packages/react/src/hooks/useStrudel.mjs | 6 +- repl/package.json | 1 + repl/src/App.jsx | 3 +- repl/src/Footer.jsx | 17 +- repl/src/Reference.jsx | 39 ++++ 9 files changed, 178 insertions(+), 127 deletions(-) create mode 100644 repl/src/Reference.jsx diff --git a/jsdoc.config.json b/jsdoc.config.json index 1e287280..ca9c3d81 100644 --- a/jsdoc.config.json +++ b/jsdoc.config.json @@ -1,7 +1,7 @@ { "source": { "includePattern": ".+\\.(js(doc|x)?|mjs)$", - "excludePattern": "node_modules|shift-parser|shift-reducer|shift-traverser" + "excludePattern": "node_modules|shift-parser|shift-reducer|shift-traverser|dist" }, "plugins": ["plugins/markdown"], "opts": { diff --git a/packages/core/draw.mjs b/packages/core/draw.mjs index 5ff8a49a..9ef34e3a 100644 --- a/packages/core/draw.mjs +++ b/packages/core/draw.mjs @@ -49,9 +49,9 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery }) { return this; }; -export const cleanupDraw = () => { +export const cleanupDraw = (clearScreen = true) => { const ctx = getDrawContext(); - ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); + clearScreen && ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); if (window.strudelAnimation) { cancelAnimationFrame(window.strudelAnimation); } diff --git a/packages/react/dist/index.cjs.js b/packages/react/dist/index.cjs.js index 19f8cb09..32711cd0 100644 --- a/packages/react/dist/index.cjs.js +++ b/packages/react/dist/index.cjs.js @@ -1 +1 @@ -"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const t=require("react"),$=require("@uiw/react-codemirror"),v=require("@codemirror/view"),R=require("@codemirror/state"),G=require("@codemirror/lang-javascript"),o=require("@lezer/highlight"),Q=require("@uiw/codemirror-themes"),W=require("react-hook-inview"),z=require("@strudel.cycles/webaudio"),X=require("@strudel.cycles/core"),Y=require("@strudel.cycles/transpiler"),V=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},n=V(t),Z=V($),ee=Q.createTheme({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:o.tags.keyword,color:"#c792ea"},{tag:o.tags.operator,color:"#89ddff"},{tag:o.tags.special(o.tags.variableName),color:"#eeffff"},{tag:o.tags.typeName,color:"#c3e88d"},{tag:o.tags.atom,color:"#f78c6c"},{tag:o.tags.number,color:"#c3e88d"},{tag:o.tags.definition(o.tags.variableName),color:"#82aaff"},{tag:o.tags.string,color:"#c3e88d"},{tag:o.tags.special(o.tags.string),color:"#c3e88d"},{tag:o.tags.comment,color:"#7d8799"},{tag:o.tags.variableName,color:"#c792ea"},{tag:o.tags.tagName,color:"#c3e88d"},{tag:o.tags.bracket,color:"#525154"},{tag:o.tags.meta,color:"#ffcb6b"},{tag:o.tags.attributeName,color:"#c792ea"},{tag:o.tags.propertyName,color:"#c792ea"},{tag:o.tags.className,color:"#decb6b"},{tag:o.tags.invalid,color:"#ffffff"}]});const x=R.StateEffect.define(),te=R.StateField.define({create(){return v.Decoration.none},update(e,r){try{for(let a of r.effects)if(a.is(x))if(a.value){const s=v.Decoration.mark({attributes:{style:"background-color: #FFCA2880"}});e=v.Decoration.set([s.range(0,r.newDoc.length)])}else e=v.Decoration.set([]);return e}catch(a){return console.warn("flash error",a),e}},provide:e=>v.EditorView.decorations.from(e)}),j=e=>{e.dispatch({effects:x.of(!0)}),setTimeout(()=>{e.dispatch({effects:x.of(!1)})},200)},N=R.StateEffect.define(),re=R.StateField.define({create(){return v.Decoration.none},update(e,r){try{for(let a of r.effects)if(a.is(N)){const s=a.value.map(c=>(c.context.locations||[]).map(({start:f,end:u})=>{const d=c.context.color||"#FFCA28";let i=r.newDoc.line(f.line).from+f.column,l=r.newDoc.line(u.line).from+u.column;const g=r.newDoc.length;return i>g||l>g?void 0:v.Decoration.mark({attributes:{style:`outline: 1.5px solid ${d};`}}).range(i,l)})).flat().filter(Boolean)||[];e=v.Decoration.set(s,!0)}return e}catch{return v.Decoration.set([])}},provide:e=>v.EditorView.decorations.from(e)}),oe=[G.javascript(),ee,re,te];function B({value:e,onChange:r,onViewChanged:a,onSelectionChange:s,options:c,editorDidMount:f}){const u=t.useCallback(l=>{r?.(l)},[r]),d=t.useCallback(l=>{a?.(l)},[a]),i=t.useCallback(l=>{l.selectionSet&&s&&s?.(l.state.selection)},[s]);return n.default.createElement(n.default.Fragment,null,n.default.createElement(Z.default,{value:e,onChange:u,onCreateEditor:d,onUpdate:i,extensions:oe}))}function P(...e){return e.filter(Boolean).join(" ")}function I({view:e,pattern:r,active:a,getTime:s}){const c=t.useRef([]),f=t.useRef();t.useEffect(()=>{if(e)if(r&&a){let d=function(){try{const i=s(),g=[Math.max(f.current||i,i-1/10,0),i+1/60];f.current=g[1],c.current=c.current.filter(m=>m.whole.end>i);const w=r.queryArc(...g).filter(m=>m.hasOnset());c.current=c.current.concat(w),e.dispatch({effects:N.of(c.current)})}catch{e.dispatch({effects:N.of([])})}u=requestAnimationFrame(d)},u=requestAnimationFrame(d);return()=>{cancelAnimationFrame(u)}}else c.current=[],e.dispatch({effects:N.of([])})},[r,a,e])}const ae="_container_3i85k_1",ne="_header_3i85k_5",se="_buttons_3i85k_9",ce="_button_3i85k_9",ie="_buttonDisabled_3i85k_17",le="_error_3i85k_21",ue="_body_3i85k_25",E={container:ae,header:ne,buttons:se,button:ce,buttonDisabled:ie,error:le,body:ue};function T({type:e}){return n.default.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",className:"sc-h-5 sc-w-5",viewBox:"0 0 20 20",fill:"currentColor"},{refresh:n.default.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:n.default.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:n.default.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])}function K({defaultOutput:e,interval:r,getTime:a,evalOnMount:s=!1,initialCode:c="",autolink:f=!1,beforeEval:u,afterEval:d,onEvalError:i}){const[l,g]=t.useState(),[w,m]=t.useState(),[b,D]=t.useState(c),[_,C]=t.useState(b),[q,F]=t.useState(),[k,M]=t.useState(!1),A=b!==_,{scheduler:y,evaluate:h,start:O,stop:U,pause:J}=t.useMemo(()=>X.repl({interval:r,defaultOutput:e,onSchedulerError:g,onEvalError:p=>{m(p),i?.(p)},getTime:a,transpiler:Y.transpiler,beforeEval:({code:p})=>{D(p),u?.()},afterEval:({pattern:p,code:L})=>{C(L),F(p),m(),g(),f&&(window.location.hash="#"+encodeURIComponent(btoa(L))),d?.()},onToggle:p=>M(p)}),[e,r,a]),S=t.useCallback(async(p=!0)=>h(b,p),[h,b]),H=t.useRef();return t.useEffect(()=>{!H.current&&s&&b&&(H.current=!0,S())},[S,s,b]),t.useEffect(()=>()=>{y.stop()},[y]),{code:b,setCode:D,error:l||w,schedulerError:l,scheduler:y,evalError:w,evaluate:h,activateCode:S,activeCode:_,isDirty:A,pattern:q,started:k,start:O,stop:U,pause:J,togglePlay:async()=>{k?y.pause():await S()}}}const de=()=>z.getAudioContext().currentTime;function fe({tune:e,hideOutsideView:r=!1,init:a,enableKeyboard:s}){const{code:c,setCode:f,evaluate:u,activateCode:d,error:i,isDirty:l,activeCode:g,pattern:w,started:m,scheduler:b,togglePlay:D,stop:_}=K({initialCode:e,defaultOutput:z.webaudioOutput,getTime:de}),[C,q]=t.useState(),[F,k]=W.useInView({threshold:.01}),M=t.useRef(),A=t.useMemo(()=>((k||!r)&&(M.current=!0),k||M.current),[k,r]);return I({view:C,pattern:w,active:m&&!g?.includes("strudel disable-highlighting"),getTime:()=>b.getPhase()}),t.useLayoutEffect(()=>{if(s){const y=async h=>{(h.ctrlKey||h.altKey)&&(h.code==="Enter"?(h.preventDefault(),j(C),await d()):h.code==="Period"&&(_(),h.preventDefault()))};return window.addEventListener("keydown",y,!0),()=>window.removeEventListener("keydown",y,!0)}},[s,w,c,u,_,C]),n.default.createElement("div",{className:E.container,ref:F},n.default.createElement("div",{className:E.header},n.default.createElement("div",{className:E.buttons},n.default.createElement("button",{className:P(E.button,m?"sc-animate-pulse":""),onClick:()=>D()},n.default.createElement(T,{type:m?"pause":"play"})),n.default.createElement("button",{className:P(l?E.button:E.buttonDisabled),onClick:()=>d()},n.default.createElement(T,{type:"refresh"}))),i&&n.default.createElement("div",{className:E.error},i.message)),n.default.createElement("div",{className:E.body},A&&n.default.createElement(B,{value:c,onChange:f,onViewChanged:q})))}function ge(e){return t.useEffect(()=>(window.addEventListener("message",e),()=>window.removeEventListener("message",e)),[e]),t.useCallback(r=>window.postMessage(r,"*"),[])}const me=e=>t.useLayoutEffect(()=>(window.addEventListener("keydown",e,!0),()=>window.removeEventListener("keydown",e,!0)),[e]);exports.CodeMirror=B;exports.MiniRepl=fe;exports.cx=P;exports.flash=j;exports.useHighlighting=I;exports.useKeydown=me;exports.usePostMessage=ge;exports.useStrudel=K; +"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const t=require("react"),G=require("@uiw/react-codemirror"),b=require("@codemirror/view"),R=require("@codemirror/state"),Q=require("@codemirror/lang-javascript"),o=require("@lezer/highlight"),W=require("@uiw/codemirror-themes"),X=require("react-hook-inview"),V=require("@strudel.cycles/webaudio"),Y=require("@strudel.cycles/core"),Z=require("@strudel.cycles/transpiler"),j=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},n=j(t),ee=j(G),te=W.createTheme({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:o.tags.keyword,color:"#c792ea"},{tag:o.tags.operator,color:"#89ddff"},{tag:o.tags.special(o.tags.variableName),color:"#eeffff"},{tag:o.tags.typeName,color:"#c3e88d"},{tag:o.tags.atom,color:"#f78c6c"},{tag:o.tags.number,color:"#c3e88d"},{tag:o.tags.definition(o.tags.variableName),color:"#82aaff"},{tag:o.tags.string,color:"#c3e88d"},{tag:o.tags.special(o.tags.string),color:"#c3e88d"},{tag:o.tags.comment,color:"#7d8799"},{tag:o.tags.variableName,color:"#c792ea"},{tag:o.tags.tagName,color:"#c3e88d"},{tag:o.tags.bracket,color:"#525154"},{tag:o.tags.meta,color:"#ffcb6b"},{tag:o.tags.attributeName,color:"#c792ea"},{tag:o.tags.propertyName,color:"#c792ea"},{tag:o.tags.className,color:"#decb6b"},{tag:o.tags.invalid,color:"#ffffff"}]});const P=R.StateEffect.define(),re=R.StateField.define({create(){return b.Decoration.none},update(e,r){try{for(let a of r.effects)if(a.is(P))if(a.value){const s=b.Decoration.mark({attributes:{style:"background-color: #FFCA2880"}});e=b.Decoration.set([s.range(0,r.newDoc.length)])}else e=b.Decoration.set([]);return e}catch(a){return console.warn("flash error",a),e}},provide:e=>b.EditorView.decorations.from(e)}),B=e=>{e.dispatch({effects:P.of(!0)}),setTimeout(()=>{e.dispatch({effects:P.of(!1)})},200)},N=R.StateEffect.define(),oe=R.StateField.define({create(){return b.Decoration.none},update(e,r){try{for(let a of r.effects)if(a.is(N)){const s=a.value.map(c=>(c.context.locations||[]).map(({start:m,end:u})=>{const d=c.context.color||"#FFCA28";let i=r.newDoc.line(m.line).from+m.column,l=r.newDoc.line(u.line).from+u.column;const h=r.newDoc.length;return i>h||l>h?void 0:b.Decoration.mark({attributes:{style:`outline: 1.5px solid ${d};`}}).range(i,l)})).flat().filter(Boolean)||[];e=b.Decoration.set(s,!0)}return e}catch{return b.Decoration.set([])}},provide:e=>b.EditorView.decorations.from(e)}),ae=[Q.javascript(),te,oe,re];function I({value:e,onChange:r,onViewChanged:a,onSelectionChange:s,options:c,editorDidMount:m}){const u=t.useCallback(l=>{r?.(l)},[r]),d=t.useCallback(l=>{a?.(l)},[a]),i=t.useCallback(l=>{l.selectionSet&&s&&s?.(l.state.selection)},[s]);return n.default.createElement(n.default.Fragment,null,n.default.createElement(ee.default,{value:e,onChange:u,onCreateEditor:d,onUpdate:i,extensions:ae}))}function H(...e){return e.filter(Boolean).join(" ")}function K({view:e,pattern:r,active:a,getTime:s}){const c=t.useRef([]),m=t.useRef();t.useEffect(()=>{if(e)if(r&&a){let d=function(){try{const i=s(),h=[Math.max(m.current||i,i-1/10,0),i+1/60];m.current=h[1],c.current=c.current.filter(p=>p.whole.end>i);const w=r.queryArc(...h).filter(p=>p.hasOnset());c.current=c.current.concat(w),e.dispatch({effects:N.of(c.current)})}catch{e.dispatch({effects:N.of([])})}u=requestAnimationFrame(d)},u=requestAnimationFrame(d);return()=>{cancelAnimationFrame(u)}}else c.current=[],e.dispatch({effects:N.of([])})},[r,a,e])}const ne="_container_3i85k_1",se="_header_3i85k_5",ce="_buttons_3i85k_9",ie="_button_3i85k_9",le="_buttonDisabled_3i85k_17",ue="_error_3i85k_21",de="_body_3i85k_25",E={container:ne,header:se,buttons:ce,button:ie,buttonDisabled:le,error:ue,body:de};function z({type:e}){return n.default.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",className:"sc-h-5 sc-w-5",viewBox:"0 0 20 20",fill:"currentColor"},{refresh:n.default.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:n.default.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:n.default.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])}function O({defaultOutput:e,interval:r,getTime:a,evalOnMount:s=!1,initialCode:c="",autolink:m=!1,beforeEval:u,afterEval:d,onEvalError:i,onToggle:l}){const[h,w]=t.useState(),[p,D]=t.useState(),[v,k]=t.useState(c),[y,q]=t.useState(v),[F,_]=t.useState(),[C,A]=t.useState(!1),M=v!==y,{scheduler:f,evaluate:x,start:U,stop:J,pause:$}=t.useMemo(()=>Y.repl({interval:r,defaultOutput:e,onSchedulerError:w,onEvalError:g=>{D(g),i?.(g)},getTime:a,transpiler:Z.transpiler,beforeEval:({code:g})=>{k(g),u?.()},afterEval:({pattern:g,code:T})=>{q(T),_(g),D(),w(),m&&(window.location.hash="#"+encodeURIComponent(btoa(T))),d?.()},onToggle:g=>{A(g),l?.(g)}}),[e,r,a]),S=t.useCallback(async(g=!0)=>x(v,g),[x,v]),L=t.useRef();return t.useEffect(()=>{!L.current&&s&&v&&(L.current=!0,S())},[S,s,v]),t.useEffect(()=>()=>{f.stop()},[f]),{code:v,setCode:k,error:h||p,schedulerError:h,scheduler:f,evalError:p,evaluate:x,activateCode:S,activeCode:y,isDirty:M,pattern:F,started:C,start:U,stop:J,pause:$,togglePlay:async()=>{C?f.pause():await S()}}}const fe=()=>V.getAudioContext().currentTime;function ge({tune:e,hideOutsideView:r=!1,init:a,enableKeyboard:s}){const{code:c,setCode:m,evaluate:u,activateCode:d,error:i,isDirty:l,activeCode:h,pattern:w,started:p,scheduler:D,togglePlay:v,stop:k}=O({initialCode:e,defaultOutput:V.webaudioOutput,getTime:fe}),[y,q]=t.useState(),[F,_]=X.useInView({threshold:.01}),C=t.useRef(),A=t.useMemo(()=>((_||!r)&&(C.current=!0),_||C.current),[_,r]);return K({view:y,pattern:w,active:p&&!h?.includes("strudel disable-highlighting"),getTime:()=>D.getPhase()}),t.useLayoutEffect(()=>{if(s){const M=async f=>{(f.ctrlKey||f.altKey)&&(f.code==="Enter"?(f.preventDefault(),B(y),await d()):f.code==="Period"&&(k(),f.preventDefault()))};return window.addEventListener("keydown",M,!0),()=>window.removeEventListener("keydown",M,!0)}},[s,w,c,u,k,y]),n.default.createElement("div",{className:E.container,ref:F},n.default.createElement("div",{className:E.header},n.default.createElement("div",{className:E.buttons},n.default.createElement("button",{className:H(E.button,p?"sc-animate-pulse":""),onClick:()=>v()},n.default.createElement(z,{type:p?"pause":"play"})),n.default.createElement("button",{className:H(l?E.button:E.buttonDisabled),onClick:()=>d()},n.default.createElement(z,{type:"refresh"}))),i&&n.default.createElement("div",{className:E.error},i.message)),n.default.createElement("div",{className:E.body},A&&n.default.createElement(I,{value:c,onChange:m,onViewChanged:q})))}function me(e){return t.useEffect(()=>(window.addEventListener("message",e),()=>window.removeEventListener("message",e)),[e]),t.useCallback(r=>window.postMessage(r,"*"),[])}const he=e=>t.useLayoutEffect(()=>(window.addEventListener("keydown",e,!0),()=>window.removeEventListener("keydown",e,!0)),[e]);exports.CodeMirror=I;exports.MiniRepl=ge;exports.cx=H;exports.flash=B;exports.useHighlighting=K;exports.useKeydown=he;exports.usePostMessage=me;exports.useStrudel=O; diff --git a/packages/react/dist/index.es.js b/packages/react/dist/index.es.js index 4ee17068..9ebf09b3 100644 --- a/packages/react/dist/index.es.js +++ b/packages/react/dist/index.es.js @@ -1,15 +1,15 @@ -import n, { useCallback as N, useRef as x, useEffect as R, useState as y, useMemo as q, useLayoutEffect as I } from "react"; -import J from "@uiw/react-codemirror"; -import { Decoration as E, EditorView as K } from "@codemirror/view"; -import { StateEffect as O, StateField as j } from "@codemirror/state"; -import { javascript as Q } from "@codemirror/lang-javascript"; +import n, { useCallback as N, useRef as x, useEffect as R, useState as w, useMemo as I, useLayoutEffect as K } from "react"; +import Q from "@uiw/react-codemirror"; +import { Decoration as E, EditorView as O } from "@codemirror/view"; +import { StateEffect as j, StateField as U } from "@codemirror/state"; +import { javascript as W } from "@codemirror/lang-javascript"; import { tags as r } from "@lezer/highlight"; -import { createTheme as W } from "@uiw/codemirror-themes"; -import { useInView as X } from "react-hook-inview"; -import { webaudioOutput as Y, getAudioContext as Z } from "@strudel.cycles/webaudio"; -import { repl as ee } from "@strudel.cycles/core"; -import { transpiler as te } from "@strudel.cycles/transpiler"; -const re = W({ +import { createTheme as X } from "@uiw/codemirror-themes"; +import { useInView as Y } from "react-hook-inview"; +import { webaudioOutput as Z, getAudioContext as ee } from "@strudel.cycles/webaudio"; +import { repl as te } from "@strudel.cycles/core"; +import { transpiler as re } from "@strudel.cycles/transpiler"; +const oe = X({ theme: "dark", settings: { background: "#222", @@ -42,14 +42,14 @@ const re = W({ { tag: r.invalid, color: "#ffffff" } ] }); -const L = O.define(), oe = j.define({ +const T = j.define(), ne = U.define({ create() { return E.none; }, update(e, t) { try { for (let o of t.effects) - if (o.is(L)) + if (o.is(T)) if (o.value) { const a = E.mark({ attributes: { style: "background-color: #FFCA2880" } }); e = E.set([a.range(0, t.newDoc.length)]); @@ -60,12 +60,12 @@ const L = O.define(), oe = j.define({ return console.warn("flash error", o), e; } }, - provide: (e) => K.decorations.from(e) -}), ne = (e) => { - e.dispatch({ effects: L.of(!0) }), setTimeout(() => { - e.dispatch({ effects: L.of(!1) }); + provide: (e) => O.decorations.from(e) +}), ae = (e) => { + e.dispatch({ effects: T.of(!0) }), setTimeout(() => { + e.dispatch({ effects: T.of(!1) }); }, 200); -}, A = O.define(), ae = j.define({ +}, A = j.define(), se = U.define({ create() { return E.none; }, @@ -74,11 +74,11 @@ const L = O.define(), oe = j.define({ for (let o of t.effects) if (o.is(A)) { const a = o.value.map( - (s) => (s.context.locations || []).map(({ start: u, end: l }) => { + (s) => (s.context.locations || []).map(({ start: m, end: l }) => { const d = s.context.color || "#FFCA28"; - let c = t.newDoc.line(u.line).from + u.column, i = t.newDoc.line(l.line).from + l.column; - const f = t.newDoc.length; - return c > f || i > f ? void 0 : E.mark({ attributes: { style: `outline: 1.5px solid ${d};` } }).range(c, i); + let c = t.newDoc.line(m.line).from + m.column, i = t.newDoc.line(l.line).from + l.column; + const g = t.newDoc.length; + return c > g || i > g ? void 0 : E.mark({ attributes: { style: `outline: 1.5px solid ${d};` } }).range(c, i); }) ).flat().filter(Boolean) || []; e = E.set(a, !0); @@ -88,9 +88,9 @@ const L = O.define(), oe = j.define({ return E.set([]); } }, - provide: (e) => K.decorations.from(e) -}), se = [Q(), re, ae, oe]; -function ce({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, options: s, editorDidMount: u }) { + provide: (e) => O.decorations.from(e) +}), ce = [W(), oe, se, ne]; +function ie({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, options: s, editorDidMount: m }) { const l = N( (i) => { t?.(i); @@ -107,27 +107,27 @@ function ce({ value: e, onChange: t, onViewChanged: o, onSelectionChange: a, opt }, [a] ); - return /* @__PURE__ */ n.createElement(n.Fragment, null, /* @__PURE__ */ n.createElement(J, { + return /* @__PURE__ */ n.createElement(n.Fragment, null, /* @__PURE__ */ n.createElement(Q, { value: e, onChange: l, onCreateEditor: d, onUpdate: c, - extensions: se + extensions: ce })); } -function S(...e) { +function B(...e) { return e.filter(Boolean).join(" "); } -function ie({ view: e, pattern: t, active: o, getTime: a }) { - const s = x([]), u = x(); +function le({ view: e, pattern: t, active: o, getTime: a }) { + const s = x([]), m = x(); R(() => { if (e) if (t && o) { let d = function() { try { - const c = a(), f = [Math.max(u.current || c, c - 1 / 10, 0), c + 1 / 60]; - u.current = f[1], s.current = s.current.filter((m) => m.whole.end > c); - const v = t.queryArc(...f).filter((m) => m.hasOnset()); + const c = a(), g = [Math.max(m.current || c, c - 1 / 10, 0), c + 1 / 60]; + m.current = g[1], s.current = s.current.filter((p) => p.whole.end > c); + const v = t.queryArc(...g).filter((p) => p.hasOnset()); s.current = s.current.concat(v), e.dispatch({ effects: A.of(s.current) }); } catch { e.dispatch({ effects: A.of([]) }); @@ -141,16 +141,16 @@ function ie({ view: e, pattern: t, active: o, getTime: a }) { s.current = [], e.dispatch({ effects: A.of([]) }); }, [t, o, e]); } -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: le, - header: de, - buttons: ue, - button: fe, - buttonDisabled: me, - error: ge, - body: pe +const de = "_container_3i85k_1", ue = "_header_3i85k_5", fe = "_buttons_3i85k_9", me = "_button_3i85k_9", ge = "_buttonDisabled_3i85k_17", pe = "_error_3i85k_21", he = "_body_3i85k_25", b = { + container: de, + header: ue, + buttons: fe, + button: me, + buttonDisabled: ge, + error: pe, + body: he }; -function B({ type: e }) { +function q({ type: e }) { return /* @__PURE__ */ n.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "sc-h-5 sc-w-5", @@ -174,97 +174,100 @@ function B({ type: e }) { }) }[e]); } -function he({ +function ve({ defaultOutput: e, interval: t, getTime: o, evalOnMount: a = !1, initialCode: s = "", - autolink: u = !1, + autolink: m = !1, beforeEval: l, afterEval: d, - onEvalError: c + onEvalError: c, + onToggle: i }) { - 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( - () => ee({ + const [g, v] = w(), [p, D] = w(), [h, k] = w(s), [y, P] = w(h), [z, _] = w(), [C, H] = w(!1), F = h !== y, { scheduler: u, evaluate: L, start: $, stop: G, pause: J } = I( + () => te({ interval: t, defaultOutput: e, - onSchedulerError: f, - onEvalError: (p) => { - m(p), c?.(p); + onSchedulerError: v, + onEvalError: (f) => { + D(f), c?.(f); }, getTime: o, - transpiler: te, - beforeEval: ({ code: p }) => { - D(p), l?.(); + transpiler: re, + beforeEval: ({ code: f }) => { + k(f), l?.(); }, - afterEval: ({ pattern: p, code: V }) => { - C(V), z(p), m(), f(), u && (window.location.hash = "#" + encodeURIComponent(btoa(V))), d?.(); + afterEval: ({ pattern: f, code: S }) => { + P(S), _(f), D(), v(), m && (window.location.hash = "#" + encodeURIComponent(btoa(S))), d?.(); }, - onToggle: (p) => F(p) + onToggle: (f) => { + H(f), i?.(f); + } }), [e, t, o] - ), M = N(async (p = !0) => g(h, p), [g, h]), T = x(); + ), M = N(async (f = !0) => L(h, f), [L, h]), V = x(); return R(() => { - !T.current && a && h && (T.current = !0, M()); + !V.current && a && h && (V.current = !0, M()); }, [M, a, h]), R(() => () => { - w.stop(); - }, [w]), { + u.stop(); + }, [u]), { code: h, - setCode: D, - error: i || v, - schedulerError: i, - scheduler: w, - evalError: v, - evaluate: g, + setCode: k, + error: g || p, + schedulerError: g, + scheduler: u, + evalError: p, + evaluate: L, activateCode: M, - activeCode: _, - isDirty: H, - pattern: P, - started: k, - start: U, - stop: $, - pause: G, + activeCode: y, + isDirty: F, + pattern: z, + started: C, + start: $, + stop: G, + pause: J, togglePlay: async () => { - k ? w.pause() : await M(); + C ? u.pause() : await M(); } }; } -const ve = () => Z().currentTime; -function Re({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) { +const be = () => ee().currentTime; +function Pe({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) { const { code: s, - setCode: u, + setCode: m, evaluate: l, activateCode: d, error: c, isDirty: i, - activeCode: f, + activeCode: g, pattern: v, - started: m, - scheduler: h, - togglePlay: D, - stop: _ - } = he({ + started: p, + scheduler: D, + togglePlay: h, + stop: k + } = ve({ initialCode: e, - defaultOutput: Y, - getTime: ve - }), [C, P] = y(), [z, k] = X({ + defaultOutput: Z, + getTime: be + }), [y, P] = w(), [z, _] = Y({ threshold: 0.01 - }), F = x(), H = q(() => ((k || !t) && (F.current = !0), k || F.current), [k, t]); - return ie({ - view: C, + }), C = x(), H = I(() => ((_ || !t) && (C.current = !0), _ || C.current), [_, t]); + return le({ + view: y, pattern: v, - active: m && !f?.includes("strudel disable-highlighting"), - getTime: () => h.getPhase() - }), I(() => { + active: p && !g?.includes("strudel disable-highlighting"), + getTime: () => D.getPhase() + }), K(() => { if (a) { - const w = async (g) => { - (g.ctrlKey || g.altKey) && (g.code === "Enter" ? (g.preventDefault(), ne(C), await d()) : g.code === "Period" && (_(), g.preventDefault())); + const F = async (u) => { + (u.ctrlKey || u.altKey) && (u.code === "Enter" ? (u.preventDefault(), ae(y), await d()) : u.code === "Period" && (k(), u.preventDefault())); }; - return window.addEventListener("keydown", w, !0), () => window.removeEventListener("keydown", w, !0); + return window.addEventListener("keydown", F, !0), () => window.removeEventListener("keydown", F, !0); } - }, [a, v, s, l, _, C]), /* @__PURE__ */ n.createElement("div", { + }, [a, v, s, l, k, y]), /* @__PURE__ */ n.createElement("div", { className: b.container, ref: z }, /* @__PURE__ */ n.createElement("div", { @@ -272,36 +275,36 @@ function Re({ tune: e, hideOutsideView: t = !1, init: o, enableKeyboard: a }) { }, /* @__PURE__ */ n.createElement("div", { className: b.buttons }, /* @__PURE__ */ n.createElement("button", { - className: S(b.button, m ? "sc-animate-pulse" : ""), - onClick: () => D() - }, /* @__PURE__ */ n.createElement(B, { - type: m ? "pause" : "play" + className: B(b.button, p ? "sc-animate-pulse" : ""), + onClick: () => h() + }, /* @__PURE__ */ n.createElement(q, { + type: p ? "pause" : "play" })), /* @__PURE__ */ n.createElement("button", { - className: S(i ? b.button : b.buttonDisabled), + className: B(i ? b.button : b.buttonDisabled), onClick: () => d() - }, /* @__PURE__ */ n.createElement(B, { + }, /* @__PURE__ */ n.createElement(q, { type: "refresh" }))), c && /* @__PURE__ */ n.createElement("div", { className: b.error }, c.message)), /* @__PURE__ */ n.createElement("div", { className: b.body - }, H && /* @__PURE__ */ n.createElement(ce, { + }, H && /* @__PURE__ */ n.createElement(ie, { value: s, - onChange: u, + onChange: m, onViewChanged: P }))); } -function Pe(e) { +function ze(e) { return R(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((t) => window.postMessage(t, "*"), []); } -const ze = (e) => I(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]); +const He = (e) => K(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]); export { - ce as CodeMirror, - Re as MiniRepl, - S as cx, - ne as flash, - ie as useHighlighting, - ze as useKeydown, - Pe as usePostMessage, - he as useStrudel + ie as CodeMirror, + Pe as MiniRepl, + B as cx, + ae as flash, + le as useHighlighting, + He as useKeydown, + ze as usePostMessage, + ve as useStrudel }; diff --git a/packages/react/src/hooks/useStrudel.mjs b/packages/react/src/hooks/useStrudel.mjs index 097ca1aa..64668105 100644 --- a/packages/react/src/hooks/useStrudel.mjs +++ b/packages/react/src/hooks/useStrudel.mjs @@ -12,6 +12,7 @@ function useStrudel({ beforeEval, afterEval, onEvalError, + onToggle, }) { // scheduler const [schedulerError, setSchedulerError] = useState(); @@ -49,7 +50,10 @@ function useStrudel({ } afterEval?.(); }, - onToggle: (v) => setStarted(v), + onToggle: (v) => { + setStarted(v); + onToggle?.(v); + }, }), [defaultOutput, interval, getTime], ); diff --git a/repl/package.json b/repl/package.json index 4fec4138..b8ed1757 100644 --- a/repl/package.json +++ b/repl/package.json @@ -3,6 +3,7 @@ "private": true, "version": "0.0.0", "scripts": { + "predev": "cd ${PWD}/../tutorial/ && npm run render", "dev": "vite --host", "start": "vite", "build": "vite build", diff --git a/repl/src/App.jsx b/repl/src/App.jsx index 5adec2d4..5149027f 100644 --- a/repl/src/App.jsx +++ b/repl/src/App.jsx @@ -107,7 +107,7 @@ export const AppContext = createContext(); function App() { const [view, setView] = useState(); // codemirror view const [lastShared, setLastShared] = useState(); - const [activeFooter, setActiveFooter] = useState('console'); + const [activeFooter, setActiveFooter] = useState(''); const { code, setCode, scheduler, evaluate, activateCode, isDirty, activeCode, pattern, started, stop } = useStrudel({ initialCode: '// LOADING', @@ -118,6 +118,7 @@ function App() { cleanupUi(); cleanupDraw(); }, + onToggle: (play) => !play && cleanupDraw(false), }); // init code diff --git a/repl/src/Footer.jsx b/repl/src/Footer.jsx index 07167b95..23df2cb4 100644 --- a/repl/src/Footer.jsx +++ b/repl/src/Footer.jsx @@ -4,6 +4,7 @@ import { cx } from '@strudel.cycles/react'; import { nanoid } from 'nanoid'; import React, { useContext, useCallback, useLayoutEffect, useRef, useState } from 'react'; import { useEvent, loadedSamples, AppContext } from './App'; +import { Reference } from './Reference'; export function Footer() { // const [activeFooter, setActiveFooter] = useState('console'); @@ -67,10 +68,11 @@ export function Footer() { return ( diff --git a/repl/src/Reference.jsx b/repl/src/Reference.jsx new file mode 100644 index 00000000..6fc326e2 --- /dev/null +++ b/repl/src/Reference.jsx @@ -0,0 +1,39 @@ +import jsdocJson from '../../doc.json'; +console.log('jsdocJson', jsdocJson); +const visibleFunctions = jsdocJson.docs + .filter(({ name, description }) => name && !name.startsWith('_') && !!description) + .sort((a, b) => a.meta.filename.localeCompare(b.meta.filename) + a.name.localeCompare(b.name)); + +export function Reference() { + return ( +
+
+ {visibleFunctions.map((entry, i) => ( + + {entry.name} {/* {entry.meta.filename} */} + + ))} +
+
+
+

API Reference

+

+ This is the long list functions you can use! Remember that you don't need to remember all of those and that + you can already make music with a small set of functions! +

+ {visibleFunctions.map((entry, i) => ( +
+

{entry.name}

+ {entry.meta.filename} + +

+ {entry.examples?.map((example, j) => ( +
{example}
+ ))} +
+ ))} +
+
+
+ ); +}