remove panel hover + optimize bottom panel

This commit is contained in:
Felix Roos 2026-01-20 23:06:04 +01:00
parent 4f14ff00d5
commit 60b5024f63
No known key found for this signature in database
5 changed files with 121 additions and 138 deletions

View file

@ -13,7 +13,7 @@ export function Header({ context, embedded = false }) {
const { started, pending, isDirty, activeCode, handleTogglePlay, handleEvaluate, handleShuffle, handleShare } = const { started, pending, isDirty, activeCode, handleTogglePlay, handleEvaluate, handleShuffle, handleShare } =
context; context;
const isEmbedded = typeof window !== 'undefined' && (embedded || window.location !== window.parent.location); const isEmbedded = typeof window !== 'undefined' && (embedded || window.location !== window.parent.location);
const { isZen, isButtonRowHidden, isCSSAnimationDisabled, fontFamily, isPanelOpen } = useSettings(); const { isZen, isButtonRowHidden, isCSSAnimationDisabled, fontFamily, isPanelOpen, panelPosition } = useSettings();
return ( return (
<header <header
@ -22,11 +22,12 @@ export function Header({ context, embedded = false }) {
'flex-none text-black z-[100] text-sm select-none min-h-10', 'flex-none text-black z-[100] text-sm select-none min-h-10',
!isZen && !isEmbedded && 'bg-lineHighlight', !isZen && !isEmbedded && 'bg-lineHighlight',
isZen ? 'h-12 w-8 fixed top-0 left-0' : 'sticky top-0 w-full justify-between', isZen ? 'h-12 w-8 fixed top-0 left-0' : 'sticky top-0 w-full justify-between',
isEmbedded ? 'flex' : 'sm:flex', 'flex items-center',
)} )}
style={{ fontFamily }} style={{ fontFamily }}
> >
<div className="px-4 pt-1 flex space-x-2 md:pt-0 select-none"> <div className={cx(isEmbedded ? 'flex' : 'sm:flex', 'w-full sm:justify-between')}>
<div className="px-3 pt-1 flex space-x-2 sm:pt-0 select-none">
<h1 <h1
onClick={() => { onClick={() => {
if (isEmbedded) window.open(window.location.href.replace('embed', '')); if (isEmbedded) window.open(window.location.href.replace('embed', ''));
@ -67,11 +68,11 @@ export function Header({ context, embedded = false }) {
</h1> </h1>
</div> </div>
{!isZen && !isButtonRowHidden && ( {!isZen && !isButtonRowHidden && (
<div className="flex max-w-full overflow-auto text-foreground px-2 h-10 justify-end"> <div className="flex max-w-full overflow-auto text-foreground px-3 h-10">
<button <button
onClick={handleTogglePlay} onClick={handleTogglePlay}
title={started ? 'stop' : 'play'} title={started ? 'stop' : 'play'}
className={cx('px-2 hover:opacity-50', !started && !isCSSAnimationDisabled && 'animate-pulse')} className={cx('hover:opacity-50', !started && !isCSSAnimationDisabled && 'animate-pulse')}
> >
{!pending ? ( {!pending ? (
<span className={cx('flex items-center space-x-2')}> <span className={cx('flex items-center space-x-2')}>
@ -110,18 +111,24 @@ export function Header({ context, embedded = false }) {
<span>learn</span> <span>learn</span>
</a> </a>
)} )}
{!isEmbedded && ( </div>
)}
</div>
<div>
{!isEmbedded && !isZen && panelPosition === 'right' && (
<button <button
title="share" title="share"
className={cx('cursor-pointer hover:opacity-50 flex items-center space-x-1 pl-2 pr-1')} className={cx(
'cursor-pointer hover:opacity-50 flex items-center space-x-1 pr-3 text-foreground',
// isPanelOpen && 'opacity-0',
)}
onClick={() => setIsPanelOpened(!isPanelOpen)} onClick={() => setIsPanelOpened(!isPanelOpen)}
> >
<span>menu</span> {/* <span>menu</span> */}
<Bars3Icon className="w-5 h-5" /> <Bars3Icon className="w-5 h-5" />
</button> </button>
)} )}
</div> </div>
)}
</header> </header>
); );
} }

View file

@ -14,27 +14,25 @@ import ExportTab from './ExportTab';
const TAURI = typeof window !== 'undefined' && window.__TAURI__; const TAURI = typeof window !== 'undefined' && window.__TAURI__;
export function HorizontalPanel({ context }) { export function HorizontalPanel({ context }) {
const settings = useSettings(); const { isPanelOpen, activeFooter: tab } = useSettings();
const { isPanelOpen, activeFooter: tab } = settings;
return ( return (
<PanelNav <PanelNav
settings={settings} className={cx(
className={cx(isPanelOpen ? `min-h-[360px] max-h-[360px]` : 'min-h-12 max-h-12', 'overflow-hidden flex flex-col')} isPanelOpen ? `min-h-[360px] max-h-[360px]` : 'min-h-10 max-h-10',
'overflow-hidden flex flex-col relative ',
)}
> >
<div className="flex justify-between min-h-10 max-h-10 grid-cols-2 items-center">
<Tabs setTab={setTab} tab={tab} />
</div>
<div className="absolute right-0 top-1">
<PanelActionButton />
</div>
{isPanelOpen && ( {isPanelOpen && (
<div className="flex h-full overflow-auto pr-10 "> <div className="flex h-full overflow-auto pr-10 ">
<PanelContent context={context} tab={tab} /> <PanelContent context={context} tab={tab} />
</div> </div>
)} )}
<div className="absolute right-4 pt-4">
<PanelActionButton settings={settings} />
</div>
<div className="flex justify-between min-h-12 max-h-12 grid-cols-2 items-center">
<Tabs setTab={setTab} tab={tab} />
</div>
</PanelNav> </PanelNav>
); );
} }
@ -53,7 +51,7 @@ export function VerticalPanel({ context }) {
<div className={cx('flex flex-col h-full')}> <div className={cx('flex flex-col h-full')}>
<div className="flex justify-between w-full "> <div className="flex justify-between w-full ">
<Tabs setTab={setTab} tab={tab} /> <Tabs setTab={setTab} tab={tab} />
<PanelActionButton settings={settings} /> {/* <PanelActionButton settings={settings} /> */}
</div> </div>
<div className="overflow-auto h-full"> <div className="overflow-auto h-full">
@ -77,8 +75,8 @@ if (TAURI) {
tabNames.files = 'files'; tabNames.files = 'files';
} }
function PanelNav({ children, className, settings, ...props }) { function PanelNav({ children, className, ...props }) {
const isHoverBehavior = settings.togglePanelTrigger === 'hover'; const settings = useSettings();
return ( return (
<nav <nav
onClick={() => { onClick={() => {
@ -86,16 +84,6 @@ function PanelNav({ children, className, settings, ...props }) {
setIsPanelOpened(true); setIsPanelOpened(true);
} }
}} }}
onMouseEnter={() => {
if (isHoverBehavior && !settings.isPanelOpen) {
setIsPanelOpened(true);
}
}}
onMouseLeave={() => {
if (isHoverBehavior && !settings.isPanelPinned) {
setIsPanelOpened(false);
}
}}
aria-label="Menu Panel" aria-label="Menu Panel"
className={cx('bg-lineHighlight group overflow-x-auto', className)} className={cx('bg-lineHighlight group overflow-x-auto', className)}
{...props} {...props}
@ -142,27 +130,24 @@ function PanelTab({ label, isSelected, onClick }) {
</> </>
); );
} }
function Tabs({ setTab, tab, className }) { function Tabs({ className }) {
const { isPanelOpen, activeFooter: tab } = useSettings();
return ( return (
<div className={cx('flex select-none max-w-full overflow-auto pb-2', className)}> <div className={cx('flex select-none max-w-full overflow-auto pb-2', className)}>
{Object.keys(tabNames).map((key) => { {Object.keys(tabNames).map((key) => {
const val = tabNames[key]; const val = tabNames[key];
return <PanelTab key={key} isSelected={tab === val} label={key} onClick={() => setTab(val)} />; return <PanelTab key={key} isSelected={tab === val && isPanelOpen} label={key} onClick={() => setTab(val)} />;
})} })}
</div> </div>
); );
} }
function PanelActionButton({ settings }) { function PanelActionButton() {
const { togglePanelTrigger, isPanelPinned, isPanelOpen } = settings; const settings = useSettings();
const isHoverBehavior = togglePanelTrigger === 'hover'; const { isPanelOpen } = settings;
if (!isPanelOpen) { if (!isPanelOpen) {
return; return;
} }
if (isHoverBehavior) {
return <PinButton pinned={isPanelPinned} />;
}
return <CloseButton onClick={() => setIsPanelOpened(false)} />; return <CloseButton onClick={() => setIsPanelOpened(false)} />;
} }

View file

@ -144,7 +144,6 @@ export function SettingsTab({ started }) {
panelPosition, panelPosition,
audioDeviceName, audioDeviceName,
audioEngineTarget, audioEngineTarget,
togglePanelTrigger,
maxPolyphony, maxPolyphony,
multiChannelOrbits, multiChannelOrbits,
isTabIndentationEnabled, isTabIndentationEnabled,
@ -256,13 +255,6 @@ export function SettingsTab({ started }) {
items={{ bottom: 'Bottom', right: 'Right' }} items={{ bottom: 'Bottom', right: 'Right' }}
></ButtonGroup> ></ButtonGroup>
</FormItem> </FormItem>
<FormItem label="Open Panel on: ">
<ButtonGroup
value={togglePanelTrigger}
onChange={(value) => settingsMap.setKey('togglePanelTrigger', value)}
items={{ click: 'Click', hover: 'Hover' }}
/>
</FormItem>
<FormItem label="More Settings"> <FormItem label="More Settings">
<Checkbox <Checkbox
label="Enable bracket matching" label="Enable bracket matching"

View file

@ -7,7 +7,7 @@ export function WelcomeTab({ context }) {
const { fontFamily } = useSettings(); const { fontFamily } = useSettings();
return ( return (
<div className="prose dark:prose-invert min-w-full pt-2 font-sans pb-8 px-4 " style={{ fontFamily }}> <div className="prose dark:prose-invert min-w-full pt-2 font-sans pb-8 px-4 " style={{ fontFamily }}>
<h3> welcome</h3> <h3>welcome</h3>
<p> <p>
You have found <span className="underline">strudel</span>, a new live coding platform to write dynamic music You have found <span className="underline">strudel</span>, a new live coding platform to write dynamic music
pieces in the browser! It is free and open-source and made for beginners and experts alike. To get started: pieces in the browser! It is free and open-source and made for beginners and experts alike. To get started:
@ -30,7 +30,7 @@ export function WelcomeTab({ context }) {
</a>{' '} </a>{' '}
to ask any questions, give feedback or just say hello. to ask any questions, give feedback or just say hello.
</p> </p>
<h3> about</h3> <h3>about</h3>
<p> <p>
strudel is a JavaScript version of{' '} strudel is a JavaScript version of{' '}
<a href="https://tidalcycles.org/" target="_blank"> <a href="https://tidalcycles.org/" target="_blank">

View file

@ -43,7 +43,6 @@ export const defaultSettings = {
panelPosition: 'right', panelPosition: 'right',
isPanelPinned: false, isPanelPinned: false,
isPanelOpen: true, isPanelOpen: true,
togglePanelTrigger: 'click', //click | hover
userPatterns: '{}', userPatterns: '{}',
prebakeScript: '', prebakeScript: '',
audioEngineTarget: audioEngineTargets.webaudio, audioEngineTarget: audioEngineTargets.webaudio,