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