The taskbar's gear icon reopened the full-page "Connect to Oikos" screen even once already connected. Split that: Config.svelte stays as the first-run/unconfigured screen; a new Settings app (windowed, like Tasks or Operations) now handles in-session changes, with a section list (Connection, Appearance) built to grow — future settings are one more entry, not a new screen. - pages/Settings.svelte: Connection (server URL/token/Authentik, reusing config.ts + oidc.ts) and Appearance (Terracotta/Carbon picker) sections. - apps.ts: registered as a normal desktop app. - Taskbar's gear button now opens the Settings window; removed the onOpenConnection prop threaded through App -> Desktop -> Taskbar, since Settings' "Forget saved connection" (clear config + reload) replaces it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
67 lines
2.0 KiB
Svelte
67 lines
2.0 KiB
Svelte
<script lang="ts">
|
|
import Config from './pages/Config.svelte'
|
|
import Desktop from '$lib/components/desktop-shell/Desktop.svelte'
|
|
import { subscribeContext } from '$lib/stores/context'
|
|
import { openAppWindow, openEntityWindow } from '$lib/stores/windows'
|
|
import { isConfigured } from '$lib/config'
|
|
import { onMount } from 'svelte'
|
|
import { processPendingCallback, initOIDC } from '$lib/oidc'
|
|
import { Toaster } from '$lib/components/ui/sonner'
|
|
|
|
let configured = $state(isConfigured())
|
|
|
|
// Old hash routes (#/kb, #/entity/<slug>, ...) from the sidebar-shell era —
|
|
// translated into opening the equivalent window once, then cleared, so
|
|
// links/bookmarks from before the desktop redesign keep working without
|
|
// reintroducing a router.
|
|
const LEGACY_APP_ROUTES: Record<string, string> = {
|
|
overview: 'tasks',
|
|
chat: 'tasks',
|
|
kb: 'kb',
|
|
entities: 'kb',
|
|
graph: 'kb',
|
|
ops: 'ops',
|
|
signals: 'signals',
|
|
knowledge: 'knowledge',
|
|
learning: 'learning'
|
|
}
|
|
|
|
function resolveLegacyHash() {
|
|
const path = location.hash.slice(2)
|
|
if (!path) return
|
|
const [head, ...rest] = path.split('/')
|
|
if (head === 'entity' && rest.length) {
|
|
openEntityWindow(rest.join('/'))
|
|
} else if (LEGACY_APP_ROUTES[head]) {
|
|
openAppWindow(LEGACY_APP_ROUTES[head])
|
|
}
|
|
history.replaceState(null, '', location.pathname + location.search)
|
|
}
|
|
|
|
onMount(async () => {
|
|
if (await processPendingCallback()) {
|
|
configured = true
|
|
} else if (!configured) {
|
|
if (await initOIDC()) configured = true
|
|
}
|
|
resolveLegacyHash()
|
|
})
|
|
|
|
// Context (dashboard summary + approvals poll) and the SSE stream both
|
|
// authenticate — don't subscribe until a token exists.
|
|
$effect(() => {
|
|
if (!configured) return
|
|
return subscribeContext()
|
|
})
|
|
</script>
|
|
|
|
{#if !configured}
|
|
<Config
|
|
onConnected={() => (configured = true)}
|
|
onCancel={isConfigured() ? () => (configured = true) : undefined}
|
|
/>
|
|
{:else}
|
|
<Toaster />
|
|
<Desktop />
|
|
{/if}
|