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,115 +13,122 @@ export function Header({ context, embedded = false }) {
const { started, pending, isDirty, activeCode, handleTogglePlay, handleEvaluate, handleShuffle, handleShare } =
context;
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 (
<header
id="header"
className={cx(
'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 ? '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 }}
>
<div className="px-4 pt-1 flex space-x-2 md:pt-0 select-none">
<h1
onClick={() => {
if (isEmbedded) window.open(window.location.href.replace('embed', ''));
}}
className={cx(
isEmbedded ? 'text-l cursor-pointer' : 'text-xl',
'text-foreground font-bold flex space-x-2 items-center',
)}
>
<div
className={cx(
'mt-[1px]',
started && !isCSSAnimationDisabled && 'animate-spin',
'cursor-pointer text-blue-500',
isZen && 'fixed top-2 right-4',
)}
<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
onClick={() => {
if (!isEmbedded) {
setIsZen(!isZen);
}
if (isEmbedded) window.open(window.location.href.replace('embed', ''));
}}
>
<span className="block text-foreground rotate-90">
<StrudelIcon className="w-5 h-5 fill-foreground" />
</span>
</div>
{!isZen && (
<div className="space-x-2">
<span className="">strudel</span>
<span className="text-sm font-medium">REPL</span>
{!isEmbedded && isButtonRowHidden && (
<a href={`${baseNoTrailing}/learn`} className="text-sm opacity-25 font-medium">
DOCS
</a>
)}
</div>
)}
</h1>
</div>
{!isZen && !isButtonRowHidden && (
<div className="flex max-w-full overflow-auto text-foreground px-2 h-10 justify-end">
<button
onClick={handleTogglePlay}
title={started ? 'stop' : 'play'}
className={cx('px-2 hover:opacity-50', !started && !isCSSAnimationDisabled && 'animate-pulse')}
>
{!pending ? (
<span className={cx('flex items-center space-x-2')}>
{started ? <StopCircleIcon className="w-5 h-5" /> : <PlayCircleIcon className="w-5 h-5" />}
{!isEmbedded && <span>{started ? 'stop' : 'play'}</span>}
</span>
) : (
<>loading...</>
)}
</button>
<button
onClick={handleEvaluate}
title="update"
className={cx(
'flex items-center space-x-1 px-2',
!isDirty || !activeCode ? 'opacity-50' : 'hover:opacity-50',
isEmbedded ? 'text-l cursor-pointer' : 'text-xl',
'text-foreground font-bold flex space-x-2 items-center',
)}
>
{!isEmbedded && <span>update</span>}
</button>
{!isEmbedded && (
<button
title="share"
className={cx('cursor-pointer hover:opacity-50 flex items-center space-x-1 px-2')}
onClick={handleShare}
<div
className={cx(
'mt-[1px]',
started && !isCSSAnimationDisabled && 'animate-spin',
'cursor-pointer text-blue-500',
isZen && 'fixed top-2 right-4',
)}
onClick={() => {
if (!isEmbedded) {
setIsZen(!isZen);
}
}}
>
<span>share</span>
</button>
)}
{!isEmbedded && (
<a
title="learn"
href={`${baseNoTrailing}/workshop/getting-started/`}
className={cx('hover:opacity-50 flex items-center space-x-1', !isEmbedded ? 'p-2' : 'px-2')}
>
<span>learn</span>
</a>
)}
{!isEmbedded && (
<button
title="share"
className={cx('cursor-pointer hover:opacity-50 flex items-center space-x-1 pl-2 pr-1')}
onClick={() => setIsPanelOpened(!isPanelOpen)}
>
<span>menu</span>
<Bars3Icon className="w-5 h-5" />
</button>
)}
<span className="block text-foreground rotate-90">
<StrudelIcon className="w-5 h-5 fill-foreground" />
</span>
</div>
{!isZen && (
<div className="space-x-2">
<span className="">strudel</span>
<span className="text-sm font-medium">REPL</span>
{!isEmbedded && isButtonRowHidden && (
<a href={`${baseNoTrailing}/learn`} className="text-sm opacity-25 font-medium">
DOCS
</a>
)}
</div>
)}
</h1>
</div>
)}
{!isZen && !isButtonRowHidden && (
<div className="flex max-w-full overflow-auto text-foreground px-3 h-10">
<button
onClick={handleTogglePlay}
title={started ? 'stop' : 'play'}
className={cx('hover:opacity-50', !started && !isCSSAnimationDisabled && 'animate-pulse')}
>
{!pending ? (
<span className={cx('flex items-center space-x-2')}>
{started ? <StopCircleIcon className="w-5 h-5" /> : <PlayCircleIcon className="w-5 h-5" />}
{!isEmbedded && <span>{started ? 'stop' : 'play'}</span>}
</span>
) : (
<>loading...</>
)}
</button>
<button
onClick={handleEvaluate}
title="update"
className={cx(
'flex items-center space-x-1 px-2',
!isDirty || !activeCode ? 'opacity-50' : 'hover:opacity-50',
)}
>
{!isEmbedded && <span>update</span>}
</button>
{!isEmbedded && (
<button
title="share"
className={cx('cursor-pointer hover:opacity-50 flex items-center space-x-1 px-2')}
onClick={handleShare}
>
<span>share</span>
</button>
)}
{!isEmbedded && (
<a
title="learn"
href={`${baseNoTrailing}/workshop/getting-started/`}
className={cx('hover:opacity-50 flex items-center space-x-1', !isEmbedded ? 'p-2' : 'px-2')}
>
<span>learn</span>
</a>
)}
</div>
)}
</div>
<div>
{!isEmbedded && !isZen && panelPosition === 'right' && (
<button
title="share"
className={cx(
'cursor-pointer hover:opacity-50 flex items-center space-x-1 pr-3 text-foreground',
// isPanelOpen && 'opacity-0',
)}
onClick={() => setIsPanelOpened(!isPanelOpen)}
>
{/* <span>menu</span> */}
<Bars3Icon className="w-5 h-5" />
</button>
)}
</div>
</header>
);
}

View file

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

View file

@ -144,7 +144,6 @@ export function SettingsTab({ started }) {
panelPosition,
audioDeviceName,
audioEngineTarget,
togglePanelTrigger,
maxPolyphony,
multiChannelOrbits,
isTabIndentationEnabled,
@ -256,13 +255,6 @@ export function SettingsTab({ started }) {
items={{ bottom: 'Bottom', right: 'Right' }}
></ButtonGroup>
</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">
<Checkbox
label="Enable bracket matching"

View file

@ -7,7 +7,7 @@ export function WelcomeTab({ context }) {
const { fontFamily } = useSettings();
return (
<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>
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:
@ -30,7 +30,7 @@ export function WelcomeTab({ context }) {
</a>{' '}
to ask any questions, give feedback or just say hello.
</p>
<h3> about</h3>
<h3>about</h3>
<p>
strudel is a JavaScript version of{' '}
<a href="https://tidalcycles.org/" target="_blank">

View file

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