From 9f661e4d57c78ef3f1d9d8852a9ee9b61a3bd2a6 Mon Sep 17 00:00:00 2001 From: Joakim Repomaa Date: Sat, 21 Feb 2026 17:37:49 +0200 Subject: [PATCH] initial commit --- .envrc | 1 + .gitignore | 26 + .npmrc | 1 + .prettierignore | 9 + .prettierrc | 17 + AGENTS.md | 122 ++ README.md | 42 + deno.lock | 1603 +++++++++++++++++ devbox.json | 13 + devbox.lock | 57 + eslint.config.js | 39 + opencode.json | 9 + package.json | 42 + src/app.d.ts | 13 + src/app.html | 11 + src/lib/assets/favicon.svg | 1 + src/lib/components/DHCPLeases.svelte | 72 + src/lib/components/EmptyState.svelte | 11 + .../components/NetworkInterfaceCard.svelte | 64 + src/lib/components/PageHeader.svelte | 15 + src/lib/components/PageLayout.svelte | 13 + src/lib/components/dhcp/LeaseBadge.svelte | 20 + src/lib/components/dhcp/LeaseTable.svelte | 53 + src/lib/components/network/AddressList.svelte | 17 + src/lib/components/network/DNSList.svelte | 20 + src/lib/components/network/InfoRow.svelte | 16 + .../components/network/InterfaceHeader.svelte | 27 + .../components/network/InterfaceIcon.svelte | 22 + src/lib/components/network/RoutesTable.svelte | 67 + src/lib/components/network/StatusBadge.svelte | 12 + src/lib/components/ui/Badge.svelte | 27 + src/lib/components/ui/Card.svelte | 14 + src/lib/components/ui/Code.svelte | 15 + src/lib/components/ui/Table.svelte | 16 + src/lib/components/ui/TableBody.svelte | 13 + src/lib/components/ui/TableCell.svelte | 17 + src/lib/components/ui/TableHead.svelte | 13 + src/lib/components/ui/TableHeader.svelte | 12 + src/lib/components/ui/TableRow.svelte | 14 + src/lib/index.ts | 1 + src/lib/types.ts | 301 ++++ src/lib/utils/network.ts | 54 + src/routes/+layout.svelte | 9 + src/routes/+page.svelte | 51 + src/routes/+page.ts | 9 + src/routes/layout.css | 1 + static/robots.txt | 3 + svelte.config.js | 6 + tsconfig.json | 20 + vite.config.ts | 6 + 50 files changed, 3037 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 AGENTS.md create mode 100644 README.md create mode 100644 deno.lock create mode 100644 devbox.json create mode 100644 devbox.lock create mode 100644 eslint.config.js create mode 100644 opencode.json create mode 100644 package.json create mode 100644 src/app.d.ts create mode 100644 src/app.html create mode 100644 src/lib/assets/favicon.svg create mode 100644 src/lib/components/DHCPLeases.svelte create mode 100644 src/lib/components/EmptyState.svelte create mode 100644 src/lib/components/NetworkInterfaceCard.svelte create mode 100644 src/lib/components/PageHeader.svelte create mode 100644 src/lib/components/PageLayout.svelte create mode 100644 src/lib/components/dhcp/LeaseBadge.svelte create mode 100644 src/lib/components/dhcp/LeaseTable.svelte create mode 100644 src/lib/components/network/AddressList.svelte create mode 100644 src/lib/components/network/DNSList.svelte create mode 100644 src/lib/components/network/InfoRow.svelte create mode 100644 src/lib/components/network/InterfaceHeader.svelte create mode 100644 src/lib/components/network/InterfaceIcon.svelte create mode 100644 src/lib/components/network/RoutesTable.svelte create mode 100644 src/lib/components/network/StatusBadge.svelte create mode 100644 src/lib/components/ui/Badge.svelte create mode 100644 src/lib/components/ui/Card.svelte create mode 100644 src/lib/components/ui/Code.svelte create mode 100644 src/lib/components/ui/Table.svelte create mode 100644 src/lib/components/ui/TableBody.svelte create mode 100644 src/lib/components/ui/TableCell.svelte create mode 100644 src/lib/components/ui/TableHead.svelte create mode 100644 src/lib/components/ui/TableHeader.svelte create mode 100644 src/lib/components/ui/TableRow.svelte create mode 100644 src/lib/index.ts create mode 100644 src/lib/types.ts create mode 100644 src/lib/utils/network.ts create mode 100644 src/routes/+layout.svelte create mode 100644 src/routes/+page.svelte create mode 100644 src/routes/+page.ts create mode 100644 src/routes/layout.css create mode 100644 static/robots.txt create mode 100644 svelte.config.js create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..64a402a --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +eval "$(devbox generate direnv --print-envrc)" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ba848f --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +# Devbox +.devbox/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7d74fe2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..7ef05b7 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,17 @@ +{ + "useTabs": false, + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "semi": false, + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ], + "tailwindStylesheet": "./src/routes/layout.css" +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c6fbb6b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,122 @@ +# Agent Instructions + +This is a SvelteKit 5 project with TypeScript and Tailwind CSS for a router dashboard interface. + +## Build Commands + +```bash +# Development server +deno run dev + +# Build for production +deno run build + +# Preview production build +deno run preview + +# Type checking +deno run check +deno run check:watch + +# Linting and formatting +deno run lint +deno run format +``` + +## Code Style Guidelines + +### TypeScript + +- Use strict TypeScript mode +- Use `import type` for type-only imports +- Prefer interfaces over type aliases for object shapes +- Use PascalCase for types, interfaces, enums, and components +- Use camelCase for functions, variables, and properties +- Enum values use PascalCase (e.g., `IPv4`, `Static`) +- Use explicit return types on exported functions + +### Svelte 5 Runes + +- Use `$props()` for component props with typed interfaces +- Use `$derived()` for computed values +- Always define Props interface for component props +- Example: + ```svelte + + ``` + +### Formatting + +- Use 2 spaces for indentation +- Single quotes for strings +- Trailing commas enabled +- No semicolons +- Print width: 100 +- Tailwind CSS class sorting enabled + +### Imports + +- Use `$lib/` alias for imports from src/lib +- Group imports: external libraries, then $lib imports, then relative imports +- Use type imports: `import type { NetworkInterface } from '$lib/types'` + +### Error Handling + +- Use optional chaining (`?.`) and nullish coalescing (`??`) for safe property access +- Provide default values for optional props +- Use proper TypeScript narrowing with type guards when needed + +### Component Structure + +- One component per file +- Use `.svelte` extension for components +- Place reusable UI components in `$lib/components/ui/` +- Place feature-specific components in `$lib/components//` +- Use `Snippet` type from svelte for children props + +### Styling + +- Use Tailwind CSS classes +- Prefer utility classes over custom CSS +- Use color utilities from the project's palette (green-600, yellow-600, red-600, etc.) + +## MCP Tools + +You have access to Svelte 5 and SvelteKit documentation via MCP: + +### 1. list-sections + +Use FIRST to discover available documentation sections. + +### 2. get-documentation + +Fetch documentation for specific sections after listing. + +### 3. svelte-autofixer + +MUST use this tool whenever writing Svelte code before sending to the user. Keep calling until no issues remain. + +### 4. playground-link + +Generate Svelte Playground links after user confirmation. Never use if code was written to project files. + +## Project Structure + +``` +src/ + routes/ # SvelteKit routes + lib/ + components/ # Svelte components + ui/ # Reusable UI primitives + network/ # Network-specific components + types.ts # TypeScript type definitions + utils/ # Utility functions +static/ # Static assets +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a8118d --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# sv + +Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```sh +# create a new project +npx sv create my-app +``` + +To recreate this project with the same configuration: + +```sh +# recreate this project +deno run npm:sv create --template minimal --types ts --add prettier eslint tailwindcss="plugins:none" sveltekit-adapter="adapter:static" devtools-json mcp="ide:opencode+setup:local" --install deno router-dash +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```sh +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```sh +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..223bfa5 --- /dev/null +++ b/deno.lock @@ -0,0 +1,1603 @@ +{ + "version": "5", + "specifiers": { + "npm:@eslint/compat@^2.0.2": "2.0.2_eslint@9.39.3", + "npm:@eslint/js@^9.39.2": "9.39.3", + "npm:@sveltejs/adapter-static@^3.0.10": "3.0.10_@sveltejs+kit@2.53.0__@sveltejs+vite-plugin-svelte@6.2.4___svelte@5.53.1____acorn@8.16.0___vite@7.3.1____@types+node@24.10.13____picomatch@4.0.3___@types+node@24.10.13__svelte@5.53.1___acorn@8.16.0__typescript@5.9.3__vite@7.3.1___@types+node@24.10.13___picomatch@4.0.3__acorn@8.16.0__@types+node@24.10.13_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.1___acorn@8.16.0__vite@7.3.1___@types+node@24.10.13___picomatch@4.0.3__@types+node@24.10.13_svelte@5.53.1__acorn@8.16.0_typescript@5.9.3_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13", + "npm:@sveltejs/kit@^2.50.2": "2.53.0_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.1___acorn@8.16.0__vite@7.3.1___@types+node@24.10.13___picomatch@4.0.3__@types+node@24.10.13_svelte@5.53.1__acorn@8.16.0_typescript@5.9.3_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_acorn@8.16.0_@types+node@24.10.13", + "npm:@sveltejs/vite-plugin-svelte@^6.2.4": "6.2.4_svelte@5.53.1__acorn@8.16.0_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13", + "npm:@tailwindcss/vite@^4.1.18": "4.2.0_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13", + "npm:@types/node@24": "24.10.13", + "npm:eslint-config-prettier@^10.1.8": "10.1.8_eslint@9.39.3", + "npm:eslint-plugin-svelte@^3.14.0": "3.15.0_eslint@9.39.3_svelte@5.53.1__acorn@8.16.0_postcss@8.5.6", + "npm:eslint@^9.39.2": "9.39.3", + "npm:globals@^17.3.0": "17.3.0", + "npm:lucide-svelte@0.575": "0.575.0_svelte@5.53.1__acorn@8.16.0", + "npm:prettier-plugin-svelte@^3.4.1": "3.5.0_prettier@3.8.1_svelte@5.53.1__acorn@8.16.0", + "npm:prettier-plugin-tailwindcss@~0.7.2": "0.7.2_prettier@3.8.1_prettier-plugin-svelte@3.5.0__prettier@3.8.1__svelte@5.53.1___acorn@8.16.0_svelte@5.53.1__acorn@8.16.0", + "npm:prettier@^3.8.1": "3.8.1", + "npm:svelte-check@^4.3.6": "4.4.3_svelte@5.53.1__acorn@8.16.0_typescript@5.9.3", + "npm:svelte@^5.51.0": "5.53.1_acorn@8.16.0", + "npm:tailwindcss@^4.1.18": "4.2.0", + "npm:typescript-eslint@^8.54.0": "8.56.0_eslint@9.39.3_typescript@5.9.3_@typescript-eslint+parser@8.56.0__eslint@9.39.3__typescript@5.9.3", + "npm:typescript@^5.9.3": "5.9.3", + "npm:vite-plugin-devtools-json@1": "1.0.0_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13", + "npm:vite@^7.3.1": "7.3.1_@types+node@24.10.13_picomatch@4.0.3" + }, + "npm": { + "@esbuild/aix-ppc64@0.27.3": { + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "os": ["aix"], + "cpu": ["ppc64"] + }, + "@esbuild/android-arm64@0.27.3": { + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm@0.27.3": { + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-x64@0.27.3": { + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/darwin-arm64@0.27.3": { + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-x64@0.27.3": { + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-arm64@0.27.3": { + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-x64@0.27.3": { + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/linux-arm64@0.27.3": { + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm@0.27.3": { + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-ia32@0.27.3": { + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-loong64@0.27.3": { + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-mips64el@0.27.3": { + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-ppc64@0.27.3": { + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-riscv64@0.27.3": { + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-s390x@0.27.3": { + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-x64@0.27.3": { + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-arm64@0.27.3": { + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "os": ["netbsd"], + "cpu": ["arm64"] + }, + "@esbuild/netbsd-x64@0.27.3": { + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-arm64@0.27.3": { + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "os": ["openbsd"], + "cpu": ["arm64"] + }, + "@esbuild/openbsd-x64@0.27.3": { + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openharmony-arm64@0.27.3": { + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@esbuild/sunos-x64@0.27.3": { + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/win32-arm64@0.27.3": { + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-ia32@0.27.3": { + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-x64@0.27.3": { + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@eslint-community/eslint-utils@4.9.1_eslint@9.39.3": { + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dependencies": [ + "eslint", + "eslint-visitor-keys@3.4.3" + ] + }, + "@eslint-community/regexpp@4.12.2": { + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==" + }, + "@eslint/compat@2.0.2_eslint@9.39.3": { + "integrity": "sha512-pR1DoD0h3HfF675QZx0xsyrsU8q70Z/plx7880NOhS02NuWLgBCOMDL787nUeQ7EWLkxv3bPQJaarjcPQb2Dwg==", + "dependencies": [ + "@eslint/core@1.1.0", + "eslint" + ], + "optionalPeers": [ + "eslint" + ] + }, + "@eslint/config-array@0.21.1": { + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dependencies": [ + "@eslint/object-schema", + "debug", + "minimatch@3.1.2" + ] + }, + "@eslint/config-helpers@0.4.2": { + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dependencies": [ + "@eslint/core@0.17.0" + ] + }, + "@eslint/core@0.17.0": { + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dependencies": [ + "@types/json-schema" + ] + }, + "@eslint/core@1.1.0": { + "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "dependencies": [ + "@types/json-schema" + ] + }, + "@eslint/eslintrc@3.3.3": { + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dependencies": [ + "ajv", + "debug", + "espree", + "globals@14.0.0", + "ignore@5.3.2", + "import-fresh", + "js-yaml", + "minimatch@3.1.2", + "strip-json-comments" + ] + }, + "@eslint/js@9.39.3": { + "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==" + }, + "@eslint/object-schema@2.1.7": { + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==" + }, + "@eslint/plugin-kit@0.4.1": { + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dependencies": [ + "@eslint/core@0.17.0", + "levn" + ] + }, + "@humanfs/core@0.19.1": { + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==" + }, + "@humanfs/node@0.16.7": { + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dependencies": [ + "@humanfs/core", + "@humanwhocodes/retry" + ] + }, + "@humanwhocodes/module-importer@1.0.1": { + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" + }, + "@humanwhocodes/retry@0.4.3": { + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==" + }, + "@jridgewell/gen-mapping@0.3.13": { + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dependencies": [ + "@jridgewell/sourcemap-codec", + "@jridgewell/trace-mapping" + ] + }, + "@jridgewell/remapping@2.3.5": { + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dependencies": [ + "@jridgewell/gen-mapping", + "@jridgewell/trace-mapping" + ] + }, + "@jridgewell/resolve-uri@3.1.2": { + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" + }, + "@jridgewell/sourcemap-codec@1.5.5": { + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "@jridgewell/trace-mapping@0.3.31": { + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dependencies": [ + "@jridgewell/resolve-uri", + "@jridgewell/sourcemap-codec" + ] + }, + "@polka/url@1.0.0-next.29": { + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==" + }, + "@rollup/rollup-android-arm-eabi@4.58.0": { + "integrity": "sha512-mr0tmS/4FoVk1cnaeN244A/wjvGDNItZKR8hRhnmCzygyRXYtKF5jVDSIILR1U97CTzAYmbgIj/Dukg62ggG5w==", + "os": ["android"], + "cpu": ["arm"] + }, + "@rollup/rollup-android-arm64@4.58.0": { + "integrity": "sha512-+s++dbp+/RTte62mQD9wLSbiMTV+xr/PeRJEc/sFZFSBRlHPNPVaf5FXlzAL77Mr8FtSfQqCN+I598M8U41ccQ==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@rollup/rollup-darwin-arm64@4.58.0": { + "integrity": "sha512-MFWBwTcYs0jZbINQBXHfSrpSQJq3IUOakcKPzfeSznONop14Pxuqa0Kg19GD0rNBMPQI2tFtu3UzapZpH0Uc1Q==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@rollup/rollup-darwin-x64@4.58.0": { + "integrity": "sha512-yiKJY7pj9c9JwzuKYLFaDZw5gma3fI9bkPEIyofvVfsPqjCWPglSHdpdwXpKGvDeYDms3Qal8qGMEHZ1M/4Udg==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@rollup/rollup-freebsd-arm64@4.58.0": { + "integrity": "sha512-x97kCoBh5MOevpn/CNK9W1x8BEzO238541BGWBc315uOlN0AD/ifZ1msg+ZQB05Ux+VF6EcYqpiagfLJ8U3LvQ==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@rollup/rollup-freebsd-x64@4.58.0": { + "integrity": "sha512-Aa8jPoZ6IQAG2eIrcXPpjRcMjROMFxCt1UYPZZtCxRV68WkuSigYtQ/7Zwrcr2IvtNJo7T2JfDXyMLxq5L4Jlg==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@rollup/rollup-linux-arm-gnueabihf@4.58.0": { + "integrity": "sha512-Ob8YgT5kD/lSIYW2Rcngs5kNB/44Q2RzBSPz9brf2WEtcGR7/f/E9HeHn1wYaAwKBni+bdXEwgHvUd0x12lQSA==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rollup/rollup-linux-arm-musleabihf@4.58.0": { + "integrity": "sha512-K+RI5oP1ceqoadvNt1FecL17Qtw/n9BgRSzxif3rTL2QlIu88ccvY+Y9nnHe/cmT5zbH9+bpiJuG1mGHRVwF4Q==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rollup/rollup-linux-arm64-gnu@4.58.0": { + "integrity": "sha512-T+17JAsCKUjmbopcKepJjHWHXSjeW7O5PL7lEFaeQmiVyw4kkc5/lyYKzrv6ElWRX/MrEWfPiJWqbTvfIvjM1Q==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rollup/rollup-linux-arm64-musl@4.58.0": { + "integrity": "sha512-cCePktb9+6R9itIJdeCFF9txPU7pQeEHB5AbHu/MKsfH/k70ZtOeq1k4YAtBv9Z7mmKI5/wOLYjQ+B9QdxR6LA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rollup/rollup-linux-loong64-gnu@4.58.0": { + "integrity": "sha512-iekUaLkfliAsDl4/xSdoCJ1gnnIXvoNz85C8U8+ZxknM5pBStfZjeXgB8lXobDQvvPRCN8FPmmuTtH+z95HTmg==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@rollup/rollup-linux-loong64-musl@4.58.0": { + "integrity": "sha512-68ofRgJNl/jYJbxFjCKE7IwhbfxOl1muPN4KbIqAIe32lm22KmU7E8OPvyy68HTNkI2iV/c8y2kSPSm2mW/Q9Q==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@rollup/rollup-linux-ppc64-gnu@4.58.0": { + "integrity": "sha512-dpz8vT0i+JqUKuSNPCP5SYyIV2Lh0sNL1+FhM7eLC457d5B9/BC3kDPp5BBftMmTNsBarcPcoz5UGSsnCiw4XQ==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@rollup/rollup-linux-ppc64-musl@4.58.0": { + "integrity": "sha512-4gdkkf9UJ7tafnweBCR/mk4jf3Jfl0cKX9Np80t5i78kjIH0ZdezUv/JDI2VtruE5lunfACqftJ8dIMGN4oHew==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@rollup/rollup-linux-riscv64-gnu@4.58.0": { + "integrity": "sha512-YFS4vPnOkDTD/JriUeeZurFYoJhPf9GQQEF/v4lltp3mVcBmnsAdjEWhr2cjUCZzZNzxCG0HZOvJU44UGHSdzw==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@rollup/rollup-linux-riscv64-musl@4.58.0": { + "integrity": "sha512-x2xgZlFne+QVNKV8b4wwaCS8pwq3y14zedZ5DqLzjdRITvreBk//4Knbcvm7+lWmms9V9qFp60MtUd0/t/PXPw==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@rollup/rollup-linux-s390x-gnu@4.58.0": { + "integrity": "sha512-jIhrujyn4UnWF8S+DHSkAkDEO3hLX0cjzxJZPLF80xFyzyUIYgSMRcYQ3+uqEoyDD2beGq7Dj7edi8OnJcS/hg==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@rollup/rollup-linux-x64-gnu@4.58.0": { + "integrity": "sha512-+410Srdoh78MKSJxTQ+hZ/Mx+ajd6RjjPwBPNd0R3J9FtL6ZA0GqiiyNjCO9In0IzZkCNrpGymSfn+kgyPQocg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rollup/rollup-linux-x64-musl@4.58.0": { + "integrity": "sha512-ZjMyby5SICi227y1MTR3VYBpFTdZs823Rs/hpakufleBoufoOIB6jtm9FEoxn/cgO7l6PM2rCEl5Kre5vX0QrQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rollup/rollup-openbsd-x64@4.58.0": { + "integrity": "sha512-ds4iwfYkSQ0k1nb8LTcyXw//ToHOnNTJtceySpL3fa7tc/AsE+UpUFphW126A6fKBGJD5dhRvg8zw1rvoGFxmw==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@rollup/rollup-openharmony-arm64@4.58.0": { + "integrity": "sha512-fd/zpJniln4ICdPkjWFhZYeY/bpnaN9pGa6ko+5WD38I0tTqk9lXMgXZg09MNdhpARngmxiCg0B0XUamNw/5BQ==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@rollup/rollup-win32-arm64-msvc@4.58.0": { + "integrity": "sha512-YpG8dUOip7DCz3nr/JUfPbIUo+2d/dy++5bFzgi4ugOGBIox+qMbbqt/JoORwvI/C9Kn2tz6+Bieoqd5+B1CjA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@rollup/rollup-win32-ia32-msvc@4.58.0": { + "integrity": "sha512-b9DI8jpFQVh4hIXFr0/+N/TzLdpBIoPzjt0Rt4xJbW3mzguV3mduR9cNgiuFcuL/TeORejJhCWiAXe3E/6PxWA==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@rollup/rollup-win32-x64-gnu@4.58.0": { + "integrity": "sha512-CSrVpmoRJFN06LL9xhkitkwUcTZtIotYAF5p6XOR2zW0Zz5mzb3IPpcoPhB02frzMHFNo1reQ9xSF5fFm3hUsQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@rollup/rollup-win32-x64-msvc@4.58.0": { + "integrity": "sha512-QFsBgQNTnh5K0t/sBsjJLq24YVqEIVkGpfN2VHsnN90soZyhaiA9UUHufcctVNL4ypJY0wrwad0wslx2KJQ1/w==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@standard-schema/spec@1.1.0": { + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==" + }, + "@sveltejs/acorn-typescript@1.0.9_acorn@8.16.0": { + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "dependencies": [ + "acorn" + ] + }, + "@sveltejs/adapter-static@3.0.10_@sveltejs+kit@2.53.0__@sveltejs+vite-plugin-svelte@6.2.4___svelte@5.53.1____acorn@8.16.0___vite@7.3.1____@types+node@24.10.13____picomatch@4.0.3___@types+node@24.10.13__svelte@5.53.1___acorn@8.16.0__typescript@5.9.3__vite@7.3.1___@types+node@24.10.13___picomatch@4.0.3__acorn@8.16.0__@types+node@24.10.13_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.1___acorn@8.16.0__vite@7.3.1___@types+node@24.10.13___picomatch@4.0.3__@types+node@24.10.13_svelte@5.53.1__acorn@8.16.0_typescript@5.9.3_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13": { + "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==", + "dependencies": [ + "@sveltejs/kit" + ] + }, + "@sveltejs/kit@2.53.0_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.1___acorn@8.16.0__vite@7.3.1___@types+node@24.10.13___picomatch@4.0.3__@types+node@24.10.13_svelte@5.53.1__acorn@8.16.0_typescript@5.9.3_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_acorn@8.16.0_@types+node@24.10.13": { + "integrity": "sha512-Brh/9h8QEg7rWIj+Nnz/2sC49NUeS8g3Qd9H5dTO3EbWG8vCEUl06jE+r5jQVDMHdr1swmCkwZkONFsWelGTpQ==", + "dependencies": [ + "@standard-schema/spec", + "@sveltejs/acorn-typescript", + "@sveltejs/vite-plugin-svelte", + "@types/cookie", + "acorn", + "cookie", + "devalue", + "esm-env", + "kleur", + "magic-string", + "mrmime", + "set-cookie-parser", + "sirv", + "svelte", + "typescript", + "vite" + ], + "optionalPeers": [ + "typescript" + ], + "bin": true + }, + "@sveltejs/vite-plugin-svelte-inspector@5.0.2_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.1___acorn@8.16.0__vite@7.3.1___@types+node@24.10.13___picomatch@4.0.3__@types+node@24.10.13_svelte@5.53.1__acorn@8.16.0_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13": { + "integrity": "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==", + "dependencies": [ + "@sveltejs/vite-plugin-svelte", + "obug", + "svelte", + "vite" + ] + }, + "@sveltejs/vite-plugin-svelte@6.2.4_svelte@5.53.1__acorn@8.16.0_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13": { + "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==", + "dependencies": [ + "@sveltejs/vite-plugin-svelte-inspector", + "deepmerge", + "magic-string", + "obug", + "svelte", + "vite", + "vitefu" + ] + }, + "@tailwindcss/node@4.2.0": { + "integrity": "sha512-Yv+fn/o2OmL5fh/Ir62VXItdShnUxfpkMA4Y7jdeC8O81WPB8Kf6TT6GSHvnqgSwDzlB5iT7kDpeXxLsUS0T6Q==", + "dependencies": [ + "@jridgewell/remapping", + "enhanced-resolve", + "jiti", + "lightningcss", + "magic-string", + "source-map-js", + "tailwindcss" + ] + }, + "@tailwindcss/oxide-android-arm64@4.2.0": { + "integrity": "sha512-F0QkHAVaW/JNBWl4CEKWdZ9PMb0khw5DCELAOnu+RtjAfx5Zgw+gqCHFvqg3AirU1IAd181fwOtJQ5I8Yx5wtw==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-darwin-arm64@4.2.0": { + "integrity": "sha512-I0QylkXsBsJMZ4nkUNSR04p6+UptjcwhcVo3Zu828ikiEqHjVmQL9RuQ6uT/cVIiKpvtVA25msu/eRV97JeNSA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-darwin-x64@4.2.0": { + "integrity": "sha512-6TmQIn4p09PBrmnkvbYQ0wbZhLtbaksCDx7Y7R3FYYx0yxNA7xg5KP7dowmQ3d2JVdabIHvs3Hx4K3d5uCf8xg==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-freebsd-x64@4.2.0": { + "integrity": "sha512-qBudxDvAa2QwGlq9y7VIzhTvp2mLJ6nD/G8/tI70DCDoneaUeLWBJaPcbfzqRIWraj+o969aDQKvKW9dvkUizw==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-linux-arm-gnueabihf@4.2.0": { + "integrity": "sha512-7XKkitpy5NIjFZNUQPeUyNJNJn1CJeV7rmMR+exHfTuOsg8rxIO9eNV5TSEnqRcaOK77zQpsyUkBWmPy8FgdSg==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@tailwindcss/oxide-linux-arm64-gnu@4.2.0": { + "integrity": "sha512-Mff5a5Q3WoQR01pGU1gr29hHM1N93xYrKkGXfPw/aRtK4bOc331Ho4Tgfsm5WDGvpevqMpdlkCojT3qlCQbCpA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-linux-arm64-musl@4.2.0": { + "integrity": "sha512-XKcSStleEVnbH6W/9DHzZv1YhjE4eSS6zOu2eRtYAIh7aV4o3vIBs+t/B15xlqoxt6ef/0uiqJVB6hkHjWD/0A==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-linux-x64-gnu@4.2.0": { + "integrity": "sha512-/hlXCBqn9K6fi7eAM0RsobHwJYa5V/xzWspVTzxnX+Ft9v6n+30Pz8+RxCn7sQL/vRHHLS30iQPrHQunu6/vJA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-linux-x64-musl@4.2.0": { + "integrity": "sha512-lKUaygq4G7sWkhQbfdRRBkaq4LY39IriqBQ+Gk6l5nKq6Ay2M2ZZb1tlIyRNgZKS8cbErTwuYSor0IIULC0SHw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-wasm32-wasi@4.2.0": { + "integrity": "sha512-xuDjhAsFdUuFP5W9Ze4k/o4AskUtI8bcAGU4puTYprr89QaYFmhYOPfP+d1pH+k9ets6RoE23BXZM1X1jJqoyw==", + "cpu": ["wasm32"] + }, + "@tailwindcss/oxide-win32-arm64-msvc@4.2.0": { + "integrity": "sha512-2UU/15y1sWDEDNJXxEIrfWKC2Yb4YgIW5Xz2fKFqGzFWfoMHWFlfa1EJlGO2Xzjkq/tvSarh9ZTjvbxqWvLLXA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-win32-x64-msvc@4.2.0": { + "integrity": "sha512-CrFadmFoc+z76EV6LPG1jx6XceDsaCG3lFhyLNo/bV9ByPrE+FnBPckXQVP4XRkN76h3Fjt/a+5Er/oA/nCBvQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide@4.2.0": { + "integrity": "sha512-AZqQzADaj742oqn2xjl5JbIOzZB/DGCYF/7bpvhA8KvjUj9HJkag6bBuwZvH1ps6dfgxNHyuJVlzSr2VpMgdTQ==", + "optionalDependencies": [ + "@tailwindcss/oxide-android-arm64", + "@tailwindcss/oxide-darwin-arm64", + "@tailwindcss/oxide-darwin-x64", + "@tailwindcss/oxide-freebsd-x64", + "@tailwindcss/oxide-linux-arm-gnueabihf", + "@tailwindcss/oxide-linux-arm64-gnu", + "@tailwindcss/oxide-linux-arm64-musl", + "@tailwindcss/oxide-linux-x64-gnu", + "@tailwindcss/oxide-linux-x64-musl", + "@tailwindcss/oxide-wasm32-wasi", + "@tailwindcss/oxide-win32-arm64-msvc", + "@tailwindcss/oxide-win32-x64-msvc" + ] + }, + "@tailwindcss/vite@4.2.0_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13": { + "integrity": "sha512-da9mFCaHpoOgtQiWtDGIikTrSpUFBtIZCG3jy/u2BGV+l/X1/pbxzmIUxNt6JWm19N3WtGi4KlJdSH/Si83WOA==", + "dependencies": [ + "@tailwindcss/node", + "@tailwindcss/oxide", + "tailwindcss", + "vite" + ] + }, + "@types/cookie@0.6.0": { + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" + }, + "@types/estree@1.0.8": { + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "@types/json-schema@7.0.15": { + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "@types/node@24.10.13": { + "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", + "dependencies": [ + "undici-types" + ] + }, + "@types/trusted-types@2.0.7": { + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, + "@typescript-eslint/eslint-plugin@8.56.0_@typescript-eslint+parser@8.56.0__eslint@9.39.3__typescript@5.9.3_eslint@9.39.3_typescript@5.9.3": { + "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", + "dependencies": [ + "@eslint-community/regexpp", + "@typescript-eslint/parser", + "@typescript-eslint/scope-manager", + "@typescript-eslint/type-utils", + "@typescript-eslint/utils", + "@typescript-eslint/visitor-keys", + "eslint", + "ignore@7.0.5", + "natural-compare", + "ts-api-utils", + "typescript" + ] + }, + "@typescript-eslint/parser@8.56.0_eslint@9.39.3_typescript@5.9.3": { + "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", + "dependencies": [ + "@typescript-eslint/scope-manager", + "@typescript-eslint/types", + "@typescript-eslint/typescript-estree", + "@typescript-eslint/visitor-keys", + "debug", + "eslint", + "typescript" + ] + }, + "@typescript-eslint/project-service@8.56.0_typescript@5.9.3": { + "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", + "dependencies": [ + "@typescript-eslint/tsconfig-utils", + "@typescript-eslint/types", + "debug", + "typescript" + ] + }, + "@typescript-eslint/scope-manager@8.56.0": { + "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", + "dependencies": [ + "@typescript-eslint/types", + "@typescript-eslint/visitor-keys" + ] + }, + "@typescript-eslint/tsconfig-utils@8.56.0_typescript@5.9.3": { + "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", + "dependencies": [ + "typescript" + ] + }, + "@typescript-eslint/type-utils@8.56.0_eslint@9.39.3_typescript@5.9.3": { + "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", + "dependencies": [ + "@typescript-eslint/types", + "@typescript-eslint/typescript-estree", + "@typescript-eslint/utils", + "debug", + "eslint", + "ts-api-utils", + "typescript" + ] + }, + "@typescript-eslint/types@8.56.0": { + "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==" + }, + "@typescript-eslint/typescript-estree@8.56.0_typescript@5.9.3": { + "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", + "dependencies": [ + "@typescript-eslint/project-service", + "@typescript-eslint/tsconfig-utils", + "@typescript-eslint/types", + "@typescript-eslint/visitor-keys", + "debug", + "minimatch@9.0.5", + "semver", + "tinyglobby", + "ts-api-utils", + "typescript" + ] + }, + "@typescript-eslint/utils@8.56.0_eslint@9.39.3_typescript@5.9.3": { + "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", + "dependencies": [ + "@eslint-community/eslint-utils", + "@typescript-eslint/scope-manager", + "@typescript-eslint/types", + "@typescript-eslint/typescript-estree", + "eslint", + "typescript" + ] + }, + "@typescript-eslint/visitor-keys@8.56.0": { + "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", + "dependencies": [ + "@typescript-eslint/types", + "eslint-visitor-keys@5.0.1" + ] + }, + "acorn-jsx@5.3.2_acorn@8.16.0": { + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dependencies": [ + "acorn" + ] + }, + "acorn@8.16.0": { + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "bin": true + }, + "ajv@6.14.0": { + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dependencies": [ + "fast-deep-equal", + "fast-json-stable-stringify", + "json-schema-traverse", + "uri-js" + ] + }, + "ansi-styles@4.3.0": { + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": [ + "color-convert" + ] + }, + "argparse@2.0.1": { + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "aria-query@5.3.2": { + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==" + }, + "axobject-query@4.1.0": { + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==" + }, + "balanced-match@1.0.2": { + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "brace-expansion@1.1.12": { + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dependencies": [ + "balanced-match", + "concat-map" + ] + }, + "brace-expansion@2.0.2": { + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dependencies": [ + "balanced-match" + ] + }, + "callsites@3.1.0": { + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "chalk@4.1.2": { + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": [ + "ansi-styles", + "supports-color" + ] + }, + "chokidar@4.0.3": { + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dependencies": [ + "readdirp" + ] + }, + "clsx@2.1.1": { + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" + }, + "color-convert@2.0.1": { + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": [ + "color-name" + ] + }, + "color-name@1.1.4": { + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "concat-map@0.0.1": { + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "cookie@0.6.0": { + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + }, + "cross-spawn@7.0.6": { + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": [ + "path-key", + "shebang-command", + "which" + ] + }, + "cssesc@3.0.0": { + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": true + }, + "debug@4.4.3": { + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": [ + "ms" + ] + }, + "deep-is@0.1.4": { + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "deepmerge@4.3.1": { + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "detect-libc@2.1.2": { + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, + "devalue@5.6.3": { + "integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==" + }, + "enhanced-resolve@5.19.0": { + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "dependencies": [ + "graceful-fs", + "tapable" + ] + }, + "esbuild@0.27.3": { + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "optionalDependencies": [ + "@esbuild/aix-ppc64", + "@esbuild/android-arm", + "@esbuild/android-arm64", + "@esbuild/android-x64", + "@esbuild/darwin-arm64", + "@esbuild/darwin-x64", + "@esbuild/freebsd-arm64", + "@esbuild/freebsd-x64", + "@esbuild/linux-arm", + "@esbuild/linux-arm64", + "@esbuild/linux-ia32", + "@esbuild/linux-loong64", + "@esbuild/linux-mips64el", + "@esbuild/linux-ppc64", + "@esbuild/linux-riscv64", + "@esbuild/linux-s390x", + "@esbuild/linux-x64", + "@esbuild/netbsd-arm64", + "@esbuild/netbsd-x64", + "@esbuild/openbsd-arm64", + "@esbuild/openbsd-x64", + "@esbuild/openharmony-arm64", + "@esbuild/sunos-x64", + "@esbuild/win32-arm64", + "@esbuild/win32-ia32", + "@esbuild/win32-x64" + ], + "scripts": true, + "bin": true + }, + "escape-string-regexp@4.0.0": { + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "eslint-config-prettier@10.1.8_eslint@9.39.3": { + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dependencies": [ + "eslint" + ], + "bin": true + }, + "eslint-plugin-svelte@3.15.0_eslint@9.39.3_svelte@5.53.1__acorn@8.16.0_postcss@8.5.6": { + "integrity": "sha512-QKB7zqfuB8aChOfBTComgDptMf2yxiJx7FE04nneCmtQzgTHvY8UJkuh8J2Rz7KB9FFV9aTHX6r7rdYGvG8T9Q==", + "dependencies": [ + "@eslint-community/eslint-utils", + "@jridgewell/sourcemap-codec", + "eslint", + "esutils", + "globals@16.5.0", + "known-css-properties", + "postcss", + "postcss-load-config", + "postcss-safe-parser", + "semver", + "svelte", + "svelte-eslint-parser" + ], + "optionalPeers": [ + "svelte" + ] + }, + "eslint-scope@8.4.0": { + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dependencies": [ + "esrecurse", + "estraverse" + ] + }, + "eslint-visitor-keys@3.4.3": { + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" + }, + "eslint-visitor-keys@4.2.1": { + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==" + }, + "eslint-visitor-keys@5.0.1": { + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==" + }, + "eslint@9.39.3": { + "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", + "dependencies": [ + "@eslint-community/eslint-utils", + "@eslint-community/regexpp", + "@eslint/config-array", + "@eslint/config-helpers", + "@eslint/core@0.17.0", + "@eslint/eslintrc", + "@eslint/js", + "@eslint/plugin-kit", + "@humanfs/node", + "@humanwhocodes/module-importer", + "@humanwhocodes/retry", + "@types/estree", + "ajv", + "chalk", + "cross-spawn", + "debug", + "escape-string-regexp", + "eslint-scope", + "eslint-visitor-keys@4.2.1", + "espree", + "esquery", + "esutils", + "fast-deep-equal", + "file-entry-cache", + "find-up", + "glob-parent", + "ignore@5.3.2", + "imurmurhash", + "is-glob", + "json-stable-stringify-without-jsonify", + "lodash.merge", + "minimatch@3.1.2", + "natural-compare", + "optionator" + ], + "bin": true + }, + "esm-env@1.2.2": { + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==" + }, + "espree@10.4.0_acorn@8.16.0": { + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dependencies": [ + "acorn", + "acorn-jsx", + "eslint-visitor-keys@4.2.1" + ] + }, + "esquery@1.7.0": { + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dependencies": [ + "estraverse" + ] + }, + "esrap@2.2.3": { + "integrity": "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==", + "dependencies": [ + "@jridgewell/sourcemap-codec" + ] + }, + "esrecurse@4.3.0": { + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": [ + "estraverse" + ] + }, + "estraverse@5.3.0": { + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "esutils@2.0.3": { + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "fast-deep-equal@3.1.3": { + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify@2.1.0": { + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein@2.0.6": { + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fdir@6.5.0_picomatch@4.0.3": { + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dependencies": [ + "picomatch" + ], + "optionalPeers": [ + "picomatch" + ] + }, + "file-entry-cache@8.0.0": { + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dependencies": [ + "flat-cache" + ] + }, + "find-up@5.0.0": { + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": [ + "locate-path", + "path-exists" + ] + }, + "flat-cache@4.0.1": { + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dependencies": [ + "flatted", + "keyv" + ] + }, + "flatted@3.3.3": { + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==" + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "os": ["darwin"], + "scripts": true + }, + "glob-parent@6.0.2": { + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": [ + "is-glob" + ] + }, + "globals@14.0.0": { + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==" + }, + "globals@16.5.0": { + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==" + }, + "globals@17.3.0": { + "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==" + }, + "graceful-fs@4.2.11": { + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "has-flag@4.0.0": { + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "ignore@5.3.2": { + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" + }, + "ignore@7.0.5": { + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==" + }, + "import-fresh@3.3.1": { + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dependencies": [ + "parent-module", + "resolve-from" + ] + }, + "imurmurhash@0.1.4": { + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": [ + "is-extglob" + ] + }, + "is-reference@3.0.3": { + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dependencies": [ + "@types/estree" + ] + }, + "isexe@2.0.0": { + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "jiti@2.6.1": { + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "bin": true + }, + "js-yaml@4.1.1": { + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dependencies": [ + "argparse" + ], + "bin": true + }, + "json-buffer@3.0.1": { + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-schema-traverse@0.4.1": { + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify@1.0.1": { + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "keyv@4.5.4": { + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": [ + "json-buffer" + ] + }, + "kleur@4.1.5": { + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + }, + "known-css-properties@0.37.0": { + "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==" + }, + "levn@0.4.1": { + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": [ + "prelude-ls", + "type-check" + ] + }, + "lightningcss-android-arm64@1.31.1": { + "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-arm64@1.31.1": { + "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-x64@1.31.1": { + "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "lightningcss-freebsd-x64@1.31.1": { + "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "lightningcss-linux-arm-gnueabihf@1.31.1": { + "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", + "os": ["linux"], + "cpu": ["arm"] + }, + "lightningcss-linux-arm64-gnu@1.31.1": { + "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-arm64-musl@1.31.1": { + "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-x64-gnu@1.31.1": { + "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-linux-x64-musl@1.31.1": { + "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-win32-arm64-msvc@1.31.1": { + "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "lightningcss-win32-x64-msvc@1.31.1": { + "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", + "os": ["win32"], + "cpu": ["x64"] + }, + "lightningcss@1.31.1": { + "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", + "dependencies": [ + "detect-libc" + ], + "optionalDependencies": [ + "lightningcss-android-arm64", + "lightningcss-darwin-arm64", + "lightningcss-darwin-x64", + "lightningcss-freebsd-x64", + "lightningcss-linux-arm-gnueabihf", + "lightningcss-linux-arm64-gnu", + "lightningcss-linux-arm64-musl", + "lightningcss-linux-x64-gnu", + "lightningcss-linux-x64-musl", + "lightningcss-win32-arm64-msvc", + "lightningcss-win32-x64-msvc" + ] + }, + "lilconfig@2.1.0": { + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" + }, + "locate-character@3.0.0": { + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" + }, + "locate-path@6.0.0": { + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": [ + "p-locate" + ] + }, + "lodash.merge@4.6.2": { + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lucide-svelte@0.575.0_svelte@5.53.1__acorn@8.16.0": { + "integrity": "sha512-Tu15tJfbmRNPaU61yeNFf3jfRHs8ABA+NwTt7TWmwVbhlSA3H7sW65tX6RttcP7HGV4aHUlYhXixZOlntoFBdw==", + "dependencies": [ + "svelte" + ] + }, + "magic-string@0.30.21": { + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dependencies": [ + "@jridgewell/sourcemap-codec" + ] + }, + "minimatch@3.1.2": { + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": [ + "brace-expansion@1.1.12" + ] + }, + "minimatch@9.0.5": { + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": [ + "brace-expansion@2.0.2" + ] + }, + "mri@1.2.0": { + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" + }, + "mrmime@2.0.1": { + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==" + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "nanoid@3.3.11": { + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "bin": true + }, + "natural-compare@1.4.0": { + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "obug@2.1.1": { + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==" + }, + "optionator@0.9.4": { + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dependencies": [ + "deep-is", + "fast-levenshtein", + "levn", + "prelude-ls", + "type-check", + "word-wrap" + ] + }, + "p-limit@3.1.0": { + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": [ + "yocto-queue" + ] + }, + "p-locate@5.0.0": { + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": [ + "p-limit" + ] + }, + "parent-module@1.0.1": { + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": [ + "callsites" + ] + }, + "path-exists@4.0.0": { + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-key@3.1.1": { + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "picocolors@1.1.1": { + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "picomatch@4.0.3": { + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==" + }, + "postcss-load-config@3.1.4_postcss@8.5.6": { + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": [ + "lilconfig", + "postcss", + "yaml" + ], + "optionalPeers": [ + "postcss" + ] + }, + "postcss-safe-parser@7.0.1_postcss@8.5.6": { + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", + "dependencies": [ + "postcss" + ] + }, + "postcss-scss@4.0.9_postcss@8.5.6": { + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dependencies": [ + "postcss" + ] + }, + "postcss-selector-parser@7.1.1": { + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dependencies": [ + "cssesc", + "util-deprecate" + ] + }, + "postcss@8.5.6": { + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dependencies": [ + "nanoid", + "picocolors", + "source-map-js" + ] + }, + "prelude-ls@1.2.1": { + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "prettier-plugin-svelte@3.5.0_prettier@3.8.1_svelte@5.53.1__acorn@8.16.0": { + "integrity": "sha512-2lLO/7EupnjO/95t+XZesXs8Bf3nYLIDfCo270h5QWbj/vjLqmrQ1LiRk9LPggxSDsnVYfehamZNf+rgQYApZg==", + "dependencies": [ + "prettier", + "svelte" + ] + }, + "prettier-plugin-tailwindcss@0.7.2_prettier@3.8.1_prettier-plugin-svelte@3.5.0__prettier@3.8.1__svelte@5.53.1___acorn@8.16.0_svelte@5.53.1__acorn@8.16.0": { + "integrity": "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==", + "dependencies": [ + "prettier", + "prettier-plugin-svelte" + ], + "optionalPeers": [ + "prettier-plugin-svelte" + ] + }, + "prettier@3.8.1": { + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "bin": true + }, + "punycode@2.3.1": { + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, + "readdirp@4.1.2": { + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==" + }, + "resolve-from@4.0.0": { + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "rollup@4.58.0": { + "integrity": "sha512-wbT0mBmWbIvvq8NeEYWWvevvxnOyhKChir47S66WCxw1SXqhw7ssIYejnQEVt7XYQpsj2y8F9PM+Cr3SNEa0gw==", + "dependencies": [ + "@types/estree" + ], + "optionalDependencies": [ + "@rollup/rollup-android-arm-eabi", + "@rollup/rollup-android-arm64", + "@rollup/rollup-darwin-arm64", + "@rollup/rollup-darwin-x64", + "@rollup/rollup-freebsd-arm64", + "@rollup/rollup-freebsd-x64", + "@rollup/rollup-linux-arm-gnueabihf", + "@rollup/rollup-linux-arm-musleabihf", + "@rollup/rollup-linux-arm64-gnu", + "@rollup/rollup-linux-arm64-musl", + "@rollup/rollup-linux-loong64-gnu", + "@rollup/rollup-linux-loong64-musl", + "@rollup/rollup-linux-ppc64-gnu", + "@rollup/rollup-linux-ppc64-musl", + "@rollup/rollup-linux-riscv64-gnu", + "@rollup/rollup-linux-riscv64-musl", + "@rollup/rollup-linux-s390x-gnu", + "@rollup/rollup-linux-x64-gnu", + "@rollup/rollup-linux-x64-musl", + "@rollup/rollup-openbsd-x64", + "@rollup/rollup-openharmony-arm64", + "@rollup/rollup-win32-arm64-msvc", + "@rollup/rollup-win32-ia32-msvc", + "@rollup/rollup-win32-x64-gnu", + "@rollup/rollup-win32-x64-msvc", + "fsevents" + ], + "bin": true + }, + "sade@1.8.1": { + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dependencies": [ + "mri" + ] + }, + "semver@7.7.4": { + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "bin": true + }, + "set-cookie-parser@3.0.1": { + "integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==" + }, + "shebang-command@2.0.0": { + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": [ + "shebang-regex" + ] + }, + "shebang-regex@3.0.0": { + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "sirv@3.0.2": { + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dependencies": [ + "@polka/url", + "mrmime", + "totalist" + ] + }, + "source-map-js@1.2.1": { + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "strip-json-comments@3.1.1": { + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "supports-color@7.2.0": { + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": [ + "has-flag" + ] + }, + "svelte-check@4.4.3_svelte@5.53.1__acorn@8.16.0_typescript@5.9.3": { + "integrity": "sha512-4HtdEv2hOoLCEsSXI+RDELk9okP/4sImWa7X02OjMFFOWeSdFF3NFy3vqpw0z+eH9C88J9vxZfUXz/Uv2A1ANw==", + "dependencies": [ + "@jridgewell/trace-mapping", + "chokidar", + "fdir", + "picocolors", + "sade", + "svelte", + "typescript" + ], + "bin": true + }, + "svelte-eslint-parser@1.4.1_svelte@5.53.1__acorn@8.16.0_postcss@8.5.6": { + "integrity": "sha512-1eqkfQ93goAhjAXxZiu1SaKI9+0/sxp4JIWQwUpsz7ybehRE5L8dNuz7Iry7K22R47p5/+s9EM+38nHV2OlgXA==", + "dependencies": [ + "eslint-scope", + "eslint-visitor-keys@4.2.1", + "espree", + "postcss", + "postcss-scss", + "postcss-selector-parser", + "svelte" + ], + "optionalPeers": [ + "svelte" + ] + }, + "svelte@5.53.1_acorn@8.16.0": { + "integrity": "sha512-WzxFHZhhD23Qzu7JCYdvm1rxvRSzdt9HtHO8TScMBX51bLRFTcJmATVqjqXG+6Ln6hrViGCo9DzwOhAasxwC/w==", + "dependencies": [ + "@jridgewell/remapping", + "@jridgewell/sourcemap-codec", + "@sveltejs/acorn-typescript", + "@types/estree", + "@types/trusted-types", + "acorn", + "aria-query", + "axobject-query", + "clsx", + "devalue", + "esm-env", + "esrap", + "is-reference", + "locate-character", + "magic-string", + "zimmerframe" + ] + }, + "tailwindcss@4.2.0": { + "integrity": "sha512-yYzTZ4++b7fNYxFfpnberEEKu43w44aqDMNM9MHMmcKuCH7lL8jJ4yJ7LGHv7rSwiqM0nkiobF9I6cLlpS2P7Q==" + }, + "tapable@2.3.0": { + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==" + }, + "tinyglobby@0.2.15_picomatch@4.0.3": { + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dependencies": [ + "fdir", + "picomatch" + ] + }, + "totalist@3.0.1": { + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==" + }, + "ts-api-utils@2.4.0_typescript@5.9.3": { + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dependencies": [ + "typescript" + ] + }, + "type-check@0.4.0": { + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": [ + "prelude-ls" + ] + }, + "typescript-eslint@8.56.0_eslint@9.39.3_typescript@5.9.3_@typescript-eslint+parser@8.56.0__eslint@9.39.3__typescript@5.9.3": { + "integrity": "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg==", + "dependencies": [ + "@typescript-eslint/eslint-plugin", + "@typescript-eslint/parser", + "@typescript-eslint/typescript-estree", + "@typescript-eslint/utils", + "eslint", + "typescript" + ] + }, + "typescript@5.9.3": { + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "bin": true + }, + "undici-types@7.16.0": { + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + }, + "uri-js@4.4.1": { + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": [ + "punycode" + ] + }, + "util-deprecate@1.0.2": { + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "uuid@11.1.0": { + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "bin": true + }, + "vite-plugin-devtools-json@1.0.0_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13": { + "integrity": "sha512-MobvwqX76Vqt/O4AbnNMNWoXWGrKUqZbphCUle/J2KXH82yKQiunOeKnz/nqEPosPsoWWPP9FtNuPBSYpiiwkw==", + "dependencies": [ + "uuid", + "vite" + ] + }, + "vite@7.3.1_@types+node@24.10.13_picomatch@4.0.3": { + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dependencies": [ + "@types/node", + "esbuild", + "fdir", + "picomatch", + "postcss", + "rollup", + "tinyglobby" + ], + "optionalDependencies": [ + "fsevents" + ], + "optionalPeers": [ + "@types/node" + ], + "bin": true + }, + "vitefu@1.1.1_vite@7.3.1__@types+node@24.10.13__picomatch@4.0.3_@types+node@24.10.13": { + "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", + "dependencies": [ + "vite" + ], + "optionalPeers": [ + "vite" + ] + }, + "which@2.0.2": { + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": [ + "isexe" + ], + "bin": true + }, + "word-wrap@1.2.5": { + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" + }, + "yaml@1.10.2": { + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yocto-queue@0.1.0": { + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zimmerframe@1.1.4": { + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==" + } + }, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@eslint/compat@^2.0.2", + "npm:@eslint/js@^9.39.2", + "npm:@sveltejs/adapter-static@^3.0.10", + "npm:@sveltejs/kit@^2.50.2", + "npm:@sveltejs/vite-plugin-svelte@^6.2.4", + "npm:@tailwindcss/vite@^4.1.18", + "npm:@types/node@24", + "npm:eslint-config-prettier@^10.1.8", + "npm:eslint-plugin-svelte@^3.14.0", + "npm:eslint@^9.39.2", + "npm:globals@^17.3.0", + "npm:lucide-svelte@0.575", + "npm:prettier-plugin-svelte@^3.4.1", + "npm:prettier-plugin-tailwindcss@~0.7.2", + "npm:prettier@^3.8.1", + "npm:svelte-check@^4.3.6", + "npm:svelte@^5.51.0", + "npm:tailwindcss@^4.1.18", + "npm:typescript-eslint@^8.54.0", + "npm:typescript@^5.9.3", + "npm:vite-plugin-devtools-json@1", + "npm:vite@^7.3.1" + ] + } + } +} diff --git a/devbox.json b/devbox.json new file mode 100644 index 0000000..33715c4 --- /dev/null +++ b/devbox.json @@ -0,0 +1,13 @@ +{ + "packages": ["deno@latest"], + "shell": { + "init_hook": ["echo 'Welcome to router-dash dev environment!'"], + "scripts": { + "dev": "deno task dev", + "build": "deno task build", + "check": "deno task check", + "lint": "deno task lint", + "format": "deno task format" + } + } +} diff --git a/devbox.lock b/devbox.lock new file mode 100644 index 0000000..ac5bd8c --- /dev/null +++ b/devbox.lock @@ -0,0 +1,57 @@ +{ + "lockfile_version": "1", + "packages": { + "deno@latest": { + "last_modified": "2026-02-15T09:18:18Z", + "resolved": "github:NixOS/nixpkgs/e3cb16bccd9facebae3ba29c6a76a4cc1b73462a#deno", + "source": "devbox-search", + "version": "2.6.8", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/g1ppl8rayksihnhnzi97xyhzyzdfc8qc-deno-2.6.8", + "default": true + } + ], + "store_path": "/nix/store/g1ppl8rayksihnhnzi97xyhzyzdfc8qc-deno-2.6.8" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/rj9fgwv2frhqmg0v2g64kmanlfayib03-deno-2.6.8", + "default": true + } + ], + "store_path": "/nix/store/rj9fgwv2frhqmg0v2g64kmanlfayib03-deno-2.6.8" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/hs3cj286jdbn8ahrqa3h8jvc3wj3a0c0-deno-2.6.8", + "default": true + } + ], + "store_path": "/nix/store/hs3cj286jdbn8ahrqa3h8jvc3wj3a0c0-deno-2.6.8" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/61qqy6wi21p9a9a1glwiy80crd0kbr9f-deno-2.6.8", + "default": true + } + ], + "store_path": "/nix/store/61qqy6wi21p9a9a1glwiy80crd0kbr9f-deno-2.6.8" + } + } + }, + "github:NixOS/nixpkgs/nixpkgs-unstable": { + "last_modified": "2026-02-11T21:01:36Z", + "resolved": "github:NixOS/nixpkgs/2343bbb58f99267223bc2aac4fc9ea301a155a16?lastModified=1770843696&narHash=sha256-LovWTGDwXhkfCOmbgLVA10bvsi%2FP8eDDpRudgk68HA8%3D" + } + } +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..e1818bc --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,39 @@ +import prettier from 'eslint-config-prettier' +import path from 'node:path' +import { includeIgnoreFile } from '@eslint/compat' +import js from '@eslint/js' +import svelte from 'eslint-plugin-svelte' +import { defineConfig } from 'eslint/config' +import globals from 'globals' +import ts from 'typescript-eslint' +import svelteConfig from './svelte.config.js' + +const gitignorePath = path.resolve(import.meta.dirname, '.gitignore') + +export default defineConfig( + includeIgnoreFile(gitignorePath), + js.configs.recommended, + ...ts.configs.recommended, + ...svelte.configs.recommended, + prettier, + ...svelte.configs.prettier, + { + languageOptions: { globals: { ...globals.browser, ...globals.node } }, + rules: { + // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. + // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + 'no-undef': 'off', + }, + }, + { + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], + languageOptions: { + parserOptions: { + projectService: true, + extraFileExtensions: ['.svelte'], + parser: ts.parser, + svelteConfig, + }, + }, + }, +) diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..c13665d --- /dev/null +++ b/opencode.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "svelte": { + "type": "local", + "command": ["npx", "-y", "@sveltejs/mcp"] + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a2f9c41 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "router-dash", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "prettier --check . && eslint .", + "format": "prettier --write ." + }, + "dependencies": { + "lucide-svelte": "^0.575.0" + }, + "devDependencies": { + "@eslint/compat": "^2.0.2", + "@eslint/js": "^9.39.2", + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.50.2", + "@sveltejs/vite-plugin-svelte": "^6.2.4", + "@tailwindcss/vite": "^4.1.18", + "@types/node": "^24", + "eslint": "^9.39.2", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-svelte": "^3.14.0", + "globals": "^17.3.0", + "prettier": "^3.8.1", + "prettier-plugin-svelte": "^3.4.1", + "prettier-plugin-tailwindcss": "^0.7.2", + "svelte": "^5.51.0", + "svelte-check": "^4.3.6", + "tailwindcss": "^4.1.18", + "typescript": "^5.9.3", + "typescript-eslint": "^8.54.0", + "vite": "^7.3.1", + "vite-plugin-devtools-json": "^1.0.0" + } +} diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..c0c0816 --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {} diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..adf8bd8 --- /dev/null +++ b/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/src/lib/components/DHCPLeases.svelte b/src/lib/components/DHCPLeases.svelte new file mode 100644 index 0000000..4092948 --- /dev/null +++ b/src/lib/components/DHCPLeases.svelte @@ -0,0 +1,72 @@ + + + +
+
+

