bos-settings/frontend/src/lib/views/Breadlock.svelte
Breadway cbac50683e Harden settings: split breadcrumbs secrets, typed exec, new panels
Load and save Wi-Fi networks in networks.toml (0600) instead of writing
PSKs back into breadcrumbs.toml. The password field is write-only.

Replace the generic argv runner with typed bakery/pacman/fwupd commands
and set a real Tauri CSP. Add Lock, Screenshots, Monitors, and Help
panels. Pin bread-theme and bread-utils to bread-ecosystem v0.7.1.
bread-screenshots is not on that tag, so --screenshot calls grim
locally. bakery.toml lists webkitgtk-4.1 deps; README/CLAUDE.md match
Tauri 2 + Svelte 5 and single-trunk main.
2026-08-15 21:47:00 +08:00

94 lines
3.7 KiB
Svelte

<script lang="ts">
import { onMount } from "svelte";
import { invoke } from "@tauri-apps/api/core";
import ViewScaffold from "$lib/components/ViewScaffold.svelte";
import Group from "$lib/components/Group.svelte";
import Hint from "$lib/components/Hint.svelte";
import SelectField from "$lib/components/SelectField.svelte";
import FileField from "$lib/components/FileField.svelte";
import TextField from "$lib/components/TextField.svelte";
import NumberField from "$lib/components/NumberField.svelte";
import SwitchField from "$lib/components/SwitchField.svelte";
import SaveButton from "$lib/components/SaveButton.svelte";
interface BreadlockConfig {
background_mode: string;
background_path: string;
background_blur: boolean;
clock_format: string;
font_family: string;
fail_timeout_ms: number;
}
let cfg = $state<BreadlockConfig | null>(null);
let examplePath = $state<string | null>(null);
onMount(async () => {
cfg = await invoke<BreadlockConfig>("get_breadlock_config");
examplePath = await invoke<string | null>("breadlock_example_path");
});
async function save() {
await invoke("save_breadlock_config", { cfg });
}
</script>
<ViewScaffold title="Lock & greet">
<Group
title="How locking works"
hint="This panel does not configure PAM. Authentication is the breadlock PAM service (/etc/pam.d/breadlock) plus greetd for login — both are packaged, not user-editable from Settings."
>
<Hint
text="Super+L runs loginctl lock-session. hypridle's lock_cmd / idle listener then starts breadlock, which owns the already-running Hyprland session via ext-session-lock-v1."
/>
<Hint
text="breadgreet is the graphical greetd greeter (replacing tuigreet). Its live config is typically /etc/greetd/breadgreet.toml, owned by the greeter user — not written from here."
/>
<button class="primary" onclick={() => invoke("lock_session")}>Lock now</button>
</Group>
{#if cfg}
<Group title="Lock screen" hint="~/.config/breadlock/breadlock.toml — every field is optional; breadlock runs with these defaults if the file is missing.">
<SelectField label="Background" bind:value={cfg.background_mode} options={["color", "image"]} />
{#if cfg.background_mode === "image"}
<FileField label="Image" bind:value={cfg.background_path} placeholder="PNG, cover-fit" extensions={["png"]} />
{/if}
<SwitchField label="Blur background" bind:value={cfg.background_blur} />
<Hint text="Blur is accepted in the file but not implemented yet (needs a wlr-screencopy capture). breadlock logs a warning and shows the background unblurred." />
<TextField label="Clock format" bind:value={cfg.clock_format} placeholder="%H:%M" />
<TextField label="Font" bind:value={cfg.font_family} placeholder="Varela Round" />
<NumberField label="Wrong-password timeout (ms)" bind:value={cfg.fail_timeout_ms} min={0} max={10000} />
<SaveButton onSave={save} />
</Group>
{/if}
<Group title="Files">
<button class="secondary" onclick={() => invoke("open_breadlock_config")}>Open breadlock.toml in editor</button>
{#if examplePath}
<button class="secondary" onclick={() => invoke("open_breadlock_example")}>Open example ({examplePath})</button>
{:else}
<Hint text="No packaged breadlock.example.toml found. The schema is [background] mode/path/blur, [clock] format, [font] family, [input] fail_timeout_ms." />
{/if}
</Group>
</ViewScaffold>
<style>
button {
border: none;
border-radius: var(--radius-primary, 8px);
padding: var(--space-sm, 8px) var(--space-lg, 16px);
cursor: pointer;
align-self: flex-start;
margin-top: var(--space-xs, 4px);
}
.primary {
background-color: var(--accent);
color: var(--on-accent);
}
.secondary {
background-color: var(--surface);
color: var(--on-surface);
}
</style>