mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 21:58:31 +00:00
react packaging
This commit is contained in:
parent
fb2f63ee26
commit
b8a0559012
31
package-lock.json
generated
31
package-lock.json
generated
@ -6740,6 +6740,7 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
@ -9374,6 +9375,7 @@
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
|
||||
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@ -11100,11 +11102,10 @@
|
||||
"packages/react": {
|
||||
"name": "@strudel.cycles/react",
|
||||
"version": "0.0.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^0.19.0",
|
||||
"react": "^17.0.2",
|
||||
"react-codemirror6": "^1.1.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-hook-inview": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -11113,8 +11114,14 @@
|
||||
"@vitejs/plugin-react": "^1.3.0",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"postcss": "^8.4.13",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"tailwindcss": "^3.0.24",
|
||||
"vite": "^2.9.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
},
|
||||
"packages/react/node_modules/@types/react": {
|
||||
@ -11141,6 +11148,7 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@ -11153,6 +11161,7 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@ -13069,13 +13078,13 @@
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@vitejs/plugin-react": "^1.3.0",
|
||||
"autoprefixer": "10.4.7",
|
||||
"postcss": "8.4.13",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"postcss": "^8.4.13",
|
||||
"react": "^17.0.2",
|
||||
"react-codemirror6": "^1.1.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-hook-inview": "^4.5.0",
|
||||
"tailwindcss": "3.0.24",
|
||||
"tailwindcss": "^3.0.24",
|
||||
"vite": "^2.9.9"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -13103,6 +13112,7 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@ -13112,6 +13122,7 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@ -16579,6 +16590,7 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
@ -17979,13 +17991,13 @@
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@vitejs/plugin-react": "^1.3.0",
|
||||
"autoprefixer": "10.4.7",
|
||||
"postcss": "8.4.13",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"postcss": "^8.4.13",
|
||||
"react": "^17.0.2",
|
||||
"react-codemirror6": "^1.1.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-hook-inview": "^4.5.0",
|
||||
"tailwindcss": "3.0.24",
|
||||
"tailwindcss": "^3.0.24",
|
||||
"vite": "^2.9.9"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -18013,6 +18025,7 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@ -18022,6 +18035,7 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@ -18610,6 +18624,7 @@
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
|
||||
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
|
||||
3
packages/react/.gitignore
vendored
3
packages/react/.gitignore
vendored
@ -8,10 +8,11 @@ pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
!dist
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
|
||||
5
packages/react/dist/index.cjs.js
vendored
Normal file
5
packages/react/dist/index.cjs.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var s=require("react"),re=require("react-codemirror6"),x=require("@codemirror/view"),G=require("@codemirror/state"),oe=require("@codemirror/lang-javascript"),i=require("@codemirror/highlight"),ae=require("react-hook-inview"),Q=require("@strudel.cycles/eval"),ne=require("@strudel.cycles/core/util.mjs"),f=require("@strudel.cycles/tone"),A=require("@strudel.cycles/core");function ce(e){return e&&typeof e=="object"&&"default"in e?e:{default:e}}function M(e){if(e&&e.__esModule)return e;var o=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});return e&&Object.keys(e).forEach(function(a){if(a!=="default"){var t=Object.getOwnPropertyDescriptor(e,a);Object.defineProperty(o,a,t.get?t:{enumerable:!0,get:function(){return e[a]}})}}),o.default=e,Object.freeze(o)}var m=ce(s);const se="#abb2bf",le="#7d8799",ie="#ffffff",ue="#21252b",j="rgba(0, 0, 0, 0.5)",de="transparent",B="#353a42",fe="rgba(128, 203, 196, 0.2)",z="#ffcc00",ge=x.EditorView.theme({"&":{color:"#ffffff",backgroundColor:de,fontSize:"15px","z-index":11},".cm-content":{caretColor:z,lineHeight:"22px"},".cm-line":{background:"#2C323699"},"&.cm-focused .cm-cursor":{borderLeftColor:z},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":{backgroundColor:fe},".cm-panels":{backgroundColor:ue,color:"#ffffff"},".cm-panels.cm-panels-top":{borderBottom:"2px solid black"},".cm-panels.cm-panels-bottom":{borderTop:"2px solid black"},".cm-searchMatch":{backgroundColor:"#72a1ff59",outline:"1px solid #457dff"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:"#6199ff2f"},".cm-activeLine":{backgroundColor:j},".cm-selectionMatch":{backgroundColor:"#aafe661a"},"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket":{backgroundColor:"#bad0f847",outline:"1px solid #515a6b"},".cm-gutters":{background:"#2C323699",color:"#676e95",border:"none"},".cm-activeLineGutter":{backgroundColor:j},".cm-foldPlaceholder":{backgroundColor:"transparent",border:"none",color:"#ddd"},".cm-tooltip":{border:"none",backgroundColor:B},".cm-tooltip .cm-tooltip-arrow:before":{borderTopColor:"transparent",borderBottomColor:"transparent"},".cm-tooltip .cm-tooltip-arrow:after":{borderTopColor:B,borderBottomColor:B},".cm-tooltip-autocomplete":{"& > ul > li[aria-selected]":{backgroundColor:j,color:se}}},{dark:!0}),me=i.HighlightStyle.define([{tag:i.tags.keyword,color:"#c792ea"},{tag:i.tags.operator,color:"#89ddff"},{tag:i.tags.special(i.tags.variableName),color:"#eeffff"},{tag:i.tags.typeName,color:"#f07178"},{tag:i.tags.atom,color:"#f78c6c"},{tag:i.tags.number,color:"#ff5370"},{tag:i.tags.definition(i.tags.variableName),color:"#82aaff"},{tag:i.tags.string,color:"#c3e88d"},{tag:i.tags.special(i.tags.string),color:"#f07178"},{tag:i.tags.comment,color:le},{tag:i.tags.variableName,color:"#f07178"},{tag:i.tags.tagName,color:"#ff5370"},{tag:i.tags.bracket,color:"#a2a1a4"},{tag:i.tags.meta,color:"#ffcb6b"},{tag:i.tags.attributeName,color:"#c792ea"},{tag:i.tags.propertyName,color:"#c792ea"},{tag:i.tags.className,color:"#decb6b"},{tag:i.tags.invalid,color:ie}]),he=[ge,me],D=G.StateEffect.define(),be=G.StateField.define({create(){return x.Decoration.none},update(e,o){try{for(let a of o.effects)a.is(D)&&(e=x.Decoration.set(a.value.flatMap(t=>(t.context.locations||[]).map(({start:r,end:c})=>{const u=t.context.color||"#FFCA28";let g=o.newDoc.line(r.line).from+r.column,l=o.newDoc.line(c.line).from+c.column;const h=o.newDoc.length;return g>h||l>h?void 0:x.Decoration.mark({attributes:{style:`outline: 1px solid ${u}`}}).range(g,l)})).filter(Boolean),!0));return e}catch{return e}},provide:e=>x.EditorView.decorations.from(e)});function U({value:e,onChange:o,onViewChanged:a,onCursor:t,options:r,editorDidMount:c}){return m.default.createElement(m.default.Fragment,null,m.default.createElement(re.CodeMirror,{onViewChange:a,style:{display:"flex",flexDirection:"column",flex:"1 0 auto"},value:e,onChange:o,extensions:[oe.javascript(),he,be]}))}let F;const pe=(e,o)=>{const a=e.getDoc().getValue(),t=W(a,o);F?.clear(),F=e.getDoc().markText(...t,{css:"background-color: #00007720"})};function J(e,o){const a=o.split(`
|
||||
`);let t=0,r=0;for(let c=0;c<e;c++)r===a[t].length?(t++,r=0):r++;return{line:t,ch:r}}function K(e,o){const a=o.split(`
|
||||
`);if(e.line>a.length)return 0;let t=0;for(let r=0;r<e.line;r++)t+=a[r].length+1;return t+=e.ch,t}function W(e,o){const a=K(o,e);let t,r,c,u;for(r=a,t=0;r>0&&(e[r-1]==="("?t--:e[r-1]===")"&&t++,t!==-1);)r--;for(c=r,r=a,t=0;r<e.length&&(e[r]==="("?t--:e[r]===")"&&t++,t!==1);)r++;return u=r,[c,u].map(g=>J(g,e))}var ve=Object.freeze(Object.defineProperty({__proto__:null,setHighlights:D,default:U,markParens:pe,offsetToPosition:J,positionToOffset:K,getCurrentParenArea:W},Symbol.toStringTag,{value:"Module"}));function ye(e){const{onEvent:o,onQuery:a,onSchedule:t,ready:r=!0,onDraw:c}=e,[u,g]=s.useState(!1),l=1,h=()=>Math.floor(f.Tone.getTransport().seconds/l),y=(v=h())=>{const q=new A.TimeSpan(v,v+1),R=a?.(new A.State(q))||[];t?.(R,v);const O=q.begin.valueOf();f.Tone.getTransport().cancel(O);const P=(v+1)*l-.5,N=Math.max(f.Tone.getTransport().seconds,P)+.1;f.Tone.getTransport().schedule(()=>{y(v+1)},N),R?.filter(p=>p.part.begin.equals(p.whole?.begin)).forEach(p=>{f.Tone.getTransport().schedule(w=>{o(w,p,f.Tone.getContext().currentTime),f.Tone.Draw.schedule(()=>{c?.(w,p)},w)},p.part.begin.valueOf())})};s.useEffect(()=>{r&&y()},[o,t,a,c,r]);const E=async()=>{g(!0),await f.Tone.start(),f.Tone.getTransport().start("+0.1")},b=()=>{f.Tone.getTransport().pause(),g(!1)};return{start:E,stop:b,onEvent:o,started:u,setStarted:g,toggle:()=>u?b():E(),query:y,activeCycle:h}}function Ce(e){return s.useEffect(()=>(window.addEventListener("message",e),()=>window.removeEventListener("message",e)),[e]),s.useCallback(o=>window.postMessage(o,"*"),[])}let we=()=>Math.floor((1+Math.random())*65536).toString(16).substring(1);const ke=e=>encodeURIComponent(btoa(e));function Te({tune:e,defaultSynth:o,autolink:a=!0,onEvent:t,onDraw:r}){const c=s.useMemo(()=>we(),[]),[u,g]=s.useState(e),[l,h]=s.useState(),[y,E]=s.useState(""),[b,C]=s.useState(),[v,q]=s.useState(!1),[R,O]=s.useState(""),[P,N]=s.useState(),p=s.useMemo(()=>u!==l||b,[u,l,b]),w=s.useCallback(d=>E(n=>n+`${n?`
|
||||
|
||||
`:""}${d}`),[]),X=s.useMemo(()=>{if(l&&!l.includes("strudel disable-highlighting"))return(d,n)=>r?.(d,n,l)},[l,r]),T=ye({onDraw:X,onEvent:s.useCallback((d,n,Z)=>{try{t?.(n),n.context.logs?.length&&n.context.logs.forEach(w);const{onTrigger:_,velocity:ee}=n.context;if(_)_(d,n,Z,1,n.wholeOrPart().begin.valueOf(),n.duration.valueOf());else if(o){const te=ne.getPlayableNoteValue(n);o.triggerAttackRelease(te,n.duration.valueOf(),d,ee)}else throw new Error("no defaultSynth passed to useRepl.")}catch(_){console.warn(_),_.message="unplayable event: "+_?.message,w(_.message)}},[t,w,o]),onQuery:s.useCallback(d=>{try{return P?.query(d)||[]}catch(n){return console.warn(n),n.message="query error: "+n.message,C(n),[]}},[P]),onSchedule:s.useCallback((d,n)=>Y(d,n),[]),ready:!!P&&!!l}),H=Ce(({data:{from:d,type:n}})=>{n==="start"&&d!==c&&(T.setStarted(!1),h(void 0))}),V=s.useCallback(async(d=u)=>{if(l&&!p){C(void 0),T.start();return}try{q(!0);const n=await Q.evaluate(d);T.start(),H({type:"start",from:c}),N(()=>n.pattern),a&&(window.location.hash="#"+encodeURIComponent(btoa(u))),O(ke(u)),C(void 0),h(d),q(!1)}catch(n){n.message="evaluation error: "+n.message,console.warn(n),C(n)}},[l,p,u,T,a,c,H]),Y=(d,n)=>{d.length};return{pending:v,code:u,setCode:g,pattern:P,error:b,cycle:T,setPattern:N,dirty:p,log:y,togglePlay:()=>{T.started?T.stop():V()},setActiveCode:h,activateCode:V,activeCode:l,pushLog:w,hash:R}}function L(...e){return e.filter(Boolean).join(" ")}let S=[],I;function _e({view:e,pattern:o,active:a}){s.useEffect(()=>{if(e)if(o&&a){let r=function(){try{const c=f.Tone.getTransport().seconds,u=[I||c,c+1/60];I=c+1/60,S=S.filter(l=>l.whole.end>c);const g=o.queryArc(...u).filter(l=>l.hasOnset());S=S.concat(g),e.dispatch({effects:D.of(S)})}catch{e.dispatch({effects:D.of([])})}t=requestAnimationFrame(r)},t=requestAnimationFrame(r);return()=>{cancelAnimationFrame(t)}}else S=[],e.dispatch({effects:D.of([])})},[o,a,e])}const Me="_container_10e1g_1",Ee="_header_10e1g_5",Pe="_buttons_10e1g_9",Se="_button_10e1g_9",qe="_buttonDisabled_10e1g_17",xe="_error_10e1g_21",De="_body_10e1g_25";var k={container:Me,header:Ee,buttons:Pe,button:Se,buttonDisabled:qe,error:xe,body:De};function $({type:e}){return React.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.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.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.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])}Q.evalScope(f.Tone,Promise.resolve().then(function(){return M(require("@strudel.cycles/core"))}),Promise.resolve().then(function(){return M(require("@strudel.cycles/tone"))}),Promise.resolve().then(function(){return M(require("@strudel.cycles/tonal"))}),Promise.resolve().then(function(){return M(require("@strudel.cycles/mini"))}),Promise.resolve().then(function(){return M(require("@strudel.cycles/midi"))}),Promise.resolve().then(function(){return M(require("@strudel.cycles/xen"))}),Promise.resolve().then(function(){return M(require("@strudel.cycles/webaudio"))}));const Re=new f.Tone.PolySynth().chain(new f.Tone.Gain(.5),f.Tone.Destination).set({oscillator:{type:"triangle"},envelope:{release:.01}});function Ne({tune:e}){const{code:o,setCode:a,pattern:t,activateCode:r,error:c,cycle:u,dirty:g,togglePlay:l}=Te({tune:e,defaultSynth:Re,autolink:!1}),[h,y]=s.useState(),[E,b]=ae.useInView({threshold:.01}),C=s.useRef(),v=s.useMemo(()=>(b&&(C.current=!0),b||C.current),[b]);return _e({view:h,pattern:t,active:u.started}),m.default.createElement("div",{className:k.container,ref:E},m.default.createElement("div",{className:k.header},m.default.createElement("div",{className:k.buttons},m.default.createElement("button",{className:L(k.button,u.started?"sc-animate-pulse":""),onClick:()=>l()},m.default.createElement($,{type:u.started?"pause":"play"})),m.default.createElement("button",{className:L(g?k.button:k.buttonDisabled),onClick:()=>r()},m.default.createElement($,{type:"refresh"}))),c&&m.default.createElement("div",{className:k.error},c.message)),m.default.createElement("div",{className:k.body},v&&m.default.createElement(U,{value:o,onChange:a,onViewChanged:y})))}exports.CodeMirror=ve;exports.MiniRepl=Ne;
|
||||
663
packages/react/dist/index.es.js
vendored
Normal file
663
packages/react/dist/index.es.js
vendored
Normal file
@ -0,0 +1,663 @@
|
||||
import React$1, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import { CodeMirror as CodeMirror$1 } from 'react-codemirror6';
|
||||
import { EditorView, Decoration } from '@codemirror/view';
|
||||
import { StateEffect, StateField } from '@codemirror/state';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { HighlightStyle, tags } from '@codemirror/highlight';
|
||||
import { useInView } from 'react-hook-inview';
|
||||
import { evaluate, evalScope } from '@strudel.cycles/eval';
|
||||
import { getPlayableNoteValue } from '@strudel.cycles/core/util.mjs';
|
||||
import { Tone } from '@strudel.cycles/tone';
|
||||
import { TimeSpan, State } from '@strudel.cycles/core';
|
||||
|
||||
/*
|
||||
Credits for color palette:
|
||||
|
||||
Author: Mattia Astorino (http://github.com/equinusocio)
|
||||
Website: https://material-theme.site/
|
||||
*/
|
||||
|
||||
const ivory = '#abb2bf',
|
||||
stone = '#7d8799', // Brightened compared to original to increase contrast
|
||||
invalid = '#ffffff',
|
||||
darkBackground = '#21252b',
|
||||
highlightBackground = 'rgba(0, 0, 0, 0.5)',
|
||||
// background = '#292d3e',
|
||||
background = 'transparent',
|
||||
tooltipBackground = '#353a42',
|
||||
selection = 'rgba(128, 203, 196, 0.2)',
|
||||
cursor = '#ffcc00';
|
||||
|
||||
/// The editor theme styles for Material Palenight.
|
||||
const materialPalenightTheme = EditorView.theme(
|
||||
{
|
||||
// done
|
||||
'&': {
|
||||
color: '#ffffff',
|
||||
backgroundColor: background,
|
||||
fontSize: '15px',
|
||||
'z-index': 11,
|
||||
},
|
||||
|
||||
// done
|
||||
'.cm-content': {
|
||||
caretColor: cursor,
|
||||
lineHeight: '22px',
|
||||
},
|
||||
'.cm-line': {
|
||||
background: '#2C323699',
|
||||
},
|
||||
// done
|
||||
'&.cm-focused .cm-cursor': {
|
||||
borderLeftColor: cursor,
|
||||
},
|
||||
|
||||
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
|
||||
backgroundColor: selection,
|
||||
},
|
||||
|
||||
'.cm-panels': { backgroundColor: darkBackground, color: '#ffffff' },
|
||||
'.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
|
||||
'.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
|
||||
|
||||
// done, use onedarktheme
|
||||
'.cm-searchMatch': {
|
||||
backgroundColor: '#72a1ff59',
|
||||
outline: '1px solid #457dff',
|
||||
},
|
||||
'.cm-searchMatch.cm-searchMatch-selected': {
|
||||
backgroundColor: '#6199ff2f',
|
||||
},
|
||||
|
||||
'.cm-activeLine': { backgroundColor: highlightBackground },
|
||||
'.cm-selectionMatch': { backgroundColor: '#aafe661a' },
|
||||
|
||||
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
||||
backgroundColor: '#bad0f847',
|
||||
outline: '1px solid #515a6b',
|
||||
},
|
||||
|
||||
// done
|
||||
'.cm-gutters': {
|
||||
background: '#2C323699',
|
||||
color: '#676e95',
|
||||
border: 'none',
|
||||
},
|
||||
|
||||
'.cm-activeLineGutter': {
|
||||
backgroundColor: highlightBackground,
|
||||
},
|
||||
|
||||
'.cm-foldPlaceholder': {
|
||||
backgroundColor: 'transparent',
|
||||
border: 'none',
|
||||
color: '#ddd',
|
||||
},
|
||||
|
||||
'.cm-tooltip': {
|
||||
border: 'none',
|
||||
backgroundColor: tooltipBackground,
|
||||
},
|
||||
'.cm-tooltip .cm-tooltip-arrow:before': {
|
||||
borderTopColor: 'transparent',
|
||||
borderBottomColor: 'transparent',
|
||||
},
|
||||
'.cm-tooltip .cm-tooltip-arrow:after': {
|
||||
borderTopColor: tooltipBackground,
|
||||
borderBottomColor: tooltipBackground,
|
||||
},
|
||||
'.cm-tooltip-autocomplete': {
|
||||
'& > ul > li[aria-selected]': {
|
||||
backgroundColor: highlightBackground,
|
||||
color: ivory,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ dark: true },
|
||||
);
|
||||
|
||||
/// The highlighting style for code in the Material Palenight theme.
|
||||
const materialPalenightHighlightStyle = HighlightStyle.define([
|
||||
{ tag: tags.keyword, color: '#c792ea' },
|
||||
{ tag: tags.operator, color: '#89ddff' },
|
||||
{ tag: tags.special(tags.variableName), color: '#eeffff' },
|
||||
{ tag: tags.typeName, color: '#f07178' },
|
||||
{ tag: tags.atom, color: '#f78c6c' },
|
||||
{ tag: tags.number, color: '#ff5370' },
|
||||
{ tag: tags.definition(tags.variableName), color: '#82aaff' },
|
||||
{ tag: tags.string, color: '#c3e88d' },
|
||||
{ tag: tags.special(tags.string), color: '#f07178' },
|
||||
{ tag: tags.comment, color: stone },
|
||||
{ tag: tags.variableName, color: '#f07178' },
|
||||
{ tag: tags.tagName, color: '#ff5370' },
|
||||
{ tag: tags.bracket, color: '#a2a1a4' },
|
||||
{ tag: tags.meta, color: '#ffcb6b' },
|
||||
{ tag: tags.attributeName, color: '#c792ea' },
|
||||
{ tag: tags.propertyName, color: '#c792ea' },
|
||||
{ tag: tags.className, color: '#decb6b' },
|
||||
{ tag: tags.invalid, color: invalid },
|
||||
]);
|
||||
|
||||
/// Extension to enable the Material Palenight theme (both the editor theme and
|
||||
/// the highlight style).
|
||||
// : Extension
|
||||
const materialPalenight = [materialPalenightTheme, materialPalenightHighlightStyle];
|
||||
|
||||
const setHighlights = StateEffect.define();
|
||||
const highlightField = StateField.define({
|
||||
create() {
|
||||
return Decoration.none;
|
||||
},
|
||||
update(highlights, tr) {
|
||||
try {
|
||||
for (let e of tr.effects) {
|
||||
if (e.is(setHighlights)) {
|
||||
highlights = Decoration.set(e.value.flatMap((hap) => (hap.context.locations || []).map(({ start, end }) => {
|
||||
const color = hap.context.color || "#FFCA28";
|
||||
let from = tr.newDoc.line(start.line).from + start.column;
|
||||
let to = tr.newDoc.line(end.line).from + end.column;
|
||||
const l = tr.newDoc.length;
|
||||
if (from > l || to > l) {
|
||||
return;
|
||||
}
|
||||
const mark = Decoration.mark({ attributes: { style: `outline: 1px solid ${color}` } });
|
||||
return mark.range(from, to);
|
||||
})).filter(Boolean), true);
|
||||
}
|
||||
}
|
||||
return highlights;
|
||||
} catch (err) {
|
||||
return highlights;
|
||||
}
|
||||
},
|
||||
provide: (f) => EditorView.decorations.from(f)
|
||||
});
|
||||
function CodeMirror({ value, onChange, onViewChanged, onCursor, options, editorDidMount }) {
|
||||
return /* @__PURE__ */ React$1.createElement(React$1.Fragment, null, /* @__PURE__ */ React$1.createElement(CodeMirror$1, {
|
||||
onViewChange: onViewChanged,
|
||||
style: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flex: "1 0 auto"
|
||||
},
|
||||
value,
|
||||
onChange,
|
||||
extensions: [
|
||||
javascript(),
|
||||
materialPalenight,
|
||||
highlightField
|
||||
]
|
||||
}));
|
||||
}
|
||||
let parenMark;
|
||||
const markParens = (editor, data) => {
|
||||
const v = editor.getDoc().getValue();
|
||||
const marked = getCurrentParenArea(v, data);
|
||||
parenMark?.clear();
|
||||
parenMark = editor.getDoc().markText(...marked, { css: "background-color: #00007720" });
|
||||
};
|
||||
function offsetToPosition(offset, code) {
|
||||
const lines = code.split("\n");
|
||||
let line = 0;
|
||||
let ch = 0;
|
||||
for (let i = 0; i < offset; i++) {
|
||||
if (ch === lines[line].length) {
|
||||
line++;
|
||||
ch = 0;
|
||||
} else {
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
return { line, ch };
|
||||
}
|
||||
function positionToOffset(position, code) {
|
||||
const lines = code.split("\n");
|
||||
if (position.line > lines.length) {
|
||||
return 0;
|
||||
}
|
||||
let offset = 0;
|
||||
for (let i = 0; i < position.line; i++) {
|
||||
offset += lines[i].length + 1;
|
||||
}
|
||||
offset += position.ch;
|
||||
return offset;
|
||||
}
|
||||
function getCurrentParenArea(code, caretPosition) {
|
||||
const caret = positionToOffset(caretPosition, code);
|
||||
let open, i, begin, end;
|
||||
i = caret;
|
||||
open = 0;
|
||||
while (i > 0) {
|
||||
if (code[i - 1] === "(") {
|
||||
open--;
|
||||
} else if (code[i - 1] === ")") {
|
||||
open++;
|
||||
}
|
||||
if (open === -1) {
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
begin = i;
|
||||
i = caret;
|
||||
open = 0;
|
||||
while (i < code.length) {
|
||||
if (code[i] === "(") {
|
||||
open--;
|
||||
} else if (code[i] === ")") {
|
||||
open++;
|
||||
}
|
||||
if (open === 1) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
end = i;
|
||||
return [begin, end].map((o) => offsetToPosition(o, code));
|
||||
}
|
||||
|
||||
var CodeMirror6 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
||||
__proto__: null,
|
||||
setHighlights: setHighlights,
|
||||
'default': CodeMirror,
|
||||
markParens: markParens,
|
||||
offsetToPosition: offsetToPosition,
|
||||
positionToOffset: positionToOffset,
|
||||
getCurrentParenArea: getCurrentParenArea
|
||||
}, Symbol.toStringTag, { value: 'Module' }));
|
||||
|
||||
/*
|
||||
useCycle.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/useCycle.mjs>
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* export declare interface UseCycleProps {
|
||||
onEvent: ToneEventCallback<any>;
|
||||
onQuery?: (state: State) => Hap[];
|
||||
onSchedule?: (events: Hap[], cycle: number) => void;
|
||||
onDraw?: ToneEventCallback<any>;
|
||||
ready?: boolean; // if false, query will not be called on change props
|
||||
} */
|
||||
|
||||
// function useCycle(props: UseCycleProps) {
|
||||
function useCycle(props) {
|
||||
// onX must use useCallback!
|
||||
const { onEvent, onQuery, onSchedule, ready = true, onDraw } = props;
|
||||
const [started, setStarted] = useState(false);
|
||||
const cycleDuration = 1;
|
||||
const activeCycle = () => Math.floor(Tone.getTransport().seconds / cycleDuration);
|
||||
|
||||
// pull events with onQuery + count up to next cycle
|
||||
const query = (cycle = activeCycle()) => {
|
||||
const timespan = new TimeSpan(cycle, cycle + 1);
|
||||
const events = onQuery?.(new State(timespan)) || [];
|
||||
onSchedule?.(events, cycle);
|
||||
// cancel events after current query. makes sure no old events are player for rescheduled cycles
|
||||
// console.log('schedule', cycle);
|
||||
// query next cycle in the middle of the current
|
||||
const cancelFrom = timespan.begin.valueOf();
|
||||
Tone.getTransport().cancel(cancelFrom);
|
||||
// const queryNextTime = (cycle + 1) * cycleDuration - 0.1;
|
||||
const queryNextTime = (cycle + 1) * cycleDuration - 0.5;
|
||||
|
||||
// if queryNextTime would be before current time, execute directly (+0.1 for safety that it won't miss)
|
||||
const t = Math.max(Tone.getTransport().seconds, queryNextTime) + 0.1;
|
||||
Tone.getTransport().schedule(() => {
|
||||
query(cycle + 1);
|
||||
}, t);
|
||||
|
||||
// schedule events for next cycle
|
||||
events
|
||||
?.filter((event) => event.part.begin.equals(event.whole?.begin))
|
||||
.forEach((event) => {
|
||||
Tone.getTransport().schedule((time) => {
|
||||
onEvent(time, event, Tone.getContext().currentTime);
|
||||
Tone.Draw.schedule(() => {
|
||||
// do drawing or DOM manipulation here
|
||||
onDraw?.(time, event);
|
||||
}, time);
|
||||
}, event.part.begin.valueOf());
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
ready && query();
|
||||
}, [onEvent, onSchedule, onQuery, onDraw, ready]);
|
||||
|
||||
const start = async () => {
|
||||
setStarted(true);
|
||||
await Tone.start();
|
||||
Tone.getTransport().start('+0.1');
|
||||
};
|
||||
const stop = () => {
|
||||
Tone.getTransport().pause();
|
||||
setStarted(false);
|
||||
};
|
||||
const toggle = () => (started ? stop() : start());
|
||||
return {
|
||||
start,
|
||||
stop,
|
||||
onEvent,
|
||||
started,
|
||||
setStarted,
|
||||
toggle,
|
||||
query,
|
||||
activeCycle,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
usePostMessage.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/usePostMessage.mjs>
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
function usePostMessage(listener) {
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', listener);
|
||||
return () => window.removeEventListener('message', listener);
|
||||
}, [listener]);
|
||||
return useCallback((data) => window.postMessage(data, '*'), []);
|
||||
}
|
||||
|
||||
/*
|
||||
useRepl.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/useRepl.mjs>
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
let s4 = () => {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
};
|
||||
const generateHash = (code) => encodeURIComponent(btoa(code));
|
||||
|
||||
function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawProp }) {
|
||||
const id = useMemo(() => s4(), []);
|
||||
const [code, setCode] = useState(tune);
|
||||
const [activeCode, setActiveCode] = useState();
|
||||
const [log, setLog] = useState('');
|
||||
const [error, setError] = useState();
|
||||
const [pending, setPending] = useState(false);
|
||||
const [hash, setHash] = useState('');
|
||||
const [pattern, setPattern] = useState();
|
||||
const dirty = useMemo(() => code !== activeCode || error, [code, activeCode, error]);
|
||||
const pushLog = useCallback((message) => setLog((log) => log + `${log ? '\n\n' : ''}${message}`), []);
|
||||
|
||||
// below block allows disabling the highlighting by including "strudel disable-highlighting" in the code (as comment)
|
||||
const onDraw = useMemo(() => {
|
||||
if (activeCode && !activeCode.includes('strudel disable-highlighting')) {
|
||||
return (time, event) => onDrawProp?.(time, event, activeCode);
|
||||
}
|
||||
}, [activeCode, onDrawProp]);
|
||||
|
||||
// cycle hook to control scheduling
|
||||
const cycle = useCycle({
|
||||
onDraw,
|
||||
onEvent: useCallback(
|
||||
(time, event, currentTime) => {
|
||||
try {
|
||||
onEvent?.(event);
|
||||
if (event.context.logs?.length) {
|
||||
event.context.logs.forEach(pushLog);
|
||||
}
|
||||
const { onTrigger, velocity } = event.context;
|
||||
if (!onTrigger) {
|
||||
if (defaultSynth) {
|
||||
const note = getPlayableNoteValue(event);
|
||||
defaultSynth.triggerAttackRelease(note, event.duration.valueOf(), time, velocity);
|
||||
} else {
|
||||
throw new Error('no defaultSynth passed to useRepl.');
|
||||
}
|
||||
/* console.warn('no instrument chosen', event);
|
||||
throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */
|
||||
} else {
|
||||
onTrigger(
|
||||
time,
|
||||
event,
|
||||
currentTime,
|
||||
1 /* cps */,
|
||||
event.wholeOrPart().begin.valueOf(),
|
||||
event.duration.valueOf(),
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
err.message = 'unplayable event: ' + err?.message;
|
||||
pushLog(err.message); // not with setError, because then we would have to setError(undefined) on next playable event
|
||||
}
|
||||
},
|
||||
[onEvent, pushLog, defaultSynth],
|
||||
),
|
||||
onQuery: useCallback(
|
||||
(state) => {
|
||||
try {
|
||||
return pattern?.query(state) || [];
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
err.message = 'query error: ' + err.message;
|
||||
setError(err);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
[pattern],
|
||||
),
|
||||
onSchedule: useCallback((_events, cycle) => logCycle(_events, cycle), []),
|
||||
ready: !!pattern && !!activeCode,
|
||||
});
|
||||
|
||||
const broadcast = usePostMessage(({ data: { from, type } }) => {
|
||||
if (type === 'start' && from !== id) {
|
||||
// console.log('message', from, type);
|
||||
cycle.setStarted(false);
|
||||
setActiveCode(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
const activateCode = useCallback(
|
||||
async (_code = code) => {
|
||||
if (activeCode && !dirty) {
|
||||
setError(undefined);
|
||||
cycle.start();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setPending(true);
|
||||
const parsed = await evaluate(_code);
|
||||
cycle.start();
|
||||
broadcast({ type: 'start', from: id });
|
||||
setPattern(() => parsed.pattern);
|
||||
if (autolink) {
|
||||
window.location.hash = '#' + encodeURIComponent(btoa(code));
|
||||
}
|
||||
setHash(generateHash(code));
|
||||
setError(undefined);
|
||||
setActiveCode(_code);
|
||||
setPending(false);
|
||||
} catch (err) {
|
||||
err.message = 'evaluation error: ' + err.message;
|
||||
console.warn(err);
|
||||
setError(err);
|
||||
}
|
||||
},
|
||||
[activeCode, dirty, code, cycle, autolink, id, broadcast],
|
||||
);
|
||||
// logs events of cycle
|
||||
const logCycle = (_events, cycle) => {
|
||||
if (_events.length) ;
|
||||
};
|
||||
|
||||
const togglePlay = () => {
|
||||
if (!cycle.started) {
|
||||
activateCode();
|
||||
} else {
|
||||
cycle.stop();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
pending,
|
||||
code,
|
||||
setCode,
|
||||
pattern,
|
||||
error,
|
||||
cycle,
|
||||
setPattern,
|
||||
dirty,
|
||||
log,
|
||||
togglePlay,
|
||||
setActiveCode,
|
||||
activateCode,
|
||||
activeCode,
|
||||
pushLog,
|
||||
hash,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
cx.js - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/cx.js>
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
function cx(...classes) {
|
||||
// : Array<string | undefined>
|
||||
return classes.filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
let highlights = []; // actively highlighted events
|
||||
let lastEnd;
|
||||
|
||||
function useHighlighting({ view, pattern, active }) {
|
||||
useEffect(() => {
|
||||
if (view) {
|
||||
if (pattern && active) {
|
||||
let frame = requestAnimationFrame(updateHighlights);
|
||||
|
||||
function updateHighlights() {
|
||||
try {
|
||||
const audioTime = Tone.getTransport().seconds;
|
||||
const span = [lastEnd || audioTime, audioTime + 1 / 60];
|
||||
lastEnd = audioTime + 1 / 60;
|
||||
highlights = highlights.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
|
||||
const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset());
|
||||
highlights = highlights.concat(haps); // add potential new onsets
|
||||
view.dispatch({ effects: setHighlights.of(highlights) }); // highlight all still active + new active haps
|
||||
} catch (err) {
|
||||
// console.log('error in updateHighlights', err);
|
||||
view.dispatch({ effects: setHighlights.of([]) });
|
||||
}
|
||||
frame = requestAnimationFrame(updateHighlights);
|
||||
}
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(frame);
|
||||
};
|
||||
} else {
|
||||
highlights = [];
|
||||
view.dispatch({ effects: setHighlights.of([]) });
|
||||
}
|
||||
}
|
||||
}, [pattern, active, view]);
|
||||
}
|
||||
|
||||
var tailwind = '';
|
||||
|
||||
const container = "_container_10e1g_1";
|
||||
const header = "_header_10e1g_5";
|
||||
const buttons = "_buttons_10e1g_9";
|
||||
const button = "_button_10e1g_9";
|
||||
const buttonDisabled = "_buttonDisabled_10e1g_17";
|
||||
const error = "_error_10e1g_21";
|
||||
const body = "_body_10e1g_25";
|
||||
var styles = {
|
||||
container: container,
|
||||
header: header,
|
||||
buttons: buttons,
|
||||
button: button,
|
||||
buttonDisabled: buttonDisabled,
|
||||
error: error,
|
||||
body: body
|
||||
};
|
||||
|
||||
function Icon({ type }) {
|
||||
return /* @__PURE__ */ React.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__ */ React.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__ */ React.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__ */ React.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"
|
||||
})
|
||||
}[type]);
|
||||
}
|
||||
|
||||
evalScope(Tone, import('@strudel.cycles/core'), import('@strudel.cycles/tone'), import('@strudel.cycles/tonal'), import('@strudel.cycles/mini'), import('@strudel.cycles/midi'), import('@strudel.cycles/xen'), import('@strudel.cycles/webaudio'));
|
||||
const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination).set({
|
||||
oscillator: { type: "triangle" },
|
||||
envelope: {
|
||||
release: 0.01
|
||||
}
|
||||
});
|
||||
function MiniRepl({ tune }) {
|
||||
const { code, setCode, pattern, activateCode, error, cycle, dirty, togglePlay } = useRepl({
|
||||
tune,
|
||||
defaultSynth,
|
||||
autolink: false
|
||||
});
|
||||
const [view, setView] = useState();
|
||||
const [ref, isVisible] = useInView({
|
||||
threshold: 0.01
|
||||
});
|
||||
const wasVisible = useRef();
|
||||
const show = useMemo(() => {
|
||||
if (isVisible) {
|
||||
wasVisible.current = true;
|
||||
}
|
||||
return isVisible || wasVisible.current;
|
||||
}, [isVisible]);
|
||||
useHighlighting({ view, pattern, active: cycle.started });
|
||||
return /* @__PURE__ */ React$1.createElement("div", {
|
||||
className: styles.container,
|
||||
ref
|
||||
}, /* @__PURE__ */ React$1.createElement("div", {
|
||||
className: styles.header
|
||||
}, /* @__PURE__ */ React$1.createElement("div", {
|
||||
className: styles.buttons
|
||||
}, /* @__PURE__ */ React$1.createElement("button", {
|
||||
className: cx(styles.button, cycle.started ? "sc-animate-pulse" : ""),
|
||||
onClick: () => togglePlay()
|
||||
}, /* @__PURE__ */ React$1.createElement(Icon, {
|
||||
type: cycle.started ? "pause" : "play"
|
||||
})), /* @__PURE__ */ React$1.createElement("button", {
|
||||
className: cx(dirty ? styles.button : styles.buttonDisabled),
|
||||
onClick: () => activateCode()
|
||||
}, /* @__PURE__ */ React$1.createElement(Icon, {
|
||||
type: "refresh"
|
||||
}))), error && /* @__PURE__ */ React$1.createElement("div", {
|
||||
className: styles.error
|
||||
}, error.message)), /* @__PURE__ */ React$1.createElement("div", {
|
||||
className: styles.body
|
||||
}, show && /* @__PURE__ */ React$1.createElement(CodeMirror, {
|
||||
value: code,
|
||||
onChange: setCode,
|
||||
onViewChanged: setView
|
||||
})));
|
||||
}
|
||||
|
||||
export { CodeMirror6 as CodeMirror, MiniRepl };
|
||||
1
packages/react/dist/style.css
vendored
Normal file
1
packages/react/dist/style.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
*,:before,:after{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}._container_10e1g_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(68 76 87 / var(--tw-bg-opacity))}._header_10e1g_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_10e1g_9{display:flex}._button_10e1g_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_10e1g_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_10e1g_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_10e1g_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_10e1g_25{position:relative;overflow:auto}
|
||||
@ -2,6 +2,18 @@
|
||||
"name": "@strudel.cycles/react",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"main": "dist/index.cjs.js",
|
||||
"module": "dist/index.es.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./dist/index.cjs.js",
|
||||
"import": "./dist/index.es.js"
|
||||
},
|
||||
"./dist/style.css": "./dist/style.css"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import MiniRepl from './components/MiniRepl';
|
||||
import { MiniRepl } from './components/MiniRepl';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
|
||||
function App() {
|
||||
|
||||
31
packages/react/src/components/Icon.tsx
Normal file
31
packages/react/src/components/Icon.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
export function Icon({ type }) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="sc-h-5 sc-w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
{
|
||||
{
|
||||
refresh: (
|
||||
<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: (
|
||||
<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: (
|
||||
<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"
|
||||
/>
|
||||
),
|
||||
}[type]
|
||||
}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@ -1,14 +1,16 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useMemo, useRef } from 'react';
|
||||
import { useInView } from 'react-hook-inview';
|
||||
import { Tone } from '@strudel.cycles/tone';
|
||||
import { evalScope } from '@strudel.cycles/eval';
|
||||
|
||||
import useRepl from '../hooks/useRepl.mjs';
|
||||
import cx from '../cx';
|
||||
import useHighlighting from '../hooks/useHighlighting.mjs';
|
||||
import CodeMirror6 from './CodeMirror6';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import styles from './MiniRepl.module.css';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
import { Tone } from '@strudel.cycles/tone';
|
||||
import { evalScope } from '@strudel.cycles/eval';
|
||||
evalScope(
|
||||
Tone,
|
||||
import('@strudel.cycles/core'),
|
||||
@ -27,85 +29,40 @@ const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destina
|
||||
},
|
||||
});
|
||||
|
||||
// "balanced" | "interactive" | "playback";
|
||||
// Tone.setContext(new Tone.Context({ latencyHint: 'playback', lookAhead: 1 }));
|
||||
function MiniRepl({ tune, maxHeight = 500 }) {
|
||||
export function MiniRepl({ tune }) {
|
||||
const { code, setCode, pattern, activateCode, error, cycle, dirty, togglePlay } = useRepl({
|
||||
tune,
|
||||
defaultSynth,
|
||||
autolink: false,
|
||||
});
|
||||
const lines = code.split('\n').length;
|
||||
const [view, setView] = useState();
|
||||
const [ref, isVisible] = useInView({
|
||||
threshold: 0.01,
|
||||
});
|
||||
const wasVisible = useRef();
|
||||
const show = useMemo(() => {
|
||||
if (isVisible) {
|
||||
wasVisible.current = true;
|
||||
}
|
||||
return isVisible || wasVisible.current;
|
||||
}, [isVisible]);
|
||||
useHighlighting({ view, pattern, active: cycle.started });
|
||||
return (
|
||||
<div className="sc-rounded-md sc-overflow-hidden sc-bg-[#444C57]" ref={ref}>
|
||||
<div className="sc-flex sc-justify-between sc-bg-slate-700 sc-border-t sc-border-slate-500">
|
||||
<div className="sc-flex">
|
||||
<button
|
||||
className={cx(
|
||||
'sc-w-16 sc-flex sc-items-center sc-justify-center sc-p-1 sc-bg-slate-700 sc-border-r sc-border-slate-500 sc-text-white sc-hover:bg-slate-600',
|
||||
cycle.started ? 'sc-animate-pulse' : '',
|
||||
)}
|
||||
onClick={() => togglePlay()}
|
||||
>
|
||||
{!cycle.started ? (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="sc-h-5 sc-w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="sc-h-5 sc-w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
<div className={styles.container} ref={ref}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.buttons}>
|
||||
<button className={cx(styles.button, cycle.started ? 'sc-animate-pulse' : '')} onClick={() => togglePlay()}>
|
||||
<Icon type={cycle.started ? 'pause' : 'play'} />
|
||||
</button>
|
||||
<button
|
||||
className={cx(
|
||||
'sc-w-16 sc-flex sc-items-center sc-justify-center sc-p-1 sc-border-slate-500 sc-hover:bg-slate-600',
|
||||
dirty
|
||||
? 'sc-bg-slate-700 sc-border-r sc-border-slate-500 sc-text-white'
|
||||
: 'sc-bg-slate-600 sc-text-slate-400 sc-cursor-not-allowed',
|
||||
)}
|
||||
onClick={() => activateCode()}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="sc-h-5 sc-w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<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"
|
||||
/>
|
||||
</svg>
|
||||
<button className={cx(dirty ? styles.button : styles.buttonDisabled)} onClick={() => activateCode()}>
|
||||
<Icon type="refresh" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="sc-text-right sc-p-1 sc-text-sm">
|
||||
{error && <span className="sc-text-red-200">{error.message}</span>}
|
||||
</div>{' '}
|
||||
{error && <div className={styles.error}>{error.message}</div>}
|
||||
</div>
|
||||
<div className="sc-flex sc-space-y-0 sc-overflow-auto sc-relative">
|
||||
{isVisible && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
|
||||
<div className={styles.body}>
|
||||
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
|
||||
</div>
|
||||
{/* <div className="bg-slate-700 border-t border-slate-500 content-right pr-2 text-right">
|
||||
<a href={`https://strudel.tidalcycles.org/#${hash}`} className="text-white items-center inline-flex">
|
||||
<span>open in REPL</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
||||
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MiniRepl;
|
||||
|
||||
27
packages/react/src/components/MiniRepl.module.css
Normal file
27
packages/react/src/components/MiniRepl.module.css
Normal file
@ -0,0 +1,27 @@
|
||||
.container {
|
||||
@apply sc-rounded-md sc-overflow-hidden sc-bg-[#444C57];
|
||||
}
|
||||
|
||||
.header {
|
||||
@apply sc-flex sc-justify-between sc-bg-slate-700 sc-border-t sc-border-slate-500;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
@apply sc-flex;
|
||||
}
|
||||
|
||||
.button {
|
||||
@apply sc-cursor-pointer sc-w-16 sc-flex sc-items-center sc-justify-center sc-p-1 sc-bg-slate-700 sc-border-r sc-border-slate-500 sc-text-white hover:sc-bg-slate-600;
|
||||
}
|
||||
|
||||
.buttonDisabled {
|
||||
@apply sc-cursor-pointer sc-w-16 sc-flex sc-items-center sc-justify-center sc-p-1 sc-bg-slate-600 sc-text-slate-400 sc-cursor-not-allowed;
|
||||
}
|
||||
|
||||
.error {
|
||||
@apply sc-text-right sc-p-1 sc-text-sm sc-text-red-200;
|
||||
}
|
||||
|
||||
.body {
|
||||
@apply sc-overflow-auto sc-relative;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// import 'tailwindcss/tailwind.css';
|
||||
|
||||
export * as CodeMirror from './components/CodeMirror6';
|
||||
export * as MiniRepl from './components/MiniRepl';
|
||||
export * from './components/MiniRepl';
|
||||
|
||||
@ -30,6 +30,7 @@ export default defineConfig({
|
||||
'@strudel.cycles/midi',
|
||||
'@strudel.cycles/xen',
|
||||
'@strudel.cycles/serial',
|
||||
'@strudel.cycles/webaudio',
|
||||
'@codemirror/view',
|
||||
'@codemirror/highlight',
|
||||
'@codemirror/state'
|
||||
|
||||
@ -8,6 +8,7 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Tutorial from './tutorial.mdx';
|
||||
import './style.css';
|
||||
import '@strudel.cycles/react/dist/style.css';
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
@ -33,21 +34,3 @@ ReactDOM.render(
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
// for pragmatic reasons, I just added the tailwind classes from MiniRepl here to make them work
|
||||
// TODO: find a way to "export" tailwind classes from package
|
||||
rounded-md overflow-hidden bg-[#444C57]
|
||||
flex justify-between bg-slate-700 border-t border-slate-500
|
||||
flex
|
||||
w-16 flex items-center justify-center p-1 bg-slate-700 border-r border-slate-500 text-white hover:bg-slate-600
|
||||
animate-pulse
|
||||
h-5 w-5
|
||||
w-16 flex items-center justify-center p-1 border-slate-500 hover:bg-slate-600
|
||||
bg-slate-700 border-r border-slate-500 text-white
|
||||
bg-slate-600 text-slate-400 cursor-not-allowed
|
||||
text-right p-1 text-sm
|
||||
text-red-200
|
||||
flex space-y-0 overflow-auto relative
|
||||
*/
|
||||
@ -1,4 +1,4 @@
|
||||
import MiniRepl from '@strudel.cycles/react/src/components/MiniRepl';
|
||||
import { MiniRepl } from '@strudel.cycles/react';
|
||||
|
||||
# What is Strudel?
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user