DHCP Leases

+

{activeLeases} active of {totalLeases} total

+
+
+ + {#if totalLeases > 0} + + {:else} +

No DHCP leases found

+ {/if} +
diff --git a/src/lib/components/EmptyState.svelte b/src/lib/components/EmptyState.svelte new file mode 100644 index 0000000..3020e34 --- /dev/null +++ b/src/lib/components/EmptyState.svelte @@ -0,0 +1,11 @@ + + +
+

{message}

+
diff --git a/src/lib/components/NetworkInterfaceCard.svelte b/src/lib/components/NetworkInterfaceCard.svelte new file mode 100644 index 0000000..47c072a --- /dev/null +++ b/src/lib/components/NetworkInterfaceCard.svelte @@ -0,0 +1,64 @@ + + + + + +
+ {#if iface.HardwareAddress} + + + + {/if} + + {#if ipv4Addresses.length > 0} +
+ IPv4: + +
+ {/if} + + {#if ipv6Addresses.length > 0} +
+ IPv6: + +
+ {/if} + + + {iface.MTU} + + + {#if iface.OnlineState} + + + + {/if} +
+ + {#if iface.DNS && iface.DNS.length > 0} + + {/if} +
diff --git a/src/lib/components/PageHeader.svelte b/src/lib/components/PageHeader.svelte new file mode 100644 index 0000000..143c77c --- /dev/null +++ b/src/lib/components/PageHeader.svelte @@ -0,0 +1,15 @@ + + +
+
+

{title}

+

{subtitle}

+
+
diff --git a/src/lib/components/PageLayout.svelte b/src/lib/components/PageLayout.svelte new file mode 100644 index 0000000..e87381f --- /dev/null +++ b/src/lib/components/PageLayout.svelte @@ -0,0 +1,13 @@ + + +
+ {@render children()} +
diff --git a/src/lib/components/dhcp/LeaseBadge.svelte b/src/lib/components/dhcp/LeaseBadge.svelte new file mode 100644 index 0000000..d70917b --- /dev/null +++ b/src/lib/components/dhcp/LeaseBadge.svelte @@ -0,0 +1,20 @@ + + +{#if isExpired} + + Expired + +{:else} + + Active ({timeRemaining}) + +{/if} diff --git a/src/lib/components/dhcp/LeaseTable.svelte b/src/lib/components/dhcp/LeaseTable.svelte new file mode 100644 index 0000000..ecf5ccf --- /dev/null +++ b/src/lib/components/dhcp/LeaseTable.svelte @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + {#each leases as lease (formatIP(lease.Address, 2))} + + + {lease.Hostname || 'Unknown'} + + {formatIP(lease.Address, 2)} + + {#if lease.HardwareAddress && lease.HardwareAddressLength > 0} + {formatMAC(lease.HardwareAddress)} + {:else} + - + {/if} + + + + + + + + + {/each} + +
diff --git a/src/lib/components/network/AddressList.svelte b/src/lib/components/network/AddressList.svelte new file mode 100644 index 0000000..7075c48 --- /dev/null +++ b/src/lib/components/network/AddressList.svelte @@ -0,0 +1,17 @@ + + +
+ {#each addresses as addr (formatIP(addr.Address, addr.Family))} + + {/each} +
diff --git a/src/lib/components/network/DNSList.svelte b/src/lib/components/network/DNSList.svelte new file mode 100644 index 0000000..919fe32 --- /dev/null +++ b/src/lib/components/network/DNSList.svelte @@ -0,0 +1,20 @@ + + +
+
DNS Servers:
+
+ {#each dnsServers as dns (formatIP(dns.Address, dns.Family))} + + {/each} +
+
diff --git a/src/lib/components/network/InfoRow.svelte b/src/lib/components/network/InfoRow.svelte new file mode 100644 index 0000000..b2ba3ee --- /dev/null +++ b/src/lib/components/network/InfoRow.svelte @@ -0,0 +1,16 @@ + + +
+ {label}: + {@render children()} +
diff --git a/src/lib/components/network/InterfaceHeader.svelte b/src/lib/components/network/InterfaceHeader.svelte new file mode 100644 index 0000000..5323a8e --- /dev/null +++ b/src/lib/components/network/InterfaceHeader.svelte @@ -0,0 +1,27 @@ + + +
+
+ +
+

{iface.Name}

+ {#if iface.Kind} + {iface.Kind} + {/if} +
+
+
+ +
{iface.CarrierState}
+
+
diff --git a/src/lib/components/network/InterfaceIcon.svelte b/src/lib/components/network/InterfaceIcon.svelte new file mode 100644 index 0000000..e327499 --- /dev/null +++ b/src/lib/components/network/InterfaceIcon.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/network/RoutesTable.svelte b/src/lib/components/network/RoutesTable.svelte new file mode 100644 index 0000000..301311f --- /dev/null +++ b/src/lib/components/network/RoutesTable.svelte @@ -0,0 +1,67 @@ + + + +

Routes

+ + + + + + + + + + + {#each displayRoutes as route (`${route.Family}-${route.Destination.join('.')}-${route.Gateway?.join('.') || 'nogw'}-${route.Priority}-${route.Table}`)} + + {formatRouteDestination(route)} + {formatGateway(route)} + {route.TableString} + {route.Priority} + + {/each} + +
+ {#if routes.length > 10} + + {/if} +
diff --git a/src/lib/components/network/StatusBadge.svelte b/src/lib/components/network/StatusBadge.svelte new file mode 100644 index 0000000..ff0b836 --- /dev/null +++ b/src/lib/components/network/StatusBadge.svelte @@ -0,0 +1,12 @@ + + +{state} diff --git a/src/lib/components/ui/Badge.svelte b/src/lib/components/ui/Badge.svelte new file mode 100644 index 0000000..823f675 --- /dev/null +++ b/src/lib/components/ui/Badge.svelte @@ -0,0 +1,27 @@ + + + + {@render children()} + diff --git a/src/lib/components/ui/Card.svelte b/src/lib/components/ui/Card.svelte new file mode 100644 index 0000000..8c7ae2f --- /dev/null +++ b/src/lib/components/ui/Card.svelte @@ -0,0 +1,14 @@ + + +
+ {@render children()} +
diff --git a/src/lib/components/ui/Code.svelte b/src/lib/components/ui/Code.svelte new file mode 100644 index 0000000..ef2b242 --- /dev/null +++ b/src/lib/components/ui/Code.svelte @@ -0,0 +1,15 @@ + + +{value} diff --git a/src/lib/components/ui/Table.svelte b/src/lib/components/ui/Table.svelte new file mode 100644 index 0000000..e9508d2 --- /dev/null +++ b/src/lib/components/ui/Table.svelte @@ -0,0 +1,16 @@ + + +
+ + {@render children()} +
+
diff --git a/src/lib/components/ui/TableBody.svelte b/src/lib/components/ui/TableBody.svelte new file mode 100644 index 0000000..c4ead29 --- /dev/null +++ b/src/lib/components/ui/TableBody.svelte @@ -0,0 +1,13 @@ + + + + {@render children()} + diff --git a/src/lib/components/ui/TableCell.svelte b/src/lib/components/ui/TableCell.svelte new file mode 100644 index 0000000..494e8b6 --- /dev/null +++ b/src/lib/components/ui/TableCell.svelte @@ -0,0 +1,17 @@ + + + + {@render children()} + diff --git a/src/lib/components/ui/TableHead.svelte b/src/lib/components/ui/TableHead.svelte new file mode 100644 index 0000000..8b4ddfe --- /dev/null +++ b/src/lib/components/ui/TableHead.svelte @@ -0,0 +1,13 @@ + + + + {@render children()} + diff --git a/src/lib/components/ui/TableHeader.svelte b/src/lib/components/ui/TableHeader.svelte new file mode 100644 index 0000000..41c2275 --- /dev/null +++ b/src/lib/components/ui/TableHeader.svelte @@ -0,0 +1,12 @@ + + + + {label} + diff --git a/src/lib/components/ui/TableRow.svelte b/src/lib/components/ui/TableRow.svelte new file mode 100644 index 0000000..f4d9632 --- /dev/null +++ b/src/lib/components/ui/TableRow.svelte @@ -0,0 +1,14 @@ + + + + {@render children()} + diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000..1e9b468 --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,301 @@ +export enum AddressFamily { + IPv4 = 2, + IPv6 = 10, +} + +export type IPAddress = number[] // Array of byte values + +export enum ConfigSource { + Static = 'static', + Foreign = 'foreign', + DHCPv4 = 'DHCPv4', + DHCPv6 = 'DHCPv6', + DHCPPD = 'DHCP-PD', + NDisc = 'NDisc', +} + +export enum ConfigState { + Configured = 'configured', +} + +export enum ScopeString { + Host = 'host', + Link = 'link', + Global = 'global', +} + +export enum ProtocolString { + Kernel = 'kernel', + Boot = 'boot', +} + +export enum TypeString { + Local = 'local', + Broadcast = 'broadcast', + Unicast = 'unicast', + Anycast = 'anycast', + Multicast = 'multicast', + Unreachable = 'unreachable', + Table = 'table', +} + +export enum TableString { + Local = 'local', + Main = 'main', + Default = 'default', +} + +export type TableStringType = TableString | string + +export enum AdministrativeState { + Unmanaged = 'unmanaged', + Configured = 'configured', +} + +export enum OperationalState { + Carrier = 'carrier', + Routable = 'routable', + Enslaved = 'enslaved', + NoCarrier = 'no-carrier', + Degraded = 'degraded', +} + +export enum CarrierState { + Carrier = 'carrier', + Enslaved = 'enslaved', + NoCarrier = 'no-carrier', + DegradedCarrier = 'degraded-carrier', +} + +export enum AddressState { + Off = 'off', + Routable = 'routable', + Degraded = 'degraded', +} + +export enum OnlineState { + Online = 'online', + Offline = 'offline', +} + +export enum ActivationPolicy { + Up = 'up', +} + +export enum DNSEnabled { + Yes = 'yes', + No = 'no', +} + +export enum InterfaceType { + Loopback = 'loopback', + Ether = 'ether', + Bridge = 'bridge', + Vlan = 'vlan', + None = 'none', +} + +export interface DNS { + Family: AddressFamily + Address: IPAddress + ConfigSource: ConfigSource + ConfigProvider?: IPAddress +} + +export interface DNSSetting { + LLMNR?: DNSEnabled + MDNS?: DNSEnabled + ConfigSource: ConfigSource +} + +export interface InterfaceAddress { + Family: AddressFamily + Address: IPAddress + PrefixLength: number + ConfigSource: ConfigSource + ConfigProvider?: IPAddress + Scope?: number + ScopeString?: ScopeString + Flags: number + FlagsString: string | null + Broadcast?: IPAddress + PreferredLifetimeUSec?: number + PreferredLifetimeUsec?: number // Note: both variants exist + ValidLifetimeUSec?: number + ValidLifetimeUsec?: number // Note: both variants exist + ConfigState?: ConfigState +} + +export interface InterfaceRoute { + Family: AddressFamily + Destination: IPAddress + DestinationPrefixLength: number + Gateway?: IPAddress + PreferredSource?: IPAddress + TOS: number + Scope: number + Protocol: number + Type: number + Priority: number + Table: number + Flags: number + NextHopID?: number + ConfigSource: ConfigSource + ConfigProvider?: IPAddress + ScopeString: ScopeString + ProtocolString: ProtocolString | string + TypeString: TypeString | string + TableString: TableStringType + Preference: number + FlagsString: string + ConfigState?: ConfigState + LifetimeUSec?: number +} + +export interface NextHop { + ID: number + Family: AddressFamily + ConfigSource: ConfigSource + ConfigProvider: IPAddress + Gateway: IPAddress + Flags: number + FlagsString: string + Protocol: number + ProtocolString: string + Blackhole: boolean + ConfigState?: ConfigState +} + +export interface DHCPv4Lease { + LeaseTimestampUSec: number + Timeout1USec: number + Timeout2USec: number +} + +export interface DHCPv4Client { + Lease: DHCPv4Lease + ClientIdentifier: number[] +} + +export interface DHCPv6Prefix { + Prefix: IPAddress + PrefixLength: number + PreferredLifetimeUSec: number + ValidLifetimeUSec: number +} + +export interface DHCPv6Lease { + Timeout1USec: number + Timeout2USec: number + LeaseTimestampUSec: number +} + +export interface DHCPv6Client { + Lease: DHCPv6Lease + Prefixes: DHCPv6Prefix[] + DUID: number[] +} + +export interface DHCPLease { + ClientId: number[] + Address: IPAddress + Hostname?: string + HardwareAddressType: number + HardwareAddressLength: number + HardwareAddress: IPAddress + ExpirationUSec: number + ExpirationRealtimeUSec: number +} + +export interface StaticLease { + ClientId: number[] + Address: IPAddress + HardwareAddressType: number + HardwareAddressLength: number + HardwareAddress: IPAddress +} + +export interface DHCPServer { + PoolOffset: number + PoolSize: number + Leases?: DHCPLease[] + StaticLeases?: StaticLease[] +} + +export interface NetworkInterface { + Index: number + Name: string + Kind?: string + Type: InterfaceType + Driver?: string + Flags: number + FlagsString: string + KernelOperationalState: number + KernelOperationalStateString: string + MTU: number + MinimumMTU: number + MaximumMTU: number + HardwareAddress?: IPAddress + PermanentHardwareAddress?: IPAddress + BroadcastAddress?: IPAddress + IPv6LinkLocalAddress?: IPAddress + MasterInterfaceIndex?: number + AdministrativeState: AdministrativeState + OperationalState: OperationalState + CarrierState: CarrierState + AddressState: AddressState + IPv4AddressState: AddressState + IPv6AddressState: AddressState + OnlineState?: OnlineState + NetworkFile?: string + NetworkFileDropins?: string[] + RequiredForOnline?: boolean + RequiredFamilyForOnline?: 'any' + ActivationPolicy?: ActivationPolicy + LinkFile?: string + NetDevFile?: string + NetDevFileDropins?: string[] + Path?: string + Vendor?: string + Model?: string + DNS?: DNS[] + DNSSettings?: DNSSetting[] + Addresses?: InterfaceAddress[] + Routes?: InterfaceRoute[] + NextHops?: NextHop[] + DHCPv4Client?: DHCPv4Client + DHCPv6Client?: DHCPv6Client + DHCPServer?: DHCPServer +} + +export interface RoutingPolicyRule { + Family: AddressFamily + Protocol: number + ProtocolString: ProtocolString | string + TOS: number + Type: number + TypeString: TypeString | string + IPProtocol: number + IPProtocolString: ProtocolString | string + Priority: number + FirewallMark: number + FirewallMask: number + Table?: number + TableString: TableStringType + Invert: boolean + ConfigSource: ConfigSource + ConfigState?: ConfigState +} + +export interface NetworkStatus { + Interfaces: NetworkInterface[] + Routes: InterfaceRoute[] + RoutingPolicyRules: RoutingPolicyRule[] +} + +// DHCP Lease with interface context +export interface LeaseWithInterface extends DHCPLease { + interfaceName: string + isExpired: boolean + timeRemaining: string +} diff --git a/src/lib/utils/network.ts b/src/lib/utils/network.ts new file mode 100644 index 0000000..802141d --- /dev/null +++ b/src/lib/utils/network.ts @@ -0,0 +1,54 @@ +import type { IPAddress, AddressFamily } from '$lib/types' + +export function formatIP(address: IPAddress, family: AddressFamily): string { + if (family === 2) { + // IPv4 + return address.slice(0, 4).join('.') + } else if (family === 10) { + // IPv6 + const hexParts: string[] = [] + for (let i = 0; i < 16; i += 2) { + const hex = ((address[i] << 8) | address[i + 1]).toString(16) + hexParts.push(hex) + } + // Compress zeros + let ipv6 = hexParts.join(':') + // Simple compression of leading zeros in each group + ipv6 = ipv6.replace(/:0+/g, ':') + // Compress longest run of zeros + const zeroRuns = ipv6.match(/:(?::0*)+/g) + if (zeroRuns) { + const longest = zeroRuns.reduce((a, b) => (a.length > b.length ? a : b)) + ipv6 = ipv6.replace(longest, '::') + } + return ipv6 + } + return address.join('.') +} + +export function formatMAC(address: IPAddress): string { + return address + .slice(0, 6) + .map((b) => b.toString(16).padStart(2, '0')) + .join(':') + .toUpperCase() +} + +export function getStatusColor(state: string): string { + switch (state) { + case 'routable': + case 'online': + case 'carrier': + return 'text-green-600' + case 'degraded': + return 'text-yellow-600' + case 'no-carrier': + case 'offline': + case 'off': + return 'text-red-600' + case 'enslaved': + return 'text-blue-600' + default: + return 'text-gray-600' + } +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..e049a06 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,9 @@ + + + +{@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..bba90d8 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,51 @@ + + + + Router Dash + + + + + +
+ {#if data.networkStatus} +
+ {#each displayedInterfaces as iface (iface.Name)} + + {/each} +
+ + {#if allRoutes.length > 0} + + {/if} + +
+ +
+ {:else} + + {/if} +
+
diff --git a/src/routes/+page.ts b/src/routes/+page.ts new file mode 100644 index 0000000..336596b --- /dev/null +++ b/src/routes/+page.ts @@ -0,0 +1,9 @@ +import type { NetworkStatus } from '$lib/types' +import networkData from '$lib/network-status.json' +import type { PageLoad } from './$types' + +export const load: PageLoad = async () => { + return { + networkStatus: networkData as NetworkStatus, + } +} diff --git a/src/routes/layout.css b/src/routes/layout.css new file mode 100644 index 0000000..d4b5078 --- /dev/null +++ b/src/routes/layout.css @@ -0,0 +1 @@ +@import 'tailwindcss'; diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000..6fb78bb --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,6 @@ +import adapter from '@sveltejs/adapter-static' + +/** @type {import('@sveltejs/kit').Config} */ +const config = { kit: { adapter: adapter() } } + +export default config diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c7b9df5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..346a5df --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,6 @@ +import devtoolsJson from 'vite-plugin-devtools-json' +import tailwindcss from '@tailwindcss/vite' +import { sveltekit } from '@sveltejs/kit/vite' +import { defineConfig } from 'vite' + +export default defineConfig({ plugins: [tailwindcss(), sveltekit(), devtoolsJson()] })