tabby-quick-scripts-chenlei 1.0.0

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.
@@ -0,0 +1,23 @@
1
+ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
2
+ /**
3
+ * 脚本编辑弹窗组件
4
+ * 用于新建和编辑快捷脚本
5
+ */
6
+ export declare class ScriptEditModalComponent {
7
+ private modalInstance;
8
+ /** 脚本名称 */
9
+ scriptName: string;
10
+ /** 命令文本(换行分隔) */
11
+ commandsText: string;
12
+ /** 标签颜色 */
13
+ scriptColor: string;
14
+ /** 是否为新建模式 */
15
+ isNew: boolean;
16
+ constructor(modalInstance: NgbActiveModal);
17
+ /** 保存并关闭弹窗 */
18
+ save(): void;
19
+ /** 删除脚本 */
20
+ deleteScript(): void;
21
+ /** 取消操作 */
22
+ cancel(): void;
23
+ }
@@ -0,0 +1,26 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { FileDownload, FileUpload } from 'tabby-core';
4
+ export declare class LocalPathFileUpload extends FileUpload {
5
+ private filePath;
6
+ private fd;
7
+ private position;
8
+ constructor(filePath: string);
9
+ getName(): string;
10
+ getMode(): number;
11
+ getSize(): number;
12
+ read(): Promise<Buffer>;
13
+ close(): void;
14
+ }
15
+ export declare class LocalPathFileDownload extends FileDownload {
16
+ private targetPath;
17
+ private mode;
18
+ private size;
19
+ private fd;
20
+ constructor(targetPath: string, mode: number, size: number);
21
+ getName(): string;
22
+ getMode(): number;
23
+ getSize(): number;
24
+ write(buffer: Buffer): Promise<void>;
25
+ close(): void;
26
+ }
@@ -0,0 +1,255 @@
1
+ import { Injector, OnInit } from '@angular/core';
2
+ import { AppService, BaseTabComponent, FileTransfer, ProfilesService } from 'tabby-core';
3
+ import { SftpConnectionService, SFTPFile, SSHSessionLike } from './sftp.service';
4
+ type LocalEntry = {
5
+ name: string;
6
+ fullPath: string;
7
+ isDirectory: boolean;
8
+ size?: number;
9
+ mtimeMs?: number;
10
+ };
11
+ export declare class SftpManagerTabComponent extends BaseTabComponent implements OnInit {
12
+ private sftp;
13
+ private profilesService;
14
+ private app;
15
+ sshSession: SSHSessionLike | null;
16
+ profile: any;
17
+ t(zhText: string, enText: string): string;
18
+ getOctalPerms(mode: number | undefined): string;
19
+ getDateColorParts(timeValue: Date | number | undefined | null): {
20
+ date: string;
21
+ hour: string;
22
+ minute: string;
23
+ dateMatch: boolean;
24
+ hourMatch: boolean;
25
+ minuteMatch: boolean;
26
+ } | null;
27
+ connecting: boolean;
28
+ connected: boolean;
29
+ host: string;
30
+ port: number;
31
+ username: string;
32
+ password: string;
33
+ localPath: string;
34
+ localEntries: LocalEntry[];
35
+ remotePath: string;
36
+ remoteEntries: SFTPFile[];
37
+ private sftpSession;
38
+ localDropActive: boolean;
39
+ remoteDropActive: boolean;
40
+ transfers: Array<{
41
+ transfer: FileTransfer;
42
+ direction: 'upload' | 'download';
43
+ name: string;
44
+ remotePath: string;
45
+ localPath: string;
46
+ }>;
47
+ private transfersTimer;
48
+ localFilter: string;
49
+ remoteFilter: string;
50
+ remotePathInput: string;
51
+ localPathInput: string;
52
+ private localFolderSizeLoading;
53
+ private remoteFolderSizeLoading;
54
+ private localSortBy;
55
+ private localSortAsc;
56
+ private remoteSortBy;
57
+ private remoteSortAsc;
58
+ private localCache;
59
+ private remoteCache;
60
+ showHiddenLocal: boolean;
61
+ showHiddenRemote: boolean;
62
+ selectedLocal: LocalEntry[];
63
+ selectedRemote: SFTPFile[];
64
+ localActionName: string;
65
+ localActionPerms: string;
66
+ remoteActionName: string;
67
+ remoteActionPerms: string;
68
+ private localLastSelectedIndex;
69
+ private remoteLastSelectedIndex;
70
+ deleteConfirmVisible: boolean;
71
+ deleteConfirmMode: 'local' | 'remote' | null;
72
+ deleteConfirmText: string;
73
+ private pendingLocalDelete;
74
+ private pendingRemoteDelete;
75
+ favDeleteConfirmVisible: boolean;
76
+ favDeleteConfirmText: string;
77
+ private pendingFavDeleteId;
78
+ replaceConfirmVisible: boolean;
79
+ replaceConfirmText: string;
80
+ private replaceConfirmResolve;
81
+ inputDialogVisible: boolean;
82
+ inputDialogTitle: string;
83
+ inputDialogPlaceholder: string;
84
+ inputDialogValue: string;
85
+ inputDialogPathValue: string;
86
+ private inputDialogMode;
87
+ private inputDialogTargetPath;
88
+ private inputDialogRemotePath;
89
+ private platform;
90
+ private config;
91
+ private openedRemoteFiles;
92
+ localPathPresets: Array<{
93
+ id: string;
94
+ label: string;
95
+ path: string;
96
+ }>;
97
+ localFavorites: Array<{
98
+ id: string;
99
+ label: string;
100
+ path: string;
101
+ }>;
102
+ remoteFavorites: Array<{
103
+ id: string;
104
+ label: string;
105
+ path: string;
106
+ }>;
107
+ remoteDropdownOpen: boolean;
108
+ selectedRemoteFavId: string;
109
+ recentProfiles: any[];
110
+ localMenuVisible: boolean;
111
+ localMenuX: number;
112
+ localMenuY: number;
113
+ localMenuItems: Array<{
114
+ label: string;
115
+ path: string;
116
+ }>;
117
+ private notifications;
118
+ constructor(injector: Injector, sftp: SftpConnectionService, profilesService: ProfilesService, app: AppService);
119
+ ngOnInit(): void;
120
+ connect(): Promise<void>;
121
+ disconnect(): Promise<void>;
122
+ canLocalUp(): boolean;
123
+ localUp(): void;
124
+ remoteUp(): void;
125
+ refreshLocal(): Promise<void>;
126
+ refreshRemote(): Promise<void>;
127
+ openLocal(e: LocalEntry): void;
128
+ openRemote(e: SFTPFile): void;
129
+ onDragOver(ev: DragEvent): void;
130
+ onLocalMouseDown(entry: LocalEntry, event: MouseEvent): void;
131
+ onRemoteMouseDown(entry: SFTPFile, event: MouseEvent): void;
132
+ selectLocal(entry: LocalEntry, event: MouseEvent): void;
133
+ isLocalSelected(entry: LocalEntry): boolean;
134
+ selectRemote(entry: SFTPFile, event: MouseEvent): void;
135
+ isRemoteSelected(entry: SFTPFile): boolean;
136
+ setLocalSort(field: 'name' | 'size' | 'modified'): void;
137
+ setRemoteSort(field: 'name' | 'size' | 'modified'): void;
138
+ onDragStartLocal(ev: DragEvent, e: LocalEntry): void;
139
+ onDragStartRemote(ev: DragEvent, item: SFTPFile): void;
140
+ onDropOnRemote(ev: DragEvent): Promise<void>;
141
+ onDropOnLocal(ev: DragEvent): Promise<void>;
142
+ private uploadLocalPathToRemote;
143
+ private copyLocalPathIntoLocalDir;
144
+ private uploadDirectoryUploadToRemote;
145
+ private writeDirectoryUploadToLocal;
146
+ private downloadRemoteDirectoryRecursive;
147
+ private getDroppedOsPaths;
148
+ getFilteredLocalEntries(): LocalEntry[];
149
+ getFilteredRemoteEntries(): SFTPFile[];
150
+ private sortLocalEntries;
151
+ private sortRemoteEntries;
152
+ private getLocalMovePayload;
153
+ private getRemoteMovePayload;
154
+ formatSize(bytes?: number): string;
155
+ formatSpeed(bytesPerSecond?: number): string;
156
+ getLocalSizeDisplay(e: LocalEntry): string;
157
+ getRemoteSizeDisplay(e: SFTPFile): string;
158
+ onLocalEntryDragOver(entry: LocalEntry, ev: DragEvent): void;
159
+ onLocalEntryDrop(entry: LocalEntry, ev: DragEvent): Promise<void>;
160
+ onRemoteEntryDragOver(entry: SFTPFile, ev: DragEvent): void;
161
+ onRemoteEntryDrop(entry: SFTPFile, ev: DragEvent): Promise<void>;
162
+ getRemoteBreadcrumbs(): Array<{
163
+ label: string;
164
+ path: string;
165
+ }>;
166
+ navigateRemoteBreadcrumb(index: number): void;
167
+ goToRemotePathInput(): void;
168
+ goToLocalPathInput(): void;
169
+ getLocalBreadcrumbs(): Array<{
170
+ label: string;
171
+ path: string;
172
+ }>;
173
+ navigateLocalBreadcrumb(index: number): void;
174
+ private goToLocalPath;
175
+ onLocalPresetChange(id: string): void;
176
+ isCurrentFavorite(): boolean;
177
+ toggleCurrentFavorite(): void;
178
+ onLocalFavoriteSelect(id: string): void;
179
+ favDropdownOpen: boolean;
180
+ onDocumentClickFav(event: MouseEvent): void;
181
+ toggleFavDropdown(): void;
182
+ getSelectedFavLabel(): string;
183
+ onFavEditNameClick(event: MouseEvent, fav: any): void;
184
+ onFavDeleteClick(event: MouseEvent, fav: any): void;
185
+ onLocalBreadcrumbContextMenu(index: number, event: MouseEvent): void;
186
+ toggleRemoteFavDropdown(): void;
187
+ getSelectedRemoteFavLabel(): string;
188
+ onRemoteFavoriteSelect(favId: string): void;
189
+ isCurrentRemoteFavorite(): boolean;
190
+ toggleCurrentRemoteFavorite(): void;
191
+ onRemoteFavEditNameClick(event: MouseEvent, fav: any): void;
192
+ private loadLocalFavorites;
193
+ private saveLocalFavorites;
194
+ private getRemoteProfileId;
195
+ private loadRemoteFavorites;
196
+ private saveRemoteFavorites;
197
+ onLocalMenuItemClick(item: {
198
+ label: string;
199
+ path: string;
200
+ }): void;
201
+ private normalizeLocalPath;
202
+ private normalizeRemotePath;
203
+ private getDefaultRemotePath;
204
+ private loadRecentProfiles;
205
+ getProfileLabel(p: any): string;
206
+ launchProfileFromSFTP(p: any): void;
207
+ onDocumentClick(): void;
208
+ localRename(): void;
209
+ localDelete(): void;
210
+ localNewFolder(): void;
211
+ localEditPermissions(): void;
212
+ localShowSize(): void;
213
+ remoteRename(): void;
214
+ remoteDelete(): void;
215
+ remoteNewFolder(): void;
216
+ private openInputDialog;
217
+ deleteFavoriteFromDialog(): void;
218
+ confirmFavDelete(): void;
219
+ cancelFavDelete(): void;
220
+ cancelInputDialog(): void;
221
+ confirmInputDialog(): Promise<void>;
222
+ remoteEditPermissions(): void;
223
+ private applyRemoteEditPermissions;
224
+ remoteShowSize(): void;
225
+ remoteDownload(): void;
226
+ ensureLocalFolderSize(entry: LocalEntry): void;
227
+ ensureRemoteFolderSize(entry: SFTPFile): void;
228
+ private computeLocalFolderSize;
229
+ private computeRemoteFolderSize;
230
+ onLocalContextMenu(entry: LocalEntry, event: MouseEvent): void;
231
+ onRemoteContextMenu(entry: SFTPFile, event: MouseEvent): void;
232
+ private trackTransfer;
233
+ cancelTransfer(entry: {
234
+ transfer: FileTransfer;
235
+ }): void;
236
+ getTransferProgress(transfer: FileTransfer): number;
237
+ onKeyDown(event: KeyboardEvent): void;
238
+ destroy(): void;
239
+ getRecoveryToken(_options?: any): Promise<null>;
240
+ confirmDelete(): Promise<void>;
241
+ cancelDelete(): void;
242
+ private showReplaceConfirm;
243
+ confirmReplace(): Promise<void>;
244
+ cancelReplace(): void;
245
+ private deleteLocalEntry;
246
+ private deleteRemoteEntry;
247
+ private buildDeleteConfirmText;
248
+ private deleteLocalPathRecursive;
249
+ private deleteRemotePathRecursive;
250
+ private openRemoteFile;
251
+ private scheduleSyncBackRemoteFile;
252
+ private waitForStableLocalFile;
253
+ private syncBackRemoteFile;
254
+ }
255
+ export {};
@@ -0,0 +1,13 @@
1
+ import { NgZone } from '@angular/core';
2
+ import { AppService, HotkeysService, LogService, NotificationsService } from 'tabby-core';
3
+ export declare class SftpUiService {
4
+ private app;
5
+ private hotkeys;
6
+ private log;
7
+ private zone;
8
+ private notifications;
9
+ private logger;
10
+ constructor(app: AppService, hotkeys: HotkeysService, log: LogService, zone: NgZone, notifications: NotificationsService);
11
+ open(): void;
12
+ openForSourceTab(sourceTab: any, explicitRemotePath?: string): void;
13
+ }
@@ -0,0 +1,24 @@
1
+ export type SFTPFile = {
2
+ name: string;
3
+ fullPath: string;
4
+ isDirectory: boolean;
5
+ isSymlink: boolean;
6
+ mode: number;
7
+ size: number;
8
+ modified: Date;
9
+ };
10
+ export type SFTPSessionLike = {
11
+ readdir: (p: string) => Promise<SFTPFile[]>;
12
+ mkdir: (p: string) => Promise<void>;
13
+ rmdir: (p: string) => Promise<void>;
14
+ unlink: (p: string) => Promise<void>;
15
+ rename: (oldPath: string, newPath: string) => Promise<void>;
16
+ upload: (remotePath: string, transfer: import('tabby-core').FileUpload) => Promise<void>;
17
+ download: (remotePath: string, transfer: import('tabby-core').FileDownload) => Promise<void>;
18
+ };
19
+ export type SSHSessionLike = {
20
+ openSFTP: () => Promise<SFTPSessionLike>;
21
+ };
22
+ export declare class SftpConnectionService {
23
+ openFromSSHSession(sshSession: SSHSessionLike): Promise<SFTPSessionLike>;
24
+ }
@@ -0,0 +1,52 @@
1
+ import { Injector } from '@angular/core';
2
+ import { ConfigService } from 'tabby-core';
3
+ import { TerminalDecorator, BaseTerminalTabComponent } from 'tabby-terminal';
4
+ import { SftpUiService } from './sftp/sftp-ui.service';
5
+ import './quickScriptsBar.scss';
6
+ export declare class QuickScriptsDecorator extends TerminalDecorator {
7
+ private config;
8
+ private injector;
9
+ private sftpUi;
10
+ constructor(config: ConfigService, injector: Injector, sftpUi: SftpUiService);
11
+ attach(tab: BaseTerminalTabComponent): void;
12
+ detach(_tab: BaseTerminalTabComponent): void;
13
+ /**
14
+ * 在终端 tab 的 DOM 中注入脚本按钮栏
15
+ */
16
+ private injectBar;
17
+ /**
18
+ * 获取当前终端标签页的站点 Profile ID
19
+ */
20
+ private getProfileId;
21
+ /**
22
+ * 获取当前站点的专属脚本列表(带旧数据向前兼容)
23
+ */
24
+ private getScriptsForProfile;
25
+ /**
26
+ * 渲染按钮栏内容
27
+ */
28
+ private renderButtons;
29
+ /**
30
+ * 执行脚本:按顺序逐条发送命令
31
+ * 策略:发送命令后监听 output$,检测到提示符再发下一条,超时兜底
32
+ */
33
+ private executeScript;
34
+ /**
35
+ * 发送一条命令并等待执行完毕
36
+ * @param tab 终端标签页
37
+ * @param session 终端会话
38
+ * @param command 要执行的命令
39
+ * @param promptPattern 命令提示符正则
40
+ * @param timeout 超时时间(毫秒)
41
+ * @param minDelay 最小延时(毫秒)
42
+ */
43
+ private sendAndWait;
44
+ /**
45
+ * 新建脚本
46
+ */
47
+ private addScript;
48
+ /**
49
+ * 编辑已有脚本
50
+ */
51
+ private editScript;
52
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "tabby-quick-scripts-chenlei",
3
+ "version": "1.0.0",
4
+ "description": "在终端上方显示快捷脚本按钮,点击即可按顺序执行预设命令",
5
+ "keywords": [
6
+ "tabby-plugin"
7
+ ],
8
+ "engines": {
9
+ "tabby": ">=1.0.0"
10
+ },
11
+ "main": "dist/index.js",
12
+ "typings": "dist/index.d.ts",
13
+ "scripts": {
14
+ "build": "webpack --progress --color",
15
+ "watch": "webpack --progress --color --watch",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "author": "chenlei",
22
+ "license": "MIT",
23
+ "devDependencies": {
24
+ "@angular/animations": "^12.0.0",
25
+ "@angular/common": "^12.0.0",
26
+ "@angular/core": "^12.0.0",
27
+ "@angular/forms": "^12.0.0",
28
+ "@angular/platform-browser": "^12.0.0",
29
+ "@ng-bootstrap/ng-bootstrap": "^2.2.0",
30
+ "@types/webpack-env": "^1.16.0",
31
+ "apply-loader": "^2.0.0",
32
+ "css-loader": "^5.1.1",
33
+ "sass": "^1.32.0",
34
+ "pug": "^2.0.3",
35
+ "pug-loader": "^2.4.0",
36
+ "rxjs": "^7.3.0",
37
+ "sass-loader": "^11.0.1",
38
+ "style-loader": "^2.0.0",
39
+ "tabby-core": "^1.0.156",
40
+ "tabby-settings": "^1.0.156",
41
+ "tabby-terminal": "^1.0.156",
42
+ "ts-loader": "^9.2.6",
43
+ "typescript": "^4.2.3",
44
+ "webpack": "^5.24.4",
45
+ "webpack-cli": "^4.5.0"
46
+ }
47
+ }
package/readme.md ADDED
@@ -0,0 +1,79 @@
1
+ # Tabby Quick Scripts 插件
2
+
3
+ `tabby-quick-scripts` 是一款专为 [Tabby 终端](https://github.com/Eugeny/tabby) 开发的快捷脚本面板插件。它能够在终端标签页上方显示常驻的快捷脚本按钮,方便一键执行多条预设的运维或开发命令。
4
+
5
+ ---
6
+
7
+ ## 🌟 功能特性
8
+
9
+ * **常驻工具栏**:在 SSH、串口等终端面板的顶部注入一个快捷按钮栏。
10
+ * **顺序执行机制**:支持逐行、按顺序发送多条命令。插件会自动检测终端提示符(如 `$`, `#`, `>`, `%`),确保上一条命令完成响应后再发送下一条。
11
+ * **可视化管理**:
12
+ * **新建**:点击工具栏最右侧的 `+` 号添加新脚本。
13
+ * **执行**:左键单击脚本名称即可开始依次运行预设指令。
14
+ * **管理**:右键单击对应脚本可进入编辑模式或进行删除。
15
+
16
+ ---
17
+
18
+ ## 📦 安装与部署 (本地加载)
19
+
20
+ 作为一个本地开发的插件,你需要手动或通过软链接将该项目导入到 Tabby 的插件存放目录的 `node_modules` 中。
21
+
22
+ ### 默认 Tabby 插件路径
23
+ - **Windows**: `%APPDATA%\tabby\plugins\node_modules\` (例如:`C:\Users\你的用户名\AppData\Roaming\tabby\plugins\node_modules\`)
24
+ - **macOS**: `~/Library/Application Support/tabby/plugins/node_modules/`
25
+ - **Linux**: `~/.config/tabby/plugins/node_modules/`
26
+
27
+ ### 方式一:创建符号链接(推荐)
28
+ 最便捷的方式是在 `node_modules` 目录下新建软链接,无需频繁复制构建后的文件。
29
+
30
+ **PowerShell (Windows):**
31
+ ```powershell
32
+ # 注意:请将 -Target 后面的路径替换为本项目源码在你电脑上的真实绝对路径
33
+ New-Item -ItemType SymbolicLink -Path "$env:APPDATA\tabby\plugins\node_modules\tabby-quick-scripts" -Target "D:\git\gitea\tabby--QuickScripts"
34
+ ```
35
+
36
+ ### 方式二:手动拷贝发布
37
+ 若使用软链接不便,可在项目构建完成后,必须按照项目原有目录结构将其拷贝至 Tabby 的 `node_modules` 目录。
38
+ 1. 运行构建:`npm run build`
39
+ 2. 在 Tabby 插件的 `node_modules` 目录中新建 `tabby-quick-scripts` 文件夹。
40
+ 3. 将项目中的 **`package.json`** 文件以及整个 **`dist/`** 文件夹(注意:必须是保留 `dist` 文件夹层级,不能直接将 `dist` 内的文件提取至根目录)完整拷贝至该新建文件夹中。
41
+
42
+ > ⚠️ **提示**:无论是何种安装方式,配置完成后必须**完全退出并重新启动 Tabby** 才能成功加载。
43
+
44
+ ---
45
+
46
+ ## 🛠️ 本地开发指南
47
+
48
+ 如果你需要对插件逻辑进行维护、二次开发或修复 Bug,可遵循以下规范:
49
+
50
+ 1. **准备环境**
51
+ 确保你本地已安装 Node.js 环境,在项目根目录执行安装命令:
52
+ ```bash
53
+ npm install
54
+ ```
55
+
56
+ 2. **打包构建**
57
+ ```bash
58
+ # 单次构建(生产环境)
59
+ npm run build
60
+ ```
61
+
62
+ 3. **热更新 / 监听模式**
63
+ 开发过程中推荐使用 watch 模式,只要修改了 `src/` 内的源码,就会触发自动增量构建:
64
+ ```bash
65
+ npm run watch
66
+ ```
67
+
68
+ ---
69
+
70
+ ## ⚙️ 高级配置项
71
+
72
+ 插件默认的指令等待提示符正则为 `(\$|#|>|%)\s*$`。若你有特定的设备或定制化 Shell,可以在 Tabby 的 `config.yaml` 配置中找到 `quickScriptsPlugin` 字段调整参数:
73
+
74
+ ```yaml
75
+ quickScriptsPlugin:
76
+ promptPattern: '(\$|#|>|%)\s*$' # 判断上一条命令结束的正则匹配式
77
+ commandTimeout: 30000 # 单条命令超时等待上限 (ms)
78
+ minDelay: 500 # 发送命令之间的最小物理安全延迟 (ms)
79
+ ```