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:
@@ -8,3 +8,7 @@ Start/restart with: `./scripts/dev-server.sh`
|
|||||||
- Auto-generates `ENCRYPTION_KEY` if not set
|
- Auto-generates `ENCRYPTION_KEY` if not set
|
||||||
- Default login: `admin` / `admin123`
|
- Default login: `admin` / `admin123`
|
||||||
- Override port: `LISTEN_ADDR=:9000 ./scripts/dev-server.sh`
|
- 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.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import FormField from '$lib/components/FormField.svelte';
|
import FormField from '$lib/components/FormField.svelte';
|
||||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||||
import ForgeHero from '$lib/components/ForgeHero.svelte';
|
import ForgeHero from '$lib/components/ForgeHero.svelte';
|
||||||
|
import ToggleSwitch from '$lib/components/ToggleSwitch.svelte';
|
||||||
import WebhookPanel from '$lib/components/WebhookPanel.svelte';
|
import WebhookPanel from '$lib/components/WebhookPanel.svelte';
|
||||||
import ContainerStats from '$lib/components/ContainerStats.svelte';
|
import ContainerStats from '$lib/components/ContainerStats.svelte';
|
||||||
import ContainerLogs from '$lib/components/ContainerLogs.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">
|
<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.secretKey')} name="secretKey" bind:value={secretKey} placeholder="API_KEY" required />
|
||||||
<FormField label={$t('sites.secretValue')} name="secretValue" bind:value={secretValue} placeholder="sk-..." />
|
<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)]">
|
<div class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
||||||
<input type="checkbox" bind:checked={secretEncrypted} class="rounded border-[var(--border-input)]" />
|
<ToggleSwitch bind:checked={secretEncrypted} label={$t('sites.encryptSecret')} />
|
||||||
{$t('sites.encryptSecret')}
|
<span>{$t('sites.encryptSecret')}</span>
|
||||||
</label>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!secretKey.trim() || secretSubmitting}
|
disabled={!secretKey.trim() || secretSubmitting}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import FormField from '$lib/components/FormField.svelte';
|
import FormField from '$lib/components/FormField.svelte';
|
||||||
import ForgeHero from '$lib/components/ForgeHero.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 { IconCheck, IconLoader, IconChevronRight, IconSearch } from '$lib/components/icons';
|
||||||
import EntityPicker from '$lib/components/EntityPicker.svelte';
|
import EntityPicker from '$lib/components/EntityPicker.svelte';
|
||||||
import type { EntityPickerItem } from '$lib/types';
|
import type { EntityPickerItem } from '$lib/types';
|
||||||
@@ -590,17 +591,17 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Options -->
|
<!-- Options -->
|
||||||
<label class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
<div class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
||||||
<input type="checkbox" bind:checked={renderMarkdown} class="rounded border-[var(--border-input)]" />
|
<ToggleSwitch bind:checked={renderMarkdown} label={$t('sites.renderMarkdown')} />
|
||||||
{$t('sites.renderMarkdown')}
|
<span>{$t('sites.renderMarkdown')}</span>
|
||||||
</label>
|
</div>
|
||||||
|
|
||||||
<!-- Persistent Storage (Deno only) -->
|
<!-- Persistent Storage (Deno only) -->
|
||||||
{#if mode === 'deno'}
|
{#if mode === 'deno'}
|
||||||
<label class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
<div class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
||||||
<input type="checkbox" bind:checked={storageEnabled} class="rounded border-[var(--border-input)]" />
|
<ToggleSwitch bind:checked={storageEnabled} label={$t('sites.enableStorage')} />
|
||||||
{$t('sites.enableStorage')}
|
<span>{$t('sites.enableStorage')}</span>
|
||||||
</label>
|
</div>
|
||||||
{#if storageEnabled}
|
{#if storageEnabled}
|
||||||
<div class="space-y-3 rounded-lg border border-[var(--border-secondary)] p-4">
|
<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>
|
<p class="text-xs text-[var(--text-tertiary)]">{$t('sites.storageHelp')}</p>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import * as api from '$lib/api';
|
import * as api from '$lib/api';
|
||||||
import { IconArrowLeft } from '$lib/components/icons';
|
import { IconArrowLeft } from '$lib/components/icons';
|
||||||
|
import ToggleSwitch from '$lib/components/ToggleSwitch.svelte';
|
||||||
import { t } from '$lib/i18n';
|
import { t } from '$lib/i18n';
|
||||||
|
|
||||||
let name = $state('');
|
let name = $state('');
|
||||||
@@ -192,14 +193,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="deploy-toggle">
|
<div class="deploy-toggle">
|
||||||
<input type="checkbox" bind:checked={deployNow} />
|
<ToggleSwitch bind:checked={deployNow} label={$t('stacks.new.deployImmediate')} />
|
||||||
<span class="toggle-box"></span>
|
|
||||||
<span class="toggle-text">
|
<span class="toggle-text">
|
||||||
<strong>{$t('stacks.new.deployImmediate')}</strong>
|
<strong>{$t('stacks.new.deployImmediate')}</strong>
|
||||||
<span class="dim">{$t('stacks.new.deployHint')}</span>
|
<span class="dim">{$t('stacks.new.deployHint')}</span>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<a href="/stacks" class="btn-ghost">{$t('stacks.new.cancel')}</a>
|
<a href="/stacks" class="btn-ghost">{$t('stacks.new.cancel')}</a>
|
||||||
@@ -538,33 +538,10 @@
|
|||||||
border: 1px solid var(--border-primary);
|
border: 1px solid var(--border-primary);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
cursor: pointer;
|
|
||||||
transition: border-color 150ms ease;
|
transition: border-color 150ms ease;
|
||||||
}
|
}
|
||||||
.deploy-toggle:hover { border-color: var(--color-brand-300); }
|
.deploy-toggle:hover { border-color: var(--color-brand-300); }
|
||||||
.deploy-toggle input { position: absolute; opacity: 0; pointer-events: none; }
|
.deploy-toggle :global(.toggle-switch) { margin-top: 2px; }
|
||||||
.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);
|
|
||||||
}
|
|
||||||
.toggle-text strong {
|
.toggle-text strong {
|
||||||
display: block; font-family: var(--serif);
|
display: block; font-family: var(--serif);
|
||||||
font-size: 1.15rem; font-weight: 400; line-height: 1.2;
|
font-size: 1.15rem; font-weight: 400; line-height: 1.2;
|
||||||
|
|||||||
Reference in New Issue
Block a user