Better UI

This commit is contained in:
2025-10-15 11:27:58 +02:00
parent bbdb01200e
commit 65c3422610
6 changed files with 270 additions and 183 deletions

View File

@ -28,6 +28,8 @@
let isResizing = $state(false);
let startY = $state(0);
let startHeight = $state(0);
let previousHeight = $state(70);
let isCollapsed = $state(false);
function handleResizeStart(e: MouseEvent) {
isResizing = true;
@ -54,6 +56,17 @@
isResizing = false;
}
function toggleCollapse() {
if (isCollapsed) {
editorHeight = previousHeight;
isCollapsed = false;
} else {
previousHeight = editorHeight;
editorHeight = 96;
isCollapsed = true;
}
}
onMount(() => {
document.addEventListener('mousemove', handleResizeMove);
document.addEventListener('mouseup', handleResizeEnd);
@ -80,7 +93,7 @@
<div class="resize-divider" onmousedown={handleResizeStart}></div>
<div class="logs-section" style="height: {100 - editorHeight}%;">
<LogPanel bind:this={logPanelRef} {logs} />
<LogPanel bind:this={logPanelRef} {logs} onHeaderClick={toggleCollapse} collapsed={isCollapsed} />
</div>
</div>

View File

@ -6,9 +6,11 @@
interface Props {
logs?: LogEntry[];
onHeaderClick?: () => void;
collapsed?: boolean;
}
let { logs = [] }: Props = $props();
let { logs = [], onHeaderClick, collapsed = false }: Props = $props();
const { csound } = getAppContext();
@ -109,48 +111,50 @@
</script>
<div class="log-panel">
<div class="log-header">
<span class="log-title">Output</span>
<div class="log-actions">
<button
class="action-button"
class:search-active={searchVisible}
onclick={toggleSearch}
title={searchVisible ? 'Hide search' : 'Show search'}
>
<Search size={14} />
</button>
<button
class="action-button"
class:pause-active={!autoFollow}
onclick={toggleAutoFollow}
title={autoFollow ? 'Pause auto-follow' : 'Resume auto-follow'}
>
{#if autoFollow}
<Pause size={14} />
{:else}
<Play size={14} />
{/if}
</button>
<button
class="action-button"
onclick={copyLogs}
disabled={logs.length === 0}
title="Copy logs"
>
<Copy size={14} />
</button>
<button
class="action-button"
onclick={clearLogs}
disabled={logs.length === 0}
title="Clear logs"
>
<Trash2 size={14} />
</button>
</div>
<div class="log-header" onclick={onHeaderClick}>
<span class="log-title">Logs</span>
{#if !collapsed}
<div class="log-actions" onclick={(e) => e.stopPropagation()}>
<button
class="action-button"
class:search-active={searchVisible}
onclick={toggleSearch}
title={searchVisible ? 'Hide search' : 'Show search'}
>
<Search size={14} />
</button>
<button
class="action-button"
class:pause-active={!autoFollow}
onclick={toggleAutoFollow}
title={autoFollow ? 'Pause auto-follow' : 'Resume auto-follow'}
>
{#if autoFollow}
<Pause size={14} />
{:else}
<Play size={14} />
{/if}
</button>
<button
class="action-button"
onclick={copyLogs}
disabled={logs.length === 0}
title="Copy logs"
>
<Copy size={14} />
</button>
<button
class="action-button"
onclick={clearLogs}
disabled={logs.length === 0}
title="Clear logs"
>
<Trash2 size={14} />
</button>
</div>
{/if}
</div>
{#if searchVisible}
{#if searchVisible && !collapsed}
<div class="search-bar">
<Search size={14} class="search-icon" />
<input
@ -161,7 +165,8 @@
/>
</div>
{/if}
<div class="log-content" bind:this={logContentEl} onscroll={handleScroll}>
{#if !collapsed}
<div class="log-content" bind:this={logContentEl} onscroll={handleScroll}>
{#if filteredLogs.length === 0 && searchQuery.trim() !== ''}
<div class="empty-state">No matching logs found...</div>
{:else if logs.length === 0}
@ -174,7 +179,8 @@
</div>
{/each}
{/if}
</div>
</div>
{/if}
</div>
<style>
@ -192,6 +198,12 @@
padding: 0.5rem 0.75rem;
background-color: #2a2a2a;
border-bottom: 1px solid #3a3a3a;
cursor: pointer;
transition: background-color 0.2s;
}
.log-header:hover {
background-color: #323232;
}
.search-bar {

View File

@ -130,7 +130,7 @@
<button class="action-button" onclick={handleNewEmptyFile} title="New empty file">
<FilePlus size={18} />
</button>
<button class="action-button template-button" onclick={handleNewFromTemplate} title="New from template">
<button class="action-button" onclick={handleNewFromTemplate} title="New from template">
<FileStack size={18} />
</button>
</div>
@ -277,15 +277,6 @@
background-color: rgba(255, 255, 255, 0.1);
}
.action-button.template-button {
color: rgba(100, 200, 255, 0.8);
}
.action-button.template-button:hover {
color: rgba(100, 200, 255, 1);
background-color: rgba(100, 200, 255, 0.15);
}
.browser-content {
flex: 1;
overflow-y: auto;

View File

@ -1,5 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { X, ArrowLeftRight } from 'lucide-svelte';
interface Props {
visible?: boolean;
@ -8,6 +9,8 @@
maxWidth?: number;
position?: 'left' | 'right' | 'bottom';
tabs?: Array<{ id: string; label: string; content: any }>;
onClose?: () => void;
onCyclePosition?: () => void;
}
let {
@ -16,7 +19,9 @@
minWidth = 200,
maxWidth = 600,
position = $bindable('right'),
tabs = []
tabs = [],
onClose,
onCyclePosition
}: Props = $props();
let width = $state(initialWidth);
@ -94,6 +99,14 @@
{tab.label}
</button>
{/each}
<div class="panel-controls">
<button class="control-button" onclick={onCyclePosition} title="Switch panel side">
<ArrowLeftRight size={16} />
</button>
<button class="control-button" onclick={onClose} title="Close panel">
<X size={16} />
</button>
</div>
</div>
<div class="side-panel-content">
@ -106,6 +119,16 @@
{/each}
</div>
{:else}
<div class="panel-header-fallback">
<div class="panel-controls">
<button class="control-button" onclick={onCyclePosition} title="Switch panel side">
<ArrowLeftRight size={16} />
</button>
<button class="control-button" onclick={onClose} title="Close panel">
<X size={16} />
</button>
</div>
</div>
<div class="side-panel-content">
{@render children?.()}
</div>
@ -177,10 +200,45 @@
.tabs {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #333;
background-color: #1a1a1a;
}
.panel-controls {
display: flex;
gap: 0.25rem;
margin-left: auto;
padding-right: 0.5rem;
}
.control-button {
padding: 0.375rem;
background-color: transparent;
color: rgba(255, 255, 255, 0.7);
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.control-button:hover {
color: rgba(255, 255, 255, 1);
background-color: rgba(255, 255, 255, 0.1);
}
.panel-header-fallback {
display: flex;
justify-content: flex-end;
align-items: center;
border-bottom: 1px solid #333;
background-color: #1a1a1a;
padding: 0.5rem 0;
}
.tab {
padding: 0.75rem 1rem;
background: none;

View File

@ -1,4 +1,4 @@
type PanelPosition = 'left' | 'right' | 'bottom';
type PanelPosition = 'left' | 'right';
export class UIState {
sidePanelVisible = $state(true);
@ -21,8 +21,6 @@ export class UIState {
cyclePanelPosition() {
if (this.sidePanelPosition === 'right') {
this.sidePanelPosition = 'left';
} else if (this.sidePanelPosition === 'left') {
this.sidePanelPosition = 'bottom';
} else {
this.sidePanelPosition = 'right';
}