diff --git a/apps/kimi-web/index.html b/apps/kimi-web/index.html index 1c024e8e1..255b88737 100644 --- a/apps/kimi-web/index.html +++ b/apps/kimi-web/index.html @@ -15,7 +15,7 @@ (function () { try { var theme = localStorage.getItem('kimi-web.theme'); - document.documentElement.dataset.theme = theme === 'terminal' || theme === 'modern' ? theme : 'modern'; + document.documentElement.dataset.theme = theme === 'terminal' || theme === 'modern' || theme === 'kimi' ? theme : 'modern'; var v = localStorage.getItem('kimi-web.color-scheme'); if (v === 'light' || v === 'dark' || v === 'system') { document.documentElement.dataset.colorScheme = v; diff --git a/apps/kimi-web/src/App.vue b/apps/kimi-web/src/App.vue index 3027f5661..04aa72216 100644 --- a/apps/kimi-web/src/App.vue +++ b/apps/kimi-web/src/App.vue @@ -86,9 +86,11 @@ const sideWidth = computed(() => sessionColWidth.value); // tab keeps its local split-pane preview. // --------------------------------------------------------------------------- const PREVIEW_WIDTH_KEY = 'kimi-web.file-preview-width'; -const PREVIEW_DEFAULT = 460; const PREVIEW_MIN = 320; const PREVIEW_MAX = 760; +const PREVIEW_DEFAULT = typeof window !== 'undefined' + ? Math.max(PREVIEW_MIN, Math.min(PREVIEW_MAX, Math.round(window.innerWidth / 2))) + : 460; const previewWidth = ref(PREVIEW_DEFAULT); const previewTarget = ref(null); @@ -536,7 +538,7 @@ function handleCreateSessionInWorkspace(workspaceId: string): void { v-if="!hasMultiSelect" ref="conversationPaneRef" :mobile="isMobile" - :modern="client.theme.value === 'modern'" + :modern="client.theme.value === 'modern' || client.theme.value === 'kimi'" :turns="client.turns.value" :approvals="client.pendingApprovals.value" :changes="client.changes.value" @@ -908,8 +910,10 @@ function handleCreateSessionInWorkspace(workspaceId: string): void { diff --git a/apps/kimi-web/src/components/ConversationPane.vue b/apps/kimi-web/src/components/ConversationPane.vue index 5507cee88..06e1b898b 100644 --- a/apps/kimi-web/src/components/ConversationPane.vue +++ b/apps/kimi-web/src/components/ConversationPane.vue @@ -50,7 +50,7 @@ const props = defineProps<{ fileReloadKey?: string | number; /** Mobile shell: compact chrome + give the TabBar bigger taps. */ mobile?: boolean; - /** Modern theme: render chat bubbles at all widths (desktop included). */ + /** Bubble themes (Modern/Kimi): render chat bubbles at all widths (desktop included). */ modern?: boolean; /** True while switching sessions and the turns array is not yet loaded. */ sessionLoading?: boolean; @@ -127,7 +127,7 @@ watch( }, ); -// Bubble chat layout: always on mobile, and on desktop under the Modern theme. +// Bubble chat layout: always on mobile, and on desktop under Modern/Kimi. const bubble = computed(() => props.mobile === true || props.modern === true); const runningTasks = computed(() => props.tasks.filter((t) => t.state === 'run').length); @@ -841,6 +841,7 @@ onUnmounted(() => { @media (max-width: 1199px) { .float-stack { display: none; } } + .panes { flex: 1; min-height: 0; diff --git a/apps/kimi-web/src/components/MobileSettingsSheet.vue b/apps/kimi-web/src/components/MobileSettingsSheet.vue index 0727caf66..2085311f7 100644 --- a/apps/kimi-web/src/components/MobileSettingsSheet.vue +++ b/apps/kimi-web/src/components/MobileSettingsSheet.vue @@ -168,10 +168,10 @@ function onLogout(): void { {{ t('theme.terminal') }} + :class="{ on: theme === 'kimi' }" + :aria-pressed="theme === 'kimi'" + @click="emit('setTheme', 'kimi')" + >{{ t('theme.kimi') }} @@ -204,7 +204,9 @@ function onLogout(): void { - + + {{ t('theme.accentLabel') }} diff --git a/apps/kimi-web/src/components/Onboarding.vue b/apps/kimi-web/src/components/Onboarding.vue index a660a53b3..ccc8870b5 100644 --- a/apps/kimi-web/src/components/Onboarding.vue +++ b/apps/kimi-web/src/components/Onboarding.vue @@ -101,21 +101,21 @@ function finish(): void { - - + + - {{ t('theme.terminal') }} - {{ t('onboarding.terminalDesc') }} + {{ t('theme.kimi') }} + {{ t('onboarding.kimiDesc') }} - - + + {{ t('theme.accentLabel') }} {{ t('theme.terminal') }} + :class="{ on: theme === 'kimi' }" + :aria-pressed="theme === 'kimi'" + @click="emit('setTheme', 'kimi')" + >{{ t('theme.kimi') }} @@ -656,7 +656,9 @@ function blinkOnce(): void { >{{ t('theme.system') }} - + + {{ t('theme.accentLabel') }} {{ t('theme.terminal') }} + :class="{ on: theme === 'kimi' }" + :aria-pressed="theme === 'kimi'" + @click="emit('setTheme', 'kimi')" + >{{ t('theme.kimi') }} @@ -733,7 +735,9 @@ function blinkOnce(): void { >{{ t('theme.system') }} - + + {{ t('theme.accentLabel') }} div { +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.open .bb > div { animation: kimi-line-in 0.14s ease-out both; } -html[data-theme="modern"] .box.open .bb > div:nth-child(1) { animation-delay: 0ms; } -html[data-theme="modern"] .box.open .bb > div:nth-child(2) { animation-delay: 30ms; } -html[data-theme="modern"] .box.open .bb > div:nth-child(3) { animation-delay: 60ms; } -html[data-theme="modern"] .box.open .bb > div:nth-child(4) { animation-delay: 90ms; } -html[data-theme="modern"] .box.open .bb > div:nth-child(5) { animation-delay: 120ms; } -html[data-theme="modern"] .box.open .bb > div:nth-child(6) { animation-delay: 150ms; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.open .bb > div:nth-child(1) { animation-delay: 0ms; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.open .bb > div:nth-child(2) { animation-delay: 30ms; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.open .bb > div:nth-child(3) { animation-delay: 60ms; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.open .bb > div:nth-child(4) { animation-delay: 90ms; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.open .bb > div:nth-child(5) { animation-delay: 120ms; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.open .bb > div:nth-child(6) { animation-delay: 150ms; } /* =========================================================================== MODERN DE-TERMINALIZATION @@ -531,181 +535,181 @@ html[data-theme="modern"] .box.open .bb > div:nth-child(6) { animation-delay: 15 =========================================================================== */ /* ---- App shell ---- */ -html[data-theme="modern"] .app { border-top: none; } -html[data-theme="modern"] .auth-banner-inner { font-family: var(--sans); } -html[data-theme="modern"] .auth-banner-btn { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .coming-soon { font-family: var(--sans); } -html[data-theme="modern"] .gload-text { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .app { border-top: none; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .auth-banner-inner { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .auth-banner-btn { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .coming-soon { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .gload-text { font-family: var(--sans); } /* ---- Sidebar: buttons, rename inputs, kebab menu, settings popover ---- */ -html[data-theme="modern"] .btn-new-ws { font-family: var(--sans); } -html[data-theme="modern"] .theme-opt { font-family: var(--sans); } -html[data-theme="modern"] .gh-rename { border-radius: var(--r-xs); font-family: var(--sans); } -html[data-theme="modern"] .gh.sel { border-radius: var(--r-sm); } -html[data-theme="modern"] .gh-add { color: var(--faint); } -html[data-theme="modern"] .gh-add:hover { color: var(--dim); } -html[data-theme="modern"] .am-item.danger { color: var(--err); } -html[data-theme="modern"] .sessions .se .menu { border-radius: var(--r-sm); box-shadow: var(--sh); } -html[data-theme="modern"] .sessions .se .menu-item { font-family: var(--sans); } -html[data-theme="modern"] .sessions .se .rename-input { border-radius: var(--r-xs); font-family: var(--sans); } -html[data-theme="modern"] .sessions .se .kebab { border-radius: var(--r-xs); } -html[data-theme="modern"] .sessions .se .btn-confirm, -html[data-theme="modern"] .sessions .se .btn-cancel { border-radius: var(--r-xs); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .btn-new-ws { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .theme-opt { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .gh-rename { border-radius: var(--r-xs); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .gh.sel { border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .gh-add { color: var(--faint); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .gh-add:hover { color: var(--dim); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .am-item.danger { color: var(--err); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .sessions .se .menu { border-radius: var(--r-sm); box-shadow: var(--sh); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .sessions .se .menu-item { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .sessions .se .rename-input { border-radius: var(--r-xs); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .sessions .se .kebab { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .sessions .se .btn-confirm, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .sessions .se .btn-cancel { border-radius: var(--r-xs); font-family: var(--sans); } /* ---- Chat content: code blocks, inline code, thinking, tool chips ---- */ -html[data-theme="modern"] .md .code-block-container { border-radius: var(--r-sm); box-shadow: var(--shc); } -html[data-theme="modern"] .md .diff-wrap { border-radius: var(--r-sm); box-shadow: var(--shc); } -html[data-theme="modern"] .md :not(pre) > code, -html[data-theme="modern"] .md .inline-code { border-radius: var(--r-xs); } -html[data-theme="modern"] .mthink .h, -html[data-theme="modern"] .mthink .c { font-family: var(--sans); } -html[data-theme="modern"] .box .chip { border-radius: var(--r-xs); font-family: var(--mono); } -html[data-theme="modern"] .box.err { border-color: color-mix(in srgb, var(--err) 25%, var(--bg)); } -html[data-theme="modern"] .box.err .bh { background: color-mix(in srgb, var(--err) 4%, var(--bg)); } -html[data-theme="modern"] .box.err .bh:hover { background: color-mix(in srgb, var(--err) 7%, var(--bg)); } -html[data-theme="modern"] .newmsg-pill { font-family: var(--sans); } -html[data-theme="modern"] .seg-btn { font-family: var(--sans); } -html[data-theme="modern"] .files-back { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .md .code-block-container { border-radius: var(--r-sm); box-shadow: var(--shc); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .md .diff-wrap { border-radius: var(--r-sm); box-shadow: var(--shc); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .md :not(pre) > code, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .md .inline-code { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .mthink .h, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .mthink .c { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box .chip { border-radius: var(--r-xs); font-family: var(--mono); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.err { border-color: color-mix(in srgb, var(--err) 25%, var(--bg)); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.err .bh { background: color-mix(in srgb, var(--err) 4%, var(--bg)); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .box.err .bh:hover { background: color-mix(in srgb, var(--err) 7%, var(--bg)); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .newmsg-pill { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .seg-btn { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .files-back { font-family: var(--sans); } /* ---- Approval & question cards (match the rounded .box tool cards) ---- */ -html[data-theme="modern"] .appr { border-radius: var(--r-md); box-shadow: var(--shc); overflow: hidden; } -html[data-theme="modern"] .appr .ah { border-radius: var(--r-md) var(--r-md) 0 0; } -html[data-theme="modern"] .kbtn { font-family: var(--sans); transition: background-color 0.18s ease, color 0.18s ease; } -html[data-theme="modern"] .feedback-ta { font-family: var(--sans); border-radius: var(--r-sm); } -html[data-theme="modern"] .shell-cmd, -html[data-theme="modern"] .shell-danger { border-radius: var(--r-sm); } -html[data-theme="modern"] .aw, -html[data-theme="modern"] .abadge, -html[data-theme="modern"] .chip-label { border-radius: var(--r-xs); } -html[data-theme="modern"] .qcard { border-radius: var(--r-md); box-shadow: var(--shc); overflow: hidden; } -html[data-theme="modern"] .qcard .qh { border-radius: var(--r-md) var(--r-md) 0 0; } -html[data-theme="modern"] .qopt { border-radius: var(--r-sm); } -html[data-theme="modern"] .qbtn { font-family: var(--sans); border-radius: var(--r-sm); } -html[data-theme="modern"] .other-input { font-family: var(--sans); } -html[data-theme="modern"] .qnav { font-family: var(--sans); border-radius: var(--r-xs); } -html[data-theme="modern"] .qheader-chip { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .appr { border-radius: var(--r-md); box-shadow: var(--shc); overflow: hidden; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .appr .ah { border-radius: var(--r-md) var(--r-md) 0 0; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .kbtn { font-family: var(--sans); transition: background-color 0.18s ease, color 0.18s ease; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .feedback-ta { font-family: var(--sans); border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .shell-cmd, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .shell-danger { border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .aw, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .abadge, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .chip-label { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .qcard { border-radius: var(--r-md); box-shadow: var(--shc); overflow: hidden; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .qcard .qh { border-radius: var(--r-md) var(--r-md) 0 0; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .qopt { border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .qbtn { font-family: var(--sans); border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .other-input { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .qnav { font-family: var(--sans); border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .qheader-chip { border-radius: var(--r-xs); } /* ---- Composer: permission pill, model menu, queue strip ---- */ -html[data-theme="modern"] .perm-pill { font-family: var(--sans); border-radius: var(--r-sm); } -html[data-theme="modern"] .md-row { font-family: var(--sans); } -html[data-theme="modern"] .queue-item, -html[data-theme="modern"] .queue-text { font-family: var(--sans); } -html[data-theme="modern"] .compact-chip { border-radius: var(--r-xs); } -html[data-theme="modern"] .att-thumb { border-radius: 5px; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .perm-pill { font-family: var(--sans); border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .md-row { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .queue-item, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .queue-text { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .compact-chip { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .att-thumb { border-radius: 5px; } /* ---- Floating menus + status line ---- */ -html[data-theme="modern"] .slash-menu { border-radius: var(--r-md); box-shadow: var(--sh); } -html[data-theme="modern"] .slash-desc { font-family: var(--sans); } -html[data-theme="modern"] .mention-menu { border-radius: var(--r-md); box-shadow: var(--sh); } -html[data-theme="modern"] .mention-state { font-family: var(--sans); } -html[data-theme="modern"] .statusline .popover { +:is(html[data-theme="modern"], html[data-theme="kimi"]) .slash-menu { border-radius: var(--r-md); box-shadow: var(--sh); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .slash-desc { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .mention-menu { border-radius: var(--r-md); box-shadow: var(--sh); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .mention-state { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .statusline .popover { border-radius: var(--r-md); border-top: 1px solid var(--line); box-shadow: var(--sh); } -html[data-theme="modern"] .statusline .pop-row { font-family: var(--sans); border-radius: var(--r-xs); } -html[data-theme="modern"] .interrupt-btn { border-radius: var(--r-xs); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .statusline .pop-row { font-family: var(--sans); border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .interrupt-btn { border-radius: var(--r-xs); font-family: var(--sans); } /* ---- Dialog shell (ModelPicker / Login / NewSession / Sessions / AddWorkspace / ProviderManager / StatusPanel share these classes). UI copy goes sans; per-element mono below keeps code-like content (paths, model ids, device codes) terminal-crisp. ---- */ -html[data-theme="modern"] .backdrop .dialog { +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .dialog { font-family: var(--sans); border-top: 1px solid var(--line); box-shadow: 0 8px 30px rgba(20, 23, 28, 0.16); } @media (min-width: 641px) { /* Desktop only: ≤640px keeps the bottom-sheet 16px top corners. */ - html[data-theme="modern"] .backdrop .dialog { border-radius: var(--r-lg); } - html[data-theme="modern"] .backdrop .footer-hint { border-radius: 0 0 var(--r-lg) var(--r-lg); } + :is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .dialog { border-radius: var(--r-lg); } + :is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .footer-hint { border-radius: 0 0 var(--r-lg) var(--r-lg); } } -html[data-theme="modern"] .backdrop .act-btn { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .backdrop .search-input { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .backdrop .finput { border-radius: var(--r-sm); } -html[data-theme="modern"] .backdrop .recent-item { border-radius: var(--r-sm); } -html[data-theme="modern"] .backdrop .folder-row { +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .act-btn { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .search-input { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .finput { border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .recent-item { border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .folder-row { margin: 1px 6px; width: calc(100% - 12px); border-radius: var(--r-sm); } -html[data-theme="modern"] .backdrop .session-row { margin: 1px 6px; border-radius: var(--r-sm); } -html[data-theme="modern"] .backdrop .session-row.is-active { box-shadow: inset 0 0 0 1px var(--bd); } -html[data-theme="modern"] .backdrop .up-btn, -html[data-theme="modern"] .backdrop .crumb { border-radius: var(--r-xs); } -html[data-theme="modern"] .backdrop .paste-input { border-radius: var(--r-sm); } -html[data-theme="modern"] .backdrop .paste-add { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .backdrop .model-row { font-family: var(--mono); } /* model ids */ -html[data-theme="modern"] .backdrop .caps { border-radius: var(--r-xs); } -html[data-theme="modern"] .backdrop .dc-code-wrap { border-radius: var(--r-md); } -html[data-theme="modern"] .backdrop .dc-uri-btn { border-radius: var(--r-sm); } -html[data-theme="modern"] .backdrop .dc-copy-btn { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .backdrop .dc-countdown { font-family: var(--mono); } /* mm:ss timer */ -html[data-theme="modern"] .backdrop .add-btn-oauth, -html[data-theme="modern"] .backdrop .add-btn { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .backdrop .prov-key-state { border-radius: var(--r-xs); } -html[data-theme="modern"] .backdrop .bar { background: var(--line); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .session-row { margin: 1px 6px; border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .session-row.is-active { box-shadow: inset 0 0 0 1px var(--bd); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .up-btn, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .crumb { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .paste-input { border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .paste-add { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .model-row { font-family: var(--mono); } /* model ids */ +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .caps { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .dc-code-wrap { border-radius: var(--r-md); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .dc-uri-btn { border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .dc-copy-btn { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .dc-countdown { font-family: var(--mono); } /* mm:ss timer */ +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .add-btn-oauth, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .add-btn { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .prov-key-state { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .backdrop .bar { background: var(--line); } /* ---- Files / diff / tasks panes ---- */ -html[data-theme="modern"] .back-btn { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .changes-pane .empty-state { font-family: var(--sans); } -html[data-theme="modern"] .br-label, -html[data-theme="modern"] .empty-head { font-family: var(--sans); } -html[data-theme="modern"] .change-count { font-family: var(--sans); border-radius: 999px; } -html[data-theme="modern"] .ch-row, -html[data-theme="modern"] .ct-row { +:is(html[data-theme="modern"], html[data-theme="kimi"]) .back-btn { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .changes-pane .empty-state { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .br-label, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .empty-head { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .change-count { font-family: var(--sans); border-radius: 999px; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ch-row, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ct-row { margin: 1px 6px; width: calc(100% - 12px); border-radius: var(--r-sm); } -html[data-theme="modern"] .changes-pane .badge, -html[data-theme="modern"] .changed-tree .badge { border-radius: var(--r-xs); } -html[data-theme="modern"] .ft-row { margin: 1px 6px; border-radius: var(--r-sm); } -html[data-theme="modern"] .ft-row.selected { box-shadow: inset 0 0 0 1px var(--bd); } -html[data-theme="modern"] .ft-badge { border-radius: var(--r-xs); } -html[data-theme="modern"] .ft-loading, -html[data-theme="modern"] .ft-empty { font-family: var(--sans); } -html[data-theme="modern"] .fp-copy { font-family: var(--sans); border-radius: var(--r-sm); } -html[data-theme="modern"] .fp-empty, -html[data-theme="modern"] .fp-loading { font-family: var(--sans); } -html[data-theme="modern"] .fp-binary-card { border-radius: var(--r-md); box-shadow: var(--shc); } -html[data-theme="modern"] .fp-binary-label { font-family: var(--sans); } -html[data-theme="modern"] .fp-image { border-radius: var(--r-sm); box-shadow: var(--shc); } -html[data-theme="modern"] .tp-out { border-radius: var(--r-sm); font-family: var(--mono); } /* command output */ -html[data-theme="modern"] .tp-stop { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .tp-kind { border-radius: var(--r-xs); } -html[data-theme="modern"] .tabs.mobile .tb { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .changes-pane .badge, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .changed-tree .badge { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ft-row { margin: 1px 6px; border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ft-row.selected { box-shadow: inset 0 0 0 1px var(--bd); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ft-badge { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ft-loading, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ft-empty { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .fp-copy { font-family: var(--sans); border-radius: var(--r-sm); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .fp-empty, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .fp-loading { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .fp-binary-card { border-radius: var(--r-md); box-shadow: var(--shc); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .fp-binary-label { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .fp-image { border-radius: var(--r-sm); box-shadow: var(--shc); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .tp-out { border-radius: var(--r-sm); font-family: var(--mono); } /* command output */ +:is(html[data-theme="modern"], html[data-theme="kimi"]) .tp-stop { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .tp-kind { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .tabs.mobile .tb { font-family: var(--sans); } /* ---- Warning toasts: route the error reds through the token system ---- */ -html[data-theme="modern"] .toast.err { border-color: color-mix(in srgb, var(--err) 35%, transparent); } -html[data-theme="modern"] .toast.err .dot { background: var(--err); } -html[data-theme="modern"] .toast.err .msg { color: var(--err); } -html[data-theme="modern"] .toast .x { border-radius: var(--r-xs); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .toast.err { border-color: color-mix(in srgb, var(--err) 35%, transparent); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .toast.err .dot { background: var(--err); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .toast.err .msg { color: var(--err); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .toast .x { border-radius: var(--r-xs); } /* ---- Todo card (rounded + shadow under Modern) ---- */ -html[data-theme="modern"] .todo-card { +:is(html[data-theme="modern"], html[data-theme="kimi"]) .todo-card { border-radius: var(--r-sm); box-shadow: var(--shc); } -html[data-theme="modern"] .tc-head { font-family: var(--sans); } -html[data-theme="modern"] .tc-title { font-weight: 600; } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .tc-head { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .tc-title { font-weight: 600; } /* ---- Context ring (softer track under Modern) ---- */ -html[data-theme="modern"] .ctx-ring-track { +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ctx-ring-track { stroke: var(--faint); } /* ---- Misc chrome ---- */ -html[data-theme="modern"] .lang-switch { border-radius: var(--r-sm); font-family: var(--sans); } -html[data-theme="modern"] .ob-seg-btn { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .lang-switch { border-radius: var(--r-sm); font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .ob-seg-btn { font-family: var(--sans); } /* ---- Mobile surfaces (bottom sheets, top bar, switcher) ---- */ -html[data-theme="modern"] .sheet-panel { font-family: var(--sans); } -html[data-theme="modern"] .topbar .tb-path { font-family: var(--sans); } -html[data-theme="modern"] .newrow { font-family: var(--sans); } -html[data-theme="modern"] .kmenu { border-radius: var(--r-md); box-shadow: var(--sh); } -html[data-theme="modern"] .kitem { font-family: var(--sans); } -html[data-theme="modern"] .mlist .srow { +:is(html[data-theme="modern"], html[data-theme="kimi"]) .sheet-panel { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .topbar .tb-path { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .newrow { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .kmenu { border-radius: var(--r-md); box-shadow: var(--sh); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .kitem { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .mlist .srow { margin: 1px 8px; border-radius: var(--r-sm); border-bottom: none; @@ -713,10 +717,10 @@ html[data-theme="modern"] .mlist .srow { sheet's --m-indent alignment line (under the workspace name). */ padding: 12px calc(var(--m-pad, 16px) - 8px) 12px calc(var(--m-indent, 39px) - 8px); } -html[data-theme="modern"] .mlist .srow.cur { box-shadow: inset 0 0 0 1px var(--bd); } -html[data-theme="modern"] .srow, -html[data-theme="modern"] .srow-sub, -html[data-theme="modern"] .srow-val { font-family: var(--sans); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .mlist .srow.cur { box-shadow: inset 0 0 0 1px var(--bd); } +:is(html[data-theme="modern"], html[data-theme="kimi"]) .srow, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .srow-sub, +:is(html[data-theme="modern"], html[data-theme="kimi"]) .srow-val { font-family: var(--sans); } /* ---- Modern keyframes (shared by component-scoped rules) ---- */ @keyframes kimi-bubble-in { @@ -739,13 +743,251 @@ html[data-theme="modern"] .srow-val { font-family: var(--sans); } /* ---- Reduced-motion: disable ALL Modern entrance/micro animations ---- */ @media (prefers-reduced-motion: reduce) { - html[data-theme="modern"] * { + :is(html[data-theme="modern"], html[data-theme="kimi"]) * { animation-duration: 0.001ms !important; animation-delay: 0ms !important; transition-duration: 0.001ms !important; } } +/* =========================================================================== + KIMI THEME (html[data-theme="kimi"]) + The official Kimi design language ("Quiet Utility") — values from + kimi-design-skill tokens.json v0.2.0. It shares the de-terminalization + rules above with Modern (the :is(...) selectors); everything below is the + delta: the full token palette (interaction accent = kimiDark — black in + light, white in dark; kimiBlue stays reserved for brand/data-viz), flat + cards (no hover lift; shadows only on the composer + floating menus), gray + user bubbles, and the PingFang / Geist Mono type ramp. + These token blocks intentionally sit AFTER the html[data-accent] rules: + equal specificity + later source order means the kimi palette wins over any + persisted accent choice (the accent picker is hidden while kimi is active). + =========================================================================== */ +html[data-theme="kimi"] { + /* labels (color.labels.*) — Kimi has no separate body tone; hierarchy + comes from size/weight, so --ink == --text by design. */ + --ink: rgba(0, 0, 0, 0.9); + --text: rgba(0, 0, 0, 0.9); + --dim: rgba(0, 0, 0, 0.6); + --muted: rgba(0, 0, 0, 0.45); + --faint: rgba(0, 0, 0, 0.3); + /* surfaces + separators */ + --line: rgba(0, 0, 0, 0.13); /* color.separator.s1 */ + --line2: rgba(0, 0, 0, 0.05); /* color.fills.f2 (no subtler separator token) */ + --panel: #f9fbfc; /* color.background.groundPc */ + --panel2: #f5f5f5; /* color.background.secondary */ + --bg: #ffffff; /* color.background.primary */ + --canvas: #f9fbfc; + /* interaction accent = color.brand.kimiDark (NOT kimiBlue) */ + --blue: rgba(0, 0, 0, 0.9); + --blue2: rgba(37, 37, 37, 1); + --soft: rgba(0, 0, 0, 0.05); /* color.fills.f2: selected = fill, not a blue tint */ + --bd: transparent; /* no ring on selected rows (surface over stroke) */ + --logo: rgba(0, 0, 0, 0.9); /* logo-system.md: the mark uses labels.primary */ + /* user bubble = color.others.bubbleGrayPc — gray, never blue; border + matches the fill so the bubble reads borderless without layout shift */ + --bluebg: #f5f5f5; + --blueln: #f5f5f5; + /* status (color.status.*) */ + --ok: #16c456; + --warn: #ff9500; + --err: #ff3849; + --hover: rgba(0, 0, 0, 0.03); /* color.fills.f1 */ + /* radius scale snaps to radius.xxs/sm/lg/xl = 4/8/12/16 */ + --r-xs: 4px; + /* flat by default; --sh (floating menus only) = effect.shadow.small */ + --sh: 0 4px 16.4px 0 rgba(0, 0, 0, 0.1); + --shc: none; + /* type: PingFang for UI, Geist Mono for code (JetBrains Mono is the + bundled fallback; no font files are added for this theme) */ + --sans: "PingFang SC", -apple-system, BlinkMacSystemFont, "Segoe UI", + "Microsoft YaHei", "Noto Sans SC", "Helvetica Neue", Arial, sans-serif; + --mono: "Geist Mono", "JetBrains Mono Variable", "JetBrains Mono", + ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; +} + +/* Kimi × dark (same three-block pattern as Modern: explicit choice here, + system preference via the media query below). */ +html[data-color-scheme="dark"][data-theme="kimi"] { + --ink: rgba(255, 255, 255, 0.84); + --text: rgba(255, 255, 255, 0.84); + --dim: rgba(255, 255, 255, 0.56); + --muted: rgba(255, 255, 255, 0.42); + --faint: rgba(255, 255, 255, 0.26); + --line: rgba(255, 255, 255, 0.12); + --line2: rgba(255, 255, 255, 0.1); + --panel: #161717; + --panel2: #1f1f1f; + --bg: #121212; + --canvas: #161717; + --blue: rgba(255, 255, 255, 0.84); + --blue2: rgba(255, 255, 255, 0.848); + --soft: rgba(255, 255, 255, 0.1); + --bd: transparent; + --logo: rgba(255, 255, 255, 0.84); + --bluebg: #292929; + --blueln: #292929; + --ok: #16c456; + --warn: #ff9f0a; + --err: #ff4756; + --hover: rgba(255, 255, 255, 0.05); +} +@media (prefers-color-scheme: dark) { + html[data-color-scheme="system"][data-theme="kimi"] { + --ink: rgba(255, 255, 255, 0.84); + --text: rgba(255, 255, 255, 0.84); + --dim: rgba(255, 255, 255, 0.56); + --muted: rgba(255, 255, 255, 0.42); + --faint: rgba(255, 255, 255, 0.26); + --line: rgba(255, 255, 255, 0.12); + --line2: rgba(255, 255, 255, 0.1); + --panel: #161717; + --panel2: #1f1f1f; + --bg: #121212; + --canvas: #161717; + --blue: rgba(255, 255, 255, 0.84); + --blue2: rgba(255, 255, 255, 0.848); + --soft: rgba(255, 255, 255, 0.1); + --bd: transparent; + --logo: rgba(255, 255, 255, 0.84); + --bluebg: #292929; + --blueln: #292929; + --ok: #16c456; + --warn: #ff9f0a; + --err: #ff4756; + --hover: rgba(255, 255, 255, 0.05); + } +} + +/* ---- Kimi deltas over the shared Modern structure ------------------------ */ + +/* Composer: the one card that keeps a shadow (effect.shadow.inputDefault), + with the chat-input radius documented in web-best-practices §8. */ +html[data-theme="kimi"] .composer-card { + border-radius: 20px; + box-shadow: 0 5px 16px -4px rgba(0, 0, 0, 0.07); +} + +/* Cards stay flat: no hover lift, no shadow (Quiet Utility / card.md). */ +html[data-theme="kimi"] .box:hover { + transform: none; + box-shadow: none; +} + +/* Dialogs: the mask isolates — no drop shadow, no border (modal.md). + The desktop scope keeps the mobile bottom sheet's top hairline, which the + sheet needs to stay visible against a near-black page in dark mode. */ +html[data-theme="kimi"] .backdrop .dialog { + box-shadow: none; +} +@media (min-width: 641px) { + html[data-theme="kimi"] .backdrop .dialog { + border-top: none; + } +} + +/* The send button is kimiDark fill only — no decorative shadow (§5). */ +html[data-theme="kimi"] .send { + box-shadow: none; +} + +/* Tool-result checkmarks appear on every tool call — per the animation + frequency rule (100+×/day → no animation), and the shared overshoot + keyframe is a bounce, which animation.md forbids. */ +html[data-theme="kimi"] .box .ok, +html[data-theme="kimi"] .box .er { + animation: none; +} + +/* User bubble corners on the token scale (radius.xl / radius.xxs). */ +html[data-theme="kimi"] .u-bub { + border-radius: 16px 16px 4px 16px; +} + +/* Toasts (toast.md): constant dark surface (color.mask.toastPc), white text, + no border/shadow — the type cue lives in the dot, not the text color. */ +html[data-theme="kimi"] .toast { + background: #2b2b2b; + border: none; + border-radius: 12px; + box-shadow: none; +} +html[data-theme="kimi"] .toast .msg, +html[data-theme="kimi"] .toast.err .msg { + color: #ffffff; +} +html[data-theme="kimi"] .toast .dot { + background: rgba(255, 255, 255, 0.56); +} +/* Explicit so the type cue never depends on the shared rule's specificity. */ +html[data-theme="kimi"] .toast.err .dot { + background: var(--err); +} +html[data-theme="kimi"] .toast .x { + color: rgba(255, 255, 255, 0.56); +} +html[data-theme="kimi"] .toast .x:hover { + color: #ffffff; + background: rgba(255, 255, 255, 0.1); +} +html[data-color-scheme="dark"][data-theme="kimi"] .toast { + background: #404040; /* color.mask.toastPc dark */ +} + +/* Elevated dark surfaces step up to color.background.tertiary (#292929): + floating menus and dialogs separate from the #121212 page by surface + contrast, not shadow (principles §11). */ +html[data-color-scheme="dark"][data-theme="kimi"] .slash-menu, +html[data-color-scheme="dark"][data-theme="kimi"] .mention-menu, +html[data-color-scheme="dark"][data-theme="kimi"] .model-dropdown, +html[data-color-scheme="dark"][data-theme="kimi"] .perm-dropdown, +html[data-color-scheme="dark"][data-theme="kimi"] .kmenu, +html[data-color-scheme="dark"][data-theme="kimi"] .acct-menu, +html[data-color-scheme="dark"][data-theme="kimi"] .sessions .se .menu, +html[data-color-scheme="dark"][data-theme="kimi"] .statusline .popover, +html[data-color-scheme="dark"][data-theme="kimi"] .backdrop .dialog { + background: #292929; +} +@media (prefers-color-scheme: dark) { + html[data-color-scheme="system"][data-theme="kimi"] .toast { + background: #404040; + } + html[data-color-scheme="system"][data-theme="kimi"] .slash-menu, + html[data-color-scheme="system"][data-theme="kimi"] .mention-menu, + html[data-color-scheme="system"][data-theme="kimi"] .model-dropdown, + html[data-color-scheme="system"][data-theme="kimi"] .perm-dropdown, + html[data-color-scheme="system"][data-theme="kimi"] .kmenu, + html[data-color-scheme="system"][data-theme="kimi"] .acct-menu, + html[data-color-scheme="system"][data-theme="kimi"] .sessions .se .menu, + html[data-color-scheme="system"][data-theme="kimi"] .statusline .popover, + html[data-color-scheme="system"][data-theme="kimi"] .backdrop .dialog { + background: #292929; + } +} + +/* Focus: kimiDark ring (principles §9) — outline, so layout never shifts. + Text fields are excluded: :focus-visible matches them on mouse focus too, + which would draw a ring around the raw textarea inside the composer card; + the caret is their focus cue. */ +html[data-theme="kimi"] :focus-visible:not(input):not(textarea):not(select):not([contenteditable="true"]) { + outline: 2px solid var(--blue); + outline-offset: 2px; +} + +/* Text selection: color.others.textSelected (a sanctioned kimiBlue use). */ +html[data-theme="kimi"] ::selection { + background: rgba(23, 131, 255, 0.2); +} +html[data-color-scheme="dark"][data-theme="kimi"] ::selection { + background: rgba(26, 136, 255, 0.2); +} +@media (prefers-color-scheme: dark) { + html[data-color-scheme="system"][data-theme="kimi"] ::selection { + background: rgba(26, 136, 255, 0.2); + } +} + + /* ---- Kimi Code logo (sidebar header): lively eyes — glance left/right + the occasional blink. The two bars are mask cutouts, so animating them moves / squishes the transparent holes (adapts to whatever's behind the logo). The