feat(electrobun): fixes + 7 new features (terminal input, file browser, memory, toasts)

Fixes:
- Terminal accepts keyboard input (echo mode with prompt)
- Terminal drawer collapses properly (display:none preserves xterm state)

Features:
- 6 project tabs: Model | Docs | Context | Files | SSH | Memory
- FileBrowser.svelte: recursive tree with expand/collapse + file preview
- MemoryTab.svelte: memory cards with trust badges (human/agent/auto)
- Subagent tree in AgentPane (demo: search-agent, test-runner)
- Drag resize handle between agent pane and terminal
- Theme dropdown in Settings (4 Catppuccin flavors)
- ToastContainer.svelte: auto-dismiss notifications
This commit is contained in:
Hibryda 2026-03-20 02:07:18 +01:00
parent b11a856b72
commit 4ae558af17
14 changed files with 1168 additions and 196 deletions

View file

@ -7,7 +7,21 @@
let { open, onClose }: Props = $props();
// Local settings state (demo — no persistence in prototype)
let theme = $state('Catppuccin Mocha');
const THEMES = [
{ id: 'mocha', label: 'Catppuccin Mocha' },
{ id: 'macchiato', label: 'Catppuccin Macchiato' },
{ id: 'frappe', label: 'Catppuccin Frappé' },
{ id: 'latte', label: 'Catppuccin Latte' },
];
let themeId = $state('mocha');
let themeDropdownOpen = $state(false);
let selectedThemeLabel = $derived(THEMES.find(t => t.id === themeId)?.label ?? 'Catppuccin Mocha');
function selectTheme(id: string) {
themeId = id;
themeDropdownOpen = false;
}
let uiFontSize = $state(14);
let termFontSize = $state(13);
@ -60,8 +74,36 @@
<h3 class="section-heading">Appearance</h3>
<div class="setting-row">
<label class="setting-label" for="theme-select">Theme</label>
<div class="setting-value theme-pill">{theme}</div>
<label class="setting-label" for="theme-dropdown-btn">Theme</label>
<div class="theme-dropdown">
<button
id="theme-dropdown-btn"
class="theme-dropdown-btn"
onclick={() => themeDropdownOpen = !themeDropdownOpen}
aria-haspopup="listbox"
aria-expanded={themeDropdownOpen}
>
<span class="theme-dropdown-label">{selectedThemeLabel}</span>
<svg class="theme-chevron" class:open={themeDropdownOpen} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="6 9 12 15 18 9"/>
</svg>
</button>
{#if themeDropdownOpen}
<ul class="theme-dropdown-list" role="listbox" aria-label="Theme options">
{#each THEMES as t}
<li
class="theme-option"
class:selected={themeId === t.id}
role="option"
aria-selected={themeId === t.id}
onclick={() => selectTheme(t.id)}
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && selectTheme(t.id)}
tabindex="0"
>{t.label}</li>
{/each}
</ul>
{/if}
</div>
</div>
<div class="setting-row">
@ -232,17 +274,76 @@
color: var(--ctp-subtext1);
}
.setting-value {
font-size: 0.8125rem;
color: var(--ctp-text);
/* Theme dropdown */
.theme-dropdown {
position: relative;
}
.theme-pill {
.theme-dropdown-btn {
display: flex;
align-items: center;
gap: 0.375rem;
background: var(--ctp-surface0);
padding: 0.2rem 0.5rem;
border: 1px solid var(--ctp-surface1);
border-radius: 0.3rem;
font-size: 0.75rem;
padding: 0.2rem 0.5rem;
color: var(--ctp-mauve);
font-family: var(--ui-font-family);
font-size: 0.75rem;
cursor: pointer;
transition: border-color 0.12s;
white-space: nowrap;
}
.theme-dropdown-btn:hover { border-color: var(--ctp-surface2); }
.theme-dropdown-label { flex: 1; }
.theme-chevron {
width: 0.75rem;
height: 0.75rem;
color: var(--ctp-overlay1);
transition: transform 0.15s;
flex-shrink: 0;
}
.theme-chevron.open { transform: rotate(180deg); }
.theme-dropdown-list {
position: absolute;
right: 0;
top: calc(100% + 0.25rem);
z-index: 10;
list-style: none;
margin: 0;
padding: 0.25rem;
background: var(--ctp-mantle);
border: 1px solid var(--ctp-surface1);
border-radius: 0.375rem;
min-width: 11rem;
box-shadow: 0 0.5rem 1.25rem color-mix(in srgb, var(--ctp-crust) 60%, transparent);
display: flex;
flex-direction: column;
gap: 0.0625rem;
}
.theme-option {
padding: 0.35rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.8125rem;
color: var(--ctp-subtext1);
cursor: pointer;
transition: background 0.08s, color 0.08s;
outline: none;
}
.theme-option:hover,
.theme-option:focus { background: var(--ctp-surface0); color: var(--ctp-text); }
.theme-option.selected {
background: color-mix(in srgb, var(--ctp-mauve) 15%, transparent);
color: var(--ctp-mauve);
font-weight: 500;
}
/* Font stepper */