From a7562c3803f776a3c680927c3d02120968921992 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 18 Aug 2022 16:15:44 +0200 Subject: [PATCH] add useKeydown helper --- packages/react/dist/index.cjs.js | 4 ++-- packages/react/dist/index.es.js | 29 +++++++++++++++++-------- packages/react/src/hooks/useKeydown.mjs | 10 +++++++++ packages/react/src/index.js | 1 + 4 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 packages/react/src/hooks/useKeydown.mjs diff --git a/packages/react/dist/index.cjs.js b/packages/react/dist/index.cjs.js index e7af1c0d..dbad2b85 100644 --- a/packages/react/dist/index.cjs.js +++ b/packages/react/dist/index.cjs.js @@ -1,3 +1,3 @@ -"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var React=require("react"),_CodeMirror=require("@uiw/react-codemirror"),view=require("@codemirror/view"),state=require("@codemirror/state"),langJavascript=require("@codemirror/lang-javascript"),highlight=require("@lezer/highlight"),codemirrorThemes=require("@uiw/codemirror-themes"),reactHookInview=require("react-hook-inview"),_eval=require("@strudel.cycles/eval"),util_mjs=require("@strudel.cycles/core/util.mjs"),tone=require("@strudel.cycles/tone"),core=require("@strudel.cycles/core"),midi=require("@strudel.cycles/midi");function _interopDefaultLegacy(e){return e&&typeof e=="object"&&"default"in e?e:{default:e}}var React__default=_interopDefaultLegacy(React),_CodeMirror__default=_interopDefaultLegacy(_CodeMirror),strudelTheme=codemirrorThemes.createTheme({theme:"dark",settings:{background:"#222",foreground:"#75baff",caret:"#ffcc00",selection:"rgba(128, 203, 196, 0.5)",selectionMatch:"#036dd626",lineHighlight:"#8a91991a",gutterBackground:"transparent",gutterForeground:"#676e95"},styles:[{tag:highlight.tags.keyword,color:"#c792ea"},{tag:highlight.tags.operator,color:"#89ddff"},{tag:highlight.tags.special(highlight.tags.variableName),color:"#eeffff"},{tag:highlight.tags.typeName,color:"#f07178"},{tag:highlight.tags.atom,color:"#f78c6c"},{tag:highlight.tags.number,color:"#ff5370"},{tag:highlight.tags.definition(highlight.tags.variableName),color:"#82aaff"},{tag:highlight.tags.string,color:"#c3e88d"},{tag:highlight.tags.special(highlight.tags.string),color:"#f07178"},{tag:highlight.tags.comment,color:"#7d8799"},{tag:highlight.tags.variableName,color:"#f07178"},{tag:highlight.tags.tagName,color:"#ff5370"},{tag:highlight.tags.bracket,color:"#a2a1a4"},{tag:highlight.tags.meta,color:"#ffcb6b"},{tag:highlight.tags.attributeName,color:"#c792ea"},{tag:highlight.tags.propertyName,color:"#c792ea"},{tag:highlight.tags.className,color:"#decb6b"},{tag:highlight.tags.invalid,color:"#ffffff"}]}),style="";const setFlash=state.StateEffect.define(),flashField=state.StateField.define({create(){return view.Decoration.none},update(e,t){try{for(let r of t.effects)if(r.is(setFlash))if(r.value){const c=view.Decoration.mark({attributes:{style:"background-color: #FFCA2880"}});e=view.Decoration.set([c.range(0,t.newDoc.length)])}else e=view.Decoration.set([]);return e}catch(r){return console.warn("flash error",r),e}},provide:e=>view.EditorView.decorations.from(e)}),flash=e=>{e.dispatch({effects:setFlash.of(!0)}),setTimeout(()=>{e.dispatch({effects:setFlash.of(!1)})},200)},setHighlights=state.StateEffect.define(),highlightField=state.StateField.define({create(){return view.Decoration.none},update(e,t){try{for(let r of t.effects)if(r.is(setHighlights)){const c=r.value.map(l=>(l.context.locations||[]).map(({start:n,end:i})=>{const u=l.context.color||"#FFCA28";let o=t.newDoc.line(n.line).from+n.column,a=t.newDoc.line(i.line).from+i.column;const p=t.newDoc.length;return o>p||a>p?void 0:view.Decoration.mark({attributes:{style:`outline: 1.5px solid ${u};`}}).range(o,a)})).flat().filter(Boolean)||[];e=view.Decoration.set(c,!0)}return e}catch{return view.Decoration.set([])}},provide:e=>view.EditorView.decorations.from(e)}),extensions=[langJavascript.javascript(),strudelTheme,highlightField,flashField];function CodeMirror({value:e,onChange:t,onViewChanged:r,onSelectionChange:c,options:l,editorDidMount:n}){const i=React.useCallback(a=>{t?.(a)},[t]),u=React.useCallback(a=>{r?.(a)},[r]),o=React.useCallback(a=>{a.selectionSet&&c&&c?.(a.state.selection)},[c]);return React__default.default.createElement(React__default.default.Fragment,null,React__default.default.createElement(_CodeMirror__default.default,{value:e,onChange:i,onCreateEditor:u,onUpdate:o,extensions}))}function useCycle(e){const{onEvent:t,onQuery:r,onSchedule:c,ready:l=!0,onDraw:n}=e,[i,u]=React.useState(!1),o=1,a=()=>Math.floor(tone.Tone.getTransport().seconds/o),p=(h=a())=>{const _=new core.TimeSpan(h,h+1),M=r?.(new core.State(_))||[];c?.(M,h);const C=_.begin.valueOf();tone.Tone.getTransport().cancel(C);const v=(h+1)*o-.5,E=Math.max(tone.Tone.getTransport().seconds,v)+.1;tone.Tone.getTransport().schedule(()=>{p(h+1)},E),M?.filter(f=>f.part.begin.equals(f.whole?.begin)).forEach(f=>{tone.Tone.getTransport().schedule(m=>{t(m,f,tone.Tone.getContext().currentTime),tone.Tone.Draw.schedule(()=>{n?.(m,f)},m)},f.part.begin.valueOf())})};React.useEffect(()=>{l&&p()},[t,c,r,n,l]);const b=async()=>{u(!0),await tone.Tone.start(),tone.Tone.getTransport().start("+0.1")},y=()=>{tone.Tone.getTransport().pause(),u(!1)};return{start:b,stop:y,onEvent:t,started:i,setStarted:u,toggle:()=>i?y():b(),query:p,activeCycle:a}}function usePostMessage(e){return React.useEffect(()=>(window.addEventListener("message",e),()=>window.removeEventListener("message",e)),[e]),React.useCallback(t=>window.postMessage(t,"*"),[])}let s4=()=>Math.floor((1+Math.random())*65536).toString(16).substring(1);const generateHash=e=>encodeURIComponent(btoa(e));function useRepl({tune:e,defaultSynth:t,autolink:r=!0,onEvent:c,onDraw:l}){const n=React.useMemo(()=>s4(),[]),[i,u]=React.useState(e),[o,a]=React.useState(),[p,b]=React.useState(""),[y,R]=React.useState(),[h,_]=React.useState(!1),[M,C]=React.useState(""),[v,E]=React.useState(),f=React.useMemo(()=>i!==o||y,[i,o,y]),m=React.useCallback(d=>b(s=>s+`${s?` +"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var t=require("react"),oe=require("@uiw/react-codemirror"),k=require("@codemirror/view"),H=require("@codemirror/state"),se=require("@codemirror/lang-javascript"),d=require("@lezer/highlight"),ne=require("@uiw/codemirror-themes"),ce=require("react-hook-inview"),U=require("@strudel.cycles/eval"),ie=require("@strudel.cycles/core/util.mjs"),E=require("@strudel.cycles/tone"),V=require("@strudel.cycles/core"),C=require("@strudel.cycles/midi");function $(e){return e&&typeof e=="object"&&"default"in e?e:{default:e}}var m=$(t),le=$(oe),ue=ne.createTheme({theme:"dark",settings:{background:"#222",foreground:"#75baff",caret:"#ffcc00",selection:"rgba(128, 203, 196, 0.5)",selectionMatch:"#036dd626",lineHighlight:"#8a91991a",gutterBackground:"transparent",gutterForeground:"#676e95"},styles:[{tag:d.tags.keyword,color:"#c792ea"},{tag:d.tags.operator,color:"#89ddff"},{tag:d.tags.special(d.tags.variableName),color:"#eeffff"},{tag:d.tags.typeName,color:"#f07178"},{tag:d.tags.atom,color:"#f78c6c"},{tag:d.tags.number,color:"#ff5370"},{tag:d.tags.definition(d.tags.variableName),color:"#82aaff"},{tag:d.tags.string,color:"#c3e88d"},{tag:d.tags.special(d.tags.string),color:"#f07178"},{tag:d.tags.comment,color:"#7d8799"},{tag:d.tags.variableName,color:"#f07178"},{tag:d.tags.tagName,color:"#ff5370"},{tag:d.tags.bracket,color:"#a2a1a4"},{tag:d.tags.meta,color:"#ffcb6b"},{tag:d.tags.attributeName,color:"#c792ea"},{tag:d.tags.propertyName,color:"#c792ea"},{tag:d.tags.className,color:"#decb6b"},{tag:d.tags.invalid,color:"#ffffff"}]});const z=H.StateEffect.define(),de=H.StateField.define({create(){return k.Decoration.none},update(e,a){try{for(let o of a.effects)if(o.is(z))if(o.value){const i=k.Decoration.mark({attributes:{style:"background-color: #FFCA2880"}});e=k.Decoration.set([i.range(0,a.newDoc.length)])}else e=k.Decoration.set([]);return e}catch(o){return console.warn("flash error",o),e}},provide:e=>k.EditorView.decorations.from(e)}),Q=e=>{e.dispatch({effects:z.of(!0)}),setTimeout(()=>{e.dispatch({effects:z.of(!1)})},200)},A=H.StateEffect.define(),fe=H.StateField.define({create(){return k.Decoration.none},update(e,a){try{for(let o of a.effects)if(o.is(A)){const i=o.value.map(u=>(u.context.locations||[]).map(({start:c,end:l})=>{const f=u.context.color||"#FFCA28";let s=a.newDoc.line(c.line).from+c.column,r=a.newDoc.line(l.line).from+l.column;const b=a.newDoc.length;return s>b||r>b?void 0:k.Decoration.mark({attributes:{style:`outline: 1.5px solid ${f};`}}).range(s,r)})).flat().filter(Boolean)||[];e=k.Decoration.set(i,!0)}return e}catch{return k.Decoration.set([])}},provide:e=>k.EditorView.decorations.from(e)}),ge=[se.javascript(),ue,fe,de];function J({value:e,onChange:a,onViewChanged:o,onSelectionChange:i,options:u,editorDidMount:c}){const l=t.useCallback(r=>{a?.(r)},[a]),f=t.useCallback(r=>{o?.(r)},[o]),s=t.useCallback(r=>{r.selectionSet&&i&&i?.(r.state.selection)},[i]);return m.default.createElement(m.default.Fragment,null,m.default.createElement(le.default,{value:e,onChange:l,onCreateEditor:f,onUpdate:s,extensions:ge}))}function G(e){const{onEvent:a,onQuery:o,onSchedule:i,ready:u=!0,onDraw:c}=e,[l,f]=t.useState(!1),s=1,r=()=>Math.floor(E.Tone.getTransport().seconds/s),b=(v=r())=>{const D=new V.TimeSpan(v,v+1),N=o?.(new V.State(D))||[];i?.(N,v);const F=D.begin.valueOf();E.Tone.getTransport().cancel(F);const M=(v+1)*s-.5,R=Math.max(E.Tone.getTransport().seconds,M)+.1;E.Tone.getTransport().schedule(()=>{b(v+1)},R),N?.filter(y=>y.part.begin.equals(y.whole?.begin)).forEach(y=>{E.Tone.getTransport().schedule(w=>{a(w,y,E.Tone.getContext().currentTime),E.Tone.Draw.schedule(()=>{c?.(w,y)},w)},y.part.begin.valueOf())})};t.useEffect(()=>{u&&b()},[a,i,o,c,u]);const h=async()=>{f(!0),await E.Tone.start(),E.Tone.getTransport().start("+0.1")},_=()=>{E.Tone.getTransport().pause(),f(!1)};return{start:h,stop:_,onEvent:a,started:l,setStarted:f,toggle:()=>l?_():h(),query:b,activeCycle:r}}function X(e){return t.useEffect(()=>(window.addEventListener("message",e),()=>window.removeEventListener("message",e)),[e]),t.useCallback(a=>window.postMessage(a,"*"),[])}let me=()=>Math.floor((1+Math.random())*65536).toString(16).substring(1);const he=e=>encodeURIComponent(btoa(e));function Y({tune:e,defaultSynth:a,autolink:o=!0,onEvent:i,onDraw:u}){const c=t.useMemo(()=>me(),[]),[l,f]=t.useState(e),[s,r]=t.useState(),[b,h]=t.useState(""),[_,T]=t.useState(),[v,D]=t.useState(!1),[N,F]=t.useState(""),[M,R]=t.useState(),y=t.useMemo(()=>l!==s||_,[l,s,_]),w=t.useCallback(g=>h(n=>n+`${n?` -`:""}${d}`),[]),k=React.useMemo(()=>{if(o&&!o.includes("strudel disable-highlighting"))return(d,s)=>l?.(d,s,o)},[o,l]),T=React.useMemo(()=>o&&o.includes("strudel hide-header"),[o]),x=React.useMemo(()=>o&&o.includes("strudel hide-console"),[o]),g=useCycle({onDraw:k,onEvent:React.useCallback((d,s,N)=>{try{c?.(s),s.context.logs?.length&&s.context.logs.forEach(m);const{onTrigger:w,velocity:F}=s.context;if(w)w(d,s,N,1);else if(t){const H=util_mjs.getPlayableNoteValue(s);t.triggerAttackRelease(H,s.duration.valueOf(),d,F)}else throw new Error("no defaultSynth passed to useRepl.")}catch(w){console.warn(w),w.message="unplayable event: "+w?.message,m(w.message)}},[c,m,t]),onQuery:React.useCallback(d=>{try{return v?.query(d)||[]}catch(s){return console.warn(s),s.message="query error: "+s.message,R(s),[]}},[v]),onSchedule:React.useCallback((d,s)=>q(d),[]),ready:!!v&&!!o}),S=usePostMessage(({data:{from:d,type:s}})=>{s==="start"&&d!==n&&(g.setStarted(!1),a(void 0))}),D=React.useCallback(async(d=i)=>{if(o&&!f){R(void 0),g.start();return}try{_(!0);const s=await _eval.evaluate(d);g.start(),S({type:"start",from:n}),E(()=>s.pattern),r&&(window.location.hash="#"+encodeURIComponent(btoa(i))),C(generateHash(i)),R(void 0),a(d),_(!1)}catch(s){s.message="evaluation error: "+s.message,console.warn(s),R(s)}},[o,f,i,g,r,n,S]),q=(d,s)=>{d.length};return{hideHeader:T,hideConsole:x,pending:h,code:i,setCode:u,pattern:v,error:y,cycle:g,setPattern:E,dirty:f,log:p,togglePlay:()=>{g.started?g.stop():D()},setActiveCode:a,activateCode:D,activeCode:o,pushLog:m,hash:M}}function cx(...e){return e.filter(Boolean).join(" ")}let highlights=[],lastEnd;function useHighlighting({view:e,pattern:t,active:r}){React.useEffect(()=>{if(e)if(t&&r){let l=function(){try{const n=tone.Tone.getTransport().seconds,u=[Math.max(lastEnd||n,n-1/10),n+1/60];lastEnd=n+1/60,highlights=highlights.filter(a=>a.whole.end>n);const o=t.queryArc(...u).filter(a=>a.hasOnset());highlights=highlights.concat(o),e.dispatch({effects:setHighlights.of(highlights)})}catch{e.dispatch({effects:setHighlights.of([])})}c=requestAnimationFrame(l)},c=requestAnimationFrame(l);return()=>{cancelAnimationFrame(c)}}else highlights=[],e.dispatch({effects:setHighlights.of([])})},[t,r,e])}var tailwind="";const container="_container_3i85k_1",header="_header_3i85k_5",buttons="_buttons_3i85k_9",button="_button_3i85k_9",buttonDisabled="_buttonDisabled_3i85k_17",error="_error_3i85k_21",body="_body_3i85k_25";var styles={container,header,buttons,button,buttonDisabled,error,body};function Icon({type:e}){return React__default.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:React__default.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:React__default.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:React__default.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 MiniRepl({tune:e,defaultSynth:t,hideOutsideView:r=!1,theme:c,init:l,onEvent:n,enableKeyboard:i}){const{code:u,setCode:o,pattern:a,activeCode:p,activateCode:b,evaluateOnly:y,error:R,cycle:h,dirty:_,togglePlay:M,stop:C}=useRepl({tune:e,defaultSynth:t,autolink:!1,onEvent:n});React.useEffect(()=>{l&&y()},[e,l]);const[v,E]=React.useState(),[f,m]=reactHookInview.useInView({threshold:.01}),k=React.useRef(),T=React.useMemo(()=>((m||!r)&&(k.current=!0),m||k.current),[m,r]);return useHighlighting({view:v,pattern:a,active:h.started&&!p?.includes("strudel disable-highlighting")}),React.useLayoutEffect(()=>{if(i){const x=async g=>{(g.ctrlKey||g.altKey)&&(g.code==="Enter"?(g.preventDefault(),flash(v),await b()):g.code==="Period"&&(h.stop(),g.preventDefault()))};return window.addEventListener("keydown",x,!0),()=>window.removeEventListener("keydown",x,!0)}},[i,a,u,b,h,v]),React__default.default.createElement("div",{className:styles.container,ref:f},React__default.default.createElement("div",{className:styles.header},React__default.default.createElement("div",{className:styles.buttons},React__default.default.createElement("button",{className:cx(styles.button,h.started?"sc-animate-pulse":""),onClick:()=>M()},React__default.default.createElement(Icon,{type:h.started?"pause":"play"})),React__default.default.createElement("button",{className:cx(_?styles.button:styles.buttonDisabled),onClick:()=>b()},React__default.default.createElement(Icon,{type:"refresh"}))),R&&React__default.default.createElement("div",{className:styles.error},R.message)),React__default.default.createElement("div",{className:styles.body},T&&React__default.default.createElement(CodeMirror,{value:u,onChange:o,onViewChanged:E})))}function useScheduler(e,t,r=.1){const[c,l]=React.useState(),n=React.useMemo(()=>new core.Scheduler({interval:r,onTrigger:t,onError:l}),[t,r]);return React.useEffect(()=>{e&&n?.setPattern(e)},[e,n]),{error:c,scheduler:n}}function useEvaluator(code,evalOnMount=!0){const[error,setError]=React.useState(),[activeCode,setActiveCode]=React.useState(code),[pattern,setPattern]=React.useState(),isDirty=code!==activeCode,evaluate=React.useCallback(()=>{try{const _pattern=eval(code);setActiveCode(activeCode),setPattern(_pattern),setError()}catch(e){setError(e),console.warn("eval error",e)}},[code,scheduler]),inited=React.useRef();return React.useEffect(()=>{!inited.current&&evalOnMount&&evaluate(),inited.current=!0},[evaluate,evalOnMount]),{error,evaluate,activeCode,pattern,isDirty}}function useWebMidi(e){const{ready:t,connected:r,disconnected:c}=e,[l,n]=React.useState(!0),[i,u]=React.useState(midi.WebMidi?.outputs||[]);return React.useEffect(()=>{midi.enableWebMidi().then(()=>{midi.WebMidi.addListener("connected",a=>{u([...midi.WebMidi.outputs]),r?.(midi.WebMidi,a)}),midi.WebMidi.addListener("disconnected",a=>{u([...midi.WebMidi.outputs]),c?.(midi.WebMidi,a)}),t?.(midi.WebMidi),n(!1)}).catch(a=>{if(a){console.error(a),console.warn("Web Midi could not be enabled..");return}})},[t,r,c,i]),{loading:l,outputs:i,outputByName:a=>midi.WebMidi.getOutputByName(a)}}exports.CodeMirror=CodeMirror;exports.MiniRepl=MiniRepl;exports.cx=cx;exports.flash=flash;exports.useCycle=useCycle;exports.useEvaluator=useEvaluator;exports.useHighlighting=useHighlighting;exports.usePostMessage=usePostMessage;exports.useRepl=useRepl;exports.useScheduler=useScheduler;exports.useWebMidi=useWebMidi; +`:""}${g}`),[]),L=t.useMemo(()=>{if(s&&!s.includes("strudel disable-highlighting"))return(g,n)=>u?.(g,n,s)},[s,u]),P=t.useMemo(()=>s&&s.includes("strudel hide-header"),[s]),W=t.useMemo(()=>s&&s.includes("strudel hide-console"),[s]),p=G({onDraw:L,onEvent:t.useCallback((g,n,te)=>{try{i?.(n),n.context.logs?.length&&n.context.logs.forEach(w);const{onTrigger:q,velocity:re}=n.context;if(q)q(g,n,te,1);else if(a){const ae=ie.getPlayableNoteValue(n);a.triggerAttackRelease(ae,n.duration.valueOf(),g,re)}else throw new Error("no defaultSynth passed to useRepl.")}catch(q){console.warn(q),q.message="unplayable event: "+q?.message,w(q.message)}},[i,w,a]),onQuery:t.useCallback(g=>{try{return M?.query(g)||[]}catch(n){return console.warn(n),n.message="query error: "+n.message,T(n),[]}},[M]),onSchedule:t.useCallback((g,n)=>ee(g),[]),ready:!!M&&!!s}),j=X(({data:{from:g,type:n}})=>{n==="start"&&g!==c&&(p.setStarted(!1),r(void 0))}),B=t.useCallback(async(g=l)=>{if(s&&!y){T(void 0),p.start();return}try{D(!0);const n=await U.evaluate(g);p.start(),j({type:"start",from:c}),R(()=>n.pattern),o&&(window.location.hash="#"+encodeURIComponent(btoa(l))),F(he(l)),T(void 0),r(g),D(!1)}catch(n){n.message="evaluation error: "+n.message,console.warn(n),T(n)}},[s,y,l,p,o,c,j]),ee=(g,n)=>{g.length};return{hideHeader:P,hideConsole:W,pending:v,code:l,setCode:f,pattern:M,error:_,cycle:p,setPattern:R,dirty:y,log:b,togglePlay:()=>{p.started?p.stop():B()},setActiveCode:r,activateCode:B,activeCode:s,pushLog:w,hash:N}}function O(...e){return e.filter(Boolean).join(" ")}let x=[],I;function Z({view:e,pattern:a,active:o}){t.useEffect(()=>{if(e)if(a&&o){let u=function(){try{const c=E.Tone.getTransport().seconds,f=[Math.max(I||c,c-1/10),c+1/60];I=c+1/60,x=x.filter(r=>r.whole.end>c);const s=a.queryArc(...f).filter(r=>r.hasOnset());x=x.concat(s),e.dispatch({effects:A.of(x)})}catch{e.dispatch({effects:A.of([])})}i=requestAnimationFrame(u)},i=requestAnimationFrame(u);return()=>{cancelAnimationFrame(i)}}else x=[],e.dispatch({effects:A.of([])})},[a,o,e])}const pe="_container_3i85k_1",be="_header_3i85k_5",ve="_buttons_3i85k_9",ye="_button_3i85k_9",we="_buttonDisabled_3i85k_17",Ee="_error_3i85k_21",Me="_body_3i85k_25";var S={container:pe,header:be,buttons:ve,button:ye,buttonDisabled:we,error:Ee,body:Me};function K({type:e}){return m.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:m.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:m.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:m.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 Ce({tune:e,defaultSynth:a,hideOutsideView:o=!1,theme:i,init:u,onEvent:c,enableKeyboard:l}){const{code:f,setCode:s,pattern:r,activeCode:b,activateCode:h,evaluateOnly:_,error:T,cycle:v,dirty:D,togglePlay:N,stop:F}=Y({tune:e,defaultSynth:a,autolink:!1,onEvent:c});t.useEffect(()=>{u&&_()},[e,u]);const[M,R]=t.useState(),[y,w]=ce.useInView({threshold:.01}),L=t.useRef(),P=t.useMemo(()=>((w||!o)&&(L.current=!0),w||L.current),[w,o]);return Z({view:M,pattern:r,active:v.started&&!b?.includes("strudel disable-highlighting")}),t.useLayoutEffect(()=>{if(l){const W=async p=>{(p.ctrlKey||p.altKey)&&(p.code==="Enter"?(p.preventDefault(),Q(M),await h()):p.code==="Period"&&(v.stop(),p.preventDefault()))};return window.addEventListener("keydown",W,!0),()=>window.removeEventListener("keydown",W,!0)}},[l,r,f,h,v,M]),m.default.createElement("div",{className:S.container,ref:y},m.default.createElement("div",{className:S.header},m.default.createElement("div",{className:S.buttons},m.default.createElement("button",{className:O(S.button,v.started?"sc-animate-pulse":""),onClick:()=>N()},m.default.createElement(K,{type:v.started?"pause":"play"})),m.default.createElement("button",{className:O(D?S.button:S.buttonDisabled),onClick:()=>h()},m.default.createElement(K,{type:"refresh"}))),T&&m.default.createElement("div",{className:S.error},T.message)),m.default.createElement("div",{className:S.body},P&&m.default.createElement(J,{value:f,onChange:s,onViewChanged:R})))}function ke(e,a,o=.1){const[i,u]=t.useState(),c=t.useMemo(()=>new V.Scheduler({interval:o,onTrigger:a,onError:u}),[a,o]);return t.useEffect(()=>{e&&c?.setPattern(e)},[e,c]),{error:i,scheduler:c}}function _e({code:e,evalOnMount:a=!0}){const[o,i]=t.useState(),[u,c]=t.useState(e),[l,f]=t.useState(),s=e!==u,r=t.useCallback(async()=>{if(!e){console.log("no code..");return}try{const{pattern:h}=await U.evaluate(e);c(e),f(h),i()}catch(h){i(h),console.warn("eval error",h)}},[e,scheduler]),b=t.useRef();return t.useEffect(()=>{!b.current&&a&&(b.current=!0,r())},[r,a]),{error:o,evaluate:r,activeCode:u,pattern:l,isDirty:s}}const Te=e=>t.useLayoutEffect(()=>(window.addEventListener("keydown",e,!0),()=>window.removeEventListener("keydown",e,!0)),[e]);function Se(e){const{ready:a,connected:o,disconnected:i}=e,[u,c]=t.useState(!0),[l,f]=t.useState(C.WebMidi?.outputs||[]);return t.useEffect(()=>{C.enableWebMidi().then(()=>{C.WebMidi.addListener("connected",r=>{f([...C.WebMidi.outputs]),o?.(C.WebMidi,r)}),C.WebMidi.addListener("disconnected",r=>{f([...C.WebMidi.outputs]),i?.(C.WebMidi,r)}),a?.(C.WebMidi),c(!1)}).catch(r=>{if(r){console.error(r),console.warn("Web Midi could not be enabled..");return}})},[a,o,i,l]),{loading:u,outputs:l,outputByName:r=>C.WebMidi.getOutputByName(r)}}exports.CodeMirror=J;exports.MiniRepl=Ce;exports.cx=O;exports.flash=Q;exports.useCycle=G;exports.useEvaluator=_e;exports.useHighlighting=Z;exports.useKeydown=Te;exports.usePostMessage=X;exports.useRepl=Y;exports.useScheduler=ke;exports.useWebMidi=Se; diff --git a/packages/react/dist/index.es.js b/packages/react/dist/index.es.js index 0a37ff09..a3cf3a3a 100644 --- a/packages/react/dist/index.es.js +++ b/packages/react/dist/index.es.js @@ -564,15 +564,19 @@ function useScheduler(pattern, defaultOutput, interval = 0.1) { return { error, scheduler }; } -function useEvaluator(code, evalOnMount = true) { +function useEvaluator({ code, evalOnMount = true }) { const [error, setError] = useState(); const [activeCode, setActiveCode] = useState(code); const [pattern, setPattern] = useState(); const isDirty = code !== activeCode; - const evaluate = useCallback(() => { + const evaluate$1 = useCallback(async () => { + if (!code) { + console.log("no code.."); + return; + } try { - const _pattern = eval(code); - setActiveCode(activeCode); + const { pattern: _pattern } = await evaluate(code); + setActiveCode(code); setPattern(_pattern); setError(); } catch (err) { @@ -583,13 +587,20 @@ function useEvaluator(code, evalOnMount = true) { const inited = useRef(); useEffect(() => { if (!inited.current && evalOnMount) { - evaluate(); + inited.current = true; + evaluate$1(); } - inited.current = true; - }, [evaluate, evalOnMount]); - return { error, evaluate, activeCode, pattern, isDirty }; + }, [evaluate$1, evalOnMount]); + return { error, evaluate: evaluate$1, activeCode, pattern, isDirty }; } +// set active pattern on ctrl+enter +const useKeydown = (callback) => + useLayoutEffect(() => { + window.addEventListener('keydown', callback, true); + return () => window.removeEventListener('keydown', callback, true); + }, [callback]); + /* useWebMidi.js - Copyright (C) 2022 Strudel contributors - see @@ -629,4 +640,4 @@ function useWebMidi(props) { return { loading, outputs, outputByName }; } -export { CodeMirror, MiniRepl, cx, flash, useCycle, useEvaluator, useHighlighting, usePostMessage, useRepl, useScheduler, useWebMidi }; +export { CodeMirror, MiniRepl, cx, flash, useCycle, useEvaluator, useHighlighting, useKeydown, usePostMessage, useRepl, useScheduler, useWebMidi }; diff --git a/packages/react/src/hooks/useKeydown.mjs b/packages/react/src/hooks/useKeydown.mjs new file mode 100644 index 00000000..88d1cb94 --- /dev/null +++ b/packages/react/src/hooks/useKeydown.mjs @@ -0,0 +1,10 @@ +import { useLayoutEffect } from 'react'; + +// set active pattern on ctrl+enter +const useKeydown = (callback) => + useLayoutEffect(() => { + window.addEventListener('keydown', callback, true); + return () => window.removeEventListener('keydown', callback, true); + }, [callback]); + +export default useKeydown; diff --git a/packages/react/src/index.js b/packages/react/src/index.js index b15bdf05..f39b53d5 100644 --- a/packages/react/src/index.js +++ b/packages/react/src/index.js @@ -8,5 +8,6 @@ export { default as usePostMessage } from './hooks/usePostMessage'; export { default as useRepl } from './hooks/useRepl'; export { default as useScheduler } from './hooks/useScheduler'; export { default as useEvaluator } from './hooks/useEvaluator'; +export { default as useKeydown } from './hooks/useKeydown'; export { default as cx } from './cx'; export { useWebMidi } from './hooks/useWebMidi';