Przeglądaj źródła

fix: show banners in the picocrank AppShell banner area

picocrank 1.40 moves the header toolbar into a closed "More actions"
menu when it does not fit, which hid the configured banner and the
disconnected banner on narrow screens. Lay the app out with picocrank
1.41's AppShell and render both banners in its banner area, below the
header, as NotificationBlocks.

vue-router moves to ^5.5.0 so picocrank shares the app's router instead
of installing its own copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jamesread 23 godzin temu
rodzic
commit
4b001be9aa

+ 245 - 157
frontend/package-lock.json

@@ -17,10 +17,10 @@
 				"@xterm/addon-web-links": "^0.12.0",
 				"@xterm/addon-web-links": "^0.12.0",
 				"@xterm/xterm": "^6.0.0",
 				"@xterm/xterm": "^6.0.0",
 				"iconify-icon": "^3.0.4",
 				"iconify-icon": "^3.0.4",
-				"picocrank": "^1.40.0",
+				"picocrank": "^1.41.0",
 				"vue": "^3.5.44",
 				"vue": "^3.5.44",
 				"vue-i18n": "^11.4.16",
 				"vue-i18n": "^11.4.16",
-				"vue-router": "^5.4.0"
+				"vue-router": "^5.5.0"
 			},
 			},
 			"devDependencies": {
 			"devDependencies": {
 				"@vitejs/plugin-vue": "^6.0.10",
 				"@vitejs/plugin-vue": "^6.0.10",
@@ -51,9 +51,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@babel/helper-string-parser": {
 		"node_modules/@babel/helper-string-parser": {
-			"version": "7.29.7",
-			"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
-			"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+			"version": "7.29.13",
+			"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.13.tgz",
+			"integrity": "sha512-fWegr+WsEsiMVW4KUNdIIzzMTZSzNiqhy0//6+2MRi2d5NFNsL/AYwBQW8eBnRbTBVRnDVzjaJEUqKaCWjMMdQ==",
 			"license": "MIT",
 			"license": "MIT",
 			"engines": {
 			"engines": {
 				"node": ">=6.9.0"
 				"node": ">=6.9.0"
@@ -69,12 +69,12 @@
 			}
 			}
 		},
 		},
 		"node_modules/@babel/parser": {
 		"node_modules/@babel/parser": {
-			"version": "7.29.9",
-			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz",
-			"integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==",
+			"version": "7.29.13",
+			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.13.tgz",
+			"integrity": "sha512-fJM50YYjlTiBhgdkGmC1tBpv+FMAyo4annf2X6Aks+0yRlaiVkYW/3f07xAfVVkYIoZ9fFZp+mEThUFnuybhkQ==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@babel/types": "^7.29.8"
+				"@babel/types": "^7.29.11"
 			},
 			},
 			"bin": {
 			"bin": {
 				"parser": "bin/babel-parser.js"
 				"parser": "bin/babel-parser.js"
@@ -84,12 +84,12 @@
 			}
 			}
 		},
 		},
 		"node_modules/@babel/types": {
 		"node_modules/@babel/types": {
-			"version": "7.29.8",
-			"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
-			"integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+			"version": "7.29.11",
+			"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz",
+			"integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@babel/helper-string-parser": "^7.29.7",
+				"@babel/helper-string-parser": "^7.29.11",
 				"@babel/helper-validator-identifier": "^7.29.7"
 				"@babel/helper-validator-identifier": "^7.29.7"
 			},
 			},
 			"engines": {
 			"engines": {
@@ -308,6 +308,37 @@
 				"postcss-selector-parser": "^7.1.1"
 				"postcss-selector-parser": "^7.1.1"
 			}
 			}
 		},
 		},
