zephyrfs/zephyrfs-desktop / b8020b1

Browse files

4.1 desktop app with safe storage allocation

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
b8020b1faf0c7c141cb0d5cbbc36263f08798cc7
Tree
383f93b

39 changed files

StatusFile+-
A .gitignore 107 0
A .vscode/extensions.json 7 0
A README.md 7 0
A index.html 14 0
A package-lock.json 1679 0
A package.json 24 0
A public/tauri.svg 6 0
A public/vite.svg 1 0
A src-tauri/.gitignore 7 0
A src-tauri/Cargo.toml 37 0
A src-tauri/build.rs 3 0
A src-tauri/capabilities/default.json 10 0
A src-tauri/icons/128x128.png bin
A src-tauri/icons/128x128@2x.png bin
A src-tauri/icons/32x32.png bin
A src-tauri/icons/Square107x107Logo.png bin
A src-tauri/icons/Square142x142Logo.png bin
A src-tauri/icons/Square150x150Logo.png bin
A src-tauri/icons/Square284x284Logo.png bin
A src-tauri/icons/Square30x30Logo.png bin
A src-tauri/icons/Square310x310Logo.png bin
A src-tauri/icons/Square44x44Logo.png bin
A src-tauri/icons/Square71x71Logo.png bin
A src-tauri/icons/Square89x89Logo.png bin
A src-tauri/icons/StoreLogo.png bin
A src-tauri/icons/icon.icns bin
A src-tauri/icons/icon.ico bin
A src-tauri/icons/icon.png bin
A src-tauri/src/folder_manager.rs 339 0
A src-tauri/src/lib.rs 31 0
A src-tauri/src/main.rs 6 0
A src-tauri/tauri.conf.json 35 0
A src/App.vue 580 0
A src/assets/vue.svg 1 0
A src/main.ts 4 0
A src/vite-env.d.ts 7 0
A tsconfig.json 25 0
A tsconfig.node.json 10 0
A vite.config.ts 32 0
.gitignoreadded
@@ -0,0 +1,107 @@
1
+# Logs
2
+logs
3
+*.log
4
+npm-debug.log*
5
+yarn-debug.log*
6
+yarn-error.log*
7
+pnpm-debug.log*
8
+lerna-debug.log*
9
+
10
+# Dependencies
11
+node_modules
12
+dist
13
+dist-ssr
14
+*.local
15
+
16
+# Tauri build artifacts
17
+src-tauri/target/
18
+src-tauri/Cargo.lock
19
+src-tauri/gen/
20
+
21
+# Application bundles and installers
22
+*.dmg
23
+*.pkg
24
+*.msi
25
+*.exe
26
+*.deb
27
+*.rpm
28
+*.AppImage
29
+*.app
30
+bundle/
31
+
32
+# Runtime and development
33
+.tauri/
34
+WixTools/
35
+
36
+# Environment variables
37
+.env
38
+.env.local
39
+.env.development.local
40
+.env.test.local
41
+.env.production.local
42
+
43
+# Database files (if using local storage)
44
+*.db
45
+*.sqlite
46
+*.sqlite3
47
+storage/
48
+
49
+# Storage node data (volunteer storage)
50
+ZephyrFS-Storage/
51
+chunks/
52
+metadata/
53
+logs/
54
+config/
55
+
56
+# OS generated files
57
+.DS_Store
58
+.DS_Store?
59
+._*
60
+.Spotlight-V100
61
+.Trashes
62
+ehthumbs.db
63
+Thumbs.db
64
+desktop.ini
65
+
66
+# Editor directories and files
67
+.vscode/*
68
+!.vscode/extensions.json
69
+.idea/
70
+*.swp
71
+*.swo
72
+*~
73
+.vim/
74
+.emacs.d/
75
+*.suo
76
+*.ntvs*
77
+*.njsproj
78
+*.sln
79
+*.sw?
80
+
81
+# Rust artifacts
82
+**/*.rs.bk
83
+Cargo.lock
84
+target/
85
+
86
+# Temporary files
87
+*.tmp
88
+*.temp
89
+.cache/
90
+.parcel-cache/
91
+
92
+# Test coverage
93
+coverage/
94
+*.lcov
95
+.nyc_output/
96
+
97
+# Backup files
98
+*.bak
99
+*.backup
100
+*.orig
101
+
102
+# IDE files
103
+*.code-workspace
104
+.history/
105
+
106
+# Platform specific
107
+.platform/
.vscode/extensions.jsonadded
@@ -0,0 +1,7 @@
1
+{
2
+  "recommendations": [
3
+    "Vue.volar",
4
+    "tauri-apps.tauri-vscode",
5
+    "rust-lang.rust-analyzer"
6
+  ]
7
+}
README.mdadded
@@ -0,0 +1,7 @@
1
+# Tauri + Vue + TypeScript
2
+
3
+This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4
+
5
+## Recommended IDE Setup
6
+
7
+- [VS Code](https://code.visualstudio.com/) + [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
index.htmladded
@@ -0,0 +1,14 @@
1
+<!doctype html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="UTF-8" />
5
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+    <title>Tauri + Vue + Typescript App</title>
8
+  </head>
9
+
10
+  <body>
11
+    <div id="app"></div>
12
+    <script type="module" src="/src/main.ts"></script>
13
+  </body>
14
+</html>
package-lock.jsonadded
1679 lines changed — click to load
@@ -0,0 +1,1679 @@
1
+{
2
+  "name": "zephyrfs-desktop",
3
+  "version": "0.1.0",
4
+  "lockfileVersion": 3,
5
+  "requires": true,
6
+  "packages": {
7
+    "": {
8
+      "name": "zephyrfs-desktop",
9
+      "version": "0.1.0",
10
+      "dependencies": {
11
+        "@tauri-apps/api": "^2",
12
+        "@tauri-apps/plugin-opener": "^2",
13
+        "vue": "^3.5.13"
14
+      },
15
+      "devDependencies": {
16
+        "@tauri-apps/cli": "^2",
17
+        "@vitejs/plugin-vue": "^5.2.1",
18
+        "typescript": "~5.6.2",
19
+        "vite": "^6.0.3",
20
+        "vue-tsc": "^2.1.10"
21
+      }
22
+    },
23
+    "node_modules/@babel/helper-string-parser": {
24
+      "version": "7.27.1",
25
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
26
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
27
+      "license": "MIT",
28
+      "engines": {
29
+        "node": ">=6.9.0"
30
+      }
31
+    },
32
+    "node_modules/@babel/helper-validator-identifier": {
33
+      "version": "7.27.1",
34
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
35
+      "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
36
+      "license": "MIT",
37
+      "engines": {
38
+        "node": ">=6.9.0"
39
+      }
40
+    },
41
+    "node_modules/@babel/parser": {
42
+      "version": "7.28.4",
43
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
44
+      "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
45
+      "license": "MIT",
46
+      "dependencies": {
47
+        "@babel/types": "^7.28.4"
48
+      },
49
+      "bin": {
50
+        "parser": "bin/babel-parser.js"
51
+      },
52
+      "engines": {
53
+        "node": ">=6.0.0"
54
+      }
55
+    },
56
+    "node_modules/@babel/types": {
57
+      "version": "7.28.4",
58
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
59
+      "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
60
+      "license": "MIT",
61
+      "dependencies": {
62
+        "@babel/helper-string-parser": "^7.27.1",
63
+        "@babel/helper-validator-identifier": "^7.27.1"
64
+      },
65
+      "engines": {
66
+        "node": ">=6.9.0"
67
+      }
68
+    },
69
+    "node_modules/@esbuild/aix-ppc64": {
70
+      "version": "0.25.9",
71
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
72
+      "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
73
+      "cpu": [
74
+        "ppc64"
75
+      ],
76
+      "dev": true,
77
+      "license": "MIT",
78
+      "optional": true,
79
+      "os": [
80
+        "aix"
81
+      ],
82
+      "engines": {
83
+        "node": ">=18"
84
+      }
85
+    },
86
+    "node_modules/@esbuild/android-arm": {
87
+      "version": "0.25.9",
88
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
89
+      "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
90
+      "cpu": [
91
+        "arm"
92
+      ],
93
+      "dev": true,
94
+      "license": "MIT",
95
+      "optional": true,
96
+      "os": [
97
+        "android"
98
+      ],
99
+      "engines": {
100
+        "node": ">=18"
101
+      }
102
+    },
103
+    "node_modules/@esbuild/android-arm64": {
104
+      "version": "0.25.9",
105
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
106
+      "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
107
+      "cpu": [
108
+        "arm64"
109
+      ],
110
+      "dev": true,
111
+      "license": "MIT",
112
+      "optional": true,
113
+      "os": [
114
+        "android"
115
+      ],
116
+      "engines": {
117
+        "node": ">=18"
118
+      }
119
+    },
120
+    "node_modules/@esbuild/android-x64": {
121
+      "version": "0.25.9",
122
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
123
+      "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
124
+      "cpu": [
125
+        "x64"
126
+      ],
127
+      "dev": true,
128
+      "license": "MIT",
129
+      "optional": true,
130
+      "os": [
131
+        "android"
132
+      ],
133
+      "engines": {
134
+        "node": ">=18"
135
+      }
136
+    },
137
+    "node_modules/@esbuild/darwin-arm64": {
138
+      "version": "0.25.9",
139
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
140
+      "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
141
+      "cpu": [
142
+        "arm64"
143
+      ],
144
+      "dev": true,
145
+      "license": "MIT",
146
+      "optional": true,
147
+      "os": [
148
+        "darwin"
149
+      ],
150
+      "engines": {
151
+        "node": ">=18"
152
+      }
153
+    },
154
+    "node_modules/@esbuild/darwin-x64": {
155
+      "version": "0.25.9",
156
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
157
+      "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
158
+      "cpu": [
159
+        "x64"
160
+      ],
161
+      "dev": true,
162
+      "license": "MIT",
163
+      "optional": true,
164
+      "os": [
165
+        "darwin"
166
+      ],
167
+      "engines": {
168
+        "node": ">=18"
169
+      }
170
+    },
171
+    "node_modules/@esbuild/freebsd-arm64": {
172
+      "version": "0.25.9",
173
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
174
+      "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
175
+      "cpu": [
176
+        "arm64"
177
+      ],
178
+      "dev": true,
179
+      "license": "MIT",
180
+      "optional": true,
181
+      "os": [
182
+        "freebsd"
183
+      ],
184
+      "engines": {
185
+        "node": ">=18"
186
+      }
187
+    },
188
+    "node_modules/@esbuild/freebsd-x64": {
189
+      "version": "0.25.9",
190
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
191
+      "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
192
+      "cpu": [
193
+        "x64"
194
+      ],
195
+      "dev": true,
196
+      "license": "MIT",
197
+      "optional": true,
198
+      "os": [
199
+        "freebsd"
200
+      ],
201
+      "engines": {
202
+        "node": ">=18"
203
+      }
204
+    },
205
+    "node_modules/@esbuild/linux-arm": {
206
+      "version": "0.25.9",
207
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
208
+      "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
209
+      "cpu": [
210
+        "arm"
211
+      ],
212
+      "dev": true,
213
+      "license": "MIT",
214
+      "optional": true,
215
+      "os": [
216
+        "linux"
217
+      ],
218
+      "engines": {
219
+        "node": ">=18"
220
+      }
221
+    },
222
+    "node_modules/@esbuild/linux-arm64": {
223
+      "version": "0.25.9",
224
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
225
+      "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
226
+      "cpu": [
227
+        "arm64"
228
+      ],
229
+      "dev": true,
230
+      "license": "MIT",
231
+      "optional": true,
232
+      "os": [
233
+        "linux"
234
+      ],
235
+      "engines": {
236
+        "node": ">=18"
237
+      }
238
+    },
239
+    "node_modules/@esbuild/linux-ia32": {
240
+      "version": "0.25.9",
241
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
242
+      "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
243
+      "cpu": [
244
+        "ia32"
245
+      ],
246
+      "dev": true,
247
+      "license": "MIT",
248
+      "optional": true,
249
+      "os": [
250
+        "linux"
251
+      ],
252
+      "engines": {
253
+        "node": ">=18"
254
+      }
255
+    },
256
+    "node_modules/@esbuild/linux-loong64": {
257
+      "version": "0.25.9",
258
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
259
+      "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
260
+      "cpu": [
261
+        "loong64"
262
+      ],
263
+      "dev": true,
264
+      "license": "MIT",
265
+      "optional": true,
266
+      "os": [
267
+        "linux"
268
+      ],
269
+      "engines": {
270
+        "node": ">=18"
271
+      }
272
+    },
273
+    "node_modules/@esbuild/linux-mips64el": {
274
+      "version": "0.25.9",
275
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
276
+      "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
277
+      "cpu": [
278
+        "mips64el"
279
+      ],
280
+      "dev": true,
281
+      "license": "MIT",
282
+      "optional": true,
283
+      "os": [
284
+        "linux"
285
+      ],
286
+      "engines": {
287
+        "node": ">=18"
288
+      }
289
+    },
290
+    "node_modules/@esbuild/linux-ppc64": {
291
+      "version": "0.25.9",
292
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
293
+      "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
294
+      "cpu": [
295
+        "ppc64"
296
+      ],
297
+      "dev": true,
298
+      "license": "MIT",
299
+      "optional": true,
300
+      "os": [
301
+        "linux"
302
+      ],
303
+      "engines": {
304
+        "node": ">=18"
305
+      }
306
+    },
307
+    "node_modules/@esbuild/linux-riscv64": {
308
+      "version": "0.25.9",
309
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
310
+      "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
311
+      "cpu": [
312
+        "riscv64"
313
+      ],
314
+      "dev": true,
315
+      "license": "MIT",
316
+      "optional": true,
317
+      "os": [
318
+        "linux"
319
+      ],
320
+      "engines": {
321
+        "node": ">=18"
322
+      }
323
+    },
324
+    "node_modules/@esbuild/linux-s390x": {
325
+      "version": "0.25.9",
326
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
327
+      "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
328
+      "cpu": [
329
+        "s390x"
330
+      ],
331
+      "dev": true,
332
+      "license": "MIT",
333
+      "optional": true,
334
+      "os": [
335
+        "linux"
336
+      ],
337
+      "engines": {
338
+        "node": ">=18"
339
+      }
340
+    },
341
+    "node_modules/@esbuild/linux-x64": {
342
+      "version": "0.25.9",
343
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
344
+      "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
345
+      "cpu": [
346
+        "x64"
347
+      ],
348
+      "dev": true,
349
+      "license": "MIT",
350
+      "optional": true,
351
+      "os": [
352
+        "linux"
353
+      ],
354
+      "engines": {
355
+        "node": ">=18"
356
+      }
357
+    },
358
+    "node_modules/@esbuild/netbsd-arm64": {
359
+      "version": "0.25.9",
360
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
361
+      "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
362
+      "cpu": [
363
+        "arm64"
364
+      ],
365
+      "dev": true,
366
+      "license": "MIT",
367
+      "optional": true,
368
+      "os": [
369
+        "netbsd"
370
+      ],
371
+      "engines": {
372
+        "node": ">=18"
373
+      }
374
+    },
375
+    "node_modules/@esbuild/netbsd-x64": {
376
+      "version": "0.25.9",
377
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
378
+      "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
379
+      "cpu": [
380
+        "x64"
381
+      ],
382
+      "dev": true,
383
+      "license": "MIT",
384
+      "optional": true,
385
+      "os": [
386
+        "netbsd"
387
+      ],
388
+      "engines": {
389
+        "node": ">=18"
390
+      }
391
+    },
392
+    "node_modules/@esbuild/openbsd-arm64": {
393
+      "version": "0.25.9",
394
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
395
+      "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
396
+      "cpu": [
397
+        "arm64"
398
+      ],
399
+      "dev": true,
400
+      "license": "MIT",
401
+      "optional": true,
402
+      "os": [
403
+        "openbsd"
404
+      ],
405
+      "engines": {
406
+        "node": ">=18"
407
+      }
408
+    },
409
+    "node_modules/@esbuild/openbsd-x64": {
410
+      "version": "0.25.9",
411
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
412
+      "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
413
+      "cpu": [
414
+        "x64"
415
+      ],
416
+      "dev": true,
417
+      "license": "MIT",
418
+      "optional": true,
419
+      "os": [
420
+        "openbsd"
421
+      ],
422
+      "engines": {
423
+        "node": ">=18"
424
+      }
425
+    },
426
+    "node_modules/@esbuild/openharmony-arm64": {
427
+      "version": "0.25.9",
428
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
429
+      "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
430
+      "cpu": [
431
+        "arm64"
432
+      ],
433
+      "dev": true,
434
+      "license": "MIT",
435
+      "optional": true,
436
+      "os": [
437
+        "openharmony"
438
+      ],
439
+      "engines": {
440
+        "node": ">=18"
441
+      }
442
+    },
443
+    "node_modules/@esbuild/sunos-x64": {
444
+      "version": "0.25.9",
445
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
446
+      "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
447
+      "cpu": [
448
+        "x64"
449
+      ],
450
+      "dev": true,
451
+      "license": "MIT",
452
+      "optional": true,
453
+      "os": [
454
+        "sunos"
455
+      ],
456
+      "engines": {
457
+        "node": ">=18"
458
+      }
459
+    },
460
+    "node_modules/@esbuild/win32-arm64": {
461
+      "version": "0.25.9",
462
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
463
+      "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
464
+      "cpu": [
465
+        "arm64"
466
+      ],
467
+      "dev": true,
468
+      "license": "MIT",
469
+      "optional": true,
470
+      "os": [
471
+        "win32"
472
+      ],
473
+      "engines": {
474
+        "node": ">=18"
475
+      }
476
+    },
477
+    "node_modules/@esbuild/win32-ia32": {
478
+      "version": "0.25.9",
479
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
480
+      "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
481
+      "cpu": [
482
+        "ia32"
483
+      ],
484
+      "dev": true,
485
+      "license": "MIT",
486
+      "optional": true,
487
+      "os": [
488
+        "win32"
489
+      ],
490
+      "engines": {
491
+        "node": ">=18"
492
+      }
493
+    },
494
+    "node_modules/@esbuild/win32-x64": {
495
+      "version": "0.25.9",
496
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
497
+      "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
498
+      "cpu": [
499
+        "x64"
500
+      ],
501
+      "dev": true,
502
+      "license": "MIT",
503
+      "optional": true,
504
+      "os": [
505
+        "win32"
506
+      ],
507
+      "engines": {
508
+        "node": ">=18"
509
+      }
510
+    },
511
+    "node_modules/@jridgewell/sourcemap-codec": {
512
+      "version": "1.5.5",
513
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
514
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
515
+      "license": "MIT"
516
+    },
517
+    "node_modules/@rollup/rollup-android-arm-eabi": {
518
+      "version": "4.50.1",
519
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz",
520
+      "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==",
521
+      "cpu": [
522
+        "arm"
523
+      ],
524
+      "dev": true,
525
+      "license": "MIT",
526
+      "optional": true,
527
+      "os": [
528
+        "android"
529
+      ]
530
+    },
531
+    "node_modules/@rollup/rollup-android-arm64": {
532
+      "version": "4.50.1",
533
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz",
534
+      "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==",
535
+      "cpu": [
536
+        "arm64"
537
+      ],
538
+      "dev": true,
539
+      "license": "MIT",
540
+      "optional": true,
541
+      "os": [
542
+        "android"
543
+      ]
544
+    },
545
+    "node_modules/@rollup/rollup-darwin-arm64": {
546
+      "version": "4.50.1",
547
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz",
548
+      "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==",
549
+      "cpu": [
550
+        "arm64"
551
+      ],
552
+      "dev": true,
553
+      "license": "MIT",
554
+      "optional": true,
555
+      "os": [
556
+        "darwin"
557
+      ]
558
+    },
559
+    "node_modules/@rollup/rollup-darwin-x64": {
560
+      "version": "4.50.1",
561
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz",
562
+      "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==",
563
+      "cpu": [
564
+        "x64"
565
+      ],
566
+      "dev": true,
567
+      "license": "MIT",
568
+      "optional": true,
569
+      "os": [
570
+        "darwin"
571
+      ]
572
+    },
573
+    "node_modules/@rollup/rollup-freebsd-arm64": {
574
+      "version": "4.50.1",
575
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz",
576
+      "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==",
577
+      "cpu": [
578
+        "arm64"
579
+      ],
580
+      "dev": true,
581
+      "license": "MIT",
582
+      "optional": true,
583
+      "os": [
584
+        "freebsd"
585
+      ]
586
+    },
587
+    "node_modules/@rollup/rollup-freebsd-x64": {
588
+      "version": "4.50.1",
589
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz",
590
+      "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==",
591
+      "cpu": [
592
+        "x64"
593
+      ],
594
+      "dev": true,
595
+      "license": "MIT",
596
+      "optional": true,
597
+      "os": [
598
+        "freebsd"
599
+      ]
600
+    },
601
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
602
+      "version": "4.50.1",
603
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz",
604
+      "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==",
605
+      "cpu": [
606
+        "arm"
607
+      ],
608
+      "dev": true,
609
+      "license": "MIT",
610
+      "optional": true,
611
+      "os": [
612
+        "linux"
613
+      ]
614
+    },
615
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
616
+      "version": "4.50.1",
617
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz",
618
+      "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==",
619
+      "cpu": [
620
+        "arm"
621
+      ],
622
+      "dev": true,
623
+      "license": "MIT",
624
+      "optional": true,
625
+      "os": [
626
+        "linux"
627
+      ]
628
+    },
629
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
630
+      "version": "4.50.1",
631
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz",
632
+      "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==",
633
+      "cpu": [
634
+        "arm64"
635
+      ],
636
+      "dev": true,
637
+      "license": "MIT",
638
+      "optional": true,
639
+      "os": [
640
+        "linux"
641
+      ]
642
+    },
643
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
644
+      "version": "4.50.1",
645
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz",
646
+      "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==",
647
+      "cpu": [
648
+        "arm64"
649
+      ],
650
+      "dev": true,
651
+      "license": "MIT",
652
+      "optional": true,
653
+      "os": [
654
+        "linux"
655
+      ]
656
+    },
657
+    "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
658
+      "version": "4.50.1",
659
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz",
660
+      "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==",
661
+      "cpu": [
662
+        "loong64"
663
+      ],
664
+      "dev": true,
665
+      "license": "MIT",
666
+      "optional": true,
667
+      "os": [
668
+        "linux"
669
+      ]
670
+    },
671
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
672
+      "version": "4.50.1",
673
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz",
674
+      "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==",
675
+      "cpu": [
676
+        "ppc64"
677
+      ],
678
+      "dev": true,
679
+      "license": "MIT",
680
+      "optional": true,
681
+      "os": [
682
+        "linux"
683
+      ]
684
+    },
685
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
686
+      "version": "4.50.1",
687
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz",
688
+      "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==",
689
+      "cpu": [
690
+        "riscv64"
691
+      ],
692
+      "dev": true,
693
+      "license": "MIT",
694
+      "optional": true,
695
+      "os": [
696
+        "linux"
697
+      ]
698
+    },
699
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
700
+      "version": "4.50.1",
701
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz",
702
+      "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==",
703
+      "cpu": [
704
+        "riscv64"
705
+      ],
706
+      "dev": true,
707
+      "license": "MIT",
708
+      "optional": true,
709
+      "os": [
710
+        "linux"
711
+      ]
712
+    },
713
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
714
+      "version": "4.50.1",
715
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz",
716
+      "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==",
717
+      "cpu": [
718
+        "s390x"
719
+      ],
720
+      "dev": true,
721
+      "license": "MIT",
722
+      "optional": true,
723
+      "os": [
724
+        "linux"
725
+      ]
726
+    },
727
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
728
+      "version": "4.50.1",
729
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz",
730
+      "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==",
731
+      "cpu": [
732
+        "x64"
733
+      ],
734
+      "dev": true,
735
+      "license": "MIT",
736
+      "optional": true,
737
+      "os": [
738
+        "linux"
739
+      ]
740
+    },
741
+    "node_modules/@rollup/rollup-linux-x64-musl": {
742
+      "version": "4.50.1",
743
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz",
744
+      "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==",
745
+      "cpu": [
746
+        "x64"
747
+      ],
748
+      "dev": true,
749
+      "license": "MIT",
750
+      "optional": true,
751
+      "os": [
752
+        "linux"
753
+      ]
754
+    },
755
+    "node_modules/@rollup/rollup-openharmony-arm64": {
756
+      "version": "4.50.1",
757
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz",
758
+      "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==",
759
+      "cpu": [
760
+        "arm64"
761
+      ],
762
+      "dev": true,
763
+      "license": "MIT",
764
+      "optional": true,
765
+      "os": [
766
+        "openharmony"
767
+      ]
768
+    },
769
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
770
+      "version": "4.50.1",
771
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz",
772
+      "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==",
773
+      "cpu": [
774
+        "arm64"
775
+      ],
776
+      "dev": true,
777
+      "license": "MIT",
778
+      "optional": true,
779
+      "os": [
780
+        "win32"
781
+      ]
782
+    },
783
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
784
+      "version": "4.50.1",
785
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz",
786
+      "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==",
787
+      "cpu": [
788
+        "ia32"
789
+      ],
790
+      "dev": true,
791
+      "license": "MIT",
792
+      "optional": true,
793
+      "os": [
794
+        "win32"
795
+      ]
796
+    },
797
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
798
+      "version": "4.50.1",
799
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz",
800
+      "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==",
801
+      "cpu": [
802
+        "x64"
803
+      ],
804
+      "dev": true,
805
+      "license": "MIT",
806
+      "optional": true,
807
+      "os": [
808
+        "win32"
809
+      ]
810
+    },
811
+    "node_modules/@tauri-apps/api": {
812
+      "version": "2.8.0",
813
+      "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.8.0.tgz",
814
+      "integrity": "sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==",
815
+      "license": "Apache-2.0 OR MIT",
816
+      "funding": {
817
+        "type": "opencollective",
818
+        "url": "https://opencollective.com/tauri"
819
+      }
820
+    },
821
+    "node_modules/@tauri-apps/cli": {
822
+      "version": "2.8.4",
823
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.8.4.tgz",
824
+      "integrity": "sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g==",
825
+      "dev": true,
826
+      "license": "Apache-2.0 OR MIT",
827
+      "bin": {
828
+        "tauri": "tauri.js"
829
+      },
830
+      "engines": {
831
+        "node": ">= 10"
832
+      },
833
+      "funding": {
834
+        "type": "opencollective",
835
+        "url": "https://opencollective.com/tauri"
836
+      },
837
+      "optionalDependencies": {
838
+        "@tauri-apps/cli-darwin-arm64": "2.8.4",
839
+        "@tauri-apps/cli-darwin-x64": "2.8.4",
840
+        "@tauri-apps/cli-linux-arm-gnueabihf": "2.8.4",
841
+        "@tauri-apps/cli-linux-arm64-gnu": "2.8.4",
842
+        "@tauri-apps/cli-linux-arm64-musl": "2.8.4",
843
+        "@tauri-apps/cli-linux-riscv64-gnu": "2.8.4",
844
+        "@tauri-apps/cli-linux-x64-gnu": "2.8.4",
845
+        "@tauri-apps/cli-linux-x64-musl": "2.8.4",
846
+        "@tauri-apps/cli-win32-arm64-msvc": "2.8.4",
847
+        "@tauri-apps/cli-win32-ia32-msvc": "2.8.4",
848
+        "@tauri-apps/cli-win32-x64-msvc": "2.8.4"
849
+      }
850
+    },
851
+    "node_modules/@tauri-apps/cli-darwin-arm64": {
852
+      "version": "2.8.4",
853
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.8.4.tgz",
854
+      "integrity": "sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA==",
855
+      "cpu": [
856
+        "arm64"
857
+      ],
858
+      "dev": true,
859
+      "license": "Apache-2.0 OR MIT",
860
+      "optional": true,
861
+      "os": [
862
+        "darwin"
863
+      ],
864
+      "engines": {
865
+        "node": ">= 10"
866
+      }
867
+    },
868
+    "node_modules/@tauri-apps/cli-darwin-x64": {
869
+      "version": "2.8.4",
870
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.8.4.tgz",
871
+      "integrity": "sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g==",
872
+      "cpu": [
873
+        "x64"
874
+      ],
875
+      "dev": true,
876
+      "license": "Apache-2.0 OR MIT",
877
+      "optional": true,
878
+      "os": [
879
+        "darwin"
880
+      ],
881
+      "engines": {
882
+        "node": ">= 10"
883
+      }
884
+    },
885
+    "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
886
+      "version": "2.8.4",
887
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.8.4.tgz",
888
+      "integrity": "sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw==",
889
+      "cpu": [
890
+        "arm"
891
+      ],
892
+      "dev": true,
893
+      "license": "Apache-2.0 OR MIT",
894
+      "optional": true,
895
+      "os": [
896
+        "linux"
897
+      ],
898
+      "engines": {
899
+        "node": ">= 10"
900
+      }
901
+    },
902
+    "node_modules/@tauri-apps/cli-linux-arm64-gnu": {
903
+      "version": "2.8.4",
904
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.8.4.tgz",
905
+      "integrity": "sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw==",
906
+      "cpu": [
907
+        "arm64"
908
+      ],
909
+      "dev": true,
910
+      "license": "Apache-2.0 OR MIT",
911
+      "optional": true,
912
+      "os": [
913
+        "linux"
914
+      ],
915
+      "engines": {
916
+        "node": ">= 10"
917
+      }
918
+    },
919
+    "node_modules/@tauri-apps/cli-linux-arm64-musl": {
920
+      "version": "2.8.4",
921
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.8.4.tgz",
922
+      "integrity": "sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw==",
923
+      "cpu": [
924
+        "arm64"
925
+      ],
926
+      "dev": true,
927
+      "license": "Apache-2.0 OR MIT",
928
+      "optional": true,
929
+      "os": [
930
+        "linux"
931
+      ],
932
+      "engines": {
933
+        "node": ">= 10"
934
+      }
935
+    },
936
+    "node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
937
+      "version": "2.8.4",
938
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.8.4.tgz",
939
+      "integrity": "sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ==",
940
+      "cpu": [
941
+        "riscv64"
942
+      ],
943
+      "dev": true,
944
+      "license": "Apache-2.0 OR MIT",
945
+      "optional": true,
946
+      "os": [
947
+        "linux"
948
+      ],
949
+      "engines": {
950
+        "node": ">= 10"
951
+      }
952
+    },
953
+    "node_modules/@tauri-apps/cli-linux-x64-gnu": {
954
+      "version": "2.8.4",
955
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.8.4.tgz",
956
+      "integrity": "sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw==",
957
+      "cpu": [
958
+        "x64"
959
+      ],
960
+      "dev": true,
961
+      "license": "Apache-2.0 OR MIT",
962
+      "optional": true,
963
+      "os": [
964
+        "linux"
965
+      ],
966
+      "engines": {
967
+        "node": ">= 10"
968
+      }
969
+    },
970
+    "node_modules/@tauri-apps/cli-linux-x64-musl": {
971
+      "version": "2.8.4",
972
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.8.4.tgz",
973
+      "integrity": "sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA==",
974
+      "cpu": [
975
+        "x64"
976
+      ],
977
+      "dev": true,
978
+      "license": "Apache-2.0 OR MIT",
979
+      "optional": true,
980
+      "os": [
981
+        "linux"
982
+      ],
983
+      "engines": {
984
+        "node": ">= 10"
985
+      }
986
+    },
987
+    "node_modules/@tauri-apps/cli-win32-arm64-msvc": {
988
+      "version": "2.8.4",
989
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.8.4.tgz",
990
+      "integrity": "sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w==",
991
+      "cpu": [
992
+        "arm64"
993
+      ],
994
+      "dev": true,
995
+      "license": "Apache-2.0 OR MIT",
996
+      "optional": true,
997
+      "os": [
998
+        "win32"
999
+      ],
1000
+      "engines": {
1001
+        "node": ">= 10"
1002
+      }
1003
+    },
1004
+    "node_modules/@tauri-apps/cli-win32-ia32-msvc": {
1005
+      "version": "2.8.4",
1006
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.8.4.tgz",
1007
+      "integrity": "sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA==",
1008
+      "cpu": [
1009
+        "ia32"
1010
+      ],
1011
+      "dev": true,
1012
+      "license": "Apache-2.0 OR MIT",
1013
+      "optional": true,
1014
+      "os": [
1015
+        "win32"
1016
+      ],
1017
+      "engines": {
1018
+        "node": ">= 10"
1019
+      }
1020
+    },
1021
+    "node_modules/@tauri-apps/cli-win32-x64-msvc": {
1022
+      "version": "2.8.4",
1023
+      "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.8.4.tgz",
1024
+      "integrity": "sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA==",
1025
+      "cpu": [
1026
+        "x64"
1027
+      ],
1028
+      "dev": true,
1029
+      "license": "Apache-2.0 OR MIT",
1030
+      "optional": true,
1031
+      "os": [
1032
+        "win32"
1033
+      ],
1034
+      "engines": {
1035
+        "node": ">= 10"
1036
+      }
1037
+    },
1038
+    "node_modules/@tauri-apps/plugin-opener": {
1039
+      "version": "2.5.0",
1040
+      "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.0.tgz",
1041
+      "integrity": "sha512-B0LShOYae4CZjN8leiNDbnfjSrTwoZakqKaWpfoH6nXiJwt6Rgj6RnVIffG3DoJiKsffRhMkjmBV9VeilSb4TA==",
1042
+      "license": "MIT OR Apache-2.0",
1043
+      "dependencies": {
1044
+        "@tauri-apps/api": "^2.8.0"
1045
+      }
1046
+    },
1047
+    "node_modules/@types/estree": {
1048
+      "version": "1.0.8",
1049
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
1050
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
1051
+      "dev": true,
1052
+      "license": "MIT"
1053
+    },
1054
+    "node_modules/@vitejs/plugin-vue": {
1055
+      "version": "5.2.4",
1056
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
1057
+      "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
1058
+      "dev": true,
1059
+      "license": "MIT",
1060
+      "engines": {
1061
+        "node": "^18.0.0 || >=20.0.0"
1062
+      },
1063
+      "peerDependencies": {
1064
+        "vite": "^5.0.0 || ^6.0.0",
1065
+        "vue": "^3.2.25"
1066
+      }
1067
+    },
1068
+    "node_modules/@volar/language-core": {
1069
+      "version": "2.4.15",
1070
+      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz",
1071
+      "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==",
1072
+      "dev": true,
1073
+      "license": "MIT",
1074
+      "dependencies": {
1075
+        "@volar/source-map": "2.4.15"
1076
+      }
1077
+    },
1078
+    "node_modules/@volar/source-map": {
1079
+      "version": "2.4.15",
1080
+      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz",
1081
+      "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==",
1082
+      "dev": true,
1083
+      "license": "MIT"
1084
+    },
1085
+    "node_modules/@volar/typescript": {
1086
+      "version": "2.4.15",
1087
+      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz",
1088
+      "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==",
1089
+      "dev": true,
1090
+      "license": "MIT",
1091
+      "dependencies": {
1092
+        "@volar/language-core": "2.4.15",
1093
+        "path-browserify": "^1.0.1",
1094
+        "vscode-uri": "^3.0.8"
1095
+      }
1096
+    },
1097
+    "node_modules/@vue/compiler-core": {
1098
+      "version": "3.5.21",
1099
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
1100
+      "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
1101
+      "license": "MIT",
1102
+      "dependencies": {
1103
+        "@babel/parser": "^7.28.3",
1104
+        "@vue/shared": "3.5.21",
1105
+        "entities": "^4.5.0",
1106
+        "estree-walker": "^2.0.2",
1107
+        "source-map-js": "^1.2.1"
1108
+      }
1109
+    },
1110
+    "node_modules/@vue/compiler-dom": {
1111
+      "version": "3.5.21",
1112
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
1113
+      "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
1114
+      "license": "MIT",
1115
+      "dependencies": {
1116
+        "@vue/compiler-core": "3.5.21",
1117
+        "@vue/shared": "3.5.21"
1118
+      }
1119
+    },
1120
+    "node_modules/@vue/compiler-sfc": {
1121
+      "version": "3.5.21",
1122
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
1123
+      "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
1124
+      "license": "MIT",
1125
+      "dependencies": {
1126
+        "@babel/parser": "^7.28.3",
1127
+        "@vue/compiler-core": "3.5.21",
1128
+        "@vue/compiler-dom": "3.5.21",
1129
+        "@vue/compiler-ssr": "3.5.21",
1130
+        "@vue/shared": "3.5.21",
1131
+        "estree-walker": "^2.0.2",
1132
+        "magic-string": "^0.30.18",
1133
+        "postcss": "^8.5.6",
1134
+        "source-map-js": "^1.2.1"
1135
+      }
1136
+    },
1137
+    "node_modules/@vue/compiler-ssr": {
1138
+      "version": "3.5.21",
1139
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
1140
+      "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
1141
+      "license": "MIT",
1142
+      "dependencies": {
1143
+        "@vue/compiler-dom": "3.5.21",
1144
+        "@vue/shared": "3.5.21"
1145
+      }
1146
+    },
1147
+    "node_modules/@vue/compiler-vue2": {
1148
+      "version": "2.7.16",
1149
+      "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
1150
+      "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
1151
+      "dev": true,
1152
+      "license": "MIT",
1153
+      "dependencies": {
1154
+        "de-indent": "^1.0.2",
1155
+        "he": "^1.2.0"
1156
+      }
1157
+    },
1158
+    "node_modules/@vue/language-core": {
1159
+      "version": "2.2.12",
1160
+      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz",
1161
+      "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==",
1162
+      "dev": true,
1163
+      "license": "MIT",
1164
+      "dependencies": {
1165
+        "@volar/language-core": "2.4.15",
1166
+        "@vue/compiler-dom": "^3.5.0",
1167
+        "@vue/compiler-vue2": "^2.7.16",
1168
+        "@vue/shared": "^3.5.0",
1169
+        "alien-signals": "^1.0.3",
1170
+        "minimatch": "^9.0.3",
1171
+        "muggle-string": "^0.4.1",
1172
+        "path-browserify": "^1.0.1"
1173
+      },
1174
+      "peerDependencies": {
1175
+        "typescript": "*"
1176
+      },
1177
+      "peerDependenciesMeta": {
1178
+        "typescript": {
1179
+          "optional": true
1180
+        }
1181
+      }
1182
+    },
1183
+    "node_modules/@vue/reactivity": {
1184
+      "version": "3.5.21",
1185
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz",
1186
+      "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
1187
+      "license": "MIT",
1188
+      "dependencies": {
1189
+        "@vue/shared": "3.5.21"
1190
+      }
1191
+    },
1192
+    "node_modules/@vue/runtime-core": {
1193
+      "version": "3.5.21",
1194
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
1195
+      "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
1196
+      "license": "MIT",
1197
+      "dependencies": {
1198
+        "@vue/reactivity": "3.5.21",
1199
+        "@vue/shared": "3.5.21"
1200
+      }
1201
+    },
1202
+    "node_modules/@vue/runtime-dom": {
1203
+      "version": "3.5.21",
1204
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
1205
+      "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
1206
+      "license": "MIT",
1207
+      "dependencies": {
1208
+        "@vue/reactivity": "3.5.21",
1209
+        "@vue/runtime-core": "3.5.21",
1210
+        "@vue/shared": "3.5.21",
1211
+        "csstype": "^3.1.3"
1212
+      }
1213
+    },
1214
+    "node_modules/@vue/server-renderer": {
1215
+      "version": "3.5.21",
1216
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
1217
+      "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
1218
+      "license": "MIT",
1219
+      "dependencies": {
1220
+        "@vue/compiler-ssr": "3.5.21",
1221
+        "@vue/shared": "3.5.21"
1222
+      },
1223
+      "peerDependencies": {
1224
+        "vue": "3.5.21"
1225
+      }
1226
+    },
1227
+    "node_modules/@vue/shared": {
1228
+      "version": "3.5.21",
1229
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz",
1230
+      "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==",
1231
+      "license": "MIT"
1232
+    },
1233
+    "node_modules/alien-signals": {
1234
+      "version": "1.0.13",
1235
+      "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz",
1236
+      "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==",
1237
+      "dev": true,
1238
+      "license": "MIT"
1239
+    },
1240
+    "node_modules/balanced-match": {
1241
+      "version": "1.0.2",
1242
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
1243
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
1244
+      "dev": true,
1245
+      "license": "MIT"
1246
+    },
1247
+    "node_modules/brace-expansion": {
1248
+      "version": "2.0.2",
1249
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
1250
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
1251
+      "dev": true,
1252
+      "license": "MIT",
1253
+      "dependencies": {
1254
+        "balanced-match": "^1.0.0"
1255
+      }
1256
+    },
1257
+    "node_modules/csstype": {
1258
+      "version": "3.1.3",
1259
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
1260
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
1261
+      "license": "MIT"
1262
+    },
1263
+    "node_modules/de-indent": {
1264
+      "version": "1.0.2",
1265
+      "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
1266
+      "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
1267
+      "dev": true,
1268
+      "license": "MIT"
1269
+    },
1270
+    "node_modules/entities": {
1271
+      "version": "4.5.0",
1272
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
1273
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
1274
+      "license": "BSD-2-Clause",
1275
+      "engines": {
1276
+        "node": ">=0.12"
1277
+      },
1278
+      "funding": {
1279
+        "url": "https://github.com/fb55/entities?sponsor=1"
1280
+      }
1281
+    },
1282
+    "node_modules/esbuild": {
1283
+      "version": "0.25.9",
1284
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
1285
+      "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
1286
+      "dev": true,
1287
+      "hasInstallScript": true,
1288
+      "license": "MIT",
1289
+      "bin": {
1290
+        "esbuild": "bin/esbuild"
1291
+      },
1292
+      "engines": {
1293
+        "node": ">=18"
1294
+      },
1295
+      "optionalDependencies": {
1296
+        "@esbuild/aix-ppc64": "0.25.9",
1297
+        "@esbuild/android-arm": "0.25.9",
1298
+        "@esbuild/android-arm64": "0.25.9",
1299
+        "@esbuild/android-x64": "0.25.9",
1300
+        "@esbuild/darwin-arm64": "0.25.9",
1301
+        "@esbuild/darwin-x64": "0.25.9",
1302
+        "@esbuild/freebsd-arm64": "0.25.9",
1303
+        "@esbuild/freebsd-x64": "0.25.9",
1304
+        "@esbuild/linux-arm": "0.25.9",
1305
+        "@esbuild/linux-arm64": "0.25.9",
1306
+        "@esbuild/linux-ia32": "0.25.9",
1307
+        "@esbuild/linux-loong64": "0.25.9",
1308
+        "@esbuild/linux-mips64el": "0.25.9",
1309
+        "@esbuild/linux-ppc64": "0.25.9",
1310
+        "@esbuild/linux-riscv64": "0.25.9",
1311
+        "@esbuild/linux-s390x": "0.25.9",
1312
+        "@esbuild/linux-x64": "0.25.9",
1313
+        "@esbuild/netbsd-arm64": "0.25.9",
1314
+        "@esbuild/netbsd-x64": "0.25.9",
1315
+        "@esbuild/openbsd-arm64": "0.25.9",
1316
+        "@esbuild/openbsd-x64": "0.25.9",
1317
+        "@esbuild/openharmony-arm64": "0.25.9",
1318
+        "@esbuild/sunos-x64": "0.25.9",
1319
+        "@esbuild/win32-arm64": "0.25.9",
1320
+        "@esbuild/win32-ia32": "0.25.9",
1321
+        "@esbuild/win32-x64": "0.25.9"
1322
+      }
1323
+    },
1324
+    "node_modules/estree-walker": {
1325
+      "version": "2.0.2",
1326
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
1327
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
1328
+      "license": "MIT"
1329
+    },
1330
+    "node_modules/fdir": {
1331
+      "version": "6.5.0",
1332
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
1333
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1334
+      "dev": true,
1335
+      "license": "MIT",
1336
+      "engines": {
1337
+        "node": ">=12.0.0"
1338
+      },
1339
+      "peerDependencies": {
1340
+        "picomatch": "^3 || ^4"
1341
+      },
1342
+      "peerDependenciesMeta": {
1343
+        "picomatch": {
1344
+          "optional": true
1345
+        }
1346
+      }
1347
+    },
1348
+    "node_modules/fsevents": {
1349
+      "version": "2.3.3",
1350
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1351
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1352
+      "dev": true,
1353
+      "hasInstallScript": true,
1354
+      "license": "MIT",
1355
+      "optional": true,
1356
+      "os": [
1357
+        "darwin"
1358
+      ],
1359
+      "engines": {
1360
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1361
+      }
1362
+    },
1363
+    "node_modules/he": {
1364
+      "version": "1.2.0",
1365
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
1366
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
1367
+      "dev": true,
1368
+      "license": "MIT",
1369
+      "bin": {
1370
+        "he": "bin/he"
1371
+      }
1372
+    },
1373
+    "node_modules/magic-string": {
1374
+      "version": "0.30.19",
1375
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
1376
+      "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
1377
+      "license": "MIT",
1378
+      "dependencies": {
1379
+        "@jridgewell/sourcemap-codec": "^1.5.5"
1380
+      }
1381
+    },
1382
+    "node_modules/minimatch": {
1383
+      "version": "9.0.5",
1384
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
1385
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
1386
+      "dev": true,
1387
+      "license": "ISC",
1388
+      "dependencies": {
1389
+        "brace-expansion": "^2.0.1"
1390
+      },
1391
+      "engines": {
1392
+        "node": ">=16 || 14 >=14.17"
1393
+      },
1394
+      "funding": {
1395
+        "url": "https://github.com/sponsors/isaacs"
1396
+      }
1397
+    },
1398
+    "node_modules/muggle-string": {
1399
+      "version": "0.4.1",
1400
+      "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
1401
+      "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
1402
+      "dev": true,
1403
+      "license": "MIT"
1404
+    },
1405
+    "node_modules/nanoid": {
1406
+      "version": "3.3.11",
1407
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
1408
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1409
+      "funding": [
1410
+        {
1411
+          "type": "github",
1412
+          "url": "https://github.com/sponsors/ai"
1413
+        }
1414
+      ],
1415
+      "license": "MIT",
1416
+      "bin": {
1417
+        "nanoid": "bin/nanoid.cjs"
1418
+      },
1419
+      "engines": {
1420
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1421
+      }
1422
+    },
1423
+    "node_modules/path-browserify": {
1424
+      "version": "1.0.1",
1425
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
1426
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
1427
+      "dev": true,
1428
+      "license": "MIT"
1429
+    },
1430
+    "node_modules/picocolors": {
1431
+      "version": "1.1.1",
1432
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1433
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1434
+      "license": "ISC"
1435
+    },
1436
+    "node_modules/picomatch": {
1437
+      "version": "4.0.3",
1438
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
1439
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
1440
+      "dev": true,
1441
+      "license": "MIT",
1442
+      "engines": {
1443
+        "node": ">=12"
1444
+      },
1445
+      "funding": {
1446
+        "url": "https://github.com/sponsors/jonschlinkert"
1447
+      }
1448
+    },
1449
+    "node_modules/postcss": {
1450
+      "version": "8.5.6",
1451
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
1452
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
1453
+      "funding": [
1454
+        {
1455
+          "type": "opencollective",
1456
+          "url": "https://opencollective.com/postcss/"
1457
+        },
1458
+        {
1459
+          "type": "tidelift",
1460
+          "url": "https://tidelift.com/funding/github/npm/postcss"
1461
+        },
1462
+        {
1463
+          "type": "github",
1464
+          "url": "https://github.com/sponsors/ai"
1465
+        }
1466
+      ],
1467
+      "license": "MIT",
1468
+      "dependencies": {
1469
+        "nanoid": "^3.3.11",
1470
+        "picocolors": "^1.1.1",
1471
+        "source-map-js": "^1.2.1"
1472
+      },
1473
+      "engines": {
1474
+        "node": "^10 || ^12 || >=14"
1475
+      }
1476
+    },
1477
+    "node_modules/rollup": {
1478
+      "version": "4.50.1",
1479
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz",
1480
+      "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==",
1481
+      "dev": true,
1482
+      "license": "MIT",
1483
+      "dependencies": {
1484
+        "@types/estree": "1.0.8"
1485
+      },
1486
+      "bin": {
1487
+        "rollup": "dist/bin/rollup"
1488
+      },
1489
+      "engines": {
1490
+        "node": ">=18.0.0",
1491
+        "npm": ">=8.0.0"
1492
+      },
1493
+      "optionalDependencies": {
1494
+        "@rollup/rollup-android-arm-eabi": "4.50.1",
1495
+        "@rollup/rollup-android-arm64": "4.50.1",
1496
+        "@rollup/rollup-darwin-arm64": "4.50.1",
1497
+        "@rollup/rollup-darwin-x64": "4.50.1",
1498
+        "@rollup/rollup-freebsd-arm64": "4.50.1",
1499
+        "@rollup/rollup-freebsd-x64": "4.50.1",
1500
+        "@rollup/rollup-linux-arm-gnueabihf": "4.50.1",
1501
+        "@rollup/rollup-linux-arm-musleabihf": "4.50.1",
1502
+        "@rollup/rollup-linux-arm64-gnu": "4.50.1",
1503
+        "@rollup/rollup-linux-arm64-musl": "4.50.1",
1504
+        "@rollup/rollup-linux-loongarch64-gnu": "4.50.1",
1505
+        "@rollup/rollup-linux-ppc64-gnu": "4.50.1",
1506
+        "@rollup/rollup-linux-riscv64-gnu": "4.50.1",
1507
+        "@rollup/rollup-linux-riscv64-musl": "4.50.1",
1508
+        "@rollup/rollup-linux-s390x-gnu": "4.50.1",
1509
+        "@rollup/rollup-linux-x64-gnu": "4.50.1",
1510
+        "@rollup/rollup-linux-x64-musl": "4.50.1",
1511
+        "@rollup/rollup-openharmony-arm64": "4.50.1",
1512
+        "@rollup/rollup-win32-arm64-msvc": "4.50.1",
1513
+        "@rollup/rollup-win32-ia32-msvc": "4.50.1",
1514
+        "@rollup/rollup-win32-x64-msvc": "4.50.1",
1515
+        "fsevents": "~2.3.2"
1516
+      }
1517
+    },
1518
+    "node_modules/source-map-js": {
1519
+      "version": "1.2.1",
1520
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1521
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1522
+      "license": "BSD-3-Clause",
1523
+      "engines": {
1524
+        "node": ">=0.10.0"
1525
+      }
1526
+    },
1527
+    "node_modules/tinyglobby": {
1528
+      "version": "0.2.15",
1529
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
1530
+      "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
1531
+      "dev": true,
1532
+      "license": "MIT",
1533
+      "dependencies": {
1534
+        "fdir": "^6.5.0",
1535
+        "picomatch": "^4.0.3"
1536
+      },
1537
+      "engines": {
1538
+        "node": ">=12.0.0"
1539
+      },
1540
+      "funding": {
1541
+        "url": "https://github.com/sponsors/SuperchupuDev"
1542
+      }
1543
+    },
1544
+    "node_modules/typescript": {
1545
+      "version": "5.6.3",
1546
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
1547
+      "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
1548
+      "devOptional": true,
1549
+      "license": "Apache-2.0",
1550
+      "bin": {
1551
+        "tsc": "bin/tsc",
1552
+        "tsserver": "bin/tsserver"
1553
+      },
1554
+      "engines": {
1555
+        "node": ">=14.17"
1556
+      }
1557
+    },
1558
+    "node_modules/vite": {
1559
+      "version": "6.3.6",
1560
+      "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz",
1561
+      "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==",
1562
+      "dev": true,
1563
+      "license": "MIT",
1564
+      "dependencies": {
1565
+        "esbuild": "^0.25.0",
1566
+        "fdir": "^6.4.4",
1567
+        "picomatch": "^4.0.2",
1568
+        "postcss": "^8.5.3",
1569
+        "rollup": "^4.34.9",
1570
+        "tinyglobby": "^0.2.13"
1571
+      },
1572
+      "bin": {
1573
+        "vite": "bin/vite.js"
1574
+      },
1575
+      "engines": {
1576
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
1577
+      },
1578
+      "funding": {
1579
+        "url": "https://github.com/vitejs/vite?sponsor=1"
1580
+      },
1581
+      "optionalDependencies": {
1582
+        "fsevents": "~2.3.3"
1583
+      },
1584
+      "peerDependencies": {
1585
+        "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
1586
+        "jiti": ">=1.21.0",
1587
+        "less": "*",
1588
+        "lightningcss": "^1.21.0",
1589
+        "sass": "*",
1590
+        "sass-embedded": "*",
1591
+        "stylus": "*",
1592
+        "sugarss": "*",
1593
+        "terser": "^5.16.0",
1594
+        "tsx": "^4.8.1",
1595
+        "yaml": "^2.4.2"
1596
+      },
1597
+      "peerDependenciesMeta": {
1598
+        "@types/node": {
1599
+          "optional": true
1600
+        },
1601
+        "jiti": {
1602
+          "optional": true
1603
+        },
1604
+        "less": {
1605
+          "optional": true
1606
+        },
1607
+        "lightningcss": {
1608
+          "optional": true
1609
+        },
1610
+        "sass": {
1611
+          "optional": true
1612
+        },
1613
+        "sass-embedded": {
1614
+          "optional": true
1615
+        },
1616
+        "stylus": {
1617
+          "optional": true
1618
+        },
1619
+        "sugarss": {
1620
+          "optional": true
1621
+        },
1622
+        "terser": {
1623
+          "optional": true
1624
+        },
1625
+        "tsx": {
1626
+          "optional": true
1627
+        },
1628
+        "yaml": {
1629
+          "optional": true
1630
+        }
1631
+      }
1632
+    },
1633
+    "node_modules/vscode-uri": {
1634
+      "version": "3.1.0",
1635
+      "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
1636
+      "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
1637
+      "dev": true,
1638
+      "license": "MIT"
1639
+    },
1640
+    "node_modules/vue": {
1641
+      "version": "3.5.21",
1642
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
1643
+      "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
1644
+      "license": "MIT",
1645
+      "dependencies": {
1646
+        "@vue/compiler-dom": "3.5.21",
1647
+        "@vue/compiler-sfc": "3.5.21",
1648
+        "@vue/runtime-dom": "3.5.21",
1649
+        "@vue/server-renderer": "3.5.21",
1650
+        "@vue/shared": "3.5.21"
1651
+      },
1652
+      "peerDependencies": {
1653
+        "typescript": "*"
1654
+      },
1655
+      "peerDependenciesMeta": {
1656
+        "typescript": {
1657
+          "optional": true
1658
+        }
1659
+      }
1660
+    },
1661
+    "node_modules/vue-tsc": {
1662
+      "version": "2.2.12",
1663
+      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz",
1664
+      "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==",
1665
+      "dev": true,
1666
+      "license": "MIT",
1667
+      "dependencies": {
1668
+        "@volar/typescript": "2.4.15",
1669
+        "@vue/language-core": "2.2.12"
1670
+      },
1671
+      "bin": {
1672
+        "vue-tsc": "bin/vue-tsc.js"
1673
+      },
1674
+      "peerDependencies": {
1675
+        "typescript": ">=5.0.0"
1676
+      }
1677
+    }
1678
+  }
1679
+}
package.jsonadded
@@ -0,0 +1,24 @@
1
+{
2
+  "name": "zephyrfs-desktop",
3
+  "private": true,
4
+  "version": "0.1.0",
5
+  "type": "module",
6
+  "scripts": {
7
+    "dev": "vite",
8
+    "build": "vue-tsc --noEmit && vite build",
9
+    "preview": "vite preview",
10
+    "tauri": "tauri"
11
+  },
12
+  "dependencies": {
13
+    "vue": "^3.5.13",
14
+    "@tauri-apps/api": "^2",
15
+    "@tauri-apps/plugin-opener": "^2"
16
+  },
17
+  "devDependencies": {
18
+    "@vitejs/plugin-vue": "^5.2.1",
19
+    "typescript": "~5.6.2",
20
+    "vite": "^6.0.3",
21
+    "vue-tsc": "^2.1.10",
22
+    "@tauri-apps/cli": "^2"
23
+  }
24
+}
public/tauri.svgadded
@@ -0,0 +1,6 @@
1
+<svg width="206" height="231" viewBox="0 0 206 231" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+<path d="M143.143 84C143.143 96.1503 133.293 106 121.143 106C108.992 106 99.1426 96.1503 99.1426 84C99.1426 71.8497 108.992 62 121.143 62C133.293 62 143.143 71.8497 143.143 84Z" fill="#FFC131"/>
3
+<ellipse cx="84.1426" cy="147" rx="22" ry="22" transform="rotate(180 84.1426 147)" fill="#24C8DB"/>
4
+<path fill-rule="evenodd" clip-rule="evenodd" d="M166.738 154.548C157.86 160.286 148.023 164.269 137.757 166.341C139.858 160.282 141 153.774 141 147C141 144.543 140.85 142.121 140.558 139.743C144.975 138.204 149.215 136.139 153.183 133.575C162.73 127.404 170.292 118.608 174.961 108.244C179.63 97.8797 181.207 86.3876 179.502 75.1487C177.798 63.9098 172.884 53.4021 165.352 44.8883C157.82 36.3744 147.99 30.2165 137.042 27.1546C126.095 24.0926 114.496 24.2568 103.64 27.6274C92.7839 30.998 83.1319 37.4317 75.8437 46.1553C74.9102 47.2727 74.0206 48.4216 73.176 49.5993C61.9292 50.8488 51.0363 54.0318 40.9629 58.9556C44.2417 48.4586 49.5653 38.6591 56.679 30.1442C67.0505 17.7298 80.7861 8.57426 96.2354 3.77762C111.685 -1.01901 128.19 -1.25267 143.769 3.10474C159.348 7.46215 173.337 16.2252 184.056 28.3411C194.775 40.457 201.767 55.4101 204.193 71.404C206.619 87.3978 204.374 103.752 197.73 118.501C191.086 133.25 180.324 145.767 166.738 154.548ZM41.9631 74.275L62.5557 76.8042C63.0459 72.813 63.9401 68.9018 65.2138 65.1274C57.0465 67.0016 49.2088 70.087 41.9631 74.275Z" fill="#FFC131"/>
5
+<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4045 76.4519C47.3493 70.6709 57.2677 66.6712 67.6171 64.6132C65.2774 70.9669 64 77.8343 64 85.0001C64 87.1434 64.1143 89.26 64.3371 91.3442C60.0093 92.8732 55.8533 94.9092 51.9599 97.4256C42.4128 103.596 34.8505 112.392 30.1816 122.756C25.5126 133.12 23.9357 144.612 25.6403 155.851C27.3449 167.09 32.2584 177.598 39.7906 186.112C47.3227 194.626 57.153 200.784 68.1003 203.846C79.0476 206.907 90.6462 206.743 101.502 203.373C112.359 200.002 122.011 193.568 129.299 184.845C130.237 183.722 131.131 182.567 131.979 181.383C143.235 180.114 154.132 176.91 164.205 171.962C160.929 182.49 155.596 192.319 148.464 200.856C138.092 213.27 124.357 222.426 108.907 227.222C93.458 232.019 76.9524 232.253 61.3736 227.895C45.7948 223.538 31.8055 214.775 21.0867 202.659C10.3679 190.543 3.37557 175.59 0.949823 159.596C-1.47592 143.602 0.768139 127.248 7.41237 112.499C14.0566 97.7497 24.8183 85.2327 38.4045 76.4519ZM163.062 156.711L163.062 156.711C162.954 156.773 162.846 156.835 162.738 156.897C162.846 156.835 162.954 156.773 163.062 156.711Z" fill="#24C8DB"/>
6
+</svg>
public/vite.svgadded
@@ -0,0 +1,1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
src-tauri/.gitignoreadded
@@ -0,0 +1,7 @@
1
+# Generated by Cargo
2
+# will have compiled files and executables
3
+/target/
4
+
5
+# Generated by Tauri
6
+# will have schema files for capabilities auto-completion
7
+/gen/schemas
src-tauri/Cargo.tomladded
@@ -0,0 +1,37 @@
1
+[package]
2
+name = "zephyrfs-desktop"
3
+version = "0.1.0"
4
+description = "A Tauri App"
5
+authors = ["you"]
6
+edition = "2021"
7
+
8
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9
+
10
+[lib]
11
+# The `_lib` suffix may seem redundant but it is necessary
12
+# to make the lib name unique and wouldn't conflict with the bin name.
13
+# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
14
+name = "zephyrfs_desktop_lib"
15
+crate-type = ["staticlib", "cdylib", "rlib"]
16
+
17
+[build-dependencies]
18
+tauri-build = { version = "2", features = [] }
19
+
20
+[dependencies]
21
+tauri = { version = "2", features = [] }
22
+tauri-plugin-opener = "2"
23
+serde = { version = "1", features = ["derive"] }
24
+serde_json = "1"
25
+tokio = { version = "1.0", features = ["full"] }
26
+anyhow = "1.0"
27
+uuid = { version = "1.6", features = ["v4"] }
28
+
29
+# File system operations
30
+dirs = "5.0"            # User directories
31
+sysinfo = "0.30"        # System information
32
+fs2 = "0.4"             # Disk usage monitoring
33
+chrono = { version = "0.4", features = ["serde"] }  # Date/time formatting
34
+
35
+# ZephyrFS integration (local path dependency)
36
+# zephyrfs-node = { path = "../../zephyrfs-node" }  # Enable after Phase 4.1
37
+
src-tauri/build.rsadded
@@ -0,0 +1,3 @@
1
+fn main() {
2
+    tauri_build::build()
3
+}
src-tauri/capabilities/default.jsonadded
@@ -0,0 +1,10 @@
1
+{
2
+  "$schema": "../gen/schemas/desktop-schema.json",
3
+  "identifier": "default",
4
+  "description": "Capability for the main window",
5
+  "windows": ["main"],
6
+  "permissions": [
7
+    "core:default",
8
+    "opener:default"
9
+  ]
10
+}
src-tauri/icons/128x128.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/128x128@2x.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/32x32.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square107x107Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square142x142Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square150x150Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square284x284Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square30x30Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square310x310Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square44x44Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square71x71Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/Square89x89Logo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/StoreLogo.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/icons/icon.icnsadded
Binary file changed.
src-tauri/icons/icon.icoadded
Binary file changed.
src-tauri/icons/icon.pngadded
Image file changed (preview rendering wires once /raw URLs are threaded into the diff renderer).
src-tauri/src/folder_manager.rsadded
@@ -0,0 +1,339 @@
1
+use anyhow::{Context, Result};
2
+use dirs;
3
+use serde::{Deserialize, Serialize};
4
+use std::path::PathBuf;
5
+use sysinfo::{System, SystemExt, DiskExt};
6
+use tauri::command;
7
+use fs2::free_space;
8
+
9
+/// Safe storage configuration for volunteers
10
+#[derive(Debug, Clone, Serialize, Deserialize)]
11
+pub struct SafeStorageConfig {
12
+    /// User-selected folder path (NO partitioning)
13
+    pub folder_path: PathBuf,
14
+    /// Maximum storage to allocate in bytes
15
+    pub max_size_bytes: u64,
16
+    /// Warning threshold (80% by default)
17
+    pub warn_at_percent: f32,
18
+    /// Auto cleanup enabled
19
+    pub auto_cleanup_enabled: bool,
20
+}
21
+
22
+/// Current storage status
23
+#[derive(Debug, Clone, Serialize, Deserialize)]
24
+pub struct StorageStatus {
25
+    /// Currently used storage in bytes
26
+    pub used_bytes: u64,
27
+    /// Total allocated storage in bytes
28
+    pub total_allocated_bytes: u64,
29
+    /// Usage percentage
30
+    pub usage_percent: f32,
31
+    /// Number of chunks stored
32
+    pub chunks_count: u32,
33
+    /// Available space on disk
34
+    pub disk_available_bytes: u64,
35
+    /// Is approaching warning threshold
36
+    pub needs_attention: bool,
37
+}
38
+
39
+/// Storage folder validation result
40
+#[derive(Debug, Clone, Serialize, Deserialize)]
41
+pub struct FolderValidation {
42
+    pub is_valid: bool,
43
+    pub error_message: Option<String>,
44
+    pub available_space_bytes: u64,
45
+    pub recommended_max_size: u64,
46
+    pub warnings: Vec<String>,
47
+}
48
+
49
+impl Default for SafeStorageConfig {
50
+    fn default() -> Self {
51
+        let default_folder = dirs::document_dir()
52
+            .unwrap_or_else(|| dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")))
53
+            .join("ZephyrFS-Storage");
54
+
55
+        Self {
56
+            folder_path: default_folder,
57
+            max_size_bytes: 10 * 1024 * 1024 * 1024, // 10GB default
58
+            warn_at_percent: 80.0,
59
+            auto_cleanup_enabled: true,
60
+        }
61
+    }
62
+}
63
+
64
+/// Tauri command to get default storage suggestions
65
+#[command]
66
+pub fn get_default_storage_suggestions() -> Result<Vec<PathBuf>, String> {
67
+    let mut suggestions = Vec::new();
68
+
69
+    // Documents folder (safest)
70
+    if let Some(docs) = dirs::document_dir() {
71
+        suggestions.push(docs.join("ZephyrFS-Storage"));
72
+    }
73
+
74
+    // Desktop folder (visible)
75
+    if let Some(desktop) = dirs::desktop_dir() {
76
+        suggestions.push(desktop.join("ZephyrFS-Storage"));
77
+    }
78
+
79
+    // Home folder (traditional)
80
+    if let Some(home) = dirs::home_dir() {
81
+        suggestions.push(home.join("ZephyrFS-Storage"));
82
+    }
83
+
84
+    // Downloads folder (temporary files area)
85
+    if let Some(downloads) = dirs::download_dir() {
86
+        suggestions.push(downloads.join("ZephyrFS-Storage"));
87
+    }
88
+
89
+    Ok(suggestions)
90
+}
91
+
92
+/// Tauri command to validate a selected folder
93
+#[command]
94
+pub fn validate_storage_folder(folder_path: String) -> Result<FolderValidation, String> {
95
+    let path = PathBuf::from(&folder_path);
96
+    let mut warnings = Vec::new();
97
+
98
+    // Check if path exists or can be created
99
+    let parent = path.parent().unwrap_or(&path);
100
+    if !parent.exists() {
101
+        return Ok(FolderValidation {
102
+            is_valid: false,
103
+            error_message: Some("Parent directory does not exist".to_string()),
104
+            available_space_bytes: 0,
105
+            recommended_max_size: 0,
106
+            warnings,
107
+        });
108
+    }
109
+
110
+    // Check disk space
111
+    let available_space = match free_space(parent) {
112
+        Ok(space) => space,
113
+        Err(e) => {
114
+            return Ok(FolderValidation {
115
+                is_valid: false,
116
+                error_message: Some(format!("Cannot check disk space: {}", e)),
117
+                available_space_bytes: 0,
118
+                recommended_max_size: 0,
119
+                warnings,
120
+            });
121
+        }
122
+    };
123
+
124
+    // Safety checks
125
+    let path_str = folder_path.to_lowercase();
126
+
127
+    // Warn about system directories
128
+    if path_str.contains("/system") || path_str.contains("\\system32") ||
129
+       path_str.contains("/usr") || path_str.contains("/bin") ||
130
+       path_str.contains("program files") {
131
+        warnings.push("This appears to be a system directory. Please choose a safer location.".to_string());
132
+    }
133
+
134
+    // Warn about root directories
135
+    if path == PathBuf::from("/") || path == PathBuf::from("C:\\") {
136
+        return Ok(FolderValidation {
137
+            is_valid: false,
138
+            error_message: Some("Cannot use root directory for storage".to_string()),
139
+            available_space_bytes: 0,
140
+            recommended_max_size: 0,
141
+            warnings,
142
+        });
143
+    }
144
+
145
+    // Recommend conservative storage size (max 50% of available space)
146
+    let recommended_max = (available_space as f64 * 0.5) as u64;
147
+    let recommended_max = std::cmp::min(recommended_max, 100 * 1024 * 1024 * 1024); // Max 100GB
148
+
149
+    // Check minimum space requirement (1GB)
150
+    if available_space < 1024 * 1024 * 1024 {
151
+        warnings.push("Less than 1GB available space. Consider choosing a different location.".to_string());
152
+    }
153
+
154
+    Ok(FolderValidation {
155
+        is_valid: true,
156
+        error_message: None,
157
+        available_space_bytes: available_space,
158
+        recommended_max_size: recommended_max,
159
+        warnings,
160
+    })
161
+}
162
+
163
+/// Tauri command to create storage folder safely
164
+#[command]
165
+pub async fn create_storage_folder(config: SafeStorageConfig) -> Result<bool, String> {
166
+    // Ensure the folder exists
167
+    if !config.folder_path.exists() {
168
+        std::fs::create_dir_all(&config.folder_path)
169
+            .map_err(|e| format!("Failed to create directory: {}", e))?;
170
+    }
171
+
172
+    // Create subdirectory structure
173
+    let chunks_dir = config.folder_path.join("chunks");
174
+    let metadata_dir = config.folder_path.join("metadata");
175
+    let logs_dir = config.folder_path.join("logs");
176
+    let config_dir = config.folder_path.join("config");
177
+
178
+    std::fs::create_dir_all(&chunks_dir)
179
+        .map_err(|e| format!("Failed to create chunks directory: {}", e))?;
180
+    std::fs::create_dir_all(&metadata_dir)
181
+        .map_err(|e| format!("Failed to create metadata directory: {}", e))?;
182
+    std::fs::create_dir_all(&logs_dir)
183
+        .map_err(|e| format!("Failed to create logs directory: {}", e))?;
184
+    std::fs::create_dir_all(&config_dir)
185
+        .map_err(|e| format!("Failed to create config directory: {}", e))?;
186
+
187
+    // Save configuration
188
+    let config_file = config_dir.join("storage_config.json");
189
+    let config_json = serde_json::to_string_pretty(&config)
190
+        .map_err(|e| format!("Failed to serialize config: {}", e))?;
191
+
192
+    std::fs::write(&config_file, config_json)
193
+        .map_err(|e| format!("Failed to write config file: {}", e))?;
194
+
195
+    // Create README file for transparency
196
+    let readme_content = format!(
197
+        r#"# ZephyrFS Storage Folder
198
+
199
+This folder contains encrypted chunks from the ZephyrFS network.
200
+
201
+## What's stored here:
202
+- `/chunks/` - Encrypted file chunks (completely unreadable)
203
+- `/metadata/` - Local indexing information
204
+- `/logs/` - Activity logs for transparency
205
+- `/config/` - Configuration files
206
+
207
+## Safety Information:
208
+- All files in /chunks/ are encrypted and cannot be read by this computer
209
+- No personal information from other users is stored here
210
+- You can safely delete this entire folder to stop participating
211
+- Storage limit: {} GB
212
+
213
+## Created: {}
214
+
215
+For more information, visit: https://zephyrfs.org
216
+"#,
217
+        config.max_size_bytes / (1024 * 1024 * 1024),
218
+        chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
219
+    );
220
+
221
+    std::fs::write(config.folder_path.join("README.txt"), readme_content)
222
+        .map_err(|e| format!("Failed to write README: {}", e))?;
223
+
224
+    Ok(true)
225
+}
226
+
227
+/// Tauri command to get current storage status
228
+#[command]
229
+pub fn get_storage_status(folder_path: String) -> Result<StorageStatus, String> {
230
+    let path = PathBuf::from(&folder_path);
231
+
232
+    if !path.exists() {
233
+        return Err("Storage folder does not exist".to_string());
234
+    }
235
+
236
+    // Calculate used space by scanning chunks directory
237
+    let chunks_dir = path.join("chunks");
238
+    let mut used_bytes = 0u64;
239
+    let mut chunks_count = 0u32;
240
+
241
+    if chunks_dir.exists() {
242
+        match std::fs::read_dir(&chunks_dir) {
243
+            Ok(entries) => {
244
+                for entry in entries {
245
+                    if let Ok(entry) = entry {
246
+                        if let Ok(metadata) = entry.metadata() {
247
+                            if metadata.is_file() {
248
+                                used_bytes += metadata.len();
249
+                                chunks_count += 1;
250
+                            }
251
+                        }
252
+                    }
253
+                }
254
+            }
255
+            Err(_) => return Err("Cannot read chunks directory".to_string()),
256
+        }
257
+    }
258
+
259
+    // Get disk available space
260
+    let disk_available_bytes = free_space(&path)
261
+        .map_err(|e| format!("Cannot check disk space: {}", e))?;
262
+
263
+    // Load configuration to get total allocated
264
+    let config_file = path.join("config").join("storage_config.json");
265
+    let total_allocated_bytes = if config_file.exists() {
266
+        match std::fs::read_to_string(&config_file) {
267
+            Ok(content) => {
268
+                match serde_json::from_str::<SafeStorageConfig>(&content) {
269
+                    Ok(config) => config.max_size_bytes,
270
+                    Err(_) => 10 * 1024 * 1024 * 1024, // Default 10GB
271
+                }
272
+            }
273
+            Err(_) => 10 * 1024 * 1024 * 1024, // Default 10GB
274
+        }
275
+    } else {
276
+        10 * 1024 * 1024 * 1024 // Default 10GB
277
+    };
278
+
279
+    let usage_percent = if total_allocated_bytes > 0 {
280
+        (used_bytes as f64 / total_allocated_bytes as f64 * 100.0) as f32
281
+    } else {
282
+        0.0
283
+    };
284
+
285
+    let needs_attention = usage_percent > 80.0 || disk_available_bytes < 1024 * 1024 * 1024; // < 1GB
286
+
287
+    Ok(StorageStatus {
288
+        used_bytes,
289
+        total_allocated_bytes,
290
+        usage_percent,
291
+        chunks_count,
292
+        disk_available_bytes,
293
+        needs_attention,
294
+    })
295
+}
296
+
297
+/// Tauri command to safely remove all storage data
298
+#[command]
299
+pub async fn remove_storage_data(folder_path: String, confirm: bool) -> Result<bool, String> {
300
+    if !confirm {
301
+        return Err("Confirmation required to remove storage data".to_string());
302
+    }
303
+
304
+    let path = PathBuf::from(&folder_path);
305
+
306
+    if !path.exists() {
307
+        return Ok(true); // Already doesn't exist
308
+    }
309
+
310
+    // Safety check - ensure this looks like a ZephyrFS storage folder
311
+    let readme_path = path.join("README.txt");
312
+    if !readme_path.exists() {
313
+        return Err("This doesn't appear to be a ZephyrFS storage folder".to_string());
314
+    }
315
+
316
+    // Remove the entire directory
317
+    std::fs::remove_dir_all(&path)
318
+        .map_err(|e| format!("Failed to remove storage directory: {}", e))?;
319
+
320
+    Ok(true)
321
+}
322
+
323
+/// Format bytes to human-readable string
324
+pub fn format_bytes(bytes: u64) -> String {
325
+    const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
326
+    let mut size = bytes as f64;
327
+    let mut unit_index = 0;
328
+
329
+    while size >= 1024.0 && unit_index < UNITS.len() - 1 {
330
+        size /= 1024.0;
331
+        unit_index += 1;
332
+    }
333
+
334
+    if unit_index == 0 {
335
+        format!("{} {}", size as u64, UNITS[unit_index])
336
+    } else {
337
+        format!("{:.2} {}", size, UNITS[unit_index])
338
+    }
339
+}
src-tauri/src/lib.rsadded
@@ -0,0 +1,31 @@
1
+// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
2
+mod folder_manager;
3
+
4
+use folder_manager::{
5
+    get_default_storage_suggestions,
6
+    validate_storage_folder,
7
+    create_storage_folder,
8
+    get_storage_status,
9
+    remove_storage_data
10
+};
11
+
12
+#[tauri::command]
13
+fn greet(name: &str) -> String {
14
+    format!("Hello, {}! You've been greeted from Rust!", name)
15
+}
16
+
17
+#[cfg_attr(mobile, tauri::mobile_entry_point)]
18
+pub fn run() {
19
+    tauri::Builder::default()
20
+        .plugin(tauri_plugin_opener::init())
21
+        .invoke_handler(tauri::generate_handler![
22
+            greet,
23
+            get_default_storage_suggestions,
24
+            validate_storage_folder,
25
+            create_storage_folder,
26
+            get_storage_status,
27
+            remove_storage_data
28
+        ])
29
+        .run(tauri::generate_context!())
30
+        .expect("error while running tauri application");
31
+}
src-tauri/src/main.rsadded
@@ -0,0 +1,6 @@
1
+// Prevents additional console window on Windows in release, DO NOT REMOVE!!
2
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3
+
4
+fn main() {
5
+    zephyrfs_desktop_lib::run()
6
+}
src-tauri/tauri.conf.jsonadded
@@ -0,0 +1,35 @@
1
+{
2
+  "$schema": "https://schema.tauri.app/config/2",
3
+  "productName": "zephyrfs-desktop",
4
+  "version": "0.1.0",
5
+  "identifier": "com.mfwolffe.zephyrfs-desktop",
6
+  "build": {
7
+    "beforeDevCommand": "npm run dev",
8
+    "devUrl": "http://localhost:1420",
9
+    "beforeBuildCommand": "npm run build",
10
+    "frontendDist": "../dist"
11
+  },
12
+  "app": {
13
+    "windows": [
14
+      {
15
+        "title": "zephyrfs-desktop",
16
+        "width": 800,
17
+        "height": 600
18
+      }
19
+    ],
20
+    "security": {
21
+      "csp": null
22
+    }
23
+  },
24
+  "bundle": {
25
+    "active": true,
26
+    "targets": "all",
27
+    "icon": [
28
+      "icons/32x32.png",
29
+      "icons/128x128.png",
30
+      "icons/128x128@2x.png",
31
+      "icons/icon.icns",
32
+      "icons/icon.ico"
33
+    ]
34
+  }
35
+}
src/App.vueadded
@@ -0,0 +1,580 @@
1
+<script setup lang="ts">
2
+import { ref, onMounted } from "vue";
3
+import { invoke } from "@tauri-apps/api/core";
4
+
5
+interface StorageConfig {
6
+  folder_path: string;
7
+  max_size_bytes: number;
8
+  warn_at_percent: number;
9
+  auto_cleanup_enabled: boolean;
10
+}
11
+
12
+interface FolderValidation {
13
+  is_valid: boolean;
14
+  error_message?: string;
15
+  available_space_bytes: number;
16
+  recommended_max_size: number;
17
+  warnings: string[];
18
+}
19
+
20
+interface StorageStatus {
21
+  used_bytes: number;
22
+  total_allocated_bytes: number;
23
+  usage_percent: number;
24
+  chunks_count: number;
25
+  disk_available_bytes: number;
26
+  needs_attention: boolean;
27
+}
28
+
29
+const currentStep = ref(1);
30
+const suggestions = ref<string[]>([]);
31
+const selectedFolder = ref("");
32
+const validation = ref<FolderValidation | null>(null);
33
+const maxSizeGB = ref(10);
34
+const isCreating = ref(false);
35
+const status = ref<StorageStatus | null>(null);
36
+
37
+const formatBytes = (bytes: number): string => {
38
+  const units = ["B", "KB", "MB", "GB", "TB"];
39
+  let size = bytes;
40
+  let unitIndex = 0;
41
+
42
+  while (size >= 1024 && unitIndex < units.length - 1) {
43
+    size /= 1024;
44
+    unitIndex++;
45
+  }
46
+
47
+  return unitIndex === 0
48
+    ? `${Math.floor(size)} ${units[unitIndex]}`
49
+    : `${size.toFixed(2)} ${units[unitIndex]}`;
50
+};
51
+
52
+const loadSuggestions = async () => {
53
+  try {
54
+    suggestions.value = await invoke("get_default_storage_suggestions");
55
+    if (suggestions.value.length > 0) {
56
+      selectedFolder.value = suggestions.value[0];
57
+    }
58
+  } catch (error) {
59
+    console.error("Failed to load suggestions:", error);
60
+  }
61
+};
62
+
63
+const validateFolder = async () => {
64
+  if (!selectedFolder.value) return;
65
+
66
+  try {
67
+    validation.value = await invoke("validate_storage_folder", {
68
+      folderPath: selectedFolder.value
69
+    });
70
+    if (validation.value?.is_valid) {
71
+      maxSizeGB.value = Math.floor(validation.value.recommended_max_size / (1024 * 1024 * 1024));
72
+    }
73
+  } catch (error) {
74
+    console.error("Failed to validate folder:", error);
75
+  }
76
+};
77
+
78
+const createStorage = async () => {
79
+  if (!selectedFolder.value || !validation.value?.is_valid) return;
80
+
81
+  isCreating.value = true;
82
+  try {
83
+    const config: StorageConfig = {
84
+      folder_path: selectedFolder.value,
85
+      max_size_bytes: maxSizeGB.value * 1024 * 1024 * 1024,
86
+      warn_at_percent: 80.0,
87
+      auto_cleanup_enabled: true
88
+    };
89
+
90
+    await invoke("create_storage_folder", { config });
91
+    currentStep.value = 3;
92
+    loadStatus();
93
+  } catch (error) {
94
+    console.error("Failed to create storage:", error);
95
+  } finally {
96
+    isCreating.value = false;
97
+  }
98
+};
99
+
100
+const loadStatus = async () => {
101
+  if (!selectedFolder.value) return;
102
+
103
+  try {
104
+    status.value = await invoke("get_storage_status", {
105
+      folderPath: selectedFolder.value
106
+    });
107
+  } catch (error) {
108
+    console.error("Failed to load status:", error);
109
+  }
110
+};
111
+
112
+onMounted(() => {
113
+  loadSuggestions();
114
+});
115
+</script>
116
+
117
+<template>
118
+  <main class="container">
119
+    <h1>ZephyrFS Storage Node Setup</h1>
120
+
121
+    <!-- Step 1: Folder Selection -->
122
+    <div v-if="currentStep === 1" class="step">
123
+      <h2>Step 1: Choose Storage Location</h2>
124
+      <p>Select a safe folder to store encrypted chunks from other users.</p>
125
+
126
+      <div class="suggestions">
127
+        <h3>Recommended locations:</h3>
128
+        <div class="folder-options">
129
+          <label v-for="suggestion in suggestions" :key="suggestion" class="folder-option">
130
+            <input
131
+              type="radio"
132
+              v-model="selectedFolder"
133
+              :value="suggestion"
134
+              @change="validateFolder"
135
+            />
136
+            <span>{{ suggestion }}</span>
137
+          </label>
138
+        </div>
139
+      </div>
140
+
141
+      <div class="custom-folder">
142
+        <label>
143
+          Custom folder path:
144
+          <input
145
+            type="text"
146
+            v-model="selectedFolder"
147
+            @input="validateFolder"
148
+            placeholder="Enter custom folder path..."
149
+          />
150
+        </label>
151
+      </div>
152
+
153
+      <div v-if="validation" class="validation">
154
+        <div v-if="validation.is_valid" class="valid">
155
+          ✓ Valid location
156
+          <p>Available space: {{ formatBytes(validation.available_space_bytes) }}</p>
157
+          <p>Recommended max: {{ formatBytes(validation.recommended_max_size) }}</p>
158
+        </div>
159
+        <div v-else class="invalid">
160
+          ✗ {{ validation.error_message }}
161
+        </div>
162
+
163
+        <div v-if="validation.warnings.length > 0" class="warnings">
164
+          <h4>Warnings:</h4>
165
+          <ul>
166
+            <li v-for="warning in validation.warnings" :key="warning">{{ warning }}</li>
167
+          </ul>
168
+        </div>
169
+      </div>
170
+
171
+      <button
172
+        @click="currentStep = 2"
173
+        :disabled="!validation?.is_valid"
174
+        class="next-btn"
175
+      >
176
+        Next: Configure Storage
177
+      </button>
178
+    </div>
179
+
180
+    <!-- Step 2: Storage Configuration -->
181
+    <div v-if="currentStep === 2" class="step">
182
+      <h2>Step 2: Configure Storage Allocation</h2>
183
+      <p>Set how much space to dedicate to ZephyrFS storage.</p>
184
+
185
+      <div class="config-section">
186
+        <label>
187
+          Maximum Storage (GB):
188
+          <input
189
+            type="number"
190
+            v-model="maxSizeGB"
191
+            min="1"
192
+            :max="Math.floor((validation?.recommended_max_size || 0) / (1024 * 1024 * 1024))"
193
+          />
194
+        </label>
195
+        <p class="size-info">{{ formatBytes(maxSizeGB * 1024 * 1024 * 1024) }}</p>
196
+      </div>
197
+
198
+      <div class="safety-info">
199
+        <h3>Safety Guarantees:</h3>
200
+        <ul>
201
+          <li>✓ All stored data is encrypted and unreadable</li>
202
+          <li>✓ No personal information is stored</li>
203
+          <li>✓ You can safely delete this folder anytime</li>
204
+          <li>✓ Storage is limited to your specified amount</li>
205
+        </ul>
206
+      </div>
207
+
208
+      <div class="buttons">
209
+        <button @click="currentStep = 1" class="back-btn">Back</button>
210
+        <button
211
+          @click="createStorage"
212
+          :disabled="isCreating"
213
+          class="create-btn"
214
+        >
215
+          {{ isCreating ? "Creating..." : "Create Storage Node" }}
216
+        </button>
217
+      </div>
218
+    </div>
219
+
220
+    <!-- Step 3: Success & Status -->
221
+    <div v-if="currentStep === 3" class="step">
222
+      <h2>✓ Storage Node Ready!</h2>
223
+      <p>Your ZephyrFS storage node has been created successfully.</p>
224
+
225
+      <div v-if="status" class="status-panel">
226
+        <h3>Current Status:</h3>
227
+        <div class="status-grid">
228
+          <div class="status-item">
229
+            <span class="label">Used Space:</span>
230
+            <span class="value">{{ formatBytes(status.used_bytes) }}</span>
231
+          </div>
232
+          <div class="status-item">
233
+            <span class="label">Total Allocated:</span>
234
+            <span class="value">{{ formatBytes(status.total_allocated_bytes) }}</span>
235
+          </div>
236
+          <div class="status-item">
237
+            <span class="label">Usage:</span>
238
+            <span class="value">{{ status.usage_percent.toFixed(1) }}%</span>
239
+          </div>
240
+          <div class="status-item">
241
+            <span class="label">Chunks Stored:</span>
242
+            <span class="value">{{ status.chunks_count }}</span>
243
+          </div>
244
+        </div>
245
+
246
+        <div class="progress-bar">
247
+          <div
248
+            class="progress-fill"
249
+            :style="{ width: `${Math.min(status.usage_percent, 100)}%` }"
250
+          ></div>
251
+        </div>
252
+      </div>
253
+
254
+      <div class="next-steps">
255
+        <h3>What's Next:</h3>
256
+        <p>Your storage node will now participate in the ZephyrFS network, safely storing encrypted chunks from other users.</p>
257
+        <p>Check the system tray for real-time status updates.</p>
258
+      </div>
259
+    </div>
260
+  </main>
261
+</template>
262
+
263
+<style scoped>
264
+.step {
265
+  max-width: 600px;
266
+  margin: 0 auto;
267
+  padding: 2rem;
268
+  background: #ffffff;
269
+  border-radius: 12px;
270
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
271
+}
272
+
273
+.suggestions {
274
+  margin: 1.5rem 0;
275
+}
276
+
277
+.folder-options {
278
+  display: flex;
279
+  flex-direction: column;
280
+  gap: 0.5rem;
281
+  margin: 1rem 0;
282
+}
283
+
284
+.folder-option {
285
+  display: flex;
286
+  align-items: center;
287
+  gap: 0.5rem;
288
+  padding: 0.5rem;
289
+  border: 1px solid #e0e0e0;
290
+  border-radius: 6px;
291
+  cursor: pointer;
292
+  transition: background-color 0.2s;
293
+}
294
+
295
+.folder-option:hover {
296
+  background-color: #f5f5f5;
297
+}
298
+
299
+.custom-folder {
300
+  margin: 1.5rem 0;
301
+}
302
+
303
+.custom-folder input {
304
+  width: 100%;
305
+  margin-top: 0.5rem;
306
+}
307
+
308
+.validation {
309
+  margin: 1.5rem 0;
310
+  padding: 1rem;
311
+  border-radius: 6px;
312
+}
313
+
314
+.valid {
315
+  background-color: #d4edda;
316
+  color: #155724;
317
+  border: 1px solid #c3e6cb;
318
+}
319
+
320
+.invalid {
321
+  background-color: #f8d7da;
322
+  color: #721c24;
323
+  border: 1px solid #f5c6cb;
324
+}
325
+
326
+.warnings {
327
+  margin-top: 1rem;
328
+  padding: 1rem;
329
+  background-color: #fff3cd;
330
+  color: #856404;
331
+  border: 1px solid #ffeaa7;
332
+  border-radius: 6px;
333
+}
334
+
335
+.config-section {
336
+  margin: 1.5rem 0;
337
+}
338
+
339
+.size-info {
340
+  font-size: 0.9rem;
341
+  color: #666;
342
+  margin-top: 0.5rem;
343
+}
344
+
345
+.safety-info {
346
+  margin: 2rem 0;
347
+  padding: 1.5rem;
348
+  background-color: #e8f5e8;
349
+  border-radius: 8px;
350
+}
351
+
352
+.safety-info ul {
353
+  list-style: none;
354
+  padding: 0;
355
+}
356
+
357
+.safety-info li {
358
+  margin: 0.5rem 0;
359
+  padding-left: 1rem;
360
+}
361
+
362
+.buttons {
363
+  display: flex;
364
+  gap: 1rem;
365
+  justify-content: flex-end;
366
+  margin-top: 2rem;
367
+}
368
+
369
+.next-btn, .create-btn {
370
+  background-color: #007bff;
371
+  color: white;
372
+}
373
+
374
+.back-btn {
375
+  background-color: #6c757d;
376
+  color: white;
377
+}
378
+
379
+.next-btn:disabled, .create-btn:disabled {
380
+  background-color: #cccccc;
381
+  cursor: not-allowed;
382
+}
383
+
384
+.status-panel {
385
+  margin: 2rem 0;
386
+  padding: 1.5rem;
387
+  background-color: #f8f9fa;
388
+  border-radius: 8px;
389
+}
390
+
391
+.status-grid {
392
+  display: grid;
393
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
394
+  gap: 1rem;
395
+  margin: 1rem 0;
396
+}
397
+
398
+.status-item {
399
+  display: flex;
400
+  justify-content: space-between;
401
+  padding: 0.5rem;
402
+  background-color: white;
403
+  border-radius: 4px;
404
+}
405
+
406
+.label {
407
+  font-weight: 500;
408
+}
409
+
410
+.value {
411
+  font-weight: 600;
412
+  color: #007bff;
413
+}
414
+
415
+.progress-bar {
416
+  width: 100%;
417
+  height: 8px;
418
+  background-color: #e0e0e0;
419
+  border-radius: 4px;
420
+  margin: 1rem 0;
421
+  overflow: hidden;
422
+}
423
+
424
+.progress-fill {
425
+  height: 100%;
426
+  background-color: #28a745;
427
+  transition: width 0.3s ease;
428
+}
429
+
430
+.next-steps {
431
+  margin: 2rem 0;
432
+  padding: 1.5rem;
433
+  background-color: #e8f4fd;
434
+  border-radius: 8px;
435
+}
436
+
437
+@media (prefers-color-scheme: dark) {
438
+  .step {
439
+    background-color: #2d2d2d;
440
+    color: #f6f6f6;
441
+  }
442
+
443
+  .folder-option {
444
+    border-color: #555;
445
+    background-color: #333;
446
+  }
447
+
448
+  .folder-option:hover {
449
+    background-color: #444;
450
+  }
451
+
452
+  .status-panel {
453
+    background-color: #333;
454
+  }
455
+
456
+  .status-item {
457
+    background-color: #444;
458
+  }
459
+
460
+  .safety-info {
461
+    background-color: #2d4a2d;
462
+  }
463
+
464
+  .next-steps {
465
+    background-color: #2d3a4a;
466
+  }
467
+}
468
+</style>
469
+<style>
470
+:root {
471
+  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
472
+  font-size: 16px;
473
+  line-height: 24px;
474
+  font-weight: 400;
475
+
476
+  color: #0f0f0f;
477
+  background-color: #f6f6f6;
478
+
479
+  font-synthesis: none;
480
+  text-rendering: optimizeLegibility;
481
+  -webkit-font-smoothing: antialiased;
482
+  -moz-osx-font-smoothing: grayscale;
483
+  -webkit-text-size-adjust: 100%;
484
+}
485
+
486
+.container {
487
+  margin: 0;
488
+  padding-top: 10vh;
489
+  display: flex;
490
+  flex-direction: column;
491
+  justify-content: center;
492
+  text-align: center;
493
+}
494
+
495
+.logo {
496
+  height: 6em;
497
+  padding: 1.5em;
498
+  will-change: filter;
499
+  transition: 0.75s;
500
+}
501
+
502
+.logo.tauri:hover {
503
+  filter: drop-shadow(0 0 2em #24c8db);
504
+}
505
+
506
+.row {
507
+  display: flex;
508
+  justify-content: center;
509
+}
510
+
511
+a {
512
+  font-weight: 500;
513
+  color: #646cff;
514
+  text-decoration: inherit;
515
+}
516
+
517
+a:hover {
518
+  color: #535bf2;
519
+}
520
+
521
+h1 {
522
+  text-align: center;
523
+}
524
+
525
+input,
526
+button {
527
+  border-radius: 8px;
528
+  border: 1px solid transparent;
529
+  padding: 0.6em 1.2em;
530
+  font-size: 1em;
531
+  font-weight: 500;
532
+  font-family: inherit;
533
+  color: #0f0f0f;
534
+  background-color: #ffffff;
535
+  transition: border-color 0.25s;
536
+  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
537
+}
538
+
539
+button {
540
+  cursor: pointer;
541
+}
542
+
543
+button:hover {
544
+  border-color: #396cd8;
545
+}
546
+button:active {
547
+  border-color: #396cd8;
548
+  background-color: #e8e8e8;
549
+}
550
+
551
+input,
552
+button {
553
+  outline: none;
554
+}
555
+
556
+#greet-input {
557
+  margin-right: 5px;
558
+}
559
+
560
+@media (prefers-color-scheme: dark) {
561
+  :root {
562
+    color: #f6f6f6;
563
+    background-color: #2f2f2f;
564
+  }
565
+
566
+  a:hover {
567
+    color: #24c8db;
568
+  }
569
+
570
+  input,
571
+  button {
572
+    color: #ffffff;
573
+    background-color: #0f0f0f98;
574
+  }
575
+  button:active {
576
+    background-color: #0f0f0f69;
577
+  }
578
+}
579
+
580
+</style>
src/assets/vue.svgadded
@@ -0,0 +1,1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
src/main.tsadded
@@ -0,0 +1,4 @@
1
+import { createApp } from "vue";
2
+import App from "./App.vue";
3
+
4
+createApp(App).mount("#app");
src/vite-env.d.tsadded
@@ -0,0 +1,7 @@
1
+/// <reference types="vite/client" />
2
+
3
+declare module "*.vue" {
4
+  import type { DefineComponent } from "vue";
5
+  const component: DefineComponent<{}, {}, any>;
6
+  export default component;
7
+}
tsconfig.jsonadded
@@ -0,0 +1,25 @@
1
+{
2
+  "compilerOptions": {
3
+    "target": "ES2020",
4
+    "useDefineForClassFields": true,
5
+    "module": "ESNext",
6
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+    "skipLibCheck": true,
8
+
9
+    /* Bundler mode */
10
+    "moduleResolution": "bundler",
11
+    "allowImportingTsExtensions": true,
12
+    "resolveJsonModule": true,
13
+    "isolatedModules": true,
14
+    "noEmit": true,
15
+    "jsx": "preserve",
16
+
17
+    /* Linting */
18
+    "strict": true,
19
+    "noUnusedLocals": true,
20
+    "noUnusedParameters": true,
21
+    "noFallthroughCasesInSwitch": true
22
+  },
23
+  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
24
+  "references": [{ "path": "./tsconfig.node.json" }]
25
+}
tsconfig.node.jsonadded
@@ -0,0 +1,10 @@
1
+{
2
+  "compilerOptions": {
3
+    "composite": true,
4
+    "skipLibCheck": true,
5
+    "module": "ESNext",
6
+    "moduleResolution": "bundler",
7
+    "allowSyntheticDefaultImports": true
8
+  },
9
+  "include": ["vite.config.ts"]
10
+}
vite.config.tsadded
@@ -0,0 +1,32 @@
1
+import { defineConfig } from "vite";
2
+import vue from "@vitejs/plugin-vue";
3
+
4
+// @ts-expect-error process is a nodejs global
5
+const host = process.env.TAURI_DEV_HOST;
6
+
7
+// https://vite.dev/config/
8
+export default defineConfig(async () => ({
9
+  plugins: [vue()],
10
+
11
+  // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
12
+  //
13
+  // 1. prevent Vite from obscuring rust errors
14
+  clearScreen: false,
15
+  // 2. tauri expects a fixed port, fail if that port is not available
16
+  server: {
17
+    port: 1420,
18
+    strictPort: true,
19
+    host: host || false,
20
+    hmr: host
21
+      ? {
22
+          protocol: "ws",
23
+          host,
24
+          port: 1421,
25
+        }
26
+      : undefined,
27
+    watch: {
28
+      // 3. tell Vite to ignore watching `src-tauri`
29
+      ignored: ["**/src-tauri/**"],
30
+    },
31
+  },
32
+}));