Skip to content

Commit 86c2e9e

Browse files
committed
feat(core): 添加检查更新功能及相关日志支持
- 新增 checkForUpdates 工具函数,实现从市场拉取扩展最新版本并提示更新 - 在激活流程中增加日志记录功能,区分开发、生产及测试模式 - 生产模式激活时自动执行更新检查 - 新建日志模块,提供基于扩展模式的日志记录功能 - 扩展 deactivate 时记录日志并清理资源 - 新增对应的单元测试覆盖更新检查及日志模块 - 添加 showAlert RPC 接口及测试支持,实现消息提示功能 - 新增 ProgressShimmer 组件及样式,用于展示加载动画提示 - ToolBubble 组件中集成 ProgressShimmer,优化确认等待展示 - 更新测试环境配置,增加 vscode 模块的 mock 支持 - 添加 semver 依赖及其类型声明支持版本比较功能
1 parent 848a78a commit 86c2e9e

17 files changed

Lines changed: 2930 additions & 2 deletions

File tree

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/vscode-ide-companion/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
"react": "^19.2.0",
112112
"react-dom": "^19.2.0",
113113
"react-hook-form": "^7.81.0",
114+
"semver": "^7.8.5",
114115
"shadcn": "^4.12.0",
115116
"sonner": "^2.0.7",
116117
"vaul": "^1.1.2",
@@ -125,6 +126,7 @@
125126
"@types/node": "22.x",
126127
"@types/react": "^19.2.2",
127128
"@types/react-dom": "^19.2.2",
129+
"@types/semver": "^7.7.1",
128130
"@types/vscode": "^1.85.0",
129131
"@vitejs/plugin-react-swc": "^4.2.1",
130132
"@vscode/vsce": "^3.6.0",

packages/vscode-ide-companion/src/extension.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
import { getWebviewContent } from "./getWebviewContent.js";
1919
import { appRouter, type RouterContext } from "./router.js";
2020
import { attachRouterToPanel } from "@webview-rpc/host";
21+
import { createLogger } from "./utils/logger.js";
22+
import { checkForUpdates } from "./utils/checkForUpdates.js";
2123

2224
const DEFAULT_MODEL = "deepseek-v4-pro";
2325
const DEFAULT_BASE_URL = "https://api.deepseek.com";
@@ -26,6 +28,9 @@ type ReasoningMessageParams = {
2628
reasoning_content?: string;
2729
};
2830

31+
let log: (message: string) => void = () => {};
32+
let logger: vscode.OutputChannel;
33+
2934
export class DeepCodeViewProvider implements vscode.WebviewViewProvider {
3035
public static readonly viewType = "deepcode.chatView";
3136

@@ -279,8 +284,21 @@ export class DeepCodeViewProvider implements vscode.WebviewViewProvider {
279284
}
280285
}
281286

282-
export function activate(context: vscode.ExtensionContext): void {
287+
export async function activate(context: vscode.ExtensionContext): Promise<void> {
283288
process.env.NoDefaultCurrentDirectoryInExePath = "1";
289+
logger = vscode.window.createOutputChannel("Deep Code");
290+
log = createLogger(context, logger);
291+
log("Extension activated");
292+
293+
if (context.extensionMode === vscode.ExtensionMode.Development) {
294+
log("Running in Development mode");
295+
} else if (context.extensionMode === vscode.ExtensionMode.Production) {
296+
log("Running in Production mode");
297+
await checkForUpdates(context, log);
298+
} else {
299+
log("Running in Test mode");
300+
}
301+
284302
try {
285303
setShellIfWindows();
286304
} catch (error) {
@@ -326,8 +344,18 @@ export function activate(context: vscode.ExtensionContext): void {
326344
await vscode.commands.executeCommand("deepcode.chatView.focus");
327345
})
328346
);
347+
context.subscriptions.push(logger);
329348
}
330349

331350
export function deactivate(): void {
332-
// no-op
351+
try {
352+
log("Extension deactivated");
353+
} catch (err) {
354+
const message = err instanceof Error ? err.message : String(err);
355+
log(`Error during deactivation: ${message}`);
356+
} finally {
357+
if (logger) {
358+
logger.dispose();
359+
}
360+
}
333361
}

