Compare commits

..

15 Commits

Author SHA1 Message Date
Jade (Rose) Rowland 90c05ec38a fix edge cases 2026-02-08 14:12:21 -05:00
Jade (Rose) Rowland 7e0eed87cb normalize shortcuts 2026-01-27 17:21:36 -05:00
Jade (Rose) Rowland 1a1197f67a Merge branch 'main' into soloshortcut 2026-01-22 21:11:31 -05:00
froos 28db6d1fd4 Merge pull request 'clean up crumbs of crunchy design' (#1942) from stylez into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1942
2026-01-22 22:15:01 +01:00
Aria dc9cadc979 Merge pull request 'Feat: Make default alignment configurable' (#1941) from glossing/alignment into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1941
Reviewed-by: Switch Angel AKA Jade Rose <daslyfe@noreply.codeberg.org>
2026-01-22 21:50:28 +01:00
Felix Roos f99b13efc2 fix: zen mode lost border 2026-01-22 21:46:48 +01:00
Felix Roos 4993ec4cbf fix: console scroll to bottom on change + more logs + dont wrap 2026-01-22 20:40:35 +01:00
Felix Roos 8e5ca57edd cleanup + pending state 2026-01-22 20:29:03 +01:00
Felix Roos 107a3acd3a rename panel components + fix borders 2026-01-22 20:24:04 +01:00
Felix Roos 1d887e81ef fix: h-full 2026-01-22 20:18:59 +01:00
Jade (Rose) Rowland 3c5afb32f3 scroll to center 2026-01-22 02:04:27 -05:00
Jade (Rose) Rowland 76cbd23859 Merge branch 'soloshortcut' of ssh://codeberg.org/uzu/strudel into soloshortcut 2026-01-22 00:25:41 -05:00
Jade (Rose) Rowland b436ae789c rm dead code 2026-01-22 00:25:28 -05:00
Switch Angel AKA Jade Rose 275731afc7 Merge branch 'main' into soloshortcut 2026-01-22 06:22:17 +01:00
Jade (Rose) Rowland 5cb2214d88 working 2026-01-22 00:20:44 -05:00
7 changed files with 239 additions and 65 deletions
+75 -3
View File
@@ -24,7 +24,12 @@ import { sliderPlugin, updateSliderWidgets } from './slider.mjs';
import { activateTheme, initTheme, theme } from './themes.mjs'; import { activateTheme, initTheme, theme } from './themes.mjs';
import { isTooltipEnabled } from './tooltip.mjs'; import { isTooltipEnabled } from './tooltip.mjs';
import { updateWidgets, widgetPlugin } from './widget.mjs'; import { updateWidgets, widgetPlugin } from './widget.mjs';
import { jumpToCharacter } from './labelJump.mjs'; import {
deleteAllInlineBeforeCharacter,
InsertCharBeforeChar,
jumpToCharacter,
jumpToNextCharacter,
} from './labelJump.mjs';
export { toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment } from '@codemirror/commands'; export { toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment } from '@codemirror/commands';
@@ -74,6 +79,10 @@ export const codemirrorSettings = persistentAtom('codemirror-settings', defaultS
decode: JSON.parse, decode: JSON.parse,
}); });
const ANON_LABEL = '$';
const SOLO_LABEL = 'S';
const MUTE_LABEL = '_';
// https://codemirror.net/docs/guide/ // https://codemirror.net/docs/guide/
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root, mondo }) { export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root, mondo }) {
const settings = codemirrorSettings.get(); const settings = codemirrorSettings.get();
@@ -122,12 +131,75 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo
}, },
{ {
key: 'Alt-w', key: 'Alt-w',
run: (view) => jumpToCharacter(view, '$', 1), run: (view) => jumpToNextCharacter(view, ANON_LABEL, 1),
}, },
{ {
key: 'Alt-q', key: 'Alt-q',
run: (view) => jumpToCharacter(view, '$', -1), run: (view) => {
return jumpToNextCharacter(view, ANON_LABEL, -1);
},
}, },
// clear all muted
{
key: `Alt-Ctrl-0`,
run: (view) => {
return deleteAllInlineBeforeCharacter(view, MUTE_LABEL + ANON_LABEL);
},
},
// clear all solod
{
key: `Alt-Shift-0`,
run: (view) => {
return deleteAllInlineBeforeCharacter(view, SOLO_LABEL + ANON_LABEL);
},
},
// clear all solo and mute
{
key: `Ctrl-Shift-0`,
run: (view) => {
return deleteAllInlineBeforeCharacter(view, ANON_LABEL);
},
},
...Array.from({ length: 9 }).map((_, i) => {
let num = i + 1;
return {
key: `Alt-${num}`,
run: (view) => {
return jumpToCharacter(view, ANON_LABEL, i);
},
};
}),
// handle solo toggles 1-9
...Array.from({ length: 9 }).map((_, i) => {
let num = i + 1;
return {
key: `Alt-Shift-${num}`,
run: (view) => {
return InsertCharBeforeChar(view, ANON_LABEL, SOLO_LABEL, i);
},
};
}),
// handle mute toggles 1-9
...Array.from({ length: 9 }).map((_, i) => {
let num = i + 1;
return {
key: `Alt-Ctrl-${num}`,
run: (view) => {
return InsertCharBeforeChar(view, ANON_LABEL, MUTE_LABEL, i);
},
};
}),
// Handle clearing mutes and solos 1-9
...Array.from({ length: 9 }).map((_, i) => {
let num = i + 1;
return {
key: `Ctrl-Shift-${num}`,
run: (view) => {
return InsertCharBeforeChar(view, ANON_LABEL,'', i);
},
};
}),
/* { /* {
key: 'Ctrl-Shift-.', key: 'Ctrl-Shift-.',
run: () => (onPanic ? onPanic() : onStop?.()), run: () => (onPanic ? onPanic() : onStop?.()),
+133 -11
View File
@@ -1,18 +1,54 @@
import { EditorSelection } from '@codemirror/state'; import { EditorSelection } from '@codemirror/state';
import { SearchCursor } from '@codemirror/search'; import { SearchCursor } from '@codemirror/search';
import { EditorView } from '@codemirror/view';
import { syntaxTree } from '@codemirror/language';
export function jumpToCharacter(view, character, direction = 1) { /**
* gets all of the positions of a character in a document, excluding commented out lines
* @param { EditorState} state
* @param {String} character
* @returns {number[]}
*/
function getCharacterPositions(state, character) {
const cursor = new SearchCursor(state.doc, character);
const characterPositions = [];
while (!cursor.next().done) {
const linestartpos = state.doc.lineAt(cursor.value.to).from
if (!isLineCommentedOut(state, linestartpos)) {
characterPositions.push(cursor.value.to);
}
}
return characterPositions;
}
function isLineCommentedOut(state, pos) {
const line = state.doc.lineAt(pos);
// remove white space
pos = line.from + line.text.search(/\S/)
const tree = syntaxTree(state);
const node = tree.resolveInner(pos, 1)
return node.name.includes("Comment")
}
/**
* jump to the next character in a document
* @param {EditorView} view
* @param {String} character
* @param {number} direction 0 or 1
* @returns {boolean}
*/
export function jumpToNextCharacter(view, character, direction = 1) {
const { state, dispatch } = view; const { state, dispatch } = view;
const pos = state.selection.main.head; const pos = state.selection.main.head;
const cursor = new SearchCursor(state.doc, character);
let characterPositions = [];
let jumpPos; let jumpPos;
while (!cursor.next().done) { const characterPositions = getCharacterPositions(state, character);
characterPositions.push(cursor.value.to);
}
if (!characterPositions.length) { if (!characterPositions.length) {
return false; return true;
} }
if (direction > 0) { if (direction > 0) {
jumpPos = characterPositions.find((x) => x > pos + 1) ?? characterPositions.at(0); // Loop back around for convenience jumpPos = characterPositions.find((x) => x > pos + 1) ?? characterPositions.at(0); // Loop back around for convenience
@@ -21,11 +57,97 @@ export function jumpToCharacter(view, character, direction = 1) {
} }
if (jumpPos == null) { if (jumpPos == null) {
return false; return true;
} }
const selection = EditorSelection.cursor(jumpPos - 1);
dispatch({ dispatch({
selection: EditorSelection.cursor(jumpPos - 1), selection,
scrollIntoView: true, effects: EditorView.scrollIntoView(
selection.head,
{ y: "start" }
)
}); });
return true; return true;
} }
/**
*
* @param {EditorView} view
* @param {String} character
* @param {number} index the instance of the character
* @returns
*/
export function jumpToCharacter(view, character, index) {
const { state, dispatch } = view;
const characterPositions = getCharacterPositions(state, character);
const pos = characterPositions.at(index) ?? characterPositions.at(-1);
if (pos == null) {
return true;
}
const selection = EditorSelection.cursor(pos - 1);
dispatch({
selection,
effects: EditorView.scrollIntoView(
selection.head,
{ y: "start" }
)
});
return true;
}
/**
*
* @param {EditorView} view
* @param {String} character
* @returns {true}
*/
export function deleteAllInlineBeforeCharacter(view, character) {
const { state, dispatch } = view;
const characterPositions = getCharacterPositions(state, character);
const changes = [];
characterPositions.forEach((pos) => {
const line = state.doc.lineAt(pos);
if (state.doc.sliceString(line.from, line.from + 2) === COMMENT_STRING) {
return;
}
changes.push({
from: line.from,
to: pos - 1,
insert: '',
});
});
dispatch({ changes });
return true;
}
/**
*
* @param {EditorView} view
* @param {String} character
* @param {String} character2
* @param {number} index
* @returns
*/
export function InsertCharBeforeChar(view, character, character2, index) {
const { state, dispatch } = view;
const changes = [];
const characterPositions = getCharacterPositions(state, character);
const labelpos = characterPositions.at(index) ?? characterPositions.at(-1);
const line = state.doc.lineAt(labelpos);
//delete preceeding characters
changes.push({
from: line.from,
to: labelpos - 1,
insert: '',
});
changes.push({
insert: character2,
from: line.from,
});
dispatch({ changes });
return true;
}
+2 -2
View File
@@ -1,5 +1,5 @@
import Loader from '@src/repl/components/Loader'; import Loader from '@src/repl/components/Loader';
import { HorizontalPanel } from '@src/repl/components/panel/Panel'; import { BottomPanel } from '@src/repl/components/panel/Panel';
import { Code } from '@src/repl/components/Code'; import { Code } from '@src/repl/components/Code';
import BigPlayButton from '@src/repl/components/BigPlayButton'; import BigPlayButton from '@src/repl/components/BigPlayButton';
import UserFacingErrorMessage from '@src/repl/components/UserFacingErrorMessage'; import UserFacingErrorMessage from '@src/repl/components/UserFacingErrorMessage';
@@ -20,7 +20,7 @@ export default function UdelsEditor(Props) {
<Code containerRef={containerRef} editorRef={editorRef} init={init} /> <Code containerRef={containerRef} editorRef={editorRef} init={init} />
</div> </div>
<UserFacingErrorMessage error={error} /> <UserFacingErrorMessage error={error} />
<HorizontalPanel context={context} /> <BottomPanel context={context} />
</div> </div>
); );
} }
+4 -4
View File
@@ -1,6 +1,6 @@
import { Code } from '@src/repl/components/Code'; import { Code } from '@src/repl/components/Code';
import Loader from '@src/repl/components/Loader'; import Loader from '@src/repl/components/Loader';
import { HorizontalPanel, MainPanel, VerticalPanel } from '@src/repl/components/panel/Panel'; import { BottomPanel, MainPanel, RightPanel } from '@src/repl/components/panel/Panel';
import UserFacingErrorMessage from '@src/repl/components/UserFacingErrorMessage'; import UserFacingErrorMessage from '@src/repl/components/UserFacingErrorMessage';
import { useSettings } from '@src/settings.mjs'; import { useSettings } from '@src/settings.mjs';
@@ -21,13 +21,13 @@ export default function ReplEditor(Props) {
<div className="flex flex-col grow overflow-hidden"> <div className="flex flex-col grow overflow-hidden">
{/* <MainPanel context={context} isEmbedded={isEmbedded} className="hidden sm:block" /> */} {/* <MainPanel context={context} isEmbedded={isEmbedded} className="hidden sm:block" /> */}
<MainPanel context={context} isEmbedded={isEmbedded} /> <MainPanel context={context} isEmbedded={isEmbedded} />
<div className="flex overflow-hidden"> <div className="flex overflow-hidden h-full">
<Code containerRef={containerRef} editorRef={editorRef} init={init} /> <Code containerRef={containerRef} editorRef={editorRef} init={init} />
{!isZen && panelPosition === 'right' && <VerticalPanel context={context} />} {!isZen && panelPosition === 'right' && <RightPanel context={context} />}
</div> </div>
</div> </div>
<UserFacingErrorMessage error={error} /> <UserFacingErrorMessage error={error} />
{!isZen && panelPosition === 'bottom' && <HorizontalPanel context={context} />} {!isZen && panelPosition === 'bottom' && <BottomPanel context={context} />}
{/* <MainPanel context={context} isEmbedded={isEmbedded} className="block sm:hidden" /> */} {/* <MainPanel context={context} isEmbedded={isEmbedded} className="block sm:hidden" /> */}
</div> </div>
); );
@@ -2,13 +2,21 @@ import cx from '@src/cx.mjs';
import { useSettings } from '../../../settings.mjs'; import { useSettings } from '../../../settings.mjs';
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { $strudel_log_history } from '../useLogger'; import { $strudel_log_history } from '../useLogger';
import { useEffect, useRef } from 'react';
export function ConsoleTab() { export function ConsoleTab() {
const log = useStore($strudel_log_history); const log = useStore($strudel_log_history);
const { fontFamily } = useSettings(); const { fontFamily } = useSettings();
const scrollRef = useRef();
// scroll to bottom when log changes
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}
}, [log]);
return ( return (
<div id="console-tab" className="break-all w-full h-full" style={{ fontFamily }}> <div id="console-tab" className="break-all w-full h-full" style={{ fontFamily }}>
<div className="h-full w-full overflow-auto space-y-1 p-2 rounded-md"> <div className="h-full w-full overflow-auto space-y-1 p-2 rounded-md" ref={scrollRef}>
{' '} {' '}
{/* bg-background */} {/* bg-background */}
{log.map((l, i) => { {log.map((l, i) => {
@@ -18,12 +26,13 @@ export function ConsoleTab() {
<div <div
key={l.id} key={l.id}
className={cx( className={cx(
'whitespace-nowrap',
l.type === 'error' ? 'text-background bg-foreground' : 'text-foreground', l.type === 'error' ? 'text-background bg-foreground' : 'text-foreground',
l.type === 'highlight' && 'underline', l.type === 'highlight' && 'underline',
)} )}
style={color ? { color } : {}} style={color ? { color } : {}}
> >
<span dangerouslySetInnerHTML={{ __html: message }} /> <span dangerouslySetInnerHTML={{ __html: message }} className="whitespace-nowrap" />
{l.count ? ` (${l.count})` : ''} {l.count ? ` (${l.count})` : ''}
</div> </div>
); );
+13 -42
View File
@@ -48,11 +48,8 @@ export function MainPanel({ context, isEmbedded = false, className }) {
<nav <nav
id="header" id="header"
className={cx( className={cx(
//'border-t sm:border-b sm:border-t-0 border-muted',
'border-b border-muted',
'flex-none text-black z-[100] text-sm select-none min-h-10 max-h-10', 'flex-none text-black z-[100] text-sm select-none min-h-10 max-h-10',
!isZen && !isEmbedded && 'bg-lineHighlight', !isZen && !isEmbedded && 'border-b border-muted bg-lineHighlight',
// isZen ? 'h-12 w-8 fixed top-0 left-0' : 'h-10 sticky top-0 w-full justify-between',
isZen ? 'h-12 w-8 fixed top-0 left-0' : '', isZen ? 'h-12 w-8 fixed top-0 left-0' : '',
'flex items-center', 'flex items-center',
className, className,
@@ -60,8 +57,6 @@ export function MainPanel({ context, isEmbedded = false, className }) {
style={{ fontFamily }} style={{ fontFamily }}
> >
<div className={cx('flex w-full justify-between')}> <div className={cx('flex w-full justify-between')}>
{' '}
{/*flex-wrap*/}
<div className="px-3 py-1 flex space-x-2 select-none"> <div className="px-3 py-1 flex space-x-2 select-none">
<h1 <h1
onClick={() => { onClick={() => {
@@ -77,11 +72,6 @@ export function MainPanel({ context, isEmbedded = false, className }) {
<div className="space-x-2 flex items-baseline"> <div className="space-x-2 flex items-baseline">
<span className="hidden sm:block">strudel</span> <span className="hidden sm:block">strudel</span>
<span className="text-sm font-medium hidden sm:block">REPL</span> <span className="text-sm font-medium hidden sm:block">REPL</span>
{/* !isEmbedded && isButtonRowHidden && (
<a href={`${baseNoTrailing}/learn`} className="text-sm opacity-25 font-medium">
DOCS
</a>
) */}
</div> </div>
)} )}
</h1> </h1>
@@ -89,7 +79,6 @@ export function MainPanel({ context, isEmbedded = false, className }) {
{!isZen && ( {!isZen && (
<div className="flex grow justify-end"> <div className="flex grow justify-end">
{!isButtonRowHidden && <MainMenu isEmbedded={isEmbedded} context={context} />} {!isButtonRowHidden && <MainMenu isEmbedded={isEmbedded} context={context} />}
{/* className="hidden sm:flex" */}
<PanelToggle isEmbedded={isEmbedded} isZen={isZen} /> <PanelToggle isEmbedded={isEmbedded} isZen={isZen} />
</div> </div>
)} )}
@@ -101,7 +90,6 @@ export function MainPanel({ context, isEmbedded = false, className }) {
export function Footer({ context, isEmbedded = false }) { export function Footer({ context, isEmbedded = false }) {
return ( return (
<div className="border-t border-muted bg-lineHighlight block lg:hidden"> <div className="border-t border-muted bg-lineHighlight block lg:hidden">
{/* block lg:hidden */}
<MainMenu context={context} isEmbedded={isEmbedded} /> <MainMenu context={context} isEmbedded={isEmbedded} />
</div> </div>
); );
@@ -117,15 +105,10 @@ function MainMenu({ context, isEmbedded = false, className }) {
title={started ? 'stop' : 'play'} title={started ? 'stop' : 'play'}
className={cx('px-2 hover:opacity-50', !started && !isCSSAnimationDisabled && 'animate-pulse')} className={cx('px-2 hover:opacity-50', !started && !isCSSAnimationDisabled && 'animate-pulse')}
> >
{/* {!pending ? ( */}
<span className={cx('flex items-center space-x-2')}> <span className={cx('flex items-center space-x-2')}>
{/* {started ? <StopCircleIcon className="w-5 h-5" /> : <PlayCircleIcon className="w-5 h-5" />} */}
{started ? <StopIcon className="w-5 h-5" /> : <PlayIcon className="w-5 h-5" />} {started ? <StopIcon className="w-5 h-5" /> : <PlayIcon className="w-5 h-5" />}
{!isEmbedded && <span>{started ? 'stop' : 'play'}</span>} {!isEmbedded && <span>{pending ? '...' : started ? 'stop' : 'play'}</span>}
</span> </span>
{/* ) : (
<>loading...</>
)} */}
</button> </button>
<button <button
onClick={handleEvaluate} onClick={handleEvaluate}
@@ -162,7 +145,7 @@ function PanelCloseButton() {
isPanelOpen && ( isPanelOpen && (
<button <button
onClick={() => setIsPanelOpened(false)} onClick={() => setIsPanelOpened(false)}
className={cx('border-l border-muted px-2 py-0 text-foreground hover:opacity-50')} className={cx('px-2 py-0 text-foreground hover:opacity-50')}
aria-label="Close Menu" aria-label="Close Menu"
> >
<XMarkIcon className="w-6 h-6" /> <XMarkIcon className="w-6 h-6" />
@@ -171,7 +154,7 @@ function PanelCloseButton() {
); );
} }
export function HorizontalPanel({ context }) { export function BottomPanel({ context }) {
const { isPanelOpen, activeFooter: tab } = useSettings(); const { isPanelOpen, activeFooter: tab } = useSettings();
return ( return (
<PanelNav <PanelNav
@@ -182,7 +165,7 @@ export function HorizontalPanel({ context }) {
> >
<div className="flex justify-between min-h-10 max-h-10 grid-cols-2 items-center border-t border-muted"> <div className="flex justify-between min-h-10 max-h-10 grid-cols-2 items-center border-t border-muted">
<PanelCloseButton /> <PanelCloseButton />
<Tabs setTab={setTab} tab={tab} /> <Tabs setTab={setTab} tab={tab} className={cx(isPanelOpen && 'border-l border-muted')} />
</div> </div>
{isPanelOpen && ( {isPanelOpen && (
<div className="w-full h-full overflow-auto border-t border-muted"> <div className="w-full h-full overflow-auto border-t border-muted">
@@ -193,7 +176,7 @@ export function HorizontalPanel({ context }) {
); );
} }
export function VerticalPanel({ context }) { export function RightPanel({ context }) {
const settings = useSettings(); const settings = useSettings();
const { activeFooter: tab, isPanelOpen } = settings; const { activeFooter: tab, isPanelOpen } = settings;
if (!isPanelOpen) { if (!isPanelOpen) {
@@ -203,24 +186,16 @@ export function VerticalPanel({ context }) {
<PanelNav <PanelNav
settings={settings} settings={settings}
className={cx( className={cx(
//'border-l border-muted shrink-0', 'border-l border-muted shrink-0 h-full overflow-hidden',
'border-0 border-muted shrink-0 h-full overflow-hidden', isPanelOpen ? `min-w-[min(600px,100vw)] max-w-[min(600px,80vw)]` : 'min-w-12 max-w-12',
isPanelOpen
? //? `min-w-full max-w-full lg:min-w-[min(600px,100vw)] lg:max-w-[min(600px,80vw)]`
`min-w-[min(600px,100vw)] max-w-[min(600px,80vw)]`
: 'min-w-12 max-w-12',
)} )}
> >
<div className={cx('flex flex-col h-full')}> <div className={cx('flex flex-col h-full')}>
<div className="flex justify-between w-full overflow-hidden border-b border-muted min-h-10 max-h-10"> <div className="flex justify-between w-full overflow-hidden border-b border-muted min-h-10 max-h-10">
{/* <div className="block sm:hidden text-foreground px-3 py-2 border-l border-muted">
<LogoButton context={context} />
</div> */}
<PanelCloseButton /> <PanelCloseButton />
<Tabs setTab={setTab} tab={tab} /> <Tabs setTab={setTab} tab={tab} className="border-l border-muted" />
{/* <PanelCloseButton /> */}
</div> </div>
<div className="overflow-auto h-full border-l border-muted"> <div className="overflow-auto h-full">
<PanelContent context={context} tab={tab} /> <PanelContent context={context} tab={tab} />
</div> </div>
</div> </div>
@@ -301,7 +276,7 @@ function Tabs({ className }) {
return ( return (
<div <div
className={cx( className={cx(
'px-2 border-l border-muted w-full flex select-none max-w-full h-10 max-h-10 min-h-10 overflow-auto items-center', 'px-2 w-full flex select-none max-w-full h-10 max-h-10 min-h-10 overflow-auto items-center',
className, className,
)} )}
> >
@@ -320,14 +295,10 @@ export function PanelToggle({ isEmbedded, isZen }) {
!isZen && !isZen &&
panelPosition === 'right' && ( panelPosition === 'right' && (
<button <button
title="share" title="menu"
className={cx( className={cx('border-l border-muted px-2 py-0 text-foreground hover:opacity-50')}
'border-l border-muted px-2 py-0 text-foreground hover:opacity-50' /* , isPanelOpen && 'hidden' */,
)}
onClick={() => setIsPanelOpened(!isPanelOpen)} onClick={() => setIsPanelOpened(!isPanelOpen)}
> >
{/* <span>menu</span> */}
{/* isPanelOpen ? <XMarkIcon className="w-6 h-6" /> : <Bars3Icon className="w-6 h-6" /> */}
<Bars3Icon className="w-6 h-6" /> <Bars3Icon className="w-6 h-6" />
</button> </button>
) )
+1 -1
View File
@@ -21,7 +21,7 @@ function getUpdatedLog(log, event) {
} else { } else {
log = log.concat([{ message, type, id, data }]); log = log.concat([{ message, type, id, data }]);
} }
return log.slice(-20); return log.slice(-40);
} }
export function useLogger() { export function useLogger() {