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