diff --git a/components.json b/components.json
new file mode 100644
index 00000000..2639828c
--- /dev/null
+++ b/components.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://shadcn-vue.com/schema.json",
+ "style": "new-york",
+ "typescript": false,
+ "tailwind": {
+ "config": "",
+ "css": "src/styles/globals.css",
+ "baseColor": "zinc",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "composables": "@/composables",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/package-lock.json b/package-lock.json
index 4eba3d8c..c1b6a9e6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,8 +7,14 @@
"name": "VRCX",
"license": "MIT",
"dependencies": {
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
"hazardous": "^0.3.0",
- "node-api-dotnet": "^0.9.18"
+ "lucide-vue-next": "^0.562.0",
+ "node-api-dotnet": "^0.9.18",
+ "reka-ui": "^2.7.0",
+ "tailwind-merge": "^3.4.0",
+ "tw-animate-css": "^1.4.0"
},
"devDependencies": {
"@electron/rebuild": "^4.0.2",
@@ -23,6 +29,7 @@
"@sentry/vite-plugin": "^4.6.1",
"@sentry/vue": "^10.32.1",
"@tailwindcss/vite": "^4.1.18",
+ "@tanstack/vue-table": "^8.21.3",
"@types/jest": "^30.0.0",
"@types/node": "^25.0.3",
"@vitejs/plugin-vue": "^6.0.3",
@@ -227,7 +234,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -237,7 +243,6 @@
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -271,7 +276,6 @@
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.5"
@@ -587,7 +591,6 @@
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -2317,7 +2320,6 @@
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
@@ -2327,7 +2329,6 @@
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.3",
@@ -2338,9 +2339,45 @@
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/@floating-ui/vue": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.9.tgz",
+ "integrity": "sha512-BfNqNW6KA83Nexspgb9DZuz578R7HT8MZw1CfK9I6Ah4QReNWEJsXWHN+SdmOVLNGmTPDi+fDT535Df5PzMLbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.7.4",
+ "@floating-ui/utils": "^0.2.10",
+ "vue-demi": ">=0.13.0"
+ }
+ },
+ "node_modules/@floating-ui/vue/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@fontsource-variable/inter": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.2.8.tgz",
@@ -2450,6 +2487,24 @@
"url": "https://github.com/sponsors/nzakas"
}
},
+ "node_modules/@internationalized/date": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.1.tgz",
+ "integrity": "sha512-oJrXtQiAXLvT9clCf1K4kxp3eKsQhIaZqxEyowkBcsvZDdZkbWrVmnGknxs5flTD0VGsxrxKgBCZty1EzoiMzA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@internationalized/number": {
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.5.tgz",
+ "integrity": "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
"node_modules/@intlify/core-base": {
"version": "11.2.8",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.2.8.tgz",
@@ -3840,7 +3895,6 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
@@ -5126,6 +5180,21 @@
"@sinonjs/commons": "^3.0.1"
}
},
+ "node_modules/@swc/helpers": {
+ "version": "0.5.18",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz",
+ "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@swc/helpers/node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
@@ -5421,6 +5490,66 @@
"vite": "^5.2.0 || ^6 || ^7"
}
},
+ "node_modules/@tanstack/table-core": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
+ "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.13.14",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.14.tgz",
+ "integrity": "sha512-b5Uvd8J2dc7ICeX9SRb/wkCxWk7pUwN214eEPAQsqrsktSKTCmyLxOQWSMgogBByXclZeAdgZ3k4o0fIYUIBqQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/vue-table": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/vue-table/-/vue-table-8.21.3.tgz",
+ "integrity": "sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/table-core": "8.21.3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "vue": ">=3.2"
+ }
+ },
+ "node_modules/@tanstack/vue-virtual": {
+ "version": "3.13.14",
+ "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.14.tgz",
+ "integrity": "sha512-dLKQCWj0uu6Rc1OsTGiClpH75hyf92MvJ9YALAzWdblwImSFnxfXD0mu8yOI7PlxiDAcDA5Pq0Q47YvADAfyfg==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.13.14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "vue": "^2.7.0 || ^3.0.0"
+ }
+ },
"node_modules/@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -5690,7 +5819,6 @@
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
- "dev": true,
"license": "MIT"
},
"node_modules/@types/yargs": {
@@ -6032,7 +6160,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz",
"integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
@@ -6046,7 +6173,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz",
"integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.26",
@@ -6057,7 +6183,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz",
"integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
@@ -6075,7 +6200,6 @@
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
@@ -6085,7 +6209,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz",
"integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.26",
@@ -6132,7 +6255,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.26.tgz",
"integrity": "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.26"
@@ -6142,7 +6264,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.26.tgz",
"integrity": "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.26",
@@ -6153,7 +6274,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.26.tgz",
"integrity": "sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.26",
@@ -6166,7 +6286,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.26.tgz",
"integrity": "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.26",
@@ -6180,7 +6299,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz",
"integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==",
- "dev": true,
"license": "MIT"
},
"node_modules/@vueuse/core": {
@@ -6634,6 +6752,18 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/aria-hidden": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
+ "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -7543,6 +7673,18 @@
"node": ">= 0.4"
}
},
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
"node_modules/clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -7691,6 +7833,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -8056,7 +8207,6 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/dayjs": {
@@ -8230,6 +8380,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/defu": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
+ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
+ "license": "MIT"
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -8935,7 +9091,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz",
"integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -9385,7 +9540,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true,
"license": "MIT"
},
"node_modules/esutils": {
@@ -13984,6 +14138,15 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lucide-vue-next": {
+ "version": "0.562.0",
+ "resolved": "https://registry.npmjs.org/lucide-vue-next/-/lucide-vue-next-0.562.0.tgz",
+ "integrity": "sha512-LN0BLGKMFulv0lnfK29r14DcngRUhIqdcaL0zXTt2o0oS9odlrjCGaU3/X9hIihOjjN8l8e+Y9G/famcNYaI7Q==",
+ "license": "ISC",
+ "peerDependencies": {
+ "vue": ">=3.0.1"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.8",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
@@ -14401,7 +14564,6 @@
"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",
@@ -14811,6 +14973,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/ohash": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
+ "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
+ "license": "MIT"
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -15113,7 +15281,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -15260,7 +15427,6 @@
"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",
@@ -15565,6 +15731,63 @@
"node": ">=0.10.0"
}
},
+ "node_modules/reka-ui": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.7.0.tgz",
+ "integrity": "sha512-m+XmxQN2xtFzBP3OAdIafKq7C8OETo2fqfxcIIxYmNN2Ch3r5oAf6yEYCIJg5tL/yJU2mHqF70dCCekUkrAnXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.13",
+ "@floating-ui/vue": "^1.1.6",
+ "@internationalized/date": "^3.5.0",
+ "@internationalized/number": "^3.5.0",
+ "@tanstack/vue-virtual": "^3.12.0",
+ "@vueuse/core": "^12.5.0",
+ "@vueuse/shared": "^12.5.0",
+ "aria-hidden": "^1.2.4",
+ "defu": "^6.1.4",
+ "ohash": "^2.0.11"
+ },
+ "peerDependencies": {
+ "vue": ">= 3.2.0"
+ }
+ },
+ "node_modules/reka-ui/node_modules/@vueuse/core": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz",
+ "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/web-bluetooth": "^0.0.21",
+ "@vueuse/metadata": "12.8.2",
+ "@vueuse/shared": "12.8.2",
+ "vue": "^3.5.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/reka-ui/node_modules/@vueuse/metadata": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz",
+ "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/reka-ui/node_modules/@vueuse/shared": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz",
+ "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==",
+ "license": "MIT",
+ "dependencies": {
+ "vue": "^3.5.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/remixicon": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/remixicon/-/remixicon-4.8.0.tgz",
@@ -17033,7 +17256,6 @@
"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"
@@ -17476,6 +17698,16 @@
"url": "https://opencollective.com/synckit"
}
},
+ "node_modules/tailwind-merge": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+ "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
"node_modules/tailwindcss": {
"version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
@@ -17893,9 +18125,17 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
- "dev": true,
"license": "0BSD"
},
+ "node_modules/tw-animate-css": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
+ "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Wombosvideo"
+ }
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -17946,7 +18186,7 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
@@ -18400,7 +18640,6 @@
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz",
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
- "dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
diff --git a/package.json b/package.json
index b81ed436..a775cbf2 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"@sentry/vite-plugin": "^4.6.1",
"@sentry/vue": "^10.32.1",
"@tailwindcss/vite": "^4.1.18",
+ "@tanstack/vue-table": "^8.21.3",
"@types/jest": "^30.0.0",
"@types/node": "^25.0.3",
"@vitejs/plugin-vue": "^6.0.3",
@@ -164,7 +165,13 @@
}
},
"dependencies": {
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
"hazardous": "^0.3.0",
- "node-api-dotnet": "^0.9.18"
+ "lucide-vue-next": "^0.562.0",
+ "node-api-dotnet": "^0.9.18",
+ "reka-ui": "^2.7.0",
+ "tailwind-merge": "^3.4.0",
+ "tw-animate-css": "^1.4.0"
}
}
diff --git a/src/app.css b/src/app.css
index e3f93df6..7743e26b 100644
--- a/src/app.css
+++ b/src/app.css
@@ -15,10 +15,10 @@
@import 'noty/lib/noty.css';
@import 'remixicon/fonts/remixicon.css';
-@import './assets/scss/flags.css';
-@import './assets/scss/animated-emoji.css';
-@import './assets/scss/fonts.css';
-@import './assets/scss/noty.css';
+@import './styles/flags.css';
+@import './styles/animated-emoji.css';
+@import './styles/fonts.css';
+@import './styles/noty.css';
:root {
--font-western:
diff --git a/src/components/ui/button/Button.vue b/src/components/ui/button/Button.vue
new file mode 100644
index 00000000..8abe4776
--- /dev/null
+++ b/src/components/ui/button/Button.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/button/index.js b/src/components/ui/button/index.js
new file mode 100644
index 00000000..bbb3f1f8
--- /dev/null
+++ b/src/components/ui/button/index.js
@@ -0,0 +1,35 @@
+import { cva } from "class-variance-authority";
+
+export { default as Button } from "./Button.vue";
+
+export const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ icon: "size-9",
+ "icon-sm": "size-8",
+ "icon-lg": "size-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ },
+);
diff --git a/src/components/ui/dropdown-menu/DropdownMenu.vue b/src/components/ui/dropdown-menu/DropdownMenu.vue
new file mode 100644
index 00000000..00bc158f
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenu.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue b/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue
new file mode 100644
index 00000000..31aa8638
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuContent.vue b/src/components/ui/dropdown-menu/DropdownMenuContent.vue
new file mode 100644
index 00000000..e8bb882a
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuContent.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuGroup.vue b/src/components/ui/dropdown-menu/DropdownMenuGroup.vue
new file mode 100644
index 00000000..3895eaa4
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuGroup.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuItem.vue b/src/components/ui/dropdown-menu/DropdownMenuItem.vue
new file mode 100644
index 00000000..06746819
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuItem.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuLabel.vue b/src/components/ui/dropdown-menu/DropdownMenuLabel.vue
new file mode 100644
index 00000000..83891183
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuLabel.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue b/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue
new file mode 100644
index 00000000..a21b0e4b
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue b/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue
new file mode 100644
index 00000000..b467428d
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue b/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue
new file mode 100644
index 00000000..1afca359
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue b/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue
new file mode 100644
index 00000000..07b92ec2
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuSub.vue b/src/components/ui/dropdown-menu/DropdownMenuSub.vue
new file mode 100644
index 00000000..4063d3d3
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuSub.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue b/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue
new file mode 100644
index 00000000..8db0cb66
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue b/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue
new file mode 100644
index 00000000..991feeee
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue b/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue
new file mode 100644
index 00000000..0b693a50
--- /dev/null
+++ b/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/dropdown-menu/index.js b/src/components/ui/dropdown-menu/index.js
new file mode 100644
index 00000000..9e57848a
--- /dev/null
+++ b/src/components/ui/dropdown-menu/index.js
@@ -0,0 +1,16 @@
+export { default as DropdownMenu } from "./DropdownMenu.vue";
+
+export { default as DropdownMenuCheckboxItem } from "./DropdownMenuCheckboxItem.vue";
+export { default as DropdownMenuContent } from "./DropdownMenuContent.vue";
+export { default as DropdownMenuGroup } from "./DropdownMenuGroup.vue";
+export { default as DropdownMenuItem } from "./DropdownMenuItem.vue";
+export { default as DropdownMenuLabel } from "./DropdownMenuLabel.vue";
+export { default as DropdownMenuRadioGroup } from "./DropdownMenuRadioGroup.vue";
+export { default as DropdownMenuRadioItem } from "./DropdownMenuRadioItem.vue";
+export { default as DropdownMenuSeparator } from "./DropdownMenuSeparator.vue";
+export { default as DropdownMenuShortcut } from "./DropdownMenuShortcut.vue";
+export { default as DropdownMenuSub } from "./DropdownMenuSub.vue";
+export { default as DropdownMenuSubContent } from "./DropdownMenuSubContent.vue";
+export { default as DropdownMenuSubTrigger } from "./DropdownMenuSubTrigger.vue";
+export { default as DropdownMenuTrigger } from "./DropdownMenuTrigger.vue";
+export { DropdownMenuPortal } from "reka-ui";
diff --git a/src/components/ui/pagination/Pagination.vue b/src/components/ui/pagination/Pagination.vue
new file mode 100644
index 00000000..7ac2a36a
--- /dev/null
+++ b/src/components/ui/pagination/Pagination.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/pagination/PaginationContent.vue b/src/components/ui/pagination/PaginationContent.vue
new file mode 100644
index 00000000..61f78091
--- /dev/null
+++ b/src/components/ui/pagination/PaginationContent.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/pagination/PaginationEllipsis.vue b/src/components/ui/pagination/PaginationEllipsis.vue
new file mode 100644
index 00000000..48e04a64
--- /dev/null
+++ b/src/components/ui/pagination/PaginationEllipsis.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ More pages
+
+
+
diff --git a/src/components/ui/pagination/PaginationFirst.vue b/src/components/ui/pagination/PaginationFirst.vue
new file mode 100644
index 00000000..8fe158b2
--- /dev/null
+++ b/src/components/ui/pagination/PaginationFirst.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ First
+
+
+
diff --git a/src/components/ui/pagination/PaginationItem.vue b/src/components/ui/pagination/PaginationItem.vue
new file mode 100644
index 00000000..7a6b6ef3
--- /dev/null
+++ b/src/components/ui/pagination/PaginationItem.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/pagination/PaginationLast.vue b/src/components/ui/pagination/PaginationLast.vue
new file mode 100644
index 00000000..7c67efa1
--- /dev/null
+++ b/src/components/ui/pagination/PaginationLast.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+ Last
+
+
+
+
diff --git a/src/components/ui/pagination/PaginationNext.vue b/src/components/ui/pagination/PaginationNext.vue
new file mode 100644
index 00000000..40c1ee60
--- /dev/null
+++ b/src/components/ui/pagination/PaginationNext.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+ Next
+
+
+
+
diff --git a/src/components/ui/pagination/PaginationPrevious.vue b/src/components/ui/pagination/PaginationPrevious.vue
new file mode 100644
index 00000000..b682259d
--- /dev/null
+++ b/src/components/ui/pagination/PaginationPrevious.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ Previous
+
+
+
diff --git a/src/components/ui/pagination/index.js b/src/components/ui/pagination/index.js
new file mode 100644
index 00000000..349edff7
--- /dev/null
+++ b/src/components/ui/pagination/index.js
@@ -0,0 +1,8 @@
+export { default as Pagination } from "./Pagination.vue";
+export { default as PaginationContent } from "./PaginationContent.vue";
+export { default as PaginationEllipsis } from "./PaginationEllipsis.vue";
+export { default as PaginationFirst } from "./PaginationFirst.vue";
+export { default as PaginationItem } from "./PaginationItem.vue";
+export { default as PaginationLast } from "./PaginationLast.vue";
+export { default as PaginationNext } from "./PaginationNext.vue";
+export { default as PaginationPrevious } from "./PaginationPrevious.vue";
diff --git a/src/components/ui/table/Table.vue b/src/components/ui/table/Table.vue
new file mode 100644
index 00000000..3f558a2d
--- /dev/null
+++ b/src/components/ui/table/Table.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/src/components/ui/table/TableBody.vue b/src/components/ui/table/TableBody.vue
new file mode 100644
index 00000000..4de6afed
--- /dev/null
+++ b/src/components/ui/table/TableBody.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/table/TableCaption.vue b/src/components/ui/table/TableCaption.vue
new file mode 100644
index 00000000..a76cf856
--- /dev/null
+++ b/src/components/ui/table/TableCaption.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/table/TableCell.vue b/src/components/ui/table/TableCell.vue
new file mode 100644
index 00000000..b47a0da1
--- /dev/null
+++ b/src/components/ui/table/TableCell.vue
@@ -0,0 +1,20 @@
+
+
+
+ |
+
+ |
+
diff --git a/src/components/ui/table/TableEmpty.vue b/src/components/ui/table/TableEmpty.vue
new file mode 100644
index 00000000..1529f2ea
--- /dev/null
+++ b/src/components/ui/table/TableEmpty.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ui/table/TableFooter.vue b/src/components/ui/table/TableFooter.vue
new file mode 100644
index 00000000..a029cec1
--- /dev/null
+++ b/src/components/ui/table/TableFooter.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/table/TableHead.vue b/src/components/ui/table/TableHead.vue
new file mode 100644
index 00000000..ab406134
--- /dev/null
+++ b/src/components/ui/table/TableHead.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+ |
+
diff --git a/src/components/ui/table/TableHeader.vue b/src/components/ui/table/TableHeader.vue
new file mode 100644
index 00000000..826d60e1
--- /dev/null
+++ b/src/components/ui/table/TableHeader.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/table/TableRow.vue b/src/components/ui/table/TableRow.vue
new file mode 100644
index 00000000..4fcc52fa
--- /dev/null
+++ b/src/components/ui/table/TableRow.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/table/index.js b/src/components/ui/table/index.js
new file mode 100644
index 00000000..c9712165
--- /dev/null
+++ b/src/components/ui/table/index.js
@@ -0,0 +1,9 @@
+export { default as Table } from './Table.vue';
+export { default as TableBody } from './TableBody.vue';
+export { default as TableCaption } from './TableCaption.vue';
+export { default as TableCell } from './TableCell.vue';
+export { default as TableEmpty } from './TableEmpty.vue';
+export { default as TableFooter } from './TableFooter.vue';
+export { default as TableHead } from './TableHead.vue';
+export { default as TableHeader } from './TableHeader.vue';
+export { default as TableRow } from './TableRow.vue';
diff --git a/src/components/ui/table/utils.js b/src/components/ui/table/utils.js
new file mode 100644
index 00000000..e4c44618
--- /dev/null
+++ b/src/components/ui/table/utils.js
@@ -0,0 +1,7 @@
+import { isFunction } from '@tanstack/vue-table';
+
+export function valueUpdater(updaterOrValue, ref) {
+ ref.value = isFunction(updaterOrValue)
+ ? updaterOrValue(ref.value)
+ : updaterOrValue;
+}
diff --git a/src/components/ui/tooltip/Tooltip.vue b/src/components/ui/tooltip/Tooltip.vue
new file mode 100644
index 00000000..15c361bf
--- /dev/null
+++ b/src/components/ui/tooltip/Tooltip.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/tooltip/TooltipContent.vue b/src/components/ui/tooltip/TooltipContent.vue
new file mode 100644
index 00000000..a3df1fa4
--- /dev/null
+++ b/src/components/ui/tooltip/TooltipContent.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ui/tooltip/TooltipProvider.vue b/src/components/ui/tooltip/TooltipProvider.vue
new file mode 100644
index 00000000..e0e03430
--- /dev/null
+++ b/src/components/ui/tooltip/TooltipProvider.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/tooltip/TooltipTrigger.vue b/src/components/ui/tooltip/TooltipTrigger.vue
new file mode 100644
index 00000000..471be21b
--- /dev/null
+++ b/src/components/ui/tooltip/TooltipTrigger.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/src/components/ui/tooltip/index.js b/src/components/ui/tooltip/index.js
new file mode 100644
index 00000000..0a8a8786
--- /dev/null
+++ b/src/components/ui/tooltip/index.js
@@ -0,0 +1,4 @@
+export { default as Tooltip } from "./Tooltip.vue";
+export { default as TooltipContent } from "./TooltipContent.vue";
+export { default as TooltipProvider } from "./TooltipProvider.vue";
+export { default as TooltipTrigger } from "./TooltipTrigger.vue";
diff --git a/src/lib/utils.js b/src/lib/utils.js
new file mode 100644
index 00000000..a25ae45d
--- /dev/null
+++ b/src/lib/utils.js
@@ -0,0 +1,6 @@
+import { clsx } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs) {
+ return twMerge(clsx(inputs));
+}
diff --git a/src/assets/scss/animated-emoji.css b/src/styles/animated-emoji.css
similarity index 100%
rename from src/assets/scss/animated-emoji.css
rename to src/styles/animated-emoji.css
diff --git a/src/assets/scss/flags.css b/src/styles/flags.css
similarity index 100%
rename from src/assets/scss/flags.css
rename to src/styles/flags.css
diff --git a/src/assets/scss/fonts.css b/src/styles/fonts.css
similarity index 100%
rename from src/assets/scss/fonts.css
rename to src/styles/fonts.css
diff --git a/src/styles/globals.css b/src/styles/globals.css
new file mode 100644
index 00000000..4a684d7e
--- /dev/null
+++ b/src/styles/globals.css
@@ -0,0 +1,123 @@
+@import 'tailwindcss';
+@import 'tw-animate-css';
+
+@custom-variant dark (&:is(.dark *));
+
+:root {
+ --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);
+ --destructive-foreground: 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);
+ --radius: 0.625rem;
+ --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.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.145 0 0);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.985 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.396 0.141 25.723);
+ --destructive-foreground: oklch(0.637 0.237 25.331);
+ --border: oklch(0.269 0 0);
+ --input: oklch(0.269 0 0);
+ --ring: oklch(0.439 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(0.269 0 0);
+ --sidebar-ring: oklch(0.439 0 0);
+}
+
+@theme inline {
+ --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-destructive-foreground: var(--destructive-foreground);
+ --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);
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --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;
+ }
+}
diff --git a/src/assets/scss/noty.css b/src/styles/noty.css
similarity index 100%
rename from src/assets/scss/noty.css
rename to src/styles/noty.css
diff --git a/src/assets/scss/themes/_theme.dark_styles.scss b/src/styles/themes/_theme.dark_styles.scss
similarity index 100%
rename from src/assets/scss/themes/_theme.dark_styles.scss
rename to src/styles/themes/_theme.dark_styles.scss
diff --git a/src/assets/scss/themes/app_legacy.scss b/src/styles/themes/app_legacy.scss
similarity index 98%
rename from src/assets/scss/themes/app_legacy.scss
rename to src/styles/themes/app_legacy.scss
index 12008bf1..fa105e71 100644
--- a/src/assets/scss/themes/app_legacy.scss
+++ b/src/styles/themes/app_legacy.scss
@@ -11,12 +11,12 @@
@use '../fonts.css';
@use '../flags.css';
@use '../animated-emoji.css';
-@use 'element-plus/theme-chalk/src/index.scss' as *;
+@use 'index' as *;
-@use 'element-plus/theme-chalk/src/dark/css-vars.scss';
-@use 'animate.css/animate.min.css';
-@use 'noty/lib/noty.css';
-@use 'remixicon/fonts/remixicon.css';
+@use 'css-vars';
+@use '../../../node_modules/animate.css/animate.min.css';
+@use '../../../node_modules/noty/lib/noty.css';
+@use '../../../node_modules/remixicon/fonts/remixicon.css';
:root {
--font-western:
diff --git a/src/assets/scss/themes/theme.amoled.scss b/src/styles/themes/theme.amoled.scss
similarity index 99%
rename from src/assets/scss/themes/theme.amoled.scss
rename to src/styles/themes/theme.amoled.scss
index b1ed9c78..e6749d84 100644
--- a/src/assets/scss/themes/theme.amoled.scss
+++ b/src/styles/themes/theme.amoled.scss
@@ -59,7 +59,7 @@ $--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
-@forward 'element-plus/theme-chalk/src/common/var.scss' with (
+@forward 'var' with (
$colors: (
'white': $--theme-text-1,
'black': $--theme-bg-1,
@@ -217,7 +217,7 @@ $--card-background-color: $--theme-bg-4;
--group-calendar-badge-normal: #{$--theme-info};
}
-@import '_theme.dark_styles';
+@import 'theme.dark_styles';
.el-table tr,
.el-table td.el-table__cell,
diff --git a/src/assets/scss/themes/theme.dark.scss b/src/styles/themes/theme.dark.scss
similarity index 98%
rename from src/assets/scss/themes/theme.dark.scss
rename to src/styles/themes/theme.dark.scss
index 776b1c68..33aa8dd5 100644
--- a/src/assets/scss/themes/theme.dark.scss
+++ b/src/styles/themes/theme.dark.scss
@@ -56,7 +56,7 @@ $--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
-@forward 'element-plus/theme-chalk/src/common/var.scss' with (
+@forward 'var' with (
$colors: (
'white': $--theme-text-1,
'black': $--theme-bg-1,
@@ -211,7 +211,7 @@ $--card-background-color: $--theme-bg-4;
--group-calendar-badge-normal: #{$--color-primary};
}
-@import '_theme.dark_styles';
+@import 'theme.dark_styles';
.el-calendar {
background-color: $--theme-bg-4 !important;
diff --git a/src/assets/scss/themes/theme.darkblue.scss b/src/styles/themes/theme.darkblue.scss
similarity index 98%
rename from src/assets/scss/themes/theme.darkblue.scss
rename to src/styles/themes/theme.darkblue.scss
index 52034eb2..12a1d902 100644
--- a/src/assets/scss/themes/theme.darkblue.scss
+++ b/src/styles/themes/theme.darkblue.scss
@@ -56,7 +56,7 @@ $--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
-@forward 'element-plus/theme-chalk/src/common/var.scss' with (
+@forward 'var' with (
$colors: (
'white': $--theme-text-1,
'black': $--theme-bg-1,
@@ -211,7 +211,7 @@ $--card-background-color: $--theme-bg-4;
--group-calendar-badge-normal: #{$--color-primary};
}
-@import '_theme.dark_styles';
+@import 'theme.dark_styles';
.el-calendar {
background-color: $--theme-bg-4 !important;
diff --git a/src/assets/scss/themes/theme.darkvanilla.scss b/src/styles/themes/theme.darkvanilla.scss
similarity index 100%
rename from src/assets/scss/themes/theme.darkvanilla.scss
rename to src/styles/themes/theme.darkvanilla.scss
diff --git a/src/assets/scss/themes/theme.darkvanillaold.scss b/src/styles/themes/theme.darkvanillaold.scss
similarity index 100%
rename from src/assets/scss/themes/theme.darkvanillaold.scss
rename to src/styles/themes/theme.darkvanillaold.scss
diff --git a/src/assets/scss/themes/theme.material3.scss b/src/styles/themes/theme.material3.scss
similarity index 100%
rename from src/assets/scss/themes/theme.material3.scss
rename to src/styles/themes/theme.material3.scss
diff --git a/src/assets/scss/themes/theme.pink.scss b/src/styles/themes/theme.pink.scss
similarity index 100%
rename from src/assets/scss/themes/theme.pink.scss
rename to src/styles/themes/theme.pink.scss
diff --git a/src/vite.config.js b/src/vite.config.js
index 0cd82c23..99609f9d 100644
--- a/src/vite.config.js
+++ b/src/vite.config.js
@@ -82,6 +82,11 @@ export default defineConfig(({ mode }) => {
})
)
],
+ resolve: {
+ alias: {
+ '@': resolve(import.meta.dirname, '.')
+ }
+ },
css: {
transformer: 'lightningcss',
lightningcss: {
diff --git a/src/vr/vr.css b/src/vr/vr.css
index b217802f..23d85c89 100644
--- a/src/vr/vr.css
+++ b/src/vr/vr.css
@@ -12,8 +12,8 @@
@import 'noty/lib/noty.css';
@import 'remixicon/fonts/remixicon.css';
-@import '../assets/scss/flags.css';
-@import '../assets/scss/fonts.css';
+@import '../styles/flags.css';
+@import '../styles/fonts.css';
/*
마지노선인듯
diff --git a/src/jsconfig.json b/tsconfig.app.json
similarity index 71%
rename from src/jsconfig.json
rename to tsconfig.app.json
index 859f3280..b9928e67 100644
--- a/src/jsconfig.json
+++ b/tsconfig.app.json
@@ -1,5 +1,6 @@
{
"compilerOptions": {
+ "composite": true,
"module": "ESNext",
"target": "ESNext",
"allowJs": true,
@@ -13,8 +14,12 @@
"lib": ["esnext", "dom", "dom.iterable"],
"types": ["vite/client", "element-plus/global"],
"resolveJsonModule": true,
- "noEmit": true
+ "noEmit": true,
+ "paths": {
+ "*": ["./*"],
+ "@/*": ["./src/*"]
+ }
},
- "include": ["**/*"],
- "exclude": ["../node_modules", "../build", "vite.config.js", "shared/utils/localizationHelperCLI.js"]
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "build"]
}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..82aca16a
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.app.json"
+ },
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ],
+ "compilerOptions": {
+ "paths": {
+ "*": ["./*"],
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/jsconfig.json b/tsconfig.node.json
similarity index 64%
rename from jsconfig.json
rename to tsconfig.node.json
index 6765f276..deb5ef4d 100644
--- a/jsconfig.json
+++ b/tsconfig.node.json
@@ -1,5 +1,6 @@
{
"compilerOptions": {
+ "composite": true,
"module": "ESNext",
"target": "ESNext",
"allowJs": true,
@@ -11,8 +12,15 @@
"forceConsistentCasingInFileNames": true,
"lib": ["esnext"],
"types": ["node"],
- "noEmit": true
+ "noEmit": true,
+ "paths": {
+ "*": ["./*"],
+ "@/*": ["./src/*"]
+ }
},
- "include": ["src/vite.config.js", "src/shared/utils/localizationHelperCLI.js"],
+ "include": [
+ "src/vite.config.js",
+ "src/shared/utils/localizationHelperCLI.js"
+ ],
"exclude": ["node_modules", "build"]
}