diff --git a/packages/repl/index.mjs b/packages/repl/index.mjs index 330cd77d..4119059d 100644 --- a/packages/repl/index.mjs +++ b/packages/repl/index.mjs @@ -1 +1,2 @@ export * from './repl-component.mjs'; +export * from './prebake.mjs'; diff --git a/packages/repl/repl-component.mjs b/packages/repl/repl-component.mjs index 2eda358d..b8947640 100644 --- a/packages/repl/repl-component.mjs +++ b/packages/repl/repl-component.mjs @@ -103,8 +103,6 @@ if (typeof HTMLElement !== 'undefined') { // init settings this.editor.updateSettings(this.settings); this.editor.setCode(this.code); - // settingsMap.listen((settings, key) => editor.changeSetting(key, settings[key])); - // onEvent('strudel-toggle-play', () => this.editor.toggle()); } // Element functionality written in here } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 344a24bc..67599cd9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -793,6 +793,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-hook-inview: + specifier: ^4.5.0 + version: 4.5.0(react-dom@18.2.0)(react@18.2.0) rehype-autolink-headings: specifier: ^6.1.1 version: 6.1.1 diff --git a/website/package.json b/website/package.json index b2267a69..b2c5f39a 100644 --- a/website/package.json +++ b/website/package.json @@ -34,9 +34,9 @@ "@strudel.cycles/transpiler": "workspace:*", "@strudel.cycles/webaudio": "workspace:*", "@strudel.cycles/xen": "workspace:*", - "@strudel/hydra": "workspace:*", "@strudel/codemirror": "workspace:*", "@strudel/desktopbridge": "workspace:*", + "@strudel/hydra": "workspace:*", "@strudel/repl": "workspace:*", "@supabase/supabase-js": "^2.21.0", "@tailwindcss/forms": "^0.5.3", @@ -54,6 +54,7 @@ "nanostores": "^0.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-inview": "^4.5.0", "rehype-autolink-headings": "^6.1.1", "rehype-slug": "^5.0.1", "rehype-urls": "^1.1.1", diff --git a/website/src/docs/MicroRepl.jsx b/website/src/docs/MicroRepl.jsx index c0e34637..cc401cd6 100644 --- a/website/src/docs/MicroRepl.jsx +++ b/website/src/docs/MicroRepl.jsx @@ -1,32 +1,78 @@ import { useState, useRef, useCallback, useEffect } from 'react'; import { Icon } from './Icon'; -import '@strudel/repl'; +import { getDrawContext, silence } from '@strudel.cycles/core'; +import { transpiler } from '@strudel.cycles/transpiler'; +import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; +import { StrudelMirror } from '@strudel/codemirror'; +import { prebake } from '@strudel/repl'; +import { useInView } from 'react-hook-inview'; -// import { useInView } from 'react-hook-inview'; +const initialSettings = { + keybindings: 'strudelTheme', + isLineNumbersDisplayed: false, + isActiveLineHighlighted: true, + isAutoCompletionEnabled: false, + isPatternHighlightingEnabled: true, + isFlashEnabled: true, + isTooltipEnabled: false, + isLineWrappingEnabled: false, + theme: 'strudelTheme', + fontFamily: 'monospace', + fontSize: 18, +}; -export function MicroRepl({ code, hideHeader = false }) { - /* const [ref, isVisible] = useInView({ +export function MicroRepl({ code, hideHeader = false, canvasHeight = 200, punchcard, punchcardLabels }) { + const init = useCallback(({ code }) => { + const drawContext = getDrawContext(); + const drawTime = [-2, 2]; + const editor = new StrudelMirror({ + defaultOutput: webaudioOutput, + getTime: () => getAudioContext().currentTime, + transpiler, + root: containerRef.current, + initialCode: '// LOADING', + pattern: silence, + settings: initialSettings, + drawTime, + onDraw: (haps, time, frame, painters) => { + painters.length && drawContext.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); + painters?.forEach((painter) => { + // ctx time haps drawTime paintOptions + painter(drawContext, time, haps, drawTime, { clear: false }); + }); + }, + prebake, + onUpdateState: (state) => { + setReplState({ ...state }); + }, + }); + // init settings + editor.updateSettings(initialSettings); + editor.setCode(code); + editorRef.current = editor; + }, []); + + const [ref, isVisible] = useInView({ threshold: 0.01, - }); */ + onEnter: () => { + if (!editorRef.current) { + init({ code }); + } + }, + }); const [replState, setReplState] = useState({}); const { started, isDirty, error } = replState; - const wc = useRef(); - function togglePlay() { - if (wc.current) { - wc.current?.editor.toggle(); - } - } - const listener = useCallback((e) => setReplState({ ...e.detail }), []); - useEffect(() => { - return () => { - wc.current.removeEventListener('update', listener); - }; - }, []); + const editorRef = useRef(); + const containerRef = useRef(); + + const [canvasId] = useState(Date.now()); + const drawContext = useCallback( + punchcard ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, + [punchcard], + ); + return ( -
+
{!hideHeader && (
@@ -35,7 +81,7 @@ export function MicroRepl({ code, hideHeader = false }) { 'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background', started ? 'animate-pulse' : '', )} - onClick={() => togglePlay()} + onClick={() => editorRef.current?.toggle()} > @@ -44,40 +90,34 @@ export function MicroRepl({ code, hideHeader = false }) { 'w-16 flex items-center justify-center p-1 text-foreground border-lineHighlight bg-lineHighlight', isDirty ? 'text-foreground hover:bg-background cursor-pointer' : 'opacity-50 cursor-not-allowed', )} - onClick={() => activateCode()} + onClick={() => editorRef.current?.evaluate()} >
)} -
- { - if (wc.current) { - return; - } - wc.current = el; - el.addEventListener('update', listener); - }} - > +
+
{error &&
{error.message}
}
{/* punchcard && ( - { - if (el && el.width !== el.clientWidth) { - el.width = el.clientWidth; - } - }} - > - ) */} + { + if (el && el.width !== el.clientWidth) { + el.width = el.clientWidth; + } + //const ratio = el.clientWidth / canvasHeight; + //const targetWidth = Math.round(el.width * ratio); + //if (el.width !== targetWidth) { + // el.width = targetWidth; + //} + }} + > + ) */} {/* !!log.length && (
{log.map(({ message }, i) => (