diff --git a/ui/package-lock.json b/ui/package-lock.json index e2026cf..428137d 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,17 +8,15 @@ "name": "ui", "version": "0.0.0", "dependencies": { - "@tailwindcss/vite": "^4.1.8", - "@tanstack/react-query": "^5.80.6", "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", "react-resizable-panels": "^3.0.4", - "react-router-dom": "^7.6.2", - "tailwindcss": "^4.1.8" + "react-router-dom": "^7.6.2" }, "devDependencies": { "@eslint/js": "^9.25.0", + "@tailwindcss/vite": "^4.1.8", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@vitejs/plugin-react": "^4.4.1", @@ -26,6 +24,7 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", + "tailwindcss": "^4.1.8", "typescript": "~5.8.3", "typescript-eslint": "^8.30.1", "vite": "^6.3.5" @@ -35,6 +34,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -333,6 +333,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -349,6 +350,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -365,6 +367,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -381,6 +384,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -397,6 +401,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -413,6 +418,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -429,6 +435,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -445,6 +452,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -461,6 +469,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -477,6 +486,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -493,6 +503,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -509,6 +520,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -525,6 +537,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -541,6 +554,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -557,6 +571,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -573,6 +588,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -589,6 +605,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -605,6 +622,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -621,6 +639,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -637,6 +656,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -653,6 +673,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -669,6 +690,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -685,6 +707,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -701,6 +724,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -717,6 +741,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -950,6 +975,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.4" @@ -962,6 +988,7 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -976,6 +1003,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -985,6 +1013,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -994,12 +1023,14 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1058,6 +1089,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1071,6 +1103,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1084,6 +1117,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1097,6 +1131,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1110,6 +1145,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1123,6 +1159,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1136,6 +1173,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1149,6 +1187,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1162,6 +1201,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1175,6 +1215,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1188,6 +1229,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1201,6 +1243,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1214,6 +1257,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1227,6 +1271,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1240,6 +1285,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1253,6 +1299,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1266,6 +1313,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1279,6 +1327,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1292,6 +1341,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1305,6 +1355,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1315,6 +1366,7 @@ "version": "4.1.8", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.8.tgz", "integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==", + "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -1330,6 +1382,7 @@ "version": "4.1.8", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.8.tgz", "integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==", + "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -1361,6 +1414,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1377,6 +1431,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1393,6 +1448,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1409,6 +1465,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1425,6 +1482,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1441,6 +1499,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1457,6 +1516,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1473,6 +1533,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1489,6 +1550,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1513,6 +1575,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1534,6 +1597,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1550,6 +1614,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1563,6 +1628,7 @@ "version": "4.1.8", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.8.tgz", "integrity": "sha512-CQ+I8yxNV5/6uGaJjiuymgw0kEQiNKRinYbZXPdx1fk5WgiyReG0VaUx/Xq6aVNSUNJFzxm6o8FNKS5aMaim5A==", + "dev": true, "license": "MIT", "dependencies": { "@tailwindcss/node": "4.1.8", @@ -1573,32 +1639,6 @@ "vite": "^5.2.0 || ^6" } }, - "node_modules/@tanstack/query-core": { - "version": "5.80.6", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.80.6.tgz", - "integrity": "sha512-nl7YxT/TAU+VTf+e2zTkObGTyY8YZBMnbgeA1ee66lIVqzKlYursAII6z5t0e6rXgwUMJSV4dshBTNacNpZHbQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.80.6", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.80.6.tgz", - "integrity": "sha512-izX+5CnkpON3NQGcEm3/d7LfFQNo9ZpFtX2QsINgCYK9LT2VCIdi8D3bMaMSNhrAJCznRoAkFic76uvLroALBw==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.80.6" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2148,6 +2188,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -2247,6 +2288,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -2263,6 +2305,7 @@ "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2276,6 +2319,7 @@ "version": "0.25.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -2642,6 +2686,7 @@ "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, @@ -2692,6 +2737,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -2792,6 +2838,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -2892,6 +2939,7 @@ "version": "1.30.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -2923,6 +2971,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2943,6 +2992,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2963,6 +3013,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2983,6 +3034,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3003,6 +3055,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3023,6 +3076,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3043,6 +3097,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3063,6 +3118,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3083,6 +3139,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3103,6 +3160,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3153,6 +3211,7 @@ "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -3199,6 +3258,7 @@ "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" @@ -3208,6 +3268,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.1.2" @@ -3220,6 +3281,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, "license": "MIT", "bin": { "mkdirp": "dist/cjs/src/bin.js" @@ -3242,6 +3304,7 @@ "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", @@ -3357,6 +3420,7 @@ "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": { @@ -3376,6 +3440,7 @@ "version": "8.5.4", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", + "dev": true, "funding": [ { "type": "opencollective", @@ -3554,6 +3619,7 @@ "version": "4.42.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz", "integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.7" @@ -3593,6 +3659,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, "license": "MIT" }, "node_modules/run-parallel": { @@ -3668,6 +3735,7 @@ "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" @@ -3703,12 +3771,14 @@ "version": "4.1.8", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.8.tgz", "integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==", + "dev": true, "license": "MIT" }, "node_modules/tapable": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3718,6 +3788,7 @@ "version": "7.4.3", "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -3735,6 +3806,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -3744,6 +3816,7 @@ "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.4.4", @@ -3760,6 +3833,7 @@ "version": "6.4.5", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "dev": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -3774,6 +3848,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3903,6 +3978,7 @@ "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -3977,6 +4053,7 @@ "version": "6.4.5", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "dev": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -3991,6 +4068,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" diff --git a/ui/package.json b/ui/package.json index 6a68a0a..f0e18d4 100644 --- a/ui/package.json +++ b/ui/package.json @@ -4,23 +4,21 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "start": "vite", "build": "tsc -b && vite build --emptyOutDir", "lint": "eslint .", "preview": "vite preview" }, "dependencies": { - "@tailwindcss/vite": "^4.1.8", - "@tanstack/react-query": "^5.80.6", "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", "react-resizable-panels": "^3.0.4", - "react-router-dom": "^7.6.2", - "tailwindcss": "^4.1.8" + "react-router-dom": "^7.6.2" }, "devDependencies": { "@eslint/js": "^9.25.0", + "@tailwindcss/vite": "^4.1.8", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@vitejs/plugin-react": "^4.4.1", @@ -28,6 +26,7 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", + "tailwindcss": "^4.1.8", "typescript": "~5.8.3", "typescript-eslint": "^8.30.1", "vite": "^6.3.5" diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 12dc322..9842b7b 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,21 +1,14 @@ -import { useEffect, useCallback } from "react"; -import { BrowserRouter as Router, Routes, Route, Navigate, NavLink } from "react-router-dom"; -import { useTheme } from "./contexts/ThemeProvider"; +import { useEffect } from "react"; +import { Navigate, Route, BrowserRouter as Router, Routes } from "react-router-dom"; +import { Header } from "./components/Header"; import { useAPI } from "./contexts/APIProvider"; +import { useTheme } from "./contexts/ThemeProvider"; +import ActivityPage from "./pages/Activity"; import LogViewerPage from "./pages/LogViewer"; import ModelPage from "./pages/Models"; -import ActivityPage from "./pages/Activity"; -import ConnectionStatusIcon from "./components/ConnectionStatus"; -import { RiSunFill, RiMoonFill } from "react-icons/ri"; function App() { - const { isNarrow, toggleTheme, isDarkMode, appTitle, setAppTitle, setConnectionState } = useTheme(); - const handleTitleChange = useCallback( - (newTitle: string) => { - setAppTitle(newTitle.replace(/\n/g, "").trim().substring(0, 64) || "llama-swap"); - }, - [setAppTitle] - ); + const { setConnectionState } = useTheme(); const { connectionStatus } = useAPI(); @@ -27,42 +20,7 @@ function App() { return (
- +
diff --git a/ui/src/components/ConnectionStatus.tsx b/ui/src/components/ConnectionStatus.tsx index 0f0589c..52285b6 100644 --- a/ui/src/components/ConnectionStatus.tsx +++ b/ui/src/components/ConnectionStatus.tsx @@ -7,9 +7,9 @@ const ConnectionStatusIcon = () => { const eventStatusColor = useMemo(() => { switch (connectionStatus) { case "connected": - return "bg-green-500"; + return "bg-emerald-500"; case "connecting": - return "bg-yellow-500"; + return "bg-amber-500"; case "disconnected": default: return "bg-red-500"; diff --git a/ui/src/components/Header.tsx b/ui/src/components/Header.tsx new file mode 100644 index 0000000..425584f --- /dev/null +++ b/ui/src/components/Header.tsx @@ -0,0 +1,56 @@ +import { useCallback } from "react"; +import { RiMoonFill, RiSunFill } from "react-icons/ri"; +import { NavLink, type NavLinkRenderProps } from "react-router-dom"; +import { useTheme } from "../contexts/ThemeProvider"; +import ConnectionStatusIcon from "./ConnectionStatus"; + +export function Header() { + const { screenWidth, toggleTheme, isDarkMode, appTitle, setAppTitle } = useTheme(); + const handleTitleChange = useCallback( + (newTitle: string) => { + setAppTitle(newTitle.replace(/\n/g, "").trim().substring(0, 64) || "llama-swap"); + }, + [setAppTitle] + ); + + const navLinkClass = ({ isActive }: NavLinkRenderProps) => + `text-gray-600 hover:text-black dark:text-gray-300 dark:hover:text-gray-100 p-1 ${isActive ? "font-semibold" : ""}`; + + return ( +
+ {screenWidth !== "xs" && screenWidth !== "sm" && ( +

handleTitleChange(e.currentTarget.textContent || "(set title)")} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + handleTitleChange(e.currentTarget.textContent || "(set title)"); + e.currentTarget.blur(); + } + }} + > + {appTitle} +

+ )} + + + + Logs + + + Models + + + Activity + + + + +
+ ); +} diff --git a/ui/src/index.css b/ui/src/index.css index dd75759..3e684bd 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -93,6 +93,14 @@ @apply px-4; } + /* Tables */ + table th { + @apply p-2 font-semibold; + } + table td { + @apply p-2; + } + /* Navigation Header */ .navlink { @@ -122,7 +130,7 @@ /* Status Badges */ .status { - @apply inline-block px-2 py-1 text-xs font-medium rounded-full; + @apply inline-block px-2 py-1 text-xs font-medium rounded-lg; } .status--ready { @@ -140,7 +148,7 @@ /* Buttons */ .btn { - @apply bg-surface p-2 px-4 text-sm rounded-full border border-2 transition-colors duration-200 border-btn-border; + @apply bg-surface py-2 px-4 text-sm rounded-md border transition-colors duration-200 border-btn-border; } .btn:hover { diff --git a/ui/src/pages/Activity.tsx b/ui/src/pages/Activity.tsx index 772460e..d1faf03 100644 --- a/ui/src/pages/Activity.tsx +++ b/ui/src/pages/Activity.tsx @@ -43,47 +43,46 @@ const ActivityPage = () => { }, [metrics]); return ( -
-

Activity

+
+

Activity

- {metrics.length === 0 ? ( + {metrics.length === 0 && (

No metrics data available

- ) : ( -
+ )} + {metrics.length > 0 && ( +
- - - - - - + + + + + - - - - - + + + + {sortedMetrics.map((metric) => ( - - - - - - - - - - + + + + + + + + + + ))} diff --git a/ui/src/pages/LogViewer.tsx b/ui/src/pages/LogViewer.tsx index ea7002c..24bc340 100644 --- a/ui/src/pages/LogViewer.tsx +++ b/ui/src/pages/LogViewer.tsx @@ -14,8 +14,8 @@ import { useTheme } from "../contexts/ThemeProvider"; const LogViewer = () => { const { proxyLogs, upstreamLogs } = useAPI(); - const { isNarrow } = useTheme(); - const direction = isNarrow ? "vertical" : "horizontal"; + const { screenWidth } = useTheme(); + const direction = screenWidth === "xs" || screenWidth === "sm" ? "vertical" : "horizontal"; return ( @@ -115,19 +115,19 @@ export const LogPanel = ({ id, title, logData }: LogPanelProps) => { }, [filteredLogs]); return ( -
-
+
+

{title}

- - -
@@ -139,7 +139,7 @@ export const LogPanel = ({ id, title, logData }: LogPanelProps) => {
setFilterRegex(e.target.value)} @@ -151,7 +151,7 @@ export const LogPanel = ({ id, title, logData }: LogPanelProps) => {
)}
-
+
           {filteredLogs}
         
diff --git a/ui/src/pages/Models.tsx b/ui/src/pages/Models.tsx index e98614f..cd205bb 100644 --- a/ui/src/pages/Models.tsx +++ b/ui/src/pages/Models.tsx @@ -69,19 +69,27 @@ function ModelsPanel() {

Models

-
-
@@ -90,26 +98,27 @@ function ModelsPanel() {
IDTimeModel +
IDTimeModel Cached + Prompt GeneratedPrompt ProcessingGeneration SpeedDurationGeneratedPrompt ProcessingGeneration SpeedDuration
{metric.id + 1 /* un-zero index */}{formatRelativeTime(metric.timestamp)}{metric.model} - {metric.cache_tokens > 0 ? metric.cache_tokens.toLocaleString() : "-"} - {metric.input_tokens.toLocaleString()}{metric.output_tokens.toLocaleString()}{formatSpeed(metric.prompt_per_second)}{formatSpeed(metric.tokens_per_second)}{formatDuration(metric.duration_ms)}
{metric.id + 1 /* un-zero index */}{formatRelativeTime(metric.timestamp)}{metric.model}{metric.cache_tokens > 0 ? metric.cache_tokens.toLocaleString() : "-"}{metric.input_tokens.toLocaleString()}{metric.output_tokens.toLocaleString()}{formatSpeed(metric.prompt_per_second)}{formatSpeed(metric.tokens_per_second)}{formatDuration(metric.duration_ms)}
- - - - + + + + {filteredModels.map((model) => ( - - + - - ))} @@ -146,24 +155,26 @@ function StatsPanel() { return (
-
+
{showIdorName === "id" ? "Model ID" : "Name"}State
{showIdorName === "id" ? "Model ID" : "Name"}State
- +
+ {showIdorName === "id" ? model.id : model.name !== "" ? model.name : model.id} - {model.description !== "" && ( + + {!!model.description && (

{model.description}

)}
+ - {model.state} + + {model.state}
- - - - - - + + + + + + - - - + + + + - - +
RequestsProcessedGeneratedTokens/Sec
RequestsProcessedGeneratedTokens/Sec
{totalRequests} +
{totalRequests} {new Intl.NumberFormat().format(totalInputTokens)} + {new Intl.NumberFormat().format(totalOutputTokens)} {avgTokensPerSecond}{avgTokensPerSecond}