qwen-code/packages/vscode-ide-companion/package.json
dreamWB 5e4ff3755c
feat(vscode): add native context menu copy actions for webview chat (#3477)
* feat(vscode): add native context menu copy actions for webview chat

Add three right-click context menu items to the chat message area using
VSCode's native webview/context API:
- Copy Message: copies the right-clicked message's raw markdown content
- Copy All Messages: copies the full conversation in markdown format
- Copy Last Reply: copies the last assistant response

Implementation details:
- Commands registered in package.json with webview/context menu entries
- Clipboard writes go through extension host (vscode.env.clipboard) for
  reliability in webview sandbox
- Message identification via data-msg-idx stamped after render
- Tool-call outputs supported including diff format (git diff style)
- i18n support via package.nls.json (English) and package.nls.zh-cn.json
- Menu only shown in message area (not input box or empty state)

Closes #3052

* fix(vscode): wrap tool-call content text in code blocks for copy

* fix(vscode): only wrap tool-call content in code blocks for Copy All, not single Copy Message

* fix(vscode): route copy commands to the right-clicked webview and use dynamic code fences

* fix(vscode): use childIndexMap for copy-message routing and extract shared message handling

Replace the wrapper-div approach (which broke CSS layout) with a
render-time childIndexMap that maps DOM child positions to allMessages
indices. This avoids both the useLayoutEffect index-drift bug and the
wrapper-div CSS side effects.

- Remove data-msg-idx wrapper divs; messages render directly as
  container children, preserving original [&>*] CSS layout
- Build childIndexMap during MessageList render, skipping null items
  (empty AssistantMessage, hidden tool calls via shouldShowToolCall)
- findMessageIndex walks up from click target to container's direct
  child, then maps through childIndexMap
- Filter hidden tool calls and empty content in copyAllMessages
- Extract handleCommonWebviewMessage to deduplicate routing logic
  across sidebar, editor panel, and restored panel handlers
- Clear lastContextMenuProvider on dispose to prevent memory leaks

* fix(vscode): handle image messages in copy and resolve intermittent copy failure

- Copy Message on image messages now outputs markdown format ![image](path)
  instead of empty string
- Copy All Messages includes image messages as ![image](path) instead of
  skipping them
- Copy Last Reply skips empty assistant placeholders during streaming
- Resolve intermittent copy failure by pre-resolving message index on
  right-click instead of storing a DOM element reference that can become
  stale after React re-renders
2026-04-24 20:26:56 +08:00

311 lines
8.5 KiB
JSON

{
"name": "qwen-code-vscode-ide-companion",
"displayName": "Qwen Code Companion",
"description": "Enable Qwen Code with direct access to your VS Code workspace.",
"version": "0.15.2",
"publisher": "qwenlm",
"icon": "assets/icon.png",
"repository": {
"type": "git",
"url": "https://github.com/QwenLM/qwen-code.git",
"directory": "packages/vscode-ide-companion"
},
"engines": {
"vscode": "^1.85.0"
},
"license": "LICENSE",
"preview": true,
"categories": [
"AI"
],
"keywords": [
"qwen-code",
"qwen code",
"qwen",
"qwen code",
"cli",
"ide integration",
"ide companion"
],
"activationEvents": [
"onStartupFinished",
"onView:qwen-code.chatView.sidebar",
"onView:qwen-code.chatView.secondary",
"onCommand:qwen-code.openChat",
"onCommand:qwen-code.focusChat",
"onCommand:qwen-code.newConversation",
"onCommand:qwen-code.showLogs"
],
"contributes": {
"jsonValidation": [
{
"fileMatch": "**/.qwen/settings.json",
"url": "./schemas/settings.schema.json"
}
],
"viewsContainers": {
"activitybar": [
{
"id": "qwen-code-sidebar",
"title": "Qwen Code",
"icon": "assets/sidebar-icon.svg",
"when": "!qwen-code:supportsSecondarySidebar"
}
],
"secondarySidebar": [
{
"id": "qwen-code-secondary",
"title": "Qwen Code",
"icon": "assets/sidebar-icon.svg",
"when": "qwen-code:supportsSecondarySidebar"
}
]
},
"views": {
"qwen-code-sidebar": [
{
"type": "webview",
"id": "qwen-code.chatView.sidebar",
"name": "Qwen Code",
"icon": "assets/sidebar-icon.svg",
"when": "!qwen-code:supportsSecondarySidebar"
}
],
"qwen-code-secondary": [
{
"type": "webview",
"id": "qwen-code.chatView.secondary",
"name": "Qwen Code",
"icon": "assets/sidebar-icon.svg",
"when": "qwen-code:supportsSecondarySidebar"
}
]
},
"languages": [
{
"id": "qwen-diff-editable"
}
],
"commands": [
{
"command": "qwen.diff.accept",
"title": "Qwen Code: Accept Current Diff",
"icon": "$(check)"
},
{
"command": "qwen.diff.cancel",
"title": "Qwen Code: Close Diff Editor",
"icon": "$(close)"
},
{
"command": "qwen-code.runQwenCode",
"title": "Qwen Code: Run"
},
{
"command": "qwen-code.showNotices",
"title": "Qwen Code: View Third-Party Notices"
},
{
"command": "qwen-code.openChat",
"title": "Qwen Code: Open",
"icon": "./assets/icon.png"
},
{
"command": "qwen-code.auth",
"title": "Qwen Code: Auth"
},
{
"command": "qwen-code.focusChat",
"title": "Qwen Code: Focus Chat View"
},
{
"command": "qwen-code.newConversation",
"title": "Qwen Code: New Conversation"
},
{
"command": "qwen-code.showLogs",
"title": "Qwen Code: Show Logs"
},
{
"command": "qwen-code.copyMessage",
"title": "%qwen-code.copyMessage.title%"
},
{
"command": "qwen-code.copyAllMessages",
"title": "%qwen-code.copyAllMessages.title%"
},
{
"command": "qwen-code.copyLastReply",
"title": "%qwen-code.copyLastReply.title%"
}
],
"menus": {
"commandPalette": [
{
"command": "qwen.diff.accept",
"when": "qwen.diff.isVisible"
},
{
"command": "qwen.diff.cancel",
"when": "qwen.diff.isVisible"
},
{
"command": "qwen-code.auth"
},
{
"command": "qwen-code.copyMessage",
"when": "false"
},
{
"command": "qwen-code.copyAllMessages",
"when": "false"
},
{
"command": "qwen-code.copyLastReply",
"when": "false"
}
],
"webview/context": [
{
"command": "qwen-code.copyMessage",
"when": "webviewSection == 'chat-messages'",
"group": "9_cutcopypaste@1"
},
{
"command": "qwen-code.copyAllMessages",
"when": "webviewSection == 'chat-messages'",
"group": "9_cutcopypaste@2"
},
{
"command": "qwen-code.copyLastReply",
"when": "webviewSection == 'chat-messages'",
"group": "9_cutcopypaste@3"
}
],
"editor/title": [
{
"command": "qwen.diff.accept",
"when": "qwen.diff.isVisible",
"group": "navigation"
},
{
"command": "qwen.diff.cancel",
"when": "qwen.diff.isVisible",
"group": "navigation"
},
{
"command": "qwen-code.openChat",
"group": "navigation"
}
]
},
"configuration": {
"title": "Qwen Code",
"properties": {
"qwen-code.provider": {
"order": 0,
"type": "string",
"enum": [
"coding-plan",
"api-key"
],
"enumDescriptions": [
"Alibaba Cloud Coding Plan — configurable from VS Code Settings",
"Configured via Qwen Code: Auth or the onboarding button"
],
"default": "coding-plan",
"markdownDescription": "**Coding Plan**: enter API Key + Region here to sync `~/.qwen/settings.json`.\n\n**API Key**: use **Qwen Code: Auth** or the onboarding button to configure ModelStudio or custom OpenAI-compatible providers."
},
"qwen-code.apiKey": {
"order": 1,
"type": "string",
"default": "",
"markdownDescription": "API key used for **Coding Plan** settings sync. For **API Key** providers, configure the full provider details through **Qwen Code: Auth**."
},
"qwen-code.codingPlanRegion": {
"order": 2,
"type": "string",
"enum": [
"china",
"global"
],
"enumDescriptions": [
"China — 阿里云百炼 (aliyun.com)",
"Global — Alibaba Cloud (alibabacloud.com)"
],
"default": "china",
"markdownDescription": "Region for Coding Plan. _(Only used when Provider is `coding-plan`)_"
}
}
},
"keybindings": [
{
"command": "qwen.diff.accept",
"key": "ctrl+s",
"when": "qwen.diff.isVisible"
},
{
"command": "qwen.diff.accept",
"key": "cmd+s",
"when": "qwen.diff.isVisible"
},
{
"command": "qwen-code.focusChat",
"key": "ctrl+shift+l",
"mac": "cmd+shift+l"
}
]
},
"main": "./dist/extension.cjs",
"type": "module",
"scripts": {
"prepackage": "node ./scripts/prepackage.js",
"build": "npm run build:dev",
"build:dev": "npm run check-types && npm run lint && node esbuild.js",
"build:prod": "node esbuild.js --production",
"generate:notices": "node ./scripts/generate-notices.js",
"prepare": "npm run generate:notices",
"check-types": "tsc --noEmit",
"lint": "eslint src",
"watch": "npm-run-all2 -p watch:*",
"watch:esbuild": "node esbuild.js --watch",
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
"package": "vsce package --no-dependencies",
"test": "vitest run",
"test:ci": "vitest run --coverage",
"validate:notices": "node ./scripts/validate-notices.js"
},
"devDependencies": {
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/markdown-it": "^14.1.2",
"@types/node": "20.x",
"@types/react": "^19.2.10",
"@types/react-dom": "^19.2.3",
"@types/semver": "^7.7.1",
"@types/vscode": "^1.85.0",
"@typescript-eslint/eslint-plugin": "^8.31.1",
"@typescript-eslint/parser": "^8.31.1",
"autoprefixer": "^10.4.22",
"esbuild": "^0.25.3",
"eslint": "^9.25.1",
"eslint-plugin-react-hooks": "^5.2.0",
"npm-run-all2": "^8.0.2",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.18",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.14.1",
"@qwen-code/webui": "*",
"@modelcontextprotocol/sdk": "^1.25.1",
"cors": "^2.8.5",
"express": "^5.1.0",
"markdown-it": "^14.1.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"semver": "^7.7.2",
"zod": "^3.25.76"
}
}