Commit graph

460 commits

Author SHA1 Message Date
qer
ea03f30e51
feat(web): render LaTeX math in chat via KaTeX (#1035)
* feat(web): render LaTeX math in chat via KaTeX

* fix(web): keep literal prose dollars out of KaTeX inline math

Enabling KaTeX turned plain prose with two dollar-prefixed tokens
(`Check $PATH before $HOME`, `costs $5 and $10`) into a single
inline formula, since markstream's $…$ tokenizer has no
"no whitespace inside the delimiters" rule.

Add a postTransformTokens guard that turns a single-$ inline span back
into literal text when its content starts or ends with whitespace. Real
inline math is written tight (`$E=mc^2$`, `$\frac{1}{2}$`), while
the prose false-positives always have whitespace inside the delimiters,
so this keeps inline/block math working while leaving prices, env vars,
and ranges as readable text. Code spans are already excluded by the
tokenizer, and running on the flat token stream also covers dollars
nested inside lists and blockquotes.

Addresses the Codex review comment on PR #1035.

* fix(web): reject compact currency ranges before rendering math

The literal-dollar guard only caught prose whose content had whitespace
inside the delimiters, so a compact range like `costs $5/$10` still
rendered `5/` as a formula and dropped the second dollar. (markstream's
own currency check rejects `-`/`~` ranges but not `/`.)

Extend the guard to also reject a single-$ span whose content is a
numeric amount with a trailing range connector (`/`, `-`, `~`,
en/em dash) -- a complete formula never ends in a dangling operator.
Scoped to digit-led content so symbolic math is left alone, and numeric
math that is not a range (`$5/2$`, `$5-2$`, `$0.5$`) still
renders. Added tests for the range cases and the non-range math.

Addresses the follow-up Codex review comment on PR #1035.

* fix(web): treat shell/path dollar pairs as literal text

Adjacent shell variables and PATH-like values (`Use \$HOME/bin:\$PATH`,
`\$PATH:\$HOME`) were still rendered as math, because the prose-dollar
guard only looked at the span's own content (whitespace inside the
delimiters, or a trailing numeric range connector) and never at what
touches the delimiters from the outside.

Replace the two bespoke heuristics with the two industry-standard rules,
now driven by the surrounding text tokens:

  - Pandoc (tex_math_dollars): no whitespace immediately inside the
    delimiters.
  - GitHub: each \$ must be bounded on its outer side by whitespace, a
    line boundary, or structural punctuation. A letter or digit there
    means a second prose token, so the span is literal text.

The GitHub outer-boundary rule subsumes the old numeric-range check (a
closing \$ in \$5/\$10 is followed by a digit) and also catches
shell/path cases Pandoc's inner rule misses. Normal math -- including
bare \$x\$, \$x^2\$., and (\$x^2\$) -- still renders. Added
tests for shell/path values and punctuation-wrapped math.

Addresses the third Codex review comment on PR #1035.

* fix(web): render math next to CJK punctuation and quotes

The outer-boundary guard only accepted ASCII punctuation, so a formula
followed by full-width punctuation or wrapped in typographic quotes was
misclassified as prose: `公式为 \$E=mc^2\$,其中` and `“\$x\$”`
showed raw dollars instead of rendering.

Invert the boundary check from an allow-list of ASCII punctuation to a
deny-list of ASCII letters/digits. A \$ glued to an ASCII letter/digit
still means a second prose token (\$PATH:\$HOME, \$5/\$10), but
whitespace, line boundaries, and every other character -- full-width
punctuation, CJK ideographs, curly quotes -- is now a valid math
boundary, which is the correct behavior for localized prose.

Addresses the fourth Codex review comment on PR #1035.

* fix(web): preserve later math after literal-dollar spans

A prose dollar in front of a real formula in the same inline run
(`costs $5 and formula $x$`, `Use \$HOME before $E=mc^2$`) exposed
the core limit of the token-level guard: markstream's tokenizer greedily
pairs the first literal \$ with the formula's opening \$ before any hook
runs, so converting that span back to text could only blank it -- the
later formula's opening \$ was already consumed and the formula rendered
as raw text.

Move the guard from postTransformTokens to a source-level preprocessor
that runs before tokenization. escapeProseDollars protects code spans,
fenced code blocks, and \$\$…\$\$ display math, then pairs single \$
delimiters using the Pandoc (tight delimiters) and GitHub-style
outer-boundary rules: any \$ without a valid partner is escaped as
\\\$, so the tokenizer leaves it literal while real formulas -- including
ones that come after a prose dollar -- still parse as math.

The component now preprocesses each markdown segment's text and the
postTransformTokens hook is gone. Rewrote the tests around the
string-in/string-out helper, including the prose-before-formula case,
code spans, fenced code, and block math.

Addresses the fifth Codex review comment on PR #1035.

* fix(web): protect indented code blocks before escaping dollars

The dollar-escaping preprocessor stashed fenced code blocks, inline code,
and display math, but not 4-space / tab indented code blocks. So a
snippet like `    echo \$HOME` had its dollar rewritten to `\\$HOME`,
and because Markdown renders backslashes literally inside code, the web
chat corrupted the code to show a stray backslash.

