feat(*): init

This commit is contained in:
hh committed 2026-10-16 03:41:12 +02:00
commit d7626fd483
184 files changed
+5558

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available Svelte MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.
+123
View File
@@ -0,0 +1,123 @@
# Ultracite Code Standards
This project uses **Ultracite**, a zero-config preset that enforces strict code quality standards through automated formatting and linting.
## Quick Reference
- **Format code**: `bun x ultracite fix`
- **Check for issues**: `bun x ultracite check`
- **Diagnose setup**: `bun x ultracite doctor`
Biome (the underlying engine) provides robust linting and formatting. Most issues are automatically fixable.
---
## Core Principles
Write code that is **accessible, performant, type-safe, and maintainable**. Focus on clarity and explicit intent over brevity.
### Type Safety & Explicitness
- Use explicit types for function parameters and return values when they enhance clarity
- Prefer `unknown` over `any` when the type is genuinely unknown
- Use const assertions (`as const`) for immutable values and literal types
- Leverage TypeScript's type narrowing instead of type assertions
- Use meaningful variable names instead of magic numbers - extract constants with descriptive names
### Modern JavaScript/TypeScript
- Use arrow functions for callbacks and short functions
- Prefer `for...of` loops over `.forEach()` and indexed `for` loops
- Use optional chaining (`?.`) and nullish coalescing (`??`) for safer property access
- Prefer template literals over string concatenation
- Use destructuring for object and array assignments
- Use `const` by default, `let` only when reassignment is needed, never `var`
### Async & Promises
- Always `await` promises in async functions - don't forget to use the return value
- Use `async/await` syntax instead of promise chains for better readability
- Handle errors appropriately in async code with try-catch blocks
- Don't use async functions as Promise executors
### React & JSX
- Use function components over class components
- Call hooks at the top level only, never conditionally
- Specify all dependencies in hook dependency arrays correctly
- Use the `key` prop for elements in iterables (prefer unique IDs over array indices)
- Nest children between opening and closing tags instead of passing as props
- Don't define components inside other components
- Use semantic HTML and ARIA attributes for accessibility:
- Provide meaningful alt text for images
- Use proper heading hierarchy
- Add labels for form inputs
- Include keyboard event handlers alongside mouse events
- Use semantic elements (`<button>`, `<nav>`, etc.) instead of divs with roles
### Error Handling & Debugging
- Remove `console.log`, `debugger`, and `alert` statements from production code
- Throw `Error` objects with descriptive messages, not strings or other values
- Use `try-catch` blocks meaningfully - don't catch errors just to rethrow them
- Prefer early returns over nested conditionals for error cases
### Code Organization
- Keep functions focused and under reasonable cognitive complexity limits
- Extract complex conditions into well-named boolean variables
- Use early returns to reduce nesting
- Prefer simple conditionals over nested ternary operators
- Group related code together and separate concerns
### Security
- Add `rel="noopener"` when using `target="_blank"` on links
- Avoid `dangerouslySetInnerHTML` unless absolutely necessary
- Don't use `eval()` or assign directly to `document.cookie`
- Validate and sanitize user input
### Performance
- Avoid spread syntax in accumulators within loops
- Use top-level regex literals instead of creating them in loops
- Prefer specific imports over namespace imports
- Avoid barrel files (index files that re-export everything)
- Use proper image components (e.g., Next.js `<Image>`) over `<img>` tags
### Framework-Specific Guidance
**Next.js:**
- Use Next.js `<Image>` component for images
- Use `next/head` or App Router metadata API for head elements
- Use Server Components for async data fetching instead of async Client Components
**React 19+:**
- Use ref as a prop instead of `React.forwardRef`
**Solid/Svelte/Vue/Qwik:**
- Use `class` and `for` attributes (not `className` or `htmlFor`)
---
## Testing
- Write assertions inside `it()` or `test()` blocks
- Avoid done callbacks in async tests - use async/await instead
- Don't use `.only` or `.skip` in committed code
- Keep test suites reasonably flat - avoid excessive `describe` nesting
## When Biome Can't Help
Biome's linter will catch most issues automatically. Focus your attention on:
1. **Business logic correctness** - Biome can't validate your algorithms
2. **Meaningful naming** - Use descriptive names for functions, variables, and types
3. **Architecture decisions** - Component structure, data flow, and API design
4. **Edge cases** - Handle boundary conditions and error states
5. **User experience** - Accessibility, performance, and usability considerations
6. **Documentation** - Add comments for complex logic, but prefer self-documenting code
---
Most formatting and common issues are automatically fixed by Biome. Run `bun x ultracite fix` before committing to ensure compliance.
+15
View File
@@ -0,0 +1,15 @@
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"command": "bun run fix --skip=correctness/noUnusedImports",
"type": "command"
}
],
"matcher": "Write|Edit"
}
]
}
}
+5
View File
@@ -0,0 +1,5 @@
_commit: v0.1.4
_src_path: https://git.kotikot.com/templates/caddy.git
project_slug: firefly_ui
routes:
- frontend
+9
View File
@@ -0,0 +1,9 @@
_commit: v0.7.0
_src_path: https://git.kotikot.com/templates/frontend-svelte.git
install_deps: true
layout: root
linter: biome
project_name: Firefly UI
project_slug: firefly_ui
serve: static
ui_library: shadcn
+9
View File
@@ -0,0 +1,9 @@
_commit: v0.6.0
_src_path: https://git.kotikot.com/templates/infra.git
init_git: true
layout: single
mcp_servers:
- context7
- svelte
project_description: Keyboard-first SvelteKit client for Firefly III
project_name: Firefly UI
+7
View File
@@ -0,0 +1,7 @@
node_modules
.svelte-kit
build
# .env goes into the build: $env/static/public (and static/private) are
# inlined at build time from it. It stays in the builder stage, the runtime
# image gets build/ only. Local overrides (.env.local and the like) stay out.
.env.*
+4
View File
@@ -0,0 +1,4 @@
COMPOSE_PROFILES=services
PUBLIC_API_BASE_URL=/api
ALLOWED_HOSTS=
+18
View File
@@ -0,0 +1,18 @@
.env
.env.*
!.env.example
docker-compose.override.yml
.DS_Store
.idea
.venv/
__pycache__/
*.pyc
.ruff_cache/
.pytest_cache/
sessions/*.session*
node_modules/
.svelte-kit/
/build/
# Local playground
/t/
+6
View File
@@ -0,0 +1,6 @@
shared docs/agents
shared CLAUDE.local.md
shared PRODUCT.md
shared DESIGN.md
shared .impeccable
branch .agent
+12
View File
@@ -0,0 +1,12 @@
{
"mcpServers": {
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp"
},
"svelte": {
"type": "http",
"url": "https://mcp.svelte.dev/mcp"
}
}
}
+1
View File
@@ -0,0 +1 @@
engine-strict=true
+13
View File
@@ -0,0 +1,13 @@
repos:
- repo: local
hooks:
- id: fmt
name: fmt
entry: make fmt
language: system
pass_filenames: false
- id: check
name: check
entry: make check
language: system
pass_filenames: false
+16
View File
@@ -0,0 +1,16 @@
## Code Principles
- **Simplicity**: Write simple, straightforward code
- **Readability**: Make code easy to understand
- **Performance**: Consider performance without sacrificing readability
- **Maintainability**: Write code that's easy to update
- **Reusability**: Create reusable components and functions
- **Less Code = Less Debt**: Minimize code footprint
- **NEVER write comments** - code should be self-documenting
- **Docstrings ONLY if explicitly asked**
## Checking commands
After writing code, always run:
```shell
make fmt
make check
```
+34
View File
@@ -0,0 +1,34 @@
FROM oven/bun:1-alpine AS deps
ENV TERM=xterm-256color
ENV COLORTERM=truecolor
ENV BUN_INSTALL_CACHE_DIR=/bun-cache
WORKDIR /app
COPY package.json bun.lock* ./
RUN --mount=type=cache,target=/bun-cache bun install --frozen-lockfile
FROM deps AS builder
COPY . .
RUN bun --bun svelte-kit sync
RUN bun run build
FROM oven/bun:1-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=builder /app/build ./build
COPY --from=builder /app/serve.ts ./serve.ts
EXPOSE 3000
ENTRYPOINT ["bun"]
CMD ["run", "serve.ts"]
+42
View File
@@ -0,0 +1,42 @@
COMPOSE := docker compose
.PHONY: env recreate rebuild restart logs down fmt check build dev
fmt:
bun run fix
check:
bun run check
bun run lint
build:
$(COMPOSE) build frontend
dev:
bun run dev
env:
@fresh=; test -f .env || { cp .env.example .env; fresh=1; }; \
if ! test -f docker-compose.override.yml; then \
cp docker-compose.override.yml.example docker-compose.override.yml; \
for port in $$(sed -n 's/.*127\.0\.0\.1:\([0-9]*\):.*/\1/p' docker-compose.override.yml | sort -u); do \
random=$$((20000 + $$(od -An -N2 -tu2 /dev/urandom) % 40000)); \
sed "s/127\.0\.0\.1:$$port:/127.0.0.1:$$random:/" docker-compose.override.yml > docker-compose.override.yml.tmp && mv docker-compose.override.yml.tmp docker-compose.override.yml; \
test -z "$$fresh" || { sed "s|localhost:$$port/|localhost:$$random/|" .env > .env.tmp && mv .env.tmp .env; }; \
done; \
fi
recreate:
$(COMPOSE) up -d --force-recreate
rebuild: build
$(COMPOSE) up -d
restart:
$(COMPOSE) restart
logs:
$(COMPOSE) logs -f --tail=100
down:
$(COMPOSE) down
+8
View File
@@ -0,0 +1,8 @@
# Firefly UI
```sh
make env # .env and docker-compose.override.yml from the examples
make recreate # the services in COMPOSE_PROFILES
make dev # vite on the host
make check
```
+45
View File
@@ -0,0 +1,45 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"extends": ["ultracite/biome/core", "ultracite/biome/svelte"],
"files": {
"includes": [
"!.claude",
"!.svelte-kit",
"!build",
"!.mcp.json",
"!.impeccable",
"!src/lib/components/ui",
"!src/routes/claddcn.css"
]
},
"javascript": { "globals": ["Bun"] },
"overrides": [
{
"includes": ["src/lib/components/ui/**"],
"linter": { "enabled": false },
"assist": { "enabled": false }
},
{
"includes": ["src/lib/utils.ts"],
"linter": { "rules": { "suspicious": { "noExplicitAny": "off" } } }
},
{
"includes": ["**/*.svelte"],
"linter": {
"rules": {
"a11y": { "useAnchorContent": "off" },
"style": {
"useFilenamingConvention": {
"level": "error",
"options": { "filenameCases": ["kebab-case", "PascalCase"] }
}
}
}
}
},
{
"includes": ["src/app.d.ts"],
"linter": { "rules": { "style": { "noNamespace": "off" } } }
}
]
}
+520
View File
@@ -0,0 +1,520 @@
{
"lockfileVersion": 2,
"configVersion": 1,
"workspaces": {
"": {
"name": "firefly_ui",
"devDependencies": {
"@biomejs/biome": "^2.5.15",
"@fontsource-variable/geist": "^5.3.0",
"@fontsource-variable/geist-mono": "^5.3.0",
"@internationalized/date": "^3.12.0",
"@lucide/svelte": "^1.23.0",
"@sveltejs/adapter-static": "^4.0.0",
"@sveltejs/kit": "^3.0.1",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@tailwindcss/vite": "^4.3.0",
"@types/bun": "^1.3.0",
"bits-ui": "^2.16.3",
"clsx": "^2.1.1",
"mode-watcher": "^1.1.0",
"shadcn-svelte": "^1.3.0",
"svelte": "^5.57.1",
"svelte-check": "^4.7.6",
"svelte-sonner": "^1.1.0",
"tailwind-merge": "^3.5.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.3.0",
"tw-animate-css": "^1.4.0",
"typescript": "^6.0.3",
"ultracite": "^7.12.4",
"vite": "^8.0.16",
},
},
},
"packages": {
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
"@babel/parser": ["@babel/parser@7.29.9", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA=="],
"@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="],
"@biomejs/biome": ["@biomejs/biome@2.5.15", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.15", "@biomejs/cli-darwin-x64": "2.5.15", "@biomejs/cli-linux-arm64": "2.5.15", "@biomejs/cli-linux-arm64-musl": "2.5.15", "@biomejs/cli-linux-x64": "2.5.15", "@biomejs/cli-linux-x64-musl": "2.5.15", "@biomejs/cli-win32-arm64": "2.5.15", "@biomejs/cli-win32-x64": "2.5.15" }, "bin": { "biome": "bin/biome" } }, "sha512-WZTW4slm/pdkh92K6t/3aEN++44JD1PQ7squ7RCMLI1flHGl43DVOIGXvCjHiZgBMO1V4uYxoFNtqrIA4qGuIQ=="],
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BZVzFhJ/mUvTLMYbc9x6el0o2Uv5zP7bACYyhFb6fSc1giroXm7PL0a5KqMJ9F+/BnkYRjagD/ROSDVOZ6eapg=="],
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.5.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-V5Kw63V+fVGNFhFrizDxMbGXAzZCh8kYzJpy3GI6gTapWDg7bZK9oYC9CbVgQSEdTnupz0U5Efz8Ev+5vdBBXw=="],
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.5.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-XaG7P7eeSLYETD3K9grfB0mQmpLQZjygdbsxekWBJAg4am79fGAtRpfRyNnTvXB2fZFNnJZG3nXm7HBat0+VUw=="],
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.5.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-tGzZUTcJCV7tj5Adh/Gn6nR4MycqA3iohq2LEcrarAgRBkU0h0OKd2UzCRkOsOYOuUGaUJb4YmqRqyy2feGGSw=="],
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.5.15", "", { "os": "linux", "cpu": "x64" }, "sha512-xE4iEW/3LqlYj9GFgGrFsFSH51dEEpUbYWBfeOv0q87WkUDxK2o/HhreSb7qMJqck70RVM6Lg96hgedcAYcOkA=="],
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.5.15", "", { "os": "linux", "cpu": "x64" }, "sha512-IlxUcyxilVGPsE008x13pWdkTbU3nQpP2i9b5UrbOYj6goBkKsRX1XB7yJLcV+O1H9LBchIIm4adOaVLZBX0ZQ=="],
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.5.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-2kPKzhNlm8C+Ru3GcO0Me2ODkCBLrVOUNuyi84RJyWDVKAA4+Kjj3jMjL938lF6BRfvltC4vB2nPY9SvN51Ovg=="],
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.5.15", "", { "os": "win32", "cpu": "x64" }, "sha512-yAzh4UqEImV6Hcy0zjUqNfJAsUhoD64FBQCdTG8Md/Z2wQeVP/ZmJ3XRYAs1g8J9fjTFyl2fCtc5FB5til47+g=="],
"@clack/core": ["@clack/core@1.5.1", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ=="],
"@clack/prompts": ["@clack/prompts@1.8.1", "", { "dependencies": { "@clack/core": "1.5.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ=="],
"@floating-ui/core": ["@floating-ui/core@1.8.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.12" } }, "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ=="],
"@floating-ui/dom": ["@floating-ui/dom@1.8.0", "", { "dependencies": { "@floating-ui/core": "^1.8.0", "@floating-ui/utils": "^0.2.12" } }, "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg=="],
"@floating-ui/utils": ["@floating-ui/utils@0.2.12", "", {}, "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww=="],
"@fontsource-variable/geist": ["@fontsource-variable/geist@5.3.0", "", {}, "sha512-j0m+vLQuG5XAYoHtGCVu0spvlGreR3EzpECUVzkFmI1mTVnAO38l/NEPDCFgZ177JxzYJCLSmTQibIiYPilGrA=="],
"@fontsource-variable/geist-mono": ["@fontsource-variable/geist-mono@5.3.0", "", {}, "sha512-vBbuwDEo9AkrqADMXOrlAR3DFcJi4/JxeuU43FoiQERnNwsfXNnvxvReZG02cQKmyk4DZkZdBZX3oTDvy2zBAw=="],
"@internationalized/date": ["@internationalized/date@3.12.4", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-M1dEn4c1U1HsSlaVR8upZtSqvXrTkHDfv18H01uCSJyjVLDxnBR38v/fMxecmlwXKR4i9HeZcmgQAPE6A+aGJQ=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@lucide/svelte": ["@lucide/svelte@1.57.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-KWO10Jo80+Rd/JGBQkVcwkF1czJH+CwjHtfjZzLfRsmcI42YuMai3SCiRtdo5+3yo7fhT3oHKHaSBvN8GHEVpQ=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@oxc-project/types": ["@oxc-project/types@0.154.0", "", {}, "sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA=="],
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
"@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.14", "", { "os": "android", "cpu": "arm" }, "sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.14", "", { "os": "android", "cpu": "arm64" }, "sha512-QiVxHofl6A8lQU7WAtOUY4QMVZXohxwz7WJJoeh8NBEUyKfIkMRKhYyq8az/1RypNiIXzNTtiS6KwHz0erxYRQ=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lTQOzHuBIooh3jIr5XINQkjPGEuGy+Dsy0meXDr6Sjty0b99kYgGh4uBC/8mJX1T0kC8d6S0aPP1Y+bHkh6aBw=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-hyMKEGatjmBYCs/Q4sbIcL/GqpFEqsX3wc+HYVQ/85A4rxc1o7rPm4DUI4GV4k/h0ITddwFSGpqQunI14GXjTA=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-u371GbXoVXrEgP20PwzoGNOdAOqAMhzo19JzzY+V4YmmnC+DTOoqdbMu+O+54emM59Mik/UdrtWgcuEDKWRPKA=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.14", "", { "os": "linux", "cpu": "arm" }, "sha512-BqwKsdZQf6EqVWr8fv2MrDYzAvOZ7+NYFSJESRGs87fkseEhs0TU9TXgRJTkZoRRkxTizyEg1LKUXfU3/78uqg=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-SD9kY8M009x/7s57Z1dgUVlP5+uYBgKQK0Tkr1WIWngrtUJmRCW1RJFMY7QDla2khXhmg2i5Io7MUjcmZNZ/KQ=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-BJ2CL19M6uuWesMgnqJzgxoI+N+eV7b0A9O9vLV42y+gitC41fNa8KElau1tHVzuLWX5GzEuxMPhAO0tQXQMkg=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.14", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4+Kl8daPV8NDHaD7mfCcc1pRM+Qg7ab8DyMm5FG5WoZgv4h2i3KFN1CybYBNbQqmuYm7ftJLvmMI1eHYJ22S/Q=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.14", "", { "os": "linux", "cpu": "s390x" }, "sha512-V/h8ePq1fKDrmtFMz6xcCN3DKvuvMRrwwD1q0PkBeeo4DpK64biaKvqWGVxqCeYKqeRk7gE7/iKPxLCn08O97A=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.14", "", { "os": "linux", "cpu": "x64" }, "sha512-WxalXzgrLVpKC93xfpaJ3gCXUWzeCr+FRCbDVD2mUAy4L6Vo3lNVz0epUEUpxPWJkLNYUwM4OSIikv36OJ9mLg=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.14", "", { "os": "linux", "cpu": "x64" }, "sha512-3G3tnAmTpdk9jO3GQtyHXqeFD2SlecfTws/Y3M2mmj9nMBwwuLT4kur8sZKeu7ENUHSpVO3cIsm5A52LEWWwlg=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.14", "", { "os": "none", "cpu": "arm64" }, "sha512-qyr5XiNGxm2ytx17vvtcJ9nXn6v1gENETi503bJx3OGwZ1tSonj7BosxbMa/xeAfSl5iS9X6KjEWYXLu6v1L7Q=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-oA4o5o0S2yB199YyYXDzKUv1Z56Rc+mgOjwm7Z/vJ9Z8igiDPYvoOuDyo6g7AK/ZaF09nPhDKVnGBcYHO11GXA=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.14", "", { "os": "win32", "cpu": "x64" }, "sha512-9mE94bf2GZUaR/fUSXCMpBoWkctu421Mi0ORpm4zqfbkjKep7RxzPFa5etcGs5h+2KApoWBOA/hAJa/7saGgEQ=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.13", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ=="],
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@4.0.0", "", { "peerDependencies": { "@sveltejs/kit": "^3.0.0-next.0" } }, "sha512-aF1J23R1ebKqUxQ4/qLr/Lxu63NFmLAtg8UZl3HFLxaoA1D/nhTVGcD9lTqx65iUbjNR2iOL0TgOxSX82x6Paw=="],
"@sveltejs/kit": ["@sveltejs/kit@3.1.0", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1", "@standard-schema/spec": "^1.1.0", "cookie": "^2.0.1", "devalue": "^5.9.4", "esm-env": "^1.2.2", "magic-string": "^1.2.3", "mrmime": "^2.0.1", "sirv": "^3.0.2" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^7.0.0", "svelte": "^5.57.1", "typescript": "^6.0.0", "vite": "^8.0.12" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-JTWlq2HQlkqx6vIoxhC0R6sc4WVqbQoRyEmr5YlqKmiYbzY1U3awtSWTmjMLlS056a49AmkQR6rnNANXIE8B0Q=="],
"@sveltejs/load-config": ["@sveltejs/load-config@0.2.3", "", {}, "sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@7.3.1", "", { "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^1.0.0", "obug": "^2.1.0", "vitefu": "^1.1.2" }, "peerDependencies": { "svelte": "^5.46.4", "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "sha512-ZPsLN8B1e/En+Ak5s4V7srFDT532oS0qieLsQwu63NGKsS+iAjoO2Js1BochlHlglcU+Pt7WAO3C5Ee+4f6gVA=="],
"@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="],
"@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="],
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.3", "@tailwindcss/oxide-darwin-arm64": "4.3.3", "@tailwindcss/oxide-darwin-x64": "4.3.3", "@tailwindcss/oxide-freebsd-x64": "4.3.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", "@tailwindcss/oxide-linux-x64-musl": "4.3.3", "@tailwindcss/oxide-wasm32-wasi": "4.3.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.3", "", { "os": "android", "cpu": "arm64" }, "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw=="],
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw=="],
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ=="],
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA=="],
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img=="],
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.3", "", { "dependencies": { "@emnapi/core": "^1.11.1", "@emnapi/runtime": "^1.11.1", "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw=="],
"@tailwindcss/vite": ["@tailwindcss/vite@4.3.3", "", { "dependencies": { "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "tailwindcss": "4.3.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw=="],
"@types/bun": ["@types/bun@1.4.3", "", { "dependencies": { "bun-types": "1.4.3" } }, "sha512-N67Pqy8JXbAY/j0tNraS3V5mubGvc6rrrNDmDCtZLlFzccbUJPDb0tvgUBC5q4m8ybkP93Jcn1QB+RYQ6PvBhw=="],
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
"@types/node": ["@types/node@26.6.5", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg=="],
"acorn": ["acorn@8.19.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw=="],
"ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="],
"ansi-regex": ["ansi-regex@6.4.0", "", {}, "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw=="],
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"bits-ui": ["bits-ui@2.19.5", "", { "dependencies": { "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "esm-env": "^1.1.2", "runed": "^0.35.1", "svelte-toolbelt": "^0.10.6", "tabbable": "^6.2.0" }, "peerDependencies": { "@internationalized/date": "^3.8.1", "svelte": "^5.33.0" } }, "sha512-TEwkLSYElE5SE0fIdcItiFPfyg9vrR67N1Mt6VvZCqZo24dwTA1MpGVV3WeyIg7rfVAaZ0So8a0/dcnPngCoew=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"bun-types": ["bun-types@1.4.3", "", { "dependencies": { "@types/node": "*", "undici-types": "*" } }, "sha512-tinRJNGOmUN+tEAsn2y2e942n9JHRHR42iUYzDH75NmhJqVBokGSQkkfSFdNWwsANIdrK6ACH9t01M452YyTwg=="],
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"citty": ["citty@0.2.2", "", {}, "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w=="],
"cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
"cli-truncate": ["cli-truncate@6.1.1", "", { "dependencies": { "slice-ansi": "^9.0.0", "string-width": "^8.2.0" } }, "sha512-06p9vyLahLa4zkGcgsGxU6iEkSOiuI4fhCH6Emhe2lPAcoUv73n72DnODsnHA+5wwXGnV0n9M9/qOQJSjYhFhw=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"cn": ["cn@0.3.3", "", { "bin": { "cn": "bin/cn.mjs" } }, "sha512-OEiyJkSpYm2kOuNOKQ+c2wmzZRG8orrb9fnObtHTbCGNx/E9KLnGcmcocItOzREKk5KzQN5I2e3BUCj563KEpw=="],
"commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
"confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
"cookie": ["cookie@2.0.1", "", {}, "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w=="],
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="],
"empathic": ["empathic@2.1.0", "", {}, "sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw=="],
"enhanced-resolve": ["enhanced-resolve@5.26.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw=="],
"environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
"esrap": ["esrap@2.4.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-CQPcneEHw/czj32uwQC52nYnax8H4SXt25FCM+LIwGZ49cMIxpOj3bP8wmzcI+Z0HIy5KkClaPhRLfCySzwHPA=="],
"execa": ["execa@10.1.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "figures": "^6.1.0", "get-stream": "^9.0.1", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.3.1", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "which-command": "^0.1.0", "yoctocolors": "^2.2.0" } }, "sha512-yJAC9ulsE6fU0K6wzJBIH+wI+KAtMeu6Ck8rs23OVzvEhqXjGADdI6K+YNIDAjcQQ6AwXTD96cKExJo6xFJXyg=="],
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="],
"fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="],
"fast-wrap-ansi": ["fast-wrap-ansi@0.2.2", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q=="],
"fastq": ["fastq@1.20.3", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw=="],
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"find-workspaces": ["find-workspaces@0.3.1", "", { "dependencies": { "fast-glob": "^3.3.2", "pkg-types": "^1.0.3", "yaml": "^2.3.4" } }, "sha512-UDkGILGJSA1LN5Aa7McxCid4sqW3/e+UYsVwyxki3dDT0F8+ym0rAfnCkEfkL0rO7M+8/mvkim4t/s3IPHmg+w=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"get-east-asian-width": ["get-east-asian-width@1.7.0", "", {}, "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q=="],
"get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
"is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
"is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
"jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
"lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="],
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="],
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="],
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
"log-update": ["log-update@8.0.0", "", { "dependencies": { "ansi-escapes": "^7.3.0", "cli-cursor": "^5.0.0", "slice-ansi": "^9.0.0", "string-width": "^8.2.0", "strip-ansi": "^7.2.0", "wrap-ansi": "^10.0.0" } }, "sha512-lddSgOt3bPASrylL54ZSpy8nBHns+vBVSoILlVOx+dei300pnLRN958rj/EdlVLKuWlSESU3qdnDZdAI7FXYGg=="],
"lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="],
"magic-string": ["magic-string@1.4.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ=="],
"magicast": ["magicast@0.5.5", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA=="],
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
"mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
"mode-watcher": ["mode-watcher@1.1.0", "", { "dependencies": { "runed": "^0.25.0", "svelte-toolbelt": "^0.7.1" }, "peerDependencies": { "svelte": "^5.27.0" } }, "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g=="],
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
"nanoid": ["nanoid@3.3.20", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ=="],
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
"npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
"nypm": ["nypm@0.6.10", "", { "dependencies": { "citty": "^0.2.2", "pathe": "^2.0.3", "tinyexec": "^1.3.1" }, "bin": { "nypm": "./dist/cli.mjs" } }, "sha512-W72Hrj1petq+b3Hk2aAC+9zswetlIFTnDW4s5djseZh2nYqBbyQLOtj472HwcbcWykPBUW1WpWpjOd4nK6gMRw=="],
"obug": ["obug@2.2.1", "", {}, "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q=="],
"onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
"parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
"path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="],
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
"postcss": ["postcss@8.5.30", "", { "dependencies": { "nanoid": "^3.3.20", "picocolors": "^1.1.1", "source-map-js": "^1.2.2" } }, "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA=="],
"pretty-ms": ["pretty-ms@9.3.1", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"resolve.exports": ["resolve.exports@2.0.4", "", {}, "sha512-zepuGL0ulf+nt+XaFrlKeaiTJeyPFPLLY62+ncq1/VTHBdt3HsfXOH60priZ6tnXQozdGcDeIF5JVTanvIcYWQ=="],
"restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
"rolldown": ["rolldown@1.2.14", "", { "dependencies": { "@oxc-project/types": "=0.154.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.14", "@rolldown/binding-android-arm64": "1.2.14", "@rolldown/binding-darwin-arm64": "1.2.14", "@rolldown/binding-darwin-x64": "1.2.14", "@rolldown/binding-freebsd-x64": "1.2.14", "@rolldown/binding-linux-arm-gnueabihf": "1.2.14", "@rolldown/binding-linux-arm64-gnu": "1.2.14", "@rolldown/binding-linux-arm64-musl": "1.2.14", "@rolldown/binding-linux-ppc64-gnu": "1.2.14", "@rolldown/binding-linux-s390x-gnu": "1.2.14", "@rolldown/binding-linux-x64-gnu": "1.2.14", "@rolldown/binding-linux-x64-musl": "1.2.14", "@rolldown/binding-openharmony-arm64": "1.2.14", "@rolldown/binding-win32-arm64-msvc": "1.2.14", "@rolldown/binding-win32-x64-msvc": "1.2.14" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-eTBTTqMR6uHKLgcUqpJP8ntzDI6V4LYA8negAMBEI7gkgVvOdrx6VsvS2Ym+83KhOPdLCHWJu0w7UQFJZhHKNw=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"runed": ["runed@0.35.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0" }, "optionalPeers": ["@sveltejs/kit"] }, "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q=="],
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
"semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
"shadcn-svelte": ["shadcn-svelte@1.7.0", "", { "dependencies": { "cn": "^0.3.0", "commander": "^14.0.0", "node-fetch-native": "^1.6.4", "postcss": "^8.5.10" }, "peerDependencies": { "svelte": "^5.0.0" }, "bin": { "shadcn-svelte": "dist/index.mjs" } }, "sha512-AK3ZsYOL8PN/RxcEBCHGvQj2TrLfaaBsbw0QohyEb3nn/yPNDRC/+9iSjnTp/uKEe+tAi0tBxB/Yhzno0N7EEg=="],
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="],
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
"slice-ansi": ["slice-ansi@9.0.1", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-aBY19bn/XA+hKOKX0qL7Z0UfdBsbQvc9hn993U8ALUjzxCvDcuZqLoRXjGJrUARWAlwMnsRVD9sw2AFNOYvalA=="],
"source-map-js": ["source-map-js@1.2.2", "", {}, "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw=="],
"string-width": ["string-width@8.3.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ=="],
"strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
"strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
"style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="],
"svelte": ["svelte@5.57.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "@jridgewell/sourcemap-codec": "^1.6.0", "@sveltejs/acorn-typescript": "^1.0.13", "@types/estree": "^1.0.9", "acorn": "^8.18.0", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.9.2", "esm-env": "^1.2.1", "esrap": "^2.3.6", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-1YiPCAXT0Ct6g1vOMmSLa0Nsc3QRMvtDF32ihgglGVrugIuIK38RyDiFNXVDhtQ81TJjSO57NdMw6DyYLqSGDQ=="],
"svelte-check": ["svelte-check@4.7.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "@sveltejs/load-config": "^0.2.3", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.0.0 || ^6.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-t2scM//ZuVbSY/T2w6FSBw1v9s2NEmh/g+sy1lqtosW5ylBV5AF4wFb1Ts9Kf3MbfPDUDJDZ9L436YT0SPTdvw=="],
"svelte-sonner": ["svelte-sonner@1.2.1", "", { "dependencies": { "runed": "^0.28.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-N00LOWb60fjKXULbpngku+7E9Krb16KpNq4/Eax5Vu7o1Dpbca55QpaLPsB+yp82JIEdFhfJuv5cVXLoe0ZtVA=="],
"svelte-toolbelt": ["svelte-toolbelt@0.10.6", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.35.1", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.30.2" } }, "sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ=="],
"tabbable": ["tabbable@6.5.0", "", {}, "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA=="],
"tailwind-merge": ["tailwind-merge@3.7.0", "", {}, "sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA=="],
"tailwind-variants": ["tailwind-variants@3.3.1", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge", "tailwindcss"] }, "sha512-4pAvwUtM4HKBiRZftncAbpn6V9Hhwoa5Fl7O2u5zbp7Z5Cvu+/o/6+176WY3WCEES209543quG8zFIcXCsc5Jw=="],
"tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="],
"tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
"tinyexec": ["tinyexec@1.3.1", "", {}, "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA=="],
"tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
"typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
"ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="],
"ultracite": ["ultracite@7.12.4", "", { "dependencies": { "@clack/prompts": "^1.8.1", "cli-truncate": "^6.1.1", "commander": "^15.0.0", "deepmerge": "^4.3.1", "empathic": "^2.1.0", "execa": "^10.0.1", "fast-glob": "^3.3.3", "find-workspaces": "^0.3.1", "jsonc-parser": "^3.3.1", "log-update": "^8.0.0", "magicast": "^0.5.5", "npm-run-path": "^6.0.0", "nypm": "^0.6.10", "resolve.exports": "^2.0.3", "semver": "^7.8.5", "string-width": "^8.3.0", "which-command": "^0.1.0", "yaml": "^2.9.1", "zod": "^4.6.5" }, "peerDependencies": { "@biomejs/biome": "^2.5.0", "eslint": "^10.9.0", "oxfmt": ">=0.59.0", "oxlint": "^1.86.0", "prettier": "^3.0.0", "stylelint": "^17.0.0" }, "optionalPeers": ["@biomejs/biome", "eslint", "oxfmt", "oxlint", "prettier", "stylelint"], "bin": { "ultracite": "dist/index.js" } }, "sha512-6jVEwaZBaytFm6XRoI3WGS2ZydrkrNvHTD0qTf7983ti2FlEWL1YcxnPaMaYuS6PkXqMhIK2XGr0cbsL4x5pQA=="],
"undici-types": ["undici-types@8.12.2", "", {}, "sha512-VoMeD2O0KdHhcn+oPqOrpivN+7aDOklVSwOLD7JNjbzV1CboAFek9IFOXnreoufTUtAsCsOK0iETiERPNR/fDA=="],
"unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
"vite": ["vite@8.3.4", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.29", "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ=="],
"vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="],
"which-command": ["which-command@0.1.0", "", { "bin": { "which-command": "cli.js" } }, "sha512-XZyoF5/5hZtXitIwzrU4NKK+Wtbb9aB9CezUEw2Q0wlYK8NUYQxC1rRXgNueYLtBAJwXIb+/tFVk4dozciNJMA=="],
"wrap-ansi": ["wrap-ansi@10.0.2", "", { "dependencies": { "ansi-styles": "^6.2.3", "string-width": "^8.2.0" } }, "sha512-6OLRNZqRntVGBm26ghZ5eZhCuWWyOkRCSn8XsOIuq5stpCE/tjLbdozd93naILZig0lmD8ANfRwnhn0DLeXGlg=="],
"yaml": ["yaml@2.9.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw=="],
"yoctocolors": ["yoctocolors@2.2.0", "", {}, "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg=="],
"zimmerframe": ["zimmerframe@1.1.5", "", {}, "sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA=="],
"zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="],
"@tailwindcss/node/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
"@tailwindcss/node/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.5", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" }, "bundled": true }, "sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="],
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@types/node/undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="],
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="],
"mode-watcher/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="],
"svelte/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"svelte-sonner/runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="],
"ultracite/commander": ["commander@15.0.0", "", {}, "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg=="],
"@tailwindcss/node/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
"@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
"@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
"@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
"@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
"@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
"@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
"mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="],
}
}
+2
View File
@@ -0,0 +1,2 @@
FIREFLY_UI_DOMAIN=firefly-ui.localhost
CLOUDFLARE_API_TOKEN=
+2
View File
@@ -0,0 +1,2 @@
.env
/Caddyfile
+14
View File
@@ -0,0 +1,14 @@
{
admin localhost:2019
log {
format console
}
servers {
trusted_proxies cloudflare
client_ip_headers Cf-Connecting-Ip
}
}
import /etc/caddy/projects.d/*/site.caddy
+3
View File
@@ -0,0 +1,3 @@
# caddy
`site.caddy` routes `{$FIREFLY_UI_DOMAIN}` to the project over the external `caddy` network. In a shared Caddy, mount this directory as `/etc/caddy/projects.d/firefly-ui:ro` and set `FIREFLY_UI_DOMAIN`. Keep it portable: domain only from the env, no `tls`, snippets prefixed with `firefly_ui_`.
+23
View File
@@ -0,0 +1,23 @@
services:
caddy:
image: ghcr.io/caddybuilds/caddy-cloudflare:latest
restart: unless-stopped
ports:
- "0.0.0.0:80:80"
- "0.0.0.0:443:443"
- "0.0.0.0:443:443/udp"
networks:
- caddy
env_file:
- .env
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- .:/etc/caddy/projects.d/firefly-ui:ro
- caddy_data:/data
networks:
caddy:
external: true
volumes:
caddy_data:
+5
View File
@@ -0,0 +1,5 @@
{$FIREFLY_UI_DOMAIN} {
encode zstd gzip
reverse_proxy firefly-ui-frontend:3000
}
+20
View File
@@ -0,0 +1,20 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"tailwind": {
"css": "src/routes/layout.css",
"baseColor": "neutral"
},
"aliases": {
"components": "#lib/components",
"utils": "#lib/utils",
"ui": "#lib/components/ui",
"hooks": "#lib/hooks",
"lib": "#lib"
},
"typescript": true,
"registry": "https://shadcn-svelte.com/registry",
"style": "luma",
"iconLibrary": "lucide",
"menuColor": "default-translucent",
"menuAccent": "subtle"
}
+22
View File
@@ -0,0 +1,22 @@
services:
frontend:
networks:
default: {}
caddy:
aliases:
- firefly-ui-frontend
# ports:
# - "127.0.0.1:3000:3000"
frontend-dev:
networks:
default: {}
caddy:
aliases:
- firefly-ui-frontend
# ports:
# - "127.0.0.1:3000:3000"
networks:
caddy:
external: true
+30
View File
@@ -0,0 +1,30 @@
services:
frontend:
build: .
image: firefly-ui/frontend
profiles: [frontend, services]
restart: unless-stopped
env_file:
- path: .env
required: false
command: [run, serve.ts]
frontend-dev:
image: oven/bun:alpine
profiles: [frontend-dev]
restart: unless-stopped
working_dir: /app
env_file:
- path: .env
required: false
environment:
API_PROXY_TARGET: http://api:8080
volumes:
- .:/app
- frontend_node_modules:/app/node_modules
- frontend_svelte_kit:/app/.svelte-kit
command: [sh, -c, "bun install && bun run dev --host 0.0.0.0 --port 3000"]
volumes:
frontend_node_modules:
frontend_svelte_kit:
+15
View File
@@ -0,0 +1,15 @@
# Решения
## 2026-10-16, до кикоффа
- Отдельный клиент, а не форк Firefly и не PR в апстрим. Мотив: Firefly на проде на `:latest` с `auto_update`, а релизы выходят примерно дважды в неделю (v6.7.1 15.09, v6.7.9 11.10). Форк пришлось бы постоянно сливать руками. Большой фронт, написанный нейронкой, автор-одиночка в апстрим не возьмёт.
- Клиент живёт на домене Firefly (`firefly.hhhhh.dev`) и ходит в `/api/v1` с кукой `laravel_token`. Вход, MFA и выход остаются за Firefly. Мотив: не нужны ни OAuth, ни токены в браузере.
- Экраны занимают те же пути, что в Firefly; чего нет, отдаёт сам Firefly. Мотив человека: «не сломались пути».
- Раскладка, меню и состав страниц как у Firefly v1, качество новое. Мотив человека: привык к раскладке, а пользоваться невозможно (таблицы, отчёты, поиск, редактор транзакций, клавиатура).
- Объём: надмножество Firefly. Редактирование автоматизаций (правила, повторяющиеся, вебхуки) уходит в старый UI. Мотив человека: «ими всё равно не пользуюсь», скоуп не резать.
- Без попапов, для power user, vim-навигация.
- Тема claddcn-svelte (`lib/claddcn-svelte`), цвета от Firefly, не розовый msos.
- Шаблон: `newproject --parts frontend,caddy --frontend svelte --serve static --mcp svelte,context7`, `ui_library=shadcn`, линтер biome. Потом `claddcn init --strip-vars`.
- SvelteKit 3 даёт алиас `#lib` (subpath imports), а шаблонный `components.json` и компоненты claddcn писали `$lib`. Переписано на `#lib`, иначе `svelte-check` давал 87 ошибок. Это стоит поправить в шаблоне `frontend-svelte` и в claddcn-svelte.
- Вендорный UI (`src/lib/components/ui`, `src/routes/claddcn.css`) исключён из biome целиком: правила ultracite спорили с кодом shadcn и claddcn.
- Эталоны старого интерфейса: образ `fireflyiii/core:version-6.6.6`, последний с v1 и v2 (убраны в коммите `4c29d34ef7` 02.09.2026, первым без них вышел v6.7.0).
+116
View File
@@ -0,0 +1,116 @@
$impeccable делаем Firefly UI: новый интерфейс к моему Firefly III. Это отдельный SvelteKit-клиент поверх его API, узнаваемый Firefly по раскладке, но красивый, быстрый и целиком управляемый с клавиатуры. К утру он должен работать на https://firefly.hhhhh.dev, на тех же адресах, что и сам Firefly. Пользователь один, я: power user, люблю vim-навигацию.
Сначала 15 минут со мной, потом я сплю, дальше ты сам до утра.
## Старт, пока я рядом
1. Прочитай код Firefly (ниже, что и где), без скринов и без запуска. Составь для себя карту: страницы, что на каждой, какие эндпоинты API за ней стоят, чего в API нет.
2. `impeccable init` с интервью, жёстко, минут на 15. Спрашивай про вкус, плотность, что меня бесит в Firefly и что я в нём люблю. Ответы в PRODUCT.md и `docs/agents/decisions.md`.
3. Один раунд вопросов по «Что нужно от меня» (ниже), до 4 штук, у каждого дефолт. Не отвечу за 10 минут: бери дефолты и пиши их в decisions.md с пометкой «допущение».
4. Дальше всё без меня.
## Что это за продукт
Мне нравится сама система Firefly III и его раскладка, к ней я привык. Пользоваться им невозможно: таблицы рендерятся криво, отчёты и способ их получения ужасны, поиск странный, редактор транзакций глючит, с клавиатуры не работает почти ничего. В v6.7 автор выкинул старый интерфейс (v1 на Twig и v2 на Alpine) и оставил только новый v3 (Blade, Alpine, AdminLTE 4). Сам он пишет, что v3 сырой (`changelog.md`, v6.7.1, Known issues).
Нужно надмножество обычного Firefly. Всё, что он умеет, плюс то, чего ему не хватает. Это не «привет, Claude, сделай финансовое приложение». Человек, привыкший к Firefly, открывает его и сразу узнаёт: то же меню, те же страницы, те же блоки на тех же местах. Только сделано хорошо.
Как работать над каждой страницей:
1. Посмотри, как страница выглядела в v1 и как выглядит в v3: скрин со стенда, шаблон и контроллер.
2. Пойми, что автор, вероятно, хотел этим сказать. Одна строка на страницу в `docs/agents/pages.md`.
3. Сделай нашу версию. Раскладка и места элементов как в Firefly, чтобы рука находила. Таблицы, формы, навигация и клавиатура сделаны заново и хорошо.
4. Где можно выпендриться, выпендрись: дашборд, страницы счетов, бюджетов, категорий, отчёты, всё со статистикой и графиками. Выпендрёж не должен ломать узнаваемость.
5. Скрин нашей версии рядом со скрином v1 в `docs/agents/demos/<страница>/`.
## Что читать
- **Исходники Firefly III**: `~/projects/contributions/firefly-iii`. Если на машине его нет: `git clone https://github.com/firefly-iii/firefly-iii ~/projects/contributions/firefly-iii`. Это чужой репо, ничего в нём не меняй и рабочую копию не переключай. Старые версии читай через `git show <тег>:<путь>` или отдельный `git worktree` во временном каталоге.
- `v6.6.6`: последний релиз со старыми интерфейсами. v1 лежит в `resources/views/*.twig` (кроме `v2/`), v2 в `resources/views/v2` и `resources/assets/v2`. Отсюда раскладка, меню, состав страниц, формы.
- Последний тег (сейчас `v6.7.9`): v3 в `resources/views`, `resources/assets/v3`. Отсюда текущие возможности и новые экраны.
- `routes/web.php`: адреса страниц. Наши экраны живут по тем же путям.
- `routes/api.php`, `app/Api/V1`: что умеет API. Типы генерируй из OpenAPI-спеки (`https://github.com/firefly-iii/api-docs`, yaml), руками их не пиши.
- `changelog.md` и issues на GitHub с меткой про layout: что у людей болит в v3.
- **Firefly Pico** (`https://github.com/cioraneanu/firefly-pico`): сторонний фронт на Nuxt поверх того же API. Бери только грабли API, которые они уже нашли. Вид оттуда не брать.
- **claddcn-svelte** (`https://git.kotikot.com/lib/claddcn-svelte`): тема cladd для shadcn-svelte, уже поставлена в этот репо (`src/routes/claddcn.css`, `#lib/components/ui`). README там: утилиты `cladd-*`, акценты, Surface.
- **Деплой**: `~/projects/infra/komodo` (если нет: `git clone git@git.kotikot.com:infra/komodo.git`).
- `fleet/personal/personal-main/fireflyiii/`: стек Firefly (`stack.toml`, `site.caddy`). Firefly на `:latest` с `auto_update`, так и остаётся.
- `scripts/new-app-stack.py` и `docs/architecture.md`, раздел «Деплой приложений»: как завести стек приложения из шаблона.
- `fleet/personal/personal-main/lodge/`: живой образец такого стека.
Исследуй параллельными субагентами, быстро.
## Устройство
- SPA на SvelteKit (`adapter-static`), раздаётся `serve.ts` на Bun, как задал шаблон. Своего бэкенда нет, всё через `/api/v1` Firefly.
- **Авторизация через Firefly.** Вход, MFA и выход остаются его страницами. После входа Firefly ставит куку `laravel_token` (`CreateFreshApiToken` в `bootstrap/app.php`), и API принимает её с CSRF-заголовком. Так ходит и сам v3: `resources/assets/v3/js/boot/axios.js`. Для этого клиент обязан жить на том же домене. На 401 полная перезагрузка на `/login`, после входа возврат туда, где был.
- **Риск, проверить первым:** кука выдаётся только на web-ответах Laravel, а наши страницы отдаёт не Laravel. Узнай, сколько она живёт и как её продлевать, пока вкладка открыта. Решение и проверку запиши в decisions.md.
- **Пути.** Каждый экран живёт по тому же адресу, что в Firefly (`/accounts/asset`, `/transactions/show/12`, `/budgets`). Закладки и ссылки продолжают работать. Всё, чего у нас нет, отдаёт сам Firefly: `/api`, `/oauth`, `/login`, `/profile`, `/two-factor`, cron-URL, автоматизации, POST-запросы его форм. Переход из клиента на такую страницу делается полной загрузкой. Нужен переключатель «открыть эту страницу в старом интерфейсе» (клавиша плюс пункт палитры), который работает на любом адресе. Маршрутизация живёт в `site.caddy` стека `fireflyiii`. Отказ от нового интерфейса это откат одного файла, опиши его в report.md.
- Firefly обновляется сам, поэтому клиент не должен молча ломаться. Типы из спеки и `make smoke`: прогон основных запросов против локального `fireflyiii/core:latest`. Версию API клиент читает из `/api/v1/about`. Если она уехала вперёд, показывает это одной строкой в подвале.
- В dev Vite проксирует на локальный Firefly всё, что не наше, чтобы вход и куки работали как на проде (`vite.config.ts`, `.env`).
- SvelteKit здесь третий: алиас `#lib` (subpath imports в `package.json`), а не `$lib`. `components.json` уже переведён.
## Стенд для отсмотра
Поднимай на этой машине в докере. Имя compose-проекта `ffui-*`, порты случайные высокие, трогать только своё.
- `fireflyiii/core:version-6.6.6` с `FIREFLY_III_LAYOUT=v1` (для сравнения можно и `v2`).
- `fireflyiii/core:latest`: на нём v3 и на нём же разрабатывается клиент.
- База у каждого своя: sqlite или mariadb, как быстрее заведётся.
- Одни и те же демо-данные в обоих через `scripts/seed` по API. Это отдельный скрипт, моков во фронте нет. Данные правдоподобные: 6–12 месяцев истории; счета (основные, сберегательные, кредитка, кредит), расходные и доходные; сплиты, переводы, две-три валюты, бюджеты с лимитами, категории, теги, подписки (bills), копилки, группы объектов, повторяющиеся транзакции и пара правил. Снос данных тем же скриптом.
- Эталонные скрины v1 и v3 каждой страницы: `docs/agents/reference/<страница>/{v1,v3}.png`.
## Что должно быть к утру, по приоритету
Объём не режь, делай всё по списку. Порядок нужен только на случай, если время кончится: тогда режь снизу. Первый пункт доведи до конца.
1. **Каркас на проде.** Узнаваемая раскладка Firefly v1 (боковое меню того же состава и порядка, шапка, хлебные крошки, выбор периода как у Firefly). Вход через Firefly. Палитра команд (`⌘K` / `Ctrl+K`) по страницам, счетам, бюджетам, категориям, тегам и действиям. Vim-навигация: `j/k` по строкам, `gg/G`, `/` поиск, `g`-переходы (`g d` дашборд, `g t` транзакции и так далее), `?` шпаргалка. Переключатель в старый интерфейс. Маршрутизация путей, как описано выше.
Приёмка: на https://firefly.hhhhh.dev после входа открывается новый дашборд. `/rules` открывает старую страницу Firefly. `/api/v1/about` с токеном отвечает. Выход и повторный вход возвращают на страницу, где был. Клавишей открывается та же страница в старом интерфейсе.
2. **Транзакции.** Списки по типам и по счёту. Нормальные таблицы: плотные, выравнивание сумм, валюты, сплиты раскрываются на месте. Множественный выбор с клавиатуры и массовые действия (категория, бюджет, теги, удаление). Поиск на языке запросов Firefly с автодополнением операторов и значений, запрос живёт в URL. Редактор транзакции страницей, без модалки: создание, правка, сплиты, клон, удаление, ссылки между транзакциями, вложения. Автодополнение счетов, категорий, бюджетов, тегов. Сохранение `⌘Enter`. Быстрый ввод с любой страницы.
Приёмка: на стенде ввести с клавиатуры, без мыши, расход со сплитом на две категории, найти его поиском, поменять категорию массовым действием, удалить.
3. **Счета и дашборд.** Счета по типам (список, просмотр с графиком баланса и транзакциями за период, создание и правка). Дашборд с тем же составом блоков, что в Firefly, нарисованный заново.
4. **Бюджеты** (лимиты по периодам, доступный бюджет, траты против лимита), **категории**, **теги**, **подписки (bills)**, **копилки**, **группы объектов**.
5. **Отчёты.** Генератор как в Firefly (тип, счета, период), но сразу живая страница: параметры в URL, меняются на месте, графики красивые, данные из insight- и chart-эндпоинтов. Главное место для выпендрёжа.
6. **Остальное с API:** валюты и курсы, вложения, типы ссылок, настройки (preferences), экспорт, финансовые группы (administrations).
7. **Автоматизации** (правила, группы правил, повторяющиеся транзакции, вебхуки): списки и просмотр можно, создание и правка уходят полной загрузкой в старый интерфейс Firefly.
Чего не делаем: редактирование автоматизаций (только старый UI); то, чего нет в API (профиль, пароль, MFA, OAuth-клиенты, админка пользователей): это ссылки в старый UI. Если сверку счёта (reconcile) или смену типа транзакции нельзя честно сделать через API, они тоже остаются в старом UI, а в decisions.md записано почему.
## Данные
Разработка и проверка идут только на стенде с демо-данными. Прод это мои настоящие финансы. На проде только чтение и один смоук записи: транзакция с тегом `ffui-smoke` создаётся и удаляется, после смоука на проде не остаётся ничего твоего.
## Дизайн
impeccable, режим Operate, claddcn-svelte (уже стоит). PRODUCT.md по итогам интервью.
- Вкус: узнаваемый Firefly. Палитра «примерно как у Firefly, только не уродская»: возьми его синий и цвета сумм (доход, расход, перевод) из v1 и v3, приведи в порядок через акценты claddcn. Не розовый msos. Светлая и тёмная тема, по умолчанию как в системе.
- Плотность как у инструмента для power user: таблицы плотные, числа моноширинные и выровнены, ничего не прячется за наведением.
- Без попапов. Редактирование страницей или на месте. Подтверждение удаления повторным нажатием на месте, а не диалогом.
- Силы вкладывать в: таблицы транзакций и редактор; дашборд и отчёты (графики); палитру и клавиатуру. Остальное спокойное.
- Не надо: таблетки, пульсация, скелетоны, тосты, длинные тире, висячие предлоги, некликабельные кнопки, подписи под блоками, жаргон. Карточки одной высоты. Фильтры, период и вкладки в URL. ПКМ вместо трёх точек. Ответ там, где нажал.
- Тексты интерфейса как в Firefly (язык решим в интервью). Числа и даты по локали из настроек Firefly пользователя.
## Как работаем
- Карта страниц и контракт API в `docs/agents/pages.md` и `docs/agents/api-contract.md`, потом экраны параллельно субагентами.
- Ядро по приоритетам доводи до приёмки. Дальше пространство твоё: всё, что сделает продукт лучше, делай без вопроса, смелые ставки приветствуются. Условие одно: каждая такая вещь описана в tour.md в блоке «сверх кикоффа» со строкой «зачем».
- Чего в кикоффе нет, не придумывай. Нашёл дыру, назови дыру. Если репо устроен иначе, чем тут написано, следуй репо.
- Чужие процессы и контейнеры на машине не трогай. Только свои PID, своё дерево. Никаких `pkill -f`, `prune`, `git reset` в общих копиях. Субагентам то же.
- Проверка глазами (`eyes`): headless chromium, 1440×900, 1920×1080 и 390×844. Каждый экран, рядом с эталоном v1. Посмотри сам, исправь одним заходом, сними ещё раз. Скрины в `docs/agents/demos`.
- `make fmt` и `make check` после каждого куска.
- Коммиты через `/commit` в `dev`. Деплой: сервер `personal-main`, домен `firefly.hhhhh.dev`. Новый стек приложения через `scripts/new-app-stack.py` в `infra/komodo`, маршрутизация в `fleet/personal/personal-main/fireflyiii/site.caddy`, выкатка через `kom`. После деплоя доказать, что доехало: `kom` и живой адрес.
- Главный сценарий (пункты 1 и 2) пройди на проде целиком в рамках смоука выше, потом проверь, что твоих данных не осталось.
- Не заканчивай ход, пока работают фоновые агенты или идёт деплой.
## Что нужно от меня (спроси в раунде вопросов)
1. Права на деплой: можно ли пушить в `infra/komodo` (`main`) новый стек и правку `site.caddy` у `fireflyiii` и выкатывать через `kom` без меня. Дефолт: да, только эти два каталога. Проверь `kom`-ключом этой машины, что он вообще разрешает. Если отказ, обходить нельзя: всё готовишь, а выкатку оставляешь мне с точной командой в report.md.
2. Смоук на проде: одна транзакция с тегом `ffui-smoke`, создать и удалить. Дефолт: да.
3. Язык интерфейса. Дефолт: английский, как подписи Firefly v1; форматы по локали пользователя.
4. Что открывается на корне домена: новый интерфейс (старый по переключателю) или старый (новый на `/app`). Дефолт: новый на тех же путях.
## Утром
Два файла в `docs/agents` и короткий отчёт в чат простым языком:
- `report.md`: адрес; что работает и чем проверено; что не проверено вживую; что не сделано и почему; где угадывал (особенно про API); как откатить маршрутизацию одним шагом; что бы ещё закрыл. Всё, что обещал в раунде вопросов, тут есть.
- `tour.md`: по экрану одна строка «что это, как попасть (адрес, клавиша, ПКМ), зачем». Отдельно полная раскладка клавиш. Отдельно блок «сверх кикоффа» в том же формате. Все скрытые аффордансы перечислены: меню по ПКМ, клавиши, тумблеры, переключатель в старый UI.
Когда я вернусь, первым ходом проведи меня по tour.md, потом правки.
+46
View File
@@ -0,0 +1,46 @@
{
"name": "firefly_ui",
"version": "0.0.1",
"private": true,
"type": "module",
"imports": {
"#lib": "./src/lib/index.js",
"#lib/*": "./src/lib/*"
},
"scripts": {
"build": "vite build",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"dev": "vite dev",
"fix": "ultracite fix",
"lint": "ultracite check",
"prepare": "svelte-kit sync || echo ''",
"preview": "vite preview"
},
"devDependencies": {
"@biomejs/biome": "^2.5.15",
"@fontsource-variable/geist": "^5.3.0",
"@fontsource-variable/geist-mono": "^5.3.0",
"@internationalized/date": "^3.12.0",
"@lucide/svelte": "^1.23.0",
"@sveltejs/adapter-static": "^4.0.0",
"@sveltejs/kit": "^3.0.1",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@tailwindcss/vite": "^4.3.0",
"@types/bun": "^1.3.0",
"bits-ui": "^2.16.3",
"clsx": "^2.1.1",
"mode-watcher": "^1.1.0",
"shadcn-svelte": "^1.3.0",
"svelte": "^5.57.1",
"svelte-check": "^4.7.6",
"svelte-sonner": "^1.1.0",
"tailwind-merge": "^3.5.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.3.0",
"tw-animate-css": "^1.4.0",
"typescript": "^6.0.3",
"ultracite": "^7.12.4",
"vite": "^8.0.16"
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["local>templates/renovate"]
}
+36
View File
@@ -0,0 +1,36 @@
const ROOT = `${import.meta.dir}/build`;
const PORT = Number(Bun.env.PORT ?? 3000);
const IMMUTABLE = "/_app/immutable/";
const fallback = Bun.file(`${ROOT}/index.html`);
function headersFor(pathname: string): HeadersInit {
if (pathname.startsWith(IMMUTABLE)) {
return { "Cache-Control": "public, max-age=31536000, immutable" };
}
return { "Cache-Control": "no-cache" };
}
Bun.serve({
async fetch(request) {
const { pathname } = new URL(request.url);
if (pathname.includes("..")) {
return new Response("Bad Request", { status: 400 });
}
const file = Bun.file(`${ROOT}${pathname}`);
if (await file.exists()) {
return new Response(file, { headers: headersFor(pathname) });
}
return new Response(fallback, {
headers: {
"Cache-Control": "no-cache",
"Content-Type": "text/html; charset=utf-8",
},
});
},
idleTimeout: 30,
port: PORT,
});
+13
View File
@@ -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 {};
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<meta content="scale" name="text-scale">
<link href="%sveltekit.assets%/favicon.svg" rel="icon" type="image/svg+xml">
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
import { defineEnvVars } from "@sveltejs/kit/env";
export const variables = defineEnvVars({
PUBLIC_API_BASE_URL: { public: true, schema: (input) => input ?? "/api" },
});
+56
View File
@@ -0,0 +1,56 @@
import { PUBLIC_API_BASE_URL } from "$app/env/public";
const BASE = PUBLIC_API_BASE_URL;
export class ApiError extends Error {
status: number;
constructor(status: number, message: string) {
super(message);
this.status = status;
this.name = "ApiError";
}
}
type Json = Record<string, unknown> | unknown[];
async function handle<T>(res: Response): Promise<T> {
if (res.status === 204) {
return undefined as T;
}
if (res.ok) {
return (await res.json()) as T;
}
let detail = res.statusText;
try {
const { detail: parsed } = (await res.json()) as { detail?: string };
if (parsed) {
detail = parsed;
}
} catch {
detail = res.statusText;
}
throw new ApiError(res.status, detail);
}
async function request<T>(
method: string,
path: string,
body?: Json
): Promise<T> {
const init: RequestInit = { credentials: "include", method };
if (body !== undefined) {
init.headers = { "Content-Type": "application/json" };
init.body = JSON.stringify(body);
}
const res = await fetch(BASE + path, init);
return handle<T>(res);
}
export const api = {
del: <T>(path: string) => request<T>("DELETE", path),
get: <T>(path: string) => request<T>("GET", path),
patch: <T>(path: string, body?: Json) => request<T>("PATCH", path, body),
post: <T>(path: string, body?: Json) => request<T>("POST", path, body),
put: <T>(path: string, body?: Json) => request<T>("PUT", path, body),
};
@@ -0,0 +1,20 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: AvatarPrimitive.FallbackProps = $props();
</script>
<AvatarPrimitive.Fallback
bind:ref
data-slot="avatar-fallback"
class={cn(
"flex size-full items-center justify-center rounded-full font-semibold text-cladd-fg-soft text-cladd-xs uppercase",
className
)}
{...restProps}
/>
@@ -0,0 +1,17 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: AvatarPrimitive.ImageProps = $props();
</script>
<AvatarPrimitive.Image
bind:ref
data-slot="avatar-image"
class={cn("aspect-square size-full", className)}
{...restProps}
/>
@@ -0,0 +1,22 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
loadingStatus = $bindable("loading"),
class: className,
...restProps
}: AvatarPrimitive.RootProps = $props();
</script>
<AvatarPrimitive.Root
bind:ref
bind:loadingStatus
data-slot="avatar"
class={cn(
"cladd-surface cladd-gradient cladd-outline relative flex size-cladd-lg shrink-0 overflow-hidden rounded-full",
className
)}
{...restProps}
/>
+13
View File
@@ -0,0 +1,13 @@
import Root from "./avatar.svelte";
import Image from "./avatar-image.svelte";
import Fallback from "./avatar-fallback.svelte";
export {
Root,
Image,
Fallback,
//
Root as Avatar,
Image as AvatarImage,
Fallback as AvatarFallback,
};
+61
View File
@@ -0,0 +1,61 @@
<script lang="ts" module>
import { type VariantProps, tv } from "tailwind-variants";
export const badgeVariants = tv({
base: "cladd-surface inline-flex h-cladd-nested-md w-fit shrink-0 select-none items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded-cladd-sm px-2 font-semibold text-cladd-xs leading-none has-[>svg:first-child]:ps-1.5 has-[>svg:last-child]:pe-1.5 [&>svg]:pointer-events-none [&>svg]:size-3",
variants: {
variant: {
default:
"cladd-gradient cladd-outline cladd-color-brand text-cladd-primary",
secondary: "cladd-gradient cladd-outline text-cladd-fg",
destructive:
"cladd-gradient cladd-outline cladd-color-red text-cladd-primary",
success:
"cladd-gradient cladd-outline cladd-color-green text-cladd-primary",
warning:
"cladd-gradient cladd-outline cladd-color-yellow text-cladd-primary",
outline: "cladd-ghost text-cladd-fg shadow-cladd-outline",
fill: "cladd-gradient-fill cladd-outline cladd-color-brand",
},
},
defaultVariants: {
variant: "default",
},
});
export type BadgeVariant = VariantProps<typeof badgeVariants>["variant"];
</script>
<script lang="ts">
import type { HTMLAnchorAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
import { withColor, type CladdColor } from "../button/button.svelte";
let {
ref = $bindable(null),
href,
class: className,
variant = "default",
color,
children,
...restProps
}: WithElementRef<HTMLAnchorAttributes> & {
variant?: BadgeVariant;
color?: CladdColor;
} = $props();
</script>
<svelte:element
this={href ? "a" : "span"}
bind:this={ref}
data-slot="badge"
{href}
class={cn(
withColor(badgeVariants({ variant }), color),
href && "cladd-hoverable cladd-focusable",
className
)}
{...restProps}
>
{@render children?.()}
</svelte:element>
+2
View File
@@ -0,0 +1,2 @@
export { default as Badge } from "./badge.svelte";
export { badgeVariants, type BadgeVariant } from "./badge.svelte";
@@ -0,0 +1,25 @@
<script lang="ts">
import EllipsisIcon from "@lucide/svelte/icons/ellipsis";
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef, type WithoutChildren } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: WithoutChildren<
WithElementRef<HTMLAttributes<HTMLSpanElement>>
> = $props();
</script>
<span
bind:this={ref}
data-slot="breadcrumb-ellipsis"
role="presentation"
aria-hidden="true"
class={cn("flex size-9 items-center justify-center", className)}
{...restProps}
>
<EllipsisIcon class="size-4" />
<span class="sr-only">More</span>
</span>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLLiAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLLiAttributes> = $props();
</script>
<li
bind:this={ref}
data-slot="breadcrumb-item"
class={cn("inline-flex items-center gap-1.5", className)}
{...restProps}
>
{@render children?.()}
</li>
@@ -0,0 +1,29 @@
<script lang="ts">
import type { HTMLAnchorAttributes } from "svelte/elements";
import type { Snippet } from "svelte";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
href,
child,
children,
...restProps
}: WithElementRef<HTMLAnchorAttributes> & {
child?: Snippet<[{ props: HTMLAnchorAttributes }]>;
} = $props();
const attrs = $derived({
"data-slot": "breadcrumb-link",
class: cn("transition-colors hover:text-foreground", className),
href,
...restProps,
});
</script>
{#if child}
{@render child({ props: attrs })}
{:else}
<a bind:this={ref} {...attrs}> {@render children?.()} </a>
{/if}
@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLOlAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLOlAttributes> = $props();
</script>
<ol
bind:this={ref}
data-slot="breadcrumb-list"
class={cn(
"flex flex-wrap items-center gap-1.5 break-words font-medium text-cladd-fg-soft text-cladd-xs sm:gap-2.5",
className
)}
{...restProps}
>
{@render children?.()}
</ol>
@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
data-slot="breadcrumb-page"
role="link"
aria-disabled="true"
aria-current="page"
class={cn("font-medium text-cladd-fg", className)}
{...restProps}
>
{@render children?.()}
</span>
@@ -0,0 +1,27 @@
<script lang="ts">
import ChevronRightIcon from "@lucide/svelte/icons/chevron-right";
import { cn, type WithElementRef } from "#lib/utils.js";
import type { HTMLLiAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLLiAttributes> = $props();
</script>
<li
bind:this={ref}
data-slot="breadcrumb-separator"
role="presentation"
aria-hidden="true"
class={cn("[&>svg]:size-3.5", className)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
<ChevronRightIcon />
{/if}
</li>
@@ -0,0 +1,21 @@
<script lang="ts">
import type { WithElementRef } from "#lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
</script>
<nav
bind:this={ref}
data-slot="breadcrumb"
class={className}
aria-label="хлебные крошки"
{...restProps}
>
{@render children?.()}
</nav>
+25
View File
@@ -0,0 +1,25 @@
import Root from "./breadcrumb.svelte";
import Ellipsis from "./breadcrumb-ellipsis.svelte";
import Item from "./breadcrumb-item.svelte";
import Separator from "./breadcrumb-separator.svelte";
import Link from "./breadcrumb-link.svelte";
import List from "./breadcrumb-list.svelte";
import Page from "./breadcrumb-page.svelte";
export {
Root,
Ellipsis,
Item,
Separator,
Link,
List,
Page,
//
Root as Breadcrumb,
Ellipsis as BreadcrumbEllipsis,
Item as BreadcrumbItem,
Separator as BreadcrumbSeparator,
Link as BreadcrumbLink,
List as BreadcrumbList,
Page as BreadcrumbPage,
};
+131
View File
@@ -0,0 +1,131 @@
<script lang="ts" module>
import { cn, type WithElementRef } from "#lib/utils.js";
import type {
HTMLAnchorAttributes,
HTMLButtonAttributes,
} from "svelte/elements";
import { type VariantProps, tv } from "tailwind-variants";
export type CladdColor =
| "neutral"
| "brand"
| "red"
| "pink"
| "purple"
| "blue"
| "cyan"
| "lime"
| "green"
| "yellow"
| "orange";
export const buttonVariants = tv({
base: "cladd-clickable cladd-focusable relative inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap font-semibold disabled:pointer-events-none disabled:*:opacity-40 aria-disabled:pointer-events-none aria-disabled:*:opacity-40 aria-invalid:outline-destructive [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
variants: {
variant: {
default:
"cladd-surface cladd-gradient cladd-outline cladd-hoverable cladd-color-brand text-cladd-primary",
destructive:
"cladd-surface cladd-gradient cladd-outline cladd-hoverable cladd-color-red text-cladd-primary",
outline:
"cladd-surface cladd-gradient cladd-outline cladd-hoverable text-cladd-fg",
secondary: "cladd-surface cladd-gradient cladd-hoverable text-cladd-fg",
ghost: "cladd-ghost cladd-hoverable text-cladd-fg",
fill: "cladd-surface cladd-gradient-fill cladd-outline cladd-hoverable",
link: "cladd-color-brand text-cladd-primary underline-offset-4 hover:underline",
},
size: {
default: "h-cladd-md rounded-cladd-md px-2.5 text-cladd-xs",
xs: "h-cladd-xs gap-1.5 rounded-cladd-xs px-2 text-cladd-xs [&_svg:not([class*=size-])]:size-3",
sm: "h-cladd-sm gap-1.5 rounded-cladd-sm px-2.5 text-cladd-xs",
lg: "h-cladd-lg rounded-cladd-lg px-3 text-cladd-xs",
xl: "h-cladd-xl rounded-cladd-xl px-3.5 text-cladd-sm",
icon: "size-cladd-md rounded-cladd-md",
"icon-xs":
"size-cladd-xs rounded-cladd-xs [&_svg:not([class*=size-])]:size-3",
"icon-sm": "size-cladd-sm rounded-cladd-sm",
"icon-lg": "size-cladd-lg rounded-cladd-lg",
},
rounded: {
true: "rounded-full",
false: "",
},
},
compoundVariants: [{ variant: "link", class: "h-auto rounded-none px-0" }],
defaultVariants: {
variant: "default",
size: "default",
rounded: false,
},
});
/** Swaps the variant's accent for an explicit one: `<Button color="green">`. */
export function withColor(classes: string, color?: CladdColor) {
if (!color) {
return classes;
}
return `${classes.replace(/\bcladd-color-\w+/g, "")} cladd-color-${color}`;
}
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant;
size?: ButtonSize;
rounded?: boolean;
color?: CladdColor;
};
</script>
<script lang="ts">
let {
class: className,
variant = "default",
size = "default",
rounded = false,
color,
ref = $bindable(null),
href,
type = "button",
disabled,
children,
...restProps
}: ButtonProps = $props();
const classes = $derived(
cn(withColor(buttonVariants({ variant, size, rounded }), color), className)
);
</script>
<!-- Content sits in its own box so the press can shrink it while the surface stays put. -->
{#if href}
<a
bind:this={ref}
data-slot="button"
class={classes}
href={disabled ? undefined : href}
aria-disabled={disabled}
role={disabled ? "link" : undefined}
tabindex={disabled ? -1 : undefined}
{...restProps}
>
<span data-slot="button-content" class="cladd-button-content"
>{@render children?.()}</span
>
</a>
{:else}
<button
bind:this={ref}
data-slot="button"
class={classes}
{type}
{disabled}
{...restProps}
>
<span data-slot="button-content" class="cladd-button-content"
>{@render children?.()}</span
>
</button>
{/if}
+21
View File
@@ -0,0 +1,21 @@
import Root, {
type ButtonProps,
type ButtonSize,
type ButtonVariant,
type CladdColor,
buttonVariants,
withColor,
} from "./button.svelte";
export {
Root,
type ButtonProps as Props,
//
Root as Button,
buttonVariants,
withColor,
type CladdColor,
type ButtonProps,
type ButtonSize,
type ButtonVariant,
};
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn, type WithElementRef } from "#lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-action"
class={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-content"
class={cn("px-4", className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
</script>
<p
bind:this={ref}
data-slot="card-description"
class={cn("text-cladd-fg-soft text-cladd-xs leading-relaxed", className)}
{...restProps}
>
{@render children?.()}
</p>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from "#lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-footer"
class={cn("flex items-center gap-2 px-4 [.border-t]:pt-4", className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn, type WithElementRef } from "#lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-header"
class={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-4",
className
)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-title"
class={cn("font-semibold text-cladd-sm leading-tight", className)}
{...restProps}
>
{@render children?.()}
</div>
+23
View File
@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card"
class={cn(
"cladd-surface cladd-outline flex flex-col gap-4 rounded-cladd-panel py-4 text-cladd-fg *:[form]:flex *:[form]:flex-col *:[form]:gap-4",
className
)}
{...restProps}
>
{@render children?.()}
</div>
+25
View File
@@ -0,0 +1,25 @@
import Root from "./card.svelte";
import Content from "./card-content.svelte";
import Description from "./card-description.svelte";
import Footer from "./card-footer.svelte";
import Header from "./card-header.svelte";
import Title from "./card-title.svelte";
import Action from "./card-action.svelte";
export {
Root,
Content,
Description,
Footer,
Header,
Title,
Action,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
Action as CardAction,
};
@@ -0,0 +1,49 @@
<script lang="ts">
import { Checkbox as CheckboxPrimitive } from "bits-ui";
import CheckIcon from "@lucide/svelte/icons/check";
import MinusIcon from "@lucide/svelte/icons/minus";
import { cn, type WithoutChildrenOrChild } from "#lib/utils.js";
let {
ref = $bindable(null),
checked = $bindable(false),
indeterminate = $bindable(false),
class: className,
...restProps
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
</script>
<CheckboxPrimitive.Root
bind:ref
data-slot="checkbox"
class={cn(
"peer group/checkbox cladd-surface cladd-gradient cladd-outline cladd-hoverable cladd-clickable cladd-focusable relative flex size-cladd-thumb-sm shrink-0 items-center justify-center rounded-full text-cladd-fg-soft disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:outline-destructive",
"cladd-fill-checked",
(checked || indeterminate) && "cladd-color-brand",
className
)}
bind:checked
bind:indeterminate
{...restProps}
>
{#snippet children({
checked,
indeterminate,
})}
<span
data-slot="checkbox-indicator"
class={cn(
"flex items-center justify-center transition-[scale,color] duration-200",
checked || indeterminate
? "text-cladd-on-primary group-active/checkbox:scale-90"
: "scale-75 group-active/checkbox:scale-65"
)}
>
{#if indeterminate}
<MinusIcon class="size-3" strokeWidth={3} />
{:else}
<CheckIcon class="size-3" strokeWidth={3} />
{/if}
</span>
{/snippet}
</CheckboxPrimitive.Root>
+6
View File
@@ -0,0 +1,6 @@
import Root from "./checkbox.svelte";
export {
Root,
//
Root as Checkbox,
};
@@ -0,0 +1,14 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
let {
ref = $bindable(null),
...restProps
}: CollapsiblePrimitive.ContentProps = $props();
</script>
<CollapsiblePrimitive.Content
bind:ref
data-slot="collapsible-content"
{...restProps}
/>
@@ -0,0 +1,14 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
let {
ref = $bindable(null),
...restProps
}: CollapsiblePrimitive.TriggerProps = $props();
</script>
<CollapsiblePrimitive.Trigger
bind:ref
data-slot="collapsible-trigger"
{...restProps}
/>
@@ -0,0 +1,16 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
let {
ref = $bindable(null),
open = $bindable(false),
...restProps
}: CollapsiblePrimitive.RootProps = $props();
</script>
<CollapsiblePrimitive.Root
bind:ref
bind:open
data-slot="collapsible"
{...restProps}
/>
@@ -0,0 +1,13 @@
import Root from "./collapsible.svelte";
import Trigger from "./collapsible-trigger.svelte";
import Content from "./collapsible-content.svelte";
export {
Root,
Content,
Trigger,
//
Root as Collapsible,
Content as CollapsibleContent,
Trigger as CollapsibleTrigger,
};
@@ -0,0 +1,42 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
import CheckIcon from "@lucide/svelte/icons/check";
import { cn, type WithoutChildrenOrChild } from "#lib/utils.js";
import type { Snippet } from "svelte";
let {
ref = $bindable(null),
checked = $bindable(false),
indeterminate = $bindable(false),
class: className,
children: childrenProp,
...restProps
}: WithoutChildrenOrChild<ContextMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script>
<ContextMenuPrimitive.CheckboxItem
bind:ref
bind:checked
bind:indeterminate
data-slot="context-menu-checkbox-item"
class={cn(
"relative flex min-h-cladd-lg cursor-default select-none items-center gap-2 rounded-full py-1 ps-9 pe-3 font-medium text-cladd-xs outline-hidden transition-[background-color,box-shadow] duration-200 data-[disabled]:pointer-events-none data-highlighted:bg-cladd-surface-next data-[disabled]:opacity-40 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
)}
{...restProps}
>
{#snippet children({
checked,
})}
<span
class="pointer-events-none absolute start-3 flex size-4 items-center justify-center"
>
{#if checked}
<CheckIcon class="size-4" />
{/if}
</span>
{@render childrenProp?.()}
{/snippet}
</ContextMenuPrimitive.CheckboxItem>
@@ -0,0 +1,30 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
import ContextMenuPortal from "./context-menu-portal.svelte";
import type { ComponentProps } from "svelte";
import type { WithoutChildrenOrChild } from "#lib/utils.js";
let {
ref = $bindable(null),
portalProps,
class: className,
...restProps
}: ContextMenuPrimitive.ContentProps & {
portalProps?: WithoutChildrenOrChild<
ComponentProps<typeof ContextMenuPortal>
>;
} = $props();
</script>
<ContextMenuPortal {...portalProps}>
<ContextMenuPrimitive.Content
bind:ref
data-slot="context-menu-content"
class={cn(
"cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-popover z-50 max-h-(--bits-context-menu-content-available-height) min-w-[10rem] origin-(--bits-context-menu-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-cladd-popover p-2 text-cladd-fg shadow-cladd-popover outline-none",
className
)}
{...restProps}
/>
</ContextMenuPortal>
@@ -0,0 +1,24 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: ContextMenuPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
</script>
<ContextMenuPrimitive.GroupHeading
bind:ref
data-slot="context-menu-group-heading"
data-inset={inset}
class={cn(
"cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-8 data-[inset]:ps-9",
className
)}
{...restProps}
/>
@@ -0,0 +1,12 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
let { ref = $bindable(null), ...restProps }: ContextMenuPrimitive.GroupProps =
$props();
</script>
<ContextMenuPrimitive.Group
bind:ref
data-slot="context-menu-group"
{...restProps}
/>
@@ -0,0 +1,27 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
inset,
variant = "default",
...restProps
}: ContextMenuPrimitive.ItemProps & {
inset?: boolean;
variant?: "default" | "destructive";
} = $props();
</script>
<ContextMenuPrimitive.Item
bind:ref
data-slot="context-menu-item"
data-inset={inset}
data-variant={variant}
class={cn(
"data-[variant=destructive]:*:[svg]:!text-destructive relative flex min-h-cladd-lg cursor-default select-none items-center gap-2 rounded-full px-3 py-1 font-medium text-cladd-xs outline-hidden transition-[background-color,box-shadow] duration-200 data-[variant=destructive]:data-highlighted:bg-destructive/12 data-[disabled]:pointer-events-none data-highlighted:bg-cladd-surface-next data-[inset]:ps-8 data-[inset]:ps-9 data-[variant=destructive]:text-destructive data-[disabled]:opacity-40 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
)}
{...restProps}
/>
@@ -0,0 +1,27 @@
<script lang="ts">
import { cn, type WithElementRef } from "#lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean;
} = $props();
</script>
<div
bind:this={ref}
data-slot="context-menu-label"
data-inset={inset}
class={cn(
"cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-8 data-[inset]:ps-9",
className
)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,7 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
let { ...restProps }: ContextMenuPrimitive.PortalProps = $props();
</script>
<ContextMenuPrimitive.Portal {...restProps} />
@@ -0,0 +1,16 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
let {
ref = $bindable(null),
value = $bindable(""),
...restProps
}: ContextMenuPrimitive.RadioGroupProps = $props();
</script>
<ContextMenuPrimitive.RadioGroup
bind:ref
bind:value
data-slot="context-menu-radio-group"
{...restProps}
/>
@@ -0,0 +1,35 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
import CircleIcon from "@lucide/svelte/icons/circle";
import { cn, type WithoutChild } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children: childrenProp,
...restProps
}: WithoutChild<ContextMenuPrimitive.RadioItemProps> = $props();
</script>
<ContextMenuPrimitive.RadioItem
bind:ref
data-slot="context-menu-radio-item"
class={cn(
"relative flex min-h-cladd-lg cursor-default select-none items-center gap-2 rounded-full py-1 ps-9 pe-3 font-medium text-cladd-xs outline-hidden transition-[background-color,box-shadow] duration-200 data-[disabled]:pointer-events-none data-highlighted:bg-cladd-surface-next data-[disabled]:opacity-40 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
)}
{...restProps}
>
{#snippet children({
checked,
})}
<span
class="pointer-events-none absolute start-3 flex size-4 items-center justify-center"
>
{#if checked}
<CircleIcon class="size-2 fill-current" />
{/if}
</span>
{@render childrenProp?.({ checked })}
{/snippet}
</ContextMenuPrimitive.RadioItem>
@@ -0,0 +1,17 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: ContextMenuPrimitive.SeparatorProps = $props();
</script>
<ContextMenuPrimitive.Separator
bind:ref
data-slot="context-menu-separator"
class={cn("-mx-2 my-2 h-px bg-cladd-bg-outline", className)}
{...restProps}
/>
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn, type WithElementRef } from "#lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
data-slot="context-menu-shortcut"
class={cn(
"ms-auto font-mono text-cladd-2xs text-cladd-fg-softer tracking-widest",
className
)}
{...restProps}
>
{@render children?.()}
</span>
@@ -0,0 +1,20 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: ContextMenuPrimitive.SubContentProps = $props();
</script>
<ContextMenuPrimitive.SubContent
bind:ref
data-slot="context-menu-sub-content"
class={cn(
"cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-popover z-50 min-w-[10rem] origin-(--bits-context-menu-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-cladd-popover p-2 text-cladd-fg shadow-cladd-popover outline-none",
className
)}
{...restProps}
/>
@@ -0,0 +1,29 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
import ChevronRightIcon from "@lucide/svelte/icons/chevron-right";
import { cn, type WithoutChild } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithoutChild<ContextMenuPrimitive.SubTriggerProps> & {
inset?: boolean;
} = $props();
</script>
<ContextMenuPrimitive.SubTrigger
bind:ref
data-slot="context-menu-sub-trigger"
data-inset={inset}
class={cn(
"relative flex min-h-cladd-lg cursor-default select-none items-center gap-2 rounded-full px-3 py-1 font-medium text-cladd-xs outline-hidden transition-[background-color,box-shadow] duration-200 data-[disabled]:pointer-events-none data-[state=open]:bg-cladd-surface-next data-highlighted:bg-cladd-surface-next data-[inset]:ps-8 data-[inset]:ps-9 data-[disabled]:opacity-40 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
)}
{...restProps}
>
{@render children?.()}
<ChevronRightIcon class="ms-auto" />
</ContextMenuPrimitive.SubTrigger>
@@ -0,0 +1,8 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
let { open = $bindable(false), ...restProps }: ContextMenuPrimitive.SubProps =
$props();
</script>
<ContextMenuPrimitive.Sub bind:open {...restProps} />
@@ -0,0 +1,14 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
let {
ref = $bindable(null),
...restProps
}: ContextMenuPrimitive.TriggerProps = $props();
</script>
<ContextMenuPrimitive.Trigger
bind:ref
data-slot="context-menu-trigger"
{...restProps}
/>
@@ -0,0 +1,10 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
let {
open = $bindable(false),
...restProps
}: ContextMenuPrimitive.RootProps = $props();
</script>
<ContextMenuPrimitive.Root bind:open {...restProps} />
@@ -0,0 +1,52 @@
import Root from "./context-menu.svelte";
import Sub from "./context-menu-sub.svelte";
import Portal from "./context-menu-portal.svelte";
import Trigger from "./context-menu-trigger.svelte";
import Group from "./context-menu-group.svelte";
import RadioGroup from "./context-menu-radio-group.svelte";
import Item from "./context-menu-item.svelte";
import GroupHeading from "./context-menu-group-heading.svelte";
import Content from "./context-menu-content.svelte";
import Shortcut from "./context-menu-shortcut.svelte";
import RadioItem from "./context-menu-radio-item.svelte";
import Separator from "./context-menu-separator.svelte";
import SubContent from "./context-menu-sub-content.svelte";
import SubTrigger from "./context-menu-sub-trigger.svelte";
import CheckboxItem from "./context-menu-checkbox-item.svelte";
import Label from "./context-menu-label.svelte";
export {
Root,
Sub,
Portal,
Item,
GroupHeading,
Label,
Group,
Trigger,
Content,
Shortcut,
Separator,
RadioItem,
SubContent,
SubTrigger,
RadioGroup,
CheckboxItem,
//
Root as ContextMenu,
Sub as ContextMenuSub,
Portal as ContextMenuPortal,
Item as ContextMenuItem,
GroupHeading as ContextMenuGroupHeading,
Group as ContextMenuGroup,
Content as ContextMenuContent,
Trigger as ContextMenuTrigger,
Shortcut as ContextMenuShortcut,
RadioItem as ContextMenuRadioItem,
Separator as ContextMenuSeparator,
RadioGroup as ContextMenuRadioGroup,
SubContent as ContextMenuSubContent,
SubTrigger as ContextMenuSubTrigger,
CheckboxItem as ContextMenuCheckboxItem,
Label as ContextMenuLabel,
};
@@ -0,0 +1,8 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
let { ref = $bindable(null), ...restProps }: DialogPrimitive.CloseProps =
$props();
</script>
<DialogPrimitive.Close bind:ref data-slot="dialog-close" {...restProps} />
@@ -0,0 +1,45 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import DialogPortal from "./dialog-portal.svelte";
import XIcon from "@lucide/svelte/icons/x";
import type { Snippet } from "svelte";
import * as Dialog from "./index.js";
import { cn, type WithoutChildrenOrChild } from "#lib/utils.js";
import type { ComponentProps } from "svelte";
let {
ref = $bindable(null),
class: className,
portalProps,
children,
showCloseButton = true,
...restProps
}: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof DialogPortal>>;
children: Snippet;
showCloseButton?: boolean;
} = $props();
</script>
<DialogPortal {...portalProps}>
<Dialog.Overlay />
<DialogPrimitive.Content
bind:ref
data-slot="dialog-content"
class={cn(
"cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-dialog fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-cladd-dialog p-4 text-cladd-fg shadow-cladd-popover outline-none sm:max-w-lg",
className
)}
{...restProps}
>
{@render children?.()}
{#if showCloseButton}
<DialogPrimitive.Close
class="cladd-ghost cladd-hoverable cladd-clickable cladd-focusable absolute end-3 top-3 flex size-cladd-md items-center justify-center rounded-full text-cladd-fg-soft disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
>
<XIcon />
<span class="sr-only">Close</span>
</DialogPrimitive.Close>
{/if}
</DialogPrimitive.Content>
</DialogPortal>
@@ -0,0 +1,17 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.DescriptionProps = $props();
</script>
<DialogPrimitive.Description
bind:ref
data-slot="dialog-description"
class={cn("text-cladd-fg-soft text-cladd-sm leading-relaxed", className)}
{...restProps}
/>
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn, type WithElementRef } from "#lib/utils.js";
import type { HTMLAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="dialog-footer"
class={cn(
"mt-2 flex flex-col-reverse gap-2 sm:flex-row sm:flex-wrap sm:items-center sm:justify-end",
className
)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="dialog-header"
class={cn("flex flex-col gap-1.5 text-start", className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,17 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.OverlayProps = $props();
</script>
<DialogPrimitive.Overlay
bind:ref
data-slot="dialog-overlay"
class={cn("cladd-backdrop cladd-anim-fade fixed inset-0 z-50", className)}
{...restProps}
/>
@@ -0,0 +1,7 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
let { ...restProps }: DialogPrimitive.PortalProps = $props();
</script>
<DialogPrimitive.Portal {...restProps} />
@@ -0,0 +1,17 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.TitleProps = $props();
</script>
<DialogPrimitive.Title
bind:ref
data-slot="dialog-title"
class={cn("pe-8 font-semibold text-cladd-md leading-tight", className)}
{...restProps}
/>
@@ -0,0 +1,8 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
let { ref = $bindable(null), ...restProps }: DialogPrimitive.TriggerProps =
$props();
</script>
<DialogPrimitive.Trigger bind:ref data-slot="dialog-trigger" {...restProps} />
@@ -0,0 +1,8 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
let { open = $bindable(false), ...restProps }: DialogPrimitive.RootProps =
$props();
</script>
<DialogPrimitive.Root bind:open {...restProps} />
+34
View File
@@ -0,0 +1,34 @@
import Root from "./dialog.svelte";
import Portal from "./dialog-portal.svelte";
import Title from "./dialog-title.svelte";
import Footer from "./dialog-footer.svelte";
import Header from "./dialog-header.svelte";
import Overlay from "./dialog-overlay.svelte";
import Content from "./dialog-content.svelte";
import Description from "./dialog-description.svelte";
import Trigger from "./dialog-trigger.svelte";
import Close from "./dialog-close.svelte";
export {
Root,
Title,
Portal,
Footer,
Header,
Trigger,
Overlay,
Content,
Description,
Close,
//
Root as Dialog,
Title as DialogTitle,
Portal as DialogPortal,
Footer as DialogFooter,
Header as DialogHeader,
Trigger as DialogTrigger,
Overlay as DialogOverlay,
Content as DialogContent,
Description as DialogDescription,
Close as DialogClose,
};
@@ -0,0 +1,16 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
let {
ref = $bindable(null),
value = $bindable([]),
...restProps
}: DropdownMenuPrimitive.CheckboxGroupProps = $props();
</script>
<DropdownMenuPrimitive.CheckboxGroup
bind:ref
bind:value
data-slot="dropdown-menu-checkbox-group"
{...restProps}
/>
@@ -0,0 +1,46 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import CheckIcon from "@lucide/svelte/icons/check";
import MinusIcon from "@lucide/svelte/icons/minus";
import { cn, type WithoutChildrenOrChild } from "#lib/utils.js";
import type { Snippet } from "svelte";
let {
ref = $bindable(null),
checked = $bindable(false),
indeterminate = $bindable(false),
class: className,
children: childrenProp,
...restProps
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script>
<DropdownMenuPrimitive.CheckboxItem
bind:ref
bind:checked
bind:indeterminate
data-slot="dropdown-menu-checkbox-item"
class={cn(
"relative flex min-h-cladd-lg cursor-default select-none items-center gap-2 rounded-full py-1 ps-9 pe-3 font-medium text-cladd-xs outline-hidden transition-[background-color,box-shadow] duration-200 data-[disabled]:pointer-events-none data-highlighted:bg-cladd-surface-next data-[disabled]:opacity-40 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft [&_svg]:pointer-events-none [&_svg]:shrink-0",
className
)}
{...restProps}
>
{#snippet children({
checked,
indeterminate,
})}
<span
class="pointer-events-none absolute start-3 flex size-4 items-center justify-center"
>
{#if indeterminate}
<MinusIcon class="size-4" />
{:else}
<CheckIcon class={cn("size-4", !checked && "text-transparent")} />
{/if}
</span>
{@render childrenProp?.()}
{/snippet}
</DropdownMenuPrimitive.CheckboxItem>
@@ -0,0 +1,31 @@
<script lang="ts">
import { cn, type WithoutChildrenOrChild } from "#lib/utils.js";
import DropdownMenuPortal from "./dropdown-menu-portal.svelte";
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import type { ComponentProps } from "svelte";
let {
ref = $bindable(null),
sideOffset = 4,
portalProps,
class: className,
...restProps
}: DropdownMenuPrimitive.ContentProps & {
portalProps?: WithoutChildrenOrChild<
ComponentProps<typeof DropdownMenuPortal>
>;
} = $props();
</script>
<DropdownMenuPortal {...portalProps}>
<DropdownMenuPrimitive.Content
bind:ref
data-slot="dropdown-menu-content"
{sideOffset}
class={cn(
"cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-popover z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[10rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-cladd-popover p-2 text-cladd-fg shadow-cladd-popover outline-none",
className
)}
{...restProps}
/>
</DropdownMenuPortal>
@@ -0,0 +1,25 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "#lib/utils.js";
import type { ComponentProps } from "svelte";
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: ComponentProps<typeof DropdownMenuPrimitive.GroupHeading> & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.GroupHeading
bind:ref
data-slot="dropdown-menu-group-heading"
data-inset={inset}
class={cn(
"cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-8 data-[inset]:ps-9",
className
)}
{...restProps}
/>
@@ -0,0 +1,14 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
let {
ref = $bindable(null),
...restProps
}: DropdownMenuPrimitive.GroupProps = $props();
</script>
<DropdownMenuPrimitive.Group
bind:ref
data-slot="dropdown-menu-group"
{...restProps}
/>
Loaded 100 of 184 files, more files were not shown because too many files have changed in this diff. Show more