mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-09 17:29:12 +00:00
* feat(web): open design-system easter egg at /design-system route
Replace the long-press-logo iframe overlay, which loaded a separately maintained static design-system.html, with a real /design-system route. The new view aliases to the product design tokens, so the design system is maintained in one place. Adds vue-router for the route, removes the duplicate static HTML copies, and exempts the showcase view from the style scanner.
* fix(web): lazy-load the design-system view
Address Codex review: load the 2.4k-line showcase via defineAsyncComponent so it is code-split and fetched only when /design-system is visited, instead of bloating the initial bundle for every load.
* chore(nix): update pnpmDeps hash for vue-router
Adding vue-router changed pnpm-lock.yaml, which invalidated the fetchPnpmDeps hash. Set it to the value reported by the nix build.
* fix(web): return to app root when closing the design system
In-page nav anchors push hash history entries, so router.back() only stepped through them and required multiple clicks to leave. Navigate to / directly; the client lives above the route so session state is preserved.
* feat(web): let /design-system bypass the auth gate
Render the design-system route ahead of the auth/server gates and skip the /login rewrite for it, so a direct deep link shows the showcase even before the app is OAuth-ready. The view is read-only and holds no user data, matching the old static page behavior.
* fix(web): make the design-system root scrollable
The route renders inside .app-shell (height:100dvh; overflow:hidden), so a position:fixed root could be clipped. Make .ds-page a flex item that fills the shell and scrolls internally, so later sections and hash navigation remain reachable.
* fix(web): preserve /design-system during initial session load
On load the app auto-selects the first session and rewrites the URL to /sessions/<id>, which clobbered a deep-linked /design-system. Skip the session URL write while on the design-system route so refreshing keeps the route.
* fix(web): restore the prior session URL when leaving the design system
Record the URL on entry to /design-system and navigate back to it on close, so a /sessions/<id> URL is preserved instead of falling back to /. This also keeps the earlier fix that sidesteps in-page hash anchors.
* fix(web): capture the real browser URL before opening the design system
Session URLs are rewritten via the native history API, so vue-router's from.fullPath can be stale ('/' after a session is selected). Read window.location on entry instead, falling back to / for a direct deep link.
* fix(web): sync the active session URL when leaving the design system
After a direct deep link to /design-system the app auto-selects a session but the address stays '/'. On close, fall back to the active session's canonical URL so the address bar matches the displayed session.
* fix(web): capture the design-system return path at logo entry
Capture window.location in the logo long-press handler instead of a navigation guard. The guard fired on browser Back/Forward too and overwrote the return path with the design-system URL itself; capturing only at the explicit entry action avoids that.
* fix(web): replace the design-system route when closing
Use router.replace instead of push for the captured return URL, so closing does not append a second app URL after /design-system and the browser Back button returns to the page before the easter egg.
* revert(web): drop the design-system auth-gate bypass
Keep /design-system behind the auth gate so it has a single in-app entry (logo long-press). This removes the deep-link machinery (auth exemption, active-session fallback) that drove most of the URL/session edge cases, while keeping the lazy-loaded route, the flex scroll fix, the logo-entry return-path capture, and replace-on-close.
* refactor(web): rebuild the design-system easter egg as an in-app overlay
The easter egg is a hidden, read-only spec viewer opened by long-pressing the logo; it does not need to be a URL route. Replace the vue-router approach with an overlay: Sidebar opens a lazy-loaded DesignSystemView in a body-teleported full-screen overlay, dismissed by the Back button or Escape. This removes vue-router, the route, the auth-gate exemption, the session-URL guards, and the return-path machinery — none of which the feature needs.
* chore(nix): restore pnpmDeps hash after removing vue-router
6.6 KiB
6.6 KiB
kimi-web Agent Guide
Package-local rules for apps/kimi-web (@moonshot-ai/kimi-web).
What it is
The browser web UI for Kimi Code — a peer to the TUI in apps/kimi-code. It talks to the local server over REST + WebSocket under /api/v1. Stack: Vue 3 + Vite 6 + TypeScript (strict) + vue-i18n v11. (Tailwind was removed; all styling is via design tokens in src/style.css + scoped component styles.) There is no client router and no Pinia; state lives in composables/refs and provide/inject.
Design system (normative — required when modifying the UI)
- Before changing any component, style, layout, or theme, read the design system view at
src/views/DesignSystemView.vue(open it as an overlay: long-press the sidebar logo). It is the canonical design system and visual spec for this app (tokens §02, primitives §03, chat §04, theme rules §05, style rules §06). It consumes the product tokens fromsrc/style.cssdirectly, so it stays in sync with the app. New and modified UI must match it. - Use the primitives in
src/components/ui/. The library covers Button, IconButton, Badge, Pill, Card, Input/Select/Textarea/Field, Dialog, Spinner, MoonSpinner, Link, Menu/MenuItem, SegmentedControl, Tabs, Switch, Checkbox, Avatar, EmptyState, Divider, Tooltip, Banner, Sheet, Skeleton, CommandBar, TopBar. One semantic = one component — do not hand-roll a bespoke button/badge/dialog/input for a single screen. When a primitive replaces an element, delete the old scoped CSS (do not append override blocks). - Use the tokens, not ad-hoc values. Colors, fonts, radii, spacing, shadows, z-index, and motion come from the CSS custom properties in
src/style.css(catalogued in the design-system view §02). Canonical names are--color-*/--radius-*/--space-*/--text-*/--font-*/--z-*/--shadow-*/--ease-*/--duration-*/--weight-*/--leading-*. A small set of layout/focus tokens keep the--p-prefix:--p-focus-ring,--p-selection,--p-ic-sm/md/lg,--p-sidebar-w,--p-content-max/-wide,--p-bp-sm/-md. - The moon spinner (🌑…🌘) is reserved for the chat "waiting for the agent's first response" state only, and is rendered solely by
ui/MoonSpinner.vue; every other loading state uses the plainSpinner. - Run
pnpm --filter @moonshot-ai/kimi-web check:style(scripts/check-style.mjs) — it enforces the §06 anti-pattern rules (no-gradient, no-glassmorphism except TopBarfrost, no-emoji-icon except moon, no-hardcoded-hex/font, radius/z/weight from scale). Do not add new violations. - Verify visually. For any UI change, render it in the browser (light + dark, plus hover/focus states) and confirm it matches the design-system view and introduces no regression before considering it done. Build/typecheck/check-style are necessary but not sufficient.
Layout (src/)
main.ts— bootstrap (creates the app, installs i18n, mounts#app).App.vue— root component, holds most app state.api/— server client.index.tsexposes thegetKimiWebApi()singleton;config.tsbuilds REST/WS URLs;daemon/holds the wire client (http.ts,ws.ts,wire.ts,mappers.ts,agentEventProjector.ts,eventReducer.ts).components/— SFCs grouped by area:chat/(conversation/chat UI),settings/(settings & configuration),dialogs/(modal dialogs & sheets),mobile/(mobile-specific shell),ui/(design-system primitives — see "Design system" above), plus shared layout components at the top level.composables/— reusable state logic,useXnaming (useKimiWebClient,useIsDark,usePaneLayout, …).lib/— pure helpers (parseDiff,slashCommands,sessionRoute,toolMeta, …).i18n/— vue-i18n setup plus locale namespaces.debug/—DebugPanel.vueandtrace.tsfor client error/trace capture.
Vue conventions (normative)
- SFCs use
<script setup lang="ts">+ the Composition API. Component files are PascalCase (ChatHeader.vue). - Type props with the generic form
defineProps<{ ... }>(); type emits withdefineEmits<{ evt: [arg: Type] }>(). - Shared components go in
src/components/; reusable logic goes insrc/composables/with auseprefix. - There is no auto-import plugin and no path alias —
#/and@/are intentionally unused. Write relative imports (../i18n,./config).
i18n (normative — keeping locales in sync is manual)
- Setup:
src/i18n/index.ts, vue-i18n in Composition mode (legacy: false), fallbacken. The active locale is persisted inlocalStorageunderkimi-locale. - Locale files:
src/i18n/locales/{en,zh}/<namespace>.ts, eachexport default { ... } as const. New namespaces are registered insrc/i18n/locales/index.ts. - Reference with
const { t } = useI18n()andt('namespace.key')(same form in templates). - Adding a key: add it to both
en/<ns>.tsandzh/<ns>.ts. Adding a namespace: create the file in both locales and register it inlocales/index.ts. - There is no automated missing-key or en/zh parity check. Keeping the two locales in sync is a manual responsibility — do not leave a key present in only one locale.
Commands
All via pnpm --filter @moonshot-ai/kimi-web …:
dev— Vite dev server (portWEB_PORT, default 5175; proxies/api/v1toKIMI_SERVER_URL, defaulthttp://127.0.0.1:58627).dev:stub— offline stub daemon (dev/stub-daemon.mjs).build— production build intodist/.typecheck—vue-tsc --noEmit.test—vitest run(pure logic tests only; no jsdom / component tests).check:style— design-system §06 anti-pattern guard (scripts/check-style.mjs).- There is no
lintscript in this package; linting runs at the repo root via oxlint.
Gotchas / hard rules
- Do not depend on
@moonshot-ai/agent-core(mirrors the CLI/SDK rule). The web app is decoupled from core/protocol; wire types are re-implemented locally insrc/api/daemon/wire.ts. Keep it that way. - Same-origin by default: the browser only talks to its own origin; Vite proxies
/api/v1for both HTTP and WS. SetVITE_KIMI_SERVER_HTTP_URLonly when you intentionally want direct (CORS) mode. - Vite-injected globals (
__KIMI_DEV_PROXY_TARGET__,__KIMI_WEB_VERSION__,__KIMI_WEB_COMMIT__) are declared insrc/env.d.tsand defined invite.config.ts. Do not hand-editdist/. - Theming: the root element carries
data-color-scheme(light|dark|system); react to it throughuseIsDark(), not by reading the DOM directly. - Keep the Vite dev proxy and
previewproxy in sync — both are defined invite.config.ts.