diff --git a/README.md b/README.md index 6e553f5..43a4d2a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# DOSTalgia đŸ•šī¸ +# DOSTALGIA đŸ•šī¸ A nostalgic DOS game hub. Upload your old DOS games, auto-scrape artwork and metadata from IGDB, and play them directly in your browser via js-dos (DOSBox compiled to WebAssembly). @@ -9,15 +9,6 @@ Every uploaded game is packaged into a `.jsdos` bundle — a standard ZIP with e **Save states are automatic** — js-dos persists your game progress to the browser's storage. Come back anytime and pick up where you left off. -### 📡 IGDB metadata & media -Dostalgia integrates with [IGDB](https://www.igdb.com/) (via Twitch OAuth2) to auto-populate game info: - -- **Auto Scrape** — On upload, Dostalgia searches IGDB by title and fills in year, genre, developer, publisher, description, and cover art. If IGDB finds a DOS result, it prioritises it. -- **Manual Search** — From the game detail page you can search IGDB by any query, browse results with cover thumbnails and DOS badges, and apply the one you want. -- **Media** — Videos (YouTube embeds) and screenshots (1080p) are fetched and displayed in a scrollable media gallery with a preview player. - -Everything degrades gracefully — if Twitch credentials aren't configured, uploads and editing work fine, just without auto-scrape. - ### đŸ“Ļ Handles any file structure DOS games come in all shapes. Dostalgia handles them transparently: @@ -27,6 +18,8 @@ DOS games come in all shapes. Dostalgia handles them transparently: - **CD-only games** — If the archive contains only CD images with no executable, a CD-only bundle is created that mounts the disc and drops you at the DOS prompt. - **Hardcoded paths** — `ConfigPatcher` scans game config files for hardcoded absolute paths (e.g. `C:\FALLOUT1\MASTER.DAT`) that broke after flattening, and rewrites them to relative paths. +Other solutions such as [RomM](https://www.romm.app/) generate raw ZIP bundles and require you to write your own `dosbox.conf` with manual `mount` and `imgmount` commands. Dostalgia automates all of this for you. + ### 🔍 Smart executable detection On upload, Dostalgia scans every `.exe`, `.com`, and `.bat` file and picks the best candidate as the main executable using a scoring system: @@ -38,6 +31,8 @@ On upload, Dostalgia scans every `.exe`, `.com`, and `.bat` file and picks the b All discovered executables are stored and available in the **Edit** page, where you can pick a different one via radio buttons. +Again, RomM requires you to manually specify the executable in a custom `dosbox.conf`, while Dostalgia detects and configures it for you. + > **âš ī¸ Cache note:** If you change the executable, the `.jsdos` bundle is patched in-place. Your browser may serve a cached version of the old bundle — if the game doesn't launch with the new executable, **hard-refresh** the play page (Ctrl+Shift+R / Cmd+Shift+R). ### 🛠 One-click Setup launcher @@ -47,6 +42,8 @@ Many DOS games include a `SETUP.EXE`, `INSTALL.EXE`, or `CONFIG.EXE` used to con - Clicking it launches the setup executable *without modifying the main game bundle* - The setup bundle is generated **on-the-fly** by the server — a modified `.jsdos` is streamed with the setup executable in the autoexec, then discarded. Nothing is written to disk. +This is not supported in RomM, where you would have to run the setup manually from the DOS prompt every time. + ### đŸĒŸ Windows game detection DOSBox can't run Windows executables. Dostalgia's `PlatformDetector` reads MZ/PE/NE headers to detect Windows executables: @@ -55,41 +52,33 @@ DOSBox can't run Windows executables. Dostalgia's `PlatformDetector` reads MZ/PE - If you click Play anyway, a warning overlay explains the limitation with a **Try anyway** fallback - Setup executables that are Windows-native are also filtered out from the Setup button -## How it works +### 📡 IGDB metadata & media +Dostalgia integrates with [IGDB](https://www.igdb.com/) (via Twitch OAuth2) to auto-populate game info: -``` - ┌──────────────────────────────────────────┐ - │ Upload ZIP (multipart) │ - └────────────â”Ŧ─────────────────────────────┘ - â–ŧ - ┌──────────────────────────┐ - │ Unzip & flatten dirs │ - │ Fix .cue references │ - │ Fix absolute paths │ - └────────────â”Ŧ─────────────┘ - â–ŧ - ┌──────────────────────────┐ - │ Detect executables │ - │ Detect CD images │ - │ Detect platform (DOS/W) │ - └────────────â”Ŧ─────────────┘ - â–ŧ - ┌──────────────────────────┐ - │ Build .jsdos bundle │ - │ (ZIP + dosbox.conf + │ - │ jsdos.json) │ - └────────────â”Ŧ─────────────┘ - â–ŧ - ┌──────────────────────────┐ - │ Auto-scrape IGDB │ - │ (year, genre, cover, │ - │ videos, screenshots) │ - └────────────â”Ŧ─────────────┘ - â–ŧ - ┌──────────────────────────┐ - │ Save game.json │ - └──────────────────────────┘ -``` +- **Auto Scrape** — On upload, Dostalgia searches IGDB by title and fills in year, genre, developer, publisher, description, and cover art. If IGDB finds a DOS result, it prioritises it. +- **Manual Search** — From the game detail page you can search IGDB by any query, browse results with cover thumbnails and DOS badges, and apply the one you want. +- **Media** — Videos (YouTube embeds) and screenshots (1080p) are fetched and displayed in a scrollable media gallery with a preview player. + +**Setup:** + +1. Go to https://dev.twitch.tv/console/apps → **Register Your Application** +2. Name: `Dostalgia` (or anything), OAuth Redirect URL: `http://localhost`, Category: **Other** +3. Copy the **Client ID** and generate a **Client Secret** +4. Provide them via environment variables: + +Method | How +--- | --- +Docker run | `-e TWITCH_CLIENT_ID=xxx -e TWITCH_CLIENT_SECRET=yyy` +Docker Compose | Copy `.env.example` to `.env` and fill in the values +Local dev | `export TWITCH_CLIENT_ID=xxx TWITCH_CLIENT_SECRET=yyy` + +IGDB features degrade gracefully — if credentials aren't set, uploads and metadata editing still work, just without auto-scrape. + +### 📷 Screenshots + +![library](images/library.jpeg "Library") +![game](images/game.jpeg "game") +![edit](images/edit.jpeg "edit") ## Quick Start @@ -143,103 +132,4 @@ java -jar target/quarkus-app/quarkus-run.jar - **Frontend**: Svelte 5 SPA with hash-based routing - **Emulation**: js-dos v8 loaded from CDN, runs DOSBox in WebAssembly - **Storage**: JSON-per-game under `/data/games/{id}/game.json` -- **Saves**: Browser localStorage / indexedDB (managed by js-dos) - -## API - -Method | Path | Description ---- | --- | --- -GET | `/api/games` | List all games -GET | `/api/games/:id` | Get game details -PATCH | `/api/games/:id` | Update game metadata -DELETE | `/api/games/:id` | Delete a game -POST | `/api/upload` | Upload a game ZIP (multipart) -POST | `/api/games/:id/cover` | Upload cover art -GET | `/api/games/:id/setup-bundle` | Stream a setup .jsdos bundle (on-the-fly) -GET | `/api/games/:id/download` | Download the game's .jsdos bundle -GET | `/api/igdb/status` | Check if IGDB is configured -GET | `/api/igdb/search?q=` | Search IGDB for a game -POST | `/api/igdb/scrape/:id` | Auto-scrape or apply specific IGDB result - -## Game JSON Schema - -```json -{ - "id": "doom", - "title": "Doom", - "year": 1993, - "genre": "FPS", - "developer": "id Software", - "publisher": "id Software", - "description": "You've signed up for...", - "bundle_file": "doom/doom.jsdos", - "executable": "DOOM.EXE", - "executables": ["DOOM.EXE", "SETUP.EXE"], - "setup_exe": "SETUP.EXE", - "has_setup": true, - "platform": "dos", - "bundle_size": 4194304, - "has_cover": true, - "screenshots": ["https://..."], - "videos": ["dQw4w9WgXcQ"], - "igdb_id": 3498, - "ready": true, - "created_at": "2026-05-27T10:00:00Z", - "updated_at": "2026-05-27T10:00:00Z" -} -``` - -## IGDB Metadata (Optional) - -Dostalgia can auto-populate game metadata (year, genre, developer, publisher, cover art, screenshots, videos) from the [IGDB](https://www.igdb.com/) database. This requires a Twitch API application. - -**Setup:** - -1. Go to https://dev.twitch.tv/console/apps → **Register Your Application** -2. Name: `Dostalgia` (or anything), OAuth Redirect URL: `http://localhost`, Category: **Other** -3. Copy the **Client ID** and generate a **Client Secret** -4. Provide them via environment variables: - -Method | How ---- | --- -Docker run | `-e TWITCH_CLIENT_ID=xxx -e TWITCH_CLIENT_SECRET=yyy` -Docker Compose | Copy `.env.example` to `.env` and fill in the values -Local dev | `export TWITCH_CLIENT_ID=xxx TWITCH_CLIENT_SECRET=yyy` - -IGDB features degrade gracefully — if credentials aren't set, uploads and metadata editing still work, just without auto-scrape. - -## Comparison with RomM - -[RomM](https://www.romm.app/) is a popular ROM manager with web-based emulation. Here's how Dostalgia compares specifically for MS-DOS games: - -| Feature | Dostalgia | RomM | -|---|---|---| -| **Emulator** | js-dos v8 (dedicated DOSBox WebAssembly) | EmulatorJS with dosbox-pure core | -| **Bundle format** | Auto-generated .jsdos with embedded config | Raw ZIP + manual .conf file | -| **Executable detection** | Automatic (smart scoring: depth, size, platform, installer filtering) | Manual — user must type `mount`, `cd`, and `exe` commands or write a .conf | -| **CD mounting** | Automatic — detects .iso/.cue/.img/.ccd/.bin, fixes broken cues, mounts in config | Manual — user must write `imgmount` commands in .conf | -| **Setup executable** | Auto-detected, one-click launch via on-the-fly streaming bundle | Not supported — user must run setup manually from DOS prompt | -| **Windows detection** | PE/NE header analysis, UI badge, disabled play button with fallback | Not supported | -| **IGDB integration** | Yes — auto-scrape, manual search, cover, genre, dev, publisher, videos, screenshots | Not for DOS specifically | -| **Config patching** | Automatic — `ConfigPatcher` rewrites hardcoded `C:\` paths in game configs | Not available | -| **GOG DOS games** | Supported (after flattening + path patching) | Explicitly NOT supported | -| **Database** | None — JSON files per game | PostgreSQL | -| **Upload size** | 2 GB limit | Depends on configuration | -| **Save states** | Yes (browser storage via js-dos) | Yes (via EmulatorJS) | -| **Target audience** | DOS-only, streamlined | Multi-platform, general-purpose | - -**When to use RomM:** If you're managing ROMs across many platforms (NES, SNES, Genesis, PS1, etc.) alongside DOS games. - -**When to use Dostalgia:** If you have a focused DOS collection and want a zero-fuss experience where you upload a ZIP and everything just works — executable detection, CD mounting, metadata, screenshots. - -## Publishing the Docker image - -The Docker image is fully self-contained (Java JAR + static frontend, no database). To publish: - -```bash -docker build -t dostalgia . -docker tag dostalgia ghcr.io/yourname/dostalgia:latest -docker push ghcr.io/yourname/dostalgia:latest -``` - -Secrets are never baked into the image — users provide `TWITCH_CLIENT_ID` / `TWITCH_CLIENT_SECRET` at runtime via environment variables (or skip IGDB entirely). +- **Saves**: Browser localStorage / indexedDB (managed by js-dos) \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d63c0e0..66c0173 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -561,24 +561,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", - "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/netbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", @@ -595,24 +577,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", - "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/openbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", @@ -629,24 +593,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", - "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/sunos-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", @@ -2944,420 +2890,6 @@ } } }, - "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", - "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/android-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", - "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/android-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", - "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/android-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", - "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", - "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/darwin-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", - "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", - "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", - "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", - "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", - "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", - "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-loong64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", - "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", - "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", - "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", - "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-s390x": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", - "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/linux-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", - "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", - "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", - "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/sunos-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", - "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/win32-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", - "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/win32-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", - "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/vitest/node_modules/@esbuild/win32-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", - "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/vitest/node_modules/@vitest/mocker": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", @@ -3385,50 +2917,6 @@ } } }, - "node_modules/vitest/node_modules/esbuild": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", - "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.0", - "@esbuild/android-arm": "0.28.0", - "@esbuild/android-arm64": "0.28.0", - "@esbuild/android-x64": "0.28.0", - "@esbuild/darwin-arm64": "0.28.0", - "@esbuild/darwin-x64": "0.28.0", - "@esbuild/freebsd-arm64": "0.28.0", - "@esbuild/freebsd-x64": "0.28.0", - "@esbuild/linux-arm": "0.28.0", - "@esbuild/linux-arm64": "0.28.0", - "@esbuild/linux-ia32": "0.28.0", - "@esbuild/linux-loong64": "0.28.0", - "@esbuild/linux-mips64el": "0.28.0", - "@esbuild/linux-ppc64": "0.28.0", - "@esbuild/linux-riscv64": "0.28.0", - "@esbuild/linux-s390x": "0.28.0", - "@esbuild/linux-x64": "0.28.0", - "@esbuild/netbsd-arm64": "0.28.0", - "@esbuild/netbsd-x64": "0.28.0", - "@esbuild/openbsd-arm64": "0.28.0", - "@esbuild/openbsd-x64": "0.28.0", - "@esbuild/openharmony-arm64": "0.28.0", - "@esbuild/sunos-x64": "0.28.0", - "@esbuild/win32-arm64": "0.28.0", - "@esbuild/win32-ia32": "0.28.0", - "@esbuild/win32-x64": "0.28.0" - } - }, "node_modules/vitest/node_modules/vite": { "version": "8.0.16", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", diff --git a/images/edit.jpeg b/images/edit.jpeg new file mode 100644 index 0000000..a6e41dc Binary files /dev/null and b/images/edit.jpeg differ diff --git a/images/game.jpeg b/images/game.jpeg new file mode 100644 index 0000000..a7551de Binary files /dev/null and b/images/game.jpeg differ diff --git a/images/library.jpeg b/images/library.jpeg new file mode 100644 index 0000000..f101c2d Binary files /dev/null and b/images/library.jpeg differ