refactor(ui): standardize boolean inputs on ToggleSwitch
Build / build (push) Successful in 11m22s

Replace raw <input type="checkbox"> with the ToggleSwitch component on
sites/[id] (encrypt-secret), sites/new (render-markdown, enable-storage),
and stacks/new (deploy-immediately). Document the convention in CLAUDE.md
so future forms keep the same control instead of mixing styles.
This commit is contained in:
2026-05-07 00:57:18 +03:00
parent a4362b842d
commit 134fe22fde
4 changed files with 23 additions and 40 deletions
+4
View File
@@ -8,3 +8,7 @@ Start/restart with: `./scripts/dev-server.sh`
- Auto-generates `ENCRYPTION_KEY` if not set
- Default login: `admin` / `admin123`
- Override port: `LISTEN_ADDR=:9000 ./scripts/dev-server.sh`
## Frontend
- **Boolean inputs use `ToggleSwitch`** (`$lib/components/ToggleSwitch.svelte`) — the slide-style switch is the unified control across the WebUI. Do not introduce raw `<input type="checkbox">` elements; place a `<ToggleSwitch>` next to a label/help block instead.
+5 -4
View File
@@ -8,6 +8,7 @@
import FormField from '$lib/components/FormField.svelte';
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
import ForgeHero from '$lib/components/ForgeHero.svelte';
import ToggleSwitch from '$lib/components/ToggleSwitch.svelte';
import WebhookPanel from '$lib/components/WebhookPanel.svelte';
import ContainerStats from '$lib/components/ContainerStats.svelte';
import ContainerLogs from '$lib/components/ContainerLogs.svelte';
@@ -304,10 +305,10 @@
<div class="mb-4 space-y-3 rounded-lg bg-[var(--surface-card-hover)] p-4">
<FormField label={$t('sites.secretKey')} name="secretKey" bind:value={secretKey} placeholder="API_KEY" required />
<FormField label={$t('sites.secretValue')} name="secretValue" bind:value={secretValue} placeholder="sk-..." />
<label class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<input type="checkbox" bind:checked={secretEncrypted} class="rounded border-[var(--border-input)]" />
{$t('sites.encryptSecret')}
</label>
<div class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<ToggleSwitch bind:checked={secretEncrypted} label={$t('sites.encryptSecret')} />
<span>{$t('sites.encryptSecret')}</span>
</div>
<button
type="button"
disabled={!secretKey.trim() || secretSubmitting}
+9 -8
View File
@@ -5,6 +5,7 @@
import { goto } from '$app/navigation';
import FormField from '$lib/components/FormField.svelte';
import ForgeHero from '$lib/components/ForgeHero.svelte';
import ToggleSwitch from '$lib/components/ToggleSwitch.svelte';
import { IconCheck, IconLoader, IconChevronRight, IconSearch } from '$lib/components/icons';
import EntityPicker from '$lib/components/EntityPicker.svelte';
import type { EntityPickerItem } from '$lib/types';
@@ -590,17 +591,17 @@
{/if}
<!-- Options -->
<label class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<input type="checkbox" bind:checked={renderMarkdown} class="rounded border-[var(--border-input)]" />
{$t('sites.renderMarkdown')}
</label>
<div class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<ToggleSwitch bind:checked={renderMarkdown} label={$t('sites.renderMarkdown')} />
<span>{$t('sites.renderMarkdown')}</span>
</div>
<!-- Persistent Storage (Deno only) -->
{#if mode === 'deno'}
<label class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<input type="checkbox" bind:checked={storageEnabled} class="rounded border-[var(--border-input)]" />
{$t('sites.enableStorage')}
</label>
<div class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<ToggleSwitch bind:checked={storageEnabled} label={$t('sites.enableStorage')} />
<span>{$t('sites.enableStorage')}</span>
</div>
{#if storageEnabled}
<div class="space-y-3 rounded-lg border border-[var(--border-secondary)] p-4">
<p class="text-xs text-[var(--text-tertiary)]">{$t('sites.storageHelp')}</p>
+5 -28
View File
@@ -2,6 +2,7 @@
import { goto } from '$app/navigation';
import * as api from '$lib/api';
import { IconArrowLeft } from '$lib/components/icons';
import ToggleSwitch from '$lib/components/ToggleSwitch.svelte';
import { t } from '$lib/i18n';
let name = $state('');
@@ -192,14 +193,13 @@
</div>
</div>
<label class="deploy-toggle">
<input type="checkbox" bind:checked={deployNow} />
<span class="toggle-box"></span>
<div class="deploy-toggle">
<ToggleSwitch bind:checked={deployNow} label={$t('stacks.new.deployImmediate')} />
<span class="toggle-text">
<strong>{$t('stacks.new.deployImmediate')}</strong>
<span class="dim">{$t('stacks.new.deployHint')}</span>
</span>
</label>
</div>
<div class="actions">
<a href="/stacks" class="btn-ghost">{$t('stacks.new.cancel')}</a>
@@ -538,33 +538,10 @@
border: 1px solid var(--border-primary);
border-radius: var(--radius-lg);
margin-bottom: 1.25rem;
cursor: pointer;
transition: border-color 150ms ease;
}
.deploy-toggle:hover { border-color: var(--color-brand-300); }
.deploy-toggle input { position: absolute; opacity: 0; pointer-events: none; }
.toggle-box {
flex-shrink: 0;
width: 20px; height: 20px;
border: 1px solid var(--border-input);
border-radius: var(--radius-sm);
background: var(--surface-input);
position: relative;
margin-top: 2px;
}
.deploy-toggle input:checked + .toggle-box {
background: var(--accent);
border-color: var(--accent);
}
.deploy-toggle input:checked + .toggle-box::after {
content: '';
position: absolute;
left: 6px; top: 2px;
width: 5px; height: 10px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.deploy-toggle :global(.toggle-switch) { margin-top: 2px; }
.toggle-text strong {
display: block; font-family: var(--serif);
font-size: 1.15rem; font-weight: 400; line-height: 1.2;