Repository: firezone/firezone
Pull request #9148: chore(gui-client): make better use of `vite` as a bundler
Author: login: thomaseizinger
Created: 2025-05-15T01:56:04Z | Merged: 2025-05-15T04:20:22Z

We already use `vite` as a bundling tool but only to rollup some of the pre-built files. This setup (and therefore our buildscripts) can be massively simplified by instructing `vite` to also build our TypeScript code and compile tailwind.

This makes it much easier to develop locally because one only needs to run `pnpm vite build --watch` to keep everything up to date.


Diff:
diff --git a/.github/workflows/_rust.yml b/.github/workflows/_rust.yml
index 73548b15fd2..0d6a8ab36be 100644
--- a/.github/workflows/_rust.yml
+++ b/.github/workflows/_rust.yml
@@ -142,15 +142,8 @@ jobs:
         timeout-minutes: 10
         with:
           runtime: true
-        # These steps must be synchronized with build.sh and build.bat in `rust/gui-client`
-      - name: pnpm install
-        run: |
-          pnpm install
-          cp "node_modules/flowbite/dist/flowbite.min.js" "src/"
-      - name: Compile Tailwind
-        run: pnpm tailwindcss -i src/input.css -o src/output.css
-      - name: Run Vite bundler
-        run: pnpm vite build
+      - run: pnpm install
+      - run: pnpm vite build
       - name: Build client
         run: cargo build -p firezone-gui-client --all-targets
       - uses: taiki-e/install-action@33734a118689b0b418824fb78ea2bf18e970b43b # v2.50.4
diff --git a/rust/gui-client/build-debug.bat b/rust/gui-client/build-debug.bat
index 499eace95ca..177446651bb 100755
--- a/rust/gui-client/build-debug.bat
+++ b/rust/gui-client/build-debug.bat
@@ -1,14 +1,5 @@
 @echo off
 setlocal enabledelayedexpansion
 
-REM Copy frontend dependencies
-copy "node_modules\flowbite\dist\flowbite.min.js" "src\"
-
-REM Compile TypeScript
-call pnpm tsc
-
-REM Compile CSS
-call pnpm tailwindcss -i src\input.css -o src\output.css
-
 REM Compile Rust and bundle
 call tauri build --debug --bundles none
diff --git a/rust/gui-client/build.bat b/rust/gui-client/build.bat
index 442fc60c2fe..86c8745db5a 100755
--- a/rust/gui-client/build.bat
+++ b/rust/gui-client/build.bat
@@ -1,12 +1,6 @@
 @echo off
 setlocal enabledelayedexpansion
 
-REM Copy frontend dependencies
-copy "node_modules\flowbite\dist\flowbite.min.js" "src\"
-
-REM Compile CSS
-call pnpm tailwindcss -i src\input.css -o src\output.css
-
 REM bundle web assets
 call pnpm vite build
 
diff --git a/rust/gui-client/build.sh b/rust/gui-client/build.sh
index 526bb179f7c..a317e45cb7e 100755
--- a/rust/gui-client/build.sh
+++ b/rust/gui-client/build.sh
@@ -9,12 +9,6 @@ BUNDLES_DIR=../target/release/bundle/deb
 # Prep the RPM container
 docker build . -f ../Dockerfile-rpm -t rpmbuild
 
-# Copy frontend dependencies
-cp node_modules/flowbite/dist/flowbite.min.js src/
-
-# Compile CSS
-pnpm tailwindcss -i src/input.css -o src/output.css
-
 # Bundle all web assets
 pnpm vite build
 
diff --git a/rust/gui-client/dev.bat b/rust/gui-client/dev.bat
index e7564a0ba65..a3ea29070d7 100755
--- a/rust/gui-client/dev.bat
+++ b/rust/gui-client/dev.bat
@@ -1,14 +1,5 @@
 @echo off
 setlocal enabledelayedexpansion
 
-REM Copy frontend dependencies
-copy "node_modules\flowbite\dist\flowbite.min.js" "src\"
-
-REM Compile TypeScript in watch mode
-start tsc --watch
-
-REM Compile CSS in watch mode
-start call npx tailwindcss -i src\input.css -o src\output.css --watch
-
 REM Start Tauri hot-reloading
 tauri dev
