This commit is contained in:
Kelorastus
2026-09-25 10:14:44 +02:00
parent 4d620fc5ad
commit 21d6e6bffb
34 changed files with 615 additions and 321 deletions
+27 -2
View File
@@ -10,7 +10,7 @@
let { open = $bindable(false) }: Props = $props();
const EMPTY: Settings = { site_url: "", mcp_url: "", install_dir: "" };
const EMPTY: Settings = { site_url: "", mcp_url: "", ai_url: "", install_dir: "" };
let dialog: HTMLDialogElement | undefined = $state();
let draft = $state<Settings>({ ...EMPTY });
@@ -31,20 +31,26 @@
const trimmed = $derived<Settings>({
site_url: draft.site_url.trim(),
mcp_url: draft.mcp_url.trim(),
ai_url: draft.ai_url.trim(),
install_dir: draft.install_dir.trim(),
});
const dirty = $derived(
current !== null &&
(trimmed.site_url !== current.site_url ||
trimmed.mcp_url !== current.mcp_url ||
trimmed.ai_url !== current.ai_url ||
trimmed.install_dir !== current.install_dir),
);
/** A drive's launcher needs a folder on a disk: a root or a share holds no install. */
const offDrive = $derived(portable && trimmed.install_dir !== "" && !/^[A-Za-z]:\\.+/.test(trimmed.install_dir));
const siteError = $derived(urlProblem(trimmed.site_url));
const mcpError = $derived(urlProblem(trimmed.mcp_url));
/** Optional: an empty AI address is how an install with no line out is left. */
const aiError = $derived(trimmed.ai_url === "" ? null : urlProblem(trimmed.ai_url));
const dirError = $derived(trimmed.install_dir ? null : "Choose where ODIAN is installed.");
const invalid = $derived(siteError !== null || mcpError !== null || dirError !== null || offDrive);
const invalid = $derived(
siteError !== null || mcpError !== null || aiError !== null || dirError !== null || offDrive,
);
function urlProblem(value: string): string | null {
if (!value) return "Required.";
@@ -199,6 +205,25 @@
{dirty && mcpError ? mcpError : "The DHP's MCP handles Keycloak sign-in."}
</span>
</div>
<div class="field">
<label class="field-label" for="set-ai-url">AI URL</label>
<input
id="set-ai-url"
class="input"
type="url"
spellcheck="false"
autocomplete="off"
placeholder="https://engine.odian.fr"
bind:value={draft.ai_url}
aria-invalid={dirty && aiError !== null}
aria-describedby="set-ai-url-help"
/>
<span id="set-ai-url-help" class={dirty && aiError ? "field-error" : "field-help"}>
{dirty && aiError
? aiError
: "Destination prediction, chat and vision, served over the internet. Leave empty and ODIAN runs wholly offline."}
</span>
</div>
</fieldset>
<fieldset class="group" disabled={locked || saving}>