From 3c57f95b90b2659107cc7fe09b37554aac6ba52c Mon Sep 17 00:00:00 2001 From: josedario87 Date: Fri, 13 Feb 2026 18:00:54 -0600 Subject: [PATCH] feat: Add PWA support and CORS configuration - Configure VitePWA with manifest, icons and service worker - Add PwaInstallBanner component for install prompt in header - Enable CORS for z590.interno.com access - Use dynamic hostname for terminal WebSocket connection - Add generate-icons script with sharp for PWA icons - Fix theme-color to match header background --- frontend/index.html | 17 +- frontend/package-lock.json | 739 ++++++++++++++++++- frontend/package.json | 4 +- frontend/public/favicon.png | Bin 0 -> 1136 bytes frontend/public/icons/apple-touch-icon.png | Bin 0 -> 6150 bytes frontend/public/icons/icon-192.png | Bin 0 -> 6466 bytes frontend/public/icons/icon-512.png | Bin 0 -> 24673 bytes frontend/public/icons/icon-maskable-512.png | Bin 0 -> 24673 bytes frontend/public/icons/icon.svg | 18 + frontend/scripts/generate-icons.js | 43 ++ frontend/src/App.vue | 11 + frontend/src/components/FloatingTerminal.vue | 609 ++++++++------- frontend/src/components/PwaInstallBanner.vue | 218 ++++++ frontend/src/pages/TerminalPage.vue | 2 +- frontend/src/styles/main.css | 7 +- frontend/vite.config.ts | 39 +- server/services/terminal.ts | 13 +- 17 files changed, 1438 insertions(+), 282 deletions(-) create mode 100644 frontend/public/favicon.png create mode 100644 frontend/public/icons/apple-touch-icon.png create mode 100644 frontend/public/icons/icon-192.png create mode 100644 frontend/public/icons/icon-512.png create mode 100644 frontend/public/icons/icon-maskable-512.png create mode 100644 frontend/public/icons/icon.svg create mode 100644 frontend/scripts/generate-icons.js create mode 100644 frontend/src/components/PwaInstallBanner.vue diff --git a/frontend/index.html b/frontend/index.html index 5c47060..a3702d1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,10 +2,21 @@ + + + + + + + + + + + - - - + + + Agent UI diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d0fc354..d464865 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,6 +21,7 @@ "@types/node": "^24.10.1", "@vitejs/plugin-vue": "^6.0.2", "@vue/tsconfig": "^0.8.1", + "sharp": "^0.34.5", "typescript": "~5.9.3", "vite": "^7.3.1", "vue-tsc": "^3.1.5" @@ -1295,6 +1296,17 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/win32-x64": { "version": "0.27.3", "cpu": [ @@ -1311,6 +1323,8 @@ }, "node_modules/@hono/node-server": { "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -1319,6 +1333,496 @@ "hono": "^4" } }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "9.0.0", "license": "BlueOak-1.0.0", @@ -1371,6 +1875,8 @@ }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", + "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9", @@ -1409,7 +1915,7 @@ }, "node_modules/@nucleoriofrio/webmcp": { "version": "0.2.0", - "resolved": "git+https://gitea.nucleoriofrio.com/nucleo000/webmcp.git#d5a912be97dbcf49adf5dc055fd437d5653ef5d0", + "resolved": "git+https://gitea.nucleoriofrio.com/nucleo000/webmcp.git#cec5be355d67e0cf9049380ece74e9eac0e85f5e", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.6.1", @@ -2129,6 +2635,8 @@ }, "node_modules/accepts": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -2164,6 +2672,8 @@ }, "node_modules/ajv-formats": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -2305,6 +2815,8 @@ }, "node_modules/body-parser": { "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -2372,6 +2884,8 @@ }, "node_modules/bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2438,6 +2952,8 @@ }, "node_modules/child_process": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==", "license": "ISC" }, "node_modules/commander": { @@ -2453,6 +2969,8 @@ }, "node_modules/content-disposition": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", "engines": { "node": ">=18" @@ -2464,6 +2982,8 @@ }, "node_modules/content-type": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2475,6 +2995,8 @@ }, "node_modules/cookie": { "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2482,6 +3004,8 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "engines": { "node": ">=6.6.0" @@ -2513,6 +3037,8 @@ }, "node_modules/cors": { "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -2540,6 +3066,9 @@ }, "node_modules/crypto": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", "license": "ISC" }, "node_modules/crypto-random-string": { @@ -2652,13 +3181,27 @@ }, "node_modules/depd": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dotenv": { "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -2681,6 +3224,8 @@ }, "node_modules/ee-first": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/ejs": { @@ -2702,6 +3247,8 @@ }, "node_modules/encodeurl": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2719,6 +3266,8 @@ }, "node_modules/env-paths": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -2893,6 +3442,8 @@ }, "node_modules/escape-html": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/estree-walker": { @@ -2908,6 +3459,8 @@ }, "node_modules/etag": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2915,6 +3468,8 @@ }, "node_modules/eventsource": { "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.1" @@ -2925,6 +3480,8 @@ }, "node_modules/eventsource-parser": { "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -2932,6 +3489,8 @@ }, "node_modules/express": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -2973,6 +3532,8 @@ }, "node_modules/express-rate-limit": { "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", "license": "MIT", "dependencies": { "ip-address": "10.0.1" @@ -3054,6 +3615,8 @@ }, "node_modules/finalhandler": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -3100,6 +3663,8 @@ }, "node_modules/forwarded": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3107,6 +3672,8 @@ }, "node_modules/fresh": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3354,6 +3921,8 @@ }, "node_modules/hono": { "version": "4.11.9", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", + "integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -3364,10 +3933,14 @@ "license": "MIT" }, "node_modules/http": { - "version": "0.0.1-security" + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", + "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" }, "node_modules/http-errors": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -3384,12 +3957,10 @@ "url": "https://opencollective.com/express" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, "node_modules/iconv-lite": { "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -3407,7 +3978,9 @@ "license": "ISC" }, "node_modules/inherits": { - "version": "2.0.3", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/internal-slot": { @@ -3424,6 +3997,8 @@ }, "node_modules/ip-address": { "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", "license": "MIT", "engines": { "node": ">= 12" @@ -3431,6 +4006,8 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -3624,6 +4201,8 @@ }, "node_modules/is-promise": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/is-regex": { @@ -3809,6 +4388,8 @@ }, "node_modules/jose": { "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -3838,6 +4419,8 @@ }, "node_modules/json-schema-typed": { "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, "node_modules/json5": { @@ -3909,6 +4492,8 @@ }, "node_modules/media-typer": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3916,6 +4501,8 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { "node": ">=18" @@ -3926,6 +4513,8 @@ }, "node_modules/mime-db": { "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3933,6 +4522,8 @@ }, "node_modules/mime-types": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -3996,6 +4587,8 @@ }, "node_modules/negotiator": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4007,6 +4600,8 @@ }, "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" @@ -4049,6 +4644,8 @@ }, "node_modules/on-finished": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -4059,6 +4656,8 @@ }, "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -4066,6 +4665,8 @@ }, "node_modules/os": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", + "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==", "license": "MIT" }, "node_modules/own-keys": { @@ -4089,6 +4690,8 @@ }, "node_modules/parseurl": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4096,6 +4699,8 @@ }, "node_modules/path": { "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", "license": "MIT", "dependencies": { "process": "^0.11.1", @@ -4141,6 +4746,8 @@ }, "node_modules/path-to-regexp": { "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", "funding": { "type": "opencollective", @@ -4186,6 +4793,8 @@ }, "node_modules/pkce-challenge": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -4236,6 +4845,8 @@ }, "node_modules/process": { "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -4243,6 +4854,8 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -4254,10 +4867,14 @@ }, "node_modules/punycode": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "license": "MIT" }, "node_modules/qs": { "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -4278,6 +4895,8 @@ }, "node_modules/range-parser": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4285,6 +4904,8 @@ }, "node_modules/raw-body": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -4423,6 +5044,8 @@ }, "node_modules/router": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -4501,6 +5124,8 @@ }, "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": { @@ -4512,6 +5137,8 @@ }, "node_modules/send": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -4543,6 +5170,8 @@ }, "node_modules/serve-static": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -4600,8 +5229,68 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "license": "MIT", @@ -4745,6 +5434,8 @@ }, "node_modules/statuses": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4930,6 +5621,8 @@ }, "node_modules/toidentifier": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -4949,6 +5642,14 @@ "node": ">=6" } }, + "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==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/type-fest": { "version": "0.16.0", "license": "(MIT OR CC0-1.0)", @@ -4961,6 +5662,8 @@ }, "node_modules/type-is": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -5121,6 +5824,8 @@ }, "node_modules/unpipe": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5164,6 +5869,8 @@ }, "node_modules/url": { "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", "license": "MIT", "dependencies": { "punycode": "^1.4.1", @@ -5175,13 +5882,23 @@ }, "node_modules/util": { "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", "license": "MIT", "dependencies": { "inherits": "2.0.3" } }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, "node_modules/vary": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5665,10 +6382,14 @@ }, "node_modules/wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -5692,6 +6413,8 @@ }, "node_modules/zod": { "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -5699,6 +6422,8 @@ }, "node_modules/zod-to-json-schema": { "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "license": "ISC", "peerDependencies": { "zod": "^3.25 || ^4" diff --git a/frontend/package.json b/frontend/package.json index 998c558..51b3df5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,8 @@ "predev": "npm install @nucleoriofrio/webmcp@git+https://gitea.nucleoriofrio.com/nucleo000/webmcp.git --silent", "dev": "vite", "build": "vue-tsc -b && vite build", - "preview": "vite preview" + "preview": "vite preview", + "generate-icons": "node scripts/generate-icons.js" }, "dependencies": { "@nucleoriofrio/webmcp": "git+https://gitea.nucleoriofrio.com/nucleo000/webmcp.git", @@ -23,6 +24,7 @@ "@types/node": "^24.10.1", "@vitejs/plugin-vue": "^6.0.2", "@vue/tsconfig": "^0.8.1", + "sharp": "^0.34.5", "typescript": "~5.9.3", "vite": "^7.3.1", "vue-tsc": "^3.1.5" diff --git a/frontend/public/favicon.png b/frontend/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..21fda7f4859486e8a2a8889dadeb8ae0c34f0e9d GIT binary patch literal 1136 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 K-H+Pv=>Gw~2-Z3P literal 0 HcmV?d00001 diff --git a/frontend/public/icons/icon-192.png b/frontend/public/icons/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..d7bcdfe5acb476a70828282aefd5e44f22abf798 GIT binary patch literal 6466 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 literal 0 HcmV?d00001 diff --git a/frontend/public/icons/icon-512.png b/frontend/public/icons/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..9b8ac682abc99d832ee91b205b2842b5b86835a6 GIT binary patch literal 24673 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 + + + + + + + + + + + + + + + + + diff --git a/frontend/scripts/generate-icons.js b/frontend/scripts/generate-icons.js new file mode 100644 index 0000000..de0a3a3 --- /dev/null +++ b/frontend/scripts/generate-icons.js @@ -0,0 +1,43 @@ +import sharp from 'sharp' +import { readFileSync, mkdirSync } from 'fs' +import { dirname, join } from 'path' +import { fileURLToPath } from 'url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const iconsDir = join(__dirname, '../public/icons') + +// Read SVG +const svgBuffer = readFileSync(join(iconsDir, 'icon.svg')) + +// Generate icons +const sizes = [ + { name: 'icon-192.png', size: 192 }, + { name: 'icon-512.png', size: 512 }, + { name: 'icon-maskable-512.png', size: 512 } +] + +async function generate() { + for (const { name, size } of sizes) { + await sharp(svgBuffer) + .resize(size, size) + .png() + .toFile(join(iconsDir, name)) + console.log(`Generated ${name}`) + } + + // Also generate apple-touch-icon + await sharp(svgBuffer) + .resize(180, 180) + .png() + .toFile(join(iconsDir, 'apple-touch-icon.png')) + console.log('Generated apple-touch-icon.png') + + // Favicon + await sharp(svgBuffer) + .resize(32, 32) + .png() + .toFile(join(__dirname, '../public/favicon.png')) + console.log('Generated favicon.png') +} + +generate().catch(console.error) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index a4c5459..2e087be 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -4,7 +4,10 @@ import { RouterView, useRoute, useRouter } from 'vue-router' import StatusBar from './components/StatusBar.vue' import Toolbar from './components/Toolbar.vue' import ComponentsDropdown from './components/ComponentsDropdown.vue' +import ToolsDropdown from './components/ToolsDropdown.vue' +import ConnectionDropdown from './components/ConnectionDropdown.vue' import FloatingTerminal from './components/FloatingTerminal.vue' +import PwaInstallBanner from './components/PwaInstallBanner.vue' import { initWebMCP, getWebMCP, startTokenPolling, stopTokenPolling, connectWithToken } from './services/webmcp' import { initToolRegistry, activatePageTools, initToolsOnRefresh } from './services/toolRegistry' import { useCanvasStore } from './stores/canvas' @@ -57,7 +60,10 @@ watch(() => route.name, (newPage) => {