packages/vscode-ide-companion/src/router.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { initWRPC } from "@webview-rpc/host";
2+
import * as vscodeApi from "vscode";
23
import z from "zod";
34
import type { SessionManager, SkillInfo, UserToolPermission, PermissionScope } from "@vegamo/deepcode-core";
45

@@ -242,6 +243,11 @@ export const appRouter = router({
242243
await ctx.openSettings();
243244
return { ok: true };
244245
}),
246+
showAlert: procedure.input(z.string()).resolve(({ input }) => {
247+
console.log("[Extension] Received message:", input);
248+
vscodeApi.window.showInformationMessage(input);
249+
return { ok: true };
250+
}),
245251
});
246252

247253
export type AppRouter = typeof appRouter;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Vitest mock for the `vscode` module.
3+
*
4+
* VS Code is only available at runtime in the actual VS Code extension host.
5+
* This mock allows extension utility tests to resolve the import.
6+
*/
7+
8+
const vscodeMock = {
9+
window: {
10+
showInformationMessage: vi.fn(),
11+
showErrorMessage: vi.fn(),
12+
showTextDocument: vi.fn(),
13+
activeTextEditor: undefined,
14+
createOutputChannel: vi.fn(() => ({
15+
appendLine: vi.fn(),
16+
})),
17+
},
18+
commands: {
19+
executeCommand: vi.fn(),
20+
registerCommand: vi.fn(() => ({ dispose: vi.fn() })),
21+
},
22+
ExtensionMode: {
23+
Development: 1,
24+
Production: 2,
25+
Test: 3,
26+
},
27+
Uri: {
28+
file: vi.fn((path: string) => ({ fsPath: path })),
29+
},
30+
workspace: {
31+
workspaceFolders: [{ uri: { fsPath: "/test/workspace" } }],
32+
},
33+
env: {
34+
clipboard: { writeText: vi.fn() },
35+
machineId: "test-machine-id",
36+
},
37+
Position: vi.fn(),
38+
Selection: vi.fn(),
39+
Range: vi.fn(),
40+
TextEditorRevealType: {
41+
InCenter: 1,
42+
},
43+
WebviewViewProvider: class {},
44+
ExtensionContext: class {},
45+
};
46+
47+
(globalThis as any).vi = vi;
48+
49+
export default vscodeMock;
50+
export const window = vscodeMock.window;
51+
export const commands = vscodeMock.commands;
52+
export const ExtensionMode = vscodeMock.ExtensionMode;
53+
export const Uri = vscodeMock.Uri;
54+
export const workspace = vscodeMock.workspace;
55+
export const env = vscodeMock.env;
56+
export const Position = vscodeMock.Position;
57+
export const Selection = vscodeMock.Selection;
58+
export const Range = vscodeMock.Range;
59+
export const TextEditorRevealType = vscodeMock.TextEditorRevealType;
60+
export const WebviewViewProvider = vscodeMock.WebviewViewProvider;
61+
export const ExtensionContext = vscodeMock.ExtensionContext;

packages/vscode-ide-companion/src/tests/setup.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,28 @@ import { vi } from "vitest";
1111
// Global mocks
1212
// ---------------------------------------------------------------------------
1313

14+
// Mock VS Code module for extension utilities
15+
vi.mock("vscode", () => ({
16+
window: {
17+
showInformationMessage: vi.fn(),
18+
showErrorMessage: vi.fn(),
19+
showTextDocument: vi.fn(),
20+
activeTextEditor: undefined,
21+
},
22+
commands: {
23+
executeCommand: vi.fn(),
24+
registerCommand: vi.fn(),
25+
},
26+
ExtensionMode: {
27+
Development: 1,
28+
Production: 2,
29+
Test: 3,
30+
},
31+
Uri: {
32+
file: vi.fn((path: string) => ({ fsPath: path })),
33+
},
34+
}));
35+
1436
// Mock window.matchMedia
1537
Object.defineProperty(window, "matchMedia", {
1638
writable: true,
@@ -44,6 +66,7 @@ class IntersectionObserverMock implements IntersectionObserver {
4466
rootMargin: string = "";
4567
thresholds: ReadonlyArray<number> = [];
4668
constructor() {}
69+
scrollMargin: string = "";
4770
}
4871
window.IntersectionObserver = IntersectionObserverMock as unknown as typeof IntersectionObserver;
4972

0 commit comments

Comments
 (0)