* feat(web-shell): add git branch picker, commit dialog, and create PR flow Add an IntelliJ-style branch picker popover to the web shell git workspace, accessible from the branch chip in both the composer toolbar and sidebar. The picker provides search-filtered branch listing (local, remote, tags, recent), branch checkout, new branch creation, pull, push, and a commit view integrated into the existing GitDialog. The commit view reuses the diff panel (expandable file diffs with syntax highlighting, fullscreen support) and adds a commit message textarea with Commit / Commit and Push buttons. When a session is available (or one is auto-created), the commit message and PR title/body are generated via the model using session side-queries (btwSession), giving the agent full conversation context for accurate generation. The Create PR flow provides an inline form with auto-detected base branch and model-generated title/description, backed by a new daemon route that shells out to gh pr create. New daemon routes: - GET /workspaces/:workspace/git/branches - POST /workspaces/:workspace/git/checkout - POST /workspaces/:workspace/git/branch - POST /workspaces/:workspace/git/push - POST /workspaces/:workspace/git/pull - POST /workspaces/:workspace/git/commit - POST /workspaces/:workspace/github/prs/create - GET /workspaces/:workspace/github/default-branch * fix(web-shell): resolve correct workspace session for AI generation The commit message and PR title/body generation now resolves the most recent session for the target workspace via listWorkspaceSessions, rather than using the globally active connection.sessionId which may belong to a different workspace or session. Falls back to creating a new session only when no sessions exist for the workspace. Also improves the PR body editor with an Edit/Preview toggle using the existing Markdown component, and updates the generation prompt to follow the project PR template structure from AGENTS.md. * fix(web-shell): stabilize session resolver prop to prevent infinite re-generation Pass resolveSessionForWorkspace as a stable useCallback reference instead of an inline arrow function. The inline function created a new reference on every App render, causing the GitDialog useEffect to abort and restart generation in an infinite loop. * fix(web-shell): group remote branches by remote name and add PR target branch dropdown Remote branches in the branch picker are now grouped by remote (origin, upstream, etc.) with sub-headers, making fork workflows clear. The PR create form's base branch field is now a select dropdown populated from the workspace's branch list, grouped by remote with optgroup labels, instead of a free-text input. * fix(web-shell): use ref for session resolver to prevent effect re-run abort When resolveSessionForWorkspace creates a new session, it updates connection.sessionId in the provider, which changes the useCallback reference, which triggers the useEffect to re-run and abort the in-flight btwSession generation. Store the callback in a ref so the effect never depends on its identity. * fix(web-shell): resolve session per workspace, not from global active session The commit/PR generation effects used connection.sessionId directly without checking if it belongs to the target workspace. When opening the commit dialog from a sidebar workspace different from the active session's workspace, the wrong session was used for generation, producing incorrect content. Now always routes through resolveSessionForWorkspace(workspaceCwd) which checks workspace membership before reusing the active session. Also replaces 'PR' with 'Pull Request' / '合并请求' in all UI strings and adds error logging to the generation catch blocks. * fix(web-shell): retry btwSession with fresh session when stale session detected When listWorkspaceSessions returns a session that no longer exists in the daemon's memory (e.g. after daemon restart), btwSession fails with 'No session with id ...'. The generation effects now catch this error, force-create a new session via resolveSessionForWorkspace(cwd, true), and retry the btwSession call once. Both btwWithRetry and resolveSessionForWorkspace are stored in refs to avoid useEffect dependency chain aborts. * fix(web-shell): base PR generation on branch diff, not working tree PR title/body generation now fetches the commit log between the resolved base branch and HEAD (git log <base>..HEAD) plus any uncommitted changes, instead of only the working tree diff. The base branch is resolved inside the effect's promise chain (not from state) to avoid stale values and dependency warnings. Also adds range parameter support to fetchGitLog and workspaceGitLog. * feat(web-shell): replace PR base branch select with searchable popover The native <select> for the PR target branch is replaced with a custom searchable popover (BranchSelect) that shows a search input and a filtered branch list grouped by remote. The default selection is the target repository's main branch (resolved via getDefaultBranch). Supports filtering by typing in the search box. * fix(web-shell): show full remote ref in branch select (origin/main) Branch select now stores and displays full remote refs like origin/main instead of stripped names. getDefaultBranch returns the full ref (origin/main) instead of stripping the prefix. When creating the PR, the remote prefix is stripped for the gh pr create --base flag (origin/main → main). * fix(web-shell): stop pointer propagation in branch picker list to prevent popover dismiss Radix Popover's outside-click detection was incorrectly firing when clicking section headers (Recent/Local/Remote/Tags) inside the popover content, causing the popover to close immediately. Adding onPointerDown stopPropagation on the list container prevents pointer events from reaching Radix's document-level handlers. * fix(web-shell): use onPointerDownOutside guard for branch picker popover The previous stopPropagation approach failed because Radix Popover uses capture-phase document listeners for outside-click detection, which fire before bubble-phase stopPropagation. The correct fix is onPointerDownOutside on PopoverContent: when Radix incorrectly fires the outside handler for a click that is actually inside the content (can happen with portal containers), we check contentRef.contains() and preventDefault to keep the popover open. * fix(web-shell): stop click propagation on branch picker popover content Root cause: the ChatEditor composer container has onClick that calls core.focus(), stealing focus from the popover. React synthetic events bubble through the React tree (not DOM tree), so portaled popover clicks reach the container handler. Radix then detects focus-outside and dismisses the popover. Fix: onClick stopPropagation on PopoverContent, matching the existing pattern in GitModePopover and ToolbarPopover which already have this fix with an explanatory comment. * docs: add PR verification screenshots for branch picker feature * fix(web-shell): mock useWorkspace in tests for BranchPickerPopover BranchPickerPopover calls useWorkspace() which requires DaemonWorkspaceProvider context. The existing WorkspaceSection and ChatEditor tests didn't provide this context, causing 5 test failures. Added vi.mock with importActual to preserve other exports while providing a mock useWorkspace. Also updated the git chip click test to reflect that clicking now opens the branch picker popover instead of directly calling onOpenGitDiff. * fix: harden git write paths against argument injection Address review feedback on the web-shell git surface: - Reject option/pathspec injection in git checkout ref and branch start point (isValidCheckoutRef), and terminate `git checkout` argv with `--`. - Drop `git log` range values that start with `-` and terminate the argv with `--` so a range can never be reinterpreted as `--output=<file>`. - Fix getDefaultBranch always falling back to origin/main: the promisified exec lacked `encoding: 'utf8'`, so stdout was a Buffer and .trim() threw. - Parameterize ghErrorMessage so `gh pr create` timeouts name the right command and duration; sanitize workspace paths in PR-create errors. - GitDialog: guard doCommit against double-submit (button + keyboard), and strip only a known remote prefix from the PR base so local branches with "/" are not mangled; use theme tokens for commit button/success colors. - BranchPickerPopover: guard checkout/new-branch behind busyAction, reset inline-input text on reopen, and hide the commit action when unavailable. Adds regression tests for the checkout/branch validation and the git log range guard. * fix(web-shell): address review feedback on branch picker PR (#7731) - Fix Commit+Push error masking: split try/catch so push failure reports alongside the successful commit SHA - Replace hardcoded screenshot path with captureScreenshot harness - Replace silent if-isVisible skip with explicit assertion in visual test - Add focus-visible style for search input accessibility - Fix CSS specificity for active PR tab hover state - Add viewChanges to actionsVisible search filter - Wrap toggleSection in useCallback to avoid unnecessary re-renders - Add windowsHide: true to getDefaultBranch subprocess - Fix trailing slash handling in mockDaemon git action routing - Add git methods to top-level client mock in tests - Remove dead branchPicker.commitSuccess i18n key - Remove dead .actionShortcut CSS class - Show generation failure feedback in commit message placeholder * fix(web-shell): address review feedback on branch picker PR (#7731) - Add workspace trust checks to bound git branch routes - Use workspace-scoped client in BranchPickerPopover (fixes wrong-workspace mutation) - Add branch name validation and -- terminator to gitCreateBranch - Filter refs/remotes/*/HEAD from branch listings - Force LC_ALL=C for reflog parsing (non-English locale fix) - Narrow 'could not resolve' error regex to avoid DNS false positives - Add range validation to git log (reject path traversal) - Fix commit+push error i18n (dedicated key instead of concatenation) - Add i18n for BranchSelect component strings - Fix commit tab ARIA attributes - Add onBranchChanged callback to handlePush - Add btw to mockDaemon isDaemonPath regex - Add workspace_github_prs to visuals spec capabilities - Add -- terminator regression test - Remove docs/pr-assets/ from repo * fix(web-shell): address review feedback on branch picker PR (#7731) * fix(cli): reject dash-prefixed branch name with 400 in branch route (#7731) * fix(web-shell): address review feedback on git branch picker (#7731) Security: - Clear GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/GIT_INDEX_FILE from git subprocess env to prevent repository redirection - Add strict mutation gate to all POST git branch routes - Add generation guard to qualified write routes - Fail closed on invalid ?cwd= in mutation routes (resolveContainedCwdOrFail) - Reject wrong-typed startPoint, fetchOnly, rebase, and PR options with 400 Correctness: - Filter remote symbolic refs (origin/HEAD) by %(symref) instead of /HEAD name suffix, preserving valid branches like feature/HEAD - Add git rev-parse --git-dir probe so non-git dirs get 404 instead of empty available:true - Push preserves existing upstream; only adds --set-upstream when unset, resolving the remote from branch config or the sole configured remote - git commit -a replaced with git add -A + git commit so untracked files displayed in the UI are included - Always pass --body to gh pr create to prevent interactive prompts - getDefaultBranch returns null instead of fabricating origin/main - Memoize workspaceByCwd client in BranchPickerPopover to fix infinite render loop - Move sessionId to a ref in GitDialog effects to prevent self-abort - Bound commit-message prompt to fit /btw 4096-char limit - Mark all platforms as unverified in PR template (no fabricated ✅) - Guard PR auto-fill effect against wiping user edits on reconnect Accessibility: - Add tabIndex and onKeyDown to commit-mode tab span - Add aria-label to BranchSelect trigger and search input Cleanup: - Remove dead CSS (.prInputSmall, .prSelect) - Remove 9 unused i18n keys - Add ^ to git log range validation regex - Add busyAction guard to handlePush/handlePull - Add unit tests for gitCommit, gitPull, and route input validation * fix(web-shell): address review feedback on git branch picker PR (#7731) - Change commit tab from <span> to <button> for keyboard accessibility - Move setCommitMsg('') to success-only branches so the message is preserved when push fails after a successful commit - Add mutate middleware and generationGuard to PR creation route, matching all other POST mutation routes - Set genFailed when session resolution returns undefined so the user sees the failure indicator instead of a silent empty textarea - Make PR number nullable when URL regex does not match instead of returning a misleading 0 - Add LC_ALL=C and LANG=C to gitEnv() so for-each-ref upstream track parsing is locale-independent - Validate setUpstream and force as booleans in handlePush, matching the existing validation in handlePull - Add missing workspaceCwd and available fields to test mocks - Use stable data-web-shell-git-branch attribute in e2e selector * fix(web-shell): address R5 review feedback on git branch picker PR (#7731) - Classify git errors on stdout+stderr instead of err.message to fix false-positive no_upstream on every push failure and dead nothing_to_commit classifier - Sanitize workspace paths and cap error message length in sendGitError - Fix remote branch checkout to strip remote prefix so git DWIM creates a local tracking branch instead of detaching HEAD - Restore keyboard accessibility on composer branch chip (span → button) - Trim startPoint in handleCreateBranch before forwarding to git - Return bare branch name from getDefaultBranch (strip remote prefix) - Fix i18n shortcut hint to show ⌘/Ctrl+Enter for cross-platform - Update aria-label to reflect git management menu, not just changes - Add available: true to mockDaemon gitDiff default payload - Add regression tests: upstream preservation, sole remote resolution, strengthened fetch-only with divergent remote commit - Add aria-expanded assertion to sidebar picker test Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(web-shell): address R6 review feedback on git branch picker PR (#7731) * fix(web-shell): address R7 review feedback on git branch picker PR (#7731) * fix(web-shell): address R8 review feedback on git branch picker PR (#7731) * fix(web-shell): address R9 review feedback on git branch picker PR (#7731) * fix(web-shell): address R10 review feedback on git branch picker PR (#7731) * fix(web-shell): address R11 review feedback on git branch picker PR (#7731) * fix(web-shell): address R12 review feedback on git branch picker PR (#7731) * fix(web-shell): address R13 review feedback on git branch picker PR (#7731) * fix(web-shell): address R14 review feedback on git branch picker PR (#7731) * fix(web-shell): address R15 review feedback on git branch picker PR (#7731) - Validate localName derived from remote-tracking ref to prevent option injection (e.g. origin/-f → git checkout -f) - Add gitCwd prop to BranchPickerPopover and pass it to all git SDK calls so worktree sessions target the correct directory - Add symlink-escape and non-existent-path tests for resolveContainedCwdOrFail - Pin initial branch name in makeRepo() with git init -b master - Add gitPull merge and rebase integration tests * fix(web-shell): address git branch picker review feedback (#7731) * fix(web-shell): address R6 review feedback on git branch picker PR (#7731) * fix(web-shell): address review feedback on git branch picker PR (#7731) - Strip GIT_CONFIG_GLOBAL/SYSTEM/NOSYSTEM in gitEnv to prevent inherited config redirection (consistent with extension/github.ts) - Pass gitCwd to workspaceGitBranches in GitDialog loadPrBranches so worktree sessions fetch branches from the correct repository - Add aria-expanded to collapsible branch section headers - Add happy-path tests for PR create (201) and default-branch (200) routes, including the null fallback to origin/main * fix(cli): add sendGenerationClosedError to POST routes and cover untested branches (#7731) * fix(web-shell): address review feedback on branch picker and PR creation (#7731) - Refresh branch list after push/pull to avoid stale ahead/behind counts - Add pre-flight check for unpushed branches before PR creation - Fix base branch prefix stripping when branch list is unavailable - Cap PR body file list at MAX_SUMMARY_CHARS to bound model prompt size - Add qualified route tests: trust guard, input validation, cwd containment * fix(web-shell): hoist MAX_SUMMARY_CHARS to module scope for PR body generation (#7731) * fix(web-shell): address review feedback for git branch picker (#7731) - Hoist onOpenCommit to useCallback to fix App.test.tsx prop stability test - Keep commit tab visible after navigating away (startedInCommit ref) - Add onClick handler to commit tab for navigation back to commit view - Fix branch-prefix strip mangling local branch names containing '/' - Update sessionIdRef after force-creating a stale session replacement - Pin core.hooksPath in test makeRepo for reliable rollback tests - Add test asserting --force-with-lease is used for force pushes * fix(web-shell): target the worktree for sidebar commits and harden git actions (#7731) Scope the sidebar commit dialog to the active session's worktree checkout (matching the composer path) so linked-worktree sessions commit to the right checkout, guard PR creation against a double-click race, and surface an error when an invalid branch name is submitted. Adds focused coverage for the branch picker action wiring and the git branch route validation paths. * fix(web-shell): address review feedback for git branch picker (#7731) --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com> Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| client | ||
| docs/examples/qwencode-viz | ||
| components.json | ||
| package.json | ||
| playwright.config.ts | ||
| playwright.visuals.config.ts | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.json | ||
| tsconfig.lib.json | ||
| vite.config.ts | ||
| vite.lib.config.ts | ||
| vitest.config.ts | ||
@qwen-code/web-shell
Qwen Code Web Shell 是面向浏览器的 daemon 会话终端 UI,可以作为 React 组件嵌入到其他项目中。
环境要求
- React:
^18.0.0 || ^19.0.0 - React DOM:
^18.0.0 || ^19.0.0 @qwen-code/webui:>=0.0.1@qwen-code/sdk:>=0.1.8- 浏览器环境需要能访问 Qwen Code daemon serve 的 HTTP 接口。
组件包会自动注入自身的 CSS(包括 Tailwind 编译产物),接入方不需要配置 Tailwind 或额外引入全局 CSS。
Tailwind 与 shadcn/ui
Web Shell 已配置 Tailwind CSS v4 和 shadcn/ui。shadcn 的 token 仅用于新增的 Tailwind/shadcn 组件;现有 CSS Modules 的主题色值保持不变。组件代码在仓库内, 可直接修改。
新增 UI 的约定
- 新增通用 UI 或交互组件时,优先使用 shadcn/ui 已提供的组件,再根据 Web Shell 的需求修改生成到仓库中的源码。已有且稳定的 CSS Modules 组件不要求为了统一而 重写。
- Tailwind class 使用标准的无前缀写法,例如
flex gap-2。发布构建会通过 PostCSS 将生成的选择器限制在 Web Shell root 和 portal root,并为全局动画、CSS property 注册增加 Web Shell 前缀,避免与接入方样式冲突。 - shadcn 颜色必须使用
background、primary、muted等语义 token,不要直接 引用 Web Shell 原有颜色变量。原有 CSS Modules 继续使用原来的 token,两套色值 各自维护。 - Dialog、Popover、DropdownMenu、Tooltip 等包含 Portal 的组件,必须将内容挂载到
Web Shell 的 portal root。新增 shadcn 组件后,应参考现有
dialog.tsx,使用useWebShellPortalRoot()向 Radix Portal 传入container。这样主题、旧 CSS 变量以及外部配置的 z-index 才能正确继承。 - 保留组件上的
data-web-shell-*属性和公开 CSS 变量。接入方可能通过这些属性或--web-shell-dialog-backdrop-z-index、--web-shell-popover-z-index、--web-shell-tooltip-z-index等变量定制样式和层级。
在 packages/web-shell 目录添加后续组件,例如:
npx shadcn@latest add button
生成后需要检查 diff。shadcn CLI 可能更新 globals.css、依赖或生成默认 Portal
实现,不应覆盖现有的 CSS scope、语义 token 和 portal root 适配。组件默认仅供
Web Shell 内部使用;没有明确的公共 API 需求时,不要从包入口导出。
Tailwind 会在发布前编译并内联到 npm 包,接入方不需要安装或配置 Tailwind,也不
需要额外引入 globals.css。
可选 Shadow DOM 隔离
宿主页面存在 *、h2、button 等全局规则时,可以按场景开启 Shadow DOM:
import customShadowStyles from './web-shell-shadow.css?inline';
<WebShellWithProviders
shadowDom={{
plugins: true,
portals: true,
styles: customShadowStyles,
}}
/>;
plugins隔离所有插件管理页面主体,包括统一的 Plugins 页面,以及/extensions、/mcp、/skills等兼容入口打开的页面。portals统一隔离 Web Shell 的所有弹窗层,包括 Dialog、Drawer、Popover、 DropdownMenu、Select 和 Tooltip;插件页面发起的弹窗也由这个开关管理。styles会追加到每个启用的 ShadowRoot,供 render props 等业务自定义内容继续 使用 class 样式。内联样式和通过 Web Shellstyle设置的 CSS 变量不需要迁移。--web-shell-portal-root-z-index控制 Shadow portal host 的整体层级,默认1000。需要与宿主自己的全局浮层协调时,可以通过 Web Shellstyle覆盖。shadowDom={true}是同时开启plugins和portals的简写。
默认不开启,现有 Light DOM 接入行为不变。两个场景相互独立,例如
{ plugins: true, portals: false } 会隔离插件页面主体,但所有弹窗仍挂载到原来的
Light DOM portal root。
Shadow 内部仍由原 React 树通过 portal 渲染,不会创建第二个 React root;props、
context、事件、ref 和状态语义保持不变。开启后,宿主普通选择器不会匹配 Shadow
内部节点,但宿主也无法再用普通选择器直接覆盖这些节点,所需定制样式应通过
shadowDom.styles 传入。
Web Shell 会在挂载 Shadow 内容前安装样式,并在浏览器支持时让多个 ShadowRoot 复用已经解析的 constructable stylesheet,以避免页面首次进入时的无样式闪烁和 重复解析 CSS。
图标约定
- 新增图标统一优先使用
lucide-react,不要为已有的常见图标重复编写 SVG。 - 使用具名静态导入,确保 Vite/Rollup 可以按需打包:
import { CheckIcon, XIcon } from 'lucide-react';
- 不要使用
import * as Icons后按名称动态取图标,这可能把整个图标库打入产物。 - 图标默认使用
currentColor,尺寸优先交给 shadcn 组件或 Tailwind class 控制, 避免在每个调用处重复添加颜色、margin 和 padding。 - 只有 Lucide 没有对应图标或需要产品专属图形时,才新增自定义 SVG。
安装
npm install @qwen-code/web-shell
Peer dependencies 需要同时安装:
npm install react react-dom @qwen-code/webui @qwen-code/sdk
接入方式
WebShell 提供两种接入形态:
1. 独立接入(自带 Provider)
适合只需要嵌入一个终端视图的场景。组件内部自建
DaemonWorkspaceProvider + DaemonSessionProvider。
import { WebShellWithProviders } from '@qwen-code/web-shell';
export function QwenCodePanel() {
return (
<WebShellWithProviders
baseUrl="http://127.0.0.1:4170"
token="your-bearer-token"
sessionId="838e1811-9f84-4848-9915-d9a7f01ff5c6"
onSessionIdChange={(sessionId) => {
console.log('current session:', sessionId);
}}
onSessionCreated={async (sessionId) => {
await registerSession(sessionId);
}}
theme="dark"
language="zh-CN"
/>
);
}
2. 共享 Provider 接入(纯消费者)
适合同一个 React 应用中多个视图共享同一个 daemon session 的场景(如 chat + terminal)。宿主自行提供 Provider,WebShell 只消费 hooks。
import {
DaemonWorkspaceProvider,
DaemonSessionProvider,
} from '@qwen-code/webui/daemon-react-sdk';
import { WebShell } from '@qwen-code/web-shell';
export function App() {
return (
<DaemonWorkspaceProvider baseUrl="http://127.0.0.1:4170" token="...">
<DaemonSessionProvider sessionId="...">
<ChatPanel />
<WebShell theme="dark" language="zh-CN" />
</DaemonSessionProvider>
</DaemonWorkspaceProvider>
);
}
注意:不要在已有
DaemonSessionProvider下使用WebShellWithProviders,否则会创建嵌套的重复 Provider。
3. 只读 ChatRecord JSONL
WebShellTranscript 只接收已经投影完成的 blocks,不连接 daemon,也不提供 composer、
审批或 session mutation。浏览器宿主可以逐行解析 JSONL,再通过 SDK 的 opt-in facade
投影:
import { projectChatRecordsToDaemonTranscript } from '@qwen-code/sdk/daemon/transcript';
import { WebShellTranscript } from '@qwen-code/web-shell';
const records = jsonl
.split(/\r?\n/)
.filter((line) => line.trim())
.map((line) => JSON.parse(line) as unknown);
const projection = projectChatRecordsToDaemonTranscript(records);
<WebShellTranscript
blocks={projection.blocks}
theme="dark"
language="zh-CN"
style={{ height: 640 }}
/>;
宿主应显示 projection.diagnostics,并在 complete=false 或 truncated=true 时提示
历史可能不完整。组件需要一个可用高度;自定义 renderer 的副作用仍由宿主负责。
Props
WebShellWithProviders
包含 WebShell 的所有 Props,加上 Provider 配置:
| 属性 | 类型 | 说明 |
|---|---|---|
baseUrl |
string |
daemon API 地址,未传时使用 window.location.origin |
token |
string |
daemon API Bearer token |
sessionId |
string |
要连接的 session id;未传或 undefined 时保持空页面 |
workspaceId |
string |
已注册工作区 id,主要用于定位已有 session;不会注册或锁定工作区 |
workspaceCwd |
string |
已注册工作区路径,语义同 workspaceId;不会注册或锁定工作区,且优先于 workspaceId |
lockWorkspaceCwd |
string |
锁定到指定工作区路径;未注册时自动持久注册,并隐藏其他工作区及添加、移除和选择入口 |
restartSseOnPrompt |
boolean |
每次 prompt 被 daemon 接收后重建 SSE;默认关闭 |
WebShell
| 属性 | 类型 | 说明 |
|---|---|---|
onSessionIdChange |
(sessionId: string | undefined, workspaceId?: string, workspaceCwd?: string) => void |
当前 session 或工作区变化时触发 |
onSessionCreated |
(sessionId: string) => Promise<void> | void |
新 session 创建后触发;完成前会阻塞 session 初始化和 prompt 提交,最长等待 30 秒 |
theme |
'dark' | 'light' |
UI 主题,默认 dark |
onThemeChange |
(theme: WebShellTheme) => void |
/theme 命令切换主题后触发 |
language |
'en' | 'zh-CN' | 'zh' | 'zh-cn' |
UI 语言 |
onLanguageChange |
(language: WebShellLanguage) => void |
/language ui 切换 UI 语言后触发 |
onSlashCommand |
(command: WebShellSlashCommand) => boolean | void |
斜杠命令进入默认处理前触发;返回 true 时由宿主接管并跳过默认行为 |
宿主可以监听命令,也可以返回 true 接管对应操作:
<WebShell
onSlashCommand={({ command, args, input }) => {
if (command !== 'deploy') return;
openDeployDialog({ environment: args, source: input });
return true;
}}
/>
回调在主聊天和分屏聊天中都会触发,也可以在 daemon 断连时处理纯宿主操作。
命令名后必须是空白或输入结束,因此 /usr/local/bin/tool 等绝对路径不会触发
回调。如果回调抛出异常,Web Shell 会报告错误并继续执行默认命令流程。
锁定工作区时,可以自定义 Sidebar 文件夹行的内容:
<WebShellWithProviders
lockWorkspaceCwd="/path/to/workspace"
sidebar={{
lockedWorkspace: {
render: (workspace, { expanded }) => (
<span>
{expanded ? '📂' : '📁'} {workspace.cwd}
</span>
),
},
}}
/>
自定义内容仍使用内置的展开、收起行为,expanded 会随状态更新;文件夹行右侧的内置操作不会渲染。
未提供 lockWorkspaceCwd 时,该 renderer 不会执行。
可选图表 Renderer
WebShell 支持宿主通过 customization.markdown.renderCodeBlock 接管特定
fenced code block 的渲染。图表类场景可以注册内置的
echarts-fulldata renderer:
import { createEchartsFullDataRenderer } from '@qwen-code/web-shell';
<WebShellWithProviders
markdown={{
renderCodeBlock: createEchartsFullDataRenderer({
loadEcharts: () => window.echarts,
resolveDataRef: async (ref, meta) =>
loadControlledChartDataset(ref, meta),
}),
}}
/>;
renderer 会把 echarts-fulldata code block 替换为图表卡片,并内置图表/数据
icon 切换;ECharts runtime 由宿主通过 loadEcharts 提供。若启用
data.kind="ref" envelope,数据只能通过宿主提供的 resolveDataRef 解析,
renderer 不会自己读取 URL 或本地路径。
如果需要让模型主动输出 echarts-fulldata block,宿主应在自己的 skills 来源中
提供对应 skill,并且只在确认当前 Web Shell 宿主已经注册 renderer 时启用。
@qwen-code/web-shell 不内置或自动加载这个 skill;可从
packages/web-shell/docs/examples/qwencode-viz/SKILL.md 复制模板到宿主的
.qwen/skills/qwencode-viz/SKILL.md,或通过宿主自己的 skill 注入机制提供等价
说明。
echarts-fulldata 的 block body 可以是旧版纯 JSON ECharts option,也可以是
{ "version": 1, "data": ..., "option": ... } envelope。新版 inline envelope
使用 data.dimensions: string[] 和 data.source array-of-arrays;renderer 会先
normalize 成原生 ECharts option,并注入 option.dataset,再渲染图表和数据视图。
新版 ref envelope 必须使用受控 artifact:// 或 session-file:// ref,并提供
data.format(csv 或 json)和 data.dimensions,这些元信息会传给宿主的
resolveDataRef(ref, meta)。
宿主应使用 JSON.parse 解析,不能用 eval、new Function 或 script injection
执行模型生成内容。
架构说明
@qwen-code/sdk/daemon ← 协议层(SSE, REST, normalizer)
@qwen-code/webui/daemon-react-sdk ← React adapter(Provider, hooks, store)
@qwen-code/web-shell ← 终端 UI 组件
WebShell必须在DaemonWorkspaceProvider和DaemonSessionProvider之下使用。WebShellWithProviders是内置 Provider 的便捷 wrapper。- 同一个 React 树共享一个
DaemonSessionProvider时只开一条 SSE。
已支持的斜杠命令
下面列出当前 web-shell 已支持的命令。支持方式分为两类:
- 本地实现:web-shell 前端直接打开弹窗、调用 daemon REST API,或切换本地状态。
- ACP 透传:web-shell 将命令发送给 daemon,由 daemon/ACP 执行。
| 命令 | 支持方式 | 说明 |
|---|---|---|
/help |
本地实现 | 打开帮助弹窗,支持键盘浏览命令和快捷键。 |
/theme |
本地实现 | 打开主题选择弹窗;支持 /theme light、/theme dark。 |
/settings |
本地实现 | 打开设置面板,管理工作区与用户级(~/.qwen/settings.json)配置;两个作用域均可编辑并写回对应的 settings.json。 |
/language |
本地实现 + ACP 透传 | /language ui <lang> 会切换 web-shell UI 语言并同步给 daemon;其他语言能力由 daemon 执行。包含 ui、output 子命令。 |
/model |
本地实现 + 部分透传 | 无参数打开模型弹窗;普通参数直接切换模型;/model --fast <model> 透传给 daemon。 |
/plan |
本地实现 | 切换到 plan approval mode,并可继续发送后续 prompt。 |
/approval-mode |
本地实现 | 打开审批模式弹窗或直接切换审批模式。 |
/mode |
本地实现 | web-shell 本地别名,用于切换审批模式。 |
/mcp |
本地实现 | 打开 MCP 管理弹窗。 |
/skills |
本地实现 + ACP 透传 | 无参数打开 skills 弹窗;带参数时透传给 daemon 执行。 |
/tools |
本地实现 | 打开 tools 弹窗,列表展示工具名称、启用状态和 description。 |
/memory |
本地实现 | 打开 memory 弹窗,支持 show、refresh、add user、add project 等分支。 |
/agents |
本地实现 | 打开 agents 弹窗,支持 manage、create user、create project 等分支。 |
/copy |
本地实现 | 复制最后一条 assistant 输出;支持 code、语言名、LaTeX、inline LaTeX 等选择器。 |
/release |
本地实现 | 释放 live session 连接,不删除历史会话记录。 |
/clear |
本地实现 | 清空当前 web-shell transcript store。 |
/new |
本地实现 | 创建新的 daemon session。 |
/reset |
本地实现 | 与 /new 一样创建新的 daemon session。 |
/rename <name> |
本地实现 | 修改当前 daemon session 的展示名称。 |
/resume |
本地实现 | 无参数打开恢复会话弹窗;带 session id 时直接加载。 |
/status |
ACP 透传 | daemon 支持,包含 paths 子命令。 |
/auth |
ACP 透传 | 连接 LLM provider。 |
/bug |
ACP 透传 | 提交错误报告。 |
/compress |
ACP 透传 | 通过摘要替换来压缩上下文。 |
/context |
ACP 透传 | 显示上下文窗口使用情况,包含 detail 子命令。 |
/diff |
ACP 透传 | 显示工作区相对 HEAD 的变更统计。 |
/docs |
ACP 透传 | 打开 Qwen Code 文档。 |
/doctor |
ACP 透传 | 执行安装与环境诊断,包含 memory 子命令。 |
/export |
ACP 透传 | 导出当前会话记录,包含 html、md、json、jsonl 子命令。 |
/goal |
ACP 透传 | 设置目标,并持续工作直到条件满足。 |
/init |
ACP 透传 | 分析项目并创建定制的 QWEN.md。 |
/stats |
ACP 透传 | 显示统计信息,包含 model、tools 子命令。 |
/summary |
ACP 透传 | 生成当前会话摘要。 |
/tasks |
ACP 透传 | 列出后台任务。 |
/insight |
ACP 透传 | 查看 insight 相关信息。 |