-
Notifications
You must be signed in to change notification settings - Fork 944
fix(kimi-web): full-width chat column, Ctrl+Enter steering, code block line numbers #2369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b6d88b5
7e44ece
6edc9c0
0d9835e
c1eab79
d78caaf
b059278
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| web: Fix code block line numbers drifting out of sync with the code lines in plain-text code blocks. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| web: Change the steer-into-running-turn shortcut to Ctrl+Enter / Cmd+Enter; the previously advertised Ctrl+S could open the browser's Save Page dialog. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| web: Let the chat column follow the window width instead of capping it at a fixed reading width, removing the wide empty margins on large screens. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,16 +29,17 @@ const emit = defineEmits<{ | |
|
|
||
| const { t } = useI18n(); | ||
|
|
||
| // Width the rail needs beside the reading column once its labels are fully | ||
| // revealed on hover/focus: 3px bar + 10px gap + 220px label, plus a small | ||
| // buffer so the text never kisses the container edge. Kept in sync with the | ||
| // `.toc-bar` / `.toc-label` rules below. | ||
| // Width the rail needs to its left once its labels are fully revealed on | ||
| // hover/focus: 3px bar + 10px gap + 220px label, plus a small buffer so the | ||
| // text never kisses the container edge. Kept in sync with the `.toc-bar` / | ||
| // `.toc-label` rules below. | ||
| const EXPANDED_WIDTH = 240; | ||
|
|
||
| const navRef = ref<HTMLElement | null>(null); | ||
| // Whether the rail, once expanded, fits within the room to the right of the | ||
| // reading column. When it would overflow, we hide the outline entirely rather | ||
| // than showing a panel that gets clipped by the container edge. | ||
| // Whether the rail, once expanded, fits within the room to its left (the | ||
| // labels reveal leftward over the content). When it would overflow, we hide | ||
| // the outline entirely rather than showing a panel that gets clipped by the | ||
| // container edge. | ||
| const fits = ref(true); | ||
|
|
||
| let observer: ResizeObserver | null = null; | ||
|
|
@@ -47,9 +48,9 @@ function measure(): void { | |
| const nav = navRef.value; | ||
| const parent = nav?.offsetParent as HTMLElement | null; | ||
| if (!nav || !parent) return; | ||
| const navLeft = nav.getBoundingClientRect().left; | ||
| const parentRight = parent.getBoundingClientRect().right; | ||
| fits.value = parentRight - navLeft >= EXPANDED_WIDTH; | ||
| const navRight = nav.getBoundingClientRect().right; | ||
| const parentLeft = parent.getBoundingClientRect().left; | ||
| fits.value = navRight - parentLeft >= EXPANDED_WIDTH; | ||
| } | ||
|
|
||
| // The outline is only useful once there is something to navigate, and it never | ||
|
|
@@ -93,8 +94,9 @@ onBeforeUnmount(() => { | |
|
|
||
| <template> | ||
| <!-- Conversation outline: a vertical list of short bars (one per user query), | ||
| vertically centered beside the chat. Hovering the list enlarges the bars | ||
| and reveals each query's title to the right, making rows easy to click. --> | ||
| vertically centered at the pane's right edge. Hovering the list enlarges | ||
| the bars and reveals each query's title to the left, over the content, | ||
| making rows easy to click. --> | ||
| <nav | ||
| v-if="visible" | ||
| ref="navRef" | ||
|
|
@@ -125,38 +127,18 @@ onBeforeUnmount(() => { | |
| z-index: var(--z-sticky); | ||
| top: 50%; | ||
| transform: translateY(-50%); | ||
| /* Anchor to the reading-column edge, the rail's original position. Tables | ||
| that grow past it (up to --p-table-max) temporarily hide the rail via the | ||
| occlusion hit-test in ConversationPane, so proximity is safe again. | ||
| The cqi cap keeps the rail inside narrow containers. */ | ||
| --toc-content-max: min( | ||
| var(--p-content-max), | ||
| calc(100cqi - var(--space-5) - var(--space-5)) | ||
| ); | ||
| left: calc(50% + (var(--toc-content-max) / 2) + 14px); | ||
| /* Anchor to the pane's right edge, just inside the panes scrollbar gutter. | ||
| The reading column spans the full pane width, so that edge is the only | ||
| stable outside position left; labels reveal leftward over the content. | ||
| Tables that reach the rail temporarily hide it via the occlusion hit-test | ||
| in ConversationPane, so the overlap is safe. */ | ||
| right: calc(var(--panes-scrollbar-width, 0px) + var(--space-2)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
With the rail now anchored inside the pane edge, its existing invisible hover bridge ( Useful? React with 👍 / 👎.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in d78caaf: the hover bridge is removed entirely — only the actual outline rows receive pointer events now, so clicks and text selection along the pane's right edge reach the messages underneath. The expanded rows are wide enough targets once the panel is open, so the bridge was no longer pulling its weight. |
||
| display: flex; | ||
| flex-direction: column; | ||
| justify-content: center; | ||
| opacity: 0.5; | ||
| transition: opacity var(--duration-base) var(--ease-out); | ||
| } | ||
| /* Invisible hover bridge: the collapsed rail is only a few px wide, so this | ||
| extends the hover target on both sides to make the outline easy to open and | ||
| forgiving to stay within. The left side covers only the 14px gap to the | ||
| content edge — a table wide enough to reach past the gap also covers the | ||
| bar, which hides the rail (pointer-events: none) before the bridge can | ||
| steal its events. Kept at z-index 0 so it sits behind the rows (which are | ||
| raised to z-index 1) — otherwise the bridge, as a positioned pseudo-element, | ||
| paints above the in-flow rows and swallows their clicks. */ | ||
| .conversation-toc::before { | ||
| content: ""; | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| left: -14px; | ||
| right: -48px; | ||
| z-index: 0; | ||
| } | ||
| .conversation-toc:hover, | ||
| .conversation-toc:focus-within { opacity: 1; } | ||
|
|
||
|
|
@@ -175,6 +157,8 @@ onBeforeUnmount(() => { | |
|
|
||
| .toc-row { | ||
| display: flex; | ||
| /* Bar rightmost (its x stays pinned at the pane edge), label extends left. */ | ||
| flex-direction: row-reverse; | ||
| align-items: center; | ||
| gap: 10px; | ||
| height: 18px; | ||
|
|
@@ -213,20 +197,31 @@ onBeforeUnmount(() => { | |
| color var(--duration-fast) var(--ease-out); | ||
| } | ||
|
|
||
| /* Hover / focus: enlarge bars and reveal labels to the right. */ | ||
| /* Hover / focus: enlarge bars and reveal labels to the left. */ | ||
| .conversation-toc:hover .toc-bar, | ||
| .conversation-toc:focus-within .toc-bar { height: 18px; opacity: 0.5; } | ||
| .conversation-toc:hover .toc-label, | ||
| .conversation-toc:focus-within .toc-label { max-width: 220px; opacity: 1; } | ||
| /* The revealed labels float over the message content, so the expanded outline | ||
| gets its own panel background to stay readable. The padding grows leftward | ||
| only, keeping the rail bar's x stable (the table-occlusion hit-test in | ||
| ConversationPane depends on it). */ | ||
| .conversation-toc:hover .toc-scroll, | ||
| .conversation-toc:focus-within .toc-scroll { | ||
| padding-left: var(--space-3); | ||
| border-radius: var(--radius-md); | ||
| background: var(--color-bg); | ||
| box-shadow: var(--shadow-md); | ||
| } | ||
|
|
||
| .toc-row.active .toc-bar { opacity: 1; height: 18px; } | ||
| .toc-row.active .toc-label { color: var(--color-accent); font-weight: var(--weight-medium); } | ||
| .toc-row:hover .toc-bar { opacity: 1; } | ||
| .toc-row:hover .toc-label { color: var(--color-text); } | ||
|
|
||
| /* When there is not enough room to the right of the reading column to reveal | ||
| the labels, the rail is kept mounted (so its position can keep being | ||
| measured) but hidden from view and from pointer/screen-reader interaction. */ | ||
| /* When there is not enough room to the left of the rail to reveal the labels, | ||
| the rail is kept mounted (so its position can keep being measured) but | ||
| hidden from view and from pointer/screen-reader interaction. */ | ||
| .conversation-toc.toc-clipped { | ||
| visibility: hidden; | ||
| pointer-events: none; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -419,7 +419,7 @@ onUnmounted(() => { | |
| <thead><tr><th>Token</th><th>Value</th><th>Usage</th></tr></thead> | ||
| <tbody> | ||
| <tr><td class="tk">--p-sidebar-w</td><td class="val">264px</td><td>left session sidebar width</td></tr> | ||
| <tr><td class="tk">--p-content-max</td><td class="val">760px</td><td>chat reading-column max width (regular chat prose)</td></tr> | ||
| <tr><td class="tk">--p-content-max</td><td class="val">760px</td><td>xl dialog width</td></tr> | ||
| <tr><td class="tk">--p-content-wide</td><td class="val">920px</td><td>wide content (settings / panel)</td></tr> | ||
| <tr><td class="tk">--p-table-max</td><td class="val">1040px</td><td>desktop wide-table max width (see §04)</td></tr> | ||
| <tr><td class="tk">--p-table-cell-max</td><td class="val">700px</td><td>max width of a single table column; longer cell content wraps (see §04)</td></tr> | ||
|
|
@@ -1028,7 +1028,7 @@ onUnmounted(() => { | |
|
|
||
| <h3 class="sub">Unified message stream</h3> | ||
| <div class="stage-wrap"> | ||
| <div class="stage-bar"><span class="st">Conversation · 760px reading column</span></div> | ||
| <div class="stage-bar"><span class="st">Conversation · full-width column</span></div> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Fresh evidence since the earlier review is that this renamed stage now says “full-width column”, but the demo rendered immediately below is still AGENTS.md reference: apps/kimi-web/AGENTS.md:L11-L11 Useful? React with 👍 / 👎.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in d78caaf: dropped the 560px cap from |
||
| <div class="stage p col" style="align-items:center;background:#fff"> | ||
| <div class="demo-chat"> | ||
|
|
||
|
|
@@ -1122,7 +1122,7 @@ onUnmounted(() => { | |
| </div> | ||
| </div> | ||
| </div> | ||
| <p><b>Wide markdown tables (desktop):</b> regular chat prose stays within the 760px reading column (<code>--p-content-max</code>). On desktop a wide table may grow naturally with its content up to 1040px (<code>--p-table-max</code>), centred within the conversation pane; beyond that the excess scrolls horizontally inside the table's own wrapper — the page and the chat area never scroll sideways. A single column is capped at 700px (<code>--p-table-cell-max</code>), so long cell content wraps inside the cell instead of stretching the table. The conversation outline (TOC) keeps its usual position just outside the reading column; when a table grows past it and scrolls under the rail, the TOC is hidden temporarily and returns as soon as the table leaves, without touching the user's TOC setting. On mobile a table never breaks out of the reading column.</p> | ||
| <p><b>Wide markdown tables (desktop):</b> the chat reading column follows the pane width — there is no fixed max-width cap. On desktop a wide table may grow naturally with its content up to 1040px (<code>--p-table-max</code>), centred within the conversation pane; beyond that the excess scrolls horizontally inside the table's own wrapper — the page and the chat area never scroll sideways. A single column is capped at 700px (<code>--p-table-cell-max</code>), so long cell content wraps inside the cell instead of stretching the table. The conversation outline (TOC) is anchored to the pane's right edge, just inside the scrollbar gutter, and reveals its labels leftward over the content on hover; when a table grows past it and scrolls under the rail, the TOC is hidden temporarily and returns as soon as the table leaves, without touching the user's TOC setting. On mobile a table never breaks out of the column.</p> | ||
|
|
||
| <h3 class="sub">Tool calls: compact by default, grouped, expand on demand</h3> | ||
| <p>High-frequency calls like <code>read_file</code> / <code>bash</code> / <code>grep</code> are "operational noise" — if each one took a full card, parallel triggers would quickly drown out the conversation. | ||
|
|
@@ -1206,7 +1206,7 @@ onUnmounted(() => { | |
| <h3 class="sub">Responsive</h3> | ||
| <p>See §02 <code>--p-bp-sm</code> for the breakpoint. This section only gives mobile-adaptation pointers for the chat interface; a full mobile mockup is out of scope for this spec.</p> | ||
| <div class="callout info"><span class="ico">i</span><div> | ||
| At ≤640px: dialogs anchor to the bottom as Sheets (xl top radius, top drag handle), the sidebar collapses into an expandable drawer, the Composer toolbar is allowed to wrap, and the chat reading column drops its max-width to fill the screen. | ||
| At ≤640px: dialogs anchor to the bottom as Sheets (xl top radius, top drag handle), the sidebar collapses into an expandable drawer, and the Composer toolbar is allowed to wrap. | ||
| </div></div> | ||
| </section> | ||
|
|
||
|
|
@@ -2313,7 +2313,7 @@ onUnmounted(() => { | |
| .demo-stack { display: flex; flex-direction: column; gap: 12px; width: 100%; } | ||
| .demo-col { display: flex; flex-direction: column; gap: 10px; } | ||
| .demo-grow { flex: 1; min-width: 0; } | ||
| .demo-chat { display: flex; flex-direction: column; gap: 14px; width: 100%; max-width: 560px; } | ||
| .demo-chat { display: flex; flex-direction: column; gap: 14px; width: 100%; } | ||
|
|
||
| /* Icon catalog (§02 Icon library) */ | ||
| .icon-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; margin: 14px 0; } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As committed,
--read-max: 100%makes regular assistant prose and user bubbles expand to 94%/78% of whatever desktop pane width is available, while the web design-system view still defines--p-content-maxas the chat reading-column max width and states that regular chat prose stays within that 760px column. On large monitors this regresses readability and also bypasses the tokenized layout contract; if full-width prose is intended, the design-system contract should be updated together, otherwise keep the normal stream capped and reserve wider growth for the existing table/content exceptions.AGENTS.md reference: apps/kimi-web/AGENTS.md:L11-L11
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch — at that commit the design-system contract indeed lagged behind the change. It has been updated since (6edc9c0):
--p-content-maxno longer documents the chat reading column (it now only backs the xl dialog width), and the design-system view's chat/wide-tables sections were rewritten to state that the chat column follows the pane width with no fixed cap, including the outline rail's new anchor at the pane's right edge. Full-width prose is the deliberate behavior change of this PR — the readability trade-off on very wide monitors is accepted on purpose.