Spaces:
Build error
Build error
File size: 8,044 Bytes
2ebb1e7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | <script lang="ts">
import { onMount } from 'svelte';
import { baseUrl, NAV, FLAT_NAV } from '$lib/docs-store';
import IntroductionPage from '../routes/docs/introduction/+page.svelte';
import QuickstartPage from '../routes/docs/quickstart/+page.svelte';
import ChangelogPage from '../routes/docs/changelog/+page.svelte';
import AuthenticationPage from '../routes/docs/authentication/+page.svelte';
import ModelsPage from '../routes/docs/models/+page.svelte';
import ChatCompletionsPage from '../routes/docs/chat-completions/+page.svelte';
import ParametersPage from '../routes/docs/parameters/+page.svelte';
import ResponseFormatPage from '../routes/docs/response-format/+page.svelte';
import StreamingPage from '../routes/docs/streaming/+page.svelte';
import RateLimitsPage from '../routes/docs/rate-limits/+page.svelte';
import StructuredOutputsPage from '../routes/docs/structured-outputs/+page.svelte';
import ToolCallsPage from '../routes/docs/tool-calls/+page.svelte';
import AgenticLoopPage from '../routes/docs/agentic-loop/+page.svelte';
import ErrorsPage from '../routes/docs/errors/+page.svelte';
import BestPracticesPage from '../routes/docs/best-practices/+page.svelte';
export let activePage: string = 'introduction';
let mobileNavOpen = false;
const PAGES: Record<string, any> = {
introduction: IntroductionPage,
quickstart: QuickstartPage,
changelog: ChangelogPage,
authentication: AuthenticationPage,
models: ModelsPage,
'chat-completions': ChatCompletionsPage,
parameters: ParametersPage,
'response-format': ResponseFormatPage,
streaming: StreamingPage,
'rate-limits': RateLimitsPage,
'structured-outputs': StructuredOutputsPage,
'tool-calls': ToolCallsPage,
'agentic-loop': AgenticLoopPage,
errors: ErrorsPage,
'best-practices': BestPracticesPage,
};
$: currentIdx = FLAT_NAV.findIndex(n => n.id === activePage);
$: prev = currentIdx > 0 ? FLAT_NAV[currentIdx - 1] : null;
$: next = currentIdx < FLAT_NAV.length - 1 ? FLAT_NAV[currentIdx + 1] : null;
$: CurrentComponent = PAGES[activePage] ?? IntroductionPage;
function navigate(id: string) {
activePage = id;
mobileNavOpen = false;
scrollTop();
}
let contentEl: HTMLElement;
function scrollTop() {
setTimeout(() => contentEl?.scrollTo({ top: 0, behavior: 'smooth' }), 0);
}
function handleContentClick(e: MouseEvent) {
const link = (e.target as HTMLElement).closest('a');
if (!link) return;
const href = link.getAttribute('href');
if (href?.startsWith('/docs/')) {
const id = href.replace('/docs/', '').replace(/\/$/, '');
if (PAGES[id]) {
e.preventDefault();
navigate(id);
}
}
}
onMount(() => {
const host = window.location.hostname;
if (host === 'localhost' || host === '127.0.0.1') {
baseUrl.set('http://localhost:3001/v1');
} else {
baseUrl.set('https://' + host + '/v1');
}
});
</script>
<div class="flex flex-1 min-h-0 overflow-hidden bg-white dark:bg-gray-950 rounded-tl-[1.5rem]">
<!-- Mobile nav overlay -->
{#if mobileNavOpen}
<div
class="fixed inset-0 z-30 bg-black/20 dark:bg-black/50 md:hidden"
on:click={() => mobileNavOpen = false}
role="presentation"
></div>
{/if}
<!-- Docs Sidebar -->
<aside class="
{mobileNavOpen ? 'fixed inset-y-0 left-0 z-40 translate-x-0' : 'fixed inset-y-0 left-0 z-40 -translate-x-full'}
md:relative md:translate-x-0 md:z-auto
w-60 flex-shrink-0 border-r border-[#f0f0f0] dark:border-gray-900 bg-white dark:bg-gray-950
flex flex-col
transition-transform duration-200
md:h-full md:overflow-hidden
">
<!-- Nav -->
<nav class="flex-1 overflow-y-auto px-3 py-4 space-y-5">
{#each NAV as group}
<div>
<p class="text-[10px] font-semibold text-[#bbbbbb] dark:text-gray-700 uppercase tracking-wider mb-1.5 px-2">{group.group}</p>
{#each group.items as item}
<button
on:click={() => navigate(item.id)}
class="w-full flex items-center gap-2 px-3 py-2 rounded-lg text-sm transition-all text-left
{activePage === item.id
? 'bg-[#ebebeb] dark:bg-gray-800 text-[#0f0f0f] dark:text-gray-100 font-semibold'
: 'text-[#555] dark:text-gray-500 hover:text-[#0f0f0f] dark:hover:text-gray-200 hover:bg-[#e5e5e5]/40 dark:hover:bg-gray-800/60'}"
>
{#if activePage === item.id}
<span class="w-1.5 h-1.5 rounded-full bg-[#0f0f0f] dark:bg-gray-300 flex-shrink-0"></span>
{:else}
<span class="w-1.5 h-1.5 rounded-full bg-transparent flex-shrink-0"></span>
{/if}
{item.label}
</button>
{/each}
</div>
{/each}
</nav>
<div class="px-5 py-3 border-t border-[#f0f0f0] dark:border-gray-900 flex-shrink-0">
<p class="text-[10px] text-[#cccccc] dark:text-gray-800 text-center">AI Gateway API · v1</p>
</div>
</aside>
<!-- Content -->
<div class="flex-1 min-w-0 flex flex-col md:h-full md:overflow-hidden">
<!-- Mobile top bar -->
<div class="md:hidden flex items-center gap-3 px-4 py-3 border-b border-[#f0f0f0] dark:border-gray-900 flex-shrink-0 bg-white dark:bg-gray-950">
<button
on:click={() => mobileNavOpen = !mobileNavOpen}
class="p-1.5 rounded-lg hover:bg-[#f4f4f4] dark:hover:bg-gray-800 transition-colors"
aria-label="Toggle docs menu"
>
<svg class="w-5 h-5 text-[#444] dark:text-gray-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="3" y1="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/>
</svg>
</button>
<span class="text-sm font-semibold text-[#0f0f0f] dark:text-gray-100">
{FLAT_NAV.find(n => n.id === activePage)?.label ?? 'API Docs'}
</span>
<span class="ml-auto flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-[#f4f4f4] dark:bg-gray-900 text-xs text-[#666666] dark:text-gray-500 font-medium">
<span class="w-1.5 h-1.5 rounded-full bg-green-500"></span>
v1
</span>
</div>
<!-- Scrollable content -->
<main bind:this={contentEl} on:click={handleContentClick} class="flex-1 overflow-y-auto px-6 md:px-12 py-8 md:py-12 bg-white dark:bg-gray-950">
<svelte:component this={CurrentComponent} />
<!-- Prev / Next -->
{#if prev || next}
<div class="mt-16 pt-8 border-t border-[#f0f0f0] dark:border-gray-900 flex items-center justify-between gap-4">
{#if prev}
<button
on:click={() => navigate(prev.id)}
class="flex items-center gap-2 px-4 py-3 rounded-xl border border-[#e5e5e5] dark:border-gray-850 text-sm text-[#555] dark:text-gray-500 hover:text-[#0f0f0f] dark:hover:text-gray-200 hover:border-[#cccccc] dark:hover:border-gray-700 hover:bg-[#fafafa] dark:hover:bg-gray-900 transition-all group"
>
<svg class="w-4 h-4 group-hover:-translate-x-0.5 transition-transform" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 5l-7 7 7 7"/></svg>
<div class="text-left">
<p class="text-[10px] text-[#aaa] dark:text-gray-700 uppercase tracking-wide">Previous</p>
<p class="font-medium text-[#222] dark:text-gray-200">{prev.label}</p>
</div>
</button>
{:else}
<div></div>
{/if}
{#if next}
<button
on:click={() => navigate(next.id)}
class="flex items-center gap-2 px-4 py-3 rounded-xl border border-[#e5e5e5] dark:border-gray-850 text-sm text-[#555] dark:text-gray-500 hover:text-[#0f0f0f] dark:hover:text-gray-200 hover:border-[#cccccc] dark:hover:border-gray-700 hover:bg-[#fafafa] dark:hover:bg-gray-900 transition-all group"
>
<div class="text-right">
<p class="text-[10px] text-[#aaa] dark:text-gray-700 uppercase tracking-wide">Next</p>
<p class="font-medium text-[#222] dark:text-gray-200">{next.label}</p>
</div>
<svg class="w-4 h-4 group-hover:translate-x-0.5 transition-transform" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</button>
{/if}
</div>
{/if}
</main>
</div>
</div>
|