- add claviature flag to minirepl

- bring back option+dot on macos
- consume more editor settings in minirepl
This commit is contained in:
Felix Roos 2023-05-27 13:30:57 +02:00
parent 0d6fcf78d8
commit fc06181217
8 changed files with 121 additions and 36 deletions

View File

@ -67,13 +67,14 @@ export const getFreq = (noteOrMidi) => {
return midiToFreq(noteToMidi(noteOrMidi)); return midiToFreq(noteToMidi(noteOrMidi));
}; };
const pcs = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
/** /**
* @deprecated does not appear to be referenced or invoked anywhere in the codebase * @deprecated does not appear to be referenced or invoked anywhere in the codebase
* @noAutocomplete * @noAutocomplete
*/ */
export const midi2note = (n) => { export const midi2note = (n) => {
const oct = Math.floor(n / 12) - 1; const oct = Math.floor(n / 12) - 1;
const pc = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'][n % 12]; const pc = pcs[n % 12];
return pc + oct; return pc + oct;
}; };

View File

@ -18,18 +18,21 @@ export function MiniRepl({
tune, tune,
hideOutsideView = false, hideOutsideView = false,
enableKeyboard, enableKeyboard,
onTrigger,
drawTime, drawTime,
punchcard, punchcard,
onPaint,
canvasHeight = 200, canvasHeight = 200,
fontSize = 18, fontSize = 18,
hideHeader = false, hideHeader = false,
theme, theme,
keybindings,
}) { }) {
drawTime = drawTime || (punchcard ? [0, 4] : undefined); drawTime = drawTime || (punchcard ? [0, 4] : undefined);
const evalOnMount = !!drawTime; const evalOnMount = !!drawTime;
const drawContext = useCallback( const drawContext = useCallback(
!!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, punchcard ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
[drawTime], [punchcard],
); );
const { const {
code, code,
@ -51,7 +54,15 @@ export function MiniRepl({
defaultOutput: webaudioOutput, defaultOutput: webaudioOutput,
editPattern: (pat, id) => { editPattern: (pat, id) => {
//pat = pat.withContext((ctx) => ({ ...ctx, id })); //pat = pat.withContext((ctx) => ({ ...ctx, id }));
return punchcard ? pat.punchcard() : pat; if (onTrigger) {
pat = pat.onTrigger(onTrigger, false);
}
if (onPaint) {
pat = pat.onPaint(onPaint);
} else if (punchcard) {
pat = pat.punchcard();
}
return pat;
}, },
getTime, getTime,
evalOnMount, evalOnMount,
@ -87,7 +98,7 @@ export function MiniRepl({
e.preventDefault(); e.preventDefault();
flash(view); flash(view);
await activateCode(); await activateCode();
} else if (e.key === '.') { } else if (e.key === '.' || e.code === 'Period') {
stop(); stop();
e.preventDefault(); e.preventDefault();
} }
@ -140,11 +151,18 @@ export function MiniRepl({
)} )}
<div className="overflow-auto relative"> <div className="overflow-auto relative">
{show && ( {show && (
<CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} theme={theme} fontSize={fontSize} /> <CodeMirror6
value={code}
onChange={setCode}
onViewChanged={setView}
theme={theme}
fontSize={fontSize}
keybindings={keybindings}
/>
)} )}
{error && <div className="text-right p-1 text-md text-red-200">{error.message}</div>} {error && <div className="text-right p-1 text-md text-red-200">{error.message}</div>}
</div> </div>
{drawTime && ( {punchcard && (
<canvas <canvas
id={canvasId} id={canvasId}
className="w-full pointer-events-none" className="w-full pointer-events-none"

View File

@ -29,7 +29,8 @@ function useStrudel({
const [pattern, setPattern] = useState(); const [pattern, setPattern] = useState();
const [started, setStarted] = useState(false); const [started, setStarted] = useState(false);
const isDirty = code !== activeCode; const isDirty = code !== activeCode;
const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]); //const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]);
const shouldPaint = useCallback((pat) => !!pat?.context?.onPaint, []);
// TODO: make sure this hook reruns when scheduler.started changes // TODO: make sure this hook reruns when scheduler.started changes
const { scheduler, evaluate, start, stop, pause, setCps } = useMemo( const { scheduler, evaluate, start, stop, pause, setCps } = useMemo(

51
pnpm-lock.yaml generated
View File

@ -92,7 +92,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/core: packages/core:
dependencies: dependencies:
@ -102,7 +102,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
vitest: vitest:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0(@vitest/ui@0.28.0) version: 0.28.0(@vitest/ui@0.28.0)
@ -127,7 +127,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/core/examples/vite-vanilla-repl-cm6: packages/core/examples/vite-vanilla-repl-cm6:
dependencies: dependencies:
@ -155,7 +155,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.2 specifier: ^4.3.2
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/csound: packages/csound:
dependencies: dependencies:
@ -171,7 +171,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/embed: {} packages/embed: {}
@ -204,7 +204,7 @@ importers:
version: link:../mini version: link:../mini
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
vitest: vitest:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0(@vitest/ui@0.28.0) version: 0.28.0(@vitest/ui@0.28.0)
@ -223,7 +223,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/mini: packages/mini:
dependencies: dependencies:
@ -236,7 +236,7 @@ importers:
version: 3.0.2 version: 3.0.2
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
vitest: vitest:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0(@vitest/ui@0.28.0) version: 0.28.0(@vitest/ui@0.28.0)
@ -255,7 +255,7 @@ importers:
version: 5.8.1 version: 5.8.1
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/react: packages/react:
dependencies: dependencies:
@ -325,7 +325,7 @@ importers:
version: 3.3.2 version: 3.3.2
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/react/examples/nano-repl: packages/react/examples/nano-repl:
dependencies: dependencies:
@ -380,7 +380,7 @@ importers:
version: 3.3.2 version: 3.3.2
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/serial: packages/serial:
dependencies: dependencies:
@ -390,7 +390,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/soundfonts: packages/soundfonts:
dependencies: dependencies:
@ -412,7 +412,7 @@ importers:
version: 3.3.1 version: 3.3.1
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/tonal: packages/tonal:
dependencies: dependencies:
@ -431,7 +431,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
vitest: vitest:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0(@vitest/ui@0.28.0) version: 0.28.0(@vitest/ui@0.28.0)
@ -447,7 +447,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
vitest: vitest:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0(@vitest/ui@0.28.0) version: 0.28.0(@vitest/ui@0.28.0)
@ -469,7 +469,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
vitest: vitest:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0(@vitest/ui@0.28.0) version: 0.28.0(@vitest/ui@0.28.0)
@ -494,7 +494,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/web/examples/repl-example: packages/web/examples/repl-example:
dependencies: dependencies:
@ -504,7 +504,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.2 specifier: ^4.3.2
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/webaudio: packages/webaudio:
dependencies: dependencies:
@ -517,7 +517,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
packages/webdirt: packages/webdirt:
dependencies: dependencies:
@ -533,7 +533,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
vitest: vitest:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0(@vitest/ui@0.28.0) version: 0.28.0(@vitest/ui@0.28.0)
@ -546,7 +546,7 @@ importers:
devDependencies: devDependencies:
vite: vite:
specifier: ^4.3.3 specifier: ^4.3.3
version: 4.3.3(@types/node@18.16.3) version: 4.3.3(@types/node@18.11.18)
vitest: vitest:
specifier: ^0.28.0 specifier: ^0.28.0
version: 0.28.0(@vitest/ui@0.28.0) version: 0.28.0(@vitest/ui@0.28.0)
@ -646,6 +646,9 @@ importers:
canvas: canvas:
specifier: ^2.11.2 specifier: ^2.11.2
version: 2.11.2 version: 2.11.2
claviature:
specifier: ^0.1.0
version: 0.1.0
fraction.js: fraction.js:
specifier: ^4.2.0 specifier: ^4.2.0
version: 4.2.0 version: 4.2.0
@ -4502,7 +4505,7 @@ packages:
'@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.5) '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.5)
'@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.5) '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.5)
react-refresh: 0.14.0 react-refresh: 0.14.0
vite: 4.3.3(@types/node@18.16.3) vite: 4.3.3(@types/node@18.11.18)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@ -5413,6 +5416,10 @@ packages:
resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==} resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
engines: {node: '>=8'} engines: {node: '>=8'}
/claviature@0.1.0:
resolution: {integrity: sha512-Ai12axNwQ7x/F9QAj64RYKsgvi5Y33+X3GUSKAC/9s/adEws8TSSc0efeiqhKNGKBo6rT/c+CSCwSXzXxwxZzQ==}
dev: false
/clean-stack@2.2.0: /clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'} engines: {node: '>=6'}

View File

@ -13,7 +13,7 @@
}, },
"dependencies": { "dependencies": {
"@algolia/client-search": "^4.17.0", "@algolia/client-search": "^4.17.0",
"@astrojs/mdx": "^0.19.0", "@astrojs/mdx": "^0.19.0",
"@astrojs/react": "^2.1.1", "@astrojs/react": "^2.1.1",
"@astrojs/tailwind": "^3.1.1", "@astrojs/tailwind": "^3.1.1",
"@docsearch/css": "^3.3.4", "@docsearch/css": "^3.3.4",
@ -43,6 +43,7 @@
"@uiw/codemirror-themes-all": "^4.19.16", "@uiw/codemirror-themes-all": "^4.19.16",
"astro": "^2.3.2", "astro": "^2.3.2",
"canvas": "^2.11.2", "canvas": "^2.11.2",
"claviature": "^0.1.0",
"fraction.js": "^4.2.0", "fraction.js": "^4.2.0",
"nanoid": "^4.0.2", "nanoid": "^4.0.2",
"nanostores": "^0.8.1", "nanostores": "^0.8.1",

View File

@ -0,0 +1,24 @@
import { getClaviature } from 'claviature';
import React from 'react';
export default function Claviature({ options, onClick, onMouseDown, onMouseUp, onMouseLeave }) {
const svg = getClaviature({
options,
onClick,
onMouseDown,
onMouseUp,
onMouseLeave,
});
return (
<svg {...svg.attributes}>
{svg.children.map((el, i) => {
const TagName = el.name;
return (
<TagName key={`${el.name}-${i}`} {...el.attributes}>
{el.value}
</TagName>
);
})}
</svg>
);
}

View File

@ -1,10 +1,11 @@
import { evalScope, controls } from '@strudel.cycles/core'; import { evalScope, controls, noteToMidi } from '@strudel.cycles/core';
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio'; import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { prebake } from '../repl/prebake'; import { prebake } from '../repl/prebake';
import { themes, settings } from '../repl/themes.mjs'; import { themes, settings } from '../repl/themes.mjs';
import './MiniRepl.css'; import './MiniRepl.css';
import { useSettings } from '../settings.mjs'; import { useSettings } from '../settings.mjs';
import Claviature from '@components/Claviature';
let modules; let modules;
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@ -27,9 +28,19 @@ if (typeof window !== 'undefined') {
prebake(); prebake();
} }
export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeight = 100, hideHeader }) { export function MiniRepl({
tune,
drawTime,
punchcard,
span = [0, 4],
canvasHeight = 100,
hideHeader,
claviature,
claviatureLabels,
}) {
const [Repl, setRepl] = useState(); const [Repl, setRepl] = useState();
const { theme } = useSettings(); const { theme, keybindings, fontSize, fontFamily } = useSettings();
const [activeNotes, setActiveNotes] = useState([]);
useEffect(() => { useEffect(() => {
// we have to load this package on the client // we have to load this package on the client
// because codemirror throws an error on the server // because codemirror throws an error on the server
@ -42,13 +53,35 @@ export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeigh
<Repl <Repl
tune={tune} tune={tune}
hideOutsideView={true} hideOutsideView={true}
drawTime={drawTime} drawTime={claviature ? [0, 0] : drawTime}
punchcard={punchcard} punchcard={punchcard}
span={span} span={span}
canvasHeight={canvasHeight} canvasHeight={canvasHeight}
theme={themes[theme]} theme={themes[theme]}
hideHeader={hideHeader} hideHeader={hideHeader}
keybindings={keybindings}
onPaint={
claviature
? (ctx, time, haps, drawTime) => {
const active = haps
.map((hap) => hap.value.note)
.filter(Boolean)
.map((n) => (typeof n === 'string' ? noteToMidi(n) : n));
setActiveNotes(active);
}
: undefined
}
/> />
{claviature && (
<Claviature
options={{
range: ['C2', 'C6'],
scaleY: 0.75,
colorize: [{ keys: activeNotes, color: 'steelblue' }],
labels: claviatureLabels || {},
}}
/>
)}
</div> </div>
) : ( ) : (
<pre>{tune}</pre> <pre>{tune}</pre>

View File

@ -157,7 +157,7 @@ export function Repl({ embedded = false }) {
e.preventDefault(); e.preventDefault();
flash(view); flash(view);
await activateCode(); await activateCode();
} else if (e.key === '.') { } else if (e.key === '.' || e.keyCode === 'Period') {
stop(); stop();
e.preventDefault(); e.preventDefault();
} }