From 66cda80620baf65d8b0a1b261504756c7124f956 Mon Sep 17 00:00:00 2001
From: Till JS
Date: Tue, 14 Apr 2026 19:42:40 +0200
Subject: [PATCH] feat(settings): inline BYOK key manager under AI tier card
Moves the BYOK key CRUD from the standalone /settings/ai-keys subpage
directly under the new BYOK tier card in the main AI settings section.
Users now manage keys in-context where they toggle the tier.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
...yption-phases-1-9-period-dreams-events.md} | 0
.../lib/components/settings/AiSettings.svelte | 25 +-
.../settings/ByokKeysManager.svelte} | 615 ++++++++----------
3 files changed, 297 insertions(+), 343 deletions(-)
rename apps/mana/apps/landing/src/content/devlog/{2026-04-07-encryption-phases-1-9-cycles-dreams-events.md => 2026-04-07-encryption-phases-1-9-period-dreams-events.md} (100%)
rename apps/mana/apps/web/src/{routes/(app)/settings/ai-keys/+page.svelte => lib/components/settings/ByokKeysManager.svelte} (59%)
diff --git a/apps/mana/apps/landing/src/content/devlog/2026-04-07-encryption-phases-1-9-cycles-dreams-events.md b/apps/mana/apps/landing/src/content/devlog/2026-04-07-encryption-phases-1-9-period-dreams-events.md
similarity index 100%
rename from apps/mana/apps/landing/src/content/devlog/2026-04-07-encryption-phases-1-9-cycles-dreams-events.md
rename to apps/mana/apps/landing/src/content/devlog/2026-04-07-encryption-phases-1-9-period-dreams-events.md
diff --git a/apps/mana/apps/web/src/lib/components/settings/AiSettings.svelte b/apps/mana/apps/web/src/lib/components/settings/AiSettings.svelte
index 38a12337d..67f8fa271 100644
--- a/apps/mana/apps/web/src/lib/components/settings/AiSettings.svelte
+++ b/apps/mana/apps/web/src/lib/components/settings/AiSettings.svelte
@@ -19,7 +19,8 @@
MODELS,
DEFAULT_MODEL,
} from '@mana/local-llm';
- import { Robot, Cpu, HardDrive, Cloud, Warning, CheckCircle } from '@mana/shared-icons';
+ import { Robot, Cpu, HardDrive, Cloud, Warning, CheckCircle, Key } from '@mana/shared-icons';
+ import ByokKeysManager from './ByokKeysManager.svelte';
const settings = $derived(llmSettingsState.current);
const webgpuSupported = isLocalLlmSupported();
@@ -90,6 +91,17 @@
'Keine Inhalte werden gespeichert',
],
},
+ {
+ tier: 'byok',
+ icon: Key,
+ title: 'Eigener API-Key',
+ subtitle: 'OpenAI, Anthropic, Google Gemini oder Mistral',
+ bullets: [
+ 'Direkt aus dem Browser — keine Mana-Server-Zwischenstation',
+ 'Du zahlst beim Provider, wir sehen nichts davon',
+ 'Schluessel werden verschluesselt in deinem Vault gespeichert',
+ ],
+ },
{
tier: 'cloud',
icon: Cloud,
@@ -229,6 +241,17 @@
{/if}
+ {#if card.tier === 'byok' && enabled}
+ e.stopPropagation()}
+ onkeydown={(e) => e.stopPropagation()}
+ role="presentation"
+ >
+
+
+ {/if}
+
{#if card.tier === 'cloud' && enabled && !settings.cloudConsentGiven}
-
- KI-Keys - Mana
-
-
-
-
-
+
{#if vaultLocked}
Vault ist gesperrt — bitte zuerst anmelden um Keys zu verwalten.
{:else if loading}
-
Laedt...
+
Laedt...
{:else}
-
-
(showAdd = !showAdd)}>
- Key hinzufuegen
-
-
+ {#if keys.length > 0}
+
+ {#each keys as k (k.id)}
+
+ {#if editingId === k.id}
+
+
+ Default
+ {#each providerModels(k.provider) as m}
+ {m}
+ {/each}
+
+
+
(editingId = null)}>
+ {:else}
+
+
+
+ {k.label}
+ {#if k.isDefault}Standard {/if}
+
+
+ {providerDisplay(k.provider)} · {k.model || providerDefaultModel(k.provider)} ·
+ {k.usageCount} Aufrufe · {formatCost(k.totalCostUsd)}
+
+
+
+ {#if !k.isDefault}
+
handleSetDefault(k.id)}>Standard
+ {/if}
+
startEdit(k)} title="Bearbeiten">
+
+
+
handleDelete(k.id)} title="Loeschen">
+
+
+
+ {/if}
+
+ {/each}
+
+ {/if}
{#if showAdd}
+ {:else}
+
(showAdd = true)}>
+
+ {keys.length === 0 ? 'Ersten API-Key hinzufuegen' : 'Weiteren Key hinzufuegen'}
+
{/if}
-
-
- {#each keys as k (k.id)}
-
- {#if editingId === k.id}
-
-
-
- Provider-Default
- {#each providerModels(k.provider) as m}
- {m}
- {/each}
-
-
-
-
- (editingId = null)} title="Abbrechen">
-
-
-
- {:else}
-
-
-
-
- {k.label}
- {#if k.isDefault}
- Standard
- {/if}
-
-
- {providerDisplay(k.provider)}
- · {k.model || `Default (${providerDefaultModel(k.provider)})`}
-
-
- {k.usageCount} Aufrufe · {k.totalTokens.toLocaleString('de-DE')} Token · {formatCost(
- k.totalCostUsd
- )}
-
-
-
- {#if !k.isDefault}
-
handleSetDefault(k.id)}
- title="Als Standard setzen"
- >
- Standard
-
- {/if}
-
startEdit(k)} title="Bearbeiten">
-
-
-
handleDelete(k.id)} title="Loeschen">
-
-
-
-
- {/if}
-
- {:else}
-
-
-
Noch keine API-Keys hinterlegt.
-
- Klicke auf "Key hinzufuegen" um mit OpenAI, Anthropic, Gemini oder Mistral zu chatten.
-
-
- {/each}
-
{/if}