diff --git a/.agents/skills/hf-cli/.hf-skill-manifest.json b/.agents/skills/hf-cli/.hf-skill-manifest.json new file mode 100644 index 0000000000000000000000000000000000000000..e8078e60900c531c463daa53295e54f3b960d74c --- /dev/null +++ b/.agents/skills/hf-cli/.hf-skill-manifest.json @@ -0,0 +1,4 @@ +{ + "installed_revision": "377a0d3f596ece474d3c5082dd4057219cc726dc", + "schema_version": 1 +} diff --git a/.agents/skills/hf-cli/SKILL.md b/.agents/skills/hf-cli/SKILL.md new file mode 100644 index 0000000000000000000000000000000000000000..56041f1293d58e6980f9914ebaba197767779cdf --- /dev/null +++ b/.agents/skills/hf-cli/SKILL.md @@ -0,0 +1,195 @@ +--- +name: hf-cli +description: "Hugging Face Hub CLI (`hf`) for downloading, uploading, and managing models, datasets, spaces, buckets, repos, papers, jobs, and more on the Hugging Face Hub. Use when: handling authentication; managing local cache; managing Hugging Face Buckets; running or scheduling jobs on Hugging Face infrastructure; managing Hugging Face repos; discussions and pull requests; browsing models, datasets and spaces; reading, searching, or browsing academic papers; managing collections; querying datasets; configuring spaces; setting up webhooks; or deploying and managing HF Inference Endpoints. Make sure to use this skill whenever the user mentions 'hf', 'huggingface', 'Hugging Face', 'huggingface-cli', or 'hugging face cli', or wants to do anything related to the Hugging Face ecosystem and to AI and ML in general. Also use for cloud storage needs like training checkpoints, data pipelines, or agent traces. Use even if the user doesn't explicitly ask for a CLI command. Replaces the deprecated `huggingface-cli`." +--- + +Install: `curl -LsSf https://hf.co/cli/install.sh | bash -s`. + +The Hugging Face Hub CLI tool `hf` is available. IMPORTANT: The `hf` command replaces the deprecated `huggingface-cli` command. + +Use `hf --help` to view available functions. Note that auth commands are now all under `hf auth` e.g. `hf auth whoami`. + +Generated with `huggingface_hub v1.12.0`. Run `hf skills add --force` to regenerate. + +## Commands + +- `hf download REPO_ID` — Download files from the Hub. `[--type CHOICE --revision TEXT --include TEXT --exclude TEXT --cache-dir TEXT --local-dir TEXT --force-download --dry-run --max-workers INTEGER --format CHOICE]` +- `hf env` — Print information about the environment. +- `hf sync` — Sync files between local directory and a bucket. `[--delete --ignore-times --ignore-sizes --plan TEXT --apply TEXT --dry-run --include TEXT --exclude TEXT --filter-from TEXT --existing --ignore-existing --verbose --quiet]` +- `hf upload REPO_ID` — Upload a file or a folder to the Hub. Recommended for single-commit uploads. `[--type CHOICE --revision TEXT --private --include TEXT --exclude TEXT --delete TEXT --commit-message TEXT --commit-description TEXT --create-pr --every FLOAT --format CHOICE]` +- `hf upload-large-folder REPO_ID LOCAL_PATH` — Upload a large folder to the Hub. Recommended for resumable uploads. `[--type CHOICE --revision TEXT --private --include TEXT --exclude TEXT --num-workers INTEGER --no-report --no-bars --format CHOICE]` +- `hf version` — Print information about the hf version. + +### `hf auth` — Manage authentication (login, logout, etc.). + +- `hf auth list` — List all stored access tokens. +- `hf auth login` — Login using a token from huggingface.co/settings/tokens. `[--add-to-git-credential --force]` +- `hf auth logout` — Logout from a specific token. `[--token-name TEXT]` +- `hf auth switch` — Switch between access tokens. `[--token-name TEXT --add-to-git-credential]` +- `hf auth token` — Print the current access token to stdout. +- `hf auth whoami` — Find out which huggingface.co account you are logged in as. `[--format CHOICE]` + +### `hf buckets` — Commands to interact with buckets. + +- `hf buckets cp SRC` — Copy files to or from buckets. `[--format CHOICE]` +- `hf buckets create BUCKET_ID` — Create a new bucket. `[--private --exist-ok --format CHOICE]` +- `hf buckets delete BUCKET_ID` — Delete a bucket. `[--yes --missing-ok --format CHOICE]` +- `hf buckets info BUCKET_ID` — Get info about a bucket. `[--format CHOICE]` +- `hf buckets list` — List buckets or files in a bucket. `[--human-readable --tree --recursive --search TEXT --format CHOICE]` +- `hf buckets move FROM_ID TO_ID` — Move (rename) a bucket to a new name or namespace. `[--format CHOICE]` +- `hf buckets remove ARGUMENT` — Remove files from a bucket. `[--recursive --yes --dry-run --include TEXT --exclude TEXT --format CHOICE]` +- `hf buckets sync` — Sync files between local directory and a bucket. `[--delete --ignore-times --ignore-sizes --plan TEXT --apply TEXT --dry-run --include TEXT --exclude TEXT --filter-from TEXT --existing --ignore-existing --verbose --quiet]` + +### `hf cache` — Manage local cache directory. + +- `hf cache list` — List cached repositories or revisions. `[--cache-dir TEXT --revisions --filter TEXT --format CHOICE --sort CHOICE --limit INTEGER]` +- `hf cache prune` — Remove detached revisions from the cache. `[--cache-dir TEXT --yes --dry-run --format CHOICE]` +- `hf cache rm TARGETS` — Remove cached repositories or revisions. `[--cache-dir TEXT --yes --dry-run --format CHOICE]` +- `hf cache verify REPO_ID` — Verify checksums for a single repo revision from cache or a local directory. `[--type CHOICE --revision TEXT --cache-dir TEXT --local-dir TEXT --fail-on-missing-files --fail-on-extra-files --format CHOICE]` + +### `hf collections` — Interact with collections on the Hub. + +- `hf collections add-item COLLECTION_SLUG ITEM_ID ITEM_TYPE` — Add an item to a collection. `[--note TEXT --exists-ok --format CHOICE]` +- `hf collections create TITLE` — Create a new collection on the Hub. `[--namespace TEXT --description TEXT --private --exists-ok --format CHOICE]` +- `hf collections delete COLLECTION_SLUG` — Delete a collection from the Hub. `[--missing-ok --format CHOICE]` +- `hf collections delete-item COLLECTION_SLUG ITEM_OBJECT_ID` — Delete an item from a collection. `[--missing-ok --format CHOICE]` +- `hf collections info COLLECTION_SLUG` — Get info about a collection on the Hub. `[--format CHOICE]` +- `hf collections list` — List collections on the Hub. `[--owner TEXT --item TEXT --sort CHOICE --limit INTEGER --format CHOICE]` +- `hf collections update COLLECTION_SLUG` — Update a collection's metadata on the Hub. `[--title TEXT --description TEXT --position INTEGER --private --theme TEXT --format CHOICE]` +- `hf collections update-item COLLECTION_SLUG ITEM_OBJECT_ID` — Update an item in a collection. `[--note TEXT --position INTEGER --format CHOICE]` + +### `hf datasets` — Interact with datasets on the Hub. + +- `hf datasets info DATASET_ID` — Get info about a dataset on the Hub. `[--revision TEXT --expand TEXT --format CHOICE]` +- `hf datasets list` — List datasets on the Hub. `[--search TEXT --author TEXT --filter TEXT --sort CHOICE --limit INTEGER --expand TEXT --format CHOICE]` +- `hf datasets parquet DATASET_ID` — List parquet file URLs available for a dataset. `[--subset TEXT --split TEXT --format CHOICE]` +- `hf datasets sql SQL` — Execute a raw SQL query with DuckDB against dataset parquet URLs. `[--format CHOICE]` + +### `hf discussions` — Manage discussions and pull requests on the Hub. + +- `hf discussions close REPO_ID NUM` — Close a discussion or pull request. `[--comment TEXT --yes --type CHOICE --format CHOICE]` +- `hf discussions comment REPO_ID NUM` — Comment on a discussion or pull request. `[--body TEXT --body-file PATH --type CHOICE --format CHOICE]` +- `hf discussions create REPO_ID --title TEXT` — Create a new discussion or pull request on a repo. `[--body TEXT --body-file PATH --pull-request --type CHOICE --format CHOICE]` +- `hf discussions diff REPO_ID NUM` — Show the diff of a pull request. `[--type CHOICE --format CHOICE]` +- `hf discussions info REPO_ID NUM` — Get info about a discussion or pull request. `[--type CHOICE --format CHOICE]` +- `hf discussions list REPO_ID` — List discussions and pull requests on a repo. `[--status CHOICE --kind CHOICE --author TEXT --limit INTEGER --type CHOICE --format CHOICE]` +- `hf discussions merge REPO_ID NUM` — Merge a pull request. `[--comment TEXT --yes --type CHOICE --format CHOICE]` +- `hf discussions rename REPO_ID NUM NEW_TITLE` — Rename a discussion or pull request. `[--type CHOICE --format CHOICE]` +- `hf discussions reopen REPO_ID NUM` — Reopen a closed discussion or pull request. `[--comment TEXT --yes --type CHOICE --format CHOICE]` + +### `hf endpoints` — Manage Hugging Face Inference Endpoints. + +- `hf endpoints catalog deploy --repo TEXT` — Deploy an Inference Endpoint from the Model Catalog. `[--name TEXT --accelerator TEXT --namespace TEXT --format CHOICE]` +- `hf endpoints catalog list` — List available Catalog models. `[--format CHOICE]` +- `hf endpoints delete NAME` — Delete an Inference Endpoint permanently. `[--namespace TEXT --yes --format CHOICE]` +- `hf endpoints deploy NAME --repo TEXT --framework TEXT --accelerator TEXT --instance-size TEXT --instance-type TEXT --region TEXT --vendor TEXT` — Deploy an Inference Endpoint from a Hub repository. `[--namespace TEXT --task TEXT --format CHOICE --min-replica INTEGER --max-replica INTEGER --scale-to-zero-timeout INTEGER --scaling-metric CHOICE --scaling-threshold FLOAT]` +- `hf endpoints describe NAME` — Get information about an existing endpoint. `[--namespace TEXT --format CHOICE]` +- `hf endpoints list` — Lists all Inference Endpoints for the given namespace. `[--namespace TEXT --format CHOICE]` +- `hf endpoints pause NAME` — Pause an Inference Endpoint. `[--namespace TEXT --format CHOICE]` +- `hf endpoints resume NAME` — Resume an Inference Endpoint. `[--namespace TEXT --fail-if-already-running --format CHOICE]` +- `hf endpoints scale-to-zero NAME` — Scale an Inference Endpoint to zero. `[--namespace TEXT --format CHOICE]` +- `hf endpoints update NAME` — Update an existing endpoint. `[--namespace TEXT --repo TEXT --accelerator TEXT --instance-size TEXT --instance-type TEXT --framework TEXT --revision TEXT --task TEXT --min-replica INTEGER --max-replica INTEGER --scale-to-zero-timeout INTEGER --scaling-metric CHOICE --scaling-threshold FLOAT --format CHOICE]` + +### `hf extensions` — Manage hf CLI extensions. + +- `hf extensions exec NAME` — Execute an installed extension. +- `hf extensions install REPO_ID` — Install an extension from a public GitHub repository. `[--force]` +- `hf extensions list` — List installed extension commands. `[--format CHOICE]` +- `hf extensions remove NAME` — Remove an installed extension. +- `hf extensions search` — Search extensions available on GitHub (tagged with 'hf-extension' topic). `[--format CHOICE]` + +### `hf jobs` — Run and manage Jobs on the Hub. + +- `hf jobs cancel JOB_ID` — Cancel a Job `[--namespace TEXT]` +- `hf jobs hardware` — List available hardware options for Jobs +- `hf jobs inspect JOB_IDS` — Display detailed information on one or more Jobs `[--namespace TEXT]` +- `hf jobs logs JOB_ID` — Fetch the logs of a Job. `[--follow --tail INTEGER --namespace TEXT]` +- `hf jobs ps` — List Jobs. `[--all --namespace TEXT --filter TEXT --format TEXT --quiet]` +- `hf jobs run IMAGE COMMAND` — Run a Job. `[--env TEXT --secrets TEXT --label TEXT --volume TEXT --env-file TEXT --secrets-file TEXT --flavor CHOICE --timeout TEXT --detach --namespace TEXT]` +- `hf jobs scheduled delete SCHEDULED_JOB_ID` — Delete a scheduled Job. `[--namespace TEXT]` +- `hf jobs scheduled inspect SCHEDULED_JOB_IDS` — Display detailed information on one or more scheduled Jobs `[--namespace TEXT]` +- `hf jobs scheduled ps` — List scheduled Jobs `[--all --namespace TEXT --filter TEXT --format TEXT --quiet]` +- `hf jobs scheduled resume SCHEDULED_JOB_ID` — Resume (unpause) a scheduled Job. `[--namespace TEXT]` +- `hf jobs scheduled run SCHEDULE IMAGE COMMAND` — Schedule a Job. `[--suspend --concurrency --env TEXT --secrets TEXT --label TEXT --volume TEXT --env-file TEXT --secrets-file TEXT --flavor CHOICE --timeout TEXT --namespace TEXT]` +- `hf jobs scheduled suspend SCHEDULED_JOB_ID` — Suspend (pause) a scheduled Job. `[--namespace TEXT]` +- `hf jobs scheduled uv run SCHEDULE SCRIPT` — Run a UV script (local file or URL) on HF infrastructure `[--suspend --concurrency --image TEXT --flavor CHOICE --env TEXT --secrets TEXT --label TEXT --volume TEXT --env-file TEXT --secrets-file TEXT --timeout TEXT --namespace TEXT --with TEXT --python TEXT]` +- `hf jobs stats` — Fetch the resource usage statistics and metrics of Jobs `[--namespace TEXT]` +- `hf jobs uv run SCRIPT` — Run a UV script (local file or URL) on HF infrastructure `[--image TEXT --flavor CHOICE --env TEXT --secrets TEXT --label TEXT --volume TEXT --env-file TEXT --secrets-file TEXT --timeout TEXT --detach --namespace TEXT --with TEXT --python TEXT]` + +### `hf models` — Interact with models on the Hub. + +- `hf models info MODEL_ID` — Get info about a model on the Hub. `[--revision TEXT --expand TEXT --format CHOICE]` +- `hf models list` — List models on the Hub. `[--search TEXT --author TEXT --filter TEXT --num-parameters TEXT --sort CHOICE --limit INTEGER --expand TEXT --format CHOICE]` + +### `hf papers` — Interact with papers on the Hub. + +- `hf papers info PAPER_ID` — Get info about a paper on the Hub. `[--format CHOICE]` +- `hf papers list` — List daily papers on the Hub. `[--date TEXT --week TEXT --month TEXT --submitter TEXT --sort CHOICE --limit INTEGER --format CHOICE]` +- `hf papers read PAPER_ID` — Read a paper as markdown. +- `hf papers search QUERY` — Search papers on the Hub. `[--limit INTEGER --format CHOICE]` + +### `hf repos` — Manage repos on the Hub. + +- `hf repos branch create REPO_ID BRANCH` — Create a new branch for a repo on the Hub. `[--revision TEXT --type CHOICE --exist-ok --format CHOICE]` +- `hf repos branch delete REPO_ID BRANCH` — Delete a branch from a repo on the Hub. `[--type CHOICE --format CHOICE]` +- `hf repos create REPO_ID` — Create a new repo on the Hub. `[--type CHOICE --space-sdk TEXT --private --public --protected --exist-ok --resource-group-id TEXT --flavor CHOICE --storage CHOICE --sleep-time INTEGER --secrets TEXT --secrets-file TEXT --env TEXT --env-file TEXT --volume TEXT --format CHOICE]` +- `hf repos delete REPO_ID` — Delete a repo from the Hub. This is an irreversible operation. `[--type CHOICE --missing-ok --yes --format CHOICE]` +- `hf repos delete-files REPO_ID PATTERNS` — Delete files from a repo on the Hub. `[--type CHOICE --revision TEXT --commit-message TEXT --commit-description TEXT --create-pr --format CHOICE]` +- `hf repos duplicate FROM_ID` — Duplicate a repo on the Hub (model, dataset, or Space). `[--type CHOICE --private --public --protected --exist-ok --flavor CHOICE --storage CHOICE --sleep-time INTEGER --secrets TEXT --secrets-file TEXT --env TEXT --env-file TEXT --volume TEXT --format CHOICE]` +- `hf repos move FROM_ID TO_ID` — Move a repository from a namespace to another namespace. `[--type CHOICE --format CHOICE]` +- `hf repos settings REPO_ID` — Update the settings of a repository. `[--gated CHOICE --private --public --protected --type CHOICE --format CHOICE]` +- `hf repos tag create REPO_ID TAG` — Create a tag for a repo. `[--message TEXT --revision TEXT --type CHOICE --format CHOICE]` +- `hf repos tag delete REPO_ID TAG` — Delete a tag for a repo. `[--yes --type CHOICE --format CHOICE]` +- `hf repos tag list REPO_ID` — List tags for a repo. `[--type CHOICE --format CHOICE]` + +### `hf skills` — Manage skills for AI assistants. + +- `hf skills add` — Download a Hugging Face skill and install it for an AI assistant. `[--claude --global --dest PATH --force]` +- `hf skills preview` — Print the generated `hf-cli` SKILL.md to stdout. +- `hf skills upgrade` — Upgrade installed Hugging Face marketplace skills. `[--claude --global --dest PATH]` + +### `hf spaces` — Interact with spaces on the Hub. + +- `hf spaces dev-mode SPACE_ID` — Enable or disable dev mode on a Space. `[--stop]` +- `hf spaces hot-reload SPACE_ID` — Hot-reload any Python file of a Space without a full rebuild + restart. `[--local-file PATH --skip-checks --skip-summary]` +- `hf spaces info SPACE_ID` — Get info about a space on the Hub. `[--revision TEXT --expand TEXT --format CHOICE]` +- `hf spaces list` — List spaces on the Hub. `[--search TEXT --author TEXT --filter TEXT --sort CHOICE --limit INTEGER --expand TEXT --format CHOICE]` +- `hf spaces logs SPACE_ID` — Fetch the run or build logs of a Space. `[--build --follow --tail INTEGER]` +- `hf spaces search QUERY` — Search spaces on the Hub using semantic search. `[--filter TEXT --sdk TEXT --include-non-running --description --limit INTEGER --format CHOICE]` +- `hf spaces volumes delete SPACE_ID` — Remove all volumes from a Space. `[--yes --format CHOICE]` +- `hf spaces volumes list SPACE_ID` — List volumes mounted in a Space. `[--format CHOICE]` +- `hf spaces volumes set SPACE_ID` — Set (replace) volumes for a Space. `[--volume TEXT --format CHOICE]` + +### `hf webhooks` — Manage webhooks on the Hub. + +- `hf webhooks create --watch TEXT` — Create a new webhook. `[--url TEXT --job-id TEXT --domain CHOICE --secret TEXT --format CHOICE]` +- `hf webhooks delete WEBHOOK_ID` — Delete a webhook permanently. `[--yes --format CHOICE]` +- `hf webhooks disable WEBHOOK_ID` — Disable an active webhook. `[--format CHOICE]` +- `hf webhooks enable WEBHOOK_ID` — Enable a disabled webhook. `[--format CHOICE]` +- `hf webhooks info WEBHOOK_ID` — Show full details for a single webhook. `[--format CHOICE]` +- `hf webhooks list` — List all webhooks for the current user. `[--format CHOICE]` +- `hf webhooks update WEBHOOK_ID` — Update an existing webhook. Only provided options are changed. `[--url TEXT --watch TEXT --domain CHOICE --secret TEXT --format CHOICE]` + +## Common options + +- `--format` — Output format: `--format json` (or `--json`) or `--format table` (default). +- `-q / --quiet` — Minimal output. +- `--revision` — Git revision id which can be a branch name, a tag, or a commit hash. +- `--token` — Use a User Access Token. Prefer setting `HF_TOKEN` env var instead of passing `--token`. +- `--type` — The type of repository (model, dataset, or space). + +## Mounting repos as local filesystems + +To mount Hub repositories or buckets as local filesystems — no download, no copy, no waiting — use `hf-mount`. Files are fetched on demand. GitHub: https://github.com/huggingface/hf-mount + +Install: `curl -fsSL https://raw.githubusercontent.com/huggingface/hf-mount/main/install.sh | sh` + +Some command examples: +- `hf-mount start repo openai-community/gpt2 /tmp/gpt2` — mount a repo (read-only) +- `hf-mount start --hf-token $HF_TOKEN bucket myuser/my-bucket /tmp/data` — mount a bucket (read-write) +- `hf-mount status` / `hf-mount stop /tmp/data` — list or unmount + +## Tips + +- Use `hf --help` for full options, descriptions, usage, and real-world examples +- Authenticate with `HF_TOKEN` env var (recommended) or with `--token` diff --git a/.claude/skills/hf-cli b/.claude/skills/hf-cli new file mode 120000 index 0000000000000000000000000000000000000000..13ad81f0393bb7f71bf11fe2716eaf2d9a125266 --- /dev/null +++ b/.claude/skills/hf-cli @@ -0,0 +1 @@ +../../.agents/skills/hf-cli \ No newline at end of file diff --git a/.claude/skills/huggingface-doc/SKILL.md b/.claude/skills/huggingface-doc/SKILL.md new file mode 100644 index 0000000000000000000000000000000000000000..33266d536678165b1d363da793918c16f585768c --- /dev/null +++ b/.claude/skills/huggingface-doc/SKILL.md @@ -0,0 +1,6 @@ +--- +name: Hugging Face Doc +description: Use Hugging Face Docs API to efficiently search for huggingface related information +--- + +⁠Use api https://huggingface.co/api/docs/search?q={query} to find huggingface related information diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..f6b1f326ca4ab7cf0c8798856f8fe0020ff82d58 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,36 @@ +*.7z filter=lfs diff=lfs merge=lfs -text +*.arrow filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.ckpt filter=lfs diff=lfs merge=lfs -text +*.ftz filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.h5 filter=lfs diff=lfs merge=lfs -text +*.joblib filter=lfs diff=lfs merge=lfs -text +*.lfs.* filter=lfs diff=lfs merge=lfs -text +*.mlmodel filter=lfs diff=lfs merge=lfs -text +*.model filter=lfs diff=lfs merge=lfs -text +*.msgpack filter=lfs diff=lfs merge=lfs -text +*.npy filter=lfs diff=lfs merge=lfs -text +*.npz filter=lfs diff=lfs merge=lfs -text +*.onnx filter=lfs diff=lfs merge=lfs -text +*.ot filter=lfs diff=lfs merge=lfs -text +*.parquet filter=lfs diff=lfs merge=lfs -text +*.pb filter=lfs diff=lfs merge=lfs -text +*.pickle filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.rar filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +saved_model/**/* filter=lfs diff=lfs merge=lfs -text +*.tar.* filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.tflite filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.wasm filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text +*tfevents* filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7f8d633cbbb0bf20637f951118fe0b96f2d6eaa4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build +/dist + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000000000000000000000000000000000..b6f27f135954640c8cc5bfd7b8c9922ca6eb2aad --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000000000000000000000000000000000..54490174495e3085214433b1480a43277cee725e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "svelte.svelte-vscode", + "bradlc.vscode-tailwindcss" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..bc31e1553d61aaa17e46cb2eb661d5ca01373690 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.css": "tailwindcss" + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bc4e5c98ea6d9fdb918b60abc2e07b4b3c22c173 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +--- +title: Counter Strike 2 Dataset +emoji: 🔥 +colorFrom: pink +colorTo: gray +sdk: static +pinned: false +app_build_command: npm run build +app_file: dist/index.html +license: mit +short_description: Counter Strike 2 Dataset +--- + +# CS2 Render Dataset Viewer + +Browse Counter-Strike 2 esports matches with synchronized 10-perspective POV +videos, sourced from the [`blanchon/cs2_dataset_render`](https://huggingface.co/datasets/blanchon/cs2_dataset_render) +Hugging Face dataset. + +Built with SvelteKit (`@sveltejs/adapter-static`, SPA mode) — all data is +fetched client-side from the dataset's parquet files via `parquet-wasm`. + +## Develop + +```sh +pnpm install +pnpm dev +``` + +## Build + +```sh +pnpm build # → dist/ +pnpm preview # serve dist/ locally +``` + +The Space's build command (`npm run build`, configured in the frontmatter) +runs the same Vite build. Output is `dist/index.html` plus an `_app` chunk +directory and the `static/maps/` radar assets. + +Configuration reference: diff --git a/components.json b/components.json new file mode 100644 index 0000000000000000000000000000000000000000..462724ad6dcbfa94f70899ae17e6a9ce0eca5211 --- /dev/null +++ b/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "tailwind": { + "css": "src/routes/layout.css", + "baseColor": "taupe" + }, + "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": "mira", + "iconLibrary": "phosphor", + "menuColor": "default", + "menuAccent": "subtle" +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..bbaef50a101115e99b0deb7dede9ae291cb7c474 --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "app", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@fontsource-variable/oxanium": "^5.2.8", + "@fontsource-variable/roboto-slab": "^5.2.8", + "@internationalized/date": "^3.12.1", + "@sveltejs/adapter-static": "latest", + "@sveltejs/kit": "latest", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "@tailwindcss/vite": "^4.2.2", + "@tanstack/table-core": "^8.21.3", + "bits-ui": "^2.18.0", + "clsx": "^2.1.1", + "formsnap": "^2.0.1", + "layerchart": "2.0.0-next.48", + "mode-watcher": "^1.1.0", + "paneforge": "^1.0.2", + "phosphor-svelte": "^3.1.0", + "shadcn-svelte": "^1.2.7", + "svelte": "^5.55.2", + "svelte-check": "^4.4.6", + "svelte-sonner": "^1.1.1", + "sveltekit-superforms": "^2.30.1", + "tailwind-merge": "^3.5.0", + "tailwind-variants": "^3.2.2", + "tailwindcss": "^4.2.2", + "tw-animate-css": "^1.4.0", + "typescript": "^6.0.2", + "vaul-svelte": "1.0.0-next.7", + "vite": "^8.0.7" + }, + "dependencies": { + "apache-arrow": "^21.1.0", + "mediabunny": "^1.42.0", + "parquet-wasm": "^0.7.1" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ab52c5bde7eb50fbd9371303ee35ddb57a11cdc1 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2444 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + apache-arrow: + specifier: ^21.1.0 + version: 21.1.0 + mediabunny: + specifier: ^1.42.0 + version: 1.42.0 + parquet-wasm: + specifier: ^0.7.1 + version: 0.7.1 + devDependencies: + '@fontsource-variable/oxanium': + specifier: ^5.2.8 + version: 5.2.8 + '@fontsource-variable/roboto-slab': + specifier: ^5.2.8 + version: 5.2.8 + '@internationalized/date': + specifier: ^3.12.1 + version: 3.12.1 + '@sveltejs/adapter-static': + specifier: latest + version: 3.0.10(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1))) + '@sveltejs/kit': + specifier: latest + version: 2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + '@sveltejs/vite-plugin-svelte': + specifier: ^7.0.0 + version: 7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + '@tailwindcss/vite': + specifier: ^4.2.2 + version: 4.2.4(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + '@tanstack/table-core': + specifier: ^8.21.3 + version: 8.21.3 + bits-ui: + specifier: ^2.18.0 + version: 2.18.0(@internationalized/date@3.12.1)(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5) + clsx: + specifier: ^2.1.1 + version: 2.1.1 + formsnap: + specifier: ^2.0.1 + version: 2.0.1(svelte@5.55.5)(sveltekit-superforms@2.30.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)) + layerchart: + specifier: 2.0.0-next.48 + version: 2.0.0-next.48(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(zod@4.3.6) + mode-watcher: + specifier: ^1.1.0 + version: 1.1.0(svelte@5.55.5) + paneforge: + specifier: ^1.0.2 + version: 1.0.2(svelte@5.55.5) + phosphor-svelte: + specifier: ^3.1.0 + version: 3.1.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + shadcn-svelte: + specifier: ^1.2.7 + version: 1.2.7(svelte@5.55.5) + svelte: + specifier: ^5.55.2 + version: 5.55.5 + svelte-check: + specifier: ^4.4.6 + version: 4.4.6(picomatch@4.0.4)(svelte@5.55.5)(typescript@6.0.3) + svelte-sonner: + specifier: ^1.1.1 + version: 1.1.1(svelte@5.55.5) + sveltekit-superforms: + specifier: ^2.30.1 + version: 2.30.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3) + tailwind-merge: + specifier: ^3.5.0 + version: 3.5.0 + tailwind-variants: + specifier: ^3.2.2 + version: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.4) + tailwindcss: + specifier: ^4.2.2 + version: 4.2.4 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + typescript: + specifier: ^6.0.2 + version: 6.0.3 + vaul-svelte: + specifier: 1.0.0-next.7 + version: 1.0.0-next.7(svelte@5.55.5) + vite: + specifier: ^8.0.7 + version: 8.0.10(@types/node@24.12.2)(jiti@2.6.1) + +packages: + + '@ark/schema@0.56.0': + resolution: {integrity: sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==} + + '@ark/util@0.56.0': + resolution: {integrity: sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==} + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@dagrejs/dagre@2.0.4': + resolution: {integrity: sha512-J6vCWTNpicHF4zFlZG1cS5DkGzMr9941gddYkakjrg3ZNev4bbqEgLHFTWiFrcJm7UCRu7olO3K6IRDd9gSGhA==} + + '@dagrejs/graphlib@3.0.4': + resolution: {integrity: sha512-HxZ7fCvAwTLCWCO0WjDkzAFQze8LdC6iOpKbetDKHIuDfIgMlIzYzqZ4nxwLlclQX+3ZVeZ1K2OuaOE2WWcyOg==} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@exodus/schemasafe@1.3.0': + resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==} + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@fontsource-variable/oxanium@5.2.8': + resolution: {integrity: sha512-W3HWxRLXVB6yox3dgm1DIGOp98pz8KglwiM6/2BsMopvZrg98mXI9citFWz2pUX0FOOLwf8fmHxX+KrIn+6BoA==} + + '@fontsource-variable/roboto-slab@5.2.8': + resolution: {integrity: sha512-cdvOSwocP50xS01gXqnGxw7uFcCZQgn1IDlqiAmNuZP4uEVScYAKMDC+aWsbP1grn9QdMJ2frhcuVBNXniBVGA==} + + '@hapi/hoek@9.3.0': + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + + '@hapi/topo@5.1.0': + resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + + '@internationalized/date@3.12.1': + resolution: {integrity: sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==} + + '@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==} + + '@layerstack/svelte-actions@1.0.1-next.18': + resolution: {integrity: sha512-gxPzCnJ1c9LTfWtRqLUzefCx+k59ZpxDUQ2XB+LokveZQPe7IDSOwHaBOEMlaGoGrtwc3Ft8dSZq+2WT2o9u/g==} + + '@layerstack/svelte-state@0.1.0-next.23': + resolution: {integrity: sha512-7O4umv+gXwFfs3/vjzFWYHNXGwYnnjBapWJ5Y+9u99F4eVk6rh4ocNwqkqQNkpMZ5tUJBlRTWjPE1So6+hEzIg==} + + '@layerstack/tailwind@2.0.0-next.21': + resolution: {integrity: sha512-Qgp2EpmEHmjtura8MQzWicR6ztBRSsRvddakFtx9ShrLMz6jWzd6bCMVVRu44Q3ZOrtXmSu4QxjCZWu1ytvuPg==} + + '@layerstack/utils@2.0.0-next.18': + resolution: {integrity: sha512-EYILHpfBRYMMEahajInu9C2AXQom5IcAEdtCeucD3QIl/fdDgRbtzn6/8QW9ewumfyNZetdUvitOksmI1+gZYQ==} + + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oxc-project/types@0.127.0': + resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@poppinss/macroable@1.1.2': + resolution: {integrity: sha512-FAVBRzzWhYP5mA3lCwLH1A0fKBqq5anyjGet90Z81aRK5c/+LTGUE1zJhZrErjaenBSOOI9BVUs3WVmotneFQA==} + + '@rolldown/binding-android-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.17': + resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-rc.17': + resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} + + '@sideway/address@4.1.5': + resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} + + '@sideway/formula@3.0.1': + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + + '@sideway/pinpoint@2.0.0': + resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@sveltejs/acorn-typescript@1.0.9': + resolution: {integrity: sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-static@3.0.10': + resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/kit@2.58.0': + resolution: {integrity: sha512-kT9GCN8yJTkCK1W+Gi/bvGooWAM7y7WXP+yd+rf6QOIjyoK1ERPrMwSufXJUNu2pMWIqruhFvmz+LbOqsEmKmA==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.3.3 || ^6.0.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + typescript: + optional: true + + '@sveltejs/vite-plugin-svelte@7.0.0': + resolution: {integrity: sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.46.4 + vite: ^8.0.0-beta.7 || ^8.0.0 + + '@swc/helpers@0.5.21': + resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==} + + '@tailwindcss/node@4.2.4': + resolution: {integrity: sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==} + + '@tailwindcss/oxide-android-arm64@4.2.4': + resolution: {integrity: sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.2.4': + resolution: {integrity: sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.2.4': + resolution: {integrity: sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.2.4': + resolution: {integrity: sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4': + resolution: {integrity: sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.4': + resolution: {integrity: sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.2.4': + resolution: {integrity: sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.2.4': + resolution: {integrity: sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.2.4': + resolution: {integrity: sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.2.4': + resolution: {integrity: sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==} + 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.2.4': + resolution: {integrity: sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.2.4': + resolution: {integrity: sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.2.4': + resolution: {integrity: sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.2.4': + resolution: {integrity: sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tanstack/table-core@8.21.3': + resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} + engines: {node: '>=12'} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/command-line-args@5.2.3': + resolution: {integrity: sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==} + + '@types/command-line-usage@5.0.4': + resolution: {integrity: sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/dom-mediacapture-transform@0.1.11': + resolution: {integrity: sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ==} + + '@types/dom-webcodecs@0.1.13': + resolution: {integrity: sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/node@24.12.2': + resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/validator@13.15.10': + resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} + + '@typeschema/class-validator@0.3.0': + resolution: {integrity: sha512-OJSFeZDIQ8EK1HTljKLT5CItM2wsbgczLN8tMEfz3I1Lmhc5TBfkZ0eikFzUC16tI3d1Nag7um6TfCgp2I2Bww==} + peerDependencies: + class-validator: ^0.14.1 + peerDependenciesMeta: + class-validator: + optional: true + + '@typeschema/core@0.14.0': + resolution: {integrity: sha512-Ia6PtZHcL3KqsAWXjMi5xIyZ7XMH4aSnOQes8mfMLx+wGFGtGRNlwe6Y7cYvX+WfNK67OL0/HSe9t8QDygV0/w==} + peerDependencies: + '@types/json-schema': ^7.0.15 + peerDependenciesMeta: + '@types/json-schema': + optional: true + + '@valibot/to-json-schema@1.6.0': + resolution: {integrity: sha512-d6rYyK5KVa2XdqamWgZ4/Nr+cXhxjy7lmpe6Iajw15J/jmU+gyxl2IEd1Otg1d7Rl3gOQL5reulnSypzBtYy1A==} + peerDependencies: + valibot: ^1.3.0 + + '@vinejs/compiler@3.0.0': + resolution: {integrity: sha512-v9Lsv59nR56+bmy2p0+czjZxsLHwaibJ+SV5iK9JJfehlJMa501jUJQqqz4X/OqKXrxtE3uTQmSqjUqzF3B2mw==} + engines: {node: '>=18.0.0'} + + '@vinejs/vine@3.0.1': + resolution: {integrity: sha512-ZtvYkYpZOYdvbws3uaOAvTFuvFXoQGAtmzeiXu+XSMGxi5GVsODpoI9Xu9TplEMuD/5fmAtBbKb9cQHkWkLXDQ==} + engines: {node: '>=18.16.0'} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + apache-arrow@21.1.0: + resolution: {integrity: sha512-kQrYLxhC+NTVVZ4CCzGF6L/uPVOzJmD1T3XgbiUnP7oTeVFOFgEUu6IKNwCDkpFoBVqDKQivlX4RUFqqnWFlEA==} + hasBin: true + + aria-query@5.3.1: + resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} + engines: {node: '>= 0.4'} + + arkregex@0.0.5: + resolution: {integrity: sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==} + + arktype@2.2.0: + resolution: {integrity: sha512-t54MZ7ti5BhOEvzEkgKnWvqj+UbDfWig+DHr5I34xatymPusKLS0lQpNJd8M6DzmIto2QGszHfNKoFIT8tMCZQ==} + + array-back@6.2.3: + resolution: {integrity: sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==} + engines: {node: '>=12.17'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + bits-ui@2.18.0: + resolution: {integrity: sha512-GLOBZRVy3hxNHIQ2MpD/+5aK9KcBFZRhUJtZ1UDABXdlVR4K6zFpgt4T+Rwuhf2sQzlc6yK1q/DprHPjwT4Pjw==} + engines: {node: '>=20'} + peerDependencies: + '@internationalized/date': ^3.8.1 + svelte: ^5.33.0 + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + chalk-template@0.4.0: + resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} + engines: {node: '>=12'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + class-validator@0.14.4: + resolution: {integrity: sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + command-line-args@6.0.2: + resolution: {integrity: sha512-AIjYVxrV9X752LmPDLbVYv8aMCuHPSLZJXEo2qo/xJfv+NYhaZ4sMSF01rM+gHPaMgvPM0l5D/F+Qx+i2WfSmQ==} + engines: {node: '>=12.20'} + peerDependencies: + '@75lb/nature': latest + peerDependenciesMeta: + '@75lb/nature': + optional: true + + command-line-usage@7.0.4: + resolution: {integrity: sha512-85UdvzTNx/+s5CkSgBm/0hzP80RFHAa7PsfeADE5ezZF3uHz3/Tqj9gIKGT9PTtpycc3Ua64T0oVulGfKxzfqg==} + engines: {node: '>=12.20.0'} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo-voronoi@2.1.0: + resolution: {integrity: sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate-path@2.3.0: + resolution: {integrity: sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ==} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-tile@1.0.0: + resolution: {integrity: sha512-79fnTKpPMPDS5xQ0xuS9ir0165NEwwkFpe/DSOmc2Gl9ldYzKKRDWogmTTE8wAJ8NA7PMapNfEcyKhI9Lxdu5Q==} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-tricontour@1.1.0: + resolution: {integrity: sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ==} + engines: {node: '>=12'} + + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + delaunator@5.1.0: + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devalue@5.7.1: + resolution: {integrity: sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + effect@3.21.2: + resolution: {integrity: sha512-rXd2FGDM8KdjSIrc+mqEELo7ScW7xTVxEf1iInmPSpIde9/nyGuFM710cjTo7/EreGXiUX2MOonPpprbz2XHCg==} + + enhanced-resolve@5.21.0: + resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==} + engines: {node: '>=10.13.0'} + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + esrap@2.2.5: + resolution: {integrity: sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig==} + peerDependencies: + '@typescript-eslint/types': ^8.2.0 + peerDependenciesMeta: + '@typescript-eslint/types': + optional: true + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + find-replace@5.0.2: + resolution: {integrity: sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==} + engines: {node: '>=14'} + peerDependencies: + '@75lb/nature': latest + peerDependenciesMeta: + '@75lb/nature': + optional: true + + flatbuffers@25.9.23: + resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} + + formsnap@2.0.1: + resolution: {integrity: sha512-iJSe4YKd/W6WhLwKDVJU9FQeaJRpEFuolhju7ZXlRpUVyDdqFdMP8AUBICgnVvQPyP41IPAlBa/v0Eo35iE6wQ==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.0.0 + sveltekit-superforms: ^2.19.0 + + 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==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + joi@17.13.3: + resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} + + json-bignum@0.0.3: + resolution: {integrity: sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==} + engines: {node: '>=0.8'} + + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + layerchart@2.0.0-next.48: + resolution: {integrity: sha512-XoEYBztamA8lMxtF/Jz3aDX0HMk8dI+o4fK9fSl8ecT2Tdx3DQUjtKGtlQAOFdwC/AWifeLmKq5cMTQt9COZPQ==} + peerDependencies: + svelte: ^5.0.0 + + libphonenumber-js@1.12.42: + resolution: {integrity: sha512-oKQFPTibqQwZZkChCDVMFVJXMZdyJNqDWZWYNn8BgyAaK/6yFJEowxCY0RVFirRyWP63hMRuKlkSEd9qlvbWXg==} + + 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'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + mediabunny@1.42.0: + resolution: {integrity: sha512-s9ypTqLi6kbh95gC+YaJlG0PkLvMxu37Q/wO/pFZx0fUCA5Ym5mp+2dWoa83mKQ3Uo18aNlgev5iJ5ESZqWwgQ==} + + memoize-weak@1.0.2: + resolution: {integrity: sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==} + + memoize@10.2.0: + resolution: {integrity: sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==} + engines: {node: '>=18'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + mode-watcher@1.1.0: + resolution: {integrity: sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g==} + peerDependencies: + svelte: ^5.27.0 + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + normalize-url@8.1.1: + resolution: {integrity: sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==} + engines: {node: '>=14.16'} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + paneforge@1.0.2: + resolution: {integrity: sha512-KzmIXQH1wCfwZ4RsMohD/IUtEjVhteR+c+ulb/CHYJHX8SuDXoJmChtsc/Xs5Wl8NHS4L5Q7cxL8MG40gSU1bA==} + peerDependencies: + svelte: ^5.29.0 + + parquet-wasm@0.7.1: + resolution: {integrity: sha512-fjEGpMApzt3mpI2pUxdRgQGu5G+s4nr0vm5xn43JO7jxdYzzu2fHrVrTHtfeEhtB6vfvTzJBz0WydDYzLWvszQ==} + + phosphor-svelte@3.1.0: + resolution: {integrity: sha512-nldtxx+XCgNREvrb7O5xgDsefytXpSkPTx8Rnu3f2qQCUZLDV1rLxYSd2Jcwckuo9lZB1qKMqGR17P4UDC0PrA==} + peerDependencies: + svelte: ^5.0.0 || ^5.0.0-next.96 + vite: '>=5' + peerDependenciesMeta: + vite: + optional: true + + 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.12: + resolution: {integrity: sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==} + engines: {node: ^10 || ^12 || >=14} + + property-expr@2.0.6: + resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + + rolldown@1.0.0-rc.17: + resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + runed@0.23.4: + resolution: {integrity: sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==} + peerDependencies: + svelte: ^5.7.0 + + runed@0.25.0: + resolution: {integrity: sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg==} + peerDependencies: + svelte: ^5.7.0 + + runed@0.28.0: + resolution: {integrity: sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==} + peerDependencies: + svelte: ^5.7.0 + + runed@0.29.2: + resolution: {integrity: sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA==} + peerDependencies: + svelte: ^5.7.0 + + runed@0.35.1: + resolution: {integrity: sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==} + peerDependencies: + '@sveltejs/kit': ^2.21.0 + svelte: ^5.7.0 + peerDependenciesMeta: + '@sveltejs/kit': + optional: true + + runed@0.37.1: + resolution: {integrity: sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA==} + peerDependencies: + '@sveltejs/kit': ^2.21.0 + svelte: ^5.7.0 + zod: ^4.1.0 + peerDependenciesMeta: + '@sveltejs/kit': + optional: true + zod: + optional: true + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + set-cookie-parser@3.1.0: + resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} + + shadcn-svelte@1.2.7: + resolution: {integrity: sha512-mWuQk4H4gtV+J2wJQ7nEPKNnB/v86AALFryZU0SSM7ChHmJJMZ1kH+qIuxYKrXm9vOOOcSWHRsWzPDB71DnjYA==} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + superstruct@2.0.2: + resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} + engines: {node: '>=14.0.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + svelte-check@4.4.6: + resolution: {integrity: sha512-kP1zG81EWaFe9ZyTv4ZXv44Csi6Pkdpb7S3oj6m+K2ec/IcDg/a8LsFsnVLqm2nxtkSwsd5xPj/qFkTBgXHXjg==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte-sonner@1.1.1: + resolution: {integrity: sha512-5cd3p7wa4cq0NsqslMwdlPb7x1JglEZ/GKrLePWNr5bCxR1nagAVrY01FRFrXfUGs41miLt3C327+8XJo5BzZw==} + peerDependencies: + svelte: ^5.0.0 + + svelte-toolbelt@0.10.6: + resolution: {integrity: sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.30.2 + + svelte-toolbelt@0.5.0: + resolution: {integrity: sha512-t3tenZcnfQoIeRuQf/jBU7bvTeT3TGkcEE+1EUr5orp0lR7NEpprflpuie3x9Dn0W9nOKqs3HwKGJeeN5Ok1sQ==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.0.0-next.126 + + svelte-toolbelt@0.7.1: + resolution: {integrity: sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.0.0 + + svelte-toolbelt@0.9.3: + resolution: {integrity: sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.30.2 + + svelte@5.55.5: + resolution: {integrity: sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw==} + engines: {node: '>=18'} + + sveltekit-superforms@2.30.1: + resolution: {integrity: sha512-wBzyqsE0idvEJWuNJ+HCiAtdxa7Z55GZ8jmtlVHJfonrk9bRYC49MoPaloYyFoYuU3QPy6Omna/Qzn1kaIkgew==} + peerDependencies: + '@sveltejs/kit': 1.x || 2.x + svelte: 3.x || 4.x || >=5.0.0-next.51 + + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + + table-layout@4.1.1: + resolution: {integrity: sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==} + engines: {node: '>=12.17'} + + tailwind-merge@3.5.0: + resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} + + tailwind-variants@3.2.2: + resolution: {integrity: sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==} + engines: {node: '>=16.x', pnpm: '>=7.x'} + peerDependencies: + tailwind-merge: '>=3.0.0' + tailwindcss: '*' + peerDependenciesMeta: + tailwind-merge: + optional: true + + tailwindcss@4.2.4: + resolution: {integrity: sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tiny-case@1.0.3: + resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + + ts-deepmerge@7.0.3: + resolution: {integrity: sha512-Du/ZW2RfwV/D4cmA5rXafYjBQVuvu4qGiEEla4EmEHVHgRdx68Gftx7i66jn2bzHPwSVZY36Ae6OuDn9el4ZKA==} + engines: {node: '>=14.13.1'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + typebox@1.1.34: + resolution: {integrity: sha512-V0fM5W5DTXlEMDxqtX1dQ25HR1RQ11DPUVrIup4sJi1yQtIyI30SHfxBy/HjXKL1CtUqc5or2igA/wa/v4hMKQ==} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + typical@7.3.0: + resolution: {integrity: sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==} + engines: {node: '>=12.17'} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + valibot@1.3.1: + resolution: {integrity: sha512-sfdRir/QFM0JaF22hqTroPc5xy4DimuGQVKFrzF1YfGwaS1nJot3Y8VqMdLO2Lg27fMzat2yD3pY5PbAYO39Gg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + + validator@13.15.35: + resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} + engines: {node: '>= 0.10'} + + vaul-svelte@1.0.0-next.7: + resolution: {integrity: sha512-7zN7Bi3dFQixvvbUJY9uGDe7Ws/dGZeBQR2pXdXmzQiakjrxBvWo0QrmsX3HK+VH+SZOltz378cmgmCS9f9rSg==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.0.0 + + vite@8.0.10: + resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.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 + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + + wordwrapjs@5.1.1: + resolution: {integrity: sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==} + engines: {node: '>=12.17'} + + yup@1.7.1: + resolution: {integrity: sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==} + + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + + zod-v3-to-json-schema@4.0.0: + resolution: {integrity: sha512-KixLrhX/uPmRFnDgsZrzrk4x5SSJA+PmaE5adbfID9+3KPJcdxqRobaHU397EfWBqfQircrjKqvEqZ/mW5QH6w==} + peerDependencies: + zod: ^3.25 || ^4.0.14 + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + +snapshots: + + '@ark/schema@0.56.0': + dependencies: + '@ark/util': 0.56.0 + optional: true + + '@ark/util@0.56.0': + optional: true + + '@babel/runtime@7.29.2': + optional: true + + '@dagrejs/dagre@2.0.4': + dependencies: + '@dagrejs/graphlib': 3.0.4 + + '@dagrejs/graphlib@3.0.4': {} + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@exodus/schemasafe@1.3.0': + optional: true + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/utils@0.2.11': {} + + '@fontsource-variable/oxanium@5.2.8': {} + + '@fontsource-variable/roboto-slab@5.2.8': {} + + '@hapi/hoek@9.3.0': + optional: true + + '@hapi/topo@5.1.0': + dependencies: + '@hapi/hoek': 9.3.0 + optional: true + + '@internationalized/date@3.12.1': + dependencies: + '@swc/helpers': 0.5.21 + + '@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 + + '@layerstack/svelte-actions@1.0.1-next.18': + dependencies: + '@floating-ui/dom': 1.7.6 + '@layerstack/utils': 2.0.0-next.18 + d3-scale: 4.0.2 + + '@layerstack/svelte-state@0.1.0-next.23': + dependencies: + '@layerstack/utils': 2.0.0-next.18 + + '@layerstack/tailwind@2.0.0-next.21': + dependencies: + '@layerstack/utils': 2.0.0-next.18 + clsx: 2.1.1 + d3-array: 3.2.4 + tailwind-merge: 3.5.0 + + '@layerstack/utils@2.0.0-next.18': + dependencies: + d3-array: 3.2.4 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@oxc-project/types@0.127.0': {} + + '@polka/url@1.0.0-next.29': {} + + '@poppinss/macroable@1.1.2': + optional: true + + '@rolldown/binding-android-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + optional: true + + '@rolldown/pluginutils@1.0.0-rc.17': {} + + '@sideway/address@4.1.5': + dependencies: + '@hapi/hoek': 9.3.0 + optional: true + + '@sideway/formula@3.0.1': + optional: true + + '@sideway/pinpoint@2.0.0': + optional: true + + '@standard-schema/spec@1.1.0': {} + + '@sveltejs/acorn-typescript@1.0.9(acorn@8.16.0)': + dependencies: + acorn: 8.16.0 + + '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))': + dependencies: + '@sveltejs/kit': 2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + + '@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0) + '@sveltejs/vite-plugin-svelte': 7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + '@types/cookie': 0.6.0 + acorn: 8.16.0 + cookie: 0.6.0 + devalue: 5.7.1 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + set-cookie-parser: 3.1.0 + sirv: 3.0.2 + svelte: 5.55.5 + vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1) + optionalDependencies: + typescript: 6.0.3 + + '@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1))': + dependencies: + deepmerge: 4.3.1 + magic-string: 0.30.21 + obug: 2.1.1 + svelte: 5.55.5 + vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1) + vitefu: 1.1.3(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + + '@swc/helpers@0.5.21': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.2.4': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.0 + jiti: 2.6.1 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.2.4 + + '@tailwindcss/oxide-android-arm64@4.2.4': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.2.4': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.2.4': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.2.4': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.2.4': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.2.4': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.4': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.2.4': + optional: true + + '@tailwindcss/oxide@4.2.4': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.2.4 + '@tailwindcss/oxide-darwin-arm64': 4.2.4 + '@tailwindcss/oxide-darwin-x64': 4.2.4 + '@tailwindcss/oxide-freebsd-x64': 4.2.4 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.4 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.4 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.4 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.4 + '@tailwindcss/oxide-linux-x64-musl': 4.2.4 + '@tailwindcss/oxide-wasm32-wasi': 4.2.4 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.4 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.4 + + '@tailwindcss/vite@4.2.4(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1))': + dependencies: + '@tailwindcss/node': 4.2.4 + '@tailwindcss/oxide': 4.2.4 + tailwindcss: 4.2.4 + vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1) + + '@tanstack/table-core@8.21.3': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/command-line-args@5.2.3': {} + + '@types/command-line-usage@5.0.4': {} + + '@types/cookie@0.6.0': {} + + '@types/d3-array@3.2.2': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/dom-mediacapture-transform@0.1.11': + dependencies: + '@types/dom-webcodecs': 0.1.13 + + '@types/dom-webcodecs@0.1.13': {} + + '@types/estree@1.0.8': {} + + '@types/geojson@7946.0.16': {} + + '@types/node@24.12.2': + dependencies: + undici-types: 7.16.0 + + '@types/trusted-types@2.0.7': {} + + '@types/validator@13.15.10': + optional: true + + '@typeschema/class-validator@0.3.0(class-validator@0.14.4)': + dependencies: + '@typeschema/core': 0.14.0 + optionalDependencies: + class-validator: 0.14.4 + transitivePeerDependencies: + - '@types/json-schema' + optional: true + + '@typeschema/core@0.14.0': + optional: true + + '@valibot/to-json-schema@1.6.0(valibot@1.3.1(typescript@6.0.3))': + dependencies: + valibot: 1.3.1(typescript@6.0.3) + optional: true + + '@vinejs/compiler@3.0.0': + optional: true + + '@vinejs/vine@3.0.1': + dependencies: + '@poppinss/macroable': 1.1.2 + '@types/validator': 13.15.10 + '@vinejs/compiler': 3.0.0 + camelcase: 8.0.0 + dayjs: 1.11.20 + dlv: 1.1.3 + normalize-url: 8.1.1 + validator: 13.15.35 + optional: true + + acorn@8.16.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + apache-arrow@21.1.0: + dependencies: + '@swc/helpers': 0.5.21 + '@types/command-line-args': 5.2.3 + '@types/command-line-usage': 5.0.4 + '@types/node': 24.12.2 + command-line-args: 6.0.2 + command-line-usage: 7.0.4 + flatbuffers: 25.9.23 + json-bignum: 0.0.3 + tslib: 2.8.1 + transitivePeerDependencies: + - '@75lb/nature' + + aria-query@5.3.1: {} + + arkregex@0.0.5: + dependencies: + '@ark/util': 0.56.0 + optional: true + + arktype@2.2.0: + dependencies: + '@ark/schema': 0.56.0 + '@ark/util': 0.56.0 + arkregex: 0.0.5 + optional: true + + array-back@6.2.3: {} + + axobject-query@4.1.0: {} + + bits-ui@2.18.0(@internationalized/date@3.12.1)(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5): + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/dom': 1.7.6 + '@internationalized/date': 3.12.1 + esm-env: 1.2.2 + runed: 0.35.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5) + svelte: 5.55.5 + svelte-toolbelt: 0.10.6(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5) + tabbable: 6.4.0 + transitivePeerDependencies: + - '@sveltejs/kit' + + camelcase@8.0.0: + optional: true + + chalk-template@0.4.0: + dependencies: + chalk: 4.1.2 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + class-validator@0.14.4: + dependencies: + '@types/validator': 13.15.10 + libphonenumber-js: 1.12.42 + validator: 13.15.35 + optional: true + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + command-line-args@6.0.2: + dependencies: + array-back: 6.2.3 + find-replace: 5.0.2 + lodash.camelcase: 4.3.0 + typical: 7.3.0 + + command-line-usage@7.0.4: + dependencies: + array-back: 6.2.3 + chalk-template: 0.4.0 + table-layout: 4.1.1 + typical: 7.3.0 + + commander@14.0.3: {} + + commander@7.2.0: {} + + cookie@0.6.0: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.1.0 + + d3-dispatch@3.0.1: {} + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo-voronoi@2.1.0: + dependencies: + d3-array: 3.2.4 + d3-delaunay: 6.0.4 + d3-geo: 3.1.1 + d3-tricontour: 1.1.0 + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate-path@2.3.0: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-tile@1.0.0: {} + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-tricontour@1.1.0: + dependencies: + d3-delaunay: 6.0.4 + d3-scale: 4.0.2 + + dayjs@1.11.20: + optional: true + + deepmerge@4.3.1: {} + + delaunator@5.1.0: + dependencies: + robust-predicates: 3.0.3 + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + devalue@5.7.1: {} + + dlv@1.1.3: + optional: true + + effect@3.21.2: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 + optional: true + + enhanced-resolve@5.21.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + esm-env@1.2.2: {} + + esrap@2.2.5: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + optional: true + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + find-replace@5.0.2: {} + + flatbuffers@25.9.23: {} + + formsnap@2.0.1(svelte@5.55.5)(sveltekit-superforms@2.30.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)): + dependencies: + svelte: 5.55.5 + svelte-toolbelt: 0.5.0(svelte@5.55.5) + sveltekit-superforms: 2.30.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3) + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + inline-style-parser@0.2.7: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + jiti@2.6.1: {} + + joi@17.13.3: + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 + optional: true + + json-bignum@0.0.3: {} + + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.29.2 + ts-algebra: 2.0.0 + optional: true + + kleur@4.1.5: {} + + layerchart@2.0.0-next.48(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(zod@4.3.6): + dependencies: + '@dagrejs/dagre': 2.0.4 + '@layerstack/svelte-actions': 1.0.1-next.18 + '@layerstack/svelte-state': 0.1.0-next.23 + '@layerstack/tailwind': 2.0.0-next.21 + '@layerstack/utils': 2.0.0-next.18 + '@types/d3-contour': 3.0.6 + d3-array: 3.2.4 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dsv: 3.0.1 + d3-force: 3.0.0 + d3-geo: 3.1.1 + d3-geo-voronoi: 2.1.0 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-interpolate-path: 2.3.0 + d3-path: 3.1.0 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-sankey: 0.12.3 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-shape: 3.2.0 + d3-tile: 1.0.0 + d3-time: 3.1.0 + memoize: 10.2.0 + runed: 0.37.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(zod@4.3.6) + svelte: 5.55.5 + transitivePeerDependencies: + - '@sveltejs/kit' + - zod + + libphonenumber-js@1.12.42: + optional: true + + 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 + + locate-character@3.0.0: {} + + lodash.camelcase@4.3.0: {} + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + mediabunny@1.42.0: + dependencies: + '@types/dom-mediacapture-transform': 0.1.11 + '@types/dom-webcodecs': 0.1.13 + + memoize-weak@1.0.2: {} + + memoize@10.2.0: + dependencies: + mimic-function: 5.0.1 + + mimic-function@5.0.1: {} + + mode-watcher@1.1.0(svelte@5.55.5): + dependencies: + runed: 0.25.0(svelte@5.55.5) + svelte: 5.55.5 + svelte-toolbelt: 0.7.1(svelte@5.55.5) + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + nanoid@3.3.11: {} + + node-fetch-native@1.6.7: {} + + normalize-url@8.1.1: + optional: true + + obug@2.1.1: {} + + paneforge@1.0.2(svelte@5.55.5): + dependencies: + runed: 0.23.4(svelte@5.55.5) + svelte: 5.55.5 + svelte-toolbelt: 0.9.3(svelte@5.55.5) + + parquet-wasm@0.7.1: {} + + phosphor-svelte@3.1.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)): + dependencies: + estree-walker: 3.0.3 + magic-string: 0.30.21 + svelte: 5.55.5 + optionalDependencies: + vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1) + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss@8.5.12: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + property-expr@2.0.6: + optional: true + + pure-rand@6.1.0: + optional: true + + readdirp@4.1.2: {} + + robust-predicates@3.0.3: {} + + rolldown@1.0.0-rc.17: + dependencies: + '@oxc-project/types': 0.127.0 + '@rolldown/pluginutils': 1.0.0-rc.17 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.17 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.17 + '@rolldown/binding-darwin-x64': 1.0.0-rc.17 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.17 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 + + runed@0.23.4(svelte@5.55.5): + dependencies: + esm-env: 1.2.2 + svelte: 5.55.5 + + runed@0.25.0(svelte@5.55.5): + dependencies: + esm-env: 1.2.2 + svelte: 5.55.5 + + runed@0.28.0(svelte@5.55.5): + dependencies: + esm-env: 1.2.2 + svelte: 5.55.5 + + runed@0.29.2(svelte@5.55.5): + dependencies: + esm-env: 1.2.2 + svelte: 5.55.5 + + runed@0.35.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5): + dependencies: + dequal: 2.0.3 + esm-env: 1.2.2 + lz-string: 1.5.0 + svelte: 5.55.5 + optionalDependencies: + '@sveltejs/kit': 2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + + runed@0.37.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(zod@4.3.6): + dependencies: + dequal: 2.0.3 + esm-env: 1.2.2 + lz-string: 1.5.0 + svelte: 5.55.5 + optionalDependencies: + '@sveltejs/kit': 2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + zod: 4.3.6 + + rw@1.3.3: {} + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safer-buffer@2.1.2: {} + + set-cookie-parser@3.1.0: {} + + shadcn-svelte@1.2.7(svelte@5.55.5): + dependencies: + commander: 14.0.3 + node-fetch-native: 1.6.7 + postcss: 8.5.12 + svelte: 5.55.5 + tailwind-merge: 3.5.0 + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + superstruct@2.0.2: + optional: true + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + svelte-check@4.4.6(picomatch@4.0.4)(svelte@5.55.5)(typescript@6.0.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.55.5 + typescript: 6.0.3 + transitivePeerDependencies: + - picomatch + + svelte-sonner@1.1.1(svelte@5.55.5): + dependencies: + runed: 0.28.0(svelte@5.55.5) + svelte: 5.55.5 + + svelte-toolbelt@0.10.6(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5): + dependencies: + clsx: 2.1.1 + runed: 0.35.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5) + style-to-object: 1.0.14 + svelte: 5.55.5 + transitivePeerDependencies: + - '@sveltejs/kit' + + svelte-toolbelt@0.5.0(svelte@5.55.5): + dependencies: + clsx: 2.1.1 + style-to-object: 1.0.14 + svelte: 5.55.5 + + svelte-toolbelt@0.7.1(svelte@5.55.5): + dependencies: + clsx: 2.1.1 + runed: 0.23.4(svelte@5.55.5) + style-to-object: 1.0.14 + svelte: 5.55.5 + + svelte-toolbelt@0.9.3(svelte@5.55.5): + dependencies: + clsx: 2.1.1 + runed: 0.29.2(svelte@5.55.5) + style-to-object: 1.0.14 + svelte: 5.55.5 + + svelte@5.55.5: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0) + '@types/estree': 1.0.8 + '@types/trusted-types': 2.0.7 + acorn: 8.16.0 + aria-query: 5.3.1 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.7.1 + esm-env: 1.2.2 + esrap: 2.2.5 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + transitivePeerDependencies: + - '@typescript-eslint/types' + + sveltekit-superforms@2.30.1(@sveltejs/kit@2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3): + dependencies: + '@sveltejs/kit': 2.58.0(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.5)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)))(svelte@5.55.5)(typescript@6.0.3)(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)) + devalue: 5.7.1 + memoize-weak: 1.0.2 + svelte: 5.55.5 + ts-deepmerge: 7.0.3 + optionalDependencies: + '@exodus/schemasafe': 1.3.0 + '@standard-schema/spec': 1.1.0 + '@typeschema/class-validator': 0.3.0(class-validator@0.14.4) + '@valibot/to-json-schema': 1.6.0(valibot@1.3.1(typescript@6.0.3)) + '@vinejs/vine': 3.0.1 + arktype: 2.2.0 + class-validator: 0.14.4 + effect: 3.21.2 + joi: 17.13.3 + json-schema-to-ts: 3.1.1 + superstruct: 2.0.2 + typebox: 1.1.34 + valibot: 1.3.1(typescript@6.0.3) + yup: 1.7.1 + zod: 4.3.6 + zod-v3-to-json-schema: 4.0.0(zod@4.3.6) + transitivePeerDependencies: + - '@types/json-schema' + - typescript + + tabbable@6.4.0: {} + + table-layout@4.1.1: + dependencies: + array-back: 6.2.3 + wordwrapjs: 5.1.1 + + tailwind-merge@3.5.0: {} + + tailwind-variants@3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.4): + dependencies: + tailwindcss: 4.2.4 + optionalDependencies: + tailwind-merge: 3.5.0 + + tailwindcss@4.2.4: {} + + tapable@2.3.3: {} + + tiny-case@1.0.3: + optional: true + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + toposort@2.0.2: + optional: true + + totalist@3.0.1: {} + + ts-algebra@2.0.0: + optional: true + + ts-deepmerge@7.0.3: {} + + tslib@2.8.1: {} + + tw-animate-css@1.4.0: {} + + type-fest@2.19.0: + optional: true + + typebox@1.1.34: + optional: true + + typescript@6.0.3: {} + + typical@7.3.0: {} + + undici-types@7.16.0: {} + + valibot@1.3.1(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + optional: true + + validator@13.15.35: + optional: true + + vaul-svelte@1.0.0-next.7(svelte@5.55.5): + dependencies: + runed: 0.23.4(svelte@5.55.5) + svelte: 5.55.5 + svelte-toolbelt: 0.7.1(svelte@5.55.5) + + vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.12 + rolldown: 1.0.0-rc.17 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 24.12.2 + fsevents: 2.3.3 + jiti: 2.6.1 + + vitefu@1.1.3(vite@8.0.10(@types/node@24.12.2)(jiti@2.6.1)): + optionalDependencies: + vite: 8.0.10(@types/node@24.12.2)(jiti@2.6.1) + + wordwrapjs@5.1.1: {} + + yup@1.7.1: + dependencies: + property-expr: 2.0.6 + tiny-case: 1.0.3 + toposort: 2.0.2 + type-fest: 2.19.0 + optional: true + + zimmerframe@1.1.4: {} + + zod-v3-to-json-schema@4.0.0(zod@4.3.6): + dependencies: + zod: 4.3.6 + optional: true + + zod@4.3.6: + optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5b23a1c1b5a83d39440f5b773d9d088f0fed6c7a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +onlyBuiltDependencies: + - '@tailwindcss/oxide' + - esbuild diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..da08e6da592d210d5cc574d8a629868eced88543 --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000000000000000000000000000000000000..6a2bb58226c2e7626187c84bf4b01f21faf0a934 --- /dev/null +++ b/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/lib/api/hf.ts b/src/lib/api/hf.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a9b46b7ce756d84683d7fad2b85d80a1062f31f --- /dev/null +++ b/src/lib/api/hf.ts @@ -0,0 +1,136 @@ +import type { Match, PreviewChunk, Round } from '../types'; +import { fetchParquetRows } from './parquet'; +import { listTree, resolveUrl, type FetchOpts } from './hub'; + +export type FetchOptions = FetchOpts; + +const PLAYER_COUNT = 10; + +let matchesPromise: Promise | null = null; +let roundsPromise: Promise | null = null; +const matchPreviewsCache = new Map>(); + +// The dataset is sharded by upload batch. Index files are named like +// `index/manifest-.parquet` / `index/rounds-.parquet`. +// Discover them at runtime so new shards don't require a code change. +async function listIndexShards( + prefix: 'manifest' | 'rounds', + opts: FetchOptions +): Promise { + const entries = await listTree('index', opts); + return entries + .filter((e) => e.type === 'file') + .map((e) => e.path) + .filter((p) => { + const name = p.split('/').pop() ?? ''; + return name.startsWith(`${prefix}-`) && name.endsWith('.parquet'); + }); +} + +async function loadShardedIndex( + prefix: 'manifest' | 'rounds', + opts: FetchOptions +): Promise { + const paths = await listIndexShards(prefix, opts); + if (!paths.length) return []; + const shards = await Promise.all( + paths.map((p) => fetchParquetRows(resolveUrl(p), opts)) + ); + return shards.flat(); +} + +export function listMatches(opts: FetchOptions = {}): Promise { + if (matchesPromise) return matchesPromise; + matchesPromise = loadShardedIndex('manifest', opts) + .then((rows) => { + rows.sort( + (a, b) => + new Date(b.match_date).getTime() - new Date(a.match_date).getTime() || + (a.map_index ?? 0) - (b.map_index ?? 0) + ); + return rows; + }) + .catch((err) => { + matchesPromise = null; + throw err; + }); + return matchesPromise; +} + +export async function listAllRounds(opts: FetchOptions = {}): Promise { + if (!roundsPromise) { + roundsPromise = loadShardedIndex('rounds', opts).catch((err) => { + roundsPromise = null; + throw err; + }); + } + return roundsPromise; +} + +export async function listRounds( + matchId: number, + mapName: string, + opts: FetchOptions = {} +): Promise { + const all = await listAllRounds(opts); + return all + .filter((r) => r.match_id === matchId && r.map_name === mapName) + .sort((a, b) => a.round - b.round); +} + +/** + * Fetch all preview rows for every player on this (match, map) in parallel. + * The shard_id from the match's manifest row is enough to construct each + * player's parquet URL — no tree-API discovery needed. + */ +async function loadMatchPreviews( + matchId: number, + mapName: string, + opts: FetchOptions +): Promise { + const key = `${matchId}/${mapName}`; + const cached = matchPreviewsCache.get(key); + if (cached) return cached; + + const promise = (async () => { + const matches = await listMatches(opts); + const match = matches.find((m) => m.match_id === matchId && m.map_name === mapName); + if (!match) return []; + + const players = Array.from({ length: PLAYER_COUNT }, (_, i) => i); + const results = await Promise.all( + players.map(async (player) => { + const dir = `data/match_id=${matchId}/map_name=${mapName}/player=${player}`; + try { + const rows = await fetchParquetRows( + resolveUrl(`${dir}/chunks-preview-${match.shard_id}.parquet`), + opts + ); + for (const r of rows) r.preview_video = { src: resolveUrl(`${dir}/${r.preview_path}`) }; + return rows; + } catch { + return [] as PreviewChunk[]; + } + }) + ); + return results.flat(); + })().catch((err) => { + matchPreviewsCache.delete(key); + throw err; + }); + + matchPreviewsCache.set(key, promise); + return promise; +} + +export async function listRoundPreviews( + matchId: number, + mapName: string, + round: number, + opts: FetchOptions = {} +): Promise { + const all = await loadMatchPreviews(matchId, mapName, opts); + return all + .filter((p) => p.round === round) + .sort((a, b) => a.player - b.player || a.chunk_index - b.chunk_index); +} diff --git a/src/lib/api/hub.ts b/src/lib/api/hub.ts new file mode 100644 index 0000000000000000000000000000000000000000..6ea47f58ec099958c114a431a2d664bf834bc46d --- /dev/null +++ b/src/lib/api/hub.ts @@ -0,0 +1,21 @@ +import { DATASET } from '../types'; + +const REF = 'main'; + +export type FetchOpts = { fetch?: typeof fetch; signal?: AbortSignal }; + +/** Build a `huggingface.co/datasets//resolve/main/` URL. */ +export function resolveUrl(repoPath: string): string { + return `https://huggingface.co/datasets/${DATASET}/resolve/${REF}/${repoPath}`; +} + +export type TreeEntry = { type: 'file' | 'directory'; path: string; size?: number }; + +/** List entries under `/` via the HF tree API. */ +export async function listTree(dirPath: string, opts: FetchOpts = {}): Promise { + const fetchFn = opts.fetch ?? fetch; + const url = `https://huggingface.co/api/datasets/${DATASET}/tree/${REF}/${dirPath}`; + const r = await fetchFn(url, { signal: opts.signal }); + if (!r.ok) throw new Error(`tree ${dirPath}: ${r.status}`); + return (await r.json()) as TreeEntry[]; +} diff --git a/src/lib/api/parquet.ts b/src/lib/api/parquet.ts new file mode 100644 index 0000000000000000000000000000000000000000..5fac828d52959b135c45b7b6cdcac1df5b1cac8b --- /dev/null +++ b/src/lib/api/parquet.ts @@ -0,0 +1,55 @@ +import * as arrow from 'apache-arrow'; + +let wasmReady: Promise | null = null; +let readParquetFn: ((bytes: Uint8Array) => { intoIPCStream: () => Uint8Array }) | null = null; + +async function ensureWasm(): Promise { + if (wasmReady) return wasmReady; + wasmReady = (async () => { + const mod = await import('parquet-wasm'); + const init = (mod as unknown as { default?: () => Promise }).default; + if (typeof init === 'function') await init(); + readParquetFn = (mod as unknown as { readParquet: typeof readParquetFn }).readParquet!; + })().catch((err) => { + wasmReady = null; + throw err; + }); + return wasmReady; +} + +export type ParquetFetchOptions = { + fetch?: typeof fetch; + signal?: AbortSignal; +}; + +/** + * Fetch a parquet file over HTTP and return its rows as plain JS objects. + * Timestamp columns are converted to ISO strings; BigInts become numbers. + * Nested struct/list columns are returned as Arrow proxies (we don't read any). + */ +export async function fetchParquetRows>( + url: string, + opts: ParquetFetchOptions = {} +): Promise { + await ensureWasm(); + const f = opts.fetch ?? fetch; + const res = await f(url, { signal: opts.signal }); + if (!res.ok) throw new Error(`parquet fetch ${url}: ${res.status} ${res.statusText}`); + const bytes = new Uint8Array(await res.arrayBuffer()); + const table = arrow.tableFromIPC(readParquetFn!(bytes).intoIPCStream()); + + const timestamps = new Set( + table.schema.fields.filter((f) => arrow.DataType.isTimestamp(f.type)).map((f) => f.name) + ); + + return table.toArray().map((row) => { + const out: Record = {}; + for (const [k, v] of Object.entries(row as Record)) { + if (v == null) out[k] = v; + else if (timestamps.has(k)) out[k] = new Date(Number(v)).toISOString(); + else if (typeof v === 'bigint') out[k] = Number(v); + else out[k] = v; + } + return out; + }) as T[]; +} diff --git a/src/lib/api/world.ts b/src/lib/api/world.ts new file mode 100644 index 0000000000000000000000000000000000000000..95c5fc73399a1118633373fc3141c8b03ba63cc2 --- /dev/null +++ b/src/lib/api/world.ts @@ -0,0 +1,232 @@ +import type { PreviewChunk } from '../types'; +import type { FetchOpts } from './hub'; + +// One player's pose at one sampled tick. Round-relative `t` (seconds). +export type WorldFrame = { + t: number; + player: number; // dataset slot 0..9 + steamid: string; + name: string; + team_num: number; + is_alive: boolean; + health: number; + armor_value: number; + X: number; + Y: number; + Z: number; + yaw?: number; +}; + +export type PlayerSeries = { + player: number; + frames: WorldFrame[]; +}; + +export type RoundWorld = { + players: PlayerSeries[]; + duration: number; +}; + +const cache = new Map>(); + +// preview_video.src is ".../player=N/previews/chunk_NNN/preview.mp4" — strip +// the filename to get the chunk dir we'll resolve sibling assets against. +function chunkBaseUrl(chunk: PreviewChunk): string { + return chunk.preview_video.src.replace(/\/[^/]+$/, ''); +} + +type WorldColumns = { + t: number[]; + steamid: (number | string)[]; + name: string[]; + team_num: number[]; + is_alive: boolean[]; + health: number[]; + armor_value: number[]; + X: number[]; + Y: number[]; + Z: number[]; +}; + +type InputsJson = { + t: number[]; + view?: { yaw?: number[] }; +}; + +async function loadChunkFrames( + chunk: PreviewChunk, + chunkStartT: number, + player: number, + opts: FetchOpts +): Promise { + const base = chunkBaseUrl(chunk); + const fetchFn = opts.fetch ?? fetch; + const [worldR, inputsR] = await Promise.all([ + fetchFn(`${base}/world.preview.jsonl`, { signal: opts.signal }), + fetchFn(`${base}/inputs.preview.json`, { signal: opts.signal }) + ]); + if (!worldR.ok) return []; + const worldText = await worldR.text(); + // JSONL with one columnar object on the first line. + const firstLine = worldText.split('\n').find((l) => l.trim().length > 0); + if (!firstLine) return []; + const world = JSON.parse(firstLine) as WorldColumns; + const inputs: InputsJson | null = inputsR.ok ? await inputsR.json() : null; + const yawArr = inputs?.view?.yaw; + + const n = world.t.length; + const frames: WorldFrame[] = new Array(n); + for (let i = 0; i < n; i++) { + frames[i] = { + t: chunkStartT + world.t[i], + player, + steamid: String(world.steamid[i]), + name: world.name[i], + team_num: world.team_num[i], + is_alive: world.is_alive[i], + health: world.health[i], + armor_value: world.armor_value[i], + X: world.X[i], + Y: world.Y[i], + Z: world.Z[i], + yaw: yawArr?.[i] + }; + } + return frames; +} + +export async function loadRoundWorld( + matchId: number, + mapName: string, + round: number, + chunks: PreviewChunk[], + opts: FetchOpts = {} +): Promise { + const key = `${matchId}/${mapName}/${round}`; + const cached = cache.get(key); + if (cached) return cached; + + const promise = (async () => { + const byPlayer = new Map(); + for (const c of chunks) { + if (c.round !== round) continue; + if (!byPlayer.has(c.player)) byPlayer.set(c.player, []); + byPlayer.get(c.player)!.push(c); + } + + // If we were asked to load a round whose chunks haven't arrived yet, + // don't poison the cache with an empty result — let the next call + // (with chunks) actually fetch. + if (byPlayer.size === 0) { + cache.delete(key); + return { players: [], duration: 0 } as RoundWorld; + } + + const players: PlayerSeries[] = await Promise.all( + Array.from(byPlayer.entries()).map(async ([player, list]) => { + list.sort((a, b) => a.chunk_index - b.chunk_index); + let startT = 0; + const frames: WorldFrame[] = []; + for (const c of list) { + const f = await loadChunkFrames(c, startT, player, opts); + frames.push(...f); + startT += c.duration_s || 0; + } + return { player, frames }; + }) + ); + + const duration = players.reduce( + (m, p) => Math.max(m, p.frames[p.frames.length - 1]?.t ?? 0), + 0 + ); + return { players, duration }; + })().catch((err) => { + cache.delete(key); + throw err; + }); + + cache.set(key, promise); + return promise; +} + +function nearestLeq(frames: WorldFrame[], t: number): number { + let lo = 0; + let hi = frames.length - 1; + let best = -1; + while (lo <= hi) { + const mid = (lo + hi) >> 1; + if (frames[mid].t <= t) { + best = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return best; +} + +// Shortest-arc lerp so a player turning across the ±180° boundary doesn't +// spin the long way around between sampled ticks. +function lerpAngle(a: number, b: number, alpha: number): number { + const d = (((b - a + 540) % 360) - 180) || 0; + return a + d * alpha; +} + +export type Snapshot = { + player: number; + steamid: string; + name: string; + team_num: number; + is_alive: boolean; + health: number; + X: number; + Y: number; + Z: number; + yaw?: number; +}; + +export function snapshotAt(world: RoundWorld, t: number): Snapshot[] { + const out: Snapshot[] = []; + for (const series of world.players) { + const frames = series.frames; + if (!frames.length) continue; + const i = nearestLeq(frames, t); + const cur = i < 0 ? frames[0] : frames[i]; + const next = i >= 0 && i + 1 < frames.length ? frames[i + 1] : null; + + // If we've run off the end of this player's series (typically: died + // mid-round and their chunks stop), drop them after a small grace + // window so the last-known position doesn't linger forever. + const last = frames[frames.length - 1]; + if (!next && t > last.t + 2) continue; + + let X = cur.X; + let Y = cur.Y; + let Z = cur.Z; + let yaw = cur.yaw; + if (next && next.t > cur.t) { + const alpha = Math.max(0, Math.min(1, (t - cur.t) / (next.t - cur.t))); + X = cur.X + (next.X - cur.X) * alpha; + Y = cur.Y + (next.Y - cur.Y) * alpha; + Z = cur.Z + (next.Z - cur.Z) * alpha; + if (cur.yaw !== undefined && next.yaw !== undefined) { + yaw = lerpAngle(cur.yaw, next.yaw, alpha); + } + } + + out.push({ + player: cur.player, + steamid: cur.steamid, + name: cur.name, + team_num: cur.team_num, + is_alive: cur.is_alive, + health: cur.health, + X, + Y, + Z, + yaw + }); + } + return out; +} diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000000000000000000000000000000000000..cc5dc66a3a524f4167cc097452cbe3c3883a684c --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/src/lib/components/grid-tile.svelte b/src/lib/components/grid-tile.svelte new file mode 100644 index 0000000000000000000000000000000000000000..073f76375e25df98ec9365de0c33fb0ea05ef6a8 --- /dev/null +++ b/src/lib/components/grid-tile.svelte @@ -0,0 +1,187 @@ + + + + +
available && onSelect()} + role="button" + tabindex={available ? 0 : -1} + aria-label="Switch to player {player}" +> + + + + +
+ {player} +
+ + {#if isLeader} +
+ +
+ {/if} + + {#if !available} +
+ +
+ {/if} +
diff --git a/src/lib/components/header.svelte b/src/lib/components/header.svelte new file mode 100644 index 0000000000000000000000000000000000000000..2b3ecf6456683d48b3ff5163f9637a1d59e0b0d3 --- /dev/null +++ b/src/lib/components/header.svelte @@ -0,0 +1,80 @@ + + +
+
+ + + CS2 Render Dataset + + + {#if breadcrumb.length} + + {/if} + +
+ + + {#snippet child({ props })} + + {/snippet} + + View dataset on Hugging Face + + + + {#snippet child({ props })} + + {/snippet} + + + Switch to {mode.current === 'dark' ? 'light' : 'dark'} mode + + +
+
+
diff --git a/src/lib/components/hf-logo.svelte b/src/lib/components/hf-logo.svelte new file mode 100644 index 0000000000000000000000000000000000000000..cda7b42b75f056d6853d328e5ef30bb550de78fb --- /dev/null +++ b/src/lib/components/hf-logo.svelte @@ -0,0 +1,28 @@ + + + + + + + + + + diff --git a/src/lib/components/map-preview.svelte b/src/lib/components/map-preview.svelte new file mode 100644 index 0000000000000000000000000000000000000000..576554d689f6b5234e4c68e2ea268ea40cd57082 --- /dev/null +++ b/src/lib/components/map-preview.svelte @@ -0,0 +1,179 @@ + + +
+ {#if !map} + + {:else} + {mapName} radar + + {#each players as p (p.steamid)} + {@const { px, py } = worldToImage(map, p.X, p.Y)} + {@const playerFloor = floorForZ(map, p.Z)} + {@const onDisplayed = !lowerAvailable || playerFloor === displayedFloor} + {@const baseOpacity = onDisplayed ? 1 : 0.3} + + {p.name}{p.slot !== undefined ? ` (#${p.slot})` : ''} + {#if p.is_alive} + {#if p.yaw !== undefined} + + {/if} + + {#if p.slot !== undefined} + + {p.slot} + + {/if} + {:else} + + + + + + {/if} + + {/each} + + + {#if lowerAvailable} +
+ { + if (v === 'upper' || v === 'lower') userFloor = v; + }} + variant="outline" + size="sm" + spacing={1} + aria-label="Floor" + class="bg-background/80 backdrop-blur-sm" + > + + Upper + + + Lower + + +
+ {/if} + {/if} +
diff --git a/src/lib/components/match-card.svelte b/src/lib/components/match-card.svelte new file mode 100644 index 0000000000000000000000000000000000000000..fc0872acaa218bdffcfb9eea4be2e608b12a9bc7 --- /dev/null +++ b/src/lib/components/match-card.svelte @@ -0,0 +1,87 @@ + + + + + +
+ + + {formatDate(match.match_date)} + + + {match.format} + +
+ + {match.event} + +
+ + +
+
+ {match.team1} +
+
+ {match.score1} + : + {match.score2} +
+
+ {match.team2} +
+
+ +
+ + {prettyMap(match.map_name)} + + + {match.rounds_played} rounds + + {#if winnerSide === 'ct'} + + CT won + + {:else if winnerSide === 't'} + + T won + + {/if} +
+
+
+
diff --git a/src/lib/components/match-info.svelte b/src/lib/components/match-info.svelte new file mode 100644 index 0000000000000000000000000000000000000000..4851562efb625dda2e21c1ed9d2c91b400f2bb7e --- /dev/null +++ b/src/lib/components/match-info.svelte @@ -0,0 +1,103 @@ + + +
+
+
Event
+
{match.event}
+
+ +
+
+
+ {match.team1} +
+
+ {match.score1} + : + {match.score2} +
+
+ {match.team2} +
+
+
+ +
+
+ Map +
+
+ + {prettyMap(match.map_name)} + +
+ +
+ Format +
+
{match.format}
+ +
+ Played +
+
{formatDateTime(match.match_date)}
+ + {#if winnerSide === 'ct' || winnerSide === 't'} +
+ {#if winnerSide === 'ct'} + + {:else} + + {/if} + Winner +
+
+ {#if winnerSide === 'ct'} + + CT won + + {:else} + + T won + + {/if} +
+ {/if} + +
+ Rounds +
+
{match.rounds_played}
+
+ + {#if match.match_url} + + View on HLTV + + {/if} +
diff --git a/src/lib/components/match-map.svelte b/src/lib/components/match-map.svelte new file mode 100644 index 0000000000000000000000000000000000000000..dc3e5d68016613fed80b3b3e6a8abdc49e846b9b --- /dev/null +++ b/src/lib/components/match-map.svelte @@ -0,0 +1,74 @@ + + +{#if error} +
+ Map data unavailable: {error} +
+{:else if !world} + +{:else} + +{/if} diff --git a/src/lib/components/perspective-grid.svelte b/src/lib/components/perspective-grid.svelte new file mode 100644 index 0000000000000000000000000000000000000000..21ab526b8c5d3a634d47e8d834781dee6ad3ee31 --- /dev/null +++ b/src/lib/components/perspective-grid.svelte @@ -0,0 +1,190 @@ + + +
+
+ {#if ctPlayers.length} +
+
+ Counter-Terrorists +
+
+ {#each ctPlayers as p (p.player)} +
+ onSelect(p.player)} + {onLeaderTime} + onLeaderEnded={() => onLeaderEnded?.()} + onBufferedChange={handleTileBuffered} + /> +
+ + Player {p.player} +
+
+ {/each} +
+
+ {/if} + {#if tPlayers.length} +
+
+ Terrorists +
+
+ {#each tPlayers as p (p.player)} +
+ onSelect(p.player)} + {onLeaderTime} + onLeaderEnded={() => onLeaderEnded?.()} + onBufferedChange={handleTileBuffered} + /> +
+ + Player {p.player} +
+
+ {/each} +
+
+ {/if} +
+
diff --git a/src/lib/components/player-grid.svelte b/src/lib/components/player-grid.svelte new file mode 100644 index 0000000000000000000000000000000000000000..99ff40ebd93056397ae46227a3bc7240b4f22b9c --- /dev/null +++ b/src/lib/components/player-grid.svelte @@ -0,0 +1,158 @@ + + +{#snippet playerItem(p: PlayerSummary)} + {@const ct = p.side === 'CT'} + {@const ready = preloadedPlayers.has(p.player)} + {@const available = availablePlayers.has(p.player)} + + + {p.player} + +
+
Player {p.player}
+ {#if !available} +
+ died · {formatDuration(p.duration)} +
+ {/if} +
+ {#if p.survived} + + {:else} + + {/if} +
+{/snippet} + +
+
+
Player perspectives
+
+ + onPreloadAllChange(!!v)} + /> +
+
+ + v && onSelect(Number(v))} + variant="outline" + spacing={1} + aria-label="Player perspective" + class="w-full! flex-col gap-2" + > +
+
+ Counter-Terrorists +
+
+ {#each ctPlayers as p (p.player)} + {@render playerItem(p)} + {/each} +
+
+ +
+
+ Terrorists +
+
+ {#each tPlayers as p (p.player)} + {@render playerItem(p)} + {/each} +
+
+
+
diff --git a/src/lib/components/round-list.svelte b/src/lib/components/round-list.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7f79552e9b1b97a295ba68faa161a23a73f40f6b --- /dev/null +++ b/src/lib/components/round-list.svelte @@ -0,0 +1,66 @@ + + +
+
+ Rounds ({rounds.length}) +
+ + v && onSelect(Number(v))} + variant="outline" + size="sm" + spacing={1} + aria-label="Round" + class="grid w-full! grid-cols-1 gap-1 p-1.5" + > + {#each rounds as r, i (r.round)} + {#if i > 0 && isSideSwitchBefore(r.round)} + + {/if} + + {r.round} + + {/each} + + +
diff --git a/src/lib/components/timeline-bar.svelte b/src/lib/components/timeline-bar.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e5990ffa67fc55106c11676eb2f36e5d5014e8e0 --- /dev/null +++ b/src/lib/components/timeline-bar.svelte @@ -0,0 +1,86 @@ + + +
+ + + + {formatDuration(currentTime)} + + +
+ +
+ + + {formatDuration(duration)} + + + +
diff --git a/src/lib/components/ui/accordion/accordion-content.svelte b/src/lib/components/ui/accordion/accordion-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..a4d8b1c5508d22e949ac1a13c8ae4e263b1e3449 --- /dev/null +++ b/src/lib/components/ui/accordion/accordion-content.svelte @@ -0,0 +1,27 @@ + + + +
+ {@render children?.()} +
+
diff --git a/src/lib/components/ui/accordion/accordion-item.svelte b/src/lib/components/ui/accordion/accordion-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..bcbea1a9dfc265bb29081402490ec2ef4bdd63e4 --- /dev/null +++ b/src/lib/components/ui/accordion/accordion-item.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/accordion/accordion-trigger.svelte b/src/lib/components/ui/accordion/accordion-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..2ed681643d180e85885be236858fc420b4391749 --- /dev/null +++ b/src/lib/components/ui/accordion/accordion-trigger.svelte @@ -0,0 +1,32 @@ + + + + + {@render children?.()} + + + diff --git a/src/lib/components/ui/accordion/accordion.svelte b/src/lib/components/ui/accordion/accordion.svelte new file mode 100644 index 0000000000000000000000000000000000000000..cef44024018a08811ecfc67ba836912844e38519 --- /dev/null +++ b/src/lib/components/ui/accordion/accordion.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/lib/components/ui/accordion/index.ts b/src/lib/components/ui/accordion/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ac343a10b8d2c83fa07f7e0860254e926c898351 --- /dev/null +++ b/src/lib/components/ui/accordion/index.ts @@ -0,0 +1,16 @@ +import Root from "./accordion.svelte"; +import Content from "./accordion-content.svelte"; +import Item from "./accordion-item.svelte"; +import Trigger from "./accordion-trigger.svelte"; + +export { + Root, + Content, + Item, + Trigger, + // + Root as Accordion, + Content as AccordionContent, + Item as AccordionItem, + Trigger as AccordionTrigger, +}; diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7e63004f66293987d5c34c926f3aa2da540914a8 --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte new file mode 100644 index 0000000000000000000000000000000000000000..a4ce03c8c4650e9573029bb27329b9c05cacff4b --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..b5d1960033d2c5621a76680c40f2171835395410 --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte @@ -0,0 +1,32 @@ + + + + + + diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7b26f8a64f862990b4833ec7c63cb859a40b5e56 --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte new file mode 100644 index 0000000000000000000000000000000000000000..4d133b7c34857586e3210a22fdbf905868767f2a --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte new file mode 100644 index 0000000000000000000000000000000000000000..b75a71e202710563cb48be7cc7d088f5e42c1ceb --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte new file mode 100644 index 0000000000000000000000000000000000000000..20871b81dc5ec5bc0fbffa750a437a0b157ba3e2 --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte new file mode 100644 index 0000000000000000000000000000000000000000..87e4037c1b94861890f622b5987f1e35224437fe --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f0a19a8cd57b5cc1cfc2ca851e45b4f1ab67afee --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte new file mode 100644 index 0000000000000000000000000000000000000000..8e55d89764d02995ba085ffc5a972ce4144b6dc7 --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte b/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..b22d1d50b5ceb119690ecce47726fd9eedbf1eca --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/alert-dialog/alert-dialog.svelte b/src/lib/components/ui/alert-dialog/alert-dialog.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7ea78bb4cb7426c458f0f7e15e687e02fdc7a175 --- /dev/null +++ b/src/lib/components/ui/alert-dialog/alert-dialog.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/alert-dialog/index.ts b/src/lib/components/ui/alert-dialog/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ca81c2a9660ebf525f2d1e137d3d8ff45063068c --- /dev/null +++ b/src/lib/components/ui/alert-dialog/index.ts @@ -0,0 +1,40 @@ +import Root from "./alert-dialog.svelte"; +import Portal from "./alert-dialog-portal.svelte"; +import Trigger from "./alert-dialog-trigger.svelte"; +import Title from "./alert-dialog-title.svelte"; +import Action from "./alert-dialog-action.svelte"; +import Cancel from "./alert-dialog-cancel.svelte"; +import Footer from "./alert-dialog-footer.svelte"; +import Header from "./alert-dialog-header.svelte"; +import Overlay from "./alert-dialog-overlay.svelte"; +import Content from "./alert-dialog-content.svelte"; +import Description from "./alert-dialog-description.svelte"; +import Media from "./alert-dialog-media.svelte"; + +export { + Root, + Title, + Action, + Cancel, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Media, + // + Root as AlertDialog, + Title as AlertDialogTitle, + Action as AlertDialogAction, + Cancel as AlertDialogCancel, + Portal as AlertDialogPortal, + Footer as AlertDialogFooter, + Header as AlertDialogHeader, + Trigger as AlertDialogTrigger, + Overlay as AlertDialogOverlay, + Content as AlertDialogContent, + Description as AlertDialogDescription, + Media as AlertDialogMedia, +}; diff --git a/src/lib/components/ui/alert/alert-action.svelte b/src/lib/components/ui/alert/alert-action.svelte new file mode 100644 index 0000000000000000000000000000000000000000..dab5d624b06be60a9ef46a4680d8ad63e5d6bb8a --- /dev/null +++ b/src/lib/components/ui/alert/alert-action.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/alert/alert-description.svelte b/src/lib/components/ui/alert/alert-description.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7d9d37ec6c5f309e9cc37cf82327ace03ff5298f --- /dev/null +++ b/src/lib/components/ui/alert/alert-description.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/alert/alert-title.svelte b/src/lib/components/ui/alert/alert-title.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7591a87b349a7ee1a31a618eedc40127c6ba275a --- /dev/null +++ b/src/lib/components/ui/alert/alert-title.svelte @@ -0,0 +1,23 @@ + + +
svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3", + className + )} + {...restProps} +> + {@render children?.()} +
diff --git a/src/lib/components/ui/alert/alert.svelte b/src/lib/components/ui/alert/alert.svelte new file mode 100644 index 0000000000000000000000000000000000000000..175f983a947e7680075da9638e29017ed6d3a789 --- /dev/null +++ b/src/lib/components/ui/alert/alert.svelte @@ -0,0 +1,43 @@ + + + + + diff --git a/src/lib/components/ui/alert/index.ts b/src/lib/components/ui/alert/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..071b113c95493c8012658bcd7f7f4f5cee85e1ec --- /dev/null +++ b/src/lib/components/ui/alert/index.ts @@ -0,0 +1,17 @@ +import Root from "./alert.svelte"; +import Description from "./alert-description.svelte"; +import Title from "./alert-title.svelte"; +import Action from "./alert-action.svelte"; +export { alertVariants, type AlertVariant } from "./alert.svelte"; + +export { + Root, + Description, + Title, + Action, + // + Root as Alert, + Description as AlertDescription, + Title as AlertTitle, + Action as AlertAction, +}; diff --git a/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte b/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte new file mode 100644 index 0000000000000000000000000000000000000000..815aab035809d33fc84782bb699bdbded6797036 --- /dev/null +++ b/src/lib/components/ui/aspect-ratio/aspect-ratio.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/aspect-ratio/index.ts b/src/lib/components/ui/aspect-ratio/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..985c75fdbff78e6728e9c36f0a79559bf06dd9d1 --- /dev/null +++ b/src/lib/components/ui/aspect-ratio/index.ts @@ -0,0 +1,3 @@ +import Root from "./aspect-ratio.svelte"; + +export { Root, Root as AspectRatio }; diff --git a/src/lib/components/ui/avatar/avatar-badge.svelte b/src/lib/components/ui/avatar/avatar-badge.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ca9d9ed2c8ee040fa99687a0a63130fd74d49fae --- /dev/null +++ b/src/lib/components/ui/avatar/avatar-badge.svelte @@ -0,0 +1,26 @@ + + +svg]:hidden", + "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", + "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", + className + )} + {...restProps} +> + {@render children?.()} + diff --git a/src/lib/components/ui/avatar/avatar-fallback.svelte b/src/lib/components/ui/avatar/avatar-fallback.svelte new file mode 100644 index 0000000000000000000000000000000000000000..63c8b0229c00f811e05a4516643dcc6e2c0a55d9 --- /dev/null +++ b/src/lib/components/ui/avatar/avatar-fallback.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/avatar/avatar-group-count.svelte b/src/lib/components/ui/avatar/avatar-group-count.svelte new file mode 100644 index 0000000000000000000000000000000000000000..4e010214df93be16c27df5637872495f908bfdcf --- /dev/null +++ b/src/lib/components/ui/avatar/avatar-group-count.svelte @@ -0,0 +1,23 @@ + + +
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2", + className + )} + {...restProps} +> + {@render children?.()} +
diff --git a/src/lib/components/ui/avatar/avatar-group.svelte b/src/lib/components/ui/avatar/avatar-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..78714fc09d97095da1807b18e58b78a433300c26 --- /dev/null +++ b/src/lib/components/ui/avatar/avatar-group.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/avatar/avatar-image.svelte b/src/lib/components/ui/avatar/avatar-image.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f41cf8febeb5af740ffd8ae7cbb8721e655a888e --- /dev/null +++ b/src/lib/components/ui/avatar/avatar-image.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/avatar/avatar.svelte b/src/lib/components/ui/avatar/avatar.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ea6fde56e62ff8494519350372fe7ff8ed0df7fd --- /dev/null +++ b/src/lib/components/ui/avatar/avatar.svelte @@ -0,0 +1,26 @@ + + + diff --git a/src/lib/components/ui/avatar/index.ts b/src/lib/components/ui/avatar/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..38ccef846d31fb15693ba4682a6a1e79be1fe5a3 --- /dev/null +++ b/src/lib/components/ui/avatar/index.ts @@ -0,0 +1,22 @@ +import Root from "./avatar.svelte"; +import Image from "./avatar-image.svelte"; +import Fallback from "./avatar-fallback.svelte"; +import Badge from "./avatar-badge.svelte"; +import Group from "./avatar-group.svelte"; +import GroupCount from "./avatar-group-count.svelte"; + +export { + Root, + Image, + Fallback, + Badge, + Group, + GroupCount, + // + Root as Avatar, + Image as AvatarImage, + Fallback as AvatarFallback, + Badge as AvatarBadge, + Group as AvatarGroup, + GroupCount as AvatarGroupCount, +}; diff --git a/src/lib/components/ui/badge/badge.svelte b/src/lib/components/ui/badge/badge.svelte new file mode 100644 index 0000000000000000000000000000000000000000..178a2dca8751e3f9ae6493f7f8188d7a8e69d976 --- /dev/null +++ b/src/lib/components/ui/badge/badge.svelte @@ -0,0 +1,49 @@ + + + + + + {@render children?.()} + diff --git a/src/lib/components/ui/badge/index.ts b/src/lib/components/ui/badge/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..64e0aa9b0805f5f62c05ccb321a941ba8b0193af --- /dev/null +++ b/src/lib/components/ui/badge/index.ts @@ -0,0 +1,2 @@ +export { default as Badge } from "./badge.svelte"; +export { badgeVariants, type BadgeVariant } from "./badge.svelte"; diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte new file mode 100644 index 0000000000000000000000000000000000000000..b64753e3eadb319bccb7c5f37b4c60ba9159d46b --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte @@ -0,0 +1,23 @@ + + + diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e9c77ea402e2a574c68871c803fc68631ccf2c31 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte @@ -0,0 +1,20 @@ + + +
  • + {@render children?.()} +
  • diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e6bc17d3185475c1b39471a2d53b227d5c473c75 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte @@ -0,0 +1,31 @@ + + +{#if child} + {@render child({ props: attrs })} +{:else} + + {@render children?.()} + +{/if} diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f05e7f8d0c567a5629c6b0af09b42b0b0638ae25 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte @@ -0,0 +1,20 @@ + + +
      + {@render children?.()} +
    diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte new file mode 100644 index 0000000000000000000000000000000000000000..5fb697947403d2dfc1dd3797845e304076457ac8 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte new file mode 100644 index 0000000000000000000000000000000000000000..351e07908b551d14b7f1ceca5b621b77711dddb7 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/lib/components/ui/breadcrumb/breadcrumb.svelte b/src/lib/components/ui/breadcrumb/breadcrumb.svelte new file mode 100644 index 0000000000000000000000000000000000000000..29ea3f5d6be811a088d771722d131bd9da45f5eb --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/ui/breadcrumb/index.ts b/src/lib/components/ui/breadcrumb/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc914ec345ba66ad13caa07d96f6ce2f90ae301a --- /dev/null +++ b/src/lib/components/ui/breadcrumb/index.ts @@ -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, +}; diff --git a/src/lib/components/ui/button-group/button-group-separator.svelte b/src/lib/components/ui/button-group/button-group-separator.svelte new file mode 100644 index 0000000000000000000000000000000000000000..a13f023e5698ab5f5e920e33fa632d74f170ec99 --- /dev/null +++ b/src/lib/components/ui/button-group/button-group-separator.svelte @@ -0,0 +1,23 @@ + + + diff --git a/src/lib/components/ui/button-group/button-group-text.svelte b/src/lib/components/ui/button-group/button-group-text.svelte new file mode 100644 index 0000000000000000000000000000000000000000..315d6035d6021930225d9beb60c0a30a41e8041c --- /dev/null +++ b/src/lib/components/ui/button-group/button-group-text.svelte @@ -0,0 +1,28 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} +
    + {@render mergedProps.children?.()} +
    +{/if} diff --git a/src/lib/components/ui/button-group/button-group.svelte b/src/lib/components/ui/button-group/button-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..bcbf7830696437c5c71ebc9240a651854b91a8a1 --- /dev/null +++ b/src/lib/components/ui/button-group/button-group.svelte @@ -0,0 +1,46 @@ + + + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/button-group/index.ts b/src/lib/components/ui/button-group/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..664167f7e1c4e6e6b41ba595b559eebd23ea5ae8 --- /dev/null +++ b/src/lib/components/ui/button-group/index.ts @@ -0,0 +1,15 @@ +import Root, { buttonGroupVariants, type ButtonGroupOrientation } from "./button-group.svelte"; +import Text from "./button-group-text.svelte"; +import Separator from "./button-group-separator.svelte"; + +export { + Root, + Text, + Separator, + buttonGroupVariants, + type ButtonGroupOrientation, + // + Root as ButtonGroup, + Text as ButtonGroupText, + Separator as ButtonGroupSeparator, +}; diff --git a/src/lib/components/ui/button/button.svelte b/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000000000000000000000000000000000000..db63315a9106b23e4467bb84d445326da515c240 --- /dev/null +++ b/src/lib/components/ui/button/button.svelte @@ -0,0 +1,82 @@ + + + + +{#if href} + + {@render children?.()} + +{:else} + +{/if} diff --git a/src/lib/components/ui/button/index.ts b/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fb585d7688e11d20a6ca051ebe4d58b35c3c5de7 --- /dev/null +++ b/src/lib/components/ui/button/index.ts @@ -0,0 +1,17 @@ +import Root, { + type ButtonProps, + type ButtonSize, + type ButtonVariant, + buttonVariants, +} from "./button.svelte"; + +export { + Root, + type ButtonProps as Props, + // + Root as Button, + buttonVariants, + type ButtonProps, + type ButtonSize, + type ButtonVariant, +}; diff --git a/src/lib/components/ui/card/card-action.svelte b/src/lib/components/ui/card/card-action.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7c48844a8fc255f5fc497c49b4614f7105e2ccb8 --- /dev/null +++ b/src/lib/components/ui/card/card-action.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/card/card-content.svelte b/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..4f60ee3a019652a8bdbf6d4e83bfc9288f911602 --- /dev/null +++ b/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/card/card-description.svelte b/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000000000000000000000000000000000000..8366534fb80be5ed2a440e70b60dada23bc9d132 --- /dev/null +++ b/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,20 @@ + + +

    + {@render children?.()} +

    diff --git a/src/lib/components/ui/card/card-footer.svelte b/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000000000000000000000000000000000000..694e3d558c0c6966a6d4543b4ddba4c4ab2a4e8d --- /dev/null +++ b/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/card/card-header.svelte b/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000000000000000000000000000000000000..48509ef47e47995e363e947a0114121818f5504b --- /dev/null +++ b/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/card/card-title.svelte b/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000000000000000000000000000000000000..a86e2af7028ec804f3b2437aeb6ecbefaa108a07 --- /dev/null +++ b/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/card/card.svelte b/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ade8bef7b82070189e20771444200561d649e47b --- /dev/null +++ b/src/lib/components/ui/card/card.svelte @@ -0,0 +1,22 @@ + + +
    img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg group/card flex flex-col", className)} + {...restProps} +> + {@render children?.()} +
    diff --git a/src/lib/components/ui/card/index.ts b/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..4d3fce489d33b48b20ba5b7fa40e15043daae01a --- /dev/null +++ b/src/lib/components/ui/card/index.ts @@ -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, +}; diff --git a/src/lib/components/ui/chart/chart-container.svelte b/src/lib/components/ui/chart/chart-container.svelte new file mode 100644 index 0000000000000000000000000000000000000000..1eb8e39061cefbad94b8034f3164265f8f61862c --- /dev/null +++ b/src/lib/components/ui/chart/chart-container.svelte @@ -0,0 +1,80 @@ + + +
    + + {@render children?.()} +
    diff --git a/src/lib/components/ui/chart/chart-style.svelte b/src/lib/components/ui/chart/chart-style.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ea1b3b20051f202c51bda620192e98f7efba2102 --- /dev/null +++ b/src/lib/components/ui/chart/chart-style.svelte @@ -0,0 +1,37 @@ + + +{#if themeContents} + {#key id} + + {themeContents} + + {/key} +{/if} diff --git a/src/lib/components/ui/chart/chart-tooltip.svelte b/src/lib/components/ui/chart/chart-tooltip.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e0da9ca91e6a8065e586e0028f7c30d50ac97aa7 --- /dev/null +++ b/src/lib/components/ui/chart/chart-tooltip.svelte @@ -0,0 +1,184 @@ + + +{#snippet TooltipLabel()} + {#if formattedLabel} +
    + {#if typeof formattedLabel === "function"} + {@render formattedLabel()} + {:else} + {formattedLabel} + {/if} +
    + {/if} +{/snippet} + + +
    + {#if !nestLabel} + {@render TooltipLabel()} + {/if} +
    + {#each visibleSeries as item, i (item.key + i)} + {@const key = `${nameKey || item.key || item.label || "value"}`} + {@const itemConfig = getPayloadConfigFromPayload( + chart.config, + item, + key, + chartCtx.tooltip.data + )} + {@const indicatorColor = color || item.config?.color || item.color} +
    svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5", + indicator === "dot" && "items-center" + )} + > + {#if formatter && item.value !== undefined && item.label} + {@render formatter({ + value: item.value, + name: item.label, + item, + index: i, + payload: visibleSeries, + })} + {:else} + {#if itemConfig?.icon} + + {:else if !hideIndicator} +
    + {/if} +
    +
    + {#if nestLabel} + {@render TooltipLabel()} + {/if} + + {itemConfig?.label || item.label} + +
    + {#if item.value !== undefined} + + {item.value.toLocaleString()} + + {/if} +
    + {/if} +
    + {/each} +
    +
    +
    diff --git a/src/lib/components/ui/chart/chart-utils.ts b/src/lib/components/ui/chart/chart-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..a289e355cfa6ae61925f890df1e0b9f70b81f3a6 --- /dev/null +++ b/src/lib/components/ui/chart/chart-utils.ts @@ -0,0 +1,68 @@ +import type { Tooltip } from "layerchart"; +import { getContext, setContext, type Component, type Snippet } from "svelte"; + +export const THEMES = { light: "", dark: ".dark" } as const; + +export type ChartConfig = { + [k in string]: { + label?: string; + icon?: Component; + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ); +}; + +export type ExtractSnippetParams = T extends Snippet<[infer P]> ? P : never; + +export type TooltipPayload = Tooltip.TooltipSeries; + +// Helper to extract item config from a payload. +export function getPayloadConfigFromPayload( + config: ChartConfig, + payload: TooltipPayload, + key: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data?: Record | null +) { + if (typeof payload !== "object" || payload === null) return undefined; + + const payloadConfig = + "config" in payload && typeof payload.config === "object" && payload.config !== null + ? payload.config + : undefined; + + let configLabelKey: string = key; + + if (payload.key === key) { + configLabelKey = payload.key; + } else if (payload.label === key) { + configLabelKey = payload.label; + } else if (key in payload && typeof payload[key as keyof typeof payload] === "string") { + configLabelKey = payload[key as keyof typeof payload] as string; + } else if ( + payloadConfig !== undefined && + key in payloadConfig && + typeof payloadConfig[key as keyof typeof payloadConfig] === "string" + ) { + configLabelKey = payloadConfig[key as keyof typeof payloadConfig] as string; + } else if (data != null && key in data && typeof data[key] === "string") { + configLabelKey = data[key] as string; + } + + return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config]; +} + +type ChartContextValue = { + config: ChartConfig; +}; + +const chartContextKey = Symbol("chart-context"); + +export function setChartContext(value: ChartContextValue) { + return setContext(chartContextKey, value); +} + +export function useChart() { + return getContext(chartContextKey); +} diff --git a/src/lib/components/ui/chart/index.ts b/src/lib/components/ui/chart/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..f22375e09f5b261b9de0d7bc4f3091d9c86e29e2 --- /dev/null +++ b/src/lib/components/ui/chart/index.ts @@ -0,0 +1,6 @@ +import ChartContainer from "./chart-container.svelte"; +import ChartTooltip from "./chart-tooltip.svelte"; + +export { getPayloadConfigFromPayload, type ChartConfig } from "./chart-utils.js"; + +export { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip }; diff --git a/src/lib/components/ui/checkbox/checkbox.svelte b/src/lib/components/ui/checkbox/checkbox.svelte new file mode 100644 index 0000000000000000000000000000000000000000..26db4a0d322b49e563672151e6beb2be1918be03 --- /dev/null +++ b/src/lib/components/ui/checkbox/checkbox.svelte @@ -0,0 +1,39 @@ + + + + {#snippet children({ checked, indeterminate })} +
    + {#if checked} + + {:else if indeterminate} + + {/if} +
    + {/snippet} +
    diff --git a/src/lib/components/ui/checkbox/index.ts b/src/lib/components/ui/checkbox/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d92d94580b03ee0c024a2f9daffcc21673d58be --- /dev/null +++ b/src/lib/components/ui/checkbox/index.ts @@ -0,0 +1,6 @@ +import Root from "./checkbox.svelte"; +export { + Root, + // + Root as Checkbox, +}; diff --git a/src/lib/components/ui/collapsible/collapsible-content.svelte b/src/lib/components/ui/collapsible/collapsible-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..bdabb559c944d8d51059928b269d84d95180847a --- /dev/null +++ b/src/lib/components/ui/collapsible/collapsible-content.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/collapsible/collapsible-trigger.svelte b/src/lib/components/ui/collapsible/collapsible-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ece7ad68ec1321c462b94abb50a0076184e427c3 --- /dev/null +++ b/src/lib/components/ui/collapsible/collapsible-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/collapsible/collapsible.svelte b/src/lib/components/ui/collapsible/collapsible.svelte new file mode 100644 index 0000000000000000000000000000000000000000..39cdd4e4e008b65f806a7046a5fb2aba2736f180 --- /dev/null +++ b/src/lib/components/ui/collapsible/collapsible.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/lib/components/ui/collapsible/index.ts b/src/lib/components/ui/collapsible/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..169b4791d3e18efcc8d15d86e54a08f909d856f0 --- /dev/null +++ b/src/lib/components/ui/collapsible/index.ts @@ -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, +}; diff --git a/src/lib/components/ui/command/command-dialog.svelte b/src/lib/components/ui/command/command-dialog.svelte new file mode 100644 index 0000000000000000000000000000000000000000..c5c7ec8d62c4e3de0fd6afaf6809f14a5aff03b6 --- /dev/null +++ b/src/lib/components/ui/command/command-dialog.svelte @@ -0,0 +1,42 @@ + + + + + {title} + {description} + + + + + diff --git a/src/lib/components/ui/command/command-empty.svelte b/src/lib/components/ui/command/command-empty.svelte new file mode 100644 index 0000000000000000000000000000000000000000..c0bb44de908439123fec5e47ada6d0be6a865b43 --- /dev/null +++ b/src/lib/components/ui/command/command-empty.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/command/command-group.svelte b/src/lib/components/ui/command/command-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ed598607f52969eb4577b47a5df3c20dd2f69662 --- /dev/null +++ b/src/lib/components/ui/command/command-group.svelte @@ -0,0 +1,32 @@ + + + + {#if heading} + + {heading} + + {/if} + + diff --git a/src/lib/components/ui/command/command-input.svelte b/src/lib/components/ui/command/command-input.svelte new file mode 100644 index 0000000000000000000000000000000000000000..fa9b998b55268b94133f99e9923218c232bc8822 --- /dev/null +++ b/src/lib/components/ui/command/command-input.svelte @@ -0,0 +1,34 @@ + + +
    + + + {#snippet child({ props })} + + {/snippet} + + + + + +
    diff --git a/src/lib/components/ui/command/command-item.svelte b/src/lib/components/ui/command/command-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7072efb170730e4c44988137c44691e63f90bdaa --- /dev/null +++ b/src/lib/components/ui/command/command-item.svelte @@ -0,0 +1,25 @@ + + + + {@render children?.()} + + diff --git a/src/lib/components/ui/command/command-link-item.svelte b/src/lib/components/ui/command/command-link-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ada6d2c440df01ffb387d5a11c9bbf39c59eb2c3 --- /dev/null +++ b/src/lib/components/ui/command/command-link-item.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/command/command-list.svelte b/src/lib/components/ui/command/command-list.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f663bd3e5f31f8b5e8e812ac7914b6eb67d6e997 --- /dev/null +++ b/src/lib/components/ui/command/command-list.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/command/command-loading.svelte b/src/lib/components/ui/command/command-loading.svelte new file mode 100644 index 0000000000000000000000000000000000000000..19dd298559f6acb4dc595191136638361ac9bddc --- /dev/null +++ b/src/lib/components/ui/command/command-loading.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/command/command-separator.svelte b/src/lib/components/ui/command/command-separator.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ba4df0458cc913d82918d1563d75e595ec70aff1 --- /dev/null +++ b/src/lib/components/ui/command/command-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/command/command-shortcut.svelte b/src/lib/components/ui/command/command-shortcut.svelte new file mode 100644 index 0000000000000000000000000000000000000000..15e04e010eae3ab2c8d1dee6d08970965d98bd8c --- /dev/null +++ b/src/lib/components/ui/command/command-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/command/command.svelte b/src/lib/components/ui/command/command.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7d6b2595c7d8d492f5c0b4f69217eca6c24ed3dc --- /dev/null +++ b/src/lib/components/ui/command/command.svelte @@ -0,0 +1,25 @@ + + + diff --git a/src/lib/components/ui/command/index.ts b/src/lib/components/ui/command/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5435fbe82fabdbf16ac57b34658498a1c1a45442 --- /dev/null +++ b/src/lib/components/ui/command/index.ts @@ -0,0 +1,37 @@ +import Root from "./command.svelte"; +import Loading from "./command-loading.svelte"; +import Dialog from "./command-dialog.svelte"; +import Empty from "./command-empty.svelte"; +import Group from "./command-group.svelte"; +import Item from "./command-item.svelte"; +import Input from "./command-input.svelte"; +import List from "./command-list.svelte"; +import Separator from "./command-separator.svelte"; +import Shortcut from "./command-shortcut.svelte"; +import LinkItem from "./command-link-item.svelte"; + +export { + Root, + Dialog, + Empty, + Group, + Item, + LinkItem, + Input, + List, + Separator, + Shortcut, + Loading, + // + Root as Command, + Dialog as CommandDialog, + Empty as CommandEmpty, + Group as CommandGroup, + Item as CommandItem, + LinkItem as CommandLinkItem, + Input as CommandInput, + List as CommandList, + Separator as CommandSeparator, + Shortcut as CommandShortcut, + Loading as CommandLoading, +}; diff --git a/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte b/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..6da639bed732bfc5abaff9fdb497ca105c135df6 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-checkbox-item.svelte @@ -0,0 +1,41 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/src/lib/components/ui/context-menu/context-menu-content.svelte b/src/lib/components/ui/context-menu/context-menu-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7594a559a1f37835b9980fb598337e0f2e461c23 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-content.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/src/lib/components/ui/context-menu/context-menu-group-heading.svelte b/src/lib/components/ui/context-menu/context-menu-group-heading.svelte new file mode 100644 index 0000000000000000000000000000000000000000..0c0f45991027a1066501c80c1d51d67ca84ed5ce --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-group-heading.svelte @@ -0,0 +1,21 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu-group.svelte b/src/lib/components/ui/context-menu/context-menu-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..c7c1e06add7eb43742e53fba4cfe2f0fdde3f5c4 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu-item.svelte b/src/lib/components/ui/context-menu/context-menu-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..82af7b5b006e03eb243342245c68057715c42d39 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu-label.svelte b/src/lib/components/ui/context-menu/context-menu-label.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e13301ee668dbdf705e238415d123ab905b7f7ed --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-label.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/context-menu/context-menu-portal.svelte b/src/lib/components/ui/context-menu/context-menu-portal.svelte new file mode 100644 index 0000000000000000000000000000000000000000..96b1e3e40821c660e3d8cb2067e8e2908d2900c7 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu-radio-group.svelte b/src/lib/components/ui/context-menu/context-menu-radio-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..964cb55fdc8d8db61d27c29e4dffebd1cd898dbd --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu-radio-item.svelte b/src/lib/components/ui/context-menu/context-menu-radio-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..6c6540b5d2663cd96b7242847afccd4fc784000b --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-radio-item.svelte @@ -0,0 +1,35 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.({ checked })} + {/snippet} + diff --git a/src/lib/components/ui/context-menu/context-menu-separator.svelte b/src/lib/components/ui/context-menu/context-menu-separator.svelte new file mode 100644 index 0000000000000000000000000000000000000000..31cfcc89028520e5e1d1eb5a2dd3875505eb3ecd --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu-shortcut.svelte b/src/lib/components/ui/context-menu/context-menu-shortcut.svelte new file mode 100644 index 0000000000000000000000000000000000000000..eb514bf629fe1dff48ae0e922bcc77a6b8c81ba6 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/context-menu/context-menu-sub-content.svelte b/src/lib/components/ui/context-menu/context-menu-sub-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..34b973ee022936436c01f6517e75fb2a46e70dd2 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-sub-content.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte b/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ea658bc115b6dd14da8274e60e1f46e43daa969e --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/src/lib/components/ui/context-menu/context-menu-sub.svelte b/src/lib/components/ui/context-menu/context-menu-sub.svelte new file mode 100644 index 0000000000000000000000000000000000000000..a03827b9e39f4aab3e795624c1ebf63e94c6f7d6 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-sub.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu-trigger.svelte b/src/lib/components/ui/context-menu/context-menu-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f6a1542f1264e93cb07b3200a592e40f1fd33751 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu-trigger.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/context-menu/context-menu.svelte b/src/lib/components/ui/context-menu/context-menu.svelte new file mode 100644 index 0000000000000000000000000000000000000000..cfaefb398ef7da599c55d9eb4b8d4066684dddf5 --- /dev/null +++ b/src/lib/components/ui/context-menu/context-menu.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/context-menu/index.ts b/src/lib/components/ui/context-menu/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..cbeaee10550ffc218c98e9481529d1b44afa02dc --- /dev/null +++ b/src/lib/components/ui/context-menu/index.ts @@ -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, +}; diff --git a/src/lib/components/ui/data-table/data-table.svelte.ts b/src/lib/components/ui/data-table/data-table.svelte.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3c30a7f9b47677a979a68066e024e9057e05a1a --- /dev/null +++ b/src/lib/components/ui/data-table/data-table.svelte.ts @@ -0,0 +1,142 @@ +import { + type RowData, + type TableOptions, + type TableOptionsResolved, + type TableState, + type Updater, + createTable, +} from "@tanstack/table-core"; + +/** + * Creates a reactive TanStack table object for Svelte. + * @param options Table options to create the table with. + * @returns A reactive table object. + * @example + * ```svelte + * + * + * + * + * {#each table.getHeaderGroups() as headerGroup} + * + * {#each headerGroup.headers as header} + * + * {/each} + * + * {/each} + * + * + *
    + * + *
    + * ``` + */ +export function createSvelteTable(options: TableOptions) { + const resolvedOptions: TableOptionsResolved = mergeObjects( + { + state: {}, + onStateChange() {}, + renderFallbackValue: null, + mergeOptions: ( + defaultOptions: TableOptions, + options: Partial> + ) => { + return mergeObjects(defaultOptions, options); + }, + }, + options + ); + + const table = createTable(resolvedOptions); + let state = $state(table.initialState); + + function updateOptions() { + table.setOptions(() => { + return mergeObjects(resolvedOptions, options, { + state: mergeObjects(state, options.state || {}), + + onStateChange: (updater: Updater) => { + if (updater instanceof Function) state = updater(state); + else state = mergeObjects(state, updater); + + options.onStateChange?.(updater); + }, + }); + }); + } + + updateOptions(); + + $effect.pre(() => { + updateOptions(); + }); + + return table; +} + +type MaybeThunk = T | (() => T | null | undefined); +type Intersection = (T extends [infer H, ...infer R] + ? H & Intersection + : unknown) & {}; + +/** + * Lazily merges several objects (or thunks) while preserving + * getter semantics from every source. + * + * Proxy-based to avoid known WebKit recursion issue. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function mergeObjects[]>( + ...sources: Sources +): Intersection<{ [K in keyof Sources]: Sources[K] }> { + const resolve = (src: MaybeThunk): T | undefined => + typeof src === "function" ? (src() ?? undefined) : src; + + const findSourceWithKey = (key: PropertyKey) => { + for (let i = sources.length - 1; i >= 0; i--) { + const obj = resolve(sources[i]); + if (obj && key in obj) return obj; + } + return undefined; + }; + + return new Proxy(Object.create(null), { + get(_, key) { + const src = findSourceWithKey(key); + + return src?.[key as never]; + }, + + has(_, key) { + return !!findSourceWithKey(key); + }, + + ownKeys(): (string | symbol)[] { + // eslint-disable-next-line svelte/prefer-svelte-reactivity + const all = new Set(); + for (const s of sources) { + const obj = resolve(s); + if (obj) { + for (const k of Reflect.ownKeys(obj) as (string | symbol)[]) { + all.add(k); + } + } + } + return [...all]; + }, + + getOwnPropertyDescriptor(_, key) { + const src = findSourceWithKey(key); + if (!src) return undefined; + return { + configurable: true, + enumerable: true, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: (src as any)[key], + writable: true, + }; + }, + }) as Intersection<{ [K in keyof Sources]: Sources[K] }>; +} diff --git a/src/lib/components/ui/data-table/flex-render.svelte b/src/lib/components/ui/data-table/flex-render.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ac82a581e99a843a85036e0a8194645e06496a75 --- /dev/null +++ b/src/lib/components/ui/data-table/flex-render.svelte @@ -0,0 +1,40 @@ + + +{#if typeof content === "string"} + {content} +{:else if content instanceof Function} + + + {@const result = content(context as any)} + {#if result instanceof RenderComponentConfig} + {@const { component: Component, props } = result} + + {:else if result instanceof RenderSnippetConfig} + {@const { snippet, params } = result} + {@render snippet({ ...params, attach })} + {:else} + {result} + {/if} +{/if} diff --git a/src/lib/components/ui/data-table/index.ts b/src/lib/components/ui/data-table/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f4e77ea06f34b1ec26e02ea095ca9560e1c906b --- /dev/null +++ b/src/lib/components/ui/data-table/index.ts @@ -0,0 +1,3 @@ +export { default as FlexRender } from "./flex-render.svelte"; +export { renderComponent, renderSnippet } from "./render-helpers.js"; +export { createSvelteTable } from "./data-table.svelte.js"; diff --git a/src/lib/components/ui/data-table/render-helpers.ts b/src/lib/components/ui/data-table/render-helpers.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa036d62af0457a0c46ff2e4c3ff205f24d6844f --- /dev/null +++ b/src/lib/components/ui/data-table/render-helpers.ts @@ -0,0 +1,111 @@ +import type { Component, ComponentProps, Snippet } from "svelte"; + +/** + * A helper class to make it easy to identify Svelte components in + * `columnDef.cell` and `columnDef.header` properties. + * + * > NOTE: This class should only be used internally by the adapter. If you're + * reading this and you don't know what this is for, you probably don't need it. + * + * @example + * ```svelte + * {@const result = content(context as any)} + * {#if result instanceof RenderComponentConfig} + * {@const { component: Component, props } = result} + * + * {/if} + * ``` + */ +export class RenderComponentConfig { + component: TComponent; + props: ComponentProps | Record; + constructor( + component: TComponent, + props: ComponentProps | Record = {} + ) { + this.component = component; + this.props = props; + } +} + +/** + * A helper class to make it easy to identify Svelte Snippets in `columnDef.cell` and `columnDef.header` properties. + * + * > NOTE: This class should only be used internally by the adapter. If you're + * reading this and you don't know what this is for, you probably don't need it. + * + * @example + * ```svelte + * {@const result = content(context as any)} + * {#if result instanceof RenderSnippetConfig} + * {@const { snippet, params } = result} + * {@render snippet(params)} + * {/if} + * ``` + */ +export class RenderSnippetConfig { + snippet: Snippet<[TProps]>; + params: TProps; + constructor(snippet: Snippet<[TProps]>, params: TProps) { + this.snippet = snippet; + this.params = params; + } +} + +/** + * A helper function to help create cells from Svelte components through ColumnDef's `cell` and `header` properties. + * + * This is only to be used with Svelte Components - use `renderSnippet` for Svelte Snippets. + * + * @param component A Svelte component + * @param props The props to pass to `component` + * @returns A `RenderComponentConfig` object that helps svelte-table know how to render the header/cell component. + * @example + * ```ts + * // +page.svelte + * const defaultColumns = [ + * columnHelper.accessor('name', { + * header: header => renderComponent(SortHeader, { label: 'Name', header }), + * }), + * columnHelper.accessor('state', { + * header: header => renderComponent(SortHeader, { label: 'State', header }), + * }), + * ] + * ``` + * @see {@link https://tanstack.com/table/latest/docs/guide/column-defs} + */ +export function renderComponent< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends Component, + Props extends ComponentProps, +>(component: T, props: Props = {} as Props) { + return new RenderComponentConfig(component, props); +} + +/** + * A helper function to help create cells from Svelte Snippets through ColumnDef's `cell` and `header` properties. + * + * The snippet must only take one parameter. + * + * This is only to be used with Snippets - use `renderComponent` for Svelte Components. + * + * @param snippet + * @param params + * @returns - A `RenderSnippetConfig` object that helps svelte-table know how to render the header/cell snippet. + * @example + * ```ts + * // +page.svelte + * const defaultColumns = [ + * columnHelper.accessor('name', { + * cell: cell => renderSnippet(nameSnippet, { name: cell.row.name }), + * }), + * columnHelper.accessor('state', { + * cell: cell => renderSnippet(stateSnippet, { state: cell.row.state }), + * }), + * ] + * ``` + * @see {@link https://tanstack.com/table/latest/docs/guide/column-defs} + */ +export function renderSnippet(snippet: Snippet<[TProps]>, params: TProps = {} as TProps) { + return new RenderSnippetConfig(snippet, params); +} diff --git a/src/lib/components/ui/dialog/dialog-close.svelte b/src/lib/components/ui/dialog/dialog-close.svelte new file mode 100644 index 0000000000000000000000000000000000000000..de68f2f0ec635d4121aab53ea7d0bf8e98f44ee5 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-close.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-content.svelte b/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..c3dc754c9d544f5ff560d9ac424bc787ff0c3bc9 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,48 @@ + + + + + + {@render children?.()} + {#if showCloseButton} + + {#snippet child({ props })} + + {/snippet} + + {/if} + + diff --git a/src/lib/components/ui/dialog/dialog-description.svelte b/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000000000000000000000000000000000000..bd220ac8de1bc04abd4550120037727046979544 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-footer.svelte b/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000000000000000000000000000000000000..56858957f2fc2acdff07bc48168765c9e3fa2a47 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,32 @@ + + +
    + {@render children?.()} + {#if showCloseButton} + + {#snippet child({ props })} + + {/snippet} + + {/if} +
    diff --git a/src/lib/components/ui/dialog/dialog-header.svelte b/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000000000000000000000000000000000000..c1abee0cbc86e57740d4a500139fa287e5e44cb2 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000000000000000000000000000000000000..5db284113a48eb46a376dff44f438bffc0676aed --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-portal.svelte b/src/lib/components/ui/dialog/dialog-portal.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ccfa79caf3c3f8b9ea33419ebb386c3a725ce760 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-title.svelte b/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000000000000000000000000000000000000..a17f57328766d7799c6a9b83c411952052010062 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-trigger.svelte b/src/lib/components/ui/dialog/dialog-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..589ee0c3d0bbe8785621329592a805377c68448f --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-trigger.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog.svelte b/src/lib/components/ui/dialog/dialog.svelte new file mode 100644 index 0000000000000000000000000000000000000000..211672c6d1c812933a3a39c7a30c89f0299afe61 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dialog/index.ts b/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..076cef5283010dac66695cac4c8fbf58d3e04e85 --- /dev/null +++ b/src/lib/components/ui/dialog/index.ts @@ -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, +}; diff --git a/src/lib/components/ui/drawer/drawer-close.svelte b/src/lib/components/ui/drawer/drawer-close.svelte new file mode 100644 index 0000000000000000000000000000000000000000..95c247963a2997644b7a03ad4ddc2c3f917cf0db --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-content.svelte b/src/lib/components/ui/drawer/drawer-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..3a2ae5b6dda0c16558233cf7775ecd1e0790e9c1 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-content.svelte @@ -0,0 +1,33 @@ + + + + + + + {@render children?.()} + + diff --git a/src/lib/components/ui/drawer/drawer-description.svelte b/src/lib/components/ui/drawer/drawer-description.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ded26d2ae306aff639646f6b62d0a22122fa5eba --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-footer.svelte b/src/lib/components/ui/drawer/drawer-footer.svelte new file mode 100644 index 0000000000000000000000000000000000000000..56a1fc6e9f0854582014a872f85357a757d7a728 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-footer.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/drawer/drawer-header.svelte b/src/lib/components/ui/drawer/drawer-header.svelte new file mode 100644 index 0000000000000000000000000000000000000000..b746dc64691c89011d7e36644834cad576bf3e9b --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/drawer/drawer-nested.svelte b/src/lib/components/ui/drawer/drawer-nested.svelte new file mode 100644 index 0000000000000000000000000000000000000000..834af9461301bdb3d5e709e006474ed0c03720a3 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-nested.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-overlay.svelte b/src/lib/components/ui/drawer/drawer-overlay.svelte new file mode 100644 index 0000000000000000000000000000000000000000..714f2390ba33c2ea310bde0e6ac863cdec581adb --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-portal.svelte b/src/lib/components/ui/drawer/drawer-portal.svelte new file mode 100644 index 0000000000000000000000000000000000000000..5a0dd74032d2592256305ce32337c7c670aef0e6 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-title.svelte b/src/lib/components/ui/drawer/drawer-title.svelte new file mode 100644 index 0000000000000000000000000000000000000000..48c450ec3c91beae552f2c142bbfdeb7509b7d21 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-trigger.svelte b/src/lib/components/ui/drawer/drawer-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f1877d8e215d729ac1ca3bef3ae23ce7a27dfe3e --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer.svelte b/src/lib/components/ui/drawer/drawer.svelte new file mode 100644 index 0000000000000000000000000000000000000000..0cb57ff99cd90955ec460cb0a50e6ecfb2fb5577 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/components/ui/drawer/index.ts b/src/lib/components/ui/drawer/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1656cac5414cb98b03a4481cf79a5365364c2950 --- /dev/null +++ b/src/lib/components/ui/drawer/index.ts @@ -0,0 +1,38 @@ +import Root from "./drawer.svelte"; +import Content from "./drawer-content.svelte"; +import Description from "./drawer-description.svelte"; +import Overlay from "./drawer-overlay.svelte"; +import Footer from "./drawer-footer.svelte"; +import Header from "./drawer-header.svelte"; +import Title from "./drawer-title.svelte"; +import NestedRoot from "./drawer-nested.svelte"; +import Close from "./drawer-close.svelte"; +import Trigger from "./drawer-trigger.svelte"; +import Portal from "./drawer-portal.svelte"; + +export { + Root, + NestedRoot, + Content, + Description, + Overlay, + Footer, + Header, + Title, + Trigger, + Portal, + Close, + + // + Root as Drawer, + NestedRoot as DrawerNestedRoot, + Content as DrawerContent, + Description as DrawerDescription, + Overlay as DrawerOverlay, + Footer as DrawerFooter, + Header as DrawerHeader, + Title as DrawerTitle, + Trigger as DrawerTrigger, + Portal as DrawerPortal, + Close as DrawerClose, +}; diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e0e19718792ba6132d973584fa7952502505c91b --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ff84b144afa6949f72808bd9eed0b6eee1caba58 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte @@ -0,0 +1,44 @@ + + + + {#snippet children({ checked, indeterminate })} + + {#if indeterminate} + + {:else if checked} + + {/if} + + {@render childrenProp?.()} + {/snippet} + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..261a5b08bd427dcde1bdc05462f4ec194a017bad --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte new file mode 100644 index 0000000000000000000000000000000000000000..433540fd2559a98d9080d8270be93a38521b2d07 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..aca1f7bd554e2269bcabb6475ea5362a947f5c90 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..d23d05be294ad231b1c95c150142d252a24cbc49 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e9cbf5811b17b450dac9324e1eeb417b4ee6dd06 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte new file mode 100644 index 0000000000000000000000000000000000000000..274cfef78cb405a9e83a1361b5234b9c4b605df1 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..189aef40e44212dd3da08a692ae1a7d2f5066fe4 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..9aaf1dfff2ad3afad0395bd5b77cf5fc291d24ba --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte @@ -0,0 +1,34 @@ + + + + {#snippet children({ checked })} + + {#if checked} + + {/if} + + {@render childrenProp?.({ checked })} + {/snippet} + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte new file mode 100644 index 0000000000000000000000000000000000000000..1d0cc5783fb0060600dc50e5d44f8588beaa5a08 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte new file mode 100644 index 0000000000000000000000000000000000000000..30e292b9606229869182961865d936787aaf9df2 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..b3df26fff2135174c0d54f8cfc6c33e93c3c1704 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..cc2809cd68bf80b51f420f3d002454a2d61f372d --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f04458139282dea8a85bfac596954b2e3526aefe --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..cb053444d76caba0c74a634f8b12575cf07dc716 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte new file mode 100644 index 0000000000000000000000000000000000000000..cb4bc621c2668dbd0ab8de0111ced8b8fba8f44f --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dropdown-menu/index.ts b/src/lib/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..7850c6a3000a144cda696b7588b912e1de1943f2 --- /dev/null +++ b/src/lib/components/ui/dropdown-menu/index.ts @@ -0,0 +1,54 @@ +import Root from "./dropdown-menu.svelte"; +import Sub from "./dropdown-menu-sub.svelte"; +import CheckboxGroup from "./dropdown-menu-checkbox-group.svelte"; +import CheckboxItem from "./dropdown-menu-checkbox-item.svelte"; +import Content from "./dropdown-menu-content.svelte"; +import Group from "./dropdown-menu-group.svelte"; +import Item from "./dropdown-menu-item.svelte"; +import Label from "./dropdown-menu-label.svelte"; +import RadioGroup from "./dropdown-menu-radio-group.svelte"; +import RadioItem from "./dropdown-menu-radio-item.svelte"; +import Separator from "./dropdown-menu-separator.svelte"; +import Shortcut from "./dropdown-menu-shortcut.svelte"; +import Trigger from "./dropdown-menu-trigger.svelte"; +import SubContent from "./dropdown-menu-sub-content.svelte"; +import SubTrigger from "./dropdown-menu-sub-trigger.svelte"; +import GroupHeading from "./dropdown-menu-group-heading.svelte"; +import Portal from "./dropdown-menu-portal.svelte"; + +export { + CheckboxGroup, + CheckboxItem, + Content, + Portal, + Root as DropdownMenu, + CheckboxGroup as DropdownMenuCheckboxGroup, + CheckboxItem as DropdownMenuCheckboxItem, + Content as DropdownMenuContent, + Portal as DropdownMenuPortal, + Group as DropdownMenuGroup, + Item as DropdownMenuItem, + Label as DropdownMenuLabel, + RadioGroup as DropdownMenuRadioGroup, + RadioItem as DropdownMenuRadioItem, + Separator as DropdownMenuSeparator, + Shortcut as DropdownMenuShortcut, + Sub as DropdownMenuSub, + SubContent as DropdownMenuSubContent, + SubTrigger as DropdownMenuSubTrigger, + Trigger as DropdownMenuTrigger, + GroupHeading as DropdownMenuGroupHeading, + Group, + GroupHeading, + Item, + Label, + RadioGroup, + RadioItem, + Root, + Separator, + Shortcut, + Sub, + SubContent, + SubTrigger, + Trigger, +}; diff --git a/src/lib/components/ui/empty/empty-content.svelte b/src/lib/components/ui/empty/empty-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..d43cc00b26612f6a740b4b8a18fa313e2727184c --- /dev/null +++ b/src/lib/components/ui/empty/empty-content.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/empty/empty-description.svelte b/src/lib/components/ui/empty/empty-description.svelte new file mode 100644 index 0000000000000000000000000000000000000000..9e8cd78000fc5f90740b4c0d5f63dddd17c72d4c --- /dev/null +++ b/src/lib/components/ui/empty/empty-description.svelte @@ -0,0 +1,23 @@ + + +
    a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4", + className + )} + {...restProps} +> + {@render children?.()} +
    diff --git a/src/lib/components/ui/empty/empty-header.svelte b/src/lib/components/ui/empty/empty-header.svelte new file mode 100644 index 0000000000000000000000000000000000000000..9b76538ad0b8a292501788506c45a15309a754cd --- /dev/null +++ b/src/lib/components/ui/empty/empty-header.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/empty/empty-media.svelte b/src/lib/components/ui/empty/empty-media.svelte new file mode 100644 index 0000000000000000000000000000000000000000..15739ef039b1a7cb96c072f4111d3427784f66f4 --- /dev/null +++ b/src/lib/components/ui/empty/empty-media.svelte @@ -0,0 +1,41 @@ + + + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/empty/empty-title.svelte b/src/lib/components/ui/empty/empty-title.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f895e5f9606ceca83318ffc20acf55075c5d976a --- /dev/null +++ b/src/lib/components/ui/empty/empty-title.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/empty/empty.svelte b/src/lib/components/ui/empty/empty.svelte new file mode 100644 index 0000000000000000000000000000000000000000..3aef06241687286a3135487c0c95f9de9602f356 --- /dev/null +++ b/src/lib/components/ui/empty/empty.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/empty/index.ts b/src/lib/components/ui/empty/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae4c106ee5fc058769da35a628a39edb4999bedb --- /dev/null +++ b/src/lib/components/ui/empty/index.ts @@ -0,0 +1,22 @@ +import Root from "./empty.svelte"; +import Header from "./empty-header.svelte"; +import Media from "./empty-media.svelte"; +import Title from "./empty-title.svelte"; +import Description from "./empty-description.svelte"; +import Content from "./empty-content.svelte"; + +export { + Root, + Header, + Media, + Title, + Description, + Content, + // + Root as Empty, + Header as EmptyHeader, + Media as EmptyMedia, + Title as EmptyTitle, + Description as EmptyDescription, + Content as EmptyContent, +}; diff --git a/src/lib/components/ui/field/field-content.svelte b/src/lib/components/ui/field/field-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..1545c43502ac9c06487a7d895f0184f3c721c1da --- /dev/null +++ b/src/lib/components/ui/field/field-content.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/field/field-description.svelte b/src/lib/components/ui/field/field-description.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f033c10f76da8d556be97ca90c3b7560b9972633 --- /dev/null +++ b/src/lib/components/ui/field/field-description.svelte @@ -0,0 +1,25 @@ + + +

    a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", + className + )} + {...restProps} +> + {@render children?.()} +

    diff --git a/src/lib/components/ui/field/field-error.svelte b/src/lib/components/ui/field/field-error.svelte new file mode 100644 index 0000000000000000000000000000000000000000..88262d2e7f20db222cd2ff97a358a3d8e7142c92 --- /dev/null +++ b/src/lib/components/ui/field/field-error.svelte @@ -0,0 +1,58 @@ + + +{#if hasContent} + +{/if} diff --git a/src/lib/components/ui/field/field-group.svelte b/src/lib/components/ui/field/field-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..2558a3cbe8c762dc4a8b492a4231cec1e4942c01 --- /dev/null +++ b/src/lib/components/ui/field/field-group.svelte @@ -0,0 +1,23 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/field/field-label.svelte b/src/lib/components/ui/field/field-label.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7f63237f3e8ee4a96a3d9450f1d8ae979fe0c7d1 --- /dev/null +++ b/src/lib/components/ui/field/field-label.svelte @@ -0,0 +1,25 @@ + + + diff --git a/src/lib/components/ui/field/field-legend.svelte b/src/lib/components/ui/field/field-legend.svelte new file mode 100644 index 0000000000000000000000000000000000000000..1e8c06514ecfa050483fa075b8db1e924daf424b --- /dev/null +++ b/src/lib/components/ui/field/field-legend.svelte @@ -0,0 +1,24 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/field/field-separator.svelte b/src/lib/components/ui/field/field-separator.svelte new file mode 100644 index 0000000000000000000000000000000000000000..e7833dc6899085a1c0596fd135f5e1ce3381a9d0 --- /dev/null +++ b/src/lib/components/ui/field/field-separator.svelte @@ -0,0 +1,35 @@ + + +
    + + {#if children} + + {@render children()} + + {/if} +
    diff --git a/src/lib/components/ui/field/field-set.svelte b/src/lib/components/ui/field/field-set.svelte new file mode 100644 index 0000000000000000000000000000000000000000..27ee3307e731099f73c44d6a7975d531af188a74 --- /dev/null +++ b/src/lib/components/ui/field/field-set.svelte @@ -0,0 +1,20 @@ + + +
    [data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col", className)} + {...restProps} +> + {@render children?.()} +
    diff --git a/src/lib/components/ui/field/field-title.svelte b/src/lib/components/ui/field/field-title.svelte new file mode 100644 index 0000000000000000000000000000000000000000..30c6ce4c44e4b807bf27321fc468764ed201d658 --- /dev/null +++ b/src/lib/components/ui/field/field-title.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/field/field.svelte b/src/lib/components/ui/field/field.svelte new file mode 100644 index 0000000000000000000000000000000000000000..03d23c02a5e4c72674decb969907d71e8975c799 --- /dev/null +++ b/src/lib/components/ui/field/field.svelte @@ -0,0 +1,47 @@ + + + + +
    + {@render children?.()} +
    diff --git a/src/lib/components/ui/field/index.ts b/src/lib/components/ui/field/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a644a956b8c426f8dd0835f551e5976837386ee7 --- /dev/null +++ b/src/lib/components/ui/field/index.ts @@ -0,0 +1,33 @@ +import Field from "./field.svelte"; +import Set from "./field-set.svelte"; +import Legend from "./field-legend.svelte"; +import Group from "./field-group.svelte"; +import Content from "./field-content.svelte"; +import Label from "./field-label.svelte"; +import Title from "./field-title.svelte"; +import Description from "./field-description.svelte"; +import Separator from "./field-separator.svelte"; +import Error from "./field-error.svelte"; + +export { + Field, + Set, + Legend, + Group, + Content, + Label, + Title, + Description, + Separator, + Error, + // + Set as FieldSet, + Legend as FieldLegend, + Group as FieldGroup, + Content as FieldContent, + Label as FieldLabel, + Title as FieldTitle, + Description as FieldDescription, + Separator as FieldSeparator, + Error as FieldError, +}; diff --git a/src/lib/components/ui/form/form-button.svelte b/src/lib/components/ui/form/form-button.svelte new file mode 100644 index 0000000000000000000000000000000000000000..48d393693681cf26c590ec6ea6c61013b9517ebf --- /dev/null +++ b/src/lib/components/ui/form/form-button.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/input-group/input-group-input.svelte b/src/lib/components/ui/input-group/input-group-input.svelte new file mode 100644 index 0000000000000000000000000000000000000000..34a0135b9fd2c6f6f7cdf1bbf859efd61f82fba0 --- /dev/null +++ b/src/lib/components/ui/input-group/input-group-input.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/input-group/input-group-text.svelte b/src/lib/components/ui/input-group/input-group-text.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ba07d22f8888eb1310de7678bc64cead8010f34c --- /dev/null +++ b/src/lib/components/ui/input-group/input-group-text.svelte @@ -0,0 +1,19 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/input-group/input-group-textarea.svelte b/src/lib/components/ui/input-group/input-group-textarea.svelte new file mode 100644 index 0000000000000000000000000000000000000000..94141f622dcd9760d4fc30db7ce3db753abaf796 --- /dev/null +++ b/src/lib/components/ui/input-group/input-group-textarea.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/toggle-group/index.ts b/src/lib/components/ui/toggle-group/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..12b14b96c8ae679a13f818bc88b0a4ecb1eeece7 --- /dev/null +++ b/src/lib/components/ui/toggle-group/index.ts @@ -0,0 +1,10 @@ +import Root from "./toggle-group.svelte"; +import Item from "./toggle-group-item.svelte"; + +export { + Root, + Item, + // + Root as ToggleGroup, + Item as ToggleGroupItem, +}; diff --git a/src/lib/components/ui/toggle-group/toggle-group-item.svelte b/src/lib/components/ui/toggle-group/toggle-group-item.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ce35fbe8a8d61126b7d0e52e2a61562f70a0a436 --- /dev/null +++ b/src/lib/components/ui/toggle-group/toggle-group-item.svelte @@ -0,0 +1,35 @@ + + + diff --git a/src/lib/components/ui/toggle-group/toggle-group.svelte b/src/lib/components/ui/toggle-group/toggle-group.svelte new file mode 100644 index 0000000000000000000000000000000000000000..7835d4fb8aa79bf6159c529fcb0415896ed5122d --- /dev/null +++ b/src/lib/components/ui/toggle-group/toggle-group.svelte @@ -0,0 +1,75 @@ + + + + + + diff --git a/src/lib/components/ui/toggle/index.ts b/src/lib/components/ui/toggle/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8cb2936fc5dfefc8224f40ae16834b90abbba6c2 --- /dev/null +++ b/src/lib/components/ui/toggle/index.ts @@ -0,0 +1,13 @@ +import Root from "./toggle.svelte"; +export { + toggleVariants, + type ToggleSize, + type ToggleVariant, + type ToggleVariants, +} from "./toggle.svelte"; + +export { + Root, + // + Root as Toggle, +}; diff --git a/src/lib/components/ui/toggle/toggle.svelte b/src/lib/components/ui/toggle/toggle.svelte new file mode 100644 index 0000000000000000000000000000000000000000..30935b4139954506e5a6f9d14496161559d79820 --- /dev/null +++ b/src/lib/components/ui/toggle/toggle.svelte @@ -0,0 +1,51 @@ + + + + + diff --git a/src/lib/components/ui/tooltip/index.ts b/src/lib/components/ui/tooltip/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..171860421073ed4cb86a97490e3937d7c9a001bb --- /dev/null +++ b/src/lib/components/ui/tooltip/index.ts @@ -0,0 +1,19 @@ +import Root from "./tooltip.svelte"; +import Trigger from "./tooltip-trigger.svelte"; +import Content from "./tooltip-content.svelte"; +import Provider from "./tooltip-provider.svelte"; +import Portal from "./tooltip-portal.svelte"; + +export { + Root, + Trigger, + Content, + Provider, + Portal, + // + Root as Tooltip, + Content as TooltipContent, + Trigger as TooltipTrigger, + Provider as TooltipProvider, + Portal as TooltipPortal, +}; diff --git a/src/lib/components/ui/tooltip/tooltip-content.svelte b/src/lib/components/ui/tooltip/tooltip-content.svelte new file mode 100644 index 0000000000000000000000000000000000000000..0cf06949738f36c9ae6fe2925a908cde4484c5bc --- /dev/null +++ b/src/lib/components/ui/tooltip/tooltip-content.svelte @@ -0,0 +1,52 @@ + + + + + {@render children?.()} + + {#snippet child({ props })} +
    + {/snippet} +
    +
    +
    diff --git a/src/lib/components/ui/tooltip/tooltip-portal.svelte b/src/lib/components/ui/tooltip/tooltip-portal.svelte new file mode 100644 index 0000000000000000000000000000000000000000..d234f7d7d1150966dd691a452ef780c74ada76fa --- /dev/null +++ b/src/lib/components/ui/tooltip/tooltip-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/tooltip/tooltip-provider.svelte b/src/lib/components/ui/tooltip/tooltip-provider.svelte new file mode 100644 index 0000000000000000000000000000000000000000..6dba9a670d3076bf4907e7d2f39d19629a8d9548 --- /dev/null +++ b/src/lib/components/ui/tooltip/tooltip-provider.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/tooltip/tooltip-trigger.svelte b/src/lib/components/ui/tooltip/tooltip-trigger.svelte new file mode 100644 index 0000000000000000000000000000000000000000..1acdaa47be048caedef2dfaec62f426919aae250 --- /dev/null +++ b/src/lib/components/ui/tooltip/tooltip-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/tooltip/tooltip.svelte b/src/lib/components/ui/tooltip/tooltip.svelte new file mode 100644 index 0000000000000000000000000000000000000000..0b0f9cef2a6053fde3ad27020ece58a98500826d --- /dev/null +++ b/src/lib/components/ui/tooltip/tooltip.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/video-player/chunked-mse.ts b/src/lib/components/video-player/chunked-mse.ts new file mode 100644 index 0000000000000000000000000000000000000000..f59c4ae2ad0aa5af2b830c72cba5fdbf251c76bf --- /dev/null +++ b/src/lib/components/video-player/chunked-mse.ts @@ -0,0 +1,287 @@ +/** + * Stitches a list of MP4 chunks into a single Media Source Extensions + * timeline. Each input chunk is remuxed via mediabunny into fragmented MP4 + * and appended to one shared SourceBuffer with a per-chunk + * `timestampOffset`, producing one continuous virtual video. + * + * For player-switch we tear this object down and build a fresh one: it's + * the well-trodden MSE path and avoids the corner cases of re-using a + * SourceBuffer across heterogeneous content. Cached codec + smart ingest + * order from the priority time keep the rebuild fast. + */ + +import { + ALL_FORMATS, + AppendOnlyStreamTarget, + Conversion, + ConversionCanceledError, + Input, + Mp4OutputFormat, + Output, + UrlSource, + type UrlSourceOptions +} from 'mediabunny'; + +export type Chunk = { src: string; duration: number }; + +export type ChunkedMediaSourceOptions = { + urlOptions?: UrlSourceOptions; + codecOverride?: string; + priorityTime?: number; + onCodecResolved?: (codec: string) => void; + onIngestProgress?: (chunkIndex: number, fraction: number) => void; + onError?: (err: unknown) => void; +}; + +const sbAvailable = typeof MediaSource !== 'undefined'; + +function locateChunk(offsets: number[], t: number): number { + if (offsets.length === 0) return 0; + const tt = Math.max(0, t); + for (let i = offsets.length - 1; i >= 0; i--) { + if (tt >= offsets[i]) return i; + } + return 0; +} + +function ingestOrder(n: number, target: number): number[] { + if (n <= 0) return []; + const t = Math.max(0, Math.min(target, n - 1)); + const out: number[] = [t]; + for (let i = t + 1; i < n; i++) out.push(i); + for (let i = t - 1; i >= 0; i--) out.push(i); + return out; +} + +export class ChunkedMediaSource { + private static globalCachedCodec: string | null = null; + + static getCachedCodec(): string | null { + return ChunkedMediaSource.globalCachedCodec; + } + + readonly mediaSource: MediaSource; + readonly url: string; + private chunks: Chunk[]; + private opts: ChunkedMediaSourceOptions; + private sourceBuffer: SourceBuffer | null = null; + private appendChain: Promise = Promise.resolve(); + private destroyed = false; + private startedPromise: Promise | null = null; + private currentConv: Conversion | null = null; + private resolvedCodec: string | null = null; + + constructor(chunks: Chunk[], opts: ChunkedMediaSourceOptions = {}) { + if (!sbAvailable) { + throw new Error('MediaSource is not available in this environment'); + } + this.chunks = chunks; + this.opts = opts; + this.mediaSource = new MediaSource(); + this.url = URL.createObjectURL(this.mediaSource); + } + + get codec(): string | null { + return this.resolvedCodec; + } + + start(): Promise { + if (this.startedPromise) return this.startedPromise; + this.startedPromise = this.run(); + return this.startedPromise; + } + + destroy(): void { + if (this.destroyed) return; + this.destroyed = true; + try { + this.currentConv?.cancel(); + } catch { + /* ignore */ + } + try { + if (this.mediaSource.readyState === 'open') { + this.mediaSource.endOfStream(); + } + } catch { + /* ignore */ + } + try { + URL.revokeObjectURL(this.url); + } catch { + /* ignore */ + } + } + + private async waitSourceOpen(): Promise { + if (this.mediaSource.readyState === 'open') return; + await new Promise((resolve) => { + this.mediaSource.addEventListener('sourceopen', () => resolve(), { once: true }); + }); + } + + private async probeCodec(chunk: Chunk): Promise { + if (this.opts.codecOverride) return this.opts.codecOverride; + if (ChunkedMediaSource.globalCachedCodec) return ChunkedMediaSource.globalCachedCodec; + const input = new Input({ + formats: ALL_FORMATS, + source: new UrlSource(chunk.src, this.opts.urlOptions) + }); + const videoTrack = await input.getPrimaryVideoTrack(); + const audioTrack = await input.getPrimaryAudioTrack(); + const videoCodec = videoTrack ? await videoTrack.getCodecParameterString() : null; + const audioCodec = audioTrack ? await audioTrack.getCodecParameterString() : null; + const parts = [videoCodec, audioCodec].filter((s): s is string => !!s); + if (!parts.length) { + throw new Error('Could not determine codec for first chunk'); + } + const mime = `video/mp4; codecs="${parts.join(', ')}"`; + ChunkedMediaSource.globalCachedCodec = mime; + return mime; + } + + private appendBytes(data: Uint8Array): Promise { + const sb = this.sourceBuffer; + if (!sb) return Promise.resolve(); + this.appendChain = this.appendChain.then( + () => + new Promise((resolve, reject) => { + if (this.destroyed) return resolve(); + const cleanup = () => { + sb.removeEventListener('updateend', onEnd); + sb.removeEventListener('error', onError); + }; + const onEnd = () => { + cleanup(); + resolve(); + }; + const onError = () => { + cleanup(); + reject(new Error('SourceBuffer append error')); + }; + sb.addEventListener('updateend', onEnd); + sb.addEventListener('error', onError); + try { + sb.appendBuffer(data as unknown as BufferSource); + } catch (err) { + cleanup(); + reject(err); + } + }) + ); + return this.appendChain; + } + + private async run(): Promise { + try { + await this.waitSourceOpen(); + if (this.destroyed) return; + + if (!this.chunks.length) { + try { + this.mediaSource.endOfStream(); + } catch { + /* ignore */ + } + return; + } + + const codec = await this.probeCodec(this.chunks[0]); + if (this.destroyed) return; + this.resolvedCodec = codec; + ChunkedMediaSource.globalCachedCodec = codec; + this.opts.onCodecResolved?.(codec); + + this.sourceBuffer = this.mediaSource.addSourceBuffer(codec); + this.sourceBuffer.mode = 'segments'; + + const offsets: number[] = []; + let acc = 0; + for (const c of this.chunks) { + offsets.push(acc); + acc += c.duration; + } + try { + this.mediaSource.duration = acc; + } catch { + /* ignored */ + } + + const priority = this.opts.priorityTime ?? 0; + const order = ingestOrder(this.chunks.length, locateChunk(offsets, priority)); + + for (const i of order) { + if (this.destroyed) return; + await this.ingestChunk(this.chunks[i], i, offsets[i]); + } + + if (!this.destroyed) { + await this.appendChain.catch(() => {}); + if (this.mediaSource.readyState === 'open') { + try { + this.mediaSource.endOfStream(); + } catch { + /* ignore */ + } + } + } + } catch (err) { + if (this.destroyed) return; + if (err instanceof ConversionCanceledError) return; + this.opts.onError?.(err); + try { + if (this.mediaSource.readyState === 'open') { + this.mediaSource.endOfStream('decode'); + } + } catch { + /* ignore */ + } + } + } + + private async ingestChunk(chunk: Chunk, index: number, timestampOffset: number): Promise { + await this.appendChain.catch(() => {}); + if (this.destroyed) return; + const sb = this.sourceBuffer; + if (!sb) return; + sb.timestampOffset = timestampOffset; + + const input = new Input({ + formats: ALL_FORMATS, + source: new UrlSource(chunk.src, this.opts.urlOptions) + }); + + const writable = new WritableStream({ + write: async (data) => { + if (this.destroyed) return; + await this.appendBytes(data); + } + }); + + const output = new Output({ + target: new AppendOnlyStreamTarget(writable), + format: new Mp4OutputFormat({ + fastStart: 'fragmented', + minimumFragmentDuration: 0.5 + }) + }); + + const conv = await Conversion.init({ input, output }); + if (!conv.isValid) { + throw new Error(`Conversion invalid for chunk ${index} (${chunk.src})`); + } + conv.onProgress = (p) => { + this.opts.onIngestProgress?.(index, p); + }; + this.currentConv = conv; + try { + await conv.execute(); + } catch (err) { + if (err instanceof ConversionCanceledError) return; + throw err; + } finally { + if (this.currentConv === conv) this.currentConv = null; + } + this.opts.onIngestProgress?.(index, 1); + } +} diff --git a/src/lib/components/video-player/index.ts b/src/lib/components/video-player/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b3d2a1c9a65c84dbd5450da176d69f5036193a7 --- /dev/null +++ b/src/lib/components/video-player/index.ts @@ -0,0 +1,3 @@ +export { default as VideoPlayer } from './video-player.svelte'; +export { ChunkedMediaSource } from './chunked-mse.js'; +export type { Chunk, ChunkedMediaSourceOptions } from './chunked-mse.js'; diff --git a/src/lib/components/video-player/playbar.svelte b/src/lib/components/video-player/playbar.svelte new file mode 100644 index 0000000000000000000000000000000000000000..c26a511388f63f9e9fdfd622c96527406f612c8a --- /dev/null +++ b/src/lib/components/video-player/playbar.svelte @@ -0,0 +1,117 @@ + + + + +
    (hoverPercent = null)} +> +
    + {#each bufferedRanges as range, i (i)} + {@const left = (Math.max(0, range.start) / safeDuration) * 100} + {@const width = (Math.max(0, range.end - range.start) / safeDuration) * 100} + {#if width > 0} +
    + {/if} + {/each} +
    +
    + +
    + + {#if hoverPercent != null && duration > 0} +
    + {formatDuration(hoverPercent * duration)} +
    + {/if} +
    diff --git a/src/lib/components/video-player/video-player.svelte b/src/lib/components/video-player/video-player.svelte new file mode 100644 index 0000000000000000000000000000000000000000..a8629bdaffef3dcb588c4b9aadd12af14beccffe --- /dev/null +++ b/src/lib/components/video-player/video-player.svelte @@ -0,0 +1,317 @@ + + + + + + +
    + + + + + + {#if showCenterPlay} + + {/if} + + {#if showSpinner} +
    + +
    + {/if} +
    diff --git a/src/lib/components/video-stage.svelte b/src/lib/components/video-stage.svelte new file mode 100644 index 0000000000000000000000000000000000000000..effff83d221a10a5237a1195270d95491b8b3022 --- /dev/null +++ b/src/lib/components/video-stage.svelte @@ -0,0 +1,141 @@ + + +
    + + {#if loading} + +
    +
    + +

    Loading clip…

    +
    +
    + {:else if error} +
    +
    + +

    Couldn't load clip

    +

    {error}

    +
    +
    + {:else if !chunks.length} +
    +
    + +

    No clip available for this perspective.

    +
    +
    + {:else} + + {#if outOfFrame} +
    + +

    No clip — player is dead

    +
    + {/if} + {/if} + + {#if switchPing} +
    +
    + + {switchPing.player} + + + Switched to {switchPing.side} + +
    +
    + {/if} +
    + +
    diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..856f2b6c38aec1085db88189bcf492dbb49a1c45 --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a4b9a1c2519288d406cfc12f08eab3138d37fcd --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,56 @@ +export const DATASET = 'blanchon/cs2_dataset_render'; + +export type Match = { + match_id: number; + map_name: string; + map_index: number; + shard_id: string; + hltv_demo_id: string; + match_url: string; + event: string; + team1: string; + team2: string; + score1: number; + score2: number; + winner: 'team1' | 'team2' | string; + format: string; + stars: number; + match_date: string; + patch_version: string; + rounds_played: number; + winner_side: 'ct' | 't' | string | null; + uploaded_at: string; +}; + +export type Round = { + match_id: number; + map_name: string; + round: number; + demo_round: number; + shard_id: string; + round_start_tick: number; + freeze_end_tick: number; + round_end_tick: number; + round_duration_ticks: number; + uploaded_at: string; +}; + +export type PreviewChunk = { + row_id: string; + match_id: number; + map_name: string; + round: number; + player: number; + chunk_index: number; + shard_id: string; + preview_path: string; + preview_video: { src: string }; + primary_weapon: string; + player_side: 'CT' | 'T' | string; + survived_chunk: boolean; + duration_s: number; + fps: number; + width: number; + height: number; + uploaded_at: string; +}; diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..55b3a918e71b05bd9f5972e7427a45bf1a2945f7 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,13 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChild = T extends { child?: any } ? Omit : T; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChildren = T extends { children?: any } ? Omit : T; +export type WithoutChildrenOrChild = WithoutChildren>; +export type WithElementRef = T & { ref?: U | null }; diff --git a/src/lib/utils/format.ts b/src/lib/utils/format.ts new file mode 100644 index 0000000000000000000000000000000000000000..4eb18aafee8116b4b095be4cc3c38b06ba97975a --- /dev/null +++ b/src/lib/utils/format.ts @@ -0,0 +1,33 @@ +export function formatDate(iso: string): string { + if (!iso) return ''; + const d = new Date(iso); + return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }); +} + +export function formatDateTime(iso: string): string { + if (!iso) return ''; + const d = new Date(iso); + return d.toLocaleString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); +} + +export function formatDuration(seconds: number): string { + if (!Number.isFinite(seconds) || seconds < 0) return '0:00'; + const total = Math.round(seconds); + const m = Math.floor(total / 60); + const s = total % 60; + return `${m}:${s.toString().padStart(2, '0')}`; +} + +export function ticksToSeconds(ticks: number, tickRate = 64): number { + return ticks / tickRate; +} + +export function prettyMap(map: string): string { + return map.replace(/^de_/, '').replace(/^cs_/, '').replace(/_/g, ' '); +} diff --git a/src/lib/utils/map-colors.ts b/src/lib/utils/map-colors.ts new file mode 100644 index 0000000000000000000000000000000000000000..ac216c813e4331f2a905bbca0361349835ff7b80 --- /dev/null +++ b/src/lib/utils/map-colors.ts @@ -0,0 +1,25 @@ +// Map-specific tinted chip classes, themed to each map's in-game palette. +// Returned class strings are static so Tailwind can pick them up at build. + +const PALETTE: Record = { + dust2: 'bg-yellow-500/15 text-yellow-700 dark:text-yellow-300 border-yellow-500/40', + mirage: 'bg-orange-500/15 text-orange-700 dark:text-orange-300 border-orange-500/40', + inferno: 'bg-red-600/15 text-red-700 dark:text-red-300 border-red-600/40', + nuke: 'bg-zinc-500/15 text-zinc-700 dark:text-zinc-300 border-zinc-500/40', + overpass: 'bg-emerald-600/15 text-emerald-700 dark:text-emerald-300 border-emerald-600/40', + anubis: 'bg-amber-500/15 text-amber-700 dark:text-amber-300 border-amber-500/40', + vertigo: 'bg-sky-500/15 text-sky-700 dark:text-sky-300 border-sky-500/40', + train: 'bg-slate-500/15 text-slate-700 dark:text-slate-300 border-slate-500/40', + ancient: 'bg-green-700/15 text-green-700 dark:text-green-300 border-green-700/40', + office: 'bg-indigo-500/15 text-indigo-700 dark:text-indigo-300 border-indigo-500/40', + italy: 'bg-rose-500/15 text-rose-700 dark:text-rose-300 border-rose-500/40', + cache: 'bg-stone-500/15 text-stone-700 dark:text-stone-300 border-stone-500/40', + cobblestone: 'bg-amber-700/15 text-amber-800 dark:text-amber-300 border-amber-700/40' +}; + +const FALLBACK = 'bg-muted text-muted-foreground border-border'; + +export function mapColorClasses(map: string): string { + const key = map.toLowerCase().replace(/^(de_|cs_)/, ''); + return PALETTE[key] ?? FALLBACK; +} diff --git a/src/lib/utils/map.ts b/src/lib/utils/map.ts new file mode 100644 index 0000000000000000000000000000000000000000..0616de88b51cede88cbe88f919f847d98ad5ded0 --- /dev/null +++ b/src/lib/utils/map.ts @@ -0,0 +1,71 @@ +// CS2 map calibration. `pos_x`, `pos_y` are the world coords of the radar +// PNG's upper-left corner; `scale` is units-per-pixel. Multi-floor maps use +// `lower_level_max_units` as a Z threshold: any player at or below it is on +// the lower floor and should be drawn against the `_lower` radar. +// +// Source: awpy's map-data.json (https://awpycs.com//maps.zip). + +export type MapData = { + pos_x: number; + pos_y: number; + scale: number; + rotate: number | null; + zoom: number | null; + lower_level_max_units: number; +}; + +export const RADAR_PX = 1024; + +let cache: Record | null = null; +let inflight: Promise> | null = null; + +export async function loadMapData( + fetchFn: typeof fetch = fetch +): Promise> { + if (cache) return cache; + if (!inflight) { + inflight = fetchFn('/maps/map-data.json') + .then((r) => { + if (!r.ok) throw new Error(`map-data.json ${r.status}`); + return r.json() as Promise>; + }) + .then((d) => { + cache = d; + return d; + }); + } + return inflight; +} + +export type Floor = 'upper' | 'lower'; + +export function radarUrl(mapName: string, floor: Floor): string { + return floor === 'lower' ? `/maps/${mapName}_lower.png` : `/maps/${mapName}.png`; +} + +export function hasLowerFloor(map: MapData): boolean { + // awpy uses -1_000_000 as the sentinel for "no lower floor". + return map.lower_level_max_units > -100_000; +} + +export function floorForZ(map: MapData, z: number): Floor { + if (!hasLowerFloor(map)) return 'upper'; + // de_vertigo is sky-high (lower_level_max_units = 11700 means below that = + // lower B site). de_nuke / de_train use a negative threshold. The + // inequality is the same in both cases. + return z <= map.lower_level_max_units ? 'lower' : 'upper'; +} + +export function worldToImage(map: MapData, x: number, y: number): { px: number; py: number } { + return { + px: (x - map.pos_x) / map.scale, + py: (map.pos_y - y) / map.scale + }; +} + +// CS yaw: 0° = +X (east), 90° = +Y (north), CCW positive. Image space has +// Y pointing down, so a screen-space rotation of `-yaw` makes an icon whose +// "forward" is +X point in the player's facing direction. +export function yawToScreenDeg(yaw: number): number { + return -yaw; +} diff --git a/src/lib/utils/player-colors.ts b/src/lib/utils/player-colors.ts new file mode 100644 index 0000000000000000000000000000000000000000..8ef0a0a9c330b19bc3afd7785c537aac334de487 --- /dev/null +++ b/src/lib/utils/player-colors.ts @@ -0,0 +1,24 @@ +// Per-slot colors for the 10 player perspectives. Slot is stable across the +// match (player=0 is always the same person), so a slot-based palette gives +// each player a persistent visual identity even when sides switch at half. +// +// Palette is desaturated / earthier than a vivid Tailwind ramp — it borrows +// from CSGO HUD/comp tokens so 10 chips can sit next to each other without +// fighting the rest of the UI for attention. + +export const PLAYER_COLORS = [ + '#a73a3a', // 0 crimson + '#b4663b', // 1 rust + '#b29144', // 2 golden tan + '#7a8a40', // 3 olive + '#3f7878', // 4 teal + '#3f6796', // 5 dusk blue + '#6b5b95', // 6 dusty violet + '#9a5a7a', // 7 muted rose + '#638a64', // 8 sage + '#8c6f56' // 9 warm taupe +] as const; + +export function playerColor(slot: number): string { + return PLAYER_COLORS[((slot % PLAYER_COLORS.length) + PLAYER_COLORS.length) % PLAYER_COLORS.length]; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000000000000000000000000000000000000..4a05ddb0e86a1f53aae4d0698edd2d6c9106db45 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,27 @@ + + + + + CS2 Render Dataset + + + + + + + +
    + {@render children()} +
    +
    diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd8214f5e531da92ab293548cf21f1bcabe9a6bb --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,4 @@ +// Data is loaded from parquet files via parquet-wasm in the browser; disable +// SSR so the load functions run only on the client. +export const ssr = false; +export const prerender = false; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000000000000000000000000000000000000..90e9d398aabfd2cf53a06e9b616929311a480b16 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,80 @@ + + +
    + +
    +
    +

    + Counter-Strike 2 Match Archive +

    +

    + Browse rendered HLTV demo matches with full per-player POV video. Each map of every match is split + into rounds, and each round into ten synchronized player perspectives — switch perspectives on the + fly to follow the action from any angle. +

    + +
    +
    +
    Matches
    +
    {totalMatches}
    +
    +
    +
    Maps
    +
    {totalMaps}
    +
    +
    +
    Rounds
    +
    {totalRounds}
    +
    +
    +
    Footage
    +
    {totalHoursLabel}
    +
    +
    +
    + +
    +

    All maps

    + + {#if data.matches.length === 0} +
    + {#each Array(8) as _, i (i)} + + {/each} +
    + {:else} +
    + {#each data.matches as match (match.match_id + '-' + match.map_name)} + + {/each} +
    + {/if} +
    +
    diff --git a/src/routes/+page.ts b/src/routes/+page.ts new file mode 100644 index 0000000000000000000000000000000000000000..732a9400688c5c9921778747edd68e5db94b281f --- /dev/null +++ b/src/routes/+page.ts @@ -0,0 +1,10 @@ +import type { PageLoad } from './$types'; +import { listMatches, listAllRounds } from '$lib/api/hf'; + +export const load: PageLoad = async ({ fetch }) => { + const [matches, rounds] = await Promise.all([ + listMatches({ fetch }), + listAllRounds({ fetch }).catch(() => []) + ]); + return { matches, rounds }; +}; diff --git a/src/routes/layout.css b/src/routes/layout.css new file mode 100644 index 0000000000000000000000000000000000000000..083d5ceb0cd7a76568d4b4c3e6eca77c31c6781b --- /dev/null +++ b/src/routes/layout.css @@ -0,0 +1,131 @@ +@import 'tailwindcss'; +@import "tw-animate-css"; +@import "shadcn-svelte/tailwind.css"; +@import "@fontsource-variable/roboto-slab"; +@import "@fontsource-variable/oxanium"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.147 0.004 49.3); + --card: oklch(1 0 0); + --card-foreground: oklch(0.147 0.004 49.3); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.147 0.004 49.3); + --primary: oklch(0.214 0.009 43.1); + --primary-foreground: oklch(0.986 0.002 67.8); + --secondary: oklch(0.96 0.002 17.2); + --secondary-foreground: oklch(0.214 0.009 43.1); + --muted: oklch(0.96 0.002 17.2); + --muted-foreground: oklch(0.547 0.021 43.1); + --accent: oklch(0.96 0.002 17.2); + --accent-foreground: oklch(0.214 0.009 43.1); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0.005 34.3); + --input: oklch(0.922 0.005 34.3); + --ring: oklch(0.714 0.014 41.2); + --chart-1: oklch(0.868 0.007 39.5); + --chart-2: oklch(0.547 0.021 43.1); + --chart-3: oklch(0.438 0.017 39.3); + --chart-4: oklch(0.367 0.016 35.7); + --chart-5: oklch(0.268 0.011 36.5); + --radius: 0rem; + --sidebar: oklch(0.986 0.002 67.8); + --sidebar-foreground: oklch(0.147 0.004 49.3); + --sidebar-primary: oklch(0.214 0.009 43.1); + --sidebar-primary-foreground: oklch(0.986 0.002 67.8); + --sidebar-accent: oklch(0.96 0.002 17.2); + --sidebar-accent-foreground: oklch(0.214 0.009 43.1); + --sidebar-border: oklch(0.922 0.005 34.3); + --sidebar-ring: oklch(0.714 0.014 41.2); +} + +.dark { + --background: oklch(0.147 0.004 49.3); + --foreground: oklch(0.986 0.002 67.8); + --card: oklch(0.214 0.009 43.1); + --card-foreground: oklch(0.986 0.002 67.8); + --popover: oklch(0.214 0.009 43.1); + --popover-foreground: oklch(0.986 0.002 67.8); + --primary: oklch(0.922 0.005 34.3); + --primary-foreground: oklch(0.214 0.009 43.1); + --secondary: oklch(0.268 0.011 36.5); + --secondary-foreground: oklch(0.986 0.002 67.8); + --muted: oklch(0.268 0.011 36.5); + --muted-foreground: oklch(0.714 0.014 41.2); + --accent: oklch(0.268 0.011 36.5); + --accent-foreground: oklch(0.986 0.002 67.8); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.547 0.021 43.1); + --chart-1: oklch(0.868 0.007 39.5); + --chart-2: oklch(0.547 0.021 43.1); + --chart-3: oklch(0.438 0.017 39.3); + --chart-4: oklch(0.367 0.016 35.7); + --chart-5: oklch(0.268 0.011 36.5); + --sidebar: oklch(0.214 0.009 43.1); + --sidebar-foreground: oklch(0.986 0.002 67.8); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.986 0.002 67.8); + --sidebar-accent: oklch(0.268 0.011 36.5); + --sidebar-accent-foreground: oklch(0.986 0.002 67.8); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.547 0.021 43.1); +} + +@theme inline { + --font-serif: 'Roboto Slab Variable', serif; + --font-heading: 'Oxanium Variable', sans-serif; + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --color-foreground: var(--foreground); + --color-background: var(--background); + --radius-sm: calc(var(--radius) * 0.6); + --radius-md: calc(var(--radius) * 0.8); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) * 1.4); + --radius-2xl: calc(var(--radius) * 1.8); + --radius-3xl: calc(var(--radius) * 2.2); + --radius-4xl: calc(var(--radius) * 2.6); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } + html { + @apply font-serif; + } +} diff --git a/src/routes/map-demo/+page.svelte b/src/routes/map-demo/+page.svelte new file mode 100644 index 0000000000000000000000000000000000000000..f37c38ad284077446545f6b170f429586fb543e4 --- /dev/null +++ b/src/routes/map-demo/+page.svelte @@ -0,0 +1,237 @@ + + +
    +
    +

    Map preview · mock

    +

    + Mocked player ticks. Drag the slider to scrub motion. Real data wiring will replace + SEEDS_DUST2 with rows from world.preview.jsonl. +

    +
    + +
    + + + + + + +
    + + + + Map + + + + v && (selectedMap = v)} + > + + {selectedMap} + + + + {#each MAPS as m (m)} + {m} + {/each} + + +

    + Player coords are tuned for de_dust2; pins on other maps will look weird. +

    +
    +
    + + + + + Tick (mock progress) + + + + +
    + round-start + {progress[0]}% + round-end +
    +
    +
    + + + + + Roster + + + +
    + CT + {ctCount} alive + · + T + {tCount} alive +
    +
      + {#each players as p (p.steamid)} +
    • + + + {p.name} + + + {Math.round(p.X)}, {Math.round(p.Y)} + +
    • + {/each} +
    +
    +
    +
    +
    +
    diff --git a/src/routes/match/[matchId]/[mapName]/+page.svelte b/src/routes/match/[matchId]/[mapName]/+page.svelte new file mode 100644 index 0000000000000000000000000000000000000000..ea6f4591694aea2ae15e24db81465c6ac8730a4e --- /dev/null +++ b/src/routes/match/[matchId]/[mapName]/+page.svelte @@ -0,0 +1,540 @@ + + + + +
    + +
    +
    +
    +

    + {data.match.team1} + vs + {data.match.team2} +

    +
    + + {#if seriesMaps.length > 1} + { + if (!v || v === data.match.map_name) return; + goto(`/match/${encodeURIComponent(data.match.match_id)}/${encodeURIComponent(v)}`); + }} + > + + {prettyMap(data.match.map_name)} + + + {#each seriesMaps as m (m.map_name)} + + Map {m.map_index} · {prettyMap(m.map_name)} + {m.score1}-{m.score2} + + {/each} + + + {/if} +
    + + +
    + + +
    +
    + v && setViewMode(v as 'single' | 'grid')} + variant="outline" + size="sm" + aria-label="View mode" + > + + Single + + + Grid + + +
    + + {#if viewMode === 'single'} + 0} + onPausedChange={(p) => (masterPaused = p)} + onTimeUpdate={handleTimeUpdate} + onEnded={handleVideoEnded} + onBufferedChange={(r) => (bufferedRanges = r)} + /> + {:else if previewsLoading || previewsError || !previews.length} + + {:else} + (masterPaused = p)} + onSelect={(p) => { + selectPlayer(p); + // Stay in grid mode after selecting; the new + // current player just becomes the audio leader. + }} + onTimeUpdate={handleTimeUpdate} + onLeaderEnded={handleVideoEnded} + onBufferedChange={(r) => (bufferedRanges = r)} + /> + {/if} + + {#if previews.length} + (masterMuted = !masterMuted)} + onSeek={handleSeek} + /> + {/if} + + {#if viewMode === 'single'} + + + {#if previewsLoading && !previews.length} +
    + {#each Array(10) as _, i (i)} + + {/each} +
    + {:else if previews.length === 0} +

    No previews available for this round.

    + {:else} + (preloadAllPlayers = v)} + /> + {/if} +
    +
    + {/if} +
    + + +
    +
    diff --git a/src/routes/match/[matchId]/[mapName]/+page.ts b/src/routes/match/[matchId]/[mapName]/+page.ts new file mode 100644 index 0000000000000000000000000000000000000000..389d3c5ae7f5fb8d969ea8ad004a8e9bdaedca52 --- /dev/null +++ b/src/routes/match/[matchId]/[mapName]/+page.ts @@ -0,0 +1,18 @@ +import type { PageLoad } from './$types'; +import { error } from '@sveltejs/kit'; +import { listMatches, listRounds } from '$lib/api/hf'; + +export const load: PageLoad = async ({ params, fetch }) => { + const matchId = Number(decodeURIComponent(params.matchId)); + const mapName = decodeURIComponent(params.mapName); + if (!Number.isFinite(matchId)) throw error(400, `Invalid match id: ${params.matchId}`); + + const [matches, rounds] = await Promise.all([ + listMatches({ fetch }), + listRounds(matchId, mapName, { fetch }) + ]); + const match = matches.find((m) => m.match_id === matchId && m.map_name === mapName); + if (!match) throw error(404, `Match ${matchId} on ${mapName} not found`); + + return { match, rounds, matches }; +}; diff --git a/static/maps/ar_baggage.png b/static/maps/ar_baggage.png new file mode 100644 index 0000000000000000000000000000000000000000..f981b11484d896e6890390f8decf59369c08d1c3 --- /dev/null +++ b/static/maps/ar_baggage.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b98a3151d936dab6a6521c8a0e7c08d4a1aa2b5eefc20f439080bbc17c9564b1 +size 117476 diff --git a/static/maps/ar_baggage_lower.png b/static/maps/ar_baggage_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..84dc111b08a3838d58da57fd0ecb516e13de4720 --- /dev/null +++ b/static/maps/ar_baggage_lower.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f24250c021d9570f9b3743dbc554ad3095f20687a5942162ea3b075dfff42bb +size 62532 diff --git a/static/maps/ar_shoots.png b/static/maps/ar_shoots.png new file mode 100644 index 0000000000000000000000000000000000000000..a48a3f84d8a8a60554f1f3c273963aef464ec591 --- /dev/null +++ b/static/maps/ar_shoots.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fa9d859f8f428af5c2b43aa2d2707b1670f9d2d30b09bc91e159716ec974f23 +size 328314 diff --git a/static/maps/cs_italy.png b/static/maps/cs_italy.png new file mode 100644 index 0000000000000000000000000000000000000000..34d6d3cb931a3f3c9c3e6f7c3b6112a98c66b2f6 --- /dev/null +++ b/static/maps/cs_italy.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4dbd658ccf6df78022c3ff411cb36129c5d5786b37e6403c8ddf3e328bf05241 +size 105277 diff --git a/static/maps/cs_office.png b/static/maps/cs_office.png new file mode 100644 index 0000000000000000000000000000000000000000..899cdb20747db3a7783ad47f3f25a60841c23a62 --- /dev/null +++ b/static/maps/cs_office.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ddb3941a88256732d4c06cdf63777743423d595f59e0a5c2caa7fbf6469a9fd5 +size 199246 diff --git a/static/maps/de_ancient.png b/static/maps/de_ancient.png new file mode 100644 index 0000000000000000000000000000000000000000..528e0c53ca60b9cca39f8fce7fcf920ef6621d85 --- /dev/null +++ b/static/maps/de_ancient.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2f7b0f4af985fdc4985589a55ec0dc424dfd6dea65cb4a1b42a966b891d6aa4 +size 90267 diff --git a/static/maps/de_anubis.png b/static/maps/de_anubis.png new file mode 100644 index 0000000000000000000000000000000000000000..13d7b7f47eb2281b38a28cd0c49fea0ce0f627d8 --- /dev/null +++ b/static/maps/de_anubis.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b325c840db6a639965c035a542a329b2ef46cef24908a6f1a3a927d946e28955 +size 128723 diff --git a/static/maps/de_dust2.png b/static/maps/de_dust2.png new file mode 100644 index 0000000000000000000000000000000000000000..698753163bbb6eda36b452a3c5f4fdccf0002c48 --- /dev/null +++ b/static/maps/de_dust2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6515ab4ba319187b2130edb0c2f60ba64c9d422e75de113c65bdf37c2db1479b +size 175884 diff --git a/static/maps/de_inferno.png b/static/maps/de_inferno.png new file mode 100644 index 0000000000000000000000000000000000000000..b417c5429154482037d062cbb3cde76d97037e46 --- /dev/null +++ b/static/maps/de_inferno.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66f980acabd90d9b0da12819a605f24440e4adb3f59f006d6fd98e891922c575 +size 132536 diff --git a/static/maps/de_mirage.png b/static/maps/de_mirage.png new file mode 100644 index 0000000000000000000000000000000000000000..df4ac33abd25dde4eb36130c14298d8100bc8c47 --- /dev/null +++ b/static/maps/de_mirage.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af139a92cf929214f7c0ac35e2d8c82bad385d83e71eeaf7b955dac96467a136 +size 144543 diff --git a/static/maps/de_nuke.png b/static/maps/de_nuke.png new file mode 100644 index 0000000000000000000000000000000000000000..42513f5d91329eb4924a60ce4c6a09b1b43a7990 --- /dev/null +++ b/static/maps/de_nuke.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edab1b53b1c475d33032d5ceff5bd4debf35d0f77bd8d57fe9675a619f2355bc +size 78534 diff --git a/static/maps/de_nuke_lower.png b/static/maps/de_nuke_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..09bfd6ac67a5687221d9b00cab9011ee0a86b3a4 --- /dev/null +++ b/static/maps/de_nuke_lower.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86c289e8cfbc9b0fc2c24d0f4e704254366aa8052b0b123f4773d4da1d6b5867 +size 192300 diff --git a/static/maps/de_overpass.png b/static/maps/de_overpass.png new file mode 100644 index 0000000000000000000000000000000000000000..b5a1c45afac2b4e8fff39de8a220458c0bb7d928 --- /dev/null +++ b/static/maps/de_overpass.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:304352068bdd1e8337b73962481659956f691dddb756d7943bcfe89c37630308 +size 205176 diff --git a/static/maps/de_train.png b/static/maps/de_train.png new file mode 100644 index 0000000000000000000000000000000000000000..c9bc65e963994e6119e0aba001a6790a0bb6a84a --- /dev/null +++ b/static/maps/de_train.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:735e104c007d2cd500309f6fd49a999e4f1b389ef85be7bd58cb25aca69f383d +size 146822 diff --git a/static/maps/de_train_lower.png b/static/maps/de_train_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..ee129f6b949014933ffd5ec419de7c91f3bd2cdb --- /dev/null +++ b/static/maps/de_train_lower.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acfd7044b5771995d100ecb95f23b642582d9b2bbac5bc99e9fa0fdf052ce94d +size 136207 diff --git a/static/maps/de_vertigo.png b/static/maps/de_vertigo.png new file mode 100644 index 0000000000000000000000000000000000000000..983d084a2f29f53578de0dfdc41797a210ea9965 --- /dev/null +++ b/static/maps/de_vertigo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2de17bb5348679b0a81166fca585512f7fd11f75569348108e4d20ac308fa8dd +size 98334 diff --git a/static/maps/de_vertigo_lower.png b/static/maps/de_vertigo_lower.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8316d8854b50f287906cbe14b35dd5d3b343bc --- /dev/null +++ b/static/maps/de_vertigo_lower.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2c58bce0f655c4994391755cadc3a8720cd4f818013f9c6b238f1c508f67f9d +size 124081 diff --git a/static/maps/map-data.json b/static/maps/map-data.json new file mode 100644 index 0000000000000000000000000000000000000000..329afe650a73138e93cb72e002d5448aa7c5d43b --- /dev/null +++ b/static/maps/map-data.json @@ -0,0 +1 @@ +{"ar_baggage": {"pos_x": -1316, "pos_y": 1288, "scale": 2.539062, "rotate": 1, "zoom": 1.3, "lower_level_max_units": -5.0}, "ar_shoots": {"pos_x": -1368, "pos_y": 1952, "scale": 2.6875, "rotate": null, "zoom": null, "lower_level_max_units": -1000000.0}, "cs_italy": {"pos_x": -2647, "pos_y": 2592, "scale": 4.6, "rotate": 1, "zoom": 1.5, "lower_level_max_units": -1000000.0}, "cs_office": {"pos_x": -1838, "pos_y": 1858, "scale": 4.1, "rotate": null, "zoom": null, "lower_level_max_units": -1000000.0}, "de_ancient": {"pos_x": -2953, "pos_y": 2164, "scale": 5.0, "rotate": 0, "zoom": 0.0, "lower_level_max_units": -1000000.0}, "de_anubis": {"pos_x": -2796, "pos_y": 3328, "scale": 5.22, "rotate": null, "zoom": null, "lower_level_max_units": -1000000.0}, "de_dust": {"pos_x": -2850, "pos_y": 4073, "scale": 6.0, "rotate": 1, "zoom": 1.3, "lower_level_max_units": -1000000.0}, "de_dust2": {"pos_x": -2476, "pos_y": 3239, "scale": 4.4, "rotate": 1, "zoom": 1.1, "lower_level_max_units": -1000000.0}, "de_inferno": {"pos_x": -2087, "pos_y": 3870, "scale": 4.9, "rotate": null, "zoom": null, "lower_level_max_units": -1000000.0}, "de_mirage": {"pos_x": -3230, "pos_y": 1713, "scale": 5.0, "rotate": 0, "zoom": 0.0, "lower_level_max_units": -1000000.0}, "de_nuke": {"pos_x": -3453, "pos_y": 2887, "scale": 7.0, "rotate": null, "zoom": null, "lower_level_max_units": -495.0}, "de_overpass": {"pos_x": -4831, "pos_y": 1781, "scale": 5.2, "rotate": 0, "zoom": 0.0, "lower_level_max_units": -1000000.0}, "de_train": {"pos_x": -2308, "pos_y": 2078, "scale": 4.082077, "rotate": null, "zoom": null, "lower_level_max_units": -50.0}, "de_vertigo": {"pos_x": -3168, "pos_y": 1762, "scale": 4.0, "rotate": null, "zoom": null, "lower_level_max_units": 11700.0}, "workshop_preview": {"pos_x": -2071, "pos_y": 711, "scale": 1.699219, "rotate": null, "zoom": null, "lower_level_max_units": -1000000.0}} diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000000000000000000000000000000000000..b6dd6670cbb07772618c4449c41f90d72edb8a74 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000000000000000000000000000000000000..794d2b965436280b4e3d9a9715911ece7d10fedf --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,26 @@ +import adapter from '@sveltejs/adapter-static'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + compilerOptions: { + // Force runes mode for the project, except for libraries. Can be removed in svelte 6. + runes: ({ filename }) => (filename.split(/[/\\]/).includes('node_modules') ? undefined : true) + }, + kit: { + // Static SPA build for Hugging Face Spaces. SSR is disabled in + // +layout.ts, so we ship a single index.html that hydrates on the + // client and a fallback for any dynamic route. + adapter: adapter({ + pages: 'dist', + assets: 'dist', + fallback: 'index.html', + precompress: false, + strict: false + }), + alias: { + '@/*': './src/lib/*' + } + } +}; + +export default config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..2c2ed3c4d85da301c40654bdf3ac2872b1a87621 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..2d35c4f5af9151490b7567a40461d3430a9451b8 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,7 @@ +import tailwindcss from '@tailwindcss/vite'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [tailwindcss(), sveltekit()] +});