make it crunchier: border borders, less padding

This commit is contained in:
Felix Roos 2026-01-21 01:31:25 +01:00
parent 84e4dde3d7
commit cb46dd7840
No known key found for this signature in database
13 changed files with 96 additions and 93 deletions

View file

@ -5,7 +5,7 @@ export const settings = {
background: '#222', background: '#222',
lineBackground: '#22222299', lineBackground: '#22222299',
foreground: '#fff', foreground: '#fff',
muted: '#ffffff50', muted: '#8a919966',
caret: '#ffcc00', caret: '#ffcc00',
selection: 'rgba(128, 203, 196, 0.5)', selection: 'rgba(128, 203, 196, 0.5)',
selectionMatch: '#036dd626', selectionMatch: '#036dd626',

View file

@ -8,7 +8,7 @@ export function Code(Props) {
return ( return (
<section <section
className={'text-gray-100 cursor-text pb-0 overflow-auto grow'} className={'text-gray-100 cursor-text pb-0 overflow-auto grow z-10'}
id="code" id="code"
ref={(el) => { ref={(el) => {
containerRef.current = el; containerRef.current = el;

View file

@ -16,7 +16,7 @@ export function Header({ context, isEmbedded = false }) {
<header <header
id="header" id="header"
className={cx( className={cx(
'flex-none text-black z-[100] text-sm select-none min-h-10', 'border-b border-muted 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',
'flex items-center', 'flex items-center',

View file

@ -2,7 +2,7 @@ import cx from '@src/cx.mjs';
export function ActionButton({ children, label, labelIsHidden, className, ...buttonProps }) { export function ActionButton({ children, label, labelIsHidden, className, ...buttonProps }) {
return ( return (
<button className={cx('hover:opacity-50 text-nowrap w-fit text-sm ', className)} title={label} {...buttonProps}> <button className={cx('hover:opacity-50 text-nowrap w-fit', className)} title={label} {...buttonProps}>
{labelIsHidden !== true && label} {labelIsHidden !== true && label}
{children} {children}
</button> </button>

View file

@ -7,8 +7,10 @@ export function ConsoleTab() {
const log = useStore($strudel_log_history); const log = useStore($strudel_log_history);
const { fontFamily } = useSettings(); const { fontFamily } = useSettings();
return ( return (
<div id="console-tab" className="break-all w-full text-sm p-2 h-full" style={{ fontFamily }}> <div id="console-tab" className="break-all w-full h-full" style={{ fontFamily }}>
<div className="bg-background 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">
{' '}
{/* bg-background */}
{log.map((l, i) => { {log.map((l, i) => {
const message = linkify(l.message); const message = linkify(l.message);
const color = l.data?.hap?.value?.color; const color = l.data?.hap?.value?.color;

View file

@ -45,7 +45,7 @@ export default function ExportTab(Props) {
return ( return (
<> <>
<div className="text-foreground w-full p-4 space-y-4"> <div className="text-foreground w-full space-y-4 p-4">
<FormItem label="File name" disabled={exporting}> <FormItem label="File name" disabled={exporting}>
<Textbox <Textbox
onBlur={(e) => { onBlur={(e) => {

View file

@ -2,7 +2,7 @@ import cx from '@src/cx.mjs';
export function ButtonGroup({ value, onChange, items }) { export function ButtonGroup({ value, onChange, items }) {
return ( return (
<div className="flex max-w-lg space-x-0 text-sm"> <div className="flex max-w-lg space-x-0">
{Object.entries(items).map(([key, label], i, arr) => ( {Object.entries(items).map(([key, label], i, arr) => (
<button <button
key={key} key={key}

View file

@ -19,7 +19,7 @@ function PanelCloseButton() {
isPanelOpen && ( isPanelOpen && (
<button <button
onClick={() => setIsPanelOpened(false)} onClick={() => setIsPanelOpened(false)}
className={cx('px-4 py-2 text-foreground hover:opacity-50')} className={cx('px-4 py-1 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" />
@ -34,15 +34,15 @@ export function HorizontalPanel({ context }) {
<PanelNav <PanelNav
className={cx( className={cx(
isPanelOpen ? `min-h-[360px] max-h-[360px]` : 'min-h-10 max-h-10', isPanelOpen ? `min-h-[360px] max-h-[360px]` : 'min-h-10 max-h-10',
'overflow-hidden flex flex-col relative ', 'overflow-hidden flex flex-col relative',
)} )}
> >
<div className="flex justify-between min-h-10 max-h-10 grid-cols-2 items-center"> <div className="flex justify-between min-h-10 max-h-10 grid-cols-2 items-center border-b border-muted">
<Tabs setTab={setTab} tab={tab} /> <Tabs setTab={setTab} tab={tab} />
<PanelCloseButton /> <PanelCloseButton />
</div> </div>
{isPanelOpen && ( {isPanelOpen && (
<div className="flex h-full overflow-auto w-full"> <div className="w-full h-full overflow-hidden">
<PanelContent context={context} tab={tab} /> <PanelContent context={context} tab={tab} />
</div> </div>
)} )}
@ -59,10 +59,13 @@ export function VerticalPanel({ context }) {
return ( return (
<PanelNav <PanelNav
settings={settings} settings={settings}
className={cx(isPanelOpen ? `min-w-[min(600px,100vw)] max-w-[min(600px,80vw)]` : 'min-w-12 max-w-12')} className={cx(
'border-l border-muted',
isPanelOpen ? `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 "> <div className="flex justify-between w-full border-b border-muted">
<Tabs setTab={setTab} tab={tab} /> <Tabs setTab={setTab} tab={tab} />
<PanelCloseButton /> <PanelCloseButton />
</div> </div>
@ -134,7 +137,7 @@ function PanelTab({ label, isSelected, onClick }) {
<button <button
onClick={onClick} onClick={onClick}
className={cx( className={cx(
'h-8 px-2 text-sm text-foreground cursor-pointer hover:opacity-50 flex items-center space-x-1 border-b', 'h-8 px-2 text-sm text-foreground cursor-pointer hover:opacity-50 flex items-center space-x-1 border-b-2',
isSelected ? 'border-foreground' : 'border-transparent', isSelected ? 'border-foreground' : 'border-transparent',
)} )}
> >
@ -146,7 +149,7 @@ function PanelTab({ label, isSelected, onClick }) {
function Tabs({ className }) { function Tabs({ className }) {
const { isPanelOpen, activeFooter: tab } = useSettings(); const { isPanelOpen, activeFooter: tab } = useSettings();
return ( return (
<div className={cx('flex select-none max-w-full overflow-auto items-center', className)}> <div className={cx('w-full flex select-none max-w-full overflow-auto items-center', 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 && isPanelOpen} label={key} onClick={() => setTab(val)} />; return <PanelTab key={key} isSelected={tab === val && isPanelOpen} label={key} onClick={() => setTab(val)} />;
@ -164,7 +167,7 @@ export function PanelToggle({ isEmbedded, isZen }) {
<button <button
title="share" title="share"
className={cx( className={cx(
'absolute top-0 right-3 rounded-0 px-2 py-2 bg-background z-[1000] cursor-pointer hover:opacity-80 flex justify-center items-center space-x-1 text-foreground ', 'absolute top-0.5 right-3 rounded-0 px-2 py-2 bg-background z-[1000] cursor-pointer hover:opacity-80 flex justify-center items-center space-x-1 text-foreground ',
isPanelOpen && 'hidden', isPanelOpen && 'hidden',
)} )}
onClick={() => setIsPanelOpened(!isPanelOpen)} onClick={() => setIsPanelOpened(!isPanelOpen)}

View file

@ -43,7 +43,7 @@ function PatternButton({ showOutline, onClick, pattern, showHiglight }) {
className={cx( className={cx(
'mr-4 hover:opacity-50 cursor-pointer block', 'mr-4 hover:opacity-50 cursor-pointer block',
showOutline && 'outline outline-1', showOutline && 'outline outline-1',
showHiglight && 'bg-selection', showHiglight && 'ring-selection',
)} )}
onClick={onClick} onClick={onClick}
> >
@ -57,7 +57,7 @@ function PatternButtons({ patterns, activePattern, onClick, started }) {
const viewingPatternData = parseJSON(viewingPatternStore); const viewingPatternData = parseJSON(viewingPatternStore);
const viewingPatternID = viewingPatternData.id; const viewingPatternID = viewingPatternData.id;
return ( return (
<div className=""> <div className="p-2">
{Object.values(patterns) {Object.values(patterns)
.reverse() .reverse()
.map((pattern) => { .map((pattern) => {
@ -124,71 +124,68 @@ export function PatternsTab({ context }) {
}, [search, viewingPatternStore]); }, [search, viewingPatternStore]);
return ( return (
<div className="px-4 w-full text-foreground space-y-2 flex flex-col overflow-hidden max-h-full h-full"> <div className="w-full h-full text-foreground flex flex-col overflow-hidden">
<div className="w-full flex"> <Textbox className="w-full border-0" placeholder="Search" value={search} onChange={setSearch} />
<Textbox className="w-full" placeholder="Search" value={search} onChange={setSearch} /> <div className="px-2 shrink-0 py-2 space-x-4 flex max-w-full overflow-x-auto border-y border-muted">
<ActionButton
label="new"
onClick={() => {
const { data } = userPattern.createAndAddToDB();
updateCodeWindow(context, data);
}}
/>
<ActionButton
label="duplicate"
onClick={() => {
const { data } = userPattern.duplicate(viewingPatternData);
updateCodeWindow(context, data);
}}
/>
<ActionButton
label="delete"
onClick={() => {
const { data } = userPattern.delete(viewingPatternID);
updateCodeWindow(context, { ...data, collection: userPattern.collection });
}}
/>
<label className="hover:opacity-50 cursor-pointer">
<input
style={{ display: 'none' }}
type="file"
multiple
accept="text/plain,text/x-markdown,application/json"
onChange={(e) => importPatterns(e.target.files)}
/>
import
</label>
<ActionButton label="export" onClick={exportPatterns} />
<ActionButton
label="delete-all"
onClick={() => {
const { data } = userPattern.clearAll();
updateCodeWindow(context, data);
}}
/>
</div> </div>
<div className="flex flex-col gap-2 flex-grow overflow-hidden h-full pb-2 ">
<div className="pr-4 space-x-4 flex max-w-full overflow-x-auto">
<ActionButton
label="new"
onClick={() => {
const { data } = userPattern.createAndAddToDB();
updateCodeWindow(context, data);
}}
/>
<ActionButton
label="duplicate"
onClick={() => {
const { data } = userPattern.duplicate(viewingPatternData);
updateCodeWindow(context, data);
}}
/>
<ActionButton
label="delete"
onClick={() => {
const { data } = userPattern.delete(viewingPatternID);
updateCodeWindow(context, { ...data, collection: userPattern.collection });
}}
/>
<label className="hover:opacity-50 cursor-pointer">
<input
style={{ display: 'none' }}
type="file"
multiple
accept="text/plain,text/x-markdown,application/json"
onChange={(e) => importPatterns(e.target.files)}
/>
import
</label>
<ActionButton label="export" onClick={exportPatterns} />
<ActionButton <div className="overflow-auto">
label="delete-all" {/* bg-background */}
onClick={() => { {/* {patternFilter === patternFilterName.user && ( */}
const { data } = userPattern.clearAll(); <PatternButtons
updateCodeWindow(context, data); onClick={(id) => {
}} updateCodeWindow(context, { ...userPatterns[id], collection: userPattern.collection }, patternAutoStart);
/>
</div>
<div className="overflow-auto h-full bg-background p-2"> if (context.started && activePattern === id) {
{/* {patternFilter === patternFilterName.user && ( */} context.handleEvaluate();
<PatternButtons }
onClick={(id) => { }}
updateCodeWindow(context, { ...userPatterns[id], collection: userPattern.collection }, patternAutoStart); patterns={visiblePatterns}
started={context.started}
if (context.started && activePattern === id) { activePattern={activePattern}
context.handleEvaluate(); viewingPatternID={viewingPatternID}
} />
}} {/* )} */}
patterns={visiblePatterns}
started={context.started}
activePattern={activePattern}
viewingPatternID={viewingPatternID}
/>
{/* )} */}
</div>
</div> </div>
</div> </div>
); );

View file

@ -112,11 +112,12 @@ export const Reference = memo(function Reference() {
}, [selectedFunction]); }, [selectedFunction]);
return ( return (
<div className="flex h-full w-full p-2 overflow-hidden"> <div className="flex h-full w-full overflow-hidden">
<div className="h-full text-foreground flex flex-col gap-3 w-1/3 "> <div className="h-full text-foreground flex flex-col w-1/3 border-r border-muted">
{/* bg-background */}
<div className="w-full flex"> <div className="w-full flex">
<Textbox <Textbox
className="w-full" className="w-full border-0 border-b border-muted"
placeholder="Search" placeholder="Search"
value={search} value={search}
onChange={(e) => { onChange={(e) => {
@ -128,14 +129,14 @@ export const Reference = memo(function Reference() {
{selectedTag && ( {selectedTag && (
<div className="w-72"> <div className="w-72">
<span <span
className="text-foreground border border-muted px-1 py-0.5 my-2 cursor-pointer font-sans" className="text-foreground border border-muted border-t-0 border-l-0 px-1 py-0.5 my-2 cursor-pointer font-sans"
onClick={onSearchTagFilterClick} onClick={onSearchTagFilterClick}
> >
{selectedTag} {selectedTag}
</span> </span>
</div> </div>
)} )}
<div className="flex flex-col h-full overflow-y-auto gap-1.5 bg-background bg-opacity-50"> <div className="flex flex-col h-full py-2 overflow-y-auto gap-1.5 bg-opacity-50">
{searchVisibleFunctions.map((entry, i) => ( {searchVisibleFunctions.map((entry, i) => (
<Fragment key={`entry-${entry.name}`}> <Fragment key={`entry-${entry.name}`}>
<a <a
@ -161,7 +162,7 @@ export const Reference = memo(function Reference() {
className="break-normal flex-col overflow-y-auto overflow-x-hidden p-2 flex relative" className="break-normal flex-col overflow-y-auto overflow-x-hidden p-2 flex relative"
id="reference-container" id="reference-container"
> >
<div className="prose dark:prose-invert min-w-full px-1 "> <div className="prose dark:prose-invert min-w-full px-1 text-sm">
<h2>API Reference</h2> <h2>API Reference</h2>
<p className="font-sans text-md"> <p className="font-sans text-md">
This is the long list of functions you can use. Remember that you don't need to remember all of those and This is the long list of functions you can use. Remember that you don't need to remember all of those and

View file

@ -154,7 +154,7 @@ export function SettingsTab({ started }) {
const shouldAlwaysSync = isUdels(); const shouldAlwaysSync = isUdels();
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null; const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
return ( return (
<div className="text-foreground p-4 space-y-4 w-full" style={{ fontFamily }}> <div className="p-4 text-foreground space-y-4 w-full" style={{ fontFamily }}>
{canChangeAudioDevice && ( {canChangeAudioDevice && (
<FormItem label="Audio Output Device"> <FormItem label="Audio Output Device">
<AudioDeviceSelector <AudioDeviceSelector

View file

@ -77,10 +77,10 @@ export function SoundsTab() {
numRef.current = 0; numRef.current = 0;
}); });
return ( return (
<div id="sounds-tab" className="px-4 flex gap-2 flex-col w-full h-full text-foreground"> <div id="sounds-tab" className="flex flex-col w-full h-full text-foreground">
<Textbox placeholder="Search" value={search} onChange={(v) => setSearch(v)} /> <Textbox placeholder="Search" className="border-0" value={search} onChange={(v) => setSearch(v)} />
<div className=" flex shrink-0 flex-wrap"> <div className="flex shrink-0 flex-wrap border-y border-muted">
<ButtonGroup <ButtonGroup
value={soundsFilter} value={soundsFilter}
onChange={(value) => settingsMap.setKey('soundsFilter', value)} onChange={(value) => settingsMap.setKey('soundsFilter', value)}
@ -114,7 +114,7 @@ export function SoundsTab() {
/> />
)} )}
<div className="min-h-0 max-h-full grow overflow-auto text-sm break-normal bg-background p-2 rounded-md"> <div className="min-h-0 max-h-full grow overflow-auto break-normal p-2">
{soundEntries.map(([name, { data, onTrigger }]) => { {soundEntries.map(([name, { data, onTrigger }]) => {
return ( return (
<span <span

View file

@ -6,7 +6,7 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL
export function WelcomeTab({ context }) { 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 py-4 font-sans px-4 text-sm" 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