tabby-ai-workspace-manager 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +6 -4
- package/README.md +6 -4
- package/dist/adapters/sftp-read.d.ts +3 -0
- package/dist/arw-cli/commands.d.ts +2 -1
- package/dist/arw-cli/instructions.d.ts +2 -2
- package/dist/arw-tree/tree.component.d.ts +10 -1
- package/dist/arw-tree/tree.view.d.ts +1 -1
- package/dist/arw-tree/workspace-copy.d.ts +13 -0
- package/dist/arw.cjs +266 -29
- package/dist/build-meta.json +250 -107
- package/dist/cli-lifecycle.cjs +8 -0
- package/dist/index.js +911 -261
- package/dist/security/remote-path.d.ts +3 -0
- package/dist/shared/concurrency.d.ts +2 -0
- package/dist/shared/decode-text.d.ts +2 -0
- package/dist/shared/terminal-directory.d.ts +17 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Manage local and remote workspaces in Tabby's sidebar: browse and edit files, transfer folders, preview content, use Git, and launch local AI CLIs such as Codex or Claude to work through an existing SSH connection.
|
|
6
6
|
|
|
7
|
-
Package: `tabby-ai-workspace-manager` · CLI: `arw` · Version: `0.1.
|
|
7
|
+
Package: `tabby-ai-workspace-manager` · CLI: `arw` · Version: `0.1.1`
|
|
8
8
|
|
|
9
9
|
[Interface preview](docs/images/demo.png)
|
|
10
10
|
|
|
@@ -12,11 +12,11 @@ Package: `tabby-ai-workspace-manager` · CLI: `arw` · Version: `0.1.0`
|
|
|
12
12
|
|
|
13
13
|
Verified with Tabby 1.0.238, Node.js 22+, npm, and Git. Install and sign in to your AI CLI separately.
|
|
14
14
|
|
|
15
|
-
1. Download `tabby-ai-workspace-manager-0.1.
|
|
15
|
+
1. Download `tabby-ai-workspace-manager-0.1.1.tgz` from [GitHub Releases](https://github.com/SupremeJulyan/tabby-ai-workspace-manager/releases/tag/v0.1.1).
|
|
16
16
|
2. Open **Settings → Plugins → Open Plugins Directory** in Tabby, then run in that directory:
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
npm install --omit=dev --legacy-peer-deps /absolute/path/to/tabby-ai-workspace-manager-0.1.
|
|
19
|
+
npm install --omit=dev --legacy-peer-deps /absolute/path/to/tabby-ai-workspace-manager-0.1.1.tgz
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
3. Fully quit and restart Tabby.
|
|
@@ -109,12 +109,14 @@ File recovery diagnostics are stored at `<Tabby config directory>/omw/file-recov
|
|
|
109
109
|
npm ci
|
|
110
110
|
npm run check # Build and run all tests
|
|
111
111
|
npm run test:portable # Run portable host tests after building
|
|
112
|
-
npm run pack:plugin # Generate artifacts/tabby-ai-workspace-manager-0.1.
|
|
112
|
+
npm run pack:plugin # Generate artifacts/tabby-ai-workspace-manager-0.1.1.tgz
|
|
113
113
|
npm run dev -- /opt/Tabby/tabby
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
Development mode uses separate userData and plugin directories. See the [architecture documentation (Chinese)](docs/architecture.md) for implementation details.
|
|
117
117
|
|
|
118
|
+
See the [performance review (Chinese)](docs/performance.md) for the optimizations, benchmark results, and reproduction commands.
|
|
119
|
+
|
|
118
120
|
CI covers Linux, macOS, and Windows with Node.js 22 and 24. Linux runs the full suite; all three hosts run native Git, path, CLI lifecycle, IPC, file recovery, search/replace, packaging, and preview tests. GNU/Linux remote-shell fixtures run on Linux only; local Windows Git is not tested through Git Bash. This matrix targets the local Tabby host, not Windows SSH servers. A configured workflow is not evidence of a successful native run until CI executes.
|
|
119
121
|
|
|
120
122
|
## License
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
在 Tabby 侧栏管理本地与远程工作区:浏览和编辑文件、上传下载、预览内容、操作 Git,并启动 Codex、Claude 等本机 AI CLI,通过已有 SSH 连接开发远程项目。
|
|
6
6
|
|
|
7
|
-
插件:`tabby-ai-workspace-manager` · CLI:`arw` · 版本:`0.1.
|
|
7
|
+
插件:`tabby-ai-workspace-manager` · CLI:`arw` · 版本:`0.1.1`
|
|
8
8
|
|
|
9
9
|
[界面预览](docs/images/demo.png)
|
|
10
10
|
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
验证环境:Tabby 1.0.238、Node.js 22+、npm、Git。AI CLI 需另行安装并登录。
|
|
14
14
|
|
|
15
|
-
1. 从 [GitHub Releases](https://github.com/SupremeJulyan/tabby-ai-workspace-manager/releases/tag/v0.1.
|
|
15
|
+
1. 从 [GitHub Releases](https://github.com/SupremeJulyan/tabby-ai-workspace-manager/releases/tag/v0.1.1) 下载 `tabby-ai-workspace-manager-0.1.1.tgz`。
|
|
16
16
|
2. 在 Tabby 中打开 **Settings → Plugins → Open Plugins Directory**,进入该目录执行:
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
npm install --omit=dev --legacy-peer-deps /absolute/path/to/tabby-ai-workspace-manager-0.1.
|
|
19
|
+
npm install --omit=dev --legacy-peer-deps /absolute/path/to/tabby-ai-workspace-manager-0.1.1.tgz
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
3. 完全退出并重新启动 Tabby。
|
|
@@ -110,12 +110,14 @@ ARW 检查远程路径边界并拦截高危命令。审计记录位于 `<Tabby
|
|
|
110
110
|
npm ci
|
|
111
111
|
npm run check # 构建并运行全部测试
|
|
112
112
|
npm run test:portable # 构建后运行跨平台本机测试
|
|
113
|
-
npm run pack:plugin # 生成 artifacts/tabby-ai-workspace-manager-0.1.
|
|
113
|
+
npm run pack:plugin # 生成 artifacts/tabby-ai-workspace-manager-0.1.1.tgz
|
|
114
114
|
npm run dev -- /opt/Tabby/tabby
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
开发模式使用独立的 userData 和插件目录。实现细节见 [架构说明](docs/architecture.md)。
|
|
118
118
|
|
|
119
|
+
性能优化、基准对比及复现命令见 [性能审查与验证](docs/performance.md)。
|
|
120
|
+
|
|
119
121
|
CI 矩阵覆盖 Linux、macOS、Windows 的 Node.js 22 / 24:Linux 执行全部测试,三平台均执行原生 Git、路径、CLI 生命周期、IPC、文件恢复、搜索替换和打包预览测试。Linux shell/GNU 工具的远端测试仍在 Linux 上运行,不通过 Git Bash 冒充 Windows 本地 Git。该矩阵针对本机 Tabby;SSH 远端仍需满足前述 POSIX/GNU 工具要求,不代表支持 Windows 远端。未运行的 CI 不作为实机验证结果。
|
|
120
122
|
|
|
121
123
|
## License
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SFTPFileHandle } from 'tabby-ssh/typings/session/sftp';
|
|
2
|
+
/** Fill one owned buffer instead of retaining every chunk and copying them again at EOF. */
|
|
3
|
+
export declare function readSftpBytes(file: Pick<SFTPFileHandle, 'read'>, size: number, signal?: AbortSignal): Promise<Buffer>;
|
|
@@ -4,4 +4,5 @@ export declare const commands: Record<string, {
|
|
|
4
4
|
positional?: string[];
|
|
5
5
|
fields: string[];
|
|
6
6
|
}>;
|
|
7
|
-
export declare const help = "Usage: arw
|
|
7
|
+
export declare const help = "Usage: arw workspaces [--saved]\n arw COMMAND --id ID [arguments]\n\nWorkspace: workspaces, current-file\nRead: list, read, read-many, search, find, output\nWrite: write, edit, create, delete, chmod, move\nTransfer: upload, download\nGit: git-pull, git-push\nExecute: exec, batch\n\nRun arw COMMAND --help for arguments, limits and examples (no connection required).\nAll operations require --id except workspaces. --workspace aliases --id.\nUse arw workspaces to find the target ID. Remote paths start at workspaceRoot;\nlocal transfer paths start at the CLI directory. Terminal mode supports exec/output.\nOptions: -h/--help, -V/--version, --compact; exec --verbose returns JSON.\n";
|
|
8
|
+
export declare function helpForCommand(command: string): string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const cliGuidance = "# ARW remote workspace\n\
|
|
2
|
-
export declare const terminalCliGuidance = "# ARW terminal mode\n\nThis placeholder
|
|
1
|
+
export declare const cliGuidance = "# ARW remote workspace\n\nThis local directory is a placeholder, not a project copy or mount. Read .awm/workspace.json locally for metadata; use arw for all remote project files, Git, builds, tests and commands. Do not create or edit project files in the placeholder.\n\nRun arw workspaces and match host, port, user and workspaceRoot against the metadata (remoteRoot). Use --id ID for every operation on that forwarded workspace. Ask the user if ambiguous; if unavailable, have it reconnected in Tabby. Never switch targets implicitly.\n\nBefore working, discover and read applicable remote project instructions, including AGENTS.md at the project root and along the directories containing files you will change. They are not automatically loaded here.\n\nRun arw --help for the command index and arw COMMAND --help for syntax, path rules, limits and examples. Inspect incomplete results before drawing conclusions. Use arw output to continue stored command output instead of rerunning the command.\n\nKeep mutations inside workspaceRoot. Do not bypass POLICY_DENIED or expose connection URLs, tokens or credentials.\n";
|
|
2
|
+
export declare const terminalCliGuidance = "# ARW terminal mode\n\nThis local directory is a placeholder, not a project copy or mount. Read .awm/workspace.json locally for metadata; do not create or edit project files here.\n\nRun arw workspaces and verify the exact workspaceId from the metadata is forwarded. Use --id ID for each operation. If unavailable, reopen the AI terminal in terminal mode; never substitute another workspace with the same path.\n\nUse arw exec for all project operations in the existing terminal, and arw output for stored output. Keep operations inside remoteRoot from the metadata. The terminal must be at a shell prompt; if its directory changes, resynchronize it in Tabby and reopen the AI terminal.\n\nBefore working, discover and read applicable remote project instructions, including AGENTS.md at the project root and along the directories containing files you will change. They are not automatically loaded here.\n\nRun arw --help for the command index and arw COMMAND --help for syntax, terminal behavior, limits and examples. Inspect incomplete results and continue stored output instead of rerunning commands. Do not bypass POLICY_DENIED or expose connection URLs, tokens or credentials.\n";
|
|
@@ -140,6 +140,7 @@ export declare class ArwTreeComponent implements AfterViewInit, OnDestroy {
|
|
|
140
140
|
changes: GitChange[];
|
|
141
141
|
}>;
|
|
142
142
|
private version;
|
|
143
|
+
private treeAbort?;
|
|
143
144
|
private subscription;
|
|
144
145
|
projects: ArwProject[];
|
|
145
146
|
activeProject: string;
|
|
@@ -148,6 +149,11 @@ export declare class ArwTreeComponent implements AfterViewInit, OnDestroy {
|
|
|
148
149
|
commitMessages: Map<string, string>;
|
|
149
150
|
private browserWorkspace?;
|
|
150
151
|
private tabStates;
|
|
152
|
+
private workspaceTrees;
|
|
153
|
+
private workspaceFilters;
|
|
154
|
+
private rememberWorkspaceFilter;
|
|
155
|
+
private workspaceTreeKey;
|
|
156
|
+
private rememberWorkspaceTree;
|
|
151
157
|
private activeTab?;
|
|
152
158
|
private browsingKey;
|
|
153
159
|
private stateSnapshot;
|
|
@@ -176,7 +182,7 @@ export declare class ArwTreeComponent implements AfterViewInit, OnDestroy {
|
|
|
176
182
|
private loadProjects;
|
|
177
183
|
private restoreProjectForwarding;
|
|
178
184
|
get currentFolder(): string;
|
|
179
|
-
addProject(): Promise<void>;
|
|
185
|
+
addProject(root?: string): Promise<void>;
|
|
180
186
|
switchingWorkspace: boolean;
|
|
181
187
|
private projectSelectionRequest;
|
|
182
188
|
selectProject(project: ArwProject): Promise<void>;
|
|
@@ -230,6 +236,9 @@ export declare class ArwTreeComponent implements AfterViewInit, OnDestroy {
|
|
|
230
236
|
cancelGitSetup(): void;
|
|
231
237
|
submitGitSetup(): Promise<void>;
|
|
232
238
|
private showWorkspace;
|
|
239
|
+
private copyHostKey;
|
|
240
|
+
private pasteIntoWorkspace;
|
|
241
|
+
showBackgroundContextMenu(event: MouseEvent): void;
|
|
233
242
|
private downloadingNodes;
|
|
234
243
|
showNodeContextMenu(node: TreeNode, event: MouseEvent): void;
|
|
235
244
|
clickNode(node: TreeNode, event: MouseEvent): Promise<void>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const TREE_TEMPLATE = "\n<header class=\"sidebar-header\">\n <button class=\"brand\" (click)=\"toggleProjects()\" [attr.aria-expanded]=\"projectsExpanded\" aria-controls=\"arw-saved-workspaces\" [title]=\"'\u5C55\u5F00\u6216\u6536\u8D77\u5DE5\u4F5C\u533A\u5217\u8868' | arwTranslate\"><i class=\"brand-chevron fas\" [class.fa-chevron-down]=\"projectsExpanded\" [class.fa-chevron-right]=\"!projectsExpanded\"></i><span class=\"brand-mark\"><svg class=\"workspace-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m12 3 9 5-9 5-9-5 9-5Z\"/><path d=\"m3 12 9 5 9-5M3 16l9 5 9-5\"/></svg></span><span class=\"brand-title\">{{isRemote ? 'AI Remote Workspace' : 'AI Local Workspace'}}</span></button>\n <div class=\"header-tools\">\n <button class=\"icon-button\" [disabled]=\"!selection?.workspace || loading || terminalMode\" (click)=\"addProject()\" [title]=\"terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\u4E0B\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u6DFB\u52A0\u5F53\u524D\u76EE\u5F55\u5230\u5DE5\u4F5C\u533A' | arwTranslate)\" [attr.aria-label]=\"terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\u4E0B\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u6DFB\u52A0\u5F53\u524D\u76EE\u5F55\u5230\u5DE5\u4F5C\u533A' | arwTranslate)\"><i class=\"fas fa-plus\"></i></button>\n <button class=\"icon-button\" (click)=\"followTerminal()\" [disabled]=\"!selection?.terminal || loading\" [title]=\"'\u8DDF\u968F\u7EC8\u7AEF\u5F53\u524D\u76EE\u5F55\uFF08\u5355\u51FB\u4E00\u6B21\uFF09' | arwTranslate\" [attr.aria-label]=\"'\u8DDF\u968F\u7EC8\u7AEF\u5F53\u524D\u76EE\u5F55' | arwTranslate\"><i class=\"fas fa-location-arrow\"></i></button>\n <button *ngIf=\"isRemote\" class=\"icon-button\" [class.terminal-mode]=\"terminalMode\" [attr.aria-pressed]=\"terminalMode\" (click)=\"toggleTerminalMode()\" [title]=\"terminalMode ? ('\u5173\u95ED\u7EC8\u7AEF\u6A21\u5F0F\uFF0C\u6062\u590D\u666E\u901A\u8F6C\u53D1' | arwTranslate) : ('\u5F00\u542F\u7EC8\u7AEF\u6A21\u5F0F\uFF1A\u5728\u539F\u7EC8\u7AEF\u6267\u884C Agent \u547D\u4EE4' | arwTranslate)\" [attr.aria-label]=\"terminalMode ? ('\u5173\u95ED\u7EC8\u7AEF\u6A21\u5F0F' | arwTranslate) : ('\u5F00\u542F\u7EC8\u7AEF\u6A21\u5F0F' | arwTranslate)\"><i class=\"fas fa-terminal\"></i></button>\n <button class=\"icon-button\" (click)=\"refreshCurrentView()\" [disabled]=\"loading || gitLoading || gitSetupBusy\" [title]=\"'\u5237\u65B0\u5DE5\u4F5C\u533A' | arwTranslate\" [attr.aria-label]=\"'\u5237\u65B0\u5DE5\u4F5C\u533A' | arwTranslate\"><i class=\"fas fa-sync-alt\" [class.spinning]=\"manualRefreshing\"></i></button>\n </div>\n</header>\n<div id=\"arw-saved-workspaces\" class=\"saved-workspaces\" [class.switching-workspace]=\"switchingWorkspace\" [attr.aria-busy]=\"switchingWorkspace\" *ngIf=\"selection?.workspace && projectsExpanded\" role=\"tree\" [attr.aria-label]=\"'\u4FDD\u5B58\u7684\u5DE5\u4F5C\u533A' | arwTranslate\">\n <div class=\"project-row\" *ngFor=\"let project of projects\" role=\"treeitem\" [attr.aria-selected]=\"activeProject===project.id\" [class.selected]=\"activeProject===project.id\" [class.forwarding-failed]=\"isRemote && projectForwarding(project)==='error'\">\n <span *ngIf=\"isRemote && !terminalMode\" class=\"forwarding-status\" [class.forwarding-active]=\"projectForwarding(project)==='forwarding'\" [class.forwarding-starting]=\"projectForwarding(project)==='starting'\" [class.forwarding-error]=\"projectForwarding(project)==='error'\" [title]=\"projectForwardingTitle(project)\" [attr.aria-label]=\"projectForwardingTitle(project)\" role=\"img\" tabindex=\"0\"><i class=\"fas\" [class.fa-circle]=\"projectForwarding(project)==='forwarding' || projectForwarding(project)==='saved'\" [class.fa-circle-notch]=\"projectForwarding(project)==='starting'\" [class.fa-exclamation-triangle]=\"projectForwarding(project)==='error'\" aria-hidden=\"true\"></i></span>\n <button class=\"project-open\" [disabled]=\"switchingWorkspace\" (click)=\"selectProject(project)\" [title]=\"terminalMode ? project.root : projectForwardingTitle(project)\"><i *ngIf=\"terminalMode\" class=\"fas fa-terminal\" aria-hidden=\"true\"></i><svg *ngIf=\"!terminalMode\" class=\"workspace-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m12 3 9 5-9 5-9-5 9-5Z\"/><path d=\"m3 12 9 5 9-5M3 16l9 5 9-5\"/></svg><span class=\"file-name\">{{project.name}}</span></button>\n <button class=\"project-ai\" [disabled]=\"switchingWorkspace || activeProject!==project.id\" (click)=\"launchProject(project,$event)\" [attr.aria-label]=\"('\u6253\u5F00 AI \u7EC8\u7AEF\uFF1A' | arwTranslate)+project.name\" [title]=\"'\u4F7F\u7528\u8BBE\u7F6E\u4E2D\u7684 CLI \u6253\u5F00 AI \u7EC8\u7AEF' | arwTranslate\">AI</button>\n <button *ngIf=\"!terminalMode\" class=\"icon-button\" [disabled]=\"switchingWorkspace\" (click)=\"removeProject(project,$event)\" [title]=\"'\u79FB\u9664\u5DE5\u4F5C\u533A' | arwTranslate\" [attr.aria-label]=\"('\u79FB\u9664\u5DE5\u4F5C\u533A\uFF1A' | arwTranslate)+project.name\"><i class=\"fas fa-times\"></i></button>\n </div>\n <div class=\"project-empty\" *ngIf=\"!projects.length\">{{terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\uFF1A\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u70B9\u51FB\u9876\u90E8 + \u4FDD\u5B58\u5F53\u524D\u76EE\u5F55' | arwTranslate)}}</div>\n</div>\n<div class=\"current-directory\" [title]=\"selection?.workspace ? breadcrumb : workspaceLabel\">\n <span *ngIf=\"!selection?.workspace\" class=\"current-directory-label\">{{connectionLabel}}</span>\n <div *ngIf=\"selection?.workspace\" class=\"directory-completion\" [title]=\"('\u5F53\u524D\u76EE\u5F55\uFF1A' | arwTranslate)+directoryInput\">\n <input #directoryBox class=\"current-directory-path\" aria-autocomplete=\"inline\" [attr.aria-label]=\"'\u5F53\u524D\u76EE\u5F55' | arwTranslate\" autocomplete=\"off\" spellcheck=\"false\" [ngModel]=\"directoryInput\" (ngModelChange)=\"updateDirectoryCandidates($event)\" (keyup)=\"syncDirectoryCaret(directoryBox)\" (click)=\"syncDirectoryCaret(directoryBox)\" (select)=\"syncDirectoryCaret(directoryBox)\" (scroll)=\"syncDirectoryCaret(directoryBox)\" (blur)=\"closeDirectoryCandidates()\" (keydown.enter)=\"$event.preventDefault(); navigateDirectory()\" (keydown.escape)=\"closeDirectoryCandidates(); directoryDraft=null\" (keydown.arrowdown)=\"$event.preventDefault(); moveDirectoryCandidate(1)\" (keydown.arrowup)=\"$event.preventDefault(); moveDirectoryCandidate(-1)\" (keydown.arrowright)=\"completeDirectoryRight($event,directoryBox)\" (keydown.tab)=\"$event.preventDefault(); completeDirectory(directoryBox)\" />\n <div *ngIf=\"directoryGhost\" class=\"directory-ghost\" aria-hidden=\"true\"><span [style.transform]=\"'translateX(-'+directoryInputScroll+'px)'\"><span class=\"directory-ghost-prefix\">{{directoryInput}}</span><span>{{directoryGhost}}</span></span></div>\n </div>\n <button *ngIf=\"selection?.workspace\" class=\"icon-button\" [disabled]=\"!selection?.workspace || loading || terminalMode\" (click)=\"addProject()\" [title]=\"terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\u4E0B\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u6DFB\u52A0\u5F53\u524D\u76EE\u5F55\u5230\u5DE5\u4F5C\u533A' | arwTranslate)\" [attr.aria-label]=\"terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\u4E0B\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u6DFB\u52A0\u5F53\u524D\u76EE\u5F55\u5230\u5DE5\u4F5C\u533A' | arwTranslate)\"><i class=\"fas fa-arrow-up\"></i></button>\n <button *ngIf=\"selection?.workspace\" class=\"icon-button\" (click)=\"copyDirectory()\" [title]=\"'\u590D\u5236\u5F53\u524D\u76EE\u5F55' | arwTranslate\" [attr.aria-label]=\"'\u590D\u5236\u5F53\u524D\u76EE\u5F55' | arwTranslate\"><i class=\"far fa-copy\"></i></button>\n <span *ngIf=\"!selection?.workspace\" class=\"current-directory-path\">{{workspaceLabel}}</span>\n</div>\n <nav class=\"view-tabs\" [attr.aria-label]=\"'\u5DE5\u4F5C\u533A\u89C6\u56FE' | arwTranslate\">\n <button [class.selected]=\"mode==='files'\" (click)=\"setMode('files')\"><i class=\"far fa-folder\"></i> {{ '\u6587\u4EF6' | arwTranslate }}</button>\n <button [class.selected]=\"mode==='git'\" [disabled]=\"!selection?.workspace\" (click)=\"showGit()\"><i class=\"fas fa-code-branch\"></i> Git <span class=\"count\" *ngIf=\"changeCount\">{{changeCount}}</span></button>\n <button *ngIf=\"isRemote\" [class.selected]=\"mode==='activity'\" (click)=\"setMode('activity')\"><i class=\"fas fa-robot\"></i> {{ 'AI \u6D3B\u52A8' | arwTranslate }} <span class=\"count\" *ngIf=\"runningActivities\">{{runningActivities}}</span></button>\n </nav>\n<ng-container *ngIf=\"selection?.workspace || mode==='activity'; else noWorkspace\">\n <div class=\"search-box\" *ngIf=\"mode!=='activity'\"><i class=\"fas fa-search\"></i><input [ngModel]=\"filterText\" (ngModelChange)=\"updateFilter($event)\" [placeholder]=\"'\u7B5B\u9009\u5DF2\u52A0\u8F7D\u7684\u6587\u4EF6\u2026' | arwTranslate\" [attr.aria-label]=\"'\u7B5B\u9009\u6587\u4EF6' | arwTranslate\"><button *ngIf=\"filterText\" class=\"icon-button\" (click)=\"updateFilter('')\" [attr.aria-label]=\"'\u6E05\u9664\u7B5B\u9009' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <div class=\"browser-area activity-panel\" *ngIf=\"isRemote && mode==='activity'\">\n <div class=\"activity-overview\"><strong>{{runningActivities ? ('AI \u6B63\u5728\u64CD\u4F5C\u8FDC\u7A0B\u5DE5\u4F5C\u533A' | arwTranslate) : ('AI \u6D3B\u52A8' | arwTranslate)}}</strong><span>{{ '\u5F53\u524D\u6807\u7B7E\u9875 \u00B7 \u6700\u8FD1 200 \u6761' | arwTranslate }}</span></div>\n <div class=\"activity-controls\">\n <select [ngModel]=\"activityCategory\" (ngModelChange)=\"setActivityCategory($event)\" [attr.aria-label]=\"'\u6309\u64CD\u4F5C\u7C7B\u578B\u7B5B\u9009' | arwTranslate\"><option value=\"all\">{{ '\u5168\u90E8\u7C7B\u578B' | arwTranslate }}</option><option value=\"read\">{{ '\u8BFB\u53D6 / \u67E5\u8BE2' | arwTranslate }}</option><option value=\"write\">{{ '\u4FEE\u6539' | arwTranslate }}</option><option value=\"command\">{{ '\u547D\u4EE4' | arwTranslate }}</option></select>\n <select [ngModel]=\"activityStatus\" (ngModelChange)=\"setActivityStatus($event)\" [attr.aria-label]=\"'\u6309\u6267\u884C\u72B6\u6001\u7B5B\u9009' | arwTranslate\"><option value=\"all\">{{ '\u5168\u90E8\u72B6\u6001' | arwTranslate }}</option><option value=\"running\">{{ '\u6267\u884C\u4E2D' | arwTranslate }}</option><option value=\"success\">{{ '\u6210\u529F' | arwTranslate }}</option><option value=\"error\">{{ '\u5931\u8D25 / \u62E6\u622A' | arwTranslate }}</option></select>\n <button class=\"icon-button\" (click)=\"clearActivities()\" [title]=\"'\u6E05\u7A7A\u5DF2\u5B8C\u6210\u6D3B\u52A8\uFF0C\u4FDD\u7559\u5B89\u5168\u5BA1\u8BA1\u65E5\u5FD7' | arwTranslate\" [attr.aria-label]=\"'\u6E05\u7A7A\u5DF2\u5B8C\u6210\u6D3B\u52A8' | arwTranslate\"><i class=\"fas fa-trash-alt\"></i></button>\n </div>\n <div class=\"tree activity-timeline\" [attr.aria-label]=\"'AI \u8FDC\u7A0B\u64CD\u4F5C\u65F6\u95F4\u7EBF' | arwTranslate\" aria-live=\"polite\">\n <div class=\"empty-state compact\" *ngIf=\"!visibleActivities.length\"><i class=\"fas fa-robot\"></i><span>{{activities.length ? ('\u5F53\u524D\u7B5B\u9009\u6761\u4EF6\u4E0B\u6CA1\u6709\u6D3B\u52A8' | arwTranslate) : ('\u6682\u65E0 AI \u8FDC\u7A0B\u64CD\u4F5C' | arwTranslate)}}</span><small>{{ '\u901A\u8FC7 arw \u8F6C\u53D1\u7684\u8BFB\u53D6\u3001\u4FEE\u6539\u548C\u547D\u4EE4\u4F1A\u81EA\u52A8\u663E\u793A\uFF1B\u4E0D\u5305\u542B AI \u5BF9\u8BDD\u548C\u672C\u5730\u547D\u4EE4\u3002' | arwTranslate }}</small></div>\n <details class=\"activity-entry\" *ngFor=\"let activity of visibleActivities; trackBy: trackActivity\" [attr.data-status]=\"activity.status\">\n <summary><span class=\"activity-dot\" [class.spinning]=\"activity.status==='running'\"><i class=\"fas\" [class.fa-circle-notch]=\"activity.status==='running'\" [class.fa-circle]=\"activity.status!=='running'\"></i></span><span class=\"activity-title\">{{activityLabel(activity)}}<small class=\"activity-workspace\" [title]=\"activity.root\">{{activity.root}}</small><small>{{activity.command || activity.path || activity.root}}</small></span><span class=\"activity-state\">{{activityState(activity)}}</span></summary>\n <div class=\"activity-detail\"><div>{{activity.startedAt | date:'HH:mm:ss'}}<span *ngIf=\"activity.durationMs !== undefined\"> \u00B7 {{activity.durationMs}} ms</span></div><div>{{activity.host}} \u00B7 {{activity.root}}</div><div>{{ '\u5DE5\u4F5C\u533A\uFF1A' | arwTranslate }}{{activity.workspaceId}}</div><div>{{ '\u5DE5\u5177\uFF1A' | arwTranslate }}{{activity.operation}}</div><pre *ngIf=\"activity.command || activity.path\">{{activity.command || activity.path}}</pre><div *ngIf=\"activity.exitCode !== undefined\">{{ '\u9000\u51FA\u7801\uFF1A' | arwTranslate }}{{activity.exitCode}}</div><pre class=\"activity-error\" *ngIf=\"activity.error\">{{activity.error}}</pre></div>\n </details>\n </div>\n </div>\n <div class=\"error\" *ngIf=\"error\" role=\"alert\"><i class=\"fas fa-exclamation-circle\"></i><span>{{error}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissError()\" [attr.aria-label]=\"'\u5173\u95ED\u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <div class=\"browser-area\" *ngIf=\"mode==='files'\" (dragover)=\"dragLocalFiles($event)\" (dragleave)=\"leaveLocalFiles($event)\" (drop)=\"dropLocalFiles($event)\" [class.upload-drop]=\"dropTarget==='.'\">\n <div class=\"upload-status\" *ngIf=\"uploadBusy\" role=\"status\"><span>{{ '\u6B63\u5728\u4E0A\u4F20\uFF1A' | arwTranslate }}{{uploadPath}}</span><button (click)=\"cancelUpload()\">{{ '\u53D6\u6D88' | arwTranslate }}</button></div>\n <div class=\"section-heading\"><span><i class=\"fas fa-chevron-down\"></i> {{ '\u8D44\u6E90\u7BA1\u7406\u5668' | arwTranslate }}</span><span class=\"section-count\">{{visibleNodes.length}}</span></div>\n <div *ngIf=\"watchUnavailable\" class=\"watch-unavailable\" role=\"status\" [title]=\"watchUnavailable\"><i class=\"fas fa-eye-slash\"></i> {{ '\u672C\u5730\u6587\u4EF6\u76D1\u542C\u4E0D\u53EF\u7528\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0' | arwTranslate }}</div>\n <div *ngIf=\"snapshotUnavailable\" class=\"watch-unavailable\" role=\"status\" [title]=\"snapshotUnavailable\"><i class=\"fas fa-eye-slash\"></i> {{ '\u540E\u53F0\u6587\u4EF6\u68C0\u6D4B\u4E0D\u53EF\u7528\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0' | arwTranslate }}</div>\n <div class=\"directory-bar\" *ngIf=\"relativePath\"><button class=\"icon-button\" (click)=\"up()\" [disabled]=\"loading\" [title]=\"'\u8FD4\u56DE\u4E0A\u4E00\u7EA7' | arwTranslate\" [attr.aria-label]=\"'\u8FD4\u56DE\u4E0A\u4E00\u7EA7' | arwTranslate\"><i class=\"fas fa-arrow-left\"></i></button><span class=\"directory-path\" [title]=\"breadcrumb\">{{breadcrumb}}</span></div>\n <div class=\"tree\" role=\"tree\" [attr.aria-label]=\"'\u6587\u4EF6\u76EE\u5F55\u6811' | arwTranslate\">\n <div *ngIf=\"loading && manualRefreshing\" class=\"empty-state compact\"><i class=\"fas fa-circle-notch spinning\"></i><span>{{ '\u6B63\u5728\u8BFB\u53D6\u76EE\u5F55\u2026' | arwTranslate }}</span></div>\n <div *ngFor=\"let node of visibleNodes\" role=\"treeitem\" [attr.aria-expanded]=\"node.directory ? !!node.expanded : null\">\n <button class=\"file-row\" (dragover)=\"dragLocalFiles($event,node)\" (drop)=\"dropLocalFiles($event,node)\" [class.upload-drop]=\"dropTarget===node.path\" [class.folder-row]=\"node.directory\" [style.padding-left.px]=\"10+node.depth*16\" [title]=\"node.symlink ? (('\u7B26\u53F7\u94FE\u63A5\u4E0D\u652F\u6301\u5728\u6587\u4EF6\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\uFF1A' | arwTranslate) + node.path) : (node.directory ? ('\u53CC\u51FB\u8FDB\u5165\u76EE\u5F55' | arwTranslate) : node.path)\" (click)=\"clickNode(node,$event)\" (dblclick)=\"node.directory && !node.symlink ? open(node) : null\" (contextmenu)=\"showNodeContextMenu(node,$event)\">\n <i class=\"chevron fas\" [class.fa-chevron-right]=\"node.directory && !node.expanded\" [class.fa-chevron-down]=\"node.directory && node.expanded\" (click)=\"toggle(node); $event.stopPropagation()\" (dblclick)=\"$event.stopPropagation()\"></i>\n <svg *ngIf=\"node.directory\" class=\"file-icon folder-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path [attr.d]=\"node.expanded ? 'M3 17V5a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v2' : 'M3 7V5a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z'\"/><path *ngIf=\"node.expanded\" d=\"M5 10h17l-3 10H2l3-10Z\"/></svg><i *ngIf=\"!node.directory\" [class]=\"fileIcon(node)\"></i><span class=\"file-name\">{{node.name}}</span><i *ngIf=\"node.symlink\" class=\"fas fa-external-link-alt symlink-mark\"></i>\n </button>\n </div>\n <div *ngIf=\"!loading && !visibleNodes.length\" class=\"empty-state compact\"><i class=\"far fa-folder-open\"></i><span>{{filterText ? ('\u6CA1\u6709\u5339\u914D\u7684\u6587\u4EF6' | arwTranslate) : ('\u6B64\u76EE\u5F55\u4E3A\u7A7A' | arwTranslate)}}</span></div>\n </div>\n </div>\n <div class=\"browser-area\" *ngIf=\"mode==='git'\">\n <div class=\"section-heading\"><span><i class=\"fas fa-code-branch\"></i> {{ '\u6E90\u4EE3\u7801\u7BA1\u7406' | arwTranslate }}</span><button *ngIf=\"!gitLoading\" class=\"icon-button\" [disabled]=\"gitSetupBusy\" (click)=\"refreshCurrentView()\" [attr.aria-label]=\"'\u5237\u65B0 Git' | arwTranslate\" [title]=\"'\u91CD\u65B0\u626B\u63CF\u5F53\u524D\u76EE\u5F55\u4E2D\u7684 Git \u4ED3\u5E93' | arwTranslate\"><i class=\"fas fa-sync-alt\"></i></button><button *ngIf=\"gitLoading\" class=\"icon-button\" (click)=\"cancelGitScan()\" [attr.aria-label]=\"'\u53D6\u6D88 Git \u626B\u63CF' | arwTranslate\" [title]=\"'\u53D6\u6D88 Git \u626B\u63CF' | arwTranslate\"><i class=\"fas fa-stop\"></i></button></div>\n <div class=\"git-notice\" *ngIf=\"gitSetupBusy\">{{gitSetupProgress}}<button class=\"icon-button\" (click)=\"cancelGitSetup()\" [attr.aria-label]=\"'\u53D6\u6D88 Git \u521B\u5EFA' | arwTranslate\">{{ '\u53D6\u6D88' | arwTranslate }}</button></div>\n <div class=\"git-notice\" *ngIf=\"gitNotice\">{{gitNotice}}</div>\n <div class=\"tree git-tree\">\n <section class=\"git-repository\" *ngFor=\"let repository of repositories; trackBy: trackRepository\">\n <button class=\"repository-heading\" (click)=\"toggleRepository(repository)\" [attr.aria-expanded]=\"repository.expanded\" [title]=\"repository.root\"><i class=\"fas\" [class.fa-chevron-down]=\"repository.expanded\" [class.fa-chevron-right]=\"!repository.expanded\"></i><i class=\"fas fa-code-branch\"></i><span class=\"file-name\">{{repository.name}}</span><span class=\"count\">{{repository.count}}</span></button>\n <ng-container *ngIf=\"repository.expanded\">\n <div class=\"repository-path\" [title]=\"repository.root\">{{repository.root}}</div>\n <div class=\"repository-path\" *ngIf=\"repository.branch\"><i class=\"fas fa-code-branch\"></i> {{repository.branch}}</div>\n <div class=\"repository-path\" [title]=\"repository.syncError || ('\u57FA\u4E8E\u6700\u8FD1\u4E00\u6B21\u63D0\u53D6\u6216\u63A8\u9001\u8BB0\u5F55\uFF1B\u63D0\u53D6\u540E\u53EF\u68C0\u67E5\u4E0A\u6E38\u7684\u65B0\u63D0\u4EA4\u3002' | arwTranslate)\">{{gitSyncLabel(repository)}}</div>\n <div class=\"error\" *ngIf=\"repository.inspectorError\" role=\"alert\"><span>{{repository.inspectorError}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissRepositoryError(repository,'inspectorError')\" [attr.aria-label]=\"'\u5173\u95ED Git \u4FE1\u606F\u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <div class=\"git-inspector-tabs\" role=\"tablist\" [attr.aria-label]=\"'Git \u4FE1\u606F' | arwTranslate\"><button [class.selected]=\"repository.inspector==='history'\" (click)=\"showInspector(repository,'history')\" role=\"tab\" [attr.aria-selected]=\"repository.inspector==='history'\"><i class=\"fas fa-history\"></i> {{ '\u63D0\u4EA4\u8BB0\u5F55' | arwTranslate }} <span class=\"count\">{{repository.commits.length}}</span></button><button [class.selected]=\"repository.inspector==='branches'\" (click)=\"showInspector(repository,'branches')\" role=\"tab\" [attr.aria-selected]=\"repository.inspector==='branches'\"><i class=\"fas fa-code-branch\"></i> {{ '\u5206\u652F' | arwTranslate }} <span class=\"count\">{{repository.branches.length}}</span></button></div>\n <div class=\"git-inspector\" *ngIf=\"repository.inspector==='history'\"><div class=\"repository-clean\" *ngIf=\"!repository.commits.length\">{{repository.inspectorPending ? ('\u6B63\u5728\u8BFB\u53D6\u63D0\u4EA4\u8BB0\u5F55\u2026' | arwTranslate) : ('\u8FD8\u6CA1\u6709\u63D0\u4EA4\u8BB0\u5F55' | arwTranslate)}}</div><button class=\"git-history-item\" *ngFor=\"let commit of repository.commits; trackBy: trackCommit\" (click)=\"openCommit(repository,commit)\" [disabled]=\"repository.inspectorLoading\" [title]=\"commit.hash\"><span class=\"commit-subject\">{{commit.subject}}</span><span class=\"commit-meta\"><code>{{commit.shortHash}}</code> \u00B7 {{commit.author}} \u00B7 {{commitDate(commit)}}<span class=\"commit-sync\" [attr.data-sync]=\"commit.syncStatus\" [title]=\"repository.syncError || ('\u57FA\u4E8E\u6700\u8FD1\u5DF2\u77E5\u7684\u4E0A\u6E38\u8BB0\u5F55\uFF1B\u5DF2\u63A8\u9001\u548C\u5DF2\u62C9\u53D6\u8868\u793A\u672C\u6B21\u4F1A\u8BDD\u4E2D\u6210\u529F\u6267\u884C\u7684\u64CD\u4F5C\u3002' | arwTranslate)\">{{commitSyncLabel(commit)}}</span></span></button></div>\n <div class=\"git-inspector\" *ngIf=\"repository.inspector==='branches'\"><div class=\"repository-clean\" *ngIf=\"!repository.branches.length\">{{repository.inspectorPending ? ('\u6B63\u5728\u8BFB\u53D6\u5206\u652F\u2026' | arwTranslate) : ('\u6CA1\u6709\u53EF\u5207\u6362\u7684\u672C\u5730\u5206\u652F' | arwTranslate)}}</div><div class=\"git-branch-item\" *ngFor=\"let branch of repository.branches; trackBy: trackBranch\"><i class=\"fas\" [class.fa-check]=\"branch.current\" [class.fa-code-branch]=\"!branch.current\"></i><span class=\"file-name\">{{branch.name}}</span><span class=\"branch-current\" *ngIf=\"branch.current\">{{ '\u5F53\u524D' | arwTranslate }}</span><button *ngIf=\"!branch.current\" (click)=\"switchBranch(repository,branch)\" [disabled]=\"repository.busy\">{{ '\u5207\u6362' | arwTranslate }}</button></div><small class=\"git-inspector-hint\">{{ '\u4EC5\u5217\u51FA\u672C\u5730\u5206\u652F\u3002\u5207\u6362\u65F6\u4E0D\u4F1A\u81EA\u52A8\u8FD8\u539F\u672A\u63D0\u4EA4\u6539\u52A8\u3002' | arwTranslate }}</small></div>\n <div class=\"git-relay\"><button (click)=\"syncGit('fetch',repository)\" [disabled]=\"!canSyncGit(repository) || repository.busy || repository.loading || gitLoading\" [title]=\"'\u63D0\u53D6\u4E0A\u6E38\u72B6\u6001\uFF0C\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A' | arwTranslate\"><i class=\"fas fa-sync-alt\"></i> {{ '\u63D0\u53D6' | arwTranslate }}</button><button (click)=\"syncGit('pull',repository)\" [disabled]=\"!canSyncGit(repository) || repository.busy || repository.loading || gitLoading\" [title]=\"'\u62C9\u53D6\u4E0A\u6E38\u66F4\u65B0\uFF0C\u53EA\u5141\u8BB8\u5FEB\u8FDB\u5408\u5E76' | arwTranslate\"><i class=\"fas fa-arrow-down\"></i> {{ '\u62C9\u53D6' | arwTranslate }}</button><button (click)=\"syncGit('push',repository)\" [disabled]=\"!canSyncGit(repository) || repository.busy || repository.loading || gitLoading\" [title]=\"'\u63A8\u9001\u5F53\u524D\u5206\u652F\u5230\u4E0A\u6E38\uFF0C\u4E0D\u5F3A\u5236\u8986\u76D6' | arwTranslate\"><i class=\"fas fa-arrow-up\"></i> {{ '\u63A8\u9001' | arwTranslate }}</button></div>\n <div class=\"git-relay\" *ngIf=\"repository.relayProgress\" role=\"status\"><span><i class=\"fas fa-circle-notch spinning\"></i> {{repository.relayProgress}}</span><button type=\"button\" (click)=\"cancelRelay(repository)\" [disabled]=\"!repository.relayCancel\">{{ '\u53D6\u6D88' | arwTranslate }}</button></div>\n <div class=\"git-commit\"><textarea [ngModel]=\"commitMessage(repository)\" (ngModelChange)=\"setCommitMessage(repository,$event)\" rows=\"2\" [disabled]=\"repository.busy\" [attr.aria-label]=\"('\u63D0\u4EA4\u8BF4\u660E\uFF1A' | arwTranslate)+repository.name\" [placeholder]=\"'\u63D0\u4EA4\u8BF4\u660E\uFF08\u53EA\u63D0\u4EA4\u5DF2\u6682\u5B58\u66F4\u6539\uFF09' | arwTranslate\"></textarea><button (click)=\"commit(repository)\" [disabled]=\"repository.busy || !commitMessage(repository).trim()\" [title]=\"repository.groups[0]?.changes.length ? ('\u63D0\u4EA4\u5DF2\u6682\u5B58\u66F4\u6539' | arwTranslate) : ('\u8BF7\u5148\u6682\u5B58\u9700\u8981\u63D0\u4EA4\u7684\u66F4\u6539' | arwTranslate)\"><i class=\"fas fa-check\"></i> {{ '\u63D0\u4EA4' | arwTranslate }}</button></div>\n <div class=\"git-actions\" *ngIf=\"repository.count\"><button (click)=\"stageAll(repository)\" [disabled]=\"repository.busy || !repository.groups[1]?.changes.length\"><i class=\"fas fa-plus\"></i> {{ '\u5168\u90E8\u6682\u5B58' | arwTranslate }}</button><button (click)=\"unstageAll(repository)\" [disabled]=\"repository.busy || !repository.groups[0]?.changes.length\"><i class=\"fas fa-minus\"></i> {{ '\u5168\u90E8\u53D6\u6D88\u6682\u5B58' | arwTranslate }}</button><button class=\"discard-action\" (click)=\"discardChanges(repository)\" [disabled]=\"repository.busy || !repository.groups[1]?.changes.length\" [title]=\"'\u8FD8\u539F\u5168\u90E8\u672A\u6682\u5B58\u66F4\u6539\uFF0C\u4FDD\u7559\u5DF2\u6682\u5B58\u5185\u5BB9\uFF1B\u672A\u8DDF\u8E2A\u6587\u4EF6\u5C06\u5220\u9664' | arwTranslate\"><i class=\"fas fa-undo\"></i> {{ '\u5168\u90E8\u8FD8\u539F' | arwTranslate }}</button></div>\n <div class=\"git-success\" *ngIf=\"repository.message\" role=\"status\"><i class=\"fas fa-check-circle\"></i> {{repository.message}}</div>\n <div class=\"error\" *ngIf=\"repository.error\" role=\"alert\"><span>{{repository.error}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissRepositoryError(repository)\" [attr.aria-label]=\"'\u5173\u95ED Git \u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <section *ngFor=\"let group of repository.groups; trackBy: trackGitGroup\"><div class=\"git-group-heading\"><span>{{group.title}}</span><span class=\"count\">{{group.changes.length}}</span></div>\n <div class=\"git-change\" *ngFor=\"let change of filteredChanges(group.changes); trackBy: trackChange\"><button class=\"file-row\" [title]=\"change.path\" (click)=\"openChange(change,group.staged,repository)\"><i class=\"far fa-file-code file-icon\"></i><span class=\"file-name\">{{change.path}}</span><span class=\"git-status\" [class.added]=\"change.index==='?' || change.index==='A'\" [class.deleted]=\"change.working==='D' || change.index==='D'\">{{group.staged ? change.index : change.working}}</span></button><button class=\"icon-button git-change-action\" *ngIf=\"group.staged\" (click)=\"unstageChange(change,repository)\" [disabled]=\"repository.busy\" [title]=\"('\u53D6\u6D88\u6682\u5B58\uFF1A' | arwTranslate)+change.path\" [attr.aria-label]=\"('\u53D6\u6D88\u6682\u5B58\uFF1A' | arwTranslate)+change.path\"><i class=\"fas fa-minus\"></i></button><button class=\"icon-button git-change-action\" *ngIf=\"!group.staged\" (click)=\"stageChange(change,repository)\" [disabled]=\"repository.busy\" [title]=\"('\u6682\u5B58\uFF1A' | arwTranslate)+change.path\" [attr.aria-label]=\"('\u6682\u5B58\uFF1A' | arwTranslate)+change.path\"><i class=\"fas fa-plus\"></i></button><button class=\"icon-button git-change-action discard-action\" *ngIf=\"!group.staged\" (click)=\"discardChanges(repository,change)\" [disabled]=\"repository.busy\" [title]=\"('\u8FD8\u539F\u672A\u6682\u5B58\u66F4\u6539\uFF1A' | arwTranslate)+change.path\" [attr.aria-label]=\"('\u8FD8\u539F\u672A\u6682\u5B58\u66F4\u6539\uFF1A' | arwTranslate)+change.path\"><i class=\"fas fa-undo\"></i></button></div>\n </section>\n <div *ngIf=\"!repository.loading && !repository.error && !repository.count\" class=\"repository-clean\"><i class=\"far fa-check-circle\"></i> {{ '\u6CA1\u6709\u53D8\u66F4' | arwTranslate }}</div>\n </ng-container>\n </section>\n <div *ngIf=\"!gitLoading && !error && !gitNotice && !repositories.length\" class=\"empty-state compact\"><i class=\"fas fa-code-branch\"></i><span>{{gitEmptyMessage}}</span>\n <div class=\"git-setup-actions\" *ngIf=\"canSetupGit && !gitSetupMode\"><button (click)=\"openGitSetup('init')\" [disabled]=\"gitSetupBusy\">{{ '\u521D\u59CB\u5316 Git' | arwTranslate }}</button><button (click)=\"openGitSetup('clone')\" [disabled]=\"gitSetupBusy\">{{ '\u514B\u9686 Git \u9879\u76EE' | arwTranslate }}</button></div>\n </div>\n <form class=\"git-setup-form\" *ngIf=\"canSetupGit && gitSetupMode\" (ngSubmit)=\"submitGitSetup()\">\n <strong>{{gitSetupMode==='init' ? ('\u521D\u59CB\u5316 Git' | arwTranslate) : ('\u514B\u9686 Git \u9879\u76EE' | arwTranslate)}}</strong>\n <div class=\"git-setup-target\"><span>{{ '\u76EE\u6807\u76EE\u5F55' | arwTranslate }}</span><code [title]=\"gitSetupRoot\">{{gitSetupRoot}}</code></div>\n <label>{{ '\u7528\u6237\u540D' | arwTranslate }}<input name=\"gitUser\" [(ngModel)]=\"gitSetupName\" [disabled]=\"gitSetupBusy\" required autocomplete=\"off\" /></label>\n <label>{{ '\u90AE\u7BB1' | arwTranslate }}<input name=\"gitEmail\" type=\"email\" [(ngModel)]=\"gitSetupEmail\" [disabled]=\"gitSetupBusy\" required autocomplete=\"off\" /></label>\n <label>{{ '\u8FDC\u7A0B\u94FE\u63A5' | arwTranslate }}<input name=\"gitUrl\" [(ngModel)]=\"gitSetupUrl\" [disabled]=\"gitSetupBusy\" required placeholder=\"https://host/owner/project.git\" autocomplete=\"off\" /></label>\n <label *ngIf=\"gitSetupMode==='clone'\">{{ '\u65B0\u6587\u4EF6\u5939\u540D\u79F0' | arwTranslate }}<input name=\"gitDirectory\" [(ngModel)]=\"gitSetupDirectory\" [disabled]=\"gitSetupBusy\" [placeholder]=\"'\u7559\u7A7A\u4F7F\u7528\u4ED3\u5E93\u540D\u79F0' | arwTranslate\" autocomplete=\"off\" /></label>\n <small class=\"git-setup-hint\">{{gitSetupMode==='init' ? ('\u5728\u5F53\u524D\u76EE\u5F55\u521D\u59CB\u5316\uFF0C\u4EC5\u4E3A\u6B64\u4ED3\u5E93\u4FDD\u5B58\u7528\u6237\u540D\u3001\u90AE\u7BB1\u548C origin\u3002' | arwTranslate) : ('\u901A\u8FC7\u672C\u673A Git \u548C\u51ED\u636E\u514B\u9686\uFF0C\u4FDD\u5B58\u5230\u5F53\u524D\u76EE\u5F55\u4E0B\u7684\u65B0\u6587\u4EF6\u5939\u3002' | arwTranslate)}}</small>\n <div class=\"error\" *ngIf=\"gitSetupError\" role=\"alert\"><span>{{gitSetupError}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissError('gitSetupError')\" [attr.aria-label]=\"'\u5173\u95ED Git \u8BBE\u7F6E\u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <div class=\"git-setup-actions\"><button type=\"submit\" [disabled]=\"gitSetupBusy || !gitSetupName.trim() || !gitSetupEmail.trim() || !gitSetupUrl.trim()\">{{gitSetupMode==='init' ? ('\u521D\u59CB\u5316' | arwTranslate) : ('\u514B\u9686' | arwTranslate)}}</button><button type=\"button\" (click)=\"gitSetupBusy ? cancelGitSetup() : closeGitSetup()\">{{gitSetupBusy ? ('\u53D6\u6D88\u64CD\u4F5C' | arwTranslate) : ('\u53D6\u6D88' | arwTranslate)}}</button></div>\n </form>\n </div>\n </div>\n</ng-container>\n<ng-template #noWorkspace><div class=\"empty-state welcome\"><span class=\"empty-icon\"><svg class=\"workspace-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m12 3 9 5-9 5-9-5 9-5Z\"/><path d=\"m3 12 9 5 9-5M3 16l9 5 9-5\"/></svg></span><strong>{{isRemote ? ('\u8FDE\u63A5\u6210\u529F\uFF0C\u542F\u7528\u76EE\u5F55\u68C0\u6D4B' | arwTranslate) : ('\u4ECE\u7EC8\u7AEF\u5F00\u59CB' | arwTranslate)}}</strong><p>{{hint}}</p>\n<div class=\"cwd-setup\" *ngIf=\"isRemote\"><label>{{ '\u5F53\u524D\u8FDC\u7A0B shell' | arwTranslate }} <select [(ngModel)]=\"cwdShell\" [disabled]=\"cwdSetupPending\" [attr.aria-label]=\"'\u8FDC\u7A0B shell' | arwTranslate\"><option value=\"bash\">Bash</option><option value=\"zsh\">Zsh</option><option value=\"fish\">Fish</option></select></label><button class=\"setup-button primary\" [disabled]=\"!canEnableCwd || cwdSetupPending\" (click)=\"enableCwd()\"><i class=\"fas\" [class.fa-location-arrow]=\"!cwdSetupPending\" [class.fa-circle-notch]=\"cwdSetupPending\" [class.spinning]=\"cwdSetupPending\"></i>{{cwdSetupPending ? ('\u6B63\u5728\u83B7\u53D6\u76EE\u5F55\u2026' | arwTranslate) : ('\u542F\u7528\u8DEF\u5F84\u68C0\u6D4B\u5E76\u8DDF\u968F' | arwTranslate)}}</button><small>{{ '\u8BF7\u5728 shell \u547D\u4EE4\u63D0\u793A\u7B26\u4E0B\u70B9\u51FB\u3002\u4EC5\u5BF9\u5F53\u524D\u4F1A\u8BDD\u751F\u6548\uFF0C\u4E0D\u4FEE\u6539\u8FDC\u7AEF\u914D\u7F6E\u3002' | arwTranslate }}</small><button class=\"setup-button\" (click)=\"copyCwdHook()\"><i class=\"far fa-copy\"></i> {{ '\u590D\u5236\u547D\u4EE4\u624B\u52A8\u6267\u884C' | arwTranslate }}</button><div class=\"error\" *ngIf=\"cwdSetupError\" role=\"alert\"><span>{{cwdSetupError}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissError('cwdSetupError')\" [attr.aria-label]=\"'\u5173\u95ED\u76EE\u5F55\u68C0\u6D4B\u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div></div>\n</div></ng-template>\n";
|
|
1
|
+
export declare const TREE_TEMPLATE = "\n<header class=\"sidebar-header\">\n <button class=\"brand\" (click)=\"toggleProjects()\" [attr.aria-expanded]=\"projectsExpanded\" aria-controls=\"arw-saved-workspaces\" [title]=\"'\u5C55\u5F00\u6216\u6536\u8D77\u5DE5\u4F5C\u533A\u5217\u8868' | arwTranslate\"><i class=\"brand-chevron fas\" [class.fa-chevron-down]=\"projectsExpanded\" [class.fa-chevron-right]=\"!projectsExpanded\"></i><span class=\"brand-mark\"><svg class=\"workspace-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m12 3 9 5-9 5-9-5 9-5Z\"/><path d=\"m3 12 9 5 9-5M3 16l9 5 9-5\"/></svg></span><span class=\"brand-title\">{{isRemote ? 'AI Remote Workspace' : 'AI Local Workspace'}}</span></button>\n <div class=\"header-tools\">\n <button class=\"icon-button\" [disabled]=\"!selection?.workspace || loading || terminalMode\" (click)=\"addProject()\" [title]=\"terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\u4E0B\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u6DFB\u52A0\u5F53\u524D\u76EE\u5F55\u5230\u5DE5\u4F5C\u533A' | arwTranslate)\" [attr.aria-label]=\"terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\u4E0B\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u6DFB\u52A0\u5F53\u524D\u76EE\u5F55\u5230\u5DE5\u4F5C\u533A' | arwTranslate)\"><i class=\"fas fa-plus\"></i></button>\n <button class=\"icon-button\" (click)=\"followTerminal()\" [disabled]=\"!selection?.terminal || loading\" [title]=\"'\u8DDF\u968F\u7EC8\u7AEF\u5F53\u524D\u76EE\u5F55\uFF08\u5355\u51FB\u4E00\u6B21\uFF09' | arwTranslate\" [attr.aria-label]=\"'\u8DDF\u968F\u7EC8\u7AEF\u5F53\u524D\u76EE\u5F55' | arwTranslate\"><i class=\"fas fa-location-arrow\"></i></button>\n <button *ngIf=\"isRemote\" class=\"icon-button\" [class.terminal-mode]=\"terminalMode\" [attr.aria-pressed]=\"terminalMode\" (click)=\"toggleTerminalMode()\" [title]=\"terminalMode ? ('\u5173\u95ED\u7EC8\u7AEF\u6A21\u5F0F\uFF0C\u6062\u590D\u666E\u901A\u8F6C\u53D1' | arwTranslate) : ('\u5F00\u542F\u7EC8\u7AEF\u6A21\u5F0F\uFF1A\u5728\u539F\u7EC8\u7AEF\u6267\u884C Agent \u547D\u4EE4' | arwTranslate)\" [attr.aria-label]=\"terminalMode ? ('\u5173\u95ED\u7EC8\u7AEF\u6A21\u5F0F' | arwTranslate) : ('\u5F00\u542F\u7EC8\u7AEF\u6A21\u5F0F' | arwTranslate)\"><i class=\"fas fa-terminal\"></i></button>\n <button class=\"icon-button\" (click)=\"refreshCurrentView()\" [disabled]=\"loading || gitLoading || gitSetupBusy\" [title]=\"'\u5237\u65B0\u5DE5\u4F5C\u533A' | arwTranslate\" [attr.aria-label]=\"'\u5237\u65B0\u5DE5\u4F5C\u533A' | arwTranslate\"><i class=\"fas fa-sync-alt\" [class.spinning]=\"manualRefreshing\"></i></button>\n </div>\n</header>\n<div id=\"arw-saved-workspaces\" class=\"saved-workspaces\" [class.switching-workspace]=\"switchingWorkspace\" [attr.aria-busy]=\"switchingWorkspace\" *ngIf=\"selection?.workspace && projectsExpanded\" role=\"tree\" [attr.aria-label]=\"'\u4FDD\u5B58\u7684\u5DE5\u4F5C\u533A' | arwTranslate\">\n <div class=\"project-row\" *ngFor=\"let project of projects\" role=\"treeitem\" [attr.aria-selected]=\"activeProject===project.id\" [class.selected]=\"activeProject===project.id\" [class.forwarding-failed]=\"isRemote && projectForwarding(project)==='error'\">\n <span *ngIf=\"isRemote && !terminalMode\" class=\"forwarding-status\" [class.forwarding-active]=\"projectForwarding(project)==='forwarding'\" [class.forwarding-starting]=\"projectForwarding(project)==='starting'\" [class.forwarding-error]=\"projectForwarding(project)==='error'\" [title]=\"projectForwardingTitle(project)\" [attr.aria-label]=\"projectForwardingTitle(project)\" role=\"img\" tabindex=\"0\"><i class=\"fas\" [class.fa-circle]=\"projectForwarding(project)==='forwarding' || projectForwarding(project)==='saved'\" [class.fa-circle-notch]=\"projectForwarding(project)==='starting'\" [class.fa-exclamation-triangle]=\"projectForwarding(project)==='error'\" aria-hidden=\"true\"></i></span>\n <button class=\"project-open\" [disabled]=\"switchingWorkspace\" (click)=\"selectProject(project)\" [title]=\"terminalMode ? project.root : projectForwardingTitle(project)\"><i *ngIf=\"terminalMode\" class=\"fas fa-terminal\" aria-hidden=\"true\"></i><svg *ngIf=\"!terminalMode\" class=\"workspace-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m12 3 9 5-9 5-9-5 9-5Z\"/><path d=\"m3 12 9 5 9-5M3 16l9 5 9-5\"/></svg><span class=\"file-name\">{{project.name}}</span></button>\n <button class=\"project-ai\" [disabled]=\"switchingWorkspace || activeProject!==project.id\" (click)=\"launchProject(project,$event)\" [attr.aria-label]=\"('\u6253\u5F00 AI \u7EC8\u7AEF\uFF1A' | arwTranslate)+project.name\" [title]=\"'\u4F7F\u7528\u8BBE\u7F6E\u4E2D\u7684 CLI \u6253\u5F00 AI \u7EC8\u7AEF' | arwTranslate\">AI</button>\n <button *ngIf=\"!terminalMode\" class=\"icon-button\" [disabled]=\"switchingWorkspace\" (click)=\"removeProject(project,$event)\" [title]=\"'\u79FB\u9664\u5DE5\u4F5C\u533A' | arwTranslate\" [attr.aria-label]=\"('\u79FB\u9664\u5DE5\u4F5C\u533A\uFF1A' | arwTranslate)+project.name\"><i class=\"fas fa-times\"></i></button>\n </div>\n <div class=\"project-empty\" *ngIf=\"!projects.length\">{{terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\uFF1A\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u70B9\u51FB\u9876\u90E8 + \u4FDD\u5B58\u5F53\u524D\u76EE\u5F55' | arwTranslate)}}</div>\n</div>\n<div class=\"current-directory\" [title]=\"selection?.workspace ? breadcrumb : workspaceLabel\">\n <span *ngIf=\"!selection?.workspace\" class=\"current-directory-label\">{{connectionLabel}}</span>\n <div *ngIf=\"selection?.workspace\" class=\"directory-completion\" [title]=\"('\u5F53\u524D\u76EE\u5F55\uFF1A' | arwTranslate)+directoryInput\">\n <input #directoryBox class=\"current-directory-path\" aria-autocomplete=\"inline\" [attr.aria-label]=\"'\u5F53\u524D\u76EE\u5F55' | arwTranslate\" autocomplete=\"off\" spellcheck=\"false\" [ngModel]=\"directoryInput\" (ngModelChange)=\"updateDirectoryCandidates($event)\" (keyup)=\"syncDirectoryCaret(directoryBox)\" (click)=\"syncDirectoryCaret(directoryBox)\" (select)=\"syncDirectoryCaret(directoryBox)\" (scroll)=\"syncDirectoryCaret(directoryBox)\" (blur)=\"closeDirectoryCandidates()\" (keydown.enter)=\"$event.preventDefault(); navigateDirectory()\" (keydown.escape)=\"closeDirectoryCandidates(); directoryDraft=null\" (keydown.arrowdown)=\"$event.preventDefault(); moveDirectoryCandidate(1)\" (keydown.arrowup)=\"$event.preventDefault(); moveDirectoryCandidate(-1)\" (keydown.arrowright)=\"completeDirectoryRight($event,directoryBox)\" (keydown.tab)=\"$event.preventDefault(); completeDirectory(directoryBox)\" />\n <div *ngIf=\"directoryGhost\" class=\"directory-ghost\" aria-hidden=\"true\"><span [style.transform]=\"'translateX(-'+directoryInputScroll+'px)'\"><span class=\"directory-ghost-prefix\">{{directoryInput}}</span><span>{{directoryGhost}}</span></span></div>\n </div>\n <button *ngIf=\"selection?.workspace\" class=\"icon-button\" [disabled]=\"!selection?.workspace || loading || terminalMode\" (click)=\"addProject()\" [title]=\"terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\u4E0B\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u6DFB\u52A0\u5F53\u524D\u76EE\u5F55\u5230\u5DE5\u4F5C\u533A' | arwTranslate)\" [attr.aria-label]=\"terminalMode ? ('\u7EC8\u7AEF\u6A21\u5F0F\u4E0B\u4E0D\u80FD\u6DFB\u52A0\u5DE5\u4F5C\u533A' | arwTranslate) : ('\u6DFB\u52A0\u5F53\u524D\u76EE\u5F55\u5230\u5DE5\u4F5C\u533A' | arwTranslate)\"><i class=\"fas fa-arrow-up\"></i></button>\n <button *ngIf=\"selection?.workspace\" class=\"icon-button\" (click)=\"copyDirectory()\" [title]=\"'\u590D\u5236\u5F53\u524D\u76EE\u5F55' | arwTranslate\" [attr.aria-label]=\"'\u590D\u5236\u5F53\u524D\u76EE\u5F55' | arwTranslate\"><i class=\"far fa-copy\"></i></button>\n <span *ngIf=\"!selection?.workspace\" class=\"current-directory-path\">{{workspaceLabel}}</span>\n</div>\n <nav class=\"view-tabs\" [attr.aria-label]=\"'\u5DE5\u4F5C\u533A\u89C6\u56FE' | arwTranslate\">\n <button [class.selected]=\"mode==='files'\" (click)=\"setMode('files')\"><i class=\"far fa-folder\"></i> {{ '\u6587\u4EF6' | arwTranslate }}</button>\n <button [class.selected]=\"mode==='git'\" [disabled]=\"!selection?.workspace\" (click)=\"showGit()\"><i class=\"fas fa-code-branch\"></i> Git <span class=\"count\" *ngIf=\"changeCount\">{{changeCount}}</span></button>\n <button *ngIf=\"isRemote\" [class.selected]=\"mode==='activity'\" (click)=\"setMode('activity')\"><i class=\"fas fa-robot\"></i> {{ 'AI \u6D3B\u52A8' | arwTranslate }} <span class=\"count\" *ngIf=\"runningActivities\">{{runningActivities}}</span></button>\n </nav>\n<ng-container *ngIf=\"selection?.workspace || mode==='activity'; else noWorkspace\">\n <div class=\"search-box\" *ngIf=\"mode!=='activity'\"><i class=\"fas fa-search\"></i><input [ngModel]=\"filterText\" (ngModelChange)=\"updateFilter($event)\" [placeholder]=\"'\u7B5B\u9009\u5DF2\u52A0\u8F7D\u7684\u6587\u4EF6\u2026' | arwTranslate\" [attr.aria-label]=\"'\u7B5B\u9009\u6587\u4EF6' | arwTranslate\"><button *ngIf=\"filterText\" class=\"icon-button\" (click)=\"updateFilter('')\" [attr.aria-label]=\"'\u6E05\u9664\u7B5B\u9009' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <div class=\"browser-area activity-panel\" *ngIf=\"isRemote && mode==='activity'\">\n <div class=\"activity-overview\"><strong>{{runningActivities ? ('AI \u6B63\u5728\u64CD\u4F5C\u8FDC\u7A0B\u5DE5\u4F5C\u533A' | arwTranslate) : ('AI \u6D3B\u52A8' | arwTranslate)}}</strong><span>{{ '\u5F53\u524D\u6807\u7B7E\u9875 \u00B7 \u6700\u8FD1 200 \u6761' | arwTranslate }}</span></div>\n <div class=\"activity-controls\">\n <select [ngModel]=\"activityCategory\" (ngModelChange)=\"setActivityCategory($event)\" [attr.aria-label]=\"'\u6309\u64CD\u4F5C\u7C7B\u578B\u7B5B\u9009' | arwTranslate\"><option value=\"all\">{{ '\u5168\u90E8\u7C7B\u578B' | arwTranslate }}</option><option value=\"read\">{{ '\u8BFB\u53D6 / \u67E5\u8BE2' | arwTranslate }}</option><option value=\"write\">{{ '\u4FEE\u6539' | arwTranslate }}</option><option value=\"command\">{{ '\u547D\u4EE4' | arwTranslate }}</option></select>\n <select [ngModel]=\"activityStatus\" (ngModelChange)=\"setActivityStatus($event)\" [attr.aria-label]=\"'\u6309\u6267\u884C\u72B6\u6001\u7B5B\u9009' | arwTranslate\"><option value=\"all\">{{ '\u5168\u90E8\u72B6\u6001' | arwTranslate }}</option><option value=\"running\">{{ '\u6267\u884C\u4E2D' | arwTranslate }}</option><option value=\"success\">{{ '\u6210\u529F' | arwTranslate }}</option><option value=\"error\">{{ '\u5931\u8D25 / \u62E6\u622A' | arwTranslate }}</option></select>\n <button class=\"icon-button\" (click)=\"clearActivities()\" [title]=\"'\u6E05\u7A7A\u5DF2\u5B8C\u6210\u6D3B\u52A8\uFF0C\u4FDD\u7559\u5B89\u5168\u5BA1\u8BA1\u65E5\u5FD7' | arwTranslate\" [attr.aria-label]=\"'\u6E05\u7A7A\u5DF2\u5B8C\u6210\u6D3B\u52A8' | arwTranslate\"><i class=\"fas fa-trash-alt\"></i></button>\n </div>\n <div class=\"tree activity-timeline\" [attr.aria-label]=\"'AI \u8FDC\u7A0B\u64CD\u4F5C\u65F6\u95F4\u7EBF' | arwTranslate\" aria-live=\"polite\">\n <div class=\"empty-state compact\" *ngIf=\"!visibleActivities.length\"><i class=\"fas fa-robot\"></i><span>{{activities.length ? ('\u5F53\u524D\u7B5B\u9009\u6761\u4EF6\u4E0B\u6CA1\u6709\u6D3B\u52A8' | arwTranslate) : ('\u6682\u65E0 AI \u8FDC\u7A0B\u64CD\u4F5C' | arwTranslate)}}</span><small>{{ '\u901A\u8FC7 arw \u8F6C\u53D1\u7684\u8BFB\u53D6\u3001\u4FEE\u6539\u548C\u547D\u4EE4\u4F1A\u81EA\u52A8\u663E\u793A\uFF1B\u4E0D\u5305\u542B AI \u5BF9\u8BDD\u548C\u672C\u5730\u547D\u4EE4\u3002' | arwTranslate }}</small></div>\n <details class=\"activity-entry\" *ngFor=\"let activity of visibleActivities; trackBy: trackActivity\" [attr.data-status]=\"activity.status\">\n <summary><span class=\"activity-dot\" [class.spinning]=\"activity.status==='running'\"><i class=\"fas\" [class.fa-circle-notch]=\"activity.status==='running'\" [class.fa-circle]=\"activity.status!=='running'\"></i></span><span class=\"activity-title\">{{activityLabel(activity)}}<small class=\"activity-workspace\" [title]=\"activity.root\">{{activity.root}}</small><small>{{activity.command || activity.path || activity.root}}</small></span><span class=\"activity-state\">{{activityState(activity)}}</span></summary>\n <div class=\"activity-detail\"><div>{{activity.startedAt | date:'HH:mm:ss'}}<span *ngIf=\"activity.durationMs !== undefined\"> \u00B7 {{activity.durationMs}} ms</span></div><div>{{activity.host}} \u00B7 {{activity.root}}</div><div>{{ '\u5DE5\u4F5C\u533A\uFF1A' | arwTranslate }}{{activity.workspaceId}}</div><div>{{ '\u5DE5\u5177\uFF1A' | arwTranslate }}{{activity.operation}}</div><pre *ngIf=\"activity.command || activity.path\">{{activity.command || activity.path}}</pre><div *ngIf=\"activity.exitCode !== undefined\">{{ '\u9000\u51FA\u7801\uFF1A' | arwTranslate }}{{activity.exitCode}}</div><pre class=\"activity-error\" *ngIf=\"activity.error\">{{activity.error}}</pre></div>\n </details>\n </div>\n </div>\n <div class=\"error\" *ngIf=\"error\" role=\"alert\"><i class=\"fas fa-exclamation-circle\"></i><span>{{error}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissError()\" [attr.aria-label]=\"'\u5173\u95ED\u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <div class=\"browser-area\" *ngIf=\"mode==='files'\" (dragover)=\"dragLocalFiles($event)\" (dragleave)=\"leaveLocalFiles($event)\" (drop)=\"dropLocalFiles($event)\" [class.upload-drop]=\"dropTarget==='.'\">\n <div class=\"upload-status\" *ngIf=\"uploadBusy\" role=\"status\"><span>{{ '\u6B63\u5728\u4E0A\u4F20\uFF1A' | arwTranslate }}{{uploadPath}}</span><button (click)=\"cancelUpload()\">{{ '\u53D6\u6D88' | arwTranslate }}</button></div>\n <div class=\"section-heading\"><span><i class=\"fas fa-chevron-down\"></i> {{ '\u8D44\u6E90\u7BA1\u7406\u5668' | arwTranslate }}</span><span class=\"section-count\">{{visibleNodes.length}}</span></div>\n <div *ngIf=\"watchUnavailable\" class=\"watch-unavailable\" role=\"status\" [title]=\"watchUnavailable\"><i class=\"fas fa-eye-slash\"></i> {{ '\u672C\u5730\u6587\u4EF6\u76D1\u542C\u4E0D\u53EF\u7528\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0' | arwTranslate }}</div>\n <div *ngIf=\"snapshotUnavailable\" class=\"watch-unavailable\" role=\"status\" [title]=\"snapshotUnavailable\"><i class=\"fas fa-eye-slash\"></i> {{ '\u540E\u53F0\u6587\u4EF6\u68C0\u6D4B\u4E0D\u53EF\u7528\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0' | arwTranslate }}</div>\n <div class=\"directory-bar\" *ngIf=\"relativePath\"><button class=\"icon-button\" (click)=\"up()\" [disabled]=\"loading\" [title]=\"'\u8FD4\u56DE\u4E0A\u4E00\u7EA7' | arwTranslate\" [attr.aria-label]=\"'\u8FD4\u56DE\u4E0A\u4E00\u7EA7' | arwTranslate\"><i class=\"fas fa-arrow-left\"></i></button><span class=\"directory-path\" [title]=\"breadcrumb\">{{breadcrumb}}</span></div>\n <div class=\"tree\" role=\"tree\" (contextmenu)=\"showBackgroundContextMenu($event)\" [attr.aria-label]=\"'\u6587\u4EF6\u76EE\u5F55\u6811' | arwTranslate\">\n <div *ngIf=\"loading && manualRefreshing\" class=\"empty-state compact\"><i class=\"fas fa-circle-notch spinning\"></i><span>{{ '\u6B63\u5728\u8BFB\u53D6\u76EE\u5F55\u2026' | arwTranslate }}</span></div>\n <div *ngFor=\"let node of visibleNodes\" role=\"treeitem\" [attr.aria-expanded]=\"node.directory ? !!node.expanded : null\">\n <button class=\"file-row\" (dragover)=\"dragLocalFiles($event,node)\" (drop)=\"dropLocalFiles($event,node)\" [class.upload-drop]=\"dropTarget===node.path\" [class.folder-row]=\"node.directory\" [style.padding-left.px]=\"10+node.depth*16\" [title]=\"node.symlink ? (('\u7B26\u53F7\u94FE\u63A5\u4E0D\u652F\u6301\u5728\u6587\u4EF6\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\uFF1A' | arwTranslate) + node.path) : (node.directory ? ('\u53CC\u51FB\u8FDB\u5165\u76EE\u5F55' | arwTranslate) : node.path)\" (click)=\"clickNode(node,$event)\" (dblclick)=\"node.directory && !node.symlink ? open(node) : null\" (contextmenu)=\"showNodeContextMenu(node,$event)\">\n <i class=\"chevron fas\" [class.fa-chevron-right]=\"node.directory && !node.expanded\" [class.fa-chevron-down]=\"node.directory && node.expanded\" (click)=\"toggle(node); $event.stopPropagation()\" (dblclick)=\"$event.stopPropagation()\"></i>\n <svg *ngIf=\"node.directory\" class=\"file-icon folder-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path [attr.d]=\"node.expanded ? 'M3 17V5a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v2' : 'M3 7V5a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z'\"/><path *ngIf=\"node.expanded\" d=\"M5 10h17l-3 10H2l3-10Z\"/></svg><i *ngIf=\"!node.directory\" [class]=\"fileIcon(node)\"></i><span class=\"file-name\">{{node.name}}</span><i *ngIf=\"node.symlink\" class=\"fas fa-external-link-alt symlink-mark\"></i>\n </button>\n </div>\n <div *ngIf=\"!loading && !visibleNodes.length\" class=\"empty-state compact\"><i class=\"far fa-folder-open\"></i><span>{{filterText ? ('\u6CA1\u6709\u5339\u914D\u7684\u6587\u4EF6' | arwTranslate) : ('\u6B64\u76EE\u5F55\u4E3A\u7A7A' | arwTranslate)}}</span></div>\n </div>\n </div>\n <div class=\"browser-area\" *ngIf=\"mode==='git'\">\n <div class=\"section-heading\"><span><i class=\"fas fa-code-branch\"></i> {{ '\u6E90\u4EE3\u7801\u7BA1\u7406' | arwTranslate }}</span><button *ngIf=\"!gitLoading\" class=\"icon-button\" [disabled]=\"gitSetupBusy\" (click)=\"refreshCurrentView()\" [attr.aria-label]=\"'\u5237\u65B0 Git' | arwTranslate\" [title]=\"'\u91CD\u65B0\u626B\u63CF\u5F53\u524D\u76EE\u5F55\u4E2D\u7684 Git \u4ED3\u5E93' | arwTranslate\"><i class=\"fas fa-sync-alt\"></i></button><button *ngIf=\"gitLoading\" class=\"icon-button\" (click)=\"cancelGitScan()\" [attr.aria-label]=\"'\u53D6\u6D88 Git \u626B\u63CF' | arwTranslate\" [title]=\"'\u53D6\u6D88 Git \u626B\u63CF' | arwTranslate\"><i class=\"fas fa-stop\"></i></button></div>\n <div class=\"git-notice\" *ngIf=\"gitSetupBusy\">{{gitSetupProgress}}<button class=\"icon-button\" (click)=\"cancelGitSetup()\" [attr.aria-label]=\"'\u53D6\u6D88 Git \u521B\u5EFA' | arwTranslate\">{{ '\u53D6\u6D88' | arwTranslate }}</button></div>\n <div class=\"git-notice\" *ngIf=\"gitNotice\">{{gitNotice}}</div>\n <div class=\"tree git-tree\">\n <section class=\"git-repository\" *ngFor=\"let repository of repositories; trackBy: trackRepository\">\n <button class=\"repository-heading\" (click)=\"toggleRepository(repository)\" [attr.aria-expanded]=\"repository.expanded\" [title]=\"repository.root\"><i class=\"fas\" [class.fa-chevron-down]=\"repository.expanded\" [class.fa-chevron-right]=\"!repository.expanded\"></i><i class=\"fas fa-code-branch\"></i><span class=\"file-name\">{{repository.name}}</span><span class=\"count\">{{repository.count}}</span></button>\n <ng-container *ngIf=\"repository.expanded\">\n <div class=\"repository-path\" [title]=\"repository.root\">{{repository.root}}</div>\n <div class=\"repository-path\" *ngIf=\"repository.branch\"><i class=\"fas fa-code-branch\"></i> {{repository.branch}}</div>\n <div class=\"repository-path\" [title]=\"repository.syncError || ('\u57FA\u4E8E\u6700\u8FD1\u4E00\u6B21\u63D0\u53D6\u6216\u63A8\u9001\u8BB0\u5F55\uFF1B\u63D0\u53D6\u540E\u53EF\u68C0\u67E5\u4E0A\u6E38\u7684\u65B0\u63D0\u4EA4\u3002' | arwTranslate)\">{{gitSyncLabel(repository)}}</div>\n <div class=\"error\" *ngIf=\"repository.inspectorError\" role=\"alert\"><span>{{repository.inspectorError}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissRepositoryError(repository,'inspectorError')\" [attr.aria-label]=\"'\u5173\u95ED Git \u4FE1\u606F\u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <div class=\"git-inspector-tabs\" role=\"tablist\" [attr.aria-label]=\"'Git \u4FE1\u606F' | arwTranslate\"><button [class.selected]=\"repository.inspector==='history'\" (click)=\"showInspector(repository,'history')\" role=\"tab\" [attr.aria-selected]=\"repository.inspector==='history'\"><i class=\"fas fa-history\"></i> {{ '\u63D0\u4EA4\u8BB0\u5F55' | arwTranslate }} <span class=\"count\">{{repository.commits.length}}</span></button><button [class.selected]=\"repository.inspector==='branches'\" (click)=\"showInspector(repository,'branches')\" role=\"tab\" [attr.aria-selected]=\"repository.inspector==='branches'\"><i class=\"fas fa-code-branch\"></i> {{ '\u5206\u652F' | arwTranslate }} <span class=\"count\">{{repository.branches.length}}</span></button></div>\n <div class=\"git-inspector\" *ngIf=\"repository.inspector==='history'\"><div class=\"repository-clean\" *ngIf=\"!repository.commits.length\">{{repository.inspectorPending ? ('\u6B63\u5728\u8BFB\u53D6\u63D0\u4EA4\u8BB0\u5F55\u2026' | arwTranslate) : ('\u8FD8\u6CA1\u6709\u63D0\u4EA4\u8BB0\u5F55' | arwTranslate)}}</div><button class=\"git-history-item\" *ngFor=\"let commit of repository.commits; trackBy: trackCommit\" (click)=\"openCommit(repository,commit)\" [disabled]=\"repository.inspectorLoading\" [title]=\"commit.hash\"><span class=\"commit-subject\">{{commit.subject}}</span><span class=\"commit-meta\"><code>{{commit.shortHash}}</code> \u00B7 {{commit.author}} \u00B7 {{commitDate(commit)}}<span class=\"commit-sync\" [attr.data-sync]=\"commit.syncStatus\" [title]=\"repository.syncError || ('\u57FA\u4E8E\u6700\u8FD1\u5DF2\u77E5\u7684\u4E0A\u6E38\u8BB0\u5F55\uFF1B\u5DF2\u63A8\u9001\u548C\u5DF2\u62C9\u53D6\u8868\u793A\u672C\u6B21\u4F1A\u8BDD\u4E2D\u6210\u529F\u6267\u884C\u7684\u64CD\u4F5C\u3002' | arwTranslate)\">{{commitSyncLabel(commit)}}</span></span></button></div>\n <div class=\"git-inspector\" *ngIf=\"repository.inspector==='branches'\"><div class=\"repository-clean\" *ngIf=\"!repository.branches.length\">{{repository.inspectorPending ? ('\u6B63\u5728\u8BFB\u53D6\u5206\u652F\u2026' | arwTranslate) : ('\u6CA1\u6709\u53EF\u5207\u6362\u7684\u672C\u5730\u5206\u652F' | arwTranslate)}}</div><div class=\"git-branch-item\" *ngFor=\"let branch of repository.branches; trackBy: trackBranch\"><i class=\"fas\" [class.fa-check]=\"branch.current\" [class.fa-code-branch]=\"!branch.current\"></i><span class=\"file-name\">{{branch.name}}</span><span class=\"branch-current\" *ngIf=\"branch.current\">{{ '\u5F53\u524D' | arwTranslate }}</span><button *ngIf=\"!branch.current\" (click)=\"switchBranch(repository,branch)\" [disabled]=\"repository.busy\">{{ '\u5207\u6362' | arwTranslate }}</button></div><small class=\"git-inspector-hint\">{{ '\u4EC5\u5217\u51FA\u672C\u5730\u5206\u652F\u3002\u5207\u6362\u65F6\u4E0D\u4F1A\u81EA\u52A8\u8FD8\u539F\u672A\u63D0\u4EA4\u6539\u52A8\u3002' | arwTranslate }}</small></div>\n <div class=\"git-relay\"><button (click)=\"syncGit('fetch',repository)\" [disabled]=\"!canSyncGit(repository) || repository.busy || repository.loading || gitLoading\" [title]=\"'\u63D0\u53D6\u4E0A\u6E38\u72B6\u6001\uFF0C\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A' | arwTranslate\"><i class=\"fas fa-sync-alt\"></i> {{ '\u63D0\u53D6' | arwTranslate }}</button><button (click)=\"syncGit('pull',repository)\" [disabled]=\"!canSyncGit(repository) || repository.busy || repository.loading || gitLoading\" [title]=\"'\u62C9\u53D6\u4E0A\u6E38\u66F4\u65B0\uFF0C\u53EA\u5141\u8BB8\u5FEB\u8FDB\u5408\u5E76' | arwTranslate\"><i class=\"fas fa-arrow-down\"></i> {{ '\u62C9\u53D6' | arwTranslate }}</button><button (click)=\"syncGit('push',repository)\" [disabled]=\"!canSyncGit(repository) || repository.busy || repository.loading || gitLoading\" [title]=\"'\u63A8\u9001\u5F53\u524D\u5206\u652F\u5230\u4E0A\u6E38\uFF0C\u4E0D\u5F3A\u5236\u8986\u76D6' | arwTranslate\"><i class=\"fas fa-arrow-up\"></i> {{ '\u63A8\u9001' | arwTranslate }}</button></div>\n <div class=\"git-relay\" *ngIf=\"repository.relayProgress\" role=\"status\"><span><i class=\"fas fa-circle-notch spinning\"></i> {{repository.relayProgress}}</span><button type=\"button\" (click)=\"cancelRelay(repository)\" [disabled]=\"!repository.relayCancel\">{{ '\u53D6\u6D88' | arwTranslate }}</button></div>\n <div class=\"git-commit\"><textarea [ngModel]=\"commitMessage(repository)\" (ngModelChange)=\"setCommitMessage(repository,$event)\" rows=\"2\" [disabled]=\"repository.busy\" [attr.aria-label]=\"('\u63D0\u4EA4\u8BF4\u660E\uFF1A' | arwTranslate)+repository.name\" [placeholder]=\"'\u63D0\u4EA4\u8BF4\u660E\uFF08\u53EA\u63D0\u4EA4\u5DF2\u6682\u5B58\u66F4\u6539\uFF09' | arwTranslate\"></textarea><button (click)=\"commit(repository)\" [disabled]=\"repository.busy || !commitMessage(repository).trim()\" [title]=\"repository.groups[0]?.changes.length ? ('\u63D0\u4EA4\u5DF2\u6682\u5B58\u66F4\u6539' | arwTranslate) : ('\u8BF7\u5148\u6682\u5B58\u9700\u8981\u63D0\u4EA4\u7684\u66F4\u6539' | arwTranslate)\"><i class=\"fas fa-check\"></i> {{ '\u63D0\u4EA4' | arwTranslate }}</button></div>\n <div class=\"git-actions\" *ngIf=\"repository.count\"><button (click)=\"stageAll(repository)\" [disabled]=\"repository.busy || !repository.groups[1]?.changes.length\"><i class=\"fas fa-plus\"></i> {{ '\u5168\u90E8\u6682\u5B58' | arwTranslate }}</button><button (click)=\"unstageAll(repository)\" [disabled]=\"repository.busy || !repository.groups[0]?.changes.length\"><i class=\"fas fa-minus\"></i> {{ '\u5168\u90E8\u53D6\u6D88\u6682\u5B58' | arwTranslate }}</button><button class=\"discard-action\" (click)=\"discardChanges(repository)\" [disabled]=\"repository.busy || !repository.groups[1]?.changes.length\" [title]=\"'\u8FD8\u539F\u5168\u90E8\u672A\u6682\u5B58\u66F4\u6539\uFF0C\u4FDD\u7559\u5DF2\u6682\u5B58\u5185\u5BB9\uFF1B\u672A\u8DDF\u8E2A\u6587\u4EF6\u5C06\u5220\u9664' | arwTranslate\"><i class=\"fas fa-undo\"></i> {{ '\u5168\u90E8\u8FD8\u539F' | arwTranslate }}</button></div>\n <div class=\"git-success\" *ngIf=\"repository.message\" role=\"status\"><i class=\"fas fa-check-circle\"></i> {{repository.message}}</div>\n <div class=\"error\" *ngIf=\"repository.error\" role=\"alert\"><span>{{repository.error}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissRepositoryError(repository)\" [attr.aria-label]=\"'\u5173\u95ED Git \u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <section *ngFor=\"let group of repository.groups; trackBy: trackGitGroup\"><div class=\"git-group-heading\"><span>{{group.title}}</span><span class=\"count\">{{group.changes.length}}</span></div>\n <div class=\"git-change\" *ngFor=\"let change of filteredChanges(group.changes); trackBy: trackChange\"><button class=\"file-row\" [title]=\"change.path\" (click)=\"openChange(change,group.staged,repository)\"><i class=\"far fa-file-code file-icon\"></i><span class=\"file-name\">{{change.path}}</span><span class=\"git-status\" [class.added]=\"change.index==='?' || change.index==='A'\" [class.deleted]=\"change.working==='D' || change.index==='D'\">{{group.staged ? change.index : change.working}}</span></button><button class=\"icon-button git-change-action\" *ngIf=\"group.staged\" (click)=\"unstageChange(change,repository)\" [disabled]=\"repository.busy\" [title]=\"('\u53D6\u6D88\u6682\u5B58\uFF1A' | arwTranslate)+change.path\" [attr.aria-label]=\"('\u53D6\u6D88\u6682\u5B58\uFF1A' | arwTranslate)+change.path\"><i class=\"fas fa-minus\"></i></button><button class=\"icon-button git-change-action\" *ngIf=\"!group.staged\" (click)=\"stageChange(change,repository)\" [disabled]=\"repository.busy\" [title]=\"('\u6682\u5B58\uFF1A' | arwTranslate)+change.path\" [attr.aria-label]=\"('\u6682\u5B58\uFF1A' | arwTranslate)+change.path\"><i class=\"fas fa-plus\"></i></button><button class=\"icon-button git-change-action discard-action\" *ngIf=\"!group.staged\" (click)=\"discardChanges(repository,change)\" [disabled]=\"repository.busy\" [title]=\"('\u8FD8\u539F\u672A\u6682\u5B58\u66F4\u6539\uFF1A' | arwTranslate)+change.path\" [attr.aria-label]=\"('\u8FD8\u539F\u672A\u6682\u5B58\u66F4\u6539\uFF1A' | arwTranslate)+change.path\"><i class=\"fas fa-undo\"></i></button></div>\n </section>\n <div *ngIf=\"!repository.loading && !repository.error && !repository.count\" class=\"repository-clean\"><i class=\"far fa-check-circle\"></i> {{ '\u6CA1\u6709\u53D8\u66F4' | arwTranslate }}</div>\n </ng-container>\n </section>\n <div *ngIf=\"!gitLoading && !error && !gitNotice && !repositories.length\" class=\"empty-state compact\"><i class=\"fas fa-code-branch\"></i><span>{{gitEmptyMessage}}</span>\n <div class=\"git-setup-actions\" *ngIf=\"canSetupGit && !gitSetupMode\"><button (click)=\"openGitSetup('init')\" [disabled]=\"gitSetupBusy\">{{ '\u521D\u59CB\u5316 Git' | arwTranslate }}</button><button (click)=\"openGitSetup('clone')\" [disabled]=\"gitSetupBusy\">{{ '\u514B\u9686 Git \u9879\u76EE' | arwTranslate }}</button></div>\n </div>\n <form class=\"git-setup-form\" *ngIf=\"canSetupGit && gitSetupMode\" (ngSubmit)=\"submitGitSetup()\">\n <strong>{{gitSetupMode==='init' ? ('\u521D\u59CB\u5316 Git' | arwTranslate) : ('\u514B\u9686 Git \u9879\u76EE' | arwTranslate)}}</strong>\n <div class=\"git-setup-target\"><span>{{ '\u76EE\u6807\u76EE\u5F55' | arwTranslate }}</span><code [title]=\"gitSetupRoot\">{{gitSetupRoot}}</code></div>\n <label>{{ '\u7528\u6237\u540D' | arwTranslate }}<input name=\"gitUser\" [(ngModel)]=\"gitSetupName\" [disabled]=\"gitSetupBusy\" required autocomplete=\"off\" /></label>\n <label>{{ '\u90AE\u7BB1' | arwTranslate }}<input name=\"gitEmail\" type=\"email\" [(ngModel)]=\"gitSetupEmail\" [disabled]=\"gitSetupBusy\" required autocomplete=\"off\" /></label>\n <label>{{ '\u8FDC\u7A0B\u94FE\u63A5' | arwTranslate }}<input name=\"gitUrl\" [(ngModel)]=\"gitSetupUrl\" [disabled]=\"gitSetupBusy\" required placeholder=\"https://host/owner/project.git\" autocomplete=\"off\" /></label>\n <label *ngIf=\"gitSetupMode==='clone'\">{{ '\u65B0\u6587\u4EF6\u5939\u540D\u79F0' | arwTranslate }}<input name=\"gitDirectory\" [(ngModel)]=\"gitSetupDirectory\" [disabled]=\"gitSetupBusy\" [placeholder]=\"'\u7559\u7A7A\u4F7F\u7528\u4ED3\u5E93\u540D\u79F0' | arwTranslate\" autocomplete=\"off\" /></label>\n <small class=\"git-setup-hint\">{{gitSetupMode==='init' ? ('\u5728\u5F53\u524D\u76EE\u5F55\u521D\u59CB\u5316\uFF0C\u4EC5\u4E3A\u6B64\u4ED3\u5E93\u4FDD\u5B58\u7528\u6237\u540D\u3001\u90AE\u7BB1\u548C origin\u3002' | arwTranslate) : ('\u901A\u8FC7\u672C\u673A Git \u548C\u51ED\u636E\u514B\u9686\uFF0C\u4FDD\u5B58\u5230\u5F53\u524D\u76EE\u5F55\u4E0B\u7684\u65B0\u6587\u4EF6\u5939\u3002' | arwTranslate)}}</small>\n <div class=\"error\" *ngIf=\"gitSetupError\" role=\"alert\"><span>{{gitSetupError}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissError('gitSetupError')\" [attr.aria-label]=\"'\u5173\u95ED Git \u8BBE\u7F6E\u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div>\n <div class=\"git-setup-actions\"><button type=\"submit\" [disabled]=\"gitSetupBusy || !gitSetupName.trim() || !gitSetupEmail.trim() || !gitSetupUrl.trim()\">{{gitSetupMode==='init' ? ('\u521D\u59CB\u5316' | arwTranslate) : ('\u514B\u9686' | arwTranslate)}}</button><button type=\"button\" (click)=\"gitSetupBusy ? cancelGitSetup() : closeGitSetup()\">{{gitSetupBusy ? ('\u53D6\u6D88\u64CD\u4F5C' | arwTranslate) : ('\u53D6\u6D88' | arwTranslate)}}</button></div>\n </form>\n </div>\n </div>\n</ng-container>\n<ng-template #noWorkspace><div class=\"empty-state welcome\"><span class=\"empty-icon\"><svg class=\"workspace-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m12 3 9 5-9 5-9-5 9-5Z\"/><path d=\"m3 12 9 5 9-5M3 16l9 5 9-5\"/></svg></span><strong>{{isRemote ? ('\u8FDE\u63A5\u6210\u529F\uFF0C\u542F\u7528\u76EE\u5F55\u68C0\u6D4B' | arwTranslate) : ('\u4ECE\u7EC8\u7AEF\u5F00\u59CB' | arwTranslate)}}</strong><p>{{hint}}</p>\n<div class=\"cwd-setup\" *ngIf=\"isRemote\"><label>{{ '\u5F53\u524D\u8FDC\u7A0B shell' | arwTranslate }} <select [(ngModel)]=\"cwdShell\" [disabled]=\"cwdSetupPending\" [attr.aria-label]=\"'\u8FDC\u7A0B shell' | arwTranslate\"><option value=\"bash\">Bash</option><option value=\"zsh\">Zsh</option><option value=\"fish\">Fish</option></select></label><button class=\"setup-button primary\" [disabled]=\"!canEnableCwd || cwdSetupPending\" (click)=\"enableCwd()\"><i class=\"fas\" [class.fa-location-arrow]=\"!cwdSetupPending\" [class.fa-circle-notch]=\"cwdSetupPending\" [class.spinning]=\"cwdSetupPending\"></i>{{cwdSetupPending ? ('\u6B63\u5728\u83B7\u53D6\u76EE\u5F55\u2026' | arwTranslate) : ('\u542F\u7528\u8DEF\u5F84\u68C0\u6D4B\u5E76\u8DDF\u968F' | arwTranslate)}}</button><small>{{ '\u8BF7\u5728 shell \u547D\u4EE4\u63D0\u793A\u7B26\u4E0B\u70B9\u51FB\u3002\u4EC5\u5BF9\u5F53\u524D\u4F1A\u8BDD\u751F\u6548\uFF0C\u4E0D\u4FEE\u6539\u8FDC\u7AEF\u914D\u7F6E\u3002' | arwTranslate }}</small><button class=\"setup-button\" (click)=\"copyCwdHook()\"><i class=\"far fa-copy\"></i> {{ '\u590D\u5236\u547D\u4EE4\u624B\u52A8\u6267\u884C' | arwTranslate }}</button><div class=\"error\" *ngIf=\"cwdSetupError\" role=\"alert\"><span>{{cwdSetupError}}</span><button type=\"button\" class=\"icon-button dismiss-error\" (click)=\"dismissError('cwdSetupError')\" [attr.aria-label]=\"'\u5173\u95ED\u76EE\u5F55\u68C0\u6D4B\u9519\u8BEF\u63D0\u793A' | arwTranslate\" [title]=\"'\u5173\u95ED' | arwTranslate\"><i class=\"fas fa-times\"></i></button></div></div>\n</div></ng-template>\n";
|
|
2
2
|
export declare const TREE_STYLES: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Workspace } from './workspace';
|
|
2
|
+
import type { SSHSession } from '../adapters/ssh-compat';
|
|
3
|
+
export interface CopySource {
|
|
4
|
+
workspace: Workspace;
|
|
5
|
+
path: string;
|
|
6
|
+
hostKey: string;
|
|
7
|
+
remote?: SSHSession;
|
|
8
|
+
}
|
|
9
|
+
export declare function copyWorkspaceItem(source: CopySource): void;
|
|
10
|
+
export declare function copiedWorkspaceItem(): CopySource | undefined;
|
|
11
|
+
export declare function canPasteWorkspaceItem(source: CopySource, target: Workspace, hostKey: string): boolean;
|
|
12
|
+
/** Copy locally or on the shared remote host; use SFTP between local and remote. */
|
|
13
|
+
export declare function pasteWorkspaceItem(source: CopySource, target: Workspace, directory: string, hostKey: string, targetRemote?: SSHSession): Promise<void>;
|