Compare commits
7
Commits
v0.6.8
..
de132ffb3a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de132ffb3a | ||
|
|
a285d192eb | ||
|
|
6bc9e4644e | ||
|
|
615dfb54e0 | ||
|
|
8914b40bec | ||
|
|
4071930aff | ||
|
|
1b5cd0f416 |
No files matched your search
@@ -86,6 +86,5 @@ jobs:
|
||||
- **Linux**: `.deb` / `.AppImage` / `.rpm`
|
||||
|
||||
Full changelog: see commit history.
|
||||
releaseDraft: true
|
||||
prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-alpha') }}
|
||||
args: ${{ matrix.args }}
|
||||
Generated
+1
-1
@@ -1958,7 +1958,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kimiswitch"
|
||||
version = "0.6.7"
|
||||
version = "0.6.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
||||
+4
-4
@@ -13,7 +13,7 @@
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="KimiSwitch, KimiCodeSwitch, SwitchKimi, Kimi Code Switch, Kimi 切换, LLM 供应商管理, AI 供应商切换, 多 LLM 管理, Kimi 用量监控, 智谱 GLM, MiniMax, DeepSeek, OpenRouter, 基元律动, 用量查询, 账单监控, Token 套餐, Claude, GPT, Windows LLM 桌面工具"
|
||||
content="Kimi Switch, KimiSwitch, KimiCode Switch, KimiCodeSwitch, SwitchKimi, Kimi Code Switch, Kimi 切换, LLM 供应商管理, AI 供应商切换, 多 LLM 管理, Kimi 用量监控, 智谱 GLM, MiniMax, DeepSeek, OpenRouter, 基元律动, 用量查询, 账单监控, Token 套餐, Claude, GPT, Windows LLM 桌面工具"
|
||||
/>
|
||||
|
||||
<!-- Open Graph -->
|
||||
@@ -24,7 +24,7 @@
|
||||
content="一键切换 Kimi / 智谱 GLM / MiniMax / DeepSeek 等供应商,用量与账单一目了然,开源 MIT。"
|
||||
/>
|
||||
<meta property="og:url" content="https://billowliu2.github.io/KimiSwitch/" />
|
||||
<meta property="og:image" content="%BASE_URL%og.png" />
|
||||
<meta property="og:image" content="https://billowliu2.github.io/KimiSwitch/og.png" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
@@ -33,7 +33,7 @@
|
||||
name="twitter:description"
|
||||
content="一键切换 Kimi / 智谱 GLM / MiniMax / DeepSeek 等供应商,用量与账单一目了然,开源 MIT。"
|
||||
/>
|
||||
<meta name="twitter:image" content="%BASE_URL%og.png" />
|
||||
<meta name="twitter:image" content="https://billowliu2.github.io/KimiSwitch/og.png" />
|
||||
|
||||
<!-- Canonical -->
|
||||
<link rel="canonical" href="https://billowliu2.github.io/KimiSwitch/" />
|
||||
@@ -46,7 +46,7 @@
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "Kimi Switch",
|
||||
"alternateName": ["KimiSwitch", "KimiCodeSwitch"],
|
||||
"alternateName": ["KimiSwitch", "KimiCodeSwitch", "Kimi Switch", "KimiCode Switch"],
|
||||
"url": "https://billowliu2.github.io/KimiSwitch/",
|
||||
"applicationCategory": "DeveloperApplication",
|
||||
"operatingSystem": "Windows 10, Windows 11",
|
||||
|
||||
Generated
+18
@@ -16,6 +16,7 @@
|
||||
"react-router-dom": "^7.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^26.1.2",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
@@ -1299,6 +1300,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz",
|
||||
"integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~8.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
||||
@@ -2723,6 +2734,13 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"react-router-dom": "^7.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^26.1.2",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#0F62FE"><path d="M12 2L4 6v6c0 5 3.5 8.5 8 10 4.5-1.5 8-5 8-10V6l-8-4zm0 4l4 2v4c0 3-1.5 5-4 6-2.5-1-4-3-4-6V8l4-2z"/></svg>
|
||||
|
Before Width: | Height: | Size: 192 B |
@@ -6,27 +6,17 @@
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://billowliu2.github.io/KimiSwitch/#/features</loc>
|
||||
<loc>https://billowliu2.github.io/KimiSwitch/features</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://billowliu2.github.io/KimiSwitch/#/showcase</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://billowliu2.github.io/KimiSwitch/#/performance</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://billowliu2.github.io/KimiSwitch/#/changelog</loc>
|
||||
<loc>https://billowliu2.github.io/KimiSwitch/changelog</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://billowliu2.github.io/KimiSwitch/#/download</loc>
|
||||
<loc>https://billowliu2.github.io/KimiSwitch/download</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
|
||||
@@ -8,6 +8,10 @@ import Download from "./sections/Download";
|
||||
import Privacy from "./sections/Privacy";
|
||||
import CTA from "./sections/CTA";
|
||||
import Footer from "./components/Footer";
|
||||
import { useLang, type Dict } from "./i18n";
|
||||
|
||||
const SITE_URL = "https://billowliu2.github.io/KimiSwitch";
|
||||
type SeoKey = keyof Dict["seo"];
|
||||
|
||||
function ScrollToTop() {
|
||||
const { pathname } = useLocation();
|
||||
@@ -17,6 +21,47 @@ function ScrollToTop() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Update per-page title / description / OG / canonical for SEO. */
|
||||
function RouteMeta() {
|
||||
const { pathname } = useLocation();
|
||||
const { t } = useLang();
|
||||
|
||||
useEffect(() => {
|
||||
const page = pathname.replace(/\/+$/, "");
|
||||
const seoKey: SeoKey =
|
||||
page === "/features" || page === "/changelog" || page === "/download"
|
||||
? (page.slice(1) as SeoKey)
|
||||
: "home";
|
||||
const m = t.seo[seoKey];
|
||||
const url = `${SITE_URL}${page === "" ? "/" : page}`;
|
||||
|
||||
document.title = m.title;
|
||||
setMeta("name", "description", m.description);
|
||||
setMeta("property", "og:title", m.title);
|
||||
setMeta("property", "og:description", m.description);
|
||||
setMeta("property", "og:url", url);
|
||||
setMeta("name", "twitter:title", m.title);
|
||||
setMeta("name", "twitter:description", m.description);
|
||||
|
||||
const canon = document.querySelector('link[rel="canonical"]');
|
||||
if (canon) canon.setAttribute("href", url);
|
||||
}, [pathname, t]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function setMeta(attr: "name" | "property", key: string, content: string) {
|
||||
let el = document.head.querySelector<HTMLMetaElement>(
|
||||
`meta[${attr}="${key}"]`,
|
||||
);
|
||||
if (!el) {
|
||||
el = document.createElement("meta");
|
||||
el.setAttribute(attr, key);
|
||||
document.head.appendChild(el);
|
||||
}
|
||||
el.setAttribute("content", content);
|
||||
}
|
||||
|
||||
const homePage = (
|
||||
<>
|
||||
<Hero />
|
||||
@@ -30,6 +75,7 @@ export default function App() {
|
||||
<Nav />
|
||||
<main className="flex-1">
|
||||
<ScrollToTop />
|
||||
<RouteMeta />
|
||||
<Routes>
|
||||
<Route path="/" element={homePage} />
|
||||
<Route
|
||||
|
||||
@@ -5,7 +5,7 @@ import { asset } from "../lib/asset";
|
||||
/** Map recommendLink.name → logo file in public/logos (only when we have one). */
|
||||
const logoFor: Record<string, string> = {
|
||||
"Kimi": "logos/kimi.svg",
|
||||
"智谱 GLM": "logos/glm.svg",
|
||||
"智谱 GLM": "logos/zhipu.svg",
|
||||
"DeepSeek": "logos/deepseek.svg",
|
||||
"OpenCodeGo": "logos/opencode.svg",
|
||||
"MiniMax": "logos/minimax.svg",
|
||||
|
||||
+70
-6
@@ -16,10 +16,10 @@ const zh = {
|
||||
performance: "性能",
|
||||
changelog: "更新日志",
|
||||
download: "下载",
|
||||
downloadBtn: "下载 v0.6.7",
|
||||
downloadBtn: "下载 v0.6.8",
|
||||
},
|
||||
hero: {
|
||||
badge: "开源 MIT · Windows 10 / 11",
|
||||
badge: "v0.6.8 · 开源 MIT · Windows / macOS / Linux",
|
||||
titleBefore: "统一管理你的",
|
||||
titleAccent: "AI 供应商",
|
||||
titleAfter: "",
|
||||
@@ -140,6 +140,16 @@ const zh = {
|
||||
syncedNote: "数据已同步 GitHub Releases",
|
||||
fallbackNote: "内置版本记录",
|
||||
entries: [
|
||||
{
|
||||
version: "v0.6.8",
|
||||
date: "2026-08-01",
|
||||
items: [
|
||||
"仪表盘加载提速:models.dev 快照移出主 bundle,异步加载不阻塞首帧",
|
||||
"Rust 价格索引启动时后台预热;get_summary 扫描结果 8 秒缓存",
|
||||
"k3-256k 按 k3 牌价计费",
|
||||
"官网更新:Kimi 风格重设计、中英文切换、更新日志同步 GitHub Releases、三平台下载",
|
||||
],
|
||||
},
|
||||
{
|
||||
version: "v0.6.7",
|
||||
date: "2026-08-01",
|
||||
@@ -234,7 +244,7 @@ const zh = {
|
||||
},
|
||||
download: {
|
||||
title: "下载 Kimi Switch",
|
||||
subtitle: "当前版本 v0.6.7 · Windows 10 / 11 主推,macOS 与 Linux 路线图上。",
|
||||
subtitle: "当前版本 v0.6.8 · Windows / macOS / Linux 三平台已发布",
|
||||
autoUpdate: "应用内置自动检测更新,新版本发布后在设置页一键升级。",
|
||||
ready: "已发布",
|
||||
wip: "开发中",
|
||||
@@ -252,6 +262,28 @@ const zh = {
|
||||
download: "免费下载",
|
||||
source: "GitHub 源码",
|
||||
},
|
||||
seo: {
|
||||
home: {
|
||||
title: "Kimi Switch — 多 LLM 供应商统一管理 · 用量监控 · 账单一目了然",
|
||||
description:
|
||||
"Kimi Switch / KimiCodeSwitch — Windows 桌面端多 LLM 供应商统一管理器,一键切换 Kimi / 智谱 GLM / MiniMax / DeepSeek / OpenRouter / 基元律动,用量账单实时监控,开源 MIT。",
|
||||
},
|
||||
features: {
|
||||
title: "核心功能 - Kimi Switch",
|
||||
description:
|
||||
"Kimi Switch 核心功能:多供应商管理、用量与账单监控、Kimi 一键授权登录、智能模型发现与自动更新,22 个预设开箱即用。",
|
||||
},
|
||||
changelog: {
|
||||
title: "更新日志 - Kimi Switch",
|
||||
description:
|
||||
"Kimi Switch 更新日志:每个版本的重要变更记录,同步自 GitHub Releases。",
|
||||
},
|
||||
download: {
|
||||
title: "下载 - Kimi Switch",
|
||||
description:
|
||||
"下载 Kimi Switch:Windows .msi、macOS .dmg、Linux .deb / .AppImage / .rpm,免费开源 MIT。",
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
recommend: "推荐供应商",
|
||||
mirror: "国内镜像",
|
||||
@@ -280,10 +312,10 @@ const en: Dict = {
|
||||
performance: "Performance",
|
||||
changelog: "Changelog",
|
||||
download: "Download",
|
||||
downloadBtn: "Download v0.6.7",
|
||||
downloadBtn: "Download v0.6.8",
|
||||
},
|
||||
hero: {
|
||||
badge: "Open source MIT · Windows 10 / 11",
|
||||
badge: "v0.6.8 · Open source MIT · Windows / macOS / Linux",
|
||||
titleBefore: "One app for all your ",
|
||||
titleAccent: "AI providers",
|
||||
titleAfter: "",
|
||||
@@ -404,6 +436,16 @@ const en: Dict = {
|
||||
syncedNote: "Synced from GitHub Releases",
|
||||
fallbackNote: "Built-in release notes",
|
||||
entries: [
|
||||
{
|
||||
version: "v0.6.8",
|
||||
date: "2026-08-01",
|
||||
items: [
|
||||
"Dashboard speedup: models.dev snapshot moved out of the main bundle, async load without blocking first paint",
|
||||
"Rust price index warmed in the background at startup; get_summary scan cached for 8s",
|
||||
"k3-256k billed at the k3 list price",
|
||||
"Website refresh: Kimi-style redesign, zh/en switch, changelog synced from GitHub Releases, downloads for all three platforms",
|
||||
],
|
||||
},
|
||||
{
|
||||
version: "v0.6.7",
|
||||
date: "2026-08-01",
|
||||
@@ -498,7 +540,7 @@ const en: Dict = {
|
||||
},
|
||||
download: {
|
||||
title: "Download Kimi Switch",
|
||||
subtitle: "Current version v0.6.7 · Windows 10 / 11 first, macOS and Linux on the roadmap.",
|
||||
subtitle: "Current version v0.6.8 · Windows, macOS and Linux now released",
|
||||
autoUpdate: "Built-in update detection: upgrade in one click from Settings when a new version ships.",
|
||||
ready: "Available",
|
||||
wip: "In development",
|
||||
@@ -516,6 +558,28 @@ const en: Dict = {
|
||||
download: "Free Download",
|
||||
source: "GitHub Source",
|
||||
},
|
||||
seo: {
|
||||
home: {
|
||||
title: "Kimi Switch — Unified Management for AI Providers · Usage & Billing",
|
||||
description:
|
||||
"Kimi Switch / KimiCodeSwitch — a Windows desktop manager for multiple LLM providers. Switch between Kimi, Zhipu GLM, MiniMax, DeepSeek, OpenRouter and TokenRhythm in one click, with usage and billing at a glance. Open source MIT.",
|
||||
},
|
||||
features: {
|
||||
title: "Core Features - Kimi Switch",
|
||||
description:
|
||||
"Kimi Switch core features: multi-provider management, usage & billing, one-click Kimi login, smart model discovery and auto updates. 22 presets out of the box.",
|
||||
},
|
||||
changelog: {
|
||||
title: "Changelog - Kimi Switch",
|
||||
description:
|
||||
"Kimi Switch changelog: notable changes in every release, synced from GitHub Releases.",
|
||||
},
|
||||
download: {
|
||||
title: "Download - Kimi Switch",
|
||||
description:
|
||||
"Download Kimi Switch: Windows .msi, macOS .dmg, Linux .deb / .AppImage / .rpm. Free and open source under MIT.",
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
recommend: "Recommended Providers",
|
||||
mirror: "China mirror",
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { HashRouter } from "react-router-dom";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { LangProvider } from "./i18n";
|
||||
import App from "./App";
|
||||
import "./index.css";
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<HashRouter>
|
||||
<BrowserRouter basename="/KimiSwitch">
|
||||
<LangProvider>
|
||||
<App />
|
||||
</LangProvider>
|
||||
</HashRouter>
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
);
|
||||
@@ -10,9 +10,14 @@ interface ChangelogEntry {
|
||||
|
||||
const API_URL =
|
||||
"https://api.github.com/repos/billowliu2/KimiSwitch/releases?per_page=20";
|
||||
const CACHE_KEY = "kimi-switch-changelog";
|
||||
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
|
||||
|
||||
/** Cache key is versioned by the newest embedded release so redeploys
|
||||
* invalidate stale caches in the browser immediately. */
|
||||
function cacheKey(embedded: ChangelogEntry[]) {
|
||||
return `kimi-switch-changelog-${embedded[0]?.version ?? "v1"}`;
|
||||
}
|
||||
|
||||
/** Parse a GitHub release body into bullet items. */
|
||||
function parseBody(body: string): string[] {
|
||||
return body
|
||||
@@ -36,9 +41,9 @@ async function fetchReleases(): Promise<ChangelogEntry[]> {
|
||||
}));
|
||||
}
|
||||
|
||||
function readCache(): ChangelogEntry[] | null {
|
||||
function readCache(embedded: ChangelogEntry[]): ChangelogEntry[] | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(CACHE_KEY);
|
||||
const raw = localStorage.getItem(cacheKey(embedded));
|
||||
if (!raw) return null;
|
||||
const { ts, data } = JSON.parse(raw) as { ts: number; data: ChangelogEntry[] };
|
||||
if (Date.now() - ts > CACHE_TTL) return null;
|
||||
@@ -48,10 +53,10 @@ function readCache(): ChangelogEntry[] | null {
|
||||
}
|
||||
}
|
||||
|
||||
function writeCache(data: ChangelogEntry[]) {
|
||||
function writeCache(data: ChangelogEntry[], embedded: ChangelogEntry[]) {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
CACHE_KEY,
|
||||
cacheKey(embedded),
|
||||
JSON.stringify({ ts: Date.now(), data }),
|
||||
);
|
||||
} catch {
|
||||
@@ -67,7 +72,7 @@ export default function Changelog() {
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
const cached = readCache();
|
||||
const cached = readCache(embedded);
|
||||
if (cached) {
|
||||
setRemote(cached);
|
||||
setSynced(true);
|
||||
@@ -78,7 +83,7 @@ export default function Changelog() {
|
||||
if (cancelled) return;
|
||||
setRemote(data);
|
||||
setSynced(true);
|
||||
writeCache(data);
|
||||
writeCache(data, embedded);
|
||||
})
|
||||
.catch(() => {
|
||||
/* 网络不通时使用内置列表兜底 */
|
||||
|
||||
+22
-3
@@ -1,11 +1,30 @@
|
||||
import { defineConfig } from "vite";
|
||||
import { defineConfig, type Plugin } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { copyFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
/**
|
||||
* GitHub Pages SPA fallback: any deep path (e.g. /KimiSwitch/features) is
|
||||
* served by 404.html; duplicating the built index.html there lets the
|
||||
* BrowserRouter boot and render the right route. This keeps every page
|
||||
* indexable as a real URL instead of a `#/` fragment.
|
||||
*/
|
||||
function generate404(): Plugin {
|
||||
return {
|
||||
name: "generate-404",
|
||||
apply: "build",
|
||||
closeBundle() {
|
||||
const dist = fileURLToPath(new URL("./dist", import.meta.url));
|
||||
copyFileSync(`${dist}/index.html`, `${dist}/404.html`);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// GitHub Pages subpath: billowliu2.github.io/KimiSwitch/
|
||||
export default defineConfig({
|
||||
base: "/KimiSwitch/",
|
||||
plugins: [react()],
|
||||
plugins: [react(), generate404()],
|
||||
resolve: {
|
||||
alias: { "@": "/src" },
|
||||
},
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user