Browse Source

bootstrap 5 WIP (#7891)

New standard theme that follows your chosen terminal colors, Bootstrap 5 & Angular 15 upgrade
Eugene 2 years ago
parent
commit
1e5cfd1d4b
100 changed files with 1537 additions and 742 deletions
  1. 145 135
      .eslintrc.yml
  2. 15 15
      .github/workflows/build.yml
  3. 1 0
      app/index.pug
  4. 3 1
      app/lib/window.ts
  5. 6 4
      app/package.json
  6. 5 5
      app/src/app.module.ts
  7. 0 1
      app/src/entry.preload.ts
  8. 3 1
      app/src/entry.ts
  9. 8 8
      app/src/global.scss
  10. 0 6
      app/src/root.component.ts
  11. 4 0
      app/src/toastr.scss
  12. 11 7
      app/webpack.config.main.mjs
  13. 40 10
      app/webpack.config.mjs
  14. 28 12
      app/yarn.lock
  15. 1 1
      build/mac/afterBuildHook.cjs
  16. 0 0
      build/mac/afterSignHook.cjs
  17. 2 2
      electron-builder.yml
  18. 143 136
      locale/app.pot
  19. 34 31
      package.json
  20. 3 3
      scripts/build-docs.mjs
  21. 2 2
      scripts/build-linux.mjs
  22. 2 2
      scripts/build-macos.mjs
  23. 0 22
      scripts/build-modules.js
  24. 22 0
      scripts/build-modules.mjs
  25. 7 3
      scripts/build-native.mjs
  26. 3 3
      scripts/build-typings.mjs
  27. 2 2
      scripts/build-windows.mjs
  28. 8 3
      scripts/generate-icon-metadata.mjs
  29. 5 5
      scripts/i18n-extract.mjs
  30. 3 3
      scripts/install-deps.mjs
  31. 10 6
      scripts/prepackage-plugins.mjs
  32. 4 4
      scripts/publish-plugins.mjs
  33. 2 2
      scripts/sentry-upload.mjs
  34. 20 17
      scripts/vars.mjs
  35. 0 6
      tabby-community-color-schemes/webpack.config.js
  36. 14 0
      tabby-community-color-schemes/webpack.config.mjs
  37. 3 5
      tabby-core/package.json
  38. 2 1
      tabby-core/src/api/index.ts
  39. 1 0
      tabby-core/src/api/theme.ts
  40. 3 2
      tabby-core/src/components/appRoot.component.ts
  41. 1 0
      tabby-core/src/components/baseTab.component.ts
  42. 3 3
      tabby-core/src/components/checkbox.component.ts
  43. 1 1
      tabby-core/src/components/profileIcon.component.pug
  44. 2 2
      tabby-core/src/components/profileIcon.component.ts
  45. 1 1
      tabby-core/src/components/promptModal.component.pug
  46. 2 1
      tabby-core/src/components/promptModal.component.ts
  47. 1 1
      tabby-core/src/components/renameTabModal.component.ts
  48. 1 1
      tabby-core/src/components/safeModeModal.component.ts
  49. 6 6
      tabby-core/src/components/selectorModal.component.pug
  50. 2 2
      tabby-core/src/components/selectorModal.component.ts
  51. 6 5
      tabby-core/src/components/selfPositioning.component.ts
  52. 1 1
      tabby-core/src/components/splitTab.component.ts
  53. 1 1
      tabby-core/src/components/splitTabDropZone.component.ts
  54. 2 2
      tabby-core/src/components/splitTabPaneLabel.component.ts
  55. 0 5
      tabby-core/src/components/splitTabSpanner.component.scss
  56. 1 1
      tabby-core/src/components/splitTabSpanner.component.ts
  57. 5 5
      tabby-core/src/components/startPage.component.pug
  58. 4 3
      tabby-core/src/components/startPage.component.ts
  59. 0 5
      tabby-core/src/components/tabBody.component.scss
  60. 3 3
      tabby-core/src/components/tabBody.component.ts
  61. 1 1
      tabby-core/src/components/tabHeader.component.pug
  62. 0 1
      tabby-core/src/components/tabHeader.component.scss
  63. 2 2
      tabby-core/src/components/tabHeader.component.ts
  64. 1 1
      tabby-core/src/components/titleBar.component.pug
  65. 6 3
      tabby-core/src/components/titleBar.component.ts
  66. 8 2
      tabby-core/src/components/toggle.component.scss
  67. 4 4
      tabby-core/src/components/toggle.component.ts
  68. 1 1
      tabby-core/src/components/transfersMenu.component.pug
  69. 2 2
      tabby-core/src/components/transfersMenu.component.ts
  70. 3 4
      tabby-core/src/components/unlockVaultModal.component.pug
  71. 1 1
      tabby-core/src/components/unlockVaultModal.component.ts
  72. 2 2
      tabby-core/src/components/welcomeTab.component.ts
  73. 2 2
      tabby-core/src/components/windowControls.component.ts
  74. 5 5
      tabby-core/src/config.ts
  75. 1 2
      tabby-core/src/configDefaults.macos.yaml
  76. 0 0
      tabby-core/src/icons.json
  77. 13 34
      tabby-core/src/index.ts
  78. 1 1
      tabby-core/src/services/commands.service.ts
  79. 5 3
      tabby-core/src/services/config.service.ts
  80. 19 18
      tabby-core/src/services/locale.service.ts
  81. 105 2
      tabby-core/src/services/themes.service.ts
  82. 568 0
      tabby-core/src/theme.new.scss
  83. 18 11
      tabby-core/src/theme.paper.scss
  84. 20 9
      tabby-core/src/theme.scss
  85. 16 3
      tabby-core/src/theme.ts
  86. 16 7
      tabby-core/src/theme.vars.scss
  87. 3 0
      tabby-core/src/theme.vendor.scss
  88. 0 5
      tabby-core/webpack.config.js
  89. 10 0
      tabby-core/webpack.config.mjs
  90. 43 52
      tabby-core/yarn.lock
  91. 0 5
      tabby-electron/webpack.config.js
  92. 10 0
      tabby-electron/webpack.config.mjs
  93. 0 5
      tabby-linkifier/webpack.config.js
  94. 10 0
      tabby-linkifier/webpack.config.mjs
  95. 18 21
      tabby-local/src/components/commandLineEditor.component.pug
  96. 1 1
      tabby-local/src/components/commandLineEditor.component.ts
  97. 7 9
      tabby-local/src/components/environmentEditor.component.pug
  98. 2 2
      tabby-local/src/components/environmentEditor.component.ts
  99. 4 5
      tabby-local/src/components/localProfileSettings.component.pug
  100. 1 1
      tabby-local/src/components/localProfileSettings.component.ts

+ 145 - 135
.eslintrc.yml

@@ -1,15 +1,3 @@
-parser: '@typescript-eslint/parser'
-parserOptions:
-  project:
-  - tsconfig.json
-  - '*/tsconfig.typings.json'
-extends:
-  - 'plugin:@typescript-eslint/all'
-  - plugin:import/recommended
-  - plugin:import/typescript
-plugins:
-  - '@typescript-eslint'
-  - 'import'
 settings:
   import/resolver:
     typescript: true
@@ -19,126 +7,148 @@ env:
   es6: true
   node: true
   commonjs: true
-rules:
-  '@typescript-eslint/semi':
-  - error
-  - never
-  '@typescript-eslint/indent':
-  - error
-  - 4
-  '@typescript-eslint/explicit-member-accessibility':
-  - error
-  - accessibility: no-public
-    overrides:
-      parameterProperties: explicit
-  '@typescript-eslint/no-require-imports': off
-  '@typescript-eslint/no-parameter-properties': off
-  '@typescript-eslint/explicit-function-return-type': off
-  '@typescript-eslint/no-explicit-any': off
-  '@typescript-eslint/no-magic-numbers': off
-  '@typescript-eslint/member-delimiter-style': off
-  '@typescript-eslint/promise-function-async': off
-  '@typescript-eslint/require-array-sort-compare': off
-  '@typescript-eslint/no-floating-promises': off
-  '@typescript-eslint/prefer-readonly': off
-  '@typescript-eslint/require-await': off
-  '@typescript-eslint/strict-boolean-expressions': off
-  '@typescript-eslint/no-misused-promises':
-  - error
-  - checksVoidReturn: false
-  '@typescript-eslint/typedef': off
-  '@typescript-eslint/consistent-type-imports': off
-  '@typescript-eslint/sort-type-union-intersection-members': off
-  '@typescript-eslint/no-use-before-define':
-  - error
-  - classes: false
-  no-duplicate-imports: error
-  array-bracket-spacing:
-  - error
-  - never
-  block-scoped-var: error
-  brace-style: off
-  '@typescript-eslint/brace-style':
-  - error
-  - 1tbs
-  - allowSingleLine: true
-  computed-property-spacing:
-  - error
-  - never
-  comma-dangle: off
-  '@typescript-eslint/comma-dangle':
-  - error
-  - always-multiline
-  curly: error
-  eol-last: error
-  eqeqeq:
-  - error
-  - smart
-  max-depth:
-  - 1
-  - 5
-  max-statements:
-  - 1
-  - 80
-  no-multiple-empty-lines: error
-  no-mixed-spaces-and-tabs: error
-  no-trailing-spaces: error
-  '@typescript-eslint/no-unused-vars':
-    - error
-    - vars: all
-      args: after-used
-      argsIgnorePattern: ^_
-  no-undef: error
-  no-var: error
-  object-curly-spacing: off
-  '@typescript-eslint/object-curly-spacing':
-  - error
-  - always
-  quote-props:
-  - warn
-  - as-needed
-  - keywords: true
-    numbers: true
-  quotes: off
-  '@typescript-eslint/quotes':
-  - error
-  - single
-  - allowTemplateLiterals: true
-  '@typescript-eslint/no-confusing-void-expression':
-  - error
-  - ignoreArrowShorthand: true
-  '@typescript-eslint/no-non-null-assertion': off
-  '@typescript-eslint/no-unnecessary-condition':
-  - error
-  - allowConstantLoopConditions: true
-  '@typescript-eslint/restrict-template-expressions': off
-  '@typescript-eslint/prefer-readonly-parameter-types': off
-  '@typescript-eslint/no-unsafe-member-access': off
-  '@typescript-eslint/no-unsafe-call': off
-  '@typescript-eslint/no-unsafe-return': off
-  '@typescript-eslint/no-unsafe-assignment': off
-  '@typescript-eslint/naming-convention': off
-  '@typescript-eslint/lines-between-class-members':
-  - error
-  - exceptAfterSingleLine: true
-  '@typescript-eslint/dot-notation': off
-  '@typescript-eslint/no-implicit-any-catch': off
-  '@typescript-eslint/member-ordering': off
-  '@typescript-eslint/no-var-requires': off
-  '@typescript-eslint/no-unsafe-argument': off
-  '@typescript-eslint/restrict-plus-operands': off
-  '@typescript-eslint/space-infix-ops': off
-  '@typescript-eslint/no-type-alias':
-  - error
-  - allowAliases: in-unions-and-intersections
-    allowLiterals: always
-    allowCallbacks: always
-  '@typescript-eslint/sort-type-constituents': off
-  '@typescript-eslint/parameter-properties':
-  - error
-  - prefer: parameter-property
-  'import/no-named-as-default-member': off
-  '@typescript-eslint/consistent-type-exports': off
-  '@typescript-eslint/consistent-generic-constructors': off
-  'keyword-spacing': off
-  '@typescript-eslint/keyword-spacing': off
+
+overrides:
+- files: '*.mjs'
+  plugins:
+    - 'import'
+  parserOptions:
+    sourceType: module
+    ecmaVersion: latest
+
+- files: '*.ts'
+  parser: '@typescript-eslint/parser'
+  parserOptions:
+    project:
+    - tsconfig.json
+    - '*/tsconfig.typings.json'
+  extends:
+    - 'plugin:@typescript-eslint/all'
+    - plugin:import/recommended
+    - plugin:import/typescript
+  plugins:
+    - '@typescript-eslint'
+    - 'import'
+  rules:
+    '@typescript-eslint/semi':
+    - error
+    - never
+    '@typescript-eslint/indent':
+    - error
+    - 4
+    '@typescript-eslint/explicit-member-accessibility':
+    - error
+    - accessibility: no-public
+      overrides:
+        parameterProperties: explicit
+    '@typescript-eslint/no-require-imports': off
+    '@typescript-eslint/no-parameter-properties': off
+    '@typescript-eslint/explicit-function-return-type': off
+    '@typescript-eslint/no-explicit-any': off
+    '@typescript-eslint/no-magic-numbers': off
+    '@typescript-eslint/member-delimiter-style': off
+    '@typescript-eslint/promise-function-async': off
+    '@typescript-eslint/require-array-sort-compare': off
+    '@typescript-eslint/no-floating-promises': off
+    '@typescript-eslint/prefer-readonly': off
+    '@typescript-eslint/require-await': off
+    '@typescript-eslint/strict-boolean-expressions': off
+    '@typescript-eslint/no-misused-promises':
+    - error
+    - checksVoidReturn: false
+    '@typescript-eslint/typedef': off
+    '@typescript-eslint/consistent-type-imports': off
+    '@typescript-eslint/sort-type-union-intersection-members': off
+    '@typescript-eslint/no-use-before-define':
+    - error
+    - classes: false
+    no-duplicate-imports: error
+    array-bracket-spacing:
+    - error
+    - never
+    block-scoped-var: error
+    brace-style: off
+    '@typescript-eslint/brace-style':
+    - error
+    - 1tbs
+    - allowSingleLine: true
+    computed-property-spacing:
+    - error
+    - never
+    comma-dangle: off
+    '@typescript-eslint/comma-dangle':
+    - error
+    - always-multiline
+    curly: error
+    eol-last: error
+    eqeqeq:
+    - error
+    - smart
+    max-depth:
+    - 1
+    - 5
+    max-statements:
+    - 1
+    - 80
+    no-multiple-empty-lines: error
+    no-mixed-spaces-and-tabs: error
+    no-trailing-spaces: error
+    '@typescript-eslint/no-unused-vars':
+      - error
+      - vars: all
+        args: after-used
+        argsIgnorePattern: ^_
+    no-undef: error
+    no-var: error
+    object-curly-spacing: off
+    '@typescript-eslint/object-curly-spacing':
+    - error
+    - always
+    quote-props:
+    - warn
+    - as-needed
+    - keywords: true
+      numbers: true
+    quotes: off
+    '@typescript-eslint/quotes':
+    - error
+    - single
+    - allowTemplateLiterals: true
+    '@typescript-eslint/no-confusing-void-expression':
+    - error
+    - ignoreArrowShorthand: true
+    '@typescript-eslint/no-non-null-assertion': off
+    '@typescript-eslint/no-unnecessary-condition':
+    - error
+    - allowConstantLoopConditions: true
+    '@typescript-eslint/restrict-template-expressions': off
+    '@typescript-eslint/prefer-readonly-parameter-types': off
+    '@typescript-eslint/no-unsafe-member-access': off
+    '@typescript-eslint/no-unsafe-call': off
+    '@typescript-eslint/no-unsafe-return': off
+    '@typescript-eslint/no-unsafe-assignment': off
+    '@typescript-eslint/naming-convention': off
+    '@typescript-eslint/lines-between-class-members':
+    - error
+    - exceptAfterSingleLine: true
+    '@typescript-eslint/dot-notation': off
+    '@typescript-eslint/no-implicit-any-catch': off
+    '@typescript-eslint/member-ordering': off
+    '@typescript-eslint/no-var-requires': off
+    '@typescript-eslint/no-unsafe-argument': off
+    '@typescript-eslint/restrict-plus-operands': off
+    '@typescript-eslint/space-infix-ops': off
+    '@typescript-eslint/no-type-alias':
+    - error
+    - allowAliases: in-unions-and-intersections
+      allowLiterals: always
+      allowCallbacks: always
+    '@typescript-eslint/sort-type-constituents': off
+    '@typescript-eslint/parameter-properties':
+    - error
+    - prefer: parameter-property
+    'import/no-named-as-default-member': off
+    '@typescript-eslint/consistent-type-exports': off
+    '@typescript-eslint/consistent-generic-constructors': off
+    'keyword-spacing': off
+    '@typescript-eslint/keyword-spacing': off

+ 15 - 15
.github/workflows/build.yml

@@ -68,7 +68,7 @@ jobs:
       run: yarn run build
 
     - name: Prepackage plugins
-      run: scripts/prepackage-plugins.js
+      run: scripts/prepackage-plugins.mjs
       env:
         ARCH: ${{matrix.arch}}
 
@@ -78,7 +78,7 @@ jobs:
     - run: ln -s ../../node_modules/electron app/node_modules
 
     - name: Build and sign packages
-      run: scripts/build-macos.js
+      run: scripts/build-macos.mjs
       if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
       env:
         ARCH: ${{matrix.arch}}
@@ -92,7 +92,7 @@ jobs:
         # DEBUG: electron-builder,electron-builder:*
 
     - name: Build packages without signing
-      run: scripts/build-macos.js
+      run: scripts/build-macos.mjs
       if: "! (github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
       env:
         ARCH: ${{matrix.arch}}
@@ -101,7 +101,7 @@ jobs:
     - name: Upload symbols
       run: |
         sudo npm install -g @sentry/cli --unsafe-perm
-        ./scripts/sentry-upload.js
+        ./scripts/sentry-upload.mjs
       env:
         SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
         SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
@@ -162,11 +162,11 @@ jobs:
       if: matrix.build-arch == 'x64'
 
     - name: Prepackage plugins (x64)
-      run: scripts/prepackage-plugins.js
+      run: scripts/prepackage-plugins.mjs
       if: ${{matrix.build-arch == 'x64'}}
 
     - name: Build packages (x64)
-      run: scripts/build-linux.js
+      run: scripts/build-linux.mjs
       if: ${{matrix.build-arch == 'x64'}}
       env:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -193,8 +193,8 @@ jobs:
             cd /github/workspace &&
             yarn --network-timeout 1000000 &&
             yarn run build &&
-            scripts/prepackage-plugins.js &&
-            USE_SYSTEM_FPM=true scripts/build-linux.js"
+            scripts/prepackage-plugins.mjs &&
+            USE_SYSTEM_FPM=true scripts/build-linux.mjs"
       env:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
@@ -217,8 +217,8 @@ jobs:
             sed -i '/    \"electron\":/c\    \"electron\": \"17.0.0\",' package.json &&
             yarn --network-timeout 1000000 &&
             yarn run build &&
-            scripts/prepackage-plugins.js &&
-            USE_SYSTEM_FPM=true scripts/build-linux.js"
+            scripts/prepackage-plugins.mjs &&
+            USE_SYSTEM_FPM=true scripts/build-linux.mjs"
       env:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
@@ -228,7 +228,7 @@ jobs:
     - name: Upload symbols
       run: |
         sudo npm install -g @sentry/cli --unsafe-perm
-        ./scripts/sentry-upload.js
+        ./scripts/sentry-upload.mjs
       if: matrix.build-arch == 'x64'
       env:
         SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
@@ -308,12 +308,12 @@ jobs:
         npm i -g [email protected]
         yarn --network-timeout 1000000
         yarn run build
-        node scripts/prepackage-plugins.js
+        node scripts/prepackage-plugins.mjs
       env:
         ARCH: ${{matrix.arch}}
 
     - name: Build and sign packages
-      run: node scripts/build-windows.js
+      run: node scripts/build-windows.mjs
       if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
       env:
         ARCH: ${{matrix.arch}}
@@ -324,7 +324,7 @@ jobs:
         DEBUG: electron-builder,electron-builder:*
 
     - name: Build packages without signing
-      run: node scripts/build-windows.js
+      run: node scripts/build-windows.mjs
       if: "!(github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
       env:
         ARCH: ${{matrix.arch}}
@@ -332,7 +332,7 @@ jobs:
     - name: Upload symbols
       run: |
         npm install @sentry/cli
-        node scripts/sentry-upload.js
+        node scripts/sentry-upload.mjs
       env:
         SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
         SENTRY_ORG: ${{ secrets.SENTRY_ORG }}

+ 1 - 0
app/index.pug

@@ -12,6 +12,7 @@ html.tabby
             body { transition: 0.5s background; }
     body
         style#custom-css
+        root
         app-root
             .preload-logo
                 div

+ 3 - 1
app/lib/window.ts

@@ -413,7 +413,9 @@ export class Window {
             this.touchBarControl.selectedIndex = selectedIndex
         })
 