+		"node_modules/@emnapi/core": {
+			"version": "2.0.0-beta.2",
+			"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-beta.2.tgz",
+			"integrity": "sha512-MzCVoWtcg4Pm2B3je5mMvYMvj3MmBKw+fU7vbsJc9/iT8ip6/eYIHo/Dsm/mr+wsHB6SEyJDuxB09Yy6r/8+4A==",
+			"license": "MIT",
+			"optional": true,
+			"dependencies": {
+				"@emnapi/wasi-threads": "2.2.0",
+				"tslib": "^2.4.0"
+			}
+		},
+		"node_modules/@emnapi/runtime": {
+			"version": "2.0.0-beta.2",
+			"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-beta.2.tgz",
+			"integrity": "sha512-JcrSPzPvZuNZvJqPTF9Fw25LbOJ8avzFVlaf2grmH4Cku0oGP47tRgqPZadcfj3YoZMHkFVnCYFbQM8I+i5QEw==",
+			"license": "MIT",
+			"optional": true,
+			"dependencies": {
+				"tslib": "^2.4.0"
+			}
+		},
+		"node_modules/@emnapi/wasi-threads": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.2.0.tgz",
+			"integrity": "sha512-JP7OYH38Q7+pyTAe14raZHLQIK7BDKbdwji+Xi3pcd/6QkJYQH3SX4wu6EplGrGJWRepoNzedfm0G3B9Z4e9Rw==",
+			"license": "MIT",
+			"optional": true,
+			"dependencies": {
+				"tslib": "^2.4.0"
+			}
+		},
 		"node_modules/@eslint-community/eslint-utils": {
 		"node_modules/@eslint-community/eslint-utils": {
 			"version": "4.10.1",
 			"version": "4.10.1",
 			"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
 			"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
@@ -416,9 +447,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@hugeicons/core-free-icons": {
 		"node_modules/@hugeicons/core-free-icons": {
-			"version": "4.3.5",
-			"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-4.3.5.tgz",
-			"integrity": "sha512-Sv+NjHRPnQk+yZsGCMcGznCHdTfJR9PMMOEKcJYAQU4gy90dmlc9PwdtvYOImBZIjiheMsTLL5eMn2DmHxUiUg==",
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-4.4.0.tgz",
+			"integrity": "sha512-7Mz8bmGoY6tZ6l3C2TXwNMLVSr0I8HPwjjFIERAuJFj3rWwLxdF2ZLkDqdrVuEE1dx5b/VXyIsP4WoSU6Q40KQ==",
 			"license": "MIT"
 			"license": "MIT"
 		},
 		},
 		"node_modules/@hugeicons/vue": {
 		"node_modules/@hugeicons/vue": {
@@ -643,6 +674,27 @@
 			"dev": true,
 			"dev": true,
 			"license": "MIT"
 			"license": "MIT"
 		},
 		},
+		"node_modules/@napi-rs/wasm-runtime": {
+			"version": "1.2.6",
+			"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.6.tgz",
+			"integrity": "sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw==",
+			"license": "MIT",
+			"optional": true,
+			"dependencies": {
+				"@tybys/wasm-util": "^0.10.3"
+			},
+			"engines": {
+				"node": "^20.19.0 || ^22.13.0 || >=23.5.0"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/Brooooooklyn"
+			},
+			"peerDependencies": {
+				"@emnapi/core": "^1.7.1 || 2.0.0-beta.2",
+				"@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2"
+			}
+		},
 		"node_modules/@nodelib/fs.scandir": {
 		"node_modules/@nodelib/fs.scandir": {
 			"version": "2.1.5",
 			"version": "2.1.5",
 			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
 			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -682,18 +734,18 @@
 			}
 			}
 		},
 		},
 		"node_modules/@oxc-project/types": {
 		"node_modules/@oxc-project/types": {
-			"version": "0.155.0",
-			"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz",
-			"integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==",
+			"version": "0.156.0",
+			"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.156.0.tgz",
+			"integrity": "sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw==",
 			"license": "MIT",
 			"license": "MIT",
 			"funding": {
 			"funding": {
 				"url": "https://github.com/sponsors/oxc-project"
 				"url": "https://github.com/sponsors/oxc-project"
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-android-arm-eabi": {
 		"node_modules/@rolldown/binding-android-arm-eabi": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz",
-			"integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.19.tgz",
+			"integrity": "sha512-e+Ebe0WT+9tbXmNKPIiC8YrWAHYwug3m6i4p3rZGGulfZri1jtI4HslT+OZnFf/OIHowWF71DTlHwXBP2SiA7A==",
 			"cpu": [
 			"cpu": [
 				"arm"
 				"arm"
 			],
 			],
@@ -707,9 +759,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-android-arm64": {
 		"node_modules/@rolldown/binding-android-arm64": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz",
-			"integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.19.tgz",
+			"integrity": "sha512-hOombrTJ+szt2ciEzS7oCA78lMPBgF5UMQWCJIyNfnrI7GLl8HYoFqCnpL2gcPOaXMqryGCU5ExdQKyDDcC+yA==",
 			"cpu": [
 			"cpu": [
 				"arm64"
 				"arm64"
 			],
 			],
@@ -723,9 +775,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-darwin-arm64": {
 		"node_modules/@rolldown/binding-darwin-arm64": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz",
-			"integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.19.tgz",
+			"integrity": "sha512-XoMA2pwCKGTQzUjPgtRwkedH4WmXhdIPKT6l6oOxd1PUbNodGI6DcBekgN7EC//+x3pfBYX/qL1gNmZAqivw5g==",
 			"cpu": [
 			"cpu": [
 				"arm64"
 				"arm64"
 			],
 			],
@@ -739,9 +791,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-darwin-x64": {
 		"node_modules/@rolldown/binding-darwin-x64": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz",
-			"integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.19.tgz",
+			"integrity": "sha512-zo8EsSSwMZCY/e+fRnGaX02YGgvJD0yvhNi/9hpGPA2MJ9ZF8dPCw75UGfpI6OpQ/myR/dIYmogRvqiZD0TrXg==",
 			"cpu": [
 			"cpu": [
 				"x64"
 				"x64"
 			],
 			],
@@ -755,9 +807,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-freebsd-x64": {
 		"node_modules/@rolldown/binding-freebsd-x64": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz",
-			"integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.19.tgz",
+			"integrity": "sha512-O8ISAOfI1nj7AuBPd30FSF47z7f7lzlk5poQVGgiikvQuGEBFYvW8ki+UEnT+YJPnNGjQzn304klt4O9FUE7mA==",
 			"cpu": [
 			"cpu": [
 				"x64"
 				"x64"
 			],
 			],
@@ -771,9 +823,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
 		"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz",
-			"integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.19.tgz",
+			"integrity": "sha512-/lYCba5NO0yq4S0jGMH8QTBtkvQxoXjoQWzF5BcWp9MjB9jxVRD1GQDNofkVjcOj30DWyhLIN/4Oe60JKzaONw==",
 			"cpu": [
 			"cpu": [
 				"arm"
 				"arm"
 			],
 			],
@@ -787,9 +839,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-linux-arm64-gnu": {
 		"node_modules/@rolldown/binding-linux-arm64-gnu": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz",
-			"integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.19.tgz",
+			"integrity": "sha512-pm/apFMBnSbFb3O6nG5pHO1iqHhZrjGDFEjSm+ot9J17XXPvH9L90y5h+jgfHCE+Qdlsvy8qKEcU4VKxgjD+vA==",
 			"cpu": [
 			"cpu": [
 				"arm64"
 				"arm64"
 			],
 			],
@@ -803,9 +855,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-linux-arm64-musl": {
 		"node_modules/@rolldown/binding-linux-arm64-musl": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz",
-			"integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.19.tgz",
+			"integrity": "sha512-K1SmQp3FFichn8oqrNIwvFoZ5wcvjC7goBzpaMNvLf9FZc+QB/QvLmnpJZgAAGHiD980tP8FZZnXk2K94rvlxg==",
 			"cpu": [
 			"cpu": [
 				"arm64"
 				"arm64"
 			],
 			],
@@ -819,9 +871,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-linux-ppc64-gnu": {
 		"node_modules/@rolldown/binding-linux-ppc64-gnu": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz",
-			"integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.19.tgz",
+			"integrity": "sha512-I0xwD57jP/3A7+5UPZin2w8Y7XHEmEM5zqpYZOcQ45MlNW6tUJUziymV3yRr7rL4vwyfEcRsRksC4a7Kc08p5Q==",
 			"cpu": [
 			"cpu": [
 				"ppc64"
 				"ppc64"
 			],
 			],
@@ -835,9 +887,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-linux-s390x-gnu": {
 		"node_modules/@rolldown/binding-linux-s390x-gnu": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz",
-			"integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.19.tgz",
+			"integrity": "sha512-BrtEHT24SNXj9HiXqy+s/kXiSiQGt6EKCPUlRirAoIioVxKGnbXBUEkbZASAei7iZJB0/Ynm+0JP00Ipx7AnSw==",
 			"cpu": [
 			"cpu": [
 				"s390x"
 				"s390x"
 			],
 			],
@@ -851,9 +903,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-linux-x64-gnu": {
 		"node_modules/@rolldown/binding-linux-x64-gnu": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz",
-			"integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.19.tgz",
+			"integrity": "sha512-rm75/9DkHKeU2WnA8w4X94WGYgLlDCFCfkQC+4bguVJmC/jvDRv2B+2wLOYhyE0fVuv7//Ckk39OoOufyCu76Q==",
 			"cpu": [
 			"cpu": [
 				"x64"
 				"x64"
 			],
 			],
@@ -867,9 +919,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-linux-x64-musl": {
 		"node_modules/@rolldown/binding-linux-x64-musl": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz",
-			"integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.19.tgz",
+			"integrity": "sha512-irh/4+iYFM4GAzpluDErgjz89bj6NDtqqMAG/6O1LG38Ed3NpyebzCpcVNkFW8uhy+/3/BocUvnXDR33CXWe9A==",
 			"cpu": [
 			"cpu": [
 				"x64"
 				"x64"
 			],
 			],
@@ -883,9 +935,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-openharmony-arm64": {
 		"node_modules/@rolldown/binding-openharmony-arm64": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz",
-			"integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.19.tgz",
+			"integrity": "sha512-+KWTWNoPt+CfSq94CiceWu3yPE+QzkaG+phPyz9G60tmZLwfQkcA4AeksafzvJAw4pvg1mcd5a1TkUqVv0s4JA==",
 			"cpu": [
 			"cpu": [
 				"arm64"
 				"arm64"
 			],
 			],
@@ -898,10 +950,28 @@
 				"node": "^20.19.0 || >=22.12.0"
 				"node": "^20.19.0 || >=22.12.0"
 			}
 			}
 		},
 		},
