diff --git a/new-web/package-lock.json b/new-web/package-lock.json
index f99311c2..b8b92c26 100644
--- a/new-web/package-lock.json
+++ b/new-web/package-lock.json
@@ -8,14 +8,24 @@
"name": "new-web",
"version": "0.0.1",
"dependencies": {
+ "@battlefieldduck/xterm-svelte": "^2.1.0",
"@fontsource/noto-sans": "^5.2.7",
"@layerstack/svelte-stores": "^1.0.2",
+ "@lucide/svelte": "^0.516.0",
+ "@svelte-put/shortcut": "^4.1.0",
+ "@sveltestack/svelte-query": "^1.6.0",
"adze": "^2.2.4",
"axios": "^1.10.0",
+ "d3-shape": "^3.2.0",
"date-fns": "^4.1.0",
+ "date-fns-tz": "^3.2.0",
+ "human-format": "^1.2.1",
"is-cidr": "^5.1.1",
"is-ip": "^5.0.1",
+ "layerchart": "^1.0.11",
+ "lucide-svelte": "^0.516.0",
"svelte-i18n": "^4.0.1",
+ "tabulator-tables": "^6.3.1",
"validator": "^13.15.15",
"zod": "^3.25.67"
},
@@ -23,19 +33,22 @@
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@iconify/svelte": "^5.0.0",
- "@lucide/svelte": "^0.516.0",
+ "@internationalized/date": "^3.8.2",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/vite": "^4.0.0",
+ "@types/tabulator-tables": "^6.2.6",
+ "bits-ui": "^2.8.0",
"clsx": "^2.1.1",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^3.0.0",
"globals": "^16.0.0",
"mode-watcher": "^1.0.8",
+ "paneforge": "^1.0.0-next.5",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
@@ -51,6 +64,18 @@
"vite": "^6.2.6"
}
},
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@@ -64,6 +89,46 @@
"node": ">=6.0.0"
}
},
+ "node_modules/@battlefieldduck/xterm-svelte": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@battlefieldduck/xterm-svelte/-/xterm-svelte-2.1.0.tgz",
+ "integrity": "sha512-OeuGnPqblPBc57WZ8TtcU2yX0MvMs7rMbhZaLethyD5i8/WqM/XJej0NOp0KcND8jNmPnBWjePwghAQDlDXnlw==",
+ "license": "MIT",
+ "dependencies": {
+ "@xterm/addon-attach": "^0.11.0",
+ "@xterm/addon-canvas": "^0.7.0",
+ "@xterm/addon-clipboard": "^0.1.0",
+ "@xterm/addon-fit": "^0.10.0",
+ "@xterm/addon-image": "^0.8.0",
+ "@xterm/addon-search": "^0.15.0",
+ "@xterm/addon-serialize": "^0.13.0",
+ "@xterm/addon-unicode11": "^0.8.0",
+ "@xterm/addon-web-links": "^0.11.0",
+ "@xterm/addon-webgl": "^0.18.0",
+ "@xterm/xterm": "^5.5.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.0.0"
+ }
+ },
+ "node_modules/@dagrejs/dagre": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.5.tgz",
+ "integrity": "sha512-Ghgrh08s12DCL5SeiR6AoyE80mQELTWhJBRmXfFoqDiFkR458vPEdgTbbjA0T+9ETNxUblnD0QW55tfdvi5pjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@dagrejs/graphlib": "2.2.4"
+ }
+ },
+ "node_modules/@dagrejs/graphlib": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.4.tgz",
+ "integrity": "sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">17.0.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
@@ -674,6 +739,31 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz",
+ "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.9"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz",
+ "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.1",
+ "@floating-ui/utils": "^0.2.9"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
+ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
+ "license": "MIT"
+ },
"node_modules/@fontsource/noto-sans": {
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/@fontsource/noto-sans/-/noto-sans-5.2.7.tgz",
@@ -823,6 +913,33 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@internationalized/date": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz",
+ "integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@isaacs/fs-minipass": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
@@ -884,6 +1001,20 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@layerstack/svelte-actions": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@layerstack/svelte-actions/-/svelte-actions-1.0.1.tgz",
+ "integrity": "sha512-Tv8B3TeT7oaghx0R0I4avnSdfAT6GxEK+StL8k/hEaa009iNOIGFl3f76kfvNvPioQHAMFGtnWGLPHfsfD41nQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.13",
+ "@layerstack/utils": "1.0.1",
+ "d3-array": "^3.2.4",
+ "d3-scale": "^4.0.2",
+ "date-fns": "^4.1.0",
+ "lodash-es": "^4.17.21"
+ }
+ },
"node_modules/@layerstack/svelte-stores": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@layerstack/svelte-stores/-/svelte-stores-1.0.2.tgz",
@@ -898,6 +1029,198 @@
"zod": "^3.24.2"
}
},
+ "node_modules/@layerstack/tailwind": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@layerstack/tailwind/-/tailwind-1.0.1.tgz",
+ "integrity": "sha512-nlshEkUCfaV0zYzrFXVVYRnS8bnBjs4M7iui6l/tu6NeBBlxDivIyRraJkdYGCSL1lZHi6FqacLQ3eerHtz90A==",
+ "license": "MIT",
+ "dependencies": {
+ "@layerstack/utils": "^1.0.1",
+ "clsx": "^2.1.1",
+ "culori": "^4.0.1",
+ "d3-array": "^3.2.4",
+ "date-fns": "^4.1.0",
+ "lodash-es": "^4.17.21",
+ "tailwind-merge": "^2.5.4",
+ "tailwindcss": "^3.4.15"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/jiti": {
+ "version": "1.21.7",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/postcss-load-config": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+ "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.0.0",
+ "yaml": "^2.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/tailwind-merge": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
+ "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/@layerstack/tailwind/node_modules/tailwindcss": {
+ "version": "3.4.17",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
+ "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.6.0",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.2",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.6",
+ "lilconfig": "^3.1.3",
+ "micromatch": "^4.0.8",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.4.47",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.2",
+ "postcss-nested": "^6.2.0",
+ "postcss-selector-parser": "^6.1.2",
+ "resolve": "^1.22.8",
+ "sucrase": "^3.35.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@layerstack/utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@layerstack/utils/-/utils-1.0.1.tgz",
@@ -913,7 +1236,6 @@
"version": "0.516.0",
"resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-0.516.0.tgz",
"integrity": "sha512-Ovkg2IgUeF85guSbnG9iPwUMMRuz+rdh8HsnKbD1uEY2Wv3d9qf0ERKFrz0ePZqmPXOedCPcf2a/rvRsqkaZcw==",
- "dev": true,
"license": "ISC",
"peerDependencies": {
"svelte": "^5"
@@ -923,7 +1245,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
@@ -937,7 +1258,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -947,7 +1267,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
@@ -957,6 +1276,16 @@
"node": ">= 8"
}
},
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@polka/url": {
"version": "1.0.0-next.29",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
@@ -1244,6 +1573,15 @@
"win32"
]
},
+ "node_modules/@svelte-put/shortcut": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-4.1.0.tgz",
+ "integrity": "sha512-wImNEIkbxAIWFqlfuhcbC+jRPDeRa/uJGIXHMEVVD+jqL9xCwWNnkGQJ6Qb2XVszuRLHlb8SGZDL3Io/h3vs8w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "svelte": "^5.1.0"
+ }
+ },
"node_modules/@sveltejs/acorn-typescript": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
@@ -1336,6 +1674,30 @@
"vite": "^6.0.0"
}
},
+ "node_modules/@sveltestack/svelte-query": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@sveltestack/svelte-query/-/svelte-query-1.6.0.tgz",
+ "integrity": "sha512-C0wWuh6av1zu3Pzwrg6EQmX3BhDZQ4gMAdYu6Tfv4bjbEZTB00uEDz52z92IZdONh+iUKuyo0xRZ2e16k2Xifg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "broadcast-channel": "^4.5.0"
+ },
+ "peerDependenciesMeta": {
+ "broadcast-channel": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.17",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
+ "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
"node_modules/@tailwindcss/forms": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
@@ -1662,6 +2024,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/tabulator-tables": {
+ "version": "6.2.6",
+ "resolved": "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-6.2.6.tgz",
+ "integrity": "sha512-A+2VrqDluI6hNw5dQl1Z7b8pjQfAE62+3Kj0cFfenWzj0T0ewMicPrpPINHL7ASqz9u9FTDn1Mz1Ige2tF4Wlw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz",
@@ -1925,6 +2294,105 @@
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"license": "ISC"
},
+ "node_modules/@xterm/addon-attach": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-attach/-/addon-attach-0.11.0.tgz",
+ "integrity": "sha512-JboCN0QAY6ZLY/SSB/Zl2cQ5zW1Eh4X3fH7BnuR1NB7xGRhzbqU2Npmpiw/3zFlxDaU88vtKzok44JKi2L2V2Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/addon-canvas": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-canvas/-/addon-canvas-0.7.0.tgz",
+ "integrity": "sha512-LF5LYcfvefJuJ7QotNRdRSPc9YASAVDeoT5uyXS/nZshZXjYplGXRECBGiznwvhNL2I8bq1Lf5MzRwstsYQ2Iw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/addon-clipboard": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.1.0.tgz",
+ "integrity": "sha512-zdoM7p53T5sv/HbRTyp4hY0kKmEQ3MZvAvEtiXqNIHc/JdpqwByCtsTaQF5DX2n4hYdXRPO4P/eOS0QEhX1nPw==",
+ "license": "MIT",
+ "dependencies": {
+ "js-base64": "^3.7.5"
+ },
+ "peerDependencies": {
+ "@xterm/xterm": "^5.4.0"
+ }
+ },
+ "node_modules/@xterm/addon-fit": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
+ "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/addon-image": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.8.0.tgz",
+ "integrity": "sha512-b/dqpFn3jUad2pUP5UpF4scPIh0WdxRQL/1qyiahGfUI85XZTCXo0py9G6AcOR2QYUw8eJ8EowGspT7BQcgw6A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.2.0"
+ }
+ },
+ "node_modules/@xterm/addon-search": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.15.0.tgz",
+ "integrity": "sha512-ZBZKLQ+EuKE83CqCmSSz5y1tx+aNOCUaA7dm6emgOX+8J9H1FWXZyrKfzjwzV+V14TV3xToz1goIeRhXBS5qjg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/addon-serialize": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.13.0.tgz",
+ "integrity": "sha512-kGs8o6LWAmN1l2NpMp01/YkpxbmO4UrfWybeGu79Khw5K9+Krp7XhXbBTOTc3GJRRhd6EmILjpR8k5+odY39YQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/addon-unicode11": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.8.0.tgz",
+ "integrity": "sha512-LxinXu8SC4OmVa6FhgwsVCBZbr8WoSGzBl2+vqe8WcQ6hb1r6Gj9P99qTNdPiFPh4Ceiu2pC8xukZ6+2nnh49Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/addon-web-links": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.11.0.tgz",
+ "integrity": "sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/addon-webgl": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.18.0.tgz",
+ "integrity": "sha512-xCnfMBTI+/HKPdRnSOHaJDRqEpq2Ugy8LEj9GiY4J3zJObo3joylIFaMvzBwbYRg8zLtkO0KQaStCeSfoaI2/w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/xterm": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
+ "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
+ "license": "MIT"
+ },
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -1977,11 +2445,22 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@@ -1993,6 +2472,43 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "license": "MIT"
+ },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -2039,9 +2555,85 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/bits-ui": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.8.0.tgz",
+ "integrity": "sha512-WiTZcCbYLm4Cx6/67NqXVSD0BkfNmdX8Abs84HpIaplX/wRRbg8tkMtJYlLw7mepgGvwGR3enLi6tFkcHU3JXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.0",
+ "@floating-ui/dom": "^1.7.0",
+ "css.escape": "^1.5.1",
+ "esm-env": "^1.1.2",
+ "runed": "^0.28.0",
+ "svelte-toolbelt": "^0.9.1",
+ "tabbable": "^6.2.0"
+ },
+ "engines": {
+ "node": ">=20",
+ "pnpm": ">=8.7.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/huntabyte"
+ },
+ "peerDependencies": {
+ "@internationalized/date": "^3.8.1",
+ "svelte": "^5.33.0"
+ }
+ },
+ "node_modules/bits-ui/node_modules/runed": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/runed/-/runed-0.28.0.tgz",
+ "integrity": "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/huntabyte",
+ "https://github.com/sponsors/tglide"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "esm-env": "^1.0.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.7.0"
+ }
+ },
+ "node_modules/bits-ui/node_modules/svelte-toolbelt": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.9.1.tgz",
+ "integrity": "sha512-wBX6MtYw/kpht80j5zLpxJyR9soLizXPIAIWEVd9llAi17SR44ZdG291bldjB7r/K5duC0opDFcuhk2cA1hb8g==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/huntabyte"
+ ],
+ "dependencies": {
+ "clsx": "^2.1.1",
+ "runed": "^0.28.0",
+ "style-to-object": "^1.0.8"
+ },
+ "engines": {
+ "node": ">=18",
+ "pnpm": ">=8.7.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.30.2"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -2057,7 +2649,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
@@ -2089,6 +2680,15 @@
"node": ">=6"
}
},
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2188,7 +2788,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -2201,7 +2800,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
"license": "MIT"
},
"node_modules/combined-stream": {
@@ -2216,6 +2814,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2249,7 +2856,6 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -2260,11 +2866,17 @@
"node": ">= 8"
}
},
+ "node_modules/css.escape": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
+ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "dev": true,
"license": "MIT",
"bin": {
"cssesc": "bin/cssesc"
@@ -2273,6 +2885,15 @@
"node": ">=4"
}
},
+ "node_modules/culori": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/culori/-/culori-4.0.1.tgz",
+ "integrity": "sha512-LSnjA6HuIUOlkfKVbzi2OlToZE8OjFi667JWN9qNymXVXzGDmvuP60SSgC+e92sd7B7158f7Fy3Mb6rXS5EDPw==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
"node_modules/d": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
@@ -2298,6 +2919,298 @@
"node": ">=12"
}
},
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "license": "ISC",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "license": "ISC",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo-voronoi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/d3-geo-voronoi/-/d3-geo-voronoi-2.1.0.tgz",
+ "integrity": "sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-delaunay": "6",
+ "d3-geo": "3",
+ "d3-tricontour": "1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate-path": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/d3-interpolate-path/-/d3-interpolate-path-2.3.0.tgz",
+ "integrity": "sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-sankey": {
+ "version": "0.12.3",
+ "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
+ "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "1 - 2",
+ "d3-shape": "^1.2.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-array": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+ "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "internmap": "^1.0.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-sankey/node_modules/d3-shape": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+ "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-path": "1"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/internmap": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
+ "license": "ISC"
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-tile": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/d3-tile/-/d3-tile-1.0.0.tgz",
+ "integrity": "sha512-79fnTKpPMPDS5xQ0xuS9ir0165NEwwkFpe/DSOmc2Gl9ldYzKKRDWogmTTE8wAJ8NA7PMapNfEcyKhI9Lxdu5Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-tricontour": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/d3-tricontour/-/d3-tricontour-1.0.2.tgz",
+ "integrity": "sha512-HIRxHzHagPtUPNabjOlfcyismJYIsc+Xlq4mlsts4e8eAcwyq9Tgk/sYdyhlBpQ0MHwVquc/8j+e29YjXnmxeA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-delaunay": "6",
+ "d3-scale": "4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
@@ -2308,6 +3221,15 @@
"url": "https://github.com/sponsors/kossnocorp"
}
},
+ "node_modules/date-fns-tz": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.2.0.tgz",
+ "integrity": "sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "date-fns": "^3.0.0 || ^4.0.0"
+ }
+ },
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
@@ -2348,6 +3270,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/delaunator": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
+ "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
+ "license": "ISC",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -2374,6 +3305,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "license": "MIT"
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2388,6 +3331,18 @@
"node": ">= 0.4"
}
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
"node_modules/enhanced-resolve": {
"version": "5.18.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
@@ -2824,7 +3779,6 @@
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
@@ -2841,7 +3795,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
@@ -2868,7 +3821,6 @@
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
- "dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -2906,7 +3858,6 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -2973,6 +3924,22 @@
}
}
},
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/form-data": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
@@ -2993,7 +3960,6 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -3062,11 +4028,30 @@
"node": ">= 0.4"
}
},
+ "node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
@@ -3075,6 +4060,30 @@
"node": ">=10.13.0"
}
},
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/globals": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
@@ -3175,6 +4184,27 @@
"node": ">= 0.4"
}
},
+ "node_modules/human-format": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/human-format/-/human-format-1.2.1.tgz",
+ "integrity": "sha512-o5Ldz62VWR5lYUZ8aVQaLKiN37NsHnmk3xjMoUjza3mGkk8MvMofgZT0T6HKSCKSJIir+AWk9Dx8KhxvZAUgCg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3262,6 +4292,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-cidr": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-5.1.1.tgz",
@@ -3274,21 +4316,43 @@
"node": ">=14"
}
},
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -3317,7 +4381,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
@@ -3354,9 +4417,23 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
"license": "ISC"
},
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
"node_modules/jiti": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
@@ -3367,6 +4444,12 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/js-base64": {
+ "version": "3.7.7",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz",
+ "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -3428,6 +4511,60 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/layercake": {
+ "version": "8.4.3",
+ "resolved": "https://registry.npmjs.org/layercake/-/layercake-8.4.3.tgz",
+ "integrity": "sha512-PZDduaPFxgHHkxlmsz5MVBECf6ZCT39DI3LgMVvuMwrmlrtlXwXUM/elJp46zHYzCE1j+cGyDuBDxnANv94tOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "d3-array": "^3.2.4",
+ "d3-color": "^3.1.0",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.2.0"
+ },
+ "peerDependencies": {
+ "svelte": "3 - 5 || >=5.0.0-next.120",
+ "typescript": "^5.0.2"
+ }
+ },
+ "node_modules/layerchart": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/layerchart/-/layerchart-1.0.11.tgz",
+ "integrity": "sha512-1Na3BO3a32x0X8PR3vCl7zwiD0NMp90deoxOJPrmIy+34INEPeyowv7NuJ0zfjNMA4BCDTYj/1xK4nnIIh8Nmw==",
+ "license": "MIT",
+ "dependencies": {
+ "@dagrejs/dagre": "^1.1.4",
+ "@layerstack/svelte-actions": "^1.0.1",
+ "@layerstack/svelte-stores": "^1.0.2",
+ "@layerstack/tailwind": "^1.0.1",
+ "@layerstack/utils": "^1.0.1",
+ "d3-array": "^3.2.4",
+ "d3-color": "^3.1.0",
+ "d3-delaunay": "^6.0.4",
+ "d3-dsv": "^3.0.1",
+ "d3-force": "^3.0.0",
+ "d3-geo": "^3.1.1",
+ "d3-geo-voronoi": "^2.1.0",
+ "d3-hierarchy": "^3.1.2",
+ "d3-interpolate": "^3.0.1",
+ "d3-interpolate-path": "^2.3.0",
+ "d3-path": "^3.1.0",
+ "d3-quadtree": "^3.0.1",
+ "d3-random": "^3.0.1",
+ "d3-sankey": "^0.12.3",
+ "d3-scale": "^4.0.2",
+ "d3-scale-chromatic": "^3.1.0",
+ "d3-shape": "^3.2.0",
+ "d3-tile": "^1.0.0",
+ "d3-time": "^3.1.0",
+ "date-fns": "^4.1.0",
+ "layercake": "^8.4.3",
+ "lodash-es": "^4.17.21"
+ },
+ "peerDependencies": {
+ "svelte": "^3.56.0 || ^4.0.0 || ^5.0.0"
+ }
+ },
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -3691,6 +4828,12 @@
"node": ">=10"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
"node_modules/locate-character": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
@@ -3740,6 +4883,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "license": "ISC"
+ },
"node_modules/lru-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
@@ -3749,6 +4898,15 @@
"es5-ext": "~0.10.2"
}
},
+ "node_modules/lucide-svelte": {
+ "version": "0.516.0",
+ "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.516.0.tgz",
+ "integrity": "sha512-8YmF3pke1owMbtdYWscJFN47dEqJ41oXz3hVRKnYjQbDXE9uYqkE9KnAME1268mBhsC97xTqMYh07/RYrojLKA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "svelte": "^3 || ^4 || ^5.0.0-next.42"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.17",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
@@ -3790,7 +4948,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -3800,7 +4957,6 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
@@ -3814,7 +4970,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
@@ -3871,7 +5026,6 @@
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -3946,11 +5100,21 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -3978,6 +5142,33 @@
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
"license": "ISC"
},
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -4028,6 +5219,42 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/paneforge": {
+ "version": "1.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/paneforge/-/paneforge-1.0.0-next.5.tgz",
+ "integrity": "sha512-1ArDM+GMEO+o6pixEAFobhTkWkyxUDdHyw2bKruvQIXBStJmdRP7HoV4jNBZ/2i9UHDzmczxJzA3D2tKa91phw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "runed": "^0.23.4",
+ "svelte-toolbelt": "^0.7.1"
+ },
+ "peerDependencies": {
+ "svelte": "^5.20.0"
+ }
+ },
+ "node_modules/paneforge/node_modules/runed": {
+ "version": "0.23.4",
+ "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz",
+ "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/huntabyte",
+ "https://github.com/sponsors/tglide"
+ ],
+ "dependencies": {
+ "esm-env": "^1.0.0"
+ },
+ "peerDependencies": {
+ "svelte": "^5.7.0"
+ }
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -4055,12 +5282,33 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "license": "MIT"
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4080,11 +5328,28 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -4109,6 +5374,42 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
+ "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
"node_modules/postcss-load-config": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
@@ -4149,6 +5450,44 @@
"node": ">= 6"
}
},
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-nested/node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/postcss-safe-parser": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
@@ -4217,6 +5556,12 @@
"node": ">=4"
}
},
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "license": "MIT"
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -4353,7 +5698,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -4370,6 +5714,15 @@
],
"license": "MIT"
},
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@@ -4384,6 +5737,26 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -4398,13 +5771,18 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true,
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
+ "license": "Unlicense"
+ },
"node_modules/rollup": {
"version": "4.43.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz",
@@ -4456,7 +5834,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -4492,6 +5869,12 @@
"svelte": "^5.7.0"
}
},
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/sade": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
@@ -4504,6 +5887,12 @@
"node": ">=6"
}
},
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -4528,7 +5917,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -4541,12 +5929,23 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/sirv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz",
@@ -4566,12 +5965,107 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -4595,6 +6089,37 @@
"inline-style-parser": "0.2.4"
}
},
+ "node_modules/sucrase": {
+ "version": "3.35.0",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
+ "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "glob": "^10.3.10",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/sucrase/node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/super-regex": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/super-regex/-/super-regex-0.2.0.tgz",
@@ -4625,6 +6150,18 @@
"node": ">=8"
}
},
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/svelte": {
"version": "5.34.3",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.34.3.tgz",
@@ -5214,6 +6751,19 @@
"svelte": "^5.7.0"
}
},
+ "node_modules/tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tabulator-tables": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-6.3.1.tgz",
+ "integrity": "sha512-qFW7kfadtcaISQIibKAIy0f3eeIXUVi8242Vly1iJfMD79kfEGzfczNuPBN/80hDxHzQJXYbmJ8VipI40hQtfA==",
+ "license": "MIT"
+ },
"node_modules/tailwind-merge": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
@@ -5288,6 +6838,27 @@
"node": ">=18"
}
},
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/time-span": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz",
@@ -5347,7 +6918,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
@@ -5379,6 +6949,12 @@
"typescript": ">=4.8.4"
}
},
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "license": "Apache-2.0"
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -5418,7 +6994,6 @@
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
- "dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -5465,7 +7040,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true,
"license": "MIT"
},
"node_modules/validator": {
@@ -5575,7 +7149,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -5597,6 +7170,94 @@
"node": ">=0.10.0"
}
},
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/yallist": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
@@ -5611,10 +7272,7 @@
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
"integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
- "dev": true,
"license": "ISC",
- "optional": true,
- "peer": true,
"bin": {
"yaml": "bin.mjs"
},
diff --git a/new-web/package.json b/new-web/package.json
index db32ae21..9b08831e 100644
--- a/new-web/package.json
+++ b/new-web/package.json
@@ -17,19 +17,22 @@
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@iconify/svelte": "^5.0.0",
- "@lucide/svelte": "^0.516.0",
+ "@internationalized/date": "^3.8.2",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/vite": "^4.0.0",
+ "@types/tabulator-tables": "^6.2.6",
+ "bits-ui": "^2.8.0",
"clsx": "^2.1.1",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^3.0.0",
"globals": "^16.0.0",
"mode-watcher": "^1.0.8",
+ "paneforge": "^1.0.0-next.5",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
@@ -45,14 +48,24 @@
"vite": "^6.2.6"
},
"dependencies": {
+ "@battlefieldduck/xterm-svelte": "^2.1.0",
"@fontsource/noto-sans": "^5.2.7",
"@layerstack/svelte-stores": "^1.0.2",
+ "@lucide/svelte": "^0.516.0",
+ "@svelte-put/shortcut": "^4.1.0",
+ "@sveltestack/svelte-query": "^1.6.0",
"adze": "^2.2.4",
"axios": "^1.10.0",
+ "d3-shape": "^3.2.0",
"date-fns": "^4.1.0",
+ "date-fns-tz": "^3.2.0",
+ "human-format": "^1.2.1",
"is-cidr": "^5.1.1",
"is-ip": "^5.0.1",
+ "layerchart": "^1.0.11",
+ "lucide-svelte": "^0.516.0",
"svelte-i18n": "^4.0.1",
+ "tabulator-tables": "^6.3.1",
"validator": "^13.15.15",
"zod": "^3.25.67"
}
diff --git a/new-web/src/app.css b/new-web/src/app.css
index e48f5f09..68f1002a 100644
--- a/new-web/src/app.css
+++ b/new-web/src/app.css
@@ -1,121 +1,125 @@
-@import "tailwindcss";
+@import 'tailwindcss';
-@import "tw-animate-css";
+@import 'tw-animate-css';
@custom-variant dark (&:is(.dark *));
:root {
- --radius: 0.625rem;
- --background: oklch(1 0 0);
- --foreground: oklch(0.129 0.042 264.695);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.129 0.042 264.695);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.129 0.042 264.695);
- --primary: oklch(0.208 0.042 265.755);
- --primary-foreground: oklch(0.984 0.003 247.858);
- --secondary: oklch(0.968 0.007 247.896);
- --secondary-foreground: oklch(0.208 0.042 265.755);
- --muted: oklch(0.968 0.007 247.896);
- --muted-foreground: oklch(0.554 0.046 257.417);
- --accent: oklch(0.968 0.007 247.896);
- --accent-foreground: oklch(0.208 0.042 265.755);
- --destructive: oklch(0.577 0.245 27.325);
- --border: oklch(0.929 0.013 255.508);
- --input: oklch(0.929 0.013 255.508);
- --ring: oklch(0.704 0.04 256.788);
- --chart-1: oklch(0.646 0.222 41.116);
- --chart-2: oklch(0.6 0.118 184.704);
- --chart-3: oklch(0.398 0.07 227.392);
- --chart-4: oklch(0.828 0.189 84.429);
- --chart-5: oklch(0.769 0.188 70.08);
- --sidebar: oklch(0.984 0.003 247.858);
- --sidebar-foreground: oklch(0.129 0.042 264.695);
- --sidebar-primary: oklch(0.208 0.042 265.755);
- --sidebar-primary-foreground: oklch(0.984 0.003 247.858);
- --sidebar-accent: oklch(0.968 0.007 247.896);
- --sidebar-accent-foreground: oklch(0.208 0.042 265.755);
- --sidebar-border: oklch(0.929 0.013 255.508);
- --sidebar-ring: oklch(0.704 0.04 256.788);
+ --radius: 0.625rem;
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.145 0 0);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.145 0 0);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.145 0 0);
+ --primary: oklch(0.205 0 0);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.97 0 0);
+ --secondary-foreground: oklch(0.205 0 0);
+ --muted: oklch(0.97 0 0);
+ --muted-foreground: oklch(0.556 0 0);
+ --accent: oklch(0.97 0 0);
+ --accent-foreground: oklch(0.205 0 0);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.922 0 0);
+ --input: oklch(0.922 0 0);
+ --ring: oklch(0.708 0 0);
+ --chart-1: oklch(0.646 0.222 41.116);
+ --chart-2: oklch(0.6 0.118 184.704);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+ --sidebar: oklch(0.985 0 0);
+ --sidebar-foreground: oklch(0.145 0 0);
+ --sidebar-primary: oklch(0.205 0 0);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.97 0 0);
+ --sidebar-accent-foreground: oklch(0.205 0 0);
+ --sidebar-border: oklch(0.922 0 0);
+ --sidebar-ring: oklch(0.708 0 0);
}
.dark {
- --background: oklch(0.129 0.042 264.695);
- --foreground: oklch(0.984 0.003 247.858);
- --card: oklch(0.208 0.042 265.755);
- --card-foreground: oklch(0.984 0.003 247.858);
- --popover: oklch(0.208 0.042 265.755);
- --popover-foreground: oklch(0.984 0.003 247.858);
- --primary: oklch(0.929 0.013 255.508);
- --primary-foreground: oklch(0.208 0.042 265.755);
- --secondary: oklch(0.279 0.041 260.031);
- --secondary-foreground: oklch(0.984 0.003 247.858);
- --muted: oklch(0.279 0.041 260.031);
- --muted-foreground: oklch(0.704 0.04 256.788);
- --accent: oklch(0.279 0.041 260.031);
- --accent-foreground: oklch(0.984 0.003 247.858);
- --destructive: oklch(0.704 0.191 22.216);
- --border: oklch(1 0 0 / 10%);
- --input: oklch(1 0 0 / 15%);
- --ring: oklch(0.551 0.027 264.364);
- --chart-1: oklch(0.488 0.243 264.376);
- --chart-2: oklch(0.696 0.17 162.48);
- --chart-3: oklch(0.769 0.188 70.08);
- --chart-4: oklch(0.627 0.265 303.9);
- --chart-5: oklch(0.645 0.246 16.439);
- --sidebar: oklch(0.208 0.042 265.755);
- --sidebar-foreground: oklch(0.984 0.003 247.858);
- --sidebar-primary: oklch(0.488 0.243 264.376);
- --sidebar-primary-foreground: oklch(0.984 0.003 247.858);
- --sidebar-accent: oklch(0.279 0.041 260.031);
- --sidebar-accent-foreground: oklch(0.984 0.003 247.858);
- --sidebar-border: oklch(1 0 0 / 10%);
- --sidebar-ring: oklch(0.551 0.027 264.364);
+ --background: oklch(0.145 0 0);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.145 0 0);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.205 0 0);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.922 0 0);
+ --primary-foreground: oklch(0.205 0 0);
+ --secondary: oklch(0.269 0 0);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.269 0 0);
+ --muted-foreground: oklch(0.708 0 0);
+ --accent: oklch(0.269 0 0);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.556 0 0);
+ --chart-1: oklch(0.488 0.243 264.376);
+ --chart-2: oklch(0.696 0.17 162.48);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+ --sidebar: oklch(0.205 0 0);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: oklch(0.488 0.243 264.376);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.269 0 0);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: oklch(0.556 0 0);
}
@theme inline {
- --radius-sm: calc(var(--radius) - 4px);
- --radius-md: calc(var(--radius) - 2px);
- --radius-lg: var(--radius);
- --radius-xl: calc(var(--radius) + 4px);
- --color-background: var(--background);
- --color-foreground: var(--foreground);
- --color-card: var(--card);
- --color-card-foreground: var(--card-foreground);
- --color-popover: var(--popover);
- --color-popover-foreground: var(--popover-foreground);
- --color-primary: var(--primary);
- --color-primary-foreground: var(--primary-foreground);
- --color-secondary: var(--secondary);
- --color-secondary-foreground: var(--secondary-foreground);
- --color-muted: var(--muted);
- --color-muted-foreground: var(--muted-foreground);
- --color-accent: var(--accent);
- --color-accent-foreground: var(--accent-foreground);
- --color-destructive: var(--destructive);
- --color-border: var(--border);
- --color-input: var(--input);
- --color-ring: var(--ring);
- --color-chart-1: var(--chart-1);
- --color-chart-2: var(--chart-2);
- --color-chart-3: var(--chart-3);
- --color-chart-4: var(--chart-4);
- --color-chart-5: var(--chart-5);
- --color-sidebar: var(--sidebar);
- --color-sidebar-foreground: var(--sidebar-foreground);
- --color-sidebar-primary: var(--sidebar-primary);
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
- --color-sidebar-accent: var(--sidebar-accent);
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
- --color-sidebar-border: var(--sidebar-border);
- --color-sidebar-ring: var(--sidebar-ring);
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
- * {
- @apply border-border outline-ring/50;
- }
- body {
- @apply bg-background text-foreground;
- }
-}
\ No newline at end of file
+ * {
+ @apply border-border outline-ring/50;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+@layer components {
+ @import './css/tabulator_simple.scss';
+}
diff --git a/new-web/src/app.html b/new-web/src/app.html
index 77a5ff52..2f615ff9 100644
--- a/new-web/src/app.html
+++ b/new-web/src/app.html
@@ -3,6 +3,7 @@
+
%sveltekit.head%
diff --git a/new-web/src/css/tabulator.scss b/new-web/src/css/tabulator.scss
new file mode 100644
index 00000000..e9edcafa
--- /dev/null
+++ b/new-web/src/css/tabulator.scss
@@ -0,0 +1,1657 @@
+@use 'sass:color';
+
+//Main Theme Variables
+$backgroundColor: #888 !default; //background color of tabulator
+$borderColor: #999 !default; //border to tabulator
+$textSize: 14px !default; //table text size
+
+//header theming
+$headerBackgroundColor: #e6e6e6 !default; //border to tabulator
+$headerTextColor: #555 !default; //header text color
+$headerBorderColor: #aaa !default; //header border color
+$headerSeparatorColor: #999 !default; //header bottom separator color
+$headerMargin: 4px !default; //padding round header.io
+//column header arrows
+$sortArrowHover: #555 !default;
+$sortArrowActive: #666 !default;
+$sortArrowInactive: #bbb !default;
+
+$columnResizeGuideColor: #999 !default;
+
+//row theming
+$rowBackgroundColor: #fff !default; //table row background color
+$rowAltBackgroundColor: #efefef !default; //table row background color
+$rowBorderColor: #aaa !default; //table border color
+$rowTextColor: #333 !default; //table text color
+$rowHoverBackground: #bbb !default; //row background color on hover
+
+$rowSelectedBackground: #9abcea !default; //row background color when selected
+$rowSelectedBackgroundHover: #769bcc !default; //row background color when selected and hovered
+
+$editBoxColor: #1d68cd !default; //border color for edit boxes
+$errorColor: #dd0000 !default; //error indication
+
+//footer theming
+$footerBackgroundColor: #e6e6e6 !default; //border to tabulator
+$footerTextColor: #555 !default; //footer text color
+$footerBorderColor: #aaa !default; //footer border color
+$footerSeparatorColor: #999 !default; //footer bottom separator color
+$footerActiveColor: #d00 !default; //footer bottom active text color
+
+$spreadsheetActiveTabColor: #fff !default; //color for the active spreadsheet tab
+
+//range selection
+$rangeBorderColor: #2975dd !default; //range border color
+$rangeHandleColor: $rangeBorderColor !default; //range handle color
+$rangeHeaderSelectedBackground: #3876ca !default; //header background color when selected
+$rangeHeaderSelectedTextColor: #ffffff !default; //header text color when selected
+$rangeHeaderHighlightBackground: #d6d6d6 !default; //header background color when highlighted
+$rangeHeaderTextHighlightBackground: #000000 !default; //header text color when highlighted
+
+//Tabulator Containing Element
+.tabulator {
+ position: relative;
+
+ border: 1px solid $borderColor;
+
+ background-color: $backgroundColor;
+
+ font-size: $textSize;
+ text-align: left;
+ overflow: hidden;
+
+ -webkit-transform: translateZ(0);
+ -moz-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ -o-transform: translateZ(0);
+ transform: translateZ(0);
+
+ &[tabulator-layout='fitDataFill'] {
+ .tabulator-tableholder {
+ .tabulator-table {
+ min-width: 100%;
+ }
+ }
+ }
+
+ &[tabulator-layout='fitDataTable'] {
+ display: inline-block;
+ }
+
+ &.tabulator-block-select {
+ user-select: none;
+ }
+
+ &.tabulator-ranges {
+ .tabulator-cell:not(.tabulator-editing) {
+ user-select: none;
+ }
+ }
+
+ //column header containing element
+ .tabulator-header {
+ position: relative;
+ box-sizing: border-box;
+
+ width: 100%;
+
+ border-bottom: 1px solid $headerSeparatorColor;
+ background-color: $headerBackgroundColor;
+ color: $headerTextColor;
+ font-weight: bold;
+
+ white-space: nowrap;
+ overflow: hidden;
+
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ -o-user-select: none;
+
+ outline: none;
+
+ &.tabulator-header-hidden {
+ display: none;
+ }
+
+ .tabulator-header-contents {
+ position: relative;
+ overflow: hidden;
+
+ .tabulator-headers {
+ display: inline-block;
+ }
+ }
+
+ //individual column header element
+ .tabulator-col {
+ display: inline-flex;
+ position: relative;
+ box-sizing: border-box;
+
+ flex-direction: column;
+ justify-content: flex-start;
+
+ border-right: 1px solid $headerBorderColor;
+ background: $headerBackgroundColor;
+
+ text-align: left;
+ vertical-align: bottom;
+ overflow: hidden;
+
+ &.tabulator-moving {
+ position: absolute;
+ border: 1px solid $headerSeparatorColor;
+ background: color.adjust($headerBackgroundColor, $lightness: -10%);
+ pointer-events: none;
+ }
+
+ &.tabulator-range-highlight {
+ background-color: $rangeHeaderHighlightBackground;
+ color: $rangeHeaderTextHighlightBackground;
+ }
+
+ &.tabulator-range-selected {
+ background-color: $rangeHeaderSelectedBackground;
+ color: $rangeHeaderSelectedTextColor;
+ }
+
+ //hold content of column header
+ .tabulator-col-content {
+ box-sizing: border-box;
+ position: relative;
+ padding: 4px;
+
+ //header menu button
+ .tabulator-header-popup-button {
+ padding: 0 8px;
+
+ &:hover {
+ cursor: pointer;
+ opacity: 0.6;
+ }
+ }
+
+ //hold title and sort arrow
+ .tabulator-col-title-holder {
+ position: relative;
+ }
+
+ //hold title of column header
+ .tabulator-col-title {
+ box-sizing: border-box;
+ width: 100%;
+
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: bottom;
+
+ &.tabulator-col-title-wrap {
+ white-space: normal;
+ text-overflow: initial;
+ }
+
+ //element to hold title editor
+ .tabulator-title-editor {
+ box-sizing: border-box;
+ width: 100%;
+
+ border: 1px solid #999;
+
+ padding: 1px;
+
+ background: #fff;
+ }
+
+ .tabulator-header-popup-button + .tabulator-title-editor {
+ width: calc(100% - 22px);
+ }
+ }
+
+ //column sorter arrow
+ .tabulator-col-sorter {
+ display: flex;
+ align-items: center;
+
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 4px;
+
+ .tabulator-arrow {
+ width: 0;
+ height: 0;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid $sortArrowInactive;
+ }
+ }
+ }
+
+ //complex header column group
+ &.tabulator-col-group {
+ //gelement to hold sub columns in column group
+ .tabulator-col-group-cols {
+ position: relative;
+ display: flex;
+
+ border-top: 1px solid $headerBorderColor;
+ overflow: hidden;
+
+ margin-right: -1px;
+ }
+ }
+
+ //header filter containing element
+ .tabulator-header-filter {
+ position: relative;
+ box-sizing: border-box;
+ margin-top: 2px;
+ width: 100%;
+ text-align: center;
+
+ //styling adjustment for inbuilt editors
+ textarea {
+ height: auto !important;
+ }
+
+ svg {
+ margin-top: 3px;
+ }
+
+ input {
+ &::-ms-clear {
+ width: 0;
+ height: 0;
+ }
+ }
+ }
+
+ //styling child elements for sortable columns
+ &.tabulator-sortable {
+ .tabulator-col-title {
+ padding-right: 25px;
+ }
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-col-sorter-element:hover {
+ @apply cursor-pointer bg-neutral-200;
+ }
+ }
+
+ &[aria-sort='none'] {
+ .tabulator-col-content .tabulator-col-sorter {
+ color: $sortArrowInactive;
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-col-sorter-element .tabulator-arrow:hover {
+ cursor: pointer;
+ border-bottom: 6px solid $sortArrowHover;
+ }
+ }
+
+ .tabulator-arrow {
+ border-top: none;
+ border-bottom: 6px solid $sortArrowInactive;
+ }
+ }
+ }
+
+ &[aria-sort='ascending'] {
+ .tabulator-col-content .tabulator-col-sorter {
+ color: $sortArrowActive;
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-col-sorter-element .tabulator-arrow:hover {
+ cursor: pointer;
+ border-bottom: 6px solid $sortArrowHover;
+ }
+ }
+
+ .tabulator-arrow {
+ border-top: none;
+ border-bottom: 6px solid $sortArrowActive;
+ }
+ }
+ }
+
+ &[aria-sort='descending'] {
+ .tabulator-col-content .tabulator-col-sorter {
+ color: $sortArrowActive;
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-col-sorter-element .tabulator-arrow:hover {
+ cursor: pointer;
+ border-top: 6px solid $sortArrowHover;
+ }
+ }
+
+ .tabulator-arrow {
+ border-bottom: none;
+ border-top: 6px solid $sortArrowActive;
+ color: $sortArrowActive;
+ }
+ }
+ }
+ }
+
+ &.tabulator-col-vertical {
+ .tabulator-col-content {
+ .tabulator-col-title {
+ writing-mode: vertical-rl;
+ text-orientation: mixed;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ }
+
+ &.tabulator-col-vertical-flip {
+ .tabulator-col-title {
+ transform: rotate(180deg);
+ }
+ }
+
+ &.tabulator-sortable {
+ .tabulator-col-title {
+ padding-right: 0;
+ padding-top: 20px;
+ }
+
+ &.tabulator-col-vertical-flip {
+ .tabulator-col-title {
+ padding-right: 0;
+ padding-bottom: 20px;
+ }
+ }
+
+ .tabulator-col-sorter {
+ justify-content: center;
+ left: 0;
+ right: 0;
+ top: 4px;
+ bottom: auto;
+ }
+ }
+ }
+ }
+
+ .tabulator-frozen {
+ position: sticky;
+ left: 0;
+
+ // background-color: inherit;
+
+ z-index: 11;
+
+ &.tabulator-frozen-left {
+ border-right: 2px solid $rowBorderColor;
+ }
+
+ &.tabulator-frozen-right {
+ border-left: 2px solid $rowBorderColor;
+ }
+ }
+
+ .tabulator-calcs-holder {
+ box-sizing: border-box;
+ display: inline-block;
+
+ background: color.adjust($headerBackgroundColor, $lightness: 5%) !important;
+
+ border-top: 1px solid $rowBorderColor;
+ border-bottom: 1px solid $headerBorderColor;
+
+ // overflow: hidden;
+
+ .tabulator-row {
+ background: color.adjust($headerBackgroundColor, $lightness: 5%) !important;
+
+ .tabulator-col-resize-handle {
+ display: none;
+ }
+ }
+ }
+
+ .tabulator-frozen-rows-holder {
+ display: inline-block;
+ &:empty {
+ display: none;
+ }
+ }
+ }
+
+ //scrolling element to hold table
+ .tabulator-tableholder {
+ position: relative;
+ width: 100%;
+ white-space: nowrap;
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+
+ &:focus {
+ outline: none;
+ }
+
+ //default placeholder element
+ .tabulator-placeholder {
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ min-width: 100%;
+ width: 100%;
+
+ &[tabulator-render-mode='virtual'] {
+ min-height: 100%;
+ }
+
+ .tabulator-placeholder-contents {
+ display: inline-block;
+ text-align: center;
+
+ padding: 10px;
+
+ color: #ccc;
+ font-weight: bold;
+ font-size: 20px;
+
+ white-space: normal;
+ }
+ }
+
+ //element to hold table rows
+ .tabulator-table {
+ position: relative;
+ display: inline-block;
+ background-color: $rowBackgroundColor;
+ white-space: nowrap;
+ overflow: visible;
+ color: $rowTextColor;
+
+ //row element
+ .tabulator-row {
+ &.tabulator-calcs {
+ font-weight: bold;
+ background: color.adjust($rowAltBackgroundColor, $lightness: -5%) !important;
+
+ &.tabulator-calcs-top {
+ border-bottom: 2px solid $rowBorderColor;
+ }
+
+ &.tabulator-calcs-bottom {
+ border-top: 2px solid $rowBorderColor;
+ }
+ }
+ }
+ }
+
+ .tabulator-range-overlay {
+ position: absolute;
+ inset: 0;
+ z-index: 10;
+ pointer-events: none;
+
+ .tabulator-range {
+ position: absolute;
+ box-sizing: border-box;
+ border: 1px solid $rangeBorderColor;
+
+ &.tabulator-range-active::after {
+ content: '';
+ position: absolute;
+ right: -3px;
+ bottom: -3px;
+ width: 6px;
+ height: 6px;
+ background-color: $rangeHandleColor;
+ border-radius: 999px;
+ }
+ }
+
+ .tabulator-range-cell-active {
+ position: absolute;
+ box-sizing: border-box;
+ border: 2px solid $rangeBorderColor;
+ }
+ }
+ }
+
+ //footer element
+ .tabulator-footer {
+ border-top: 1px solid $footerSeparatorColor;
+ background-color: $footerBackgroundColor;
+
+ color: $footerTextColor;
+ font-weight: bold;
+ white-space: nowrap;
+ user-select: none;
+
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ -o-user-select: none;
+
+ .tabulator-footer-contents {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+
+ padding: 5px 10px;
+
+ &:empty {
+ display: none;
+ }
+ }
+
+ .tabulator-spreadsheet-tabs {
+ margin-top: -5px;
+
+ overflow-x: auto;
+
+ .tabulator-spreadsheet-tab {
+ display: inline-block;
+ padding: 5px;
+
+ border: $borderColor 1px solid;
+ border-top: none;
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+
+ font-size: 0.9em;
+
+ &:hover {
+ cursor: pointer;
+ opacity: 0.7;
+ }
+
+ &.tabulator-spreadsheet-tab-active {
+ background: $spreadsheetActiveTabColor;
+ }
+ }
+ }
+
+ .tabulator-calcs-holder {
+ box-sizing: border-box;
+ width: 100%;
+
+ text-align: left;
+
+ background: color.adjust($footerBackgroundColor, $lightness: 5%) !important;
+ border-bottom: 1px solid $rowBorderColor;
+ border-top: 1px solid $rowBorderColor;
+
+ overflow: hidden;
+
+ .tabulator-row {
+ display: inline-block;
+ background: color.adjust($footerBackgroundColor, $lightness: 5%) !important;
+
+ .tabulator-col-resize-handle {
+ display: none;
+ }
+ }
+
+ &:only-child {
+ margin-bottom: -5px;
+ border-bottom: none;
+ }
+ }
+
+ & > * + .tabulator-page-counter {
+ margin-left: 10px;
+ }
+
+ .tabulator-page-counter {
+ font-weight: normal;
+ }
+
+ .tabulator-paginator {
+ flex: 1;
+
+ text-align: right;
+
+ color: $footerTextColor;
+ font-family: inherit;
+ font-weight: inherit;
+ font-size: inherit;
+ }
+
+ //pagination container element
+ .tabulator-page-size {
+ display: inline-block;
+
+ margin: 0 5px;
+ padding: 2px 5px;
+
+ border: 1px solid $footerBorderColor;
+ border-radius: 3px;
+ }
+
+ .tabulator-pages {
+ margin: 0 7px;
+ }
+
+ //pagination button
+ .tabulator-page {
+ display: inline-block;
+
+ margin: 0 2px;
+ padding: 2px 5px;
+
+ border: 1px solid $footerBorderColor;
+ border-radius: 3px;
+
+ background: rgba(255, 255, 255, 0.2);
+
+ &.active {
+ color: $footerActiveColor;
+ }
+
+ &:disabled {
+ opacity: 0.5;
+ }
+
+ &:not(disabled) {
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ cursor: pointer;
+ background: rgba(0, 0, 0, 0.2);
+ color: #fff;
+ }
+ }
+ }
+ }
+ }
+
+ //column resize handles
+ .tabulator-col-resize-handle {
+ position: relative;
+ display: inline-block;
+ width: 6px;
+
+ margin-left: -3px;
+ margin-right: -3px;
+
+ z-index: 11;
+ vertical-align: middle;
+
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ cursor: ew-resize;
+ }
+ }
+
+ &:last-of-type {
+ width: 3px;
+ margin-right: 0;
+ }
+ }
+
+ //column resize guide
+ .tabulator-col-resize-guide {
+ position: absolute;
+ top: 0;
+ width: 4px;
+ height: 100%;
+ margin-left: -0.5px;
+ background-color: $columnResizeGuideColor;
+ opacity: 0.5;
+ }
+
+ //row resize guide
+ .tabulator-row-resize-guide {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ height: 4px;
+ margin-top: -0.5px;
+ background-color: $columnResizeGuideColor;
+ opacity: 0.5;
+ }
+
+ //holding div that contains loader and covers tabulator element to prevent interaction
+ .tabulator-alert {
+ position: absolute;
+ display: flex;
+ align-items: center;
+
+ top: 0;
+ left: 0;
+ z-index: 100;
+
+ height: 100%;
+ width: 100%;
+ background: rgba(0, 0, 0, 0.4);
+ text-align: center;
+
+ //loading message element
+ .tabulator-alert-msg {
+ display: inline-block;
+
+ margin: 0 auto;
+ padding: 10px 20px;
+
+ border-radius: 10px;
+
+ background: #fff;
+ font-weight: bold;
+ font-size: 16px;
+
+ //loading message
+ &.tabulator-alert-state-msg {
+ border: 4px solid #333;
+ color: #000;
+ }
+
+ //error message
+ &.tabulator-alert-state-error {
+ border: 4px solid #d00;
+ color: #590000;
+ }
+ }
+ }
+}
+
+//row element
+.tabulator-row {
+ position: relative;
+ box-sizing: border-box;
+ min-height: $textSize + ($headerMargin * 2);
+ background-color: $rowBackgroundColor;
+
+ &.tabulator-row-even {
+ background-color: $rowAltBackgroundColor;
+ }
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-selectable:hover {
+ background-color: $rowHoverBackground;
+ cursor: pointer;
+ }
+ }
+
+ &.tabulator-selected {
+ background-color: $rowSelectedBackground;
+ }
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-selected:hover {
+ background-color: $rowSelectedBackgroundHover;
+ cursor: pointer;
+ }
+ }
+
+ &.tabulator-row-moving {
+ border: 1px solid #000;
+ background: #fff;
+ }
+
+ &.tabulator-moving {
+ position: absolute;
+
+ border-top: 1px solid $rowBorderColor;
+ border-bottom: 1px solid $rowBorderColor;
+
+ pointer-events: none;
+ z-index: 15;
+ }
+
+ &.tabulator-range-highlight {
+ .tabulator-cell.tabulator-range-row-header {
+ background-color: $rangeHeaderHighlightBackground;
+ color: $rangeHeaderTextHighlightBackground;
+ }
+
+ &.tabulator-range-selected {
+ .tabulator-cell.tabulator-range-row-header {
+ background-color: $rangeHeaderSelectedBackground;
+ color: $rangeHeaderSelectedTextColor;
+ }
+ }
+ }
+
+ &.tabulator-range-selected {
+ .tabulator-cell.tabulator-range-row-header {
+ background-color: $rangeHeaderSelectedBackground;
+ color: $rangeHeaderSelectedTextColor;
+ }
+ }
+
+ //row resize handles
+ .tabulator-row-resize-handle {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ height: 5px;
+
+ &.prev {
+ top: 0;
+ bottom: auto;
+ }
+
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ cursor: ns-resize;
+ }
+ }
+ }
+
+ .tabulator-responsive-collapse {
+ box-sizing: border-box;
+
+ padding: 5px;
+
+ border-top: 1px solid $rowBorderColor;
+ border-bottom: 1px solid $rowBorderColor;
+
+ &:empty {
+ display: none;
+ }
+
+ table {
+ font-size: $textSize;
+
+ tr {
+ td {
+ position: relative;
+
+ &:first-of-type {
+ padding-right: 10px;
+ }
+ }
+ }
+ }
+ }
+
+ //cell element
+ .tabulator-cell {
+ display: inline-block;
+ position: relative;
+ box-sizing: border-box;
+ padding: 4px;
+ // border-right:1px solid $rowBorderColor;
+ vertical-align: middle;
+ white-space: nowrap;
+ overflow: hidden;
+ // text-overflow: ellipsis;
+ outline: none;
+
+ &.tabulator-row-header {
+ border-right: 1px solid $borderColor;
+ border-bottom: 1px solid $rowBorderColor;
+ background: $headerBackgroundColor;
+ }
+
+ &.tabulator-frozen {
+ display: inline-block;
+ position: sticky;
+
+ left: 0;
+
+ background-color: inherit;
+
+ z-index: 11;
+
+ &.tabulator-frozen-left {
+ border: none;
+ }
+
+ &.tabulator-frozen-right {
+ border: none;
+ }
+ }
+
+ &.tabulator-editing {
+ border: 1px solid $editBoxColor;
+ outline: none;
+
+ padding: 0;
+
+ input,
+ select {
+ border: 1px;
+ background: transparent;
+ outline: none;
+ }
+ }
+
+ &.tabulator-validation-fail {
+ border: 1px solid $errorColor;
+
+ input,
+ select {
+ border: 1px;
+ background: transparent;
+
+ color: $errorColor;
+ }
+ }
+
+ //movable row handle
+ &.tabulator-row-handle {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ -o-user-select: none;
+
+ //handle holder
+ .tabulator-row-handle-box {
+ width: 80%;
+
+ //Hamburger element
+ .tabulator-row-handle-bar {
+ width: 100%;
+ height: 3px;
+ margin-top: 2px;
+ background: #666;
+ }
+ }
+ }
+
+ &.tabulator-range-selected:not(.tabulator-range-only-cell-selected):not(
+ .tabulator-range-row-header
+ ) {
+ background-color: $rowSelectedBackground;
+ }
+
+ .tabulator-data-tree-branch-empty {
+ display: inline-block;
+ width: 7px;
+ }
+
+ .tabulator-data-tree-branch {
+ display: inline-block;
+ vertical-align: middle;
+
+ height: 9px;
+ width: 7px;
+
+ margin-top: -9px;
+ margin-right: 5px;
+
+ border-bottom-left-radius: 1px;
+
+ border-left: 2px solid $rowBorderColor;
+ border-bottom: 2px solid $rowBorderColor;
+ }
+
+ .tabulator-data-tree-control {
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ vertical-align: middle;
+
+ height: 11px;
+ width: 11px;
+
+ margin-right: 5px;
+
+ border: 1px solid $rowTextColor;
+ border-radius: 2px;
+ background: rgba(0, 0, 0, 0.1);
+
+ overflow: hidden;
+
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ cursor: pointer;
+ background: rgba(0, 0, 0, 0.2);
+ }
+ }
+
+ .tabulator-data-tree-control-collapse {
+ display: inline-block;
+ position: relative;
+
+ height: 7px;
+ width: 1px;
+
+ background: transparent;
+
+ &:after {
+ position: absolute;
+ content: '';
+ left: -3px;
+ top: 3px;
+
+ height: 1px;
+ width: 7px;
+
+ background: $rowTextColor;
+ }
+ }
+
+ .tabulator-data-tree-control-expand {
+ display: inline-block;
+ position: relative;
+
+ height: 7px;
+ width: 1px;
+
+ background: $rowTextColor;
+
+ &:after {
+ position: absolute;
+ content: '';
+ left: -3px;
+ top: 3px;
+
+ height: 1px;
+ width: 7px;
+
+ background: $rowTextColor;
+ }
+ }
+ }
+
+ .tabulator-responsive-collapse-toggle {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ -o-user-select: none;
+
+ height: 15px;
+ width: 15px;
+
+ border-radius: 20px;
+ background: #666;
+
+ color: $rowBackgroundColor;
+ font-weight: bold;
+ font-size: 1.1em;
+
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ opacity: 0.7;
+ cursor: pointer;
+ }
+ }
+
+ &.open {
+ .tabulator-responsive-collapse-toggle-close {
+ display: initial;
+ }
+
+ .tabulator-responsive-collapse-toggle-open {
+ display: none;
+ }
+ }
+
+ svg {
+ stroke: $rowBackgroundColor;
+ }
+
+ .tabulator-responsive-collapse-toggle-close {
+ display: none;
+ }
+ }
+
+ .tabulator-traffic-light {
+ display: inline-block;
+ height: 14px;
+ width: 14px;
+
+ border-radius: 14px;
+ }
+ }
+
+ //row grouping element
+ &.tabulator-group {
+ box-sizing: border-box;
+ border-bottom: 1px solid #999;
+ border-right: 1px solid $rowBorderColor;
+ border-top: 1px solid #999;
+ padding: 5px;
+ padding-left: 10px;
+ background: #ccc;
+ font-weight: bold;
+
+ min-width: 100%;
+
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ cursor: pointer;
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+ }
+
+ &.tabulator-group-visible {
+ .tabulator-arrow {
+ margin-right: 10px;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-top: 6px solid $sortArrowActive;
+ border-bottom: 0;
+ }
+ }
+
+ &.tabulator-group-level-1 {
+ padding-left: 30px;
+ }
+
+ &.tabulator-group-level-2 {
+ padding-left: 50px;
+ }
+
+ &.tabulator-group-level-3 {
+ padding-left: 70px;
+ }
+
+ &.tabulator-group-level-4 {
+ padding-left: 90px;
+ }
+
+ &.tabulator-group-level-5 {
+ padding-left: 110px;
+ }
+
+ .tabulator-group-toggle {
+ display: inline-block;
+ }
+
+ //sorting arrow
+ .tabulator-arrow {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-right: 16px;
+ border-top: 6px solid transparent;
+ border-bottom: 6px solid transparent;
+ border-right: 0;
+ border-left: 6px solid $sortArrowActive;
+ vertical-align: middle;
+ }
+
+ span {
+ margin-left: 10px;
+ color: #d00;
+ }
+ }
+}
+
+.tabulator-toggle {
+ box-sizing: border-box;
+
+ display: flex;
+ flex-direction: row;
+
+ border: 1px solid #ccc;
+ background: #dcdcdc;
+
+ &.tabulator-toggle-on {
+ background: #1c6cc2;
+ }
+
+ .tabulator-toggle-switch {
+ box-sizing: border-box;
+ border: 1px solid #ccc;
+
+ background: #fff;
+ }
+}
+
+.tabulator-popup-container {
+ position: absolute;
+ display: inline-block;
+ box-sizing: border-box;
+
+ background: $rowBackgroundColor;
+ border: 1px solid $rowBorderColor;
+ box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2);
+
+ font-size: $textSize;
+
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+
+ z-index: 10000;
+}
+
+.tabulator-popup {
+ padding: 5px;
+
+ border-radius: 3px;
+}
+
+.tabulator-tooltip {
+ max-width: Min(500px, 100%);
+
+ padding: 3px 5px;
+
+ border-radius: 2px;
+ box-shadow: none;
+
+ font-size: 12px;
+
+ pointer-events: none;
+}
+
+.tabulator-menu {
+ .tabulator-menu-item {
+ position: relative;
+ box-sizing: border-box;
+
+ padding: 5px 10px;
+
+ user-select: none;
+
+ &.tabulator-menu-item-disabled {
+ opacity: 0.5;
+ }
+
+ @media (hover: hover) and (pointer: fine) {
+ &:not(.tabulator-menu-item-disabled):hover {
+ cursor: pointer;
+ background: $rowAltBackgroundColor;
+ }
+ }
+
+ &.tabulator-menu-item-submenu {
+ padding-right: 25px;
+
+ &::after {
+ display: inline-block;
+ position: absolute;
+
+ top: calc(5px + 0.4em);
+ right: 10px;
+ height: 7px;
+ width: 7px;
+
+ content: '';
+
+ border-width: 1px 1px 0 0;
+ border-style: solid;
+ border-color: $rowBorderColor;
+ vertical-align: top;
+
+ transform: rotate(45deg);
+ }
+ }
+ }
+
+ .tabulator-menu-separator {
+ border-top: 1px solid $rowBorderColor;
+ }
+}
+
+.tabulator-edit-list {
+ max-height: 200px;
+
+ font-size: $textSize;
+
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+
+ .tabulator-edit-list-item {
+ padding: 4px;
+
+ color: $rowTextColor;
+ outline: none;
+
+ &.active {
+ color: $rowBackgroundColor;
+ background: $editBoxColor;
+
+ &.focused {
+ outline: 1px solid rgba($rowBackgroundColor, 0.5);
+ }
+ }
+
+ &.focused {
+ outline: 1px solid $editBoxColor;
+ }
+
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ cursor: pointer;
+
+ color: $rowBackgroundColor;
+ background: $editBoxColor;
+ }
+ }
+ }
+
+ .tabulator-edit-list-placeholder {
+ padding: 4px;
+
+ color: $rowTextColor;
+ text-align: center;
+ }
+
+ .tabulator-edit-list-group {
+ border-bottom: 1px solid $rowBorderColor;
+
+ padding: 4px;
+ padding-top: 6px;
+
+ color: $rowTextColor;
+ font-weight: bold;
+ }
+
+ .tabulator-edit-list-item,
+ .tabulator-edit-list-group {
+ &.tabulator-edit-list-group-level-2 {
+ padding-left: 12px;
+ }
+
+ &.tabulator-edit-list-group-level-3 {
+ padding-left: 20px;
+ }
+
+ &.tabulator-edit-list-group-level-4 {
+ padding-left: 28px;
+ }
+
+ &.tabulator-edit-list-group-level-5 {
+ padding-left: 36px;
+ }
+ }
+}
+
+//RTL Styling
+
+.tabulator.tabulator-ltr {
+ direction: ltr;
+}
+
+.tabulator.tabulator-rtl {
+ text-align: initial;
+ direction: rtl;
+
+ .tabulator-header {
+ .tabulator-col {
+ text-align: initial;
+ border-left: 1px solid $headerBorderColor;
+ border-right: initial;
+
+ &.tabulator-col-group {
+ .tabulator-col-group-cols {
+ margin-right: initial;
+ margin-left: -1px;
+ }
+ }
+
+ &.tabulator-sortable {
+ .tabulator-col-title {
+ padding-right: 0;
+ padding-left: 25px;
+ }
+ }
+
+ .tabulator-col-content {
+ .tabulator-col-sorter {
+ left: 8px;
+ right: initial;
+ }
+ }
+ }
+ }
+
+ .tabulator-tableholder {
+ .tabulator-range-overlay {
+ .tabulator-range {
+ &.tabulator-range-active::after {
+ content: '';
+ position: absolute;
+ left: -3px;
+ right: initial;
+ bottom: -3px;
+ width: 6px;
+ height: 6px;
+ background-color: $rangeHandleColor;
+ border-radius: 999px;
+ }
+ }
+ }
+ }
+
+ .tabulator-row {
+ .tabulator-cell {
+ border-right: initial;
+ border-left: 1px solid $rowBorderColor;
+
+ .tabulator-data-tree-branch {
+ margin-right: initial;
+ margin-left: 5px;
+
+ border-bottom-left-radius: initial;
+ border-bottom-right-radius: 1px;
+
+ border-left: initial;
+ border-right: 2px solid $rowBorderColor;
+ }
+
+ .tabulator-data-tree-control {
+ margin-right: initial;
+ margin-left: 5px;
+ }
+
+ &.tabulator-frozen {
+ &.tabulator-frozen-left {
+ border-left: 2px solid $rowBorderColor;
+ }
+
+ &.tabulator-frozen-right {
+ border-right: 2px solid $rowBorderColor;
+ }
+ }
+ }
+
+ .tabulator-col-resize-handle {
+ &:last-of-type {
+ width: 3px;
+ margin-left: 0;
+ margin-right: -3px;
+ }
+ }
+ }
+
+ .tabulator-footer {
+ .tabulator-calcs-holder {
+ text-align: initial;
+ }
+ }
+}
+
+// Table print styling
+
+.tabulator-print-fullscreen {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+
+ z-index: 10000;
+}
+
+body.tabulator-print-fullscreen-hide > *:not(.tabulator-print-fullscreen) {
+ display: none !important;
+}
+
+.tabulator-print-table {
+ border-collapse: collapse;
+
+ .tabulator-data-tree-branch {
+ display: inline-block;
+ vertical-align: middle;
+
+ height: 9px;
+ width: 7px;
+
+ margin-top: -9px;
+ margin-right: 5px;
+
+ border-bottom-left-radius: 1px;
+
+ border-left: 2px solid $rowBorderColor;
+ border-bottom: 2px solid $rowBorderColor;
+ }
+
+ //row grouping element
+ .tabulator-print-table-group {
+ box-sizing: border-box;
+ border-bottom: 1px solid #999;
+ border-right: 1px solid $rowBorderColor;
+ border-top: 1px solid #999;
+ padding: 5px;
+ padding-left: 10px;
+ background: #ccc;
+ font-weight: bold;
+
+ min-width: 100%;
+
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ cursor: pointer;
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+ }
+
+ &.tabulator-group-visible {
+ .tabulator-arrow {
+ margin-right: 10px;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-top: 6px solid $sortArrowActive;
+ border-bottom: 0;
+ }
+ }
+
+ &.tabulator-group-level-1 {
+ td {
+ padding-left: 30px !important;
+ }
+ }
+
+ &.tabulator-group-level-2 {
+ td {
+ padding-left: 50px !important;
+ }
+ }
+
+ &.tabulator-group-level-3 {
+ td {
+ padding-left: 70px !important;
+ }
+ }
+
+ &.tabulator-group-level-4 {
+ td {
+ padding-left: 90px !important;
+ }
+ }
+
+ &.tabulator-group-level-5 {
+ td {
+ padding-left: 110px !important;
+ }
+ }
+
+ .tabulator-group-toggle {
+ display: inline-block;
+ }
+
+ //sorting arrow
+ .tabulator-arrow {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-right: 16px;
+ border-top: 6px solid transparent;
+ border-bottom: 6px solid transparent;
+ border-right: 0;
+ border-left: 6px solid $sortArrowActive;
+ vertical-align: middle;
+ }
+
+ span {
+ margin-left: 10px;
+ color: #d00;
+ }
+ }
+
+ .tabulator-data-tree-control {
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ vertical-align: middle;
+
+ height: 11px;
+ width: 11px;
+
+ margin-right: 5px;
+
+ border: 1px solid $rowTextColor;
+ border-radius: 2px;
+ background: rgba(0, 0, 0, 0.1);
+
+ overflow: hidden;
+
+ @media (hover: hover) and (pointer: fine) {
+ &:hover {
+ cursor: pointer;
+ background: rgba(0, 0, 0, 0.2);
+ }
+ }
+
+ .tabulator-data-tree-control-collapse {
+ display: inline-block;
+ position: relative;
+
+ height: 7px;
+ width: 1px;
+
+ background: transparent;
+
+ &:after {
+ position: absolute;
+ content: '';
+ left: -3px;
+ top: 3px;
+
+ height: 1px;
+ width: 7px;
+
+ background: $rowTextColor;
+ }
+ }
+
+ .tabulator-data-tree-control-expand {
+ display: inline-block;
+ position: relative;
+
+ height: 7px;
+ width: 1px;
+
+ background: $rowTextColor;
+
+ &:after {
+ position: absolute;
+ content: '';
+ left: -3px;
+ top: 3px;
+
+ height: 1px;
+ width: 7px;
+
+ background: $rowTextColor;
+ }
+ }
+ }
+}
diff --git a/new-web/src/css/tabulator_simple.scss b/new-web/src/css/tabulator_simple.scss
new file mode 100644
index 00000000..bd0de7e5
--- /dev/null
+++ b/new-web/src/css/tabulator_simple.scss
@@ -0,0 +1,233 @@
+@use './tabulator.scss';
+
+.tabulator {
+ @apply bg-primary border-none;
+
+ .tabulator-header {
+ @apply border-border bg-background;
+
+ .tabulator-header-contents {
+ @apply bg-background;
+ }
+
+ .tabulator-col-content {
+ @apply bg-background hover:bg-muted;
+ }
+
+ .tabulator-calcs-holder {
+ @apply !border-border !bg-background !border;
+
+ .tabulator-row {
+ @apply !bg-background;
+ }
+ }
+
+ .tabulator-col {
+ @apply bg-background;
+
+ &.tabulator-sortable {
+ @media (hover: hover) and (pointer: fine) {
+ @apply border-border;
+ &.tabulator-col-sorter-element:hover {
+ @apply bg-background cursor-pointer;
+ }
+ }
+ }
+ }
+
+ .tabulator-col-title-holder {
+ @apply text-black dark:text-white;
+
+ .tabulator-col-title {
+ @apply text-black dark:text-white;
+ }
+ }
+
+ .tabulator-col-title input {
+ @apply border-border border;
+ }
+ }
+
+ .tabulator-row {
+ @apply border-border bg-background border-b text-black dark:text-white;
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-selectable:hover {
+ @apply bg-background;
+ cursor: pointer;
+ }
+ }
+
+ .tabulator-cell {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ &:last-of-type {
+ border-right: none;
+ }
+
+ &.tabulator-row-header {
+ border-bottom: none;
+ }
+
+ .tabulator-data-tree-control {
+ @apply border-primary bg-primary;
+
+ .tabulator-data-tree-control-collapse {
+ background: transparent;
+ &:after {
+ @apply bg-primary-foreground;
+ }
+ }
+
+ .tabulator-data-tree-control-expand {
+ @apply bg-primary-foreground;
+
+ &:after {
+ @apply bg-primary-foreground;
+ }
+ }
+ }
+ }
+
+ &.tabulator-group {
+ span {
+ color: #666;
+ }
+ }
+
+ .tabulator-frozen input {
+ @apply opacity-40;
+ background-color: rgb(63 63 70) !important;
+ @apply border-border border;
+ }
+ }
+
+ .tabulator-tableholder {
+ .tabulator-cell {
+ .tabulator-data-tree-control {
+ @apply bg-primary;
+ }
+ }
+
+ @apply bg-background;
+ .tabulator-placeholder {
+ span {
+ @apply text-secondary;
+ }
+ .tabulator-placeholder-contents {
+ @apply text-secondary;
+ }
+ }
+ }
+
+ .tabulator-footer {
+ @apply border-border bg-background;
+
+ .tabulator-footer-contents {
+ @apply text-black dark:text-white;
+
+ .tabulator-paginator {
+ label {
+ @apply text-black dark:text-white;
+ }
+
+ select {
+ @apply border-border bg-primary text-black dark:text-white;
+ }
+
+ .tabulator-page {
+ @apply border-border bg-primary-foreground hover:border-primary/10 hover:bg-primary/10 dark:bg-muted hover:dark:border-muted hover:dark:bg-muted/10 text-black dark:text-white;
+
+ &.active {
+ @apply !bg-primary !text-secondary;
+ }
+
+ &:disabled {
+ opacity: 0.4;
+ }
+ }
+ }
+ }
+
+ .tabulator-calcs-holder {
+ .tabulator-row {
+ @apply !bg-primary;
+ }
+ }
+
+ .tabulator-spreadsheet-tabs {
+ .tabulator-spreadsheet-tab {
+ font-weight: normal;
+
+ &.tabulator-spreadsheet-tab-active {
+ color: tabulator.$footerActiveColor;
+ font-weight: bold;
+ }
+ }
+ }
+ }
+}
+
+.tabulator-table .tabulator-row-odd {
+ @apply border-border bg-background border-b border-none text-black dark:text-white;
+
+ .tabulator-frozen {
+ @apply bg-secondary;
+ }
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-selectable:hover {
+ // @apply bg-background hover:bg-muted;
+ cursor: pointer;
+ }
+ }
+}
+
+.tabulator-table .tabulator-row-even {
+ @apply border-border bg-background border-b border-none text-black dark:text-white;
+
+ .tabulator-frozen {
+ @apply bg-secondary;
+ }
+
+ .tabulator-frozen input {
+ @apply border-border border;
+ }
+
+ @media (hover: hover) and (pointer: fine) {
+ &.tabulator-selectable:hover {
+ // @apply bg-background hover:bg-muted;
+ cursor: pointer;
+ }
+ }
+}
+
+.tabulator-table .tabulator-selected {
+ @apply !bg-muted;
+}
+
+.tabulator-table .tabulator-tree-level-0 .tabulator-cell:first-of-type {
+ padding-left: 9px;
+}
+
+.tabulator-table .tabulator-tree-level-1 .tabulator-cell:first-of-type {
+ padding-left: 5px;
+}
+
+.tabulator-table .tabulator-tree-level-2 .tabulator-cell:first-of-type {
+ padding-left: 12px;
+}
+
+.tabulator-table .tabulator-tree-level-3 .tabulator-cell:first-of-type {
+ padding-left: 12px;
+}
+
+.tabulator-print-table {
+ .tabulator-print-table-group {
+ span {
+ margin-left: 10px;
+ color: #666;
+ }
+ }
+}
diff --git a/new-web/src/lib/api/common.ts b/new-web/src/lib/api/common.ts
new file mode 100644
index 00000000..6867453d
--- /dev/null
+++ b/new-web/src/lib/api/common.ts
@@ -0,0 +1,90 @@
+/**
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 The FreeBSD Foundation.
+ *
+ * This software was developed by Hayzam Sherif
+ * of Alchemilla Ventures Pvt. Ltd. ,
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+import { browser } from '$app/environment';
+import { goto } from '$app/navigation';
+import { store as token } from '$lib/stores/auth';
+import adze from 'adze';
+import axios, { AxiosError, type AxiosInstance, type InternalAxiosRequestConfig } from 'axios';
+import { toast } from "svelte-sonner";
+import { get } from 'svelte/store';
+
+export let ENDPOINT: string;
+export let API_ENDPOINT: string;
+
+if (browser) {
+ ENDPOINT = window.location.origin;
+ API_ENDPOINT = `${window.location.origin}/api`;
+} else {
+ ENDPOINT = '';
+ API_ENDPOINT = '';
+}
+
+export const api: AxiosInstance = axios.create({
+ baseURL: API_ENDPOINT
+});
+
+api.interceptors.request.use(
+ (config: InternalAxiosRequestConfig) => {
+ if (browser) {
+ if (get(token)) {
+ config.headers['Authorization'] = `Bearer ${get(token)}`;
+ }
+ }
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ }
+);
+
+api.interceptors.response.use(
+ (response) => response,
+ async (error) => {
+ if (error.response?.status === 401 && browser) {
+ toast.error('Session expired, please login again', {
+ position: 'bottom-center'
+ });
+ goto('/login');
+ return;
+ }
+ handleAxiosError(error);
+ return Promise.reject(error);
+ }
+);
+
+export function handleAxiosError(error: unknown): void {
+ if (!browser) return;
+
+ if (!axios.isAxiosError(error)) {
+ toast.error('An unexpected error occurred', {
+ position: 'bottom-center'
+ });
+ adze.withEmoji.error('An unexpected error occurred');
+ return;
+ }
+
+ const axiosError = error as AxiosError<{ message?: string }>;
+ if (axiosError.response) {
+ const errorMessage =
+ axiosError.response.data?.message || axiosError.message || 'An error occurred';
+ // adze.withEmoji.error('Status:', axiosError.response.status);
+ // adze.withEmoji.error('Data:', axiosError.response.data);
+ // adze.withEmoji.error('Error message:', errorMessage);
+ // showToast({ text: errorMessage, type: 'error', timeout: 5000 });
+ } else if (axiosError.request) {
+ // adze.withEmoji.error('No response:', axiosError.request);
+ // showToast({
+ // text: 'No response from server',
+ // type: 'error',
+ // timeout: 5000
+ // });
+ }
+}
diff --git a/new-web/src/lib/api/disk/disk.ts b/new-web/src/lib/api/disk/disk.ts
new file mode 100644
index 00000000..848d7608
--- /dev/null
+++ b/new-web/src/lib/api/disk/disk.ts
@@ -0,0 +1,33 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import { DiskSchema, type Disk } from '$lib/types/disk/disk';
+import { apiRequest } from '$lib/utils/http';
+import { z } from 'zod/v4';
+
+export async function listDisks(): Promise {
+ return await apiRequest('/disk/list', z.array(DiskSchema), 'GET');
+}
+
+export async function destroyDisk(disk: string): Promise {
+ return await apiRequest(`/disk/wipe`, APIResponseSchema, 'POST', {
+ device: disk
+ });
+}
+
+export async function destroyPartition(partition: string): Promise {
+ return await apiRequest(`/disk/delete-partition`, APIResponseSchema, 'POST', {
+ device: partition
+ });
+}
+
+export async function initializeGPT(disk: string): Promise {
+ return await apiRequest(`/disk/initialize-gpt`, APIResponseSchema, 'POST', {
+ device: disk
+ });
+}
+
+export async function createPartitions(disk: string, sizes: number[]): Promise {
+ return await apiRequest(`/disk/create-partitions`, APIResponseSchema, 'POST', {
+ device: disk,
+ sizes
+ });
+}
diff --git a/new-web/src/lib/api/info/audit.ts b/new-web/src/lib/api/info/audit.ts
new file mode 100644
index 00000000..26f3c752
--- /dev/null
+++ b/new-web/src/lib/api/info/audit.ts
@@ -0,0 +1,41 @@
+import { AuditLogSchema, type AuditLog } from '$lib/types/info/audit';
+import { apiRequest } from '$lib/utils/http';
+import { getTranslation } from '$lib/utils/i18n';
+import { capitalizeFirstLetter } from '$lib/utils/string';
+
+export async function getAuditLogs(): Promise {
+ return await apiRequest('/info/audit-logs', AuditLogSchema, 'GET');
+}
+
+export function formatAction(action: string): string {
+ if (action.includes('|-|')) {
+ const parts = action.split('|-|');
+ return capitalizeFirstLetter(getTranslation(parts[0], parts[0]), true) + ' ' + parts[1];
+ }
+
+ switch (action) {
+ case 'login':
+ return getTranslation('auth.login', 'Login');
+ case 'revoke_token':
+ return getTranslation('auth.logout', 'Logout');
+ default:
+ return action;
+ }
+}
+
+export function formatStatus(status: string): string {
+ switch (status) {
+ case 'started':
+ return 'Started';
+ case 'success':
+ return 'OK';
+ case 'failure':
+ return 'Failed';
+ case 'failed':
+ return 'Failed';
+ case 'progress':
+ return 'In Progress';
+ default:
+ return status;
+ }
+}
diff --git a/new-web/src/lib/api/info/basic.ts b/new-web/src/lib/api/info/basic.ts
new file mode 100644
index 00000000..2f818ab8
--- /dev/null
+++ b/new-web/src/lib/api/info/basic.ts
@@ -0,0 +1,6 @@
+import { BasicInfoSchema, type BasicInfo } from '$lib/types/info/basic';
+import { apiRequest } from '$lib/utils/http';
+
+export async function getBasicInfo(): Promise {
+ return await apiRequest('/info/basic', BasicInfoSchema, 'GET');
+}
diff --git a/new-web/src/lib/api/info/cpu.ts b/new-web/src/lib/api/info/cpu.ts
new file mode 100644
index 00000000..0d26dcbc
--- /dev/null
+++ b/new-web/src/lib/api/info/cpu.ts
@@ -0,0 +1,20 @@
+import {
+ CPUInfoHistoricalSchema,
+ CPUInfoSchema,
+ type CPUInfo,
+ type CPUInfoHistorical
+} from '$lib/types/info/cpu';
+import { apiRequest } from '$lib/utils/http';
+import type { QueryFunctionContext } from '@sveltestack/svelte-query';
+
+export async function getCPUInfo(
+ queryObj?: QueryFunctionContext
+): Promise {
+ if (queryObj) {
+ if (queryObj.queryKey.includes('cpuInfoHistorical')) {
+ return await apiRequest('/info/cpu/historical', CPUInfoHistoricalSchema, 'GET');
+ }
+ }
+
+ return await apiRequest('/info/cpu', CPUInfoSchema, 'GET');
+}
diff --git a/new-web/src/lib/api/info/info.ts b/new-web/src/lib/api/info/info.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/new-web/src/lib/api/info/notes.ts b/new-web/src/lib/api/info/notes.ts
new file mode 100644
index 00000000..995db12d
--- /dev/null
+++ b/new-web/src/lib/api/info/notes.ts
@@ -0,0 +1,41 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import { NoteSchema, NotesSchema, type Note, type Notes } from '$lib/types/info/notes';
+import { apiRequest } from '$lib/utils/http';
+import { z } from 'zod/v4';
+
+async function notesRequest(
+ endpoint: string,
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE',
+ body?: object
+): Promise {
+ let schema;
+
+ if (method === 'GET') {
+ schema = z.array(NoteSchema);
+ } else if (method === 'POST') {
+ schema = NoteSchema;
+ } else {
+ schema = APIResponseSchema;
+ }
+
+ return await apiRequest(endpoint, schema, method, body);
+}
+
+export const getNotes = () => notesRequest('/info/notes', 'GET');
+export const deleteNote = (id: number) => notesRequest(`/info/notes/${id}`, 'DELETE');
+
+export const createNote = async (title: string, content: string): Promise => {
+ return (await notesRequest('/info/notes', 'POST', { title, content })) as Note | APIResponse;
+};
+
+export const updateNote = async (
+ id: number,
+ title: string,
+ content: string
+): Promise => {
+ return (await notesRequest(`/info/notes/${id}`, 'PUT', { title, content })) as APIResponse;
+};
+
+export const deleteNotes = async (ids: number[]): Promise => {
+ return (await notesRequest('/info/notes/bulk-delete', 'POST', { ids })) as APIResponse;
+};
diff --git a/new-web/src/lib/api/info/ram.ts b/new-web/src/lib/api/info/ram.ts
new file mode 100644
index 00000000..849e316f
--- /dev/null
+++ b/new-web/src/lib/api/info/ram.ts
@@ -0,0 +1,10 @@
+import { RAMInfoSchema, type RAMInfo } from '$lib/types/info/ram';
+import { apiRequest } from '$lib/utils/http';
+
+export async function getRAMInfo(): Promise {
+ return await apiRequest('/info/ram', RAMInfoSchema, 'GET');
+}
+
+export async function getSwapInfo(): Promise {
+ return await apiRequest('/info/swap', RAMInfoSchema, 'GET');
+}
diff --git a/new-web/src/lib/api/network/iface.ts b/new-web/src/lib/api/network/iface.ts
new file mode 100644
index 00000000..d1efcdbb
--- /dev/null
+++ b/new-web/src/lib/api/network/iface.ts
@@ -0,0 +1,6 @@
+import { IfaceSchema, type Iface } from '$lib/types/network/iface';
+import { apiRequest } from '$lib/utils/http';
+
+export async function getInterfaces(): Promise {
+ return await apiRequest('/network/interface', IfaceSchema.array(), 'GET');
+}
diff --git a/new-web/src/lib/api/network/switch.ts b/new-web/src/lib/api/network/switch.ts
new file mode 100644
index 00000000..2c4d8997
--- /dev/null
+++ b/new-web/src/lib/api/network/switch.ts
@@ -0,0 +1,67 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import { SwitchListSchema, type SwitchList } from '$lib/types/network/switch';
+import { apiRequest } from '$lib/utils/http';
+
+export async function getSwitches(): Promise {
+ return await apiRequest('/network/switch', SwitchListSchema, 'GET');
+}
+
+export async function createSwitch(
+ name: string,
+ mtu: number,
+ vlan: number,
+ address: string,
+ address6: string,
+ privateSw: boolean,
+ dhcp: boolean,
+ ports: string[],
+ disableIPv6: boolean,
+ slaac: boolean
+): Promise {
+ const body = {
+ name,
+ mtu,
+ vlan,
+ address,
+ address6,
+ private: privateSw,
+ ports,
+ dhcp,
+ disableIPv6,
+ slaac
+ };
+
+ return await apiRequest('/network/switch/standard', APIResponseSchema, 'POST', body);
+}
+
+export async function deleteSwitch(id: number): Promise {
+ return await apiRequest(`/network/switch/standard/${id}`, APIResponseSchema, 'DELETE');
+}
+
+export async function updateSwitch(
+ id: number,
+ mtu: number,
+ vlan: number,
+ address: string,
+ address6: string,
+ privateSw: boolean,
+ ports: string[],
+ disableIPv6: boolean,
+ slaac: boolean,
+ dhcp: boolean
+): Promise {
+ const body = {
+ id,
+ mtu,
+ vlan,
+ address,
+ address6,
+ private: privateSw,
+ ports,
+ disableIPv6,
+ slaac,
+ dhcp
+ };
+
+ return await apiRequest('/network/switch/standard', APIResponseSchema, 'PUT', body);
+}
diff --git a/new-web/src/lib/api/system/pci.ts b/new-web/src/lib/api/system/pci.ts
new file mode 100644
index 00000000..06457db1
--- /dev/null
+++ b/new-web/src/lib/api/system/pci.ts
@@ -0,0 +1,24 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import {
+ PCIDeviceSchema,
+ PPTDeviceSchema,
+ type PCIDevice,
+ type PPTDevice
+} from '$lib/types/system/pci';
+import { apiRequest } from '$lib/utils/http';
+
+export async function getPCIDevices(): Promise {
+ return await apiRequest('/system/pci-devices', PCIDeviceSchema.array(), 'GET');
+}
+
+export async function getPPTDevices(): Promise {
+ return await apiRequest('/system/ppt-devices', PPTDeviceSchema.array(), 'GET');
+}
+
+export async function addPPTDevice(domain: string, deviceID: string): Promise {
+ return await apiRequest('/system/ppt-devices', APIResponseSchema, 'POST', { domain, deviceID });
+}
+
+export async function removePPTDevice(deviceID: string): Promise {
+ return await apiRequest(`/system/ppt-devices/${deviceID}`, APIResponseSchema, 'DELETE');
+}
diff --git a/new-web/src/lib/api/utilities/downloader.ts b/new-web/src/lib/api/utilities/downloader.ts
new file mode 100644
index 00000000..684e7699
--- /dev/null
+++ b/new-web/src/lib/api/utilities/downloader.ts
@@ -0,0 +1,30 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import { DownloadSchema, type Download } from '$lib/types/utilities/downloader';
+import { apiRequest } from '$lib/utils/http';
+
+export async function getDownloads(): Promise {
+ return await apiRequest('/utilities/downloads', DownloadSchema.array(), 'GET');
+}
+
+export async function startDownload(url: string): Promise {
+ return await apiRequest('/utilities/downloads', APIResponseSchema, 'POST', {
+ url
+ });
+}
+
+export async function deleteDownload(id: number): Promise {
+ return await apiRequest(`/utilities/downloads/${id}`, APIResponseSchema, 'DELETE');
+}
+
+export async function bulkDeleteDownloads(ids: number[]): Promise {
+ return await apiRequest('/utilities/downloads/bulk-delete', APIResponseSchema, 'POST', {
+ ids
+ });
+}
+
+export async function getSignedURL(name: string, parentUUID: string): Promise {
+ return await apiRequest('/utilities/downloads/signed-url', APIResponseSchema, 'POST', {
+ name,
+ parentUUID
+ });
+}
diff --git a/new-web/src/lib/api/vm/storage.ts b/new-web/src/lib/api/vm/storage.ts
new file mode 100644
index 00000000..1cd8078d
--- /dev/null
+++ b/new-web/src/lib/api/vm/storage.ts
@@ -0,0 +1,9 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import { apiRequest } from '$lib/utils/http';
+
+export async function storageDetach(vmId: number, storageId: number): Promise {
+ return await apiRequest(`/vm/storage/detach`, APIResponseSchema, 'POST', {
+ vmId,
+ storageId
+ });
+}
diff --git a/new-web/src/lib/api/vm/vm.ts b/new-web/src/lib/api/vm/vm.ts
new file mode 100644
index 00000000..50e880e8
--- /dev/null
+++ b/new-web/src/lib/api/vm/vm.ts
@@ -0,0 +1,69 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import {
+ VMDomainSchema,
+ VMSchema,
+ VMStatSchema,
+ type CreateData,
+ type VM,
+ type VMDomain,
+ type VMStat
+} from '$lib/types/vm/vm';
+import { apiRequest } from '$lib/utils/http';
+import { z } from 'zod/v4';
+
+export async function getVMs(): Promise {
+ return await apiRequest('/vm', z.array(VMSchema), 'GET');
+}
+
+export async function newVM(data: CreateData): Promise {
+ return await apiRequest('/vm', APIResponseSchema, 'POST', {
+ name: data.name,
+ vmId: data.id,
+ iso: data.storage.iso,
+ storageType: data.storage.type,
+ storageDataset: data.storage.guid,
+ storageSize: data.storage.size,
+ storageEmulationType: data.storage.emulation,
+ switchId: data.network.switch,
+ switchEmulationType: data.network.emulation,
+ macAddress: data.network.mac,
+ cpuSockets: data.hardware.sockets,
+ cpuCores: data.hardware.cores,
+ cpuThreads: data.hardware.threads,
+ ram: data.hardware.memory,
+ vncPort: data.advanced.vncPort,
+ vncPassword: data.advanced.vncPassword,
+ vncWait: data.advanced.vncWait,
+ vncResolution: data.advanced.vncResolution,
+ startAtBoot: data.advanced.startAtBoot,
+ bootOrder: data.advanced.bootOrder,
+ pciDevices: data.hardware.passthroughIds,
+ description: data.description
+ });
+}
+
+export async function deleteVM(id: number): Promise {
+ return await apiRequest(`/vm/${id}`, APIResponseSchema, 'DELETE');
+}
+
+export async function getVMDomain(id: number | string): Promise {
+ return await apiRequest(`/vm/domain/${id}`, VMDomainSchema, 'GET');
+}
+
+export async function actionVm(id: number | string, action: string): Promise {
+ return await apiRequest(`/vm/${id}/${action}`, APIResponseSchema, 'POST');
+}
+
+export async function getStats(vmId: number, limit: number): Promise {
+ return await apiRequest(`/vm/stats`, z.array(VMStatSchema), 'POST', {
+ vmId,
+ limit
+ });
+}
+
+export async function updateDescription(id: number, description: string): Promise {
+ return await apiRequest(`/vm/description`, APIResponseSchema, 'PUT', {
+ id,
+ description
+ });
+}
diff --git a/new-web/src/lib/api/zfs/datasets.ts b/new-web/src/lib/api/zfs/datasets.ts
new file mode 100644
index 00000000..390405b2
--- /dev/null
+++ b/new-web/src/lib/api/zfs/datasets.ts
@@ -0,0 +1,113 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import {
+ DatasetSchema,
+ PeriodicSnapshotSchema,
+ type Dataset,
+ type PeriodicSnapshot
+} from '$lib/types/zfs/dataset';
+
+import { apiRequest } from '$lib/utils/http';
+
+export async function getDatasets(): Promise {
+ return await apiRequest('/zfs/datasets', DatasetSchema.array(), 'GET');
+}
+
+export async function deleteSnapshot(
+ snapshot: Dataset,
+ recursive: boolean = false
+): Promise {
+ const param = recursive ? '?recursive=true' : '';
+ return await apiRequest(
+ `/zfs/datasets/snapshot/${snapshot.properties.guid}${param}`,
+ APIResponseSchema,
+ 'DELETE'
+ );
+}
+
+export async function createSnapshot(
+ dataset: Dataset,
+ name: string,
+ recursive: boolean
+): Promise {
+ return await apiRequest('/zfs/datasets/snapshot', APIResponseSchema, 'POST', {
+ name: name,
+ recursive: recursive,
+ guid: dataset.properties.guid
+ });
+}
+
+export async function getPeriodicSnapshots(): Promise {
+ return await apiRequest('/zfs/datasets/snapshot/periodic', PeriodicSnapshotSchema.array(), 'GET');
+}
+
+export async function createPeriodicSnapshot(
+ dataset: Dataset,
+ prefix: string,
+ recursive: boolean,
+ interval: number
+): Promise {
+ return await apiRequest('/zfs/datasets/snapshot/periodic', APIResponseSchema, 'POST', {
+ guid: dataset.properties.guid,
+ prefix: prefix,
+ recursive: recursive,
+ interval: interval
+ });
+}
+
+export async function deletePeriodicSnapshot(guid: string): Promise {
+ return await apiRequest(`/zfs/datasets/snapshot/periodic/${guid}`, APIResponseSchema, 'DELETE');
+}
+
+export async function createFileSystem(
+ name: string,
+ parent: string,
+ properties: Record
+): Promise {
+ return await apiRequest('/zfs/datasets/filesystem', APIResponseSchema, 'POST', {
+ name: name,
+ parent: parent,
+ properties: properties
+ });
+}
+
+export async function deleteFileSystem(dataset: Dataset): Promise {
+ return await apiRequest(
+ `/zfs/datasets/filesystem/${dataset.properties.guid}`,
+ APIResponseSchema,
+ 'DELETE'
+ );
+}
+
+export async function rollbackSnapshot(guid: string): Promise {
+ return await apiRequest(`/zfs/datasets/snapshot/rollback`, APIResponseSchema, 'POST', {
+ guid: guid,
+ destroyMoreRecent: true
+ });
+}
+
+export async function createVolume(
+ name: string,
+ parent: string,
+ props: Record
+): Promise {
+ return await apiRequest('/zfs/datasets/volume', APIResponseSchema, 'POST', {
+ name: name,
+ parent: parent,
+ properties: props
+ });
+}
+
+export async function deleteVolume(dataset: Dataset): Promise {
+ return await apiRequest(
+ `/zfs/datasets/volume/${dataset.properties.guid}`,
+ APIResponseSchema,
+ 'DELETE'
+ );
+}
+
+export async function bulkDelete(datasets: Dataset[]): Promise {
+ const guids = datasets.map((dataset) => dataset.properties.guid);
+ return await apiRequest('/zfs/datasets/bulk-delete', APIResponseSchema, 'POST', {
+ guids: guids
+ });
+}
diff --git a/new-web/src/lib/api/zfs/pool.ts b/new-web/src/lib/api/zfs/pool.ts
new file mode 100644
index 00000000..b36c35d3
--- /dev/null
+++ b/new-web/src/lib/api/zfs/pool.ts
@@ -0,0 +1,77 @@
+import { APIResponseSchema, type APIResponse } from '$lib/types/common';
+import {
+ IODelayHistoricalSchema,
+ IODelaySchema,
+ PoolStatPointsResponseSchema,
+ ZpoolSchema,
+ type CreateZpool,
+ type IODelay,
+ type IODelayHistorical,
+ type PoolStatPointsResponse,
+ type ReplaceDevice,
+ type Zpool
+} from '$lib/types/zfs/pool';
+import { apiRequest } from '$lib/utils/http';
+import type { QueryFunctionContext } from '@sveltestack/svelte-query';
+
+export async function getIODelay(
+ queryObj: QueryFunctionContext | undefined
+): Promise {
+ if (queryObj) {
+ if (queryObj.queryKey.includes('ioDelayHistorical')) {
+ const data = await apiRequest(
+ '/zfs/pool/io-delay/historical',
+ IODelayHistoricalSchema,
+ 'GET'
+ );
+ return IODelayHistoricalSchema.parse(data);
+ }
+ }
+
+ return await apiRequest('/zfs/pool/io-delay', IODelaySchema, 'GET');
+}
+
+export async function getPools(): Promise {
+ return await apiRequest('/zfs/pools', ZpoolSchema.array(), 'GET');
+}
+
+export async function createPool(data: CreateZpool) {
+ return await apiRequest('/zfs/pools', APIResponseSchema, 'POST', {
+ ...data
+ });
+}
+
+export async function replaceDevice(data: ReplaceDevice) {
+ return await apiRequest(`/zfs/pools/${data.name}/replace-device`, APIResponseSchema, 'POST', {
+ ...data
+ });
+}
+
+export async function deletePool(name: string) {
+ return await apiRequest(`/zfs/pools/${name}`, APIResponseSchema, 'DELETE');
+}
+
+export async function scrubPool(name: string) {
+ return await apiRequest(`/zfs/pools/${name}/scrub`, APIResponseSchema, 'POST');
+}
+
+export async function getPoolStats(
+ interval: number,
+ limit: number
+): Promise {
+ return await apiRequest(
+ `/zfs/pool/stats/${interval}/${limit}`,
+ PoolStatPointsResponseSchema,
+ 'GET'
+ );
+}
+
+export async function editPool(
+ name: string,
+ properties: Record
+): Promise {
+ return await apiRequest(`/zfs/pools`, APIResponseSchema, 'PATCH', {
+ name,
+ properties
+ });
+}
diff --git a/new-web/src/lib/api/zfs/volumes.ts b/new-web/src/lib/api/zfs/volumes.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/new-web/src/lib/components/custom/AlertDialog.svelte b/new-web/src/lib/components/custom/AlertDialog.svelte
new file mode 100644
index 00000000..71a35c07
--- /dev/null
+++ b/new-web/src/lib/components/custom/AlertDialog.svelte
@@ -0,0 +1,42 @@
+
+
+
+
+
+ {getTranslation('are_you_sure', 'Are you sure?')}
+
+ {#if customTitle}
+ {@html customTitle}
+ {:else}
+ {getTranslation(
+ 'common.permanent_delete_msg',
+ 'This action cannot be undone. This will permanently delete'
+ )}
+ {names.parent} {names.element} .
+ {/if}
+
+
+
+ Cancel
+ Continue
+
+
+
diff --git a/new-web/src/lib/components/custom/BarChart.svelte b/new-web/src/lib/components/custom/BarChart.svelte
new file mode 100644
index 00000000..00c0bd3b
--- /dev/null
+++ b/new-web/src/lib/components/custom/BarChart.svelte
@@ -0,0 +1,92 @@
+
+
+
+
+
+ humanFormat(d)}
+ rule={{
+ class: 'stroke-border dark:stroke-border'
+ }}
+ ticks={2}
+ />
+
+
+
+
+
+
+
+ {data.name}
+
+
+
+
+
+
+
+
diff --git a/new-web/src/lib/components/custom/CreateVM/Advanced.svelte b/new-web/src/lib/components/custom/CreateVM/Advanced.svelte
new file mode 100644
index 00000000..3407f2e8
--- /dev/null
+++ b/new-web/src/lib/components/custom/CreateVM/Advanced.svelte
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
VNC Password
+
+
+
+ {
+ vncPassword = generatePassword();
+ }}
+ >
+ {
+ vncPassword = generatePassword();
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/new-web/src/lib/components/custom/CreateVM/Basic.svelte b/new-web/src/lib/components/custom/CreateVM/Basic.svelte
new file mode 100644
index 00000000..94625d75
--- /dev/null
+++ b/new-web/src/lib/components/custom/CreateVM/Basic.svelte
@@ -0,0 +1,39 @@
+
+
+
diff --git a/new-web/src/lib/components/custom/CreateVM/CreateVM.svelte b/new-web/src/lib/components/custom/CreateVM/CreateVM.svelte
new file mode 100644
index 00000000..b14a3130
--- /dev/null
+++ b/new-web/src/lib/components/custom/CreateVM/CreateVM.svelte
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+ Create Virtual Machine
+
+
+ Configure your virtual machine with custom hardware and network settings
+
+
+
+
+
+
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ (open = false)}
+ title={capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+ {#each tabs as { value, label }}
+ {label}
+ {/each}
+
+
+ {#each tabs as { value, label }}
+
+
+ {#if value === 'basic'}
+
+ {:else if value === 'storage'}
+
+ {:else if value === 'network'}
+
+ {:else if value === 'hardware'}
+
+ {:else if value === 'advanced'}
+
+ {/if}
+
+
+ {/each}
+
+
+
+
+ create()}
+ >Create Virtual Machine
+
+
+
+
diff --git a/new-web/src/lib/components/custom/CreateVM/Hardware.svelte b/new-web/src/lib/components/custom/CreateVM/Hardware.svelte
new file mode 100644
index 00000000..dbc92ff4
--- /dev/null
+++ b/new-web/src/lib/components/custom/CreateVM/Hardware.svelte
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+ {#if pptDevices && pptDevices.length > 0}
+
PCI Passthrough
+
+
+ {#each checkboxItems as item (item.pptId)}
+
+
+
{
+ if (typeof v === 'boolean') toggle(item.pptId, v);
+ }}
+ />
+
+
+ {item.device.names.device} — {item.device.names.vendor}
+
+
+ pci{item.device.domain}:{item.device.bus}:{item.device.device}:{item.device
+ .function}
+
+
+
+
+ {/each}
+
+
+ {/if}
+
diff --git a/new-web/src/lib/components/custom/CreateVM/Network.svelte b/new-web/src/lib/components/custom/CreateVM/Network.svelte
new file mode 100644
index 00000000..b05f5ef8
--- /dev/null
+++ b/new-web/src/lib/components/custom/CreateVM/Network.svelte
@@ -0,0 +1,95 @@
+
+
+{#snippet radioItem(id: number, name: string)}
+ {@const i = `radio-${id}`}
+
+
+
+ {name}
+
+ {name === 'None'
+ ? 'No network switch will be allocated now, you can add it later'
+ : 'Standard switch'}
+
+
+
+{/snippet}
+
+
+
+
+ {#if switches && switches.standard}
+ {#each switches.standard ?? [] as sw}
+ {@render radioItem(sw.id, sw.name)}
+ {/each}
+ {/if}
+ {@render radioItem(0, 'None')}
+
+
+
+ {#if swStr !== '0'}
+
+
+
+
+
+ {/if}
+
diff --git a/new-web/src/lib/components/custom/CreateVM/Storage.svelte b/new-web/src/lib/components/custom/CreateVM/Storage.svelte
new file mode 100644
index 00000000..c41434a9
--- /dev/null
+++ b/new-web/src/lib/components/custom/CreateVM/Storage.svelte
@@ -0,0 +1,230 @@
+
+
+{#snippet radioItem(type: string)}
+
+
+
+ {details(type)[0]}
+
+ {details(type)[1]}
+
+
+
+{/snippet}
+
+{#snippet storageDetail(type: string)}
+ {#if type === 'zvol'}
+
+ {/if}
+
+ {#if type === 'raw'}
+
+
+
+
+
+ {/if}
+
+
+{/snippet}
+
+
+
+
+ {#each ['zvol', 'raw', 'none'] as storageType}
+ {@render radioItem(storageType)}
+ {/each}
+
+
+
+ {#if type !== 'none'}
+ {@render storageDetail(type)}
+ {/if}
+
+
+
diff --git a/new-web/src/lib/components/custom/Header.svelte b/new-web/src/lib/components/custom/Header.svelte
new file mode 100644
index 00000000..2c823ea6
--- /dev/null
+++ b/new-web/src/lib/components/custom/Header.svelte
@@ -0,0 +1,181 @@
+
+
+
diff --git a/new-web/src/lib/components/custom/KVTableModal.svelte b/new-web/src/lib/components/custom/KVTableModal.svelte
new file mode 100644
index 00000000..4c24f5d6
--- /dev/null
+++ b/new-web/src/lib/components/custom/KVTableModal.svelte
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+ {#if titles.icon}
+
+ {/if}
+
{titles.main}
+
+
+
+
+
+
+
+
+
+
+
+ {#if tableHeaders.length > 0}
+ {#each tableHeaders as header}
+ {header}
+ {/each}
+ {:else}
+ {titles.key}
+ {titles.value}
+ {/if}
+
+
+
+
+ {#if tableHeaders.length > 0}
+ {#each KV as Array> as row}
+
+ {#each tableHeaders as header}
+ {row[header]}
+ {/each}
+
+ {/each}
+ {:else}
+ {#each Object.entries(KV) as [key, value]}
+ {#if typeof value === 'object' && value !== null && !Array.isArray(value)}
+
+
+ toggleObjectExpansion(key)}
+ >
+
+ {key}
+
+
+
+ Object ({Object.keys(value).length} properties)
+
+
+ {#if expandedObjects[key]}
+ {#each Object.entries(value) as [nestedKey, nestedValue]}
+
+
+ {nestedKey}
+
+
+ {nestedValue}
+
+
+ {/each}
+ {/if}
+ {:else}
+
+ {key}
+ {value}
+
+ {/if}
+ {/each}
+ {/if}
+
+
+
+
+
diff --git a/new-web/src/lib/components/custom/KeyValueTable.svelte b/new-web/src/lib/components/custom/KeyValueTable.svelte
new file mode 100644
index 00000000..e575b574
--- /dev/null
+++ b/new-web/src/lib/components/custom/KeyValueTable.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
+ {#each data as item, i (i)}
+
+ {item.key}
+ {item.value}
+
+ {/each}
+
+
diff --git a/new-web/src/lib/components/custom/LineGraph.svelte b/new-web/src/lib/components/custom/LineGraph.svelte
new file mode 100644
index 00000000..cf758bec
--- /dev/null
+++ b/new-web/src/lib/components/custom/LineGraph.svelte
@@ -0,0 +1,91 @@
+
+
+
+
+ formatWithTimeZone(d, interval ? dateFormatString : 'hh:mm a')}
+ />
+ String(formatValue(d, unformattedKeys, valueType))}
+ />
+
+
+
+
+ {formatWithTimeZone(data.date, interval ? dateFormatString : 'hh:mm a')}
+
+
+ {#each Object.entries(data).filter(([key]) => key !== 'date') as [key, value]}
+ s.key === key)?.label || key}
+ value={formatValue(Number(value), unformattedKeys, valueType)}
+ color={series.find((s) => s.key === key)?.color || 'pink'}
+ />
+ {/each}
+
+
+
+
diff --git a/new-web/src/lib/components/custom/LoadingDialog.svelte b/new-web/src/lib/components/custom/LoadingDialog.svelte
new file mode 100644
index 00000000..13ad5567
--- /dev/null
+++ b/new-web/src/lib/components/custom/LoadingDialog.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
+ {title}
+
+
+ {@html description}
+
+
+
+
diff --git a/new-web/src/lib/components/custom/Login.svelte b/new-web/src/lib/components/custom/Login.svelte
new file mode 100644
index 00000000..3767ba7e
--- /dev/null
+++ b/new-web/src/lib/components/custom/Login.svelte
@@ -0,0 +1,152 @@
+
+
+
+
+
+ {#if mode.current === 'dark'}
+
+ {:else}
+
+ {/if}
+ SYLVE
+
+
+
+
+ {$_('auth.username')}
+
+
+
+ {$_('auth.password')}
+
+
+
+
+ {$_('auth.realm')}
+
+
+ {authTypeValue}
+
+
+ {$_('auth.pam')}
+ {$_('auth.sylve')}
+
+
+
+
+
+ {$_('auth.language')}
+
+
+ {languageValue}
+
+
+ English
+ Malayalam
+
+
+
+
+
+
+
+
+ Remember Me
+
+ {
+ onLogin(username, password, authType, language, remember);
+ }}
+ size="sm"
+ class="w-20 rounded-md bg-blue-700 text-white hover:bg-blue-600"
+ >
+ {#if loading}
+
+ {:else}
+ Login
+ {/if}
+
+
+
+
diff --git a/new-web/src/lib/components/custom/PieChart.svelte b/new-web/src/lib/components/custom/PieChart.svelte
new file mode 100644
index 00000000..6827401f
--- /dev/null
+++ b/new-web/src/lib/components/custom/PieChart.svelte
@@ -0,0 +1,34 @@
+
+
+
+
d.color)}
+ renderContext="svg"
+ legend
+ >
+
+
+
+
+
+
+
+
diff --git a/new-web/src/lib/components/custom/Terminal.svelte b/new-web/src/lib/components/custom/Terminal.svelte
new file mode 100644
index 00000000..1e3d22c2
--- /dev/null
+++ b/new-web/src/lib/components/custom/Terminal.svelte
@@ -0,0 +1,274 @@
+
+
+
+
+{#if $terminalStore.isOpen && !$terminalStore.isMinimized}
+
+
+
+
+
+
+ {$terminalStore.title}
+
+
+
+ visiblityAction('window-minimize')}
+ title="Minimize"
+ >
+
+
+ visiblityAction('window-close')}
+ title="Close"
+ >
+
+
+
+
+
+
+
+ {#each $terminalStore.tabs as tab}
+
visiblityAction('tab-select', tab.id)}
+ onkeydown={(e) =>
+ (e.key === 'Enter' || e.key === ' ') && visiblityAction('tab-select', tab.id)}
+ role="button"
+ tabindex="0"
+ >
+ {tab.title}
+ {#if tabsCount > 1}
+ {
+ e.stopPropagation();
+ visiblityAction('tab-close', e);
+ }}
+ >
+
+
+ {/if}
+
+ {/each}
+
+ addTab()}
+ title="Add new tab"
+ >
+
+
+
+
+
+
+
+ {#each $terminalStore.tabs as tab}
+ {#if tab.id === $terminalStore.activeTabId}
+
+
+
+ {/if}
+ {/each}
+
+
+
+{/if}
diff --git a/new-web/src/lib/components/custom/Throbber.svelte b/new-web/src/lib/components/custom/Throbber.svelte
new file mode 100644
index 00000000..64ec04e2
--- /dev/null
+++ b/new-web/src/lib/components/custom/Throbber.svelte
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SYLVE
+
+
+
+
diff --git a/new-web/src/lib/components/custom/TreeTable.svelte b/new-web/src/lib/components/custom/TreeTable.svelte
new file mode 100644
index 00000000..4a83f238
--- /dev/null
+++ b/new-web/src/lib/components/custom/TreeTable.svelte
@@ -0,0 +1,158 @@
+
+
+
diff --git a/new-web/src/lib/components/custom/TreeTable/Search.svelte b/new-web/src/lib/components/custom/TreeTable/Search.svelte
new file mode 100644
index 00000000..a9420569
--- /dev/null
+++ b/new-web/src/lib/components/custom/TreeTable/Search.svelte
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+ {#if expanded}
+ {
+ if (e.key === 'Escape') {
+ query = '';
+ expanded = false;
+ }
+ }}
+ />
+ {/if}
+
+
diff --git a/new-web/src/lib/components/custom/TreeView.svelte b/new-web/src/lib/components/custom/TreeView.svelte
new file mode 100644
index 00000000..e4a8caa1
--- /dev/null
+++ b/new-web/src/lib/components/custom/TreeView.svelte
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+ {getTranslation(`node.${item.label}`, item.label)}
+
+
+ {#if item.children}
+
+ {/if}
+
+
+
+{#if isOpen && item.children}
+ t }} style="overflow: hidden;">
+ {#each item.children as child}
+
+ {/each}
+
+{/if}
diff --git a/new-web/src/lib/components/disk/CreatePartition.svelte b/new-web/src/lib/components/disk/CreatePartition.svelte
new file mode 100644
index 00000000..f3780fa8
--- /dev/null
+++ b/new-web/src/lib/components/disk/CreatePartition.svelte
@@ -0,0 +1,278 @@
+
+
+ close()}>
+
+
+
+ Create Partitions
+
+
+
+
+ {
+ newPartitions = [];
+ remainingSpace = disk ? calculateRemainingSpace(disk) : 0;
+ currentPartition = 0;
+ currentPartitionInput = '';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ close()}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+
+
+ Name
+ Size
+ Usage
+ Actions
+
+
+
+ {#if disk && disk.partitions && disk.partitions.length > 0}
+ {#each disk.partitions as partition}
+
+ {partition.name}
+ {humanFormat(partition.size)}
+ {partition.usage}
+
+ Existing
+
+
+ {/each}
+ {/if}
+
+ {#if newPartitions.length > 0}
+ {#each newPartitions as partition, index}
+
+ {partition.name}
+ {humanFormat(partition.size)}
+ -
+
+ removePartition(index)}>
+
+
+
+
+ {/each}
+ {/if}
+
+ {#if (!disk || !disk.partitions || disk.partitions.length === 0) && newPartitions.length === 0}
+
+
+ No partitions created yet
+
+
+ {/if}
+
+
+
+
+
+
+
+ {
+ const value = Math.floor(e[0]);
+ currentPartition = value <= 0 ? 0 : value;
+ currentPartitionInput = humanFormat(currentPartition);
+ }}
+ />
+
+
+
+
+
0 ? '' : 'cursor-not-allowed'}>
+
+ {#if remainingSpace > 0}
+ Add Partition
+ {:else}
+ No space left
+ {/if}
+
+
+
+
+
+
+ Size: {humanFormat(currentPartition)}
+
+
+ Remaining space: {humanFormat(remainingSpace)}
+
+
+
+ {#if newPartitions.length > 0}
+
+
+
+ Save Partitions
+
+
+
+ {/if}
+
+
diff --git a/new-web/src/lib/components/skeleton/BottomPanel.svelte b/new-web/src/lib/components/skeleton/BottomPanel.svelte
new file mode 100644
index 00000000..93093a87
--- /dev/null
+++ b/new-web/src/lib/components/skeleton/BottomPanel.svelte
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+ Start Time
+ End Time
+ Node
+ User
+ Action
+ Status
+
+
+
+
+ {#each logs as log, i (i)}
+
+ {convertDbTime(log.started)}
+ {convertDbTime(log.ended)}
+ {log.node}
+ {log.user}@{log.authType}
+ {formatAction(log.action)}
+ {formatStatus(log.status)}
+
+ {/each}
+
+
+
+
+
diff --git a/new-web/src/lib/components/skeleton/LeftPanel.svelte b/new-web/src/lib/components/skeleton/LeftPanel.svelte
new file mode 100644
index 00000000..9d82a4c2
--- /dev/null
+++ b/new-web/src/lib/components/skeleton/LeftPanel.svelte
@@ -0,0 +1,62 @@
+
+
+
+
+
diff --git a/new-web/src/lib/components/skeleton/Shell.svelte b/new-web/src/lib/components/skeleton/Shell.svelte
new file mode 100644
index 00000000..b403b760
--- /dev/null
+++ b/new-web/src/lib/components/skeleton/Shell.svelte
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {@render children?.()}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte
new file mode 100644
index 00000000..a0056912
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte
new file mode 100644
index 00000000..a7b0cf79
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte
new file mode 100644
index 00000000..6c3c6046
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte
new file mode 100644
index 00000000..2ec67dc2
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte
new file mode 100644
index 00000000..f78b97ab
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte
new file mode 100644
index 00000000..c8fa7625
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte
new file mode 100644
index 00000000..a64ee768
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte
new file mode 100644
index 00000000..7ef2b5fb
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte
new file mode 100644
index 00000000..b22d1d50
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/alert-dialog/index.ts b/new-web/src/lib/components/ui/alert-dialog/index.ts
new file mode 100644
index 00000000..cc281c58
--- /dev/null
+++ b/new-web/src/lib/components/ui/alert-dialog/index.ts
@@ -0,0 +1,39 @@
+import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
+import Trigger from "./alert-dialog-trigger.svelte";
+import Title from "./alert-dialog-title.svelte";
+import Action from "./alert-dialog-action.svelte";
+import Cancel from "./alert-dialog-cancel.svelte";
+import Footer from "./alert-dialog-footer.svelte";
+import Header from "./alert-dialog-header.svelte";
+import Overlay from "./alert-dialog-overlay.svelte";
+import Content from "./alert-dialog-content.svelte";
+import Description from "./alert-dialog-description.svelte";
+
+const Root = AlertDialogPrimitive.Root;
+const Portal = AlertDialogPrimitive.Portal;
+
+export {
+ Root,
+ Title,
+ Action,
+ Cancel,
+ Portal,
+ Footer,
+ Header,
+ Trigger,
+ Overlay,
+ Content,
+ Description,
+ //
+ Root as AlertDialog,
+ Title as AlertDialogTitle,
+ Action as AlertDialogAction,
+ Cancel as AlertDialogCancel,
+ Portal as AlertDialogPortal,
+ Footer as AlertDialogFooter,
+ Header as AlertDialogHeader,
+ Trigger as AlertDialogTrigger,
+ Overlay as AlertDialogOverlay,
+ Content as AlertDialogContent,
+ Description as AlertDialogDescription,
+};
diff --git a/new-web/src/lib/components/ui/badge/badge.svelte b/new-web/src/lib/components/ui/badge/badge.svelte
new file mode 100644
index 00000000..50004570
--- /dev/null
+++ b/new-web/src/lib/components/ui/badge/badge.svelte
@@ -0,0 +1,50 @@
+
+
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/badge/index.ts b/new-web/src/lib/components/ui/badge/index.ts
new file mode 100644
index 00000000..64e0aa9b
--- /dev/null
+++ b/new-web/src/lib/components/ui/badge/index.ts
@@ -0,0 +1,2 @@
+export { default as Badge } from "./badge.svelte";
+export { badgeVariants, type BadgeVariant } from "./badge.svelte";
diff --git a/new-web/src/lib/components/ui/card/card-action.svelte b/new-web/src/lib/components/ui/card/card-action.svelte
new file mode 100644
index 00000000..cc36c566
--- /dev/null
+++ b/new-web/src/lib/components/ui/card/card-action.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/card/card-content.svelte b/new-web/src/lib/components/ui/card/card-content.svelte
new file mode 100644
index 00000000..bc90b837
--- /dev/null
+++ b/new-web/src/lib/components/ui/card/card-content.svelte
@@ -0,0 +1,15 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/card/card-description.svelte b/new-web/src/lib/components/ui/card/card-description.svelte
new file mode 100644
index 00000000..9b20ac70
--- /dev/null
+++ b/new-web/src/lib/components/ui/card/card-description.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/card/card-footer.svelte b/new-web/src/lib/components/ui/card/card-footer.svelte
new file mode 100644
index 00000000..cf433539
--- /dev/null
+++ b/new-web/src/lib/components/ui/card/card-footer.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/card/card-header.svelte b/new-web/src/lib/components/ui/card/card-header.svelte
new file mode 100644
index 00000000..8a91abbf
--- /dev/null
+++ b/new-web/src/lib/components/ui/card/card-header.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/card/card-title.svelte b/new-web/src/lib/components/ui/card/card-title.svelte
new file mode 100644
index 00000000..22586e61
--- /dev/null
+++ b/new-web/src/lib/components/ui/card/card-title.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/card/card.svelte b/new-web/src/lib/components/ui/card/card.svelte
new file mode 100644
index 00000000..99448cc9
--- /dev/null
+++ b/new-web/src/lib/components/ui/card/card.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/card/index.ts b/new-web/src/lib/components/ui/card/index.ts
new file mode 100644
index 00000000..4d3fce48
--- /dev/null
+++ b/new-web/src/lib/components/ui/card/index.ts
@@ -0,0 +1,25 @@
+import Root from "./card.svelte";
+import Content from "./card-content.svelte";
+import Description from "./card-description.svelte";
+import Footer from "./card-footer.svelte";
+import Header from "./card-header.svelte";
+import Title from "./card-title.svelte";
+import Action from "./card-action.svelte";
+
+export {
+ Root,
+ Content,
+ Description,
+ Footer,
+ Header,
+ Title,
+ Action,
+ //
+ Root as Card,
+ Content as CardContent,
+ Description as CardDescription,
+ Footer as CardFooter,
+ Header as CardHeader,
+ Title as CardTitle,
+ Action as CardAction,
+};
diff --git a/new-web/src/lib/components/ui/checkbox/checkbox.svelte b/new-web/src/lib/components/ui/checkbox/checkbox.svelte
new file mode 100644
index 00000000..1622e05c
--- /dev/null
+++ b/new-web/src/lib/components/ui/checkbox/checkbox.svelte
@@ -0,0 +1,36 @@
+
+
+
+ {#snippet children({ checked, indeterminate })}
+
+ {#if checked}
+
+ {:else if indeterminate}
+
+ {/if}
+
+ {/snippet}
+
diff --git a/new-web/src/lib/components/ui/checkbox/index.ts b/new-web/src/lib/components/ui/checkbox/index.ts
new file mode 100644
index 00000000..6d92d945
--- /dev/null
+++ b/new-web/src/lib/components/ui/checkbox/index.ts
@@ -0,0 +1,6 @@
+import Root from "./checkbox.svelte";
+export {
+ Root,
+ //
+ Root as Checkbox,
+};
diff --git a/new-web/src/lib/components/ui/command/command-dialog.svelte b/new-web/src/lib/components/ui/command/command-dialog.svelte
new file mode 100644
index 00000000..5c9a82a8
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-dialog.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+ {title}
+ {description}
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/command/command-empty.svelte b/new-web/src/lib/components/ui/command/command-empty.svelte
new file mode 100644
index 00000000..6726cd8d
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-empty.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/command/command-group.svelte b/new-web/src/lib/components/ui/command/command-group.svelte
new file mode 100644
index 00000000..104f8170
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-group.svelte
@@ -0,0 +1,32 @@
+
+
+
+ {#if heading}
+
+ {heading}
+
+ {/if}
+
+
diff --git a/new-web/src/lib/components/ui/command/command-input.svelte b/new-web/src/lib/components/ui/command/command-input.svelte
new file mode 100644
index 00000000..240278a6
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-input.svelte
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/command/command-item.svelte b/new-web/src/lib/components/ui/command/command-item.svelte
new file mode 100644
index 00000000..2297c978
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-item.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/command/command-link-item.svelte b/new-web/src/lib/components/ui/command/command-link-item.svelte
new file mode 100644
index 00000000..944c22dc
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-link-item.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/command/command-list.svelte b/new-web/src/lib/components/ui/command/command-list.svelte
new file mode 100644
index 00000000..569f5957
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-list.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/command/command-separator.svelte b/new-web/src/lib/components/ui/command/command-separator.svelte
new file mode 100644
index 00000000..35c4c957
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-separator.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/command/command-shortcut.svelte b/new-web/src/lib/components/ui/command/command-shortcut.svelte
new file mode 100644
index 00000000..3d68bc56
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command-shortcut.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/command/command.svelte b/new-web/src/lib/components/ui/command/command.svelte
new file mode 100644
index 00000000..c64a77ef
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/command.svelte
@@ -0,0 +1,22 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/command/index.ts b/new-web/src/lib/components/ui/command/index.ts
new file mode 100644
index 00000000..d3dbadea
--- /dev/null
+++ b/new-web/src/lib/components/ui/command/index.ts
@@ -0,0 +1,40 @@
+import { Command as CommandPrimitive } from "bits-ui";
+
+import Root from "./command.svelte";
+import Dialog from "./command-dialog.svelte";
+import Empty from "./command-empty.svelte";
+import Group from "./command-group.svelte";
+import Item from "./command-item.svelte";
+import Input from "./command-input.svelte";
+import List from "./command-list.svelte";
+import Separator from "./command-separator.svelte";
+import Shortcut from "./command-shortcut.svelte";
+import LinkItem from "./command-link-item.svelte";
+
+const Loading = CommandPrimitive.Loading;
+
+export {
+ Root,
+ Dialog,
+ Empty,
+ Group,
+ Item,
+ LinkItem,
+ Input,
+ List,
+ Separator,
+ Shortcut,
+ Loading,
+ //
+ Root as Command,
+ Dialog as CommandDialog,
+ Empty as CommandEmpty,
+ Group as CommandGroup,
+ Item as CommandItem,
+ LinkItem as CommandLinkItem,
+ Input as CommandInput,
+ List as CommandList,
+ Separator as CommandSeparator,
+ Shortcut as CommandShortcut,
+ Loading as CommandLoading,
+};
diff --git a/new-web/src/lib/components/ui/custom-input/checkbox.svelte b/new-web/src/lib/components/ui/custom-input/checkbox.svelte
new file mode 100644
index 00000000..9048903b
--- /dev/null
+++ b/new-web/src/lib/components/ui/custom-input/checkbox.svelte
@@ -0,0 +1,30 @@
+
+
+
+
+
+ {label}
+
+
diff --git a/new-web/src/lib/components/ui/custom-input/combobox.svelte b/new-web/src/lib/components/ui/custom-input/combobox.svelte
new file mode 100644
index 00000000..5ebc232d
--- /dev/null
+++ b/new-web/src/lib/components/ui/custom-input/combobox.svelte
@@ -0,0 +1,145 @@
+
+
+
+
+ {label}
+
+
+
+
+ {#if selectedLabels.length > 0}
+ {#each selectedLabels as lbl, i}
+
+ {lbl}
+
+ {/each}
+ {:else}
+ {placeholder}
+ {/if}
+
+
+
+
+
+
+
+ No data
+
+
+ {#each filteredData as element}
+ selectItem(element.value)}
+ onkeydown={(e) => {
+ if (e.key === 'Enter') selectItem(element.value);
+ }}
+ >
+
+ {element.label}
+
+ {/each}
+
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/custom-input/value.svelte b/new-web/src/lib/components/ui/custom-input/value.svelte
new file mode 100644
index 00000000..80b4b564
--- /dev/null
+++ b/new-web/src/lib/components/ui/custom-input/value.svelte
@@ -0,0 +1,49 @@
+
+
+
+ {#if label}
+ {label}
+ {/if}
+ {#if type === 'textarea'}
+
+ {:else}
+
+ {/if}
+
diff --git a/new-web/src/lib/components/ui/dialog/dialog-close.svelte b/new-web/src/lib/components/ui/dialog/dialog-close.svelte
new file mode 100644
index 00000000..840b2f68
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/dialog-close.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dialog/dialog-content.svelte b/new-web/src/lib/components/ui/dialog/dialog-content.svelte
new file mode 100644
index 00000000..06ba16cc
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/dialog-content.svelte
@@ -0,0 +1,43 @@
+
+
+
+
+
+ {@render children?.()}
+ {#if showCloseButton}
+
+
+ Close
+
+ {/if}
+
+
diff --git a/new-web/src/lib/components/ui/dialog/dialog-description.svelte b/new-web/src/lib/components/ui/dialog/dialog-description.svelte
new file mode 100644
index 00000000..38450239
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/dialog-description.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dialog/dialog-footer.svelte b/new-web/src/lib/components/ui/dialog/dialog-footer.svelte
new file mode 100644
index 00000000..e7ff4468
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/dialog-footer.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/dialog/dialog-header.svelte b/new-web/src/lib/components/ui/dialog/dialog-header.svelte
new file mode 100644
index 00000000..fc90cd9b
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/dialog-header.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/dialog/dialog-overlay.svelte b/new-web/src/lib/components/ui/dialog/dialog-overlay.svelte
new file mode 100644
index 00000000..f81ad833
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/dialog-overlay.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dialog/dialog-title.svelte b/new-web/src/lib/components/ui/dialog/dialog-title.svelte
new file mode 100644
index 00000000..067e55ec
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/dialog-title.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dialog/dialog-trigger.svelte b/new-web/src/lib/components/ui/dialog/dialog-trigger.svelte
new file mode 100644
index 00000000..9d1e8011
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/dialog-trigger.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dialog/index.ts b/new-web/src/lib/components/ui/dialog/index.ts
new file mode 100644
index 00000000..dce1d9dc
--- /dev/null
+++ b/new-web/src/lib/components/ui/dialog/index.ts
@@ -0,0 +1,37 @@
+import { Dialog as DialogPrimitive } from "bits-ui";
+
+import Title from "./dialog-title.svelte";
+import Footer from "./dialog-footer.svelte";
+import Header from "./dialog-header.svelte";
+import Overlay from "./dialog-overlay.svelte";
+import Content from "./dialog-content.svelte";
+import Description from "./dialog-description.svelte";
+import Trigger from "./dialog-trigger.svelte";
+import Close from "./dialog-close.svelte";
+
+const Root = DialogPrimitive.Root;
+const Portal = DialogPrimitive.Portal;
+
+export {
+ Root,
+ Title,
+ Portal,
+ Footer,
+ Header,
+ Trigger,
+ Overlay,
+ Content,
+ Description,
+ Close,
+ //
+ Root as Dialog,
+ Title as DialogTitle,
+ Portal as DialogPortal,
+ Footer as DialogFooter,
+ Header as DialogHeader,
+ Trigger as DialogTrigger,
+ Overlay as DialogOverlay,
+ Content as DialogContent,
+ Description as DialogDescription,
+ Close as DialogClose,
+};
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
new file mode 100644
index 00000000..e03f9491
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
@@ -0,0 +1,41 @@
+
+
+
+ {#snippet children({ checked, indeterminate })}
+
+ {#if indeterminate}
+
+ {:else}
+
+ {/if}
+
+ {@render childrenProp?.()}
+ {/snippet}
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
new file mode 100644
index 00000000..498c4b92
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte
new file mode 100644
index 00000000..48d14a91
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte
@@ -0,0 +1,22 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte
new file mode 100644
index 00000000..aca1f7bd
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
new file mode 100644
index 00000000..64bb2831
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
@@ -0,0 +1,27 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
new file mode 100644
index 00000000..f72e477e
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
@@ -0,0 +1,24 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
new file mode 100644
index 00000000..189aef40
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
@@ -0,0 +1,16 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
new file mode 100644
index 00000000..513170aa
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
@@ -0,0 +1,31 @@
+
+
+
+ {#snippet children({ checked })}
+
+ {#if checked}
+
+ {/if}
+
+ {@render childrenProp?.({ checked })}
+ {/snippet}
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
new file mode 100644
index 00000000..90f1b6f1
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
new file mode 100644
index 00000000..69749477
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
new file mode 100644
index 00000000..30deb9eb
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
new file mode 100644
index 00000000..f9b286a2
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
@@ -0,0 +1,29 @@
+
+
+
+ {@render children?.()}
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte
new file mode 100644
index 00000000..cb053444
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/dropdown-menu/index.ts b/new-web/src/lib/components/ui/dropdown-menu/index.ts
new file mode 100644
index 00000000..1cf9f701
--- /dev/null
+++ b/new-web/src/lib/components/ui/dropdown-menu/index.ts
@@ -0,0 +1,49 @@
+import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
+import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
+import Content from "./dropdown-menu-content.svelte";
+import Group from "./dropdown-menu-group.svelte";
+import Item from "./dropdown-menu-item.svelte";
+import Label from "./dropdown-menu-label.svelte";
+import RadioGroup from "./dropdown-menu-radio-group.svelte";
+import RadioItem from "./dropdown-menu-radio-item.svelte";
+import Separator from "./dropdown-menu-separator.svelte";
+import Shortcut from "./dropdown-menu-shortcut.svelte";
+import Trigger from "./dropdown-menu-trigger.svelte";
+import SubContent from "./dropdown-menu-sub-content.svelte";
+import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
+import GroupHeading from "./dropdown-menu-group-heading.svelte";
+const Sub = DropdownMenuPrimitive.Sub;
+const Root = DropdownMenuPrimitive.Root;
+
+export {
+ CheckboxItem,
+ Content,
+ Root as DropdownMenu,
+ CheckboxItem as DropdownMenuCheckboxItem,
+ Content as DropdownMenuContent,
+ Group as DropdownMenuGroup,
+ Item as DropdownMenuItem,
+ Label as DropdownMenuLabel,
+ RadioGroup as DropdownMenuRadioGroup,
+ RadioItem as DropdownMenuRadioItem,
+ Separator as DropdownMenuSeparator,
+ Shortcut as DropdownMenuShortcut,
+ Sub as DropdownMenuSub,
+ SubContent as DropdownMenuSubContent,
+ SubTrigger as DropdownMenuSubTrigger,
+ Trigger as DropdownMenuTrigger,
+ GroupHeading as DropdownMenuGroupHeading,
+ Group,
+ GroupHeading,
+ Item,
+ Label,
+ RadioGroup,
+ RadioItem,
+ Root,
+ Separator,
+ Shortcut,
+ Sub,
+ SubContent,
+ SubTrigger,
+ Trigger,
+};
diff --git a/new-web/src/lib/components/ui/input/index.ts b/new-web/src/lib/components/ui/input/index.ts
new file mode 100644
index 00000000..f47b6d3f
--- /dev/null
+++ b/new-web/src/lib/components/ui/input/index.ts
@@ -0,0 +1,7 @@
+import Root from "./input.svelte";
+
+export {
+ Root,
+ //
+ Root as Input,
+};
diff --git a/new-web/src/lib/components/ui/input/input.svelte b/new-web/src/lib/components/ui/input/input.svelte
new file mode 100644
index 00000000..19c6daeb
--- /dev/null
+++ b/new-web/src/lib/components/ui/input/input.svelte
@@ -0,0 +1,51 @@
+
+
+{#if type === "file"}
+
+{:else}
+
+{/if}
diff --git a/new-web/src/lib/components/ui/label/index.ts b/new-web/src/lib/components/ui/label/index.ts
new file mode 100644
index 00000000..8bfca0b3
--- /dev/null
+++ b/new-web/src/lib/components/ui/label/index.ts
@@ -0,0 +1,7 @@
+import Root from "./label.svelte";
+
+export {
+ Root,
+ //
+ Root as Label,
+};
diff --git a/new-web/src/lib/components/ui/label/label.svelte b/new-web/src/lib/components/ui/label/label.svelte
new file mode 100644
index 00000000..d0afda3d
--- /dev/null
+++ b/new-web/src/lib/components/ui/label/label.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/popover/index.ts b/new-web/src/lib/components/ui/popover/index.ts
new file mode 100644
index 00000000..9f30922f
--- /dev/null
+++ b/new-web/src/lib/components/ui/popover/index.ts
@@ -0,0 +1,17 @@
+import { Popover as PopoverPrimitive } from "bits-ui";
+import Content from "./popover-content.svelte";
+import Trigger from "./popover-trigger.svelte";
+const Root = PopoverPrimitive.Root;
+const Close = PopoverPrimitive.Close;
+
+export {
+ Root,
+ Content,
+ Trigger,
+ Close,
+ //
+ Root as Popover,
+ Content as PopoverContent,
+ Trigger as PopoverTrigger,
+ Close as PopoverClose,
+};
diff --git a/new-web/src/lib/components/ui/popover/popover-content.svelte b/new-web/src/lib/components/ui/popover/popover-content.svelte
new file mode 100644
index 00000000..9bced7aa
--- /dev/null
+++ b/new-web/src/lib/components/ui/popover/popover-content.svelte
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/popover/popover-trigger.svelte b/new-web/src/lib/components/ui/popover/popover-trigger.svelte
new file mode 100644
index 00000000..586323c7
--- /dev/null
+++ b/new-web/src/lib/components/ui/popover/popover-trigger.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/progress/index.ts b/new-web/src/lib/components/ui/progress/index.ts
new file mode 100644
index 00000000..25eee615
--- /dev/null
+++ b/new-web/src/lib/components/ui/progress/index.ts
@@ -0,0 +1,7 @@
+import Root from "./progress.svelte";
+
+export {
+ Root,
+ //
+ Root as Progress,
+};
diff --git a/new-web/src/lib/components/ui/progress/progress.svelte b/new-web/src/lib/components/ui/progress/progress.svelte
new file mode 100644
index 00000000..68330136
--- /dev/null
+++ b/new-web/src/lib/components/ui/progress/progress.svelte
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/radio-group/index.ts b/new-web/src/lib/components/ui/radio-group/index.ts
new file mode 100644
index 00000000..90b33fe5
--- /dev/null
+++ b/new-web/src/lib/components/ui/radio-group/index.ts
@@ -0,0 +1,10 @@
+import Root from "./radio-group.svelte";
+import Item from "./radio-group-item.svelte";
+
+export {
+ Root,
+ Item,
+ //
+ Root as RadioGroup,
+ Item as RadioGroupItem,
+};
diff --git a/new-web/src/lib/components/ui/radio-group/radio-group-item.svelte b/new-web/src/lib/components/ui/radio-group/radio-group-item.svelte
new file mode 100644
index 00000000..2cb0710b
--- /dev/null
+++ b/new-web/src/lib/components/ui/radio-group/radio-group-item.svelte
@@ -0,0 +1,31 @@
+
+
+
+ {#snippet children({ checked })}
+
+ {#if checked}
+
+ {/if}
+
+ {/snippet}
+
diff --git a/new-web/src/lib/components/ui/radio-group/radio-group.svelte b/new-web/src/lib/components/ui/radio-group/radio-group.svelte
new file mode 100644
index 00000000..da2912b0
--- /dev/null
+++ b/new-web/src/lib/components/ui/radio-group/radio-group.svelte
@@ -0,0 +1,19 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/resizable/index.ts b/new-web/src/lib/components/ui/resizable/index.ts
new file mode 100644
index 00000000..2e37f114
--- /dev/null
+++ b/new-web/src/lib/components/ui/resizable/index.ts
@@ -0,0 +1,13 @@
+import { Pane } from "paneforge";
+import Handle from "./resizable-handle.svelte";
+import PaneGroup from "./resizable-pane-group.svelte";
+
+export {
+ PaneGroup,
+ Pane,
+ Handle,
+ //
+ PaneGroup as ResizablePaneGroup,
+ Pane as ResizablePane,
+ Handle as ResizableHandle,
+};
diff --git a/new-web/src/lib/components/ui/resizable/resizable-handle.svelte b/new-web/src/lib/components/ui/resizable/resizable-handle.svelte
new file mode 100644
index 00000000..9c8da71e
--- /dev/null
+++ b/new-web/src/lib/components/ui/resizable/resizable-handle.svelte
@@ -0,0 +1,30 @@
+
+
+div]:rotate-90",
+ className
+ )}
+ {...restProps}
+>
+ {#if withHandle}
+
+
+
+ {/if}
+
diff --git a/new-web/src/lib/components/ui/resizable/resizable-pane-group.svelte b/new-web/src/lib/components/ui/resizable/resizable-pane-group.svelte
new file mode 100644
index 00000000..a810d7ec
--- /dev/null
+++ b/new-web/src/lib/components/ui/resizable/resizable-pane-group.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/scroll-area/index.ts b/new-web/src/lib/components/ui/scroll-area/index.ts
new file mode 100644
index 00000000..e86a25b2
--- /dev/null
+++ b/new-web/src/lib/components/ui/scroll-area/index.ts
@@ -0,0 +1,10 @@
+import Scrollbar from "./scroll-area-scrollbar.svelte";
+import Root from "./scroll-area.svelte";
+
+export {
+ Root,
+ Scrollbar,
+ //,
+ Root as ScrollArea,
+ Scrollbar as ScrollAreaScrollbar,
+};
diff --git a/new-web/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte b/new-web/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte
new file mode 100644
index 00000000..41274446
--- /dev/null
+++ b/new-web/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte
@@ -0,0 +1,31 @@
+
+
+
+ {@render children?.()}
+
+
diff --git a/new-web/src/lib/components/ui/scroll-area/scroll-area.svelte b/new-web/src/lib/components/ui/scroll-area/scroll-area.svelte
new file mode 100644
index 00000000..38a1847f
--- /dev/null
+++ b/new-web/src/lib/components/ui/scroll-area/scroll-area.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+ {@render children?.()}
+
+ {#if orientation === "vertical" || orientation === "both"}
+
+ {/if}
+ {#if orientation === "horizontal" || orientation === "both"}
+
+ {/if}
+
+
diff --git a/new-web/src/lib/components/ui/select/index.ts b/new-web/src/lib/components/ui/select/index.ts
new file mode 100644
index 00000000..9e8d3e90
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/index.ts
@@ -0,0 +1,37 @@
+import { Select as SelectPrimitive } from "bits-ui";
+
+import Group from "./select-group.svelte";
+import Label from "./select-label.svelte";
+import Item from "./select-item.svelte";
+import Content from "./select-content.svelte";
+import Trigger from "./select-trigger.svelte";
+import Separator from "./select-separator.svelte";
+import ScrollDownButton from "./select-scroll-down-button.svelte";
+import ScrollUpButton from "./select-scroll-up-button.svelte";
+import GroupHeading from "./select-group-heading.svelte";
+
+const Root = SelectPrimitive.Root;
+
+export {
+ Root,
+ Group,
+ Label,
+ Item,
+ Content,
+ Trigger,
+ Separator,
+ ScrollDownButton,
+ ScrollUpButton,
+ GroupHeading,
+ //
+ Root as Select,
+ Group as SelectGroup,
+ Label as SelectLabel,
+ Item as SelectItem,
+ Content as SelectContent,
+ Trigger as SelectTrigger,
+ Separator as SelectSeparator,
+ ScrollDownButton as SelectScrollDownButton,
+ ScrollUpButton as SelectScrollUpButton,
+ GroupHeading as SelectGroupHeading,
+};
diff --git a/new-web/src/lib/components/ui/select/select-content.svelte b/new-web/src/lib/components/ui/select/select-content.svelte
new file mode 100644
index 00000000..dc16d65d
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-content.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ {@render children?.()}
+
+
+
+
diff --git a/new-web/src/lib/components/ui/select/select-group-heading.svelte b/new-web/src/lib/components/ui/select/select-group-heading.svelte
new file mode 100644
index 00000000..1fab5f00
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-group-heading.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/select/select-group.svelte b/new-web/src/lib/components/ui/select/select-group.svelte
new file mode 100644
index 00000000..5454fdb3
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-group.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/select/select-item.svelte b/new-web/src/lib/components/ui/select/select-item.svelte
new file mode 100644
index 00000000..49dbbd7f
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-item.svelte
@@ -0,0 +1,38 @@
+
+
+
+ {#snippet children({ selected, highlighted })}
+
+ {#if selected}
+
+ {/if}
+
+ {#if childrenProp}
+ {@render childrenProp({ selected, highlighted })}
+ {:else}
+ {label || value}
+ {/if}
+ {/snippet}
+
diff --git a/new-web/src/lib/components/ui/select/select-label.svelte b/new-web/src/lib/components/ui/select/select-label.svelte
new file mode 100644
index 00000000..46960259
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-label.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/select/select-scroll-down-button.svelte b/new-web/src/lib/components/ui/select/select-scroll-down-button.svelte
new file mode 100644
index 00000000..36292058
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-scroll-down-button.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/select/select-scroll-up-button.svelte b/new-web/src/lib/components/ui/select/select-scroll-up-button.svelte
new file mode 100644
index 00000000..1aa2300c
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-scroll-up-button.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
diff --git a/new-web/src/lib/components/ui/select/select-separator.svelte b/new-web/src/lib/components/ui/select/select-separator.svelte
new file mode 100644
index 00000000..0eac3ebc
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-separator.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/select/select-trigger.svelte b/new-web/src/lib/components/ui/select/select-trigger.svelte
new file mode 100644
index 00000000..d405187d
--- /dev/null
+++ b/new-web/src/lib/components/ui/select/select-trigger.svelte
@@ -0,0 +1,29 @@
+
+
+
+ {@render children?.()}
+
+
diff --git a/new-web/src/lib/components/ui/separator/index.ts b/new-web/src/lib/components/ui/separator/index.ts
new file mode 100644
index 00000000..82442d2c
--- /dev/null
+++ b/new-web/src/lib/components/ui/separator/index.ts
@@ -0,0 +1,7 @@
+import Root from "./separator.svelte";
+
+export {
+ Root,
+ //
+ Root as Separator,
+};
diff --git a/new-web/src/lib/components/ui/separator/separator.svelte b/new-web/src/lib/components/ui/separator/separator.svelte
new file mode 100644
index 00000000..09d88f4a
--- /dev/null
+++ b/new-web/src/lib/components/ui/separator/separator.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/sheet/index.ts b/new-web/src/lib/components/ui/sheet/index.ts
new file mode 100644
index 00000000..01d40c80
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/index.ts
@@ -0,0 +1,36 @@
+import { Dialog as SheetPrimitive } from "bits-ui";
+import Trigger from "./sheet-trigger.svelte";
+import Close from "./sheet-close.svelte";
+import Overlay from "./sheet-overlay.svelte";
+import Content from "./sheet-content.svelte";
+import Header from "./sheet-header.svelte";
+import Footer from "./sheet-footer.svelte";
+import Title from "./sheet-title.svelte";
+import Description from "./sheet-description.svelte";
+
+const Root = SheetPrimitive.Root;
+const Portal = SheetPrimitive.Portal;
+
+export {
+ Root,
+ Close,
+ Trigger,
+ Portal,
+ Overlay,
+ Content,
+ Header,
+ Footer,
+ Title,
+ Description,
+ //
+ Root as Sheet,
+ Close as SheetClose,
+ Trigger as SheetTrigger,
+ Portal as SheetPortal,
+ Overlay as SheetOverlay,
+ Content as SheetContent,
+ Header as SheetHeader,
+ Footer as SheetFooter,
+ Title as SheetTitle,
+ Description as SheetDescription,
+};
diff --git a/new-web/src/lib/components/ui/sheet/sheet-close.svelte b/new-web/src/lib/components/ui/sheet/sheet-close.svelte
new file mode 100644
index 00000000..ae382c12
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/sheet-close.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/sheet/sheet-content.svelte b/new-web/src/lib/components/ui/sheet/sheet-content.svelte
new file mode 100644
index 00000000..856922ea
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/sheet-content.svelte
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+ {@render children?.()}
+
+
+ Close
+
+
+
diff --git a/new-web/src/lib/components/ui/sheet/sheet-description.svelte b/new-web/src/lib/components/ui/sheet/sheet-description.svelte
new file mode 100644
index 00000000..333b17a7
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/sheet-description.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/sheet/sheet-footer.svelte b/new-web/src/lib/components/ui/sheet/sheet-footer.svelte
new file mode 100644
index 00000000..dd9ed84b
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/sheet-footer.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/sheet/sheet-header.svelte b/new-web/src/lib/components/ui/sheet/sheet-header.svelte
new file mode 100644
index 00000000..757a6a56
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/sheet-header.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/sheet/sheet-overlay.svelte b/new-web/src/lib/components/ui/sheet/sheet-overlay.svelte
new file mode 100644
index 00000000..345e197b
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/sheet-overlay.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/sheet/sheet-title.svelte b/new-web/src/lib/components/ui/sheet/sheet-title.svelte
new file mode 100644
index 00000000..9fda327e
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/sheet-title.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/sheet/sheet-trigger.svelte b/new-web/src/lib/components/ui/sheet/sheet-trigger.svelte
new file mode 100644
index 00000000..e266975f
--- /dev/null
+++ b/new-web/src/lib/components/ui/sheet/sheet-trigger.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/slider/index.ts b/new-web/src/lib/components/ui/slider/index.ts
new file mode 100644
index 00000000..820f209c
--- /dev/null
+++ b/new-web/src/lib/components/ui/slider/index.ts
@@ -0,0 +1,7 @@
+import Root from "./slider.svelte";
+
+export {
+ Root,
+ //
+ Root as Slider,
+};
diff --git a/new-web/src/lib/components/ui/slider/slider.svelte b/new-web/src/lib/components/ui/slider/slider.svelte
new file mode 100644
index 00000000..b06e110b
--- /dev/null
+++ b/new-web/src/lib/components/ui/slider/slider.svelte
@@ -0,0 +1,52 @@
+
+
+
+
+ {#snippet children({ thumbs })}
+
+
+
+ {#each thumbs as thumb (thumb)}
+
+ {/each}
+ {/snippet}
+
diff --git a/new-web/src/lib/components/ui/table/index.ts b/new-web/src/lib/components/ui/table/index.ts
new file mode 100644
index 00000000..14695c81
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/index.ts
@@ -0,0 +1,28 @@
+import Root from "./table.svelte";
+import Body from "./table-body.svelte";
+import Caption from "./table-caption.svelte";
+import Cell from "./table-cell.svelte";
+import Footer from "./table-footer.svelte";
+import Head from "./table-head.svelte";
+import Header from "./table-header.svelte";
+import Row from "./table-row.svelte";
+
+export {
+ Root,
+ Body,
+ Caption,
+ Cell,
+ Footer,
+ Head,
+ Header,
+ Row,
+ //
+ Root as Table,
+ Body as TableBody,
+ Caption as TableCaption,
+ Cell as TableCell,
+ Footer as TableFooter,
+ Head as TableHead,
+ Header as TableHeader,
+ Row as TableRow,
+};
diff --git a/new-web/src/lib/components/ui/table/table-body.svelte b/new-web/src/lib/components/ui/table/table-body.svelte
new file mode 100644
index 00000000..29e96875
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/table-body.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/table/table-caption.svelte b/new-web/src/lib/components/ui/table/table-caption.svelte
new file mode 100644
index 00000000..4696cff5
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/table-caption.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/table/table-cell.svelte b/new-web/src/lib/components/ui/table/table-cell.svelte
new file mode 100644
index 00000000..59920484
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/table-cell.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/table/table-footer.svelte b/new-web/src/lib/components/ui/table/table-footer.svelte
new file mode 100644
index 00000000..b9b14ebf
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/table-footer.svelte
@@ -0,0 +1,20 @@
+
+
+tr]:last:border-b-0", className)}
+ {...restProps}
+>
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/table/table-head.svelte b/new-web/src/lib/components/ui/table/table-head.svelte
new file mode 100644
index 00000000..2742939a
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/table-head.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/table/table-header.svelte b/new-web/src/lib/components/ui/table/table-header.svelte
new file mode 100644
index 00000000..f47d2597
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/table-header.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/table/table-row.svelte b/new-web/src/lib/components/ui/table/table-row.svelte
new file mode 100644
index 00000000..8829581c
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/table-row.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {@render children?.()}
+
diff --git a/new-web/src/lib/components/ui/table/table.svelte b/new-web/src/lib/components/ui/table/table.svelte
new file mode 100644
index 00000000..a3349563
--- /dev/null
+++ b/new-web/src/lib/components/ui/table/table.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+ {@render children?.()}
+
+
diff --git a/new-web/src/lib/components/ui/tabs/index.ts b/new-web/src/lib/components/ui/tabs/index.ts
new file mode 100644
index 00000000..12d4327a
--- /dev/null
+++ b/new-web/src/lib/components/ui/tabs/index.ts
@@ -0,0 +1,16 @@
+import Root from "./tabs.svelte";
+import Content from "./tabs-content.svelte";
+import List from "./tabs-list.svelte";
+import Trigger from "./tabs-trigger.svelte";
+
+export {
+ Root,
+ Content,
+ List,
+ Trigger,
+ //
+ Root as Tabs,
+ Content as TabsContent,
+ List as TabsList,
+ Trigger as TabsTrigger,
+};
diff --git a/new-web/src/lib/components/ui/tabs/tabs-content.svelte b/new-web/src/lib/components/ui/tabs/tabs-content.svelte
new file mode 100644
index 00000000..340d65cf
--- /dev/null
+++ b/new-web/src/lib/components/ui/tabs/tabs-content.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/tabs/tabs-list.svelte b/new-web/src/lib/components/ui/tabs/tabs-list.svelte
new file mode 100644
index 00000000..08932b60
--- /dev/null
+++ b/new-web/src/lib/components/ui/tabs/tabs-list.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/tabs/tabs-trigger.svelte b/new-web/src/lib/components/ui/tabs/tabs-trigger.svelte
new file mode 100644
index 00000000..dced992e
--- /dev/null
+++ b/new-web/src/lib/components/ui/tabs/tabs-trigger.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/tabs/tabs.svelte b/new-web/src/lib/components/ui/tabs/tabs.svelte
new file mode 100644
index 00000000..ef6cada5
--- /dev/null
+++ b/new-web/src/lib/components/ui/tabs/tabs.svelte
@@ -0,0 +1,19 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/textarea/index.ts b/new-web/src/lib/components/ui/textarea/index.ts
new file mode 100644
index 00000000..ace797a8
--- /dev/null
+++ b/new-web/src/lib/components/ui/textarea/index.ts
@@ -0,0 +1,7 @@
+import Root from "./textarea.svelte";
+
+export {
+ Root,
+ //
+ Root as Textarea,
+};
diff --git a/new-web/src/lib/components/ui/textarea/textarea.svelte b/new-web/src/lib/components/ui/textarea/textarea.svelte
new file mode 100644
index 00000000..545b377b
--- /dev/null
+++ b/new-web/src/lib/components/ui/textarea/textarea.svelte
@@ -0,0 +1,22 @@
+
+
+
diff --git a/new-web/src/lib/components/ui/tooltip/index.ts b/new-web/src/lib/components/ui/tooltip/index.ts
new file mode 100644
index 00000000..313a7f06
--- /dev/null
+++ b/new-web/src/lib/components/ui/tooltip/index.ts
@@ -0,0 +1,21 @@
+import { Tooltip as TooltipPrimitive } from "bits-ui";
+import Trigger from "./tooltip-trigger.svelte";
+import Content from "./tooltip-content.svelte";
+
+const Root = TooltipPrimitive.Root;
+const Provider = TooltipPrimitive.Provider;
+const Portal = TooltipPrimitive.Portal;
+
+export {
+ Root,
+ Trigger,
+ Content,
+ Provider,
+ Portal,
+ //
+ Root as Tooltip,
+ Content as TooltipContent,
+ Trigger as TooltipTrigger,
+ Provider as TooltipProvider,
+ Portal as TooltipPortal,
+};
diff --git a/new-web/src/lib/components/ui/tooltip/tooltip-content.svelte b/new-web/src/lib/components/ui/tooltip/tooltip-content.svelte
new file mode 100644
index 00000000..b0c399ca
--- /dev/null
+++ b/new-web/src/lib/components/ui/tooltip/tooltip-content.svelte
@@ -0,0 +1,47 @@
+
+
+
+
+ {@render children?.()}
+
+ {#snippet child({ props })}
+
+ {/snippet}
+
+
+
diff --git a/new-web/src/lib/components/ui/tooltip/tooltip-trigger.svelte b/new-web/src/lib/components/ui/tooltip/tooltip-trigger.svelte
new file mode 100644
index 00000000..1acdaa47
--- /dev/null
+++ b/new-web/src/lib/components/ui/tooltip/tooltip-trigger.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/new-web/src/lib/components/zfs/ViewSnapshotJobs.svelte b/new-web/src/lib/components/zfs/ViewSnapshotJobs.svelte
new file mode 100644
index 00000000..8cb088e1
--- /dev/null
+++ b/new-web/src/lib/components/zfs/ViewSnapshotJobs.svelte
@@ -0,0 +1,156 @@
+
+
+ close()}>
+
+
+
+
+
+
+ View Snapshot Jobs
+
+
+
+
+
+
close()}
+ >
+
+
+
+
+
+
+
+
+ ID
+ Dataset
+ Prefix
+ Interval
+ Last Run
+
+
+
+
+ {#if periodicSnapshots && periodicSnapshots.length > 0}
+ {#each periodicSnapshots as snapshot, index}
+
+ {snapshot.id}
+ {getDatasetName(snapshot.guid)}
+ {snapshot.prefix}
+ {intervalToString(snapshot.interval)}
+ {dateToAgo(snapshot.lastRunAt)}
+
+ {#if !shadowDeleted.includes(snapshot.id)}
+
+ shadowDeleted.push(snapshot.id)}
+ >
+
+
+
+ {:else}
+
+ Deleted
+
+ {/if}
+
+ {/each}
+ {:else}
+
+
+ No snapshot jobs
+
+
+ {/if}
+
+
+
+
+
+
+ close()}>Cancel
+ {#if shadowDeleted.length > 0}
+ Save Snapshot Jobs
+ {/if}
+
+
+
+
diff --git a/new-web/src/lib/utils/http.ts b/new-web/src/lib/utils/http.ts
index 6e1c3b62..9c562691 100644
--- a/new-web/src/lib/utils/http.ts
+++ b/new-web/src/lib/utils/http.ts
@@ -12,132 +12,132 @@ import { api } from '$lib/api/common';
import { APIResponseSchema, type APIResponse } from '$lib/types/common';
import type { QueryFunctionContext } from '@sveltestack/svelte-query';
import adze from 'adze';
-import toast from 'svelte-french-toast';
+import { toast } from "svelte-sonner";
import { z } from 'zod/v4';
import { getValidationError } from './i18n';
export async function apiRequest(
- endpoint: string,
- schema: T,
- method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
- body?: unknown
+ endpoint: string,
+ schema: T,
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
+ body?: unknown
): Promise> {
- try {
- const config = {
- method,
- url: endpoint,
- ...(body ? { data: body } : {})
- };
+ try {
+ const config = {
+ method,
+ url: endpoint,
+ ...(body ? { data: body } : {})
+ };
- const response = await api.request({ ...config, validateStatus: () => true });
- const apiResponse = APIResponseSchema.safeParse(response.data);
+ const response = await api.request({ ...config, validateStatus: () => true });
+ const apiResponse = APIResponseSchema.safeParse(response.data);
- /* Couldn't parse response data into APIResponse so we'll just return the data? */
- if (!apiResponse.success) {
- return apiResponse.data;
- }
+ /* Couldn't parse response data into APIResponse so we'll just return the data? */
+ if (!apiResponse.success) {
+ return apiResponse.data;
+ }
- /* Caller asked for a raw response */
- if (schema._def.description === 'APIResponseSchema') {
- return apiResponse.data as z.infer;
- }
+ /* Caller asked for a raw response */
+ if (schema._def.description === 'APIResponseSchema') {
+ return apiResponse.data as z.infer;
+ }
- if (apiResponse.data.data) {
- const parsedResult = schema.safeParse(apiResponse.data.data);
- if (parsedResult.success) {
- return parsedResult.data;
- } else {
- // console.log('Validation Error', parsedResult.error);
- adze.withEmoji.warn('Zod Validation Error', parsedResult.error);
- return getDefaultValue(schema, apiResponse.data);
- }
- }
+ if (apiResponse.data.data) {
+ const parsedResult = schema.safeParse(apiResponse.data.data);
+ if (parsedResult.success) {
+ return parsedResult.data;
+ } else {
+ // console.log('Validation Error', parsedResult.error);
+ adze.withEmoji.warn('Zod Validation Error', parsedResult.error);
+ return getDefaultValue(schema, apiResponse.data);
+ }
+ }
- return getDefaultValue(schema, apiResponse.data);
- } catch (error) {
- return getDefaultValue(schema, { status: 'error' });
- }
+ return getDefaultValue(schema, apiResponse.data);
+ } catch (error) {
+ return getDefaultValue(schema, { status: 'error' });
+ }
}
function getDefaultValue(schema: T, response: APIResponse): z.infer {
- if (schema instanceof z.ZodArray) {
- return [] as z.infer;
- }
+ if (schema instanceof z.ZodArray) {
+ return [] as z.infer;
+ }
- if (schema instanceof z.ZodObject) {
- return response as z.infer;
- }
+ if (schema instanceof z.ZodObject) {
+ return response as z.infer;
+ }
- return undefined as z.infer;
+ return undefined as z.infer;
}
type CacheEntry = {
- timestamp: number;
- data: T;
+ timestamp: number;
+ data: T;
};
export async function cachedFetch(
- key: string,
- fetchFunction: (queryObj?: QueryFunctionContext) => Promise,
- duration: number
+ key: string,
+ fetchFunction: (queryObj?: QueryFunctionContext) => Promise,
+ duration: number
): Promise {
- const now = Date.now();
- const storedEntry = localStorage.getItem(key);
+ const now = Date.now();
+ const storedEntry = localStorage.getItem(key);
- if (storedEntry) {
- try {
- const entry: CacheEntry = JSON.parse(storedEntry);
- if (now - entry.timestamp < duration) {
- return entry.data;
- }
- } catch (error) {
- console.error(`Failed to parse cached data for key "${key}"`, error);
- }
- }
+ if (storedEntry) {
+ try {
+ const entry: CacheEntry = JSON.parse(storedEntry);
+ if (now - entry.timestamp < duration) {
+ return entry.data;
+ }
+ } catch (error) {
+ console.error(`Failed to parse cached data for key "${key}"`, error);
+ }
+ }
- const data = await fetchFunction();
- const entry: CacheEntry = { timestamp: now, data };
- localStorage.setItem(key, JSON.stringify(entry));
- return data;
+ const data = await fetchFunction();
+ const entry: CacheEntry = { timestamp: now, data };
+ localStorage.setItem(key, JSON.stringify(entry));
+ return data;
}
export async function updateCache(key: string, obj: T): Promise {
- const now = Date.now();
- const storedEntry = localStorage.getItem(key);
+ const now = Date.now();
+ const storedEntry = localStorage.getItem(key);
- if (storedEntry) {
- try {
- const entry: CacheEntry = JSON.parse(storedEntry);
- entry.data = obj;
- entry.timestamp = now;
- localStorage.setItem(key, JSON.stringify(entry));
- } catch (error) {
- console.error(`Failed to parse cached data for key "${key}"`, error);
- }
- }
+ if (storedEntry) {
+ try {
+ const entry: CacheEntry = JSON.parse(storedEntry);
+ entry.data = obj;
+ entry.timestamp = now;
+ localStorage.setItem(key, JSON.stringify(entry));
+ } catch (error) {
+ console.error(`Failed to parse cached data for key "${key}"`, error);
+ }
+ }
}
export function isAPIResponse(obj: any): obj is APIResponse {
- return (
- obj &&
- typeof obj.status === 'string' &&
- (typeof obj.message === 'string' || typeof obj.error === 'string')
- );
+ return (
+ obj &&
+ typeof obj.status === 'string' &&
+ (typeof obj.message === 'string' || typeof obj.error === 'string')
+ );
}
export function handleValidationErrors(result: APIResponse, section: string): void {
- adze.withEmoji.error('Validation Error', result);
- if (result.error === 'validation_error') {
- if (Array.isArray(result.data)) {
- if (result.data.length > 0) {
- toast.error(getValidationError(result.data[0], section), {
- position: 'bottom-center'
- });
- }
- }
- }
+ adze.withEmoji.error('Validation Error', result);
+ if (result.error === 'validation_error') {
+ if (Array.isArray(result.data)) {
+ if (result.data.length > 0) {
+ toast.error(getValidationError(result.data[0], section), {
+ position: 'bottom-center'
+ });
+ }
+ }
+ }
}
export function handleAPIError(result: APIResponse): void {
- adze.withEmoji.error('API Error', result);
+ adze.withEmoji.error('API Error', result);
}
diff --git a/new-web/src/lib/utils/vm/vm.ts b/new-web/src/lib/utils/vm/vm.ts
index d6e56789..707401b4 100644
--- a/new-web/src/lib/utils/vm/vm.ts
+++ b/new-web/src/lib/utils/vm/vm.ts
@@ -1,96 +1,96 @@
import type { CreateData } from '$lib/types/vm/vm';
-import toast from 'svelte-french-toast';
+import { toast } from "svelte-sonner";
import { isValidMACAddress, isValidVMName } from '../string';
export function isValidCreateData(modal: CreateData): boolean {
- const toastConfig: Record = {
- duration: 3000,
- position: 'bottom-center'
- };
+ const toastConfig: Record = {
+ duration: 3000,
+ position: 'bottom-center'
+ };
- if (!isValidVMName(modal.name)) {
- toast.error('Invalid name', toastConfig);
- return false;
- }
+ if (!isValidVMName(modal.name)) {
+ toast.error('Invalid name', toastConfig);
+ return false;
+ }
- if (modal.id < 1 || modal.id > 9999) {
- toast.error('Invalid ID', toastConfig);
- return false;
- }
+ if (modal.id < 1 || modal.id > 9999) {
+ toast.error('Invalid ID', toastConfig);
+ return false;
+ }
- if (modal.description && (modal.description.length < 1 || modal.description.length > 1024)) {
- toast.error('Invalid description', toastConfig);
- }
+ if (modal.description && (modal.description.length < 1 || modal.description.length > 1024)) {
+ toast.error('Invalid description', toastConfig);
+ }
- if (modal.storage.type === 'raw') {
- if (!modal.storage.size || modal.storage.size < 1024 * 1024 * 128) {
- toast.error('Disk size must be >= 128 MiB', toastConfig);
- }
- }
+ if (modal.storage.type === 'raw') {
+ if (!modal.storage.size || modal.storage.size < 1024 * 1024 * 128) {
+ toast.error('Disk size must be >= 128 MiB', toastConfig);
+ }
+ }
- if (modal.storage.type === 'raw' || modal.storage.type === 'zvol') {
- if (!modal.storage.guid || modal.storage.guid.length < 1) {
- const noun = modal.storage.type === 'raw' ? 'filesystem' : 'volume';
- toast.error(`No ${noun} selected`, toastConfig);
- return false;
- }
- }
+ if (modal.storage.type === 'raw' || modal.storage.type === 'zvol') {
+ if (!modal.storage.guid || modal.storage.guid.length < 1) {
+ const noun = modal.storage.type === 'raw' ? 'filesystem' : 'volume';
+ toast.error(`No ${noun} selected`, toastConfig);
+ return false;
+ }
+ }
- if (modal.storage.emulation === '') {
- toast.error('No emulation type selected', toastConfig);
- }
+ if (modal.storage.emulation === '') {
+ toast.error('No emulation type selected', toastConfig);
+ }
- if (modal.storage.type === 'none' && modal.storage.iso === '') {
- toast.error('Atleast one disk or ISO must be selected', toastConfig);
- return false;
- }
+ if (modal.storage.type === 'none' && modal.storage.iso === '') {
+ toast.error('Atleast one disk or ISO must be selected', toastConfig);
+ return false;
+ }
- if (modal.network.switch !== 0) {
- if (modal.network.emulation === '') {
- toast.error('No network emulation type selected', toastConfig);
- return false;
- }
+ if (modal.network.switch !== 0) {
+ if (modal.network.emulation === '') {
+ toast.error('No network emulation type selected', toastConfig);
+ return false;
+ }
- if (modal.network.mac && isValidMACAddress(modal.network.mac) === false) {
- toast.error('Invalid MAC address', toastConfig);
- return false;
- }
- }
+ if (modal.network.mac && isValidMACAddress(modal.network.mac) === false) {
+ toast.error('Invalid MAC address', toastConfig);
+ return false;
+ }
+ }
- if (modal.hardware.sockets < 1) {
- toast.error('Sockets must be >= 1', toastConfig);
- return false;
- }
+ if (modal.hardware.sockets < 1) {
+ toast.error('Sockets must be >= 1', toastConfig);
+ return false;
+ }
- if (modal.hardware.cores < 1) {
- toast.error('Cores must be >= 1', toastConfig);
- return false;
- }
+ if (modal.hardware.cores < 1) {
+ toast.error('Cores must be >= 1', toastConfig);
+ return false;
+ }
- if (modal.hardware.threads < 1) {
- toast.error('Threads must be >= 1', toastConfig);
- return false;
- }
+ if (modal.hardware.threads < 1) {
+ toast.error('Threads must be >= 1', toastConfig);
+ return false;
+ }
- if (modal.hardware.memory < 1024 * 1024 * 128) {
- toast.error('Memory must be >= 128 MiB', toastConfig);
- return false;
- }
+ if (modal.hardware.memory < 1024 * 1024 * 128) {
+ toast.error('Memory must be >= 128 MiB', toastConfig);
+ return false;
+ }
- if (modal.advanced.vncPort < 1 || modal.advanced.vncPort > 65535) {
- toast.error('VNC port must be between 1 and 65535', toastConfig);
- return false;
- }
+ if (modal.advanced.vncPort < 1 || modal.advanced.vncPort > 65535) {
+ toast.error('VNC port must be between 1 and 65535', toastConfig);
+ return false;
+ }
- if (modal.advanced.vncPassword && modal.advanced.vncPassword.length < 1) {
- toast.error('VNC password required', toastConfig);
- return false;
- }
+ if (modal.advanced.vncPassword && modal.advanced.vncPassword.length < 1) {
+ toast.error('VNC password required', toastConfig);
+ return false;
+ }
- if (modal.advanced.vncResolution === '') {
- toast.error('No VNC resolution selected', toastConfig);
- return false;
- }
+ if (modal.advanced.vncResolution === '') {
+ toast.error('No VNC resolution selected', toastConfig);
+ return false;
+ }
- return true;
+ return true;
}
diff --git a/new-web/src/lib/utils/zfs/dataset/fs.ts b/new-web/src/lib/utils/zfs/dataset/fs.ts
index 42931faf..fe39472e 100644
--- a/new-web/src/lib/utils/zfs/dataset/fs.ts
+++ b/new-web/src/lib/utils/zfs/dataset/fs.ts
@@ -6,334 +6,334 @@ import { generateNumberFromString } from '$lib/utils/numbers';
import { capitalizeFirstLetter } from '$lib/utils/string';
import { renderWithIcon, sizeFormatter } from '$lib/utils/table';
import { cleanChildren } from '$lib/utils/tree-table';
-import toast from 'svelte-french-toast';
+import { toast } from 'svelte-sonner';
export const createFSProps = {
- atime: [
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'off',
- value: 'off'
- }
- ],
- checksum: [
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'off',
- value: 'off'
- },
- {
- label: 'fletcher2',
- value: 'fletcher2'
- },
- {
- label: 'fletcher4',
- value: 'fletcher4'
- },
- {
- label: 'sha256',
- value: 'sha256'
- },
- {
- label: 'noparity',
- value: 'noparity'
- }
- ],
- compression: [
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'off',
- value: 'off'
- },
- {
- label: 'gzip',
- value: 'gzip'
- },
- {
- label: 'lz4',
- value: 'lz4'
- },
- {
- label: 'lzjb',
- value: 'lzjb'
- },
- {
- label: 'zle',
- value: 'zle'
- },
- {
- label: 'zstd',
- value: 'zstd'
- },
- {
- label: 'zstd-fast',
- value: 'zstd-fast'
- }
- ],
- dedup: [
- {
- label: 'off',
- value: 'off'
- },
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'Verify',
- value: 'verify'
- }
- ],
- encryption: [
- {
- label: 'off',
- value: 'off'
- },
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'aes-128-ccm',
- value: 'aes-128-ccm'
- },
- {
- label: 'aes-192-ccm',
- value: 'aes-192-ccm'
- },
- {
- label: 'aes-256-ccm',
- value: 'aes-256-ccm'
- },
- {
- label: 'aes-128-gcm',
- value: 'aes-128-gcm'
- },
- {
- label: 'aes-192-gcm',
- value: 'aes-192-gcm'
- },
- {
- label: 'aes-256-gcm',
- value: 'aes-256-gcm'
- }
- ]
+ atime: [
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'off',
+ value: 'off'
+ }
+ ],
+ checksum: [
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'off',
+ value: 'off'
+ },
+ {
+ label: 'fletcher2',
+ value: 'fletcher2'
+ },
+ {
+ label: 'fletcher4',
+ value: 'fletcher4'
+ },
+ {
+ label: 'sha256',
+ value: 'sha256'
+ },
+ {
+ label: 'noparity',
+ value: 'noparity'
+ }
+ ],
+ compression: [
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'off',
+ value: 'off'
+ },
+ {
+ label: 'gzip',
+ value: 'gzip'
+ },
+ {
+ label: 'lz4',
+ value: 'lz4'
+ },
+ {
+ label: 'lzjb',
+ value: 'lzjb'
+ },
+ {
+ label: 'zle',
+ value: 'zle'
+ },
+ {
+ label: 'zstd',
+ value: 'zstd'
+ },
+ {
+ label: 'zstd-fast',
+ value: 'zstd-fast'
+ }
+ ],
+ dedup: [
+ {
+ label: 'off',
+ value: 'off'
+ },
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'Verify',
+ value: 'verify'
+ }
+ ],
+ encryption: [
+ {
+ label: 'off',
+ value: 'off'
+ },
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'aes-128-ccm',
+ value: 'aes-128-ccm'
+ },
+ {
+ label: 'aes-192-ccm',
+ value: 'aes-192-ccm'
+ },
+ {
+ label: 'aes-256-ccm',
+ value: 'aes-256-ccm'
+ },
+ {
+ label: 'aes-128-gcm',
+ value: 'aes-128-gcm'
+ },
+ {
+ label: 'aes-192-gcm',
+ value: 'aes-192-gcm'
+ },
+ {
+ label: 'aes-256-gcm',
+ value: 'aes-256-gcm'
+ }
+ ]
};
export function generateTableData(grouped: GroupedByPool[]): { rows: Row[]; columns: Column[] } {
- const rows: Row[] = [];
- const columns: Column[] = [
- {
- field: 'id',
- title: 'ID',
- visible: false
- },
- {
- field: 'name',
- title: 'Name',
- formatter: (cell) => {
- const value = cell.getValue();
+ const rows: Row[] = [];
+ const columns: Column[] = [
+ {
+ field: 'id',
+ title: 'ID',
+ visible: false
+ },
+ {
+ field: 'name',
+ title: 'Name',
+ formatter: (cell) => {
+ const value = cell.getValue();
- if (value.includes('@')) {
- const [, snapshot] = value.split('@');
- return renderWithIcon('carbon:ibm-cloud-vpc-block-storage-snapshots', snapshot);
- }
+ if (value.includes('@')) {
+ const [, snapshot] = value.split('@');
+ return renderWithIcon('carbon:ibm-cloud-vpc-block-storage-snapshots', snapshot);
+ }
- if (value.includes('/')) {
- return renderWithIcon('material-symbols:files', value.substring(value.indexOf('/') + 1));
- }
+ if (value.includes('/')) {
+ return renderWithIcon('material-symbols:files', value.substring(value.indexOf('/') + 1));
+ }
- if (!value.includes('/') && !value.includes('@')) {
- return renderWithIcon('bi:hdd-stack-fill', value);
- }
+ if (!value.includes('/') && !value.includes('@')) {
+ return renderWithIcon('bi:hdd-stack-fill', value);
+ }
- return `${value} `;
- }
- },
- {
- field: 'used',
- title: 'Used',
- formatter: sizeFormatter
- },
- {
- field: 'avail',
- title: 'Available',
- formatter: sizeFormatter
- },
- {
- field: 'referenced',
- title: 'Referenced',
- formatter: sizeFormatter
- },
- {
- field: 'mountpoint',
- title: 'Mount Point'
- },
- {
- field: 'type',
- title: 'Type',
- visible: false
- }
- ];
+ return `${value} `;
+ }
+ },
+ {
+ field: 'used',
+ title: 'Used',
+ formatter: sizeFormatter
+ },
+ {
+ field: 'avail',
+ title: 'Available',
+ formatter: sizeFormatter
+ },
+ {
+ field: 'referenced',
+ title: 'Referenced',
+ formatter: sizeFormatter
+ },
+ {
+ field: 'mountpoint',
+ title: 'Mount Point'
+ },
+ {
+ field: 'type',
+ title: 'Type',
+ visible: false
+ }
+ ];
- for (const group of grouped) {
- const poolLevelFilesystem = group.filesystems.find(
- (fs: Dataset) => fs.type === 'filesystem' && fs.name === group.name
- );
+ for (const group of grouped) {
+ const poolLevelFilesystem = group.filesystems.find(
+ (fs: Dataset) => fs.type === 'filesystem' && fs.name === group.name
+ );
- const filesystemChildren = group.filesystems.filter(
- (fs) => fs.type === 'filesystem' && fs.name !== group.name
- );
+ const filesystemChildren = group.filesystems.filter(
+ (fs) => fs.type === 'filesystem' && fs.name !== group.name
+ );
- const snapshotChildren = group.snapshots;
+ const snapshotChildren = group.snapshots;
- if (poolLevelFilesystem) {
- const poolSnapshots = snapshotChildren.filter((snapshot) =>
- snapshot.name.startsWith(group.name + '@')
- );
+ if (poolLevelFilesystem) {
+ const poolSnapshots = snapshotChildren.filter((snapshot) =>
+ snapshot.name.startsWith(group.name + '@')
+ );
- const childFilesystemsWithSnapshots = filesystemChildren.map((filesystem: Dataset) => ({
- id: generateNumberFromString(filesystem.name) + 1,
- name: filesystem.name,
- used: filesystem.used,
- avail: filesystem.avail,
- referenced: filesystem.referenced,
- mountpoint: filesystem.mountpoint || '',
- children: snapshotChildren
- .filter((snapshot) => snapshot.name.startsWith(filesystem.name + '@'))
- .map((snapshot: Dataset) => ({
- id: generateNumberFromString(snapshot.name) + 2,
- name: snapshot.name,
- used: snapshot.used,
- avail: snapshot.avail,
- referenced: snapshot.referenced,
- mountpoint: snapshot.mountpoint || '',
- children: []
- })),
- type: filesystem.type
- }));
+ const childFilesystemsWithSnapshots = filesystemChildren.map((filesystem: Dataset) => ({
+ id: generateNumberFromString(filesystem.name) + 1,
+ name: filesystem.name,
+ used: filesystem.used,
+ avail: filesystem.avail,
+ referenced: filesystem.referenced,
+ mountpoint: filesystem.mountpoint || '',
+ children: snapshotChildren
+ .filter((snapshot) => snapshot.name.startsWith(filesystem.name + '@'))
+ .map((snapshot: Dataset) => ({
+ id: generateNumberFromString(snapshot.name) + 2,
+ name: snapshot.name,
+ used: snapshot.used,
+ avail: snapshot.avail,
+ referenced: snapshot.referenced,
+ mountpoint: snapshot.mountpoint || '',
+ children: []
+ })),
+ type: filesystem.type
+ }));
- rows.push({
- id: generateNumberFromString(group.name),
- name: group.name,
- used: poolLevelFilesystem.used,
- avail: poolLevelFilesystem.avail,
- referenced: poolLevelFilesystem.referenced,
- mountpoint: poolLevelFilesystem.mountpoint || '',
- children: [
- ...poolSnapshots.map((snapshot: Dataset) => ({
- id: generateNumberFromString(snapshot.name) + 1,
- name: snapshot.name,
- used: snapshot.used,
- avail: snapshot.avail,
- referenced: snapshot.referenced,
- mountpoint: snapshot.mountpoint || '',
- children: [],
- isPoolSnapshot: true
- })),
- ...childFilesystemsWithSnapshots
- ].sort((a, b) => {
- const aIsPoolSnapshot = a.hasOwnProperty('isPoolSnapshot');
- const bIsPoolSnapshot = b.hasOwnProperty('isPoolSnapshot');
- if (aIsPoolSnapshot && !bIsPoolSnapshot) return -1;
- if (!aIsPoolSnapshot && bIsPoolSnapshot) return 1;
- return a.name.localeCompare(b.name);
- }),
- type: 'pool'
- });
- } else if (group.filesystems.length > 0) {
- rows.push(
- ...group.filesystems
- .filter((fs) => fs.type === 'filesystem')
- .map((filesystem: Dataset) => ({
- id: generateNumberFromString(filesystem.name),
- name: filesystem.name,
- used: filesystem.used,
- avail: filesystem.avail,
- referenced: filesystem.referenced,
- mountpoint: filesystem.mountpoint || '',
- children: snapshotChildren
- .filter((snapshot) => snapshot.name.startsWith(filesystem.name + '@'))
- .map((snapshot: Dataset) => ({
- id: generateNumberFromString(snapshot.name) + 1,
- name: snapshot.name,
- used: snapshot.used,
- avail: snapshot.avail,
- referenced: snapshot.referenced,
- mountpoint: snapshot.mountpoint || '',
- children: []
- }))
- .sort((a, b) => a.name.localeCompare(b.name))
- }))
- );
- }
- }
+ rows.push({
+ id: generateNumberFromString(group.name),
+ name: group.name,
+ used: poolLevelFilesystem.used,
+ avail: poolLevelFilesystem.avail,
+ referenced: poolLevelFilesystem.referenced,
+ mountpoint: poolLevelFilesystem.mountpoint || '',
+ children: [
+ ...poolSnapshots.map((snapshot: Dataset) => ({
+ id: generateNumberFromString(snapshot.name) + 1,
+ name: snapshot.name,
+ used: snapshot.used,
+ avail: snapshot.avail,
+ referenced: snapshot.referenced,
+ mountpoint: snapshot.mountpoint || '',
+ children: [],
+ isPoolSnapshot: true
+ })),
+ ...childFilesystemsWithSnapshots
+ ].sort((a, b) => {
+ const aIsPoolSnapshot = a.hasOwnProperty('isPoolSnapshot');
+ const bIsPoolSnapshot = b.hasOwnProperty('isPoolSnapshot');
+ if (aIsPoolSnapshot && !bIsPoolSnapshot) return -1;
+ if (!aIsPoolSnapshot && bIsPoolSnapshot) return 1;
+ return a.name.localeCompare(b.name);
+ }),
+ type: 'pool'
+ });
+ } else if (group.filesystems.length > 0) {
+ rows.push(
+ ...group.filesystems
+ .filter((fs) => fs.type === 'filesystem')
+ .map((filesystem: Dataset) => ({
+ id: generateNumberFromString(filesystem.name),
+ name: filesystem.name,
+ used: filesystem.used,
+ avail: filesystem.avail,
+ referenced: filesystem.referenced,
+ mountpoint: filesystem.mountpoint || '',
+ children: snapshotChildren
+ .filter((snapshot) => snapshot.name.startsWith(filesystem.name + '@'))
+ .map((snapshot: Dataset) => ({
+ id: generateNumberFromString(snapshot.name) + 1,
+ name: snapshot.name,
+ used: snapshot.used,
+ avail: snapshot.avail,
+ referenced: snapshot.referenced,
+ mountpoint: snapshot.mountpoint || '',
+ children: []
+ }))
+ .sort((a, b) => a.name.localeCompare(b.name))
+ }))
+ );
+ }
+ }
- return {
- rows: rows.map(cleanChildren),
- columns
- };
+ return {
+ rows: rows.map(cleanChildren),
+ columns
+ };
}
export function handleError(error: APIResponse): void {
- if (error.error?.includes('dataset already exists')) {
- let [key, value] = ['', ''];
+ if (error.error?.includes('dataset already exists')) {
+ let [key, value] = ['', ''];
- if (error.error?.includes('snapshot')) {
- key = 'zfs.datasets.snapshot_already_exists';
- value = 'snapshot already exists';
- } else {
- key = 'zfs.datasets.filesystem_already_exists';
- value = 'filesystem already exists';
- }
+ if (error.error?.includes('snapshot')) {
+ key = 'zfs.datasets.snapshot_already_exists';
+ value = 'snapshot already exists';
+ } else {
+ key = 'zfs.datasets.filesystem_already_exists';
+ value = 'filesystem already exists';
+ }
- toast.error(capitalizeFirstLetter(getTranslation(key, value)), {
- position: 'bottom-center'
- });
- }
+ toast.error(capitalizeFirstLetter(getTranslation(key, value)), {
+ position: 'bottom-center'
+ });
+ }
- if (error.error?.includes('numeric value is too large')) {
- toast.error(
- capitalizeFirstLetter(
- getTranslation('zfs.datasets.numeric_value_too_large', 'Numeric value is too large')
- ),
- {
- position: 'bottom-center'
- }
- );
- }
+ if (error.error?.includes('numeric value is too large')) {
+ toast.error(
+ capitalizeFirstLetter(
+ getTranslation('zfs.datasets.numeric_value_too_large', 'Numeric value is too large')
+ ),
+ {
+ position: 'bottom-center'
+ }
+ );
+ }
- if (error.error?.includes('invalid_encryption_key_length')) {
- toast.error(
- capitalizeFirstLetter(
- getTranslation(
- 'zfs.datasets.invalid_encryption_key_length',
- 'Invalid encryption key length'
- )
- ),
- {
- position: 'bottom-center'
- }
- );
- }
+ if (error.error?.includes('invalid_encryption_key_length')) {
+ toast.error(
+ capitalizeFirstLetter(
+ getTranslation(
+ 'zfs.datasets.invalid_encryption_key_length',
+ 'Invalid encryption key length'
+ )
+ ),
+ {
+ position: 'bottom-center'
+ }
+ );
+ }
- if (error.error?.includes('pool or dataset is busy')) {
- toast.error(
- capitalizeFirstLetter(
- getTranslation('zfs.datasets.pool_or_dataset_is_busy', 'Pool or dataset is busy')
- ),
- {
- position: 'bottom-center'
- }
- );
- }
+ if (error.error?.includes('pool or dataset is busy')) {
+ toast.error(
+ capitalizeFirstLetter(
+ getTranslation('zfs.datasets.pool_or_dataset_is_busy', 'Pool or dataset is busy')
+ ),
+ {
+ position: 'bottom-center'
+ }
+ );
+ }
}
diff --git a/new-web/src/lib/utils/zfs/dataset/volume.ts b/new-web/src/lib/utils/zfs/dataset/volume.ts
index f8ea4597..9bf15560 100644
--- a/new-web/src/lib/utils/zfs/dataset/volume.ts
+++ b/new-web/src/lib/utils/zfs/dataset/volume.ts
@@ -6,322 +6,322 @@ import { generateNumberFromString } from '$lib/utils/numbers';
import { capitalizeFirstLetter } from '$lib/utils/string';
import { renderWithIcon, sizeFormatter } from '$lib/utils/table';
import { cleanChildren } from '$lib/utils/tree-table';
-import toast from 'svelte-french-toast';
+import { toast } from 'svelte-sonner';
export const createVolProps = {
- atime: [
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'off',
- value: 'off'
- }
- ],
- checksum: [
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'off',
- value: 'off'
- },
- {
- label: 'fletcher2',
- value: 'fletcher2'
- },
- {
- label: 'fletcher4',
- value: 'fletcher4'
- },
- {
- label: 'sha256',
- value: 'sha256'
- },
- {
- label: 'noparity',
- value: 'noparity'
- }
- ],
- compression: [
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'off',
- value: 'off'
- },
- {
- label: 'gzip',
- value: 'gzip'
- },
- {
- label: 'lz4',
- value: 'lz4'
- },
- {
- label: 'lzjb',
- value: 'lzjb'
- },
- {
- label: 'zle',
- value: 'zle'
- },
- {
- label: 'zstd',
- value: 'zstd'
- },
- {
- label: 'zstd-fast',
- value: 'zstd-fast'
- }
- ],
- dedup: [
- {
- label: 'off',
- value: 'off'
- },
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'Verify',
- value: 'verify'
- }
- ],
- encryption: [
- {
- label: 'off',
- value: 'off'
- },
- {
- label: 'on',
- value: 'on'
- },
- {
- label: 'aes-128-ccm',
- value: 'aes-128-ccm'
- },
- {
- label: 'aes-192-ccm',
- value: 'aes-192-ccm'
- },
- {
- label: 'aes-256-ccm',
- value: 'aes-256-ccm'
- },
- {
- label: 'aes-128-gcm',
- value: 'aes-128-gcm'
- },
- {
- label: 'aes-192-gcm',
- value: 'aes-192-gcm'
- },
- {
- label: 'aes-256-gcm',
- value: 'aes-256-gcm'
- }
- ],
- volblocksize: [
- {
- label: '512B (Legacy HDD sectors)',
- value: '512'
- },
- {
- label: '1K (1024B)',
- value: '1024'
- },
- {
- label: '2K (2048B)',
- value: '2048'
- },
- {
- label: '4K (4096B) - SSD/VMs',
- value: '4096'
- },
- {
- label: '8K (8192B)',
- value: '8192'
- },
- {
- label: '16K (16384B)',
- value: '16384'
- },
- {
- label: '32K (32768B) - Sequential workloads',
- value: '32768'
- },
- {
- label: '64K (65536B) - Large files',
- value: '65536'
- },
- {
- label: '128K (131072B) Media/backups',
- value: '131072'
- }
- ],
- primarycache: [
- {
- label: 'All',
- value: 'all'
- },
- {
- label: 'Metadata',
- value: 'metadata'
- },
- {
- label: 'None',
- value: 'none'
- }
- ],
- volmode: [
- {
- label: 'default',
- value: 'default'
- },
- {
- label: 'full',
- value: 'full'
- },
- {
- label: 'geom',
- value: 'geom'
- },
- {
- label: 'dev',
- value: 'dev'
- },
- {
- label: 'none',
- value: 'none'
- }
- ]
+ atime: [
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'off',
+ value: 'off'
+ }
+ ],
+ checksum: [
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'off',
+ value: 'off'
+ },
+ {
+ label: 'fletcher2',
+ value: 'fletcher2'
+ },
+ {
+ label: 'fletcher4',
+ value: 'fletcher4'
+ },
+ {
+ label: 'sha256',
+ value: 'sha256'
+ },
+ {
+ label: 'noparity',
+ value: 'noparity'
+ }
+ ],
+ compression: [
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'off',
+ value: 'off'
+ },
+ {
+ label: 'gzip',
+ value: 'gzip'
+ },
+ {
+ label: 'lz4',
+ value: 'lz4'
+ },
+ {
+ label: 'lzjb',
+ value: 'lzjb'
+ },
+ {
+ label: 'zle',
+ value: 'zle'
+ },
+ {
+ label: 'zstd',
+ value: 'zstd'
+ },
+ {
+ label: 'zstd-fast',
+ value: 'zstd-fast'
+ }
+ ],
+ dedup: [
+ {
+ label: 'off',
+ value: 'off'
+ },
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'Verify',
+ value: 'verify'
+ }
+ ],
+ encryption: [
+ {
+ label: 'off',
+ value: 'off'
+ },
+ {
+ label: 'on',
+ value: 'on'
+ },
+ {
+ label: 'aes-128-ccm',
+ value: 'aes-128-ccm'
+ },
+ {
+ label: 'aes-192-ccm',
+ value: 'aes-192-ccm'
+ },
+ {
+ label: 'aes-256-ccm',
+ value: 'aes-256-ccm'
+ },
+ {
+ label: 'aes-128-gcm',
+ value: 'aes-128-gcm'
+ },
+ {
+ label: 'aes-192-gcm',
+ value: 'aes-192-gcm'
+ },
+ {
+ label: 'aes-256-gcm',
+ value: 'aes-256-gcm'
+ }
+ ],
+ volblocksize: [
+ {
+ label: '512B (Legacy HDD sectors)',
+ value: '512'
+ },
+ {
+ label: '1K (1024B)',
+ value: '1024'
+ },
+ {
+ label: '2K (2048B)',
+ value: '2048'
+ },
+ {
+ label: '4K (4096B) - SSD/VMs',
+ value: '4096'
+ },
+ {
+ label: '8K (8192B)',
+ value: '8192'
+ },
+ {
+ label: '16K (16384B)',
+ value: '16384'
+ },
+ {
+ label: '32K (32768B) - Sequential workloads',
+ value: '32768'
+ },
+ {
+ label: '64K (65536B) - Large files',
+ value: '65536'
+ },
+ {
+ label: '128K (131072B) Media/backups',
+ value: '131072'
+ }
+ ],
+ primarycache: [
+ {
+ label: 'All',
+ value: 'all'
+ },
+ {
+ label: 'Metadata',
+ value: 'metadata'
+ },
+ {
+ label: 'None',
+ value: 'none'
+ }
+ ],
+ volmode: [
+ {
+ label: 'default',
+ value: 'default'
+ },
+ {
+ label: 'full',
+ value: 'full'
+ },
+ {
+ label: 'geom',
+ value: 'geom'
+ },
+ {
+ label: 'dev',
+ value: 'dev'
+ },
+ {
+ label: 'none',
+ value: 'none'
+ }
+ ]
};
export function generateTableData(grouped: GroupedByPool[]): { rows: Row[]; columns: Column[] } {
- const rows: Row[] = [];
- const columns: Column[] = [
- {
- field: 'id',
- title: 'ID',
- visible: false
- },
- {
- field: 'name',
- title: 'Name',
- formatter: (cell) => {
- const value = cell.getValue();
- if (value.includes('@')) {
- const [, snapshot] = value.split('@');
- return renderWithIcon('carbon:ibm-cloud-vpc-block-storage-snapshots', snapshot);
- }
+ const rows: Row[] = [];
+ const columns: Column[] = [
+ {
+ field: 'id',
+ title: 'ID',
+ visible: false
+ },
+ {
+ field: 'name',
+ title: 'Name',
+ formatter: (cell) => {
+ const value = cell.getValue();
+ if (value.includes('@')) {
+ const [, snapshot] = value.split('@');
+ return renderWithIcon('carbon:ibm-cloud-vpc-block-storage-snapshots', snapshot);
+ }
- if (value.includes('/')) {
- const [, volume] = value.split('/');
- return renderWithIcon('carbon:volume-block-storage', volume);
- }
+ if (value.includes('/')) {
+ const [, volume] = value.split('/');
+ return renderWithIcon('carbon:volume-block-storage', volume);
+ }
- if (!value.includes('/') && !value.includes('@')) {
- return renderWithIcon('bi:hdd-stack-fill', value);
- }
+ if (!value.includes('/') && !value.includes('@')) {
+ return renderWithIcon('bi:hdd-stack-fill', value);
+ }
- return `${value} `;
- }
- },
- {
- field: 'size',
- title: 'Size',
- formatter: sizeFormatter
- },
- {
- field: 'referenced',
- title: 'Referenced',
- formatter: sizeFormatter
- },
- {
- field: 'guid',
- title: 'GUID',
- visible: false
- }
- ];
+ return `${value} `;
+ }
+ },
+ {
+ field: 'size',
+ title: 'Size',
+ formatter: sizeFormatter
+ },
+ {
+ field: 'referenced',
+ title: 'Referenced',
+ formatter: sizeFormatter
+ },
+ {
+ field: 'guid',
+ title: 'GUID',
+ visible: false
+ }
+ ];
- for (const group of grouped) {
- const poolRow: Row = {
- id: generateNumberFromString(group.name),
- name: group.name,
- size: 0,
- referenced: '-',
- guid: undefined,
- children: []
- };
+ for (const group of grouped) {
+ const poolRow: Row = {
+ id: generateNumberFromString(group.name),
+ name: group.name,
+ size: 0,
+ referenced: '-',
+ guid: undefined,
+ children: []
+ };
- poolRow.size = group.pool?.size;
+ poolRow.size = group.pool?.size;
- const volumeChildren = group.volumes
- .filter((vol) => vol.name !== group.name)
- .map((vol) => {
- const volumeRow: Row = {
- id: generateNumberFromString(vol.name),
- name: vol.name,
- size: vol.volsize,
- referenced: vol.referenced,
- guid: vol.properties?.guid,
- children: []
- };
+ const volumeChildren = group.volumes
+ .filter((vol) => vol.name !== group.name)
+ .map((vol) => {
+ const volumeRow: Row = {
+ id: generateNumberFromString(vol.name),
+ name: vol.name,
+ size: vol.volsize,
+ referenced: vol.referenced,
+ guid: vol.properties?.guid,
+ children: []
+ };
- const snapshots = group.snapshots.filter((snap) => snap.name.startsWith(vol.name + '@'));
- volumeRow.children?.push(
- ...snapshots.map((snap) => ({
- id: generateNumberFromString(snap.name),
- name: snap.name,
- size: snap.used,
- referenced: snap.referenced,
- guid: snap.properties?.guid,
- children: []
- }))
- );
+ const snapshots = group.snapshots.filter((snap) => snap.name.startsWith(vol.name + '@'));
+ volumeRow.children?.push(
+ ...snapshots.map((snap) => ({
+ id: generateNumberFromString(snap.name),
+ name: snap.name,
+ size: snap.used,
+ referenced: snap.referenced,
+ guid: snap.properties?.guid,
+ children: []
+ }))
+ );
- return volumeRow;
- });
+ return volumeRow;
+ });
- poolRow.children?.push(...volumeChildren);
- rows.push(poolRow);
- }
+ poolRow.children?.push(...volumeChildren);
+ rows.push(poolRow);
+ }
- return {
- rows: rows.filter((row) => row.children && row.children.length > 0).map(cleanChildren),
- columns
- };
+ return {
+ rows: rows.filter((row) => row.children && row.children.length > 0).map(cleanChildren),
+ columns
+ };
}
export function handleError(error: APIResponse): void {
- if (error.error?.includes('dataset_in_use_by_vm')) {
- toast.error(
- capitalizeFirstLetter(
- getTranslation('zfs.datasets.dataset_in_use_by_vm', 'dataset is in use by a VM'),
- true
- ),
- {
- position: 'bottom-center'
- }
- );
+ if (error.error?.includes('dataset_in_use_by_vm')) {
+ toast.error(
+ capitalizeFirstLetter(
+ getTranslation('zfs.datasets.dataset_in_use_by_vm', 'dataset is in use by a VM'),
+ true
+ ),
+ {
+ position: 'bottom-center'
+ }
+ );
- return;
- }
+ return;
+ }
- if (error.error?.includes('dataset already exists')) {
- toast.error(
- capitalizeFirstLetter(
- getTranslation('zfs.datasets.dataset_already_exists', 'dataset already exists')
- ),
- {
- position: 'bottom-center'
- }
- );
- }
+ if (error.error?.includes('dataset already exists')) {
+ toast.error(
+ capitalizeFirstLetter(
+ getTranslation('zfs.datasets.dataset_already_exists', 'dataset already exists')
+ ),
+ {
+ position: 'bottom-center'
+ }
+ );
+ }
}
diff --git a/new-web/src/routes/+layout.svelte b/new-web/src/routes/+layout.svelte
index 187bb140..6973d9bc 100644
--- a/new-web/src/routes/+layout.svelte
+++ b/new-web/src/routes/+layout.svelte
@@ -5,13 +5,127 @@
import { goto, replaceState } from '$app/navigation';
import { page } from '$app/state';
import { isTokenValid, login } from '$lib/api/auth';
- import { Button } from '$lib/components/ui/button/index.js';
+ import Login from '$lib/components/custom/Login.svelte';
+ import Throbber from '$lib/components/custom/Throbber.svelte';
+ import Shell from '$lib/components/skeleton/Shell.svelte';
import { Toaster } from '$lib/components/ui/sonner/index.js';
+ import { store as token } from '$lib/stores/auth';
+ import { hostname } from '$lib/stores/basic';
+ import '$lib/utils/i18n';
+ import { preloadIcons } from '$lib/utils/icons';
+ import { addTabulatorFilters } from '$lib/utils/table';
+ import { QueryClient, QueryClientProvider } from '@sveltestack/svelte-query';
+ import { ModeWatcher } from 'mode-watcher';
+ import { onMount, tick } from 'svelte';
+ import { toast } from 'svelte-sonner';
import '../app.css';
+ const queryClient = new QueryClient();
let { children } = $props();
+ let isLoggedIn = $state(false);
+ let isLoading = $state(true);
+
+ $effect(() => {
+ if (isLoggedIn && $hostname) {
+ const path = window.location.pathname;
+ if (path === '/' || !path.startsWith(`/${$hostname}`)) {
+ goto(`/${$hostname}/summary`, { replaceState: true });
+ }
+ }
+ });
+
+ $effect(() => {
+ if (page.state.hasOwnProperty('loggedOut')) {
+ toast.success('Logged out', {
+ position: 'bottom-center'
+ });
+ }
+ });
+
+ onMount(async () => {
+ addTabulatorFilters();
+ const faviconEl = document.getElementById('favicon');
+ if (faviconEl) {
+ const darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ if (darkMode) {
+ faviconEl.setAttribute('href', '/logo/white.svg');
+ } else {
+ faviconEl.setAttribute('href', '/logo/black.svg');
+ }
+ }
+
+ if ($token) {
+ try {
+ if (await isTokenValid()) {
+ isLoggedIn = true;
+ } else {
+ $token = '';
+ }
+ } catch (error) {
+ console.error('Token validation error:', error);
+ $token = '';
+ }
+ }
+
+ await preloadIcons();
+ isLoading = false;
+ await tick();
+ });
+
+ function sleep(ms: number) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
+ async function handleLogin(
+ username: string,
+ password: string,
+ type: string,
+ language: string,
+ remember: boolean
+ ) {
+ let isError = false;
+
+ try {
+ if (await login(username, password, type, remember, language)) {
+ isLoading = true;
+ isLoggedIn = true;
+ const path = window.location.pathname;
+
+ if (path === '/' || !path.startsWith(`/${$hostname}`)) {
+ await goto(`/${$hostname}/summary`, { replaceState: true });
+ }
+ } else {
+ isError = true;
+ isLoggedIn = false;
+ }
+ } catch (error) {
+ isError = true;
+ isLoggedIn = false;
+ } finally {
+ if (!isError) {
+ await sleep(2500);
+ isLoading = false;
+ }
+ }
+ return;
+ }
-
+
+ Sylve
+
-{@render children()}
+
+
+
+{#if isLoading}
+
+{:else if isLoggedIn && $hostname}
+
+
+ {@render children()}
+
+
+{:else}
+
+{/if}
diff --git a/new-web/src/routes/[node]/+layout.svelte b/new-web/src/routes/[node]/+layout.svelte
new file mode 100644
index 00000000..4757dae7
--- /dev/null
+++ b/new-web/src/routes/[node]/+layout.svelte
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
{capitalizeFirstLetter(getTranslation('common.datacenter', 'Datacenter'))}
+
+
+ Help
+
+
+
+
+
+
+
+
+
+
+ {@render children?.()}
+
+
+
+
diff --git a/new-web/src/routes/[node]/+layout.ts b/new-web/src/routes/[node]/+layout.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/new-web/src/routes/[node]/+page.svelte b/new-web/src/routes/[node]/+page.svelte
new file mode 100644
index 00000000..e69de29b
diff --git a/new-web/src/routes/[node]/network/interfaces/+page.svelte b/new-web/src/routes/[node]/network/interfaces/+page.svelte
new file mode 100644
index 00000000..02010a2e
--- /dev/null
+++ b/new-web/src/routes/[node]/network/interfaces/+page.svelte
@@ -0,0 +1,103 @@
+
+
+{#snippet button(type: string)}
+ {#if type === 'view' && activeRow !== null && activeRow.length > 0}
+ activeRow !== null && viewInterface(activeRow[0]?.name)}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.view', 'View'))}
+
+ {/if}
+{/snippet}
+
+
+
+
+ {@render button('view')}
+
+
+
+
+
+
diff --git a/new-web/src/routes/[node]/network/interfaces/+page.ts b/new-web/src/routes/[node]/network/interfaces/+page.ts
new file mode 100644
index 00000000..6d0e8230
--- /dev/null
+++ b/new-web/src/routes/[node]/network/interfaces/+page.ts
@@ -0,0 +1,13 @@
+import { getInterfaces } from '$lib/api/network/iface';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = 1000 * 60000;
+ const [interfaces] = await Promise.all([
+ cachedFetch('networkInterfaces', async () => await getInterfaces(), cacheDuration)
+ ]);
+
+ return {
+ interfaces
+ };
+}
diff --git a/new-web/src/routes/[node]/network/switches/+page.svelte b/new-web/src/routes/[node]/network/switches/+page.svelte
new file mode 100644
index 00000000..ae7b957e
--- /dev/null
+++ b/new-web/src/routes/[node]/network/switches/+page.svelte
@@ -0,0 +1,605 @@
+
+
+{#snippet button(type: string)}
+ {#if activeRow && Object.keys(activeRow).length > 0}
+ {#if type === 'edit'}
+
+
+ {capitalizeFirstLetter(getTranslation('common.edit', 'Edit'))}
+
+ {:else if type === 'delete'}
+
+
+ {capitalizeFirstLetter(getTranslation('common.delete', 'Delete'))}
+
+ {/if}
+ {/if}
+{/snippet}
+
+
+
+
+ {
+ confirmModals.active = 'newSwitch';
+ confirmModals.newSwitch.open = true;
+ }}
+ size="sm"
+ class="h-6"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.new', 'New'))}
+
+
+ {@render button('edit')}
+ {@render button('delete')}
+
+
+
+
+
+{#if confirmModals.active === 'newSwitch' || confirmModals.active === 'editSwitch'}
+
+
+
+
+
+
+
+ {#if confirmModals.active === 'editSwitch'}
+ {capitalizeFirstLetter(getTranslation('common.edit', 'Edit'))}
+ {capitalizeFirstLetter(getTranslation('network.switch.switch', 'Switch'))}
+ {'- ' + confirmModals.editSwitch.oldName}
+ {:else}
+ {capitalizeFirstLetter(getTranslation('common.new', 'New'))}
+ {capitalizeFirstLetter(getTranslation('network.switch.switch', 'Switch'))}
+ {/if}
+
+
+
+
+
+ resetModal(false)}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ resetModal(true)}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+ {#if confirmModals.active === 'newSwitch'}
+
+ {/if}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if confirmModals.active === 'newSwitch'}
+
+ {:else}
+
+ {/if}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if confirmModals.active === 'editSwitch'}
+ {capitalizeFirstLetter(getTranslation('common.save', 'Save'))}
+ {:else}
+ {capitalizeFirstLetter(getTranslation('common.create', 'Create'))}
+ {/if}
+
+
+
+
+{/if}
+
+ {
+ const result = await deleteSwitch(confirmModals.deleteSwitch.id);
+ if (isAPIResponse(result) && result.status === 'success') {
+ let message = `${capitalizeFirstLetter(getTranslation('network.switch.switch', 'Switch'))} ${confirmModals.deleteSwitch.name} ${getTranslation('common.deleted', 'deleted')}`;
+ toast.success(message, {
+ position: 'bottom-center'
+ });
+ } else {
+ if (result && result.error) {
+ if (result.error === 'switch_in_use_by_vm') {
+ toast.error(
+ getTranslation(
+ 'network.switch.errors.switch_in_use_by_vm',
+ 'Switch is in use by a VM'
+ ),
+ {
+ position: 'bottom-center'
+ }
+ );
+ return;
+ }
+ }
+
+ toast.error(
+ getTranslation('network.switch.errors.delete_switch', 'Error deleting switch'),
+ {
+ position: 'bottom-center'
+ }
+ );
+ }
+
+ activeRows = null;
+
+ confirmModals.deleteSwitch.open = false;
+ confirmModals.deleteSwitch.name = '';
+ confirmModals.deleteSwitch.id = 0;
+ },
+ onCancel: () => {
+ confirmModals.deleteSwitch.open = false;
+ confirmModals.deleteSwitch.name = '';
+ confirmModals.deleteSwitch.id = 0;
+ }
+ }}
+>
diff --git a/new-web/src/routes/[node]/network/switches/+page.ts b/new-web/src/routes/[node]/network/switches/+page.ts
new file mode 100644
index 00000000..a3d90e8f
--- /dev/null
+++ b/new-web/src/routes/[node]/network/switches/+page.ts
@@ -0,0 +1,16 @@
+import { getInterfaces } from '$lib/api/network/iface';
+import { getSwitches } from '$lib/api/network/switch';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = 1000 * 60000;
+ const [interfaces, switches] = await Promise.all([
+ cachedFetch('networkInterfaces', async () => await getInterfaces(), cacheDuration),
+ cachedFetch('networkSwitches', async () => await getSwitches(), cacheDuration)
+ ]);
+
+ return {
+ interfaces,
+ switches
+ };
+}
diff --git a/new-web/src/routes/[node]/notes/+page.svelte b/new-web/src/routes/[node]/notes/+page.svelte
new file mode 100644
index 00000000..1a09c0d9
--- /dev/null
+++ b/new-web/src/routes/[node]/notes/+page.svelte
@@ -0,0 +1,348 @@
+
+
+{#snippet button(type: string)}
+ {#if activeRow !== null && activeRow.length === 1}
+ {#if type === 'view-note'}
+ activeRow && viewNote(activeRow[0]?.id)}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.view', 'View'))}
+
+ {/if}
+
+ {#if type === 'delete-note'}
+ activeRow && handleDelete(activeRow[0]?.id)}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.delete', 'Delete'))}
+
+ {/if}
+
+ {#if type === 'edit-note'}
+ {
+ const note = notes.find((note) => activeRow && note.id === activeRow[0]?.id);
+ handleNote(note, true);
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.edit', 'Edit'))}
+
+ {/if}
+ {/if}
+
+ {#if activeRow !== null && activeRow.length > 1}
+ {#if type === 'bulk-delete-note'}
+ {
+ const ids = activeRow?.map((row) => row.id) || [];
+ handleBulkDelete(ids as number[]);
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.bulk', 'Bulk'))}
+ {capitalizeFirstLetter(getTranslation('common.delete', 'Delete'))}
+
+ {/if}
+ {/if}
+{/snippet}
+
+
+
+
+
+ handleNote()} size="sm" class="h-6 ">
+
+ {capitalizeFirstLetter(getTranslation('common.new', 'New'))}
+
+
+ {@render button('view-note')}
+ {@render button('edit-note')}
+ {@render button('delete-note')}
+ {@render button('bulk-delete-note')}
+
+
+
+
+
+
+
+
+
+ {modalState.isEditMode
+ ? selectedId
+ ? capitalizeFirstLetter(getTranslation('common.edit', 'Edit'))
+ : capitalizeFirstLetter(getTranslation('common.new', 'New'))
+ : capitalizeFirstLetter(getTranslation('common.view', 'View'))}
+ {capitalizeFirstLetter(getTranslation('notes.note', 'Note'))}
+
+
+
+
+ {
+ modalState.title = '';
+ modalState.content = '';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ {
+ modalState.isOpen = false;
+ modalState.title = '';
+ modalState.content = '';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+
+
+ {#if modalState.isEditMode}
+
+
+
+ {:else}
+
+
+ {capitalizeFirstLetter(getTranslation('common.content', 'Content'))}
+
+
+ {@html markdownToTailwindHTML(modalState.content)}
+
+
+ {/if}
+
+
+
+
+ {#if modalState.isEditMode}
+ {capitalizeFirstLetter(getTranslation('common.save', 'Save'))}
+ {/if}
+
+
+
+
+
+
+
+
+
+
{
+ const id = activeRow ? activeRow[0]?.id : null;
+ const result = await deleteNote(id as number);
+ if (isAPIResponse(result) && result.status === 'success') {
+ handleNote(undefined, false, true);
+ } else {
+ handleValidationErrors(result as APIResponse, 'notes');
+ }
+ },
+ onCancel: () => {
+ modalState.isDeleteOpen = false;
+ }
+ }}
+ >
+
+
{
+ const ids = activeRow
+ ? activeRow.map((row) => (typeof row.id === 'number' ? row.id : parseInt(row.id)))
+ : [];
+ const result = await deleteNotes(ids);
+ if (isAPIResponse(result) && result.status === 'success') {
+ handleNote(undefined, false, true);
+ } else {
+ handleValidationErrors(result as APIResponse, 'notes');
+ }
+ },
+ onCancel: () => {
+ modalState.isBulkDeleteOpen = false;
+ }
+ }}
+ >
+
diff --git a/new-web/src/routes/[node]/notes/+page.ts b/new-web/src/routes/[node]/notes/+page.ts
new file mode 100644
index 00000000..bc95bccc
--- /dev/null
+++ b/new-web/src/routes/[node]/notes/+page.ts
@@ -0,0 +1,13 @@
+import { getNotes } from '$lib/api/info/notes';
+import type { Note } from '$lib/types/info/notes';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [notes] = await Promise.all([cachedFetch('notes', async () => getNotes(), cacheDuration)]);
+
+ return {
+ notes: notes as Note[]
+ };
+}
diff --git a/new-web/src/routes/[node]/settings/device-passthrough/+page.svelte b/new-web/src/routes/[node]/settings/device-passthrough/+page.svelte
new file mode 100644
index 00000000..6afc6e4e
--- /dev/null
+++ b/new-web/src/routes/[node]/settings/device-passthrough/+page.svelte
@@ -0,0 +1,176 @@
+
+
+{#snippet button(type: string)}
+ {#if activeRow !== null && activeRow.length === 1}
+ {#if type === 'enable-passthrough' && !activeRow[0].name.startsWith('ppt')}
+
+ activeRow && addDevice(activeRow[0].domain.toString(), activeRow[0].deviceId)}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ Enable Passthrough
+
+ {/if}
+
+ {#if type === 'disable-passthrough' && activeRow[0].name.startsWith('ppt')}
+ activeRow && removeDevice(activeRow[0].pptId)}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ Disable Passthrough
+
+ {/if}
+ {/if}
+{/snippet}
+
+
+
+
+
+ {@render button('enable-passthrough')}
+ {@render button('disable-passthrough')}
+
+
+
+
+
+
+
+ {
+ if (modalState.action === 'add') {
+ const result = await addPPTDevice(modalState.add.domain, modalState.add.deviceId);
+ if (result.status === 'success') {
+ toast.success('Device added to passthrough', {
+ position: 'bottom-center'
+ });
+ } else {
+ toast.error('Failed to add device to passthrough', {
+ position: 'bottom-center'
+ });
+ }
+
+ modalState.isOpen = false;
+ }
+
+ if (modalState.action === 'remove') {
+ const result = await removePPTDevice(modalState.remove.id.toString());
+
+ if (result.status === 'success') {
+ toast.success('Device removed from passthrough', {
+ position: 'bottom-center'
+ });
+ } else {
+ toast.error('Failed to remove device from passthrough', {
+ position: 'bottom-center'
+ });
+ }
+
+ modalState.isOpen = false;
+ }
+ },
+ onCancel: () => {
+ modalState.isOpen = false;
+ }
+ }}
+>
diff --git a/new-web/src/routes/[node]/settings/device-passthrough/+page.ts b/new-web/src/routes/[node]/settings/device-passthrough/+page.ts
new file mode 100644
index 00000000..477be7ec
--- /dev/null
+++ b/new-web/src/routes/[node]/settings/device-passthrough/+page.ts
@@ -0,0 +1,16 @@
+import { getPCIDevices, getPPTDevices } from '$lib/api/system/pci';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [pciDevices, pptDevices] = await Promise.all([
+ cachedFetch('pciDevices', async () => await getPCIDevices(), cacheDuration),
+ cachedFetch('pptDevices', async () => await getPPTDevices(), cacheDuration)
+ ]);
+
+ return {
+ pciDevices,
+ pptDevices
+ };
+}
diff --git a/new-web/src/routes/[node]/storage/disks/+page.svelte b/new-web/src/routes/[node]/storage/disks/+page.svelte
new file mode 100644
index 00000000..d3dcd61f
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/disks/+page.svelte
@@ -0,0 +1,352 @@
+
+
+{#snippet button(type: string)}
+ {#if type == 'smart' && buttonAbilities.smart.ability}
+ diskAction('smart')}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {getTranslation('disk.smart_values', 'S.M.A.R.T Values')}
+
+ {/if}
+
+ {#if type == 'gpt' && buttonAbilities.gpt.ability}
+ diskAction('gpt')}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {getTranslation('disk.initialize_gpt', 'Initialize GPT')}
+
+ {/if}
+
+ {#if type == 'wipe-disk' && buttonAbilities.wipe.ability && activeDisk !== null}
+ diskAction('wipe')}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('disk.wipe_disk', 'Wipe Disk'))}
+
+ {/if}
+
+ {#if type == 'wipe-partition' && buttonAbilities.wipe.ability && activePartition !== null}
+ diskAction('wipe')}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('disk.delete_partition', 'Delete Partition'))}
+
+ {/if}
+
+ {#if type == 'partition' && buttonAbilities.createPartition.ability}
+ diskAction('partition')}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('disk.create_partition', 'Create Partition'))}
+
+ {/if}
+{/snippet}
+
+
+
+
+
+ {@render button('smart')}
+ {@render button('gpt')}
+ {@render button('partition')}
+ {@render button('wipe-disk')}
+ {@render button('wipe-partition')}
+
+
+
{
+ smartModal.open = false;
+ }
+ }}
+ >
+
+
+
+
+ {
+ if (activeDisk || activePartition) {
+ const message = activeDisk
+ ? getTranslation('disk.full_wipe_success', 'Disk wiped successfully')
+ : getTranslation('disk.partition_wipe_success', 'Disk wiped successfully');
+
+ const result = activeDisk
+ ? await destroyDisk(`/dev/${activeDisk.device}`)
+ : await destroyPartition(`/dev/${activePartition?.name}`);
+
+ if (result.status === 'success') {
+ toast.success(message, { position: 'bottom-center' });
+ activeRow = null;
+ } else {
+ handleAPIError(result);
+ }
+ }
+ wipeModal.title = '';
+ wipeModal.open = false;
+ },
+ onCancel: () => {
+ wipeModal.title = '';
+ wipeModal.open = false;
+ }
+ }}
+ customTitle={wipeModal.title}
+>
+
+ {
+ partitionModal.open = false;
+ partitionModal.disk = null;
+ }}
+/>
diff --git a/new-web/src/routes/[node]/storage/disks/+page.ts b/new-web/src/routes/[node]/storage/disks/+page.ts
new file mode 100644
index 00000000..34c9c34a
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/disks/+page.ts
@@ -0,0 +1,17 @@
+import { listDisks } from '$lib/api/disk/disk';
+import { getPools } from '$lib/api/zfs/pool';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [disks, pools] = await Promise.all([
+ cachedFetch('disks', async () => await listDisks(), cacheDuration),
+ cachedFetch('pools', getPools, cacheDuration)
+ ]);
+
+ return {
+ disks,
+ pools
+ };
+}
diff --git a/new-web/src/routes/[node]/storage/zfs/dashboard/+page.svelte b/new-web/src/routes/[node]/storage/zfs/dashboard/+page.svelte
new file mode 100644
index 00000000..0d817ea4
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/dashboard/+page.svelte
@@ -0,0 +1,354 @@
+
+
+{#snippet card(type: string)}
+
+
+
+
+
+ {getCardDetails(type).title}
+
+
+
+
+
+ {counts[type as CardType]}
+
+
+
+{/snippet}
+
+
+
+ {#each ['pools', 'datasets', 'file_systems', 'volumes', 'snapshots'] as type}
+
+ {@render card(type)}
+
+ {/each}
+
+
+
+ {#if pools.length > 0}
+
+
+
+
+
+
+
+
+
+ Dataset Compression
+
+
+
+
+
+
+
+
+ {#if histograms.compression.data.length === 0}
+
No data available
+ {:else}
+
+ {/if}
+
+
+
+
+
+
+
+
+
+
+
+
+ PoolStats
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if poolStatsData.length === 0}
+
No data available
+ {:else}
+
+ {/if}
+
+
+
+
+ {/if}
+
+
diff --git a/new-web/src/routes/[node]/storage/zfs/dashboard/+page.ts b/new-web/src/routes/[node]/storage/zfs/dashboard/+page.ts
new file mode 100644
index 00000000..7d83a1ad
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/dashboard/+page.ts
@@ -0,0 +1,19 @@
+import { getDatasets } from '$lib/api/zfs/datasets';
+import { getPools, getPoolStats } from '$lib/api/zfs/pool';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [datasets, pools, poolStats] = await Promise.all([
+ cachedFetch('datasets', async () => await getDatasets(), cacheDuration),
+ cachedFetch('pools', getPools, cacheDuration),
+ cachedFetch('pool-stats', async () => await getPoolStats(1, 128), cacheDuration)
+ ]);
+
+ return {
+ pools: pools,
+ datasets: datasets,
+ poolStats: poolStats
+ };
+}
diff --git a/new-web/src/routes/[node]/storage/zfs/datasets/fs/+page.svelte b/new-web/src/routes/[node]/storage/zfs/datasets/fs/+page.svelte
new file mode 100644
index 00000000..5fbcd7b5
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/datasets/fs/+page.svelte
@@ -0,0 +1,1008 @@
+
+
+{#snippet button(type: string)}
+ {#if activeRows && activeRows.length == 1}
+ {#if type === 'rollback-snapshot' && activeDataset?.type === 'snapshot'}
+ {
+ if (activeDataset) {
+ confirmModals.active = 'rollbackSnapshot';
+ confirmModals.parent = 'snapshot';
+ confirmModals.rollbackSnapshot.open = true;
+ confirmModals.rollbackSnapshot.data.name = activeDataset.name;
+ confirmModals.rollbackSnapshot.title = activeDataset.name;
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Rollback To Snapshot
+
+ {/if}
+
+ {#if type === 'delete-snapshot' && activeDataset?.type === 'snapshot'}
+ {
+ if (activeDataset) {
+ confirmModals.active = 'deleteSnapshot';
+ confirmModals.parent = 'snapshot';
+ confirmModals.deleteSnapshot.open = true;
+ confirmModals.deleteSnapshot.title = activeDataset.name;
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Delete Snapshot
+
+ {/if}
+
+ {#if type === 'create-snapshot' && activeDataset?.type === 'filesystem'}
+ {
+ if (activeDataset) {
+ confirmModals.active = 'createSnapshot';
+ confirmModals.parent = 'filesystem';
+ confirmModals.createSnapshot.open = true;
+ confirmModals.createSnapshot.title = activeDataset.name;
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Create Snapshot
+
+ {/if}
+
+ {#if type === 'delete-filesystem' && activeDataset?.type === 'filesystem' && activeDataset?.name.includes('/')}
+ {
+ if (activeDataset) {
+ confirmModals.active = 'deleteFilesystem';
+ confirmModals.parent = 'filesystem';
+ confirmModals.deleteFilesystem.open = true;
+ confirmModals.deleteFilesystem.title = activeDataset.name;
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Delete Filesystem
+
+ {/if}
+ {:else if activeRows && activeRows.length > 1}
+ {#if activeDatasets.length > 0 && !poolsSelected}
+ {#if type === 'bulk-delete'}
+ {
+ confirmModals.active = 'bulkDeleteDatasets';
+ confirmModals.parent = 'dataset';
+ confirmModals.bulkDeleteDatasets.open = true;
+
+ let [snapLen, fsLen] = [0, 0];
+ activeDatasets.forEach((dataset) => {
+ if (dataset.type === 'snapshot') {
+ snapLen++;
+ } else if (dataset.type === 'filesystem') {
+ fsLen++;
+ }
+ });
+
+ let title = '';
+ if (snapLen > 0 && fsLen > 0) {
+ title = `${snapLen} snapshots and ${fsLen} filesystems`;
+ } else if (snapLen > 0) {
+ title = `${snapLen} snapshots`;
+ } else if (fsLen > 0) {
+ title = `${fsLen} filesystems`;
+ }
+
+ confirmModals.bulkDeleteDatasets.title = title;
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Delete Datasets
+
+ {/if}
+ {/if}
+ {/if}
+{/snippet}
+
+
+
+
+ {
+ confirmModals.active = 'createFilesystem';
+ confirmModals.parent = 'filesystem';
+ confirmModals.createFilesystem.open = true;
+ confirmModals.createFilesystem.title = '';
+ }}
+ size="sm"
+ class="h-6"
+ >
+ New
+
+
+ {@render button('create-snapshot')}
+ {@render button('rollback-snapshot')}
+ {@render button('delete-snapshot')}
+ {@render button('delete-filesystem')}
+ {@render button('bulk-delete')}
+
+
+
+
+
+{#if confirmModals.active == 'deleteSnapshot' || confirmModals.active == 'deleteFilesystem'}
+ {
+ if (confirmModals.active) {
+ confirmAction();
+ }
+ },
+ onCancel: () => {
+ if (confirmModals.active) {
+ confirmModals[confirmModals.active].open = false;
+ }
+ }
+ }}
+ >
+{/if}
+
+{#if confirmModals.active === 'bulkDeleteDatasets'}
+ {
+ if (confirmModals.active) {
+ confirmAction();
+ }
+ },
+ onCancel: () => {
+ if (confirmModals.active) {
+ confirmModals[confirmModals.active].open = false;
+ }
+ }
+ }}
+ >
+{/if}
+
+{#if confirmModals.active === 'rollbackSnapshot'}
+ ${confirmModals[confirmModals.active].data.name}?`}
+ names={{
+ parent: confirmModals.parent,
+ element: confirmModals.active ? confirmModals[confirmModals.active].title || '' : ''
+ }}
+ actions={{
+ onConfirm: () => {
+ if (confirmModals.active) {
+ confirmAction();
+ }
+ },
+ onCancel: () => {
+ if (confirmModals.active) {
+ confirmModals[confirmModals.active].open = false;
+ }
+ }
+ }}
+ >
+{/if}
+
+{#if confirmModals.active === 'createSnapshot'}
+
+
+
+
+
+
+
+ Snapshot -
+ {confirmModals.createSnapshot.data.name !== ''
+ ? `${confirmModals.createSnapshot.title}@${confirmModals.createSnapshot.data.name}`
+ : `${confirmModals.createSnapshot.title}`}
+
+
+
+
+ {
+ confirmModals.createSnapshot.data.name = '';
+ confirmModals.createSnapshot.data.recursive = false;
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ {
+ console.log('close');
+
+ confirmModals.createSnapshot.open = false;
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+ Name
+
+
+
+
+
+
+ Recursive
+
+
+
+
+ {
+ confirmAction();
+ }}>Create
+
+
+
+{/if}
+
+{#if confirmModals.active === 'createFilesystem'}
+
+
+
+
+
+ Create Filesystem
+
+
+
+ {
+ confirmModals.createFilesystem.data.name = '';
+ confirmModals.createFilesystem.data.properties = {
+ parent: '',
+ atime: 'on',
+ checksum: 'on',
+ compression: 'on',
+ dedup: 'off',
+ encryption: 'off',
+ encryptionKey: '',
+ quota: ''
+ };
+ confirmModals.createFilesystem.title = '';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ {
+ closeCreateFsModal();
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+
+ Name
+
+
+
+
+ Parent
+ {
+ confirmModals.createFilesystem.data.properties.parent = value?.value || '';
+ }}
+ >
+
+
+
+
+
+
+ {#each grouped as pool}
+ {#each pool.filesystems as fs}
+ {fs.name}
+ {/each}
+ {/each}
+
+
+
+
+
+
+ ATime
+ {
+ confirmModals.createFilesystem.data.properties.atime = value?.value || '';
+ }}
+ >
+
+
+
+
+
+ {#each zfsProperties.atime as option}
+ {option.label}
+ {/each}
+
+
+
+
+
+
+
+ Checkum
+ {
+ confirmModals.createFilesystem.data.properties.checksum = value?.value || '';
+ }}
+ >
+
+
+
+
+
+ {#each zfsProperties.checksum as option}
+ {option.label}
+ {/each}
+
+
+
+
+
+
+
+ Compression
+ {
+ confirmModals.createFilesystem.data.properties.compression = value?.value || '';
+ }}
+ >
+
+
+
+
+
+ {#each zfsProperties.compression as option}
+ {option.label}
+ {/each}
+
+
+
+
+
+
+
+ Deduplication
+ {
+ confirmModals.createFilesystem.data.properties.dedup = value?.value || '';
+ }}
+ >
+
+
+
+
+
+ {#each zfsProperties.dedup as option}
+ {option.label}
+ {/each}
+
+
+
+
+
+
+
+ Encryption
+ {
+ confirmModals.createFilesystem.data.properties.encryption = value?.value || '';
+ }}
+ >
+
+
+
+
+
+ {#each zfsProperties.encryption as option}
+ {option.label}
+ {/each}
+
+
+
+
+
+
+ {#if confirmModals.createFilesystem.data.properties.encryption !== 'off'}
+
+
Passphrase
+
+
+
+ {
+ confirmModals.createFilesystem.data.properties.encryptionKey =
+ generatePassword();
+ }}
+ >
+ {
+ confirmModals.createFilesystem.data.properties.encryptionKey =
+ generatePassword();
+ }}
+ />
+
+
+
+ {/if}
+
+
+ Quota
+
+
+
+
+
+
+
+ {
+ confirmAction();
+ }}
+ >
+ Create
+
+
+
+
+
+{/if}
diff --git a/new-web/src/routes/[node]/storage/zfs/datasets/fs/+page.ts b/new-web/src/routes/[node]/storage/zfs/datasets/fs/+page.ts
new file mode 100644
index 00000000..96859628
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/datasets/fs/+page.ts
@@ -0,0 +1,17 @@
+import { getDatasets } from '$lib/api/zfs/datasets';
+import { getPools } from '$lib/api/zfs/pool';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [datasets, pools] = await Promise.all([
+ cachedFetch('datasets', async () => await getDatasets(), cacheDuration),
+ cachedFetch('pools', getPools, cacheDuration)
+ ]);
+
+ return {
+ pools: pools,
+ datasets: datasets
+ };
+}
diff --git a/new-web/src/routes/[node]/storage/zfs/datasets/snapshots/+page.svelte b/new-web/src/routes/[node]/storage/zfs/datasets/snapshots/+page.svelte
new file mode 100644
index 00000000..33bb1989
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/datasets/snapshots/+page.svelte
@@ -0,0 +1,538 @@
+
+
+{#snippet button(type: string)}
+ {#if type === 'delete-snapshot' && activeDataset !== null}
+ {
+ confirmModals.active = 'deleteSnapshot';
+ confirmModals.parent = 'snapshot';
+ confirmModals.deleteSnapshot.open = true;
+ confirmModals.deleteSnapshot.data = activeDataset?.name || '';
+ confirmModals.deleteSnapshot.title = activeDataset?.name || '';
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Delete Snapshot
+
+ {/if}
+
+ {#if type === 'view-periodics' && activePeriodics.length > 0}
+ {
+ confirmModals.active = 'viewSnapshotJobs';
+ confirmModals.parent = 'snapshot';
+ // confirmModals.deleteSnapshot.open = true;
+ // confirmModals.deleteSnapshot.data = activeDataset?.name || '';
+ // confirmModals.deleteSnapshot.title = activeDataset?.name || '';
+ confirmModals.viewSnapshotJobs.open = true;
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ View Snapshot Jobs
+
+ {/if}
+{/snippet}
+
+
+
+
+
+ {
+ confirmModals.active = 'createSnapshot';
+ confirmModals.parent = 'snapshot';
+ confirmModals.createSnapshot.open = true;
+ confirmModals.createSnapshot.recursive = false;
+ confirmModals.createSnapshot.interval = 0;
+ confirmModals.createSnapshot.name = '';
+ confirmModals.createSnapshot.title = '';
+ }}
+ size="sm"
+ class="h-6"
+ >
+ New
+
+
+
+
+
+ {@render button('delete-snapshot')}
+ {@render button('view-periodics')}
+
+
+
+
+
+{#if confirmModals.active === 'createSnapshot'}
+
+
+
+
+
+
+
+ Snapshot {confirmModals.createSnapshot.extraTitle}
+
+
+
+
+ {
+ confirmModals.createSnapshot = {
+ open: true,
+ recursive: false,
+ interval: 0,
+ name: '',
+ title: '',
+ extraTitle: ''
+ };
+ comboBoxes.pool.value = '';
+ comboBoxes.datasets.value = '';
+ comboBoxes.interval.value = '0';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ {
+ closeModal();
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ confirmAction();
+ }}>Create
+
+
+
+{/if}
+
+{#if confirmModals.active === 'deleteSnapshot'}
+
+
+
+ {getTranslation('are_you_sure', 'Are you sure?')}
+
+
+
+ {getTranslation(
+ 'common.permanent_delete_msg',
+ 'This action cannot be undone. This will permanently delete'
+ )}
+ {confirmModals.parent}
+ {confirmModals[confirmModals.active].title} .
+
+
+
+
+ {
+ confirmModals[confirmModals.active].recursive =
+ !confirmModals[confirmModals.active].recursive;
+ }}
+ >
+ Recursive
+
+
+
+
+ {
+ confirmModals[confirmModals.active].open = false;
+ }}
+ >
+ Cancel
+
+ {
+ confirmAction();
+ }}
+ >
+ Delete
+
+
+
+
+{/if}
+
+
diff --git a/new-web/src/routes/[node]/storage/zfs/datasets/snapshots/+page.ts b/new-web/src/routes/[node]/storage/zfs/datasets/snapshots/+page.ts
new file mode 100644
index 00000000..16d1b8a7
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/datasets/snapshots/+page.ts
@@ -0,0 +1,19 @@
+import { getDatasets, getPeriodicSnapshots } from '$lib/api/zfs/datasets';
+import { getPools } from '$lib/api/zfs/pool';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [datasets, pools, periodicSnapshots] = await Promise.all([
+ cachedFetch('datasets', async () => await getDatasets(), cacheDuration),
+ cachedFetch('pools', getPools, cacheDuration),
+ cachedFetch('periodicSnapshots', async () => await getPeriodicSnapshots(), cacheDuration)
+ ]);
+
+ return {
+ pools: pools,
+ periodicSnapshots: periodicSnapshots,
+ datasets: datasets
+ };
+}
diff --git a/new-web/src/routes/[node]/storage/zfs/datasets/volumes/+page.svelte b/new-web/src/routes/[node]/storage/zfs/datasets/volumes/+page.svelte
new file mode 100644
index 00000000..c7143f2c
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/datasets/volumes/+page.svelte
@@ -0,0 +1,836 @@
+
+
+{#snippet button(type: string)}
+ {#if activeRows && activeRows.length == 1}
+ {#if type === 'create-snapshot' && activeVolume?.type === 'volume'}
+ {
+ if (activeVolume) {
+ confirmModals.active = 'createSnapshot';
+ confirmModals.parent = 'volume';
+ confirmModals.createSnapshot.open = true;
+ confirmModals.createSnapshot.title = activeVolume.name;
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Create Snapshot
+
+ {/if}
+
+ {#if type === 'delete-snapshot' && activeSnapshot?.type === 'snapshot'}
+ {
+ if (activeSnapshot) {
+ confirmModals.active = 'deleteSnapshot';
+ confirmModals.parent = 'snapshot';
+ confirmModals.deleteSnapshot.open = true;
+ confirmModals.deleteSnapshot.title = activeSnapshot.name;
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Delete Snapshot
+
+ {/if}
+
+ {#if type === 'delete-volume' && activeVolume?.type === 'volume'}
+ {
+ if (activeRow) {
+ confirmModals.active = 'deleteVolume';
+ confirmModals.parent = 'volume';
+ confirmModals.deleteVolume.open = true;
+ confirmModals.deleteVolume.data = activeRow.name;
+ confirmModals.deleteVolume.title = activeRow.name;
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Delete Volume
+
+ {/if}
+ {:else if activeRows && activeRows.length > 1}
+ {#if activeVolumes.length > 0 && type === 'delete-volumes' && !isPoolSelected}
+ {
+ if (activeRow) {
+ confirmModals.active = 'deleteVolumes';
+ confirmModals.parent = 'volume';
+ confirmModals.deleteVolumes.open = true;
+ confirmModals.deleteVolumes.title = `${activeVolumes.length} volumes`;
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+ Delete Volumes
+
+ {/if}
+ {/if}
+{/snippet}
+
+
+
+
+ {
+ confirmModals.active = 'createVolume';
+ confirmModals.createVolume.open = true;
+ confirmModals.createVolume.title = '';
+ }}
+ size="sm"
+ class="h-6"
+ >
+ New
+
+
+ {@render button('create-snapshot')}
+ {@render button('delete-snapshot')}
+ {@render button('delete-volume')}
+ {@render button('delete-volumes')}
+
+
+
+
+
+{#snippet simpleSlect(prop: keyof props, label: string, placeholder: string)}
+
+ {label}
+ option.value === confirmModals.createVolume.data.properties[prop]
+ )?.label || confirmModals.createVolume.data.properties[prop],
+ value: confirmModals.createVolume.data.properties[prop]
+ }}
+ onSelectedChange={(value) => {
+ confirmModals.createVolume.data.properties[prop] = value?.value || '';
+ }}
+ >
+
+
+
+
+
+
+ {#each zfsProperties[prop] as option}
+ {option.label}
+ {/each}
+
+
+
+
+{/snippet}
+
+{#if confirmModals.active === 'createVolume'}
+
+
+
+
+
+ Create Volume
+
+
+ {
+ confirmModals.createVolume.data = {
+ name: '',
+ properties: {
+ parent: '',
+ checksum: 'on',
+ compression: 'on',
+ dedup: 'off',
+ encryption: 'off',
+ encryptionKey: '',
+ volblocksize: '16384',
+ size: '',
+ primarycache: 'all',
+ volmode: 'dev'
+ }
+ };
+ confirmModals.createVolume.title = '';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ closeCreateVolumeModal()}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+
+ Name
+
+
+
+
+ Size
+
+
+
+
+ Parent
+ {
+ confirmModals.createVolume.data.properties.parent = value?.value || '';
+ }}
+ >
+
+
+
+
+
+
+ {#each grouped as group}
+ {group.pool.name}
+ {/each}
+
+
+
+
+
+ {@render simpleSlect('volblocksize', 'Block Size', 'Select block size')}
+ {@render simpleSlect('checksum', 'Checksum', 'Select checksum algorithm')}
+ {@render simpleSlect('compression', 'Compression', 'Select compression type')}
+ {@render simpleSlect('dedup', 'Deduplication', 'Select deduplication mode')}
+ {@render simpleSlect('encryption', 'Encryption', 'Select encryption')}
+ {@render simpleSlect('primarycache', 'Primary Cache', 'Select primary cache mode')}
+ {@render simpleSlect('volmode', 'Volume Mode', 'Select volume mode')}
+
+ {#if confirmModals.createVolume.data.properties.encryption !== 'off'}
+
+
Passphrase
+
+
+
+ {
+ confirmModals.createVolume.data.properties.encryptionKey = generatePassword();
+ }}
+ >
+ {
+ confirmModals.createVolume.data.properties.encryptionKey = generatePassword();
+ }}
+ />
+
+
+
+ {/if}
+
+
+
+
+
+ {
+ confirmAction();
+ }}
+ >
+ Create
+
+
+
+
+
+{/if}
+
+{#if confirmModals.active == 'deleteVolume'}
+ {
+ if (confirmModals.active) {
+ confirmAction();
+ }
+ },
+ onCancel: () => {
+ if (confirmModals.active) {
+ confirmModals[confirmModals.active].open = false;
+ }
+ }
+ }}
+ >
+{/if}
+
+{#if confirmModals.active == 'deleteVolumes'}
+ {
+ if (confirmModals.active) {
+ confirmAction();
+ }
+ },
+ onCancel: () => {
+ if (confirmModals.active) {
+ console.log(confirmModals[confirmModals.active]);
+ confirmModals[confirmModals.active].open = false;
+ }
+ }
+ }}
+ >
+{/if}
+
+{#if confirmModals.active === 'createSnapshot'}
+
+
+
+
+
+
+
+ Snapshot -
+ {confirmModals.createSnapshot.data.name !== ''
+ ? `${confirmModals.createSnapshot.title}@${confirmModals.createSnapshot.data.name}`
+ : `${confirmModals.createSnapshot.title}`}
+
+
+
+
+ {
+ confirmModals.createSnapshot.data.name = '';
+ confirmModals.createSnapshot.title = '';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ {
+ confirmModals.createSnapshot = {
+ open: false,
+ data: {
+ name: ''
+ },
+ title: ''
+ };
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+ Name
+
+
+
+
+ {
+ confirmAction();
+ }}>Create
+
+
+
+{/if}
+
+{#if confirmModals.active == 'deleteSnapshot'}
+ {
+ if (confirmModals.active) {
+ confirmAction();
+ }
+ },
+ onCancel: () => {
+ if (confirmModals.active) {
+ confirmModals[confirmModals.active].open = false;
+ }
+ }
+ }}
+ >
+{/if}
diff --git a/new-web/src/routes/[node]/storage/zfs/datasets/volumes/+page.ts b/new-web/src/routes/[node]/storage/zfs/datasets/volumes/+page.ts
new file mode 100644
index 00000000..96859628
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/datasets/volumes/+page.ts
@@ -0,0 +1,17 @@
+import { getDatasets } from '$lib/api/zfs/datasets';
+import { getPools } from '$lib/api/zfs/pool';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [datasets, pools] = await Promise.all([
+ cachedFetch('datasets', async () => await getDatasets(), cacheDuration),
+ cachedFetch('pools', getPools, cacheDuration)
+ ]);
+
+ return {
+ pools: pools,
+ datasets: datasets
+ };
+}
diff --git a/new-web/src/routes/[node]/storage/zfs/pools/+page.svelte b/new-web/src/routes/[node]/storage/zfs/pools/+page.svelte
new file mode 100644
index 00000000..996abb49
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/pools/+page.svelte
@@ -0,0 +1,2068 @@
+
+
+{#snippet button(type: string)}
+ {#if activeRow && Object.keys(activeRow).length > 0}
+ {#if type === 'pool-status'}
+ {#if isPool(pools, activeRow.name)}
+ {
+ confirmModals.active = 'statusPool';
+ confirmModals.statusPool.open = true;
+ confirmModals.statusPool.title = activeRow?.name;
+ confirmModals.statusPool.data.status = pools.find((p) => p.name === activeRow?.name)
+ ?.status as Zpool['status'];
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {getTranslation('common.status', 'Status')}
+
+ {/if}
+ {/if}
+
+ {#if type === 'pool-scrub'}
+ {#if isPool(pools, activeRow.name)}
+ {
+ const response = await scrubPool(activeRow?.name);
+ if (response.status === 'error') {
+ toast.error(parsePoolActionError(response), {
+ position: 'bottom-center'
+ });
+ } else {
+ toast.success(getTranslation(`zfs.pool.${response.message}`, 'Scrub started'), {
+ position: 'bottom-center'
+ });
+ }
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ disabled={scrubInProgress}
+ title={scrubInProgress
+ ? getTranslation('zfs.pool.scrub_in_progress', 'A scrub is already in progress')
+ : ''}
+ >
+
+ {getTranslation('zfs.pool.scrub', 'Scrub')}
+
+ {/if}
+ {/if}
+
+ {#if type === 'pool-edit'}
+ {#if isPool(pools, activeRow.name)}
+ {
+ const propsArr = pools.find((p) => p.name === activeRow?.name)?.properties || [];
+
+ console.log(pools.find((p) => p.name === activeRow?.name));
+
+ editModal.open = true;
+ editModal.properties.comment =
+ propsArr.find((prop) => prop.property === 'comment')?.value || '';
+ editModal.properties.autoexpand =
+ propsArr.find((prop) => prop.property === 'autoexpand')?.value || '';
+ editModal.properties.autotrim =
+ propsArr.find((prop) => prop.property === 'autotrim')?.value || '';
+ editModal.properties.delegation =
+ propsArr.find((prop) => prop.property === 'delegation')?.value || '';
+ editModal.properties.failmode =
+ propsArr.find((prop) => prop.property === 'failmode')?.value || '';
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ disabled={replaceInProgress}
+ title={replaceInProgress ? 'Cannot edit pool when replacing device in any pool' : ''}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.edit', 'Edit'))}
+
+ {/if}
+ {/if}
+
+ {#if type === 'pool-delete'}
+ {#if isPool(pools, activeRow.name)}
+ {
+ confirmModals.active = 'deletePool';
+ confirmModals.deletePool.open = true;
+ confirmModals.deletePool.title = activeRow?.name;
+ confirmModals.deletePool.data = activeRow?.name;
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ disabled={replaceInProgress}
+ title={replaceInProgress
+ ? getTranslation(
+ 'zfs.pool.warnings.cannot_delete_pool_while_replacing_device',
+ 'Cannot delete pool while replacing device in any pool'
+ )
+ : ''}
+ >
+
+ {getTranslation('zfs.pool.delete', 'Delete')}
+
+ {/if}
+ {/if}
+
+ {#if type === 'replace-device'}
+ {#if isReplaceableDevice(pools, activeRow.name)}
+ {
+ confirmModals.active = 'replaceDevice';
+ confirmModals.replaceDevice.open = true;
+ confirmModals.replaceDevice.title = activeRow?.name;
+ confirmModals.replaceDevice.data = {
+ pool: getPoolByDevice(pools, activeRow?.name),
+ old: activeRow?.name,
+ new: ''
+ };
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ disabled={replaceInProgress}
+ title={replaceInProgress
+ ? 'Cannot replace device while replacing device in any pool'
+ : ''}
+ >
+
+ {capitalizeFirstLetter(getTranslation('zfs.pool.replace_device', 'Replace Device'))}
+
+ {/if}
+ {/if}
+ {/if}
+{/snippet}
+
+{#snippet diskContainer(type: string)}
+
+
{type}
+
+
+
+ {#each useableDisks.filter((disk) => disk.type === type && disk.partitions.length === 0 && !isDiskInVdev(disk.uuid)) as disk (disk.uuid)}
+
+
+ {#if type === 'HDD'}
+
+ {:else if type === 'SSD'}
+
+ {:else if type === 'NVMe'}
+
+ {/if}
+
+
+ {disk.device.replaceAll('/dev/', '')}
+
+
+ {humanFormat(disk.size)}
+
+
+ {/each}
+
+ {#if useableDisks.filter((disk) => disk.type === type).length === 0 || useableDisks.filter((disk) => disk.type === type && disk.partitions.length === 0 && !isDiskInVdev(disk.uuid)).length === 0}
+
+ {getTranslation('zfs.pool.no_available_disks', 'No available disks')}
+
+ {/if}
+
+
+
+
+{/snippet}
+
+{#snippet partitionsContainer()}
+
+
{getTranslation('zfs.pool.partitions', 'Partitions')}
+
+
+
+ {#each useablePartitions.filter((partition) => !modal.vdevContainers
+ .flatMap((vdev) => vdev.partitions)
+ .some((p) => p.name === partition.name)) as partition (partition.name)}
+
+
+
+
+
+ {partition.name}
+
+
+ {humanFormat(partition.size)}
+
+
+ {/each}
+
+ {#if useablePartitions.length === 0 || useablePartitions.filter((partition) => !modal.vdevContainers
+ .flatMap((vdev) => vdev.partitions)
+ .some((p) => p.name === partition.name)).length === 0}
+
+ {getTranslation('zfs.pool.no_available_partitions', 'No available partitions')}
+
+ {/if}
+
+
+
+
+{/snippet}
+
+{#snippet vdevContainer(id: number)}
+ {#each modal.vdevContainers[id]?.disks || [] as disk (disk.uuid)}
+
+ {#if disk.type === 'HDD'}
+
+ {:else if disk.type === 'SSD'}
+
+ {:else if disk.type === 'NVMe'}
+
+ {/if}
+
+
+ {disk.device.split('/').pop()}
+
+
+
removeFromVdev(id, disk.uuid as string)}
+ >
+
+
+
+ {/each}
+
+ {#each modal.vdevContainers[id]?.partitions || [] as partition (partition.name)}
+
+
+
+
+ {partition.name.split('/').pop()}
+
+
+
removeFromVdev(id, partition.name)}
+ >
+
+
+
+ {/each}
+{/snippet}
+
+{#snippet vdevErrors(id: number)}
+ {#if getVdevErrors(id) !== ''}
+
+
+
+
+
+ {@html getVdevErrors(id)}
+
+
+
+
+ {/if}
+{/snippet}
+
+
+
+
+ (modal.open = !modal.open)} size="sm" class="h-6">
+
+ {capitalizeFirstLetter(getTranslation('common.new', 'New'))}
+
+
+ {@render button('pool-status')}
+ {@render button('pool-scrub')}
+ {@render button('pool-edit')}
+ {@render button('pool-delete')}
+ {@render button('replace-device')}
+
+
+
+
+
+
+
+
+
+
+ {getTranslation(
+ 'zfs.pool.create_zfs_pool',
+ 'Create ZFS Pool'
+ )}
+
+
+
+ {
+ modal.name = '';
+ modal.vdevCount = 1;
+ modal.vdevContainers = [];
+ modal.advanced = false;
+ modal.properties = {
+ comment: '',
+ ashift: 12,
+ autoexpand: 'off',
+ autotrim: 'off',
+ delegation: 'off',
+ failmode: 'wait'
+ };
+ modal.raidType = 'stripe';
+ modal.mountPoint = '';
+ modal.useable = 0;
+ modal.forceCreate = false;
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ modal.close()}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+ {capitalizeFirstLetter(getTranslation('common.devices', 'Devices'))}
+
+ {capitalizeFirstLetter(getTranslation('common.options', 'Options'))}
+
+
+
+
+
+
+
+
+
+ {capitalizeFirstLetter(
+ getTranslation('zfs.pool.redundancy.redundancy', 'Redundancy')
+ )}
+ ({humanFormat(modal.useable)})
+ rt.value === modal.raidType)?.label,
+ value: raidTypes.find((rt) => rt.value === modal.raidType)?.value
+ }}
+ onSelectedChange={(value) => {
+ modal.raidType = value?.value as ZpoolRaidType;
+ setRedundancyAvailability();
+ }}
+ >
+
+
+
+
+
+ {#each raidTypes as raidType}
+ {#if raidType.available}
+ {raidType.label}
+ {/if}
+ {/each}
+
+
+
+
+
+
+
+
+
VDEVs
+
+
+ {#each Array(modal.vdevCount) as _, i}
+
+ {@render vdevErrors(i)}
+
+
handleDropToVdev(i, event),
+ dragover_class: 'droppable'
+ }}
+ >
+ {#if !vdevContains(i)}
+
+ {i + 1}
+ Drop disks here
+
+ {:else}
+
+ {@render vdevContainer(i)}
+
+ {/if}
+
+
+ {/each}
+
+
+
+
+
+
+
Disks
+
+ {@render diskContainer('HDD')}
+ {@render diskContainer('SSD')}
+ {@render diskContainer('NVMe')}
+ {@render partitionsContainer()}
+
+
+
+
+
+
+
+
+
+
+
+ {#if modal.advanced}
+
+
+
+ Ashift
+ {
+ const val = i + 9;
+ return { value: val, label: `${val}` };
+ })
+ ].find((opt) => opt.value === modal.properties.ashift)?.label,
+ value: [
+ { value: 0, label: '0 (auto)' },
+ ...Array.from({ length: 8 }, (_, i) => {
+ const val = i + 9;
+ return { value: val, label: `${val}` };
+ })
+ ].find((opt) => opt.value === modal.properties.ashift)?.value
+ }}
+ onSelectedChange={(value) => {
+ modal.properties.ashift = value?.value || 0;
+ }}
+ >
+
+
+
+
+
+ 0 (auto)
+ {#each Array.from({ length: 8 }, (_, i) => i + 9) as val}
+ {val}
+ {/each}
+
+
+
+
+
+
+
+ Auto Expand
+ {
+ modal.properties.autoexpand = value?.value || 'off';
+ }}
+ >
+
+
+
+
+
+ Yes
+ No
+
+
+
+
+
+
+
+ Auto Trim
+ {
+ modal.properties.autotrim = value?.value || 'off';
+ }}
+ >
+
+
+
+
+
+ Yes
+ No
+
+
+
+
+
+
+
+ Delegation
+ {
+ modal.properties.delegation = value?.value || 'off';
+ }}
+ >
+
+
+
+
+
+ Yes
+ No
+
+
+
+
+
+
+
+ Fail Mode
+ {
+ modal.properties.failmode = value?.value || 'wait';
+ }}
+ >
+
+
+
+
+
+ Wait
+ Continue
+ Panic
+
+
+
+
+
+ {#if possibleSpares && possibleSpares.length > 0 && modal.raidType !== 'stripe'}
+
+ Spares
+ {
+ modal.spares = v as SelectSpares[];
+ }}
+ >
+
+ {#if possibleSpares.length > 0}
+
+ {modal.spares.length > 0
+ ? modal.spares.map((s) => s.label).join(', ')
+ : 'Select spares'}
+
+ {:else}
+ Select spares
+ {/if}
+
+
+
+ {#each possibleSpares as spare}
+
+ {spare}
+
+ {/each}
+
+
+
+
+ {/if}
+
+ {/if}
+
+
+
+
+
+
+
+ makePool()}>
+ {#if modal.creating}
+
+ {:else}
+ {capitalizeFirstLetter(getTranslation('common.create', 'Create'))}
+ {/if}
+
+
+
+
+
+
+{#if confirmModals.active == 'deletePool'}
+ {
+ if (confirmModals.active) {
+ confirmAction();
+ }
+ },
+ onCancel: () => {
+ if (confirmModals.active) {
+ confirmModals[confirmModals.active].open = false;
+ }
+ }
+ }}
+ >
+{/if}
+
+{#snippet dtEl(device: Zpool['status']['devices'][0], showNote: boolean)}
+
+
+
+
{device.name}
+ {#if device.note && showNote}
+
+ {#if device.note === '(resilvering)'}
+ Resilvering
+ {:else}
+ {device.note}
+ {/if}
+
+ {/if}
+
+
+ {#if device.read > 0 || device.write > 0 || device.cksum > 0}
+
+ {#if device.read > 0}
+ READ: {device.read}
+ {/if}
+ {#if device.write > 0}
+ WRITE: {device.write}
+ {/if}
+ {#if device.cksum > 0}
+ CKSUM: {device.cksum}
+ {/if}
+
+ {/if}
+{/snippet}
+
+{#snippet deviceTreeNode(device: Zpool['status']['devices'][0], showNote: boolean)}
+
+
+ {#if showNote && !device.__isLast}
+
+ {:else}
+
+ {/if}
+ {@render dtEl(device, showNote)}
+
+
+ {#if device.children && device.children.length > 0 && !device.name.startsWith('replacing')}
+
+ {#each device.children as child, index (child.name)}
+
+
+ {@render deviceTreeNode(
+ { ...child, __isLast: index === device.children.length - 1 },
+ true
+ )}
+
+ {/each}
+
+ {/if}
+
+ {#if device.name.startsWith('replacing') && device.children && device.children.length > 0}
+
+ {#each device.children as replaceDisk}
+ {@render deviceTreeNode(replaceDisk, true)}
+ {/each}
+
+ {/if}
+
+{/snippet}
+
+{#if confirmModals.active == 'statusPool'}
+ {
+ confirmModals.statusPool.open = false;
+ }}
+ closeOnOutsideClick={true}
+ closeOnEscape={false}
+ >
+
+
+
+
+ Pool Status
+ •
+ {confirmModals.statusPool.data.status.name}
+ {sPool.state}
+
+
+
+ {
+ confirmModals.statusPool.open = false;
+ }}
+ >
+
+
+
+
+
+ {#if sPool}
+ {#if sPool.status && sPool.status.length > 0}
+
+
+
+
+
{sPool.status}
+ {#if sPool.action && sPool.action.length > 0}
+
{sPool.action}
+ {/if}
+
+
+
+ {/if}
+
+
+
+
+
+ Scan Activity
+
+
+ {#if sPool.scan && sPool.scan.length > 0}
+ {#if sPool.scan.includes('in progress') || sPool.scan.includes('resilver in progress')}
+ {@const progressMatch = sPool.scan.match(/(\d+\.\d+)%/)}
+ {@const progress = progressMatch ? parseFloat(progressMatch[1]) : 0}
+ {@const isResilver = sPool.scan.includes('resilver')}
+
+
+ {capitalizeFirstLetter(sPool.scan)}
+
+
+ {:else}
+
+ {capitalizeFirstLetter(sPool.scan)}
+
+ {/if}
+ {:else}
+
+
+ No recent scan activity
+
+ {/if}
+
+
+
+
+
+
+ Device Topology
+
+
+ {#if sPool.devices && sPool.devices.length > 0}
+
+ {#each sPool.devices as device, index (device.name)}
+ {@render deviceTreeNode(
+ { ...device, __isLast: index === device.children.length - 1 },
+ false
+ )}
+ {/each}
+
+
+ {#if sPoolSpares && sPoolSpares.length > 0}
+
+
+
+
+ {#each sPoolSpares as spare, index (spare.name)}
+
+ {/each}
+
+
+ {/if}
+ {:else}
+
+
+ No devices found
+
+ {/if}
+
+
+
+
+
+ {/if}
+
+
+
+{/if}
+
+{#if confirmModals.active == 'replaceDevice'}
+
+
+
+ Replace {confirmModals.replaceDevice.data.old} in {confirmModals.replaceDevice.data
+ .pool}
+
+
+
+
+ d.device === confirmModals.replaceDevice.data?.new)
+ ?.device ||
+ useablePartitions.find((d) => d.name === confirmModals.replaceDevice.data?.new)
+ ?.name,
+ value: confirmModals.replaceDevice.data?.new
+ }}
+ onSelectedChange={(value) => {
+ confirmModals.replaceDevice.data = {
+ ...confirmModals.replaceDevice.data,
+ new: value?.value as string
+ };
+ }}
+ >
+
+
+
+
+
+ {#each useableDisks as disk}
+
+ {disk.device}
+
+ {/each}
+
+ {#each useablePartitions as partition}
+
+ {partition.name}
+
+ {/each}
+
+
+
+
+
+
+
+ {
+ confirmModals.replaceDevice.open = false;
+ }}>Cancel
+ {
+ confirmAction();
+ }}
+ >
+ Replace
+
+
+
+
+{/if}
+
+
+
+
+
+
+ {getTranslation(
+ 'zfs.pool.edit_zfs_pool',
+ 'Edit ZFS Pool'
+ )}
+
+
+
+ {
+ editModal.close(false);
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ editModal.close(true)}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+
+
+
+ Auto Expand
+ {
+ editModal.properties.autoexpand = value?.value || 'off';
+ }}
+ >
+
+
+
+
+
+ Yes
+ No
+
+
+
+
+
+
+
+ Auto Trim
+ {
+ editModal.properties.autotrim = value?.value || 'off';
+ }}
+ >
+
+
+
+
+
+ Yes
+ No
+
+
+
+
+
+
+
+ Delegation
+ {
+ editModal.properties.delegation = value?.value || 'off';
+ }}
+ >
+
+
+
+
+
+ Yes
+ No
+
+
+
+
+
+
+
+ Fail Mode
+ {
+ editModal.properties.failmode = value?.value || 'wait';
+ }}
+ >
+
+
+
+
+
+ Wait
+ Continue
+ Panic
+
+
+
+
+
+
+
+ Comment
+
+
+
+
+
+
+
+
+ saveEdit()}>
+ {capitalizeFirstLetter(getTranslation('common.edit', 'Edit'))}
+
+
+
+
+
diff --git a/new-web/src/routes/[node]/storage/zfs/pools/+page.ts b/new-web/src/routes/[node]/storage/zfs/pools/+page.ts
new file mode 100644
index 00000000..a3707cf1
--- /dev/null
+++ b/new-web/src/routes/[node]/storage/zfs/pools/+page.ts
@@ -0,0 +1,17 @@
+import { listDisks } from '$lib/api/disk/disk';
+import { editPool, getPools } from '$lib/api/zfs/pool';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [disks, pools] = await Promise.all([
+ cachedFetch('disks', async () => await listDisks(), cacheDuration),
+ cachedFetch('pools', getPools, cacheDuration)
+ ]);
+
+ return {
+ disks,
+ pools
+ };
+}
diff --git a/new-web/src/routes/[node]/summary/+page.svelte b/new-web/src/routes/[node]/summary/+page.svelte
new file mode 100644
index 00000000..39a8ace7
--- /dev/null
+++ b/new-web/src/routes/[node]/summary/+page.svelte
@@ -0,0 +1,306 @@
+
+
+
+
+
+
+
+
+ {basicInfo.hostname} (Started {secondsToHoursAgo(
+ basicInfo.uptime
+ )})
+
+
+
+
+
+
+
+ {getTranslation('summary.cpu_usage', 'CPU Usage')}
+
+
+ {floatToNDecimals(cpuInfo.usage, 2)}% {getTranslation('common.of', 'of')}
+ {cpuInfo.logicalCores}
+ {getTranslation('summary.CPU_s', 'CPU(s)')}
+
+
+
+
+
+
+
+
+ {getTranslation('summary.ram_usage', 'RAM Usage')}
+
+
+ {floatToNDecimals(ramInfo.usedPercent, 2)}% of {bytesToHumanReadable(
+ ramInfo.total
+ )}
+
+
+
+
+
+
+
+ {getTranslation(
+ 'summary.disk_usage',
+ 'Disk Usage'
+ )}
+
+
+ {floatToNDecimals(totalDiskUsage, 2)} %
+
+
+
+
+
+
+
+
+ {getTranslation('summary.io_delay', 'I/O Delay')}
+
+
{floatToNDecimals(ioDelay.delay, 3) || 0} %
+
+
+
+
+
+
+ {getTranslation(
+ 'summary.swap_usage',
+ 'Swap Usage'
+ )}
+
+
+ {floatToNDecimals(swapInfo.usedPercent, 2)}% of {bytesToHumanReadable(
+ swapInfo.total
+ )}
+
+
+
+
+
+
+
+
+
+ {getTranslation('summary.CPU_s', 'CPU(s)')}
+ {cpuInfo.logicalCores} x {cpuInfo.name}
+
+
+ {getTranslation('summary.operating_system', 'Operating System')}
+ {basicInfo.os}
+
+
+ {getTranslation('summary.load_average', 'Load Average')}
+ {basicInfo.loadAverage}
+
+
+ {getTranslation('summary.boot_mode', 'Boot Mode')}
+ {basicInfo.bootMode}
+
+
+
+ {getTranslation('summary.sylve_version', 'Sylve Version')}
+ {basicInfo.sylveVersion}
+
+
+
+
+
+
+
+
+
+
+
+
{getTranslation('summary.cpu_usage', 'CPU Usage')}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/new-web/src/routes/[node]/summary/+page.ts b/new-web/src/routes/[node]/summary/+page.ts
new file mode 100644
index 00000000..932e0ed1
--- /dev/null
+++ b/new-web/src/routes/[node]/summary/+page.ts
@@ -0,0 +1,57 @@
+import { getBasicInfo } from '$lib/api/info/basic';
+import { getCPUInfo } from '$lib/api/info/cpu';
+import { getRAMInfo, getSwapInfo } from '$lib/api/info/ram';
+import { getIODelay } from '$lib/api/zfs/pool';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+import { getTotalDiskUsage } from '$lib/utils/zfs';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [
+ basicInfo,
+ cpuInfo,
+ cpuInfoHistorical,
+ ramInfo,
+ swapInfo,
+ ioDelay,
+ ioDelayHistorical,
+ totalDiskUsage
+ ] = await Promise.all([
+ cachedFetch('basicInfo', getBasicInfo, cacheDuration),
+ cachedFetch('cpuInfo', getCPUInfo, cacheDuration),
+ cachedFetch(
+ 'cpuInfoHistorical',
+ () =>
+ getCPUInfo({
+ queryKey: ['cpuInfoHistorical'],
+ meta: undefined
+ }),
+ cacheDuration
+ ),
+ cachedFetch('ramInfo', getRAMInfo, cacheDuration),
+ cachedFetch('swapInfo', getSwapInfo, cacheDuration),
+ cachedFetch(
+ 'ioDelay',
+ () => getIODelay({ queryKey: ['ioDelay'], meta: undefined }),
+ cacheDuration
+ ),
+ cachedFetch(
+ 'ioDelayHistorical',
+ () => getIODelay({ queryKey: ['ioDelayHistorical'], meta: undefined }),
+ cacheDuration
+ ),
+ cachedFetch('totalDiskUsage', getTotalDiskUsage, cacheDuration)
+ ]);
+
+ return {
+ basicInfo,
+ cpuInfo,
+ cpuInfoHistorical,
+ ramInfo,
+ swapInfo,
+ ioDelay,
+ ioDelayHistorical,
+ totalDiskUsage
+ };
+}
diff --git a/new-web/src/routes/[node]/utilities/downloader/+page.svelte b/new-web/src/routes/[node]/utilities/downloader/+page.svelte
new file mode 100644
index 00000000..66c14928
--- /dev/null
+++ b/new-web/src/routes/[node]/utilities/downloader/+page.svelte
@@ -0,0 +1,340 @@
+
+
+{#snippet button(type: string)}
+ {#if type === 'delete' && onlyParentsSelected}
+ {#if activeRows && activeRows.length >= 1}
+
+
+ {#if activeRows.length > 1}
+ {capitalizeFirstLetter(getTranslation('common.bulk_delete', 'Bulk Delete'))}
+ {:else}
+ {capitalizeFirstLetter(getTranslation('common.delete', 'Delete'))}
+ {/if}
+
+ {/if}
+ {/if}
+
+ {#if type === 'download' && onlyChildSelected && isDownloadCompleted}
+ {#if activeRows && activeRows.length == 1}
+
+
+ {capitalizeFirstLetter(getTranslation('common.download', 'Download'))}
+
+ {/if}
+ {/if}
+
+ {#if type === 'download' && httpDownloadSelected && isDownloadCompleted}
+ {#if activeRows && activeRows.length == 1}
+
+
+ {capitalizeFirstLetter(getTranslation('common.download', 'Download'))}
+
+ {/if}
+ {/if}
+{/snippet}
+
+
+
+
+
+ (modalState.isOpen = true)} size="sm" class="h-6 ">
+
+ {capitalizeFirstLetter(getTranslation('common.new', 'New'))}
+
+
+ {@render button('delete')}
+ {@render button('download')}
+
+
+
+
+
+
+
+
+
+ {capitalizeFirstLetter(getTranslation('common.download', 'Download'))}
+
+
+
+
+
+ {
+ modalState.isOpen = false;
+ modalState.url = '';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.reset', 'Reset'))}
+
+ {
+ modalState.isOpen = false;
+ modalState.url = '';
+ }}
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.close', 'Close'))}
+
+
+
+
+
+
+
+
+ {capitalizeFirstLetter(getTranslation('common.download', 'Download'))}
+
+
+
+
+
+
+
+
{
+ const id = activeRows ? activeRows[0]?.id : null;
+ const result = await deleteDownload(id as number);
+ if (isAPIResponse(result) && result.status === 'success') {
+ modalState.isDelete = false;
+ modalState.title = '';
+ activeRows = null;
+ } else {
+ handleValidationErrors(result as APIResponse, 'downloads');
+ }
+ },
+ onCancel: () => {
+ modalState.isDelete = false;
+ modalState.title = '';
+ }
+ }}
+ >
+
+
{
+ const ids = activeRows ? activeRows.map((row) => row.id) : [];
+ const result = await bulkDeleteDownloads(ids as number[]);
+ if (isAPIResponse(result) && result.status === 'success') {
+ modalState.isBulkDelete = false;
+ modalState.title = '';
+ activeRows = null;
+ } else {
+ handleValidationErrors(result as APIResponse, 'downloads');
+ }
+ },
+ onCancel: () => {
+ modalState.isBulkDelete = false;
+ modalState.title = '';
+ }
+ }}
+ >
+
diff --git a/new-web/src/routes/[node]/utilities/downloader/+page.ts b/new-web/src/routes/[node]/utilities/downloader/+page.ts
new file mode 100644
index 00000000..83b1778d
--- /dev/null
+++ b/new-web/src/routes/[node]/utilities/downloader/+page.ts
@@ -0,0 +1,14 @@
+import { getDownloads } from '$lib/api/utilities/downloader';
+import { SEVEN_DAYS } from '$lib/utils';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load() {
+ const cacheDuration = SEVEN_DAYS;
+ const [downloads] = await Promise.all([
+ cachedFetch('downloads', async () => getDownloads(), cacheDuration)
+ ]);
+
+ return {
+ downloads
+ };
+}
diff --git a/new-web/src/routes/[node]/vm/[node]/+page.svelte b/new-web/src/routes/[node]/vm/[node]/+page.svelte
new file mode 100644
index 00000000..e69de29b
diff --git a/new-web/src/routes/[node]/vm/[node]/console/+page.svelte b/new-web/src/routes/[node]/vm/[node]/console/+page.svelte
new file mode 100644
index 00000000..848a9429
--- /dev/null
+++ b/new-web/src/routes/[node]/vm/[node]/console/+page.svelte
@@ -0,0 +1,114 @@
+
+
+
+
+
+
{@html status}
+
+
{
+ disconnectVNC();
+ connectVNC();
+ }}
+ >
+
+ Reconnect
+
+
+
+
+
+
+
+
diff --git a/new-web/src/routes/[node]/vm/[node]/console/+page.ts b/new-web/src/routes/[node]/vm/[node]/console/+page.ts
new file mode 100644
index 00000000..743dde65
--- /dev/null
+++ b/new-web/src/routes/[node]/vm/[node]/console/+page.ts
@@ -0,0 +1,19 @@
+import { getVMs } from '$lib/api/vm/vm';
+
+export async function load({ params }) {
+ const vms = (await getVMs()) || [];
+ const vm = vms.find((vm) => vm.vmId === Number(params.node));
+
+ let port = 0;
+ let password = '';
+
+ if (vm) {
+ port = vm.vncPort;
+ password = vm.vncPassword;
+ }
+
+ return {
+ port,
+ password
+ };
+}
diff --git a/new-web/src/routes/[node]/vm/[node]/storage/+page.svelte b/new-web/src/routes/[node]/vm/[node]/storage/+page.svelte
new file mode 100644
index 00000000..05f0ee29
--- /dev/null
+++ b/new-web/src/routes/[node]/vm/[node]/storage/+page.svelte
@@ -0,0 +1,161 @@
+
+
+{#snippet button(type: string)}
+ {#if domain && domain.status === 'Shutoff' && activeRows && activeRows.length === 1}
+ {#if type === 'detach'}
+ handleDetach()} size="sm" class="h-6">
+
+ {capitalizeFirstLetter(getTranslation('common.detach', 'Detach'))}
+
+ {/if}
+ {/if}
+{/snippet}
+
+
+
+ handleCreate()} size="sm" class="h-6 ">
+
+ {capitalizeFirstLetter(getTranslation('common.new', 'New'))}
+
+
+ {@render button('detach')}
+
+
+
+
diff --git a/new-web/src/routes/[node]/vm/[node]/storage/+page.ts b/new-web/src/routes/[node]/vm/[node]/storage/+page.ts
new file mode 100644
index 00000000..aeac4536
--- /dev/null
+++ b/new-web/src/routes/[node]/vm/[node]/storage/+page.ts
@@ -0,0 +1,27 @@
+import { getDownloads } from '$lib/api/utilities/downloader';
+import { getVMDomain, getVMs } from '$lib/api/vm/vm';
+import { getDatasets } from '$lib/api/zfs/datasets';
+import { getPools } from '$lib/api/zfs/pool';
+import { SEVEN_DAYS } from '$lib/utils.js';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load({ params }) {
+ const cacheDuration = SEVEN_DAYS;
+ const vmId = params.node;
+
+ const [vms, domain, datasets, pools, downloads] = await Promise.all([
+ cachedFetch('vm-list', async () => getVMs(), cacheDuration),
+ cachedFetch(`vm-domain-${vmId}`, async () => getVMDomain(Number(vmId)), cacheDuration),
+ cachedFetch('datasets', async () => await getDatasets(), cacheDuration),
+ cachedFetch('pools', getPools, cacheDuration),
+ cachedFetch('downloads', async () => getDownloads(), cacheDuration)
+ ]);
+
+ return {
+ vms: vms,
+ domain: domain,
+ datasets: datasets,
+ pools: pools,
+ downloads: downloads
+ };
+}
diff --git a/new-web/src/routes/[node]/vm/[node]/summary/+page.svelte b/new-web/src/routes/[node]/vm/[node]/summary/+page.svelte
new file mode 100644
index 00000000..4c963d67
--- /dev/null
+++ b/new-web/src/routes/[node]/vm/[node]/summary/+page.svelte
@@ -0,0 +1,405 @@
+
+
+{#snippet button(type: string)}
+ {#if type === 'start' && domain.id == -1 && domain.status !== 'Running'}
+ handleStart()}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.start', 'Start'))}
+
+
+ {
+ modalState.isDeleteOpen = true;
+ modalState.title = `${vm.name} (${vm.vmId})`;
+ }}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.delete', 'Delete'))}
+
+ {:else if type === 'stop' && domain.id !== -1 && domain.status === 'Running'}
+ handleStop()}
+ size="sm"
+ class="bg-muted-foreground/40 dark:bg-muted h-6 text-black disabled:!pointer-events-auto disabled:hover:bg-neutral-600 dark:text-white"
+ >
+
+ {capitalizeFirstLetter(getTranslation('common.stop', 'Stop'))}
+
+ {/if}
+{/snippet}
+
+
+
+ {@render button('start')}
+ {@render button('stop')}
+
+
+
+
+
+
+
+ {vm.name} {udTime ? `(${udTime})` : ''}
+
+
+
+
+
+ {getTranslation('vm.stats', 'Status')}
+
+
+ {domain.status}
+
+
+
+
+
+
+
+ {getTranslation('summary.cpu_usage', 'CPU Usage')}
+
+
+ {floatToNDecimals(recentStat.cpuUsage, 2)}% {getTranslation('common.of', 'of')}
+ {vm.cpuCores * vm.cpuThreads * vm.cpuSockets}
+ vCPU(s)
+
+
+
+
+
+
+
+
+
+ {getTranslation('summary.ram_usage', 'RAM Usage')}
+
+
+ {floatToNDecimals(recentStat.memoryUsage, 2)}% {getTranslation('common.of', 'of')}
+ {humanFormat(vm.ram)}
+
+
+
+
+
+
+
+
+
+
+ Description
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{getTranslation('summary.cpu_usage', 'CPU Usage')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{getTranslation('summary.memory_usage', 'Memory Usage')}
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ handleDelete();
+ },
+ onCancel: () => {
+ modalState.isDeleteOpen = false;
+ }
+ }}
+>
+
+
diff --git a/new-web/src/routes/[node]/vm/[node]/summary/+page.ts b/new-web/src/routes/[node]/vm/[node]/summary/+page.ts
new file mode 100644
index 00000000..35efdccd
--- /dev/null
+++ b/new-web/src/routes/[node]/vm/[node]/summary/+page.ts
@@ -0,0 +1,20 @@
+import { getStats, getVMDomain, getVMs } from '$lib/api/vm/vm';
+import { SEVEN_DAYS } from '$lib/utils.js';
+import { cachedFetch } from '$lib/utils/http';
+
+export async function load({ params }) {
+ const cacheDuration = SEVEN_DAYS;
+ const vmId = params.node;
+
+ const [vms, domain, stats] = await Promise.all([
+ cachedFetch('vm-list', async () => getVMs(), cacheDuration),
+ cachedFetch(`vm-domain-${vmId}`, async () => getVMDomain(Number(vmId)), cacheDuration),
+ cachedFetch(`vm-stats-${vmId}`, async () => getStats(Number(vmId), 10), cacheDuration)
+ ]);
+
+ return {
+ vms: vms,
+ domain: domain,
+ stats: stats
+ };
+}
diff --git a/new-web/static/logo/black.svg b/new-web/static/logo/black.svg
new file mode 100644
index 00000000..73e260d9
--- /dev/null
+++ b/new-web/static/logo/black.svg
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/new-web/static/logo/index.html b/new-web/static/logo/index.html
new file mode 100644
index 00000000..76c517a7
--- /dev/null
+++ b/new-web/static/logo/index.html
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/new-web/static/logo/white.svg b/new-web/static/logo/white.svg
new file mode 100644
index 00000000..7575b4d6
--- /dev/null
+++ b/new-web/static/logo/white.svg
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/tailwind.config.ts b/web/tailwind.config.ts
index 90baeee1..6aacb4f0 100644
--- a/web/tailwind.config.ts
+++ b/web/tailwind.config.ts
@@ -3,64 +3,64 @@ import type { Config } from 'tailwindcss';
import { fontFamily } from 'tailwindcss/defaultTheme';
const config: Config = {
- darkMode: ['class'],
- content: ['./src/**/*.{html,js,svelte,ts}', './node_modules/layerchart/**/*.{svelte,js}'],
- safelist: ['dark', 'prose', 'prose-xl'],
- theme: {
- container: {
- center: true,
- padding: '2rem',
- screens: {
- '2xl': '1400px'
- }
- },
- extend: {
- colors: {
- border: 'hsl(var(--border) / )',
- input: 'hsl(var(--input) / )',
- ring: 'hsl(var(--ring) / )',
- background: 'hsl(var(--background) / )',
- foreground: 'hsl(var(--foreground) / )',
- primary: {
- DEFAULT: 'hsl(var(--primary) / )',
- foreground: 'hsl(var(--primary-foreground) / )'
- },
- secondary: {
- DEFAULT: 'hsl(var(--secondary) / )',
- foreground: 'hsl(var(--secondary-foreground) / )'
- },
- destructive: {
- DEFAULT: 'hsl(var(--destructive) / )',
- foreground: 'hsl(var(--destructive-foreground) / )'
- },
- muted: {
- DEFAULT: 'hsl(var(--muted) / )',
- foreground: 'hsl(var(--muted-foreground) / )'
- },
- accent: {
- DEFAULT: 'hsl(var(--accent) / )',
- foreground: 'hsl(var(--accent-foreground) / )'
- },
- popover: {
- DEFAULT: 'hsl(var(--popover) / )',
- foreground: 'hsl(var(--popover-foreground) / )'
- },
- card: {
- DEFAULT: 'hsl(var(--card) / )',
- foreground: 'hsl(var(--card-foreground) / )'
- }
- },
- borderRadius: {
- lg: 'var(--radius)',
- md: 'calc(var(--radius) - 2px)',
- sm: 'calc(var(--radius) - 4px)'
- },
- fontFamily: {
- sans: [...fontFamily.sans]
- }
- },
- plugins: [typographyPlugin]
- }
+ darkMode: ['class'],
+ content: ['./src/**/*.{html,js,svelte,ts}', './node_modules/layerchart/**/*.{svelte,js}'],
+ safelist: ['dark', 'prose', 'prose-xl'],
+ theme: {
+ container: {
+ center: true,
+ padding: '2rem',
+ screens: {
+ '2xl': '1400px'
+ }
+ },
+ extend: {
+ colors: {
+ border: 'hsl(var(--border) / )',
+ input: 'hsl(var(--input) / )',
+ ring: 'hsl(var(--ring) / )',
+ background: 'hsl(var(--background) / )',
+ foreground: 'hsl(var(--foreground) / )',
+ primary: {
+ DEFAULT: 'hsl(var(--primary) / )',
+ foreground: 'hsl(var(--primary-foreground) / )'
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary) / )',
+ foreground: 'hsl(var(--secondary-foreground) / )'
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive) / )',
+ foreground: 'hsl(var(--destructive-foreground) / )'
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted) / )',
+ foreground: 'hsl(var(--muted-foreground) / )'
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent) / )',
+ foreground: 'hsl(var(--accent-foreground) / )'
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover) / )',
+ foreground: 'hsl(var(--popover-foreground) / )'
+ },
+ card: {
+ DEFAULT: 'hsl(var(--card) / )',
+ foreground: 'hsl(var(--card-foreground) / )'
+ }
+ },
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)'
+ },
+ fontFamily: {
+ sans: [...fontFamily.sans]
+ }
+ },
+ plugins: [typographyPlugin]
+ }
};
export default config;