Agent UI

+ + +
@@ -97,6 +103,7 @@ watch(() => route.name, (newPage) => { display: flex; flex-direction: column; height: 100vh; + height: 100dvh; background: var(--bg-primary); } @@ -105,8 +112,12 @@ watch(() => route.name, (newPage) => { align-items: center; justify-content: space-between; padding: 0.75rem 1.5rem; + padding-top: calc(0.75rem + env(safe-area-inset-top, 0px)); + padding-left: calc(1.5rem + env(safe-area-inset-left, 0px)); + padding-right: calc(1.5rem + env(safe-area-inset-right, 0px)); background: var(--bg-secondary); border-bottom: 1px solid var(--border-color); + flex-shrink: 0; } .header-left { diff --git a/frontend/src/components/FloatingTerminal.vue b/frontend/src/components/FloatingTerminal.vue index aa60dc8..6799e2d 100644 --- a/frontend/src/components/FloatingTerminal.vue +++ b/frontend/src/components/FloatingTerminal.vue @@ -19,17 +19,154 @@ const isOpen = computed({ }) const terminalContainer = ref(null) +const terminalRef = ref(null) const connected = ref(false) const connecting = ref(false) const sessionId = ref(null) const isMinimized = ref(false) +const isDragging = ref(false) +const position = ref({ x: 0, y: 0 }) +const dragOffset = ref({ x: 0, y: 0 }) + +// Resize state +const isResizing = ref(false) +const size = ref({ w: 580, h: 360 }) + let terminal: Terminal | null = null let fitAddon: FitAddon | null = null let socket: WebSocket | null = null let resizeObserver: ResizeObserver | null = null -const WS_URL = 'ws://localhost:4103' +const WS_URL = `ws://${window.location.hostname}:4103` + +const fabRef = ref(null) +const dragStartTime = ref(0) +const dragStartPos = ref({ x: 0, y: 0 }) +const isDraggingFab = ref(false) + +function startDrag(e: MouseEvent, isFab = false) { + if (!isFab && (e.target as HTMLElement).closest('.window-controls')) return + + isDragging.value = true + isDraggingFab.value = isFab + dragStartTime.value = Date.now() + dragStartPos.value = { x: e.clientX, y: e.clientY } + + const element = isFab ? fabRef.value : terminalRef.value + const rect = element?.getBoundingClientRect() + if (rect) { + dragOffset.value = { + x: e.clientX - rect.left, + y: e.clientY - rect.top + } + } + + document.addEventListener('mousemove', onDrag) + document.addEventListener('mouseup', stopDrag) +} + +function onDrag(e: MouseEvent) { + if (!isDragging.value) return + + const newX = e.clientX - dragOffset.value.x + const newY = e.clientY - dragOffset.value.y + + const elementWidth = isDraggingFab.value ? 120 : (terminalRef.value?.offsetWidth || 580) + const elementHeight = isDraggingFab.value ? 28 : (terminalRef.value?.offsetHeight || 360) + const maxX = window.innerWidth - elementWidth + const maxY = window.innerHeight - elementHeight + + position.value = { + x: Math.max(0, Math.min(newX, maxX)), + y: Math.max(0, Math.min(newY, maxY)) + } +} + +function stopDrag(e: MouseEvent) { + const wasDraggingFab = isDraggingFab.value + isDragging.value = false + document.removeEventListener('mousemove', onDrag) + document.removeEventListener('mouseup', stopDrag) + + if (wasDraggingFab) { + const elapsed = Date.now() - dragStartTime.value + const distance = Math.sqrt( + Math.pow(e.clientX - dragStartPos.value.x, 2) + + Math.pow(e.clientY - dragStartPos.value.y, 2) + ) + if (elapsed < 200 && distance < 5) { + toggleMinimize() + } + } + + isDraggingFab.value = false +} + +// Resize functions +const resizeStart = ref({ x: 0, y: 0, w: 0, h: 0 }) + +function startResize(e: MouseEvent) { + e.preventDefault() + e.stopPropagation() + isResizing.value = true + resizeStart.value = { + x: e.clientX, + y: e.clientY, + w: size.value.w, + h: size.value.h + } + document.addEventListener('mousemove', onResize) + document.addEventListener('mouseup', stopResize) +} + +function onResize(e: MouseEvent) { + if (!isResizing.value) return + + const deltaX = e.clientX - resizeStart.value.x + const deltaY = e.clientY - resizeStart.value.y + + size.value = { + w: Math.max(400, Math.min(resizeStart.value.w + deltaX, window.innerWidth - 40)), + h: Math.max(250, Math.min(resizeStart.value.h + deltaY, window.innerHeight - 40)) + } +} + +function stopResize() { + isResizing.value = false + document.removeEventListener('mousemove', onResize) + document.removeEventListener('mouseup', stopResize) + nextTick(() => fitAddon?.fit()) +} + +const terminalStyle = computed(() => { + const base = { + width: `${size.value.w}px`, + height: `${size.value.h}px` + } + if (position.value.x === 0 && position.value.y === 0) { + return { ...base, bottom: '16px', right: '16px' } + } + return { + ...base, + top: `${position.value.y}px`, + left: `${position.value.x}px`, + bottom: 'auto', + right: 'auto' + } +}) + +const minimizedStyle = computed(() => { + if (position.value.x === 0 && position.value.y === 0) { + return { bottom: '16px', right: '16px' } + } + return { + top: `${position.value.y}px`, + left: `${position.value.x}px`, + bottom: 'auto', + right: 'auto' + } +}) function initTerminal() { if (!terminalContainer.value || terminal) return @@ -37,30 +174,30 @@ function initTerminal() { terminal = new Terminal({ cursorBlink: true, cursorStyle: 'block', - fontSize: 13, - fontFamily: "'JetBrains Mono', 'Fira Code', Consolas, monospace", + fontSize: 12, + fontFamily: "'Consolas', 'Lucida Console', monospace", theme: { - background: '#0f0f14', - foreground: '#e4e4e7', - cursor: '#6366f1', - cursorAccent: '#0f0f14', - selectionBackground: 'rgba(99, 102, 241, 0.3)', - black: '#16161d', - red: '#ef4444', - green: '#22c55e', - yellow: '#eab308', - blue: '#3b82f6', - magenta: '#a855f7', - cyan: '#06b6d4', - white: '#e4e4e7', - brightBlack: '#52525b', - brightRed: '#f87171', - brightGreen: '#4ade80', - brightYellow: '#facc15', - brightBlue: '#60a5fa', - brightMagenta: '#c084fc', - brightCyan: '#22d3ee', - brightWhite: '#ffffff' + background: 'rgba(12, 12, 12, 0.95)', + foreground: '#ffffff', + cursor: '#ffffff', + cursorAccent: '#000000', + selectionBackground: 'rgba(100, 150, 255, 0.4)', + black: '#0c0c0c', + red: '#c50f1f', + green: '#13a10e', + yellow: '#c19c00', + blue: '#0037da', + magenta: '#881798', + cyan: '#3a96dd', + white: '#cccccc', + brightBlack: '#767676', + brightRed: '#e74856', + brightGreen: '#16c60c', + brightYellow: '#f9f1a5', + brightBlue: '#3b78ff', + brightMagenta: '#b4009e', + brightCyan: '#61d6d6', + brightWhite: '#f2f2f2' }, allowProposedApi: true }) @@ -68,12 +205,9 @@ function initTerminal() { fitAddon = new FitAddon() terminal.loadAddon(fitAddon) terminal.loadAddon(new WebLinksAddon()) - terminal.open(terminalContainer.value) - nextTick(() => { - fitAddon?.fit() - }) + nextTick(() => fitAddon?.fit()) resizeObserver = new ResizeObserver(() => { if (fitAddon && terminal && !isMinimized.value) { @@ -98,7 +232,6 @@ function initTerminal() { async function connect() { if (connecting.value || connected.value) return - connecting.value = true try { @@ -108,7 +241,6 @@ async function connect() { connected.value = true connecting.value = false terminal?.focus() - if (terminal) { socket?.send(JSON.stringify({ type: 'resize', @@ -123,7 +255,7 @@ async function connect() { if (msg.type === 'connected') { sessionId.value = msg.sessionId if (!msg.isNew) { - terminal?.write('\x1b[36m[Reconnected to session]\x1b[0m\r\n') + terminal?.write('\x1b[36m[Reconnected]\x1b[0m\r\n') } } else if (msg.type === 'replay') { terminal?.write(msg.data) @@ -150,14 +282,6 @@ async function connect() { } } -function disconnect() { - if (socket) { - socket.close() - socket = null - } - connected.value = false -} - function toggleMinimize() { isMinimized.value = !isMinimized.value if (!isMinimized.value) { @@ -178,16 +302,12 @@ function runClaude() { } } -// Watch for open state watch(isOpen, async (open) => { if (open) { + isMinimized.value = false await nextTick() - if (!terminal) { - initTerminal() - } - if (!connected.value && !connecting.value) { - connect() - } + if (!terminal) initTerminal() + if (!connected.value && !connecting.value) connect() nextTick(() => { fitAddon?.fit() terminal?.focus() @@ -207,46 +327,65 @@ onBeforeUnmount(() => { resizeObserver?.disconnect() socket?.close() terminal?.dispose() + document.removeEventListener('mousemove', onDrag) + document.removeEventListener('mouseup', stopDrag) + document.removeEventListener('mousemove', onResize) + document.removeEventListener('mouseup', stopResize) }) diff --git a/frontend/src/components/PwaInstallBanner.vue b/frontend/src/components/PwaInstallBanner.vue new file mode 100644 index 0000000..519706c --- /dev/null +++ b/frontend/src/components/PwaInstallBanner.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/frontend/src/pages/TerminalPage.vue b/frontend/src/pages/TerminalPage.vue index 9a3b004..0e15562 100644 --- a/frontend/src/pages/TerminalPage.vue +++ b/frontend/src/pages/TerminalPage.vue @@ -17,7 +17,7 @@ let fitAddon: FitAddon | null = null let socket: WebSocket | null = null let resizeObserver: ResizeObserver | null = null -const WS_URL = 'ws://localhost:4103' +const WS_URL = `ws://${window.location.hostname}:4103` function initTerminal() { if (!terminalContainer.value) return diff --git a/frontend/src/styles/main.css b/frontend/src/styles/main.css index 273af61..c1cf6ff 100644 --- a/frontend/src/styles/main.css +++ b/frontend/src/styles/main.css @@ -18,15 +18,20 @@ html, body { height: 100%; + min-height: 100vh; + min-height: 100dvh; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - background: var(--bg-primary); + background: var(--bg-secondary); color: var(--text-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + overflow: hidden; } #app { height: 100%; + min-height: 100vh; + min-height: 100dvh; } /* Scrollbar styling */ diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 45c342a..8ff8c13 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -18,15 +18,36 @@ export default defineConfig({ vue(), VitePWA({ registerType: 'autoUpdate', - includeAssets: ['favicon.ico', 'icons/*.png'], + includeAssets: ['favicon.svg', 'icons/*.svg', 'icons/*.png'], + devOptions: { + enabled: true, + type: 'module', + suppressWarnings: true + }, + workbox: { + globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'], + navigateFallbackDenylist: [/^\/api\//], + // Don't cache API calls - let them go directly to network + runtimeCaching: [] + }, manifest: { - name: 'Agent UI - Dynamic Canvas', + name: 'Agent UI', short_name: 'AgentUI', - description: 'Dynamic canvas for Claude Code interaction', - theme_color: '#1a1a2e', - background_color: '#1a1a2e', + description: 'Dynamic canvas for Claude Code interaction via WebMCP', + theme_color: '#16161d', + background_color: '#0f0f14', display: 'standalone', + orientation: 'any', + start_url: '/', + scope: '/', + categories: ['developer', 'utilities'], icons: [ + { + src: 'icons/icon.svg', + sizes: 'any', + type: 'image/svg+xml', + purpose: 'any' + }, { src: 'icons/icon-192.png', sizes: '192x192', @@ -36,6 +57,12 @@ export default defineConfig({ src: 'icons/icon-512.png', sizes: '512x512', type: 'image/png' + }, + { + src: 'icons/icon-maskable-512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'maskable' } ] } @@ -44,6 +71,8 @@ export default defineConfig({ server: { port: 4100, host: true, + allowedHosts: ['z590.interno.com', 'localhost'], + cors: true, proxy: { '/api': 'http://localhost:4101' }, diff --git a/server/services/terminal.ts b/server/services/terminal.ts index a5122f7..9a5a622 100644 --- a/server/services/terminal.ts +++ b/server/services/terminal.ts @@ -80,6 +80,17 @@ export function startTerminalServer() { fetch(req, server) { const url = new URL(req.url) + const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type' + } + + // CORS preflight + if (req.method === 'OPTIONS') { + return new Response(null, { headers: corsHeaders }) + } + // Health check with session info if (url.pathname === '/health') { const sessionsInfo = Array.from(sessions.entries()).map(([id, s]) => ({ @@ -93,7 +104,7 @@ export function startTerminalServer() { status: 'ok', sessions: sessionsInfo, cwd: WORKING_DIR - }) + }, { headers: corsHeaders }) } // List active sessions