From 528251f1769e05321e19461845125d8d8861fb82 Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Fri, 26 Jun 2026 15:26:20 +0100 Subject: [PATCH] chore: initial scaffold Vite + Vue 3 + DaisyUI SPA (project picker + Slack avatar member grid) with a TypeScript/Node (Hono) backend proxying Napta + Slack, SQLite avatar cache via node:sqlite, and docker-compose for Coolify deploy. --- .dockerignore | 7 + .gitignore | 31 + .oxfmtrc.json | 5 + .oxlintrc.json | 11 + .vscode/extensions.json | 3 + .zed/settings.json | 31 + Dockerfile | 11 + README.md | 55 ++ backend/.dockerignore | 4 + backend/.env.example | 16 + backend/Dockerfile | 11 + backend/package.json | 19 + backend/pnpm-lock.yaml | 62 ++ backend/src/config.ts | 19 + backend/src/db.ts | 47 + backend/src/fixtures.ts | 47 + backend/src/index.ts | 35 + backend/src/napta.ts | 15 + backend/src/slack.ts | 9 + backend/src/types.ts | 17 + backend/tsconfig.json | 19 + docker-compose.yml | 22 + index.html | 13 + nginx.conf | 24 + package.json | 31 + pnpm-lock.yaml | 1468 ++++++++++++++++++++++++++++++ public/favicon.svg | 24 + src/App.vue | 87 ++ src/api.ts | 18 + src/assets/icons/README.md | 37 + src/components/MemberCard.vue | 30 + src/components/MemberGrid.vue | 39 + src/components/ProjectPicker.vue | 28 + src/main.ts | 5 + src/style.css | 17 + src/types.ts | 27 + tsconfig.app.json | 17 + tsconfig.json | 4 + tsconfig.node.json | 23 + vite.config.ts | 22 + 40 files changed, 2410 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .oxfmtrc.json create mode 100644 .oxlintrc.json create mode 100644 .vscode/extensions.json create mode 100644 .zed/settings.json create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 backend/.dockerignore create mode 100644 backend/.env.example create mode 100644 backend/Dockerfile create mode 100644 backend/package.json create mode 100644 backend/pnpm-lock.yaml create mode 100644 backend/src/config.ts create mode 100644 backend/src/db.ts create mode 100644 backend/src/fixtures.ts create mode 100644 backend/src/index.ts create mode 100644 backend/src/napta.ts create mode 100644 backend/src/slack.ts create mode 100644 backend/src/types.ts create mode 100644 backend/tsconfig.json create mode 100644 docker-compose.yml create mode 100644 index.html create mode 100644 nginx.conf create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 public/favicon.svg create mode 100644 src/App.vue create mode 100644 src/api.ts create mode 100644 src/assets/icons/README.md create mode 100644 src/components/MemberCard.vue create mode 100644 src/components/MemberGrid.vue create mode 100644 src/components/ProjectPicker.vue create mode 100644 src/main.ts create mode 100644 src/style.css create mode 100644 src/types.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..45c9b38 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +.git +.DS_Store +backend +data +.zed diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c115ce4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Runtime data + secrets +data/ +*.db +.env +.env.* +!.env.example diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..5d41335 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "semi": false, + "singleQuote": false +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..31e054f --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,11 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "unicorn", "oxc"], + "categories": { + "correctness": "error" + }, + "rules": {}, + "env": { + "builtin": true + } +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000..cb88f18 --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,31 @@ +{ + "languages": { + "TypeScript": { + "format_on_save": "on", + "formatter": { + "external": { + "command": "./node_modules/.bin/oxfmt", + "arguments": ["--stdin-filepath", "{buffer_path}"] + } + } + }, + "JavaScript": { + "format_on_save": "on", + "formatter": { + "external": { + "command": "./node_modules/.bin/oxfmt", + "arguments": ["--stdin-filepath", "{buffer_path}"] + } + } + }, + "Vue.js": { + "format_on_save": "on", + "formatter": { + "external": { + "command": "./node_modules/.bin/oxfmt", + "arguments": ["--stdin-filepath", "{buffer_path}"] + } + } + } + } +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..af20aa9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM node:22-alpine AS build +WORKDIR /app +COPY package.json pnpm-lock.yaml* ./ +RUN corepack enable && pnpm install --frozen-lockfile +COPY . . +RUN pnpm build + +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 diff --git a/README.md b/README.md new file mode 100644 index 0000000..ea8d2ce --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# photofetch + +Deployed at https://photofetch.apoena.dev + +Pick a **Napta project** and get a grid of the people staffed on it — each with +their **Slack profile picture**, first name, last name, and role. People are +matched between Napta and Slack by **email**. + +## Architecture + +- **frontend** (repo root) — Vite + Vue 3 + TypeScript + Tailwind v4 + DaisyUI SPA. +- **backend/** — TypeScript/Node API (Hono) that proxies Napta + Slack so their + tokens never reach the browser. Runs `.ts` directly on Node 24 (no build step); + SQLite via the built-in `node:sqlite` module (avatar cache at `data/app.db`). + Serves demo fixtures until live credentials are configured. + +In production, nginx serves the built SPA and proxies `/api` to the backend +service (see `nginx.conf` + `docker-compose.yml`). + +## Develop + +```bash +pnpm install +pnpm dev # frontend on :5173 (proxies /api -> :8000) +cd backend && pnpm install && pnpm dev # API on :8000 +# …or run the whole stack in containers: +docker compose up --build +``` + +Deploy gates (run before pushing): + +```bash +pnpm lint # oxlint (pnpm lint:fix to autofix) +pnpm fmt:check # oxfmt (pnpm fmt to format) +pnpm build # vue-tsc + vite build, no warnings +cd backend && pnpm typecheck +``` + +## Configuration (live data) + +Set on the Coolify app (or in `backend/.env` locally — see `backend/.env.example`): + +| Var | Purpose | +|---|---| +| `NAPTA_API_TOKEN` | Napta API token (lists projects + their people) | +| `NAPTA_BASE_URL` | Napta API base (default `https://app.napta.io/api/v1`) | +| `SLACK_BOT_TOKEN` | Slack bot token, scopes `users:read` + `users:read.email` | + +Without **both** tokens the API serves demo fixtures and the UI shows a +"demo data" banner. + +## Deploy + +Pushes to `main` are picked up by Coolify at https://platform.apoena.dev and +deployed to https://photofetch.apoena.dev. diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..2c7d12b --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,4 @@ +node_modules +data +.env +.env.* diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..4eeee58 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,16 @@ +# Backend configuration. Copy to .env for local dev (gitignored), or set these +# as environment variables on the Coolify app for production. + +PORT=8000 + +# Napta API — required for live data. Without it, the API serves demo fixtures. +# Base URL + token: see https://app.napta.io (Settings → API). +NAPTA_API_TOKEN= +NAPTA_BASE_URL=https://app.napta.io/api/v1 + +# Slack bot token (xoxb-...) with scopes: users:read, users:read.email +# Used to resolve each person's profile picture by email. +SLACK_BOT_TOKEN= + +# SQLite location (avatar cache). Matches the Coolify persistent volume. +DB_PATH=data/app.db diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..4ce793a --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,11 @@ +# Node 24 runs TypeScript directly (type stripping) and ships node:sqlite +# built in — so no build step, no tsx, no native sqlite module needed. +FROM node:24-alpine +WORKDIR /app +COPY package.json pnpm-lock.yaml* ./ +RUN corepack enable && pnpm install --prod --frozen-lockfile +COPY . . +ENV PORT=8000 +ENV DB_PATH=/app/data/app.db +EXPOSE 8000 +CMD ["node", "src/index.ts"] diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..551c179 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,19 @@ +{ + "name": "photofetch-backend", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "node --watch src/index.ts", + "start": "node src/index.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@hono/node-server": "^2.0.6", + "hono": "^4.12.27" + }, + "devDependencies": { + "@types/node": "^26.0.1", + "typescript": "^6.0.3" + } +} diff --git a/backend/pnpm-lock.yaml b/backend/pnpm-lock.yaml new file mode 100644 index 0000000..e5259cc --- /dev/null +++ b/backend/pnpm-lock.yaml @@ -0,0 +1,62 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@hono/node-server': + specifier: ^2.0.6 + version: 2.0.6(hono@4.12.27) + hono: + specifier: ^4.12.27 + version: 4.12.27 + devDependencies: + '@types/node': + specifier: ^26.0.1 + version: 26.0.1 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + +packages: + + '@hono/node-server@2.0.6': + resolution: {integrity: sha512-7DeRlKG57JDBNZ5Qj2jwVdgwQy4b0tLubRLl3zCf91/rCf9i7p1V5FtW/yWibm1uUHE493ts9ZXH/7g/LQWl+g==} + engines: {node: '>=20'} + peerDependencies: + hono: ^4 + + '@types/node@26.0.1': + resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} + + hono@4.12.27: + resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} + engines: {node: '>=16.9.0'} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + +snapshots: + + '@hono/node-server@2.0.6(hono@4.12.27)': + dependencies: + hono: 4.12.27 + + '@types/node@26.0.1': + dependencies: + undici-types: 8.3.0 + + hono@4.12.27: {} + + typescript@6.0.3: {} + + undici-types@8.3.0: {} diff --git a/backend/src/config.ts b/backend/src/config.ts new file mode 100644 index 0000000..a84b9a6 --- /dev/null +++ b/backend/src/config.ts @@ -0,0 +1,19 @@ +export interface Config { + port: number + naptaApiToken: string | null + naptaBaseUrl: string + slackBotToken: string | null +} + +export const config: Config = { + port: Number(process.env.PORT ?? 8000), + naptaApiToken: process.env.NAPTA_API_TOKEN || null, + naptaBaseUrl: process.env.NAPTA_BASE_URL || "https://app.napta.io/api/v1", + slackBotToken: process.env.SLACK_BOT_TOKEN || null, +} + +// Live data needs BOTH a Napta token (to list projects/people) and a Slack +// token (to resolve avatars). Without both, the API serves demo fixtures. +export const hasLiveCredentials: boolean = Boolean( + config.naptaApiToken && config.slackBotToken, +) diff --git a/backend/src/db.ts b/backend/src/db.ts new file mode 100644 index 0000000..da68752 --- /dev/null +++ b/backend/src/db.ts @@ -0,0 +1,47 @@ +import { DatabaseSync } from "node:sqlite" +import { mkdirSync } from "node:fs" +import { dirname } from "node:path" + +const dbPath = process.env.DB_PATH || "data/app.db" +mkdirSync(dirname(dbPath), { recursive: true }) + +export const db = new DatabaseSync(dbPath) + +// Cache of email -> Slack avatar URL, so we don't hit Slack on every request. +// image_url is nullable: a NULL row means "looked up, no Slack match" (also +// worth caching, to avoid re-querying people who aren't in Slack). +db.exec(` + CREATE TABLE IF NOT EXISTS slack_avatar_cache ( + email TEXT PRIMARY KEY, + image_url TEXT, + cached_at INTEGER NOT NULL + ) +`) + +const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000 + +interface CacheRow { + image_url: string | null + cached_at: number +} + +// Returns the cached URL (which may be null = known-no-match), or undefined +// when there is no fresh cache entry and the caller should query Slack. +export function getCachedAvatar(email: string): string | null | undefined { + const row = db + .prepare("SELECT image_url, cached_at FROM slack_avatar_cache WHERE email = ?") + .get(email.toLowerCase()) as CacheRow | undefined + if (!row) return undefined + if (Date.now() - row.cached_at > CACHE_TTL_MS) return undefined + return row.image_url +} + +export function setCachedAvatar(email: string, imageUrl: string | null): void { + db.prepare( + `INSERT INTO slack_avatar_cache (email, image_url, cached_at) + VALUES (?, ?, ?) + ON CONFLICT(email) DO UPDATE SET + image_url = excluded.image_url, + cached_at = excluded.cached_at`, + ).run(email.toLowerCase(), imageUrl, Date.now()) +} diff --git a/backend/src/fixtures.ts b/backend/src/fixtures.ts new file mode 100644 index 0000000..55efbe1 --- /dev/null +++ b/backend/src/fixtures.ts @@ -0,0 +1,47 @@ +import type { Member, Project } from "./types.ts" + +// Demo data served when live Napta/Slack credentials are not configured. Lets +// the UI render a realistic team grid out of the box. imageUrl is null so the +// frontend shows initials avatars (the same fallback used for real members who +// have no Slack match). + +export const fixtureProjects: Project[] = [ + { id: "demo-apollo", name: "Apollo Platform", clientName: "Acme Corp" }, + { id: "demo-zephyr", name: "Zephyr Mobile App", clientName: "Globex" }, +] + +function member( + id: string, + firstName: string, + lastName: string, + role: string, +): Member { + return { + id, + firstName, + lastName, + role, + email: `${firstName}.${lastName}@example.com`.toLowerCase(), + imageUrl: null, + slackMatched: false, + } +} + +const membersByProject: Record = { + "demo-apollo": [ + member("1", "Ada", "Lovelace", "Tech Lead"), + member("2", "Alan", "Turing", "Backend Engineer"), + member("3", "Grace", "Hopper", "Engineering Manager"), + member("4", "Katherine", "Johnson", "Data Scientist"), + member("5", "Dennis", "Ritchie", "Platform Engineer"), + ], + "demo-zephyr": [ + member("6", "Margaret", "Hamilton", "Product Owner"), + member("7", "Linus", "Torvalds", "Mobile Lead"), + member("8", "Barbara", "Liskov", "iOS Engineer"), + ], +} + +export function fixtureMembers(projectId: string): Member[] { + return membersByProject[projectId] ?? [] +} diff --git a/backend/src/index.ts b/backend/src/index.ts new file mode 100644 index 0000000..137e8a0 --- /dev/null +++ b/backend/src/index.ts @@ -0,0 +1,35 @@ +import { serve } from "@hono/node-server" +import { Hono } from "hono" +import { logger } from "hono/logger" +import { config, hasLiveCredentials } from "./config.ts" +import { fixtureMembers, fixtureProjects } from "./fixtures.ts" +import { fetchNaptaProjectMembers, fetchNaptaProjects } from "./napta.ts" +import { enrichWithSlackAvatars } from "./slack.ts" +import "./db.ts" // initialise the SQLite schema at boot + +const app = new Hono() +app.use("*", logger()) + +app.get("/api/health", (c) => c.json({ status: "ok" })) + +app.get("/api/projects", async (c) => { + if (!hasLiveCredentials) { + return c.json({ source: "fixture", projects: fixtureProjects }) + } + const projects = await fetchNaptaProjects() + return c.json({ source: "napta", projects }) +}) + +app.get("/api/projects/:id/members", async (c) => { + const id = c.req.param("id") + if (!hasLiveCredentials) { + return c.json({ source: "fixture", members: fixtureMembers(id) }) + } + const members = await enrichWithSlackAvatars(await fetchNaptaProjectMembers(id)) + return c.json({ source: "napta", members }) +}) + +console.log( + `photofetch backend listening on :${config.port} (live credentials: ${hasLiveCredentials})`, +) +serve({ fetch: app.fetch, port: config.port }) diff --git a/backend/src/napta.ts b/backend/src/napta.ts new file mode 100644 index 0000000..85ba989 --- /dev/null +++ b/backend/src/napta.ts @@ -0,0 +1,15 @@ +import type { Member, Project } from "./types.ts" + +// Real Napta integration. Implemented in Step 11 (build-out) once the exact +// Napta API endpoints, auth header, and response shapes are confirmed in the +// walk-with-me design session. Until then these throw, and the route layer +// only calls them when live credentials are present — so the default +// experience (no tokens) serves fixtures instead of erroring. + +export async function fetchNaptaProjects(): Promise { + throw new Error("Napta integration not implemented yet (Step 11)") +} + +export async function fetchNaptaProjectMembers(_projectId: string): Promise { + throw new Error("Napta integration not implemented yet (Step 11)") +} diff --git a/backend/src/slack.ts b/backend/src/slack.ts new file mode 100644 index 0000000..e81c4e3 --- /dev/null +++ b/backend/src/slack.ts @@ -0,0 +1,9 @@ +import type { Member } from "./types.ts" + +// Given Napta members (which carry emails), fill in imageUrl from Slack by +// matching on email — caching results in slack_avatar_cache (see db.ts). +// Implemented in Step 11; for now it's a no-op pass-through so the pipeline +// is wired end-to-end and members render with initials fallbacks. +export async function enrichWithSlackAvatars(members: Member[]): Promise { + return members +} diff --git a/backend/src/types.ts b/backend/src/types.ts new file mode 100644 index 0000000..ef4ffa3 --- /dev/null +++ b/backend/src/types.ts @@ -0,0 +1,17 @@ +export interface Project { + id: string + name: string + clientName?: string +} + +export interface Member { + id: string + firstName: string + lastName: string + role: string + email: string + imageUrl: string | null + slackMatched: boolean +} + +export type DataSource = "napta" | "fixture" diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..185c7af --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "nodenext", + "moduleResolution": "nodenext", + "lib": ["esnext"], + "types": ["node"], + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..428173c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +services: + web: + build: . + ports: + - "80:80" + depends_on: + - api + + api: + build: ./backend + environment: + - PORT=8000 + - DB_PATH=/app/data/app.db + # Set these as env vars on the Coolify app to switch from demo fixtures + # to live data. Empty -> the backend serves fixtures. + - NAPTA_API_TOKEN=${NAPTA_API_TOKEN:-} + - NAPTA_BASE_URL=${NAPTA_BASE_URL:-https://app.napta.io/api/v1} + - SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN:-} + volumes: + - ./data:/app/data + ports: + - "8000:8000" diff --git a/index.html b/index.html new file mode 100644 index 0000000..8d11219 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + photofetch + + +
+ + + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..9734b8d --- /dev/null +++ b/nginx.conf @@ -0,0 +1,24 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Proxy API calls to the backend service (docker-compose service name "api"). + location /api/ { + proxy_pass http://api:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2c3229c --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "photofetch", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview", + "lint": "oxlint", + "lint:fix": "oxlint --fix", + "fmt": "oxfmt", + "fmt:check": "oxfmt --check" + }, + "dependencies": { + "daisyui": "^5.5.23", + "vue": "^3.5.38" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.3.1", + "@types/node": "^24.13.2", + "@vitejs/plugin-vue": "^6.0.7", + "@vue/tsconfig": "^0.9.1", + "oxfmt": "^0.56.0", + "oxlint": "^1.71.0", + "tailwindcss": "^4.3.1", + "typescript": "~6.0.2", + "vite": "^8.1.0", + "vue-tsc": "^3.3.5" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..1ae2f8d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1468 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + daisyui: + specifier: ^5.5.23 + version: 5.5.23 + vue: + specifier: ^3.5.38 + version: 3.5.39(typescript@6.0.3) + devDependencies: + '@tailwindcss/vite': + specifier: ^4.3.1 + version: 4.3.1(vite@8.1.0(@types/node@24.13.2)(jiti@2.7.0)) + '@types/node': + specifier: ^24.13.2 + version: 24.13.2 + '@vitejs/plugin-vue': + specifier: ^6.0.7 + version: 6.0.7(vite@8.1.0(@types/node@24.13.2)(jiti@2.7.0))(vue@3.5.39(typescript@6.0.3)) + '@vue/tsconfig': + specifier: ^0.9.1 + version: 0.9.1(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)) + oxfmt: + specifier: ^0.56.0 + version: 0.56.0 + oxlint: + specifier: ^1.71.0 + version: 1.71.0 + tailwindcss: + specifier: ^4.3.1 + version: 4.3.1 + typescript: + specifier: ~6.0.2 + version: 6.0.3 + vite: + specifier: ^8.1.0 + version: 8.1.0(@types/node@24.13.2)(jiti@2.7.0) + vue-tsc: + specifier: ^3.3.5 + version: 3.3.5(typescript@6.0.3) + +packages: + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oxc-project/types@0.137.0': + resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} + + '@oxfmt/binding-android-arm-eabi@0.56.0': + resolution: {integrity: sha512-CSCxi7ovYojgfdPOdUb9T508HKeAdDIKeRGg7x8IZwVJrWz9gVgX7MbUnFqtQAE4QvoNo07mj2JlwnOzJw4qqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.56.0': + resolution: {integrity: sha512-HYJFnd+PkDwf6S9ZPGzXXtjNqvRWFnnhdbWaouh4mi/SxU8wmDuzlMn3xo/wDTGnr4Q1VA7ZzOaE/D4biW0W6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.56.0': + resolution: {integrity: sha512-sftR/bEOr+t1gs+evwsHi/Xbq2FAPA2uU3VMr8n6ZU9PoK/IMSfnfu7+OEe/uy1+knhrFl4Wvy7Vkm3uo9mJ7g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.56.0': + resolution: {integrity: sha512-z66SdjLqa3MUPKvTp3Mbb5nSjKSbnYxJGeB+Wx987s8T5hPcIRiBMfnJ6zcPgYtQn3x5xjvdzNVkXrSeYH6ZFg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.56.0': + resolution: {integrity: sha512-t2tkrV1vtZyaItSQ71dTi2ZVKZEI39b/LqLT12V5KMfIeXK6N32TUC1jhOXKVQmhECq9j2ZXMQV3JeT1kh9Vmg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.56.0': + resolution: {integrity: sha512-+gCy+Tp3RHeXQ9y/QrS76lXIpZkbziTyp6hIgjB2MssCwfMph3vG/GEfkhO34Rai1vhYIaUkvv8UT1BcDorJPw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.56.0': + resolution: {integrity: sha512-0kKkVvQ2I+FJ2sxQyUu1zJ0yWP5kcWse/yVFnGQSFCXMwSSkfEaUGu0dW774O7nyy3jrcBGap7OSc8dZmU/CdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.56.0': + resolution: {integrity: sha512-npkA2siMbyWRh+wEhi1aTAx4RirukGcGNt8V4Ch86pG+xU9aurqS1MZOnKYMu03ISwat3rB6zkQx51SsB9obNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.56.0': + resolution: {integrity: sha512-UekqOjGkV4/MkqreCV9SPIB2jlR3/HbXrmhV1rVXJZ9wfDXMyCMriLtq3tHqLY4PkbVWNtfcm1kMojJ26KLSJw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.56.0': + resolution: {integrity: sha512-XSzveSpeZMD5XJpew5lRFVtNnT04xd3rJxENXmk7wkZzN9oWzv2aFJyoNDhOtoz69BYaS/fg4SYl+CfEZRpB0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.56.0': + resolution: {integrity: sha512-EkQ0nJa7k7HDDIVuPF7WY+k4k+bzdclLYtyIXNt7/OqVghfNiMym6YGppFBgx1XRIHW6QylxBz5OogumPjPJbQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.56.0': + resolution: {integrity: sha512-dyjAGW8jKRge0ik6U/dgvQG0nVpA3iBlRskQTz5qJLvQWIrySxX5jpqzPetLBNIIZ231KA82fDdi1nLTk8ENCw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.56.0': + resolution: {integrity: sha512-60ZGH3LtfqlW8X6vcLdSFY4lvCQYINurttYBKaALnHCDVAUCYJ1LsUgS6p1XOzVlzEDx3yNUZvDF1Lvt59zoZw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.56.0': + resolution: {integrity: sha512-u1suj1tgJHK4ZqB7buCtdbNef2n8+d0lXTPJwLHNmtyK6p+DTpsaoDvmqhQrA56fgKYv4LuRxNtL8YooebKOew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.56.0': + resolution: {integrity: sha512-aYGLvlQHt80y+qKEtfJY/Nm27G0125Lv+qyh9SJ4Cjc6lXnXjD+ndfhqQnbV24POpMi7rNRi0jvx/0d70FRpCQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.56.0': + resolution: {integrity: sha512-H/re/gO+7ysVc+kywHNuzY3C33EN9sQcZhg0kp1ZwOZl7y998ZE5mhnBiuGR/nYI0pqLL5xQfrHVUOJ/cIJsCA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.56.0': + resolution: {integrity: sha512-6qLNXfXmtAs8jXDvYMkxk6Wec5SUJoew+ZX1uOZmqaR7ks0EJFbAohuOCELDyJMWyVlxotVG8Xf8m74Bfq0O2w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.56.0': + resolution: {integrity: sha512-UXEXuKphAe15bsob4AswNMArCw38XSmUIs3wk1s6e6MX9OWGW/IRWU95s1hZDiVg09STy1jHgyN2qkqbu1FT0w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.56.0': + resolution: {integrity: sha512-HPyNDjky+NIOuaMvHZflR+kst3YWdUOH2JUQYkf99grqZ5mEBTQM6h9iGy501Z8Xt5xMScrwHOuVCOlqDrktRw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.71.0': + resolution: {integrity: sha512-ImGmd1njEg4FEJH03jhRnveEegtO3czCtfptvaHivKAZQIYATbVFBrrzbaYMYv0oJioTnxZAZVSyV+oL7W8S2g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.71.0': + resolution: {integrity: sha512-4A5BEexBrwY1YFF8Kiq/lp/wQPRG79G3BWIE1FuWaM5MvmpYSd+7ZySVcKkHdwo0UDzdQGddp6pD9mpctMqLnw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.71.0': + resolution: {integrity: sha512-9wJA9GJulLwS2usU3CEisI/ESDO1n1z9eyTCvApMDrAkbJ1ve0mORgTMjcWWsKxkzkeZ2N/Gpra5IQE7x8tYgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.71.0': + resolution: {integrity: sha512-PlLCjS06V0PeJMAJwzjrExw1sYNW9Gch3JtNlcwwZDXGlTYDuwHNN89zYH8LTXFfgkVtsYvs2nv0FqrzyuFDzg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.71.0': + resolution: {integrity: sha512-Lhil7bWre0ncxbUoDoxfS0JzpTz17BRQKW7iwoAUY8GJ66+WwJEfYPCFJ1P0WgVZR5/O/b3Q2pENlHOjeXLOGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.71.0': + resolution: {integrity: sha512-Oo9/L58PYD3RC0x05d2upAPLllHytTjHQGsnC06P6Ynn7jKkp5mdImQxXdJ3+FnBaKspNpGogzgVsi6g872LiA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.71.0': + resolution: {integrity: sha512-mSHfyfgJrEbyIR29ejaeS50BdPk+GoNPlC1dckpDiUZbJAIel68sjSMdOt4WY0/gva+ECC7FNITQkxMJU+vSBw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.71.0': + resolution: {integrity: sha512-n9yY4M2tiy3aij4AqtlnspzpfdpeT5JQfK2/w2d8oyp5W0FRwOb1dIeX99nORNcxGr08iD9bH8N5XFz3I2iy8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-arm64-musl@1.71.0': + resolution: {integrity: sha512-fJZrs5sDZtTaPIOiemRQQmo82Ezy+vOGXemPc4Ok7iVVsYsFa7SlW6Z5XN819VfsqBHRm3NJ3rTdnR8+bJYJdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-ppc64-gnu@1.71.0': + resolution: {integrity: sha512-cwl7VKGERIy9p+G+AvZdfy/06q0aHXaTt/mMRReC751iuNYJgqKjB7NydXSS30nBT9vtr2tunciOtrR4fD6FUA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-gnu@1.71.0': + resolution: {integrity: sha512-eZ8ieVXvzGi8jr7+ybQGPK2STw3mldfxZlgA2738iflfB/rzA69sE6m5rDRpQaxC7dpm745Enlh1Tod0QAk9Gg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-musl@1.71.0': + resolution: {integrity: sha512-puMDbQYe6+NXwfMusojoA7CXGn2b3utukmd23PQqc1E3XhVCwyZ+FueSMzDYeNgDV2dUfIVXAAKZBcFDeCL6sA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-s390x-gnu@1.71.0': + resolution: {integrity: sha512-4NJLxBs1ujISCt3L/1FcywLs73PWtJuw+piD6feK2V6h6OS6P7xu9/sWt1DTRLibe6QCzmfZzmM/2HPORoV/Lg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-gnu@1.71.0': + resolution: {integrity: sha512-cFDaiR8L3430qp88tfZnvFlt3KotFhR/DlbIL0nHOMMYiG/9Wy4l+6f7t8G8pTa9bd8Lt8+M0y/qjRQ/xcB74g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-musl@1.71.0': + resolution: {integrity: sha512-orfixdt76KlpNly9z0PkWBBNfwjKz+JFVLP/7wnVchlKNU9Dpt9InU/ZggeSej6fC7qwHmHNOGlhLnQXcYoGuA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/binding-openharmony-arm64@1.71.0': + resolution: {integrity: sha512-9emQu2lAp6yhPB3XuI+++vR+l/o6JR1X+EpxwcumPdQXBWXEPAsquPGL7l158EqU8SebQMXTUa/S5zN98juyHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.71.0': + resolution: {integrity: sha512-bd5kI8spYwTm3BILDtGhi73zoup5dw8MlPQNT8YB3BD5UIsjNe3K9/4ctrzQMX4SZMoK5HgzVLkLJzacEXB7fA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.71.0': + resolution: {integrity: sha512-W4HvOHGzVLHcrmFu+bMrJlho+/yrlX5ZNdJZqGe8MEldkQG+RHYhxxad9P4jvWAYFmIqUA5i9DQ8QsJqSU9GIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.71.0': + resolution: {integrity: sha512-D2kyEIPHk/G/wiZLnwTVC/sVst+T/lKldVOjAFpgTIBUAOlry72e5OiapDbDBF4LfJLkN5ypJb/8Eu6yJzkveQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-android-arm64@1.1.3': + resolution: {integrity: sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.3': + resolution: {integrity: sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.3': + resolution: {integrity: sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.3': + resolution: {integrity: sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + resolution: {integrity: sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.3': + resolution: {integrity: sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.1.3': + resolution: {integrity: sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.3': + resolution: {integrity: sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.3': + resolution: {integrity: sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.3': + resolution: {integrity: sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.1.3': + resolution: {integrity: sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.1.3': + resolution: {integrity: sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.3': + resolution: {integrity: sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.3': + resolution: {integrity: sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.3': + resolution: {integrity: sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@tailwindcss/node@4.3.1': + resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + + '@tailwindcss/oxide-android-arm64@4.3.1': + resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.1': + resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.1': + resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.1': + resolution: {integrity: sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/node@24.13.2': + resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} + + '@vitejs/plugin-vue@6.0.7': + resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vue/compiler-core@3.5.39': + resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==} + + '@vue/compiler-dom@3.5.39': + resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==} + + '@vue/compiler-sfc@3.5.39': + resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==} + + '@vue/compiler-ssr@3.5.39': + resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==} + + '@vue/language-core@3.3.5': + resolution: {integrity: sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==} + + '@vue/reactivity@3.5.39': + resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==} + + '@vue/runtime-core@3.5.39': + resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==} + + '@vue/runtime-dom@3.5.39': + resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==} + + '@vue/server-renderer@3.5.39': + resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==} + peerDependencies: + vue: 3.5.39 + + '@vue/shared@3.5.39': + resolution: {integrity: sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==} + + '@vue/tsconfig@0.9.1': + resolution: {integrity: sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==} + peerDependencies: + typescript: '>= 5.8' + vue: ^3.4.0 + peerDependenciesMeta: + typescript: + optional: true + vue: + optional: true + + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + daisyui@5.5.23: + resolution: {integrity: sha512-xuheNUSL4T6ZVtWXoioqcNkjoyGX85QTDz4HTw2aBPfqk4fuMjax5HDo8qCmpV6M1YN8bGvfx5BpYCoDeRlt+A==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + oxfmt@0.56.0: + resolution: {integrity: sha512-9Dv0wV3zKiyvhjD7bRKaInKmHQ1sPx3RGOjQkGFJbbdQ16576yf8qhMSO9Q9cvHcs+1NpBsRTkuDDYFFPTJ6gw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + oxlint@1.71.0: + resolution: {integrity: sha512-U1m1X+C0vDj7DC1e13IoZULzEcPczE7UOMTs8VlZGHUEIUaSTZKo5qkPsQEfzpgnQ29Pea/w3Xntk62UCecxZw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=0.22.1' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + rolldown@1.1.3: + resolution: {integrity: sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + tailwindcss@4.3.1: + resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + vite@8.1.0: + resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + 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 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-tsc@3.3.5: + resolution: {integrity: sha512-Rzh/G2MmNlMSAMTiQEjDrsb4dgB/jbtEM47rVN2NtidF1dfb/q4w4QvpQBtW5+y3y5H27Hjh7deVwk+YB02fNg==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.39: + resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + +snapshots: + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@oxc-project/types@0.137.0': {} + + '@oxfmt/binding-android-arm-eabi@0.56.0': + optional: true + + '@oxfmt/binding-android-arm64@0.56.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.56.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.56.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.56.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.56.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.56.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.56.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.56.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.56.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.56.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.56.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.56.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.56.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.56.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.56.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.56.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.56.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.56.0': + optional: true + + '@oxlint/binding-android-arm-eabi@1.71.0': + optional: true + + '@oxlint/binding-android-arm64@1.71.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.71.0': + optional: true + + '@oxlint/binding-darwin-x64@1.71.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.71.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.71.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.71.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.71.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.71.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.71.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.71.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.71.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.71.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.71.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.71.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.71.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.71.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.71.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.71.0': + optional: true + + '@rolldown/binding-android-arm64@1.1.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.3': + optional: true + + '@rolldown/binding-darwin-x64@1.1.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.3': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.3': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.3': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@tailwindcss/node@4.3.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.1 + + '@tailwindcss/oxide-android-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide@4.3.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-x64': 4.3.1 + '@tailwindcss/oxide-freebsd-x64': 4.3.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-x64-musl': 4.3.1 + '@tailwindcss/oxide-wasm32-wasi': 4.3.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 + + '@tailwindcss/vite@4.3.1(vite@8.1.0(@types/node@24.13.2)(jiti@2.7.0))': + dependencies: + '@tailwindcss/node': 4.3.1 + '@tailwindcss/oxide': 4.3.1 + tailwindcss: 4.3.1 + vite: 8.1.0(@types/node@24.13.2)(jiti@2.7.0) + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/node@24.13.2': + dependencies: + undici-types: 7.18.2 + + '@vitejs/plugin-vue@6.0.7(vite@8.1.0(@types/node@24.13.2)(jiti@2.7.0))(vue@3.5.39(typescript@6.0.3))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.1.0(@types/node@24.13.2)(jiti@2.7.0) + vue: 3.5.39(typescript@6.0.3) + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/compiler-core@3.5.39': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.39 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.39': + dependencies: + '@vue/compiler-core': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/compiler-sfc@3.5.39': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.39 + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.15 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.39': + dependencies: + '@vue/compiler-dom': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/language-core@3.3.5': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.39 + '@vue/shared': 3.5.39 + alien-signals: 3.2.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.4 + + '@vue/reactivity@3.5.39': + dependencies: + '@vue/shared': 3.5.39 + + '@vue/runtime-core@3.5.39': + dependencies: + '@vue/reactivity': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/runtime-dom@3.5.39': + dependencies: + '@vue/reactivity': 3.5.39 + '@vue/runtime-core': 3.5.39 + '@vue/shared': 3.5.39 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.39(vue@3.5.39(typescript@6.0.3))': + dependencies: + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + vue: 3.5.39(typescript@6.0.3) + + '@vue/shared@3.5.39': {} + + '@vue/tsconfig@0.9.1(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3))': + optionalDependencies: + typescript: 6.0.3 + vue: 3.5.39(typescript@6.0.3) + + alien-signals@3.2.1: {} + + csstype@3.2.3: {} + + daisyui@5.5.23: {} + + detect-libc@2.1.2: {} + + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@7.0.1: {} + + estree-walker@2.0.2: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + jiti@2.7.0: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + 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 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + muggle-string@0.4.1: {} + + nanoid@3.3.15: {} + + oxfmt@0.56.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.56.0 + '@oxfmt/binding-android-arm64': 0.56.0 + '@oxfmt/binding-darwin-arm64': 0.56.0 + '@oxfmt/binding-darwin-x64': 0.56.0 + '@oxfmt/binding-freebsd-x64': 0.56.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.56.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.56.0 + '@oxfmt/binding-linux-arm64-gnu': 0.56.0 + '@oxfmt/binding-linux-arm64-musl': 0.56.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.56.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.56.0 + '@oxfmt/binding-linux-riscv64-musl': 0.56.0 + '@oxfmt/binding-linux-s390x-gnu': 0.56.0 + '@oxfmt/binding-linux-x64-gnu': 0.56.0 + '@oxfmt/binding-linux-x64-musl': 0.56.0 + '@oxfmt/binding-openharmony-arm64': 0.56.0 + '@oxfmt/binding-win32-arm64-msvc': 0.56.0 + '@oxfmt/binding-win32-ia32-msvc': 0.56.0 + '@oxfmt/binding-win32-x64-msvc': 0.56.0 + + oxlint@1.71.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.71.0 + '@oxlint/binding-android-arm64': 1.71.0 + '@oxlint/binding-darwin-arm64': 1.71.0 + '@oxlint/binding-darwin-x64': 1.71.0 + '@oxlint/binding-freebsd-x64': 1.71.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.71.0 + '@oxlint/binding-linux-arm-musleabihf': 1.71.0 + '@oxlint/binding-linux-arm64-gnu': 1.71.0 + '@oxlint/binding-linux-arm64-musl': 1.71.0 + '@oxlint/binding-linux-ppc64-gnu': 1.71.0 + '@oxlint/binding-linux-riscv64-gnu': 1.71.0 + '@oxlint/binding-linux-riscv64-musl': 1.71.0 + '@oxlint/binding-linux-s390x-gnu': 1.71.0 + '@oxlint/binding-linux-x64-gnu': 1.71.0 + '@oxlint/binding-linux-x64-musl': 1.71.0 + '@oxlint/binding-openharmony-arm64': 1.71.0 + '@oxlint/binding-win32-arm64-msvc': 1.71.0 + '@oxlint/binding-win32-ia32-msvc': 1.71.0 + '@oxlint/binding-win32-x64-msvc': 1.71.0 + + path-browserify@1.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.15 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rolldown@1.1.3: + dependencies: + '@oxc-project/types': 0.137.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.3 + '@rolldown/binding-darwin-arm64': 1.1.3 + '@rolldown/binding-darwin-x64': 1.1.3 + '@rolldown/binding-freebsd-x64': 1.1.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.3 + '@rolldown/binding-linux-arm64-gnu': 1.1.3 + '@rolldown/binding-linux-arm64-musl': 1.1.3 + '@rolldown/binding-linux-ppc64-gnu': 1.1.3 + '@rolldown/binding-linux-s390x-gnu': 1.1.3 + '@rolldown/binding-linux-x64-gnu': 1.1.3 + '@rolldown/binding-linux-x64-musl': 1.1.3 + '@rolldown/binding-openharmony-arm64': 1.1.3 + '@rolldown/binding-wasm32-wasi': 1.1.3 + '@rolldown/binding-win32-arm64-msvc': 1.1.3 + '@rolldown/binding-win32-x64-msvc': 1.1.3 + + source-map-js@1.2.1: {} + + tailwindcss@4.3.1: {} + + tapable@2.3.3: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinypool@2.1.0: {} + + tslib@2.8.1: + optional: true + + typescript@6.0.3: {} + + undici-types@7.18.2: {} + + vite@8.1.0(@types/node@24.13.2)(jiti@2.7.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.1.3 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.13.2 + fsevents: 2.3.3 + jiti: 2.7.0 + + vscode-uri@3.1.0: {} + + vue-tsc@3.3.5(typescript@6.0.3): + dependencies: + '@volar/typescript': 2.4.28 + '@vue/language-core': 3.3.5 + typescript: 6.0.3 + + vue@3.5.39(typescript@6.0.3): + dependencies: + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-sfc': 3.5.39 + '@vue/runtime-dom': 3.5.39 + '@vue/server-renderer': 3.5.39(vue@3.5.39(typescript@6.0.3)) + '@vue/shared': 3.5.39 + optionalDependencies: + typescript: 6.0.3 diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..7e94d22 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..39fdb40 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,87 @@ + + + diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..bc8839d --- /dev/null +++ b/src/api.ts @@ -0,0 +1,18 @@ +import type { MembersResponse, ProjectsResponse } from "@/types" + +async function getJson(url: string): Promise { + const res = await fetch(url) + if (!res.ok) { + const body = await res.text() + throw new Error(`${res.status} ${res.statusText}${body ? `: ${body}` : ""}`) + } + return (await res.json()) as T +} + +export function fetchProjects(): Promise { + return getJson("/api/projects") +} + +export function fetchMembers(projectId: string): Promise { + return getJson(`/api/projects/${encodeURIComponent(projectId)}/members`) +} diff --git a/src/assets/icons/README.md b/src/assets/icons/README.md new file mode 100644 index 0000000..fbac4b8 --- /dev/null +++ b/src/assets/icons/README.md @@ -0,0 +1,37 @@ +# Icons + +Reusable in-app icons. Source: https://tabler.io/icons (outline variant by default). + +## Add an icon + +1. Find the icon on https://tabler.io/icons, click it, copy the SVG (or download). +2. Save it here as `.svg` — same slug Tabler uses (`bolt.svg`, `qrcode.svg`). +3. Keep `stroke="currentColor"` in the SVG so colour follows Tailwind classes. + +## Use an icon + +Static colour (cheapest, no extra component): + +```vue + +``` + +Dynamic colour (needs `currentColor` to flow through — paste the SVG inline as a component): + +```vue + +``` + +The favicon at `public/favicon.svg` was generated from this same icon set with `currentColor` replaced by the app's primary hex at scaffold time. diff --git a/src/components/MemberCard.vue b/src/components/MemberCard.vue new file mode 100644 index 0000000..476da45 --- /dev/null +++ b/src/components/MemberCard.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/components/MemberGrid.vue b/src/components/MemberGrid.vue new file mode 100644 index 0000000..ebc171f --- /dev/null +++ b/src/components/MemberGrid.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/ProjectPicker.vue b/src/components/ProjectPicker.vue new file mode 100644 index 0000000..36b0f1a --- /dev/null +++ b/src/components/ProjectPicker.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..8a1724e --- /dev/null +++ b/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from "vue" +import "./style.css" +import App from "./App.vue" + +createApp(App).mount("#app") diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..191d307 --- /dev/null +++ b/src/style.css @@ -0,0 +1,17 @@ +/* The font @import MUST come first — before @import "tailwindcss" — because + Tailwind inlines its import into real CSS rules, and the CSS spec requires + @import to precede all other rules. If it comes second, the production build + warns ("@import must precede all rules…") and browsers silently DROP the + font import, so the custom font never loads. */ +@import url("https://api.fonts.coollabs.io/css2?family=Inter:wght@400;500;600;700&display=swap"); +@import "tailwindcss"; +@plugin "daisyui"; +@plugin "daisyui/theme" { + name: "light"; + default: true; + --color-primary: #4a154b; +} + +@theme { + --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..e7aa6a7 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,27 @@ +export interface Project { + id: string + name: string + clientName?: string +} + +export interface Member { + id: string + firstName: string + lastName: string + role: string + email: string + imageUrl: string | null + slackMatched: boolean +} + +export type DataSource = "napta" | "fixture" + +export interface ProjectsResponse { + source: DataSource + projects: Project[] +} + +export interface MembersResponse { + source: DataSource + members: Member[] +} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..b8d0f2d --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,17 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "types": ["vite/client"], + "paths": { + "@/*": ["./src/*"] + }, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d32ff68 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,4 @@ +{ + "files": [], + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..8455dcb --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "module": "nodenext", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..1e5a801 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from "vite" +import { fileURLToPath, URL } from "node:url" +import vue from "@vitejs/plugin-vue" +import tailwindcss from "@tailwindcss/vite" + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue(), tailwindcss()], + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)), + }, + }, + server: { + proxy: { + "/api": { + target: "http://localhost:8000", + changeOrigin: true, + }, + }, + }, +})