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 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.0`
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.0.tgz` from [GitHub Releases](https://github.com/SupremeJulyan/tabby-ai-workspace-manager/releases/tag/v0.1.0).
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.0.tgz
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.0.tgz
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.0`
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.0) 下载 `tabby-ai-workspace-manager-0.1.0.tgz`。
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.0.tgz
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.0.tgz
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 COMMAND [arguments] --id ID\nWorkspace: workspaces [--saved]; current-file\nRead: list [PATH] [--limit N] [--cursor N]\n read PATH [--offset N --length N | --head N | --tail N | --start-line N --line-count N]\n read-many --input '{\"requests\":[{\"path\":\"README.md\",\"head\":40}]}'\n search QUERY [PATH] [--mode content|files|count|names]\n find GLOB [PATH]\n output OUTPUT_ID stdout|stderr [--offset N --length N]\nWrite: write PATH --content TEXT|--file FILE|-\n edit PATH --input '{\"edits\":[{\"oldText\":\"old\",\"newText\":\"new\"}]}'\n create PATH file|directory [--content TEXT|--file FILE|-]\n delete PATH [--input '{\"recursive\":true}']\n chmod PATH 755\n move --input '{\"sourcePath\":\"old\",\"targetPath\":\"new\",\"overwrite\":false}'\nTransfer: upload [-r] LOCAL_PATH REMOTE_PATH --id ID\n download [-r] REMOTE_PATH LOCAL_PATH --id ID\nGit: git-pull [REMOTE_PATH] --id ID\n git-push [REMOTE_PATH] --id ID\nExecute: exec -- 'COMMAND'\n batch --input '{\"operations\":[{\"command\":\"read\",\"arguments\":{\"path\":\"README.md\"}}]}'\nNamed --path, --query and --command options are supported. --workspace is an alias for --id.\nUse --input JSON|- for structured arguments; stdin avoids putting sensitive content in arguments.\nOptions: --compact omits empty status metadata; --verbose prints exec as JSON; --version prints version.\nBatch runs in order and stops at the first failure; completed changes are not rolled back.\n";
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\nUse arw for all project files, Git, builds, tests and commands in this remote workspace.\n\nRun `arw workspaces`. Match its host, port, user and workspaceRoot to `.awm/workspace.json` (remoteRoot). Pass `--id ID` to every operation on the matching forwarded workspace. If the target is ambiguous, ask the user. If it is saved but unavailable, reconnect it in Tabby. Do not switch targets implicitly.\n\nUse `arw list`, `read`, `search`, `edit` and `write` for remote files, and `arw exec --id ID -- 'COMMAND'` for remote commands. Use `arw upload [-r] LOCAL_PATH REMOTE_PATH --id ID` and `arw download [-r] REMOTE_PATH LOCAL_PATH --id ID` for transfers. Use `arw git-pull [PATH] --id ID` or `arw git-push [PATH] --id ID` to relay Git through the local network and credentials. Run `arw --help` for syntax.\n\nRead-only file commands (list, read, search, find and download sources) may access paths outside workspaceRoot, subject to the SSH account permissions. Relative paths start at workspaceRoot; absolute paths can read elsewhere. Mutations and upload destinations must remain inside workspaceRoot. Shell commands via exec retain their separate command policy.\n\nReport failures without automatically retrying mutations. For long command output, read the returned outputId instead of rerunning the command. A POLICY_DENIED result must not be bypassed. Do not print connection URLs, tokens or credentials.\n";
2
- export declare const terminalCliGuidance = "# ARW terminal mode\n\nThis placeholder represents the selected interactive terminal. Read workspaceId from .awm/workspace.json and verify that exact ID is forwarded in arw workspaces. Pass --id ID to each operation. If it is unavailable, reopen the AI terminal in terminal mode; do not use another workspace with the same path.\n\nUse only arw exec --id ID -- 'COMMAND' for project operations. Commands are injected into the existing terminal and inherit its current user, exported environment, and working directory. Do not pass a cwd. File, search, transfer, and batch tools are disabled in this mode. Read and modify files with shell commands through arw exec.\n\nKeep operations inside workspaceRoot. stdout and stderr are combined because this is a PTY. The terminal must be at a shell prompt. If its directory changes, follow the terminal directory and reopen the AI terminal. Do not automatically retry failed mutations. Do not bypass policy denials.\n";
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>;