docs: add Claude AI assistant rules and configuration
Add comprehensive rule documentation for AI-assisted development covering authentication surfaces, outbound-call safety invariants, and other project conventions to guide Claude's understanding of critical system behaviors.
This commit is contained in:
@@ -2,9 +2,15 @@
|
||||
import '../app.css';
|
||||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { auth } from '$lib/auth.svelte';
|
||||
import LoginScreen from '$lib/components/LoginScreen.svelte';
|
||||
import DeniedScreen from '$lib/components/DeniedScreen.svelte';
|
||||
import TokensModal from '$lib/components/TokensModal.svelte';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
let tokensOpen = $state(false);
|
||||
|
||||
type ThemeOverride = 'dark' | 'light' | null;
|
||||
let override = $state<ThemeOverride>(null);
|
||||
let systemDark = $state(true);
|
||||
@@ -12,7 +18,10 @@
|
||||
let isDark = $derived(override !== null ? override === 'dark' : systemDark);
|
||||
|
||||
$effect(() => {
|
||||
// Keep both theming systems in sync: Tailwind `dark:` variant (.dark class)
|
||||
// for the existing pages, and DaisyUI `data-theme` for the SSO components.
|
||||
document.documentElement.classList.toggle('dark', isDark);
|
||||
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
|
||||
});
|
||||
|
||||
function toggleTheme() {
|
||||
@@ -27,6 +36,8 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
void auth.init();
|
||||
|
||||
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
systemDark = mq.matches;
|
||||
|
||||
@@ -49,40 +60,63 @@
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="min-h-screen bg-slate-50 dark:bg-gray-950 text-gray-900 dark:text-gray-100">
|
||||
<header
|
||||
class="border-b border-gray-200 dark:border-gray-800 bg-white/80 dark:bg-gray-900/80 backdrop-blur sticky top-0 z-10"
|
||||
>
|
||||
<div class="mx-auto max-w-7xl px-4 py-3 flex items-center gap-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-orange-500 text-lg leading-none">🔥</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-white tracking-tight">Hold Slayer</span>
|
||||
<span class="text-gray-500 text-sm hidden sm:inline">Gateway</span>
|
||||
</div>
|
||||
<nav class="flex gap-1 ml-2">
|
||||
{#each nav as item}
|
||||
<a
|
||||
href={item.href}
|
||||
class="px-3 py-1.5 rounded text-sm font-medium transition-colors {$page.url.pathname ===
|
||||
item.href
|
||||
? 'bg-orange-600 text-white'
|
||||
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800'}"
|
||||
{#if auth.status === 'loading'}
|
||||
<div class="fixed inset-0 flex items-center justify-center bg-slate-50 dark:bg-gray-950">
|
||||
<span class="loading loading-spinner loading-lg text-orange-500"></span>
|
||||
</div>
|
||||
{:else if auth.status === 'unauthenticated'}
|
||||
<LoginScreen />
|
||||
{:else if auth.status === 'denied'}
|
||||
<DeniedScreen />
|
||||
{:else}
|
||||
<div class="min-h-screen bg-slate-50 dark:bg-gray-950 text-gray-900 dark:text-gray-100">
|
||||
<header
|
||||
class="border-b border-gray-200 dark:border-gray-800 bg-white/80 dark:bg-gray-900/80 backdrop-blur sticky top-0 z-10"
|
||||
>
|
||||
<div class="mx-auto max-w-7xl px-4 py-3 flex items-center gap-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-orange-500 text-lg leading-none">🔥</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-white tracking-tight">Hold Slayer</span>
|
||||
<span class="text-gray-500 text-sm hidden sm:inline">Gateway</span>
|
||||
</div>
|
||||
<nav class="flex gap-1 ml-2">
|
||||
{#each nav as item}
|
||||
<a
|
||||
href={item.href}
|
||||
class="px-3 py-1.5 rounded text-sm font-medium transition-colors {$page.url.pathname ===
|
||||
item.href
|
||||
? 'bg-orange-600 text-white'
|
||||
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800'}"
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
{/each}
|
||||
</nav>
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<button
|
||||
onclick={toggleTheme}
|
||||
class="text-xs px-2.5 py-1 rounded-full bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border border-gray-200 dark:border-gray-700 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
|
||||
title={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
{/each}
|
||||
</nav>
|
||||
<button
|
||||
onclick={toggleTheme}
|
||||
class="ml-auto text-xs px-2.5 py-1 rounded-full bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border border-gray-200 dark:border-gray-700 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
|
||||
title={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
|
||||
>
|
||||
{isDark ? 'Light' : 'Dark'}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
{isDark ? 'Light' : 'Dark'}
|
||||
</button>
|
||||
<div class="dropdown dropdown-end">
|
||||
<button tabindex="0" class="btn btn-ghost btn-sm">
|
||||
{auth.user?.display_name ?? auth.user?.name ?? 'Owner'}
|
||||
</button>
|
||||
<ul class="dropdown-content menu bg-base-200 rounded-box z-20 w-48 p-2 shadow">
|
||||
<li><button onclick={() => (tokensOpen = true)}>API Tokens</button></li>
|
||||
<li><a href="/auth/logout">Sign out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="mx-auto max-w-7xl px-4 py-6">
|
||||
{@render children()}
|
||||
</main>
|
||||
</div>
|
||||
<main class="mx-auto max-w-7xl px-4 py-6">
|
||||
{@render children()}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<TokensModal bind:open={tokensOpen} />
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user