+		"node_modules/@rolldown/binding-wasm32-wasi": {
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.19.tgz",
+			"integrity": "sha512-7EH+skOsl5qpWnK0vzamgJFhOv1cwxQpsxm8tI883K9fur5c27p8q87fzEO6BbBPabA2/+s/zR63/pLc137iOg==",
+			"cpu": [
+				"wasm32"
+			],
+			"license": "MIT",
+			"optional": true,
+			"dependencies": {
+				"@emnapi/core": "2.0.0-beta.2",
+				"@emnapi/runtime": "2.0.0-beta.2",
+				"@napi-rs/wasm-runtime": "1.2.6"
+			},
+			"engines": {
+				"node": "^20.19.0 || ^22.13.0 || >=23.5.0"
+			}
+		},
 		"node_modules/@rolldown/binding-win32-arm64-msvc": {
 		"node_modules/@rolldown/binding-win32-arm64-msvc": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz",
-			"integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.19.tgz",
+			"integrity": "sha512-3Q1Yg1Q7tEvBWoZf5rFvG/GKZQPgwfru+p4wBajDfJFBs0SHCpL++dfTjW9gyAKd1T7ZpIYvdaUax7Pa/IhFVQ==",
 			"cpu": [
 			"cpu": [
 				"arm64"
 				"arm64"
 			],
 			],
@@ -915,9 +985,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/@rolldown/binding-win32-x64-msvc": {
 		"node_modules/@rolldown/binding-win32-x64-msvc": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz",
-			"integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.19.tgz",
+			"integrity": "sha512-y/sqRRc6d7hECzOFc5n3LSeqctmIp9mMjY1Kq4s14uLPd/LZ4z7i1zrW6fMtp2mPeuQIC4e6ULMcwQzMomqQag==",
 			"cpu": [
 			"cpu": [
 				"x64"
 				"x64"
 			],
 			],
@@ -1001,6 +1071,16 @@
 				"url": "https://opencollective.com/eslint"
 				"url": "https://opencollective.com/eslint"
 			}
 			}
 		},
 		},
