Skip to content

Commit d1b067f

Browse files
author
Zehee
committed
feat: rebuild vis dashboard with Kimi Code Web framework
- Proxy Kimi theme CSS via /kimi-theme.css with fallback variables - Replicate Kimi page structure: rail, collapsible sidebar, topbar, breadcrumbs - Add Workspace, Themes, Theme detail, Decisions, Memories, Settings views - Use Kimi CSS variables for colors, radii, fonts - Persist sidebar collapse state in localStorage
1 parent d0e5754 commit d1b067f

4 files changed

Lines changed: 1385 additions & 586 deletions

File tree

‎src/vis/server.ts‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,75 @@ import {
2020
updateTheme,
2121
} from './api.js';
2222

23+
const KIMI_ORIGIN = 'http://127.0.0.1:58627';
24+
const THEME_CACHE_TTL_MS = 60_000;
25+
26+
const FALLBACK_CSS = `:root {
27+
--bg: #0d1117;
28+
--canvas: #161b22;
29+
--panel: #1c2128;
30+
--panel2: #21262d;
31+
--line: #2d333b;
32+
--ink: rgba(255,255,255,0.84);
33+
--text: rgba(255,255,255,0.84);
34+
--muted: rgba(255,255,255,0.55);
35+
--dim: rgba(255,255,255,0.35);
36+
--blue: #58a6ff;
37+
--blue2: #79b8ff;
38+
--bluebg: #1c2a3a;
39+
--soft: #21262d;
40+
--bd: #1f6feb;
41+
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
42+
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
43+
--r-sm: 6px;
44+
--r-md: 8px;
45+
--r-lg: 12px;
46+
}`;
47+
48+
interface CachedTheme {
49+
css: string;
50+
fetchedAt: number;
51+
}
52+
53+
let cachedTheme: CachedTheme | null = null;
54+
55+
async function fetchTextWithTimeout(url: string, timeoutMs: number): Promise<string> {
56+
const controller = new AbortController();
57+
const timer = setTimeout(() => controller.abort(), timeoutMs);
58+
try {
59+
const res = await fetch(url, { signal: controller.signal });
60+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
61+
return await res.text();
62+
} finally {
63+
clearTimeout(timer);
64+
}
65+
}
66+
67+
async function proxyKimiThemeCss(): Promise<{ css: string; fromCache: boolean }> {
68+
if (cachedTheme && Date.now() - cachedTheme.fetchedAt < THEME_CACHE_TTL_MS) {
69+
return { css: cachedTheme.css, fromCache: true };
70+
}
71+
72+
try {
73+
const html = await fetchTextWithTimeout(`${KIMI_ORIGIN}/`, 3000);
74+
const match =
75+
html.match(
76+
new RegExp(`<link[^>]+rel=["']stylesheet["'][^>]*href=["'](/assets/index-[a-zA-Z0-9]+\\.css)["'][^>]*>`, 'i'),
77+
) ||
78+
html.match(
79+
new RegExp(`<link[^>]+href=["'](/assets/index-[a-zA-Z0-9]+\\.css)["'][^>]*rel=["']stylesheet["'][^>]*>`, 'i'),
80+
);
81+
const cssPath = match?.[1];
82+
if (!cssPath) throw new Error('Kimi stylesheet link not found');
83+
84+
const css = await fetchTextWithTimeout(`${KIMI_ORIGIN}${cssPath}`, 3000);
85+
cachedTheme = { css, fetchedAt: Date.now() };
86+
return { css, fromCache: false };
87+
} catch {
88+
return { css: FALLBACK_CSS, fromCache: false };
89+
}
90+
}
91+
2392
export interface VisServerOptions {
2493
ctx: Ctx;
2594
port: number;
@@ -111,6 +180,13 @@ export function createApp(ctx: Ctx): Hono {
111180
return c.json(result);
112181
});
113182

183+
app.get('/kimi-theme.css', async (c) => {
184+
const result = await proxyKimiThemeCss();
185+
c.header('Content-Type', 'text/css');
186+
c.header('X-Theme-Source', result.fromCache ? 'cache' : result.css === FALLBACK_CSS ? 'fallback' : 'proxy');
187+
return c.body(result.css);
188+
});
189+
114190
const staticRoot = getStaticRoot();
115191
if (!fs.existsSync(staticRoot)) {
116192
fs.mkdirSync(staticRoot, { recursive: true });

0 commit comments

Comments
 (0)