diff --git a/package-lock.json b/package-lock.json
index bb7c602..0d5ba3a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "0.0.0",
"dependencies": {
"fuse.js": "^7.0.0",
+ "marked": "^17.0.1",
"mixpanel-browser": "^2.73.0",
"svelte-typewriter": "^3.2.3"
},
@@ -1306,6 +1307,18 @@
"@jridgewell/sourcemap-codec": "^1.4.15"
}
},
+ "node_modules/marked": {
+ "version": "17.0.1",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz",
+ "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
"node_modules/mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
diff --git a/package.json b/package.json
index 6f26624..74dd7d8 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
},
"dependencies": {
"fuse.js": "^7.0.0",
+ "marked": "^17.0.1",
"mixpanel-browser": "^2.73.0",
"svelte-typewriter": "^3.2.3"
}
diff --git a/public/_redirects b/public/_redirects
new file mode 100644
index 0000000..4e31746
--- /dev/null
+++ b/public/_redirects
@@ -0,0 +1,2 @@
+/* /index.html 200
+
diff --git a/src/Cookbook.svelte b/src/Cookbook.svelte
new file mode 100644
index 0000000..57153d8
--- /dev/null
+++ b/src/Cookbook.svelte
@@ -0,0 +1,310 @@
+
+
+
+ {#if loading}
+
+
+
Loading cookbook guides...
+
+ {:else if error}
+
+
Error Loading Guides
+
{error}
+
+
+ {:else}
+
+
+
+ {#if guides.length === 0}
+
+
No guides available at the moment.
+
+ {:else}
+
+ {#each guides as guide}
+
openGuide(guide)} on:keydown={(e) => e.key === 'Enter' && openGuide(guide)} role="button" tabindex="0">
+
{guide.title}
+
{guide.description}
+
+
+ {/each}
+
+ {/if}
+ {/if}
+
+
+
+
diff --git a/src/Main.svelte b/src/Main.svelte
index c9b4b78..b8e01ba 100644
--- a/src/Main.svelte
+++ b/src/Main.svelte
@@ -2,16 +2,34 @@
import { onMount } from "svelte";
import App from "./App.svelte";
import Providers from "./Providers.svelte";
+ import Cookbook from "./Cookbook.svelte";
import RequestForm from "./RequestForm.svelte";
import { initAnalytics, trackPageView, trackTabChange } from "./analytics";
- let activeTab: "models" | "providers" = "models";
+ let activeTab: "models" | "providers" | "cookbook" = "models";
let mobileMenuOpen = false;
let requestForm: RequestForm;
const GITHUB_URL = "https://github.com/BerriAI/litellm";
const DOCS_URL = "https://docs.litellm.ai";
+ // Map URL paths to tab names
+ function getTabFromPath(path: string): "models" | "providers" | "cookbook" {
+ if (path === "/providers" || path === "/providers/") {
+ return "providers";
+ } else if (path === "/cookbook" || path === "/cookbook/") {
+ return "cookbook";
+ }
+ return "models";
+ }
+
+ // Get path from tab name
+ function getPathFromTab(tab: "models" | "providers" | "cookbook"): string {
+ if (tab === "providers") return "/providers";
+ if (tab === "cookbook") return "/cookbook";
+ return "/";
+ }
+
function toggleMobileMenu() {
mobileMenuOpen = !mobileMenuOpen;
}
@@ -20,10 +38,22 @@
mobileMenuOpen = false;
}
- function selectTab(tab: "models" | "providers") {
+ function selectTab(tab: "models" | "providers" | "cookbook", updateUrl = true) {
activeTab = tab;
closeMobileMenu();
trackTabChange(tab);
+
+ // Update URL without page reload
+ if (updateUrl) {
+ const path = getPathFromTab(tab);
+ window.history.pushState({ tab }, "", path);
+ }
+ }
+
+ // Handle browser back/forward buttons
+ function handlePopState(event: PopStateEvent) {
+ const tab = event.state?.tab || getTabFromPath(window.location.pathname);
+ selectTab(tab, false);
}
const PROVIDERS_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/provider_endpoints_support.json";
const MODELS_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
@@ -39,6 +69,16 @@
initAnalytics();
trackPageView('Home');
+ // Set initial tab based on URL
+ const initialTab = getTabFromPath(window.location.pathname);
+ activeTab = initialTab;
+
+ // Set initial history state
+ window.history.replaceState({ tab: initialTab }, "", window.location.pathname);
+
+ // Listen for browser back/forward navigation
+ window.addEventListener("popstate", handlePopState);
+
// Check if URL has ?request=true to auto-open the form
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('request') === 'true') {
@@ -82,6 +122,11 @@
console.error("Failed to load statistics:", error);
statsLoading = false;
}
+
+ // Cleanup event listener on component destroy
+ return () => {
+ window.removeEventListener("popstate", handlePopState);
+ };
});
@@ -109,7 +154,14 @@
class:active={activeTab === "providers"}
on:click={() => selectTab("providers")}
>
- AI Gateway - Endpoints & Providers
+ Endpoints & Providers
+
+