From 85fee0cc01a0f6c8cdb500cf8032ccaeef1ecf36 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 8 Mar 2024 00:53:48 +0100 Subject: [PATCH 01/30] add claviature package --- packages/claviature/Claviature.jsx | 29 ++++ packages/claviature/index.mjs | 1 + packages/claviature/package.json | 42 ++++++ packages/claviature/vite.config.js | 20 +++ pnpm-lock.yaml | 219 +++++++++++++++++++++++++---- website/package.json | 1 + website/src/repl/util.mjs | 1 + 7 files changed, 286 insertions(+), 27 deletions(-) create mode 100644 packages/claviature/Claviature.jsx create mode 100644 packages/claviature/index.mjs create mode 100644 packages/claviature/package.json create mode 100644 packages/claviature/vite.config.js diff --git a/packages/claviature/Claviature.jsx b/packages/claviature/Claviature.jsx new file mode 100644 index 00000000..50dd2cfd --- /dev/null +++ b/packages/claviature/Claviature.jsx @@ -0,0 +1,29 @@ +import { createSignal, For } from 'solid-js'; +import { customElement } from 'solid-element'; +import { getClaviature } from 'claviature'; +import { Dynamic } from 'solid-js/web'; + +customElement('strudel-claviature', { someProp: 'one', otherProp: 'two' }, (props, { element }) => { + const svg = getClaviature({ + options: { + range: ['A1', 'C4'], + colorize: [{ keys: ['C3', 'E3', 'G3'], color: 'yellow' }], + }, + }); + const [activeNotes, setActiveNotes] = createSignal([]); + return ( +
+ + + {(el) => { + return ( + + {el.value} + + ); + }} + + +
+ ); +}); diff --git a/packages/claviature/index.mjs b/packages/claviature/index.mjs new file mode 100644 index 00000000..281c6ab8 --- /dev/null +++ b/packages/claviature/index.mjs @@ -0,0 +1 @@ +export * from './Claviature.jsx'; diff --git a/packages/claviature/package.json b/packages/claviature/package.json new file mode 100644 index 00000000..01ae8100 --- /dev/null +++ b/packages/claviature/package.json @@ -0,0 +1,42 @@ +{ + "name": "@strudel/claviature", + "version": "1.0.1", + "description": "Claviature component for Strudel", + "main": "dist/index.mjs", + "type": "module", + "publishConfig": { + "main": "dist/index.mjs" + }, + "scripts": { + "build": "vite build", + "watch": "vite build --watch", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "titdalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Felix Roos ", + "license": "AGPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel#readme", + "dependencies": { + "@strudel/core": "workspace:*", + "claviature": "^0.1.0", + "solid-element": "^1.8.0", + "solid-js": "^1.8.15", + "vite-plugin-solid": "^2.10.1" + }, + "devDependencies": { + "vite": "^5.0.10" + } +} diff --git a/packages/claviature/vite.config.js b/packages/claviature/vite.config.js new file mode 100644 index 00000000..4ed5702c --- /dev/null +++ b/packages/claviature/vite.config.js @@ -0,0 +1,20 @@ +import { defineConfig } from 'vite'; +import { dependencies } from './package.json'; +import { resolve } from 'path'; +import solid from 'vite-plugin-solid'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [solid()], + build: { + lib: { + entry: resolve(__dirname, 'index.mjs'), + formats: ['es'], + fileName: (ext) => ({ es: 'index.mjs' })[ext], + }, + rollupOptions: { + external: [...Object.keys(dependencies)], + }, + target: 'esnext', + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b31cc8b..9cd9fb87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,7 +96,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 examples/headless-repl: dependencies: @@ -106,7 +106,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 examples/minimal-repl: dependencies: @@ -128,7 +128,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 examples/superdough: dependencies: @@ -138,7 +138,29 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 + + packages/claviature: + dependencies: + '@strudel/core': + specifier: workspace:* + version: link:../core + claviature: + specifier: ^0.1.0 + version: 0.1.0 + solid-element: + specifier: ^1.8.0 + version: 1.8.0(solid-js@1.8.15) + solid-js: + specifier: ^1.8.15 + version: 1.8.15 + vite-plugin-solid: + specifier: ^2.10.1 + version: 2.10.1(solid-js@1.8.15)(vite@5.0.11) + devDependencies: + vite: + specifier: ^5.0.10 + version: 5.0.11(@types/node@20.10.6) packages/codemirror: dependencies: @@ -193,7 +215,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/core: dependencies: @@ -203,7 +225,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -222,7 +244,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/desktopbridge: dependencies: @@ -249,7 +271,7 @@ importers: version: 5.8.1 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/midi: dependencies: @@ -265,7 +287,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/mini: dependencies: @@ -278,7 +300,7 @@ importers: version: 3.0.2 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -297,7 +319,7 @@ importers: version: 5.8.1 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/repl: dependencies: @@ -337,7 +359,7 @@ importers: version: 5.12.0 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/serial: dependencies: @@ -347,7 +369,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/soundfonts: dependencies: @@ -369,7 +391,7 @@ importers: version: 3.3.2 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/superdough: dependencies: @@ -379,7 +401,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/tonal: dependencies: @@ -398,7 +420,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -423,7 +445,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -451,7 +473,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/webaudio: dependencies: @@ -464,7 +486,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 packages/xen: dependencies: @@ -474,7 +496,7 @@ importers: devDependencies: vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@20.10.6) + version: 5.0.10 vitest: specifier: ^1.1.0 version: 1.1.0(@vitest/ui@1.1.0) @@ -523,6 +545,9 @@ importers: '@nanostores/react': specifier: ^0.7.1 version: 0.7.1(nanostores@0.9.5)(react@18.2.0) + '@strudel/claviature': + specifier: workspace:* + version: link:../packages/claviature '@strudel/codemirror': specifier: workspace:* version: link:../packages/codemirror @@ -1081,6 +1106,13 @@ packages: '@babel/types': 7.23.6 dev: true + /@babel/helper-module-imports@7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: false + /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} @@ -2382,6 +2414,7 @@ packages: cpu: [arm64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-arm@0.19.11: @@ -2398,6 +2431,7 @@ packages: cpu: [arm] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/android-x64@0.19.11: @@ -2414,6 +2448,7 @@ packages: cpu: [x64] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/darwin-arm64@0.19.11: @@ -2430,6 +2465,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/darwin-x64@0.19.11: @@ -2446,6 +2482,7 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-arm64@0.19.11: @@ -2462,6 +2499,7 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/freebsd-x64@0.19.11: @@ -2478,6 +2516,7 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm64@0.19.11: @@ -2494,6 +2533,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-arm@0.19.11: @@ -2510,6 +2550,7 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ia32@0.19.11: @@ -2526,6 +2567,7 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-loong64@0.19.11: @@ -2542,6 +2584,7 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-mips64el@0.19.11: @@ -2558,6 +2601,7 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-ppc64@0.19.11: @@ -2574,6 +2618,7 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-riscv64@0.19.11: @@ -2590,6 +2635,7 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-s390x@0.19.11: @@ -2606,6 +2652,7 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-x64@0.19.11: @@ -2622,6 +2669,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/netbsd-x64@0.19.11: @@ -2638,6 +2686,7 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true + dev: true optional: true /@esbuild/openbsd-x64@0.19.11: @@ -2654,6 +2703,7 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true + dev: true optional: true /@esbuild/sunos-x64@0.19.11: @@ -2670,6 +2720,7 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true + dev: true optional: true /@esbuild/win32-arm64@0.19.11: @@ -2686,6 +2737,7 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-ia32@0.19.11: @@ -2702,6 +2754,7 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true optional: true /@esbuild/win32-x64@0.19.11: @@ -2718,6 +2771,7 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true optional: true /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): @@ -5346,8 +5400,8 @@ packages: tsconfck: 3.0.0(typescript@5.3.3) unist-util-visit: 5.0.0 vfile: 6.0.1 - vite: 5.0.10(@types/node@20.10.6) - vitefu: 0.2.5(vite@5.0.10) + vite: 5.0.11(@types/node@20.10.6) + vitefu: 0.2.5(vite@5.0.11) which-pm: 2.1.1 yargs-parser: 21.1.1 zod: 3.22.4 @@ -5424,6 +5478,19 @@ packages: resolution: {integrity: sha512-3AN/9V/rKuv90NG65m4tTHsI04XrCKsWbztIcW7a8H5iIN7WlvWucRtVV0V/rT4QvtA11n5Vmp20fLwfMWqp6g==} dev: false + /babel-plugin-jsx-dom-expressions@0.37.17(@babel/core@7.23.7): + resolution: {integrity: sha512-1bv8rOTzs6TR3DVyVZ7ElxyPEhnS556FMWRIsB3gBPfkn/cSKaLvXLGk+X1lvI+SzcUo4G+UcmJrn3vr1ig8mQ==} + peerDependencies: + '@babel/core': ^7.20.12 + dependencies: + '@babel/core': 7.23.7 + '@babel/helper-module-imports': 7.18.6 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.7) + '@babel/types': 7.23.6 + html-entities: 2.3.3 + validate-html-nesting: 1.2.2 + dev: false + /babel-plugin-polyfill-corejs2@0.4.7(@babel/core@7.23.7): resolution: {integrity: sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==} peerDependencies: @@ -5460,6 +5527,15 @@ packages: - supports-color dev: true + /babel-preset-solid@1.8.15(@babel/core@7.23.7): + resolution: {integrity: sha512-P2yOQbB7Hn/m4YvpXV6ExHIMcgNWXWXcvY4kJzG3yqAB3hKS58OZRsvJ7RObsZWqXRvZTITBIwnpK0BMGu+ZIQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.7 + babel-plugin-jsx-dom-expressions: 0.37.17(@babel/core@7.23.7) + dev: false + /bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -6069,6 +6145,10 @@ packages: dot-prop: 5.3.0 dev: true + /component-register@0.8.3: + resolution: {integrity: sha512-/0u8ov0WPWi2FL78rgB9aFOcfY8pJT4jP/l9NTOukGNLVQ6hk35sEJE1RkEnNQU3yk48Qr7HlDQjRQKEVfgeWg==} + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -6828,6 +6908,7 @@ packages: '@esbuild/win32-arm64': 0.19.5 '@esbuild/win32-ia32': 0.19.5 '@esbuild/win32-x64': 0.19.5 + dev: true /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} @@ -8085,6 +8166,10 @@ packages: lru-cache: 10.1.0 dev: true + /html-entities@2.3.3: + resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} + dev: false + /html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} @@ -8607,6 +8692,11 @@ packages: call-bind: 1.0.5 dev: true + /is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + dev: false + /is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -9573,6 +9663,13 @@ packages: yargs-parser: 20.2.9 dev: true + /merge-anything@5.1.7: + resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} + engines: {node: '>=12.13'} + dependencies: + is-what: 4.1.16 + dev: false + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -12138,6 +12235,20 @@ packages: randombytes: 2.1.0 dev: true + /seroval-plugins@1.0.4(seroval@1.0.4): + resolution: {integrity: sha512-DQ2IK6oQVvy8k+c2V5x5YCtUa/GGGsUwUBNN9UqohrZ0rWdUapBFpNMYP1bCyRHoxOJjdKGl+dieacFIpU/i1A==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + dependencies: + seroval: 1.0.4 + dev: false + + /seroval@1.0.4: + resolution: {integrity: sha512-qQs/N+KfJu83rmszFQaTxcoJoPn6KNUruX4KmnmyD0oZkUoiNvJ1rpdYKDf4YHM05k+HOgCxa3yvf15QbVijGg==} + engines: {node: '>=10'} + dev: false + /server-destroy@1.0.1: resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} @@ -12340,6 +12451,34 @@ packages: smart-buffer: 4.2.0 dev: true + /solid-element@1.8.0(solid-js@1.8.15): + resolution: {integrity: sha512-DG8HBCej5kNExUiFbVG8OFZojMGcLF8keXdGLEcHXBYtJ7zhm+a8HJnl5lfmBlTYGRk4ApgoBvlwH1ibg7quaQ==} + peerDependencies: + solid-js: ^1.8.0 + dependencies: + component-register: 0.8.3 + solid-js: 1.8.15 + dev: false + + /solid-js@1.8.15: + resolution: {integrity: sha512-d0QP/efr3UVcwGgWVPveQQ0IHOH6iU7yUhc2piy8arNG8wxKmvUy1kFxyF8owpmfCWGB87usDKMaVnsNYZm+Vw==} + dependencies: + csstype: 3.1.1 + seroval: 1.0.4 + seroval-plugins: 1.0.4(seroval@1.0.4) + dev: false + + /solid-refresh@0.6.3(solid-js@1.8.15): + resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} + peerDependencies: + solid-js: ^1.3 + dependencies: + '@babel/generator': 7.23.6 + '@babel/helper-module-imports': 7.22.15 + '@babel/types': 7.23.6 + solid-js: 1.8.15 + dev: false + /sort-array@4.1.5: resolution: {integrity: sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==} engines: {node: '>=10'} @@ -13455,6 +13594,10 @@ packages: hasBin: true dev: true + /validate-html-nesting@1.2.2: + resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} + dev: false + /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -13565,7 +13708,29 @@ packages: - supports-color dev: true - /vite@5.0.10(@types/node@20.10.6): + /vite-plugin-solid@2.10.1(solid-js@1.8.15)(vite@5.0.11): + resolution: {integrity: sha512-kfVdNLWaJqaJVL52U6iCCKNW/nXE7bS1VVGOWPGllOkJfcNILymVSY0LCBLSnyy0iYnRtrXpiHm14rMuzeC7CA==} + peerDependencies: + '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* + solid-js: ^1.7.2 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + '@testing-library/jest-dom': + optional: true + dependencies: + '@babel/core': 7.23.7 + '@types/babel__core': 7.20.5 + babel-preset-solid: 1.8.15(@babel/core@7.23.7) + merge-anything: 5.1.7 + solid-js: 1.8.15 + solid-refresh: 0.6.3(solid-js@1.8.15) + vite: 5.0.11(@types/node@20.10.6) + vitefu: 0.2.5(vite@5.0.11) + transitivePeerDependencies: + - supports-color + dev: false + + /vite@5.0.10: resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -13593,12 +13758,12 @@ packages: terser: optional: true dependencies: - '@types/node': 20.10.6 esbuild: 0.19.5 postcss: 8.4.32 rollup: 4.9.2 optionalDependencies: fsevents: 2.3.3 + dev: true /vite@5.0.11(@types/node@20.10.6): resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} @@ -13629,13 +13794,13 @@ packages: optional: true dependencies: '@types/node': 20.10.6 - esbuild: 0.19.5 + esbuild: 0.19.11 postcss: 8.4.32 rollup: 4.9.2 optionalDependencies: fsevents: 2.3.3 - /vitefu@0.2.5(vite@5.0.10): + /vitefu@0.2.5(vite@5.0.11): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -13643,7 +13808,7 @@ packages: vite: optional: true dependencies: - vite: 5.0.10(@types/node@20.10.6) + vite: 5.0.11(@types/node@20.10.6) /vitest@1.1.0(@vitest/ui@1.1.0): resolution: {integrity: sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A==} diff --git a/website/package.json b/website/package.json index af100889..13bef75d 100644 --- a/website/package.json +++ b/website/package.json @@ -24,6 +24,7 @@ "@heroicons/react": "^2.1.1", "@nanostores/persistent": "^0.9.1", "@nanostores/react": "^0.7.1", + "@strudel/claviature": "workspace:*", "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", "@strudel/csound": "workspace:*", diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index 397e2801..2d809ad6 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -81,6 +81,7 @@ export function loadModules() { import('@strudel/serial'), import('@strudel/soundfonts'), import('@strudel/csound'), + import('@strudel/claviature'), ]; if (isTauri()) { modules = modules.concat([ From 9b3509c06278152db59aaa00f04643fba6e9d1d2 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 8 Mar 2024 00:55:19 +0100 Subject: [PATCH 02/30] global watch task --- package.json | 1 + packages/core/package.json | 1 + packages/mini/package.json | 1 + website/package.json | 1 + 4 files changed, 4 insertions(+) diff --git a/package.json b/package.json index 741cf070..ea1373aa 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "repl": "npm run prestart && cd website && npm run dev", "start": "npm run prestart && cd website && npm run dev", "dev": "npm run prestart && cd website && npm run dev", + "watch": "pnpm prestart && pnpm --parallel --filter {./website/**}... --filter \"@strudel/website\" watch", "build": "npm run prebuild && cd website && npm run build", "preview": "cd website && npm run preview", "osc": "cd packages/osc && npm run server", diff --git a/packages/core/package.json b/packages/core/package.json index 7f59ed41..930acee8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -10,6 +10,7 @@ "scripts": { "test": "vitest run", "build": "vite build", + "watch": "vite build --watch", "prepublishOnly": "pnpm build" }, "repository": { diff --git a/packages/mini/package.json b/packages/mini/package.json index cb4f459c..28848404 100644 --- a/packages/mini/package.json +++ b/packages/mini/package.json @@ -11,6 +11,7 @@ "test": "vitest run", "build:parser": "peggy -o krill-parser.js --format es ./krill.pegjs", "build": "vite build", + "watch": "vite build --watch", "prepublishOnly": "npm run build" }, "repository": { diff --git a/website/package.json b/website/package.json index 13bef75d..c954e52f 100644 --- a/website/package.json +++ b/website/package.json @@ -5,6 +5,7 @@ "private": true, "scripts": { "dev": "astro dev --host 0.0.0.0", + "watch": "astro dev --host 0.0.0.0", "start": "astro dev", "check": "astro check && tsc", "build": "astro build", From 43c40f7bb011fd92cec306d50ddcabd5e6cea9d1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 8 Mar 2024 01:07:17 +0100 Subject: [PATCH 03/30] no need for publishConfig --- packages/claviature/package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/claviature/package.json b/packages/claviature/package.json index 01ae8100..194c86b0 100644 --- a/packages/claviature/package.json +++ b/packages/claviature/package.json @@ -4,9 +4,6 @@ "description": "Claviature component for Strudel", "main": "dist/index.mjs", "type": "module", - "publishConfig": { - "main": "dist/index.mjs" - }, "scripts": { "build": "vite build", "watch": "vite build --watch", From 0a1da6f6514df06f21d81d3e81fd65d9cf414019 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Mar 2024 14:20:56 +0100 Subject: [PATCH 04/30] claviature lockfile --- pnpm-lock.yaml | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdbe177a..97ff4f66 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,6 +140,28 @@ importers: specifier: ^5.0.10 version: 5.0.10 + packages/claviature: + dependencies: + '@strudel/core': + specifier: workspace:* + version: link:../core + claviature: + specifier: ^0.1.0 + version: 0.1.0 + solid-element: + specifier: ^1.8.0 + version: 1.8.0(solid-js@1.8.15) + solid-js: + specifier: ^1.8.15 + version: 1.8.15 + vite-plugin-solid: + specifier: ^2.10.1 + version: 2.10.1(solid-js@1.8.15)(vite@5.0.11) + devDependencies: + vite: + specifier: ^5.0.10 + version: 5.0.11(@types/node@20.10.6) + packages/codemirror: dependencies: '@codemirror/autocomplete': @@ -13711,6 +13733,28 @@ packages: - supports-color dev: true + /vite-plugin-solid@2.10.1(solid-js@1.8.15)(vite@5.0.11): + resolution: {integrity: sha512-kfVdNLWaJqaJVL52U6iCCKNW/nXE7bS1VVGOWPGllOkJfcNILymVSY0LCBLSnyy0iYnRtrXpiHm14rMuzeC7CA==} + peerDependencies: + '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* + solid-js: ^1.7.2 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + '@testing-library/jest-dom': + optional: true + dependencies: + '@babel/core': 7.23.7 + '@types/babel__core': 7.20.5 + babel-preset-solid: 1.8.15(@babel/core@7.23.7) + merge-anything: 5.1.7 + solid-js: 1.8.15 + solid-refresh: 0.6.3(solid-js@1.8.15) + vite: 5.0.11(@types/node@20.10.6) + vitefu: 0.2.5(vite@5.0.11) + transitivePeerDependencies: + - supports-color + dev: false + /vite@5.0.10: resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} From f2e16f946cca3f99bb30ec2d72c62362bb6a2b7f Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Mar 2024 15:17:19 +0100 Subject: [PATCH 05/30] add working claviature method --- packages/claviature/Claviature.jsx | 25 ++++++++++++++----------- packages/claviature/index.mjs | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/packages/claviature/Claviature.jsx b/packages/claviature/Claviature.jsx index 50dd2cfd..ec79edf6 100644 --- a/packages/claviature/Claviature.jsx +++ b/packages/claviature/Claviature.jsx @@ -1,20 +1,23 @@ -import { createSignal, For } from 'solid-js'; +import { For } from 'solid-js'; import { customElement } from 'solid-element'; import { getClaviature } from 'claviature'; import { Dynamic } from 'solid-js/web'; -customElement('strudel-claviature', { someProp: 'one', otherProp: 'two' }, (props, { element }) => { - const svg = getClaviature({ - options: { - range: ['A1', 'C4'], - colorize: [{ keys: ['C3', 'E3', 'G3'], color: 'yellow' }], - }, - }); - const [activeNotes, setActiveNotes] = createSignal([]); +let defaultOptions = { + range: ['A1', 'C6'], +}; + +customElement('strudel-claviature', { options: JSON.stringify(defaultOptions) }, (props, { element }) => { + let svg = () => { + let c = getClaviature({ + options: JSON.parse(props.options), + }); + return c; + }; return (
- - + + {(el) => { return ( diff --git a/packages/claviature/index.mjs b/packages/claviature/index.mjs index 281c6ab8..c4ee4860 100644 --- a/packages/claviature/index.mjs +++ b/packages/claviature/index.mjs @@ -1 +1,24 @@ export * from './Claviature.jsx'; +import { Pattern } from '@strudel/core'; + +Pattern.prototype.claviature = function (options = {}) { + if (!window.claviature) { + window.claviature = document.createElement('strudel-claviature'); + window.claviature.style.position = 'absolute'; + window.claviature.style.bottom = 0; + window.claviature.style.left = 0; + document.body.append(window.claviature); + } + return this.onFrame((haps) => { + const keys = haps.map((h) => h.value.note); + // console.log('keys',keys); + window.claviature.setAttribute( + 'options', + JSON.stringify({ + ...options, + range: options.range || ['A2', 'C6'], + colorize: [{ keys: keys, color: options.color || 'steelblue' }], + }), + ); + }); +}; From cfdd4ac36ca45fa3fa520f369787e49c6cdf1c97 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Mar 2024 15:28:06 +0100 Subject: [PATCH 06/30] add readme --- packages/claviature/README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 packages/claviature/README.md diff --git a/packages/claviature/README.md b/packages/claviature/README.md new file mode 100644 index 00000000..eb540b2e --- /dev/null +++ b/packages/claviature/README.md @@ -0,0 +1,29 @@ +# @strudel/claviature + +adds a `Patter.claviature` method that renders a [claviature](https://www.npmjs.com/package/claviature). + +example usage: + +```js +chord("").voicing().piano() + .claviature() +``` + +All [claviature options](https://www.npmjs.com/package/claviature#options) will work. + +Here is an example that uses all available options: + +```js +chord("").voicing().piano() +.claviature({ + range: ['C1', 'C6'], // rendered note range + color: 'yellow', // highlighting color + palette: ['cyan', 'magenta'], + stroke: 'black', + scaleX: 1, scaleY: 1, + upperHeight: 80, + lowerHeight: 50 +}) +``` + +Note: The `Pattern.claviature` method uses the `colorization` option internally, so don't override that and use the `color` option for changing the highlighting color. From 1e75f045b7bf1510574bf065d6cec8ecd096f4bd Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Mar 2024 15:31:50 +0100 Subject: [PATCH 07/30] remove other watch tasks for now --- packages/core/package.json | 1 - packages/mini/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 930acee8..7f59ed41 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -10,7 +10,6 @@ "scripts": { "test": "vitest run", "build": "vite build", - "watch": "vite build --watch", "prepublishOnly": "pnpm build" }, "repository": { diff --git a/packages/mini/package.json b/packages/mini/package.json index 28848404..cb4f459c 100644 --- a/packages/mini/package.json +++ b/packages/mini/package.json @@ -11,7 +11,6 @@ "test": "vitest run", "build:parser": "peggy -o krill-parser.js --format es ./krill.pegjs", "build": "vite build", - "watch": "vite build --watch", "prepublishOnly": "npm run build" }, "repository": { From 29dab578e7488f254cbe20105f9e406c1b58f6b1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 14 Mar 2024 23:49:38 +0100 Subject: [PATCH 08/30] can now load claviature as a codemirror widget --- packages/codemirror/codemirror.mjs | 9 ++- packages/codemirror/index.mjs | 1 + packages/codemirror/slider.mjs | 38 ++++++------ packages/codemirror/widget.mjs | 95 ++++++++++++++++++++++++++++++ packages/transpiler/transpiler.mjs | 32 +++++++++- 5 files changed, 152 insertions(+), 23 deletions(-) create mode 100644 packages/codemirror/widget.mjs diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 512c873a..c06ac4a0 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -20,7 +20,8 @@ import { flash, isFlashEnabled } from './flash.mjs'; import { highlightMiniLocations, isPatternHighlightingEnabled, updateMiniLocations } from './highlight.mjs'; import { keybindings } from './keybindings.mjs'; import { initTheme, activateTheme, theme } from './themes.mjs'; -import { updateWidgets, sliderPlugin } from './slider.mjs'; +import { sliderPlugin, updateSliderWidgets } from './slider.mjs'; +import { widgetPlugin, updateWidgets } from './widget.mjs'; import { persistentAtom } from '@nanostores/persistent'; const extensions = { @@ -72,6 +73,7 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo ...initialSettings, javascript(), sliderPlugin, + widgetPlugin, // indentOnInput(), // works without. already brought with javascript extension? // bracketMatching(), // does not do anything closeBrackets(), @@ -187,7 +189,10 @@ export class StrudelMirror { // remember for when highlighting is toggled on this.miniLocations = options.meta?.miniLocations; this.widgets = options.meta?.widgets; - updateWidgets(this.editor, this.widgets); + const sliders = this.widgets.filter((w) => w.type === 'slider'); + updateSliderWidgets(this.editor, sliders); + const widgets = this.widgets.filter((w) => w.type !== 'slider'); + updateWidgets(this.editor, widgets); updateMiniLocations(this.editor, this.miniLocations); replOptions?.afterEval?.(options); this.adjustDrawTime(); diff --git a/packages/codemirror/index.mjs b/packages/codemirror/index.mjs index 8f2d1630..3a5f2a23 100644 --- a/packages/codemirror/index.mjs +++ b/packages/codemirror/index.mjs @@ -3,3 +3,4 @@ export * from './highlight.mjs'; export * from './flash.mjs'; export * from './slider.mjs'; export * from './themes.mjs'; +export * from './widget.mjs'; diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index a46f0e1e..21f744bb 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -1,10 +1,27 @@ import { ref, pure } from '@strudel/core'; import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view'; -import { StateEffect, StateField } from '@codemirror/state'; +import { StateEffect } from '@codemirror/state'; + +export const setSliderWidgets = StateEffect.define(); + +export const updateSliderWidgets = (view, widgets) => { + view.dispatch({ effects: setSliderWidgets.of(widgets) }); +}; export let sliderValues = {}; const getSliderID = (from) => `slider_${from}`; +function getSliders(widgetConfigs, view) { + return widgetConfigs + .filter((w) => w.type === 'slider') + .map(({ from, to, value, min, max, step }) => { + return Decoration.widget({ + widget: new SliderWidget(value, min, max, from, to, step, view), + side: 0, + }).range(from /* , to */); + }); +} + export class SliderWidget extends WidgetType { constructor(value, min, max, from, to, step, view) { super(); @@ -60,21 +77,6 @@ export class SliderWidget extends WidgetType { } } -export const setWidgets = StateEffect.define(); - -export const updateWidgets = (view, widgets) => { - view.dispatch({ effects: setWidgets.of(widgets) }); -}; - -function getWidgets(widgetConfigs, view) { - return widgetConfigs.map(({ from, to, value, min, max, step }) => { - return Decoration.widget({ - widget: new SliderWidget(value, min, max, from, to, step, view), - side: 0, - }).range(from /* , to */); - }); -} - export const sliderPlugin = ViewPlugin.fromClass( class { decorations; //: DecorationSet @@ -99,8 +101,8 @@ export const sliderPlugin = ViewPlugin.fromClass( } } for (let e of tr.effects) { - if (e.is(setWidgets)) { - this.decorations = Decoration.set(getWidgets(e.value, update.view)); + if (e.is(setSliderWidgets)) { + this.decorations = Decoration.set(getSliders(e.value, update.view)); } } }); diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs new file mode 100644 index 00000000..3df38d8b --- /dev/null +++ b/packages/codemirror/widget.mjs @@ -0,0 +1,95 @@ +import { StateEffect, StateField } from '@codemirror/state'; +import { Decoration, EditorView, WidgetType } from '@codemirror/view'; +import { Pattern } from '@strudel/core'; + +const getWidgetID = (from) => `ui_${from}`; + +Pattern.prototype.ui = function (id, value) { + // TODO: make this work with any web component + return this.onFrame((haps) => { + let el = document.getElementById(id); + if (el) { + let options = {}; + const keys = haps.map((h) => h.value.note); + el.setAttribute( + 'options', + JSON.stringify({ + ...options, + range: options.range || ['A2', 'C6'], + colorize: [{ keys: keys, color: options.color || 'steelblue' }], + }), + ); + } + }); +}; + +export const addWidget = StateEffect.define({ + map: ({ from, to }, change) => { + return { from: change.mapPos(from), to: change.mapPos(to) }; + }, +}); + +export const updateWidgets = (view, widgets) => { + view.dispatch({ effects: addWidget.of(widgets) }); +}; + +function getWidgets(widgetConfigs, view) { + return widgetConfigs.map(({ from, to }) => { + return Decoration.widget({ + widget: new BlockWidget(view, from), + side: 0, + block: true, + }).range(to); + }); +} + +const widgetField = StateField.define( + /* */ { + create() { + return Decoration.none; + }, + update(widgets, tr) { + widgets = widgets.map(tr.changes); + for (let e of tr.effects) { + if (e.is(addWidget)) { + try { + widgets = widgets.update({ + filter: () => false, + add: getWidgets(e.value), + }); + } catch (error) { + console.log('err', error); + } + } + } + return widgets; + }, + provide: (f) => EditorView.decorations.from(f), + }, +); + +export class BlockWidget extends WidgetType { + constructor(view, from) { + super(); + this.view = view; + this.from = from; + } + eq() { + return true; + } + toDOM() { + const id = getWidgetID(this.from); // matches id generated in transpiler + let el = document.getElementById(id); + if (!el) { + // TODO: make this work with any web component + el = document.createElement('strudel-claviature'); + el.id = id; + } + return el; + } + ignoreEvent(e) { + return true; + } +} + +export const widgetPlugin = [widgetField]; diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 72f2e851..48db38a6 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -34,7 +34,7 @@ export function transpiler(input, options = {}) { emitMiniLocations && collectMiniLocations(value, node); return this.replace(miniWithLocation(value, node)); } - if (isWidgetFunction(node)) { + if (isSliderFunction(node)) { emitWidgets && widgets.push({ from: node.arguments[0].start, @@ -43,6 +43,16 @@ export function transpiler(input, options = {}) { min: node.arguments[1]?.value ?? 0, max: node.arguments[2]?.value ?? 1, step: node.arguments[3]?.value, + type: 'slider', + }); + return this.replace(sliderWithLocation(node)); + } + if (isUIFunction(node)) { + emitWidgets && + widgets.push({ + from: node.arguments[0].start, + to: node.end, + type: node.arguments[0].value, }); return this.replace(widgetWithLocation(node)); } @@ -105,11 +115,15 @@ function miniWithLocation(value, node) { // these functions are connected to @strudel/codemirror -> slider.mjs // maybe someday there will be pluggable transpiler functions, then move this there -function isWidgetFunction(node) { +function isSliderFunction(node) { return node.type === 'CallExpression' && node.callee.name === 'slider'; } -function widgetWithLocation(node) { +function isUIFunction(node) { + return node.type === 'CallExpression' && node.callee.property?.name === 'ui'; +} + +function sliderWithLocation(node) { const id = 'slider_' + node.arguments[0].start; // use loc of first arg for id // add loc as identifier to first argument // the sliderWithID function is assumed to be sliderWithID(id, value, min?, max?) @@ -122,6 +136,18 @@ function widgetWithLocation(node) { return node; } +function widgetWithLocation(node) { + const id = 'ui_' + node.arguments[0].start; // use loc of first arg for id + // add loc as identifier to first argument + // the sliderWithID function is assumed to be sliderWithID(id, value, min?, max?) + node.arguments.unshift({ + type: 'Literal', + value: id, + raw: id, + }); + return node; +} + function isBareSamplesCall(node, parent) { return node.type === 'CallExpression' && node.callee.name === 'samples' && parent.type !== 'AwaitExpression'; } From 82c4926f197c5a189c8562c24abaa6cb4bbcc6c7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 00:27:16 +0100 Subject: [PATCH 09/30] allow any web component to become a widget --- packages/claviature/index.mjs | 16 +++++------- packages/claviature/package.json | 1 + packages/codemirror/widget.mjs | 39 +++++++----------------------- packages/transpiler/transpiler.mjs | 23 +++++++++++++----- pnpm-lock.yaml | 3 +++ 5 files changed, 36 insertions(+), 46 deletions(-) diff --git a/packages/claviature/index.mjs b/packages/claviature/index.mjs index c4ee4860..c39237d5 100644 --- a/packages/claviature/index.mjs +++ b/packages/claviature/index.mjs @@ -1,18 +1,14 @@ export * from './Claviature.jsx'; import { Pattern } from '@strudel/core'; +import { registerWidget } from '@strudel/transpiler'; -Pattern.prototype.claviature = function (options = {}) { - if (!window.claviature) { - window.claviature = document.createElement('strudel-claviature'); - window.claviature.style.position = 'absolute'; - window.claviature.style.bottom = 0; - window.claviature.style.left = 0; - document.body.append(window.claviature); - } +registerWidget('claviature', 'strudel-claviature'); + +Pattern.prototype.claviature = function (id, options = {}) { return this.onFrame((haps) => { const keys = haps.map((h) => h.value.note); - // console.log('keys',keys); - window.claviature.setAttribute( + let el = document.getElementById(id); + el?.setAttribute( 'options', JSON.stringify({ ...options, diff --git a/packages/claviature/package.json b/packages/claviature/package.json index 194c86b0..16e8f549 100644 --- a/packages/claviature/package.json +++ b/packages/claviature/package.json @@ -28,6 +28,7 @@ "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", + "@strudel/transpiler": "workspace:*", "claviature": "^0.1.0", "solid-element": "^1.8.0", "solid-js": "^1.8.15", diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index 3df38d8b..3171d0df 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -1,27 +1,7 @@ import { StateEffect, StateField } from '@codemirror/state'; import { Decoration, EditorView, WidgetType } from '@codemirror/view'; -import { Pattern } from '@strudel/core'; -const getWidgetID = (from) => `ui_${from}`; - -Pattern.prototype.ui = function (id, value) { - // TODO: make this work with any web component - return this.onFrame((haps) => { - let el = document.getElementById(id); - if (el) { - let options = {}; - const keys = haps.map((h) => h.value.note); - el.setAttribute( - 'options', - JSON.stringify({ - ...options, - range: options.range || ['A2', 'C6'], - colorize: [{ keys: keys, color: options.color || 'steelblue' }], - }), - ); - } - }); -}; +const getWidgetID = (from) => `widget_${from}`; export const addWidget = StateEffect.define({ map: ({ from, to }, change) => { @@ -33,10 +13,10 @@ export const updateWidgets = (view, widgets) => { view.dispatch({ effects: addWidget.of(widgets) }); }; -function getWidgets(widgetConfigs, view) { - return widgetConfigs.map(({ from, to }) => { +function getWidgets(widgetConfigs) { + return widgetConfigs.map(({ to, type }) => { return Decoration.widget({ - widget: new BlockWidget(view, from), + widget: new BlockWidget(to, type), side: 0, block: true, }).range(to); @@ -69,20 +49,19 @@ const widgetField = StateField.define( ); export class BlockWidget extends WidgetType { - constructor(view, from) { + constructor(col, type) { super(); - this.view = view; - this.from = from; + this.col = col; + this.type = type; } eq() { return true; } toDOM() { - const id = getWidgetID(this.from); // matches id generated in transpiler + const id = getWidgetID(this.col); // matches id generated in transpiler let el = document.getElementById(id); if (!el) { - // TODO: make this work with any web component - el = document.createElement('strudel-claviature'); + el = document.createElement(this.type); el.id = id; } return el; diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 48db38a6..db805e14 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -3,6 +3,18 @@ import { parse } from 'acorn'; import escodegen from 'escodegen'; import { walk } from 'estree-walker'; +let widgetComponents = {}; +// this function allows registering a pattern method name and component name +// e.g. register('claviature', 'strudel-claviature') +// .. will map the Pattern method 'claviature' to the web component 'strudel-claviature' +// the transpiler will turn .claviature(...args) into .claviature(id, ...args) +// the widgetPlugin of @strudel/codemirror will automatically create an instance of 'strudel-claviature' +// .. so you only have to implement the actual .claviature method (or what you've called it..) + +export function registerWidget(name, tagName) { + widgetComponents[name] = tagName; +} + export function transpiler(input, options = {}) { const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options; @@ -47,12 +59,11 @@ export function transpiler(input, options = {}) { }); return this.replace(sliderWithLocation(node)); } - if (isUIFunction(node)) { + if (isWidgetMethod(node)) { emitWidgets && widgets.push({ - from: node.arguments[0].start, to: node.end, - type: node.arguments[0].value, + type: widgetComponents[node.callee.property.name], }); return this.replace(widgetWithLocation(node)); } @@ -119,8 +130,8 @@ function isSliderFunction(node) { return node.type === 'CallExpression' && node.callee.name === 'slider'; } -function isUIFunction(node) { - return node.type === 'CallExpression' && node.callee.property?.name === 'ui'; +function isWidgetMethod(node) { + return node.type === 'CallExpression' && Object.keys(widgetComponents).includes(node.callee.property?.name); } function sliderWithLocation(node) { @@ -137,7 +148,7 @@ function sliderWithLocation(node) { } function widgetWithLocation(node) { - const id = 'ui_' + node.arguments[0].start; // use loc of first arg for id + const id = 'widget_' + node.end; // add loc as identifier to first argument // the sliderWithID function is assumed to be sliderWithID(id, value, min?, max?) node.arguments.unshift({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97ff4f66..75c4e0a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -145,6 +145,9 @@ importers: '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/transpiler': + specifier: workspace:* + version: link:../transpiler claviature: specifier: ^0.1.0 version: 0.1.0 From 708675077f2dcc9e3f942e02728e0347b1b77117 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 00:34:13 +0100 Subject: [PATCH 10/30] rename claviature package to widgets --- packages/claviature/index.mjs | 20 ------- .../{claviature => widgets}/Claviature.jsx | 19 ++++++ packages/{claviature => widgets}/README.md | 8 ++- packages/widgets/index.mjs | 1 + packages/{claviature => widgets}/package.json | 4 +- .../{claviature => widgets}/vite.config.js | 0 pnpm-lock.yaml | 60 +++++++++---------- website/package.json | 2 +- website/src/repl/util.mjs | 2 +- 9 files changed, 59 insertions(+), 57 deletions(-) delete mode 100644 packages/claviature/index.mjs rename packages/{claviature => widgets}/Claviature.jsx (57%) rename packages/{claviature => widgets}/README.md (81%) create mode 100644 packages/widgets/index.mjs rename packages/{claviature => widgets}/package.json (91%) rename packages/{claviature => widgets}/vite.config.js (100%) diff --git a/packages/claviature/index.mjs b/packages/claviature/index.mjs deleted file mode 100644 index c39237d5..00000000 --- a/packages/claviature/index.mjs +++ /dev/null @@ -1,20 +0,0 @@ -export * from './Claviature.jsx'; -import { Pattern } from '@strudel/core'; -import { registerWidget } from '@strudel/transpiler'; - -registerWidget('claviature', 'strudel-claviature'); - -Pattern.prototype.claviature = function (id, options = {}) { - return this.onFrame((haps) => { - const keys = haps.map((h) => h.value.note); - let el = document.getElementById(id); - el?.setAttribute( - 'options', - JSON.stringify({ - ...options, - range: options.range || ['A2', 'C6'], - colorize: [{ keys: keys, color: options.color || 'steelblue' }], - }), - ); - }); -}; diff --git a/packages/claviature/Claviature.jsx b/packages/widgets/Claviature.jsx similarity index 57% rename from packages/claviature/Claviature.jsx rename to packages/widgets/Claviature.jsx index ec79edf6..0d78d242 100644 --- a/packages/claviature/Claviature.jsx +++ b/packages/widgets/Claviature.jsx @@ -2,6 +2,8 @@ import { For } from 'solid-js'; import { customElement } from 'solid-element'; import { getClaviature } from 'claviature'; import { Dynamic } from 'solid-js/web'; +import { Pattern } from '@strudel/core'; +import { registerWidget } from '@strudel/transpiler'; let defaultOptions = { range: ['A1', 'C6'], @@ -30,3 +32,20 @@ customElement('strudel-claviature', { options: JSON.stringify(defaultOptions) },
); }); + +registerWidget('claviature', 'strudel-claviature'); + +Pattern.prototype.claviature = function (id, options = {}) { + return this.onFrame((haps) => { + const keys = haps.map((h) => h.value.note); + let el = document.getElementById(id); + el?.setAttribute( + 'options', + JSON.stringify({ + ...options, + range: options.range || ['A2', 'C6'], + colorize: [{ keys: keys, color: options.color || 'steelblue' }], + }), + ); + }); +}; diff --git a/packages/claviature/README.md b/packages/widgets/README.md similarity index 81% rename from packages/claviature/README.md rename to packages/widgets/README.md index eb540b2e..a5c36193 100644 --- a/packages/claviature/README.md +++ b/packages/widgets/README.md @@ -1,6 +1,10 @@ -# @strudel/claviature +# @strudel/widgets -adds a `Patter.claviature` method that renders a [claviature](https://www.npmjs.com/package/claviature). +adds UI widgets to codemirror + +## claviature + +`Patter.claviature` renders a [claviature](https://www.npmjs.com/package/claviature). example usage: diff --git a/packages/widgets/index.mjs b/packages/widgets/index.mjs new file mode 100644 index 00000000..281c6ab8 --- /dev/null +++ b/packages/widgets/index.mjs @@ -0,0 +1 @@ +export * from './Claviature.jsx'; diff --git a/packages/claviature/package.json b/packages/widgets/package.json similarity index 91% rename from packages/claviature/package.json rename to packages/widgets/package.json index 16e8f549..213ae526 100644 --- a/packages/claviature/package.json +++ b/packages/widgets/package.json @@ -1,7 +1,7 @@ { - "name": "@strudel/claviature", + "name": "@strudel/widgets", "version": "1.0.1", - "description": "Claviature component for Strudel", + "description": "Widget web components for Strudel", "main": "dist/index.mjs", "type": "module", "scripts": { diff --git a/packages/claviature/vite.config.js b/packages/widgets/vite.config.js similarity index 100% rename from packages/claviature/vite.config.js rename to packages/widgets/vite.config.js diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75c4e0a9..9ceaf3d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,31 +140,6 @@ importers: specifier: ^5.0.10 version: 5.0.10 - packages/claviature: - dependencies: - '@strudel/core': - specifier: workspace:* - version: link:../core - '@strudel/transpiler': - specifier: workspace:* - version: link:../transpiler - claviature: - specifier: ^0.1.0 - version: 0.1.0 - solid-element: - specifier: ^1.8.0 - version: 1.8.0(solid-js@1.8.15) - solid-js: - specifier: ^1.8.15 - version: 1.8.15 - vite-plugin-solid: - specifier: ^2.10.1 - version: 2.10.1(solid-js@1.8.15)(vite@5.0.11) - devDependencies: - vite: - specifier: ^5.0.10 - version: 5.0.11(@types/node@20.10.6) - packages/codemirror: dependencies: '@codemirror/autocomplete': @@ -513,6 +488,31 @@ importers: specifier: ^5.0.10 version: 5.0.10 + packages/widgets: + dependencies: + '@strudel/core': + specifier: workspace:* + version: link:../core + '@strudel/transpiler': + specifier: workspace:* + version: link:../transpiler + claviature: + specifier: ^0.1.0 + version: 0.1.0 + solid-element: + specifier: ^1.8.0 + version: 1.8.0(solid-js@1.8.15) + solid-js: + specifier: ^1.8.15 + version: 1.8.15 + vite-plugin-solid: + specifier: ^2.10.1 + version: 2.10.1(solid-js@1.8.15)(vite@5.0.11) + devDependencies: + vite: + specifier: ^5.0.10 + version: 5.0.11(@types/node@20.10.6) + packages/xen: dependencies: '@strudel/core': @@ -570,9 +570,6 @@ importers: '@nanostores/react': specifier: ^0.7.1 version: 0.7.1(nanostores@0.9.5)(react@18.2.0) - '@strudel/claviature': - specifier: workspace:* - version: link:../packages/claviature '@strudel/codemirror': specifier: workspace:* version: link:../packages/codemirror @@ -618,6 +615,9 @@ importers: '@strudel/webaudio': specifier: workspace:* version: link:../packages/webaudio + '@strudel/widgets': + specifier: workspace:* + version: link:../packages/widgets '@strudel/xen': specifier: workspace:* version: link:../packages/xen @@ -854,7 +854,7 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/trace-mapping': 0.3.20 /@apideck/better-ajv-errors@0.3.6(ajv@8.12.0): resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} @@ -3096,7 +3096,6 @@ packages: /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} @@ -3126,7 +3125,6 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 - dev: true /@jsdoc/salty@0.2.3: resolution: {integrity: sha512-bbtCxCkxcnWhi50I+4Lj6mdz9w3pOXOgEQrID8TCZ/DF51fW7M9GCQW2y45SpBDdHd1Eirm1X/Cf6CkAAe8HPg==} diff --git a/website/package.json b/website/package.json index e80bfa7a..3fd05d54 100644 --- a/website/package.json +++ b/website/package.json @@ -25,7 +25,7 @@ "@heroicons/react": "^2.1.1", "@nanostores/persistent": "^0.9.1", "@nanostores/react": "^0.7.1", - "@strudel/claviature": "workspace:*", + "@strudel/widgets": "workspace:*", "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", "@strudel/draw": "workspace:*", diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index 31e518cb..1df5a5b5 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -82,7 +82,7 @@ export function loadModules() { import('@strudel/serial'), import('@strudel/soundfonts'), import('@strudel/csound'), - import('@strudel/claviature'), + import('@strudel/widgets'), ]; if (isTauri()) { modules = modules.concat([ From 094b30cd264dc61879fbe1d8f39db47e17741318 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 00:39:31 +0100 Subject: [PATCH 11/30] use hap color for claviature --- packages/widgets/Claviature.jsx | 4 ++-- packages/widgets/README.md | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/widgets/Claviature.jsx b/packages/widgets/Claviature.jsx index 0d78d242..e4ebfec6 100644 --- a/packages/widgets/Claviature.jsx +++ b/packages/widgets/Claviature.jsx @@ -37,14 +37,14 @@ registerWidget('claviature', 'strudel-claviature'); Pattern.prototype.claviature = function (id, options = {}) { return this.onFrame((haps) => { - const keys = haps.map((h) => h.value.note); + const colorize = haps.map((h) => ({ keys: [h.value.note], color: h.context?.color || 'steelblue' })); let el = document.getElementById(id); el?.setAttribute( 'options', JSON.stringify({ ...options, range: options.range || ['A2', 'C6'], - colorize: [{ keys: keys, color: options.color || 'steelblue' }], + colorize, }), ); }); diff --git a/packages/widgets/README.md b/packages/widgets/README.md index a5c36193..53847c5d 100644 --- a/packages/widgets/README.md +++ b/packages/widgets/README.md @@ -19,9 +19,9 @@ Here is an example that uses all available options: ```js chord("").voicing().piano() +.color('cyan') .claviature({ range: ['C1', 'C6'], // rendered note range - color: 'yellow', // highlighting color palette: ['cyan', 'magenta'], stroke: 'black', scaleX: 1, scaleY: 1, @@ -29,5 +29,3 @@ chord("").voicing().piano() lowerHeight: 50 }) ``` - -Note: The `Pattern.claviature` method uses the `colorization` option internally, so don't override that and use the `color` option for changing the highlighting color. From 48e0691eec3f0a5496258ce0bc52336a8ba01774 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 01:40:21 +0100 Subject: [PATCH 12/30] add pianoroll widget --- packages/codemirror/widget.mjs | 19 ++++++++++++------- packages/draw/draw.mjs | 4 ++-- packages/draw/pianoroll.mjs | 3 ++- packages/widgets/Claviature.jsx | 24 +++++++++++------------- packages/widgets/Pianoroll.jsx | 30 ++++++++++++++++++++++++++++++ packages/widgets/index.mjs | 1 + packages/widgets/package.json | 1 + pnpm-lock.yaml | 3 +++ 8 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 packages/widgets/Pianoroll.jsx diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index 3171d0df..c0bd9a24 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -14,13 +14,18 @@ export const updateWidgets = (view, widgets) => { }; function getWidgets(widgetConfigs) { - return widgetConfigs.map(({ to, type }) => { - return Decoration.widget({ - widget: new BlockWidget(to, type), - side: 0, - block: true, - }).range(to); - }); + return ( + widgetConfigs + // codemirror throws an error if we don't sort + .sort((a, b) => a.to - b.to) + .map(({ to, type }) => { + return Decoration.widget({ + widget: new BlockWidget(to, type), + side: 0, + block: true, + }).range(to); + }) + ); } const widgetField = StateField.define( diff --git a/packages/draw/draw.mjs b/packages/draw/draw.mjs index 2ea531cf..63245baf 100644 --- a/packages/draw/draw.mjs +++ b/packages/draw/draw.mjs @@ -29,14 +29,14 @@ export const getDrawContext = (id = 'test-canvas', options) => { return canvas.getContext(contextType); }; -Pattern.prototype.draw = function (callback, { from, to, onQuery } = {}) { +Pattern.prototype.draw = function (callback, { from, to, onQuery, ctx } = {}) { if (typeof window === 'undefined') { return this; } if (window.strudelAnimation) { cancelAnimationFrame(window.strudelAnimation); } - const ctx = getDrawContext(); + ctx = ctx || getDrawContext(); let cycle, events = []; const animate = (time) => { diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index 74b1480e..b032ef1a 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -30,7 +30,7 @@ const getValue = (e) => { }; Pattern.prototype.pianoroll = function (options = {}) { - let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false } = options; + let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false, ctx } = options; let from = -cycles * playhead; let to = cycles * (1 - playhead); @@ -49,6 +49,7 @@ Pattern.prototype.pianoroll = function (options = {}) { { from: from - overscan, to: to + overscan, + ctx, }, ); return this; diff --git a/packages/widgets/Claviature.jsx b/packages/widgets/Claviature.jsx index e4ebfec6..33777be4 100644 --- a/packages/widgets/Claviature.jsx +++ b/packages/widgets/Claviature.jsx @@ -17,19 +17,17 @@ customElement('strudel-claviature', { options: JSON.stringify(defaultOptions) }, return c; }; return ( -
- - - {(el) => { - return ( - - {el.value} - - ); - }} - - -
+ + + {(el) => { + return ( + + {el.value} + + ); + }} + + ); }); diff --git a/packages/widgets/Pianoroll.jsx b/packages/widgets/Pianoroll.jsx new file mode 100644 index 00000000..bf7c145c --- /dev/null +++ b/packages/widgets/Pianoroll.jsx @@ -0,0 +1,30 @@ +import { Pattern } from '@strudel/core'; +import { registerWidget } from '@strudel/transpiler'; +import { customElement } from 'solid-element'; + +customElement('strudel-pianoroll', { options: JSON.stringify('{}') }, (props, { element }) => { + return ; +}); + +registerWidget('roll', 'strudel-pianoroll'); + +Pattern.prototype.roll = function (id, options = { fold: 1 }) { + // TODO: remove setTimeout... + setTimeout(() => { + let el = document.getElementById(id); + if (!el) { + console.log('widget not found...'); + return this; + } + const { width = 400, height = 100 } = options; + const canvas = el?.shadowRoot.firstChild; + const pixelRatio = window.devicePixelRatio; + canvas.width = width * pixelRatio; + canvas.height = height * pixelRatio; + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + const ctx = canvas.getContext('2d'); + this.pianoroll({ ...options, ctx }); + }); + return this; +}; diff --git a/packages/widgets/index.mjs b/packages/widgets/index.mjs index 281c6ab8..80d01ab5 100644 --- a/packages/widgets/index.mjs +++ b/packages/widgets/index.mjs @@ -1 +1,2 @@ export * from './Claviature.jsx'; +export * from './Pianoroll.jsx'; diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 213ae526..c77c2712 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -29,6 +29,7 @@ "dependencies": { "@strudel/core": "workspace:*", "@strudel/transpiler": "workspace:*", + "@strudel/draw": "workspace:*", "claviature": "^0.1.0", "solid-element": "^1.8.0", "solid-js": "^1.8.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ceaf3d9..ee159ab2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -493,6 +493,9 @@ importers: '@strudel/core': specifier: workspace:* version: link:../core + '@strudel/draw': + specifier: workspace:* + version: link:../draw '@strudel/transpiler': specifier: workspace:* version: link:../transpiler From 83ea3bb138468aa4d4953c6dcaae3c5707f742ca Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 01:47:35 +0100 Subject: [PATCH 13/30] encapsulate canvas logic --- packages/widgets/Canvas.jsx | 21 +++++++++++++++++++++ packages/widgets/Pianoroll.jsx | 22 +++------------------- packages/widgets/index.mjs | 1 + 3 files changed, 25 insertions(+), 19 deletions(-) create mode 100644 packages/widgets/Canvas.jsx diff --git a/packages/widgets/Canvas.jsx b/packages/widgets/Canvas.jsx new file mode 100644 index 00000000..9b6b668c --- /dev/null +++ b/packages/widgets/Canvas.jsx @@ -0,0 +1,21 @@ +import { customElement } from 'solid-element'; + +customElement('strudel-canvas', {}, () => { + return ; +}); + +export function getWidgetDrawContext(id, options) { + let el = document.getElementById(id); + if (!el) { + console.warn(`widget with id ${id} not found in the DOM`); + return; + } + const { width = 300, height = 100, pixelRatio = window.devicePixelRatio } = options || {}; + const canvas = el?.shadowRoot.firstChild; + canvas.width = width * pixelRatio; + canvas.height = height * pixelRatio; + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + const ctx = canvas.getContext('2d'); + return ctx; +} diff --git a/packages/widgets/Pianoroll.jsx b/packages/widgets/Pianoroll.jsx index bf7c145c..f28444ca 100644 --- a/packages/widgets/Pianoroll.jsx +++ b/packages/widgets/Pianoroll.jsx @@ -1,29 +1,13 @@ import { Pattern } from '@strudel/core'; import { registerWidget } from '@strudel/transpiler'; -import { customElement } from 'solid-element'; +import { getWidgetDrawContext } from './Canvas.jsx'; -customElement('strudel-pianoroll', { options: JSON.stringify('{}') }, (props, { element }) => { - return ; -}); - -registerWidget('roll', 'strudel-pianoroll'); +registerWidget('roll', 'strudel-canvas'); Pattern.prototype.roll = function (id, options = { fold: 1 }) { // TODO: remove setTimeout... setTimeout(() => { - let el = document.getElementById(id); - if (!el) { - console.log('widget not found...'); - return this; - } - const { width = 400, height = 100 } = options; - const canvas = el?.shadowRoot.firstChild; - const pixelRatio = window.devicePixelRatio; - canvas.width = width * pixelRatio; - canvas.height = height * pixelRatio; - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - const ctx = canvas.getContext('2d'); + const ctx = getWidgetDrawContext(id, options); this.pianoroll({ ...options, ctx }); }); return this; diff --git a/packages/widgets/index.mjs b/packages/widgets/index.mjs index 80d01ab5..e42e43b1 100644 --- a/packages/widgets/index.mjs +++ b/packages/widgets/index.mjs @@ -1,2 +1,3 @@ export * from './Claviature.jsx'; export * from './Pianoroll.jsx'; +export * from './Canvas.jsx'; From a8712fd8ce5db32152af54de8cb014b8b24dcedf Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 09:48:04 +0100 Subject: [PATCH 14/30] remove this mess --- packages/core/ui.mjs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/core/ui.mjs b/packages/core/ui.mjs index 9343e062..86ceb286 100644 --- a/packages/core/ui.mjs +++ b/packages/core/ui.mjs @@ -4,19 +4,6 @@ Copyright (C) 2022 Strudel contributors - see . */ -import { getTime } from './time.mjs'; - -function frame(callback) { - if (window.strudelAnimation) { - cancelAnimationFrame(window.strudelAnimation); - } - const animate = (animationTime) => { - callback(animationTime, getTime()); - window.strudelAnimation = requestAnimationFrame(animate); - }; - requestAnimationFrame(animate); -} - export const backgroundImage = function (src, animateOptions = {}) { const container = document.getElementById('code'); const bg = 'background-image:url(' + src + ');background-size:contain;'; @@ -35,11 +22,6 @@ export const backgroundImage = function (src, animateOptions = {}) { if (funcOptions.length === 0) { return; } - frame((_, t) => - funcOptions.forEach(([option, value]) => { - handleOption(option, value(t)); - }), - ); }; export const cleanupUi = () => { From 6dce9d5deb0343852b3d09fbe78c4f7718b0d823 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 09:49:00 +0100 Subject: [PATCH 15/30] support multiple animationFrames + break out spiral draw logic --- packages/draw/draw.mjs | 21 +++++---- packages/draw/pianoroll.mjs | 3 +- packages/draw/spiral.mjs | 88 +++++++++++++++++++------------------ 3 files changed, 58 insertions(+), 54 deletions(-) diff --git a/packages/draw/draw.mjs b/packages/draw/draw.mjs index 63245baf..910ac08c 100644 --- a/packages/draw/draw.mjs +++ b/packages/draw/draw.mjs @@ -29,12 +29,13 @@ export const getDrawContext = (id = 'test-canvas', options) => { return canvas.getContext(contextType); }; -Pattern.prototype.draw = function (callback, { from, to, onQuery, ctx } = {}) { +let animationFrames = {}; +Pattern.prototype.draw = function (callback, { id = 'std', from, to, onQuery, ctx } = {}) { if (typeof window === 'undefined') { return this; } - if (window.strudelAnimation) { - cancelAnimationFrame(window.strudelAnimation); + if (animationFrames[id]) { + cancelAnimationFrame(animationFrames[id]); } ctx = ctx || getDrawContext(); let cycle, @@ -56,7 +57,7 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery, ctx } = {}) { } } callback(ctx, events, t, time); - window.strudelAnimation = requestAnimationFrame(animate); + animationFrames[id] = requestAnimationFrame(animate); }; requestAnimationFrame(animate); return this; @@ -64,18 +65,18 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery, ctx } = {}) { // this is a more generic helper to get a rendering callback for the currently active haps // TODO: this misses events that are prolonged with clip or duration (would need state) -Pattern.prototype.onFrame = function (fn, offset = 0) { +Pattern.prototype.onFrame = function (id, fn, offset = 0) { if (typeof window === 'undefined') { return this; } - if (window.strudelAnimation) { - cancelAnimationFrame(window.strudelAnimation); + if (animationFrames[id]) { + cancelAnimationFrame(animationFrames[id]); } const animate = () => { const t = getTime() + offset; const haps = this.queryArc(t, t); fn(haps, t, this); - window.strudelAnimation = requestAnimationFrame(animate); + animationFrames[id] = requestAnimationFrame(animate); }; requestAnimationFrame(animate); return this; @@ -84,9 +85,7 @@ Pattern.prototype.onFrame = function (fn, offset = 0) { export const cleanupDraw = (clearScreen = true) => { const ctx = getDrawContext(); clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.width); - if (window.strudelAnimation) { - cancelAnimationFrame(window.strudelAnimation); - } + Object.values(animationFrames).forEach((id) => cancelAnimationFrame(id)); if (window.strudelScheduler) { clearInterval(window.strudelScheduler); } diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index b032ef1a..29c61d18 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -30,7 +30,7 @@ const getValue = (e) => { }; Pattern.prototype.pianoroll = function (options = {}) { - let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false, ctx } = options; + let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false, ctx, id } = options; let from = -cycles * playhead; let to = cycles * (1 - playhead); @@ -50,6 +50,7 @@ Pattern.prototype.pianoroll = function (options = {}) { from: from - overscan, to: to + overscan, ctx, + id, }, ); return this; diff --git a/packages/draw/spiral.mjs b/packages/draw/spiral.mjs index 00bd62ec..4762e843 100644 --- a/packages/draw/spiral.mjs +++ b/packages/draw/spiral.mjs @@ -49,7 +49,7 @@ function spiralSegment(options) { ctx.stroke(); } -Pattern.prototype.spiral = function (options = {}) { +function drawSpiral(options) { const { stretch = 1, size = 80, @@ -65,54 +65,58 @@ Pattern.prototype.spiral = function (options = {}) { colorizeInactive = 0, fade = true, // logSpiral = true, + ctx, + time, + haps, + drawTime, } = options; - function spiral({ ctx, time, haps, drawTime }) { - const [w, h] = [ctx.canvas.width, ctx.canvas.height]; - ctx.clearRect(0, 0, w * 2, h * 2); - const [cx, cy] = [w / 2, h / 2]; - const settings = { - margin: size / stretch, - cx, - cy, - stretch, - cap, - thickness, - }; + const [w, h] = [ctx.canvas.width, ctx.canvas.height]; + ctx.clearRect(0, 0, w * 2, h * 2); + const [cx, cy] = [w / 2, h / 2]; + const settings = { + margin: size / stretch, + cx, + cy, + stretch, + cap, + thickness, + }; - const playhead = { - ...settings, - thickness: playheadThickness, - from: inset - playheadLength, - to: inset, - color: playheadColor, - }; + const playhead = { + ...settings, + thickness: playheadThickness, + from: inset - playheadLength, + to: inset, + color: playheadColor, + }; - const [min] = drawTime; - const rotate = steady * time; - haps.forEach((hap) => { - const isActive = hap.whole.begin <= time && hap.endClipped > time; - const from = hap.whole.begin - time + inset; - const to = hap.endClipped - time + inset - padding; - const { color } = hap.context; - const opacity = fade ? 1 - Math.abs((hap.whole.begin - time) / min) : 1; - spiralSegment({ - ctx, - ...settings, - from, - to, - rotate, - color: colorizeInactive || isActive ? color : inactiveColor, - fromOpacity: opacity, - toOpacity: opacity, - }); - }); + const [min] = drawTime; + const rotate = steady * time; + haps.forEach((hap) => { + const isActive = hap.whole.begin <= time && hap.endClipped > time; + const from = hap.whole.begin - time + inset; + const to = hap.endClipped - time + inset - padding; + const { color } = hap.context; + const opacity = fade ? 1 - Math.abs((hap.whole.begin - time) / min) : 1; spiralSegment({ ctx, - ...playhead, + ...settings, + from, + to, rotate, + color: colorizeInactive || isActive ? color : inactiveColor, + fromOpacity: opacity, + toOpacity: opacity, }); - } + }); + spiralSegment({ + ctx, + ...playhead, + rotate, + }); +} - return this.onPaint((ctx, time, haps, drawTime) => spiral({ ctx, time, haps, drawTime })); +Pattern.prototype.spiral = function (options = {}) { + return this.onPaint((ctx, time, haps, drawTime) => drawSpiral({ ctx, time, haps, drawTime, ...options })); }; From 8742d50bad8edd6e43980c06f5324f5bb2207e0d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 09:50:17 +0100 Subject: [PATCH 16/30] simplify widget creation + fix bugs --- packages/codemirror/widget.mjs | 12 +++++++----- packages/transpiler/transpiler.mjs | 17 +++++------------ packages/widgets/Canvas.jsx | 21 --------------------- packages/widgets/Claviature.jsx | 14 ++++++-------- packages/widgets/Pianoroll.jsx | 14 -------------- packages/widgets/canvas.mjs | 23 +++++++++++++++++++++++ packages/widgets/index.mjs | 4 ++-- packages/widgets/package.json | 1 + packages/widgets/registry.mjs | 11 +++++++++++ pnpm-lock.yaml | 3 +++ 10 files changed, 58 insertions(+), 62 deletions(-) delete mode 100644 packages/widgets/Canvas.jsx delete mode 100644 packages/widgets/Pianoroll.jsx create mode 100644 packages/widgets/canvas.mjs create mode 100644 packages/widgets/registry.mjs diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index c0bd9a24..8499e28e 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -53,6 +53,12 @@ const widgetField = StateField.define( }, ); +const widgetElements = {}; +export function setWidget(id, el) { + widgetElements[id] = el; + el.id = id; +} + export class BlockWidget extends WidgetType { constructor(col, type) { super(); @@ -64,11 +70,7 @@ export class BlockWidget extends WidgetType { } toDOM() { const id = getWidgetID(this.col); // matches id generated in transpiler - let el = document.getElementById(id); - if (!el) { - el = document.createElement(this.type); - el.id = id; - } + const el = widgetElements[id]; return el; } ignoreEvent(e) { diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index db805e14..475b1e96 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -3,16 +3,9 @@ import { parse } from 'acorn'; import escodegen from 'escodegen'; import { walk } from 'estree-walker'; -let widgetComponents = {}; -// this function allows registering a pattern method name and component name -// e.g. register('claviature', 'strudel-claviature') -// .. will map the Pattern method 'claviature' to the web component 'strudel-claviature' -// the transpiler will turn .claviature(...args) into .claviature(id, ...args) -// the widgetPlugin of @strudel/codemirror will automatically create an instance of 'strudel-claviature' -// .. so you only have to implement the actual .claviature method (or what you've called it..) - -export function registerWidget(name, tagName) { - widgetComponents[name] = tagName; +let widgetMethods = []; +export function registerWidgetType(type) { + widgetMethods.push(type); } export function transpiler(input, options = {}) { @@ -63,7 +56,7 @@ export function transpiler(input, options = {}) { emitWidgets && widgets.push({ to: node.end, - type: widgetComponents[node.callee.property.name], + type: node.callee.property.name, }); return this.replace(widgetWithLocation(node)); } @@ -131,7 +124,7 @@ function isSliderFunction(node) { } function isWidgetMethod(node) { - return node.type === 'CallExpression' && Object.keys(widgetComponents).includes(node.callee.property?.name); + return node.type === 'CallExpression' && widgetMethods.includes(node.callee.property?.name); } function sliderWithLocation(node) { diff --git a/packages/widgets/Canvas.jsx b/packages/widgets/Canvas.jsx deleted file mode 100644 index 9b6b668c..00000000 --- a/packages/widgets/Canvas.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import { customElement } from 'solid-element'; - -customElement('strudel-canvas', {}, () => { - return ; -}); - -export function getWidgetDrawContext(id, options) { - let el = document.getElementById(id); - if (!el) { - console.warn(`widget with id ${id} not found in the DOM`); - return; - } - const { width = 300, height = 100, pixelRatio = window.devicePixelRatio } = options || {}; - const canvas = el?.shadowRoot.firstChild; - canvas.width = width * pixelRatio; - canvas.height = height * pixelRatio; - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - const ctx = canvas.getContext('2d'); - return ctx; -} diff --git a/packages/widgets/Claviature.jsx b/packages/widgets/Claviature.jsx index 33777be4..ed06e671 100644 --- a/packages/widgets/Claviature.jsx +++ b/packages/widgets/Claviature.jsx @@ -2,8 +2,7 @@ import { For } from 'solid-js'; import { customElement } from 'solid-element'; import { getClaviature } from 'claviature'; import { Dynamic } from 'solid-js/web'; -import { Pattern } from '@strudel/core'; -import { registerWidget } from '@strudel/transpiler'; +import { registerWidget } from './registry.mjs'; let defaultOptions = { range: ['A1', 'C6'], @@ -31,12 +30,11 @@ customElement('strudel-claviature', { options: JSON.stringify(defaultOptions) }, ); }); -registerWidget('claviature', 'strudel-claviature'); - -Pattern.prototype.claviature = function (id, options = {}) { - return this.onFrame((haps) => { +registerWidget('claviature', (id, options = {}, pat) => { + const el = document.getElementById(id) || document.createElement('strudel-claviature'); + setWidget(id, el); + return pat.onFrame(id, (haps) => { const colorize = haps.map((h) => ({ keys: [h.value.note], color: h.context?.color || 'steelblue' })); - let el = document.getElementById(id); el?.setAttribute( 'options', JSON.stringify({ @@ -46,4 +44,4 @@ Pattern.prototype.claviature = function (id, options = {}) { }), ); }); -}; +}); diff --git a/packages/widgets/Pianoroll.jsx b/packages/widgets/Pianoroll.jsx deleted file mode 100644 index f28444ca..00000000 --- a/packages/widgets/Pianoroll.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Pattern } from '@strudel/core'; -import { registerWidget } from '@strudel/transpiler'; -import { getWidgetDrawContext } from './Canvas.jsx'; - -registerWidget('roll', 'strudel-canvas'); - -Pattern.prototype.roll = function (id, options = { fold: 1 }) { - // TODO: remove setTimeout... - setTimeout(() => { - const ctx = getWidgetDrawContext(id, options); - this.pianoroll({ ...options, ctx }); - }); - return this; -}; diff --git a/packages/widgets/canvas.mjs b/packages/widgets/canvas.mjs new file mode 100644 index 00000000..e218cd76 --- /dev/null +++ b/packages/widgets/canvas.mjs @@ -0,0 +1,23 @@ +import { registerWidget } from './registry.mjs'; +import { setWidget } from '@strudel/codemirror'; + +function createCanvasWidget(id, options) { + const { width = 300, height = 100, pixelRatio = window.devicePixelRatio } = options || {}; + let canvas = document.getElementById(id) || document.createElement('canvas'); + canvas.width = width * pixelRatio; + canvas.height = height * pixelRatio; + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + setWidget(id, canvas); + return canvas; +} + +registerWidget('roll', (id, options = { fold: 1 }, pat) => { + const ctx = createCanvasWidget(id, options).getContext('2d'); + return pat.pianoroll({ ...options, ctx, id }); +}); + +registerWidget('twist', (id, options = {}, pat) => { + const ctx = createCanvasWidget(id, options).getContext('2d'); + return pat.spiral({ ...options, ctx, size: 50, id }); +}); diff --git a/packages/widgets/index.mjs b/packages/widgets/index.mjs index e42e43b1..5ebfa67e 100644 --- a/packages/widgets/index.mjs +++ b/packages/widgets/index.mjs @@ -1,3 +1,3 @@ export * from './Claviature.jsx'; -export * from './Pianoroll.jsx'; -export * from './Canvas.jsx'; +export * from './canvas.mjs'; +export * from './registry.mjs'; diff --git a/packages/widgets/package.json b/packages/widgets/package.json index c77c2712..775e4c17 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -30,6 +30,7 @@ "@strudel/core": "workspace:*", "@strudel/transpiler": "workspace:*", "@strudel/draw": "workspace:*", + "@strudel/codemirror": "workspace:*", "claviature": "^0.1.0", "solid-element": "^1.8.0", "solid-js": "^1.8.15", diff --git a/packages/widgets/registry.mjs b/packages/widgets/registry.mjs new file mode 100644 index 00000000..0d1a3e8f --- /dev/null +++ b/packages/widgets/registry.mjs @@ -0,0 +1,11 @@ +import { registerWidgetType } from '@strudel/transpiler'; +import { Pattern } from '@strudel/core'; + +export function registerWidget(type, fn) { + registerWidgetType(type); + if (fn) { + Pattern.prototype[type] = function (id, options = { fold: 1 }) { + return fn(id, options, this); + }; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee159ab2..27da53e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -490,6 +490,9 @@ importers: packages/widgets: dependencies: + '@strudel/codemirror': + specifier: workspace:* + version: link:../codemirror '@strudel/core': specifier: workspace:* version: link:../core From 4a95bf67dad444d84b98370082daf52e32e224cf Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 10:00:07 +0100 Subject: [PATCH 17/30] move widget registry to codemirror package + add transpiler as dependency to codemirror --- packages/codemirror/package.json | 1 + packages/codemirror/widget.mjs | 14 ++++++++++++++ packages/widgets/Claviature.jsx | 2 +- packages/widgets/canvas.mjs | 9 ++++----- packages/widgets/index.mjs | 1 - packages/widgets/registry.mjs | 11 ----------- pnpm-lock.yaml | 3 +++ 7 files changed, 23 insertions(+), 18 deletions(-) delete mode 100644 packages/widgets/registry.mjs diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index 0b39ebdd..133c4148 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -46,6 +46,7 @@ "@replit/codemirror-vscode-keymap": "^6.0.2", "@strudel/core": "workspace:*", "@strudel/draw": "workspace:*", + "@strudel/transpiler": "workspace:*", "@uiw/codemirror-themes": "^4.21.21", "@uiw/codemirror-themes-all": "^4.21.21", "nanostores": "^0.9.5" diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index 8499e28e..e2652327 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -1,5 +1,7 @@ import { StateEffect, StateField } from '@codemirror/state'; import { Decoration, EditorView, WidgetType } from '@codemirror/view'; +import { registerWidgetType } from '@strudel/transpiler'; +import { Pattern } from '@strudel/core'; const getWidgetID = (from) => `widget_${from}`; @@ -79,3 +81,15 @@ export class BlockWidget extends WidgetType { } export const widgetPlugin = [widgetField]; + +// widget implementer API to create a new widget type +export function registerWidget(type, fn) { + registerWidgetType(type); + if (fn) { + Pattern.prototype[type] = function (id, options = { fold: 1 }) { + // fn is expected to create a dom element and call setWidget(id, el); + // fn should also return the pattern + return fn(id, options, this); + }; + } +} diff --git a/packages/widgets/Claviature.jsx b/packages/widgets/Claviature.jsx index ed06e671..e4bda32b 100644 --- a/packages/widgets/Claviature.jsx +++ b/packages/widgets/Claviature.jsx @@ -2,7 +2,7 @@ import { For } from 'solid-js'; import { customElement } from 'solid-element'; import { getClaviature } from 'claviature'; import { Dynamic } from 'solid-js/web'; -import { registerWidget } from './registry.mjs'; +import { registerWidget } from '@strudel/codemirror'; let defaultOptions = { range: ['A1', 'C6'], diff --git a/packages/widgets/canvas.mjs b/packages/widgets/canvas.mjs index e218cd76..4dba01a0 100644 --- a/packages/widgets/canvas.mjs +++ b/packages/widgets/canvas.mjs @@ -1,7 +1,6 @@ -import { registerWidget } from './registry.mjs'; -import { setWidget } from '@strudel/codemirror'; +import { registerWidget, setWidget } from '@strudel/codemirror'; -function createCanvasWidget(id, options) { +function getCanvasWidget(id, options) { const { width = 300, height = 100, pixelRatio = window.devicePixelRatio } = options || {}; let canvas = document.getElementById(id) || document.createElement('canvas'); canvas.width = width * pixelRatio; @@ -13,11 +12,11 @@ function createCanvasWidget(id, options) { } registerWidget('roll', (id, options = { fold: 1 }, pat) => { - const ctx = createCanvasWidget(id, options).getContext('2d'); + const ctx = getCanvasWidget(id, options).getContext('2d'); return pat.pianoroll({ ...options, ctx, id }); }); registerWidget('twist', (id, options = {}, pat) => { - const ctx = createCanvasWidget(id, options).getContext('2d'); + const ctx = getCanvasWidget(id, options).getContext('2d'); return pat.spiral({ ...options, ctx, size: 50, id }); }); diff --git a/packages/widgets/index.mjs b/packages/widgets/index.mjs index 5ebfa67e..ad6e7d1f 100644 --- a/packages/widgets/index.mjs +++ b/packages/widgets/index.mjs @@ -1,3 +1,2 @@ export * from './Claviature.jsx'; export * from './canvas.mjs'; -export * from './registry.mjs'; diff --git a/packages/widgets/registry.mjs b/packages/widgets/registry.mjs deleted file mode 100644 index 0d1a3e8f..00000000 --- a/packages/widgets/registry.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import { registerWidgetType } from '@strudel/transpiler'; -import { Pattern } from '@strudel/core'; - -export function registerWidget(type, fn) { - registerWidgetType(type); - if (fn) { - Pattern.prototype[type] = function (id, options = { fold: 1 }) { - return fn(id, options, this); - }; - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 27da53e6..00608001 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -184,6 +184,9 @@ importers: '@strudel/draw': specifier: workspace:* version: link:../draw + '@strudel/transpiler': + specifier: workspace:* + version: link:../transpiler '@uiw/codemirror-themes': specifier: ^4.21.21 version: 4.21.21(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0) From dfd22b5e447bf3582eb0e1a22e96526b9f9ac3d0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 11:04:35 +0100 Subject: [PATCH 18/30] fix: claviature height + better defaults + break out solid web component creation --- packages/widgets/Claviature.jsx | 14 ++++++-------- packages/widgets/canvas.mjs | 11 ++++++----- packages/widgets/solid.mjs | 13 +++++++++++++ 3 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 packages/widgets/solid.mjs diff --git a/packages/widgets/Claviature.jsx b/packages/widgets/Claviature.jsx index e4bda32b..5624280a 100644 --- a/packages/widgets/Claviature.jsx +++ b/packages/widgets/Claviature.jsx @@ -3,12 +3,9 @@ import { customElement } from 'solid-element'; import { getClaviature } from 'claviature'; import { Dynamic } from 'solid-js/web'; import { registerWidget } from '@strudel/codemirror'; +import { getSolidWidget } from './solid.mjs'; -let defaultOptions = { - range: ['A1', 'C6'], -}; - -customElement('strudel-claviature', { options: JSON.stringify(defaultOptions) }, (props, { element }) => { +customElement('strudel-claviature', { options: '{}' }, (props, { element }) => { let svg = () => { let c = getClaviature({ options: JSON.parse(props.options), @@ -31,11 +28,12 @@ customElement('strudel-claviature', { options: JSON.stringify(defaultOptions) }, }); registerWidget('claviature', (id, options = {}, pat) => { - const el = document.getElementById(id) || document.createElement('strudel-claviature'); - setWidget(id, el); + options = { range: ['A0', 'C6'], scaleY: 1, scaleY: 0.5, scaleX: 0.5, ...options }; + const height = (options.upperHeight + options.lowerHeight) * options.scaleY; + const el = getSolidWidget('strudel-claviature', id, { ...options, height }); return pat.onFrame(id, (haps) => { const colorize = haps.map((h) => ({ keys: [h.value.note], color: h.context?.color || 'steelblue' })); - el?.setAttribute( + el.setAttribute( 'options', JSON.stringify({ ...options, diff --git a/packages/widgets/canvas.mjs b/packages/widgets/canvas.mjs index 4dba01a0..5fd15356 100644 --- a/packages/widgets/canvas.mjs +++ b/packages/widgets/canvas.mjs @@ -1,7 +1,7 @@ import { registerWidget, setWidget } from '@strudel/codemirror'; -function getCanvasWidget(id, options) { - const { width = 300, height = 100, pixelRatio = window.devicePixelRatio } = options || {}; +function getCanvasWidget(id, options = {}) { + const { width = 500, height = 60, pixelRatio = window.devicePixelRatio } = options; let canvas = document.getElementById(id) || document.createElement('canvas'); canvas.width = width * pixelRatio; canvas.height = height * pixelRatio; @@ -11,12 +11,13 @@ function getCanvasWidget(id, options) { return canvas; } -registerWidget('roll', (id, options = { fold: 1 }, pat) => { +registerWidget('roll', (id, options = {}, pat) => { const ctx = getCanvasWidget(id, options).getContext('2d'); - return pat.pianoroll({ ...options, ctx, id }); + return pat.pianoroll({ fold: 1, ...options, ctx, id }); }); registerWidget('twist', (id, options = {}, pat) => { + options = { width: 200, height: 200, size: 36, ...options }; const ctx = getCanvasWidget(id, options).getContext('2d'); - return pat.spiral({ ...options, ctx, size: 50, id }); + return pat.spiral({ ...options, ctx, id }); }); diff --git a/packages/widgets/solid.mjs b/packages/widgets/solid.mjs new file mode 100644 index 00000000..b8403249 --- /dev/null +++ b/packages/widgets/solid.mjs @@ -0,0 +1,13 @@ +import { setWidget } from '@strudel/codemirror'; + +export function getSolidWidget(type, id, options) { + let el = document.getElementById(id); + if (!el) { + el = document.createElement('div'); + const c = document.createElement(type); + el.appendChild(c); + } + el.height = options.height || 200; + setWidget(id, el); + return el?.firstChild; +} From 1043baf08a0da3acd89997f9ba71e8caff05c29e Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 12:24:03 +0100 Subject: [PATCH 19/30] fix: catch errors in pianoroll getValue --- packages/draw/pianoroll.mjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index 29c61d18..2ec2d7ee 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -18,7 +18,13 @@ const getValue = (e) => { } note = note ?? n; if (typeof note === 'string') { - return noteToMidi(note); + try { + // TODO: n(run(32)).scale("D:minor") fails when trying to query negative time.. + return noteToMidi(note); + } catch (err) { + // console.warn(`error converting note to midi: ${err}`); // this spams to crazy + return 0; + } } if (typeof note === 'number') { return note; From d9d05e21c06ed1b599a99e2f6d6f0fe4017091ef Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Mar 2024 12:24:11 +0100 Subject: [PATCH 20/30] full size piano by default --- packages/widgets/Claviature.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/widgets/Claviature.jsx b/packages/widgets/Claviature.jsx index 5624280a..dc216d56 100644 --- a/packages/widgets/Claviature.jsx +++ b/packages/widgets/Claviature.jsx @@ -28,7 +28,7 @@ customElement('strudel-claviature', { options: '{}' }, (props, { element }) => { }); registerWidget('claviature', (id, options = {}, pat) => { - options = { range: ['A0', 'C6'], scaleY: 1, scaleY: 0.5, scaleX: 0.5, ...options }; + options = { range: ['A0', 'C8'], scaleY: 1, scaleY: 0.5, scaleX: 0.5, ...options }; const height = (options.upperHeight + options.lowerHeight) * options.scaleY; const el = getSolidWidget('strudel-claviature', id, { ...options, height }); return pat.onFrame(id, (haps) => { From f4032dad22a5a8af595aabd796032c734dc321e5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 16 Mar 2024 02:14:24 +0100 Subject: [PATCH 21/30] inline multichannel scopes --- packages/superdough/superdough.mjs | 35 ++++++++++++++------------- packages/webaudio/scope.mjs | 38 ++++++++++++++++++------------ packages/widgets/canvas.mjs | 6 +++++ 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 3e6448d9..9aa8ee3c 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -215,35 +215,34 @@ function getReverb(orbit, duration, fade, lp, dim, ir) { return reverbs[orbit]; } -export let analyser, analyserData /* s = {} */; +export let analysers = {}, + analysersData = {}; -export function getAnalyser(/* orbit, */ fftSize = 2048) { - if (!analyser /*s [orbit] */) { +export function getAnalyserById(id, fftSize = 1024) { + if (!analysers[id]) { const analyserNode = getAudioContext().createAnalyser(); analyserNode.fftSize = fftSize; // getDestination().connect(analyserNode); - analyser /* s[orbit] */ = analyserNode; - //analyserData = new Uint8Array(analyser.frequencyBinCount); - analyserData = new Float32Array(analyser.frequencyBinCount); + analysers[id] = analyserNode; + analysersData[id] = new Float32Array(analysers[id].frequencyBinCount); } - if (analyser /* s[orbit] */.fftSize !== fftSize) { - analyser /* s[orbit] */.fftSize = fftSize; - //analyserData = new Uint8Array(analyser.frequencyBinCount); - analyserData = new Float32Array(analyser.frequencyBinCount); + if (analysers[id].fftSize !== fftSize) { + analysers[id].fftSize = fftSize; + analysersData[id] = new Float32Array(analysers[id].frequencyBinCount); } - return analyser /* s[orbit] */; + return analysers[id]; } -export function getAnalyzerData(type = 'time') { +export function getAnalyzerData(type = 'time', id = 1) { const getter = { - time: () => analyser?.getFloatTimeDomainData(analyserData), - frequency: () => analyser?.getFloatFrequencyData(analyserData), + time: () => analysers[id]?.getFloatTimeDomainData(analysersData[id]), + frequency: () => analyser[id]?.getFloatFrequencyData(analysersData[id]), }[type]; if (!getter) { throw new Error(`getAnalyzerData: ${type} not supported. use one of ${Object.keys(getter).join(', ')}`); } getter(); - return analyserData; + return analysersData[id]; } function effectSend(input, effect, wet) { @@ -256,6 +255,8 @@ function effectSend(input, effect, wet) { export function resetGlobalEffects() { delays = {}; reverbs = {}; + analysers = {}; + analysersData = {}; } export const superdough = async (value, deadline, hapDuration) => { @@ -512,8 +513,8 @@ export const superdough = async (value, deadline, hapDuration) => { // analyser let analyserSend; if (analyze) { - const analyserNode = getAnalyser(/* orbit, */ 2 ** (fft + 5)); - analyserSend = effectSend(post, analyserNode, analyze); + const analyserNode = getAnalyserById(analyze, 2 ** (fft + 5)); + analyserSend = effectSend(post, analyserNode, 1); } // connect chain elements together diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index 0371366c..4337ec72 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -1,13 +1,21 @@ import { Pattern, clamp } from '@strudel/core'; import { getDrawContext } from '../draw/index.mjs'; -import { analyser, getAnalyzerData } from 'superdough'; +import { getAnalyzerData } from 'superdough'; export function drawTimeScope( analyser, - { align = true, color = 'white', thickness = 3, scale = 0.25, pos = 0.75, trigger = 0 } = {}, + { + align = true, + color = 'white', + thickness = 3, + scale = 0.25, + pos = 0.75, + trigger = 0, + ctx = getDrawContext(), + id = 1, + } = {}, ) { - const ctx = getDrawContext(); - const dataArray = getAnalyzerData('time'); + const dataArray = getAnalyzerData('time', id); ctx.lineWidth = thickness; ctx.strokeStyle = color; @@ -39,10 +47,9 @@ export function drawTimeScope( export function drawFrequencyScope( analyser, - { color = 'white', scale = 0.25, pos = 0.75, lean = 0.5, min = -150, max = 0 } = {}, + { color = 'white', scale = 0.25, pos = 0.75, lean = 0.5, min = -150, max = 0, ctx = getDrawContext(), id = 1 } = {}, ) { - const dataArray = getAnalyzerData('frequency'); - const ctx = getDrawContext(); + const dataArray = getAnalyzerData('frequency', id); const canvas = ctx.canvas; ctx.fillStyle = color; @@ -61,8 +68,7 @@ export function drawFrequencyScope( } } -function clearScreen(smear = 0, smearRGB = `0,0,0`) { - const ctx = getDrawContext(); +function clearScreen(smear = 0, smearRGB = `0,0,0`, ctx = getDrawContext()) { if (!smear) { ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); } else { @@ -84,9 +90,10 @@ function clearScreen(smear = 0, smearRGB = `0,0,0`) { * s("sawtooth").fscope() */ Pattern.prototype.fscope = function (config = {}) { - return this.analyze(1).draw(() => { - clearScreen(config.smear); - analyser && drawFrequencyScope(analyser, config); + let id = config.id ?? 1; + return this.analyze(id).draw(() => { + clearScreen(config.smear, '0,0,0', config.ctx); + analysers[id] && drawFrequencyScope(analyser, config); }); }; @@ -105,9 +112,10 @@ Pattern.prototype.fscope = function (config = {}) { * s("sawtooth").scope() */ Pattern.prototype.tscope = function (config = {}) { - return this.analyze(1).draw(() => { - clearScreen(config.smear); - analyser && drawTimeScope(analyser, config); + let id = config.id ?? 1; + return this.analyze(id).draw(() => { + clearScreen(config.smear, '0,0,0', config.ctx); + analysers[id] && drawTimeScope(analysers[id], config); }); }; diff --git a/packages/widgets/canvas.mjs b/packages/widgets/canvas.mjs index 5fd15356..14aa8911 100644 --- a/packages/widgets/canvas.mjs +++ b/packages/widgets/canvas.mjs @@ -21,3 +21,9 @@ registerWidget('twist', (id, options = {}, pat) => { const ctx = getCanvasWidget(id, options).getContext('2d'); return pat.spiral({ ...options, ctx, id }); }); + +registerWidget('osci', (id, options = {}, pat) => { + options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; + const ctx = getCanvasWidget(id, options).getContext('2d'); + return pat.scope({ ...options, ctx, id }); +}); From ef284e9d131a7421702523a5793e43b67a470b5b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 16 Mar 2024 02:16:50 +0100 Subject: [PATCH 22/30] fix: bugs catched by linter --- packages/superdough/superdough.mjs | 2 +- packages/webaudio/scope.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 9aa8ee3c..d0c276b0 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -236,7 +236,7 @@ export function getAnalyserById(id, fftSize = 1024) { export function getAnalyzerData(type = 'time', id = 1) { const getter = { time: () => analysers[id]?.getFloatTimeDomainData(analysersData[id]), - frequency: () => analyser[id]?.getFloatFrequencyData(analysersData[id]), + frequency: () => analysers[id]?.getFloatFrequencyData(analysersData[id]), }[type]; if (!getter) { throw new Error(`getAnalyzerData: ${type} not supported. use one of ${Object.keys(getter).join(', ')}`); diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index 4337ec72..309b4ab7 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -1,6 +1,6 @@ import { Pattern, clamp } from '@strudel/core'; import { getDrawContext } from '../draw/index.mjs'; -import { getAnalyzerData } from 'superdough'; +import { analysers, getAnalyzerData } from 'superdough'; export function drawTimeScope( analyser, @@ -93,7 +93,7 @@ Pattern.prototype.fscope = function (config = {}) { let id = config.id ?? 1; return this.analyze(id).draw(() => { clearScreen(config.smear, '0,0,0', config.ctx); - analysers[id] && drawFrequencyScope(analyser, config); + analysers[id] && drawFrequencyScope(analysers[id], config); }); }; From dd78dc96060c9feb7eabd171afe95b563be831f3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 16 Mar 2024 02:42:26 +0100 Subject: [PATCH 23/30] fix: animation frame cleanup --- packages/draw/draw.mjs | 19 ++++++++++++------- packages/webaudio/scope.mjs | 22 ++++++++++++++-------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/packages/draw/draw.mjs b/packages/draw/draw.mjs index 910ac08c..4e84878f 100644 --- a/packages/draw/draw.mjs +++ b/packages/draw/draw.mjs @@ -30,13 +30,20 @@ export const getDrawContext = (id = 'test-canvas', options) => { }; let animationFrames = {}; +function stopAnimationFrame(id) { + if (animationFrames[id] !== undefined) { + cancelAnimationFrame(animationFrames[id]); + delete animationFrames[id]; + } +} +function stopAllAnimations() { + Object.keys(animationFrames).forEach((id) => stopAnimationFrame(id)); +} Pattern.prototype.draw = function (callback, { id = 'std', from, to, onQuery, ctx } = {}) { if (typeof window === 'undefined') { return this; } - if (animationFrames[id]) { - cancelAnimationFrame(animationFrames[id]); - } + stopAnimationFrame(id); ctx = ctx || getDrawContext(); let cycle, events = []; @@ -69,9 +76,7 @@ Pattern.prototype.onFrame = function (id, fn, offset = 0) { if (typeof window === 'undefined') { return this; } - if (animationFrames[id]) { - cancelAnimationFrame(animationFrames[id]); - } + stopAnimationFrame(id); const animate = () => { const t = getTime() + offset; const haps = this.queryArc(t, t); @@ -85,7 +90,7 @@ Pattern.prototype.onFrame = function (id, fn, offset = 0) { export const cleanupDraw = (clearScreen = true) => { const ctx = getDrawContext(); clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.width); - Object.values(animationFrames).forEach((id) => cancelAnimationFrame(id)); + stopAllAnimations(); if (window.strudelScheduler) { clearInterval(window.strudelScheduler); } diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index 309b4ab7..e4b856f3 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -91,10 +91,13 @@ function clearScreen(smear = 0, smearRGB = `0,0,0`, ctx = getDrawContext()) { */ Pattern.prototype.fscope = function (config = {}) { let id = config.id ?? 1; - return this.analyze(id).draw(() => { - clearScreen(config.smear, '0,0,0', config.ctx); - analysers[id] && drawFrequencyScope(analysers[id], config); - }); + return this.analyze(id).draw( + () => { + clearScreen(config.smear, '0,0,0', config.ctx); + analysers[id] && drawFrequencyScope(analysers[id], config); + }, + { id }, + ); }; /** @@ -113,10 +116,13 @@ Pattern.prototype.fscope = function (config = {}) { */ Pattern.prototype.tscope = function (config = {}) { let id = config.id ?? 1; - return this.analyze(id).draw(() => { - clearScreen(config.smear, '0,0,0', config.ctx); - analysers[id] && drawTimeScope(analysers[id], config); - }); + return this.analyze(id).draw( + () => { + clearScreen(config.smear, '0,0,0', config.ctx); + analysers[id] && drawTimeScope(analysers[id], config); + }, + { id }, + ); }; Pattern.prototype.scope = Pattern.prototype.tscope; From cdc200e1da2f6ff417ff5d2aafcf8f5ef9b7f187 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 16 Mar 2024 03:12:00 +0100 Subject: [PATCH 24/30] draw straight line when no analyser is defined yet + add todo for memory leak --- packages/webaudio/scope.mjs | 26 ++++++++++++++++++++++---- packages/widgets/canvas.mjs | 6 ++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/webaudio/scope.mjs b/packages/webaudio/scope.mjs index e4b856f3..c9ee1f33 100644 --- a/packages/webaudio/scope.mjs +++ b/packages/webaudio/scope.mjs @@ -15,13 +15,23 @@ export function drawTimeScope( id = 1, } = {}, ) { - const dataArray = getAnalyzerData('time', id); - ctx.lineWidth = thickness; ctx.strokeStyle = color; + let canvas = ctx.canvas; + + if (!analyser) { + // if analyser is undefined, draw straight line + // it may be undefined when no sound has been played yet + ctx.beginPath(); + let y = pos * canvas.height; + ctx.moveTo(0, y); + ctx.lineTo(canvas.width, y); + ctx.stroke(); + return; + } + const dataArray = getAnalyzerData('time', id); ctx.beginPath(); - let canvas = ctx.canvas; const bufferSize = analyser.frequencyBinCount; let triggerIndex = align @@ -49,6 +59,14 @@ export function drawFrequencyScope( analyser, { color = 'white', scale = 0.25, pos = 0.75, lean = 0.5, min = -150, max = 0, ctx = getDrawContext(), id = 1 } = {}, ) { + if (!analyser) { + ctx.beginPath(); + let y = pos * canvas.height; + ctx.moveTo(0, y); + ctx.lineTo(canvas.width, y); + ctx.stroke(); + return; + } const dataArray = getAnalyzerData('frequency', id); const canvas = ctx.canvas; @@ -119,7 +137,7 @@ Pattern.prototype.tscope = function (config = {}) { return this.analyze(id).draw( () => { clearScreen(config.smear, '0,0,0', config.ctx); - analysers[id] && drawTimeScope(analysers[id], config); + drawTimeScope(analysers[id], config); }, { id }, ); diff --git a/packages/widgets/canvas.mjs b/packages/widgets/canvas.mjs index 14aa8911..d5a705b9 100644 --- a/packages/widgets/canvas.mjs +++ b/packages/widgets/canvas.mjs @@ -25,5 +25,11 @@ registerWidget('twist', (id, options = {}, pat) => { registerWidget('osci', (id, options = {}, pat) => { options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; const ctx = getCanvasWidget(id, options).getContext('2d'); + // TODO: find way to clear previous analysers to avoid memory leak + // .scope passes id to Pattern.analyze, which is picked up by superdough + // .. which calls getAnalyserById(analyze), creating a new analyzer (+buffer) for that key + // the id here is the col number where the osci function ends (as passed by the transpiler) + // effectively, this means for each evaluation of .osci on a unique col, a new analyser will be created + // the problem is that the old ones will never get deleted.. this might pile up some memory return pat.scope({ ...options, ctx, id }); }); From 16506c5ae2e73540ae8229e7e1cb195a60e76aa4 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Mar 2024 01:58:30 +0100 Subject: [PATCH 25/30] improve scope memory footprint --- packages/codemirror/widget.mjs | 17 +++++++---------- packages/superdough/superdough.mjs | 1 + packages/transpiler/transpiler.mjs | 26 ++++++++++++++++++-------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index e2652327..16f123d7 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -1,10 +1,8 @@ import { StateEffect, StateField } from '@codemirror/state'; import { Decoration, EditorView, WidgetType } from '@codemirror/view'; -import { registerWidgetType } from '@strudel/transpiler'; +import { getWidgetID, registerWidgetType } from '@strudel/transpiler'; import { Pattern } from '@strudel/core'; -const getWidgetID = (from) => `widget_${from}`; - export const addWidget = StateEffect.define({ map: ({ from, to }, change) => { return { from: change.mapPos(from), to: change.mapPos(to) }; @@ -20,12 +18,12 @@ function getWidgets(widgetConfigs) { widgetConfigs // codemirror throws an error if we don't sort .sort((a, b) => a.to - b.to) - .map(({ to, type }) => { + .map((widgetConfig) => { return Decoration.widget({ - widget: new BlockWidget(to, type), + widget: new BlockWidget(widgetConfig), side: 0, block: true, - }).range(to); + }).range(widgetConfig.to); }) ); } @@ -62,16 +60,15 @@ export function setWidget(id, el) { } export class BlockWidget extends WidgetType { - constructor(col, type) { + constructor(widgetConfig) { super(); - this.col = col; - this.type = type; + this.widgetConfig = widgetConfig; } eq() { return true; } toDOM() { - const id = getWidgetID(this.col); // matches id generated in transpiler + const id = getWidgetID(this.widgetConfig); const el = widgetElements[id]; return el; } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index d0c276b0..cf9bd181 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -220,6 +220,7 @@ export let analysers = {}, export function getAnalyserById(id, fftSize = 1024) { if (!analysers[id]) { + // make sure this doesn't happen too often as it piles up garbage const analyserNode = getAudioContext().createAnalyser(); analyserNode.fftSize = fftSize; // getDestination().connect(analyserNode); diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 475b1e96..de06af06 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -53,12 +53,13 @@ export function transpiler(input, options = {}) { return this.replace(sliderWithLocation(node)); } if (isWidgetMethod(node)) { - emitWidgets && - widgets.push({ - to: node.end, - type: node.callee.property.name, - }); - return this.replace(widgetWithLocation(node)); + const widgetConfig = { + to: node.end, + index: widgets.length, + type: node.callee.property.name, + }; + emitWidgets && widgets.push(widgetConfig); + return this.replace(widgetWithLocation(node, widgetConfig)); } if (isBareSamplesCall(node, parent)) { return this.replace(withAwait(node)); @@ -140,8 +141,17 @@ function sliderWithLocation(node) { return node; } -function widgetWithLocation(node) { - const id = 'widget_' + node.end; +export function getWidgetID(widgetConfig) { + // the widget id is used as id for the dom element + as key for eventual resources + // for example, for each scope widget, a new analyser + buffer (large) is created + // that means, if we use the index index of line position as id, less garbage is generated + // return `widget_${widgetConfig.to}`; // more gargabe + //return `widget_${widgetConfig.index}_${widgetConfig.to}`; // also more garbage + return `widget_${widgetConfig.index}`; // less garbage +} + +function widgetWithLocation(node, widgetConfig) { + const id = getWidgetID(widgetConfig); // add loc as identifier to first argument // the sliderWithID function is assumed to be sliderWithID(id, value, min?, max?) node.arguments.unshift({ From 076b6f1c8205d5e5cf0c0b8075c3d3824d218cf9 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Mar 2024 04:00:28 +0100 Subject: [PATCH 26/30] move canvas functions to codemirror package + fix id collisions --- packages/codemirror/widget.mjs | 37 ++++++++++++++++++++++++++++++ packages/transpiler/transpiler.mjs | 2 +- packages/widgets/canvas.mjs | 35 ---------------------------- packages/widgets/index.mjs | 1 - 4 files changed, 38 insertions(+), 37 deletions(-) delete mode 100644 packages/widgets/canvas.mjs diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index 16f123d7..c2c1653b 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -90,3 +90,40 @@ export function registerWidget(type, fn) { }; } } + +// wire up @strudel/draw functions + +function getCanvasWidget(id, options = {}) { + const { width = 500, height = 60, pixelRatio = window.devicePixelRatio } = options; + let canvas = document.getElementById(id) || document.createElement('canvas'); + console.log('canvas', canvas); + canvas.width = width * pixelRatio; + canvas.height = height * pixelRatio; + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + setWidget(id, canvas); + return canvas; +} + +registerWidget('roll', (id, options = {}, pat) => { + const ctx = getCanvasWidget(id, options).getContext('2d'); + return pat.pianoroll({ fold: 1, ...options, ctx, id }); +}); + +registerWidget('twist', (id, options = {}, pat) => { + options = { width: 200, height: 200, size: 36, ...options }; + const ctx = getCanvasWidget(id, options).getContext('2d'); + return pat.spiral({ ...options, ctx, id }); +}); + +registerWidget('osci', (id, options = {}, pat) => { + options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; + const ctx = getCanvasWidget(id, options).getContext('2d'); + // TODO: find way to clear previous analysers to avoid memory leak + // .scope passes id to Pattern.analyze, which is picked up by superdough + // .. which calls getAnalyserById(analyze), creating a new analyzer (+buffer) for that key + // the id here is the col number where the osci function ends (as passed by the transpiler) + // effectively, this means for each evaluation of .osci on a unique col, a new analyser will be created + // the problem is that the old ones will never get deleted.. this might pile up some memory + return pat.scope({ ...options, ctx, id }); +}); diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 380d9ba0..d4b474d9 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -150,7 +150,7 @@ export function getWidgetID(widgetConfig) { // that means, if we use the index index of line position as id, less garbage is generated // return `widget_${widgetConfig.to}`; // more gargabe //return `widget_${widgetConfig.index}_${widgetConfig.to}`; // also more garbage - return `widget_${widgetConfig.index}`; // less garbage + return `widget_${widgetConfig.type}_${widgetConfig.index}`; // less garbage } function widgetWithLocation(node, widgetConfig) { diff --git a/packages/widgets/canvas.mjs b/packages/widgets/canvas.mjs deleted file mode 100644 index d5a705b9..00000000 --- a/packages/widgets/canvas.mjs +++ /dev/null @@ -1,35 +0,0 @@ -import { registerWidget, setWidget } from '@strudel/codemirror'; - -function getCanvasWidget(id, options = {}) { - const { width = 500, height = 60, pixelRatio = window.devicePixelRatio } = options; - let canvas = document.getElementById(id) || document.createElement('canvas'); - canvas.width = width * pixelRatio; - canvas.height = height * pixelRatio; - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - setWidget(id, canvas); - return canvas; -} - -registerWidget('roll', (id, options = {}, pat) => { - const ctx = getCanvasWidget(id, options).getContext('2d'); - return pat.pianoroll({ fold: 1, ...options, ctx, id }); -}); - -registerWidget('twist', (id, options = {}, pat) => { - options = { width: 200, height: 200, size: 36, ...options }; - const ctx = getCanvasWidget(id, options).getContext('2d'); - return pat.spiral({ ...options, ctx, id }); -}); - -registerWidget('osci', (id, options = {}, pat) => { - options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; - const ctx = getCanvasWidget(id, options).getContext('2d'); - // TODO: find way to clear previous analysers to avoid memory leak - // .scope passes id to Pattern.analyze, which is picked up by superdough - // .. which calls getAnalyserById(analyze), creating a new analyzer (+buffer) for that key - // the id here is the col number where the osci function ends (as passed by the transpiler) - // effectively, this means for each evaluation of .osci on a unique col, a new analyser will be created - // the problem is that the old ones will never get deleted.. this might pile up some memory - return pat.scope({ ...options, ctx, id }); -}); diff --git a/packages/widgets/index.mjs b/packages/widgets/index.mjs index ad6e7d1f..281c6ab8 100644 --- a/packages/widgets/index.mjs +++ b/packages/widgets/index.mjs @@ -1,2 +1 @@ export * from './Claviature.jsx'; -export * from './canvas.mjs'; From be77882d70520e7d5d84be252b5c74bcf1321f77 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Mar 2024 04:15:59 +0100 Subject: [PATCH 27/30] rename inline functions to match global ones, prefixed with _ --- packages/codemirror/widget.mjs | 13 +++---------- packages/widgets/Claviature.jsx | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index c2c1653b..f98dcf89 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -96,7 +96,6 @@ export function registerWidget(type, fn) { function getCanvasWidget(id, options = {}) { const { width = 500, height = 60, pixelRatio = window.devicePixelRatio } = options; let canvas = document.getElementById(id) || document.createElement('canvas'); - console.log('canvas', canvas); canvas.width = width * pixelRatio; canvas.height = height * pixelRatio; canvas.style.width = width + 'px'; @@ -105,25 +104,19 @@ function getCanvasWidget(id, options = {}) { return canvas; } -registerWidget('roll', (id, options = {}, pat) => { +registerWidget('_pianoroll', (id, options = {}, pat) => { const ctx = getCanvasWidget(id, options).getContext('2d'); return pat.pianoroll({ fold: 1, ...options, ctx, id }); }); -registerWidget('twist', (id, options = {}, pat) => { +registerWidget('_spiral', (id, options = {}, pat) => { options = { width: 200, height: 200, size: 36, ...options }; const ctx = getCanvasWidget(id, options).getContext('2d'); return pat.spiral({ ...options, ctx, id }); }); -registerWidget('osci', (id, options = {}, pat) => { +registerWidget('_scope', (id, options = {}, pat) => { options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; const ctx = getCanvasWidget(id, options).getContext('2d'); - // TODO: find way to clear previous analysers to avoid memory leak - // .scope passes id to Pattern.analyze, which is picked up by superdough - // .. which calls getAnalyserById(analyze), creating a new analyzer (+buffer) for that key - // the id here is the col number where the osci function ends (as passed by the transpiler) - // effectively, this means for each evaluation of .osci on a unique col, a new analyser will be created - // the problem is that the old ones will never get deleted.. this might pile up some memory return pat.scope({ ...options, ctx, id }); }); diff --git a/packages/widgets/Claviature.jsx b/packages/widgets/Claviature.jsx index dc216d56..584dd09d 100644 --- a/packages/widgets/Claviature.jsx +++ b/packages/widgets/Claviature.jsx @@ -27,7 +27,7 @@ customElement('strudel-claviature', { options: '{}' }, (props, { element }) => { ); }); -registerWidget('claviature', (id, options = {}, pat) => { +registerWidget('_claviature', (id, options = {}, pat) => { options = { range: ['A0', 'C8'], scaleY: 1, scaleY: 0.5, scaleX: 0.5, ...options }; const height = (options.upperHeight + options.lowerHeight) * options.scaleY; const el = getSolidWidget('strudel-claviature', id, { ...options, height }); From 4319c43ceb4a85b67a4ceaa8090f3f7c1337b1e8 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Mar 2024 04:31:30 +0100 Subject: [PATCH 28/30] comment out _spiral for now --- packages/codemirror/widget.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index f98dcf89..facfe1c2 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -109,11 +109,11 @@ registerWidget('_pianoroll', (id, options = {}, pat) => { return pat.pianoroll({ fold: 1, ...options, ctx, id }); }); -registerWidget('_spiral', (id, options = {}, pat) => { +/* registerWidget('_spiral', (id, options = {}, pat) => { options = { width: 200, height: 200, size: 36, ...options }; const ctx = getCanvasWidget(id, options).getContext('2d'); return pat.spiral({ ...options, ctx, id }); -}); +}); */ registerWidget('_scope', (id, options = {}, pat) => { options = { width: 500, height: 60, pos: 0.5, scale: 1, ...options }; From b5312c27dc4e86cd53acafa9509c53bafa4a0bbe Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Mar 2024 04:36:48 +0100 Subject: [PATCH 29/30] move stuff for less changes --- packages/codemirror/slider.mjs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/codemirror/slider.mjs b/packages/codemirror/slider.mjs index 21f744bb..72f95125 100644 --- a/packages/codemirror/slider.mjs +++ b/packages/codemirror/slider.mjs @@ -2,26 +2,9 @@ import { ref, pure } from '@strudel/core'; import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view'; import { StateEffect } from '@codemirror/state'; -export const setSliderWidgets = StateEffect.define(); - -export const updateSliderWidgets = (view, widgets) => { - view.dispatch({ effects: setSliderWidgets.of(widgets) }); -}; - export let sliderValues = {}; const getSliderID = (from) => `slider_${from}`; -function getSliders(widgetConfigs, view) { - return widgetConfigs - .filter((w) => w.type === 'slider') - .map(({ from, to, value, min, max, step }) => { - return Decoration.widget({ - widget: new SliderWidget(value, min, max, from, to, step, view), - side: 0, - }).range(from /* , to */); - }); -} - export class SliderWidget extends WidgetType { constructor(value, min, max, from, to, step, view) { super(); @@ -77,6 +60,23 @@ export class SliderWidget extends WidgetType { } } +export const setSliderWidgets = StateEffect.define(); + +export const updateSliderWidgets = (view, widgets) => { + view.dispatch({ effects: setSliderWidgets.of(widgets) }); +}; + +function getSliders(widgetConfigs, view) { + return widgetConfigs + .filter((w) => w.type === 'slider') + .map(({ from, to, value, min, max, step }) => { + return Decoration.widget({ + widget: new SliderWidget(value, min, max, from, to, step, view), + side: 0, + }).range(from /* , to */); + }); +} + export const sliderPlugin = ViewPlugin.fromClass( class { decorations; //: DecorationSet From 95446a76ef73e4fb89a62c5b97ee41b3d77ba3ee Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 17 Mar 2024 12:22:24 +0100 Subject: [PATCH 30/30] breakout widgets package for now --- package.json | 1 - packages/widgets/Claviature.jsx | 45 ---------- packages/widgets/README.md | 31 ------- packages/widgets/index.mjs | 1 - packages/widgets/package.json | 42 --------- packages/widgets/solid.mjs | 13 --- packages/widgets/vite.config.js | 20 ----- pnpm-lock.yaml | 151 -------------------------------- website/package.json | 2 - website/src/repl/util.mjs | 1 - 10 files changed, 307 deletions(-) delete mode 100644 packages/widgets/Claviature.jsx delete mode 100644 packages/widgets/README.md delete mode 100644 packages/widgets/index.mjs delete mode 100644 packages/widgets/package.json delete mode 100644 packages/widgets/solid.mjs delete mode 100644 packages/widgets/vite.config.js diff --git a/package.json b/package.json index ea1373aa..741cf070 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "repl": "npm run prestart && cd website && npm run dev", "start": "npm run prestart && cd website && npm run dev", "dev": "npm run prestart && cd website && npm run dev", - "watch": "pnpm prestart && pnpm --parallel --filter {./website/**}... --filter \"@strudel/website\" watch", "build": "npm run prebuild && cd website && npm run build", "preview": "cd website && npm run preview", "osc": "cd packages/osc && npm run server", diff --git a/packages/widgets/Claviature.jsx b/packages/widgets/Claviature.jsx deleted file mode 100644 index 584dd09d..00000000 --- a/packages/widgets/Claviature.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import { For } from 'solid-js'; -import { customElement } from 'solid-element'; -import { getClaviature } from 'claviature'; -import { Dynamic } from 'solid-js/web'; -import { registerWidget } from '@strudel/codemirror'; -import { getSolidWidget } from './solid.mjs'; - -customElement('strudel-claviature', { options: '{}' }, (props, { element }) => { - let svg = () => { - let c = getClaviature({ - options: JSON.parse(props.options), - }); - return c; - }; - return ( - - - {(el) => { - return ( - - {el.value} - - ); - }} - - - ); -}); - -registerWidget('_claviature', (id, options = {}, pat) => { - options = { range: ['A0', 'C8'], scaleY: 1, scaleY: 0.5, scaleX: 0.5, ...options }; - const height = (options.upperHeight + options.lowerHeight) * options.scaleY; - const el = getSolidWidget('strudel-claviature', id, { ...options, height }); - return pat.onFrame(id, (haps) => { - const colorize = haps.map((h) => ({ keys: [h.value.note], color: h.context?.color || 'steelblue' })); - el.setAttribute( - 'options', - JSON.stringify({ - ...options, - range: options.range || ['A2', 'C6'], - colorize, - }), - ); - }); -}); diff --git a/packages/widgets/README.md b/packages/widgets/README.md deleted file mode 100644 index 53847c5d..00000000 --- a/packages/widgets/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# @strudel/widgets - -adds UI widgets to codemirror - -## claviature - -`Patter.claviature` renders a [claviature](https://www.npmjs.com/package/claviature). - -example usage: - -```js -chord("").voicing().piano() - .claviature() -``` - -All [claviature options](https://www.npmjs.com/package/claviature#options) will work. - -Here is an example that uses all available options: - -```js -chord("").voicing().piano() -.color('cyan') -.claviature({ - range: ['C1', 'C6'], // rendered note range - palette: ['cyan', 'magenta'], - stroke: 'black', - scaleX: 1, scaleY: 1, - upperHeight: 80, - lowerHeight: 50 -}) -``` diff --git a/packages/widgets/index.mjs b/packages/widgets/index.mjs deleted file mode 100644 index 281c6ab8..00000000 --- a/packages/widgets/index.mjs +++ /dev/null @@ -1 +0,0 @@ -export * from './Claviature.jsx'; diff --git a/packages/widgets/package.json b/packages/widgets/package.json deleted file mode 100644 index 775e4c17..00000000 --- a/packages/widgets/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "@strudel/widgets", - "version": "1.0.1", - "description": "Widget web components for Strudel", - "main": "dist/index.mjs", - "type": "module", - "scripts": { - "build": "vite build", - "watch": "vite build --watch", - "prepublishOnly": "npm run build" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" - }, - "keywords": [ - "titdalcycles", - "strudel", - "pattern", - "livecoding", - "algorave" - ], - "author": "Felix Roos ", - "license": "AGPL-3.0-or-later", - "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" - }, - "homepage": "https://github.com/tidalcycles/strudel#readme", - "dependencies": { - "@strudel/core": "workspace:*", - "@strudel/transpiler": "workspace:*", - "@strudel/draw": "workspace:*", - "@strudel/codemirror": "workspace:*", - "claviature": "^0.1.0", - "solid-element": "^1.8.0", - "solid-js": "^1.8.15", - "vite-plugin-solid": "^2.10.1" - }, - "devDependencies": { - "vite": "^5.0.10" - } -} diff --git a/packages/widgets/solid.mjs b/packages/widgets/solid.mjs deleted file mode 100644 index b8403249..00000000 --- a/packages/widgets/solid.mjs +++ /dev/null @@ -1,13 +0,0 @@ -import { setWidget } from '@strudel/codemirror'; - -export function getSolidWidget(type, id, options) { - let el = document.getElementById(id); - if (!el) { - el = document.createElement('div'); - const c = document.createElement(type); - el.appendChild(c); - } - el.height = options.height || 200; - setWidget(id, el); - return el?.firstChild; -} diff --git a/packages/widgets/vite.config.js b/packages/widgets/vite.config.js deleted file mode 100644 index 4ed5702c..00000000 --- a/packages/widgets/vite.config.js +++ /dev/null @@ -1,20 +0,0 @@ -import { defineConfig } from 'vite'; -import { dependencies } from './package.json'; -import { resolve } from 'path'; -import solid from 'vite-plugin-solid'; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [solid()], - build: { - lib: { - entry: resolve(__dirname, 'index.mjs'), - formats: ['es'], - fileName: (ext) => ({ es: 'index.mjs' })[ext], - }, - rollupOptions: { - external: [...Object.keys(dependencies)], - }, - target: 'esnext', - }, -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00608001..033c6a7e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -491,37 +491,6 @@ importers: specifier: ^5.0.10 version: 5.0.10 - packages/widgets: - dependencies: - '@strudel/codemirror': - specifier: workspace:* - version: link:../codemirror - '@strudel/core': - specifier: workspace:* - version: link:../core - '@strudel/draw': - specifier: workspace:* - version: link:../draw - '@strudel/transpiler': - specifier: workspace:* - version: link:../transpiler - claviature: - specifier: ^0.1.0 - version: 0.1.0 - solid-element: - specifier: ^1.8.0 - version: 1.8.0(solid-js@1.8.15) - solid-js: - specifier: ^1.8.15 - version: 1.8.15 - vite-plugin-solid: - specifier: ^2.10.1 - version: 2.10.1(solid-js@1.8.15)(vite@5.0.11) - devDependencies: - vite: - specifier: ^5.0.10 - version: 5.0.11(@types/node@20.10.6) - packages/xen: dependencies: '@strudel/core': @@ -624,9 +593,6 @@ importers: '@strudel/webaudio': specifier: workspace:* version: link:../packages/webaudio - '@strudel/widgets': - specifier: workspace:* - version: link:../packages/widgets '@strudel/xen': specifier: workspace:* version: link:../packages/xen @@ -1143,13 +1109,6 @@ packages: '@babel/types': 7.23.6 dev: true - /@babel/helper-module-imports@7.18.6: - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.6 - dev: false - /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} @@ -5513,19 +5472,6 @@ packages: resolution: {integrity: sha512-3AN/9V/rKuv90NG65m4tTHsI04XrCKsWbztIcW7a8H5iIN7WlvWucRtVV0V/rT4QvtA11n5Vmp20fLwfMWqp6g==} dev: false - /babel-plugin-jsx-dom-expressions@0.37.17(@babel/core@7.23.7): - resolution: {integrity: sha512-1bv8rOTzs6TR3DVyVZ7ElxyPEhnS556FMWRIsB3gBPfkn/cSKaLvXLGk+X1lvI+SzcUo4G+UcmJrn3vr1ig8mQ==} - peerDependencies: - '@babel/core': ^7.20.12 - dependencies: - '@babel/core': 7.23.7 - '@babel/helper-module-imports': 7.18.6 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.7) - '@babel/types': 7.23.6 - html-entities: 2.3.3 - validate-html-nesting: 1.2.2 - dev: false - /babel-plugin-polyfill-corejs2@0.4.7(@babel/core@7.23.7): resolution: {integrity: sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==} peerDependencies: @@ -5562,15 +5508,6 @@ packages: - supports-color dev: true - /babel-preset-solid@1.8.15(@babel/core@7.23.7): - resolution: {integrity: sha512-P2yOQbB7Hn/m4YvpXV6ExHIMcgNWXWXcvY4kJzG3yqAB3hKS58OZRsvJ7RObsZWqXRvZTITBIwnpK0BMGu+ZIQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.7 - babel-plugin-jsx-dom-expressions: 0.37.17(@babel/core@7.23.7) - dev: false - /bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -6180,10 +6117,6 @@ packages: dot-prop: 5.3.0 dev: true - /component-register@0.8.3: - resolution: {integrity: sha512-/0u8ov0WPWi2FL78rgB9aFOcfY8pJT4jP/l9NTOukGNLVQ6hk35sEJE1RkEnNQU3yk48Qr7HlDQjRQKEVfgeWg==} - dev: false - /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -8201,10 +8134,6 @@ packages: lru-cache: 10.1.0 dev: true - /html-entities@2.3.3: - resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} - dev: false - /html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} @@ -8727,11 +8656,6 @@ packages: call-bind: 1.0.5 dev: true - /is-what@4.1.16: - resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} - engines: {node: '>=12.13'} - dev: false - /is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -9698,13 +9622,6 @@ packages: yargs-parser: 20.2.9 dev: true - /merge-anything@5.1.7: - resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} - engines: {node: '>=12.13'} - dependencies: - is-what: 4.1.16 - dev: false - /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -12270,20 +12187,6 @@ packages: randombytes: 2.1.0 dev: true - /seroval-plugins@1.0.4(seroval@1.0.4): - resolution: {integrity: sha512-DQ2IK6oQVvy8k+c2V5x5YCtUa/GGGsUwUBNN9UqohrZ0rWdUapBFpNMYP1bCyRHoxOJjdKGl+dieacFIpU/i1A==} - engines: {node: '>=10'} - peerDependencies: - seroval: ^1.0 - dependencies: - seroval: 1.0.4 - dev: false - - /seroval@1.0.4: - resolution: {integrity: sha512-qQs/N+KfJu83rmszFQaTxcoJoPn6KNUruX4KmnmyD0oZkUoiNvJ1rpdYKDf4YHM05k+HOgCxa3yvf15QbVijGg==} - engines: {node: '>=10'} - dev: false - /server-destroy@1.0.1: resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} @@ -12486,34 +12389,6 @@ packages: smart-buffer: 4.2.0 dev: true - /solid-element@1.8.0(solid-js@1.8.15): - resolution: {integrity: sha512-DG8HBCej5kNExUiFbVG8OFZojMGcLF8keXdGLEcHXBYtJ7zhm+a8HJnl5lfmBlTYGRk4ApgoBvlwH1ibg7quaQ==} - peerDependencies: - solid-js: ^1.8.0 - dependencies: - component-register: 0.8.3 - solid-js: 1.8.15 - dev: false - - /solid-js@1.8.15: - resolution: {integrity: sha512-d0QP/efr3UVcwGgWVPveQQ0IHOH6iU7yUhc2piy8arNG8wxKmvUy1kFxyF8owpmfCWGB87usDKMaVnsNYZm+Vw==} - dependencies: - csstype: 3.1.1 - seroval: 1.0.4 - seroval-plugins: 1.0.4(seroval@1.0.4) - dev: false - - /solid-refresh@0.6.3(solid-js@1.8.15): - resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} - peerDependencies: - solid-js: ^1.3 - dependencies: - '@babel/generator': 7.23.6 - '@babel/helper-module-imports': 7.22.15 - '@babel/types': 7.23.6 - solid-js: 1.8.15 - dev: false - /sort-array@4.1.5: resolution: {integrity: sha512-Ya4peoS1fgFN42RN1REk2FgdNOeLIEMKFGJvs7VTP3OklF8+kl2SkpVliZ4tk/PurWsrWRsdNdU+tgyOBkB9sA==} engines: {node: '>=10'} @@ -13629,10 +13504,6 @@ packages: hasBin: true dev: true - /validate-html-nesting@1.2.2: - resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} - dev: false - /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -13743,28 +13614,6 @@ packages: - supports-color dev: true - /vite-plugin-solid@2.10.1(solid-js@1.8.15)(vite@5.0.11): - resolution: {integrity: sha512-kfVdNLWaJqaJVL52U6iCCKNW/nXE7bS1VVGOWPGllOkJfcNILymVSY0LCBLSnyy0iYnRtrXpiHm14rMuzeC7CA==} - peerDependencies: - '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* - solid-js: ^1.7.2 - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - '@testing-library/jest-dom': - optional: true - dependencies: - '@babel/core': 7.23.7 - '@types/babel__core': 7.20.5 - babel-preset-solid: 1.8.15(@babel/core@7.23.7) - merge-anything: 5.1.7 - solid-js: 1.8.15 - solid-refresh: 0.6.3(solid-js@1.8.15) - vite: 5.0.11(@types/node@20.10.6) - vitefu: 0.2.5(vite@5.0.11) - transitivePeerDependencies: - - supports-color - dev: false - /vite@5.0.10: resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} diff --git a/website/package.json b/website/package.json index 3fd05d54..7af9e61e 100644 --- a/website/package.json +++ b/website/package.json @@ -5,7 +5,6 @@ "private": true, "scripts": { "dev": "astro dev --host 0.0.0.0", - "watch": "astro dev --host 0.0.0.0", "start": "astro dev", "check": "astro check && tsc", "build": "astro build", @@ -25,7 +24,6 @@ "@heroicons/react": "^2.1.1", "@nanostores/persistent": "^0.9.1", "@nanostores/react": "^0.7.1", - "@strudel/widgets": "workspace:*", "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", "@strudel/draw": "workspace:*", diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index 1df5a5b5..6dba7dab 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -82,7 +82,6 @@ export function loadModules() { import('@strudel/serial'), import('@strudel/soundfonts'), import('@strudel/csound'), - import('@strudel/widgets'), ]; if (isTauri()) { modules = modules.concat([