diff --git a/rust/gui-client/dev.sh b/rust/gui-client/dev.sh
index 793ef8a8f66..7a3927f56b2 100755
--- a/rust/gui-client/dev.sh
+++ b/rust/gui-client/dev.sh
@@ -11,14 +11,5 @@ stop() {
 }
 trap stop INT TERM
 
-# Copy frontend dependencies
-cp node_modules/flowbite/dist/flowbite.min.js src/
-
-# Compile TypeScript
-tsc --watch &
-
-# Compile CSS
-tailwindcss -i src/input.css -o src/output.css --watch &
-
 # Start Tauri hot-reloading: Not applicable for Windows
-# tauri dev
+tauri dev
diff --git a/rust/gui-client/package.json b/rust/gui-client/package.json
index acc5fea37e8..2b06ed190a8 100644
--- a/rust/gui-client/package.json
+++ b/rust/gui-client/package.json
@@ -20,12 +20,16 @@
     "flowbite": "^3.1.2"
   },
   "devDependencies": {
+    "@rollup/plugin-typescript": "^12.1.2",
+    "@tailwindcss/vite": "^4.1.6",
     "@tauri-apps/cli": "^2.5.0",
     "@types/node": "^22.15.3",
     "http-server": "^14.1.1",
     "run-script-os": "^1.1.6",
     "tailwindcss": "^4.1.5",
+    "tslib": "^2.8.1",
     "typescript": "^5.8.3",
-    "vite": "^6.3.4"
+    "vite": "^6.3.4",
+    "vite-plugin-typescript": "^1.0.4"
   }
 }
diff --git a/rust/gui-client/pnpm-lock.yaml b/rust/gui-client/pnpm-lock.yaml
index 9c979baeb8d..3a0ff88c4f8 100644
--- a/rust/gui-client/pnpm-lock.yaml
+++ b/rust/gui-client/pnpm-lock.yaml
@@ -18,6 +18,12 @@ importers:
         specifier: ^3.1.2
         version: 3.1.2(rollup@4.40.1)
     devDependencies:
+      '@rollup/plugin-typescript':
+        specifier: ^12.1.2
+        version: 12.1.2(rollup@4.40.1)(tslib@2.8.1)(typescript@5.8.3)
+      '@tailwindcss/vite':
+        specifier: ^4.1.6
+        version: 4.1.6(vite@6.3.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
       '@tauri-apps/cli':
         specifier: ^2.5.0
         version: 2.5.0
@@ -33,15 +39,25 @@ importers:
       tailwindcss:
         specifier: ^4.1.5
         version: 4.1.5
+      tslib:
+        specifier: ^2.8.1
+        version: 2.8.1
       typescript:
         specifier: ^5.8.3
         version: 5.8.3
       vite:
         specifier: ^6.3.4
         version: 6.3.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
+      vite-plugin-typescript:
+        specifier: ^1.0.4
+        version: 1.0.4(@rollup/plugin-typescript@12.1.2(rollup@4.40.1)(tslib@2.8.1)(typescript@5.8.3))(vite@6.3.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))
 
 packages:
 
+  '@ampproject/remapping@2.3.0':
+    resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+    engines: {node: '>=6.0.0'}
+
   '@esbuild/aix-ppc64@0.25.3':
     resolution: {integrity: sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==}
     engines: {node: '>=18'}
@@ -192,6 +208,28 @@ packages:
     cpu: [x64]
     os: [win32]
 
+  '@isaacs/fs-minipass@4.0.1':
+    resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+    engines: {node: '>=18.0.0'}
+
+  '@jridgewell/gen-mapping@0.3.8':
+    resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/set-array@1.2.1':
+    resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/sourcemap-codec@1.5.0':
+    resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+  '@jridgewell/trace-mapping@0.3.25':
+    resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
   '@parcel/watcher-android-arm64@2.5.1':
     resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
     engines: {node: '>= 10.0.0'}
@@ -286,6 +324,19 @@ packages:
       rollup:
         optional: true
 