Add an indented-code regex and protect those lines too. Also make the
placeholder restore iterative, so nested protected regions (e.g. inline
code that looks like display math) restore correctly instead of leaving
a placeholder behind.

Addresses the sixth Codex review comment on PR #1035.

* fix(web): do not treat list-continuation lines as indented code

The indented-code regex protected every 4-space line, but inside a list
item a 4-space indent is a normal continuation paragraph, not a code
block (code under a list marker needs deeper indentation). So a message
like `- total\n    costs \$5 and \$10` had that nested line
stashed as "code", leaving its dollars un-escaped -- and the KaTeX
parser then rendered the price range as math.

Narrow the indented-code rule to a run of 4-space / tab lines that is
preceded by a blank line (or the start of the text). That still protects
real top-level indented code blocks and deeper-indented code inside
lists, while letting 4-space list-continuation lines get their dollars
escaped.

Addresses the seventh Codex review comment on PR #1035.

* refactor(web): render only $$…$$ display math, drop single-$ inline

Enable KaTeX for display math only: disable markstream's inline math rule
(`md.inline.ruler.disable('math')`) via customMarkdownIt, leaving the
`math_block` rule for $$…$$. Single $ now stays literal everywhere, so
prices, env vars, shell paths, and code are never mis-rendered as math --
with no escaping, no code detection, and no preprocessor.

This removes the escapeProseDollars normalization layer and all of its
code-protection machinery (the 8 review comments it attracted were
symptoms of trying to make a lax single-$ tokenizer behave). Display
$$…$$ math continues to render via KaTeX.

