From e2fc1d24cf478cf1cb68b915d27cf3d4775f682a Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Mon, 22 Jun 2026 21:50:12 +0200 Subject: [PATCH] feat(phase-c): Vite + Svelte 5 Infrastruktur + erste 2 Panels migriert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - frontend/ eingerichtet: Vite 6, Svelte 5 (Runes), TypeScript - Stores: status/jobs/system mit $state-Runes (status.svelte.ts etc.) - main.ts ersetzt main.js: bundelt Legacy-Panels (overview/models/server/ jobs/cookbook/connect) + mountet neue Svelte-Panels (guides/news) - GuidesPanel.svelte: statischer Guide mit Akkordeon-Tutorials + Konzept-Grid - NewsPanel.svelte: reaktives Newsboard mit API-Fetch + Upgrade-Vorschlaegen - Vite-Output: static/dist/main.js (committet, kein Build auf dem Server) - index.html: laedt jetzt static/dist/main.js statt static/js/main.js - Build: 118 Module → 134 kB (gzip: 44 kB) Naechste Schritte: Overview → Jobs → Server → Models → Cookbook migrieren. Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 + frontend/package-lock.json | 1449 ++++++++++++++++++++++++ frontend/package.json | 18 + frontend/src/main.ts | 185 +++ frontend/src/panels/GuidesPanel.svelte | 30 + frontend/src/panels/NewsPanel.svelte | 114 ++ frontend/src/panels/guides-data.ts | 86 ++ frontend/src/stores/jobs.svelte.ts | 6 + frontend/src/stores/status.svelte.ts | 6 + frontend/src/stores/system.svelte.ts | 6 + frontend/svelte.config.js | 8 + frontend/tsconfig.json | 17 + frontend/vite.config.ts | 28 + static/dist/main.js | 557 +++++++++ static/index.html | 2 +- 15 files changed, 2512 insertions(+), 1 deletion(-) create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/main.ts create mode 100644 frontend/src/panels/GuidesPanel.svelte create mode 100644 frontend/src/panels/NewsPanel.svelte create mode 100644 frontend/src/panels/guides-data.ts create mode 100644 frontend/src/stores/jobs.svelte.ts create mode 100644 frontend/src/stores/status.svelte.ts create mode 100644 frontend/src/stores/system.svelte.ts create mode 100644 frontend/svelte.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts create mode 100644 static/dist/main.js diff --git a/.gitignore b/.gitignore index 626bcbc..8c6f7eb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ __pycache__/ *.pyc .claude/launch.json +frontend/node_modules/ diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..6d6edbc --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,1449 @@ +{ + "name": "mission-control-frontend", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mission-control-frontend", + "dependencies": { + "svelte": "^5.28.0" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "typescript": "^5.7.3", + "vite": "^6.3.5" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.10.tgz", + "integrity": "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz", + "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", + "debug": "^4.4.1", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.17", + "vitefu": "^1.0.6" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz", + "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.7" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/devalue": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", + "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.12.tgz", + "integrity": "sha512-On0QbLyaiAkVC4eXtgnXK9Kh2opit+3rcUSOc45DqJ2s/X2eXAHsGOKRSJ6IDagQEW5vPyivANfXUiqgXC67Rw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svelte": { + "version": "5.56.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.3.tgz", + "integrity": "sha512-w7JvrM5IFl5cmfbY0TLik9o7mjRUJmRMhOR51tBPu708Gr/MjbGs7VnJnr/B0CaXeI4vtnOh7RKxDr0cwhMdDA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.10", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.8.1", + "esm-env": "^1.2.1", + "esrap": "^2.2.11", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "license": "MIT" + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..e79be50 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,18 @@ +{ + "name": "mission-control-frontend", + "type": "module", + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "svelte": "^5.28.0" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "typescript": "^5.7.3", + "vite": "^6.3.5" + } +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..487afb7 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,185 @@ +import { mount } from 'svelte' +import { api, getToken, setToken, getHfToken, setHfToken } from '@core/api.js' +import { initNav } from '@core/nav.js' +import { statusStore } from './stores/status.svelte.js' +import { jobsStore } from './stores/jobs.svelte.js' +import GuidesPanel from './panels/GuidesPanel.svelte' +import NewsPanel from './panels/NewsPanel.svelte' + +// Legacy panels (bundled via Vite; guides + news sind durch Svelte ersetzt) +import overview from '@panels/overview.js' +import models from '@panels/models.js' +import server from '@panels/server.js' +import jobs from '@panels/jobs.js' +import cookbook from '@panels/cookbook.js' +import connect from '@panels/connect.js' + +const legacyPanels: any[] = [overview, models, server, jobs, cookbook, connect] +let prevModelStates: Record = {} + +// ---- Swap-Flash ---- +function flashSwap() { + const el = document.getElementById('top-active-text') + if (el) { el.classList.add('swap-flash'); setTimeout(() => el.classList.remove('swap-flash'), 900) } +} + +// ---- Status verteilen ---- +function applyStatus(s: any) { + const dot = document.getElementById('swdot') + const label = document.getElementById('swlabel') + const activeText = document.getElementById('top-active-text') + + if (!s) { + dot && (dot.className = 'dot off') + label && (label.textContent = 'Backend nicht erreichbar') + activeText && (activeText.textContent = 'Backend offline') + showAlert('Backend nicht erreichbar – läuft uvicorn?', false) + } else { + const host = (s.swap_url || '').replace(/^https?:\/\//, '') + dot && (dot.className = 'dot ' + (s.swap_ok ? 'on' : 'off')) + label && (label.textContent = (s.swap_ok ? 'LLM-Engine: Online – ' : 'LLM-Engine: Offline – ') + host) + const active = (s.models || []).find((m: any) => m.state === 'running') + if (activeText) { + activeText.innerHTML = active + ? `Geladen: ${active.name}` + : 'Kein Modell im VRAM' + } + if (s.swap_ok) hideAlert() + else showAlert(`LLM-Engine nicht erreichbar unter ${host} – läuft der llama-swap Dienst?`, true) + + // Swap-Erkennung + const newStates: Record = {} + for (const m of (s.models || [])) newStates[m.name] = m.state + for (const [name, state] of Object.entries(newStates)) { + const prev = prevModelStates[name] + if (prev && prev !== state && (state === 'running' || state === 'loading')) { flashSwap(); break } + } + prevModelStates = newStates + } + setSecChip(s) + statusStore.set(s) + for (const p of legacyPanels) p.onStatus?.(s) +} + +function setSecChip(s: any) { + const chip = document.getElementById('sec-chip') + const tx = document.getElementById('sec-chip-tx') + if (!chip || !tx) return + chip.classList.toggle('ok', !!s?.secured) + tx.textContent = s?.secured ? 'Geschützt · Heimnetz' : 'Nur im Heimnetz (offen)' +} + +function applyJobs(jobs: any[]) { + jobsStore.set(jobs || []) + for (const p of legacyPanels) p.onJobs?.(jobs) +} + +function applySystem(sys: any) { + for (const p of legacyPanels) p.onSystem?.(sys) +} + +function showAlert(html: string, warn: boolean) { + const a = document.getElementById('alert')! + a.className = 'alert' + (warn ? ' warn' : '') + a.innerHTML = `${html}` + a.style.display = 'flex' +} +function hideAlert() { + const a = document.getElementById('alert') + if (a) a.style.display = 'none' +} + +// ---- Update-Badges ---- +const goView = (v: string) => (document.querySelector(`.nav-item[data-view="${v}"]`) as HTMLElement)?.click() + +function mkBadge(text: string, cls: string, view: string, title?: string) { + const s = document.createElement('span') + s.className = 'upd-badge' + (cls ? ' ' + cls : '') + s.textContent = text + if (title) s.title = title + s.onclick = () => goView(view) + return s +} + +async function pollUpdates() { + try { + const u = await api('/api/updates') + const el = document.getElementById('update-badges'); if (!el) return + const age = u.apt_cache_age_h as number | null + const stale = age != null && age > 24 + const ageNote = age != null ? ` · apt-Cache: vor ${age}h` : '' + el.innerHTML = '' + el.appendChild(mkBadge( + u.os > 0 ? `OS: ${u.os} Updates` : stale ? 'OS: ?' : 'OS: aktuell', + u.os > 0 ? 'warn' : stale ? 'warn' : 'ok', + 'server', + u.os > 0 ? `${u.os} ausstehende Pakete${ageNote}` : stale ? `apt-Cache veraltet (${age}h)` : `Keine Updates${ageNote}` + )) + el.appendChild(mkBadge(u.engine > 0 ? 'Engine: Update' : 'Engine: aktuell', u.engine > 0 ? 'warn' : 'ok', 'server')) + el.appendChild(mkBadge(u.models > 0 ? `Modelle: ${u.models}` : 'Modelle: aktuell', u.models > 0 ? 'warn' : 'ok', 'news')) + } catch { /* noop */ } +} + +// ---- Polling ---- +async function pollStatus() { + try { applyStatus(await api('/api/status')) } + catch { applyStatus(null) } +} +async function pollJobs() { + try { applyJobs(await api('/api/jobs')) } + catch { /* noop */ } +} + +function connectSystemStream() { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' + const ws = new WebSocket(`${protocol}//${window.location.host}/api/system/stream`) + ws.onmessage = e => { try { applySystem(JSON.parse(e.data)) } catch {} } + ws.onclose = () => { setTimeout(connectSystemStream, 3000) } + ws.onerror = () => ws.close() +} + +// ---- Boot ---- + +// Legacy panels mounten (overview, models, server, jobs, cookbook, connect) +for (const p of legacyPanels) p.mount?.() + +// Svelte panels mounten (guides, news) +const guidesEl = document.querySelector('.view[data-view="guides"]') +if (guidesEl) mount(GuidesPanel, { target: guidesEl }) + +const newsEl = document.querySelector('.view[data-view="news"]') +if (newsEl) mount(NewsPanel, { target: newsEl }) + +initNav('overview') + +// Token / Settings +const tokenInput = document.getElementById('token') as HTMLInputElement | null +if (tokenInput) { + tokenInput.value = getToken() + tokenInput.addEventListener('change', e => { setToken((e.target as HTMLInputElement).value); pollStatus() }) +} +const hfInput = document.getElementById('hf-token') as HTMLInputElement | null +if (hfInput) { + hfInput.value = getHfToken() + hfInput.addEventListener('change', e => setHfToken((e.target as HTMLInputElement).value)) +} +const sbtn = document.getElementById('nav-settings') +const smod = document.getElementById('settings-modal') +const scls = document.getElementById('sm-close') +if (sbtn && smod && scls) { + sbtn.addEventListener('click', () => (smod!.style.display = 'flex')) + scls.addEventListener('click', () => (smod!.style.display = 'none')) +} + +function tickClock() { + const c = document.getElementById('clock') + if (c) c.textContent = new Date().toTimeString().slice(0, 5) +} + +document.addEventListener('mc:refresh', pollStatus as EventListener) +pollStatus(); pollJobs(); connectSystemStream() +setInterval(tickClock, 1000) +setInterval(pollStatus, 3000) +setInterval(pollJobs, 1500) +pollUpdates() +setInterval(pollUpdates, 60000) diff --git a/frontend/src/panels/GuidesPanel.svelte b/frontend/src/panels/GuidesPanel.svelte new file mode 100644 index 0000000..bc37d30 --- /dev/null +++ b/frontend/src/panels/GuidesPanel.svelte @@ -0,0 +1,30 @@ + + +
+
+

Guide für Einsteiger

+
Praktische Workflows + die Local-LLM-Welt in Klartext — ohne Vorwissen.
+
+
+ +

Tutorials & Workflows

+
+ {#each TUTORIALS as [title, body], i} +
+ {title} +
{@html body}
+
+ {/each} +
+ +

Konzepte kurz erklärt

+
+ {#each CONCEPTS as [term, text]} +
+

{term}

+

{text}

+
+ {/each} +
diff --git a/frontend/src/panels/NewsPanel.svelte b/frontend/src/panels/NewsPanel.svelte new file mode 100644 index 0000000..852242a --- /dev/null +++ b/frontend/src/panels/NewsPanel.svelte @@ -0,0 +1,114 @@ + + +
+
+

News

+
Aktuelles aus der Local-LLM-Welt — aus kuratierten Quellen.
+
+ +
+ +{#if upgrades.length} +
+
+

Für dich: Upgrades

+ {upgrades.length} +
+
Neuere Modelle, die ein installiertes ersetzen können.
+
+ {#each upgrades as u} +
+
+
+ {u.name} + {u.fit.text} +
+
Ersetzt dein „{u.old}". {u.why}
+
+ +
+ {/each} +
+
+{/if} + +{#if loadingNews} +
Lade News… (erstes Laden kann kurz dauern)
+{:else if newsError} +
{newsError}
+{:else if !items.length} +
+
Keine News geladen
+
Die Quellen sind gerade nicht erreichbar — später nochmal probieren.
+
+{:else} + +{/if} diff --git a/frontend/src/panels/guides-data.ts b/frontend/src/panels/guides-data.ts new file mode 100644 index 0000000..db38287 --- /dev/null +++ b/frontend/src/panels/guides-data.ts @@ -0,0 +1,86 @@ +export const TUTORIALS: [string, string][] = [ + ["Dein erster Workflow — in 3 Schritten", ` +
    +
  1. Modell holen: Tab Cookbook → wähle deinen Anwendungsfall (z.B. „Coden") → + Komplettes Setup installieren. Der Download läuft im Tab Aktivität.
  2. +
  3. Verbinden: Tab Verbinden → Basis-URL kopieren → in deinem Tool (z.B. OpenCode) + als „OpenAI-kompatibel" einfügen. Verbindung testen bestätigt, dass es läuft.
  4. +
  5. Arbeiten: Im Tool das Modell (z.B. coder) wählen und loslegen. Das passende + Modell wird automatisch geladen — du musst nichts starten.
  6. +
`], + ["Wie leere ich den Kontext? Machen die Tools das selbst?", ` +

Kurz: meistens ja, automatisch. Der Kontext (das „Gedächtnis") gilt pro Unterhaltung. + Startest du im Tool einen neuen Chat / eine neue Session, ist der Kontext wieder leer — + das Tool verwaltet das Fenster für dich.

+

Du musst nur selten eingreifen: wird ein Gespräch sehr lang, fang einen neuen an. Wichtig: + „Grafikspeicher leeren" (Tab Server) ist etwas anderes — das wirft das Modell aus dem Speicher, + nicht den Gesprächsverlauf.

`], + ["Was passiert, wenn der Kontext volläuft?", ` +

Ist das Kontextfenster voll, passiert je nach Tool eins von dreien: das Modell vergisst den + Anfang (älteste Nachrichten fallen raus), das Tool kürzt automatisch, oder du bekommst + einen Fehler wie „context length exceeded".

+

So gehst du vor:

+
    +
  1. Neuen Chat starten — der einfachste Reset, Kontext ist wieder leer.
  2. +
  3. Zusammenfassen lassen: „Fasse unseren Stand kurz zusammen", dann im neuen Chat einfügen.
  4. +
  5. Kontext vergrößern: Tab ModelleKonfigurieren → wir zeigen das Optimum + für deine Hardware (mehr Kontext = mehr Speicherbedarf).
  6. +
`], + ["Was ist ein Agent — und wann brauche ich einen?", ` +

Ein nacktes Modell schreibt nur Text zurück. Ein Agent ist ein Programm drumherum, + das das Modell handeln lässt: Dateien lesen und ändern, Befehle ausführen, im Web suchen — + Schritt für Schritt in einer Schleife, bis die Aufgabe erledigt ist.

+

Wann lohnt sich was?

+
    +
  • Fragen, Texte, Erklärungen, Brainstorming → kein Agent nötig, eine Chat-Oberfläche + wie OpenWebUI reicht völlig.
  • +
  • Coden über mehrere Dateien, Befehle ausführen, recherchieren + umsetzen + → Agent (OpenCode, Cline, Continue).
  • +
+

Gut zu wissen: Diese Agenten laufen lokal auf deinem PC — du brauchst + keinen Cloud-Dienst.

`], + ["Was sind Skills — und wie installiere ich sie?", ` +

Ein Skill ist verpacktes Können: eine kurze Anleitung, die dem Agenten beibringt, + eine wiederkehrende Aufgabe jedes Mal so zu erledigen, wie du es willst.

+

Skill vs. MCP: MCP gibt dem Agenten neue Werkzeuge. Skills geben + ihm Know-how. Kurz: Werkzeuge = MCP, Anleitungen = Skills.

+

Installieren: meist nur eine Datei ablegen — kein Build, kein Neustart:

+
    +
  1. Pro Projekt: in den Projektordner (z.B. AGENTS.md, .clinerules/).
  2. +
  3. Global: in den Konfig-Ordner des Tools — gilt überall.
  4. +
`], + ["MCP — deinem Agenten echte Werkzeuge geben", ` +

MCP (Model Context Protocol) ist ein einheitlicher „Stecker", über den ein Agent zusätzliche + Werkzeuge bekommt: das Web durchsuchen, Dateien lesen, eine Datenbank abfragen.

+

Du fügst in deinem Agenten einen „MCP-Server" hinzu (eine Zeile in der Konfiguration je Werkzeug). + Danach entscheidet der Agent selbst, wann er das Werkzeug einsetzt.

+
    +
  • Faustregel: Fehlt dem Agenten eine Fähigkeit → MCP. Soll er etwas nur + besser machen → Skill.
  • +
`], + ["Auto-Swap — wie das Laden funktioniert", ` +

llama-swap lädt automatisch das Modell, das gerade angefragt wird, und entlädt es nach einer + Leerlaufzeit wieder. Es läuft immer nur eines gleichzeitig.

+

Du richtest beliebig viele Modelle ein und wählst im Tool das passende — + das Laden/Entladen passiert im Hintergrund, ohne dass du etwas tun musst.

`], + ["Die empfohlenen Tools optimal nutzen", ` +
    +
  • OpenCode: Autonomes Coden im Terminal/Editor — ideal für „bau mir Feature X".
  • +
  • Cline: Dasselbe als VS-Code-Erweiterung, mit Bestätigung vor jeder Aktion.
  • +
  • OpenWebUI: Klassische Chat-Oberfläche für Fragen/Texte.
  • +
+

Tipp: starkes Modell (coder) für schwere Aufgaben, schnelles + (scout) für einfache — du wählst es einfach im Tool aus.

`], +] + +export const CONCEPTS: [string, string][] = [ + ["LLM / Modell", "Die KI selbst — versteht und schreibt Text (wie ChatGPT, nur lokal auf deinem PC). Ein Modell ist eine konkrete Datei davon, oft auf etwas spezialisiert (Code, Bilder, Chat)."], + ["GGUF", "Das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF kann sie nicht nutzen."], + ["Quantisierung (Q4_K_M)", "Verkleinert ein Modell (16-Bit → 4-Bit), damit es in den Speicher passt — bei kaum Qualitätsverlust. Q4_K_M ist der beliebte Sweetspot."], + ["Kontextfenster", "Das Kurzzeitgedächtnis: wie viel Text sich das Modell gleichzeitig merkt. Größer = mehr Überblick, mehr Speicher, etwas langsamer."], + ["Auto-Swap", "llama-swap lädt/entlädt Modelle automatisch — immer nur eins gleichzeitig. Du managst nichts."], + ["Agent", "Ein Programm (OpenCode, Cline, Continue), das das Modell handeln lässt — Dateien lesen/ändern, Befehle ausführen, planen, im Loop bis die Aufgabe erledigt ist."], + ["MCP", "Model Context Protocol — der Anschluss, über den ein Agent zusätzliche Werkzeuge bekommt (Web, Dateien, Datenbank). MCP = Werkzeuge."], + ["Skills", "Verpackte Anleitung für wiederkehrende Aufgaben — der Agent liest sie und macht es jedes Mal wie du willst. Skills = Anleitungen."], + ["Rules / Commands", "Die konkrete Form von Skills je Tool: AGENTS.md (OpenCode), .clinerules (Cline), Rules/Prompts (Continue)."], +] diff --git a/frontend/src/stores/jobs.svelte.ts b/frontend/src/stores/jobs.svelte.ts new file mode 100644 index 0000000..f3f45c1 --- /dev/null +++ b/frontend/src/stores/jobs.svelte.ts @@ -0,0 +1,6 @@ +let _value = $state([]) + +export const jobsStore = { + get value() { return _value }, + set(jobs: any[]) { _value = jobs || [] }, +} diff --git a/frontend/src/stores/status.svelte.ts b/frontend/src/stores/status.svelte.ts new file mode 100644 index 0000000..25faaff --- /dev/null +++ b/frontend/src/stores/status.svelte.ts @@ -0,0 +1,6 @@ +let _value = $state(null) + +export const statusStore = { + get value() { return _value }, + set(s: any) { _value = s }, +} diff --git a/frontend/src/stores/system.svelte.ts b/frontend/src/stores/system.svelte.ts new file mode 100644 index 0000000..02c5285 --- /dev/null +++ b/frontend/src/stores/system.svelte.ts @@ -0,0 +1,6 @@ +let _value = $state(null) + +export const systemStore = { + get value() { return _value }, + set(sys: any) { _value = sys }, +} diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js new file mode 100644 index 0000000..64c5130 --- /dev/null +++ b/frontend/svelte.config.js @@ -0,0 +1,8 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' + +export default { + preprocess: vitePreprocess(), + compilerOptions: { + runes: true, + }, +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..59d9f9e --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "strict": false, + "noEmit": true, + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "allowJs": true, + "checkJs": false + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..ada8064 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,28 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' +import { resolve } from 'path' + +export default defineConfig({ + plugins: [svelte()], + base: '/static/dist/', + build: { + outDir: resolve(__dirname, '../static/dist'), + emptyOutDir: true, + rollupOptions: { + input: resolve(__dirname, 'src/main.ts'), + output: { + entryFileNames: 'main.js', + chunkFileNames: '[name].js', + assetFileNames: '[name][extname]', + // Alles in eine Datei — kein dynamisches Chunking für einfaches Deploy + manualChunks: undefined, + }, + }, + }, + resolve: { + alias: { + '@core': resolve(__dirname, '../static/js/core'), + '@panels': resolve(__dirname, '../static/js/panels'), + }, + }, +}) diff --git a/static/dist/main.js b/static/dist/main.js new file mode 100644 index 0000000..c3cdb1e --- /dev/null +++ b/static/dist/main.js @@ -0,0 +1,557 @@ +var ir=Object.defineProperty;var ns=e=>{throw TypeError(e)};var sr=(e,t,n)=>t in e?ir(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var se=(e,t,n)=>sr(e,typeof t!="symbol"?t+"":t,n),oi=(e,t,n)=>t.has(e)||ns("Cannot "+n);var d=(e,t,n)=>(oi(e,t,"read from private field"),n?n.call(e):t.get(e)),_=(e,t,n)=>t.has(e)?ns("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),y=(e,t,n,i)=>(oi(e,t,"write to private field"),i?i.call(e,n):t.set(e,n),n),T=(e,t,n)=>(oi(e,t,"access private method"),n);var Is=Array.isArray,ar=Array.prototype.indexOf,Nn=Array.prototype.includes,Xn=Array.from,rr=Object.defineProperty,rn=Object.getOwnPropertyDescriptor,lr=Object.getOwnPropertyDescriptors,or=Object.prototype,dr=Array.prototype,Gs=Object.getPrototypeOf,is=Object.isExtensible;const cr=()=>{};function ur(e){for(var t=0;t{e=i,t=s});return{promise:n,resolve:e,reject:t}}function ss(e,t){if(Array.isArray(e))return e;if(!(Symbol.iterator in e))return Array.from(e);const n=[];for(const i of e)if(n.push(i),n.length===t)break;return n}const U=2,fn=4,ei=8,qs=1<<24,ke=16,$e=32,et=64,ki=128,ve=512,I=1024,q=2048,De=4096,Q=8192,me=16384,Ut=32768,as=1<<25,It=65536,In=1<<17,fr=1<<18,jt=1<<19,pr=1<<20,Ie=1<<25,yt=65536,Gn=1<<21,At=1<<22,Ze=1<<23,di=Symbol("$state"),Dn=Symbol("attributes"),Mi=Symbol("class"),hr=Symbol("style"),Yt=Symbol("text"),ti=new class extends Error{constructor(){super(...arguments);se(this,"name","StaleReactionError");se(this,"message","The reaction that called `getAbortSignal()` was re-run or destroyed")}};function vr(e){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function mr(){throw new Error("https://svelte.dev/e/async_derived_orphan")}function gr(e,t,n){throw new Error("https://svelte.dev/e/each_key_duplicate")}function br(e){throw new Error("https://svelte.dev/e/effect_in_teardown")}function wr(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")}function yr(e){throw new Error("https://svelte.dev/e/effect_orphan")}function xr(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")}function _r(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")}function kr(){throw new Error("https://svelte.dev/e/state_prototype_fixed")}function Mr(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")}function Sr(){throw new Error("https://svelte.dev/e/svelte_boundary_reset_onerror")}const $r=1,Er=2,Tr=16,Cr=1,Lr=2,N=Symbol("uninitialized"),Us="http://www.w3.org/1999/xhtml",Ar="http://www.w3.org/2000/svg",zr="http://www.w3.org/1998/Math/MathML";function Dr(){console.warn("https://svelte.dev/e/derived_inert")}function Pr(){console.warn("https://svelte.dev/e/svelte_boundary_reset_noop")}function js(e){return e===this.v}function Or(e,t){return e!=e?t==t:e!==t||e!==null&&typeof e=="object"||typeof e=="function"}function Vs(e){return!Or(e,this.v)}let Y=null;function Gt(e){Y=e}function Ws(e,t=!1,n){Y={p:Y,i:!1,c:null,e:null,s:e,x:null,r:E,l:null}}function Js(e){var t=Y,n=t.e;if(n!==null){t.e=null;for(var i of n)ga(i)}return t.i=!0,Y=t.p,{}}function Qs(){return!0}let Et=[];function Br(){var e=Et;Et=[],ur(e)}function zt(e){if(Et.length===0){var t=Et;queueMicrotask(()=>{t===Et&&Br()})}Et.push(e)}function Ys(e){var t=E;if(t===null)return S.f|=Ze,e;if((t.f&Ut)===0&&(t.f&fn)===0)throw e;Je(e,t)}function Je(e,t){if(!(t!==null&&(t.f&me)!==0)){for(;t!==null;){if((t.f&ki)!==0){if((t.f&Ut)===0)throw e;try{t.b.error(e);return}catch(n){e=n}}t=t.parent}throw e}}const Hr=-7169;function F(e,t){e.f=e.f&Hr|t}function Hi(e){(e.f&ve)!==0||e.deps===null?F(e,I):F(e,De)}function Zs(e){if(e!==null)for(const t of e)(t.f&U)===0||(t.f&yt)===0||(t.f^=yt,Zs(t.deps))}function Xs(e,t,n){(e.f&q)!==0?t.add(e):(e.f&De)!==0&&n.add(e),Zs(e.deps),F(e,I)}function Rr(e){let t=0,n=_t(0),i;return()=>{Gi()&&(M(n),cl(()=>(t===0&&(i=La(()=>e(()=>on(n)))),t+=1,()=>{zt(()=>{t-=1,t===0&&(i==null||i(),i=void 0,on(n))})})))}}var Fr=It|jt;function Nr(e,t,n,i){new Ir(e,t,n,i)}var ue,Bi,fe,ut,Z,pe,W,re,He,ft,Ve,Pt,vn,mn,Re,Qn,H,Gr,Kr,qr,Si,Pn,On,$i,Ei;class Ir{constructor(t,n,i,s){_(this,H);se(this,"parent");se(this,"is_pending",!1);se(this,"transform_error");_(this,ue);_(this,Bi,null);_(this,fe);_(this,ut);_(this,Z);_(this,pe,null);_(this,W,null);_(this,re,null);_(this,He,null);_(this,ft,0);_(this,Ve,0);_(this,Pt,!1);_(this,vn,new Set);_(this,mn,new Set);_(this,Re,null);_(this,Qn,Rr(()=>(y(this,Re,_t(d(this,ft))),()=>{y(this,Re,null)})));var a;y(this,ue,t),y(this,fe,n),y(this,ut,r=>{var o=E;o.b=this,o.f|=ki,i(r)}),this.parent=E.b,this.transform_error=s??((a=this.parent)==null?void 0:a.transform_error)??(r=>r),y(this,Z,Ki(()=>{T(this,H,Si).call(this)},Fr))}defer_effect(t){Xs(t,d(this,vn),d(this,mn))}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!d(this,fe).pending}update_pending_count(t,n){T(this,H,$i).call(this,t,n),y(this,ft,d(this,ft)+t),!(!d(this,Re)||d(this,Pt))&&(y(this,Pt,!0),zt(()=>{y(this,Pt,!1),d(this,Re)&&Kt(d(this,Re),d(this,ft))}))}get_effect_pending(){return d(this,Qn).call(this),M(d(this,Re))}error(t){if(!d(this,fe).onerror&&!d(this,fe).failed)throw t;x!=null&&x.is_fork?(d(this,pe)&&x.skip_effect(d(this,pe)),d(this,W)&&x.skip_effect(d(this,W)),d(this,re)&&x.skip_effect(d(this,re)),x.oncommit(()=>{T(this,H,Ei).call(this,t)})):T(this,H,Ei).call(this,t)}}ue=new WeakMap,Bi=new WeakMap,fe=new WeakMap,ut=new WeakMap,Z=new WeakMap,pe=new WeakMap,W=new WeakMap,re=new WeakMap,He=new WeakMap,ft=new WeakMap,Ve=new WeakMap,Pt=new WeakMap,vn=new WeakMap,mn=new WeakMap,Re=new WeakMap,Qn=new WeakMap,H=new WeakSet,Gr=function(){try{y(this,pe,he(()=>d(this,ut).call(this,d(this,ue))))}catch(t){this.error(t)}},Kr=function(t){const n=d(this,fe).failed;n&&y(this,re,he(()=>{n(d(this,ue),()=>t,()=>()=>{})}))},qr=function(){const t=d(this,fe).pending;t&&(this.is_pending=!0,y(this,W,he(()=>t(d(this,ue)))),zt(()=>{var n=y(this,He,document.createDocumentFragment()),i=gt();n.append(i),y(this,pe,T(this,H,On).call(this,()=>he(()=>d(this,ut).call(this,i)))),d(this,Ve)===0&&(d(this,ue).before(n),y(this,He,null),bt(d(this,W),()=>{y(this,W,null)}),T(this,H,Pn).call(this,x))}))},Si=function(){try{if(this.is_pending=this.has_pending_snippet(),y(this,Ve,0),y(this,ft,0),y(this,pe,he(()=>{d(this,ut).call(this,d(this,ue))})),d(this,Ve)>0){var t=y(this,He,document.createDocumentFragment());Ui(d(this,pe),t);const n=d(this,fe).pending;y(this,W,he(()=>n(d(this,ue))))}else T(this,H,Pn).call(this,x)}catch(n){this.error(n)}},Pn=function(t){this.is_pending=!1,t.transfer_effects(d(this,vn),d(this,mn))},On=function(t){var n=E,i=S,s=Y;Pe(d(this,Z)),ge(d(this,Z)),Gt(d(this,Z).ctx);try{return xt.ensure(),t()}catch(a){return Ys(a),null}finally{Pe(n),ge(i),Gt(s)}},$i=function(t,n){var i;if(!this.has_pending_snippet()){this.parent&&T(i=this.parent,H,$i).call(i,t,n);return}y(this,Ve,d(this,Ve)+t),d(this,Ve)===0&&(T(this,H,Pn).call(this,n),d(this,W)&&bt(d(this,W),()=>{y(this,W,null)}),d(this,He)&&(d(this,ue).before(d(this,He)),y(this,He,null)))},Ei=function(t){d(this,pe)&&(ie(d(this,pe)),y(this,pe,null)),d(this,W)&&(ie(d(this,W)),y(this,W,null)),d(this,re)&&(ie(d(this,re)),y(this,re,null));var n=d(this,fe).onerror;let i=d(this,fe).failed;var s=!1,a=!1;const r=()=>{if(s){Pr();return}s=!0,a&&Sr(),d(this,re)!==null&&bt(d(this,re),()=>{y(this,re,null)}),T(this,H,On).call(this,()=>{T(this,H,Si).call(this)})},o=c=>{try{a=!0,n==null||n(c,r),a=!1}catch(u){Je(u,d(this,Z)&&d(this,Z).parent)}i&&y(this,re,T(this,H,On).call(this,()=>{try{return he(()=>{var u=E;u.b=this,u.f|=ki,i(d(this,ue),()=>c,()=>r)})}catch(u){return Je(u,d(this,Z).parent),null}}))};zt(()=>{var c;try{c=this.transform_error(t)}catch(u){Je(u,d(this,Z)&&d(this,Z).parent);return}c!==null&&typeof c=="object"&&typeof c.then=="function"?c.then(o,u=>Je(u,d(this,Z)&&d(this,Z).parent)):o(c)})};function Ur(e,t,n,i){const s=Ri;var a=e.filter(m=>!m.settled),r=t.map(s);if(n.length===0&&a.length===0){i(r);return}var o=E,c=jr(),u=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(m=>m.promise)):null;function p(m){if((o.f&me)===0){c();try{i([...r,...m])}catch(h){Je(h,o)}Kn()}}var v=ea();if(n.length===0){u.then(()=>p([])).finally(v);return}function f(){Promise.all(n.map(m=>Vr(m))).then(p).catch(m=>Je(m,o)).finally(v)}u?u.then(()=>{c(),f(),Kn()}):f()}function jr(){var e=E,t=S,n=Y,i=x;return function(a=!0){Pe(e),ge(t),Gt(n),a&&(e.f&me)===0&&(i==null||i.activate(),i==null||i.apply())}}function Kn(e=!0){Pe(null),ge(null),Gt(null),e&&(x==null||x.deactivate())}function ea(){var e=E,t=e.b,n=x,i=!!(t!=null&&t.is_rendered());return t==null||t.update_pending_count(1,n),n.increment(i,e),()=>{t==null||t.update_pending_count(-1,n),n.decrement(i,e)}}function Ri(e){var t=U|q;return E!==null&&(E.f|=jt),{ctx:Y,deps:null,effects:null,equals:js,f:t,fn:e,reactions:null,rv:0,v:N,wv:0,parent:E,ac:null}}const Zt=Symbol("obsolete");function Vr(e,t,n){let i=E;i===null&&mr();var s=void 0,a=_t(N),r=!S,o=new Set;return dl(()=>{var m,h;var c=E,u=Ks();s=u.promise;try{Promise.resolve(e()).then(u.resolve,b=>{b!==ti&&u.reject(b)}).finally(Kn)}catch(b){u.reject(b),Kn()}var p=x;if(r){if((c.f&Ut)!==0)var v=ea();if((m=i.b)!=null&&m.is_rendered())(h=p.async_deriveds.get(c))==null||h.reject(Zt);else for(const b of o.values())b.reject(Zt);o.add(u),p.async_deriveds.set(c,u)}const f=(b,C=void 0)=>{v==null||v(),o.delete(u),C!==Zt&&(p.activate(),C?(a.f|=Ze,Kt(a,C)):((a.f&Ze)!==0&&(a.f^=Ze),Kt(a,b)),p.deactivate())};u.promise.then(f,b=>f(null,b||"unknown"))}),rl(()=>{for(const c of o)c.reject(Zt)}),new Promise(c=>{function u(p){function v(){p===s?c(a):u(s)}p.then(v,v)}u(s)})}function rs(e){const t=Ri(e);return ka(t),t}function Wr(e){const t=Ri(e);return t.equals=Vs,t}function Jr(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;nthis.schedule(i)){var i=d(this,Te).get(t);if(i){d(this,Te).delete(t);for(var s of i.d)F(s,q),n(s);for(s of i.m)F(s,De),n(s)}d(this,Nt).add(t)}capture(t,n,i=!1){t.v!==N&&!this.previous.has(t)&&this.previous.set(t,t.v),(t.f&Ze)===0&&(this.current.set(t,[n,i]),K==null||K.set(t,n)),this.is_fork||(t.v=n)}activate(){x=this}deactivate(){x=null,K=null}flush(){try{ui=!0,x=this,T(this,P,Xt).call(this)}finally{ls=0,Ti=null,Tt=null,Bn=null,ui=!1,x=null,K=null,mt.clear()}}discard(){var t;for(const n of d(this,Ht))n(this);d(this,Ht).clear();for(const n of this.async_deriveds.values())n.reject(Zt);T(this,P,en).call(this),(t=d(this,Ft))==null||t.resolve()}register_created_effect(t){d(this,gn).push(t)}increment(t,n){if(y(this,Rt,d(this,Rt)+1),t){let i=d(this,Fe).get(n)??0;d(this,Fe).set(n,i+1)}}decrement(t,n){if(y(this,Rt,d(this,Rt)-1),t){let i=d(this,Fe).get(n)??0;i===1?d(this,Fe).delete(n):d(this,Fe).set(n,i-1)}d(this,ht)||(y(this,ht,!0),zt(()=>{y(this,ht,!1),this.linked&&this.flush()}))}transfer_effects(t,n){for(const i of t)d(this,Ne).add(i);for(const i of n)d(this,xe).add(i);t.clear(),n.clear()}oncommit(t){d(this,Bt).add(t)}ondiscard(t){d(this,Ht).add(t)}settled(){return(d(this,Ft)??y(this,Ft,Ks())).promise}static ensure(){if(x===null){const t=x=new Yn;ui||zt(()=>{d(t,Ot)||t.flush()})}return x}apply(){{K=null;return}}schedule(t){var s;if(Ti=t,(s=t.b)!=null&&s.is_pending&&(t.f&(fn|ei|qs))!==0&&(t.f&Ut)===0){t.b.defer_effect(t);return}for(var n=t;n.parent!==null;){n=n.parent;var i=n.f;if(Tt!==null&&n===E&&(S===null||(S.f&U)===0))return;if((i&(et|$e))!==0){if((i&I)===0)return;n.f^=I}}d(this,X).push(n)}};Ot=new WeakMap,We=new WeakMap,pt=new WeakMap,Bt=new WeakMap,Ht=new WeakMap,Rt=new WeakMap,Fe=new WeakMap,Ft=new WeakMap,X=new WeakMap,gn=new WeakMap,Ne=new WeakMap,xe=new WeakMap,Te=new WeakMap,Nt=new WeakMap,ht=new WeakMap,P=new WeakSet,Ci=function(){if(this.is_fork)return!0;for(const i of d(this,Fe).keys()){for(var t=i,n=!1;t.parent!==null;){if(d(this,Te).has(t)){n=!0;break}t=t.parent}if(!n)return!0}return!1},Xt=function(){var c,u,p,v;y(this,Ot,!0),ls++>1e3&&(T(this,P,en).call(this),Xr());for(const f of d(this,Ne))d(this,xe).delete(f),F(f,q),this.schedule(f);for(const f of d(this,xe))F(f,De),this.schedule(f);const t=d(this,X);y(this,X,[]),this.apply();var n=Tt=[],i=[],s=Bn=[];for(const f of t)try{T(this,P,Li).call(this,f,n,i)}catch(m){throw la(f),T(this,P,Ci).call(this)||this.discard(),m}if(x=null,s.length>0){var a=Yn.ensure();for(const f of s)a.schedule(f)}if(Tt=null,Bn=null,T(this,P,Ci).call(this)){T(this,P,$t).call(this,i),T(this,P,$t).call(this,n);for(const[f,m]of d(this,Te))ra(f,m);s.length>0&&T(c=x,P,Xt).call(c);return}const r=T(this,P,ia).call(this);if(r){T(this,P,$t).call(this,i),T(this,P,$t).call(this,n),T(u=r,P,sa).call(u,this);return}d(this,Ne).clear(),d(this,xe).clear();for(const f of d(this,Bt))f(this);d(this,Bt).clear(),ln=this,os(i),os(n),ln=null,(p=d(this,Ft))==null||p.resolve();var o=x;if(d(this,Rt)===0&&(d(this,X).length===0||o!==null)&&T(this,P,en).call(this),d(this,X).length>0)if(o!==null){const f=o;d(f,X).push(...d(this,X).filter(m=>!d(f,X).includes(m)))}else o=this;o!==null&&T(v=o,P,Xt).call(v)},Li=function(t,n,i){t.f^=I;for(var s=t.first;s!==null;){var a=s.f,r=(a&($e|et))!==0,o=r&&(a&I)!==0,c=o||(a&Q)!==0||d(this,Te).has(s);if(!c&&s.fn!==null){r?s.f^=I:(a&fn)!==0?n.push(s):xn(s)&&((a&ke)!==0&&d(this,xe).add(s),qt(s));var u=s.first;if(u!==null){s=u;continue}}for(;s!==null;){var p=s.next;if(p!==null){s=p;break}s=s.parent}}},ia=function(){for(var t=d(this,We);t!==null;){if(!t.is_fork){for(const[n,[,i]]of this.current)if(t.current.has(n)&&!i)return t}t=d(t,We)}return null},sa=function(t){var i;for(const[s,a]of t.current)!this.previous.has(s)&&t.previous.has(s)&&this.previous.set(s,t.previous.get(s)),this.current.set(s,a);for(const[s,a]of t.async_deriveds){const r=this.async_deriveds.get(s);r&&a.promise.then(r.resolve).catch(r.reject)}t.async_deriveds.clear(),this.transfer_effects(d(t,Ne),d(t,xe));const n=s=>{var a=s.reactions;if(a!==null)for(const c of a){var r=c.f;if((r&U)!==0)n(c);else{var o=c;r&(At|ke)&&!this.async_deriveds.has(o)&&(d(this,xe).delete(o),F(o,q),this.schedule(o))}}};for(const s of this.current.keys())n(s);this.oncommit(()=>t.discard()),T(i=t,P,en).call(i),x=this,T(this,P,Xt).call(this)},$t=function(t){for(var n=0;n!f.current.get(m)[1]);if(!(!d(f,Ot)||s.length===0)){var a=s.filter(m=>!this.current.has(m));if(a.length===0)t&&f.discard();else if(n.length>0){if(t)for(const m of d(this,Nt))f.unskip_effect(m,h=>{var b;(h.f&(ke|At))!==0?f.schedule(h):T(b=f,P,$t).call(b,[h])});f.activate();var r=new Set,o=new Map;for(var c of n)aa(c,a,r,o);o=new Map;var u=[...f.current].filter(([m,h])=>{const b=this.current.get(m);return b?b[0]!==h[0]||b[1]!==h[1]:!0}).map(([m])=>m);if(u.length>0)for(const m of d(this,gn))(m.f&(me|Q|In))===0&&Ni(m,u,o)&&((m.f&(At|ke))!==0?(F(m,q),f.schedule(m)):d(f,Ne).add(m));if(d(f,X).length>0&&!d(f,ht)){f.apply();for(var p of d(f,X))T(v=f,P,Li).call(v,p,[],[]);y(f,X,[])}f.deactivate()}}}},en=function(){if(this.linked){var t=d(this,We),n=d(this,pt);t===null?ci=n:y(t,pt,n),n===null?kt=t:y(n,We,t),this.linked=!1}};let xt=Yn;function Xr(){try{xr()}catch(e){Je(e,Ti)}}let we=null;function os(e){var t=e.length;if(t!==0){for(var n=0;n0)){mt.clear();for(const s of we){if((s.f&(me|Q))!==0)continue;const a=[s];let r=s.parent;for(;r!==null;)we.has(r)&&(we.delete(r),a.push(r)),r=r.parent;for(let o=a.length-1;o>=0;o--){const c=a[o];(c.f&(me|Q))===0&&qt(c)}}we.clear()}}we=null}}function aa(e,t,n,i){if(!n.has(e)&&(n.add(e),e.reactions!==null))for(const s of e.reactions){const a=s.f;(a&U)!==0?aa(s,t,n,i):(a&(At|ke))!==0&&(a&q)===0&&Ni(s,t,i)&&(F(s,q),Ii(s))}}function Ni(e,t,n){const i=n.get(e);if(i!==void 0)return i;if(e.deps!==null)for(const s of e.deps){if(Nn.call(t,s))return!0;if((s.f&U)!==0&&Ni(s,t,n))return n.set(s,!0),!0}return n.set(e,!1),!1}function Ii(e){x.schedule(e)}function ra(e,t){if(!((e.f&$e)!==0&&(e.f&I)!==0)){(e.f&q)!==0?t.d.push(e):(e.f&De)!==0&&t.m.push(e),F(e,I);for(var n=e.first;n!==null;)ra(n,t),n=n.next}}function la(e){F(e,I);for(var t=e.first;t!==null;)la(t),t=t.next}let qn=new Set;const mt=new Map;let oa=!1;function _t(e,t){var n={f:0,v:e,reactions:null,equals:js,rv:0,wv:0};return n}function ee(e,t){const n=_t(e);return ka(n),n}function el(e,t=!1,n=!0){const i=_t(e);return t||(i.equals=Vs),i}function V(e,t,n=!1){S!==null&&(!Me||(S.f&In)!==0)&&Qs()&&(S.f&(U|ke|At|In))!==0&&(ze===null||!ze.has(e))&&Mr();let i=n?Qe(t):t;return Kt(e,i,Bn)}function Kt(e,t,n=null){if(!e.equals(t)){mt.set(e,tt?t:e.v);var i=xt.ensure();if(i.capture(e,t),(e.f&U)!==0){const s=e;(e.f&q)!==0&&Fi(s),K===null&&Hi(s)}e.wv=Sa(),da(e,q,n),E!==null&&(E.f&I)!==0&&(E.f&($e|et))===0&&(de===null?fl([e]):de.push(e)),!i.is_fork&&qn.size>0&&!oa&&tl()}return t}function tl(){oa=!1;for(const e of qn){(e.f&I)!==0&&F(e,De);let t;try{t=xn(e)}catch{t=!0}t&&qt(e)}qn.clear()}function on(e){V(e,e.v+1)}function da(e,t,n){var i=e.reactions;if(i!==null)for(var s=i.length,a=0;a{if(wt===a)return o();var c=S,u=wt;ge(null),us(a);var p=o();return ge(c),us(u),p};return i&&n.set("length",ee(e.length)),new Proxy(e,{defineProperty(o,c,u){(!("value"in u)||u.configurable===!1||u.enumerable===!1||u.writable===!1)&&_r();var p=n.get(c);return p===void 0?r(()=>{var v=ee(u.value);return n.set(c,v),v}):V(p,u.value,!0),!0},deleteProperty(o,c){var u=n.get(c);if(u===void 0){if(c in o){const p=r(()=>ee(N));n.set(c,p),on(s)}}else V(u,N),on(s);return!0},get(o,c,u){var m;if(c===di)return e;var p=n.get(c),v=c in o;if(p===void 0&&(!v||(m=rn(o,c))!=null&&m.writable)&&(p=r(()=>{var h=Qe(v?o[c]:N),b=ee(h);return b}),n.set(c,p)),p!==void 0){var f=M(p);return f===N?void 0:f}return Reflect.get(o,c,u)},getOwnPropertyDescriptor(o,c){var u=Reflect.getOwnPropertyDescriptor(o,c);if(u&&"value"in u){var p=n.get(c);p&&(u.value=M(p))}else if(u===void 0){var v=n.get(c),f=v==null?void 0:v.v;if(v!==void 0&&f!==N)return{enumerable:!0,configurable:!0,value:f,writable:!0}}return u},has(o,c){var f;if(c===di)return!0;var u=n.get(c),p=u!==void 0&&u.v!==N||Reflect.has(o,c);if(u!==void 0||E!==null&&(!p||(f=rn(o,c))!=null&&f.writable)){u===void 0&&(u=r(()=>{var m=p?Qe(o[c]):N,h=ee(m);return h}),n.set(c,u));var v=M(u);if(v===N)return!1}return p},set(o,c,u,p){var j;var v=n.get(c),f=c in o;if(i&&c==="length")for(var m=u;mee(N)),n.set(m+"",h))}if(v===void 0)(!f||(j=rn(o,c))!=null&&j.writable)&&(v=r(()=>ee(void 0)),V(v,Qe(u)),n.set(c,v));else{f=v.v!==N;var b=r(()=>Qe(u));V(v,b)}var C=Reflect.getOwnPropertyDescriptor(o,c);if(C!=null&&C.set&&C.set.call(p,u),!f){if(i&&typeof c=="string"){var L=n.get("length"),z=Number(c);Number.isInteger(z)&&z>=L.v&&V(L,z+1)}on(s)}return!0},ownKeys(o){M(s);var c=Reflect.ownKeys(o).filter(v=>{var f=n.get(v);return f===void 0||f.v!==N});for(var[u,p]of n)p.v!==N&&!(u in o)&&c.push(u);return c},setPrototypeOf(){kr()}})}var ds,ca,ua,fa;function nl(){if(ds===void 0){ds=window,ca=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;ua=rn(t,"firstChild").get,fa=rn(t,"nextSibling").get,is(e)&&(e[Mi]=void 0,e[Dn]=null,e[hr]=void 0,e.__e=void 0),is(n)&&(n[Yt]=void 0)}}function gt(e=""){return document.createTextNode(e)}function Ye(e){return ua.call(e)}function yn(e){return fa.call(e)}function R(e,t){return Ye(e)}function pa(e,t=!1){{var n=Ye(e);return n instanceof Comment&&n.data===""?yn(n):n}}function J(e,t=1,n=!1){let i=e;for(;t--;)i=yn(i);return i}function il(e){e.textContent=""}function ha(){return!1}function va(e,t,n){return t==null||t===Us?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function ma(e){var t=S,n=E;ge(null),Pe(null);try{return e()}finally{ge(t),Pe(n)}}function sl(e){E===null&&(S===null&&yr(),wr()),tt&&br()}function al(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function it(e,t){var n=E;n!==null&&(n.f&Q)!==0&&(e|=Q);var i={ctx:Y,deps:null,nodes:null,f:e|q|ve,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};x==null||x.register_created_effect(i);var s=i;if((e&fn)!==0)Tt!==null?Tt.push(i):xt.ensure().schedule(i);else if(t!==null){try{qt(i)}catch(r){throw ie(i),r}s.deps===null&&s.teardown===null&&s.nodes===null&&s.first===s.last&&(s.f&jt)===0&&(s=s.first,(e&ke)!==0&&(e&It)!==0&&s!==null&&(s.f|=It))}if(s!==null&&(s.parent=n,n!==null&&al(s,n),S!==null&&(S.f&U)!==0&&(e&et)===0)){var a=S;(a.effects??(a.effects=[])).push(s)}return i}function Gi(){return S!==null&&!Me}function rl(e){const t=it(ei,null);return F(t,I),t.teardown=e,t}function ll(e){sl();var t=E.f,n=!S&&(t&$e)!==0&&Y!==null&&!Y.i;if(n){var i=Y;(i.e??(i.e=[])).push(e)}else return ga(e)}function ga(e){return it(fn|pr,e)}function ol(e){xt.ensure();const t=it(et|jt,e);return(n={})=>new Promise(i=>{n.outro?bt(t,()=>{ie(t),i(void 0)}):(ie(t),i(void 0))})}function dl(e){return it(At|jt,e)}function cl(e,t=0){return it(ei|t,e)}function ot(e,t=[],n=[],i=[]){Ur(i,t,n,s=>{it(ei,()=>{e(...s.map(M))})})}function Ki(e,t=0){var n=it(ke|t,e);return n}function he(e){return it($e|jt,e)}function ba(e){var t=e.teardown;if(t!==null){const n=tt,i=S;cs(!0),ge(null);try{t.call(null)}finally{cs(n),ge(i)}}}function qi(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){const s=n.ac;s!==null&&ma(()=>{s.abort(ti)});var i=n.next;(n.f&et)!==0?n.parent=null:ie(n,t),n=i}}function ul(e){for(var t=e.first;t!==null;){var n=t.next;(t.f&$e)===0&&ie(t),t=n}}function ie(e,t=!0){var n=!1;(t||(e.f&fr)!==0)&&e.nodes!==null&&e.nodes.end!==null&&(wa(e.nodes.start,e.nodes.end),n=!0),e.f|=as,qi(e,t&&!n),pn(e,0);var i=e.nodes&&e.nodes.t;if(i!==null)for(const a of i)a.stop();ba(e),e.f^=as,e.f|=me;var s=e.parent;s!==null&&s.first!==null&&ya(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function wa(e,t){for(;e!==null;){var n=e===t?null:yn(e);e.remove(),e=n}}function ya(e){var t=e.parent,n=e.prev,i=e.next;n!==null&&(n.next=i),i!==null&&(i.prev=n),t!==null&&(t.first===e&&(t.first=i),t.last===e&&(t.last=n))}function bt(e,t,n=!0){var i=[];xa(e,i,!0);var s=()=>{n&&ie(e),t&&t()},a=i.length;if(a>0){var r=()=>--a||s();for(var o of i)o.out(r)}else s()}function xa(e,t,n){if((e.f&Q)===0){e.f^=Q;var i=e.nodes&&e.nodes.t;if(i!==null)for(const o of i)(o.is_global||n)&&t.push(o);for(var s=e.first;s!==null;){var a=s.next;if((s.f&et)===0){var r=(s.f&It)!==0||(s.f&$e)!==0&&(e.f&ke)!==0;xa(s,t,r?n:!1)}s=a}}}function Un(e){_a(e,!0)}function _a(e,t){if((e.f&Q)!==0){e.f^=Q,(e.f&I)===0&&(F(e,q),xt.ensure().schedule(e));for(var n=e.first;n!==null;){var i=n.next,s=(n.f&It)!==0||(n.f&$e)!==0;_a(n,s?t:!1),n=i}var a=e.nodes&&e.nodes.t;if(a!==null)for(const r of a)(r.is_global||t)&&r.in()}}function Ui(e,t){if(e.nodes)for(var n=e.nodes.start,i=e.nodes.end;n!==null;){var s=n===i?null:yn(n);t.append(n),n=s}}let Hn=!1,tt=!1;function cs(e){tt=e}let S=null,Me=!1;function ge(e){S=e}let E=null;function Pe(e){E=e}let ze=null;function ka(e){S!==null&&(ze??(ze=new Set)).add(e)}let te=null,ae=0,de=null;function fl(e){de=e}let Ma=1,dt=0,wt=dt;function us(e){wt=e}function Sa(){return++Ma}function xn(e){var t=e.f;if((t&q)!==0)return!0;if(t&U&&(e.f&=~yt),(t&De)!==0){for(var n=e.deps,i=n.length,s=0;se.wv)return!0}(t&ve)!==0&&K===null&&F(e,I)}return!1}function $a(e,t,n=!0){var i=e.reactions;if(i!==null&&!(ze!==null&&ze.has(e)))for(var s=0;s{e.ac.abort(ti)}),e.ac=null);try{e.f|=Gn;var p=e.fn,v=p();e.f|=Ut;var f=e.deps,m=x==null?void 0:x.is_fork;if(te!==null){var h;if(m||pn(e,ae),f!==null&&ae>0)for(f.length=ae+te.length,h=0;h{throw L});throw f}}finally{e[ct]=t,delete e.currentTarget,ge(p),Pe(v)}}}var Rs;const fi=((Rs=globalThis==null?void 0:globalThis.window)==null?void 0:Rs.trustedTypes)&&globalThis.window.trustedTypes.createPolicy("svelte-trusted-html",{createHTML:e=>e});function gl(e){return(fi==null?void 0:fi.createHTML(e))??e}function bl(e){var t=va("template");return t.innerHTML=gl(e.replaceAll("","")),t.content}function jn(e,t){var n=E;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function be(e,t){var n=(t&Cr)!==0,i=(t&Lr)!==0,s,a=!e.startsWith("");return()=>{s===void 0&&(s=bl(a?e:""+e),n||(s=Ye(s)));var r=i||ca?document.importNode(s,!0):s.cloneNode(!0);if(n){var o=Ye(r),c=r.lastChild;jn(o,c)}else jn(r,r);return r}}function ce(e,t){e!==null&&e.before(t)}function ye(e,t){var n=t==null?"":typeof t=="object"?`${t}`:t;n!==(e[Yt]??(e[Yt]=e.nodeValue))&&(e[Yt]=n,e.nodeValue=`${n}`)}function za(e,t){return wl(e,t)}const An=new Map;function wl(e,{target:t,anchor:n,props:i={},events:s,context:a,intro:r=!0,transformError:o}){nl();var c=void 0,u=ol(()=>{var p=n??t.appendChild(gt());Nr(p,{pending:()=>{}},m=>{Ws({});var h=Y;a&&(h.c=a),s&&(i.$$events=s),c=e(m,i)||{},Js()},o);var v=new Set,f=m=>{for(var h=0;h{var C;for(var m of v)for(const L of[t,document]){var h=An.get(L),b=h.get(m);--b==0?(L.removeEventListener(m,hs),h.delete(m),h.size===0&&An.delete(L)):h.set(m,b)}Ai.delete(f),p!==n&&((C=p.parentNode)==null||C.removeChild(p))}});return yl.set(c,u),c}let yl=new WeakMap;var _e,Ce,le,vt,bn,wn,Zn;class xl{constructor(t,n=!0){se(this,"anchor");_(this,_e,new Map);_(this,Ce,new Map);_(this,le,new Map);_(this,vt,new Set);_(this,bn,!0);_(this,wn,t=>{if(d(this,_e).has(t)){var n=d(this,_e).get(t),i=d(this,Ce).get(n);if(i)Un(i),d(this,vt).delete(n);else{var s=d(this,le).get(n);s&&(Un(s.effect),d(this,Ce).set(n,s.effect),d(this,le).delete(n),s.fragment.lastChild.remove(),this.anchor.before(s.fragment),i=s.effect)}for(const[a,r]of d(this,_e)){if(d(this,_e).delete(a),a===t)break;const o=d(this,le).get(r);o&&(ie(o.effect),d(this,le).delete(r))}for(const[a,r]of d(this,Ce)){if(a===n||d(this,vt).has(a))continue;const o=()=>{if(Array.from(d(this,_e).values()).includes(a)){var u=document.createDocumentFragment();Ui(r,u),u.append(gt()),d(this,le).set(a,{effect:r,fragment:u})}else ie(r);d(this,vt).delete(a),d(this,Ce).delete(a)};d(this,bn)||!i?(d(this,vt).add(a),bt(r,o,!1)):o()}}});_(this,Zn,t=>{d(this,_e).delete(t);const n=Array.from(d(this,_e).values());for(const[i,s]of d(this,le))n.includes(i)||(ie(s.effect),d(this,le).delete(i))});this.anchor=t,y(this,bn,n)}ensure(t,n){var i=x,s=ha();if(n&&!d(this,Ce).has(t)&&!d(this,le).has(t))if(s){var a=document.createDocumentFragment(),r=gt();a.append(r),d(this,le).set(t,{effect:he(()=>n(r)),fragment:a})}else d(this,Ce).set(t,he(()=>n(this.anchor)));if(d(this,_e).set(i,t),s){for(const[o,c]of d(this,Ce))o===t?i.unskip_effect(c):i.skip_effect(c);for(const[o,c]of d(this,le))o===t?i.unskip_effect(c.effect):i.skip_effect(c.effect);i.oncommit(d(this,wn)),i.ondiscard(d(this,Zn))}else d(this,wn).call(this,i)}}_e=new WeakMap,Ce=new WeakMap,le=new WeakMap,vt=new WeakMap,bn=new WeakMap,wn=new WeakMap,Zn=new WeakMap;function pi(e,t,n=!1){var i=new xl(e),s=n?It:0;function a(r,o){i.ensure(r,o)}Ki(()=>{var r=!1;t((o,c=0)=>{r=!0,a(c,o)}),r||a(-1,null)},s)}function Vn(e,t){return t}function _l(e,t,n){for(var i=[],s=t.length,a,r=t.length,o=0;o{if(a){if(a.pending.delete(v),a.done.add(v),a.pending.size===0){var f=e.outrogroups;zi(e,Xn(a.done)),f.delete(a),f.size===0&&(e.outrogroups=null)}}else r-=1},!1)}if(r===0){var c=i.length===0&&n!==null;if(c){var u=n,p=u.parentNode;il(p),p.append(u),e.items.clear()}zi(e,t,!c)}else a={pending:new Set(t),done:new Set},(e.outrogroups??(e.outrogroups=new Set)).add(a)}function zi(e,t,n=!0){var i;if(e.pending.size>0){i=new Set;for(const r of e.pending.values())for(const o of r)i.add(e.items.get(o).e)}for(var s=0;s{var z=n();return Is(z)?z:z==null?[]:Xn(z)}),v,f=new Map,m=!0;function h(z){(L.effect.f&me)===0&&(L.pending.delete(z),L.fallback=u,kl(L,v,r,t,i),u!==null&&(v.length===0?(u.f&Ie)===0?Un(u):(u.f^=Ie,tn(u,null,r)):bt(u,()=>{u=null})))}function b(z){L.pending.delete(z)}var C=Ki(()=>{v=M(p);for(var z=v.length,j=new Set,oe=x,Oe=ha(),k=0;ka(r)):(u=he(()=>a(vs??(vs=gt()))),u.f|=Ie)),z>j.size&&gr(),!m)if(f.set(oe,j),Oe){for(const[Wt,Ke]of o)j.has(Wt)||oe.skip_effect(Ke.e);oe.oncommit(h),oe.ondiscard(b)}else h(oe);M(p)}),L={effect:C,items:o,pending:f,outrogroups:null,fallback:u};m=!1}function Qt(e){for(;e!==null&&(e.f&$e)===0;)e=e.next;return e}function kl(e,t,n,i,s){var G;var a=t.length,r=e.items,o=Qt(e.effect.first),c,u=null,p=[],v=[],f,m,h,b;for(b=0;b0){var A=a===0?n:null;_l(e,Oe,A)}}}function Ml(e,t,n,i,s,a,r,o){var c=(r&$r)!==0?(r&Tr)===0?el(n,!1,!1):_t(n):null,u=(r&Er)!==0?_t(s):null;return{v:c,i:u,e:he(()=>(a(t,c??n,u??s,o),()=>{e.delete(i)}))}}function tn(e,t,n){if(e.nodes)for(var i=e.nodes.start,s=e.nodes.end,a=t&&(t.f&Ie)===0?t.nodes.start:n;i!==null;){var r=yn(i);if(a.before(i),i===s)return;i=r}}function qe(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Sl(e,t,n=!1,i=!1,s=!1,a=!1){var r=e,o="";if(n)var c=e;ot(()=>{var u=E;if(o!==(o=t()??"")){if(n){u.nodes=null,c.innerHTML=o,o!==""&&jn(Ye(c),c.lastChild);return}if(u.nodes!==null&&(wa(u.nodes.start,u.nodes.end),u.nodes=null),o!==""){var p=i?Ar:s?zr:void 0,v=va(i?"svg":s?"math":"template",p);v.innerHTML=o;var f=i||s?v:v.content;if(jn(Ye(f),f.lastChild),i||s)for(;Ye(f);)r.before(Ye(f));else r.before(f)}}})}function $l(e,t,n){var i=e==null?"":""+e;return i===""?null:i}function ms(e,t,n,i,s,a){var r=e[Mi];if(r!==n||r===void 0){var o=$l(n);o==null?e.removeAttribute("class"):e.className=o,e[Mi]=n}return a}const El=Symbol("is custom element"),Tl=Symbol("is html");function Cl(e,t,n,i){var s=Ll(e);s[t]!==(s[t]=n)&&(n==null?e.removeAttribute(t):typeof n!="string"&&Al(e).includes(t)?e[t]=n:e.setAttribute(t,n))}function Ll(e){return e[Dn]??(e[Dn]={[El]:e.nodeName.includes("-"),[Tl]:e.namespaceURI===Us})}var gs=new Map;function Al(e){var t=e.getAttribute("is")||e.nodeName,n=gs.get(t);if(n)return n;gs.set(t,n=[]);for(var i,s=e,a=Element.prototype;a!==s;){i=lr(s);for(var r in i)i[r].set&&r!=="innerHTML"&&r!=="textContent"&&r!=="innerText"&&n.push(r);s=Gs(s)}return n}function zl(e){Y===null&&vr(),ll(()=>{const t=La(e);if(typeof t=="function")return t})}let hn=localStorage.getItem("mc_token")||"";function Da(){return hn}function Dl(e){hn=(e||"").trim(),localStorage.setItem("mc_token",hn)}function Pl(){const e={"Content-Type":"application/json"};return hn&&(e["X-MC-Token"]=hn),e}function _n(){return localStorage.getItem("mc_hf_token")||""}function Ol(e){localStorage.setItem("mc_hf_token",(e||"").trim())}async function $(e,t={}){const n=await fetch(e,{headers:Pl(),...t}),i=await n.json().catch(()=>({}));if(!n.ok)throw new Error(i.error||"HTTP "+n.status);return i}const l=(e,t=document)=>t.querySelector(e),bs=(e,t=document)=>[...t.querySelectorAll(e)];function g(e){return String(e??"").replace(/[&<>]/g,t=>({"&":"&","<":"<",">":">"})[t])}let ws;function w(e,t=!1){const n=l("#toast");n&&(n.textContent=e,n.className="toast show"+(t?" err":""),clearTimeout(ws),ws=setTimeout(()=>n.className="toast",3200))}function Bl(e,t){return e==="running"||e==="ready"?'geladen':e==="loading"||e==="starting"?'lädt…':e==="downloading"?`↓ Download${t!=null?` ${Math.round(t)}%`:""}`:'bereit'}const D=e=>`${e}`,Pa={logo:D(''),grid:D(''),cpu:D(''),pulse:D(''),server:D(''),book:D(''),help:D(''),settings:D(''),swap:D(''),monitor:D(''),layers:D(''),alert:D(''),gauge:D(''),search:D(''),compass:D(''),code:D(''),eye:D(''),refresh:D(''),file:D(''),database:D(''),thermo:D(''),shield:D(''),info:D(''),chevron:D(''),bolt:D(''),x:D(''),check:D(''),download:D(''),clock:D('')};function nt(e){return Pa[e]||""}function Jn(e){return`${Pa.info}`}function rt(e){if(!e&&e!==0)return"–";const t=e/1024/1024/1024;return t>=1?t.toFixed(1)+" GB":Math.round(e/1024/1024)+" MB"}function Ee({title:e,body:t,confirmLabel:n="Bestätigen",cancelLabel:i="Abbrechen",danger:s=!1}){return new Promise(a=>{const r=document.createElement("div");r.className="modal-overlay",r.innerHTML=``;const o=c=>{r.remove(),a(c)};r.addEventListener("click",c=>{if(c.target===r)return o(!1);const u=c.target.closest("[data-x]");u&&o(u.getAttribute("data-x")==="1")}),document.body.appendChild(r),r.querySelector('[data-x="1"]').focus()})}function Oa({title:e,body:t="",placeholder:n="",password:i=!1,confirmLabel:s="Weiter",danger:a=!1}){return new Promise(r=>{const o=document.createElement("div");o.className="modal-overlay",o.innerHTML=``;const c=o.querySelector("input"),u=p=>{o.remove(),r(p)};o.addEventListener("click",p=>{if(p.target===o)return u(null);const v=p.target.closest("[data-x]");v&&u(v.getAttribute("data-x")==="1"?c.value||"":null)}),c.addEventListener("keydown",p=>{p.key==="Enter"&&u(c.value||""),p.key==="Escape"&&u(null)}),document.body.appendChild(o),c.focus()})}function Hl(e="overview"){const t=bs(".nav-item[data-view]:not(.disabled)"),n=bs(".view[data-view]"),i=new Set(n.map(a=>a.dataset.view));function s(a){i.has(a)||(a=e),n.forEach(r=>r.hidden=r.dataset.view!==a),t.forEach(r=>r.classList.toggle("active",r.dataset.view===a)),location.hash!=="#/"+a&&(location.hash="#/"+a)}return t.forEach(a=>a.addEventListener("click",r=>{r.preventDefault(),s(a.dataset.view)})),window.addEventListener("hashchange",()=>s(location.hash.replace("#/",""))),s(location.hash.replace("#/","")||e),{show:s}}let ys=ee(null);const Rl={get value(){return M(ys)},set(e){V(ys,e,!0)}};let xs=ee(Qe([]));const Fl={get value(){return M(xs)},set(e){V(xs,e||[],!0)}},Nl="5";var Fs;typeof window<"u"&&((Fs=window.__svelte??(window.__svelte={})).v??(Fs.v=new Set)).add(Nl);const Il=[["Dein erster Workflow — in 3 Schritten",` +
    +
  1. Modell holen: Tab Cookbook → wähle deinen Anwendungsfall (z.B. „Coden") → + Komplettes Setup installieren. Der Download läuft im Tab Aktivität.
  2. +
  3. Verbinden: Tab Verbinden → Basis-URL kopieren → in deinem Tool (z.B. OpenCode) + als „OpenAI-kompatibel" einfügen. Verbindung testen bestätigt, dass es läuft.
  4. +
  5. Arbeiten: Im Tool das Modell (z.B. coder) wählen und loslegen. Das passende + Modell wird automatisch geladen — du musst nichts starten.
  6. +
`],["Wie leere ich den Kontext? Machen die Tools das selbst?",` +

Kurz: meistens ja, automatisch. Der Kontext (das „Gedächtnis") gilt pro Unterhaltung. + Startest du im Tool einen neuen Chat / eine neue Session, ist der Kontext wieder leer — + das Tool verwaltet das Fenster für dich.

+

Du musst nur selten eingreifen: wird ein Gespräch sehr lang, fang einen neuen an. Wichtig: + „Grafikspeicher leeren" (Tab Server) ist etwas anderes — das wirft das Modell aus dem Speicher, + nicht den Gesprächsverlauf.

`],["Was passiert, wenn der Kontext volläuft?",` +

Ist das Kontextfenster voll, passiert je nach Tool eins von dreien: das Modell vergisst den + Anfang (älteste Nachrichten fallen raus), das Tool kürzt automatisch, oder du bekommst + einen Fehler wie „context length exceeded".

+

So gehst du vor:

+
    +
  1. Neuen Chat starten — der einfachste Reset, Kontext ist wieder leer.
  2. +
  3. Zusammenfassen lassen: „Fasse unseren Stand kurz zusammen", dann im neuen Chat einfügen.
  4. +
  5. Kontext vergrößern: Tab ModelleKonfigurieren → wir zeigen das Optimum + für deine Hardware (mehr Kontext = mehr Speicherbedarf).
  6. +
`],["Was ist ein Agent — und wann brauche ich einen?",` +

Ein nacktes Modell schreibt nur Text zurück. Ein Agent ist ein Programm drumherum, + das das Modell handeln lässt: Dateien lesen und ändern, Befehle ausführen, im Web suchen — + Schritt für Schritt in einer Schleife, bis die Aufgabe erledigt ist.

+

Wann lohnt sich was?

+
    +
  • Fragen, Texte, Erklärungen, Brainstorming → kein Agent nötig, eine Chat-Oberfläche + wie OpenWebUI reicht völlig.
  • +
  • Coden über mehrere Dateien, Befehle ausführen, recherchieren + umsetzen + → Agent (OpenCode, Cline, Continue).
  • +
+

Gut zu wissen: Diese Agenten laufen lokal auf deinem PC — du brauchst + keinen Cloud-Dienst.

`],["Was sind Skills — und wie installiere ich sie?",` +

Ein Skill ist verpacktes Können: eine kurze Anleitung, die dem Agenten beibringt, + eine wiederkehrende Aufgabe jedes Mal so zu erledigen, wie du es willst.

+

Skill vs. MCP: MCP gibt dem Agenten neue Werkzeuge. Skills geben + ihm Know-how. Kurz: Werkzeuge = MCP, Anleitungen = Skills.

+

Installieren: meist nur eine Datei ablegen — kein Build, kein Neustart:

+
    +
  1. Pro Projekt: in den Projektordner (z.B. AGENTS.md, .clinerules/).
  2. +
  3. Global: in den Konfig-Ordner des Tools — gilt überall.
  4. +
`],["MCP — deinem Agenten echte Werkzeuge geben",` +

MCP (Model Context Protocol) ist ein einheitlicher „Stecker", über den ein Agent zusätzliche + Werkzeuge bekommt: das Web durchsuchen, Dateien lesen, eine Datenbank abfragen.

+

Du fügst in deinem Agenten einen „MCP-Server" hinzu (eine Zeile in der Konfiguration je Werkzeug). + Danach entscheidet der Agent selbst, wann er das Werkzeug einsetzt.

+
    +
  • Faustregel: Fehlt dem Agenten eine Fähigkeit → MCP. Soll er etwas nur + besser machen → Skill.
  • +
`],["Auto-Swap — wie das Laden funktioniert",` +

llama-swap lädt automatisch das Modell, das gerade angefragt wird, und entlädt es nach einer + Leerlaufzeit wieder. Es läuft immer nur eines gleichzeitig.

+

Du richtest beliebig viele Modelle ein und wählst im Tool das passende — + das Laden/Entladen passiert im Hintergrund, ohne dass du etwas tun musst.

`],["Die empfohlenen Tools optimal nutzen",` +
    +
  • OpenCode: Autonomes Coden im Terminal/Editor — ideal für „bau mir Feature X".
  • +
  • Cline: Dasselbe als VS-Code-Erweiterung, mit Bestätigung vor jeder Aktion.
  • +
  • OpenWebUI: Klassische Chat-Oberfläche für Fragen/Texte.
  • +
+

Tipp: starkes Modell (coder) für schwere Aufgaben, schnelles + (scout) für einfache — du wählst es einfach im Tool aus.

`]],Gl=[["LLM / Modell","Die KI selbst — versteht und schreibt Text (wie ChatGPT, nur lokal auf deinem PC). Ein Modell ist eine konkrete Datei davon, oft auf etwas spezialisiert (Code, Bilder, Chat)."],["GGUF","Das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF kann sie nicht nutzen."],["Quantisierung (Q4_K_M)","Verkleinert ein Modell (16-Bit → 4-Bit), damit es in den Speicher passt — bei kaum Qualitätsverlust. Q4_K_M ist der beliebte Sweetspot."],["Kontextfenster","Das Kurzzeitgedächtnis: wie viel Text sich das Modell gleichzeitig merkt. Größer = mehr Überblick, mehr Speicher, etwas langsamer."],["Auto-Swap","llama-swap lädt/entlädt Modelle automatisch — immer nur eins gleichzeitig. Du managst nichts."],["Agent","Ein Programm (OpenCode, Cline, Continue), das das Modell handeln lässt — Dateien lesen/ändern, Befehle ausführen, planen, im Loop bis die Aufgabe erledigt ist."],["MCP","Model Context Protocol — der Anschluss, über den ein Agent zusätzliche Werkzeuge bekommt (Web, Dateien, Datenbank). MCP = Werkzeuge."],["Skills","Verpackte Anleitung für wiederkehrende Aufgaben — der Agent liest sie und macht es jedes Mal wie du willst. Skills = Anleitungen."],["Rules / Commands","Die konkrete Form von Skills je Tool: AGENTS.md (OpenCode), .clinerules (Cline), Rules/Prompts (Continue)."]];var Kl=be('
'),ql=be('

'),Ul=be('

Guide für Einsteiger

Praktische Workflows + die Local-LLM-Welt in Klartext — ohne Vorwissen.

Tutorials & Workflows

Konzepte kurz erklärt

',1);function jl(e){var t=Ul(),n=J(pa(t),4);Wn(n,21,()=>Il,Vn,(s,a,r)=>{var o=rs(()=>ss(M(a),2));let c=()=>M(o)[0],u=()=>M(o)[1];var p=Kl();p.open=r===0;var v=R(p),f=R(v),m=J(v,2);Sl(m,u,!0),ot(()=>ye(f,c())),ce(s,p)});var i=J(n,4);Wn(i,21,()=>Gl,Vn,(s,a)=>{var r=rs(()=>ss(M(a),2));let o=()=>M(r)[0],c=()=>M(r)[1];var u=ql(),p=R(u),v=R(p),f=J(p,2),m=R(f);ot(()=>{ye(v,o()),ye(m,c())}),ce(s,u)}),ce(e,t)}var Vl=be('
'),Wl=be('

Für dich: Upgrades

Neuere Modelle, die ein installiertes ersetzen können.
'),Jl=be('
Lade News… (erstes Laden kann kurz dauern)
'),Ql=be('
'),Yl=be('
Keine News geladen
Die Quellen sind gerade nicht erreichbar — später nochmal probieren.
'),Zl=be('für dich'),Xl=be('
'),eo=be('
'),to=be('

News

Aktuelles aus der Local-LLM-Welt — aus kuratierten Quellen.
',1);function no(e,t){Ws(t,!0);let n=ee(Qe([])),i=ee(Qe([])),s=ee(!0),a=ee("");const r=k=>k==="perfect"?"ok":k==="marginal"?"warn":"bad";function o(k){const A=Math.max(0,Date.now()/1e3-k);return A<60?A+"s":A<3600?Math.floor(A/60)+"m":A<86400?Math.floor(A/3600)+"h":Math.floor(A/86400)+"d"}async function c(){V(s,!0),V(a,"");try{V(n,(await $("/api/news")).items||[],!0)}catch(k){V(a,k.message||"Fehler beim Laden",!0)}finally{V(s,!1)}}async function u(){try{V(i,(await $("/api/cookbook/upgrades")).upgrades||[],!0)}catch{V(i,[],!0)}}async function p(k,A){var G;A.disabled=!0,A.textContent="Starte…";try{await $("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:k.repo,role:k.role,params_b:k.params_b,quant:k.quant,hf_token:_n()})}),(G=document.querySelector(".nav-item[data-view='activity']"))==null||G.dispatchEvent(new MouseEvent("click"))}catch(O){A.disabled=!1,A.textContent="Installieren",alert("Fehler: "+O.message)}}function v(){c(),u()}zl(()=>{c(),u()});var f=to(),m=pa(f),h=J(R(m),2),b=J(m,2);{var C=k=>{var A=Wl(),G=R(A),O=J(R(G),2),Wt=R(O),Ke=J(G,4);Wn(Ke,21,()=>M(i),Vn,($n,Be)=>{var En=Vl(),Jt=R(En),Tn=R(Jt),Cn=R(Tn),si=R(Cn),Ln=J(Cn,2),ai=R(Ln),st=J(Tn,2),ri=R(st),nr=J(Jt,2);ot(li=>{ye(si,M(Be).name),ms(Ln,1,`fit-badge ${li??""}`),ye(ai,M(Be).fit.text),ye(ri,`Ersetzt dein „${M(Be).old??""}". ${M(Be).why??""}`)},[()=>r(M(Be).fit.level)]),fs("click",nr,li=>p(M(Be),li.currentTarget)),ce($n,En)}),ot(()=>ye(Wt,M(i).length)),ce(k,A)};pi(b,k=>{M(i).length&&k(C)})}var L=J(b,2);{var z=k=>{var A=Jl();ce(k,A)},j=k=>{var A=Ql(),G=R(A);ot(()=>ye(G,M(a))),ce(k,A)},oe=k=>{var A=Yl();ce(k,A)},Oe=k=>{var A=eo();Wn(A,21,()=>M(n),Vn,(G,O,Wt)=>{var Ke=Xl();ms(Ke,1,`news-card${Wt===0?" featured":""}`);var $n=R(Ke),Be=R($n),En=R(Be),Jt=J(Be,2);{var Tn=st=>{var ri=Zl();ce(st,ri)};pi(Jt,st=>{M(O).relevant&&st(Tn)})}var Cn=J(Jt,2),si=R(Cn),Ln=J($n,2),ai=R(Ln);ot(st=>{Cl(Ke,"href",M(O).link),ye(En,M(O).source),ye(si,st),ye(ai,M(O).title)},[()=>M(O).ts?o(M(O).ts):""]),ce(G,Ke)}),ce(k,A)};pi(L,k=>{M(s)?k(z):M(a)?k(j,1):M(n).length?k(Oe,-1):k(oe,2)})}fs("click",h,v),ce(e,f),Js()}ml(["click"]);let Ge=null,ne=null;const Ba=new Set(["running","ready","loading","starting"]);function ji(){return(Ge==null?void 0:Ge.models)||[]}function Ha(){return ji().find(e=>Ba.has(e.state))}function io(e){var t;(t=document.querySelector(`.nav-item[data-view="${e}"]`))==null||t.click()}async function so(){if(await Ee({title:"Speicher freigeben?",body:"Alle aktuell geladenen Modelle werden aus dem Grafikspeicher geworfen. Beim nächsten Aufruf lädt das jeweilige Modell einfach neu — es geht nichts verloren.",confirmLabel:"Speicher freigeben"}))try{await $("/api/unload",{method:"POST"}),w("Speicher freigegeben."),setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),600)}catch(t){w(t.message,!0)}}window.mcOv={go:io,freeMemory:so};function Vi(){let e="Verbinde…",t="";if(Ge){const n=ji().length,i=Ha();Ge.swap_ok?ne&&ne.ram.percent>=90?(e="Achtung: Speicher wird knapp.",t=`Arbeitsspeicher bei ${Math.round(ne.ram.percent)} % — eventuell Speicher freigeben.`):(e="Alles läuft rund.",t=`${n} ${n===1?"Modell":"Modelle"} bereit · ${i?`„${i.name}" geladen`:"keins geladen"} · keine Warnungen.`):(e="LLM-Engine offline",t="Der llama-swap-Dienst antwortet gerade nicht.")}l("#ov-hero").innerHTML=`
+

${g(e)}

${g(t)}
`}function Mt(e,t,n,i="",s=""){return`
${e}
+
${t}
${n}
`}function ao(e){return e==null?"":e<55?"kühl & gesund":e<72?"normal":"läuft heiß"}function Wi(){const e=Ha(),t=Ge==null?void 0:Ge.swap_ok;let n=Mt("LLM-Engine",t?"Online":"Offline","llama-swap",t?"ok":"bad"),i=Mt("Aktives Modell",e?g(e.name):"Keins",e?"im Grafikspeicher":"nichts geladen"),s,a;if(ne){const r=ne.gpu_temp??ne.cpu.temp;s=Mt("GPU-Temperatur",r!=null?`${Math.round(r)}°`:"–",ao(r),"",r!=null&&r<72?"ok":"");const o=ne.ram.total-ne.ram.used;a=Mt("Freier Speicher",`${(o/1024**3).toFixed(0)} GB`,`von ${(ne.ram.total/1024**3).toFixed(0)} GB`)}else s=Mt("GPU-Temperatur","–","messe…"),a=Mt("Freier Speicher","–","messe…");l("#ov-tiles").innerHTML=n+i+s+a}function hi(e,t){const n=Math.max(0,Math.min(100,t||0)),i=n>=90?"bad":n>=75?"warn":"";return`
${e} + ${Math.round(n)} %
+
`}function Ra(){const e=`

System-Gesundheit

+
So ausgelastet ist dein Mini-PC gerade.
`;if(!ne){l("#ov-health").innerHTML=e+'
Warte auf Messwerte…
';return}let t=0;const n=ne.gpu;n&&n.vram.total+n.gtt.total>0&&(t=(n.vram.used+n.gtt.used)/(n.vram.total+n.gtt.total)*100),l("#ov-health").innerHTML=e+hi("Prozessor (CPU)",ne.cpu.percent)+hi("Arbeitsspeicher (RAM)",ne.ram.percent)+hi("Grafikspeicher (VRAM)",t)}function vi(e,t,n,i,s){return``}function Fa(){l("#ov-quickstart").innerHTML=`

Schnellstart

+
Die häufigsten Aufgaben — ein Klick.
`+vi("search","teal","Modell finden","Passend zu deiner Hardware","window.mcOv.go('cookbook')")+vi("refresh","amber","Speicher freigeben","Modelle entladen · fragt vorher nach","window.mcOv.freeMemory()")+vi("file","blue","Logs ansehen","Live mitlesen, was läuft","window.mcOv.go('server')")}function ro(e){return e?e.includes("Bild")?'Bild':e.includes("Code")?'Code':'Text':""}function lo(e){var r,o;const t=e.state==="downloading",n=Ba.has(e.state),i=e.state==="loading"||e.state==="starting"||t?"load":n?"on":"";let s;t?s=`↓ Download${e.download_progress!=null?` ${Math.round(e.download_progress)}%`:""}`:s=n?e.state==="loading"?"lädt…":"geladen":"bereit";const a=(r=e.meta)!=null&&r.filename?`
${g(e.meta.filename)}
`:"";return`
+
+
${g(e.name)}${ro((o=e.meta)==null?void 0:o.caps)}
${a}
+ ${g(s)}
`}async function oo(){const e=l("#ov-news");if(!e)return;const t=`

Top-News

Alle →
`;e.innerHTML=t+'
Lade…
';try{const n=((await $("/api/news")).items||[]).slice(0,4);e.innerHTML=t+(n.length?`
${n.map(i=>` +
${g(i.title)}
+
${g(i.source)}
`).join("")}
`:'
Keine News.
')}catch{e.innerHTML=t+'
News gerade nicht ladbar.
'}}function Na(){const e=ji();l("#ov-stack").innerHTML=`

Dein Stack

${e.length?e.length+" konfiguriert":""}
`+(e.length?`
${e.map(lo).join("")}
`:`
Noch keine Modelle
+
Hol dir unter „Cookbook" ein Modell, das auf deine Hardware passt.
`)}function co(){Vi(),Wi(),Ra(),Fa(),Na()}function uo(){Fa(),co(),oo()}function fo(e){Ge=e,Vi(),Wi(),Na()}function po(e){ne=e,Vi(),Wi(),Ra()}const ho={id:"overview",mount:uo,onStatus:fo,onSystem:po};let _s=[];const vo="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr (längere Dateien/Chats), braucht aber mehr Speicher und wird etwas langsamer.",mo="Die Rolle ist ein zusätzlicher, sprechender Name (z.B. coder). Du kannst in deinen Tools entweder den echten Modellnamen ODER die Rolle angeben — beides führt zum selben Modell.",go=[{id:"coder",label:"Coder",desc:"Programmieren & Code"},{id:"vision",label:"Vision",desc:"Bilder verstehen"},{id:"scout",label:"Scout",desc:"Schneller Allrounder"},{id:"reviewer",label:"Reviewer",desc:"Code/Texte prüfen"},{id:"manager",label:"Manager",desc:"Planen & koordinieren"}];let je=[];function kn(){document.dispatchEvent(new Event("mc:refresh"))}function bo(e){return Bl(e.state,e.download_progress)}function wo(e){return e?`${g(e)}`:""}function yo(e){return`
+ API-Name: + ${(e.api_ids&&e.api_ids.length?e.api_ids:[e.name]).map(n=>`${g(n)}`).join('·')} +
`}function xo(e){return e!=null&&e.length?e.map(t=>t==="Code"?'Code':t==="Bild"?'Bild':'Text').join(" "):"–"}function _o(e){if(!e)return"–";const t=e.quant||"?",n=e.ctx?Math.round(e.ctx/1024)+"K":"?",i=e.size_bytes?(e.size_bytes/1024**3).toFixed(1)+" GB":"?";return`${g(t)} · ${n} · ${i}`}function ko(){l("#m-head").innerHTML=`
+

Modelle

+
Deine konfigurierten Modelle — testen, Kontext anpassen oder aus dem Speicher werfen.
`,l("#m-chat").innerHTML=` +

Schnelltest

+
Schreib eine Nachricht — das gewählte Modell wird automatisch geweckt.
+ + + + + + `,l("#m-table").innerHTML=` +

Modelle & Ports

+
Modelle laden automatisch, sobald eine Anfrage kommt — du musst nichts manuell starten.
+ + + +
ModellKannDetailsStatusPortAktionen
+ + + + + `,l("#chat-btn").addEventListener("click",Ao),l("#cfg-close").addEventListener("click",()=>l("#cfg-modal").style.display="none"),l("#cfg-modal").addEventListener("click",e=>{e.target.id==="cfg-modal"&&(l("#cfg-modal").style.display="none")}),l("#cfg-save").addEventListener("click",Co),l("#role-close").addEventListener("click",()=>l("#role-modal").style.display="none"),l("#role-modal").addEventListener("click",e=>{e.target.id==="role-modal"&&(l("#role-modal").style.display="none")}),l("#role-save").addEventListener("click",()=>Pi(l("#role-custom").value.trim().toLowerCase())),l("#role-clear").addEventListener("click",()=>Pi("")),l("#m-table").addEventListener("click",e=>{var s,a;const t=e.target.closest("[data-action]");if(!t)return;const n=t.getAttribute("data-name"),i=t.getAttribute("data-action");n&&(i==="cfg"?To(n):i==="role"?Mo(n):i==="unload"?Lo(n):i==="del"?Eo(n):i==="copy"?((s=navigator.clipboard)==null||s.writeText(n),w("Kopiert: "+n)):i==="assign"&&(w("Lade im Cookbook ein Modell und setze den Alias auf: "+n),(a=document.querySelector(".nav-item[data-view='cookbook']"))==null||a.click()))})}let Di=null;function Mo(e){Di=e;const t=je.find(n=>n.name===e);l("#role-model-name").textContent=e,l("#role-custom").value="",l("#role-chips").innerHTML=go.map(n=>``).join(""),l("#role-chips").querySelectorAll("[data-rolepick]").forEach(n=>n.addEventListener("click",()=>Pi(n.getAttribute("data-rolepick")))),l("#role-modal").style.display="flex"}async function Pi(e){if(Di)try{await $("/api/set_role",{method:"POST",body:JSON.stringify({alias:Di,role:e})}),w(e?`Rolle gesetzt: ${e}`:"Rolle entfernt."),l("#role-modal").style.display="none",kn()}catch(t){w(t.message,!0)}}function Ia(){const e=l("#models");if(!e)return;l("#models-empty").style.display=je.length?"none":"flex",l("#m-count").textContent=je.length?je.length+" konfiguriert":"";const t=l("#chat-model"),n=t.value;t.innerHTML="",e.innerHTML=je.map(i=>{var a,r;if(i.incomplete)return` + ${g(i.name)}
kein Modell hinterlegt
+ — + leere Rolle + leer + — + + + + `;const s=(a=i.meta)!=null&&a.filename?`
${g(i.meta.filename)}
`:"";return` + +
${g(i.name)}${wo(i.role)}
+ ${s}${yo(i)} + ${xo((r=i.meta)==null?void 0:r.caps)} + ${_o(i.meta)} + ${bo(i)} + ${i.port??"auto"} + + + + + + `}).join("");for(const i of je)i.incomplete||t.insertAdjacentHTML("beforeend",``);n&&(t.value=n)}function So(e){je=(e==null?void 0:e.models)||[],Ia()}function $o(e){_s=e||[],_s.some(n=>(n.state==="running"||n.state==="queued")&&/^download\b/i.test(n.label))&&Ia()}async function Eo(e){if(await Ee({title:`„${e}" wirklich löschen?`,body:"Das Modell wird aus der Konfiguration ausgetragen und seine Dateien werden von der Festplatte gelöscht, um Speicher freizugeben. Das lässt sich nicht rückgängig machen — später brauchst du dafür einen neuen Download.",confirmLabel:"Endgültig löschen",danger:!0}))try{const n=await $("/api/delete_model",{method:"POST",body:JSON.stringify({alias:e})});w(n.note||"Gelöscht: "+e),setTimeout(kn,600)}catch(n){w(n.message,!0)}}function To(e){var s,a,r,o,c;const t=je.find(u=>u.name===e);if(!t)return;l("#cfg-model-name").textContent=t.name,l("#cfg-ctx").value=((s=t.meta)==null?void 0:s.ctx)||8192;const n=(a=t.meta)==null?void 0:a.optimal_ctx,i=l("#cfg-rec");if(n){const u=((r=t.meta)==null?void 0:r.ctx)||0,p=(o=t.meta)==null?void 0:o.peak_ram_gb,v=(c=t.meta)==null?void 0:c.peak_ram_optimal_gb;l("#cfg-rec-note").innerHTML=`Optimal ~${Math.round(n/1024)}k → ~${v} GB Spitzenbedarf.
Aktuell ~${Math.round(u/1024)}k → ~${p} GB.`,l("#cfg-rec-apply").onclick=()=>{l("#cfg-ctx").value=n},i.style.display="flex"}else i.style.display="none";l("#cfg-modal").style.display="flex"}async function Co(){const e=l("#cfg-model-name").textContent,t=parseInt(l("#cfg-ctx").value)||8192;l("#cfg-save").disabled=!0;try{await $("/api/update_model",{method:"POST",body:JSON.stringify({alias:e,ctx:t})}),w("Gespeichert — aktiv beim nächsten Modell-Start."),l("#cfg-modal").style.display="none",kn()}catch(n){w(n.message,!0)}l("#cfg-save").disabled=!1}async function Lo(e){if(await Ee({title:`„${e}" entladen?`,body:"Das Modell wird aus dem Grafikspeicher geworfen und lädt beim nächsten Aufruf automatisch neu.",confirmLabel:"Entladen"}))try{await $("/api/unload?model="+encodeURIComponent(e),{method:"POST"}),w("Entladen: "+e),setTimeout(kn,600)}catch(t){w(t.message,!0)}}async function Ao(){const e=l("#chat-model").value,t=l("#chat-msg").value.trim();if(!e)return w("Kein Modell vorhanden.",!0);if(!t)return;const n=l("#chat-btn");n.disabled=!0,n.textContent="…";const i=l("#chat-reply");i.style.display="block",i.textContent="(wecke Modell, kann beim Laden kurz dauern…)";try{const s=await $("/api/chat",{method:"POST",body:JSON.stringify({model:e,message:t})});i.textContent=s.reply}catch(s){i.textContent="Fehler: "+s.message}n.disabled=!1,n.textContent="Senden",kn()}const zo={id:"models",mount:ko,onStatus:So,onJobs:$o},dn=new Set;let lt=[],B=null;function Ji(e){dn.add(e),Vt()}const Ue={cpu:[],ram:[],gpu:[]},Do=60;function Po(e){return e==="done"?'fertig':e==="failed"?'fehler':e==="canceled"?'abgebrochen':'läuft…'}function Oo(e){return e==="done"?"on":e==="failed"||e==="canceled"?"":"load"}function Bo(e){if(e.state!=="running")return null;if(typeof e.progress=="number")return Math.min(100,e.progress);const n=((e.log||[])[e.log.length-1]||"").match(/(\d+(?:\.\d+)?)\s*%/);return n?Math.min(100,parseFloat(n[1])):null}function Ho(e){return e==null||e<0?"":e>=90?`noch ~${Math.round(e/60)} min`:`noch ~${Math.max(1,Math.round(e))} s`}async function Ro(e){try{await $(`/api/jobs/${e}/cancel`,{method:"POST"}),w("Abbruch angefordert…"),Vt()}catch(t){w(t.message,!0)}}async function Fo(e){try{await $(`/api/jobs/${e}`,{method:"DELETE"}),Vt()}catch(t){w(t.message,!0)}}async function No(){try{const e=await $("/api/jobs/clear",{method:"POST"});w(`${e.removed} Einträge entfernt.`),Vt()}catch(e){w(e.message,!0)}}function mi(e,t,n){return`
${e}
${t}
${n}
`}function gi(e,t){return'
'+e.map(n=>`
`).join("")+"
"}function ks(){const e=B==null?void 0:B.gpu;return e&&e.vram.total+e.gtt.total>0?(e.vram.used+e.gtt.used)/(e.vram.total+e.gtt.total)*100:0}function Ga(){if(!B)return;Ue.cpu.push(B.cpu.percent),Ue.ram.push(B.ram.percent),Ue.gpu.push(ks());for(const s of["cpu","ram","gpu"])Ue[s].length>Do&&Ue[s].shift();const e=l("#act-kpis");e&&(e.innerHTML=mi("Prozessor (CPU)",`${Math.round(B.cpu.percent)} %`,B.cpu.temp!=null?`${Math.round(B.cpu.temp)}° CPU-Temp`:"Auslastung")+mi("Arbeitsspeicher",`${Math.round(B.ram.percent)} %`,`${rt(B.ram.used)} / ${rt(B.ram.total)}`)+mi("Grafikspeicher",`${Math.round(ks())} %`,B.gpu_temp!=null?`${Math.round(B.gpu_temp)}° GPU-Temp`:"VRAM + GTT"));const t=B.gpu,n=t&&t.vram.total+t.gtt.total>0?`${rt(t.vram.used+t.gtt.used)} / ${rt(t.vram.total+t.gtt.total)}`:"–",i=l("#act-sys");i&&(i.innerHTML=` +

System-Metriken (Bosgame)

+
Live-Auslastung deines Mini-PCs, alle 0,5 Sekunden.
+
+
Arbeitsspeicher (RAM)${rt(B.ram.used)} / ${rt(B.ram.total)}
+
Grafikspeicher (VRAM + GTT)${n}
+
Speicherplatz (Disk)${Math.round(B.disk.percent)} % belegt
+
Temperatur (GPU / CPU)${B.gpu_temp!=null?Math.round(B.gpu_temp)+"°":"–"} / ${B.cpu.temp!=null?Math.round(B.cpu.temp)+"°":"–"}
+
+
+
CPU-Verlauf
${gi(Ue.cpu,"--accent")}
+
RAM-Verlauf
${gi(Ue.ram,"--purple")}
+
VRAM-Verlauf
${gi(Ue.gpu,"--on")}
+
`)}function Io(){l("#act-head").innerHTML=`
+

Aktivität

+
Live-Auslastung und laufende Aufgaben (Downloads, Updates) mit Protokoll.
`,l("#v-activity").innerHTML=` +

Hintergrund-Aufgaben

+
+
Downloads & Updates erscheinen hier mit Live-Protokoll — zum Aufklappen klicken.
+
+
Gerade nichts los.
Alles ruhig — keine laufenden Aufgaben.
`,l("#v-activity").addEventListener("click",e=>{if(e.target.closest("#jobs-clear")){No();return}const t=e.target.closest("[data-cancel]");if(t){e.stopPropagation(),Ro(t.getAttribute("data-cancel"));return}const n=e.target.closest("[data-del]");if(n){e.stopPropagation(),Fo(n.getAttribute("data-del"));return}const i=e.target.closest(".job-h");if(!i)return;const s=i.getAttribute("data-id");dn.has(s)?dn.delete(s):dn.add(s),Vt()}),B&&Ga()}function Vt(){const e=l("#jobs");if(!e)return;l("#jobs-empty").style.display=lt.length?"none":"flex";const t=lt.filter(s=>s.state==="failed").length,n=lt.filter(s=>s.state!=="running"&&s.state!=="queued").length;l("#job-count").textContent=lt.length?t?t+" Fehler":lt.length+" gesamt":"";const i=l("#jobs-clear");i&&(i.style.display=n?"":"none"),e.innerHTML=lt.map(s=>{const a=dn.has(s.id)?`
${g((s.log||[]).join(` +`))}
`:"",r=Bo(s),o=r!=null?`
`:"",c=s.state==="running"?Ho(s.eta_s):"",u=s.state==="running"&&s.rate_bps?rt(s.rate_bps)+"/s":"",p=[c,u].filter(Boolean).join(" · "),v=r!=null?`${Math.round(r)} %${p?` · ${p}`:""}`:"",f=s.state==="running"?``:``;return`
+ ${g(s.label)}${v}${f}${Po(s.state)}
${o}${a}
`}).join("")}function Go(e){lt=e||[],Vt()}function Ko(e){B=e,l("#act-sys")&&Ga()}const qo={id:"jobs",mount:Io,onJobs:Go,onSystem:Ko};function Qi(){document.dispatchEvent(new Event("mc:refresh"))}function at(e,t,n,i,s){return``}function Uo(){l("#wartung").outerHTML=`
+
+

Server & Wartung

+
Dienste steuern, Updates einspielen und live mitlesen — ganz ohne SSH oder Terminal.
+
+ +
+

LLM-Engine (llama-swap)

+ prüfe…
+
Das Herzstück: llama-swap lädt bei jeder Anfrage automatisch das passende Modell und entlädt die anderen — es läuft immer nur eines gleichzeitig.
+
+
Adresse (für deine Tools)
+
Aktuell geladen
+
Konfigurierte Modelle
+
+
Wie ansteuern? In deinem Tool (Zed/OpenCode) die obige Adresse als OpenAI-Basis-URL eintragen und einen Modellnamen oder eine Rolle angeben (siehe „Verbinden"). Eine einzige Anfrage genügt — das Modell wird automatisch geweckt.
+
+
+ +
+

Dienste & Applikation

+
Diese Aktionen sind harmlos und passwortlos — es geht nichts dabei verloren.
+ ${at("w-restart-swap","refresh","teal","LLM-Engine neustarten","Startet llama-swap neu. Geladene Modelle werden kurz entladen (~5 Sek), laden danach automatisch wieder.")} + ${at("w-restart-mc","refresh","blue","Dashboard neustarten","Startet diese Oberfläche neu. Sie trennt kurz und verbindet automatisch wieder.")} + ${at("w-self-update","download","teal","Mission Control aktualisieren","Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu (~10 Sek).")} + ${at("w-update","bolt","blue","LLM-Engine aktualisieren","Lädt die neueste llama.cpp-Version (ROCm) für deine GPU und installiert sie.")} + ${at("w-unload","database","amber","Grafikspeicher leeren","Wirft alle aktuell geladenen Modelle aus dem VRAM. Fragt vorher nach.")} +
+ +
+

Betriebssystem (Bosgame)

+
Tiefe Eingriffe — das Dashboard fragt einmalig nach deinem sudo-Passwort.
+ ${at("w-os-update","refresh","amber","OS-Updates installieren","Führt apt update & upgrade aus. Kann ein paar Minuten dauern — Fortschritt in der Aktivität.")} + ${at("w-reboot","bolt","red","Server neustarten (Reboot)","Bootet den ganzen Bosgame neu. Alles ist für ~1 Minute offline, auch dieses Dashboard.")} +
+ + + +
+

Live-Konsole

+ +
+
Live mitlesen, was der Dienst gerade tut.
+
Verbinde…
+
+
`,l("#w-restart-swap").addEventListener("click",()=>Ss("llama-swap","LLM-Engine neustarten?","Die Engine startet neu. Geladene Modelle werden kurz entladen (~5 Sekunden), laden danach automatisch wieder.")),l("#w-restart-mc").addEventListener("click",()=>Ss("mission-control","Dashboard neustarten?","Diese Oberfläche trennt sich kurz und verbindet automatisch wieder. Laufende Downloads laufen weiter.")),l("#w-self-update").addEventListener("click",Qo),l("#w-update").addEventListener("click",Zo),l("#w-unload").addEventListener("click",Jo),l("#w-os-update").addEventListener("click",Xo),l("#w-reboot").addEventListener("click",ed),l("#w-console-sel").addEventListener("change",Ms),l("#w-update-cancel").addEventListener("click",Wo),l("#w-swap-check").addEventListener("click",jo),Oi&&Ka(Oi),Ms()}let Oi=null;function Ka(e){const t=l("#w-swap-state");if(!t)return;const n=!!(e!=null&&e.swap_ok);t.textContent=n?"● erreichbar":"● offline",t.style.cssText="margin-left:auto;"+(n?"background:rgba(63,185,80,.12);border-color:rgba(63,185,80,.25);color:#7ee29a":"background:rgba(248,81,73,.12);border-color:rgba(248,81,73,.3);color:#ff9b95"),l("#w-swap-url").textContent=((e==null?void 0:e.swap_url)||"—")+"/v1";const i=(e==null?void 0:e.models)||[],s=i.find(r=>r.state==="running"||r.state==="loading");l("#w-swap-loaded").innerHTML=s?`${g(s.name)} (${g(s.state)})`:'nichts geladen — lädt bei der nächsten Anfrage';const a=i.filter(r=>!r.incomplete).length;l("#w-swap-count").textContent=a?`${a} bereit`:"keine"}async function jo(){const e=l("#w-swap-check-res");e.textContent="prüfe…",e.style.color="var(--mut)";try{const t=await $("/api/integration/test");t.ok?(e.style.color="#7ee29a",e.textContent=`✓ Engine antwortet — ${t.models.length} Modell(e): ${t.models.join(", ")}`):(e.style.color="#ff9b95",e.textContent="✗ keine Verbindung: "+t.error)}catch(t){e.style.color="#ff9b95",e.textContent="✗ "+t.message}}function Vo(e){Oi=e,Ka(e)}async function Wo(){if(Le&&await Ee({title:Le.label+" abbrechen?",body:"Der laufende Vorgang wird gestoppt. Bei einem Download bleibt eine unvollständige Datei zurück (kannst du später neu starten).",confirmLabel:"Abbrechen",danger:!0}))try{await $(`/api/jobs/${Le.id}/cancel`,{method:"POST"}),w("Abbruch angefordert…")}catch(e){w(e.message,!0)}}let St=null;function Ms(){St&&(St.close(),St=null);const e=l("#w-console-sel").value,t=l("#w-console");t.textContent="Verbinde mit "+e+`… +`;const n=location.protocol==="https:"?"wss:":"ws:";St=new WebSocket(`${n}//${location.host}/api/logs/${e}?token=${encodeURIComponent(Da())}`),St.onmessage=i=>{t.textContent+=i.data,t.scrollTop=t.scrollHeight},St.onclose=()=>{t.textContent+=` +— Verbindung getrennt —`}}async function Ss(e,t,n){if(await Ee({title:t,body:n,confirmLabel:"Neustarten"}))try{await $(`/api/service/${e}/restart`,{method:"POST"}),w("Neustart ausgelöst: "+e),setTimeout(Qi,2e3)}catch(i){w(i.message,!0)}}async function Jo(){if(await Ee({title:"Grafikspeicher leeren?",body:"Alle geladenen Modelle werden entladen. Sie laden beim nächsten Aufruf automatisch neu — es geht nichts verloren.",confirmLabel:"Leeren"}))try{await $("/api/unload",{method:"POST"}),w("Grafikspeicher geleert."),setTimeout(Qi,600)}catch(e){w(e.message,!0)}}async function Qo(){if(await Ee({title:"Mission Control aktualisieren?",body:"Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu. Die Seite trennt kurz und verbindet von selbst wieder (~10 Sek)."}))try{const e=await $("/api/self-update",{method:"POST"});w("Update läuft — Dashboard startet gleich neu."),Ji(e.job_id)}catch(e){w(e.message,!0)}}let Le=null;const $s=new Set;function qa(e,t){Le={id:e,label:t};const n=l("#w-update-card");n&&(n.style.display="",l("#w-update-label").textContent=t+" läuft…",l("#w-update-badge").innerHTML='läuft…',l("#w-update-log").textContent="Starte…",n.scrollIntoView({behavior:"smooth",block:"nearest"}))}function Yo(e){if(!Le)return;const t=(e||[]).find(v=>v.id===Le.id),n=l("#w-update-card");if(!t||!n)return;n.style.display="";const i=l("#w-update-log");i&&(i.textContent=(t.log||[]).join(` +`),i.scrollTop=i.scrollHeight);const s=t.state==="done",a=t.state==="failed",r=t.state==="canceled",o=s||a||r,c=l("#w-update-badge");c&&(c.innerHTML=s?'✓ fertig':a?'✗ fehlgeschlagen':r?'abgebrochen':'läuft…');let u=null;if(!o)if(typeof t.progress=="number")u=t.progress;else{const f=((t.log||[])[t.log.length-1]||"").match(/(\d+(?:\.\d+)?)\s*%/);f&&(u=parseFloat(f[1]))}const p=l("#w-update-bar");p&&(u!=null?(p.style.display="",p.querySelector("i").style.width=Math.min(100,u)+"%"):p.style.display="none"),l("#w-update-cancel").style.display=o?"none":"",o&&(l("#w-update-label").textContent=Le.label+(s?" — erfolgreich abgeschlossen.":r?" — abgebrochen.":" — fehlgeschlagen (siehe Protokoll)."),$s.has(t.id)||($s.add(t.id),w(s?"✓ "+Le.label+" abgeschlossen.":r?Le.label+" abgebrochen.":"✗ "+Le.label+" fehlgeschlagen.",a),s&&setTimeout(Qi,800)))}async function Zo(){if(await Ee({title:"LLM-Engine aktualisieren?",body:"Lädt die neueste llama.cpp-ROCm-Version und installiert sie. Die Engine ist während des Updates kurz nicht erreichbar."}))try{const e=await $("/api/update",{method:"POST"});w("LLM-Engine-Update läuft."),Ji(e.job_id),qa(e.job_id,"LLM-Engine-Update")}catch(e){w(e.message,!0)}}async function Xo(){if(!await Ee({title:"OS-Updates installieren?",body:"Führt apt update & upgrade aus. Das kann ein paar Minuten dauern; der Fortschritt erscheint in der Aktivität."}))return;const e=await Oa({title:"sudo-Passwort",body:"Für die System-Updates wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Installieren"});if(e)try{const t=await $("/api/os-update",{method:"POST",body:JSON.stringify({password:e})});w("OS-Update gestartet."),Ji(t.job_id),qa(t.job_id,"OS-Update")}catch(t){w(t.message,!0)}}async function ed(){if(!await Ee({title:"Server wirklich neu starten?",body:"Der ganze Bosgame startet physisch neu. Alles ist für ~1 Minute offline — auch dieses Dashboard.",confirmLabel:"Reboot",danger:!0}))return;const e=await Oa({title:"sudo-Passwort",body:"Für den Reboot wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Jetzt neustarten",danger:!0});if(e)try{await $("/api/reboot",{method:"POST",body:JSON.stringify({password:e})}),w("Reboot ausgelöst — bis gleich.")}catch(t){w(t.message,!0)}}const td={id:"server",mount:Uo,onJobs:Yo,onStatus:Vo},nd="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr, braucht aber mehr Speicher und wird etwas langsamer.",Ua="GGUF ist das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF-Datei kann sie nicht nutzen.",id="MoE (Mixture of Experts): viele Parameter fürs Wissen, aber pro Wort ist nur ein kleiner Teil aktiv → fast große-Modell-Qualität bei hohem Tempo. Ideal für deine APU.",sd={vision:"Bilder",coder:"Coden",reasoning:"Logik",agent:"Agenten & Tools",scout:"Allrounder",reviewer:"Review",manager:"Manager"},ad=[{label:"Bilder",kw:["-vl-","-vl","vision","llava","multimodal","-mm-","pixtral"]},{label:"Coden",kw:["coder","-code-","code-","codestral","starcoder"]},{label:"Logik",kw:["-r1","deepseek-r1","reasoning","qwq","magistral","-think","thinking","-o1"]},{label:"Agenten & Tools",kw:["hermes","-tool","command-r","watt","-fc-","function"]}];function ja(e){const t=(e||"").toLowerCase();for(const n of ad)if(n.kw.some(i=>t.includes(i)))return n.label;return"Allrounder"}function rd(e=""){const t=e.toLowerCase();return/\d+x\d+(\.\d+)?b/.test(t)||new RegExp("(?MoE${t?` · ~${t}B aktiv`:""}`}function Va(e){return e?`${g(e)}`:""}const od=[{id:"",label:"Alle"},{id:"coder",label:"Coder"},{id:"scout",label:"Scout"},{id:"vision",label:"Vision"},{id:"manager",label:"Manager"},{id:"reviewer",label:"Reviewer"}];let nn=null,sn=[],Ae=null,Dt="",Mn=[],Wa=null,cn=[];const dd={perfect:0,marginal:1,too_tight:2},Sn=e=>e==="perfect"?"ok":e==="marginal"?"warn":"bad",cd=e=>e==="perfect"?"Passt locker":e==="marginal"?"Wird knapp":"Zu groß";function ud(){const e=l(".view[data-view='cookbook']");e.innerHTML=` +
+

Modell-Cookbook

+
Wofür willst du es nutzen? Wir empfehlen das passende Setup für deine Hardware.
+
+ +

Wofür möchtest du es nutzen?

+ deine Hardware +
+
+
Lade Setups…
+
+ +

Aktuell beste Modelle für dein System

+ +
+
Automatisch aus vertrauenswürdigen Quellen (HuggingFace) — laufend aktuell, gefiltert auf das, was auf deine Hardware passt.
+
Lade aktuelle Empfehlungen…
+ +
+ Profi-Modus: HuggingFace direkt durchsuchen +
+
+ + +
+
+
+ +
+
+ passt locker + läuft, aber knapp + zu groß für deinen Speicher +
+
Es werden nur Modelle im GGUF-Format ${Jn(Ua)} gezeigt — nur die laufen lokal.
+
+
+
+ + + + + + `,Ja(),l("#cb-btn-search").addEventListener("click",Rn),l("#cb-search").addEventListener("keydown",t=>{t.key==="Enter"&&Rn()}),l("#cb-sort").addEventListener("change",()=>{(l("#cb-search").value.trim()||Dt)&&Rn()}),l("#cb-modal-close").addEventListener("click",()=>l("#cb-modal").style.display="none"),l("#cb-modal").addEventListener("click",t=>{t.target.id==="cb-modal"&&(l("#cb-modal").style.display="none")}),l("#cb-m-download").addEventListener("click",Td),l("#cb-m-files").addEventListener("change",Sd),l("#cb-m-ctx").addEventListener("change",Cs),window.cbSetCtx=t=>{l("#cb-m-ctx").value=t,Cs()},l("#cb-r-close").addEventListener("click",()=>l("#cb-recipe-modal").style.display="none"),l("#cb-recipe-modal").addEventListener("click",t=>{t.target.id==="cb-recipe-modal"&&(l("#cb-recipe-modal").style.display="none")}),l("#cb-new-open").addEventListener("click",gd),l("#cb-new-close").addEventListener("click",()=>l("#cb-new-modal").style.display="none"),l("#cb-new-modal").addEventListener("click",t=>{t.target.id==="cb-new-modal"&&(l("#cb-new-modal").style.display="none")}),l("#cb-new-add").addEventListener("click",()=>l("#cb-new-models").insertAdjacentHTML("beforeend",Xi())),l("#cb-new-models").addEventListener("click",t=>{const n=t.target.closest(".cb-nm-del");n&&n.closest(".cb-nm-row").remove()}),l("#cb-new-save").addEventListener("click",wd),l("#cb-disc-refresh").addEventListener("click",()=>Es(!0)),l("#cb-discover").addEventListener("click",t=>{const n=t.target.closest("[data-inst]");n&&vd(n.getAttribute("data-inst"),n.getAttribute("data-role"),n.getAttribute("data-pb"),n)}),Qa(),Zi(),Es()}function fd(e){const t=Date.now()/1e3-e;return t<90?"gerade eben":t<3600?`vor ${Math.round(t/60)} min`:t<86400?`vor ${Math.round(t/3600)} h`:`vor ${Math.round(t/86400)} Tg.`}async function Es(e=!1){const t=l("#cb-discover"),n=l("#cb-disc-when"),i=l("#cb-disc-refresh");e&&(i.disabled=!0,i.textContent="Suche…",t.innerHTML='
Frage Quellen ab…
');try{const s=await $("/api/cookbook/discover"+(e?"?force=true":""));n&&(n.textContent=s.updated?`aktualisiert ${fd(s.updated)}`:""),pd(s)}catch(s){t.innerHTML=`
Empfehlungen nicht ladbar: ${g(s.message)}
`}i.disabled=!1,i.textContent="Aktualisieren"}function pd(e){const t=l("#cb-discover"),n=e.categories||[];if(!n.length){t.innerHTML='
Keine passenden Modelle gefunden.
';return}t.innerHTML=n.map(i=>` +
+
${nt(i.icon)} +

${g(i.title)}

+
${i.models.map(s=>hd(s,i.recommended)).join("")}
+
`).join("")}function hd(e,t){const n=(e.requirements||[]).map(o=>`
⚠ ${g(o)}
`).join(""),i=t&&e.repo===t,s=i?'
★ Beste Wahl für dein System
':"",a=`
${Va(sd[e.role]||ja(e.repo))}${Yi(e.repo)}
`,r=e.installed?`✓ installiert`:``;return`
+ ${s} +
+

${g(e.name)}

+
${g(e.author)} · ${e.params_b}B
+ ${g(e.fit.text)} +
+ ${a} +
${Ya(e.fit)} · ⬇ ${(e.downloads||0).toLocaleString()}
+ ${n} +
+
${r}
+
`}async function vd(e,t,n,i){var s;i.disabled=!0,i.textContent="Starte…";try{await $("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:e,role:t,params_b:parseFloat(n)||7,quant:"Q4_K_M",hf_token:_n()})}),w("Download gestartet — siehe Aktivität."),(s=document.querySelector(".nav-item[data-view='activity']"))==null||s.click()}catch(a){w("Fehler: "+a.message,!0),i.disabled=!1,i.textContent="Installieren"}}async function Zi(){try{const e=await $("/api/cookbook/recipes");Mn=e.recipes||[],Wa=e.recommended_id||null,md()}catch(e){l("#cb-recipes").innerHTML=`
Setups nicht ladbar: ${g(e.message)}
`}}function md(){l("#cb-recipes").innerHTML=Mn.map(e=>{const t=e.id===Wa,n=t?'
★ Beste Wahl für dein System
':e.user?'
Dein Setup
':"",i=e.user?` + + ×`:"";return``}).join(""),l("#cb-recipes").querySelectorAll("[data-recipe]").forEach(e=>e.addEventListener("click",()=>xd(e.getAttribute("data-recipe")))),l("#cb-recipes").querySelectorAll("[data-delr]").forEach(e=>e.addEventListener("click",t=>{t.stopPropagation(),yd(e.getAttribute("data-delr"))})),l("#cb-recipes").querySelectorAll("[data-editr]").forEach(e=>e.addEventListener("click",t=>{t.stopPropagation(),bd(e.getAttribute("data-editr"))}))}function Xi(){return`
+ + + + +
`}let Ct=null;function gd(){Ct=null,l("#cb-new-title").value="",l("#cb-new-desc").value="",l("#cb-new-models").innerHTML=Xi(),l("#cb-new-modal-title").textContent="Eigenes Setup erstellen",l("#cb-new-save").textContent="Setup speichern",l("#cb-new-modal").style.display="flex"}function bd(e){const t=Mn.find(n=>n.id===e);t&&(Ct=e,l("#cb-new-title").value=t.title,l("#cb-new-desc").value=t.desc||"",l("#cb-new-models").innerHTML=t.models.map(n=>{const i=document.createElement("div");i.innerHTML=Xi();const s=i.firstElementChild;s.querySelector(".cb-nm-repo").value=n.repo,s.querySelector(".cb-nm-role").value=n.role;const a=s.querySelector(".cb-nm-quant");return a&&[...a.options].forEach(r=>{r.value===n.quant&&(r.selected=!0)}),s.outerHTML}).join(""),l("#cb-new-modal-title").textContent="Setup bearbeiten",l("#cb-new-save").textContent="Änderungen speichern",l("#cb-new-modal").style.display="flex")}async function wd(){const e=l("#cb-new-title").value.trim();if(!e)return w("Bitte einen Titel angeben.",!0);const t=[...l("#cb-new-models").querySelectorAll(".cb-nm-row")].map(i=>({repo:i.querySelector(".cb-nm-repo").value.trim(),role:i.querySelector(".cb-nm-role").value.trim()||"modell",quant:i.querySelector(".cb-nm-quant").value})).filter(i=>i.repo);if(!t.length)return w("Mindestens ein Modell (Repo) angeben.",!0);const n=l("#cb-new-save");n.disabled=!0,n.textContent="Speichere…";try{const i=JSON.stringify({title:e,desc:l("#cb-new-desc").value.trim(),models:t});Ct?(await $("/api/cookbook/user-recipe/"+encodeURIComponent(Ct),{method:"PUT",body:i}),w("Setup aktualisiert.")):(await $("/api/cookbook/user-recipe",{method:"POST",body:i}),w("Setup gespeichert.")),Ct=null,l("#cb-new-modal").style.display="none",Zi()}catch(i){w("Fehler: "+i.message,!0)}n.disabled=!1,n.textContent=Ct?"Änderungen speichern":"Setup speichern"}async function yd(e){const t=Mn.find(n=>n.id===e);if(await Ee({title:`„${t?t.title:e}" löschen?`,body:"Dein eigenes Setup wird aus dem Cookbook entfernt. Bereits installierte Modelle bleiben unangetastet.",confirmLabel:"Löschen",danger:!0}))try{await $("/api/cookbook/user-recipe/"+encodeURIComponent(e),{method:"DELETE"}),w("Setup gelöscht."),Zi()}catch(n){w(n.message,!0)}}function xd(e){const t=Mn.find(s=>s.id===e);if(!t)return;l("#cb-r-title").textContent=t.title,l("#cb-r-desc").textContent=t.desc,l("#cb-r-models").innerHTML=`
${t.models.map(s=>` +
+
+
+ ${g(s.name)}${g(s.role)}${Yi(s.repo)}
+
${g(s.why)}
+
~${s.fit.req_gb.toFixed(1)} GB · ~${Math.round(s.fit.tps)} Tok/s · optimal ~${Math.round(s.optimal_ctx/1024)}k Kontext
+
+ ${g(s.fit.text)} +
`).join("")}
`;const n=Math.max(...t.models.map(s=>s.fit.req_gb));l("#cb-r-note").innerHTML=`Größter Spitzenbedarf: ~${n.toFixed(1)} GB. Es läuft immer nur ein Modell gleichzeitig — das größte bestimmt, ob das Setup passt (kein Summieren).`;const i=l("#cb-r-install");i.className=t.fit_level==="too_tight"?"primary warn":"primary",i.textContent=t.fit_level==="too_tight"?"Trotzdem installieren (zu groß)":"Komplettes Setup installieren",i.onclick=()=>_d(t.id),l("#cb-recipe-modal").style.display="flex"}async function _d(e){var n;const t=l("#cb-r-install");t.disabled=!0,t.textContent="Starte…";try{const i=await $("/api/cookbook/install-recipe",{method:"POST",body:JSON.stringify({recipe_id:e,hf_token:_n()})});w(`Setup wird installiert (${i.count} Modelle) — siehe Aktivität.`),l("#cb-recipe-modal").style.display="none",(n=document.querySelector(".nav-item[data-view='activity']"))==null||n.click()}catch(i){w("Fehler: "+i.message,!0)}t.disabled=!1,t.textContent="Komplettes Setup installieren"}function Ja(){l("#cb-filters").innerHTML=od.map(e=>``).join(""),l("#cb-filters").querySelectorAll("[data-f]").forEach(e=>e.addEventListener("click",()=>{Dt=e.getAttribute("data-f"),Ja(),Rn()}))}function Qa(){var t;const e=l("#cb-hw");e&&(t=nn==null?void 0:nn.ram)!=null&&t.total&&(e.innerHTML=`${nt("cpu")}${Math.round(nn.ram.total/1024**3)} GB Speicher`)}function Ya(e){return`~${e.req_gb.toFixed(1)} GB · ~${Math.round(e.tps)} Tok/s`}function kd(e){e=e.trim();const t=e.match(/huggingface\.co\/([^\s/]+\/[^\s/?#]+)/i);return t?t[1]:/^[\w.-]+\/[\w.-]+$/.test(e)?e:null}async function Rn(){var s;const e=l("#cb-search").value.trim(),t=kd(e);let n=e;if(Dt&&!t&&(n=n?n+" "+Dt:Dt),!n&&!t){l("#cb-grid").innerHTML="";return}const i=l("#cb-btn-search");i.disabled=!0,i.textContent="Lade…",l("#cb-grid").innerHTML='
Suche auf HuggingFace…
';try{if(t)sn=[{id:t,author:t.split("/")[0],downloads:0}],Ts(sn);else{const a=((s=l("#cb-sort"))==null?void 0:s.value)||"downloads",r=`https://huggingface.co/api/models?search=${encodeURIComponent(n)}&filter=gguf&sort=${encodeURIComponent(a)}&direction=-1&limit=40`;sn=await(await fetch(r)).json(),Ts(sn)}}catch(a){l("#cb-grid").innerHTML=`
${g(a.message)}
`}i.disabled=!1,i.textContent="Suchen"}function Ts(e){const t=l("#cb-grid");if(cn=[],!(e!=null&&e.length)){t.innerHTML='
Keine GGUF-Modelle gefunden.
';return}t.innerHTML=e.map((n,i)=>` +
+
+

${g(n.id.split("/").pop())}

+
${g(n.author||"")}
+ prüfe… +
+
${Va(ja(n.id))}${Yi(n.id)}
+
+
+ Hardware-Fit… + ⬇ ${(n.downloads||0).toLocaleString()}
+
`).join(""),t.querySelectorAll("[data-res]").forEach(n=>n.addEventListener("click",()=>Ed(+n.getAttribute("data-res")))),e.forEach((n,i)=>Md(i,n.id))}async function Md(e,t){var n;try{const i=await $("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:t,ctx:8192})}),s=l("#cb-b-"+e),a=l("#cb-m-"+e);if(!s||!a)return;if(!((n=i.files)!=null&&n.length)){s.className="fit-badge",s.style.cssText="background:rgba(139,151,165,.14);color:var(--mut)",s.title=Ua,s.textContent="kein GGUF",a.textContent="—",cn[e]=null,bi();return}let r=i.files.find(o=>{var c;return(c=o.quant)==null?void 0:c.includes("Q4_K_M")})||i.files[0];s.className="fit-badge "+Sn(r.fit.level),s.textContent=r.fit.text,a.textContent=Ya(r.fit)+" · "+(r.quant||"GGUF"),cn[e]=r.fit.level,bi()}catch{const i=l("#cb-b-"+e);i&&(i.className="fit-badge bad",i.textContent="Fehler"),cn[e]=null,bi()}}function bi(){const e=l("#cb-grid");if(!e)return;let t=-1,n=99;if(cn.forEach((s,a)=>{if(s==null)return;const r=dd[s]??99;r{var a;s.classList.remove("res-best"),(a=s.querySelector(".cb-best-tag"))==null||a.remove()}),t<0||n>1)return;const i=e.querySelector(`[data-res="${t}"]`);i&&(i.classList.add("res-best"),i.insertAdjacentHTML("afterbegin",'
★ Beste Wahl für dein System
'))}function es(){const e=l("#cb-m-files").value,t=Ae==null?void 0:Ae.files.find(a=>a.filename===e);if(!t){l("#cb-m-fit").style.display="none";return}l("#cb-m-fit").style.display="flex",l("#cb-m-fit-text").innerHTML=`Bedarf: ~${t.fit.req_gb.toFixed(1)} GB · ${Ae.params_b}B · ${g(t.quant)} · ~${Math.round(t.fit.tps)} Tok/s`,l("#cb-m-fit-badge").innerHTML=`${g(t.fit.text)}`;const n=t.optimal_ctx,i=l("#cb-m-ctx-rec");if(n){const a=l("#cb-m-ctx");a&&(a.value==="8192"||a.value==="")&&(a.value=n)}i&&(i.innerHTML=n?`Empfohlener Kontext für deine Hardware: ~${Math.round(n/1024)}kübernehmen`:"");const s=l("#cb-m-download");t.fit.level==="too_tight"?(s.className="primary warn",s.textContent="Trotzdem holen (zu groß)"):(s.className="primary",s.textContent="Herunterladen & Einpflegen")}function Sd(){es()}async function Cs(){if(!Ae)return;const e=parseInt(l("#cb-m-ctx").value)||8192,t=l("#cb-m-files").value;try{Ae=await $("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:Ae.repo,ctx:e})}),l("#cb-m-files").value=t,es()}catch{}}function $d(e,t){l("#cb-modal").style.display="flex",l("#cb-m-title").textContent=e,l("#cb-m-repo").textContent=t}async function Ed(e){var n;const t=sn[e];if(t){$d(t.id.split("/").pop(),t.id),l("#cb-m-alias").value="",l("#cb-m-files").style.display="none",l("#cb-m-loading").style.display="block",l("#cb-m-download").disabled=!0;try{const i=parseInt(l("#cb-m-ctx").value)||8192;if(Ae=await $("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:t.id,ctx:i})}),l("#cb-m-loading").style.display="none",l("#cb-m-files").style.display="block",!((n=Ae.files)!=null&&n.length)){l("#cb-m-files").innerHTML="",l("#cb-m-fit").style.display="none";return}l("#cb-m-files").innerHTML=Ae.files.map(s=>{const a=s.fit.level==="perfect"?"●":s.fit.level==="marginal"?"◐":"○";return``}).join(""),l("#cb-m-download").disabled=!1,es()}catch(i){l("#cb-m-loading").textContent="Fehler: "+i.message}}}async function Td(){var a;const e=l("#cb-m-repo").textContent,t=l("#cb-m-files").value,n=l("#cb-m-alias").value.trim().toLowerCase(),i=parseInt(l("#cb-m-ctx").value)||8192;if(!e||!t)return w("Bitte eine GGUF-Datei wählen.",!0);const s=l("#cb-m-download");s.disabled=!0,s.textContent="Starte…";try{const r=await $("/api/download",{method:"POST",body:JSON.stringify({repo:e,file:t,hf_token:_n()})});await $("/api/register",{method:"POST",body:JSON.stringify({role:n,model_path:r.expected_path,ctx:i})}),w("Download gestartet — siehe Aktivität."),l("#cb-modal").style.display="none",(a=document.querySelector(".nav-item[data-view='activity']"))==null||a.click()}catch(r){w("Fehler: "+r.message,!0)}s.disabled=!1,s.textContent="Herunterladen & Einpflegen"}function Cd(e){nn=e,Qa()}const Ld={id:"cookbook",mount:ud,onSystem:Cd};let Fn=null,Ls="",Lt="zed",Se=[];window.mcCopyCode=e=>{var i;const t=e.parentElement.querySelector("code,pre");(i=navigator.clipboard)==null||i.writeText((t==null?void 0:t.textContent)||"");const n=e.textContent;e.textContent="Kopiert!",setTimeout(()=>e.textContent=n,1400)};window.mcCopyVal=e=>{var i;const t=e.previousElementSibling;(i=navigator.clipboard)==null||i.writeText(t.value);const n=e.textContent;e.textContent="Kopiert!",setTimeout(()=>e.textContent=n,1400)};window.cnGo=e=>{var t;return(t=document.querySelector(`.nav-item[data-view="${e}"]`))==null?void 0:t.click()};window.cnSelectTool=e=>{Lt=e,ts()};window.cnToggleModel=e=>{Se.includes(e)?Se=Se.filter(t=>t!==e):Se.push(e),ts()};function Ad(){return`http://${location.hostname}:8080/v1`}function zd(e){return/^\d{1,3}(\.\d{1,3}){3}$/.test(e)}function Dd(){return location.protocol==="https:"||!zd(location.hostname)}function Xe(){return((Fn==null?void 0:Fn.models)||[]).filter(e=>!e.incomplete)}function Pd(){return Se.length?Se:Xe().map(e=>e.name)}function an(e,t){return` +
+ + +
`}const Za=e=>`${nt("check")}${g(e)}`;function Od(e,t){const i=(t.length?Xe().filter(s=>t.includes(s.name)):Xe().length?Xe():[{name:"coder",meta:{}}]).map(s=>{var o;const r=(((o=s.meta)==null?void 0:o.caps)||[]).some(c=>/Bild|Vision/i.test(c))?', "capabilities": { "tools": true, "images": true }':"";return` { "name": "${s.name}", "display_name": "${s.name}", "max_tokens": 32768${r} }`}).join(`, +`);return`"language_models": { + "openai_compatible": { + "bosgame": { + "api_url": "${e}", + "available_models": [ +${i} + ] + } + } +}`}function Bd(e,t){const i=(t.length?t:Xe().length?Xe().map(s=>s.name):["coder"]).map(s=>` "${s}": { "name": "${s}" }`).join(`, +`);return`"provider": { + "llama-swap": { + "npm": "@ai-sdk/openai-compatible", + "name": "Bosgame (llama-swap)", + "options": { + "baseURL": "${e}", + "apiKey": "local" + }, + "models": { +${i} + } + } +}`}function Hd(e){return`
+

1 · Verbindung prüfen

+
Stelle sicher, dass die LLM-Engine erreichbar ist, bevor du Tools konfigurierst.
+ ${an("Engine-Adresse (für alle Tools)",e)} + ${Dd()?`
+ Du öffnest Mission Control über einen Proxy/Namen (HTTPS o.ä.). Die Engine läuft auf Port 8080 ohne HTTPS direkt am Bosgame. + Trag in deinen Tools die LAN-IP ein: http://192.168.178.151:8080/v1 — nicht den Proxy-Namen.
`:""} +
+ + +
+
`}function Rd(){return`
+

Wie funktioniert das Auto-Swap?

+
+
+ +
+
Kein manuelles Starten nötig
+
Wenn du in Zed oder OpenCode „coder" auswählst und eine Anfrage sendest, + lädt llama-swap das Modell automatisch — in ca. 2–5 Sekunden. Du siehst es hier im Dashboard unter „Aktives Modell".
+
+
+
+ 🔄 +
+
Modelle wechseln sich automatisch ab
+
Rufst du ein anderes Modell auf, entlädt llama-swap das aktuelle (nach TTL-Ablauf) + und lädt das neue. Es läuft immer nur eines gleichzeitig — llama-swap verwaltet das komplett selbst.
+
+
+
+ 🧠 +
+
Zwei Modelle gleichzeitig aktiv halten
+
Du hast 124 GB RAM — genug für z.B. Scout 8B (~5 GB) dauerhaft geladen + ein + größeres Modell das swappt. Dafür beim Scout im Modelle-Tab → Konfigurieren die TTL auf 99999 setzen + (= nie automatisch entladen). Das kleine Modell beantwortet schnelle Fragen sofort, ohne jede Wartezeit.
+
+
+
+
`}function Fd(){const e=Xe();if(!e.length)return"";const t=e.map(i=>`${g(i.name)}`).join(" ");return`
+

2 · Modelle auswählen

${Se.length?`${Se.length} von ${e.length} ausgewählt`:"alle werden eingeschlossen"}
+
Wähle, welche Modelle im Config-Snippet erscheinen sollen. Standardmäßig alle.
+
${t}
+ ${Se.length?'':""} +
`}window.cnClearModels=()=>{Se=[],ts()};function zn(e,t,n){return``}function Nd(){return`
+

3 · Tool wählen

+
Für welches Werkzeug brauchst du die Konfiguration?
+
+ ${zn("zed","Zed","Schneller nativer Editor — Empfohlen")} + ${zn("opencode","OpenCode","Open-Source Coding-Agent (Terminal/Desktop)")} + ${zn("cline","Cline / Cursor","VS Code Extension, Bring-Your-Own-Key")} + ${zn("openwebui","OpenWebUI","Browser-Chat-Interface")} +
+
`}function Id(e){const t=Pd(),n=t[0]||"coder";let i="";if(Lt==="zed"){const s=Od(e,t);i=` +

Öffne die Zed-Settings mit Cmd/Strg + , und füge diesen Block in deine bestehende + settings.json ein. Falls bereits ein "bosgame"-Eintrag existiert: vorher löschen.

+
+ Nur diesen Block einfügen, nicht die ganze Datei ersetzen — sonst gehen andere Einstellungen verloren. +
+
+ +
${g(s)}
+
+

API-Key: Öffne den Agent-Panel → Anbieter bosgame → trag irgendeinen Wert ein (z.B. local). + Alternativ BOSGAME_API_KEY=local als Umgebungsvariable.

+
Danach im Agent-Panel das Modell bosgame / ${g(n)} auswählen. Das Auto-Swap lädt es beim ersten Request.
`}else if(Lt==="opencode"){const s=Bd(e,t);i=` +

Datei anlegen oder öffnen:

+

Windows: %USERPROFILE%\\.config\\opencode\\opencode.json
+ Linux/Mac: ~/.config/opencode/opencode.json
+ Oder im Projektordner: opencode.json

+
+ Nur den "provider"-Block einfügen oder mergen. + Falls bereits ein "llama-swap"-Eintrag vorhanden ist: vorher löschen, sonst erscheinen Modelle doppelt. +
+
+ +
${g(s)}
+
+

Danach in OpenCode /modelsllama-swap/${g(n)} wählen.

+
Statt alles zu ersetzen: nur den provider-Block in deine bestehende Config einfügen — so bleiben andere Anbieter erhalten.
`}else Lt==="cline"?i=` +

In Cline/Cursor den Provider „OpenAI Compatible" wählen und folgende Felder eintragen:

+ ${an("Basis-URL (OpenAI Compatible)",e)} + ${an("Modell-ID",n)} +
API-Key: ein beliebiger Wert (z.B. local) — llama-swap prüft ihn nicht.
`:Lt==="openwebui"&&(i=` +

Settings → Admin Panel → Connections → neue OpenAI-Verbindung:

+ ${an("Basis-URL",e)} + ${an("API-Key","dummy-key")} +
Nach dem Speichern erscheinen die Modelle automatisch in der Modell-Auswahl.
`);return`
+

4 · Config-Snippet

+ ${i} +
`}function Gd(e){var i;const t=(i=Xe().find(s=>{var a,r;return(r=(a=s.meta)==null?void 0:a.caps)==null?void 0:r.includes("Bild")}))==null?void 0:i.name;if(!t)return"";const n=`curl ${e}/chat/completions \\ + -H "Content-Type: application/json" \\ + -d '{ + "model": "${t}", + "messages": [{"role":"user","content":[ + {"type":"text","text":"Was ist auf dem Bild falsch?"}, + {"type":"image_url","image_url":{"url":"data:image/png;base64,"}} + ]}] +}'`;return`
+

Bilder einbinden (Vision)

${Za(`„${t}" bereit`)}
+
Mit Vision-Modellen kannst du Screenshots oder Fehlerbilder direkt mitschicken.
+
+ +
${g(n)}
+
+
`}function un(){var n;const e=l(".view[data-view='connect']");if(!e)return;const t=Ad();e.innerHTML=` +
+

Verbinden

+
Schritt für Schritt: KI-Modelle in deine Tools einbinden — getestet, erklärt, mit Copy-Paste-Config.
+
+ + ${Hd(t)} + ${Rd()} + ${Fd()} + ${Nd()} + ${Id(t)} + ${Gd(t)}`,(n=l("#cn-test"))==null||n.addEventListener("click",Kd)}function ts(){if(!l(".view[data-view='connect'] .card:nth-of-type(3)")){un();return}const t=l(".view[data-view='connect']"),n=t==null?void 0:t.querySelectorAll(".card");if(!n||n.length<3){un();return}un()}async function Kd(){const e=l("#cn-test-result");e.innerHTML='Teste…';try{const t=await $("/api/integration/test");e.innerHTML=t.ok?Za(`Verbunden — ${t.models.length} Modell(e): ${t.models.join(", ")}`):`✗ Keine Verbindung: ${g(t.error)}`}catch(t){e.innerHTML=`✗ ${g(t.message)}`}}function qd(){un()}function Ud(e){Fn=e;const t=((e==null?void 0:e.models)||[]).map(n=>n.name).join(",")+"|"+!!e;t!==Ls&&(Ls=t,un())}const jd={id:"connect",mount:qd,onStatus:Ud},ni=[ho,zo,td,qo,Ld,jd];let As={};function Vd(){const e=document.getElementById("top-active-text");e&&(e.classList.add("swap-flash"),setTimeout(()=>e.classList.remove("swap-flash"),900))}function zs(e){var s;const t=document.getElementById("swdot"),n=document.getElementById("swlabel"),i=document.getElementById("top-active-text");if(!e)t&&(t.className="dot off"),n&&(n.textContent="Backend nicht erreichbar"),i&&(i.textContent="Backend offline"),Ds("Backend nicht erreichbar – läuft uvicorn?",!1);else{const a=(e.swap_url||"").replace(/^https?:\/\//,"");t&&(t.className="dot "+(e.swap_ok?"on":"off")),n&&(n.textContent=(e.swap_ok?"LLM-Engine: Online – ":"LLM-Engine: Offline – ")+a);const r=(e.models||[]).find(c=>c.state==="running");i&&(i.innerHTML=r?`Geladen: ${r.name}`:"Kein Modell im VRAM"),e.swap_ok?Yd():Ds(`LLM-Engine nicht erreichbar unter ${a} – läuft der llama-swap Dienst?`,!0);const o={};for(const c of e.models||[])o[c.name]=c.state;for(const[c,u]of Object.entries(o)){const p=As[c];if(p&&p!==u&&(u==="running"||u==="loading")){Vd();break}}As=o}Wd(e),Rl.set(e);for(const a of ni)(s=a.onStatus)==null||s.call(a,e)}function Wd(e){const t=document.getElementById("sec-chip"),n=document.getElementById("sec-chip-tx");!t||!n||(t.classList.toggle("ok",!!(e!=null&&e.secured)),n.textContent=e!=null&&e.secured?"Geschützt · Heimnetz":"Nur im Heimnetz (offen)")}function Jd(e){var t;Fl.set(e||[]);for(const n of ni)(t=n.onJobs)==null||t.call(n,e)}function Qd(e){var t;for(const n of ni)(t=n.onSystem)==null||t.call(n,e)}function Ds(e,t){const n=document.getElementById("alert");n.className="alert"+(t?" warn":""),n.innerHTML=`${e}`,n.style.display="flex"}function Yd(){const e=document.getElementById("alert");e&&(e.style.display="none")}const Zd=e=>{var t;return(t=document.querySelector(`.nav-item[data-view="${e}"]`))==null?void 0:t.click()};function wi(e,t,n,i){const s=document.createElement("span");return s.className="upd-badge"+(t?" "+t:""),s.textContent=e,i&&(s.title=i),s.onclick=()=>Zd(n),s}async function Xa(){try{const e=await $("/api/updates"),t=document.getElementById("update-badges");if(!t)return;const n=e.apt_cache_age_h,i=n!=null&&n>24,s=n!=null?` · apt-Cache: vor ${n}h`:"";t.innerHTML="",t.appendChild(wi(e.os>0?`OS: ${e.os} Updates`:i?"OS: ?":"OS: aktuell",e.os>0||i?"warn":"ok","server",e.os>0?`${e.os} ausstehende Pakete${s}`:i?`apt-Cache veraltet (${n}h)`:`Keine Updates${s}`)),t.appendChild(wi(e.engine>0?"Engine: Update":"Engine: aktuell",e.engine>0?"warn":"ok","server")),t.appendChild(wi(e.models>0?`Modelle: ${e.models}`:"Modelle: aktuell",e.models>0?"warn":"ok","news"))}catch{}}async function ii(){try{zs(await $("/api/status"))}catch{zs(null)}}async function er(){try{Jd(await $("/api/jobs"))}catch{}}function tr(){const e=window.location.protocol==="https:"?"wss:":"ws:",t=new WebSocket(`${e}//${window.location.host}/api/system/stream`);t.onmessage=n=>{try{Qd(JSON.parse(n.data))}catch{}},t.onclose=()=>{setTimeout(tr,3e3)},t.onerror=()=>t.close()}var Ns;for(const e of ni)(Ns=e.mount)==null||Ns.call(e);const Ps=document.querySelector('.view[data-view="guides"]');Ps&&za(jl,{target:Ps});const Os=document.querySelector('.view[data-view="news"]');Os&&za(no,{target:Os});Hl("overview");const yi=document.getElementById("token");yi&&(yi.value=Da(),yi.addEventListener("change",e=>{Dl(e.target.value),ii()}));const xi=document.getElementById("hf-token");xi&&(xi.value=_n(),xi.addEventListener("change",e=>Ol(e.target.value)));const Bs=document.getElementById("nav-settings"),_i=document.getElementById("settings-modal"),Hs=document.getElementById("sm-close");Bs&&_i&&Hs&&(Bs.addEventListener("click",()=>_i.style.display="flex"),Hs.addEventListener("click",()=>_i.style.display="none"));function Xd(){const e=document.getElementById("clock");e&&(e.textContent=new Date().toTimeString().slice(0,5))}document.addEventListener("mc:refresh",ii);ii();er();tr();setInterval(Xd,1e3);setInterval(ii,3e3);setInterval(er,1500);Xa();setInterval(Xa,6e4); diff --git a/static/index.html b/static/index.html index e1176d8..11ae286 100644 --- a/static/index.html +++ b/static/index.html @@ -121,6 +121,6 @@ document.getElementById("logo").innerHTML = ICON.logo; document.querySelectorAll("[data-ic]").forEach(n => (n.innerHTML = ICON[n.dataset.ic] || "")); - +