From 4fc271dd1a9872014f53261f296f8d6771ef1ade Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Fri, 3 Jul 2026 09:49:35 -0500 Subject: [PATCH] =?UTF-8?q?feat(ui):=20AI=20settings=20section=20=E2=80=94?= =?UTF-8?q?=20pick=20provider,=20set=20LAN/local=20endpoint=20+=20model,?= =?UTF-8?q?=20test=20(FR-LLM-1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/views/Settings.svelte | 111 +++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/src/lib/views/Settings.svelte b/src/lib/views/Settings.svelte index 273b4c3..194543b 100644 --- a/src/lib/views/Settings.svelte +++ b/src/lib/views/Settings.svelte @@ -20,13 +20,53 @@ CalendarDays, UploadCloud, ShieldCheck, + Sparkles, } from "@lucide/svelte"; let { onClose }: { onClose: () => void } = $props(); - let section = $state<"recording" | "hardware" | "storage" | "calendar" | "sync" | "privacy">( - "recording", - ); + let section = $state< + "recording" | "hardware" | "storage" | "calendar" | "sync" | "ai" | "privacy" + >("recording"); + + // ---- AI summary provider (T5.2, FR-LLM-1) ---- + let llmProvider = $state(settings.settings.llm_provider); + let llmEndpoint = $state(settings.settings.llm_endpoint); + let llmModel = $state(settings.settings.llm_model); + $effect(() => { + // Re-sync the form when settings (re)load or are saved elsewhere. + llmProvider = settings.settings.llm_provider; + llmEndpoint = settings.settings.llm_endpoint; + llmModel = settings.settings.llm_model; + }); + async function saveLlm() { + await settings.setLlmProvider({ + provider: llmProvider, + endpoint: llmEndpoint || undefined, + model: llmModel || undefined, + }); + } + /** Client-side mirror of the backend's loopback+private-LAN check, for a live + * "leaves your network" hint while typing. */ + function endpointIsLocalOrLan(url: string): boolean { + try { + const h = new URL(url).hostname.replace(/^\[|\]$/g, ""); + if (h === "localhost" || h.toLowerCase().endsWith(".local") || h === "::1") return true; + const m = h.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); + if (!m) return false; + const a = Number(m[1]); + const b = Number(m[2]); + return ( + a === 127 || + a === 10 || + (a === 192 && b === 168) || + (a === 172 && b >= 16 && b <= 31) || + (a === 169 && b === 254) + ); + } catch { + return false; + } + } // ---- Calendar / .pst import (T6.1/T6.2/T6.3, FR-CAL-1/2) ---- let pstPath = $state(""); @@ -246,6 +286,9 @@ + @@ -620,6 +663,68 @@ TLS is required for non-LAN targets.
+ {:else if section === "ai"} +
+ Summaries run on a local LLM. Point this at Ollama on this PC or another machine on your
+ LAN (e.g. 192.168.0.x) — both count as local, so nothing leaves your network.
+
Models: {settings.llmStatus.models.slice(0, 6).join(", ")}
+ {/if} + {:else} +