tabby-tabbyspaces 0.0.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.
Files changed (61) hide show
  1. package/.claude/settings.local.json +15 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
  3. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  5. package/CHANGELOG.md +20 -0
  6. package/CLAUDE.md +159 -0
  7. package/CONTRIBUTING.md +64 -0
  8. package/LICENSE +21 -0
  9. package/README.md +61 -0
  10. package/RELEASE_PLAN.md +161 -0
  11. package/dist/build-config.d.ts +4 -0
  12. package/dist/build-config.d.ts.map +1 -0
  13. package/dist/components/paneEditor.component.d.ts +14 -0
  14. package/dist/components/paneEditor.component.d.ts.map +1 -0
  15. package/dist/components/splitPreview.component.d.ts +36 -0
  16. package/dist/components/splitPreview.component.d.ts.map +1 -0
  17. package/dist/components/workspaceEditor.component.d.ts +29 -0
  18. package/dist/components/workspaceEditor.component.d.ts.map +1 -0
  19. package/dist/components/workspaceList.component.d.ts +28 -0
  20. package/dist/components/workspaceList.component.d.ts.map +1 -0
  21. package/dist/index.d.ts +7 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +3 -0
  24. package/dist/index.js.LICENSE.txt +43 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/models/workspace.model.d.ts +77 -0
  27. package/dist/models/workspace.model.d.ts.map +1 -0
  28. package/dist/providers/config.provider.d.ts +9 -0
  29. package/dist/providers/config.provider.d.ts.map +1 -0
  30. package/dist/providers/settings.provider.d.ts +8 -0
  31. package/dist/providers/settings.provider.d.ts.map +1 -0
  32. package/dist/providers/toolbar.provider.d.ts +13 -0
  33. package/dist/providers/toolbar.provider.d.ts.map +1 -0
  34. package/dist/services/workspaceEditor.service.d.ts +25 -0
  35. package/dist/services/workspaceEditor.service.d.ts.map +1 -0
  36. package/package.json +73 -0
  37. package/screenshots/editor.png +0 -0
  38. package/screenshots/pane-edit.png +0 -0
  39. package/screenshots/workspace-edit.png +0 -0
  40. package/scripts/build-dev.js +46 -0
  41. package/src/build-config.ts +8 -0
  42. package/src/components/paneEditor.component.pug +46 -0
  43. package/src/components/paneEditor.component.scss +112 -0
  44. package/src/components/paneEditor.component.ts +33 -0
  45. package/src/components/splitPreview.component.pug +45 -0
  46. package/src/components/splitPreview.component.scss +126 -0
  47. package/src/components/splitPreview.component.ts +111 -0
  48. package/src/components/workspaceEditor.component.pug +84 -0
  49. package/src/components/workspaceEditor.component.scss +169 -0
  50. package/src/components/workspaceEditor.component.ts +181 -0
  51. package/src/components/workspaceList.component.pug +46 -0
  52. package/src/components/workspaceList.component.scss +112 -0
  53. package/src/components/workspaceList.component.ts +124 -0
  54. package/src/index.ts +38 -0
  55. package/src/models/workspace.model.ts +126 -0
  56. package/src/providers/config.provider.ts +12 -0
  57. package/src/providers/settings.provider.ts +15 -0
  58. package/src/providers/toolbar.provider.ts +81 -0
  59. package/src/services/workspaceEditor.service.ts +228 -0
  60. package/tsconfig.json +29 -0
  61. package/webpack.config.js +62 -0
