Compare commits

...

12 Commits

Author SHA1 Message Date
09c18ec654 Merge pull request 'chore/svelte-kit-to-svelte-migration' (#6) from chore/svelte-kit-to-svelte-migration into feature/project-setup
Some checks failed
Lint / Lint Code (push) Failing after 1m38s
Test / Svelte Checks (push) Failing after 2s
Reviewed-on: glyphdiff.com/frontend-sveltekit#6
2025-12-30 16:40:55 +00:00
Ilia Mashkov
d5527929f9 fix: setup declarations for files to avoid import errors
Some checks failed
Build / build (pull_request) Failing after 16s
Lint / Lint Code (pull_request) Failing after 3s
Test / Svelte Checks (pull_request) Failing after 1s
2025-12-30 19:38:20 +03:00
Ilia Mashkov
53e3d6985b chore(ci/cd): adjust workflows ci/cd files to work with actions/cache 2025-12-30 19:37:25 +03:00
Ilia Mashkov
7c94622b95 fix(setup): fix tsconfig to work with bundler module resolution 2025-12-30 19:07:51 +03:00
Ilia Mashkov
f3315be32d chore(setup): migrate from SvelteKit to Svelte 2025-12-30 18:57:58 +03:00
def01a2cad Merge pull request 'feat(types): create comprehensive models for google fonts and fontshare api responses' (#5) from feature/typing-fonts-api into main
Some checks failed
Build / Build Project (push) Failing after 3s
Deploy / Lint Check (push) Successful in 1s
Deploy / Test Suite (push) Successful in 0s
Deploy / Build Verification (push) Successful in 0s
Lint / Lint Code (push) Failing after 2s
Test / Type Check (push) Failing after 2s
Deploy / Deploy to ${{ github.event.inputs.environment || 'production' }} (push) Failing after 2s
Reviewed-on: glyphdiff.com/frontend-sveltekit#5
2025-12-30 13:46:46 +00:00
Ilia Mashkov
31e64f4eac feat(types): create comprehensive models for google fonts and fontshare api responses
Some checks failed
Lint / Lint Code (push) Failing after 2s
Test / Type Check (push) Failing after 2s
Build / Build Project (pull_request) Failing after 3s
Lint / Lint Code (pull_request) Failing after 2s
Test / Type Check (pull_request) Failing after 2s
2025-12-30 16:44:16 +03:00
1a49a7bc34 Merge pull request 'fix(setup): Fix pre-push lint and format from all files to push_files' (#4) from fixes/lefthook-config into main
Some checks failed
Build / Build Project (push) Failing after 3s
Deploy / Lint Check (push) Successful in 0s
Deploy / Test Suite (push) Successful in 0s
Deploy / Build Verification (push) Successful in 0s
Lint / Lint Code (push) Failing after 2s
Test / Type Check (push) Failing after 2s
Deploy / Deploy to ${{ github.event.inputs.environment || 'production' }} (push) Failing after 2s
Reviewed-on: glyphdiff.com/frontend-sveltekit#4
2025-12-30 10:31:04 +00:00
Ilia Mashkov
b94b0e8b72 fix(setup): Fix pre-push lint and format from all files to push_files
Some checks failed
Build / Build Project (pull_request) Failing after 3s
Lint / Lint Code (pull_request) Failing after 2s
Test / Type Check (pull_request) Failing after 2s
2025-12-30 13:26:50 +03:00
4c965fae90 Merge pull request 'feat(setup): add gitea actions CI/CD workflows' (#2) from feature/setup-gitea-actions into main
Some checks failed
Build / Build Project (push) Has been cancelled
Deploy / Deploy to ${{ github.event.inputs.environment || 'production' }} (push) Has been cancelled
Deploy / Lint Check (push) Has been cancelled
Deploy / Test Suite (push) Has been cancelled
Deploy / Build Verification (push) Has been cancelled
Lint / Lint Code (push) Has been cancelled
Test / Type Check (push) Has been cancelled
Reviewed-on: glyphdiff.com/frontend-sveltekit#2
2025-12-30 10:24:51 +00:00
Ilia Mashkov
a1f34d6942 feat(setup): add gitea actions CI/CD workflows
Some checks failed
Lint / Lint Code (push) Failing after 49s
Test / Type Check (push) Failing after 2s
Build / Build Project (pull_request) Failing after 16s
Lint / Lint Code (pull_request) Failing after 2s
Test / Type Check (pull_request) Failing after 2s
2025-12-30 13:06:27 +03:00
509e0bd01d Merge pull request 'feature/project-setup' (#1) from feature/project-setup into main
Reviewed-on: glyphdiff.com/frontend-sveltekit#1
2025-12-30 09:18:08 +00:00
27 changed files with 1475 additions and 191 deletions

562
.gitea/README.md Normal file
View File

@@ -0,0 +1,562 @@
# Gitea Actions CI/CD Setup
This document describes the CI/CD pipeline configuration for the GlyphDiff project using Gitea Actions (GitHub Actions compatible).
## Table of Contents
- [Overview](#overview)
- [Workflow Files](#workflow-files)
- [Workflow Triggers](#workflow-triggers)
- [Setup Instructions](#setup-instructions)
- [Self-Hosted Runner Setup](#self-hosted-runner-setup)
- [Caching Strategy](#caching-strategy)
- [Environment Variables](#environment-variables)
- [Troubleshooting](#troubleshooting)
## Overview
The CI/CD pipeline consists of four main workflows:
1. **Lint** - Code quality checks (oxlint, dprint formatting)
2. **Test** - Type checking and E2E tests (Playwright)
3. **Build** - Production build verification
4. **Deploy** - Deployment automation (optional/template)
All workflows are designed to run on both push and pull request events, with appropriate branch filtering and concurrency controls.
## Workflow Files
### `.gitea/workflows/lint.yml`
**Purpose**: Run code quality checks to ensure code style and formatting standards.
**Checks performed**:
- `oxlint` - Fast JavaScript/TypeScript linter
- `dprint check` - Code formatting verification
**Triggers**:
- Push to `main`, `develop`, `feature/*` branches
- Pull requests to `main` or `develop`
- Manual workflow dispatch
**Cache**: Node modules and Yarn cache
**Concurrency**: Cancels in-progress runs for the same branch when a new commit is pushed.
---
### `.gitea/workflows/test.yml`
**Purpose**: Run type checking and end-to-end tests.
**Jobs**:
#### 1. `type-check` job
- `tsc --noEmit` - TypeScript type checking
- `svelte-check --threshold warning` - Svelte component type checking
#### 2. `e2e-tests` job
- Installs Playwright browsers with system dependencies
- Runs E2E tests using Playwright
- Uploads test report artifacts (retained for 7 days)
- Uploads screenshots on test failure for debugging
**Triggers**: Same as lint workflow
**Cache**: Node modules and Yarn cache
**Artifacts**:
- `playwright-report` - Test execution report
- `playwright-screenshots` - Screenshots from failed tests
---
### `.gitea/workflows/build.yml`
**Purpose**: Verify that the production build completes successfully.
**Steps**:
1. Checkout repository
2. Setup Node.js v20 with Yarn caching
3. Install dependencies with `--frozen-lockfile`
4. Run `svelte-kit sync` to prepare SvelteKit
5. Build the project with `NODE_ENV=production`
6. Upload build artifacts (`.svelte-kit/output`, `.svelte-kit/build`)
7. Run the preview server and verify it responds (health check)
**Triggers**:
- Push to `main` or `develop` branches
- Pull requests to `main` or `develop`
- Manual workflow dispatch
**Cache**: Node modules and Yarn cache
**Artifacts**:
- `build-artifacts` - Compiled SvelteKit output (retained for 7 days)
---
### `.gitea/workflows/deploy.yml`
**Purpose**: Automated deployment pipeline (template configuration).
**Current state**: Placeholder configuration. Uncomment and customize one of the deployment examples.
**Pre-deployment checks**:
- Must pass linting workflow
- Must pass testing workflow
- Must pass build workflow
**Deployment examples included**:
1. **Docker container registry** - Build and push Docker image
2. **SSH deployment** - Deploy to server via SSH
3. **Vercel** - Deploy to Vercel platform
**Triggers**:
- Push to `main` branch
- Manual workflow dispatch with environment selection (staging/production)
**Secrets required** (configure in Gitea):
- For Docker: `REGISTRY_URL`, `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`
- For SSH: `DEPLOY_HOST`, `DEPLOY_USER`, `DEPLOY_SSH_KEY`
- For Vercel: `VERCEL_TOKEN`, `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID`
## Workflow Triggers
### Branch-Specific Behavior
| Workflow | Push Triggers | PR Triggers | Runs on Merge |
| -------- | ------------------------------ | -------------------- | ------------- |
| Lint | `main`, `develop`, `feature/*` | To `main`, `develop` | Yes |
| Test | `main`, `develop`, `feature/*` | To `main`, `develop` | Yes |
| Build | `main`, `develop` | To `main`, `develop` | Yes |
| Deploy | `main` only | None | Yes |
### Concurrency Strategy
All workflows use concurrency groups based on the workflow name and branch reference:
```yaml
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # or false for deploy workflow
```
This ensures:
- For lint/test/build: New commits cancel in-progress runs (saves resources)
- For deploy: Prevents concurrent deployments (ensures safety)
## Setup Instructions
### Step 1: Verify Gitea Actions is Enabled
1. Navigate to your Gitea instance
2. Go to **Site Administration****Actions**
3. Ensure Actions is enabled
4. Configure default runner settings if needed
### Step 2: Configure Repository Settings
1. Go to your repository in Gitea
2. Click **Settings****Actions**
3. Enable Actions for the repository if not already enabled
4. Set appropriate permissions for read/write access
### Step 3: Push Workflows to Repository
The workflow files are already in `.gitea/workflows/`. Commit and push them:
```bash
git add .gitea/workflows/
git commit -m "Add Gitea Actions CI/CD workflows"
git push origin main
```
### Step 4: Verify Workflows Run
1. Navigate to **Actions** tab in your repository
2. You should see the workflows trigger on the next push
3. Click into a workflow run to view logs and status
### Step 5: Configure Secrets (Optional - for deployment)
1. Go to repository **Settings****Secrets****Actions**
2. Click **Add New Secret**
3. Add secrets required for your deployment method
Example secrets for SSH deployment:
```
DEPLOY_HOST=your-server.com
DEPLOY_USER=deploy
DEPLOY_SSH_KEY=-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
```
## Self-Hosted Runner Setup
### Option 1: Using Gitea's Built-in Act Runner (Recommended)
Gitea provides `act_runner` (compatible with GitHub Actions runner).
#### Install act_runner
On Linux (Debian/Ubuntu):
```bash
wget -O /usr/local/bin/act_runner https://gitea.com/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64
chmod +x /usr/local/bin/act_runner
```
Verify installation:
```bash
act_runner --version
```
#### Register the Runner
1. In Gitea, navigate to repository **Settings****Actions****Runners**
2. Click **New Runner**
3. Copy the registration token
4. Run the registration command:
```bash
act_runner register \
--instance https://your-gitea-instance.com \
--token YOUR_REGISTRATION_TOKEN \
--name "linux-runner-1" \
--labels ubuntu-latest,linux,docker \
--no-interactive
```
#### Start the Runner as a Service
Create a systemd service file at `/etc/systemd/system/gitea-runner.service`:
```ini
[Unit]
Description=Gitea Actions Runner
After=network.target
[Service]
Type=simple
User=git
WorkingDirectory=/var/lib/gitea-runner
ExecStart=/usr/local/bin/act_runner daemon
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
```
Enable and start the service:
```bash
sudo systemctl daemon-reload
sudo systemctl enable gitea-runner
sudo systemctl start gitea-runner
```
#### Check Runner Status
```bash
sudo systemctl status gitea-runner
```
Verify in Gitea: The runner should appear as **Online** with the `ubuntu-latest` label.
### Option 2: Using Self-Hosted Runners with Docker
If you prefer Docker-based execution:
#### Install Docker
```bash
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
```
#### Configure Runner to Use Docker
Ensure the runner has access to the Docker socket:
```bash
sudo usermod -aG docker act_runner_user
```
The workflows will now run containers inside the runner's Docker environment.
### Option 3: Using External Runners (GitHub Actions Runner Compatible)
If you want to use standard GitHub Actions runners:
```bash
# Download and configure GitHub Actions runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.311.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.311.0.tar.gz
# Configure to point to Gitea instance
./config.sh --url https://your-gitea-instance.com --token YOUR_TOKEN
```
## Caching Strategy
### Node.js and Yarn Cache
All workflows use `actions/setup-node@v4` with built-in caching:
```yaml
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
```
This caches:
- `node_modules` directory
- Yarn cache directory (`~/.yarn/cache`)
- Reduces installation time from minutes to seconds on subsequent runs
### Playwright Cache
Playwright browsers are installed fresh each time. To cache Playwright (optional optimization):
```yaml
- name: Cache Playwright binaries
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-playwright-
```
## Environment Variables
### Default Environment Variables
The workflows use the following environment variables:
```bash
NODE_ENV=production # For build workflow
NODE_VERSION=20 # Node.js version used across all workflows
```
### Custom Environment Variables
To add custom environment variables:
1. Go to repository **Settings****Variables****Actions**
2. Click **Add New Variable**
3. Add variable name and value
4. Set scope (environment, repository, or organization)
Example for feature flags:
```
ENABLE_ANALYTICS=false
API_URL=https://api.example.com
```
Access in workflow:
```yaml
env:
API_URL: ${{ vars.API_URL }}
ENABLE_ANALYTICS: ${{ vars.ENABLE_ANALYTICS }}
```
## Troubleshooting
### Workflows Not Running
**Symptoms**: Workflows don't appear or don't trigger
**Solutions**:
1. Verify Actions is enabled in Gitea site administration
2. Check repository Settings → Actions is enabled
3. Verify workflow files are in `.gitea/workflows/` directory
4. Check workflow YAML syntax (no indentation errors)
### Runner Offline
**Symptoms**: Runner shows as **Offline** or **Idle**
**Solutions**:
1. Check runner service status: `sudo systemctl status gitea-runner`
2. Review runner logs: `journalctl -u gitea-runner -f`
3. Verify network connectivity to Gitea instance
4. Restart runner: `sudo systemctl restart gitea-runner`
### Linting Fails with Formatting Errors
**Symptoms**: `dprint check` fails on CI but passes locally
**Solutions**:
1. Ensure dprint configuration (`dprint.json`) is committed
2. Run `yarn dprint fmt` locally before committing
3. Consider adding auto-fix workflow (see below)
### Playwright Tests Timeout
**Symptoms**: E2E tests fail with timeout errors
**Solutions**:
1. Check `playwright.config.ts` timeout settings
2. Ensure preview server starts before tests run (built into config)
3. Increase timeout in workflow:
```yaml
- name: Run Playwright tests
run: yarn test:e2e
env:
PLAYWRIGHT_TIMEOUT: 60000
```
### Build Fails with Out of Memory
**Symptoms**: Build fails with memory allocation errors
**Solutions**:
1. Increase Node.js memory limit:
```yaml
- name: Build project
run: yarn build
env:
NODE_OPTIONS: --max-old-space-size=4096
```
2. Ensure runner has sufficient RAM (minimum 2GB recommended)
### Permission Denied on Runner
**Symptoms**: Runner can't access repository or secrets
**Solutions**:
1. Verify runner has read access to repository
2. Check secret names match exactly in workflow
3. Ensure runner user has file system permissions
### Yarn Install Fails with Lockfile Conflict
**Symptoms**: `yarn install --frozen-lockfile` fails
**Solutions**:
1. Ensure `yarn.lock` is up-to-date locally
2. Run `yarn install` and commit updated `yarn.lock`
3. Do not use `--frozen-lockfile` if using different platforms (arm64 vs amd64)
### Slow Workflow Execution
**Symptoms**: Workflows take too long to complete
**Solutions**:
1. Verify caching is working (check logs for "Cache restored")
2. Use `--frozen-lockfile` for faster dependency resolution
3. Consider matrix strategy for parallel execution (not currently used)
4. Optimize Playwright tests (reduce test count, increase timeouts only if needed)
## Best Practices
### 1. Keep Dependencies Updated
Regularly update action versions:
```yaml
- uses: actions/checkout@v4 # Update from v3 to v4 when available
- uses: actions/setup-node@v4
```
### 2. Use Frozen Lockfile
Always use `--frozen-lockfile` in CI to ensure reproducible builds:
```bash
yarn install --frozen-lockfile
```
### 3. Monitor Workflow Status
Set up notifications for workflow failures:
- Email notifications in Gitea user settings
- Integrate with Slack/Mattermost for team alerts
- Use status badges in README
### 4. Test Locally Before Pushing
Run the same checks locally:
```bash
yarn lint # oxlint
yarn dprint check # Formatting check
yarn tsc --noEmit # Type check
yarn test:e2e # E2E tests
yarn build # Build
```
### 5. Leverage Git Hooks
The project uses lefthook for pre-commit/pre-push checks. This catches issues before they reach CI:
```bash
# Pre-commit: Format code, lint staged files
# Pre-push: Full type check, format check, full lint
```
## Additional Resources
- [Gitea Actions Documentation](https://docs.gitea.com/usage/actions/overview)
- [Gitea act_runner Documentation](https://docs.gitea.com/usage/actions/act-runner)
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
- [SvelteKit Deployment Guide](https://kit.svelte.dev/docs/adapters)
- [Playwright CI/CD Guide](https://playwright.dev/docs/ci)
## Status Badges
Add status badges to your README.md:
```markdown
![Lint](https://your-gitea-instance.com/username/glyphdiff/actions/badges/workflow/lint.yml/badge.svg)
![Test](https://your-gitea-instance.com/username/glyphdiff/actions/badges/workflow/test.yml/badge.svg)
![Build](https://your-gitea-instance.com/username/glyphdiff/actions/badges/workflow/build.yml/badge.svg)
```
## Next Steps
1. **Customize deployment**: Modify `deploy.yml` with your deployment strategy
2. **Add notifications**: Set up workflow failure notifications
3. **Optimize caching**: Add Playwright cache if needed
4. **Add badges**: Include status badges in README
5. **Schedule tasks**: Add periodic tests or dependency updates (optional)
---
**Last Updated**: December 30, 2025
**Version**: 1.0.0

View File

@@ -0,0 +1,32 @@
name: Build
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Install
run: yarn install --frozen-lockfile --prefer-offline
- name: Build Svelte App
run: yarn build
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: dist/
retention-days: 7

View File

@@ -0,0 +1,42 @@
name: Deploy Pipeline
on:
push:
branches: [main]
workflow_dispatch:
inputs:
environment:
description: 'Target'
required: true
default: 'production'
type: choice
options: [staging, production]
jobs:
pipeline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Install
run: yarn install --frozen-lockfile --prefer-offline
- name: Validation
run: |
yarn oxlint .
yarn svelte-check
- name: Build for Production
run: yarn build
env:
NODE_ENV: production
- name: Deploy Step
run: |
echo "Deploying dist/ to ${{ github.event.inputs.environment || 'production' }}..."
# EXAMPLE: rsync -avz dist/ user@your-vps:/var/www/html/

48
.gitea/workflows/lint.yml Normal file
View File

@@ -0,0 +1,48 @@
name: Lint
on:
push:
branches:
- main
- develop
- feature/*
pull_request:
branches:
- main
- develop
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint Code
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: Persistent Yarn Cache
uses: actions/cache@v4
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile --prefer-offline

69
.gitea/workflows/test.yml Normal file
View File

@@ -0,0 +1,69 @@
name: Test
on:
push:
branches: [main, develop, "feature/*"]
pull_request:
branches: [main, develop]
workflow_dispatch:
jobs:
test:
name: Svelte Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Install
run: yarn install --frozen-lockfile --prefer-offline
- name: Type Check
run: yarn svelte-check --threshold warning
- name: Lint
run: yarn oxlint .
# e2e-tests:
# name: E2E Tests (Playwright)
# runs-on: ubuntu-latest
#
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
#
# - name: Setup Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '20'
# cache: 'yarn'
#
# - name: Install dependencies
# run: yarn install --frozen-lockfile
#
# - name: Install Playwright browsers
# run: yarn playwright install --with-deps
#
# - name: Run Playwright tests
# run: yarn test:e2e
#
# - name: Upload Playwright report
# if: always()
# uses: actions/upload-artifact@v4
# with:
# name: playwright-report
# path: playwright-report/
# retention-days: 7
#
# - name: Upload Playwright screenshots (on failure)
# if: failure()
# uses: actions/upload-artifact@v4
# with:
# name: playwright-screenshots
# path: test-results/
# retention-days: 7
#
# Note: E2E tests are disabled until Playwright setup is complete.
# Uncomment this job section when Playwright tests are ready to run.

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@ node_modules
.wrangler
/.svelte-kit
/build
/dist
# OS
.DS_Store

Binary file not shown.

12
index.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>glyphdiff</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -20,7 +20,9 @@ pre-push:
run: yarn svelte-check --threshold warning
format-check:
run: yarn dprint check
glob: "*.{ts,js,svelte,json,md}"
run: yarn dprint check {push_files}
lint-full:
run: yarn oxlint .
glob: "*.{ts,js,svelte}"
run: yarn oxlint {push_files}

View File

@@ -4,12 +4,12 @@
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"dev": "vite",
"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",
"prepare": "svelte-check --tsconfig ./tsconfig.json || echo ''",
"check": "svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "oxlint",
"format": "dprint fmt",
"format:check": "dprint check",
@@ -19,8 +19,6 @@
"devDependencies": {
"@lucide/svelte": "^0.562.0",
"@playwright/test": "^1.57.0",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/kit": "^2.49.1",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@tailwindcss/vite": "^4.1.18",
"clsx": "^2.1.1",

View File

@@ -1,6 +1,6 @@
import { defineConfig } from '@playwright/test';
export default defineConfig({
webServer: { command: 'npm run build && npm run preview', port: 4173 },
webServer: { command: 'yarn build && yarn preview', port: 4173 },
testDir: 'e2e',
});

20
src/App.svelte Normal file
View File

@@ -0,0 +1,20 @@
<script lang="ts">
import favicon from '$lib/assets/favicon.svg';
import './app.css';
import Page from './routes/Page.svelte';
</script>
<svelte:head>
<link rel="icon" href={favicon} />
</svelte:head>
<div id="app-root">
<Page />
</div>
<style>
#app-root {
width: 100%;
height: 100vh;
}
</style>

20
src/ambient.d.ts vendored Normal file
View File

@@ -0,0 +1,20 @@
declare module '*.svelte' {
import type { ComponentType } from 'svelte';
const component: ComponentType;
export default component;
}
declare module '*.svg' {
const content: string;
export default content;
}
declare module '*.png' {
const content: string;
export default content;
}
declare module '*.jpg' {
const content: string;
export default content;
}

13
src/app.d.ts vendored
View File

@@ -1,13 +0,0 @@
// 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 {};

View File

@@ -1,11 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@@ -0,0 +1,21 @@
/**
* Generic collection API response model
* Use this for APIs that return collections of items
*
* @template T - The type of items in the collection array
* @template K - The key used to access the collection array in the response
*/
export type CollectionApiModel<T, K extends string = 'items'> = Record<K, T[]> & {
/**
* Number of items returned in the current page/response
*/
count: number;
/**
* Total number of items available across all pages
*/
count_total: number;
/**
* Indicates if there are more items available beyond this page
*/
has_more: boolean;
};

37
src/lib/types/common.ts Normal file
View File

@@ -0,0 +1,37 @@
/**
* Model of response with error
*/
export interface ApiErrorResponse {
/**
* Error text
*/
error: string;
/**
* Status
*/
status: number;
/**
* Status text
*/
statusText: string;
}
/**
* Model of response with success
*/
export interface ApiSuccessResponse<T> {
/**
* Data
*/
data: T;
/**
* Status
*/
status: number;
/**
* Status text
*/
statusText: string;
}
export type ApiResponse<T> = ApiErrorResponse | ApiSuccessResponse<T>;

View File

@@ -0,0 +1,439 @@
import type { CollectionApiModel } from './collection';
/**
* Model of Fontshare API response
* @see https://fontshare.com
*/
export type FontshareApiModel = CollectionApiModel<FontshareFont>;
/**
* Individual font metadata from Fontshare API
*/
export interface FontshareFont {
/**
* Unique identifier for the font
* UUID v4 format (e.g., "20e9fcdc-1e41-4559-a43d-1ede0adc8896")
*/
id: string;
/**
* Display name of the font family
* Examples: "Satoshi", "General Sans", "Clash Display"
*/
name: string;
/**
* Native/localized name of the font (if available)
* Often null for Latin-script fonts
*/
native_name: string | null;
/**
* URL-friendly identifier for the font
* Used in URLs: e.g., "satoshi", "general-sans", "clash-display"
*/
slug: string;
/**
* Font category classification
* Examples: "Sans", "Serif", "Display", "Script"
*/
category: string;
/**
* Script/writing system supported by the font
* Examples: "latin", "arabic", "devanagari"
*/
script: string;
/**
* Font publisher/foundry information
*/
publisher: FontsharePublisher;
/**
* Array of designers who created this font
* Multiple designers may have collaborated on a single font
*/
designers: FontshareDesigner[];
/**
* Related font families (if any)
* Often null, as fonts are typically independent
*/
related_families: string | null;
/**
* Whether to display publisher as the designer instead of individual designers
*/
display_publisher_as_designer: boolean;
/**
* Whether trial downloads are enabled for this font
*/
trials_enabled: boolean;
/**
* Whether to show Latin-specific metrics
*/
show_latin_metrics: boolean;
/**
* Type of license for this font
* Examples: "itf_ffl" (ITF Free Font License)
*/
license_type: string;
/**
* Comma-separated list of languages supported by this font
* Example: "Afar, Afrikaans, Albanian, Aranese, Aromanian, Aymara, ..."
*/
languages: string;
/**
* ISO 8601 timestamp when the font was added to Fontshare
* Format: "2021-03-12T20:49:05Z"
*/
inserted_at: string;
/**
* HTML-formatted story/description about the font
* Contains marketing text, design philosophy, and usage recommendations
*/
story: string;
/**
* Version of the font family
* Format: "1.0", "1.2", etc.
*/
version: string;
/**
* Total number of times this font has been viewed
*/
views: number;
/**
* Number of views in the recent time period
*/
views_recent: number;
/**
* Whether this font is marked as "hot"/trending
*/
is_hot: boolean;
/**
* Whether this font is marked as new
*/
is_new: boolean;
/**
* Whether this font is in the shortlisted collection
*/
is_shortlisted: boolean | null;
/**
* Whether this font is marked as top/popular
*/
is_top: boolean;
/**
* Variable font axes (for variable fonts)
* Empty array [] for static fonts
*/
axes: FontshareAxis[];
/**
* Tags/categories for this font
* Examples: ["Magazines", "Branding", "Logos", "Posters"]
*/
font_tags: FontshareTag[];
/**
* OpenType features available in this font
*/
features: FontshareFeature[];
/**
* Array of available font styles/variants
* Each style represents a different font file (weight, italic, variable)
*/
styles: FontshareStyle[];
}
/**
* Publisher/foundry information
*/
export interface FontsharePublisher {
/**
* Description/bio of the publisher
* Example: "Indian Type Foundry (ITF) creates retail and custom multilingual fonts..."
*/
bio: string;
/**
* Publisher email (if available)
*/
email: string | null;
/**
* Unique publisher identifier
* UUID format
*/
id: string;
/**
* Publisher links (social media, website, etc.)
*/
links: FontshareLink[];
/**
* Publisher name
* Example: "Indian Type Foundry"
*/
name: string;
}
/**
* Designer information
*/
export interface FontshareDesigner {
/**
* Designer bio/description
*/
bio: string;
/**
* Designer links (Twitter, website, etc.)
*/
links: FontshareLink[];
/**
* Designer name
*/
name: string;
}
/**
* Link information
*/
export interface FontshareLink {
/**
* Name of the link platform/site
* Examples: "Twitter", "GitHub", "Website"
*/
name: string;
/**
* URL of the link (may be null)
*/
url: string | null;
}
/**
* Font tag/category
*/
export interface FontshareTag {
/**
* Tag name
* Examples: "Magazines", "Branding", "Logos", "Posters"
*/
name: string;
}
/**
* OpenType feature
*/
export interface FontshareFeature {
/**
* Feature name (descriptive name or null)
* Examples: "Alternate t", "All Alternates", or null
*/
name: string | null;
/**
* Whether this feature is on by default
*/
on_by_default: boolean;
/**
* OpenType feature tag (4-character code)
* Examples: "ss01", "frac", "liga", "aalt", "case"
*/
tag: string;
}
/**
* Variable font axis (for variable fonts)
* Defines the range and properties of a variable font axis (e.g., weight)
*/
export interface FontshareAxis {
/**
* Name of the axis
* Example: "wght" (weight axis)
*/
name: string;
/**
* CSS property name for the axis
* Example: "wght"
*/
property: string;
/**
* Default value for the axis
* Example: 420.0, 650.0, 700.0
*/
range_default: number;
/**
* Minimum value for the axis
* Example: 300.0, 100.0, 200.0
*/
range_left: number;
/**
* Maximum value for the axis
* Example: 900.0, 700.0, 800.0
*/
range_right: number;
}
/**
* Individual font style/variant
* Each style represents a single downloadable font file
*/
export interface FontshareStyle {
/**
* Unique identifier for this style
* UUID format
*/
id: string;
/**
* Whether this is the default style for the font family
* Typically, one style per font is marked as default
*/
default: boolean;
/**
* CDN URL to the font file
* Protocol-relative URL: "//cdn.fontshare.com/wf/..."
* Note: URL starts with "//" (protocol-relative), may need protocol prepended
*/
file: string;
/**
* Whether this style is italic
* false for upright, true for italic styles
*/
is_italic: boolean;
/**
* Whether this is a variable font
* Variable fonts have adjustable axes (weight, slant, etc.)
*/
is_variable: boolean;
/**
* Typography properties for this style
* Contains measurements like cap height, x-height, ascenders/descenders
* May be empty object {} for some styles
*/
properties: FontshareStyleProperties | Record<string, never>;
/**
* Weight information for this style
*/
weight: FontshareWeight;
}
/**
* Typography/measurement properties for a font style
*/
export interface FontshareStyleProperties {
/**
* Distance from baseline to the top of ascenders
* Example: 1010, 990, 1000
*/
ascending_leading: number | null;
/**
* Height of uppercase letters (cap height)
* Example: 710, 680, 750
*/
cap_height: number | null;
/**
* Distance from baseline to the bottom of descenders (negative value)
* Example: -203, -186, -220
*/
descending_leading: number | null;
/**
* Body height of the font
* Often null in Fontshare data
*/
body_height: number | null;
/**
* Maximum character width in the font
* Example: 1739, 1739, 1739
*/
max_char_width: number | null;
/**
* Height of lowercase x-height
* Example: 480, 494, 523
*/
x_height: number | null;
/**
* Maximum Y coordinate (top of ascenders)
* Example: 1010, 990, 1026
*/
y_max: number | null;
/**
* Minimum Y coordinate (bottom of descenders)
* Example: -240, -250, -280
*/
y_min: number | null;
}
/**
* Weight information for a font style
*/
export interface FontshareWeight {
/**
* Display label for the weight
* Examples: "Light", "Regular", "Bold", "Variable", "Variable Italic"
*/
label: string;
/**
* Internal name for the weight
* Examples: "Light", "Regular", "Bold", "Variable", "VariableItalic"
*/
name: string;
/**
* Native/localized name for the weight (if available)
* Often null for Latin-script fonts
*/
native_name: string | null;
/**
* Numeric weight value
* Examples: 300, 400, 700, 0 (for variable fonts), 1, 2
* Note: This matches the `weight` property
*/
number: number;
/**
* Numeric weight value (duplicate of `number`)
* Appears to be redundant with `number` field
*/
weight: number;
}

View File

@@ -0,0 +1,104 @@
/**
* Model of google fonts api response
*/
export interface GoogleFontsApiModel {
/**
* Array of font items returned by the Google Fonts API
* Contains all font families matching the requested query parameters
*/
items: FontItem[];
}
export interface FontItem {
/**
* Font family name (e.g., "Roboto", "Open Sans", "Lato")
* This is the name used in CSS font-family declarations
*/
family: string;
/**
* Font category classification (e.g., "sans-serif", "serif", "display", "handwriting", "monospace")
* Useful for grouping and filtering fonts by style
*/
category: string;
/**
* Available font variants for this font family
* Array of strings representing available weights and styles
* Examples: ["regular", "italic", "100", "200", "300", "400", "500", "600", "700", "800", "900", "100italic", "900italic"]
* The keys in the `files` object correspond to these variant values
*/
variants: FontVariant[];
/**
* Supported character subsets for this font
* Examples: ["latin", "latin-ext", "cyrillic", "greek", "arabic", "devanagari", "vietnamese", "hebrew", "thai", etc.]
* Determines which character sets are included in the font files
*/
subsets: string[];
/**
* Font version identifier
* Format: "v" followed by version number (e.g., "v31", "v20", "v1")
* Used to track font updates and cache busting
*/
version: string;
/**
* Last modification date of the font
* Format: ISO 8601 date string (e.g., "2024-01-15", "2023-12-01")
* Indicates when the font was last updated by the font foundry
*/
lastModified: string;
/**
* Mapping of font variants to their downloadable URLs
* Keys correspond to values in the `variants` array
* Examples:
* - "regular" → "https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Me4W..."
* - "700" → "https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlf..."
* - "700italic" → "https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzA..."
*/
files: FontFiles;
/**
* URL to the font menu preview image
* Typically a PNG showing the font family name in the font
* Example: "https://fonts.gstatic.com/l/font?kit=KFOmCnqEu92Fr1Me4W...&s=i2"
*/
menu: string;
}
/**
* Standard font weights that can appear in Google Fonts API
*/
export type FontWeight = '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
/**
* Italic variant format: e.g., "100italic", "400italic", "700italic"
*/
export type FontWeightItalic = `${FontWeight}italic`;
/**
* All possible font variants in Google Fonts API
* - Numeric weights: "400", "700", etc.
* - Italic variants: "400italic", "700italic", etc.
* - Legacy names: "regular", "italic", "bold", "bolditalic"
*/
export type FontVariant =
| FontWeight
| FontWeightItalic
| 'regular'
| 'italic'
| 'bold'
| 'bolditalic';
/**
* Google Fonts API file mapping
* Dynamic keys that match the variants array
*
* Examples:
* - { "regular": "...", "italic": "...", "700": "...", "700italic": "..." }
* - { "400": "...", "400italic": "...", "900": "..." }
*/
export type FontFiles = Partial<Record<FontVariant, string>>;

6
src/main.ts Normal file
View File

@@ -0,0 +1,6 @@
import { mount } from 'svelte';
import App from './App.svelte';
mount(App, {
target: document.getElementById('app')!,
});

View File

@@ -1,11 +0,0 @@
<script lang="ts">
import favicon from '$lib/assets/favicon.svg';
import '../app.css';
let { children } = $props();
</script>
<svelte:head>
<link rel="icon" href={favicon} />
</svelte:head>
{@render children()}

View File

@@ -1,9 +0,0 @@
<script>
import Button from '$lib/components/ui/button/button.svelte';
</script>
<h1>Welcome to SvelteKit</h1>
<p>
Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation
</p>
<Button>Click me!</Button>

9
src/routes/Page.svelte Normal file
View File

@@ -0,0 +1,9 @@
<script>
import Button from '$lib/components/ui/button/button.svelte';
</script>
<h1>Welcome to Svelte + Vite</h1>
<p>
Visit <a href="https://svelte.dev/docs">svelte.dev/docs</a> to read the documentation
</p>
<Button>Click me!</Button>

View File

@@ -1,7 +1,5 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
@@ -10,13 +8,6 @@ const config = {
compilerOptions: {
runes: true,
},
kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter(),
},
};
export default config;

View File

@@ -1,7 +1,12 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"rewriteRelativeImportExtensions": true,
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
/* Strictness & Safety */
"strict": true,
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
@@ -9,13 +14,24 @@
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
"isolatedModules": true,
"verbatimModuleSyntax": true,
/* Path Aliases */
"baseUrl": ".",
"paths": {
"$lib/*": ["./src/lib/*"]
}
},
"exclude": ["./src/lib/components/ui"]
// 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
"include": [
"src/**/*.ts",
"src/**/*.js",
"src/**/*.svelte",
"src/**/*.d.ts"
],
"exclude": [
"node_modules",
"dist",
"./src/lib/components/ui"
]
}

View File

@@ -1,7 +1,15 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit(), tailwindcss()],
plugins: [svelte(), tailwindcss()],
resolve: {
alias: {
$lib: '/src/lib',
},
},
build: {
outDir: 'dist',
},
});

125
yarn.lock
View File

@@ -357,13 +357,13 @@ __metadata:
linkType: hard
"@napi-rs/wasm-runtime@npm:^1.1.0":
version: 1.1.0
resolution: "@napi-rs/wasm-runtime@npm:1.1.0"
version: 1.1.1
resolution: "@napi-rs/wasm-runtime@npm:1.1.1"
dependencies:
"@emnapi/core": "npm:^1.7.1"
"@emnapi/runtime": "npm:^1.7.1"
"@tybys/wasm-util": "npm:^0.10.1"
checksum: 10c0/ee351052123bfc635c4cef03ac273a686522394ccd513b1e5b7b3823cecd6abb4a31f23a3a962933192b87eb7b7c3eb3def7748bd410edc66f932d90cf44e9ab
checksum: 10c0/04d57b67e80736e41fe44674a011878db0a8ad893f4d44abb9d3608debb7c174224cba2796ed5b0c1d367368159f3ca6be45f1c59222f70e32ddc880f803d447
languageName: node
linkType: hard
@@ -456,13 +456,6 @@ __metadata:
languageName: node
linkType: hard
"@polka/url@npm:^1.0.0-next.24":
version: 1.0.0-next.29
resolution: "@polka/url@npm:1.0.0-next.29"
checksum: 10c0/0d58e081844095cb029d3c19a659bfefd09d5d51a2f791bc61eba7ea826f13d6ee204a8a448c2f5a855c17df07b37517373ff916dd05801063c0568ae9937684
languageName: node
linkType: hard
"@rollup/rollup-android-arm-eabi@npm:4.54.0":
version: 4.54.0
resolution: "@rollup/rollup-android-arm-eabi@npm:4.54.0"
@@ -617,13 +610,6 @@ __metadata:
languageName: node
linkType: hard
"@standard-schema/spec@npm:^1.0.0":
version: 1.1.0
resolution: "@standard-schema/spec@npm:1.1.0"
checksum: 10c0/d90f55acde4b2deb983529c87e8025fa693de1a5e8b49ecc6eb84d1fd96328add0e03d7d551442156c7432fd78165b2c26ff561b970a9a881f046abb78d6a526
languageName: node
linkType: hard
"@sveltejs/acorn-typescript@npm:^1.0.5":
version: 1.0.8
resolution: "@sveltejs/acorn-typescript@npm:1.0.8"
@@ -633,46 +619,6 @@ __metadata:
languageName: node
linkType: hard
"@sveltejs/adapter-auto@npm:^7.0.0":
version: 7.0.0
resolution: "@sveltejs/adapter-auto@npm:7.0.0"
peerDependencies:
"@sveltejs/kit": ^2.0.0
checksum: 10c0/928393d4e366a0094bec5e09c70e44c3c7b5f17687f1735f71eb305e1ae640c64cd9f7d8f80d8b31dcaa235ae2b07e0f021a9204b5cec895d5bd8035064c9195
languageName: node
linkType: hard
"@sveltejs/kit@npm:^2.49.1":
version: 2.49.2
resolution: "@sveltejs/kit@npm:2.49.2"
dependencies:
"@standard-schema/spec": "npm:^1.0.0"
"@sveltejs/acorn-typescript": "npm:^1.0.5"
"@types/cookie": "npm:^0.6.0"
acorn: "npm:^8.14.1"
cookie: "npm:^0.6.0"
devalue: "npm:^5.3.2"
esm-env: "npm:^1.2.2"
kleur: "npm:^4.1.5"
magic-string: "npm:^0.30.5"
mrmime: "npm:^2.0.0"
sade: "npm:^1.8.1"
set-cookie-parser: "npm:^2.6.0"
sirv: "npm:^3.0.0"
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
svelte: ^4.0.0 || ^5.0.0-next.0
vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0
peerDependenciesMeta:
"@opentelemetry/api":
optional: true
bin:
svelte-kit: svelte-kit.js
checksum: 10c0/05bf6d0d9fb87d894d1a5667f0473d8e83a4584f5c80850ed9eef1d1c8982236ea0d703bbd83befdff30c729d102fc67f2839014d900edb15f6b449ee059b9aa
languageName: node
linkType: hard
"@sveltejs/vite-plugin-svelte-inspector@npm:^5.0.0":
version: 5.0.1
resolution: "@sveltejs/vite-plugin-svelte-inspector@npm:5.0.1"
@@ -875,13 +821,6 @@ __metadata:
languageName: node
linkType: hard
"@types/cookie@npm:^0.6.0":
version: 0.6.0
resolution: "@types/cookie@npm:0.6.0"
checksum: 10c0/5b326bd0188120fb32c0be086b141b1481fec9941b76ad537f9110e10d61ee2636beac145463319c71e4be67a17e85b81ca9e13ceb6e3bb63b93d16824d6c149
languageName: node
linkType: hard
"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.5, @types/estree@npm:^1.0.6":
version: 1.0.8
resolution: "@types/estree@npm:1.0.8"
@@ -896,7 +835,7 @@ __metadata:
languageName: node
linkType: hard
"acorn@npm:^8.12.1, acorn@npm:^8.14.1":
"acorn@npm:^8.12.1":
version: 8.15.0
resolution: "acorn@npm:8.15.0"
bin:
@@ -968,13 +907,6 @@ __metadata:
languageName: node
linkType: hard
"cookie@npm:^0.6.0":
version: 0.6.0
resolution: "cookie@npm:0.6.0"
checksum: 10c0/f2318b31af7a31b4ddb4a678d024514df5e705f9be5909a192d7f116cfb6d45cbacf96a473fa733faa95050e7cff26e7832bb3ef94751592f1387b71c8956686
languageName: node
linkType: hard
"debug@npm:4, debug@npm:^4.3.4, debug@npm:^4.4.1":
version: 4.4.3
resolution: "debug@npm:4.4.3"
@@ -1001,7 +933,7 @@ __metadata:
languageName: node
linkType: hard
"devalue@npm:^5.3.2, devalue@npm:^5.5.0":
"devalue@npm:^5.5.0":
version: 5.6.1
resolution: "devalue@npm:5.6.1"
checksum: 10c0/4dca0e800336003fd1e268c142adfe78f3539cda7384b4f69762a93e0dfc33e223b580251da0a6da4be44962958fcba5eadf122f9720e09f437b28904af9c43e
@@ -1168,7 +1100,7 @@ __metadata:
languageName: node
linkType: hard
"esm-env@npm:^1.2.1, esm-env@npm:^1.2.2":
"esm-env@npm:^1.2.1":
version: 1.2.2
resolution: "esm-env@npm:1.2.2"
checksum: 10c0/3d25c973f2fd69c25ffff29c964399cea573fe10795ecc1d26f6f957ce0483d3254e1cceddb34bf3296a0d7b0f1d53a28992f064ba509dfe6366751e752c4166
@@ -1267,8 +1199,6 @@ __metadata:
dependencies:
"@lucide/svelte": "npm:^0.562.0"
"@playwright/test": "npm:^1.57.0"
"@sveltejs/adapter-auto": "npm:^7.0.0"
"@sveltejs/kit": "npm:^2.49.1"
"@sveltejs/vite-plugin-svelte": "npm:^6.2.1"
"@tailwindcss/vite": "npm:^4.1.18"
clsx: "npm:^2.1.1"
@@ -1368,13 +1298,6 @@ __metadata:
languageName: node
linkType: hard
"kleur@npm:^4.1.5":
version: 4.1.5
resolution: "kleur@npm:4.1.5"
checksum: 10c0/e9de6cb49657b6fa70ba2d1448fd3d691a5c4370d8f7bbf1c2f64c24d461270f2117e1b0afe8cb3114f13bbd8e51de158c2a224953960331904e636a5e4c0f2a
languageName: node
linkType: hard
"lefthook-darwin-arm64@npm:2.0.13":
version: 2.0.13
resolution: "lefthook-darwin-arm64@npm:2.0.13"
@@ -1620,7 +1543,7 @@ __metadata:
languageName: node
linkType: hard
"magic-string@npm:^0.30.11, magic-string@npm:^0.30.17, magic-string@npm:^0.30.21, magic-string@npm:^0.30.5":
"magic-string@npm:^0.30.11, magic-string@npm:^0.30.17, magic-string@npm:^0.30.21":
version: 0.30.21
resolution: "magic-string@npm:0.30.21"
dependencies:
@@ -1740,13 +1663,6 @@ __metadata:
languageName: node
linkType: hard
"mrmime@npm:^2.0.0":
version: 2.0.1
resolution: "mrmime@npm:2.0.1"
checksum: 10c0/af05afd95af202fdd620422f976ad67dc18e6ee29beb03dd1ce950ea6ef664de378e44197246df4c7cdd73d47f2e7143a6e26e473084b9e4aa2095c0ad1e1761
languageName: node
linkType: hard
"ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
@@ -2020,7 +1936,7 @@ __metadata:
languageName: node
linkType: hard
"sade@npm:^1.7.4, sade@npm:^1.8.1":
"sade@npm:^1.7.4":
version: 1.8.1
resolution: "sade@npm:1.8.1"
dependencies:
@@ -2045,24 +1961,6 @@ __metadata:
languageName: node
linkType: hard
"set-cookie-parser@npm:^2.6.0":
version: 2.7.2
resolution: "set-cookie-parser@npm:2.7.2"
checksum: 10c0/4381a9eb7ee951dfe393fe7aacf76b9a3b4e93a684d2162ab35594fa4053cc82a4d7d7582bf397718012c9adcf839b8cd8f57c6c42901ea9effe33c752da4a45
languageName: node
linkType: hard
"sirv@npm:^3.0.0":
version: 3.0.2
resolution: "sirv@npm:3.0.2"
dependencies:
"@polka/url": "npm:^1.0.0-next.24"
mrmime: "npm:^2.0.0"
totalist: "npm:^3.0.0"
checksum: 10c0/5930e4397afdb14fbae13751c3be983af4bda5c9aadec832607dc2af15a7162f7d518c71b30e83ae3644b9a24cea041543cc969e5fe2b80af6ce8ea3174b2d04
languageName: node
linkType: hard
"smart-buffer@npm:^4.2.0":
version: 4.2.0
resolution: "smart-buffer@npm:4.2.0"
@@ -2205,13 +2103,6 @@ __metadata:
languageName: node
linkType: hard
"totalist@npm:^3.0.0":
version: 3.0.1
resolution: "totalist@npm:3.0.1"
checksum: 10c0/4bb1fadb69c3edbef91c73ebef9d25b33bbf69afe1e37ce544d5f7d13854cda15e47132f3e0dc4cafe300ddb8578c77c50a65004d8b6e97e77934a69aa924863
languageName: node
linkType: hard
"tslib@npm:^2.4.0":
version: 2.8.1
resolution: "tslib@npm:2.8.1"