Changeset updated to describe display-math-only support.
2026-06-25 12:47:40 +08:00
qer
884b65a040
fix(web): coalesce snapshot reloads on resync (#1087)
Avoid concurrent session snapshot requests when resync_required fires repeatedly, while still allowing one queued rerun after the in-flight reload settles.
2026-06-25 11:49:05 +08:00
qer
3554f7e7d6
feat(plugins): source Superpowers from GitHub and show update badges (#1066)
Some checks are pending
CI / test (push) Waiting to run
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(plugins): source Superpowers from GitHub and show update badges

Source the Superpowers plugin from its GitHub release (v6.0.3) instead of a vendored copy, and drop the explicit version field.

Derive marketplace entry versions from GitHub source URLs when the version field is omitted, keeping the source URL the single source of truth.

Show update badges for installed plugins on the /plugins Installed tab.

* docs(plugins): document Installed tab update badges

* fix(plugins): stamp GitHub source version in CDN catalog

Older CLIs only read the explicit marketplace version and cannot derive it from a GitHub source URL. When publishing the CDN catalog, stamp the version derived from a pinned GitHub source so those clients still surface update badges.

The source plugins/marketplace.json keeps no explicit version; the version is derived at build time instead.

* feat(plugins): resolve latest version for bare GitHub sources at runtime

Point the Superpowers marketplace entry at the bare GitHub repo URL so it tracks the latest release instead of a pinned tag.

When a marketplace entry omits version and its source is a bare GitHub repo URL, resolve the latest release tag at load time (via the /releases/latest redirect) to fill the version for update detection.

Revert the build-time version stamping; it is no longer needed. Older CLIs that only read the explicit catalog version will no longer see update badges for Superpowers, since the catalog no longer carries one.

* feat(plugins): make Enter update and add I for details on Installed tab

On the Installed tab, Enter now installs the available update when one is present, and falls back to opening plugin details otherwise.

Add the I key to always open plugin details, so details remain reachable when Enter is occupied by an update. Update the installed hint, docs and changeset accordingly.

* feat(plugins): show installing state inside the plugins panel

Move the "Installing … from marketplace" notice from a transient status message into the plugins panel itself, so the user sees progress in the interactive card while an install or update is in flight.

* feat(plugins): highlight reload hint and add dev:cli:marketplace

Highlight "Run /new or /reload to apply plugin changes." in warning color after plugin install and remove, and make the two notices symmetric.

Add a root dev:cli:marketplace script that points the dev CLI at the production marketplace instead of the local dev server.

* fix(plugins): dedupe install success notice

Drop the redundant showNotice on marketplace installs so the success message is shown only once, symmetric with remove.

* fix(plugins): reset installing state on install failure

When a marketplace or Custom-tab install rejects, clear the installing state and return to the list so the user can retry, instead of leaving the panel stuck on the one-way "Installing…" view.
2026-06-24 21:58:13 +08:00
liruifengv
a86bb9757d
fix(kimi-code): show clipboard image paste hint only for newly copied images (#1072)
* fix(kimi-code): show clipboard image paste hint only once per image

The footer hint repeated on every terminal focus whenever an image remained in the clipboard, which became noisy. Replace the 30s time-based cooldown with a per-image gate: the hint shows once for a given image and stays quiet until the clipboard is observed empty and a new image appears.

* fix(kimi-code): suppress clipboard image hint for images present at startup

The footer hint fired during initialization whenever an image was already in the clipboard, treating it as new. The first clipboard observation after start now only establishes a baseline, so only images copied during the session trigger the hint.

* fix(kimi-code): show hint for first image copied after startup

* fix(kimi-code): make clipboard image probe non-blocking

The startup baseline probe in ClipboardImageHintController calls clipboardHasImage(), which on Linux/WSL ran wl-paste/xclip/powershell via spawnSync. The probe only reaches its first await after those synchronous calls, so a slow or wedged helper could freeze the TUI launch for up to the 1s-2s tool timeouts even when the user never focuses with an image.

Add an async runCommandAsync built on spawn with timeout-based kill, and route the Linux/WSL image detection through it so the event loop is never blocked. Keep the synchronous runCommand for the explicit paste-read path.

* chore: add changeset for non-blocking clipboard probe

* chore: simplify clipboard image hint changeset
2026-06-24 21:07:14 +08:00
liruifengv
3aaf1e5803
fix(kimi-code): bump native clipboard dependency to fix Linux startup crash (#1075)
* fix(kimi-code): bump native clipboard dependency to fix Linux startup crash

* chore(nix): update pnpmDeps hash
2026-06-24 21:07:02 +08:00
liruifengv
75ca3b2160
feat(tui): add Ctrl+U/Ctrl+D paging in the task output viewer (#1078)
PgUp/PgDn are often captured by terminal or tmux scrollback, so add Ctrl+U and Ctrl+D as full-page up and down alternatives, matching the existing PgUp/PgDn behavior.
2026-06-24 21:00:40 +08:00
liruifengv
500677ab8b
fix(tui): clear editor draft on Ctrl-C during compaction (#1076)
When compaction is in progress and the editor has a draft, Ctrl-C now clears the draft first instead of cancelling compaction, matching the streaming behavior. The clear-text logic is shared between the compaction and streaming branches.
2026-06-24 20:30:20 +08:00
Kai
0e227ba18a
fix(agent-core): surface git context failures for explore subagents (#1067)
* fix(agent-core): surface git context failures for explore subagents

collectGitContext collapsed every git failure (spawn error, non-zero exit, timeout) into null, so explore subagents silently lost git context with no signal. Now a definitive 'not a git repository' injects an explicit unavailable signal so the subagent does not waste turns probing git history, while other failures are logged and surface as an empty block. The block is all-or-nothing so a partial snapshot (e.g. a timed-out status making a dirty tree look clean) is never shown.

* fix(agent-core): use rev-parse for branch to support git < 2.22

`git branch --show-current` was added in Git 2.22 and fails (exit 129) on older Git even in a valid repository. Because the branch probe is fatal, this dropped the whole git-context block for older-Git users. Switch to `git rev-parse --abbrev-ref HEAD`, which is supported across Git versions, and filter the `HEAD` output produced in detached-HEAD state.

* fix(agent-core): show whatever git info is available in explore context

Git probes fail in perfectly normal states — no `origin` remote, no commits yet (unborn branch), detached HEAD, older Git — so a failed probe no longer aborts the whole collection. Each probe is now best-effort: failures are logged and their section is omitted, and the block is dropped only when nothing useful was collected. Branch is read via `symbolic-ref --short HEAD`, which works in unborn repositories and on older Git; it fails in detached-HEAD state, where the Branch section is just omitted.
2026-06-24 19:59:30 +08:00
liruifengv
b62b3a147f
feat(kimi-code): show cache read details in debug timing (#1074)
* feat(kimi-code): show cache read details in debug timing

* chore: remove changeset
2026-06-24 19:39:42 +08:00
Haozhe
ff177155ca
fix(web): stop auto-dismissing pending questions and approvals on a timeout (#1070)
* fix(web): stop dismissing questions after a 60 second timeout

The server's question broker auto-expired AskUserQuestion requests after 60s, which dismissed the question even when the user simply needed more time. Remove the timeout, and the now-unused expires_at field, so a question stays pending until the user answers or explicitly dismisses it.
2026-06-24 19:15:06 +08:00
liruifengv
d18aa1666a
perf(tui): reuse streaming markdown instances (#1069) 2026-06-24 16:08:39 +08:00
ForgottenR
bbd8a1a947
fix(cli): resolve spawn EFTYPE on Windows for kimi web and /web (#903)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix(cli): resolve spawn EFTYPE on Windows for kimi web and /web

* chore(changeset): clarify affected Windows installation methods

---------

Co-authored-by: qer <wbxl2000@outlook.com>
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-06-24 15:27:00 +08:00
_Kerman
ea6a4bfe6e
fix: preserve long tool output (#1062)
* fix: persist truncated foreground bash output

* fix: persist oversized tool results

* fix: link background task notifications to saved output

* fix: avoid lossy tool result budgeting

* fix

* fix: include fallback task output previews

* fix

* fix
2026-06-24 14:42:11 +08:00
7Sageer
4b837d6bfb
feat: auto-create missing parent directories when writing files (#1065)
The Write tool previously failed when a parent directory was missing, forcing a manual mkdir round trip. It now creates missing parents recursively before writing.
2026-06-24 14:05:27 +08:00
7Sageer
ee69e16dc8
fix: use session cwd for stdio MCP servers (#1057) 2026-06-24 13:40:56 +08:00
7Sageer
a752a5309b
fix(agent-core): mark truncated skill descriptions with an ellipsis (#1064)
The model-facing skill listing silently sliced long descriptions to 250 characters with no marker, so neither the user nor the model could tell a description was cut. Truncated entries now end with an ellipsis and the truncation walks whole grapheme clusters so it never splits a surrogate pair or combining sequence.
2026-06-24 13:20:48 +08:00
qer
5ef66ddfed
feat(tui): redesign /plugins as a tabbed panel (#1025)
* feat(tui): redesign /plugins as a tabbed panel

Split the /plugins manager into Installed / Official / Third-party /
Custom tabs. The Official and Third-party marketplace catalogs load
lazily, so /plugins opens instantly and keeps working offline, with
fetch failures shown inline instead of closing the panel. The tab strip
is shared with the /model provider tabs via the new renderTabStrip
helper.

* fix(tui): show untiered marketplace entries and update badges

Address Codex review feedback on the /plugins tab redesign:

- Untiered marketplace entries (no `tier` field) now appear on the
  Third-party tab instead of being invisible in both marketplace tabs.
- Installed plugins whose marketplace version is newer than the local
  version render an `update <local> → <latest>` badge again, and
  up-to-date plugins show `installed · v<version>` — restoring the
  update visibility the pre-redesign marketplace UI had.

* fix(tui): decode Space for installed-plugin toggle

In terminals that send printable keys via Kitty/CSI-u sequences (e.g. VS
Code's integrated terminal), the Space key arrives as a printable char
rather than a Key.space match, so the Installed-tab Space toggle silently
stopped working. Check both matchesKey(Key.space) and the decoded
printable char to match the MCP selector and other dialogs.

* fix(tui): open custom marketplaces on the Third-party tab

When `/plugins marketplace <source>` points at a custom catalog whose
entries omit `tier`, those entries are classified into the Third-party
tab. Opening on Official left the visible tab empty and Enter could not
install anything, unlike the old marketplace picker which showed all
entries from the supplied source. Open on Third-party when a custom
source is supplied; the default catalog still lands on Official.

* docs(plugins): drop open-url wording and hyphenate Shift-Tab

Address Codex review feedback:

- The marketplace Enter action is install/update only (open-url rows were
  removed), so say "install or update" instead of "open or install" and
  drop the leftover changeset sentence about setup URLs.
- Use `Shift-Tab` (hyphen) instead of `Shift+Tab` to match the docs
  typography convention.

* fix(tui): keep marketplace selection valid while loading

When the Official/Third-party catalog is still loading, `entries` is empty
and pressing ↓ computed `Math.min(-1, selectedIndex + 1)` = -1. The later
Enter then read `entries[-1]` and the first install silently did nothing.
Clamp the index to 0 while there are no entries.

* fix(tui): count tab separators in tab-strip fit check

renderTabStrip declared a strip to fit whenever the sum of tab cell widths
fit, but the returned string also inserts single spaces between tabs via
`segments.join(' ')`. At widths around 43-45 columns for a four-tab strip
this declared a fit while the joined line was wider, so the trailing tab
got truncated instead of showing the `<`/`>` scroll markers. Count the
inter-tab separators in both the full-fit check and the scrolling window
fit check.

* docs(plugins): fix Kimi Datasource redirect anchor

The datasource.md redirect pointed at ./plugins.html#kimi-datasource, but
plugins.md no longer has a `## Kimi Datasource` heading — it is now
`## Official Plugins`. Update the en/zh redirect targets and fallback
links to #official-plugins / #官方插件 so the link lands on an existing
anchor.

* docs(plugins): restore concise Kimi Datasource section

The `## Official Plugins` section had replaced the original
`## Kimi Datasource` section, leaving the datasource.md redirect pointing
at a missing anchor and the Datasource capabilities/usage unreachable.
Restore a concise `## Kimi Datasource` section (intro + OAuth login +
install steps + usage) in both en and zh so the #kimi-datasource anchor
is valid again and the content is reachable.

* docs(plugins): restore Installing-from-GitHub subheading

The tab-redesign rewrite had dropped the `### Installing from GitHub` /
`### 从 GitHub 安装` subheading and its lead sentence, leaving only the
four URL forms. Restore the heading and lead sentence in both en and zh.

* docs(plugins): expand Kimi Datasource and tidy marketplace docs

- Condense the Official / Third-party / Custom tab overview and trust-badge note

- Trim the custom marketplace JSON section to the minimal id + source shape

- Move and expand the Kimi Datasource section with install, usage, and coverage

* docs(plugins): fix heading style and drop Next steps section

- Use sentence case for the Datasource headings (How to use, What you can do)

- Rename the Datasource caveat heading to Billing and limitations / 计费与限制 to avoid a duplicate Notes / 注意事项 anchor

- Remove the Next steps section, which linked back to the on-page Datasource anchor

* fix(tui): repaint plugins panel from current theme palette

The /plugins panel and MCP selector captured a palette snapshot at construction. In auto theme mode, applyResolvedAutoTheme swaps currentTheme.palette and re-renders without remounting the open panel, so it kept stale colors until closed.

Read currentTheme.palette during render instead, drop the colors opt from both components and their call sites, and add a regression test that switches palettes on a mounted panel.

* fix(tui): repaint model tab strip from current theme palette

TabbedModelSelectorComponent cached a palette snapshot in opts and used it only for the tab strip. In auto theme mode the inner model list repaints from currentTheme but the strip kept the old colors until the dialog was closed.

Read currentTheme.palette on the render path instead, drop the colors opt and its three call sites, and add a regression test that switches palettes on a mounted selector and asserts the strip repaints. This removes the last palette snapshot among editor-replacement dialogs.
2026-06-24 13:12:28 +08:00
qer
51723bee1a
docs(changelog): sync 0.19.2 from apps/kimi-code/CHANGELOG.md (#1063) 2026-06-24 12:46:27 +08:00
Kai
66640380eb
feat: replace silent AGENTS.md truncation with a visible warning (#1040)
Oversized AGENTS.md files are no longer silently truncated. The full
content is injected, and a warning is shown in the TUI status bar and the
web UI when the combined AGENTS.md size exceeds the recommended 32 KB.

A generic session-warnings API backs this so future warning types can be
added without changing the API surface.
2026-06-24 12:26:17 +08:00
github-actions[bot]
0bcd9843c1
ci: release packages (#997)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-24 12:13:38 +08:00
qer
98d3e5b71d
feat(web): stabilize and drag-reorder workspaces in the sidebar (#1047)
* feat(web): stabilize and drag-reorder workspaces in the sidebar

* fix(web): preserve dragged workspace order after refresh

* fix(web): float session to top of its group on new message

* fix(web): align workspace drop order with insertion marker

* fix(web): allow dropping a workspace after the last item

* fix(web): use reordered workspaces for active fallback

* fix(web): honor drag order for next-workspace fallback on removal
2026-06-24 12:06:32 +08:00
qer
b93e9365b6
fix(web): stop auto-approving plan reviews and sensitive files in yolo mode (#1056)
The web app ran a client-side policy that auto-approved every approval request in auto/yolo mode, including plan reviews, sensitive file access, and other asks the daemon intentionally sends for user confirmation. The daemon already resolves auto/yolo server-side, so drop the client-side auto-approve and let those requests reach the approval UI.
2026-06-24 11:21:21 +08:00
qer
ac1882fe28
feat(web): persist collapsed workspace groups to localStorage (#1045) 2026-06-23 22:45:24 +08:00
_Kerman
c240bfab7d
fix(agent-core): realign mid-history interrupted tool calls on resume (#1027) 2026-06-23 22:39:19 +08:00
qer
9d197e0f67
fix(web): make clipboard copy work over plain HTTP (#1044)
* fix(web): make clipboard copy work over plain HTTP

The Clipboard API (navigator.clipboard) is only exposed in secure contexts. When the web UI is served over plain HTTP, every copy action threw synchronously and silently failed. Route all copy call sites through a helper that falls back to execCommand('copy') in insecure contexts, and surface success or failure feedback to the user.

* test: address review feedback and a flaky goal-badge test

- clipboard test: drop the jsdom environment and mock the small navigator/document surface in the default node environment, per the kimi-web "pure logic tests only" rule.

- footer-goal-badge test: assert the absence of the "[goal" badge instead of the bare "goal" substring, which could match a rotating working tip ("/goal ...") and fail depending on Date.now().
2026-06-23 22:21:10 +08:00
qer
27df39c7ed
fix(web): fall back to session abort for stale prompts (#1043)
* fix(web): fall back to session abort for stale prompts

* test(web): cover stale prompt abort fallback
2026-06-23 22:05:08 +08:00
qer
dc6b9ef02b
feat(web): show dev-mode indicator in sidebar (#1042)
Tint the sidebar logo yellow and append the connected backend host:port to the title when the page is served by the Vite dev server, so local development tabs are easy to tell apart. Inert in production.
2026-06-23 21:41:47 +08:00
liruifengv
be77d5da03
feat(kimi-code): show clipboard image paste hint in footer (#1028)
* feat(kimi-code): add lightweight clipboard image detection

* fix(clipboard): correct Linux X11 image detection and extract shared helpers

- Extract shared clipboard constants/helpers into clipboard-common.ts

- Fix Linux X11 branch calling macOS-only osascript

- Use native hasImage() on Linux X11, macOS, and Windows with fallbacks

- Compute xclip result once and reuse on Linux

- Add test coverage for unsupported MIME types, empty targets, failures, WSL, and native fallbacks

* fix(kimi-code): restore Wayland/WSL xclip fallback in clipboard image detection

* feat(kimi-code): add clipboard image hint controller

* fix(kimi-code): clipboard image hint focus race and cleanup

* fix(kimi-code): prevent clipboard image hint from clearing unrelated hints and stale reads

* fix(clipboard-image-hint): lifecycle issues and platform-dependent tests

* fix(kimi-code): invalidate pending clipboard hint read on stop

* feat(kimi-code): wire clipboard image hint controller into TUI

* style(kimi-code): wrap void expression in braces to fix lint warning

* style(kimi-code): prefer nullish coalescing in clipboard image detection

* chore: add changeset for clipboard image footer hint

* fix(kimi-code): let clipboard image hint observe non-consuming focus events

* fix(kimi-code): extend clipboard image hint display duration to 4 seconds

* docs: mention clipboard image footer hint in interaction guide

* chore: downgrade clipboard image hint changeset to patch

* Revert "docs: mention clipboard image footer hint in interaction guide"

This reverts commit 0fd50dcc9b.

* fix(cli): avoid treating copied Finder files as images on macOS

Filter file-like native clipboard formats in clipboardHasImage() before

calling native hasImage(), mirroring the guard already used by

readClipboardMedia(). This prevents copied Finder files from being

mis-detected as pasteable images because macOS exposes their

thumbnails as image data.

* fix(cli): align image detection with paste path on macOS and Windows

Remove osascript and PowerShell fallbacks from clipboardHasImage() on

macOS and Windows. The paste reader (readClipboardMedia()) only uses the

native clipboard module for images on those platforms, so detecting images

via methods the reader cannot consume produced misleading footer hints.

Linux fallbacks (wl-paste / xclip / PowerShell under WSL) remain because

they match the actual paste path.

* fix(tui): do not truncate inline image escape sequences

UserMessageComponent applied truncateToWidth() to every rendered line,

including the Kitty / iTerm2 inline image escape sequences produced by

ImageThumbnail. pi-tui treats the embedded base64 payload inside those

sequences as visible text, so truncation chopped the escape code and left

behind '0m...' garbage instead of the image.

Skip truncation for lines that contain an inline image protocol sequence;

the image already respects maxWidthCells via ImageThumbnail.

* fix(tui): clear stale rows when content shrinks

Enable pi-tui's setClearOnShrink so that when a tall inline image is

replaced by shorter content (e.g. after sending a message), the terminal

rows the image previously occupied are cleared. Without this, pi-tui's

differential renderer can leave behind artifacts such as duplicated input

boxes.

* chore: add changesets for inline image rendering fixes

* test(cli): stabilize pi-tui capability mocks in concurrent test runs

* test(cli): use setCapabilities instead of mocked getCapabilities
2026-06-23 20:49:01 +08:00
qer
866b91c8f5
refactor(web): group components into area subdirectories (#1036)
Move the 40 feature-specific components out of the flat components/ into
chat/, settings/, dialogs/, and mobile/ subdirectories, leaving 9 shared
layout components at the top level. Recompute every relative import (no
path alias in the web app), refresh the line-1 path comments, and update
the layout description in AGENTS.md.

No behavior change; typecheck / test / build / lint all pass.
2026-06-23 20:34:46 +08:00
liruifengv
b1e6b64319
feat(tui): show working tips behind composing spinner (#1033)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* refactor(tui): extract toolbar tip constants to tui/constant/tips.ts

* feat(tui): add WORKING_TIPS subset to tip constants

* feat(tui): allow MoonLoader to render a dim tip suffix

* feat(tui): pass optional tip through ActivityPaneComponent

* feat(tui): show working tip behind composing spinner

* feat(tui): hide working tip when spinner line does not fit

* chore: add changeset for working tips

* feat(tui): guard setAvailableWidth to skip unchanged widths

* feat(tui): show working tips on moon loader and compaction

* feat(tui): use singular 'Tip:' label for loading tips

* fix(tui): keep the same loading tip across waiting/thinking/tool/composing within one turn

* feat: show contextual working tips behind loading spinners

- Add pickRandomWorkingTip() for per-step tip selection

- Cache tips by loading kind (moon/composing) so continuous tool bursts keep the same tip

- Update tip inventory with /web, /plugins, /goal, /sessions, etc.

- Add unit tests for random tip selection

* docs: update working-tips changeset summary
2026-06-23 20:12:55 +08:00
qer
603a7679de
refactor(web): extract attachment upload into a composable (#1034)
Move the image/video attachment state, the file-picker / paste / drag-drop
handlers, the upload machinery, the preview lightbox, and the paste-listener
+ object-URL cleanup lifecycle out of Composer into useAttachmentUpload.

The composer keeps handleSubmit / handleSteer (which read the attachments to
build the payload) and the hasUpload toolbar flag; it consumes the returned
refs and handlers directly. The destructured names match the originals so the
template bindings are unchanged. handleSubmit / handleSteer now call the
composable's clearAfterSubmit() to revoke object URLs and drop the list.

Composer.vue: 1937 -> 1787 lines. Adds unit tests for useAttachmentUpload. No
behavior change.
2026-06-23 19:52:03 +08:00
qer
2bfd6860e4
refactor(web): extract composer text + draft persistence into a composable (#1031)
Move the composer's text ref, textarea ref, autosize helper, the per-session
draft load/save watchers, and the loadForEdit handle into useComposerDraft.
The returned text/textareaRef/autosize refs are passed straight through to the
history / slash / mention composables as their deps, so the rest of the
component is unchanged.

Composer.vue: 1987 -> 1937 lines. Adds unit tests for useComposerDraft. No
behavior change.
2026-06-23 19:14:50 +08:00
qer
a753b0535e
fix(web): upgrade markstream-vue to 1.0.3 to fix blank nested code blocks (#1032)
* fix(web): upgrade markstream-vue to 1.0.3 to fix blank nested code blocks

* fix(web): update flake pnpmDeps hash after markstream upgrade
2026-06-23 19:09:42 +08:00
qer
661c1fbe5b
refactor(web): extract @-mention menu into a composable (#1030)
Move the @-mention menu's open/items/active/loading state, the @token
detection, debounced search, and insertion logic out of Composer into
useMentionMenu. The composer keeps the keydown orchestration (it also
juggles the slash menu and history recall) and consumes the returned refs
directly; the destructured refs are aliased back to the original names so
the rest of the component is unchanged.

Move the FileItem view type into types.ts (mirroring the FileData move) so
the .ts composable can import it without hitting the type-aware lint rule
against importing types from .vue files; MentionMenu re-exports it for the
existing .vue consumers.

Composer.vue: 2035 -> 1987 lines. Adds unit tests for useMentionMenu. No
behavior change.
2026-06-23 18:38:50 +08:00
qer
318c964f07
refactor(web): extract slash-command menu into a composable (#1026)
Move the slash menu's open/items/active state, the filter logic, and item
selection out of Composer into useSlashMenu. The composable takes the text
ref, textarea ref, autosize, a skills getter, and the emit/history-push
callbacks as deps.

The composer keeps the keydown orchestration (arrow keys, Enter/Tab, Escape)
because it also juggles the mention menu and history recall; it consumes the
returned open/items/active refs directly and calls update/select. The
destructured refs are aliased back to the original names so the rest of the
component is unchanged.

Composer.vue: 2058 -> 2035 lines. Adds unit tests for useSlashMenu. No
behavior change.
2026-06-23 18:17:48 +08:00
qer
83384ee6d4
fix(web): persist input history so recall works after the first message (#1015)
* fix(web): persist input history so recall works after the first message

The composer has two mutually-exclusive instances: the empty-session
composer and the docked composer. The first message of a new session is
sent by the empty composer, which unmounts as soon as the first turn
appears; the docked composer then mounted with an empty in-memory history,
so ArrowUp did nothing until a second message was sent. The history was
also lost on every page reload.

Persist the history to localStorage as a single global list and re-read it
on mount. Global (not per-session) because a new session has no id until
after the first submit, so per-session keys would not line up across the
empty -> docked handoff. Caps the list at 200 entries.

Adds persistence-focused unit tests (surviving a remount, the 200-entry
cap, and a malformed stored value).

* fix(web): record slash commands in input history too

Move the history.push call ahead of the slash-command branch so that known
commands (with or without args, e.g. /goal <task> or /model) are recorded
and can be recalled with ArrowUp, instead of only plain messages. Steer
already pushed; only the submit slash path was missing it.

* fix(web): record menu-selected slash commands in history

Bare slash commands picked from the slash menu (e.g. /model, /login) go
through selectSlashCommand and emit directly, never reaching handleSubmit,
so they were not recorded even after the typed-slash fix. Push the command
name before emitting. acceptsInput commands are still recorded later by
handleSubmit together with their argument.
2026-06-23 18:07:00 +08:00
liruifengv
6d506380ce
fix(changeset): downgrade web panel resize changeset from minor to patch (#1021) 2026-06-23 17:23:31 +08:00
liruifengv
9c553e4bf7
feat(tui): add Alt+S to switch model for current session only (#1020)
In the /model picker, Enter still switches the model and saves it as the
default; Alt+S now switches only for the current session without writing
to the config file.
2026-06-23 17:14:43 +08:00
qer
fb780fce96
refactor(web): extract input-history recall into a composable (#1011)
* refactor(web): extract input-history recall into a composable

Move the shell-style up/down recall of previously sent messages out of
Composer into useInputHistory. The composable owns the history list, the
browsing cursor, and the textarea caret/selection work needed to apply a
recalled entry, taking the text ref, textarea ref, and autosize as deps.

The composer keeps the keydown orchestration (which also juggles the slash
and mention menus) and calls into the composable for push / recall / caret /
browsing state.

Composer.vue: 2104 -> 2050 lines. No behavior change.

* test(web): cover useInputHistory recall behavior

Add unit tests for the extracted input-history composable: push dedup and
empty-skip, walking backward/forward through entries, restoring the live
draft (empty and non-empty), empty-history no-op, resetBrowsing, and the
caretAtFirstLine gate.
2026-06-23 17:01:17 +08:00
liruifengv
fd16ffb80a
fix(tui): fix Tab key completion in the editor (#1012)
Stop plain Tab from opening the file completion list when the autocomplete menu is closed; Tab now only accepts the selected item while the menu is open.

After Tab-completing a slash command name, reopen the menu to show its subcommands instead of falling back to file completions.
2026-06-23 16:51:12 +08:00
qer
a2650f85d4
refactor(web): extract ConversationToc from ConversationPane (#1010)
Move the beta conversation outline (proportional bubbles, viewport
indicator, hover tooltip) into a dedicated ConversationToc component.
The child owns the nav markup, the tooltip hover state, and its own
visibility (mobile / session-loading / single-turn), while the metric
derivation and scroll-driven viewport/active-turn tracking stay in the
pane because they are coupled to the scroll container.

ConversationPane.vue: 1613 -> 1422 lines. No behavior change.
2026-06-23 15:51:49 +08:00
liruifengv
e47de610e4
feat(tui): add ctrl+t to expand the todo list (#1009)
* feat(tui): add ctrl+t to expand the todo list

Toggle between the truncated view and the full list; the shortcut only takes effect while the list actually overflows.

* docs(keyboard): document ctrl+t todo expand shortcut

* chore(changeset): mark todo expand shortcut as patch

* docs(agents): clarify minor vs patch in gen-changesets skill

* fix(tui): clear pending exit when toggling the todo list
2026-06-23 15:49:07 +08:00
liruifengv
d70c3a8c01
fix(tui): support expanding bash command while running (#1004)
Render the command in the in-flight Bash card body so it is visible while the command runs, and let Ctrl+O expand the full command before the result arrives.
2026-06-23 15:28:49 +08:00
qer
ea1b33b674
refactor(web): extract pure turn-rendering helpers from ChatPane (#1001)
* refactor(web): extract pure turn-rendering helpers from ChatPane

* chore: add changeset for chat pane helper extraction

* test(kimi-web): cover chat turn-rendering helpers

Pure-logic tests for the helpers extracted from ChatPane, focused on
assistantRenderBlocks (tool-stack grouping, interrupt/media break, single
tool) plus the formatting/boundary helpers. Doubles as a safety net
confirming the extraction preserved behavior.
2026-06-23 15:20:00 +08:00
qer
e15edfd017
fix: always expose the free-text Other option in question prompts (#1003)
The question adapter only set allow_other on the wire when the SDK item carried an otherLabel/otherDescription, but the AskUserQuestion tool never provides those fields. Web clients honor allow_other, so the free-text option silently disappeared in the web UI while the TUI (which renders it unconditionally) kept working. Set allow_other unconditionally to match the tool's 'users always have an Other option' contract.
2026-06-23 15:09:55 +08:00
7Sageer
b84704bff3
perf(kaos): optimize large file reads (#971) 2026-06-23 15:07:40 +08:00
liruifengv
6b68aa85e2
feat(cli): add -c as shorthand for --continue (#999)
The lowercase -c now maps to --continue, shown in help as the primary short flag. The uppercase -C still works as a hidden alias since commander does not allow two short flags on a single option.
2026-06-23 13:53:31 +08:00
qer
3e4793d611
refactor(web): extract WorkspaceGroup from Sidebar (#998)
* refactor(web): extract WorkspaceGroup from Sidebar

* chore: add changeset for sidebar workspace group extraction
2026-06-23 13:33:36 +08:00
qer
92c2cf0ef5
feat(web): remove sidebar and panel max-width limits (#985)
* feat(web): remove sidebar and panel max-width limits

Make the resize handle max width optional so the web sidebar and right-side detail/preview panel can be resized beyond their previous fixed maximums.

* fix(web): keep sidebar resize handle reachable on narrow windows

Cap the restored sidebar width at a viewport-aware maximum (viewport width minus the conversation pane minimum) so a width saved on a wide display cannot push the resize handle or collapse button off-screen on a narrower window. The cap updates on resize.

* fix(web): cap preview panel to viewport and share panel-width logic

Apply the same viewport-aware maximum to the right-side detail/preview panel and extract the viewport tracking and width clamping into a shared composable used by both panels.

* fix(web): keep resize caps reactive and reserve room for the preview

Make the resize handle read its max width reactively so a viewport-derived cap keeps working as the window grows after mount. Also have the sidebar reserve the preview panel's minimum width whenever the right-side panel is open, so the conversation column can never be squeezed to zero.

* fix(web): clamp sidebar content width and ignore stale preview target

Render the Sidebar content at the clamped width so controls stay reachable when the saved width exceeds the viewport cap. Also stop reserving space for a hidden right panel by keying the sidebar preview-open check off detailTarget instead of the stale previewTarget.

* fix(web): clamp drag start to current resize cap

When the saved width exceeds the current cap (after the window narrows or a side panel opens), start the drag from the clamped width so the handle responds immediately instead of first covering an invisible delta.

* fix(web): clear detailTarget when closing side chat via /btw

The bare /btw close path called client.closeSideChat() directly, which hid the panel but left detailTarget set to 'btw', so the sidebar kept reserving room for a hidden right panel. Route it through the detail-layer close which clears detailTarget.
2026-06-23 13:14:19 +08:00
liruifengv
87fb95850c
docs(changelog): sync 0.19.1 from apps/kimi-code/CHANGELOG.md (#995)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
2026-06-23 12:10:06 +08:00