diff --git a/packages/react/examples/nano-repl/src/App.jsx b/packages/react/examples/nano-repl/src/App.jsx
index 086923ac6..796f672bd 100644
--- a/packages/react/examples/nano-repl/src/App.jsx
+++ b/packages/react/examples/nano-repl/src/App.jsx
@@ -1,5 +1,5 @@
import { controls, evalScope } from '@strudel.cycles/core';
-import { CodeMirror, useHighlighting, useKeydown, useStrudel, flash } from '@strudel.cycles/react';
+import { CodeMirror, useHighlighting, useStrudel, flash } from '@strudel.cycles/react';
import {
getAudioContext,
initAudioOnFirstClick,
@@ -93,32 +93,6 @@ function App() {
});
const error = evalError || schedulerError;
- useKeydown(
- useCallback(
- async (e) => {
- if (e.ctrlKey || e.altKey) {
- if (e.code === 'Enter') {
- e.preventDefault();
- flash(view);
- await evaluate(code);
- if (e.shiftKey) {
- panic();
- scheduler.stop();
- scheduler.start();
- }
- if (!scheduler.started) {
- scheduler.start();
- }
- } else if (e.code === 'Period') {
- scheduler.stop();
- panic();
- e.preventDefault();
- }
- }
- },
- [scheduler, evaluate, view, code],
- ),
- );
return (
{error && error {error.message}
}
-
+ {
+ flash(view);
+ await evaluate(code);
+ if (!scheduler.started) {
+ scheduler.start();
+ }
+ }}
+ onReEvaluate={async () => {
+ await evaluate(code);
+ panic();
+ scheduler.stop();
+ scheduler.start();
+ }}
+ onStop={() => {
+ scheduler.stop();
+ panic();
+ }}
+ />
);
}
diff --git a/packages/react/src/components/CodeMirror6.jsx b/packages/react/src/components/CodeMirror6.jsx
index 63d4d5a13..e863b1ba3 100644
--- a/packages/react/src/components/CodeMirror6.jsx
+++ b/packages/react/src/components/CodeMirror6.jsx
@@ -29,6 +29,10 @@ export default function CodeMirror({
onViewChanged,
onSelectionChange,
onDocChange,
+ onEvaluate,
+ onReEvaluate,
+ onPanic,
+ onStop,
theme,
keybindings,
isLineNumbersDisplayed,
@@ -94,14 +98,33 @@ export default function CodeMirror({
_extensions.push(autocompletion({ override: [] }));
}
- _extensions.push([keymap.of({})]);
+ _extensions.push(
+ keymap.of([
+ {
+ key: 'Ctrl-Enter',
+ run: () => onEvaluate?.(),
+ },
+ {
+ key: 'Ctrl-.',
+ run: () => onStop?.(),
+ },
+ {
+ key: 'Ctrl-Shift-.',
+ run: () => (onPanic ? onPanic() : onStop?.()),
+ },
+ {
+ key: 'Ctrl-Shift-Enter',
+ run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()),
+ },
+ ]),
+ );
if (isLineWrappingEnabled) {
_extensions.push(EditorView.lineWrapping);
}
return _extensions;
- }, [keybindings, isAutoCompletionEnabled, isLineWrappingEnabled]);
+ }, [keybindings, isAutoCompletionEnabled, isLineWrappingEnabled, onEvaluate, onStop]);
const basicSetup = useMemo(() => ({ lineNumbers: isLineNumbersDisplayed }), [isLineNumbersDisplayed]);
diff --git a/packages/react/src/components/MiniRepl.jsx b/packages/react/src/components/MiniRepl.jsx
index 125008747..fb14f954e 100644
--- a/packages/react/src/components/MiniRepl.jsx
+++ b/packages/react/src/components/MiniRepl.jsx
@@ -10,7 +10,6 @@ import { Icon } from './Icon';
import './style.css';
import { logger } from '@strudel.cycles/core';
import useEvent from '../hooks/useEvent.mjs';
-import useKeydown from '../hooks/useKeydown.mjs';
const getTime = () => getAudioContext().currentTime;
@@ -92,27 +91,6 @@ export function MiniRepl({
getTime: () => scheduler.now(),
});
- // keyboard shortcuts
- useKeydown(
- useCallback(
- async (e) => {
- if (view?.hasFocus) {
- if (e.ctrlKey || e.altKey) {
- if (e.code === 'Enter') {
- e.preventDefault();
- flash(view);
- await activateCode();
- } else if (e.key === '.' || e.code === 'Period') {
- stop();
- e.preventDefault();
- }
- }
- }
- },
- [activateCode, stop, view],
- ),
- );
-
const [log, setLog] = useState([]);
useLogger(
useCallback((e) => {
@@ -164,6 +142,11 @@ export function MiniRepl({
fontSize={fontSize}
keybindings={keybindings}
isLineNumbersDisplayed={isLineNumbersDisplayed}
+ onEvaluate={() => {
+ flash(view);
+ activateCode();
+ }}
+ onStop={() => stop()}
/>
)}
{error && {error.message}
}
diff --git a/packages/react/src/hooks/useKeydown.mjs b/packages/react/src/hooks/useKeydown.mjs
deleted file mode 100644
index 88d1cb940..000000000
--- a/packages/react/src/hooks/useKeydown.mjs
+++ /dev/null
@@ -1,10 +0,0 @@
-import { useLayoutEffect } from 'react';
-
-// set active pattern on ctrl+enter
-const useKeydown = (callback) =>
- useLayoutEffect(() => {
- window.addEventListener('keydown', callback, true);
- return () => window.removeEventListener('keydown', callback, true);
- }, [callback]);
-
-export default useKeydown;
diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx
index 5bde3c111..377f91a5e 100644
--- a/website/src/repl/Repl.jsx
+++ b/website/src/repl/Repl.jsx
@@ -5,8 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import { cleanupDraw, cleanupUi, getDrawContext, logger } from '@strudel.cycles/core';
-import { CodeMirror, cx, flash, useHighlighting, useStrudel, useKeydown } from '@strudel.cycles/react';
-import { getAudioContext, resetLoadedSounds, webaudioOutput } from '@strudel.cycles/webaudio';
+import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react';
+import { getAudioContext, resetLoadedSounds, webaudioOutput, panic } from '@strudel.cycles/webaudio';
import { createClient } from '@supabase/supabase-js';
import { nanoid } from 'nanoid';
import React, { createContext, useCallback, useEffect, useState, useMemo } from 'react';
@@ -155,29 +155,6 @@ export function Repl({ embedded = false }) {
});
}, []);
- // keyboard shortcuts
- useKeydown(
- useCallback(
- async (e) => {
- if (e.ctrlKey || e.altKey) {
- if (e.code === 'Enter') {
- if (getAudioContext().state !== 'running') {
- alert('please click play to initialize the audio. you can use shortcuts after that!');
- return;
- }
- e.preventDefault();
- flash(view);
- await activateCode();
- } else if (e.key === '.' || e.code === 'Period') {
- stop();
- e.preventDefault();
- }
- }
- },
- [activateCode, stop, view],
- ),
- );
-
// highlighting
const { setMiniLocations } = useHighlighting({
view,
@@ -310,6 +287,24 @@ export function Repl({ embedded = false }) {
onChange={handleChangeCode}
onViewChanged={handleViewChanged}
onSelectionChange={handleSelectionChange}
+ onEvaluate={() => {
+ if (getAudioContext().state !== 'running') {
+ alert('please click play to initialize the audio. you can use shortcuts after that!');
+ return;
+ }
+ flash(view);
+ activateCode();
+ }}
+ onReEvaluate={() => {
+ stop();
+ panic();
+ activateCode();
+ }}
+ onPanic={() => {
+ stop();
+ panic();
+ }}
+ onStop={() => stop()}
/>
{panelPosition === 'right' && !isEmbedded && }