Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/web-ui-polish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

web: Polish the chat UI with Inter typography, localized labels, and tighter sidebar, composer, and menu styling.
3 changes: 2 additions & 1 deletion apps/kimi-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"check:style": "node scripts/check-style.mjs"
},
"dependencies": {
"@fontsource-variable/inter": "^5.2.8",
"@chenglou/pretext": "0.0.8",
"@fontsource-variable/inter": "5.2.8",
"@fontsource-variable/jetbrains-mono": "^5.2.8",
"@xterm/addon-fit": "^0.11.0",
"@xterm/xterm": "^6.0.0",
Expand Down
12 changes: 8 additions & 4 deletions apps/kimi-web/src/components/SessionRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,9 @@ defineExpose({ closeMenu });

.t {
color: inherit;
font-size: var(--ui-font-size);
font-weight: var(--weight-regular);
font-size: var(--text-base);
font-weight: 450;
line-height: var(--leading-tight);
flex: 1;
min-width: 0;
overflow: hidden;
Expand All @@ -353,7 +354,10 @@ defineExpose({ closeMenu });
.ts {
color: var(--color-text-faint);
font-size: var(--text-xs);
font-family: var(--font-mono);
font-family: var(--font-ui);
font-weight: 475;
font-variant-numeric: tabular-nums;
text-align: right;
}

/* Trailing action slot: time and kebab share one grid cell (grid-area:1/1).
Expand All @@ -366,7 +370,7 @@ defineExpose({ closeMenu });
display: inline-grid;
flex: none;
align-items: center;
justify-items: center;
justify-items: end;
}
.act .ts,
.act .kebab { grid-area: 1 / 1; }
Expand Down
19 changes: 17 additions & 2 deletions apps/kimi-web/src/components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import IconButton from './ui/IconButton.vue';
import Icon from './ui/Icon.vue';
import Menu from './ui/Menu.vue';
import MenuItem from './ui/MenuItem.vue';
import ShortcutKey from './ui/ShortcutKey.vue';
import { useConfirmDialog } from '../composables/useConfirmDialog';

const { t } = useI18n();
Expand Down Expand Up @@ -599,7 +600,10 @@ onBeforeUnmount(() => {
<!-- Session search — opens the Spotlight-style search dialog -->
<button class="search" type="button" @click="openSearch">
<Icon class="search-icon" name="search" />
<span class="search-input">{{ t('sidebar.searchShortcut', { shortcut: sessionSearchShortcut }) }}</span>
<span class="search-input">
<span class="search-label">{{ t('sidebar.searchPlaceholder') }}</span>
<ShortcutKey>{{ sessionSearchShortcut }}</ShortcutKey>
</span>
</button>

<!-- New chat + new workspace buttons -->
Expand Down Expand Up @@ -911,6 +915,7 @@ onBeforeUnmount(() => {
color: var(--color-text);
font-family: var(--font-ui);
font-size: var(--ui-font-size);
font-weight: var(--weight-medium);
cursor: pointer;
text-align: left;
}
Expand Down Expand Up @@ -950,15 +955,25 @@ onBeforeUnmount(() => {
flex: none;
}
.search-input {
display: inline-flex;
align-items: center;
gap: var(--space-1);
flex: 1;
min-width: 0;
color: var(--color-text);
font-family: var(--mono);
font-family: var(--font-ui);
font-size: var(--ui-font-size);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.search-input > span {
overflow: hidden;
text-overflow: ellipsis;
}
.search-label {
font-weight: var(--weight-medium);
}

/* Sessions */
.sessions {
Expand Down
9 changes: 5 additions & 4 deletions apps/kimi-web/src/components/WorkspaceGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ function onHeaderDragStart(event: DragEvent): void {

.gh-name {
font-size: var(--ui-font-size-lg);
font-weight: var(--weight-medium);
font-weight: 550;
color: var(--color-text);
flex: 1;
min-width: 0;
Expand All @@ -276,6 +276,7 @@ function onHeaderDragStart(event: DragEvent): void {
}
.gh-path {
color: var(--color-text-faint);
font-weight: 425;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
Expand Down Expand Up @@ -317,10 +318,10 @@ function onHeaderDragStart(event: DragEvent): void {
.gh-more.open { color: var(--color-text); background: var(--color-line); }

.group-empty {
padding: var(--space-1) var(--space-2) var(--space-1) calc(var(--sb-pad-x) + var(--sb-gutter) + var(--sb-gap));
padding: var(--space-1) var(--space-2) var(--space-1) calc(var(--sb-pad-x) - var(--space-2) + var(--sb-gutter) + var(--sb-gap));
font-size: var(--text-xs);
color: var(--color-text-faint);
font-family: var(--font-mono);
font-family: var(--font-ui);
}
/* Show-more / show-less — a session-row-shaped compact list control (§07). The
empty lead slot mirrors a session row's status gutter, so the label text lands
Expand All @@ -338,7 +339,7 @@ function onHeaderDragStart(event: DragEvent): void {
border: none;
border-radius: var(--radius-md);
background: transparent;
color: var(--color-text);
color: var(--color-text-muted);
font-family: var(--font-ui);
font-size: var(--text-xs);
text-align: left;
Expand Down
19 changes: 10 additions & 9 deletions apps/kimi-web/src/components/chat/ApprovalCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Badge from '../ui/Badge.vue';
import Button from '../ui/Button.vue';
import IconButton from '../ui/IconButton.vue';
import Icon from '../ui/Icon.vue';
import ShortcutKey from '../ui/ShortcutKey.vue';
import Tooltip from '../ui/Tooltip.vue';

const props = defineProps<{
Expand Down Expand Up @@ -314,20 +315,20 @@ onUnmounted(() => document.removeEventListener('keydown', handleKeydown));
:loading="pendingAction === `option:${opt.label}`"
:disabled="busy"
@click="approveOption(opt.label)"
>{{ opt.label }}<span class="k">[{{ i + 1 }}]</span></Button>
>{{ opt.label }}<ShortcutKey class="k">[{{ i + 1 }}]</ShortcutKey></Button>
</Tooltip>
</template>
<Button v-else class="kbtn" size="sm" variant="primary" :loading="pendingAction === 'approvePlan'" :disabled="busy" @click="approvePlan">{{ t('approval.approvePlan') }}<span class="k">[1]</span></Button>
<Button class="kbtn" size="sm" variant="secondary" :disabled="busy" @click="revisePlan">{{ t('approval.revise') }}<span v-if="planReview.options.length === 0" class="k">[2]</span></Button>
<Button class="kbtn" size="sm" variant="danger-soft" :loading="pendingAction === 'rejectAndExit'" :disabled="busy" @click="rejectAndExitPlan">{{ t('approval.rejectAndExit') }}<span v-if="planReview.options.length === 0" class="k">[3]</span></Button>
<Button v-else class="kbtn" size="sm" variant="primary" :loading="pendingAction === 'approvePlan'" :disabled="busy" @click="approvePlan">{{ t('approval.approvePlan') }}<ShortcutKey class="k">[1]</ShortcutKey></Button>
<Button class="kbtn" size="sm" variant="secondary" :disabled="busy" @click="revisePlan">{{ t('approval.revise') }}<ShortcutKey v-if="planReview.options.length === 0" class="k">[2]</ShortcutKey></Button>
<Button class="kbtn" size="sm" variant="danger-soft" :loading="pendingAction === 'rejectAndExit'" :disabled="busy" @click="rejectAndExitPlan">{{ t('approval.rejectAndExit') }}<ShortcutKey v-if="planReview.options.length === 0" class="k">[3]</ShortcutKey></Button>
</div>

<!-- default actions row -->
<div v-else class="abtn">
<Button class="kbtn" size="sm" variant="primary" :loading="pendingAction === 'approve'" :disabled="busy" @click="approve">{{ t('approval.approve') }}<span class="k">[1]</span></Button>
<Button class="kbtn" size="sm" variant="secondary" :loading="pendingAction === 'approveSession'" :disabled="busy" @click="approveSession">{{ t('approval.approveSession') }}<span class="k">[2]</span></Button>
<Button class="kbtn" size="sm" variant="secondary" :loading="pendingAction === 'reject'" :disabled="busy" @click="reject">{{ t('approval.reject') }}<span class="k">[3]</span></Button>
<Button class="kbtn" size="sm" variant="secondary" :disabled="busy" @click="openFeedback">{{ t('approval.feedback') }}<span class="k">[4]</span></Button>
<Button class="kbtn" size="sm" variant="primary" :loading="pendingAction === 'approve'" :disabled="busy" @click="approve">{{ t('approval.approve') }}<ShortcutKey class="k">[1]</ShortcutKey></Button>
<Button class="kbtn" size="sm" variant="secondary" :loading="pendingAction === 'approveSession'" :disabled="busy" @click="approveSession">{{ t('approval.approveSession') }}<ShortcutKey class="k">[2]</ShortcutKey></Button>
<Button class="kbtn" size="sm" variant="secondary" :loading="pendingAction === 'reject'" :disabled="busy" @click="reject">{{ t('approval.reject') }}<ShortcutKey class="k">[3]</ShortcutKey></Button>
<Button class="kbtn" size="sm" variant="secondary" :disabled="busy" @click="openFeedback">{{ t('approval.feedback') }}<ShortcutKey class="k">[4]</ShortcutKey></Button>
</div>
</template>
</Card>
Expand Down Expand Up @@ -554,7 +555,7 @@ onUnmounted(() => document.removeEventListener('keydown', handleKeydown));
width: 100%;
}
.plan-actions { flex-wrap: wrap; }
.k { margin-left: var(--space-2); font: var(--text-xs) var(--font-mono); opacity: .7; }
.k { opacity: .75; }

/* =========================================================================
MOBILE (≤640px): the card spans the full chat column, inner previews scroll
Expand Down
1 change: 1 addition & 0 deletions apps/kimi-web/src/components/chat/ChatDock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ defineExpose({ loadForEdit, loadAttachmentsForEdit, focus });
:plan-mode="planMode"
:swarm-mode="swarmMode"
:goal-mode="goalMode"
:goal="goal"
:activation-badges="activationBadges"
:models="models"
:starred-ids="starredIds"
Expand Down
24 changes: 22 additions & 2 deletions apps/kimi-web/src/components/chat/ChatHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,25 @@ const behind = computed(() => props.behind ?? 0);
const adds = computed(() => props.gitDiffStats?.totalAdditions ?? 0);
const dels = computed(() => props.gitDiffStats?.totalDeletions ?? 0);
const hasLineStats = computed(() => adds.value > 0 || dels.value > 0);
const PR_STATE_LABEL_KEYS: Record<string, string> = {
open: 'header.prStatusOpen',
closed: 'header.prStatusClosed',
merged: 'header.prStatusMerged',
draft: 'header.prStatusDraft',
};

function normalizedPrState(state: string): string {
return state.trim().toLowerCase().replaceAll('_', '-');
}

function prStateClass(state: string): string {
const stateClass = normalizedPrState(state);
return PR_STATE_LABEL_KEYS[stateClass] ? `pr-${stateClass}` : 'pr-unknown';
}

function prStateLabel(state: string): string {
return t(PR_STATE_LABEL_KEYS[normalizedPrState(state)] ?? 'header.prStatusUnknown');
}

// ---------------------------------------------------------------------------
// More-menu (kebab dropdown)
Expand Down Expand Up @@ -295,11 +314,11 @@ async function startArchive(): Promise<void> {
v-if="pr"
type="button"
class="ch-pill ch-pr"
:class="`pr-${pr.state}`"
:class="prStateClass(pr.state)"
@click="pr && emit('openPr', pr.url)"
>
<Icon name="git-pull-request" size="sm" />
<span>PR #{{ pr.number }} · {{ pr.state }}</span>
<span>PR #{{ pr.number }} · {{ prStateLabel(pr.state) }}</span>
</button>

</header>
Expand Down Expand Up @@ -420,6 +439,7 @@ async function startArchive(): Promise<void> {
.ch-pr.pr-merged { color: var(--color-done); border-color: var(--color-done-bd); background: var(--color-done-soft); }
.ch-pr.pr-closed { color: var(--color-danger); border-color: var(--color-danger-bd); background: var(--color-danger-soft); }
.ch-pr.pr-draft { color: var(--color-text-muted); border-color: var(--color-line-strong); background: var(--color-surface-sunken); }
.ch-pr.pr-unknown { color: var(--color-text-muted); border-color: var(--color-line-strong); background: var(--color-surface-sunken); }
.ch-pr:hover { border-color: var(--color-line-strong); }

/* Fixed more-menu, anchored to the kebab trigger. Surface / items come from
Expand Down
Loading
Loading