Commit graph

17 commits

Author SHA1 Message Date
ytahdn
f88a8aa6fc
feat(web-shell): use popovers for composer controls (#6877)
* feat(web-shell): use popovers for composer controls

* fix(web-shell): address popover review feedback

* fix(web-shell): update popover regression coverage

* fix(web-shell): address popover review feedback

* fix(web-shell): stabilize toolbar label collapse

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-07-15 04:54:47 +00:00
dreamWB
3e81315add
fix(web-shell): make composer height adaptive (#6872)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
2026-07-14 11:38:29 +00:00
ytahdn
79ae054bb8
feat(web-shell): modernize multi-workspace sidebar (#6804)
* feat(web-shell): modernize multi-workspace sidebar

* fix(web-shell): address sidebar review feedback

* fix(web-shell): address remaining review feedback

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-07-13 11:45:20 +00:00
Shaojin Wen
720d26b1bb
feat(web-shell): flesh out the multi-workspace split view (cross-workspace sessions, workspace labels, responsive layout) (#6746)
* fix(web-shell): surface cross-workspace sessions in split view & overview

The split-view "add session" picker and the Session Overview only listed the
primary workspace's sessions, so on a multi-workspace daemon
(`qwen serve --workspace A --workspace B …`) sessions in non-primary
workspaces could not be picked into a split pane or triaged from the overview
— even though the sidebar already lists them and the load path already drives
them.

Both surfaces now merge the primary workspace's sessions with the live
sessions of every other trusted workspace, label each session by its
workspace, and — in the split view — attach each pane under its session's own
workspace so a non-primary session no longer 409s against the primary cwd. On
a single-workspace daemon the behavior is unchanged.

- add useOtherWorkspaceSessions: fans out listWorkspaceSessions over the
  non-primary trusted workspaces in capabilities.workspaces (Promise.allSettled,
  tolerant of one failing), and empty on a single-workspace daemon
- SplitView: merge the lists, tag picker items by workspace, and pass each
  pane's workspaceCwd to its DaemonSessionProvider
- SessionOverviewPanel: merge the lists and add a per-card workspace badge
- add utils/workspace helpers, plus unit tests across all four surfaces

* fix(web-shell): repair stale composerTagIcons import that broke the build

ScheduledTasksDialog imported getComposerTagIconUrl from `../composerTagIcons`,
a module deleted when the helper was consolidated into `utils/composerTag.ts`.
The stale path failed `vite build` (and the web-shell browser-regression e2e).
Point it at the current location.

* fix(web-shell): address review feedback for cross-workspace sessions

- useOtherWorkspaceSessions: return the shared EMPTY sentinel when every
  workspace responds empty (skip a no-op re-render), guard the load effect
  against a stale in-flight fetch overwriting a newer one, and keep the
  single-workspace path fully synchronous (no fetch, no post-render setState)
- fetch non-primary workspaces at the primary list's page size so a busy
  workspace is not truncated at the daemon's smaller default
- split picker: tag primary sessions with the same localized "primary" label
  the Session Overview uses, so both surfaces read consistently
- tests: cover reload(), the poll re-query, empty-primary merge, and the
  primary badge render; quieten the act() warnings

* feat(web-shell): show each split pane's workspace in its composer placeholder

In a multi-workspace split, once a session becomes a pane there was nothing
indicating which workspace it belongs to. Name the pane's own workspace in the
composer placeholder ("Message this session in <workspace>…"), so it's clear
which workspace a message is going to before you send it. Single-workspace
daemons are unchanged.

* Revert "feat(web-shell): show each split pane's workspace in its composer placeholder"

This reverts commit c58e667127.

* feat(web-shell): label each split pane's workspace in its composer toolbar

On a multi-workspace daemon, split-view panes can hold sessions from
different workspaces, but nothing showed which workspace a pane's message
would go to. Add a compact, non-interactive workspace chip to the pane
composer toolbar (next to where the git-branch chip sits), mirroring
GitBranchIndicator.

The chip renders only on a multi-workspace daemon, is fed each pane's
workspace explicitly by the split view (which knows it per session), and
keeps its label visible as panes narrow — only tightening and truncating
rather than collapsing to an icon — since it is the pane's identity.

* feat(web-shell): show the workspace chip in the main composer too

Extend the per-pane workspace label to the main (single-session) chat
composer: on a multi-workspace daemon the composer toolbar now names the
current session's workspace, so it is always clear which workspace a
message targets — not only in split view.

Place the workspace chip before the git-branch chip. Reuses the existing
WorkspaceIndicator and `workspace` toolbar action, fed from the active
connection's capabilities and workspace cwd.

* fix(web-shell): keep a session when a shrink closes the split view

When the viewport shrinks below the large-screen breakpoint the split
view auto-closes and folds back to the single chat. If that chat had no
session of its own — the common case when the split was opened straight
from the Session Overview or a `?split=a,b` link — the user was stranded
on an empty "new chat". Fall back to the split's first pane instead.

Best-effort and gated to the uncontrolled (standalone) split: a load
failure (e.g. a non-primary-workspace session the single connection
cannot own) simply leaves the empty chat, i.e. the previous behavior.

* fix(web-shell): restore the split view when the screen grows back

A shrink below the large-screen breakpoint folds the split view down to
the single chat (its entry points are hidden on small screens). That fold
used to be permanent — widening the window back left the user on a single
chat with their panes gone. Fold it away only temporarily instead:
remember that a shrink folded it, and restore the same split once the
screen grows back past the breakpoint, so a transient resize is lossless.

Standalone/uncontrolled split only; a controlled host still owns its own
split lifecycle. While folded, the narrow chat still falls back to the
split's first pane so it isn't an empty new chat.

* fix(web-shell): keep the chat's git branch when folding the split on shrink

Folding the split on a shrink no longer switches the single chat's
session. The previous shrink-time loadSession(firstPane) re-pointed the
main connection at the split's first pane; when that pane lived in a
different (e.g. git-less) workspace it wiped the chat's git branch — and
more broadly changed the session/URL the user drops back to. Fold the
split away without touching the chat's connection, so its session, git
branch and URL are exactly what they were once the screen grows back.

* feat(web-shell): auto-collapse the sidebar in a narrow split view

In split view the session sidebar competes with the panes for width. Below
1200px it now auto-collapses to its icon rail so the panes get the room,
and expands again once the window grows back — the session list and the
"New chat" label no longer eat space a narrow split needs. A wide split
(>= 1200px) keeps the full sidebar and the user's own collapse preference;
nothing changes outside split view.

* test(web-shell): cover the cross-workspace fetch race, quiet split-picker act() warnings

Address review suggestions on the cross-workspace session listing:
- useOtherWorkspaceSessions: add a test that a stale in-flight fetch is
  discarded by the `cancelled` guard when the target workspace set changes
  mid-flight (a workspace registered/unregistered while a list is loading).
- SplitView: flush after opening the picker so the reload()/reloadOther()
  the picker-open effect fires no longer leak act() warnings in the two
  cross-workspace tests.

* test(web-shell): use a valid DaemonMode and assert the pane workspace prop

Two review suggestions on the cross-workspace tests:
- workspace.test.ts: the caps() mock used `mode: 'workspace'`, not a valid
  DaemonMode (`'http-bridge' | 'native'`) — use `'native'`.
- SplitView.test.tsx: the ChatPane mock now captures `workspaceCwd`, and the
  cross-workspace attach test asserts the pane receives it (for the composer
  chip) — so dropping that prop pass-through would now fail the test rather
  than only the provider's `data-workspace`.

* perf(web-shell): memoize pane toolbar actions; cover chip tooltip fallback & deep-link remount

Address review suggestions on the composer workspace chip:
- ChatPane: memoize `paneToolbarActions` so its array identity is stable —
  ChatEditor is `React.memo`, and a fresh `[...]` each render defeated it,
  re-rendering the composer on every pane render.
- ChatEditor.test: cover the `workspaceTitle ?? workspaceName` tooltip
  fallback (chip rendered with a name but no explicit title).
- SplitView.test: cover the deep-link remount path — a pane mounted before
  the other-workspace fan-out resolves remounts under its own workspace once
  `workspaceCwdById` populates (pane key `b1:` → `b1:/wsB`).

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-07-12 11:55:21 +00:00
dreamWB
f23a177af4
feat(web-shell): improve composer model selector (#6758)
* feat(web-shell): improve composer model selector

* fix(web-shell): address selector review feedback
2026-07-12 11:28:58 +00:00
Shaojin Wen
6a4b57b248
fix(web-shell): keep composer branch chip from wrapping the toolbar on narrow screens (#6753)
The Git branch chip kept max-width: 180px on narrow screens while the
mode/model buttons collapse to icon-only, so inside the wrapping
.toolbarLeft it claimed roughly half the row and pushed mode/model onto
a second line — making the composer taller (#6725).

Within the existing @container (max-width: 699px) block, keep the leading
controls on one row (flex-wrap: nowrap) and let the branch chip yield
space first, truncating via its existing ellipsis down to just the icon
if needed (max-width: 140px; flex-shrink: 1). The mode/model buttons stay
fixed-size. Desktop (composer > 699px) is unchanged.
2026-07-12 06:51:01 +00:00
han
5b4d0a3583
feat(web-shell): show current git branch in composer toolbar (#6725)
* feat(web-shell): show current git branch in composer toolbar

* fix(web-shell): address git branch indicator review

* test(web-shell): cover git branch review suggestions

* test(web-shell): fix chat editor test lint

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-12 05:33:40 +00:00
dreamWB
24bca9a718
feat(web-shell): add context mention customization (#6578)
* feat(web-shell): add context mention customization

* fix(web-shell): address context mention review comments

* fix(web-shell): harden custom context rendering

* fix(web-shell): guard custom tag render fallbacks

* fix(web-shell): harden custom context rendering

---------

Co-authored-by: 易良 <1204183885@qq.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-10 08:46:43 +00:00
ytahdn
acfb00e1d5
feat(web-shell): add custom at mention panel (#6242)
* feat(web-shell): add custom at mention panel

* chore(web-shell): remove dev MCP resource server

* test(web-shell): cover at mention accept paths

* fix(web-shell): support keyboard at mention activation

* fix(web-shell): address at mention review feedback

* fix(web-shell): close stale at mention panels

* fix(web-shell): keep reopened at mention query empty

* fix(web-shell): address at mention review feedback

* fix(web-shell): harden at mention panel state

* fix(web-shell): stabilize at mention menu state

* fix(web-shell): cache at mention provider listings

* fix(web-shell): address at mention review follow-ups

* fix(web-shell): address at mention review threads

* fix(web-shell): address at mention review regressions

* fix(web-shell): relax auto at trigger cleanup

* chore: remove unrelated pr diff

* fix(web-shell): address at mention review followups

* fix(web-shell): harden at mention review edges

* test(web-shell): cover at mention disabled guards

* fix(web-shell): escape at mention provider delimiters

* fix(web-shell): escape unsafe at reference characters

* fix(web-shell): preserve escaped at mention context

* fix(web-shell): strip control chars from at mentions

* test(web-shell): cover at mention mcp resource guard

* fix(web-shell): propagate at panel text color

* test(web-shell): cover at mention provider failures

* fix(web-shell): handle escaped mcp resource searches

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-05 01:17:35 +00:00
Edenman
ad7e23f99f
feat(web-shell): add MCP mentions and iconized @ references (#6279)
* feat(web-shell): add MCP server mentions in @ completion

* fix(web-shell): polish @ completion groups

* feat(web-shell): add icons for @ references

* fix(web-shell): refine @ completion behavior

* fix(cli): show MCP mentions for bare @

---------

Co-authored-by: 克竟 <dingbingzhi.dbz@alibaba-inc.com>
2026-07-04 02:13:42 +00:00
Shaojin Wen
68ff698cd2
feat(web-shell): improve slash command discovery (taller menu, group counts, fuzzy search) (#6267)
* feat(web-shell): show more slash commands with category headers

The slash-command menu capped its visible height at exactly four rows, so
with 40+ merged commands users had to scroll a thin list to find anything,
and the built-in custom/skill/system grouping was only a faint 1px divider
with no label.

Raise the cap to min(12 rows, 40vh) and render the category name as a visible
header at each group boundary (custom / skill / system), keeping the divider
between groups. Sub-command menus are ungrouped and unchanged.

* feat(web-shell): fuzzy-match slash commands and show per-group counts

Typing in the slash menu now fuzzy-ranks commands with the same fzf engine the
TUI uses, so abbreviated input like "mdl" finds "model" and "arf" finds
"agent-reproduce-feature" — substring matching alone could not. An empty query
still browses the category-ordered list; a non-empty query switches to a flat
relevance-ranked list (headers are dropped since results interleave categories).

Each category header also shows how many commands the group holds (e.g. "Skill
commands  28"), so the volume hidden below the fold is visible at a glance.

The fzf index is built once per command set (keyed on the array identity) and
falls back to substring filtering if construction fails.

* refactor(web-shell): address slash menu review feedback

- Extract the section header/divider boundary logic into a pure
  `planSlashSectionRows` helper and unit-test it (headers at group
  boundaries, first row header without a divider, no repeated headers for
  adjacent duplicate sections, per-group counts). This also moves the
  section-count computation past the `!anchorRect` early return so it no
  longer runs on first render.
- Simplify `--slash-panel-max-height` to a round `min(460px, 45vh)` instead
  of a `12 * rowHeight` formula that ignored header/divider overhead and so
  showed only ~9-10 rows; the panel now shows ~12-13 rows.
- Log a warning when fzf fuzzy search throws before falling back to
  substring matching, so a silent failure is diagnosable.
- Add a completion test for the zero-match case returning null.
2026-07-03 14:56:06 +00:00
ytahdn
1467ed3100
fix(web-shell): defer session creation until first prompt (#6066)
* fix(web-shell): defer session creation until first prompt

* fix(web-shell): stabilize deferred session attach

* docs(web-shell): document optional session change callback

* test(web-shell): cover deferred session setup failures

* fix(webui): preserve concurrent session load on clear

* fix(web-shell): keep session prep helper in utils

* fix(web-shell): harden deferred session lifecycle

* fix(web-shell): guard deferred session races

* fix(web-shell): simplify controlled session selection

* fix(web-shell): tighten empty session edge cases

* fix(web-shell): tighten deferred session lifecycle

* test(webui): align session action mocks with daemon types

* fix(web-shell): notify cleared session ids

* fix(webui): guard session cleanup races

* fix(web-shell): preserve blocked local commands

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
Co-authored-by: 易良 <1204183885@qq.com>
2026-07-01 14:04:37 +00:00
ytahdn
4fd72b3ce3
feat(web-shell): polish chat UI and table rendering (#6099)
* feat(web-shell): polish chat UI and table rendering

* fix(web-shell): address review follow-ups

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-07-01 16:20:19 +08:00
carffuca
fce79dd10b
feat(web-shell): friendlier Esc interruption + queued-prompt UX (#6025)
* fix(web-shell): prevent queued-prompt loss from drain race

The auto-drain effect popped a queued prompt, called setQueuedPrompts,
then submitted via setTimeout(0). Because the daemon flips streamingState
asynchronously, the setState re-render could re-run the effect and pop a
second prompt before the first registered as streaming — both submitted
back-to-back and the first was lost.

Arm an "awaiting turn start" gate synchronously at pop so the re-run is
blocked until streamingState goes non-idle, released by a dedicated
effect with a safety-net timer for a prompt that never streams (e.g. a
queued slash command). Cleanup no longer cancels/re-queues the pending
submit while the gate is armed.

* feat(web-shell): friendlier Esc interruption + queued-prompt UX

* refactor(web-shell): tidy Esc/queue code per review

Behavior-preserving cleanups addressing review feedback on the Esc-interruption
and queued-prompt changes:

- Remove the now-dead queue.footer i18n key (EN + ZH) and the unreferenced
  .queuedHint CSS, orphaned when the Esc-clears-queue behavior was dropped.
- Co-locate the queued-prompt styles in QueuedPromptDisplay.module.css instead
  of reaching into the parent App.module.css.
- Make the Esc confirm-window constants the single source of truth: export them
  from escapeIntent.ts and drive the countdown-ring duration from one of them
  via a CSS custom property.
- Nudge the queue-drain safety net with a dedicated tick counter instead of
  cloning queuedPrompts, so it no longer re-renders the composer for a no-op.
- Drop a redundant !compact guard in StatusBar left over from flattening a
  ternary.
- Document the pop/gate-arm ordering invariant in the drain effect.
2026-06-30 01:27:16 +00:00
ytahdn
a384c4a21a
feat(web-shell): polish chat UI (#5893)
* style(web-shell): polish chat UI

* fix(web-shell): address chat polish review feedback

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-06-26 15:38:23 +00:00
ytahdn
ac2653e9ce
fix(web-shell): stabilize active prompt loading state (#5818)
* fix(web-shell): stabilize active prompt recovery

* fix(webui): settle restored prompts on cancellation

* fix(web-shell): avoid duplicate approval panels

* test(acp-bridge): expect inactive prompt snapshots

* fix(webui): preserve restored prompt activity in actions

* fix(webui): guard restored prompt activity during settlement

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-06-25 14:10:37 +08:00
ytahdn
83e09f2b5b
refactor(web-shell): restructure chat UI (#5775)
Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-06-23 22:43:19 +08:00