+  '@rollup/plugin-typescript@12.1.2':
+    resolution: {integrity: sha512-cdtSp154H5sv637uMr1a8OTWB0L1SWDSm1rDGiyfcGcvQ6cuTs4MDk2BVEBGysUWago4OJN4EQZqOTl/QY3Jgg==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^2.14.0||^3.0.0||^4.0.0
+      tslib: '*'
+      typescript: '>=3.7.0'
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+      tslib:
+        optional: true
+
   '@rollup/pluginutils@5.1.4':
     resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
     engines: {node: '>=14.0.0'}
@@ -402,60 +453,117 @@ packages:
   '@tailwindcss/node@4.1.5':
     resolution: {integrity: sha512-CBhSWo0vLnWhXIvpD0qsPephiaUYfHUX3U9anwDaHZAeuGpTiB3XmsxPAN6qX7bFhipyGBqOa1QYQVVhkOUGxg==}
 
+  '@tailwindcss/node@4.1.6':
+    resolution: {integrity: sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==}
+
   '@tailwindcss/oxide-android-arm64@4.1.5':
     resolution: {integrity: sha512-LVvM0GirXHED02j7hSECm8l9GGJ1RfgpWCW+DRn5TvSaxVsv28gRtoL4aWKGnXqwvI3zu1GABeDNDVZeDPOQrw==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [android]
 
+  '@tailwindcss/oxide-android-arm64@4.1.6':
+    resolution: {integrity: sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [android]
+
   '@tailwindcss/oxide-darwin-arm64@4.1.5':
     resolution: {integrity: sha512-//TfCA3pNrgnw4rRJOqavW7XUk8gsg9ddi8cwcsWXp99tzdBAZW0WXrD8wDyNbqjW316Pk2hiN/NJx/KWHl8oA==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [darwin]
 
+  '@tailwindcss/oxide-darwin-arm64@4.1.6':
+    resolution: {integrity: sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [darwin]
+
   '@tailwindcss/oxide-darwin-x64@4.1.5':
     resolution: {integrity: sha512-XQorp3Q6/WzRd9OalgHgaqgEbjP3qjHrlSUb5k1EuS1Z9NE9+BbzSORraO+ecW432cbCN7RVGGL/lSnHxcd+7Q==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [darwin]
 
+  '@tailwindcss/oxide-darwin-x64@4.1.6':
+    resolution: {integrity: sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [darwin]
+
   '@tailwindcss/oxide-freebsd-x64@4.1.5':
     resolution: {integrity: sha512-bPrLWbxo8gAo97ZmrCbOdtlz/Dkuy8NK97aFbVpkJ2nJ2Jo/rsCbu0TlGx8joCuA3q6vMWTSn01JY46iwG+clg==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [freebsd]
 
+  '@tailwindcss/oxide-freebsd-x64@4.1.6':
+    resolution: {integrity: sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [freebsd]
+
   '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5':
     resolution: {integrity: sha512-1gtQJY9JzMAhgAfvd/ZaVOjh/Ju/nCoAsvOVJenWZfs05wb8zq+GOTnZALWGqKIYEtyNpCzvMk+ocGpxwdvaVg==}
     engines: {node: '>= 10'}
     cpu: [arm]
     os: [linux]
 
+  '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6':
+    resolution: {integrity: sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==}
+    engines: {node: '>= 10'}
+    cpu: [arm]
+    os: [linux]
+
   '@tailwindcss/oxide-linux-arm64-gnu@4.1.5':
     resolution: {integrity: sha512-dtlaHU2v7MtdxBXoqhxwsWjav7oim7Whc6S9wq/i/uUMTWAzq/gijq1InSgn2yTnh43kR+SFvcSyEF0GCNu1PQ==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
+  '@tailwindcss/oxide-linux-arm64-gnu@4.1.6':
+    resolution: {integrity: sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [linux]
+
   '@tailwindcss/oxide-linux-arm64-musl@4.1.5':
     resolution: {integrity: sha512-fg0F6nAeYcJ3CriqDT1iVrqALMwD37+sLzXs8Rjy8Z1ZHshJoYceodfyUwGJEsQoTyWbliFNRs2wMQNXtT7MVA==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
+  '@tailwindcss/oxide-linux-arm64-musl@4.1.6':
+    resolution: {integrity: sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [linux]
+
   '@tailwindcss/oxide-linux-x64-gnu@4.1.5':
     resolution: {integrity: sha512-SO+F2YEIAHa1AITwc8oPwMOWhgorPzzcbhWEb+4oLi953h45FklDmM8dPSZ7hNHpIk9p/SCZKUYn35t5fjGtHA==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
+  '@tailwindcss/oxide-linux-x64-gnu@4.1.6':
+    resolution: {integrity: sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [linux]
+
   '@tailwindcss/oxide-linux-x64-musl@4.1.5':
     resolution: {integrity: sha512-6UbBBplywkk/R+PqqioskUeXfKcBht3KU7juTi1UszJLx0KPXUo10v2Ok04iBJIaDPkIFkUOVboXms5Yxvaz+g==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
+  '@tailwindcss/oxide-linux-x64-musl@4.1.6':
+    resolution: {integrity: sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [linux]
+
   '@tailwindcss/oxide-wasm32-wasi@4.1.5':
     resolution: {integrity: sha512-hwALf2K9FHuiXTPqmo1KeOb83fTRNbe9r/Ixv9ZNQ/R24yw8Ge1HOWDDgTdtzntIaIUJG5dfXCf4g9AD4RiyhQ==}
     engines: {node: '>=14.0.0'}
@@ -468,22 +576,55 @@ packages:
       - '@emnapi/wasi-threads'
       - tslib
 
+  '@tailwindcss/oxide-wasm32-wasi@4.1.6':
+    resolution: {integrity: sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==}
+    engines: {node: '>=14.0.0'}
+    cpu: [wasm32]
+    bundledDependencies:
+      - '@napi-rs/wasm-runtime'
+      - '@emnapi/core'
+      - '@emnapi/runtime'
+      - '@tybys/wasm-util'
+      - '@emnapi/wasi-threads'
+      - tslib
+
   '@tailwindcss/oxide-win32-arm64-msvc@4.1.5':
     resolution: {integrity: sha512-oDKncffWzaovJbkuR7/OTNFRJQVdiw/n8HnzaCItrNQUeQgjy7oUiYpsm9HUBgpmvmDpSSbGaCa2Evzvk3eFmA==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [win32]
 
+  '@tailwindcss/oxide-win32-arm64-msvc@4.1.6':
+    resolution: {integrity: sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [win32]
+
   '@tailwindcss/oxide-win32-x64-msvc@4.1.5':
     resolution: {integrity: sha512-WiR4dtyrFdbb+ov0LK+7XsFOsG+0xs0PKZKkt41KDn9jYpO7baE3bXiudPVkTqUEwNfiglCygQHl2jklvSBi7Q==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [win32]
 
+  '@tailwindcss/oxide-win32-x64-msvc@4.1.6':
+    resolution: {integrity: sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [win32]
+
   '@tailwindcss/oxide@4.1.5':
     resolution: {integrity: sha512-1n4br1znquEvyW/QuqMKQZlBen+jxAbvyduU87RS8R3tUSvByAkcaMTkJepNIrTlYhD+U25K4iiCIxE6BGdRYA==}
     engines: {node: '>= 10'}
 
+  '@tailwindcss/oxide@4.1.6':
+    resolution: {integrity: sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==}
+    engines: {node: '>= 10'}
+
+  '@tailwindcss/vite@4.1.6':
+    resolution: {integrity: sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w==}
+    peerDependencies:
+      vite: ^5.2.0 || ^6
+
   '@tauri-apps/api@2.5.0':
     resolution: {integrity: sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA==}
 
@@ -590,6 +731,10 @@ packages:
     resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
     engines: {node: '>=10'}
 
+  chownr@3.0.0:
+    resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+    engines: {node: '>=18'}
+
   color-convert@2.0.1:
     resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
     engines: {node: '>=7.0.0'}
@@ -827,6 +972,9 @@ packages:
   lodash@4.17.21:
     resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
 
+  magic-string@0.30.17:
+    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
   micromatch@4.0.8:
     resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
     engines: {node: '>=8.6'}
@@ -843,10 +991,23 @@ packages:
   minimist@1.2.8:
     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
 
+  minipass@7.1.2:
+    resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minizlib@3.0.2:
+    resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
+    engines: {node: '>= 18'}
+
   mkdirp@0.5.6:
     resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
     hasBin: true
 
+  mkdirp@3.0.1:
+    resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
+    engines: {node: '>=10'}
+    hasBin: true
+
   mri@1.2.0:
     resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
     engines: {node: '>=4'}
@@ -945,10 +1106,17 @@ packages:
   tailwindcss@4.1.5:
     resolution: {integrity: sha512-nYtSPfWGDiWgCkwQG/m+aX83XCwf62sBgg3bIlNiiOcggnS1x3uVRDAuyelBFL+vJdOPPCGElxv9DjHJjRHiVA==}
 
+  tailwindcss@4.1.6:
+    resolution: {integrity: sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==}
+
   tapable@2.2.1:
     resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
     engines: {node: '>=6'}
 
+  tar@7.4.3:
+    resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
+    engines: {node: '>=18'}
+
   tinyglobby@0.2.13:
     resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
     engines: {node: '>=12.0.0'}
@@ -957,6 +1125,9 @@ packages:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
 
+  tslib@2.8.1:
+    resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
   typescript@5.8.3:
     resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
     engines: {node: '>=14.17'}
@@ -972,6 +1143,12 @@ packages:
   url-join@4.0.1:
     resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
 
+  vite-plugin-typescript@1.0.4:
+    resolution: {integrity: sha512-vTfXw5WzXx+qbPQhjEUVGB+DoU183FdDc2y0HfNDfpE/anYtdefKVv8re3Po/IZiKZULmwO/pY031m0btkIxUw==}
+    peerDependencies:
+      '@rollup/plugin-typescript': '*'
+      vite: '*'
+
   vite@6.3.4:
     resolution: {integrity: sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==}
     engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -1016,6 +1193,10 @@ packages:
     resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
     engines: {node: '>=12'}
 
+  yallist@5.0.0:
+    resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+    engines: {node: '>=18'}
+
   yaml@2.6.0:
     resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==}
     engines: {node: '>= 14'}
@@ -1023,6 +1204,11 @@ packages:
 
 snapshots:
 
+  '@ampproject/remapping@2.3.0':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.8
+      '@jridgewell/trace-mapping': 0.3.25
+
   '@esbuild/aix-ppc64@0.25.3':
     optional: true
 
@@ -1098,6 +1284,27 @@ snapshots:
   '@esbuild/win32-x64@0.25.3':
     optional: true
 
+  '@isaacs/fs-minipass@4.0.1':
+    dependencies:
+      minipass: 7.1.2
+
+  '@jridgewell/gen-mapping@0.3.8':
+    dependencies:
+      '@jridgewell/set-array': 1.2.1
+      '@jridgewell/sourcemap-codec': 1.5.0
+      '@jridgewell/trace-mapping': 0.3.25
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/set-array@1.2.1': {}
+
+  '@jridgewell/sourcemap-codec@1.5.0': {}
+
+  '@jridgewell/trace-mapping@0.3.25':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.0
+
   '@parcel/watcher-android-arm64@2.5.1':
     optional: true
 
@@ -1170,6 +1377,15 @@ snapshots:
     optionalDependencies:
       rollup: 4.40.1
 
+  '@rollup/plugin-typescript@12.1.2(rollup@4.40.1)(tslib@2.8.1)(typescript@5.8.3)':
+    dependencies:
+      '@rollup/pluginutils': 5.1.4(rollup@4.40.1)
+      resolve: 1.22.10
+      typescript: 5.8.3
+    optionalDependencies:
+      rollup: 4.40.1
+      tslib: 2.8.1
+
   '@rollup/pluginutils@5.1.4(rollup@4.40.1)':
     dependencies:
       '@types/estree': 1.0.7
@@ -1255,42 +1471,88 @@ snapshots:
       lightningcss: 1.29.2
       tailwindcss: 4.1.5
 
+  '@tailwindcss/node@4.1.6':
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      enhanced-resolve: 5.18.1
+      jiti: 2.4.2
+      lightningcss: 1.29.2
+      magic-string: 0.30.17
+      source-map-js: 1.2.1
+      tailwindcss: 4.1.6
+
   '@tailwindcss/oxide-android-arm64@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-android-arm64@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-darwin-arm64@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-darwin-arm64@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-darwin-x64@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-darwin-x64@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-freebsd-x64@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-freebsd-x64@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-linux-arm64-gnu@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-linux-arm64-gnu@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-linux-arm64-musl@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-linux-arm64-musl@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-linux-x64-gnu@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-linux-x64-gnu@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-linux-x64-musl@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-linux-x64-musl@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-wasm32-wasi@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-wasm32-wasi@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-win32-arm64-msvc@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-win32-arm64-msvc@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide-win32-x64-msvc@4.1.5':
     optional: true
 
+  '@tailwindcss/oxide-win32-x64-msvc@4.1.6':
+    optional: true
+
   '@tailwindcss/oxide@4.1.5':
     optionalDependencies:
       '@tailwindcss/oxide-android-arm64': 4.1.5
@@ -1306,6 +1568,31 @@ snapshots:
       '@tailwindcss/oxide-win32-arm64-msvc': 4.1.5
       '@tailwindcss/oxide-win32-x64-msvc': 4.1.5
 
+  '@tailwindcss/oxide@4.1.6':
+    dependencies:
+      detect-libc: 2.0.4
+      tar: 7.4.3
+    optionalDependencies:
+      '@tailwindcss/oxide-android-arm64': 4.1.6
+      '@tailwindcss/oxide-darwin-arm64': 4.1.6
+      '@tailwindcss/oxide-darwin-x64': 4.1.6
+      '@tailwindcss/oxide-freebsd-x64': 4.1.6
+      '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.6
+      '@tailwindcss/oxide-linux-arm64-gnu': 4.1.6
+      '@tailwindcss/oxide-linux-arm64-musl': 4.1.6
+      '@tailwindcss/oxide-linux-x64-gnu': 4.1.6
+      '@tailwindcss/oxide-linux-x64-musl': 4.1.6
+      '@tailwindcss/oxide-wasm32-wasi': 4.1.6
+      '@tailwindcss/oxide-win32-arm64-msvc': 4.1.6
+      '@tailwindcss/oxide-win32-x64-msvc': 4.1.6
+
+  '@tailwindcss/vite@4.1.6(vite@6.3.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))':
+    dependencies:
+      '@tailwindcss/node': 4.1.6
+      '@tailwindcss/oxide': 4.1.6
+      tailwindcss: 4.1.6
+      vite: 6.3.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
+
   '@tauri-apps/api@2.5.0': {}
 
   '@tauri-apps/cli-darwin-arm64@2.5.0':
@@ -1392,6 +1679,8 @@ snapshots:
       ansi-styles: 4.3.0
       supports-color: 7.2.0
 
+  chownr@3.0.0: {}
+
   color-convert@2.0.1:
     dependencies:
       color-name: 1.1.4
@@ -1626,6 +1915,10 @@ snapshots:
 
   lodash@4.17.21: {}
 
+  magic-string@0.30.17:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.0
+
   micromatch@4.0.8:
     dependencies:
       braces: 3.0.3
@@ -1637,10 +1930,18 @@ snapshots:
 
   minimist@1.2.8: {}
 
+  minipass@7.1.2: {}
+
+  minizlib@3.0.2:
+    dependencies:
+      minipass: 7.1.2
+
   mkdirp@0.5.6:
     dependencies:
       minimist: 1.2.8
 
+  mkdirp@3.0.1: {}
+
   mri@1.2.0: {}
 
   ms@2.1.3: {}
@@ -1747,8 +2048,19 @@ snapshots:
 
   tailwindcss@4.1.5: {}
 
+  tailwindcss@4.1.6: {}
+
   tapable@2.2.1: {}
 
+  tar@7.4.3:
+    dependencies:
+      '@isaacs/fs-minipass': 4.0.1
+      chownr: 3.0.0
+      minipass: 7.1.2
+      minizlib: 3.0.2
+      mkdirp: 3.0.1
+      yallist: 5.0.0
+
   tinyglobby@0.2.13:
     dependencies:
       fdir: 6.4.4(picomatch@4.0.2)
@@ -1758,6 +2070,8 @@ snapshots:
     dependencies:
       is-number: 7.0.0
 
+  tslib@2.8.1: {}
+
   typescript@5.8.3: {}
 
   undici-types@6.21.0: {}
@@ -1768,6 +2082,11 @@ snapshots:
 
   url-join@4.0.1: {}
 
+  vite-plugin-typescript@1.0.4(@rollup/plugin-typescript@12.1.2(rollup@4.40.1)(tslib@2.8.1)(typescript@5.8.3))(vite@6.3.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)):
+    dependencies:
+      '@rollup/plugin-typescript': 12.1.2(rollup@4.40.1)(tslib@2.8.1)(typescript@5.8.3)
+      vite: 6.3.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)
+
   vite@6.3.4(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0):
     dependencies:
       esbuild: 0.25.3
@@ -1787,5 +2106,7 @@ snapshots:
     dependencies:
       iconv-lite: 0.6.3
 
+  yallist@5.0.0: {}
+
   yaml@2.6.0:
     optional: true
diff --git a/rust/gui-client/src/about.html b/rust/gui-client/src/about.html
index 96b4683fd0c..7eded50117b 100644
--- a/rust/gui-client/src/about.html
+++ b/rust/gui-client/src/about.html
@@ -3,10 +3,9 @@
   <head>
     <meta charset="UTF-8" />
     <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
-    <link rel="stylesheet" href="output.css" />
+    <link rel="stylesheet" href="input.css" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>About Firezone</title>
-    <script type="module" src="./flowbite.min.js" defer></script>
     <script type="module" src="about.ts" defer></script>
   </head>
 
diff --git a/rust/gui-client/src/about.ts b/rust/gui-client/src/about.ts
index 0f7dea4fe20..7845ea2d43c 100644
--- a/rust/gui-client/src/about.ts
+++ b/rust/gui-client/src/about.ts
@@ -1,4 +1,5 @@
 import { invoke } from "@tauri-apps/api/core";
+import "flowbite"
 
 const cargoVersionSpan = <HTMLSpanElement>(
   document.getElementById("cargo-version")
diff --git a/rust/gui-client/src/settings.html b/rust/gui-client/src/settings.html
index 0045984438f..6ba6b08f73b 100644
--- a/rust/gui-client/src/settings.html
+++ b/rust/gui-client/src/settings.html
@@ -3,10 +3,9 @@
   <head>
     <meta charset="UTF-8" />
     <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
-    <link rel="stylesheet" href="output.css" />
+    <link rel="stylesheet" href="input.css" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Settings</title>
-    <script type="module" src="./flowbite.min.js" defer></script>
     <script type="module" src="settings.ts" defer></script>
   </head>
 
diff --git a/rust/gui-client/src/settings.ts b/rust/gui-client/src/settings.ts
index 9044c1950a5..a2cde427b30 100644
--- a/rust/gui-client/src/settings.ts
+++ b/rust/gui-client/src/settings.ts
@@ -1,4 +1,5 @@
 import { invoke } from "@tauri-apps/api/core";
+import "flowbite"
 
 // Custom types
 interface Settings {
diff --git a/rust/gui-client/src/welcome.html b/rust/gui-client/src/welcome.html
index 30032a9348f..fb51781ba8c 100644
--- a/rust/gui-client/src/welcome.html
+++ b/rust/gui-client/src/welcome.html
@@ -3,10 +3,9 @@
   <head>
     <meta charset="UTF-8" />
     <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
-    <link rel="stylesheet" href="output.css" />
+    <link rel="stylesheet" href="input.css" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Welcome to Firezone</title>
-    <script type="module" src="./flowbite.min.js" defer></script>
     <script type="module" src="welcome.ts" defer></script>
   </head>
 
diff --git a/rust/gui-client/src/welcome.ts b/rust/gui-client/src/welcome.ts
index 1ba50990065..41f5af45803 100644
--- a/rust/gui-client/src/welcome.ts
+++ b/rust/gui-client/src/welcome.ts
@@ -1,4 +1,5 @@
 import { invoke } from "@tauri-apps/api/core";
+import "flowbite"
 
 const signInBtn = <HTMLButtonElement>document.getElementById("sign-in");
 
diff --git a/rust/gui-client/vite.config.ts b/rust/gui-client/vite.config.ts
index 8521f2d68ce..9f76eedafca 100644
--- a/rust/gui-client/vite.config.ts
+++ b/rust/gui-client/vite.config.ts
@@ -1,5 +1,7 @@
 import { defineConfig } from "vite";
 import { resolve } from "path";
+import tailwindcss from '@tailwindcss/vite'
+import typescript from 'vite-plugin-typescript';
 
 // @ts-expect-error process is a nodejs global
 const host = process.env.TAURI_DEV_HOST;
@@ -16,6 +18,11 @@ export default defineConfig(async () => ({
     },
   },
 
+  plugins: [
+    tailwindcss(),
+    typescript(),
+  ],
+
   // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
   //
   // 1. prevent vite from obscuring rust errors


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR simplifies the build process for the gui-client by leveraging Vite’s capabilities to compile TypeScript and process Tailwind CSS. The changes update the Vite configuration to add the relevant plugins, adjust HTML files to reference the new CSS entry point, and remove redundant build steps from various scripts and workflows.
- Added Vite plugins for Tailwind CSS and TypeScript in vite.config.ts.
- Updated HTML files to load "input.css" instead of a pre-built "output.css".
- Removed outdated build commands from shell scripts, batch files, and GitHub workflows.

### Reviewed Changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.

<details>
<summary>Show a summary per file</summary>

| File                            | Description                                                     |
| ------------------------------- | --------------------------------------------------------------- |
| rust/gui-client/vite.config.ts  | Added imports and plugins for Tailwind CSS and TypeScript.       |
| rust/gui-client/src/*.ts, *.html| Updated frontend files to use the new CSS entry point and import flowbite. |
| rust/gui-client/package.json    | Added and updated dependencies to support new build flow.        |
| rust/gui-client/dev.*, build*   | Removed redundant build steps in favor of the integrated Vite process. |
| .github/workflows/_rust.yml      | Simplified workflow by removing the separate CSS build and asset copying steps. |
</details>


<details>
<summary>Files not reviewed (1)</summary>

* **rust/gui-client/pnpm-lock.yaml**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**.github/workflows/_rust.yml:146**
* Verify that consolidating build steps into 'pnpm vite build' in the workflow handles all asset processing (e.g., Tailwind CSS and Flowbite) without requiring separate commands.
```
- run: pnpm vite build
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T01:56:31Z
user: login: jamilbk
body: Nice!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-15T02:10:22Z

Review comments:
diff_hunk: @@ -16,6 +18,11 @@ export default defineConfig(async () => ({
     },
   },
 
+  plugins: [
path: rust/gui-client/vite.config.ts
user: login: Copilot
body: [nitpick] Consider adding a comment explaining the purpose of each plugin (tailwindcss and typescript) and any important configuration details, to help future maintainers understand the integration.
created_at: 2025-05-15T01:56:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,10 +3,9 @@
   <head>
     <meta charset="UTF-8" />
     <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
-    <link rel="stylesheet" href="output.css" />
+    <link rel="stylesheet" href="input.css" />
path: rust/gui-client/src/welcome.html
user: login: Copilot
body: Ensure that switching from 'output.css' to 'input.css' matches the new Vite processing workflow and that the compiled CSS is correctly output/imported in production.
created_at: 2025-05-15T01:56:30Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-05-15T01:56:08Z
body: [vc]: #8S4Rn0xbzWO6Tgv3USc97zbzAWMtYEcYwh24X9sGMPY=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJmaXJlem9uZSIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJmaXJlem9uZS1naXQtY2hvcmUtaW1wcm92ZS1mcm9udGVuZC1kZXYtc2V0dXAtZmlyZXpvbmUudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vZmlyZXpvbmUvZmlyZXpvbmUvQThCSEs5dTNVS0NVM3A3a1hYYXJ1YXdlcW1vRSIsInByZXZpZXdVcmwiOiJmaXJlem9uZS1naXQtY2hvcmUtaW1wcm92ZS1mcm9udGVuZC1kZXYtc2V0dXAtZmlyZXpvbmUudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCJ9XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **firezone** | ✅ Ready ([Inspect](https://vercel.com/firezone/firezone/A8BHK9u3UKCU3p7kXXaruaweqmoE)) | [Visit Preview](https://firezone-git-chore-improve-frontend-dev-setup-firezone.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/firezone-git-chore-improve-frontend-dev-setup-firezone.vercel.app?via=pr-comment-feedback-link) | May 15, 2025 1:56am |


author_association: NONE
user: login: thomaseizinger
created_at: 2025-05-15T01:57:10Z
body: Clients for testing: https://github.com/firezone/firezone/actions/runs/15034905237
author_association: MEMBER
user: login: thomaseizinger
created_at: 2025-05-15T04:06:00Z
body: > Clients for testing: [firezone/firezone/actions/runs/15034905237](https://github.com/firezone/firezone/actions/runs/15034905237)

Tested Windows and works. Assuming other platforms will work too then because none of this is platform-specific.
author_association: MEMBER