+		"node_modules/@tybys/wasm-util": {
+			"version": "0.10.4",
+			"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz",
+			"integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==",
+			"license": "MIT",
+			"optional": true,
+			"dependencies": {
+				"tslib": "^2.4.0"
+			}
+		},
 		"node_modules/@types/esrecurse": {
 		"node_modules/@types/esrecurse": {
 			"version": "4.3.1",
 			"version": "4.3.1",
 			"resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
 			"resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
@@ -1295,53 +1375,53 @@
 			}
 			}
 		},
 		},
 		"node_modules/@vue/compiler-core": {
 		"node_modules/@vue/compiler-core": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.44.tgz",
-			"integrity": "sha512-k34nCytbK8PQ1gR2mGROYOw6Pnv/mumqIdrZ3x2tbyXZDWm7ViKGLTfdB+DRrWqEQsLgGJvn0WN9jBif5zCTLQ==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.45.tgz",
+			"integrity": "sha512-XGHJrM6ufZARAc/w+tyOswStw9tdKiqCKBScI4gGaNxD+aaGu/HQWgKJ+JItjRLPqenQ7iE7G8AJC7T5ew1p+g==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@babel/parser": "^7.29.9",
-				"@vue/shared": "3.5.44",
+				"@babel/parser": "^7.29.12",
+				"@vue/shared": "3.5.45",
 				"entities": "^7.0.1",
 				"entities": "^7.0.1",
 				"estree-walker": "^2.0.2",
 				"estree-walker": "^2.0.2",
 				"source-map-js": "^1.2.2"
 				"source-map-js": "^1.2.2"
 			}
 			}
 		},
 		},
 		"node_modules/@vue/compiler-dom": {
 		"node_modules/@vue/compiler-dom": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.44.tgz",
-			"integrity": "sha512-W5AoBFWdw1lQAKiHowkfgFKFab82oSRT6sGsPfw/q1GRjsy/ZJFLHGhJyBRIFN9APLTUduDH9U2JAiPOIyKuYg==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.45.tgz",
+			"integrity": "sha512-LbP2d19GSOYGPmvMBBCVJVlv7TKtjM1Cfeyu3cYia720pknZP+1cLX7H5MVatvruT6MuJhiPuNbQBW/MDn/EwA==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@vue/compiler-core": "3.5.44",
-				"@vue/shared": "3.5.44"
+				"@vue/compiler-core": "3.5.45",
+				"@vue/shared": "3.5.45"
 			}
 			}
 		},
 		},
 		"node_modules/@vue/compiler-sfc": {
 		"node_modules/@vue/compiler-sfc": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.44.tgz",
-			"integrity": "sha512-dzHmQ4jXTpdr5FjIQUeykVakN/uzqFbUo9/C5ikBFg3a3PeM6uRxeczx1ZbXBML0/zJstEh3ygYipzR5Z7b2Pw==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.45.tgz",
+			"integrity": "sha512-MKFjb2UX0M/l/jhfzqMLrI8VM4pfvFKG3pqFZkSZFHRLtNld/O4RtUPZFWSwhro/14qP88UQEkWzHxAZAQ0tsw==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@babel/parser": "^7.29.9",
-				"@vue/compiler-core": "3.5.44",
-				"@vue/compiler-dom": "3.5.44",
-				"@vue/compiler-ssr": "3.5.44",
-				"@vue/shared": "3.5.44",
+				"@babel/parser": "^7.29.12",
+				"@vue/compiler-core": "3.5.45",
+				"@vue/compiler-dom": "3.5.45",
+				"@vue/compiler-ssr": "3.5.45",
+				"@vue/shared": "3.5.45",
 				"estree-walker": "^2.0.2",
 				"estree-walker": "^2.0.2",
 				"magic-string": "^0.30.21",
 				"magic-string": "^0.30.21",
-				"postcss": "^8.5.29",
+				"postcss": "^8.5.32",
 				"source-map-js": "^1.2.2"
 				"source-map-js": "^1.2.2"
 			}
 			}
 		},
 		},
 		"node_modules/@vue/compiler-ssr": {
 		"node_modules/@vue/compiler-ssr": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.44.tgz",
-			"integrity": "sha512-xr5gb1JVHlfXJwSlRz1FeP6uxoazdzHuMTVLE7up+4Vrqxk219CvxLPxD9NXBt47yvZW7+p4DXfyp4O1fkByIA==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.45.tgz",
+			"integrity": "sha512-RF5sbZOfYz5AK00tOJUn2PwFXR2fS1u2HVzI8D9VF9rGAFxuGBqg2bB1kJ3ZHMprqSltXnT9MZE1IsqjSM8oew==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@vue/compiler-dom": "3.5.44",
-				"@vue/shared": "3.5.44"
+				"@vue/compiler-dom": "3.5.45",
+				"@vue/shared": "3.5.45"
 			}
 			}
 		},
 		},
 		"node_modules/@vue/devtools-api": {
 		"node_modules/@vue/devtools-api": {
@@ -1369,51 +1449,51 @@
 			"license": "MIT"
 			"license": "MIT"
 		},
 		},
 		"node_modules/@vue/reactivity": {
 		"node_modules/@vue/reactivity": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.44.tgz",
-			"integrity": "sha512-u+QJdKeBBEADkPdevsL+6E12QlYVfBD8Zcw5qjDQKtamI6l14KyqazJMobhCKelhWN5zpyKUiEDoTyHE93cqWw==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.45.tgz",
+			"integrity": "sha512-WqkyubNWpI4OYBOipSrjIssMdJO8K1i9Zts/Wm8Er10/0msOEwCyWWWvZnAI7WW3kQsTong6DJp8Cbb+vd4cPA==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@vue/shared": "3.5.44"
+				"@vue/shared": "3.5.45"
 			}
 			}
 		},
 		},
 		"node_modules/@vue/runtime-core": {
 		"node_modules/@vue/runtime-core": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.44.tgz",
-			"integrity": "sha512-uEa/ndwjhrrhnOfb2p+sZy6zeoamJOISqaLSJGk/8R6nABT1ZldpzcJoPzKj+zHVomVBPcmQYyC2r4LtvDV6CQ==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.45.tgz",
+			"integrity": "sha512-bzIO3+FABjpj85uIKSc+Qnj36/sxDNI3tcH9i6w6xKA1k6e0ejv0G0jMCfeJBvK3yGL5XbJ3DMZj+ClqyCHFYw==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@vue/reactivity": "3.5.44",
-				"@vue/shared": "3.5.44"
+				"@vue/reactivity": "3.5.45",
+				"@vue/shared": "3.5.45"
 			}
 			}
 		},
 		},
 		"node_modules/@vue/runtime-dom": {
 		"node_modules/@vue/runtime-dom": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.44.tgz",
-			"integrity": "sha512-3fc4ARcXVhVBaJ3ujWiNZJARD6pkyRcwG+ZYhMzjKtKUp7MAY0HLwr7r3Smep3Li6ZaWT6ewdmx8XqdaD5oNZg==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.45.tgz",
+			"integrity": "sha512-ZvRTFMDgfqTDYWvJfoVpQTJx1RN5M6l7Pyd6wfdY2NaXzbPQVh9ezeQYFbNPP6bIcRVn2SqSSR25N/jSW69BFw==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@vue/reactivity": "3.5.44",
-				"@vue/runtime-core": "3.5.44",
-				"@vue/shared": "3.5.44",
+				"@vue/reactivity": "3.5.45",
+				"@vue/runtime-core": "3.5.45",
+				"@vue/shared": "3.5.45",
 				"csstype": "^3.2.3"
 				"csstype": "^3.2.3"
 			}
 			}
 		},
 		},
 		"node_modules/@vue/server-renderer": {
 		"node_modules/@vue/server-renderer": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.44.tgz",
-			"integrity": "sha512-R4S1UoH4h5Frrauv8Z5XE5TxV9oBiUSgWIC4TyVK1FK+RocRnOLwFA5Xcmo2011RlgJp+AbZCnw6afrhlgRuUQ==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.45.tgz",
+			"integrity": "sha512-1fNXUHhhsWf4EVL4XixbGBU5mKdiIXJAB82feHRTyPPo6ZUEYWIJuEeUl3mVV6QDBp50HJOX4obLhxYXlrjrzA==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@vue/compiler-ssr": "3.5.44",
-				"@vue/runtime-dom": "3.5.44",
-				"@vue/shared": "3.5.44"
+				"@vue/compiler-ssr": "3.5.45",
+				"@vue/runtime-dom": "3.5.45",
+				"@vue/shared": "3.5.45"
 			}
 			}
 		},
 		},
 		"node_modules/@vue/shared": {
 		"node_modules/@vue/shared": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.44.tgz",
-			"integrity": "sha512-H82XLH0DjmSOdbEwO15x3QTo1f5PWEtv237eLpYKc0mqLu+B8hnq16lFWmuBmNurkeiG3wr+oM1FhOll/YVFRg==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.45.tgz",
+			"integrity": "sha512-ue9fgHmVNPlP7iW3+cxoBX/Cnlw53dHlVSw0JKGY6z1lObg8pHoj928M2nzG4/yVsMZ3NaRZ2s2z61uC16NDwQ==",
 			"license": "MIT"
 			"license": "MIT"
 		},
 		},
 		"node_modules/@xterm/addon-fit": {
 		"node_modules/@xterm/addon-fit": {
@@ -3648,19 +3728,19 @@
 			"license": "ISC"
 			"license": "ISC"
 		},
 		},
 		"node_modules/picocrank": {
 		"node_modules/picocrank": {
-			"version": "1.40.0",
-			"resolved": "https://registry.npmjs.org/picocrank/-/picocrank-1.40.0.tgz",
-			"integrity": "sha512-CGtMQlbVodFetFfCp0bBL8ZsSoY2+zQkynTiOT8hB9zdmhflTIXPFc93GMktlVHj5rbP1DAYNhMPXw5qdxz2XQ==",
+			"version": "1.41.0",
+			"resolved": "https://registry.npmjs.org/picocrank/-/picocrank-1.41.0.tgz",
+			"integrity": "sha512-x+wg10nqyF1PdVgKiWN/42Q7J2VZeoC+LxKtC4bh+oTlIWCU9E8t163yiMIwMp0HMpyGWyopkx4f2YeCLwdWIA==",
 			"license": "ISC",
 			"license": "ISC",
 			"dependencies": {
 			"dependencies": {
-				"@hugeicons/core-free-icons": "^4.3.5",
+				"@hugeicons/core-free-icons": "^4.4.0",
 				"@hugeicons/vue": "^1.0.8",
 				"@hugeicons/vue": "^1.0.8",
 				"@vitejs/plugin-vue": "^6.0.10",
 				"@vitejs/plugin-vue": "^6.0.10",
 				"femtocrank": "^2.7.2",
 				"femtocrank": "^2.7.2",
 				"unplugin-vue-components": "^32.1.0",
 				"unplugin-vue-components": "^32.1.0",
-				"vite": "^8.3.4",
-				"vue": "^3.5.43",
-				"vue-router": "^5.4.0"
+				"vite": "^8.3.8",
+				"vue": "^3.5.45",
+				"vue-router": "^5.5.0"
 			}
 			}
 		},
 		},
 		"node_modules/picomatch": {
 		"node_modules/picomatch": {
@@ -3687,9 +3767,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/postcss": {
 		"node_modules/postcss": {
-			"version": "8.5.31",
-			"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz",
-			"integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==",
+			"version": "8.5.32",
+			"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz",
+			"integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==",
 			"funding": [
 			"funding": [
 				{
 				{
 					"type": "opencollective",
 					"type": "opencollective",
@@ -3894,12 +3974,12 @@
 			}
 			}
 		},
 		},
 		"node_modules/rolldown": {
 		"node_modules/rolldown": {
-			"version": "1.2.15",
-			"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz",
-			"integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==",
+			"version": "1.2.19",
+			"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.19.tgz",
+			"integrity": "sha512-QIiV1J9d0pzN4jeeTH4sjPy0AZ6cAy2cG0ExeoE1+U8Rvj+hR3gYkokx0YbeUNGlZqiQJJ2utcETe0oKXwzsuA==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@oxc-project/types": "=0.155.0",
+				"@oxc-project/types": "=0.156.0",
 				"@rolldown/pluginutils": "^1.0.0"
 				"@rolldown/pluginutils": "^1.0.0"
 			},
 			},
 			"bin": {
 			"bin": {
@@ -3909,21 +3989,22 @@
 				"node": "^20.19.0 || >=22.12.0"
 				"node": "^20.19.0 || >=22.12.0"
 			},
 			},
 			"optionalDependencies": {
 			"optionalDependencies": {
-				"@rolldown/binding-android-arm-eabi": "1.2.15",
-				"@rolldown/binding-android-arm64": "1.2.15",
-				"@rolldown/binding-darwin-arm64": "1.2.15",
-				"@rolldown/binding-darwin-x64": "1.2.15",
-				"@rolldown/binding-freebsd-x64": "1.2.15",
-				"@rolldown/binding-linux-arm-gnueabihf": "1.2.15",
-				"@rolldown/binding-linux-arm64-gnu": "1.2.15",
-				"@rolldown/binding-linux-arm64-musl": "1.2.15",
-				"@rolldown/binding-linux-ppc64-gnu": "1.2.15",
-				"@rolldown/binding-linux-s390x-gnu": "1.2.15",
-				"@rolldown/binding-linux-x64-gnu": "1.2.15",
-				"@rolldown/binding-linux-x64-musl": "1.2.15",
-				"@rolldown/binding-openharmony-arm64": "1.2.15",
-				"@rolldown/binding-win32-arm64-msvc": "1.2.15",
-				"@rolldown/binding-win32-x64-msvc": "1.2.15"
+				"@rolldown/binding-android-arm-eabi": "1.2.19",
+				"@rolldown/binding-android-arm64": "1.2.19",
+				"@rolldown/binding-darwin-arm64": "1.2.19",
+				"@rolldown/binding-darwin-x64": "1.2.19",
+				"@rolldown/binding-freebsd-x64": "1.2.19",
+				"@rolldown/binding-linux-arm-gnueabihf": "1.2.19",
+				"@rolldown/binding-linux-arm64-gnu": "1.2.19",
+				"@rolldown/binding-linux-arm64-musl": "1.2.19",
+				"@rolldown/binding-linux-ppc64-gnu": "1.2.19",
+				"@rolldown/binding-linux-s390x-gnu": "1.2.19",
+				"@rolldown/binding-linux-x64-gnu": "1.2.19",
+				"@rolldown/binding-linux-x64-musl": "1.2.19",
+				"@rolldown/binding-openharmony-arm64": "1.2.19",
+				"@rolldown/binding-wasm32-wasi": "1.2.19",
+				"@rolldown/binding-win32-arm64-msvc": "1.2.19",
+				"@rolldown/binding-win32-x64-msvc": "1.2.19"
 			}
 			}
 		},
 		},
 		"node_modules/run-parallel": {
 		"node_modules/run-parallel": {
@@ -4368,6 +4449,13 @@
 				"typescript": ">=4.8.4"
 				"typescript": ">=4.8.4"
 			}
 			}
 		},
 		},