-        this.window.webContents.on('new-window', event => event.preventDefault())
+        this.window.webContents.setWindowOpenHandler(() => {
+            return { action: 'deny' }
+        })
 
         ipcMain.on('window-set-disable-vibrancy-while-dragging', (_event, value) => {
             this.disableVibrancyWhileDragging = value && this.configStore.hacks?.disableVibrancyWhileDragging

+ 6 - 4
app/package.json

@@ -37,17 +37,19 @@
   "optionalDependencies": {
     "@tabby-gang/windows-blurbehind": "^3.0.0",
     "macos-native-processlist": "^2.1.0",
+    "patch-package": "^6.5.0",
     "serialport": "10.5.0",
     "serialport-binding-webserialapi": "^1.0.3",
     "windows-native-registry": "^3.2.1",
-    "windows-process-tree": "^0.3.4",
-    "patch-package": "^6.5.0"
+    "windows-process-tree": "^0.3.4"
   },
   "devDependencies": {
+    "@ngx-translate/core": "^14.0.0",
     "@types/mz": "2.7.4",
-    "@types/node": "18.7.23",
+    "@types/node": "18.11.19",
     "atomically": "^1.7.0",
-    "ngx-filesize": "^2.0.16"
+    "filesize": "^9",
+    "ngx-filesize": "^3.0.1"
   },
   "peerDependencies": {
     "tabby-community-color-schemes": "*",

+ 5 - 5
app/src/app.module.ts

@@ -1,14 +1,12 @@
 /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
-import { NgModule } from '@angular/core'
+import { ApplicationRef, NgModule } from '@angular/core'
 import { BrowserModule } from '@angular/platform-browser'
-import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
 import { ToastrModule } from 'ngx-toastr'
 
 export function getRootModule (plugins: any[]) {
     const imports = [
         BrowserModule,
         ...plugins,
-        NgbModule,
         ToastrModule.forRoot({
             positionClass: 'toast-bottom-center',
             toastClass: 'toast',
@@ -27,10 +25,12 @@ export function getRootModule (plugins: any[]) {
 
     @NgModule({
         imports,
-        bootstrap,
     }) class RootModule {
-        ngDoBootstrap () {
+        ngDoBootstrap (appRef: ApplicationRef) {
             (window as any)['requestAnimationFrame'] = window[window['Zone'].__symbol__('requestAnimationFrame')]
+
+            const componentDef = bootstrap[0]
+            appRef.bootstrap(componentDef)
         }
     }
 

+ 0 - 1
app/src/entry.preload.ts

@@ -6,5 +6,4 @@ import '@fortawesome/fontawesome-free/css/solid.css'
 import '@fortawesome/fontawesome-free/css/brands.css'
 import '@fortawesome/fontawesome-free/css/regular.css'
 import '@fortawesome/fontawesome-free/css/fontawesome.css'
-import 'ngx-toastr/toastr.css'
 import './preload.scss'

+ 3 - 1
app/src/entry.ts

@@ -39,8 +39,10 @@ async function bootstrap (bootstrapData: BootstrapData, plugins: PluginInfo[], s
     const pluginModules = await loadPlugins(plugins, (current, total) => {
         (document.querySelector('.progress .bar') as HTMLElement).style.width = `${100 * current / total}%` // eslint-disable-line
     })
+
+    window['pluginModules'] = pluginModules
+
     const module = getRootModule(pluginModules)
-    window['rootModule'] = module
     const moduleRef = await platformBrowserDynamic([
         { provide: BOOTSTRAP_DATA, useValue: bootstrapData },
     ]).bootstrapModule(module)

+ 8 - 8
app/src/global.scss

@@ -13,10 +13,6 @@ body {
     user-select: text;
 }
 
-[ngbradiogroup] input[type="radio"] {
-    display: none;
-}
-
 a, button {
     &.btn {
         display: inline-flex;
@@ -53,6 +49,10 @@ a, button {
     &>.form-control, &>.input-group {
         width: 33%;
     }
+
+    &>.form-check {
+        display: flex;
+    }
 }
 
 input[type=range] {
@@ -175,19 +175,19 @@ ngb-typeahead-window {
 
 // Windows high contrast mode
 @media screen and (forced-colors: active) {
-    .custom-switch .custom-control-label::before {
+    .form-switch .form-check-label::before {
         background: buttonface;
     }
 
-    .custom-switch .custom-control-label::after {
+    .form-switch .form-check-label::after {
         background: buttontext;
     }
 
-    .custom-switch .custom-control-input:checked ~ .custom-control-label::before {
+    .form-switch .form-check-input:checked ~ .form-check-label::before {
         background: activetext;
     }
 
-    .custom-switch .custom-control-input:checked ~ .custom-control-label::after {
+    .form-switch .form-check-input:checked ~ .form-check-label::after {
         background: canvas;
     }
 

+ 0 - 6
app/src/root.component.ts

@@ -1,6 +0,0 @@
-import { Component } from '@angular/core'
-
-@Component({
-    template: '<app-root></app-root>',
-})
-export class RootComponent { } // eslint-disable-line @typescript-eslint/no-extraneous-class

+ 4 - 0
app/src/toastr.scss

@@ -8,6 +8,8 @@
     box-shadow: 0 1px 0 rgba(0,0,0,.25);
     padding: 7px 12px;
     background-image: none;
+    display: block !important;
+    border: none !important;
     width: auto;
     flex-basis: auto;
     border-radius: 0.5rem;
@@ -15,10 +17,12 @@
 
     &.toast-error {
       background-color: #BD362F;
+      color: white !important;
     }
 
     &.toast-info {
       background-color: #555;
+      color: #eee !important;
     }
   }
 }

+ 11 - 7
app/webpack.main.config.js → app/webpack.config.main.mjs

@@ -1,8 +1,10 @@
-const path = require('path')
-const webpack = require('webpack')
-const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+import * as path from 'path'
+import wp from 'webpack'
+import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
 
-module.exports = {
+const config = {
     name: 'tabby-main',
     target: 'electron-main',
     entry: {
@@ -55,13 +57,15 @@ module.exports = {
         'yargs/yargs': 'commonjs yargs/yargs',
     },
     plugins: [
-        new webpack.optimize.ModuleConcatenationPlugin(),
-        new webpack.DefinePlugin({
+        new wp.optimize.ModuleConcatenationPlugin(),
+        new wp.DefinePlugin({
             'process.type': '"main"',
         }),
     ],
 }
 
 if (process.env.BUNDLE_ANALYZER) {
-    module.exports.plugins.push(new BundleAnalyzerPlugin())
+    config.plugins.push(new BundleAnalyzerPlugin())
 }
+
+export default () => config

+ 40 - 10
app/webpack.config.js → app/webpack.config.mjs

@@ -1,7 +1,23 @@
-const path = require('path')
-const webpack = require('webpack')
+import * as fs from 'fs'
+import * as path from 'path'
+import wp from 'webpack'
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
 
-module.exports = {
+import { AngularWebpackPlugin } from '@ngtools/webpack'
+import { createEs2015LinkerPlugin } from '@angular/compiler-cli/linker/babel'
+const linkerPlugin = createEs2015LinkerPlugin({
+    linkerJitMode: true,
+    fileSystem: {
+        resolve: path.resolve,
+        exists: fs.existsSync,
+        dirname: path.dirname,
+        relative: path.relative,
+        readFile: fs.readFileSync,
+    },
+})
+
+export default () => ({
     name: 'tabby',
     target: 'node',
     entry: {
@@ -28,13 +44,22 @@ module.exports = {
     },
     module: {
         rules: [
+            {
+                test: /\.(m?)js$/,
+                loader: 'babel-loader',
+                options: {
+                    plugins: [linkerPlugin],
+                    compact: false,
+                    cacheDirectory: true,
+                },
+                resolve: {
+                    fullySpecified: false,
+                },
+            },
             {
                 test: /\.ts$/,
                 use: {
-                    loader: 'ts-loader',
-                    options: {
-                        configFile: path.resolve(__dirname, 'tsconfig.json'),
-                    },
+                    loader: '@ngtools/webpack',
                 },
             },
             { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
@@ -56,9 +81,14 @@ module.exports = {
         path: 'commonjs path',
     },
     plugins: [
-        new webpack.optimize.ModuleConcatenationPlugin(),
-        new webpack.DefinePlugin({
+        new wp.optimize.ModuleConcatenationPlugin(),
+        new wp.DefinePlugin({
             'process.type': '"renderer"',
         }),
+        new AngularWebpackPlugin({
+            tsconfig: path.resolve(__dirname, 'tsconfig.json'),
+            directTemplateLoading: false,
+            jitMode: true,
+        })
     ],
-}
+})

+ 28 - 12
app/yarn.lock

@@ -16,6 +16,13 @@
     update-notifier "^2.2.0"
     yargs "^8.0.2"
 
+"@ngx-translate/core@^14.0.0":
+  version "14.0.0"
+  resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-14.0.0.tgz#af421d0e1a28376843f0fed375cd2fae7630a5ff"
+  integrity sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==
+  dependencies:
+    tslib "^2.3.0"
+
 "@serialport/binding-abstract@^9.0.2":
   version "9.2.3"
   resolved "https://registry.yarnpkg.com/@serialport/binding-abstract/-/binding-abstract-9.2.3.tgz#e7dd273357b6a698af7ad58db6f57f62443a0acb"
@@ -140,11 +147,16 @@
   dependencies:
     "@types/node" "*"
 
-"@types/node@*", "@types/[email protected]":
+"@types/node@*":
   version "18.7.23"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.23.tgz#75c580983846181ebe5f4abc40fe9dfb2d65665f"
   integrity sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg==
 
+"@types/[email protected]":
+  version "18.11.19"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.19.tgz#35e26df9ec441ab99d73e99e9aca82935eea216d"
+  integrity sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw==
+
 "@types/node@^10.12.18":
   version "10.17.60"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
@@ -1094,10 +1106,10 @@ figgy-pudding@^3.4.1, figgy-pudding@^3.5.1:
   resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
   integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
 
-"filesize@>= 4.0.0":
-  version "6.3.0"
-  resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.3.0.tgz#dff53cfb3f104c9e422f346d53be8dbcc971bf11"
-  integrity sha512-ytx0ruGpDHKWVoiui6+BY/QMNngtDQ/pJaFwfBpQif0J63+E8DLdFyqS3NkKQn7vIruUEpoGD9JUJSg7Kp+I0g==
+filesize@^9:
+  version "9.0.11"
+  resolved "https://registry.yarnpkg.com/filesize/-/filesize-9.0.11.tgz#4ac3a42c084232dd9b2a1da0107f32d42fcfa5e4"
+  integrity sha512-gTAiTtI0STpKa5xesyTA9hA3LX4ga8sm2nWRcffEa1L/5vQwb4mj2MdzMkoHoGv4QzfDshQZuYscQSf8c4TKOA==
 
 fill-range@^7.0.1:
   version "7.0.1"
@@ -2261,13 +2273,12 @@ native-process-working-directory@^1.0.2:
   dependencies:
     node-addon-api "^3.1.0"
 
-ngx-filesize@^2.0.16:
-  version "2.0.16"
-  resolved "https://registry.yarnpkg.com/ngx-filesize/-/ngx-filesize-2.0.16.tgz#fdaba04170edb6cfcdf7be932783cf913b03f016"
-  integrity sha512-VdaCirE7hSyfQh8ZEmhzNEhbddiTYUHF4V6OX+KyTmnQSVx4hp9kmzDX5YlkIlmClI6wI+LZmH9/q7XS3fsMPA==
+ngx-filesize@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/ngx-filesize/-/ngx-filesize-3.0.1.tgz#620933ae181a1128905404e43b26abb99accba90"
+  integrity sha512-792I4fiG9EEPYjGib2+YTYAzfWIlccfy8uXNSXFFepuAntoS+eijLwnl0m7ajcQuNgbTtwEf9VmYIPN8mS3jqQ==
   dependencies:
-    filesize ">= 4.0.0"
-    tslib "^2.0.0"
+    tslib "^2.3.0"
 
 nice-try@^1.0.4:
   version "1.0.5"
@@ -3717,11 +3728,16 @@ tough-cookie@~2.5.0:
     psl "^1.1.28"
     punycode "^2.1.1"
 
-tslib@^2.0.0, tslib@^2.1.0:
+tslib@^2.1.0:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
   integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
 
+tslib@^2.3.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
+  integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
+
 tunnel-agent@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz"

+ 1 - 1
build/mac/afterBuildHook.js → build/mac/afterBuildHook.cjs

@@ -1,5 +1,5 @@
 const fs = require('fs')
-const signHook = require('./afterSignHook')
+const signHook = require('./afterSignHook.cjs')
 
 module.exports = async function (params) {
     // notarize the app on Mac OS only.

+ 0 - 0
build/mac/afterSignHook.js → build/mac/afterSignHook.cjs


+ 2 - 2
electron-builder.yml

@@ -3,8 +3,8 @@ appId: org.tabby
 productName: Tabby
 compression: normal
 npmRebuild: false
-afterSign: "./build/mac/afterSignHook.js"
-afterAllArtifactBuild: "./build/mac/afterBuildHook.js"
+afterSign: "./build/mac/afterSignHook.cjs"
+afterAllArtifactBuild: "./build/mac/afterBuildHook.cjs"
 files:
 - '**/*'
 - dist

File diff suppressed because it is too large
+ 143 - 136
locale/app.pot


+ 34 - 31
package.json

@@ -1,17 +1,19 @@
 {
   "devDependencies": {
-    "@angular/animations": "^12.0.0",
-    "@angular/cdk": "^12.2.9",
-    "@angular/common": "^12.0.0",
-    "@angular/compiler": "^12.0.0",
-    "@angular/compiler-cli": "^12.0.0",
-    "@angular/core": "^12.0.0",
-    "@angular/forms": "^12.0.0",
-    "@angular/platform-browser": "^12.0.0",
-    "@angular/platform-browser-dynamic": "^12.0.0",
+    "@angular/animations": "^15.1.3",
+    "@angular/cdk": "^15.1.3",
+    "@angular/common": "^15.1.3",
+    "@angular/compiler": "^15.1.3",
+    "@angular/compiler-cli": "^15.1.3",
+    "@angular/core": "^15.1.3",
+    "@angular/forms": "^15.1.3",
+    "@angular/platform-browser": "^15.1.3",
+    "@angular/platform-browser-dynamic": "^15.1.3",
     "@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
     "@fortawesome/fontawesome-free": "^6.2.0",
-    "@ng-bootstrap/ng-bootstrap": "^10.0.0",
+    "@ng-bootstrap/ng-bootstrap": "^14.0.1",
+    "@ngtools/webpack": "^15.1.4",
+    "@popperjs/core": "^2.11.6",
     "@sentry/cli": "^1.74.3",
     "@sentry/electron": "^2.5.4",
     "@tabby-gang/to-string-loader": "^1.1.7-beta.2",
@@ -21,21 +23,21 @@
     "@types/fs-extra": "^9.0.13",
     "@types/js-yaml": "^4.0.5",
     "@types/node": "16.0.1",
-    "@types/sortablejs": "^1.15.0",
     "@types/webpack-env": "^1.18.0",
     "@typescript-eslint/eslint-plugin": "^5.45.0",
     "@typescript-eslint/parser": "^5.45.0",
     "apply-loader": "2.0.0",
     "axios": "^0.27.2",
+    "babel-loader": "^9.1.2",
     "browserify-sign": "^4.2.1",
     "clone-deep": "^4.0.1",
     "compare-versions": "^5",
     "core-js": "^3.21.1",
     "core-js-pure": "^3.21.1",
     "cross-env": "7.0.3",
-    "css-loader": "^6.7.1",
+    "css-loader": "^6.7.3",
     "deep-equal": "2.0.5",
-    "electron": "21.3.1",
+    "electron": "22.3.1",
     "electron-builder": "^24.0.0-alpha.1",
     "electron-download": "^4.1.1",
     "electron-installer-snap": "^5.1.0",
@@ -51,10 +53,8 @@
     "json-loader": "^0.5.7",
     "lru-cache": "^6.0.0",
     "macos-release": "^3.1.0",
-    "ngx-sortablejs": "^11.1.0",
-    "ngx-toastr": "^14.0.0",
-    "node-abi": "^3.25.0",
-    "node-sass": "^7.0.3",
+    "ngx-toastr": "^16.0.2",
+    "node-abi": "^3.33.0",
     "npmlog": "6.0.2",
     "npx": "^10.2.2",
     "patch-package": "^6.4.7",
@@ -66,49 +66,52 @@
     "pug-loader": "^2.4.0",
     "pug-static-loader": "2.0.0",
     "raw-loader": "4.0.2",
-    "sass-loader": "^12.6.0",
+    "rxjs": "^7.5.7",
+    "sass": "^1.58.0",
+    "sass-loader": "^13.2.0",
     "shell-quote": "^1.7.4",
     "shelljs": "0.8.5",
     "slugify": "^1.6.5",
-    "sortablejs": "^1.15.0",
     "source-code-pro": "^2.38.0",
-    "source-map-loader": "^3.0.1",
+    "source-map-loader": "^4.0.1",
     "source-sans-pro": "3.6.0",
     "ssh2": "Eugeny/ssh2#9de907d62907d6d45debdcc0ed8dda5b7b19dc7c",
     "style-loader": "^3.3.1",
     "svg-inline-loader": "^0.8.2",
     "thenby": "^1.3.4",
     "ts-loader": "^9.4.2",
-    "tslib": "^2.4.0",
+    "tsimportlib": "^0.0.3",
+    "tslib": "^2.5.0",
     "typedoc": "^0.22.18",
-    "typescript": "^4.3.5",
+    "typescript": "^4.9.5",
     "utils-decorators": "^1.10.4",
-    "val-loader": "4.0.0",
+    "val-loader": "5.0.1",
     "webpack": "^5.75.0",
     "webpack-bundle-analyzer": "^4.7.0",
-    "webpack-cli": "^5.0.0",
-    "yaml-loader": "0.6.0",
+    "webpack-cli": "^5.0.1",
+    "yaml-loader": "0.8.0",
     "zone.js": "^0.11.5"
   },
   "resolutions": {
     "*/pug": "^3",
     "lzma-native": "^8.0.0",
-    "*/node-abi": "^3.25.0",
+    "*/node-abi": "^3.33.0",
     "**/graceful-fs": "^4.2.4"
   },
   "scripts": {
-    "build": "npm run build:typings && node scripts/build-modules.js",
-    "build:typings": "node scripts/build-typings.js",
+    "build": "npm run build:typings && node scripts/build-modules.mjs",
+    "build:typings": "node scripts/build-typings.mjs",
     "watch": "cross-env TABBY_DEV=1 webpack --progress --color --watch",
     "start": "cross-env TABBY_DEV=1 electron app -d --inspect",
     "start:prod": "electron app --debug",
     "prod": "cross-env TABBY_DEV=1 electron app",
-    "docs": "node scripts/build-docs.js",
+    "docs": "node scripts/build-docs.mjs",
     "lint": "eslint --ext ts */src */lib",
-    "postinstall": "patch-package && node ./scripts/install-deps.js && node ./scripts/build-native.js",
+    "postinstall": "patch-package && node ./scripts/install-deps.mjs && node ./scripts/build-native.mjs",
     "i18n:pull": "crowdin pull --skip-untranslated-strings",
-    "i18n:extract": "node scripts/i18n-extract.js",
+    "i18n:extract": "node scripts/i18n-extract.mjs",
     "i18n:push": "crowdin push"
   },
+  "type": "module",
   "private": true
 }

+ 3 - 3
scripts/build-docs.js → scripts/build-docs.mjs

@@ -1,7 +1,7 @@
 #!/usr/bin/env node
-const sh = require('shelljs')
-const vars = require('./vars')
-const log = require('npmlog')
+import sh from 'shelljs'
+import * as vars from './vars.mjs'
+import log from 'npmlog'
 
 vars.packagesWithDocs.forEach(([dest, src]) => {
     log.info('docs', src)

+ 2 - 2
scripts/build-linux.js → scripts/build-linux.mjs

@@ -1,7 +1,7 @@
 #!/usr/bin/env node
 /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
-const builder = require('electron-builder').build
-const vars = require('./vars')
+import { build as builder } from 'electron-builder'
+import * as vars from './vars.mjs'
 
 const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/')
 

+ 2 - 2
scripts/build-macos.js → scripts/build-macos.mjs

@@ -1,7 +1,7 @@
 #!/usr/bin/env node
 /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
-const builder = require('electron-builder').build
-const vars = require('./vars')
+import { build as builder } from 'electron-builder'
+import * as vars from './vars.mjs'
 
 const isTag = (process.env.GITHUB_REF || '').startsWith('refs/tags/')
 

+ 0 - 22
scripts/build-modules.js

@@ -1,22 +0,0 @@
-#!/usr/bin/env node
-const vars = require('./vars')
-const log = require('npmlog')
-const webpack = require('webpack')
-const { promisify } = require('util')
-
-const configs = [
-    '../app/webpack.main.config.js',
-    '../app/webpack.config.js',
-    ...vars.allPackages.map(x => `../${x}/webpack.config.js`),
-]
-
-;(async () => {
-    for (const c of configs) {
-        log.info('build', c)
-        const stats = await promisify(webpack)(require(c))
-        console.log(stats.toString({ colors: true }))
-        if (stats.hasErrors()) {
-            process.exit(1)
-        }
-    }
-})()

+ 22 - 0
scripts/build-modules.mjs

@@ -0,0 +1,22 @@
+#!/usr/bin/env node
+import * as vars from './vars.mjs'
+import log from 'npmlog'
+import webpack from 'webpack'
+import { promisify } from 'node:util'
+
+const configs = [
+    '../app/webpack.config.main.mjs',
+    '../app/webpack.config.mjs',
+    ...vars.allPackages.map(x => `../${x}/webpack.config.mjs`),
+]
+
+;(async () => {
+    for (const c of configs) {
+        log.info('build', c)
+        const stats = await promisify(webpack)((await import(c)).default())
+        console.log(stats.toString({ colors: true }))
+        if (stats.hasErrors()) {
+            process.exit(1)
+        }
+    }
+})()

+ 7 - 3
scripts/build-native.js → scripts/build-native.mjs

@@ -1,7 +1,11 @@
 #!/usr/bin/env node
-const rebuild = require('electron-rebuild').default
-const path = require('path')
-const vars = require('./vars')
+import { rebuild } from 'electron-rebuild'
+import * as path from 'path'
+import * as vars from './vars.mjs'
+
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
+
 
 if (process.platform === 'win32' || process.platform === 'linux') {
     process.env.ARCH = ((process.env.ARCH || process.arch) === 'arm') ? 'armv7l' : process.env.ARCH || process.arch

+ 3 - 3
scripts/build-typings.js → scripts/build-typings.mjs

@@ -1,7 +1,7 @@
 #!/usr/bin/env node
-const sh = require('shelljs')
-const vars = require('./vars')
-const log = require('npmlog')
+import sh from 'shelljs'
+import * as vars from './vars.mjs'
+import log from 'npmlog'
 
 vars.builtinPlugins.forEach(plugin => {
     log.info('typings', plugin)

+ 2 - 2
scripts/build-windows.js → scripts/build-windows.mjs

@@ -1,7 +1,7 @@
 #!/usr/bin/env node
 /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
-const builder = require('electron-builder').build
-const vars = require('./vars')
+import { build as builder } from 'electron-builder'
+import * as vars from './vars.mjs'
 
 const isTag = (process.env.GITHUB_REF || process.env.BUILD_SOURCEBRANCH || '').startsWith('refs/tags/')
 

+ 8 - 3
scripts/generate-icon-metadata.js → scripts/generate-icon-metadata.mjs

@@ -1,7 +1,12 @@
 #!/usr/bin/env node
-const jsYaml = require('js-yaml')
-const fs = require('fs')
-const path = require('path')
+import jsYaml from 'js-yaml'
+import fs from 'node:fs'
+import path from 'node:path'
+
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
+
+
 const metadata = jsYaml.load(fs.readFileSync(path.resolve(__dirname, '../node_modules/@fortawesome/fontawesome-free/metadata/icons.yml')))
 
 let result = {}

+ 5 - 5
scripts/i18n-extract.js → scripts/i18n-extract.mjs

@@ -1,9 +1,9 @@
 #!/usr/bin/env node
-const sh = require('shelljs')
-const fs = require('fs/promises')
-const vars = require('./vars')
-const log = require('npmlog')
-const { GettextExtractor, JsExtractors, HtmlExtractors } = require('gettext-extractor')
+import sh from 'shelljs'
+import fs from 'node:fs/promises'
+import * as vars from './vars.mjs'
+import log from 'npmlog'
+import { GettextExtractor, JsExtractors, HtmlExtractors } from 'gettext-extractor'
 
 let extractor = new GettextExtractor()
 

+ 3 - 3
scripts/install-deps.js → scripts/install-deps.mjs

@@ -1,7 +1,7 @@
 #!/usr/bin/env node
-const sh = require('shelljs')
-const vars = require('./vars')
-const log = require('npmlog')
+import sh from 'shelljs'
+import * as vars from './vars.mjs'
+import log from 'npmlog'
 
 log.info('patch')
 sh.exec(`yarn patch-package`, { fatal: true })

+ 10 - 6
scripts/prepackage-plugins.js → scripts/prepackage-plugins.mjs

@@ -1,10 +1,14 @@
 #!/usr/bin/env node
-const rebuild = require('electron-rebuild').default
-const sh = require('shelljs')
-const path = require('path')
-const fs = require('fs')
-const vars = require('./vars')
-const log = require('npmlog')
+import { rebuild } from 'electron-rebuild'
+import sh from 'shelljs'
+import path from 'node:path'
+import fs from 'node:fs'
+import * as vars from './vars.mjs'
+import log from 'npmlog'
+
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
+
 
 let target = path.resolve(__dirname, '../builtin-plugins')
 sh.mkdir('-p', target)

+ 4 - 4
scripts/publish-plugins.js → scripts/publish-plugins.mjs

@@ -1,8 +1,8 @@
 #!/usr/bin/env node
-const sh = require('shelljs')
-const vars = require('./vars')
-const log = require('npmlog')
-const { execSync } = require('child_process')
+import sh from 'shelljs'
+import * as vars from './vars.mjs'
+import log from 'npmlog'
+import { execSync } from 'child_process'
 
 vars.allPackages.forEach(plugin => {
     log.info('bump', plugin)

+ 2 - 2
scripts/sentry-upload.js → scripts/sentry-upload.mjs

@@ -1,6 +1,6 @@
 #!/usr/bin/env node
-const sh = require('shelljs')
-const vars = require('./vars')
+import sh from 'shelljs'
+import * as vars from './vars.mjs'
 
 const sentryCli = process.platform === 'win32' ? 'node_modules\\.bin\\sentry-cli.cmd' : 'sentry-cli'
 

+ 20 - 17
scripts/vars.js → scripts/vars.mjs

@@ -1,19 +1,22 @@
-const path = require('path')
-const fs = require('fs')
-const semver = require('semver')
-const childProcess = require('child_process')
+import * as path from 'path'
+import * as fs from 'fs'
+import * as semver from 'semver'
+import * as childProcess from 'child_process'
+
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
 
 const electronInfo = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../node_modules/electron/package.json')))
 
-exports.version = childProcess.execSync('git describe --tags', { encoding:'utf-8' })
-exports.version = exports.version.substring(1).trim()
-exports.version = exports.version.replace('-', '-c')
+export let version = childProcess.execSync('git describe --tags', { encoding:'utf-8' })
+version = version.substring(1).trim()
+version = version.replace('-', '-c')
 
-if (exports.version.includes('-c')) {
-    exports.version = semver.inc(exports.version, 'prepatch').replace('-0', `-nightly.${process.env.REV ?? 0}`)
+if (version.includes('-c')) {
+    version = semver.inc(version, 'prepatch').replace('-0', `-nightly.${process.env.REV ?? 0}`)
 }
 
-exports.builtinPlugins = [
+export const builtinPlugins = [
     'tabby-core',
     'tabby-settings',
     'tabby-terminal',
@@ -28,26 +31,26 @@ exports.builtinPlugins = [
     'tabby-linkifier',
 ]
 
-exports.packagesWithDocs = [
+export const packagesWithDocs = [
     ['.', 'tabby-core'],
     ['terminal', 'tabby-terminal'],
     ['local', 'tabby-local'],
     ['settings', 'tabby-settings'],
 ]
 
-exports.allPackages = [
-    ...exports.builtinPlugins,
+export const allPackages = [
+    ...builtinPlugins,
     'web',
     'tabby-web-demo',
 ]
 
-exports.bundledModules = [
+export const bundledModules = [
     '@angular',
     '@ng-bootstrap',
 ]
-exports.electronVersion = electronInfo.version
+export const electronVersion = electronInfo.version
 
-exports.keygenConfig = {
+export const keygenConfig = {
     provider: 'keygen',
     account: 'a06315f2-1031-47c6-9181-e92a20ec815e',
     channel: 'stable',
@@ -70,6 +73,6 @@ exports.keygenConfig = {
     }[process.platform],
 }
 
-if (!exports.keygenConfig.product) {
+if (!keygenConfig.product) {
     throw new Error(`Unrecognized platform ${process.platform}/${process.env.ARCH ?? process.arch}`)
 }

+ 0 - 6
tabby-community-color-schemes/webpack.config.js

@@ -1,6 +0,0 @@
-const config = require('../webpack.plugin.config')
-module.exports = config({
-    name: 'community-color-schemes',
-    dirname: __dirname,
-})
-module.exports.module.rules.push({ test: /[\\\/]schemes[\\\/]/, use: 'raw-loader' })

+ 14 - 0
tabby-community-color-schemes/webpack.config.mjs

@@ -0,0 +1,14 @@
+import * as path from 'path'
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
+
+import config from '../webpack.plugin.config.mjs'
+
+export default () => {
+    const cfg = config({
+        name: 'community-color-schemes',
+        dirname: __dirname,
+    })
+    cfg.module.rules.push({ test: /[\\\/]schemes[\\\/]/, use: 'raw-loader' })
+    return cfg
+}

+ 3 - 5
tabby-core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "tabby-core",
-  "version": "1.0.189-nightly.2",
+  "version": "1.0.189-nightly.0",
   "description": "Tabby core",
   "keywords": [
     "tabby-builtin-plugin"
@@ -17,15 +17,13 @@
   "author": "Eugene Pankov",
   "license": "MIT",
   "devDependencies": {
-    "@ngx-translate/core": "^14.0.0",
-    "bootstrap": "^4.1.3",
+    "bootstrap": "^5.3.0-alpha.1",
+    "color": "^4.2.3",
     "deepmerge": "^4.1.1",
     "fuzzy-search": "^3.2.1",
     "js-yaml": "^4.0.0",
     "messageformat": "^2.3.0",
     "mixpanel": "^0.17.0",
-    "ngx-filesize": "^2.0.16",
-    "ngx-perfect-scrollbar": "^10.1.0",
     "ngx-translate-messageformat-compiler": "^4.11.0",
     "readable-stream": "4.2.0",
     "uuid": "^9.0.0"

+ 2 - 1
tabby-core/src/api/index.ts

@@ -36,6 +36,7 @@ export { TabsService, NewTabParameters, TabComponentType } from '../services/tab
 export { UpdaterService } from '../services/updater.service'
 export { VaultService, Vault, VaultSecret, VaultFileSecret, VAULT_SECRET_TYPE_FILE, StoredVault, VaultSecretKey } from '../services/vault.service'
 export { FileProvidersService } from '../services/fileProviders.service'
-export { LocaleService, TranslateServiceWrapper as TranslateService } from '../services/locale.service'
+export { LocaleService } from '../services/locale.service'
+export { TranslateService } from '@ngx-translate/core'
 export * from '../utils'
 export { UTF8Splitter } from '../utfSplitter'

+ 1 - 0
tabby-core/src/api/theme.ts

@@ -13,4 +13,5 @@ export abstract class Theme {
 
     macOSWindowButtonsInsetX?: number
     macOSWindowButtonsInsetY?: number
+    followsColorScheme?: boolean
 }

+ 3 - 2
tabby-core/src/components/appRoot.component.ts

@@ -55,8 +55,8 @@ function makeTabAnimation (dimension: string, size: number) {
 /** @hidden */
 @Component({
     selector: 'app-root',
-    template: require('./appRoot.component.pug'),
-    styles: [require('./appRoot.component.scss')],
+    templateUrl: './appRoot.component.pug',
+    styleUrls: ['./appRoot.component.scss'],
     animations: [
         trigger('animateTab', makeTabAnimation('width', 200)),
     ],
@@ -90,6 +90,7 @@ export class AppRootComponent {
         ngbModal: NgbModal,
         _themes: ThemesService,
     ) {
+        // document.querySelector('app-root')?.remove()
         this.logger = log.create('main')
         this.logger.info('v', platform.getAppVersion())
 

+ 1 - 0
tabby-core/src/components/baseTab.component.ts

@@ -19,6 +19,7 @@ export interface GetRecoveryTokenOptions {
 /**
  * Abstract base class for custom tab components
  */
+// @Component({ template: '' })
 export abstract class BaseTabComponent extends BaseComponent {
     /**
      * Parent tab (usually a SplitTabComponent)

+ 3 - 3
tabby-core/src/components/checkbox.component.ts

@@ -6,9 +6,9 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
 @Component({
     selector: 'checkbox',
     template: `
-        <div class="custom-control custom-checkbox">
-            <input type="checkbox" class="custom-control-input" [(ngModel)]='model'>
-            <label class="custom-control-label">{{text}}</label>
+        <div class="form-check form-checkbox">
+            <input type="checkbox" class="form-check-input" [(ngModel)]='model'>
+            <label class="form-check-label">{{text}}</label>
         </div>
     `,
     providers: [

+ 1 - 1
tabby-core/src/components/profileIcon.component.pug

@@ -5,5 +5,5 @@ i.icon(
 )
 .icon(
     [fastHtmlBind]='icon',
-    *ngIf='isHTML'
+    *ngIf='isHTML && icon'
 )

+ 2 - 2
tabby-core/src/components/profileIcon.component.ts

@@ -5,8 +5,8 @@ import { BaseComponent } from './base.component'
 /** @hidden */
 @Component({
     selector: 'profile-icon',
-    template: require('./profileIcon.component.pug'),
-    styles: [require('./profileIcon.component.scss')],
+    templateUrl:'./profileIcon.component.pug',
+    styleUrls: ['./profileIcon.component.scss'],
 })
 export class ProfileIconComponent extends BaseComponent {
     @Input() icon?: string

+ 1 - 1
tabby-core/src/components/promptModal.component.pug

@@ -14,6 +14,6 @@
             [(ngModel)]='remember',
             text='Remember'
         )
-        button.btn.btn-primary.ml-auto(
+        button.btn.btn-primary.ms-auto(
             (click)='ok()',
         ) OK

+ 2 - 1
tabby-core/src/components/promptModal.component.ts

@@ -3,10 +3,11 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 
 /** @hidden */
 @Component({
-    template: require('./promptModal.component.pug'),
+    templateUrl:'./promptModal.component.pug',
 })
 export class PromptModalComponent {
     @Input() value: string
+    @Input() prompt: string|undefined
     @Input() password: boolean
     @Input() remember: boolean
     @Input() showRememberCheckbox: boolean

+ 1 - 1
tabby-core/src/components/renameTabModal.component.ts

@@ -5,7 +5,7 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 /** @hidden */
 @Component({
     selector: 'rename-tab-modal',
-    template: require('./renameTabModal.component.pug'),
+    templateUrl:'./renameTabModal.component.pug',
 })
 export class RenameTabModalComponent {
     @Input() value: string

+ 1 - 1
tabby-core/src/components/safeModeModal.component.ts

@@ -3,7 +3,7 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 
 /** @hidden */
 @Component({
-    template: require('./safeModeModal.component.pug'),
+    templateUrl:'./safeModeModal.component.pug',
 })
 export class SafeModeModalComponent {
     @Input() error: Error

+ 6 - 6
tabby-core/src/components/selectorModal.component.pug

@@ -21,14 +21,14 @@
                     [icon]='option.icon',
                     [color]='option.color'
                 )
-                .title.mr-2 {{getOptionText(option)}}
+                .title.me-2 {{getOptionText(option)}}
                 .description.no-wrap.text-muted(
                     *ngIf='option.description !== getOptionText(option)'
                 ) {{option.description}}
-                .ml-auto
-                .no-wrap.badge.badge-secondary.text-muted.ml-2(*ngIf='selectedIndex == i && canEditSelected()')
+                .ms-auto
+                .no-wrap.badge.text-bg-secondary.text-muted.ms-2(*ngIf='selectedIndex == i && canEditSelected()')
                     span Backspace
-                    i.fas.fa-pencil.ml-1
-                .no-wrap.badge.badge-secondary.text-muted.ml-2(*ngIf='selectedIndex == i')
+                    i.fas.fa-pencil.ms-1
+                .no-wrap.badge.text-bg-secondary.text-muted.ms-2(*ngIf='selectedIndex == i')
                     span Enter
-                    i.fas.fa-arrow-right.ml-1
+                    i.fas.fa-arrow-right.ms-1

+ 2 - 2
tabby-core/src/components/selectorModal.component.ts

@@ -7,8 +7,8 @@ import { SelectorOption } from '../api/selector'
 /** @hidden */
 @Component({
     selector: 'selector-modal',
-    template: require('./selectorModal.component.pug'),
-    styles: [require('./selectorModal.component.scss')],
+    templateUrl:'./selectorModal.component.pug',
+    styleUrls: ['./selectorModal.component.scss'],
 })
 export class SelectorModalComponent<T> {
     @Input() options: SelectorOption<T>[]

+ 6 - 5
tabby-core/src/components/selfPositioning.component.ts

@@ -1,11 +1,12 @@
-import { HostBinding, ElementRef } from '@angular/core'
+import { HostBinding, ElementRef, Component } from '@angular/core'
 import { BaseComponent } from './base.component'
 
+@Component({})
 export abstract class SelfPositioningComponent extends BaseComponent {
-    @HostBinding('style.left') cssLeft: string
-    @HostBinding('style.top') cssTop: string
-    @HostBinding('style.width') cssWidth: string | null
-    @HostBinding('style.height') cssHeight: string | null
+    @HostBinding('style.left') cssLeft = ''
+    @HostBinding('style.top') cssTop = ''
+    @HostBinding('style.width') cssWidth: string | null = null
+    @HostBinding('style.height') cssHeight: string | null = null
 
     constructor (protected element: ElementRef) { super() }
 

+ 1 - 1
tabby-core/src/components/splitTab.component.ts

@@ -186,7 +186,7 @@ export type SplitDropZoneInfo = {
         >
         </split-tab-pane-label>
     `,
-    styles: [require('./splitTab.component.scss')],
+    styleUrls: ['./splitTab.component.scss'],
 })
 export class SplitTabComponent extends BaseTabComponent implements AfterViewInit, OnDestroy {
     static DIRECTIONS: SplitDirection[] = ['t', 'r', 'b', 'l']

+ 1 - 1
tabby-core/src/components/splitTabDropZone.component.ts

@@ -18,7 +18,7 @@ import { SplitDropZoneInfo, SplitTabComponent } from './splitTab.component'
     >
     </div>
     `,
-    styles: [require('./splitTabDropZone.component.scss')],
+    styleUrls: ['./splitTabDropZone.component.scss'],
 })
 export class SplitTabDropZoneComponent extends SelfPositioningComponent {
     @Input() dropZone: SplitDropZoneInfo

+ 2 - 2
tabby-core/src/components/splitTabPaneLabel.component.ts

@@ -15,11 +15,11 @@ import { SelfPositioningComponent } from './selfPositioning.component'
         (cdkDragStarted)='onTabDragStart(tab)'
         (cdkDragEnded)='onTabDragEnd()'
     >
-        <i class="fa fa-window-maximize mr-3"></i>
+        <i class="fa fa-window-maximize me-3"></i>
         <label>{{tab.title}}</label>
     </div>
     `,
-    styles: [require('./splitTabPaneLabel.component.scss')],
+    styleUrls: ['./splitTabPaneLabel.component.scss'],
 })
 export class SplitTabPaneLabelComponent extends SelfPositioningComponent {
     @Input() tab: BaseTabComponent

+ 0 - 5
tabby-core/src/components/splitTabSpanner.component.scss

@@ -3,7 +3,6 @@
     position: absolute;
     z-index: 5;
     transition: 0.125s background;
-    background: rgba(0, 0, 0, .2);
 
     &.v {
         cursor: ns-resize;
@@ -16,8 +15,4 @@
         width: 10px;
         margin-left: -5px;
     }
-
-    &:hover, &.active {
-        background: rgba(255, 255, 255, .125);
-    }
 }

+ 1 - 1
tabby-core/src/components/splitTabSpanner.component.ts

@@ -7,7 +7,7 @@ import { SplitContainer } from './splitTab.component'
 @Component({
     selector: 'split-tab-spanner',
     template: '',
-    styles: [require('./splitTabSpanner.component.scss')],
+    styleUrls: ['./splitTabSpanner.component.scss'],
 })
 export class SplitTabSpannerComponent extends SelfPositioningComponent {
     @Input() container: SplitContainer

+ 5 - 5
tabby-core/src/components/startPage.component.pug

@@ -1,10 +1,10 @@
-div
+.mt-5
     .tabby-logo
     h1.tabby-title Tabby
         sup α
 
     .list-group.mb-4
-        a.list-group-item.list-group-item-action.d-flex(
+        a.list-group-item.list-group-item-action.d-flex.pt-3.pb-3(
             *ngFor='let command of commands; trackBy: buttonsTrackBy',
             (click)='command.run()',
         )
@@ -12,11 +12,11 @@ div
             span {{command.label}}
 
 footer.d-flex.align-items-center
-    .btn-group.mr-auto
-        button.btn.btn-dark((click)='homeBase.openGitHub()')
+    .btn-group.me-auto
+        button.btn.btn-link((click)='homeBase.openGitHub()')
             i.fab.fa-github
             span GitHub
-        button.btn.btn-dark((click)='homeBase.reportBug()')
+        button.btn.btn-link((click)='homeBase.reportBug()')
             i.fas.fa-bug
             span(translate) Report a problem
 

+ 4 - 3
tabby-core/src/components/startPage.component.ts

@@ -7,8 +7,8 @@ import { Command, CommandLocation } from '../api/commands'
 /** @hidden */
 @Component({
     selector: 'start-page',
-    template: require('./startPage.component.pug'),
-    styles: [require('./startPage.component.scss')],
+    templateUrl:'./startPage.component.pug',
+    styleUrls: ['./startPage.component.scss'],
 })
 export class StartPageComponent {
     version: string
@@ -28,7 +28,8 @@ export class StartPageComponent {
         return this.domSanitizer.bypassSecurityTrustHtml(icon ?? '')
     }
 
-    buttonsTrackBy (btn: Command): any {
+    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+    buttonsTrackBy (_, btn: Command): any {
         return btn.label + btn.icon
     }
 }

+ 0 - 5
tabby-core/src/components/tabBody.component.scss

@@ -7,9 +7,4 @@
     >* {
         flex: auto;
     }
-
-    > perfect-scrollbar {
-        width: auto;
-        height: auto;
-    }
 }

+ 3 - 3
tabby-core/src/components/tabBody.component.ts

@@ -8,9 +8,9 @@ import { BaseTabComponent } from '../components/baseTab.component'
     template: `
         <ng-template #placeholder></ng-template>
     `,
-    styles: [
-        require('./tabBody.component.scss'),
-        require('./tabBody.deep.component.css'),
+    styleUrls: [
+        './tabBody.component.scss',
+        './tabBody.deep.component.css',
     ],
 })
 export class TabBodyComponent implements OnChanges {

+ 1 - 1
tabby-core/src/components/tabHeader.component.pug

@@ -8,7 +8,7 @@
 profile-icon(
     *ngIf='config.store.terminal.showTabProfileIcon && tab.icon',
     [icon]='tab.icon',
-    [color]='tab.color'
+    [color]='tab.color ?? undefined'
 )
 
 .name(

+ 0 - 1
tabby-core/src/components/tabHeader.component.scss

@@ -144,6 +144,5 @@ $tabs-height: 38px;
         right: 10px;
         bottom: 4px;
         height: 2px;
-        z-index: -1;
     }
 }

+ 2 - 2
tabby-core/src/components/tabHeader.component.ts

@@ -15,8 +15,8 @@ import { PlatformService } from '../api/platform'
 /** @hidden */
 @Component({
     selector: 'tab-header',
-    template: require('./tabHeader.component.pug'),
-    styles: [require('./tabHeader.component.scss')],
+    templateUrl:'./tabHeader.component.pug',
+    styleUrls: ['./tabHeader.component.scss'],
 })
 export class TabHeaderComponent extends BaseComponent {
     @Input() index: number

+ 1 - 1
tabby-core/src/components/titleBar.component.pug

@@ -1,2 +1,2 @@
-.title((dblclick)='hostApp.toggleMaximize()') Tabby
+.title((dblclick)='hostWindow.toggleMaximize()') Tabby
 window-controls

+ 6 - 3
tabby-core/src/components/titleBar.component.ts

@@ -1,9 +1,12 @@
 import { Component } from '@angular/core'
+import { HostWindowService } from '../api'
 
 /** @hidden */
 @Component({
     selector: 'title-bar',
-    template: require('./titleBar.component.pug'),
-    styles: [require('./titleBar.component.scss')],
+    templateUrl:'./titleBar.component.pug',
+    styleUrls: ['./titleBar.component.scss'],
 })
-export class TitleBarComponent { } // eslint-disable-line @typescript-eslint/no-extraneous-class
+export class TitleBarComponent {
+    constructor (public hostWindow: HostWindowService) { }
+}

+ 8 - 2
tabby-core/src/components/toggle.component.scss

@@ -4,17 +4,19 @@
     $height: 30px;
     $padding: 2px;
     display: inline-flex;
-    overflow: visible;
     border-radius: 3px;
     line-height: $height;
     height: $height;
     transition: 0.25s opacity;
     align-items: center;
-    overflow: hidden;
     padding-right: 10px;
     padding-left: 10px;
     margin-left: -10px;
 
+    .form-check {
+        margin: 0;
+    }
+
     &.disabled {
         opacity: 0.5;
     }
@@ -22,4 +24,8 @@
     * {
         cursor: pointer;
     }
+
+    label {
+        display: none;
+    }
 }

+ 4 - 4
tabby-core/src/components/toggle.component.ts

@@ -6,12 +6,12 @@ import { CheckboxComponent } from './checkbox.component'
 @Component({
     selector: 'toggle',
     template: `
-    <div class="custom-control custom-switch">
-      <input type="checkbox" class="custom-control-input" [(ngModel)]='model'>
-      <label class="custom-control-label"></label>
+    <div class="form-check form-switch">
+      <input type="checkbox" class="form-check-input" [(ngModel)]='model'>
+      <label class="cform-check-label"></label>
     </div>
     `,
-    styles: [require('./toggle.component.scss')],
+    styleUrls: ['./toggle.component.scss'],
     providers: [
         { provide: NG_VALUE_ACCESSOR, useExisting: ToggleComponent, multi: true },
     ],

+ 1 - 1
tabby-core/src/components/transfersMenu.component.pug

@@ -1,6 +1,6 @@
 .d-flex.align-items-center
     .dropdown-header(translate) File transfers
-    button.btn.btn-link.ml-auto((click)='removeAll(); $event.stopPropagation()') !{require('../icons/times.svg')}
+    button.btn.btn-link.ms-auto((click)='removeAll(); $event.stopPropagation()') !{require('../icons/times.svg')}
 .transfer(*ngFor='let transfer of transfers', (click)='showTransfer(transfer)')
     .icon(*ngIf='isDownload(transfer)') !{require('../icons/download.svg')}
     .icon(*ngIf='!isDownload(transfer)') !{require('../icons/upload.svg')}

+ 2 - 2
tabby-core/src/components/transfersMenu.component.ts

@@ -5,8 +5,8 @@ import { FileDownload, FileTransfer, PlatformService } from '../api/platform'
 /** @hidden */
 @Component({
     selector: 'transfers-menu',
-    template: require('./transfersMenu.component.pug'),
-    styles: [require('./transfersMenu.component.scss')],
+    templateUrl:'./transfersMenu.component.pug',
+    styleUrls: ['./transfersMenu.component.scss'],
 })
 export class TransfersMenuComponent {
     @Input() transfers: FileTransfer[]

+ 3 - 4
tabby-core/src/components/unlockVaultModal.component.pug

@@ -1,7 +1,7 @@
 .modal-body
     .d-flex.align-items-center.mb-3
         h3.m-0(translate) Vault is locked
-        .ml-auto(ngbDropdown, placement='bottom-right')
+        .ms-auto(ngbDropdown, placement='bottom-right')
             button.btn.btn-link(ngbDropdownToggle, (click)='$event.stopPropagation()')
                 span(
                     *ngIf='rememberFor',
@@ -29,6 +29,5 @@
             (keyup.enter)='ok()',
             (keyup.esc)='cancel()',
         )
-        .input-group-append
-            button.btn.btn-secondary((click)='ok()', *ngIf='passphrase')
-                i.fas.fa-check
+        button.btn.btn-secondary((click)='ok()', *ngIf='passphrase')
+            i.fas.fa-check

+ 1 - 1
tabby-core/src/components/unlockVaultModal.component.ts

@@ -3,7 +3,7 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 
 /** @hidden */
 @Component({
-    template: require('./unlockVaultModal.component.pug'),
+    templateUrl:'./unlockVaultModal.component.pug',
 })
 export class UnlockVaultModalComponent {
     passphrase: string

+ 2 - 2
tabby-core/src/components/welcomeTab.component.ts

@@ -8,8 +8,8 @@ import { LocaleService } from '../services/locale.service'
 /** @hidden */
 @Component({
     selector: 'welcome-page',
-    template: require('./welcomeTab.component.pug'),
-    styles: [require('./welcomeTab.component.scss')],
+    templateUrl:'./welcomeTab.component.pug',
+    styleUrls: ['./welcomeTab.component.scss'],
 })
 export class WelcomeTabComponent extends BaseTabComponent {
     enableGlobalHotkey = true

+ 2 - 2
tabby-core/src/components/windowControls.component.ts

@@ -6,8 +6,8 @@ import { AppService } from '../services/app.service'
 /** @hidden */
 @Component({
     selector: 'window-controls',
-    template: require('./windowControls.component.pug'),
-    styles: [require('./windowControls.component.scss')],
+    templateUrl:'./windowControls.component.pug',
+    styleUrls: ['./windowControls.component.scss'],
 })
 export class WindowControlsComponent {
     constructor (public hostWindow: HostWindowService, public app: AppService) { }

+ 5 - 5
tabby-core/src/config.ts

@@ -4,10 +4,10 @@ import { Platform } from './api/hostApp'
 /** @hidden */
 export class CoreConfigProvider extends ConfigProvider {
     platformDefaults = {
-        [Platform.macOS]: require('./configDefaults.macos.yaml'),
-        [Platform.Windows]: require('./configDefaults.windows.yaml'),
-        [Platform.Linux]: require('./configDefaults.linux.yaml'),
-        [Platform.Web]: require('./configDefaults.web.yaml'),
+        [Platform.macOS]: require('./configDefaults.macos.yaml').default,
+        [Platform.Windows]: require('./configDefaults.windows.yaml').default,
+        [Platform.Linux]: require('./configDefaults.linux.yaml').default,
+        [Platform.Web]: require('./configDefaults.web.yaml').default,
     }
-    defaults = require('./configDefaults.yaml')
+    defaults = require('./configDefaults.yaml').default
 }

+ 1 - 2
tabby-core/src/configDefaults.macos.yaml

@@ -36,8 +36,7 @@ hotkeys:
     - '⌘-8'
   tab-9:
     - '⌘-9'
-  tab-10:
-    - '⌘-0'
+  tab-10: []
   duplicate-tab: []
   restart-tab: []
   explode-tab:

File diff suppressed because it is too large
+ 0 - 0
tabby-core/src/icons.json


+ 13 - 34
tabby-core/src/index.ts

@@ -1,11 +1,9 @@
 import { NgModule, ModuleWithProviders, LOCALE_ID } from '@angular/core'
-import { BrowserModule } from '@angular/platform-browser'
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
+import { CommonModule } from '@angular/common'
 import { FormsModule } from '@angular/forms'
 import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
-import { PerfectScrollbarModule, PERFECT_SCROLLBAR_CONFIG } from 'ngx-perfect-scrollbar'
 import { NgxFilesizeModule } from 'ngx-filesize'
-import { SortablejsModule } from 'ngx-sortablejs'
 import { DragDropModule } from '@angular/cdk/drag-drop'
 import { TranslateModule, TranslateCompiler, TranslateService } from '@ngx-translate/core'
 import { TranslateMessageFormatCompiler, MESSAGE_FORMAT_CONFIG } from 'ngx-translate-messageformat-compiler'
@@ -43,10 +41,10 @@ import { AppService } from './services/app.service'
 import { ConfigService } from './services/config.service'
 import { VaultFileProvider } from './services/vault.service'
 import { HotkeysService } from './services/hotkeys.service'
-import { LocaleService, TranslateServiceWrapper } from './services/locale.service'
+import { LocaleService } from './services/locale.service'
 import { CommandService } from './services/commands.service'
 
-import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
+import { StandardTheme, StandardCompactTheme, PaperTheme, NewTheme } from './theme'
 import { CoreConfigProvider } from './config'
 import { AppHotkeyProvider } from './hotkeys'
 import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu, ProfilesContextMenu } from './tabContextMenu'
@@ -54,8 +52,6 @@ import { LastCLIHandler, ProfileCLIHandler } from './cli'
 import { SplitLayoutProfilesService } from './profiles'
 import { CoreCommandProvider } from './commands'
 
-import 'perfect-scrollbar/css/perfect-scrollbar.css'
-
 export function TranslateMessageFormatCompilerFactory (): TranslateMessageFormatCompiler {
     return new TranslateMessageFormatCompiler()
 }
@@ -65,6 +61,7 @@ const PROVIDERS = [
     { provide: Theme, useClass: StandardTheme, multi: true },
     { provide: Theme, useClass: StandardCompactTheme, multi: true },
     { provide: Theme, useClass: PaperTheme, multi: true },
+    { provide: Theme, useClass: NewTheme, multi: true },
     { provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
     { provide: TabContextMenuItemProvider, useClass: CommonOptionsContextMenu, multi: true },
     { provide: TabContextMenuItemProvider, useClass: TabManagementContextMenu, multi: true },
@@ -73,7 +70,6 @@ const PROVIDERS = [
     { provide: TabRecoveryProvider, useExisting: SplitTabRecoveryProvider, multi: true },
     { provide: CLIHandler, useClass: ProfileCLIHandler, multi: true },
     { provide: CLIHandler, useClass: LastCLIHandler, multi: true },
-    { provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
     { provide: FileProvider, useClass: VaultFileProvider, multi: true },
     { provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true },
     { provide: CommandProvider, useExisting: CoreCommandProvider, multi: true },
@@ -86,24 +82,24 @@ const PROVIDERS = [
         provide: MESSAGE_FORMAT_CONFIG,
         useValue: LocaleService.allLanguages.map(x => x.code),
     },
-    {
-        provide: TranslateService,
-        useClass: TranslateServiceWrapper,
-    },
 ]
 
 /** @hidden */
 @NgModule({
     imports: [
-        BrowserModule,
         BrowserAnimationsModule,
+        CommonModule,
         FormsModule,
         NgbModule,
         NgxFilesizeModule,
-        PerfectScrollbarModule,
         DragDropModule,
-        SortablejsModule.forRoot({ animation: 150 }),
-        TranslateModule,
+        TranslateModule.forRoot({
+            defaultLanguage: 'en',
+            compiler: {
+                provide: TranslateCompiler,
+                useFactory: TranslateMessageFormatCompilerFactory,
+            },
+        }),
     ],
     declarations: [
         AppRootComponent,
@@ -132,16 +128,8 @@ const PROVIDERS = [
         CdkAutoDropGroup,
         ProfileIconComponent,
     ],
-    entryComponents: [
-        PromptModalComponent,
-        RenameTabModalComponent,
-        SafeModeModalComponent,
-        SelectorModalComponent,
-        SplitTabComponent,
-        UnlockVaultModalComponent,
-        WelcomeTabComponent,
-    ],
     exports: [
+        AppRootComponent,
         CheckboxComponent,
         ToggleComponent,
         PromptModalComponent,
@@ -149,7 +137,6 @@ const PROVIDERS = [
         DropZoneDirective,
         FastHtmlBindDirective,
         AlwaysVisibleTypeaheadDirective,
-        SortablejsModule,
         DragDropModule,
         TranslateModule,
         CdkAutoDropGroup,
@@ -239,18 +226,10 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
     }
 
     static forRoot (): ModuleWithProviders<AppModule> {
-        const translateModule = TranslateModule.forRoot({
-            defaultLanguage: 'en',
-            compiler: {
-                provide: TranslateCompiler,
-                useFactory: TranslateMessageFormatCompilerFactory,
-            },
-        })
         return {
             ngModule: AppModule,
             providers: [
                 ...PROVIDERS,
-                ...translateModule.providers!.filter(x => x !== TranslateService),
             ],
         }
     }

+ 1 - 1
tabby-core/src/services/commands.service.ts

@@ -12,7 +12,7 @@ export class CommandService {
         private app: AppService,
         private translate: TranslateService,
         @Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
-        @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
+        @Optional() @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
         @Inject(CommandProvider) private commandProviders: CommandProvider[],
     ) {
         this.contextMenuProviders.sort((a, b) => a.weight - b.weight)

+ 5 - 3
tabby-core/src/services/config.service.ts

@@ -236,11 +236,13 @@ export class ConfigService {
      *
      * @typeparam T Base provider type
      */
-    enabledServices<T extends object> (services: T[]): T[] { // eslint-disable-line @typescript-eslint/ban-types
+    enabledServices<T extends object> (services: T[]|undefined): T[] { // eslint-disable-line @typescript-eslint/ban-types
+        if (!services) {
+            return []
+        }
         if (!this.servicesCache) {
             this.servicesCache = {}
-            const ngModule = window['rootModule'].ɵinj
-            for (const imp of ngModule.imports) {
+            for (const imp of window['pluginModules']) {
                 const module = imp.ngModule || imp
                 if (module.ɵinj?.providers) {
                     this.servicesCache[module.pluginName] = module.ɵinj.providers.map(provider => {

+ 19 - 18
tabby-core/src/services/locale.service.ts

@@ -55,24 +55,6 @@ function flattenMessageFormatTranslation (po: any) {
     return translation
 }
 
-@Injectable({ providedIn: 'root' })
-export class TranslateServiceWrapper extends TranslateService {
-    private _defaultTranslation: Record<string, string>|null
-
-    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-    getParsedResult (translations: any, key: any, interpolateParams?: any): any {
-        if (!this._defaultTranslation) {
-            const po = require(`../../../locale/en-US.po`)
-            this._defaultTranslation = flattenMessageFormatTranslation(po)
-        }
-        this.translations[this.defaultLang][key] ??= this.compiler.compile(
-            this._defaultTranslation[key] || key,
-            this.defaultLang,
-        )
-        return super.getParsedResult(translations, key, interpolateParams ?? {})
-    }
-}
-
 @Injectable({ providedIn: 'root' })
 export class LocaleService {
     private logger: Logger
@@ -176,6 +158,7 @@ export class LocaleService {
         private translate: TranslateService,
         log: LogService,
     ) {
+        this.patchTranslateService(translate)
         this.logger = log.create('translate')
         config.changed$.subscribe(() => {
             this.refresh()
@@ -191,6 +174,24 @@ export class LocaleService {
         }
     }
 
+    private patchTranslateService (translate: TranslateService) {
+        translate['_defaultTranslation'] = null
+        const oldGetParsedResult = translate.getParsedResult.bind(translate)
+
+        // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+        translate.getParsedResult = function (translations: any, key: any, interpolateParams?: any): any {
+            if (!this._defaultTranslation) {
+                const po = require(`../../../locale/en-US.po`)
+                this._defaultTranslation = flattenMessageFormatTranslation(po)
+            }
+            this.translations[this.defaultLang][key] ??= this.compiler.compile(
+                this._defaultTranslation[key] || key,
+                this.defaultLang,
+            )
+            return oldGetParsedResult(translations, key, interpolateParams ?? {})
+        }.bind(translate)
+    }
+
     refresh (): void {
         let lang = this.config.store.language
         if (!lang) {

+ 105 - 2
tabby-core/src/services/themes.service.ts

@@ -1,7 +1,9 @@
 import { Inject, Injectable } from '@angular/core'
 import { Subject, Observable } from 'rxjs'
+import * as Color from 'color'
 import { ConfigService } from '../services/config.service'
 import { Theme } from '../api/theme'
+import { NewTheme } from '../theme'
 
 @Injectable({ providedIn: 'root' })
 export class ThemesService {
@@ -9,19 +11,120 @@ export class ThemesService {
     private themeChanged = new Subject<Theme>()
 
     private styleElement: HTMLElement|null = null
+    private rootElementStyleBackup = ''
 
     /** @hidden */
     private constructor (
         private config: ConfigService,
+        private standardTheme: NewTheme,
         @Inject(Theme) private themes: Theme[],
     ) {
-        this.applyTheme(this.findTheme('Standard')!)
+        this.rootElementStyleBackup = document.documentElement.style.cssText
+        this.applyTheme(standardTheme)
         config.ready$.toPromise().then(() => {
             this.applyCurrentTheme()
+            this.applyThemeVariables()
             config.changed$.subscribe(() => {
                 this.applyCurrentTheme()
             })
         })
+        config.changed$.subscribe(() => this.applyThemeVariables())
+    }
+
+    private applyThemeVariables () {
+        if (!this.findCurrentTheme().followsColorScheme) {
+            document.documentElement.style.cssText = this.rootElementStyleBackup
+            return
+        }
+
+        const theme = this.config.store.terminal.colorScheme
+        const isDark = Color(theme.background).luminosity() < Color(theme.foreground).luminosity()
+
+        function more (some, factor) {
+            if (isDark) {
+                return Color(some).darken(factor)
+            }
+            return Color(some).lighten(factor)
+        }
+
+        function less (some, factor) {
+            if (!isDark) {
+                return Color(some).darken(factor)
+            }
+            return Color(some).lighten(factor)
+        }
+
+        let background = Color(theme.background)
+        if (this.config.store?.appearance.vibrancy) {
+            background = background.fade(0.6)
+        }
+        // const background = theme.background
+        const backgroundMore = more(background.string(), 0.25).string()
+        // const backgroundMore =more(theme.background, 0.25).string()
+        const accentIndex = 4
+        const vars: Record<string, string> = {}
+
+        vars['--bs-body-bg'] = background.string()
+        vars['--bs-body-color'] = theme.foreground
+        vars['--bs-black'] = theme.colors[0]
+        vars['--bs-red'] = theme.colors[1]
+        vars['--bs-green'] = theme.colors[2]
+        vars['--bs-yellow'] = theme.colors[3]
+        vars['--bs-blue'] = theme.colors[4]
+        vars['--bs-purple'] = theme.colors[5]
+        vars['--bs-cyan'] = theme.colors[6]
+        vars['--bs-gray'] = theme.colors[7]
+        vars['--bs-gray-dark'] = theme.colors[8]
+        // vars['--bs-red'] = theme.colors[9]
+        // vars['--bs-green'] = theme.colors[10]
+        // vars['--bs-yellow'] = theme.colors[11]
+        // vars['--bs-blue'] = theme.colors[12]
+        // vars['--bs-purple'] = theme.colors[13]
+        // vars['--bs-cyan'] = theme.colors[14]
+
+        vars['--theme-fg-more-2'] = more(theme.foreground, 0.5).string()
+        vars['--theme-fg-more'] = more(theme.foreground, 0.25).string()
+        vars['--theme-fg'] = theme.foreground
+        vars['--theme-fg-less'] = less(theme.foreground, 0.25).string()
+        vars['--theme-fg-less-2'] = less(theme.foreground, 0.5).string()
+
+        vars['--theme-bg-less-2'] = less(theme.background, 0.5).string()
+        vars['--theme-bg-less'] = less(theme.background, 0.25).string()
+        vars['--theme-bg'] = theme.background
+        vars['--theme-bg-more'] = backgroundMore
+        vars['--theme-bg-more-2'] = more(backgroundMore, 0.25).string()
+
+        const themeColors = {
+            primary: theme.colors[accentIndex],
+            secondary: theme.colors[8],
+            tertiary: theme.colors[8],
+            warning: theme.colors[3],
+            danger: theme.colors[1],
+            success: theme.colors[2],
+            info: theme.colors[4],
+            dark: more(theme.background, 0.5).string(),
+            light: more(theme.foreground, 0.5).string(),
+            link: theme.colors[8], // for .btn-link
+        }
+
+        for (const [key, color] of Object.entries(themeColors)) {
+            vars[`--bs-${key}-bg`] = more(color, 0.5).string()
+            vars[`--bs-${key}-color`] = less(color, 0.5).string()
+            vars[`--bs-${key}`] = color
+            vars[`--bs-${key}-rgb`] = Color(color).rgb().array().join(', ')
+            vars[`--theme-${key}-more-2`] = more(color, 1).string()
+            vars[`--theme-${key}-more`] = more(color, 0.5).string()
+            vars[`--theme-${key}`] = color
+            vars[`--theme-${key}-less`] = less(color, 0.25).string()
+            vars[`--theme-${key}-less-2`] = less(color, 0.75).string()
+        }
+
+        const switchBackground = less(theme.colors[accentIndex], 0.25).string()
+        vars['--bs-form-switch-bg'] = `url("data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%27-4 -4 8 8%27%3e%3ccircle r=%273%27 fill=%27${switchBackground}%27/%3e%3c/svg%3e")`
+
+        for (const [key, value] of Object.entries(vars)) {
+            document.documentElement.style.setProperty(key, value)
+        }
     }
 
     findTheme (name: string): Theme|null {
@@ -29,7 +132,7 @@ export class ThemesService {
     }
 
     findCurrentTheme (): Theme {
-        return this.findTheme(this.config.store.appearance.theme) ?? this.findTheme('Standard')!
+        return this.findTheme(this.config.store.appearance.theme) ?? this.standardTheme
     }
 
     applyTheme (theme: Theme): void {

+ 568 - 0
tabby-core/src/theme.new.scss

@@ -0,0 +1,568 @@
+$font-family-sans-serif: "Source Sans Pro";
+$font-size-base: 14rem / 16;
+
+app-root {
+    background: transparent;
+
+    &.vibrant {
+        background: rgba(var(--bs-dark-rgb),.65);
+    }
+
+    &> .content {
+        .tab-bar {
+            background: var(--theme-bg-more);
+
+            .btn-tab-bar {
+                background: transparent;
+                line-height: 42px;
+                align-items: center;
+                svg, path {
+                    fill: var(--bs-body-color);
+                    fill-opacity: 0.75;
+                }
+
+                &:hover { background: rgba(0, 0, 0, .125) !important; }
+                &:active { background: rgba(0, 0, 0, .25) !important; }
+            }
+
+            &>.tabs {
+                tab-header {
+                    border-left: 1px solid transparent;
+                    border-right: 1px solid transparent;
+                    transition: 0.125s ease-out width;
+
+                    .index {
+                        color: var(--bs-body-color);
+                        opacity: 0.4;
+                    }
+
+                    button {
+                        color: var(--bs-body-color);
+                        border: none;
+                        transition: 0.25s all;
+
+                        &:hover { background: var(--theme-bg-more-2) !important; }
+                        &:active { background: var(--theme-bg-more-2) !important; }
+                    }
+
+                    .progressbar {
+                        background: var(--bs-blue);
+                    }
+
+                    .activity-indicator {
+                        background:var(--bs-body-color);
+                        opacity: .2;
+                    }
+
+                    &.active {
+                        // color: $black;
+                        background: var(--bs-body-bg);
+                        // border-left: 1px solid $border-color;
+                        // border-right: 1px solid $border-color;
+                    }
+                }
+            }
+        }
+
+//         &.tabs-on-top .tab-bar {
+//             &>.background {
+//                 border-bottom: 1px solid $border-color;
+//             }
+
+//             tab-header {
+//                 border-bottom: 1px solid $border-color;
+
+//                 &.active {
+//                     border-bottom-color: transparent;
+//                 }
+//             }
+//         }
+
+//         &:not(.tabs-on-top) .tab-bar {
+//             &>.background {
+//                 border-top: 1px solid $border-color;
+//             }
+
+//             tab-header {
+//                 border-top: 1px solid $border-color;
+
+//                 &.active {
+//                     margin-top: -1px;
+//                 }
+//             }
+//         }
+    }
+
+//     &.platform-win32, &.platform-linux {
+//         border: 1px solid #111;
+//         &>.content .tab-bar .tabs tab-header:first-child {
+//             border-left: none;
+//         }
+//     }
+}
+
+tab-body {
+    background: var(--bs-body-bg);
+}
+
+// $black:     #002b36;
+// $base02:    #073642;
+// $base01:    #586e75;
+// $base00:    #657b83;
+// $base0:     #839496;
+// $base1:     #93a1a1;
+// $base2:     #eee8d5;
+// $white:     #fdf6e3;
+// $yellow:    #b58900;
+// $orange:    #cb4b16;
+// $red:       #dc322f;
+// $pink:      #d33682;
+// $purple:    #6c71c4;
+// $blue:      #268bd2;
+// $teal:      #2aa198;
+// $green:     #859900;
+
+$tab-border-radius: 5px;
+// $button-hover-bg: rgba(0, 0, 0, .125);
+// $button-active-bg: rgba(0, 0, 0, .25);
+
+
+// $btn-border-radius: 0;
+
+// $input-bg:                       $base2;
+// $input-disabled-bg:              $base1;
+
+// $input-color:                    $body-color;
+// $input-color-placeholder:        $base1;
+// $input-border-color:             $base1;
+// //$input-box-shadow:               inset 0 1px 1px rgba($black,.075);
+// $input-border-radius:            0;
+// $custom-select-border-radius: 0;
+// $input-bg-focus:                 $input-bg;
+// //$input-border-focus:             lighten($brand-primary, 25%);
+// //$input-box-shadow-focus:         $input-box-shadow, rgba($input-border-focus, .6);
+// $input-color-focus:              $input-color;
+// $input-group-addon-bg:           $body-bg;
+// $input-group-addon-border-color: $input-border-color;
+
+// $modal-content-bg:               $content-bg-solid;
+// $modal-content-border-color:     $body-bg;
+$modal-header-border-color:      transparent;
+$modal-footer-border-color:      transparent;
+
+// $popover-bg:                     $body-bg;
+
+// $dropdown-bg:                    $body-bg;
+// $dropdown-link-color:            $body-color;
+// $dropdown-link-hover-color:      #333;
+// $dropdown-link-hover-bg:         $body-bg2;
+// //$dropdown-link-active-color:     $component-active-color;
+// //$dropdown-link-active-bg:        $component-active-bg;
+// $dropdown-link-disabled-color:   #333;
+// $dropdown-header-color:          #333;
+
+// $list-group-action-bg: rgba($black,.05);
+// $list-group-action-active-bg: $list-group-link-active-bg;
+
+// $pre-bg: $dropdown-bg;
+// $pre-color: $dropdown-link-color;
+
+// $headings-font-weight: lighter;
+// $headings-color: $base0;
+
+$form-check-input-width: 1.4em;
+$form-switch-width: 2.5em;
+
+@import '~bootstrap/scss/bootstrap.scss';
+@import "./theme.vendor.scss";
+
+body {
+    --bs-border-color: var(--theme-bg-more-2);
+    --bs-form-control-bg: var(--theme-bg-more);
+    --bs-emphasis-color: var(--theme-fg-less-2);
+}
+
+.list-group {
+    --bs-list-group-bg: var(--theme-bg-more);
+    --bs-list-group-border-color: var(--theme-bg-more-2);
+    --bs-list-group-border-width: 0;
+    // --bs-list-group-item-padding-x: 1rem;
+    // --bs-list-group-item-padding-y: 0.5rem;
+    --bs-list-group-action-color: var(--bs-body-color);
+    --bs-list-group-action-hover-color: var(--theme-fg);
+    --bs-list-group-action-hover-bg: var(--theme-bg-more-2);
+
+    --bs-list-group-action-active-color: var(--theme-fg);
+    --bs-list-group-action-active-bg: var(--theme-bg-more-2);
+    --bs-list-group-disabled-color: var(--bs-secondary-color);
+    --bs-list-group-disabled-bg: var(--bs-body-bg);
+    --bs-list-group-active-color: var(--bs-primary-color);
+    --bs-list-group-active-bg: var(--bs-primary-bg);
+    // --bs-list-group-active-border-color: #0d6efd;
+}
+
+.nav {
+    // scss-docs-start nav-css-vars
+    // --bs-nav-link-padding-x: #{$nav-link-padding-x};
+    // --bs-nav-link-padding-y: #{$nav-link-padding-y};
+    // @include rfs($nav-link-font-size, --bs-nav-link-font-size);
+    // --bs-nav-link-font-weight: #{$nav-link-font-weight};
+    --bs-nav-link-color: var(--bs-body-color);
+    --bs-nav-link-hover-color: var(--theme-fg-less-2);
+    --bs-nav-link-disabled-color: var(--bs-gray);
+    // scss-docs-end nav-css-vars
+}
+
+.nav-tabs {
+    // scss-docs-start nav-tabs-css-vars
+    --bs-nav-tabs-border-width: 2px;
+    --bs-nav-tabs-border-radius: 0;
+    --bs-nav-tabs-link-hover-border-color: var(--bs-body-bg);
+    --bs-nav-tabs-border-color: var(--theme-fg-less-2);
+    --bs-nav-tabs-link-active-color: var(--theme-fg-less-2);
+
+    --bs-nav-tabs-link-active-bg: transparent;
+    --bs-nav-tabs-link-active-border-color: transparent;
+    // scss-docs-end nav-tabs-css-vars
+}
+
+.nav-pills {
+    // scss-docs-start nav-pills-css-vars
+    --bs-nav-pills-border-radius: #{$nav-pills-border-radius};
+    --bs-nav-pills-link-active-color: var(--theme-bg-more);
+    --bs-nav-pills-link-active-bg: var(--bs-primary);
+    // scss-docs-end nav-pills-css-vars
+}
+
+.nav-tabs {
+    margin-bottom: 10px;
+    border: none;
+
+    &.nav-justified .nav-link {
+        margin-right: 5px;
+    }
+
+    .nav-link {
+        border: none;
+        border-bottom: var(--bs-nav-tabs-border-width) solid transparent;
+        text-transform: uppercase;
+        font-weight: bold;
+        padding: 5px 0;
+        margin-right: 20px;
+
+        uib-tab-heading > i {
+            font-size: 18px;
+        }
+
+        &.disabled {
+            color: var(--bs-nav-tabs-link-disabled-color);
+            border-color: transparent;
+        }
+    }
+
+    .nav-item:last-child .nav-link {
+        margin-right: 0;
+    }
+
+    .nav-link.active,
+    .nav-item.show .nav-link {
+        color: var(--bs-nav-tabs-link-active-color);
+        border-color: var(--bs-nav-tabs-border-color);
+    }
+
+    .nav-item {
+        outline: none !important;
+    }
+}
+
+
+tab-body {
+    terminal-toolbar {
+        background: var(--bs-body-bg);
+
+        .btn, .toolbar-pin-button {
+            font-weight: bold;
+        }
+    }
+}
+
+@each $color, $value in $theme-colors {
+    .btn-#{$color} {
+        // 6c757d
+        --bs-btn-bg: var(--theme-#{$color});
+        --bs-btn-border-color: var(--theme-#{$color});
+        --bs-btn-disabled-bg: var(--theme-#{$color});
+        --bs-btn-disabled-border-color: var(--theme-#{$color});
+
+        --bs-btn-hover-border-color: var(--theme-#{$color}-less);
+        --bs-btn-hover-bg: var(--theme-#{$color}-less);
+
+        --bs-btn-active-border-color: var(--theme-#{$color}-less-2);
+        --bs-btn-active-bg: var(--theme-#{$color}-less-2);
+
+        --bs-btn-focus-shadow-rgb: 130, 138, 145;
+        --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+
+        --bs-btn-color: var(--theme-#{$color}-more-2);
+        --bs-btn-hover-color: var(--theme-#{$color}-more-2);
+        --bs-btn-active-color: var(--theme-#{$color}-more-2);
+        --bs-btn-disabled-color: var(--theme-#{$color}-more-2);
+    }
+
+    .alert-#{$color} {
+        --bs-alert-bg: var(--theme-#{$color}-more-2);
+        --bs-alert-border-color: var(--theme-#{$color}-more);
+        --bs-alert-color: var(--theme-#{$color});
+    }
+}
+
+
+multi-hotkey-input {
+    .item {
+        background: var(--theme-bg-more);
+        border: 1px solid var(--bs-primary);
+        border-radius: 3px;
+        margin-right: 5px;
+
+        .body {
+            padding: 3px 0 2px;
+
+            .stroke {
+                padding: 0 6px;
+                border-right: 1px solid var(--bs-body-bg);
+            }
+        }
+
+        .remove {
+            padding: 3px 8px 2px;
+        }
+    }
+
+    .item:has(.duplicate) {
+        background-color: var(--bs-danger);
+        border: 1px solid var(--bs-danger);
+    }
+
+    .add {
+        color: #777;
+        padding: 4px 10px 0;
+    }
+
+    .add, .item .body, .item .remove {
+        &:hover { background: var(--theme-bg-more); }
+        &:active { background: var(--theme-bg-more-2); }
+    }
+
+    .add:has(.duplicate), .item:has(.duplicate) .body, .item:has(.duplicate) .remove {
+        &:hover { background: var(--theme-danger-less); }
+        &:active { background: var(--theme-danger-less-2); }
+    }
+}
+
+hotkey-input-modal {
+    .input {
+        // background: $input-bg;
+        padding: 10px;
+        font-size: 24px;
+        line-height: 27px;
+        height: 55px;
+
+        .stroke {
+            background: var(--theme-bg-more);
+            border: 1px solid var(--bs-primary);
+            border-radius: 3px;
+            margin-right: 10px;
+            padding: 3px 10px;
+        }
+    }
+
+    .timeout {
+        background: $input-bg;
+
+        div {
+            background: $blue;
+        }
+    }
+}
+
+.mb-3 label {
+    margin-bottom: 2px;
+}
+
+.btn {
+    i + * {
+        margin-left: 5px;
+    }
+
+    &.btn-lg i + * {
+        margin-left: 10px;
+    }
+}
+
+.input-group-addon + .form-control {
+    border-left: none;
+}
+
+.input-group > select.form-control {
+    flex-direction: row;
+}
+
+.list-group-item {
+    // transition: 0.0625s background ease;
+
+    i + * {
+        margin-left: 10px;
+    }
+}
+
+.list-group.list-group-flush .list-group-item {
+    background: transparent;
+    border: none;
+
+    &:not(:last-child) {
+        border-bottom: none;
+    }
+}
+
+.list-group-light {
+    .list-group-item {
+        border: none !important;
+        outline: none !important;
+        background: transparent;
+        border-radius: $border-radius;
+        margin: 0 !important;
+
+        &.active {
+            background-color: var(--bs-list-group-active-bg);
+        }
+    }
+}
+
+// checkbox i.on {
+//     color: $blue;
+// }
+
+// .modal .modal-footer {
+//     background: rgba(0, 0, 0, .25);
+
+//     .btn {
+//         font-weight: bold;
+//         padding: 0.375rem 1.5rem;
+//     }
+// }
+
+.list-group-item svg {
+    fill: var(--bs-body-color);
+    fill-opacity: 0.75;
+}
+
+*::-webkit-scrollbar {
+    background: rgba(0, 0, 0, .125);
+    width: 10px;
+    margin: 5px;
+}
+
+*::-webkit-scrollbar-thumb {
+    background: rgba(255, 255, 255, .25);
+}
+
+*::-webkit-scrollbar-corner,
+*::-webkit-resizer {
+    opacity: 0;
+}
+
+search-panel {
+    background: var(--theme-bg-more) !important;
+
+    input {
+        border-radius: 0 !important;
+    }
+}
+
+
+.btn {
+    cursor: pointer;
+    justify-content: flex-start;
+    overflow: hidden;
+
+    &.disabled,
+    &:disabled {
+        cursor: not-allowed;
+    }
+}
+
+.btn-link {
+    text-decoration: none;
+
+    // &:hover, &[aria-expanded=true], &:active, &.active {
+    //     color: $link-hover-color;
+    //     border-radius: $btn-border-radius;
+    // }
+
+    // &[aria-expanded=true], &:active, &.active {
+    //     background: rgba(255, 255, 255, 0.1);
+    // }
+}
+
+// .btn-group .btn.active {
+//     border-color: transparent !important;
+// }
+
+
+// hr {
+//     border-color: $list-group-border-color;
+// }
+
+// .dropdown-menu {
+//     box-shadow: $dropdown-box-shadow;
+// }
+
+ngx-colors-panel .opened {
+    background: var(--bs-body-bg) !important;
+
+    button {
+        color: var(--bs-body-color) !important;
+    }
+
+    .button svg {
+        fill: white;
+    }
+}
+
+.text-muted {
+    // color: var(--bs-body-color) !important;
+    opacity: .5;
+}
+
+.form-switch .form-check-input {
+    --bs-form-switch-bg: inherit;
+    border-color: var(--theme-bg-more);
+    background-color: var(--theme-bg-more-2);
+
+    &:checked {
+        border-color: var(--theme-primary-more);
+        background-color: var(--theme-primary);
+    }
+}
+
+.form-control:focus {
+    border-color: var(--theme-fg-more-2);
+}
+
+.accordion {
+    --bs-accordion-bg: var(--theme-bg-more);
+    --bs-accordion-active-color: var(--theme-fg);
+    --bs-accordion-active-bg: var(--theme-bg-more-2);
+}
+
+start-page {
+    background: var(--theme-bg);
+}
+
+split-tab-spanner {
+    background: rgba(var(--bs-dark-rgb), .1);
+
+    &:hover, &.active {
+        background: rgba(var(--bs-dark-rgb), .2);
+    }
+}

+ 18 - 11
tabby-core/src/theme.paper.scss

@@ -19,10 +19,9 @@ $tab-border-radius: 5px;
 $button-hover-bg: rgba(0, 0, 0, .125);
 $button-active-bg: rgba(0, 0, 0, .25);
 
-$theme-colors: (
-    "primary":    $orange,
-    "secondary":  $base0
-);
+
+$primary: #fd7e14;
+$secondary: #495057;
 
 $content-bg: rgba($white, 0.65);
 $content-bg-solid: $white;
@@ -257,8 +256,8 @@ multi-hotkey-input {
     }
 
     .item:has(.duplicate) {
-        background-color: theme-color('danger');
-        border: 1px solid theme-color('danger');
+        background-color: map-get($theme-colors, 'danger');
+        border: 1px solid map-get($theme-colors, 'danger');
     }
 
     .add {
@@ -272,8 +271,8 @@ multi-hotkey-input {
     }
 
     .add:has(.duplicate), .item:has(.duplicate) .body, .item:has(.duplicate) .remove {
-        &:hover { background: darken(theme-color('danger'), 5%); }
-        &:active { background: darken(theme-color('danger'), 15%); }
+        &:hover { background: darken(map-get($theme-colors, 'danger'), 5%); }
+        &:active { background: darken(map-get($theme-colors, 'danger'), 15%); }
     }
 }
 
@@ -303,7 +302,7 @@ hotkey-input-modal {
     }
 }
 
-.form-group label {
+.mb-3 label {
     margin-bottom: 2px;
 }
 
@@ -314,7 +313,7 @@ hotkey-input-modal {
     }
 }
 
-[ngbradiogroup] > label.active {
+.btn-check:checked + label {
     background: $blue;
 }
 
@@ -370,7 +369,7 @@ toggle {
     }
 
     &.active .body .toggle {
-        background: theme-colors(primary) !important;
+        background: map-get($theme-colors, primary) !important;
     }
 }
 
@@ -398,3 +397,11 @@ terminal-toolbar {
 .bg-dark{
     background-color: $base2 !important;
 }
+
+split-tab-spanner {
+    background: rgba(0, 0, 0, .2);
+
+    &:hover, &.active {
+        background: rgba(255, 255, 255, .125);
+    }
+}

+ 20 - 9
tabby-core/src/theme.scss

@@ -7,6 +7,7 @@ $button-hover-bg: rgba(0, 0, 0, .25);
 $button-active-bg: rgba(0, 0, 0, .5);
 
 @import '~bootstrap/scss/bootstrap.scss';
+@import "./theme.vendor.scss";
 
 window-controls {
     svg {
@@ -163,8 +164,8 @@ multi-hotkey-input {
     }
 
     .item:has(.duplicate) {
-        background-color: theme-color('danger');
-        border: 1px solid theme-color('danger');
+        background-color: map-get($theme-colors, 'danger');
+        border: 1px solid map-get($theme-colors, 'danger');
     }
 
     .add {
@@ -178,8 +179,8 @@ multi-hotkey-input {
     }
 
     .add:has(.duplicate), .item:has(.duplicate) .body, .item:has(.duplicate) .remove {
-        &:hover { background: darken(theme-color('danger'), 5%); }
-        &:active { background: darken(theme-color('danger'), 15%); }
+        &:hover { background: darken(map-get($theme-colors, 'danger'), 5%); }
+        &:active { background: darken(map-get($theme-colors, 'danger'), 15%); }
     }
 }
 
@@ -209,11 +210,11 @@ hotkey-input-modal {
     }
 }
 
-.form-group label {
+.mb-3 label {
     margin-bottom: 2px;
 }
 
-[ngbradiogroup] > label.active {
+.btn-check:checked + label {
     background: $blue;
 }
 
@@ -342,6 +343,8 @@ search-panel {
 }
 
 .btn-link {
+    text-decoration: none;
+
     &:hover, &[aria-expanded=true], &:active, &.active {
         color: $link-hover-color;
         border-radius: $btn-border-radius;
@@ -375,9 +378,9 @@ search-panel {
             font-size: 18px;
         }
 
-        @include hover-focus {
-            color: $nav-tabs-link-active-color;
-        }
+        // @include hover-focus {
+        //     color: $nav-tabs-link-active-color;
+        // }
 
         &.disabled {
             color: $nav-link-disabled-color;
@@ -415,3 +418,11 @@ ngx-colors-panel .opened {
         fill: white;
     }
 }
+
+split-tab-spanner {
+    background: rgba(0, 0, 0, .2);
+
+    &:hover, &.active {
+        background: rgba(255, 255, 255, .125);
+    }
+}

+ 16 - 3
tabby-core/src/theme.ts

@@ -5,7 +5,7 @@ import { Theme } from './api'
 /** @hidden */
 @Injectable()
 export class StandardTheme extends Theme {
-    name = this.translate.instant('Standard')
+    name = this.translate.instant('Standard (legacy)')
     css = require('./theme.scss')
     terminalBackground = '#222a33'
 
@@ -17,7 +17,7 @@ export class StandardTheme extends Theme {
 /** @hidden */
 @Injectable()
 export class StandardCompactTheme extends Theme {
-    name = this.translate.instant('Compact')
+    name = this.translate.instant('Compact (legacy)')
     css = require('./theme.compact.scss')
     terminalBackground = '#222a33'
     macOSWindowButtonsInsetX = 8
@@ -31,7 +31,20 @@ export class StandardCompactTheme extends Theme {
 /** @hidden */
 @Injectable()
 export class PaperTheme extends Theme {
-    name = 'Paper'
+    name = 'Paper (legacy)'
     css = require('./theme.paper.scss')
     terminalBackground = '#f7f1e0'
 }
+
+/** @hidden */
+@Injectable({ providedIn: 'root' })
+export class NewTheme extends Theme {
+    name = this.translate.instant('Follow the color scheme')
+    css = require('./theme.new.scss')
+    terminalBackground = '#f7f1e0'
+    followsColorScheme = true
+
+    constructor (private translate: TranslateService) {
+        super()
+    }
+}

+ 16 - 7
tabby-core/src/theme.vars.scss

@@ -175,13 +175,12 @@ $badge-padding-y: 4px;
 $badge-padding-x: 6px;
 
 
-$custom-control-indicator-size: 1.2rem;
-$custom-control-indicator-bg: $body-bg;
-$custom-control-indicator-border-color: lighten($body-bg, 25%);
-$custom-control-indicator-checked-bg: theme-color("primary");
-$custom-control-indicator-checked-color: $body-bg;
-$custom-control-indicator-checked-border-color: transparent;
-$custom-control-indicator-active-bg: rgba(255, 255, 0, 0.5);
+$form-check-input-border: lighten($body-bg, 25%);
+$form-check-input-width: 1.4em;
+$form-switch-width: 2.5em;
+$form-switch-color: lighten($body-bg, 25%);
+$form-switch-focus-color: lighten($body-bg, 40%);
+$form-switch-checked-color: map-get($theme-colors, "primary");
 
 
 $modal-content-bg:               $content-bg-solid;
@@ -201,3 +200,13 @@ $alert-color-level: -5;
 $text-muted: rgba(255, 255, 255, 0.5);
 
 $card-bg: $list-group-bg;
+
+$tooltip-color: rgba(255, 255, 255, .75);
+$tooltip-bg: rgba(0, 0, 0, .75);
+
+$accordion-border-color: $list-group-border-color;
+$accordion-bg: $list-group-bg;
+$accordion-button-active-bg: $list-group-active-bg;
+$accordion-button-active-color: $list-group-active-color;
+
+$btn-close-color: $body-color;

+ 3 - 0
tabby-core/src/theme.vendor.scss

@@ -0,0 +1,3 @@
+// $alert-border-radius: 0;
+// @mixin border-radius ($r) {}
+@import "../../node_modules/ngx-toastr/toastr-bs5-alert.scss";

+ 0 - 5
tabby-core/webpack.config.js

@@ -1,5 +0,0 @@
-const config = require('../webpack.plugin.config')
-module.exports = config({
-    name: 'core',
-    dirname: __dirname,
-})

+ 10 - 0
tabby-core/webpack.config.mjs

@@ -0,0 +1,10 @@
+import * as path from 'path'
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
+
+import config from '../webpack.plugin.config.mjs'
+
+export default () => config({
+    name: 'core',
+    dirname: __dirname,
+})

+ 43 - 52
tabby-core/yarn.lock

@@ -2,13 +2,6 @@
 # yarn lockfile v1
 
 
-"@ngx-translate/core@^14.0.0":
-  version "14.0.0"
-  resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-14.0.0.tgz#af421d0e1a28376843f0fed375cd2fae7630a5ff"
-  integrity sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==
-  dependencies:
-    tslib "^2.3.0"
-
 abort-controller@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
@@ -33,10 +26,10 @@ base64-js@^1.3.1:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-bootstrap@^4.1.3:
-  version "4.5.3"
-  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.3.tgz#c6a72b355aaf323920be800246a6e4ef30997fe6"
-  integrity sha512-o9ppKQioXGqhw8Z7mah6KdTYpNQY//tipnkxppWhPbiSWdD+1raYsnhwEZjkTHYbGee4cVQ0Rx65EhOY/HNLcQ==
+bootstrap@^5.3.0-alpha.1:
+  version "5.3.0-alpha1"
+  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.0-alpha1.tgz#380629c4367893f02f7879a01ea3ae0f94e2e70e"
+  integrity sha512-ABZpKK4ObS3kKlIqH+ZVDqoy5t/bhFG0oHTAzByUdon7YIom0lpCeTqRniDzJmbtcWkNe800VVPBiJgxSYTYew==
 
 buffer@^6.0.3:
   version "6.0.3"
@@ -46,6 +39,34 @@ buffer@^6.0.3:
     base64-js "^1.3.1"
     ieee754 "^1.2.1"
 
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@^1.0.0, color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-string@^1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
+  integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
+  dependencies:
+    color-name "^1.0.0"
+    simple-swizzle "^0.2.2"
+
+color@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
+  integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
+  dependencies:
+    color-convert "^2.0.1"
+    color-string "^1.9.0"
+
 debug@4:
   version "4.3.1"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
@@ -68,11 +89,6 @@ events@^3.3.0:
   resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
   integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
 
-"filesize@>= 4.0.0":
-  version "6.3.0"
-  resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.3.0.tgz#dff53cfb3f104c9e422f346d53be8dbcc971bf11"
-  integrity sha512-ytx0ruGpDHKWVoiui6+BY/QMNngtDQ/pJaFwfBpQif0J63+E8DLdFyqS3NkKQn7vIruUEpoGD9JUJSg7Kp+I0g==
-
 fuzzy-search@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/fuzzy-search/-/fuzzy-search-3.2.1.tgz#65d5faad6bc633aee86f1898b7788dfe312ac6c9"
@@ -91,6 +107,11 @@ ieee754@^1.2.1:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
+is-arrayish@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+  integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
 js-yaml@^4.0.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -141,23 +162,6 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
-ngx-filesize@^2.0.16:
-  version "2.0.16"
-  resolved "https://registry.yarnpkg.com/ngx-filesize/-/ngx-filesize-2.0.16.tgz#fdaba04170edb6cfcdf7be932783cf913b03f016"
-  integrity sha512-VdaCirE7hSyfQh8ZEmhzNEhbddiTYUHF4V6OX+KyTmnQSVx4hp9kmzDX5YlkIlmClI6wI+LZmH9/q7XS3fsMPA==
-  dependencies:
-    filesize ">= 4.0.0"
-    tslib "^2.0.0"
-
-ngx-perfect-scrollbar@^10.1.0:
-  version "10.1.1"
-  resolved "https://registry.yarnpkg.com/ngx-perfect-scrollbar/-/ngx-perfect-scrollbar-10.1.1.tgz#f89832b9109e89bb59d516184638accd028e9735"
-  integrity sha512-f9IaDJGlBzSxnJ3Ki76n2JdzfQngUFyCf0E+CuVLaR5jL0IJDcTu7vOs8wexXunRMTd8xvIv0+sdIxf8hXAGWg==
-  dependencies:
-    perfect-scrollbar "1.5.0"
-    resize-observer-polyfill "^1.5.0"
-    tslib "^2.0.0"
-
 ngx-translate-messageformat-compiler@^4.11.0:
   version "4.11.0"
   resolved "https://registry.yarnpkg.com/ngx-translate-messageformat-compiler/-/ngx-translate-messageformat-compiler-4.11.0.tgz#c9b71dd139ba5fcdcd809001e22622de589fd707"
@@ -165,11 +169,6 @@ ngx-translate-messageformat-compiler@^4.11.0:
   dependencies:
     tslib "^1.10.0"
 
[email protected]:
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83"
-  integrity sha512-NrNHJn5mUGupSiheBTy6x+6SXCFbLlm8fVZh9moIzw/LgqElN5q4ncR4pbCBCYuCJ8Kcl9mYM0NgDxvW+b4LxA==
-
 process@^0.11.10:
   version "0.11.10"
   resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
@@ -185,26 +184,18 @@ [email protected]:
     events "^3.3.0"
     process "^0.11.10"
 
-resize-observer-polyfill@^1.5.0:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
-  integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
+simple-swizzle@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+  integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==
+  dependencies:
+    is-arrayish "^0.3.1"
 
 tslib@^1.10.0:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
-tslib@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
-  integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
-
-tslib@^2.3.0:
-  version "2.3.1"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
-  integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
-
 uuid@^9.0.0:
   version "9.0.0"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"

+ 0 - 5
tabby-electron/webpack.config.js

@@ -1,5 +0,0 @@
-const config = require('../webpack.plugin.config')
-module.exports = config({
-    name: 'electron',
-    dirname: __dirname,
-})

+ 10 - 0
tabby-electron/webpack.config.mjs

@@ -0,0 +1,10 @@
+import * as path from 'path'
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
+
+import config from '../webpack.plugin.config.mjs'
+
+export default () => config({
+    name: 'electron',
+    dirname: __dirname,
+})

+ 0 - 5
tabby-linkifier/webpack.config.js

@@ -1,5 +0,0 @@
-const config = require('../webpack.plugin.config')
-module.exports = config({
-    name: 'linkifier',
-    dirname: __dirname,
-})

+ 10 - 0
tabby-linkifier/webpack.config.mjs

@@ -0,0 +1,10 @@
+import * as path from 'path'
+import * as url from 'url'
+const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
+
+import config from '../webpack.plugin.config.mjs'
+
+export default () => config({
+    name: 'linkifier',
+    dirname: __dirname,
+})

+ 18 - 21
tabby-local/src/components/commandLineEditor.component.pug

@@ -1,36 +1,34 @@
 ng-container(*ngIf='!argvMode')
-    .form-group
+    .mb-3
         label(translate) Command line
         .input-group
-            .input-group-prepend
-                a.input-group-text(
-                    (click)='switchToArgv()',
-                    ngbTooltip='Split into unescaped arguments',
-                    href='#'
-                )
-                    i.fas.fa-fw.fa-caret-right
+            a.input-group-text.text-decoration-none(
+                (click)='switchToArgv()',
+                ngbTooltip='Split into unescaped arguments',
+                href='#'
+            )
+                i.fas.fa-fw.fa-caret-right
             input.form-control.text-monospace(
                 [(ngModel)]='command',
                 (ngModelChange)='parseCommand()'
             )
 
 ng-container(*ngIf='argvMode')
-    .form-group
+    .mb-3
         label(translate) Program
         .input-group
-            .input-group-prepend
-                a.input-group-text(
-                    (click)='switchToCommand()',
-                    ngbTooltip='Combine into a single escaped command',
-                    href='#'
-                )
-                    i.fas.fa-fw.fa-caret-down
+            a.input-group-text.text-decoration-none(
+                (click)='switchToCommand()',
+                ngbTooltip='Combine into a single escaped command',
+                href='#'
+            )
+                i.fas.fa-fw.fa-caret-down
             input.form-control.text-monospace(
                 type='text',
                 [(ngModel)]='_model.command',
             )
 
-    .form-group
+    .mb-3
         label(translate) Arguments
         .input-group(
             *ngFor='let arg of _model.args; index as i; trackBy: trackByIndex',
@@ -39,11 +37,10 @@ ng-container(*ngIf='argvMode')
                 type='text',
                 [(ngModel)]='_model.args[i]',
             )
-            .input-group-append
-                button.btn.btn-secondary((click)='_model.args.splice(i, 1)')
-                    i.fas.fa-fw.fa-trash
+            button.btn.btn-secondary((click)='_model.args.splice(i, 1)')
+                i.fas.fa-fw.fa-trash
 
         .mt-2
             button.btn.btn-secondary((click)='_model.args.push("")')
-                i.fas.fa-plus.mr-2
+                i.fas.fa-plus.me-2
                 span(translate) Add

+ 1 - 1
tabby-local/src/components/commandLineEditor.component.ts

@@ -6,7 +6,7 @@ import { SessionOptions } from '../api'
 /** @hidden */
 @Component({
     selector: 'command-line-editor',
-    template: require('./commandLineEditor.component.pug'),
+    templateUrl:'./commandLineEditor.component.pug',
 })
 export class CommandLineEditorComponent {
     @Input() argvMode = false

+ 7 - 9
tabby-local/src/components/environmentEditor.component.pug

@@ -1,20 +1,18 @@
 .mb-2.d-flex.align-items-center(*ngFor='let pair of vars')
     .input-group
         input.form-control.w-25.text-monospace([(ngModel)]='pair.key', (blur)='emitUpdate()', placeholder='Variable name')
-        .input-group-append
-            .input-group-text =
+        .input-group-text =
         input.form-control.w-50.text-monospace([(ngModel)]='pair.value', (blur)='emitUpdate()', placeholder='Value')
-        .input-group-append
-            button.btn.btn-secondary((click)='removeEnvironmentVar(pair.key)')
-                i.fas.fa-fw.fa-trash
+        button.btn.btn-secondary((click)='removeEnvironmentVar(pair.key)')
+            i.fas.fa-fw.fa-trash
 
 .d-flex
     button.btn.btn-secondary((click)='addEnvironmentVar()')
-        i.fas.fa-plus.mr-2
+        i.fas.fa-plus.me-2
         span(translate) Add
 
-    .ml-auto
+    .ms-auto
     .text-muted(translate) Substitutions allowed.
-    .d-flex.ml-1(*ngIf='shouldShowExample()')
+    .d-flex.ms-1(*ngIf='shouldShowExample()')
         .text-muted(translate) Example:
-        a.ml-1((click)='addExample()', href='#') extend PATH
+        a.ms-1((click)='addExample()', href='#') extend PATH

+ 2 - 2
tabby-local/src/components/environmentEditor.component.ts

@@ -5,8 +5,8 @@ import { Subject } from 'rxjs'
 /** @hidden */
 @Component({
     selector: 'environment-editor',
-    template: require('./environmentEditor.component.pug'),
-    styles: [require('./environmentEditor.component.scss')],
+    templateUrl:'./environmentEditor.component.pug',
+    styleUrls: ['./environmentEditor.component.scss'],
 })
 export class EnvironmentEditorComponent {
     @Output() modelChange = new Subject<any>()

+ 4 - 5
tabby-local/src/components/localProfileSettings.component.pug

@@ -7,7 +7,7 @@ command-line-editor([model]='profile.options')
         [(ngModel)]='profile.options.runAsAdministrator',
     )
 
-.form-group
+.mb-3
     label(translate) Working directory
 
     .input-group
@@ -16,11 +16,10 @@ command-line-editor([model]='profile.options')
             placeholder='Home directory',
             [(ngModel)]='profile.options.cwd'
         )
-        .input-group-append
-            button.btn.btn-secondary((click)='pickWorkingDirectory()')
-                i.fas.fa-folder-open
+        button.btn.btn-secondary((click)='pickWorkingDirectory()')
+            i.fas.fa-folder-open
 
-.form-group
+.mb-3
     label(translate) Environment
     environment-editor(
         type='text',

+ 1 - 1
tabby-local/src/components/localProfileSettings.component.ts

@@ -8,7 +8,7 @@ import { ProfileSettingsComponent } from 'tabby-core'
 
 /** @hidden */
 @Component({
-    template: require('./localProfileSettings.component.pug'),
+    templateUrl:'./localProfileSettings.component.pug',
 })
 export class LocalProfileSettingsComponent implements ProfileSettingsComponent<LocalProfile> {
     profile: LocalProfile

Some files were not shown because too many files changed in this diff