- 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));
};
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
* @noAutocomplete
*/
export const midi2note = (n) => {
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;
};

View File

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

View File

@ -29,7 +29,8 @@ function useStrudel({
const [pattern, setPattern] = useState();
const [started, setStarted] = useState(false);
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
const { scheduler, evaluate, start, stop, pause, setCps } = useMemo(

51
pnpm-lock.yaml generated
View File

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

View File

@ -13,7 +13,7 @@
},
"dependencies": {
"@algolia/client-search": "^4.17.0",
"@astrojs/mdx": "^0.19.0",
"@astrojs/mdx": "^0.19.0",
"@astrojs/react": "^2.1.1",
"@astrojs/tailwind": "^3.1.1",
"@docsearch/css": "^3.3.4",
@ -43,6 +43,7 @@
"@uiw/codemirror-themes-all": "^4.19.16",
"astro": "^2.3.2",
"canvas": "^2.11.2",
"claviature": "^0.1.0",
"fraction.js": "^4.2.0",
"nanoid": "^4.0.2",
"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 { useEffect, useState } from 'react';
import { prebake } from '../repl/prebake';
import { themes, settings } from '../repl/themes.mjs';
import './MiniRepl.css';
import { useSettings } from '../settings.mjs';
import Claviature from '@components/Claviature';
let modules;
if (typeof window !== 'undefined') {
@ -27,9 +28,19 @@ if (typeof window !== 'undefined') {
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 { theme } = useSettings();
const { theme, keybindings, fontSize, fontFamily } = useSettings();
const [activeNotes, setActiveNotes] = useState([]);
useEffect(() => {
// we have to load this package on the client
// because codemirror throws an error on the server
@ -42,13 +53,35 @@ export function MiniRepl({ tune, drawTime, punchcard, span = [0, 4], canvasHeigh
<Repl
tune={tune}
hideOutsideView={true}
drawTime={drawTime}
drawTime={claviature ? [0, 0] : drawTime}
punchcard={punchcard}
span={span}
canvasHeight={canvasHeight}
theme={themes[theme]}
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>
) : (
<pre>{tune}</pre>

View File

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