+		"node_modules/tslib": {
+			"version": "2.9.1",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz",
+			"integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==",
+			"license": "0BSD",
+			"optional": true
+		},
 		"node_modules/type-check": {
 		"node_modules/type-check": {
 			"version": "0.4.0",
 			"version": "0.4.0",
 			"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
 			"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -4563,15 +4651,15 @@
 			"license": "MIT"
 			"license": "MIT"
 		},
 		},
 		"node_modules/vite": {
 		"node_modules/vite": {
-			"version": "8.3.5",
-			"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz",
-			"integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==",
+			"version": "8.3.8",
+			"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.8.tgz",
+			"integrity": "sha512-LzOn3h9rG/0vhLdXrV4yJ4QFA9eEoqCl+Yx+kGJd7aB9w6nfNgVHKSYwLPZUHrz4ZGa+t+kXjV9itvcHDLQouA==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
 				"lightningcss": "^1.33.0",
 				"lightningcss": "^1.33.0",
 				"picomatch": "^4.0.7",
 				"picomatch": "^4.0.7",
-				"postcss": "^8.5.30",
-				"rolldown": "~1.2.14",
+				"postcss": "^8.5.32",
+				"rolldown": "~1.2.18",
 				"tinyglobby": "^0.2.17"
 				"tinyglobby": "^0.2.17"
 			},
 			},
 			"bin": {
 			"bin": {
@@ -4640,16 +4728,16 @@
 			}
 			}
 		},
 		},
 		"node_modules/vue": {
 		"node_modules/vue": {
-			"version": "3.5.44",
-			"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.44.tgz",
-			"integrity": "sha512-dvvmaOCG7lAMkJTPqPwWeGMsPS6DIsFsfZDUgBp9V6mHf9b3+/wyVwYR4Es+WV6uJC0FKRxp81Obcq0ZwUcGFQ==",
+			"version": "3.5.45",
+			"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.45.tgz",
+			"integrity": "sha512-3cvsib8m9c0vB24+MDKsgcW4+Da4xaBl9Z/5Tbm2pmSIsRPxcil4cdXjXlVsmJ2g64XJo5h+IRd7rsdGT9pZaw==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
-				"@vue/compiler-dom": "3.5.44",
-				"@vue/compiler-sfc": "3.5.44",
-				"@vue/runtime-dom": "3.5.44",
-				"@vue/server-renderer": "3.5.44",
-				"@vue/shared": "3.5.44"
+				"@vue/compiler-dom": "3.5.45",
+				"@vue/compiler-sfc": "3.5.45",
+				"@vue/runtime-dom": "3.5.45",
+				"@vue/server-renderer": "3.5.45",
+				"@vue/shared": "3.5.45"
 			},
 			},
 			"peerDependencies": {
 			"peerDependencies": {
 				"typescript": "*"
 				"typescript": "*"
@@ -4707,9 +4795,9 @@
 			}
 			}
 		},
 		},
 		"node_modules/vue-router": {
 		"node_modules/vue-router": {
-			"version": "5.4.0",
-			"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.4.0.tgz",
-			"integrity": "sha512-pFvcYcxinDHjZUlf7AzFBrT8r8Xqvv4uMHoJnjc+EqPawXQvHTkF8S4tfSmQLpriVHUGfkaKlI7Et0KRjTMTdw==",
+			"version": "5.5.0",
+			"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.5.0.tgz",
+			"integrity": "sha512-nYhG+YzLcrO5Z7KbH1H/xvBeBzhCWhQG0z5DYNLuaJULhavZBmEgoQ5xVHGxwe65c5hnJQHZpBxWdQhnOBzHmw==",
 			"license": "MIT",
 			"license": "MIT",
 			"dependencies": {
 			"dependencies": {
 				"@vue-macros/common": "^3.1.3",
 				"@vue-macros/common": "^3.1.3",

+ 2 - 2
frontend/package.json

@@ -31,10 +31,10 @@
 		"@xterm/addon-web-links": "^0.12.0",
 		"@xterm/addon-web-links": "^0.12.0",
 		"@xterm/xterm": "^6.0.0",
 		"@xterm/xterm": "^6.0.0",
 		"iconify-icon": "^3.0.4",
 		"iconify-icon": "^3.0.4",
-		"picocrank": "^1.40.0",
+		"picocrank": "^1.41.0",
 		"vue": "^3.5.44",
 		"vue": "^3.5.44",
 		"vue-i18n": "^11.4.16",
 		"vue-i18n": "^11.4.16",
-		"vue-router": "^5.4.0"
+		"vue-router": "^5.5.0"
 	},
 	},
 	"engines": {
 	"engines": {
 		"node": "^22.13.0 || >=24"
 		"node": "^22.13.0 || >=24"

+ 159 - 160
frontend/resources/vue/App.vue

@@ -1,38 +1,32 @@
 <template>
 <template>
-  <div class="layout-fixed-header">
-    <Header
-      :title="pageTitle"
-      :logo-url="logoUrl"
-      :breadcrumbs="false"
-      :sidebar-enabled="sidebarEnabled"
-      :top-bar-enabled="topbarEnabled"
-      :navigation="navigation"
-      :username="headerUsername"
-      :login-route="headerLoginRoute"
-      @toggle-sidebar="toggleSidebar"
-      @user-click="goToUserControlPanel"
-    >
-      <template #toolbar>
-        <QuickSearch
-          v-if="!loginRequired && headerSearchEnabled"
-          :items="searchIndexItems"
-          :auto-import-routes="false"
-          :search-fields="['title', 'description', 'category']"
-          :max-results="15"
-          placeholder="Search actions, dashboards, entities…"
-        />
-        <div
-          v-if="bannerMessage"
-          id="banner"
-          :style="bannerCss"
-        >
-          <p>{{ bannerMessage }}</p>
-        </div>
-        <ConnectionBanner />
-      </template>
-    </Header>
-
-    <div id="layout">
+  <AppShell class="layout-fixed-header">
+    <template #header>
+      <Header
+        :title="pageTitle"
+        :logo-url="logoUrl"
+        :breadcrumbs="false"
+        :sidebar-enabled="sidebarEnabled"
+        :top-bar-enabled="topbarEnabled"
+        :navigation="navigation"
+        :username="headerUsername"
+        :login-route="headerLoginRoute"
+        @toggle-sidebar="toggleSidebar"
+        @user-click="goToUserControlPanel"
+      >
+        <template #toolbar>
+          <QuickSearch
+            v-if="!loginRequired && headerSearchEnabled"
+            :items="searchIndexItems"
+            :auto-import-routes="false"
+            :search-fields="['title', 'description', 'category']"
+            :max-results="15"
+            placeholder="Search actions, dashboards, entities…"
+          />
+        </template>
+      </Header>
+    </template>
+
+    <template #sidebar>
       <Navigation ref="navigation">
       <Navigation ref="navigation">
         <Sidebar
         <Sidebar
           v-if="sidebarEnabled && showNavigation"
           v-if="sidebarEnabled && showNavigation"
@@ -40,140 +34,144 @@
           ref="sidebar"
           ref="sidebar"
         />
         />
       </Navigation>
       </Navigation>
-
-      <div
-        id="content"
-        :initial-martial-complete="hasLoaded"
-      >
-        <main title="Main content">
-          <router-view :key="$route.fullPath" />
-        </main>
-
-        <footer
-          v-if="showFooter"
-          title="footer"
+    </template>
+
+    <template #banners>
+      <NotificationBlock
+        v-if="bannerMessage"
+        id="banner"
+        type="info"
+        :message="bannerMessage"
+        :style="bannerCss"
+      />
+      <ConnectionBanner />
+    </template>
+
+    <router-view :key="$route.fullPath" />
+
+    <template
+      v-if="showFooter"
+      #footer
+    >
+      <p>
+        <img
+          title="application icon"
+          :src="logoUrl"
+          alt="OliveTin logo"
+          style="height: 1em;"
+          class="logo"
         >
         >
-          <p>
-            <img
-              title="application icon"
-              :src="logoUrl"
-              alt="OliveTin logo"
-              style="height: 1em;"
-              class="logo"
-            >
-            OliveTin <span v-if="showVersionNumber">{{ currentVersion }}</span>
-          </p>
-          <p>
-            <span>
-              <a
-                href="https://docs.olivetin.app"
-                target="_new"
-              >{{ t('docs') }}</a>
-            </span>
-
-            <span>
-              <a
-                href="https://github.com/OliveTin/OliveTin/issues/new/choose"
-                target="_new"
-              >{{ t('raise-issue') }}</a>
-            </span>
-
-            <span>
-              <a
-                href="#"
-                @click.prevent="openLanguageDialog"
-              >{{ currentLanguageName }}</a>
-            </span>
-
-            <span v-if="availableThemes.length > 1">
-              <a
-                href="#"
-                @click.prevent="openThemeDialog"
-              >{{ currentThemeName }}</a>
-            </span>
-          </p>
-          <p v-if="showVersionNumber">
-            <a
-              id="available-version"
-              href="http://olivetin.app"
-              target="_blank"
-              hidden
-            >?</a>
-          </p>
-        </footer>
-      </div>
-    </div>
-
-    <dialog
-      ref="languageDialog"
-      class="language-dialog"
-      @click="handleLanguageDialogClick"
+        OliveTin <span v-if="showVersionNumber">{{ currentVersion }}</span>
+      </p>
+      <p>
+        <span>
+          <a
+            href="https://docs.olivetin.app"
+            target="_new"
+          >{{ t('docs') }}</a>
+        </span>
+
+        <span>
+          <a
+            href="https://github.com/OliveTin/OliveTin/issues/new/choose"
+            target="_new"
+          >{{ t('raise-issue') }}</a>
+        </span>
+
+        <span>
+          <a
+            href="#"
+            @click.prevent="openLanguageDialog"
+          >{{ currentLanguageName }}</a>
+        </span>
+
+        <span v-if="availableThemes.length > 1">
+          <a
+            href="#"
+            @click.prevent="openThemeDialog"
+          >{{ currentThemeName }}</a>
+        </span>
+      </p>
+      <p v-if="showVersionNumber">
+        <a
+          id="available-version"
+          href="http://olivetin.app"
+          target="_blank"
+          hidden
+        >?</a>
+      </p>
+    </template>
+  </AppShell>
+
+  <dialog
+    ref="languageDialog"
+    class="language-dialog"
+    @click="handleLanguageDialogClick"
+  >
+    <div
+      class="dialog-content"
+      @click.stop
     >
     >
-      <div
-        class="dialog-content"
-        @click.stop
+      <h2>{{ t('language-dialog.title') }}</h2>
+      <select
+        v-model="selectedLanguage"
+        class="language-select"
+        @change="changeLanguage"
       >
       >
-        <h2>{{ t('language-dialog.title') }}</h2>
-        <select
-          v-model="selectedLanguage"
-          class="language-select"
-          @change="changeLanguage"
+        <option
+          v-for="(name, code) in availableLanguages"
+          :key="code"
+          :value="code"
         >
         >
-          <option
-            v-for="(name, code) in availableLanguages"
-            :key="code"
-            :value="code"
-          >
-            {{ code === 'auto' ? name : `${name} (${code})` }}
-          </option>
-        </select>
-        <p class="browser-languages">
-          {{ t('language-dialog.browser-languages') }}:
-          <span v-if="browserLanguages.length > 0">{{ browserLanguages.join(', ') }}</span>
-          <span v-else>{{ t('language-dialog.not-available') }}</span>
-        </p>
-        <div class="dialog-buttons">
-          <button @click="closeLanguageDialog">
-            {{ t('language-dialog.close') }}
-          </button>
-        </div>
+          {{ code === 'auto' ? name : `${name} (${code})` }}
+        </option>
+      </select>
+      <p class="browser-languages">
+        {{ t('language-dialog.browser-languages') }}:
+        <span v-if="browserLanguages.length > 0">{{ browserLanguages.join(', ') }}</span>
+        <span v-else>{{ t('language-dialog.not-available') }}</span>
+      </p>
+      <div class="dialog-buttons">
+        <button @click="closeLanguageDialog">
+          {{ t('language-dialog.close') }}
+        </button>
       </div>
       </div>
-    </dialog>
-
-    <dialog
-      ref="themeDialog"
-      class="theme-dialog"
-      @click="handleThemeDialogClick"
+    </div>
+  </dialog>
+
+  <dialog
+    ref="themeDialog"
+    class="theme-dialog"
+    @click="handleThemeDialogClick"
+  >
+    <div
+      class="dialog-content"
+      @click.stop
     >
     >
-      <div
-        class="dialog-content"
-        @click.stop
+      <h2>{{ t('theme-dialog.title') }}</h2>
+      <select
+        v-model="selectedTheme"
+        class="language-select"
+        @change="changeTheme"
       >
       >
-        <h2>{{ t('theme-dialog.title') }}</h2>
-        <select
-          v-model="selectedTheme"
-          class="language-select"
-          @change="changeTheme"
+        <option value="">
+          {{ t('theme-dialog.default') }}
+        </option>
+        <option
+          v-for="theme in availableThemes"
+          :key="theme"
+          :value="theme"
         >
         >
-          <option value="">
-            {{ t('theme-dialog.default') }}
-          </option>
-          <option
-            v-for="theme in availableThemes"
-            :key="theme"
-            :value="theme"
-          >
-            {{ theme }}
-          </option>
-        </select>
-        <div class="dialog-buttons">
-          <button @click="closeThemeDialog">
-            {{ t('theme-dialog.close') }}
-          </button>
-        </div>
+          {{ theme }}
+        </option>
+      </select>
+      <div class="dialog-buttons">
+        <button @click="closeThemeDialog">
+          {{ t('theme-dialog.close') }}
+        </button>
       </div>
       </div>
-    </dialog>
-  </div>
+    </div>
+  </dialog>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
@@ -182,6 +180,8 @@ import { useRouter } from 'vue-router'
 import Sidebar from 'picocrank/vue/components/Sidebar.vue'
 import Sidebar from 'picocrank/vue/components/Sidebar.vue'
 import Navigation from 'picocrank/vue/components/Navigation.vue'
 import Navigation from 'picocrank/vue/components/Navigation.vue'
 import Header from 'picocrank/vue/components/Header.vue'
 import Header from 'picocrank/vue/components/Header.vue'
+import AppShell from 'picocrank/vue/components/AppShell.vue'
+import NotificationBlock from 'picocrank/vue/components/NotificationBlock.vue'
 import QuickSearch from 'picocrank/vue/components/QuickSearch.vue'
 import QuickSearch from 'picocrank/vue/components/QuickSearch.vue'
 import ConnectionBanner from './components/ConnectionBanner.vue'
 import ConnectionBanner from './components/ConnectionBanner.vue'
 import { connectEventStreamIfNeeded } from '../../js/websocket.js'
 import { connectEventStreamIfNeeded } from '../../js/websocket.js'
@@ -203,7 +203,6 @@ const currentVersion = ref('?')
 const pageTitle = ref('OliveTin')
 const pageTitle = ref('OliveTin')
 const bannerMessage = ref('')
 const bannerMessage = ref('')
 const bannerCss = ref('')
 const bannerCss = ref('')
-const hasLoaded = ref(false)
 const showFooter = ref(true)
 const showFooter = ref(true)
 const showNavigation = ref(true)
 const showNavigation = ref(true)
 const showLogs = ref(true)
 const showLogs = ref(true)

+ 7 - 11
frontend/resources/vue/components/ConnectionBanner.vue

@@ -1,13 +1,12 @@
 <template>
 <template>
-  <span
+  <NotificationBlock
     v-if="connectionState.showDisconnectedBanner"
     v-if="connectionState.showDisconnectedBanner"
     id="connection-banner"
     id="connection-banner"
-    class="inline-notification critical user-info-connection"
+    type="severe"
+    role="status"
+    class="user-info-connection"
   >
   >
-    <span
-      class="connection-banner-sr-only"
-      role="status"
-    >{{ staticAnnouncement }}</span>
+    <span class="connection-banner-sr-only">{{ staticAnnouncement }}</span>
     <span aria-hidden="true">
     <span aria-hidden="true">
       <a
       <a
         :href="websocketDocsUrl"
         :href="websocketDocsUrl"
@@ -16,12 +15,13 @@
         class="connection-banner-link"
         class="connection-banner-link"
       >{{ linkText }}</a>{{ bannerSuffix }}
       >{{ linkText }}</a>{{ bannerSuffix }}
     </span>
     </span>
-  </span>
+  </NotificationBlock>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
 import { ref, computed, watch, onUnmounted } from 'vue'
 import { ref, computed, watch, onUnmounted } from 'vue'
 import { useI18n } from 'vue-i18n'
 import { useI18n } from 'vue-i18n'
+import NotificationBlock from 'picocrank/vue/components/NotificationBlock.vue'
 import { connectionState } from '../stores/connectionState.js'
 import { connectionState } from '../stores/connectionState.js'
 
 
 const { t } = useI18n()
 const { t } = useI18n()
@@ -84,10 +84,6 @@ const bannerSuffix = computed(() => {
 #connection-banner.user-info-connection {
 #connection-banner.user-info-connection {
     font-weight: 500;
     font-weight: 500;
 }
 }
-.inline-notification {
-    border: 0;
-    margin: 0;
-}
 .connection-banner-link {
 .connection-banner-link {
     color: inherit;
     color: inherit;
     text-decoration: underline;
     text-decoration: underline;