package/src/index.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { NgModule } from '@angular/core'
2
+ import { CommonModule } from '@angular/common'
3
+ import { FormsModule } from '@angular/forms'
4
+ import { ConfigProvider, ToolbarButtonProvider } from 'tabby-core'
5
+ import { SettingsTabProvider } from 'tabby-settings'
6
+
7
+ import { WorkspaceEditorConfigProvider } from './providers/config.provider'
8
+ import { WorkspaceEditorSettingsProvider } from './providers/settings.provider'
9
+ import { WorkspaceToolbarProvider } from './providers/toolbar.provider'
10
+ import { WorkspaceEditorService } from './services/workspaceEditor.service'
11
+
12
+ import { WorkspaceListComponent } from './components/workspaceList.component'
13
+ import { WorkspaceEditorComponent } from './components/workspaceEditor.component'
14
+ import { PaneEditorComponent } from './components/paneEditor.component'
15
+ import { SplitPreviewComponent } from './components/splitPreview.component'
16
+
17
+ @NgModule({
18
+ imports: [CommonModule, FormsModule],
19
+ providers: [
20
+ { provide: ConfigProvider, useClass: WorkspaceEditorConfigProvider, multi: true },
21
+ { provide: SettingsTabProvider, useClass: WorkspaceEditorSettingsProvider, multi: true },
22
+ { provide: ToolbarButtonProvider, useClass: WorkspaceToolbarProvider, multi: true },
23
+ WorkspaceEditorService,
24
+ ],
25
+ declarations: [
26
+ WorkspaceListComponent,
27
+ WorkspaceEditorComponent,
28
+ PaneEditorComponent,
29
+ SplitPreviewComponent,
30
+ ],
31
+ })
32
+ export default class WorkspaceEditorModule {}
33
+
34
+ export {
35
+ WorkspaceEditorService,
36
+ WorkspaceEditorConfigProvider,
37
+ WorkspaceEditorSettingsProvider,
38
+ }
@@ -0,0 +1,126 @@
1
+ // Tabby profile interfaces
2
+ export interface TabbyProfileOptions {
3
+ command?: string
4
+ args?: string[]
5
+ cwd?: string
6
+ env?: Record<string, string>
7
+ restoreFromPTYID?: boolean
8
+ width?: number | null
9
+ height?: number | null
10
+ pauseAfterExit?: boolean
11
+ runAsAdministrator?: boolean
12
+ }
13
+
14
+ export interface TabbyProfile {
15
+ id: string
16
+ type: string
17
+ name: string
18
+ group?: string
19
+ icon?: string
20
+ color?: string
21
+ options?: TabbyProfileOptions
22
+ isBuiltin?: boolean
23
+ isTemplate?: boolean
24
+ weight?: number
25
+ disableDynamicTitle?: boolean
26
+ terminalColorScheme?: string | null
27
+ behaviorOnSessionEnd?: string
28
+ }
29
+
30
+ export interface TabbyRecoveryToken {
31
+ type: string
32
+ orientation?: 'h' | 'v'
33
+ ratios?: number[]
34
+ children?: TabbyRecoveryToken[]
35
+ profile?: Partial<TabbyProfile>
36
+ savedState?: boolean
37
+ tabTitle?: string
38
+ tabCustomTitle?: string
39
+ disableDynamicTitle?: boolean
40
+ }
41
+
42
+ export interface TabbySplitLayoutProfile {
43
+ id: string
44
+ type: 'split-layout'
45
+ name: string
46
+ group: string
47
+ icon?: string
48
+ color?: string
49
+ isBuiltin: boolean
50
+ options: {
51
+ recoveryToken: TabbyRecoveryToken
52
+ }
53
+ }
54
+
55
+ // Workspace interfaces
56
+ export interface WorkspacePane {
57
+ id: string
58
+ profileId: string
59
+ cwd?: string
60
+ startupCommand?: string
61
+ title?: string
62
+ }
63
+
64
+ export interface WorkspaceSplit {
65
+ orientation: 'horizontal' | 'vertical'
66
+ ratios: number[]
67
+ children: (WorkspacePane | WorkspaceSplit)[]
68
+ }
69
+
70
+ export interface Workspace {
71
+ id: string
72
+ name: string
73
+ icon?: string
74
+ color?: string
75
+ root: WorkspaceSplit
76
+ isDefault?: boolean
77
+ hotkey?: string
78
+ }
79
+
80
+ export function isWorkspaceSplit(node: WorkspacePane | WorkspaceSplit): node is WorkspaceSplit {
81
+ return 'orientation' in node && 'children' in node
82
+ }
83
+
84
+ export function createDefaultPane(): WorkspacePane {
85
+ return {
86
+ id: generateUUID(),
87
+ profileId: '',
88
+ cwd: '',
89
+ startupCommand: '',
90
+ title: '',
91
+ }
92
+ }
93
+
94
+ export function createDefaultSplit(orientation: 'horizontal' | 'vertical' = 'horizontal'): WorkspaceSplit {
95
+ return {
96
+ orientation,
97
+ ratios: [0.5, 0.5],
98
+ children: [createDefaultPane(), createDefaultPane()],
99
+ }
100
+ }
101
+
102
+ export function createDefaultWorkspace(name: string = 'New Workspace'): Workspace {
103
+ return {
104
+ id: generateUUID(),
105
+ name,
106
+ icon: 'columns',
107
+ color: '#3b82f6',
108
+ root: createDefaultSplit(),
109
+ isDefault: false,
110
+ }
111
+ }
112
+
113
+ export function generateUUID(): string {
114
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
115
+ const r = (Math.random() * 16) | 0
116
+ const v = c === 'x' ? r : (r & 0x3) | 0x8
117
+ return v.toString(16)
118
+ })
119
+ }
120
+
121
+ export function countPanes(node: WorkspacePane | WorkspaceSplit): number {
122
+ if (isWorkspaceSplit(node)) {
123
+ return node.children.reduce((sum, child) => sum + countPanes(child), 0)
124
+ }
125
+ return 1
126
+ }
@@ -0,0 +1,12 @@
1
+ import { Injectable } from '@angular/core'
2
+ import { ConfigProvider } from 'tabby-core'
3
+ import { CONFIG_KEY } from '../build-config'
4
+
5
+ @Injectable()
6
+ export class WorkspaceEditorConfigProvider extends ConfigProvider {
7
+ defaults = {
8
+ [CONFIG_KEY]: {
9
+ workspaces: [],
10
+ },
11
+ }
12
+ }
@@ -0,0 +1,15 @@
1
+ import { Injectable } from '@angular/core'
2
+ import { SettingsTabProvider } from 'tabby-settings'
3
+ import { WorkspaceListComponent } from '../components/workspaceList.component'
4
+ import { CONFIG_KEY, DISPLAY_NAME } from '../build-config'
5
+
6
+ @Injectable()
7
+ export class WorkspaceEditorSettingsProvider extends SettingsTabProvider {
8
+ id = CONFIG_KEY
9
+ icon = 'columns'
10
+ title = DISPLAY_NAME
11
+
12
+ getComponentType(): any {
13
+ return WorkspaceListComponent
14
+ }
15
+ }
@@ -0,0 +1,81 @@
1
+ import { Injectable } from '@angular/core'
2
+ import { ToolbarButtonProvider, ToolbarButton, ProfilesService, AppService } from 'tabby-core'
3
+ import { WorkspaceEditorService } from '../services/workspaceEditor.service'
4
+ import { SettingsTabComponent } from 'tabby-settings'
5
+ import { CONFIG_KEY, DISPLAY_NAME } from '../build-config'
6
+ import { countPanes } from '../models/workspace.model'
7
+
8
+ @Injectable()
9
+ export class WorkspaceToolbarProvider extends ToolbarButtonProvider {
10
+ constructor(
11
+ private workspaceService: WorkspaceEditorService,
12
+ private profilesService: ProfilesService,
13
+ private app: AppService
14
+ ) {
15
+ super()
16
+ }
17
+
18
+ provide(): ToolbarButton[] {
19
+ return [
20
+ {
21
+ icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
22
+ <rect x="3" y="3" width="7" height="7"/>
23
+ <rect x="14" y="3" width="7" height="7"/>
24
+ <rect x="14" y="14" width="7" height="7"/>
25
+ <rect x="3" y="14" width="7" height="7"/>
26
+ </svg>`,
27
+ title: DISPLAY_NAME,
28
+ weight: 5,
29
+ click: () => this.showWorkspaceSelector()
30
+ }
31
+ ]
32
+ }
33
+
34
+ private async showWorkspaceSelector(): Promise<void> {
35
+ const workspaces = this.workspaceService.getWorkspaces()
36
+
37
+ if (workspaces.length === 0) {
38
+ this.openSettings()
39
+ return
40
+ }
41
+
42
+ const options = workspaces.map((ws) => ({
43
+ name: ws.name,
44
+ description: `${countPanes(ws.root)} panes`,
45
+ icon: ws.icon || 'grid',
46
+ color: ws.color,
47
+ result: ws.id
48
+ }))
49
+
50
+ // Add option to open settings
51
+ options.push({
52
+ name: 'Manage Workspaces...',
53
+ description: 'Create and edit workspaces',
54
+ icon: 'cog',
55
+ color: undefined,
56
+ result: '__settings__'
57
+ })
58
+
59
+ const selectedId = await this.app.showSelector('Select Workspace', options)
60
+
61
+ if (selectedId === '__settings__') {
62
+ this.openSettings()
63
+ } else if (selectedId) {
64
+ this.openWorkspace(selectedId)
65
+ }
66
+ }
67
+
68
+ private openSettings(): void {
69
+ this.app.openNewTabRaw({ type: SettingsTabComponent, inputs: { activeTab: CONFIG_KEY } })
70
+ }
71
+
72
+ private openWorkspace(workspaceId: string): void {
73
+ const workspaces = this.workspaceService.getWorkspaces()
74
+ const workspace = workspaces.find((w) => w.id === workspaceId)
75
+
76
+ if (!workspace) return
77
+
78
+ const profile = this.workspaceService.generateTabbyProfile(workspace)
79
+ this.profilesService.openNewTabForProfile(profile)
80
+ }
81
+ }
@@ -0,0 +1,228 @@
1
+ import { Injectable } from '@angular/core'
2
+ import { ConfigService, NotificationsService, ProfilesService } from 'tabby-core'
3
+ import {
4
+ Workspace,
5
+ WorkspacePane,
6
+ WorkspaceSplit,
7
+ isWorkspaceSplit,
8
+ generateUUID,
9
+ TabbyProfile,
10
+ TabbyRecoveryToken,
11
+ TabbySplitLayoutProfile,
12
+ } from '../models/workspace.model'
13
+ import { CONFIG_KEY, DISPLAY_NAME } from '../build-config'
14
+
15
+ @Injectable({ providedIn: 'root' })
16
+ export class WorkspaceEditorService {
17
+ constructor(
18
+ private config: ConfigService,
19
+ private notifications: NotificationsService,
20
+ private profilesService: ProfilesService
21
+ ) {}
22
+
23
+ getWorkspaces(): Workspace[] {
24
+ return this.config.store[CONFIG_KEY]?.workspaces ?? []
25
+ }
26
+
27
+ async saveWorkspaces(workspaces: Workspace[]): Promise<boolean> {
28
+ this.config.store[CONFIG_KEY].workspaces = workspaces
29
+ this.syncTabbyProfiles(workspaces)
30
+ return await this.saveConfig()
31
+ }
32
+
33
+ async addWorkspace(workspace: Workspace): Promise<void> {
34
+ const workspaces = this.getWorkspaces()
35
+ workspaces.push(workspace)
36
+ await this.saveWorkspaces(workspaces)
37
+ this.notifications.info(`Workspace "${workspace.name}" created`)
38
+ }
39
+
40
+ async updateWorkspace(workspace: Workspace): Promise<void> {
41
+ const workspaces = this.getWorkspaces()
42
+ const index = workspaces.findIndex((w) => w.id === workspace.id)
43
+ if (index !== -1) {
44
+ workspaces[index] = workspace
45
+ await this.saveWorkspaces(workspaces)
46
+ this.notifications.info(`Workspace "${workspace.name}" updated`)
47
+ }
48
+ }
49
+
50
+ async deleteWorkspace(workspaceId: string): Promise<void> {
51
+ const workspaces = this.getWorkspaces()
52
+ const workspace = workspaces.find((w) => w.id === workspaceId)
53
+ const filtered = workspaces.filter((w) => w.id !== workspaceId)
54
+ await this.saveWorkspaces(filtered)
55
+ if (workspace) {
56
+ this.notifications.info(`Workspace "${workspace.name}" deleted`)
57
+ }
58
+ }
59
+
60
+ async getAvailableProfiles(): Promise<TabbyProfile[]> {
61
+ const allProfiles = await this.profilesService.getProfiles()
62
+ return allProfiles.filter(
63
+ (p) => p.type === 'local' && !p.id?.startsWith('split-layout:')
64
+ ) as TabbyProfile[]
65
+ }
66
+
67
+ private syncTabbyProfiles(workspaces: Workspace[]): void {
68
+ const profiles: (TabbyProfile | TabbySplitLayoutProfile)[] = this.config.store.profiles ?? []
69
+
70
+ // Remove old plugin profiles (mutate in place)
71
+ for (let i = profiles.length - 1; i >= 0; i--) {
72
+ if (profiles[i].id?.startsWith(`split-layout:${CONFIG_KEY}:`)) {
73
+ profiles.splice(i, 1)
74
+ }
75
+ }
76
+
77
+ // Add new workspace profiles
78
+ for (const workspace of workspaces) {
79
+ const tabbyProfile = this.generateTabbyProfile(workspace)
80
+ profiles.push(tabbyProfile)
81
+ }
82
+ }
83
+
84
+ generateTabbyProfile(workspace: Workspace): TabbySplitLayoutProfile {
85
+ const safeName = this.sanitizeForProfileId(workspace.name)
86
+ return {
87
+ id: `split-layout:${CONFIG_KEY}:${safeName}:${workspace.id}`,
88
+ type: 'split-layout',
89
+ name: workspace.name,
90
+ group: DISPLAY_NAME,
91
+ icon: workspace.icon,
92
+ color: workspace.color,
93
+ isBuiltin: false,
94
+ options: {
95
+ recoveryToken: this.generateRecoveryToken(workspace.root),
96
+ },
97
+ }
98
+ }
99
+
100
+ private generateRecoveryToken(split: WorkspaceSplit): TabbyRecoveryToken {
101
+ return {
102
+ type: 'app:split-tab',
103
+ orientation: split.orientation === 'horizontal' ? 'h' : 'v',
104
+ ratios: split.ratios,
105
+ children: split.children.map((child) => {
106
+ if (isWorkspaceSplit(child)) {
107
+ return this.generateRecoveryToken(child)
108
+ }
109
+ return this.generatePaneToken(child)
110
+ }),
111
+ }
112
+ }
113
+
114
+ private generatePaneToken(pane: WorkspacePane): TabbyRecoveryToken {
115
+ const baseProfile = this.getProfileById(pane.profileId)
116
+
117
+ if (!baseProfile) {
118
+ return {
119
+ type: 'app:local-tab',
120
+ profile: {
121
+ type: 'local',
122
+ name: 'Shell',
123
+ },
124
+ savedState: false,
125
+ }
126
+ }
127
+
128
+ // Build complete profile object like Tabby expects
129
+ const options = {
130
+ restoreFromPTYID: false,
131
+ command: baseProfile.options?.command || '',
132
+ args: baseProfile.options?.args || [],
133
+ cwd: pane.cwd || baseProfile.options?.cwd || '',
134
+ env: baseProfile.options?.env || {},
135
+ width: null,
136
+ height: null,
137
+ pauseAfterExit: false,
138
+ runAsAdministrator: false,
139
+ }
140
+
141
+ // Handle startup command for different shells
142
+ if (pane.startupCommand) {
143
+ const cmd = baseProfile.options?.command || ''
144
+ if (cmd.includes('nu.exe') || baseProfile.name?.toLowerCase().includes('nushell')) {
145
+ options.args = ['-e', pane.startupCommand]
146
+ } else if (cmd.includes('powershell') || cmd.includes('pwsh')) {
147
+ options.args = ['-NoExit', '-Command', pane.startupCommand]
148
+ } else if (cmd.includes('cmd.exe')) {
149
+ options.args = ['/K', pane.startupCommand]
150
+ } else {
151
+ options.args = ['-c', pane.startupCommand]
152
+ }
153
+ }
154
+
155
+ const profile = {
156
+ id: baseProfile.id,
157
+ type: 'local',
158
+ name: baseProfile.name || 'Shell',
159
+ group: baseProfile.group || '',
160
+ options,
161
+ icon: baseProfile.icon || '',
162
+ color: baseProfile.color || '',
163
+ disableDynamicTitle: false,
164
+ weight: 0,
165
+ isBuiltin: false,
166
+ isTemplate: false,
167
+ terminalColorScheme: null,
168
+ behaviorOnSessionEnd: 'auto',
169
+ }
170
+
171
+ return {
172
+ type: 'app:local-tab',
173
+ profile,
174
+ savedState: false,
175
+ tabTitle: pane.title || '',
176
+ tabCustomTitle: pane.title || '',
177
+ disableDynamicTitle: !!pane.title,
178
+ }
179
+ }
180
+
181
+ duplicateWorkspace(workspace: Workspace): Workspace {
182
+ const clone = JSON.parse(JSON.stringify(workspace)) as Workspace
183
+ clone.id = generateUUID()
184
+ clone.name = `${workspace.name} (Copy)`
185
+ clone.isDefault = false
186
+ this.regenerateIds(clone.root)
187
+ return clone
188
+ }
189
+
190
+ private regenerateIds(node: WorkspacePane | WorkspaceSplit): void {
191
+ if (isWorkspaceSplit(node)) {
192
+ for (const child of node.children) {
193
+ this.regenerateIds(child)
194
+ }
195
+ } else {
196
+ node.id = generateUUID()
197
+ }
198
+ }
199
+
200
+ private sanitizeForProfileId(name: string): string {
201
+ return name
202
+ .toLowerCase()
203
+ .replace(/[^a-z0-9-]/g, '-')
204
+ .replace(/-+/g, '-')
205
+ .replace(/^-|-$/g, '')
206
+ || 'workspace'
207
+ }
208
+
209
+ private getProfileById(profileId: string): TabbyProfile | undefined {
210
+ const profiles: TabbyProfile[] = this.config.store.profiles ?? []
211
+ return profiles.find((p) => p.id === profileId && p.type === 'local')
212
+ }
213
+
214
+ getProfileName(profileId: string): string | undefined {
215
+ return this.getProfileById(profileId)?.name
216
+ }
217
+
218
+ private async saveConfig(): Promise<boolean> {
219
+ try {
220
+ await this.config.save()
221
+ return true
222
+ } catch (error) {
223
+ this.notifications.error('Failed to save configuration')
224
+ console.error('TabbySpaces save error:', error)
225
+ return false
226
+ }
227
+ }
228
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ES2020",
5
+ "moduleResolution": "node",
6
+ "lib": ["ES2020", "DOM"],
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "sourceMap": true,
10
+ "outDir": "./dist",
11
+ "rootDir": "./src",
12
+ "strict": true,
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "experimentalDecorators": true,
17
+ "emitDecoratorMetadata": true,
18
+ "resolveJsonModule": true,
19
+ "baseUrl": ".",
20
+ "paths": {
21
+ "tabby-core": ["node_modules/tabby-core"],
22
+ "tabby-settings": ["node_modules/tabby-settings"],
23
+ "tabby-local": ["node_modules/tabby-local"],
24
+ "tabby-terminal": ["node_modules/tabby-terminal"]
25
+ }
26
+ },
27
+ "include": ["src/**/*"],
28
+ "exclude": ["node_modules", "dist"]
29
+ }
@@ -0,0 +1,62 @@
1
+ const path = require('path')
2
+ const webpack = require('webpack')
3
+
4
+ module.exports = (env = {}) => ({
5
+ target: 'node',
6
+ entry: './src/index.ts',
7
+ context: __dirname,
8
+ devtool: 'source-map',
9
+ output: {
10
+ path: path.resolve(__dirname, 'dist'),
11
+ filename: 'index.js',
12
+ pathinfo: true,
13
+ libraryTarget: 'umd',
14
+ publicPath: 'auto',
15
+ },
16
+ resolve: {
17
+ modules: ['.', 'src', 'node_modules'],
18
+ extensions: ['.ts', '.js'],
19
+ },
20
+ module: {
21
+ rules: [
22
+ {
23
+ test: /\.ts$/,
24
+ use: {
25
+ loader: 'ts-loader',
26
+ options: {
27
+ configFile: path.resolve(__dirname, 'tsconfig.json'),
28
+ },
29
+ },
30
+ },
31
+ {
32
+ test: /\.pug$/,
33
+ use: ['apply-loader', 'pug-loader'],
34
+ },
35
+ {
36
+ test: /\.scss$/,
37
+ use: ['to-string-loader', 'css-loader', 'sass-loader'],
38
+ },
39
+ {
40
+ test: /\.css$/,
41
+ use: ['to-string-loader', 'css-loader'],
42
+ },
43
+ ],
44
+ },
45
+ externals: [
46
+ 'fs',
47
+ 'path',
48
+ 'electron',
49
+ /^rxjs/,
50
+ /^@angular/,
51
+ /^@ng-bootstrap/,
52
+ /^tabby-/,
53
+ /^zone\.js/,
54
+ ],
55
+ plugins: [
56
+ new webpack.DefinePlugin({
57
+ __CONFIG_KEY__: JSON.stringify(env.dev ? 'tabbyspaces_dev' : 'tabbyspaces'),
58
+ __DISPLAY_NAME__: JSON.stringify(env.dev ? 'TabbySpaces DEV' : 'TabbySpaces'),
59
+ __IS_DEV__: env.dev ? 'true' : 'false',
60
+ }),
61
+ ],
62
+ })