better repl init + a bit of ssr for main repl

This commit is contained in:
Felix Roos 2023-12-29 00:55:14 +01:00
parent b0bdd09032
commit 9974311344
6 changed files with 44 additions and 46 deletions

View File

@ -1,4 +1,4 @@
import { useState, useRef, useCallback, useMemo, useEffect, useLayoutEffect } from 'react'; import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
import { Icon } from './Icon'; import { Icon } from './Icon';
import { silence, getPunchcardPainter, noteToMidi } from '@strudel.cycles/core'; import { silence, getPunchcardPainter, noteToMidi } from '@strudel.cycles/core';
import { transpiler } from '@strudel.cycles/transpiler'; import { transpiler } from '@strudel.cycles/transpiler';
@ -8,9 +8,7 @@ import { StrudelMirror } from '@strudel/codemirror';
import { prebake } from '../repl/prebake.mjs'; import { prebake } from '../repl/prebake.mjs';
import { loadModules } from '../repl/util.mjs'; import { loadModules } from '../repl/util.mjs';
import Claviature from '@components/Claviature'; import Claviature from '@components/Claviature';
import useClient from '@src/useClient.mjs';
// https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
let prebaked, modulesLoading; let prebaked, modulesLoading;
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@ -91,19 +89,7 @@ export function MiniRepl({
const { started, isDirty, error } = replState; const { started, isDirty, error } = replState;
const editorRef = useRef(); const editorRef = useRef();
const containerRef = useRef(); const containerRef = useRef();
const [client, setClient] = useState(false); const client = useClient();
useIsomorphicLayoutEffect(() => {
setClient(true);
if (!editorRef.current) {
setTimeout(() => {
init({ code, shouldDraw });
});
}
return () => {
editorRef.current?.clear();
};
}, []);
if (!client) { if (!client) {
return <pre>{code}</pre>; return <pre>{code}</pre>;
@ -136,7 +122,14 @@ export function MiniRepl({
</div> </div>
)} )}
<div className="overflow-auto relative p-1"> <div className="overflow-auto relative p-1">
<div ref={containerRef}></div> <div
ref={(el) => {
if (!editorRef.current) {
containerRef.current = el;
init({ code, shouldDraw });
}
}}
></div>
{error && <div className="text-right p-1 text-md text-red-200">{error.message}</div>} {error && <div className="text-right p-1 text-md text-red-200">{error.message}</div>}
</div> </div>
{shouldShowCanvas && ( {shouldShowCanvas && (

View File

@ -9,6 +9,6 @@ import { Repl } from '../repl/Repl';
<title>Strudel REPL</title> <title>Strudel REPL</title>
</head> </head>
<body class="h-app-height bg-background"> <body class="h-app-height bg-background">
<Repl client:only="react" /> <Repl client:load />
</body> </body>
</html> </html>

View File

@ -23,7 +23,7 @@ export function Header({ context }) {
handleShuffle, handleShuffle,
handleShare, handleShare,
} = context; } = context;
const isEmbedded = embedded || window.location !== window.parent.location; const isEmbedded = typeof window !== 'undefined' && (embedded || window.location !== window.parent.location);
const { isZen } = useSettings(); const { isZen } = useSettings();
return ( return (

View File

@ -35,19 +35,15 @@ export const ReplContext = createContext(null);
const { latestCode } = settingsMap.get(); const { latestCode } = settingsMap.get();
initAudioOnFirstClick(); let modulesLoading, presets, drawContext, clearCanvas;
const modulesLoading = loadModules();
const presets = prebake();
let drawContext, clearCanvas;
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
initAudioOnFirstClick();
modulesLoading = loadModules();
presets = prebake();
drawContext = getDrawContext(); drawContext = getDrawContext();
clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width); clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width);
} }
// const getTime = () => getAudioContext().currentTime;
export function Repl({ embedded = false }) { export function Repl({ embedded = false }) {
//const isEmbedded = embedded || window.location !== window.parent.location; //const isEmbedded = embedded || window.location !== window.parent.location;
const isEmbedded = false; const isEmbedded = false;
@ -116,19 +112,6 @@ export function Repl({ embedded = false }) {
const { started, isDirty, error, activeCode } = replState; const { started, isDirty, error, activeCode } = replState;
const editorRef = useRef(); const editorRef = useRef();
const containerRef = useRef(); const containerRef = useRef();
const [client, setClient] = useState(false);
useEffect(() => {
setClient(true);
if (!editorRef.current) {
setTimeout(() => {
init({ shouldDraw });
});
}
return () => {
editorRef.current?.clear();
delete editorRef.current;
};
}, []);
// this can be simplified once SettingsTab has been refactored to change codemirrorSettings directly! // this can be simplified once SettingsTab has been refactored to change codemirrorSettings directly!
// this will be the case when the main repl is being replaced // this will be the case when the main repl is being replaced
@ -217,7 +200,12 @@ export function Repl({ embedded = false }) {
<section <section
className={'text-gray-100 cursor-text pb-0 overflow-auto grow' + (isZen ? ' px-10' : '')} className={'text-gray-100 cursor-text pb-0 overflow-auto grow' + (isZen ? ' px-10' : '')}
id="code" id="code"
ref={containerRef} ref={(el) => {
containerRef.current = el;
if (!editorRef.current) {
init({ shouldDraw });
}
}}
></section> ></section>
{panelPosition === 'right' && !isEmbedded && <Panel context={context} />} {panelPosition === 'right' && !isEmbedded && <Panel context={context} />}
</div> </div>

View File

@ -3,7 +3,7 @@ import { logger } from '@strudel.cycles/core';
import useEvent from '@src/useEvent.mjs'; import useEvent from '@src/useEvent.mjs';
import cx from '@src/cx.mjs'; import cx from '@src/cx.mjs';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react'; import { useCallback, useLayoutEffect, useEffect, useRef, useState } from 'react';
import { setActiveFooter, useSettings } from '../../settings.mjs'; import { setActiveFooter, useSettings } from '../../settings.mjs';
import { ConsoleTab } from './ConsoleTab'; import { ConsoleTab } from './ConsoleTab';
import { FilesTab } from './FilesTab'; import { FilesTab } from './FilesTab';
@ -12,21 +12,25 @@ import { SettingsTab } from './SettingsTab';
import { SoundsTab } from './SoundsTab'; import { SoundsTab } from './SoundsTab';
import { WelcomeTab } from './WelcomeTab'; import { WelcomeTab } from './WelcomeTab';
import { PatternsTab } from './PatternsTab'; import { PatternsTab } from './PatternsTab';
import useClient from '@src/useClient.mjs';
const TAURI = window.__TAURI__; // https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
const TAURI = typeof window !== 'undefined' && window.__TAURI__;
export function Panel({ context }) { export function Panel({ context }) {
const footerContent = useRef(); const footerContent = useRef();
const [log, setLog] = useState([]); const [log, setLog] = useState([]);
const { activeFooter, isZen, panelPosition } = useSettings(); const { activeFooter, isZen, panelPosition } = useSettings();
useLayoutEffect(() => { useIsomorphicLayoutEffect(() => {
if (footerContent.current && activeFooter === 'console') { if (footerContent.current && activeFooter === 'console') {
// scroll log box to bottom when log changes // scroll log box to bottom when log changes
footerContent.current.scrollTop = footerContent.current?.scrollHeight; footerContent.current.scrollTop = footerContent.current?.scrollHeight;
} }
}, [log, activeFooter]); }, [log, activeFooter]);
useLayoutEffect(() => { useIsomorphicLayoutEffect(() => {
if (!footerContent.current) { if (!footerContent.current) {
} else if (activeFooter === 'console') { } else if (activeFooter === 'console') {
footerContent.current.scrollTop = footerContent.current?.scrollHeight; footerContent.current.scrollTop = footerContent.current?.scrollHeight;
@ -80,6 +84,10 @@ export function Panel({ context }) {
right: cx('max-w-full flex-grow-0 flex-none overflow-hidden', isActive ? 'w-[600px] h-full' : 'absolute right-0'), right: cx('max-w-full flex-grow-0 flex-none overflow-hidden', isActive ? 'w-[600px] h-full' : 'absolute right-0'),
bottom: cx('relative', isActive ? 'h-[360px] min-h-[360px]' : ''), bottom: cx('relative', isActive ? 'h-[360px] min-h-[360px]' : ''),
}; };
const client = useClient();
if (!client) {
return null;
}
return ( return (
<nav className={cx('bg-lineHighlight z-[10] flex flex-col', positions[panelPosition])}> <nav className={cx('bg-lineHighlight z-[10] flex flex-col', positions[panelPosition])}>
<div className="flex justify-between px-2"> <div className="flex justify-between px-2">

View File

@ -0,0 +1,9 @@
import { useEffect, useState } from 'react';
export default function useClient() {
const [client, setClient] = useState(false);
useEffect(() => {
setClient(true);
}, []);
return client;
}