tabby-tabbyspaces 0.0.1 → 0.1.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.
Files changed (56) hide show
  1. package/.claude/settings.local.json +28 -2
  2. package/CHANGELOG.md +46 -20
  3. package/CLAUDE.md +163 -15
  4. package/README.md +71 -61
  5. package/RELEASE.md +91 -0
  6. package/TEST_MCP.md +176 -0
  7. package/TODO.md +72 -0
  8. package/cdp-click.js +22 -0
  9. package/cdp-test.js +28 -0
  10. package/dist/components/paneEditor.component.d.ts +6 -1
  11. package/dist/components/paneEditor.component.d.ts.map +1 -1
  12. package/dist/components/splitPreview.component.d.ts +22 -7
  13. package/dist/components/splitPreview.component.d.ts.map +1 -1
  14. package/dist/components/workspaceEditor.component.d.ts +30 -4
  15. package/dist/components/workspaceEditor.component.d.ts.map +1 -1
  16. package/dist/components/workspaceList.component.d.ts +21 -9
  17. package/dist/components/workspaceList.component.d.ts.map +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.LICENSE.txt +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/models/workspace.model.d.ts +4 -2
  23. package/dist/models/workspace.model.d.ts.map +1 -1
  24. package/dist/package.json +26 -0
  25. package/dist/providers/settings.provider.d.ts.map +1 -1
  26. package/dist/providers/toolbar.provider.d.ts +4 -1
  27. package/dist/providers/toolbar.provider.d.ts.map +1 -1
  28. package/dist/services/startupCommand.service.d.ts +20 -0
  29. package/dist/services/startupCommand.service.d.ts.map +1 -0
  30. package/dist/services/workspaceEditor.service.d.ts +11 -3
  31. package/dist/services/workspaceEditor.service.d.ts.map +1 -1
  32. package/docs/marketing_status.md +92 -0
  33. package/package.json +2 -7
  34. package/screenshots/editor.png +0 -0
  35. package/screenshots/pane-edit.png +0 -0
  36. package/scripts/build-prod.js +39 -0
  37. package/src/components/paneEditor.component.pug +2 -2
  38. package/src/components/paneEditor.component.ts +19 -1
  39. package/src/components/splitPreview.component.pug +45 -5
  40. package/src/components/splitPreview.component.scss +79 -22
  41. package/src/components/splitPreview.component.ts +91 -16
  42. package/src/components/workspaceEditor.component.pug +130 -70
  43. package/src/components/workspaceEditor.component.scss +205 -120
  44. package/src/components/workspaceEditor.component.ts +193 -6
  45. package/src/components/workspaceList.component.pug +31 -20
  46. package/src/components/workspaceList.component.scss +12 -6
  47. package/src/components/workspaceList.component.ts +116 -34
  48. package/src/index.ts +2 -0
  49. package/src/models/workspace.model.ts +33 -6
  50. package/src/providers/settings.provider.ts +2 -2
  51. package/src/providers/toolbar.provider.ts +41 -10
  52. package/src/services/startupCommand.service.ts +142 -0
  53. package/src/services/workspaceEditor.service.ts +70 -38
  54. package/test_cdp.py +50 -0
  55. package/RELEASE_PLAN.md +0 -161
  56. package/screenshots/workspace-edit.png +0 -0
@@ -34,6 +34,7 @@ export interface TabbyRecoveryToken {
34
34
  tabTitle?: string;
35
35
  tabCustomTitle?: string;
36
36
  disableDynamicTitle?: boolean;
37
+ cwd?: string;
37
38
  }
38
39
  export interface TabbySplitLayoutProfile {
39
40
  id: string;
@@ -65,12 +66,13 @@ export interface Workspace {
65
66
  icon?: string;
66
67
  color?: string;
67
68
  root: WorkspaceSplit;
68
- isDefault?: boolean;
69
- hotkey?: string;
69
+ launchOnStartup?: boolean;
70
70
  }
71
71
  export declare function isWorkspaceSplit(node: WorkspacePane | WorkspaceSplit): node is WorkspaceSplit;
72
72
  export declare function createDefaultPane(): WorkspacePane;
73
73
  export declare function createDefaultSplit(orientation?: 'horizontal' | 'vertical'): WorkspaceSplit;
74
+ export declare function getRandomColor(): string;
75
+ export declare function getRandomIcon(): string;
74
76
  export declare function createDefaultWorkspace(name?: string): Workspace;
75
77
  export declare function generateUUID(): string;
76
78
  export declare function countPanes(node: WorkspacePane | WorkspaceSplit): number;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.model.d.ts","sourceRoot":"","sources":["../../src/models/workspace.model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace.model.d.ts","sourceRoot":"","sources":["../../src/models/workspace.model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "tabby-tabbyspaces",
3
+ "version": "0.1.0",
4
+ "description": "Workspaces for Tabby - Visual split-layout workspace editor",
5
+ "main": "index.js",
6
+ "keywords": [
7
+ "tabby",
8
+ "tabby-plugin"
9
+ ],
10
+ "peerDependencies": {
11
+ "@angular/common": "^15.0.0",
12
+ "@angular/core": "^15.0.0",
13
+ "@angular/forms": "^15.0.0",
14
+ "tabby-core": "*",
15
+ "tabby-local": "*",
16
+ "tabby-settings": "*",
17
+ "tabby-terminal": "*"
18
+ },
19
+ "author": "Igor Halilovic",
20
+ "license": "MIT",
21
+ "tabbyPlugin": {
22
+ "name": "tabbyspaces",
23
+ "displayName": "TabbySpaces",
24
+ "description": "Workspaces for Tabby - Visual split-layout workspace editor"
25
+ }
26
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"settings.provider.d.ts","sourceRoot":"","sources":["../../src/providers/settings.provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAIpD,qBACa,+BAAgC,SAAQ,mBAAmB;IACtE,EAAE,SAAa;IACf,IAAI,SAAY;IAChB,KAAK,SAAe;IAEpB,gBAAgB,IAAI,GAAG;CAGxB"}
1
+ {"version":3,"file":"settings.provider.d.ts","sourceRoot":"","sources":["../../src/providers/settings.provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAIpD,qBACa,+BAAgC,SAAQ,mBAAmB;IACtE,EAAE,SAAa;IACf,IAAI,SAA+B;IACnC,KAAK,SAAe;IAEpB,gBAAgB,IAAI,GAAG;CAGxB"}
@@ -1,10 +1,13 @@
1
1
  import { ToolbarButtonProvider, ToolbarButton, ProfilesService, AppService } from 'tabby-core';
2
2
  import { WorkspaceEditorService } from '../services/workspaceEditor.service';
3
+ import { StartupCommandService } from '../services/startupCommand.service';
3
4
  export declare class WorkspaceToolbarProvider extends ToolbarButtonProvider {
4
5
  private workspaceService;
5
6
  private profilesService;
6
7
  private app;
7
- constructor(workspaceService: WorkspaceEditorService, profilesService: ProfilesService, app: AppService);
8
+ private startupService;
9
+ constructor(workspaceService: WorkspaceEditorService, profilesService: ProfilesService, app: AppService, startupService: StartupCommandService);
10
+ private launchStartupWorkspaces;
8
11
  provide(): ToolbarButton[];
9
12
  private showWorkspaceSelector;
10
13
  private openSettings;
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar.provider.d.ts","sourceRoot":"","sources":["../../src/providers/toolbar.provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC9F,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA;AAK5E,qBACa,wBAAyB,SAAQ,qBAAqB;IAE/D,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,GAAG;gBAFH,gBAAgB,EAAE,sBAAsB,EACxC,eAAe,EAAE,eAAe,EAChC,GAAG,EAAE,UAAU;IAKzB,OAAO,IAAI,aAAa,EAAE;YAgBZ,qBAAqB;IAkCnC,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,aAAa;CAStB"}
1
+ {"version":3,"file":"toolbar.provider.d.ts","sourceRoot":"","sources":["../../src/providers/toolbar.provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC9F,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAA;AAgB1E,qBACa,wBAAyB,SAAQ,qBAAqB;IAE/D,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,cAAc;gBAHd,gBAAgB,EAAE,sBAAsB,EACxC,eAAe,EAAE,eAAe,EAChC,GAAG,EAAE,UAAU,EACf,cAAc,EAAE,qBAAqB;YAYjC,uBAAuB;IASrC,OAAO,IAAI,aAAa,EAAE;YAWZ,qBAAqB;IAkCnC,OAAO,CAAC,YAAY;YAIN,aAAa;CAgB5B"}
@@ -0,0 +1,20 @@
1
+ import { AppService } from 'tabby-core';
2
+ export interface PendingCommand {
3
+ paneId: string;
4
+ command?: string;
5
+ originalTitle: string;
6
+ }
7
+ export declare class StartupCommandService {
8
+ private app;
9
+ private pendingCommands;
10
+ private subscription;
11
+ constructor(app: AppService);
12
+ registerCommands(commands: PendingCommand[]): void;
13
+ private onTabOpened;
14
+ private processChildTabs;
15
+ private processTerminalTab;
16
+ private buildFullCommand;
17
+ private clearProfileArgs;
18
+ ngOnDestroy(): void;
19
+ }
20
+ //# sourceMappingURL=startupCommand.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startupCommand.service.d.ts","sourceRoot":"","sources":["../../src/services/startupCommand.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAuC,MAAM,YAAY,CAAA;AAI5E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,qBACa,qBAAqB;IAIpB,OAAO,CAAC,GAAG;IAHvB,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,YAAY,CAAc;gBAEd,GAAG,EAAE,UAAU;IAInC,gBAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,IAAI;IAOlD,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,kBAAkB;IA+D1B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,gBAAgB;IAUxB,WAAW,IAAI,IAAI;CAGpB"}
@@ -1,25 +1,33 @@
1
1
  import { ConfigService, NotificationsService, ProfilesService } from 'tabby-core';
2
2
  import { Workspace, TabbyProfile, TabbySplitLayoutProfile } from '../models/workspace.model';
3
+ import { PendingCommand } from './startupCommand.service';
3
4
  export declare class WorkspaceEditorService {
4
5
  private config;
5
6
  private notifications;
6
7
  private profilesService;
8
+ private cachedProfiles;
7
9
  constructor(config: ConfigService, notifications: NotificationsService, profilesService: ProfilesService);
10
+ private cacheProfiles;
8
11
  getWorkspaces(): Workspace[];
9
12
  saveWorkspaces(workspaces: Workspace[]): Promise<boolean>;
10
13
  addWorkspace(workspace: Workspace): Promise<void>;
11
14
  updateWorkspace(workspace: Workspace): Promise<void>;
12
15
  deleteWorkspace(workspaceId: string): Promise<void>;
13
16
  getAvailableProfiles(): Promise<TabbyProfile[]>;
14
- private syncTabbyProfiles;
15
- generateTabbyProfile(workspace: Workspace): TabbySplitLayoutProfile;
17
+ /**
18
+ * Cleanup orphaned profiles from previous plugin versions.
19
+ * Call this once on plugin init.
20
+ */
21
+ cleanupOrphanedProfiles(): void;
22
+ generateTabbyProfile(workspace: Workspace): Promise<TabbySplitLayoutProfile>;
16
23
  private generateRecoveryToken;
17
24
  private generatePaneToken;
18
25
  duplicateWorkspace(workspace: Workspace): Workspace;
19
26
  private regenerateIds;
20
27
  private sanitizeForProfileId;
21
28
  private getProfileById;
22
- getProfileName(profileId: string): string | undefined;
29
+ collectStartupCommands(workspace: Workspace): PendingCommand[];
30
+ private collectCommandsFromNode;
23
31
  private saveConfig;
24
32
  }
25
33
  //# sourceMappingURL=workspaceEditor.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspaceEditor.service.d.ts","sourceRoot":"","sources":["../../src/services/workspaceEditor.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,EACL,SAAS,EAKT,YAAY,EAEZ,uBAAuB,EACxB,MAAM,2BAA2B,CAAA;AAGlC,qBACa,sBAAsB;IAE/B,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,eAAe;gBAFf,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,oBAAoB,EACnC,eAAe,EAAE,eAAe;IAG1C,aAAa,IAAI,SAAS,EAAE;IAItB,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAMzD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjD,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAUpD,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnD,oBAAoB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAOrD,OAAO,CAAC,iBAAiB;IAiBzB,oBAAoB,CAAC,SAAS,EAAE,SAAS,GAAG,uBAAuB;IAgBnE,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,iBAAiB;IAmEzB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS;IASnD,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,cAAc;IAKtB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;YAIvC,UAAU;CAUzB"}
1
+ {"version":3,"file":"workspaceEditor.service.d.ts","sourceRoot":"","sources":["../../src/services/workspaceEditor.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,EACL,SAAS,EAKT,YAAY,EAEZ,uBAAuB,EACxB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD,qBACa,sBAAsB;IAI/B,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,eAAe;IALzB,OAAO,CAAC,cAAc,CAAqB;gBAGjC,MAAM,EAAE,aAAa,EACrB,aAAa,EAAE,oBAAoB,EACnC,eAAe,EAAE,eAAe;YAG5B,aAAa;IAI3B,aAAa,IAAI,SAAS,EAAE;IAItB,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAQzD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjD,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAUpD,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnD,oBAAoB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IASrD;;;OAGG;IACH,uBAAuB,IAAI,IAAI;IAczB,oBAAoB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAiBlF,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,iBAAiB;IA4DzB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS;IASnD,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,cAAc;IAYtB,sBAAsB,CAAC,SAAS,EAAE,SAAS,GAAG,cAAc,EAAE;IAM9D,OAAO,CAAC,uBAAuB;YAiBjB,UAAU;CAUzB"}
@@ -0,0 +1,92 @@
1
+ # TabbySpaces Marketing Status
2
+
3
+ Track promotion results. Update after each submission.
4
+
5
+ ## How to Log
6
+
7
+ Add entry when submitted. Update when results change.
8
+
9
+ ### Reddit Posts
10
+
11
+ ```markdown
12
+ ### r/{subreddit} - {date}
13
+ - **Link**: {url}
14
+ - **Status**: posted / removed / archived
15
+ - **Upvotes**: {number}
16
+ - **Comments**: {number}
17
+ - **Notes**: {any observations}
18
+ ```
19
+
20
+ ### GitHub PRs
21
+
22
+ ```markdown
23
+ ### {list-name} - {date}
24
+ - **PR**: {url}
25
+ - **Status**: submitted / merged / rejected / closed
26
+ - **Notes**: {feedback, requirements not met, etc}
27
+ ```
28
+
29
+ ### Directory Sites
30
+
31
+ ```markdown
32
+ ### {site-name} - {date}
33
+ - **Link**: {url if public listing}
34
+ - **Status**: submitted / approved / pending / rejected
35
+ - **Backlink**: yes / no / pending
36
+ - **Notes**: {approval time, featured, etc}
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Log
42
+
43
+ *Add entries below, newest first.*
44
+
45
+ ### r/ClaudeCode - 2026-01-03
46
+ - **Link**: https://www.reddit.com/r/ClaudeCode/comments/1q2vmqx/i_let_claude_code_write_an_entire_tabby_plugin/
47
+ - **Status**: posted
48
+ - **Upvotes**: 0
49
+ - **Comments**: 0
50
+ - **Notes**: "I let Claude Code write an entire Tabby plugin"
51
+
52
+ ### r/SideProject - 2026-01-03
53
+ - **Link**: https://www.reddit.com/r/SideProject/comments/1q2vlby/i_work_with_6_terminal_splits_daily_too_lazy_to/
54
+ - **Status**: posted
55
+ - **Upvotes**: 0
56
+ - **Comments**: 0
57
+ - **Notes**: "I work with 6 terminal splits daily, too lazy to..."
58
+
59
+ ### r/commandline - 2026-01-03
60
+ - **Link**: https://www.reddit.com/r/commandline/comments/1q2vhcs/tabbyspaces_visual_workspace_editor_for_tabby/
61
+ - **Status**: posted
62
+ - **Upvotes**: 0
63
+ - **Comments**: 0
64
+ - **Notes**: TabbySpaces announcement
65
+
66
+ ### dev.to - 2026-01-03
67
+ - **Link**: https://dev.to/igor_halilovic_3fa0baa977/tabbyspaces-visual-workspace-editor-for-tabby-terminal-2ig6
68
+ - **Status**: posted
69
+ - **Notes**: "TabbySpaces: Visual Workspace Editor for Tabby Terminal"
70
+
71
+ ### r/ClaudeAI - 2026-01-03
72
+ - **Link**: https://www.reddit.com/r/ClaudeAI/comments/1q2v0xh/i_let_claude_code_write_an_entire_tabby_plugin/
73
+ - **Status**: posted
74
+ - **Upvotes**: 0
75
+ - **Comments**: 0
76
+ - **Notes**: "I let Claude Code write an entire Tabby plugin"
77
+
78
+ ### Uneed - 2026-01-03
79
+ - **Link**: https://www.uneed.best/edit/waiting-line/21145
80
+ - **Status**: pending
81
+ - **Backlink**: pending
82
+ - **Notes**: Waiting for approval
83
+
84
+ ### awesome-productivity - 2026-01-03
85
+ - **PR**: https://github.com/jyguyomarch/awesome-productivity/pull/149
86
+ - **Status**: submitted
87
+ - **Notes**: Task Automation section, no star requirements
88
+
89
+ ### terminals-are-sexy - 2026-01-03
90
+ - **PR**: https://github.com/k4m4/terminals-are-sexy/pull/346
91
+ - **Status**: submitted
92
+ - **Notes**: Waiting for community endorsement
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tabby-tabbyspaces",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Workspaces for Tabby - Visual split-layout workspace editor",
5
5
  "keywords": [
6
6
  "tabby",
@@ -8,14 +8,9 @@
8
8
  ],
9
9
  "main": "dist/index.js",
10
10
  "type": "commonjs",
11
- "dependencies": {
12
- "tabby-core": "^1.0.197-nightly.1",
13
- "tabby-settings": "^1.0.197-nightly.1",
14
- "tabby-terminal": "^1.0.197-nightly.1"
15
- },
16
11
  "typings": "dist/index.d.ts",
17
12
  "scripts": {
18
- "build": "webpack --mode production",
13
+ "build": "node scripts/build-prod.js",
19
14
  "build:dev": "node scripts/build-dev.js",
20
15
  "watch": "webpack --mode development --watch",
21
16
  "watch:dev": "webpack --mode development --watch --env dev --output-path dist-dev"
Binary file
Binary file
@@ -0,0 +1,39 @@
1
+ const { execSync } = require('child_process')
2
+ const fs = require('fs')
3
+ const path = require('path')
4
+
5
+ const rootDir = path.resolve(__dirname, '..')
6
+ const distDir = path.join(rootDir, 'dist')
7
+
8
+ // 1. Clean dist
9
+ if (fs.existsSync(distDir)) {
10
+ fs.rmSync(distDir, { recursive: true })
11
+ }
12
+
13
+ // 2. Run webpack
14
+ console.log('Building production version...')
15
+ execSync('npx webpack --mode production', {
16
+ cwd: rootDir,
17
+ stdio: 'inherit'
18
+ })
19
+
20
+ // 3. Create package.json
21
+ const pkg = require(path.join(rootDir, 'package.json'))
22
+ const prodPkg = {
23
+ name: pkg.name,
24
+ version: pkg.version,
25
+ description: pkg.description,
26
+ main: 'index.js',
27
+ keywords: pkg.keywords,
28
+ peerDependencies: pkg.peerDependencies,
29
+ author: pkg.author,
30
+ license: pkg.license,
31
+ tabbyPlugin: pkg.tabbyPlugin
32
+ }
33
+
34
+ fs.writeFileSync(
35
+ path.join(distDir, 'package.json'),
36
+ JSON.stringify(prodPkg, null, 2)
37
+ )
38
+
39
+ console.log('Production build complete: dist/')
@@ -1,5 +1,5 @@
1
- .pane-editor-overlay((click)='onCancel()')
2
- .pane-editor-modal((click)='$event.stopPropagation()')
1
+ .pane-editor-overlay((click)='onOverlayClick($event)')
2
+ .pane-editor-modal(#modal)
3
3
  .modal-header
4
4
  h4 Edit Pane
5
5
  button.btn.btn-link.close-btn(type='button', (click)='onCancel()')
@@ -1,4 +1,4 @@
1
- import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'
1
+ import { Component, Input, Output, EventEmitter, OnInit, HostListener, ElementRef, ViewChild } from '@angular/core'
2
2
  import { WorkspacePane, TabbyProfile } from '../models/workspace.model'
3
3
 
4
4
  @Component({
@@ -11,13 +11,31 @@ export class PaneEditorComponent implements OnInit {
11
11
  @Input() profiles: TabbyProfile[] = []
12
12
  @Output() save = new EventEmitter<WorkspacePane>()
13
13
  @Output() cancel = new EventEmitter<void>()
14
+ @ViewChild('modal', { static: true }) modalRef!: ElementRef<HTMLElement>
14
15
 
15
16
  editedPane!: WorkspacePane
17
+ private pointerDownInsideModal = false
16
18
 
17
19
  ngOnInit(): void {
18
20
  this.editedPane = { ...this.pane }
19
21
  }
20
22
 
23
+ @HostListener('document:keydown.escape')
24
+ onEscapeKey(): void {
25
+ this.cancel.emit()
26
+ }
27
+
28
+ @HostListener('document:pointerdown', ['$event'])
29
+ onDocumentPointerDown(event: PointerEvent): void {
30
+ this.pointerDownInsideModal = this.modalRef.nativeElement.contains(event.target as Node)
31
+ }
32
+
33
+ onOverlayClick(event: MouseEvent): void {
34
+ if (!this.pointerDownInsideModal && event.target === event.currentTarget) {
35
+ this.cancel.emit()
36
+ }
37
+ }
38
+
21
39
  onSave(): void {
22
40
  this.save.emit(this.editedPane)
23
41
  }
@@ -1,25 +1,48 @@
1
- .split-preview([class.horizontal]='split.orientation === "horizontal"', [class.vertical]='split.orientation === "vertical"')
1
+ .split-preview([class.horizontal]='split.orientation === "horizontal"', [class.vertical]='split.orientation === "vertical"', [class.nested]='depth > 0')
2
2
  ng-container(*ngFor='let child of split.children; let i = index')
3
3
  //- Pane
4
4
  .preview-pane(
5
5
  *ngIf='isPane(child)',
6
6
  [style.flex-basis]='getFlexStyle(i)',
7
- (click)='onPaneClick(asPane(child))',
7
+ [class.selected]='asPane(child).id === selectedPaneId',
8
+ (click)='onPaneClick(asPane(child)); $event.stopPropagation()',
9
+ (dblclick)='onEditClick($event, asPane(child))',
8
10
  (contextmenu)='onContextMenu($event, asPane(child))'
9
11
  )
10
12
  .pane-content
11
- .pane-label {{ getPaneLabel(asPane(child)) }}
12
- .pane-hint Click to edit
13
+ .pane-label
14
+ | {{ getPaneLabel(asPane(child)) }}
15
+ .pane-details
16
+ .pane-detail(*ngIf='asPane(child).cwd', [title]='asPane(child).cwd')
17
+ i.fas.fa-folder
18
+ span {{ truncate(asPane(child).cwd, 20) }}
19
+ .pane-detail(*ngIf='asPane(child).startupCommand', [title]='asPane(child).startupCommand')
20
+ i.fas.fa-terminal
21
+ span {{ truncate(asPane(child).startupCommand, 20) }}
22
+
23
+ button.pane-edit-btn(
24
+ type='button',
25
+ (click)='onEditClick($event, asPane(child))',
26
+ title='Edit pane'
27
+ )
28
+ i.fas.fa-pen
13
29
 
14
30
  //- Nested split
15
31
  split-preview(
16
32
  *ngIf='isSplit(child)',
17
33
  [split]='asSplit(child)',
18
34
  [depth]='depth + 1',
35
+ [selectedPaneId]='selectedPaneId',
36
+ [profiles]='profiles',
19
37
  [style.flex-basis]='getFlexStyle(i)',
20
- (paneClick)='onNestedPaneClick($event)',
38
+ (paneSelect)='onNestedPaneSelect($event)',
39
+ (paneEdit)='onNestedPaneEdit($event)',
21
40
  (splitHorizontal)='onNestedSplitH($event)',
22
41
  (splitVertical)='onNestedSplitV($event)',
42
+ (addLeft)='onNestedAddLeft($event)',
43
+ (addRight)='onNestedAddRight($event)',
44
+ (addTop)='onNestedAddTop($event)',
45
+ (addBottom)='onNestedAddBottom($event)',
23
46
  (removePane)='onNestedRemove($event)'
24
47
  )
25
48
 
@@ -33,6 +56,10 @@
33
56
  [style.top.px]='contextMenuPosition.y',
34
57
  (click)='$event.stopPropagation()'
35
58
  )
59
+ button.context-menu-item(type='button', (click)='onEdit()')
60
+ i.fas.fa-pen
61
+ | Edit
62
+ .context-menu-divider
36
63
  button.context-menu-item(type='button', (click)='onSplitH()')
37
64
  i.fas.fa-arrows-alt-h
38
65
  | Split Horizontal
@@ -40,6 +67,19 @@
40
67
  i.fas.fa-arrows-alt-v
41
68
  | Split Vertical
42
69
  .context-menu-divider
70
+ button.context-menu-item(type='button', (click)='onAddLeft()')
71
+ i.fas.fa-caret-left
72
+ | Add Left
73
+ button.context-menu-item(type='button', (click)='onAddRight()')
74
+ i.fas.fa-caret-right
75
+ | Add Right
76
+ button.context-menu-item(type='button', (click)='onAddTop()')
77
+ i.fas.fa-caret-up
78
+ | Add Top
79
+ button.context-menu-item(type='button', (click)='onAddBottom()')
80
+ i.fas.fa-caret-down
81
+ | Add Bottom
82
+ .context-menu-divider
43
83
  button.context-menu-item.danger(type='button', (click)='onRemove()')
44
84
  i.fas.fa-trash
45
85
  | Remove Pane
@@ -1,11 +1,11 @@
1
1
  .split-preview {
2
2
  display: flex;
3
3
  width: 100%;
4
- min-height: 200px;
4
+ height: 140px;
5
5
  gap: 4px;
6
- border-radius: 8px;
6
+ border-radius: 6px;
7
7
  overflow: hidden;
8
- background: var(--theme-bg-more);
8
+ background: var(--theme-bg);
9
9
  border: 1px solid var(--theme-border);
10
10
 
11
11
  &.horizontal {
@@ -15,48 +15,105 @@
15
15
  &.vertical {
16
16
  flex-direction: column;
17
17
  }
18
+
19
+ &.nested {
20
+ height: auto;
21
+ border: 1px dashed var(--theme-fg-more);
22
+ background: rgba(255, 255, 255, 0.02);
23
+ padding: 4px;
24
+ border-radius: 4px;
25
+ }
18
26
  }
19
27
 
20
28
  .preview-pane {
21
29
  display: flex;
22
30
  align-items: center;
23
31
  justify-content: center;
24
- background: var(--theme-bg-more-more);
32
+ background: var(--theme-bg-more);
25
33
  border-radius: 4px;
34
+ border: 2px solid transparent;
26
35
  cursor: pointer;
27
- transition: all 0.2s;
36
+ transition: all 0.15s;
28
37
  position: relative;
29
- min-height: 80px;
38
+ min-height: 50px;
30
39
 
31
40
  &:hover {
32
- background: var(--theme-primary);
33
-
34
- .pane-content {
35
- color: white;
36
- }
41
+ background: var(--theme-bg-more-more);
42
+ }
37
43
 
38
- .pane-hint {
39
- opacity: 1;
40
- }
44
+ &.selected {
45
+ border-color: var(--theme-primary);
46
+ background: var(--theme-bg-more-more);
41
47
  }
42
48
  }
43
49
 
44
50
  .pane-content {
45
51
  text-align: center;
46
- padding: 12px;
52
+ padding: 8px;
47
53
  color: var(--theme-fg);
54
+ max-width: 100%;
55
+ overflow: hidden;
48
56
  }
49
57
 
50
- .pane-label {
51
- font-size: 0.9rem;
58
+ .pane-title,
59
+ .pane-profile {
60
+ font-size: 0.8rem;
52
61
  font-weight: 500;
53
- margin-bottom: 4px;
62
+ margin-bottom: 2px;
63
+ white-space: nowrap;
64
+ overflow: hidden;
65
+ text-overflow: ellipsis;
66
+ }
67
+
68
+ .pane-details {
69
+ font-size: 0.7rem;
70
+ opacity: 0.7;
71
+ margin-top: 4px;
72
+
73
+ .pane-detail {
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ gap: 4px;
78
+
79
+ i {
80
+ width: 12px;
81
+ text-align: center;
82
+ font-size: 0.65rem;
83
+ }
84
+
85
+ span {
86
+ overflow: hidden;
87
+ text-overflow: ellipsis;
88
+ white-space: nowrap;
89
+ max-width: 100px;
90
+ }
91
+ }
92
+ }
93
+
94
+ .pane-edit-btn {
95
+ position: absolute;
96
+ top: 4px;
97
+ right: 4px;
98
+ padding: 4px 6px;
99
+ background: var(--theme-bg);
100
+ border: 1px solid var(--theme-border);
101
+ border-radius: 4px;
102
+ color: var(--theme-fg);
103
+ cursor: pointer;
104
+ font-size: 0.7rem;
105
+ transition: all 0.15s;
106
+ opacity: 0;
107
+
108
+ &:hover {
109
+ background: var(--theme-primary);
110
+ color: white;
111
+ border-color: var(--theme-primary);
112
+ }
54
113
  }
55
114
 
56
- .pane-hint {
57
- font-size: 0.75rem;
58
- opacity: 0.5;
59
- transition: opacity 0.2s;
115
+ .preview-pane:hover .pane-edit-btn {
116
+ opacity: 1;
60
117
  }
61
118
 
62
119
  // Context menu