fix(electrobun): wizard 7 fixes — validation, GitLab, SSHFS, icons, model dropdown, keyboard nav
- Git Platform: validates repo via git.probe before enabling Next, supports GitHub + GitLab + any git URL - Template dir configurable in Advanced Settings (template_dir key) - SSHFS: checks sshfs availability, mountpoint selector when enabled - CustomDropdown: flip-up when insufficient space below - 50 Lucide icons (was 24) with categories (AI, Data, DevOps, Security, Media, Comms) - Model: CustomDropdown from live API, max_tokens as slider, effort only with adaptive thinking - Keyboard: Escape closes wizard, Tab navigation with :focus-visible rings, source cards navigable
This commit is contained in:
parent
41b8d46a19
commit
021feba3ed
11 changed files with 368 additions and 614 deletions
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { appRpc } from './rpc.ts';
|
||||
import CustomDropdown from './ui/CustomDropdown.svelte';
|
||||
import CustomCheckbox from './ui/CustomCheckbox.svelte';
|
||||
import ModelConfigPanel from './ModelConfigPanel.svelte';
|
||||
|
|
@ -65,6 +64,14 @@
|
|||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
function handleProviderKeydown(e: KeyboardEvent, pid: string) {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
onUpdate('provider', pid);
|
||||
onUpdate('modelConfig', {});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<label class="wz-label">AI Provider</label>
|
||||
|
|
@ -74,7 +81,9 @@
|
|||
<div class="wz-provider-grid">
|
||||
{#each availableProviders as p}
|
||||
<button class="wz-provider-card" class:active={provider === p.id}
|
||||
onclick={() => { onUpdate('provider', p.id); onUpdate('modelConfig', {}); }}>
|
||||
onclick={() => { onUpdate('provider', p.id); onUpdate('modelConfig', {}); }}
|
||||
onkeydown={(e) => handleProviderKeydown(e, p.id)}
|
||||
tabindex={0}>
|
||||
<span class="wz-provider-name">{p.id}</span>
|
||||
{#if 'hasApiKey' in p}
|
||||
<span class="wz-provider-badge">{providerBadge(p as ProviderInfo)}</span>
|
||||
|
|
@ -92,10 +101,13 @@
|
|||
onSelect={v => onUpdate('model', v)}
|
||||
/>
|
||||
{:else}
|
||||
<input class="wz-input" type="text" value={model}
|
||||
oninput={(e) => onUpdate('model', (e.target as HTMLInputElement).value)}
|
||||
placeholder={defaultPlaceholder()} />
|
||||
{#if modelsLoading}<span class="wz-hint" style="color: var(--ctp-blue);">Loading models…</span>{/if}
|
||||
<CustomDropdown
|
||||
items={[{ value: '', label: modelsLoading ? 'Loading models...' : defaultPlaceholder() }]}
|
||||
selected={model}
|
||||
placeholder={modelsLoading ? 'Loading...' : defaultPlaceholder()}
|
||||
onSelect={v => onUpdate('model', v)}
|
||||
disabled={modelsLoading}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Per-model configuration -->
|
||||
|
|
@ -110,7 +122,8 @@
|
|||
<div class="wz-segmented">
|
||||
{#each ['restricted', 'default', 'bypassPermissions'] as pm}
|
||||
<button class="wz-seg-btn" class:active={permissionMode === pm}
|
||||
onclick={() => onUpdate('permissionMode', pm)}>{pm}</button>
|
||||
onclick={() => onUpdate('permissionMode', pm)}
|
||||
tabindex={0}>{pm}</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
|
@ -127,15 +140,15 @@
|
|||
|
||||
<style>
|
||||
.wz-label { font-size: 0.75rem; font-weight: 500; color: var(--ctp-subtext0); margin-top: 0.25rem; }
|
||||
.wz-input, .wz-textarea { padding: 0.375rem 0.5rem; background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1); border-radius: 0.25rem; color: var(--ctp-text); font-size: 0.8125rem; font-family: var(--ui-font-family); width: 100%; }
|
||||
.wz-input:focus, .wz-textarea:focus { outline: none; border-color: var(--ctp-blue); }
|
||||
.wz-input::placeholder, .wz-textarea::placeholder { color: var(--ctp-overlay0); }
|
||||
.wz-textarea { resize: vertical; min-height: 3rem; }
|
||||
.wz-textarea { padding: 0.375rem 0.5rem; background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1); border-radius: 0.25rem; color: var(--ctp-text); font-size: 0.8125rem; font-family: var(--ui-font-family); width: 100%; resize: vertical; min-height: 3rem; }
|
||||
.wz-textarea:focus { outline: 2px solid var(--ctp-blue); outline-offset: -1px; border-color: var(--ctp-blue); }
|
||||
.wz-textarea::placeholder { color: var(--ctp-overlay0); }
|
||||
.wz-hint { font-size: 0.6875rem; }
|
||||
.wz-hint.warn { color: var(--ctp-peach); }
|
||||
.wz-provider-grid { display: flex; flex-wrap: wrap; gap: 0.375rem; }
|
||||
.wz-provider-card { display: flex; flex-direction: column; align-items: center; gap: 0.125rem; padding: 0.5rem 0.75rem; border-radius: 0.375rem; border: 1px solid var(--ctp-surface1); background: var(--ctp-surface0); cursor: pointer; min-width: 5rem; }
|
||||
.wz-provider-card:hover { border-color: var(--ctp-overlay1); }
|
||||
.wz-provider-card:focus-visible { outline: 2px solid var(--ctp-blue); outline-offset: -1px; }
|
||||
.wz-provider-card.active { border-color: var(--ctp-blue); background: color-mix(in srgb, var(--ctp-blue) 10%, transparent); }
|
||||
.wz-provider-name { font-size: 0.8125rem; font-weight: 600; color: var(--ctp-text); text-transform: capitalize; }
|
||||
.wz-provider-badge { font-size: 0.5625rem; color: var(--ctp-subtext0); }
|
||||
|
|
@ -143,5 +156,6 @@
|
|||
.wz-seg-btn { flex: 1; padding: 0.375rem 0.5rem; font-size: 0.75rem; background: var(--ctp-surface0); border: none; color: var(--ctp-subtext0); cursor: pointer; font-family: var(--ui-font-family); border-right: 1px solid var(--ctp-surface1); }
|
||||
.wz-seg-btn:last-child { border-right: none; }
|
||||
.wz-seg-btn:hover { color: var(--ctp-text); }
|
||||
.wz-seg-btn:focus-visible { outline: 2px solid var(--ctp-blue); outline-offset: -2px; }
|
||||
.wz-seg-btn.active { background: color-mix(in srgb, var(--ctp-blue) 20%, transparent); color: var(--ctp-blue); }
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue