MTG Collection Tracker — Platform Design Document
A self-hosted Magic: The Gathering collection and deck management platform. Syncs the complete Scryfall card database, provides advanced search and filtering, tracks collections with finish-level granularity, and supports deck building with multi-source import and cross-printing ownership validation.
1. Platform Overview
MTG Collection Tracker is a self-hosted web application for Magic: The Gathering players who want full control over their collection data. It maintains a local mirror of the Scryfall card database, provides powerful search and filtering, tracks card ownership with per-finish granularity, and includes a complete deck building workflow with import from popular deck-building sites.
Core value proposition:
- Complete card database — Full Scryfall mirror with daily automated sync, including all printings, prices, and card faces
- Finish-level tracking — Separate quantities for nonfoil, foil, and etched versions of each card
- Cross-printing awareness — Deck ownership checks consider all printings of a card (via Oracle ID), not just the specific printing in the deck
- Multi-source deck import — Import from AetherHub, MTGGoldfish, Moxfield, Archidekt, or paste Arena format text
- Self-hosted & private — Your collection data stays on your infrastructure; no third-party accounts required
- Modern full-stack — React + TypeScript frontend with FastAPI async backend, containerised via Docker
2. System Architecture
┌──────────────────────────────────────────────────────────────────┐
│ DOCKER HOST │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ mtg-tracker (Python 3.11 + Static React Build) │ │
│ │ │ │
│ │ ┌─────────────────────┐ ┌───────────────────────────┐ │ │
│ │ │ FastAPI Backend │ │ React SPA (Static) │ │ │
│ │ │ │ │ │ │ │
│ │ │ /api/v1/cards │ │ Tailwind CSS │ │ │
│ │ │ /api/v1/collection │ │ Zustand state │ │ │
│ │ │ /api/v1/decks │ │ TanStack Query/Table │ │ │
│ │ │ /api/v1/sets │ │ Vite build output │ │ │
│ │ │ /api/v1/sync │ │ │ │ │
│ │ └──────────┬───────────┘ └───────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────▼───────────┐ ┌───────────────────────────┐ │ │
│ │ │ SQLAlchemy (Async) │ │ APScheduler │ │ │
│ │ │ SQLite + WAL + FTS5 │ │ Daily midnight sync │ │ │
│ │ └──────────┬───────────┘ └───────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────▼───────────────────────────────────────────┐ │ │
│ │ │ /app/data/mtg.db │ │ │
│ │ │ (Persistent volume: cards, sets, collection, decks) │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
└──────────────────────────────┼────────────────────────────────────┘
│ HTTPS (rate-limited)
┌──────────▼──────────┐
│ Scryfall API │
│ api.scryfall.com │
│ Bulk data download │
│ (~500 MB JSON) │
└─────────────────────┘
2.1 Backend Stack
| Component | Technology | Purpose |
|---|---|---|
| Web Framework | FastAPI 0.109 | Async REST API with automatic OpenAPI docs |
| ASGI Server | Uvicorn 0.27 | Production ASGI server with standard extras |
| ORM | SQLAlchemy 2.0 (async) | Async database access with selectinload for N+1 prevention |
| Database | SQLite + aiosqlite | WAL mode for concurrent reads; FTS5 for full-text search |
| HTTP Client | httpx 0.26 | Async streaming downloads from Scryfall |
| Validation | Pydantic 2.5 | Request/response schemas with strict typing |
| Task Scheduling | APScheduler 3.10 | Daily midnight Scryfall sync via CronTrigger |
| JSON Streaming | Custom parser + ijson | Memory-efficient parsing of 500 MB bulk downloads |
2.2 Frontend Stack
| Component | Technology | Purpose |
|---|---|---|
| Framework | React 18.2 + TypeScript 5.3 | Component-based UI with strict type checking |
| Build Tool | Vite 5.0 | Fast dev server with HMR; optimised production builds |
| Styling | Tailwind CSS 3.4 | Custom MTG colour palette (W, U, B, R, G, C, Gold) |
| Client State | Zustand 4.4 | Lightweight store for UI state (views, filters, modals) |
| Server State | TanStack Query 5.17 | Caching, background refetch, mutation invalidation |
| Tables | TanStack Table 8.11 | Headless table with sorting and pagination |
| HTTP Client | Axios 1.6 | API calls with base URL configuration |
| Icons | Lucide React 0.307 | Consistent icon set across the UI |
2.3 Container Layout
# Stage 1: Build frontend (Node 20 Alpine) FROM node:20-alpine AS frontend-builder npm install → npm run build → /app/frontend/dist/ # Stage 2: Production runtime (Python 3.11 Slim) FROM python:3.11-slim pip install requirements → copy backend code COPY --from=frontend-builder /app/frontend/dist ./static Uvicorn serves both API (/api/) and SPA (/) from single container
Single-container design: The frontend is built at image build time and served as static files by FastAPI. No separate web server or reverse proxy is needed within the container. The API and SPA share port 8080.
3. Data Model
3.1 Database Schema
┌──────────────────┐ ┌────────────────────┐
│ sets │ │ sync_metadata │
├──────────────────┤ ├────────────────────┤
│ code (PK) │ │ key (PK) │
│ name │ │ value │
│ set_type │ │ updated_at │
│ released_at │ └────────────────────┘
│ card_count │
│ icon_svg_uri │ ┌────────────────────┐
└────────┬─────────┘ │ cards_fts │
│ 1:N ├────────────────────┤
┌────────▼─────────┐ │ FTS5 virtual table │
│ cards │ │ card_id, name, │
├──────────────────┤ │ type_line, │
│ id (PK) │ │ oracle_text, │
│ scryfall_id (UQ) │ │ keywords │
│ oracle_id (IDX) │ └────────────────────┘
│ name, cmc │
│ color_identity │◄─────────────────────────────────┐
│ set_code (FK) │ │
│ type_line │ ┌────────────────────┐ │
│ oracle_text │ 1:N │ collection_entries │ │
│ keywords, mana │────────►├────────────────────┤ │
│ power, toughness │ │ id (PK) │ │
│ rarity, finishes │ │ card_id (FK, IDX) │ │
│ prices (usd/foil │ │ finish │ │
│ /etched) │ │ quantity │ │
│ image_uri(s) │ │ acquired_date │ │
└──────────────────┘ │ notes │ │
│ │ UQ(card_id, finish)│ │
│ 1:N └────────┬───────────┘ │
│ │ 1:N │
┌────────▼─────────┐ ┌─────────────▼──────────┐ │
│ decks │ │ deck_cards │ │
├──────────────────┤ ├────────────────────────┤ │
│ id (PK) │1:N │ id (PK) │ │
│ name │───►│ deck_id (FK) │ │
│ description │ │ card_id (FK) ───────────────►│
│ format │ │ collection_entry_id(FK)│
│ import_source │ │ quantity │
│ import_url │ │ board (main/side/cmd) │
└──────────────────┘ └────────────────────────┘
3.2 Key Relationships
| Relationship | Type | Constraint | Notes |
|---|---|---|---|
| Set → Cards | 1:N | FK on set_code | Each card belongs to one set printing |
| Card → CollectionEntries | 1:N | FK on card_id | One entry per card+finish combo (unique constraint) |
| Deck → DeckCards | 1:N | FK on deck_id, cascade delete | Deleting a deck removes all its card entries |
| DeckCard → Card | N:1 | FK on card_id | References the specific printing |
| DeckCard → CollectionEntry | N:1 | FK on collection_entry_id, nullable | Optional link to owned copy |
Oracle ID for cross-printing: The oracle_id field groups all printings of the same logical card. When checking deck ownership, the system queries total owned quantity across all printings sharing the same Oracle ID — so owning a card from any set satisfies the deck requirement.
4. Scryfall Integration
4.1 Sync Lifecycle
App Startup
│
▼
Is database empty? ──yes──► Full sync: Sets → Cards → FTS Index
│ │
no │
│ ┌───────────────┘
▼ ▼
Schedule daily sync Sync complete
(APScheduler, 0:00) │
│ ▼
└──── triggers ──► Sync Sets
│
▼
Fetch bulk data URI from /bulk-data
│
▼
Stream download (~500 MB)
│
▼
Parse JSON objects on-the-fly
(custom streaming parser)
│
▼
Batch upsert (1000 cards/batch)
│
▼
Rebuild FTS5 index
│
▼
Update sync_metadata
4.2 Bulk Data Options
| Type | Config Value | Size | Cards | Use Case |
|---|---|---|---|---|
| Default Cards | default_cards | ~500 MB | ~300,000 | All printings with prices — recommended for collection tracking |
| Oracle Cards | oracle_cards | ~50 MB | ~100,000 | One printing per card — lighter, faster sync |
4.3 Streaming Parser
The bulk data file is a single JSON array containing hundreds of thousands of card objects. To avoid loading the entire file into memory, the sync service uses a custom streaming parser:
- Downloads via
httpx.stream()with chunked transfer - Tracks JSON brace depth to identify complete objects
- Parses each object individually with
json.loads() - Batches 1000 cards per database upsert operation
- Supports progress callbacks for UI feedback
4.4 Card Transformation
Each Scryfall card object is transformed to the local schema:
- Double-faced cards: Oracle text merged from both faces with
//separator - Card faces: Power, toughness, and images extracted from face data when not at top level
- JSON fields:
color_identity,keywords, andfinishesstored as JSON strings - Prices: USD, USD foil, and USD etched extracted from Scryfall’s prices object
- Upsert:
ON CONFLICT (scryfall_id) DO UPDATEensures idempotent syncs
Rate limiting: The Scryfall API requires a minimum 100ms delay between requests. The sync service respects this via a configurable SCRYFALL_REQUEST_DELAY (default: 0.1s). Bulk downloads are single requests and do not require rate limiting.
5. Search & Filtering
The search system combines SQL queries with SQLite FTS5 for comprehensive card discovery:
| Filter | Param | Method | Notes |
|---|---|---|---|
| Name | q | ILIKE pattern | Partial match, case-insensitive |
| CMC (exact) | cmc | Equality | Exact mana value match |
| CMC (range) | cmc_min, cmc_max | Range comparison | Minimum and/or maximum mana value |
| Colours | colors | JSON contains | 4 modes: exact, include_any, include_all, at_most |
| Colour Mode | color_mode | Logic switch | Controls how colour matching works |
| Sets | sets | IN list | Multiple set codes |
| Types | types | ILIKE / OR | Creature, Instant, Sorcery, etc. |
| Oracle Text | oracle | ILIKE pattern | Rules text search |
| Rarity | rarity | IN list | common, uncommon, rare, mythic |
| Finishes | finishes | JSON contains | nonfoil, foil, etched |
| Power | power_min, power_max | Numeric cast + range | Handles special values (*, X) |
| Toughness | toughness_min, toughness_max | Numeric cast + range | Handles special values (*, X) |
| Keywords | keywords | JSON contains / OR | Flying, Trample, Deathtouch, etc. |
5.1 Colour Identity Modes
| Mode | Behaviour | Example: Filtering for W, U |
|---|---|---|
| exact | Card colour identity must match exactly | Only Azorius (W/U) cards |
| include_any | Card must contain at least one selected colour | Any card with W or U (includes mono-W, mono-U, Esper, etc.) |
| include_all | Card must contain all selected colours (may have more) | Any card with both W and U (includes Esper, Bant, etc.) |
| at_most | Card colours must be a subset of selected | W, U, or W/U only (excludes Esper, Bant) |
5.2 Full-Text Search
An FTS5 virtual table (cards_fts) indexes card_id, name, type_line, oracle_text, and keywords. This enables fast full-text search with ranking. The index is rebuilt after every Scryfall sync to stay current.
5.3 Autocomplete
The /api/v1/cards/autocomplete endpoint returns up to 20 card name suggestions for queries of 2+ characters, enabling real-time search-as-you-type in the UI.
6. Collection Management
The collection system tracks card ownership with per-finish granularity. Each collection entry represents a specific card + finish combination (e.g., “Lightning Bolt from M25, foil, qty 2”).
6.1 Finish Tracking
| Finish | Description | Price Field |
|---|---|---|
| nonfoil | Standard printing | prices.usd |
| foil | Foil / premium printing | prices.usd_foil |
| etched | Etched foil (Commander sets) | prices.usd_etched |
A unique constraint on (card_id, finish) ensures one entry per card+finish combination. Adding the same card+finish again increments the quantity rather than creating a duplicate.
6.2 Collection Statistics
The stats endpoint aggregates collection data across multiple dimensions:
- Total cards — Sum of all quantities
- Unique cards — Count of distinct card IDs
- Total value — Sum of (price × quantity) based on finish
- Distribution by set — Card count per set code
- Distribution by colour — Card count per colour identity
- Distribution by rarity — Card count per rarity tier
- Distribution by finish — Card count per finish type
6.3 Export
Collections can be exported in two formats:
- JSON — Full collection entries with card details
- CSV — Flat format suitable for spreadsheets or other tools
7. Deck Building
7.1 Deck Import
Decks can be imported from five sources:
| Source | Method | Board Support |
|---|---|---|
| AetherHub | HTML parsing | Main, Sideboard |
| MTGGoldfish | HTML parsing | Main, Sideboard |
| Moxfield | JSON API | Main, Sideboard, Commander, Maybe |
| Archidekt | JSON API | Main, Sideboard, Commander, Maybe |
| Text (Arena format) | Regex parsing | Main, Sideboard (section headers) |
The import process:
- Fetch the deck list from the source URL (or parse pasted text)
- For each card name, search the local database (exact match → case-insensitive fallback)
- When multiple printings exist, prefer the one already in the user’s collection
- Create the Deck and DeckCard records
- Return a summary:
found_count,missing_count,in_collection_count
7.2 Ownership Validation
The deck validity endpoint (GET /api/v1/decks/{id}/validity) checks whether the user owns all cards needed for a deck. The check is cross-printing aware:
- For each card in the deck, look up its
oracle_id - Query total owned quantity across all printings with the same Oracle ID
- Compare owned quantity against the required quantity in the deck
- Return:
is_complete(boolean),missing_cards[](with names and quantities),total_missing
This means owning a card from any set satisfies the deck requirement — you don’t need the exact printing listed in the deck.
7.3 Board Zones
| Zone | Typical Size | Description |
|---|---|---|
main | 60 / 99 / 100 | Main deck (format-dependent size) |
sideboard | 0–15 | Sideboard cards |
commander | 1–2 | Commander zone (EDH/Brawl) |
maybe | Any | Consideration list (not part of final deck) |
7.3 Export Formats
| Format | Description | Example Output |
|---|---|---|
| txt | Text format grouped by board zone | 4 Lightning Bolt |
| arena | MTG Arena-compatible export | 4 Lightning Bolt (M25) 141 |
| json | Full deck model as JSON | Complete deck object with all card data |
8. API Reference
8.1 Cards
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/cards | Search cards with 16 filter parameters; paginated response |
| GET | /api/v1/cards/autocomplete?q= | Name autocomplete (min 2 chars, max 20 results) |
| GET | /api/v1/cards/{id} | Get card by internal ID |
| GET | /api/v1/cards/by-scryfall/{id} | Get card by Scryfall UUID |
| GET | /api/v1/cards/by-name/{name} | Get card by exact name |
| GET | /api/v1/cards/{id}/usage | Card usage across collection & decks (all printings) |
8.2 Collection
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/collection | List collection entries with filters + finish + deck membership |
| POST | /api/v1/collection | Add card (card_id, finish, quantity); upserts on duplicate |
| PATCH | /api/v1/collection/{id} | Update quantity/notes (quantity=0 deletes the entry) |
| DELETE | /api/v1/collection/{id} | Remove collection entry |
| GET | /api/v1/collection/stats | Aggregated statistics (total, value, distributions) |
| GET | /api/v1/collection/export | Export as JSON or CSV |
8.3 Decks
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/decks | List all decks with card data |
| POST | /api/v1/decks | Create deck (name, description, format) |
| GET | /api/v1/decks/{id} | Get deck with all cards & collection status |
| PATCH | /api/v1/decks/{id} | Update deck metadata |
| DELETE | /api/v1/decks/{id} | Delete deck (cascades to deck_cards) |
| POST | /api/v1/decks/{id}/cards | Add single card to deck |
| POST | /api/v1/decks/{id}/cards/bulk | Bulk add cards by IDs |
| PATCH | /api/v1/decks/{id}/cards/{cid} | Update card in deck (quantity, board) |
| DELETE | /api/v1/decks/{id}/cards/{cid} | Remove card from deck |
| GET | /api/v1/decks/{id}/validity | Cross-printing ownership check |
| GET | /api/v1/decks/{id}/export | Export as txt, arena, or json |
| POST | /api/v1/decks/import | Import from URL (auto-detects source) |
| POST | /api/v1/decks/import-text | Import from pasted Arena text |
8.4 Sets
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/sets | List all sets (ordered by release date, filterable by name/type) |
| GET | /api/v1/sets/{code} | Get single set by code |
| GET | /api/v1/sets/{code}/cards | Get all cards in set (paginated) |
8.5 Sync
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/sync/status | Current sync state, last sync time, DB stats |
| POST | /api/v1/sync/cards | Trigger full card sync (background task) |
| POST | /api/v1/sync/sets | Sync sets only (faster) |
| POST | /api/v1/sync/fts | Rebuild full-text search index |
9. Frontend Architecture
9.1 State Management
The frontend uses a two-tier state approach:
| Layer | Library | Purpose | Examples |
|---|---|---|---|
| Client State | Zustand | UI state, user interactions | Active view, display mode, selected card/deck, modals |
| Server State | TanStack Query | Remote data, caching, sync | Card search results, collection entries, deck data |
Zustand stores:
appStore— Active view (search|collection|decks), display mode (table|grid), selected card/deck, modal visibilityfilterStore— Active filter state with auto-reset of page on filter change
Query caching strategy:
- Card search: 30s stale time (frequent filter changes)
- Sets list: 5min stale time (rarely changes)
- Collection stats: 60s stale time
- All mutations invalidate related queries (collection, cards, decks, stats)
9.2 Component Structure
App ├── MainLayout │ ├── Header # Branding, title │ ├── Navigation # Search | Collection | Decks tabs │ └── [Active View] │ ├── SearchView │ │ ├── FiltersPanel # Collapsible filter controls │ │ │ ├── TextSearchFilter # Name + oracle text │ │ │ ├── CMCFilter # Min/max mana value │ │ │ ├── ColorFilter # W/U/B/R/G/C + mode selector │ │ │ ├── SetFilter # Set multiselect │ │ │ ├── TypeFilter # Card type tags │ │ │ └── RarityFilter # Rarity checkboxes │ │ ├── ViewToggle # Grid ↔ Table switch │ │ ├── CardGrid / CardTable # Results display │ │ └── CardDetailModal # Full card details + actions │ ├── CollectionView │ │ ├── FiltersPanel │ │ ├── CollectionStats # KPI cards (total, value, etc.) │ │ ├── CollectionGrid/Table # Entries with QuantityControls │ │ └── Export controls │ └── DecksView │ ├── DeckList # All decks with summaries │ ├── DeckEditor # Card management within a deck │ ├── CreateDeckModal │ ├── ImportDeckModal # URL + text paste │ └── AddToDeckModal # Card → deck assignment
9.3 Custom Hooks
| Hook | Purpose | Key Behaviour |
|---|---|---|
useCards(filters) | Card search | Auto-refetches on filter change; 30s stale time |
useCollection(filters, finish) | Collection listing | Supports finish filter + deck membership filter |
useCollectionStats() | Collection aggregates | 60s stale time |
useDecks() | Deck listing | 30s stale time; all decks with cards |
useDeck(id) | Single deck | Conditional fetch; includes collection ownership status |
useDeckValidity(id) | Ownership check | Cross-printing aware; returns missing card list |
9.4 Styling
Tailwind CSS is extended with a custom MTG colour palette:
colors: { mtg: { white: "#f9faf4", // Plains blue: "#0e68ab", // Island black: "#150b00", // Swamp red: "#d3202a", // Mountain green: "#00733e", // Forest colorless: "#cbc2bf", // Wastes gold: "#c9a84c", // Multicolour } }
10. Performance Optimisations
| Optimisation | Layer | Impact |
|---|---|---|
| SQLite WAL mode | Database | Concurrent reads during writes; no lock contention during sync |
| FTS5 virtual table | Database | Sub-millisecond full-text card search vs sequential scan |
| Streaming JSON parser | Sync | ~30 MB peak memory during 500 MB bulk download |
| Batch upserts (1000/batch) | Sync | Reduces SQLite transaction overhead by 1000x |
| Selectinload | ORM | Eager loading prevents N+1 queries on card-collection joins |
| TanStack Query caching | Frontend | 30-300s stale times reduce API calls during navigation |
| Vite code splitting | Frontend | Route-based chunks; optimised dependency pre-bundling |
| Multi-stage Docker build | Infrastructure | Final image excludes Node.js and build tools (~200 MB smaller) |
11. Deployment
11.1 Docker Compose
services: mtg-tracker: build: . ports: - "8085:8080" # Host port varies by target volumes: - ./data:/app/data # Persistent SQLite database environment: - DATABASE_URL=sqlite+aiosqlite:///./data/mtg.db - SYNC_ON_STARTUP=true - SCRYFALL_BULK_TYPE=default_cards restart: unless-stopped
11.2 Deploy Command
# From source host ./deploy.sh mtg-helper-stu dev # → claude-dev:8085 ./deploy.sh mtg-helper-stu prod # → claude-prod:8080 ./deploy.sh mtg-helper-speed prod # → claude-prod:8090 (same source)
11.3 Data Persistence
The ./data volume mount persists the SQLite database across container rebuilds. This directory contains:
mtg.db— Main database (cards, sets, collection, decks, sync metadata, FTS index)mtg.db-wal,mtg.db-shm— WAL mode auxiliary files
The deploy script excludes data/ from rsync to protect the running database.
11.4 First Launch
- Container starts, creates
data/mtg.dbif it doesn’t exist - SQLAlchemy creates all tables and enables WAL mode
- If
SYNC_ON_STARTUP=trueand DB is empty, triggers full Scryfall sync - Sets sync: ~2 seconds (fetches all MTG sets)
- Card sync: ~20-30 minutes (downloads and processes ~500 MB bulk data)
- FTS index rebuild: ~30 seconds
- Application is usable immediately; sync runs in background
12. Configuration
| Variable | Default | Description |
|---|---|---|
DATABASE_URL | sqlite+aiosqlite:///./data/mtg.db | SQLAlchemy database connection string |
SYNC_ON_STARTUP | true | Auto-sync Scryfall data when DB is empty |
SCRYFALL_BULK_TYPE | default_cards | Bulk data type: default_cards (all printings) or oracle_cards (unique only) |
SCRYFALL_API_BASE | https://api.scryfall.com | Scryfall API base URL |
SCRYFALL_REQUEST_DELAY | 0.1 | Rate limit delay between Scryfall API requests (seconds) |
12.1 Backend Dependencies
| Package | Version | Purpose |
|---|---|---|
fastapi | 0.109.0 | Async web framework with automatic OpenAPI docs |
uvicorn[standard] | 0.27.0 | Production ASGI server |
sqlalchemy | 2.0.25 | Async ORM with relationship loading |
aiosqlite | 0.19.0 | Async SQLite database driver |
httpx | 0.26.0 | Async HTTP client for Scryfall API |
pydantic | 2.5.3 | Request/response validation |
pydantic-settings | 2.1.0 | Environment variable configuration |
apscheduler | 3.10.4 | Cron-style task scheduling |
ijson | 3.2.3 | Streaming JSON parser |
python-multipart | 0.0.6 | File upload support |
alembic | 1.13.1 | Database migration framework |
12.2 Frontend Dependencies
| Package | Version | Purpose |
|---|---|---|
react | 18.2.0 | UI component framework |
typescript | 5.3.3 | Static type checking |
vite | 5.0.12 | Build tool with HMR dev server |
tailwindcss | 3.4.1 | Utility-first CSS with custom MTG palette |
zustand | 4.4.7 | Lightweight client state management |
@tanstack/react-query | 5.17.0 | Server state caching & synchronisation |
@tanstack/react-table | 8.11.2 | Headless table with sorting/pagination |
axios | 1.6.5 | HTTP client |
lucide-react | 0.307.0 | Icon library |
12.3 Code Quality
| Tool | Scope | Config |
|---|---|---|
| Ruff | Python linting + formatting | Target Python 3.11, line-length 100, select E/F/I/W/UP/B/SIM |
| Pyright | Python type checking | Basic mode, Python 3.11 |
| MyPy | Python type checking | Basic config, ignore missing imports |
| ESLint | TypeScript linting | Flat config with React plugin |
| Prettier | Frontend formatting | Standard config |
| TypeScript | Frontend type checking | Strict mode, ES2020 target |