refactor(kon): settings page — collapsible sections, first section open by default
- Consolidated 7 separate Card components into a single Card with 8 accordion sections - Added openSection $state variable; 'transcription' open by default, others collapsed - Each section header is a clickable button with +/− indicator; clicking open section closes it - Section headings use font-display italic 18px (vs 26px page title) as accordion headers - All existing functionality preserved: toggles, dropdowns, model download/load, profiles, templates, hotkey recorder, output folder picker - Profiles & Templates remain as nested accordion-within-accordion (existing pattern kept) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,9 @@
|
|||||||
let showNewProfile = $state(false);
|
let showNewProfile = $state(false);
|
||||||
let showNewTemplate = $state(false);
|
let showNewTemplate = $state(false);
|
||||||
|
|
||||||
|
// Accordion state — first section open by default
|
||||||
|
let openSection = $state('transcription');
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
const loaded = await invoke("check_engine");
|
const loaded = await invoke("check_engine");
|
||||||
@@ -202,11 +205,19 @@
|
|||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<h2 class="font-display text-[26px] italic text-text px-7 pt-6 pb-5">Settings</h2>
|
<h2 class="font-display text-[26px] italic text-text px-7 pt-6 pb-5">Settings</h2>
|
||||||
|
|
||||||
<div class="px-7 pb-8 space-y-4">
|
<div class="px-7 pb-8">
|
||||||
<!-- Transcription -->
|
|
||||||
<Card>
|
<Card>
|
||||||
<div class="p-5">
|
<!-- Transcription -->
|
||||||
<h3 class="text-[14px] font-semibold text-text mb-5">Transcription</h3>
|
<div class="border-b border-border-subtle">
|
||||||
|
<button
|
||||||
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'transcription' ? null : 'transcription'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">Transcription</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'transcription' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'transcription'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
|
|
||||||
<!-- Engine selector -->
|
<!-- Engine selector -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
@@ -347,12 +358,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Processing -->
|
<!-- Processing -->
|
||||||
<Card>
|
<div class="border-b border-border-subtle">
|
||||||
<div class="p-5">
|
<button
|
||||||
<h3 class="text-[14px] font-semibold text-text mb-4">Processing</h3>
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'processing' ? null : 'processing'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">Processing</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'processing' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'processing'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<Toggle
|
<Toggle
|
||||||
bind:checked={settings.removeFillers}
|
bind:checked={settings.removeFillers}
|
||||||
@@ -366,23 +385,40 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- AI Assistant (LLM) -->
|
<!-- AI Assistant -->
|
||||||
<Card>
|
<div class="border-b border-border-subtle">
|
||||||
<div class="p-5">
|
<button
|
||||||
<h3 class="text-[14px] font-semibold text-text mb-1">AI Assistant</h3>
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'ai' ? null : 'ai'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">AI Assistant</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'ai' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'ai'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
<p class="text-[11px] text-text-tertiary mb-4">Local LLM for smart task extraction, transcript cleanup, and formatting. Runs 100% offline.</p>
|
<p class="text-[11px] text-text-tertiary mb-4">Local LLM for smart task extraction, transcript cleanup, and formatting. Runs 100% offline.</p>
|
||||||
<div class="bg-bg-input rounded-lg px-3 py-2.5 border border-border-subtle">
|
<div class="bg-bg-input rounded-lg px-3 py-2.5 border border-border-subtle">
|
||||||
<p class="text-[12px] text-text-secondary font-medium mb-1">Coming soon</p>
|
<p class="text-[12px] text-text-secondary font-medium mb-1">Coming soon</p>
|
||||||
<p class="text-[11px] text-text-tertiary">AI-powered cleanup and smart extraction are being rebuilt with a faster engine. Task extraction currently uses rule-based matching, which runs automatically after each recording.</p>
|
<p class="text-[11px] text-text-tertiary">AI-powered cleanup and smart extraction are being rebuilt with a faster engine. Task extraction currently uses rule-based matching, which runs automatically after each recording.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Profiles & Templates -->
|
<!-- Profiles & Templates -->
|
||||||
<Card>
|
<div class="border-b border-border-subtle">
|
||||||
<div class="p-5">
|
<button
|
||||||
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'profiles' ? null : 'profiles'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">Profiles & Templates</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'profiles' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'profiles'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
<!-- Profiles section -->
|
<!-- Profiles section -->
|
||||||
<button
|
<button
|
||||||
class="flex items-center gap-2 w-full text-left mb-1"
|
class="flex items-center gap-2 w-full text-left mb-1"
|
||||||
@@ -521,12 +557,20 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Output -->
|
<!-- Output -->
|
||||||
<Card>
|
<div class="border-b border-border-subtle">
|
||||||
<div class="p-5">
|
<button
|
||||||
<h3 class="text-[14px] font-semibold text-text mb-4">Output</h3>
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'output' ? null : 'output'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">Output</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'output' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'output'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<Toggle bind:checked={settings.autoCopy} label="Auto-copy to clipboard" />
|
<Toggle bind:checked={settings.autoCopy} label="Auto-copy to clipboard" />
|
||||||
<Toggle bind:checked={settings.includeTimestamps} label="Include timestamps in exports" />
|
<Toggle bind:checked={settings.includeTimestamps} label="Include timestamps in exports" />
|
||||||
@@ -564,22 +608,37 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Hotkey -->
|
<!-- Hotkey -->
|
||||||
<Card>
|
<div class="border-b border-border-subtle">
|
||||||
<div class="p-5">
|
<button
|
||||||
<h3 class="text-[14px] font-semibold text-text mb-1">Global Hotkey</h3>
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'hotkey' ? null : 'hotkey'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">Global Hotkey</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'hotkey' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'hotkey'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
<p class="text-[11px] text-text-tertiary mb-4">Toggle recording from anywhere. Click to change.</p>
|
<p class="text-[11px] text-text-tertiary mb-4">Toggle recording from anywhere. Click to change.</p>
|
||||||
<HotkeyRecorder />
|
<HotkeyRecorder />
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Appearance -->
|
<!-- Appearance -->
|
||||||
<Card>
|
<div class="border-b border-border-subtle">
|
||||||
<div class="p-5">
|
<button
|
||||||
<h3 class="text-[14px] font-semibold text-text mb-5">Appearance</h3>
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'appearance' ? null : 'appearance'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">Appearance</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'appearance' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'appearance'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Theme</p>
|
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Theme</p>
|
||||||
<SegmentedButton options={["Dark", "Light", "System"]} bind:value={settings.theme} />
|
<SegmentedButton options={["Dark", "Light", "System"]} bind:value={settings.theme} />
|
||||||
@@ -597,13 +656,20 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- About -->
|
<!-- About -->
|
||||||
<Card>
|
<div>
|
||||||
<div class="p-5">
|
<button
|
||||||
<h3 class="text-[14px] font-semibold text-text mb-4">About</h3>
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'about' ? null : 'about'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">About</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'about' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'about'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
<!-- Engine status -->
|
<!-- Engine status -->
|
||||||
<div class="flex items-center gap-2 mb-4">
|
<div class="flex items-center gap-2 mb-4">
|
||||||
<span class="w-[7px] h-[7px] rounded-full {engineOk ? 'bg-success' : 'bg-warning'}"></span>
|
<span class="w-[7px] h-[7px] rounded-full {engineOk ? 'bg-success' : 'bg-warning'}"></span>
|
||||||
@@ -629,6 +695,8 @@
|
|||||||
|
|
||||||
<p class="text-[11px] text-text-tertiary mt-5">Kon v1.0 · Powered by whisper.cpp · Built by CORBEL Ltd</p>
|
<p class="text-[11px] text-text-tertiary mt-5">Kon v1.0 · Powered by whisper.cpp · Built by CORBEL Ltd</p>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user