From 9aac54a78fb2f25b478a47ee85142c76759ad590 Mon Sep 17 00:00:00 2001 From: nazozokc Date: Wed, 17 Jun 2026 21:00:22 +0900 Subject: [PATCH] docs: convert docs to markdown, add TUI guide, fix sidebar layout on desktop --- docs/package.json | 3 + docs/src/app.css | 21 +- docs/src/lib/layouts/MarkdownLayout.svelte | 25 +++ docs/src/routes/+layout.svelte | 1 + docs/src/routes/commands/+page.md | 142 +++++++++++++ docs/src/routes/commands/+page.svelte | 223 --------------------- docs/src/routes/configuration/+page.md | 49 +++++ docs/src/routes/configuration/+page.svelte | 65 ------ docs/src/routes/data/+page.md | 74 +++++++ docs/src/routes/data/+page.svelte | 87 -------- docs/src/routes/development/+page.md | 78 +++++++ docs/src/routes/development/+page.svelte | 103 ---------- docs/src/routes/faq/+page.md | 66 ++++++ docs/src/routes/faq/+page.svelte | 102 ---------- docs/src/routes/guides/+page.md | 94 +++++++++ docs/src/routes/guides/+page.svelte | 113 ----------- docs/src/routes/installation/+page.md | 62 ++++++ docs/src/routes/installation/+page.svelte | 55 ----- docs/src/routes/tui/+page.md | 171 ++++++++++++++++ docs/svelte.config.js | 10 + pnpm-lock.yaml | 79 ++++++++ 21 files changed, 873 insertions(+), 750 deletions(-) create mode 100644 docs/src/lib/layouts/MarkdownLayout.svelte create mode 100644 docs/src/routes/commands/+page.md delete mode 100644 docs/src/routes/commands/+page.svelte create mode 100644 docs/src/routes/configuration/+page.md delete mode 100644 docs/src/routes/configuration/+page.svelte create mode 100644 docs/src/routes/data/+page.md delete mode 100644 docs/src/routes/data/+page.svelte create mode 100644 docs/src/routes/development/+page.md delete mode 100644 docs/src/routes/development/+page.svelte create mode 100644 docs/src/routes/faq/+page.md delete mode 100644 docs/src/routes/faq/+page.svelte create mode 100644 docs/src/routes/guides/+page.md delete mode 100644 docs/src/routes/guides/+page.svelte create mode 100644 docs/src/routes/installation/+page.md delete mode 100644 docs/src/routes/installation/+page.svelte create mode 100644 docs/src/routes/tui/+page.md diff --git a/docs/package.json b/docs/package.json index cdb9db0..d590fea 100644 --- a/docs/package.json +++ b/docs/package.json @@ -12,5 +12,8 @@ "@sveltejs/kit": "^2.20.8", "svelte": "^5.28.2", "vite": "^6.3.5" + }, + "devDependencies": { + "mdsvex": "^0.12.7" } } diff --git a/docs/src/app.css b/docs/src/app.css index 56d1b3c..03baaf7 100644 --- a/docs/src/app.css +++ b/docs/src/app.css @@ -234,6 +234,9 @@ aside nav { display: flex; flex-direction: column; height: auto; + max-width: none; + margin: 0; + padding: 0; } .sidebar-section { @@ -297,17 +300,31 @@ main { @media (min-width: 769px) { aside { transform: translateX(0); - position: sticky; + position: fixed; + left: 0; top: var(--header-height); height: calc(100vh - var(--header-height)); + z-index: 50; } .sidebar-overlay { display: none !important; } + .layout { + max-width: none; + margin: 0; + padding-left: var(--sidebar-width); + } + main { - margin-left: var(--sidebar-width); + margin-left: 0; + } + + nav { + max-width: none; + margin: 0; + padding-left: var(--sidebar-width); } } diff --git a/docs/src/lib/layouts/MarkdownLayout.svelte b/docs/src/lib/layouts/MarkdownLayout.svelte new file mode 100644 index 0000000..1eb3217 --- /dev/null +++ b/docs/src/lib/layouts/MarkdownLayout.svelte @@ -0,0 +1,25 @@ + + + + {title === "subtrack" ? "subtrack" : `${title} — subtrack`} + {#if description} + + {/if} + + +
+ {#if title !== "subtrack"} +

{title}

+ {/if} + {@render children()} +
diff --git a/docs/src/routes/+layout.svelte b/docs/src/routes/+layout.svelte index d03764e..5d4524c 100644 --- a/docs/src/routes/+layout.svelte +++ b/docs/src/routes/+layout.svelte @@ -34,6 +34,7 @@ title: "Guides", items: [ { href: "/guides", label: "Usage Guides" }, + { href: "/tui", label: "Interactive TUI" }, { href: "/data", label: "Data & Storage" }, { href: "/configuration", label: "Configuration" }, ], diff --git a/docs/src/routes/commands/+page.md b/docs/src/routes/commands/+page.md new file mode 100644 index 0000000..9b16ad8 --- /dev/null +++ b/docs/src/routes/commands/+page.md @@ -0,0 +1,142 @@ +--- +title: Commands +description: Full reference for all subtrack CLI commands. +--- + +subtrack provides six commands. Most support both interactive and non-interactive modes. + +## `list` + +Lists all subscriptions in a formatted table. Subscriptions are grouped by currency by default, with a subtotal row per group. + +| Option | Description | +|--------|-------------| +| `-c, --currency ` | Convert all prices to the given currency using live exchange rates | + +### Examples + +```bash +# List all subscriptions (grouped by currency) +subtrack list + +# Convert all prices to JPY +subtrack list --currency JPY +``` + +When `--currency` is used, all prices are converted to the target currency (fetched from [open.er-api.com](https://open.er-api.com)) and displayed as a single group with a grand total. + +## `add` + +Adds a new subscription. Without flags, prompts for all fields interactively. Providing all flags skips prompts entirely (useful for scripts). + +| Option | Description | +|--------|-------------| +| `--name ` | Subscription name (max 100 characters) | +| `--price ` | Payment amount — integer, non-negative, max 99,999,999 | +| `--currency ` | Currency code. Supported: JPY, USD, EUR, GBP, AUD, CAD, KRW, CNY, SGD, HKD | +| `--cycle ` | Billing cycle. One of: weekly, bi-weekly, monthly, quarterly, semi-annual, yearly | +| `--tags ` | Comma-separated tags (max 10 tags, each max 50 characters) | + +### Examples + +```bash +# Interactive mode +subtrack add + +# Fully non-interactive (skips confirmation) +subtrack add \ + --name Spotify \ + --price 980 \ + --currency JPY \ + --cycle monthly \ + --tags music + +# Partial flags — missing fields are prompted +subtrack add --name Netflix + +# Tags with existing tag autocomplete +subtrack add --name "AWS" --price 50 --currency USD --cycle monthly +``` + +## `delete` + +Shows an interactive checkbox list of all subscriptions. Select one or more to delete. Confirmation is required before deletion. + +
+ ⚠ Note: The delete command is always interactive. There is no non-interactive mode. +
+ +### Example + +```bash +subtrack delete +``` + +## `payment [period]` + +Calculates and displays how much you pay over a given billing period. All subscriptions are automatically converted to the target period based on their billing cycle. + +The `period` argument defaults to `monthly`. Valid values: + +| Period | Alias | +|--------|-------| +| `weekly` | per week | +| `bi-weekly` | per two weeks | +| `monthly` | per month (default) | +| `quarterly` | per 3 months | +| `semi-annual` | per 6 months | +| `yearly` | per year | + +| Option | Description | +|--------|-------------| +| `-c, --currency ` | Convert all prices to the given currency using live exchange rates | + +### Examples + +```bash +# Monthly total (default) +subtrack payment + +# Yearly total +subtrack payment yearly + +# Weekly total in JPY +subtrack payment weekly --currency JPY +``` + +When `--currency` is used, the total is displayed as a single amount in the target currency. Without it, totals are grouped by currency. + +If exchange rates cannot be fetched (e.g. offline), the command falls back to per-currency display without conversion. + +## `tags ` + +Filters and displays subscriptions that have **all** specified tags (AND logic). + +### Examples + +```bash +# Subscriptions tagged with "music" +subtrack tags music + +# Subscriptions tagged with both "music" AND "video" +subtrack tags music video + +# Subscriptions tagged with "entertainment", "video", and "kids" +subtrack tags entertainment video kids +``` + +## `backup ` + +Creates a timestamped backup of the SQLite database in the specified directory. The backup filename follows the format `subtrack_YYYYMMDD_HHmmss.db`. + +If the destination does not exist or is not a directory, the command exits with an error. The backup will not overwrite existing files (exclusive create). + +### Examples + +```bash +# Backup to current directory +subtrack backup . + +# Backup to ~/backups +subtrack backup ~/backups +``` diff --git a/docs/src/routes/commands/+page.svelte b/docs/src/routes/commands/+page.svelte deleted file mode 100644 index 8e40107..0000000 --- a/docs/src/routes/commands/+page.svelte +++ /dev/null @@ -1,223 +0,0 @@ - - - - Commands — subtrack - - - -
-

Commands

-

- subtrack provides six commands. Most support both interactive and non-interactive modes. -

- - - -

list

-

- Lists all subscriptions in a formatted table. Subscriptions are grouped by - currency by default, with a subtotal row per group. -

- - - - - - - - - - - - - - -
OptionDescription
-c, --currency <C>Convert all prices to the given currency using live exchange rates
- -

Examples

-
# List all subscriptions (grouped by currency)
-subtrack list
-
-# Convert all prices to JPY
-subtrack list --currency JPY
- -

- When --currency is used, all prices are converted to the target - currency (fetched from open.er-api.com) - and displayed as a single group with a grand total. -

- - - -

add

-

- Adds a new subscription. Without flags, prompts for all fields interactively. - Providing all flags skips prompts entirely (useful for scripts). -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
--name <name>Subscription name (max 100 characters)
--price <price>Payment amount — integer, non-negative, max 99,999,999
--currency <C>Currency code. Supported: JPY, USD, EUR, GBP, AUD, CAD, KRW, CNY, SGD, HKD
--cycle <cycle>Billing cycle. One of: weekly, bi-weekly, monthly, quarterly, semi-annual, yearly
--tags <tags>Comma-separated tags (max 10 tags, each max 50 characters)
- -

Examples

-
# Interactive mode
-subtrack add
-
-# Fully non-interactive (skips confirmation)
-subtrack add \
-  --name Spotify \
-  --price 980 \
-  --currency JPY \
-  --cycle monthly \
-  --tags music
-
-# Partial flags — missing fields are prompted
-subtrack add --name Netflix
-
-# Tags with existing tag autocomplete
-subtrack add --name "AWS" --price 50 --currency USD --cycle monthly
- - - -

delete

-

- Shows an interactive checkbox list of all subscriptions. Select one or more - to delete. Confirmation is required before deletion. -

- -
- ⚠ Note: The delete command is always interactive. - There is no non-interactive mode. -
- -

Example

-
subtrack delete
- - - -

payment [period]

-

- Calculates and displays how much you pay over a given billing period. All - subscriptions are automatically converted to the target period based on their - billing cycle. -

- -

The period argument defaults to monthly. Valid values:

- - - - - - - - - - - - - - - -
PeriodAlias
weeklyper week
bi-weeklyper two weeks
monthlyper month (default)
quarterlyper 3 months
semi-annualper 6 months
yearlyper year
- - - - - - - - - - - - - - -
OptionDescription
-c, --currency <C>Convert all prices to the given currency using live exchange rates
- -

Examples

-
# Monthly total (default)
-subtrack payment
-
-# Yearly total
-subtrack payment yearly
-
-# Weekly total in JPY
-subtrack payment weekly --currency JPY
- -

- When --currency is used, the total is displayed as a single amount - in the target currency. Without it, totals are grouped by currency. -

-

- If exchange rates cannot be fetched (e.g. offline), the command falls back to - per-currency display without conversion. -

- - - -

tags <taglist...>

-

- Filters and displays subscriptions that have all specified tags - (AND logic). -

- -

Examples

-
# Subscriptions tagged with "music"
-subtrack tags music
-
-# Subscriptions tagged with both "music" AND "video"
-subtrack tags music video
-
-# Subscriptions tagged with "entertainment", "video", and "kids"
-subtrack tags entertainment video kids
- - - -

backup <destination>

-

- Creates a timestamped backup of the SQLite database in the specified directory. - The backup filename follows the format subtrack_YYYYMMDD_HHmmss.db. -

- -

- If the destination does not exist or is not a directory, the command exits with - an error. The backup will not overwrite existing files (exclusive create). -

- -

Examples

-
# Backup to current directory
-subtrack backup .
-
-# Backup to ~/backups
-subtrack backup ~/backups
-
diff --git a/docs/src/routes/configuration/+page.md b/docs/src/routes/configuration/+page.md new file mode 100644 index 0000000..c2a52b3 --- /dev/null +++ b/docs/src/routes/configuration/+page.md @@ -0,0 +1,49 @@ +--- +title: Configuration +description: Environment variables and configuration options for subtrack. +--- + +subtrack follows a **zero-configuration** philosophy. It works out of the box with sensible defaults. The only configurable option is the database directory. + +## Environment variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `SUBSC_CLI_DB_DIR` | Override the database directory | `~/.config/subtrack` | + +### Example usage + +```bash +# Use a custom directory for the database +export SUBSC_CLI_DB_DIR=~/project/subtrack-data +subtrack list + +# Or set it per-command +SUBSC_CLI_DB_DIR=/tmp/test-db subtrack add \ + --name Spotify \ + --price 980 \ + --currency JPY \ + --cycle monthly +``` + +
+ 💡 Tip: Changing SUBSC_CLI_DB_DIR lets you maintain separate databases — useful for testing or multi-profile setups. +
+ +## No config file + +subtrack does not use configuration files (`.subtrackrc`, `subtrack.json`, etc.). All settings are controlled via environment variables or CLI flags. This keeps the tool simple and predictable. + +## Currency & cycle choices + +### Supported currencies (10) + +``` +JPY USD EUR GBP AUD CAD KRW CNY SGD HKD +``` + +### Supported billing cycles (6) + +``` +weekly bi-weekly monthly quarterly semi-annual yearly +``` diff --git a/docs/src/routes/configuration/+page.svelte b/docs/src/routes/configuration/+page.svelte deleted file mode 100644 index ae2696d..0000000 --- a/docs/src/routes/configuration/+page.svelte +++ /dev/null @@ -1,65 +0,0 @@ - - Configuration — subtrack - - - -
-

Configuration

-

- subtrack follows a zero-configuration philosophy. It works - out of the box with sensible defaults. The only configurable option is - the database directory. -

- -

Environment variables

- - - - - - - - - - - - - - - - -
VariableDescriptionDefault
SUBSC_CLI_DB_DIROverride the database directory~/.config/subtrack
- -

Example usage

-
# Use a custom directory for the database
-export SUBSC_CLI_DB_DIR=~/project/subtrack-data
-subtrack list
-
-# Or set it per-command
-SUBSC_CLI_DB_DIR=/tmp/test-db subtrack add \
-  --name Spotify \
-  --price 980 \
-  --currency JPY \
-  --cycle monthly
- -
- 💡 Tip: Changing SUBSC_CLI_DB_DIR lets you - maintain separate databases — useful for testing or multi-profile setups. -
- -

No config file

-

- subtrack does not use configuration files (.subtrackrc, - subtrack.json, etc.). All settings are controlled via - environment variables or CLI flags. This keeps the tool simple and - predictable. -

- -

Currency & cycle choices

- -

Supported currencies (10)

-
JPY  USD  EUR  GBP  AUD  CAD  KRW  CNY  SGD  HKD
- -

Supported billing cycles (6)

-
weekly  bi-weekly  monthly  quarterly  semi-annual  yearly
-
diff --git a/docs/src/routes/data/+page.md b/docs/src/routes/data/+page.md new file mode 100644 index 0000000..aed5881 --- /dev/null +++ b/docs/src/routes/data/+page.md @@ -0,0 +1,74 @@ +--- +title: Data & Storage +description: How subtrack stores data, database location, backup and restore. +--- + +## Database location + +All subscription data is stored in a single SQLite database file at: + +``` +~/.config/subtrack/subtrack.db +``` + +The database is created automatically on first use. No database server or configuration is required. + +## Database structure + +Three tables with a many-to-many relationship: + +``` +subscriptions +├── id INTEGER PRIMARY KEY AUTOINCREMENT +├── name TEXT NOT NULL +├── price INTEGER NOT NULL +├── currency TEXT NOT NULL +└── cycle TEXT NOT NULL + +tags +├── id INTEGER PRIMARY KEY AUTOINCREMENT +└── name TEXT NOT NULL UNIQUE + +subscription_tags +├── subscription_id INTEGER NOT NULL (FK → subscriptions.id) +└── tag_id INTEGER NOT NULL (FK → tags.id) +│ PRIMARY KEY (subscription_id, tag_id) +``` + +Deleting a subscription automatically removes its tag associations via `ON DELETE CASCADE`. Orphaned tags (with no subscriptions) are not automatically cleaned up. + +## Prices are stored as integers + +Prices are stored as whole numbers (integers) in the database. This avoids floating-point precision issues. For display, prices are formatted with the appropriate currency symbol and decimal places using `Intl.NumberFormat`. + +## Backup + +Use the `backup` command to create a timestamped copy of your database: + +```bash +subtrack backup ~/backups +# Creates: ~/backups/subtrack_20260617_143000.db +``` + +Backups use exclusive file creation, so they will never overwrite an existing file. See the [Commands](/commands) reference for full details. + +## Restore from backup + +To restore from a backup, simply copy the backup file to the database location: + +```bash +# Stop subtrack (close all running instances) +cp ~/backups/subtrack_20260617_143000.db ~/.config/subtrack/subtrack.db +``` + +Make sure subtrack is not running when you restore, as changes are written to the database file on every command. + +## Custom database directory + +Override the default `~/.config/subtrack` directory with the `SUBSC_CLI_DB_DIR` environment variable: + +```bash +SUBSC_CLI_DB_DIR=/path/to/custom/dir subtrack list +``` + +See [Configuration](/configuration) for more details. diff --git a/docs/src/routes/data/+page.svelte b/docs/src/routes/data/+page.svelte deleted file mode 100644 index 9be5faa..0000000 --- a/docs/src/routes/data/+page.svelte +++ /dev/null @@ -1,87 +0,0 @@ - - - - Data & Storage — subtrack - - - -
-

Data & Storage

- -

Database location

-

- All subscription data is stored in a single SQLite database file at: -

-
~/.config/subtrack/subtrack.db
-

- The database is created automatically on first use. No database server or - configuration is required. -

- -

Database structure

-

Three tables with a many-to-many relationship:

- -
subscriptions
-├── id        INTEGER PRIMARY KEY AUTOINCREMENT
-├── name      TEXT NOT NULL
-├── price     INTEGER NOT NULL
-├── currency  TEXT NOT NULL
-└── cycle     TEXT NOT NULL
-
-tags
-├── id   INTEGER PRIMARY KEY AUTOINCREMENT
-└── name TEXT NOT NULL UNIQUE
-
-subscription_tags
-├── subscription_id  INTEGER NOT NULL (FK → subscriptions.id)
-└── tag_id           INTEGER NOT NULL (FK → tags.id)
-│                    PRIMARY KEY (subscription_id, tag_id)
- -

- Deleting a subscription automatically removes its tag associations via - ON DELETE CASCADE. Orphaned tags (with no subscriptions) are - not automatically cleaned up. -

- -

Prices are stored as integers

-

- Prices are stored as whole numbers (integers) in the database. This avoids - floating-point precision issues. For display, prices are formatted with the - appropriate currency symbol and decimal places using - Intl.NumberFormat. -

- -

Backup

-

- Use the backup command to create a timestamped copy of your - database: -

-
subtrack backup ~/backups
-# Creates: ~/backups/subtrack_20260617_143000.db
-

- Backups use exclusive file creation, so they will never overwrite an existing - file. See the Commands reference for full - details. -

- -

Restore from backup

-

To restore from a backup, simply copy the backup file to the database location:

-
# Stop subtrack (close all running instances)
-cp ~/backups/subtrack_20260617_143000.db ~/.config/subtrack/subtrack.db
-

- Make sure subtrack is not running when you restore, as changes are written to - the database file on every command. -

- -

Custom database directory

-

- Override the default ~/.config/subtrack directory with the - SUBSC_CLI_DB_DIR environment variable: -

-
SUBSC_CLI_DB_DIR=/path/to/custom/dir subtrack list
-

- See Configuration for more details. -

-
diff --git a/docs/src/routes/development/+page.md b/docs/src/routes/development/+page.md new file mode 100644 index 0000000..eb588a6 --- /dev/null +++ b/docs/src/routes/development/+page.md @@ -0,0 +1,78 @@ +--- +title: Development +description: How to set up, build, test, and contribute to subtrack. +--- + +## Repository setup + +```bash +git clone https://github.com/nazozokc/subtrack.git +cd subtrack +pnpm install +``` + +## Nix devShell + +If you use Nix, a devShell is available with all required tools: + +```bash +nix develop +``` + +This provides `node`, `pnpm`, `typescript`, `typos`, and `nixfmt`. + +## Available commands + +| Command | Description | +|---------|-------------| +| `pnpm start` | Run subtrack directly from TypeScript source via `tsx` | +| `pnpm build` | Build the CLI bundle to `dist/index.mjs` via `tsdown` | +| `pnpm test` | Run tests with `vitest` | +| `pnpm test:watch` | Run tests in watch mode | +| `pnpm lint:typos` | Check for spelling errors with `typos` | + +## Tech stack + +| Category | Choice | +|----------|--------| +| Runtime | Node.js | +| Language | TypeScript (strict mode, ESM) | +| CLI framework | `commander` | +| Interactive prompts | `@inquirer/prompts` | +| Terminal output | `consola`, `picocolors`, `cli-table3` | +| Database | `sql.js` (SQLite via WASM) | +| Exchange rates | [open.er-api.com](https://open.er-api.com) | +| Build tool | `tsdown` | +| Test framework | `vitest` | +| Package manager | `pnpm` | +| Documentation | SvelteKit (this site) | + +## Contributing + +See [CONTRIBUTING.md](https://github.com/nazozokc/subtrack/blob/main/CONTRIBUTING.md) for branch policy, AI agent guidelines, and PR requirements. + +### Before opening a PR + +```bash +pnpm build +pnpm test +``` + +Make sure both pass and CI is green. + +## Project structure + +``` +subtrack/ +├── subtrack/ # CLI tool (TypeScript/ESM) +│ ├── src/ +│ │ ├── index.ts # Entry point, commander setup +│ │ ├── commands.ts # Command handlers +│ │ ├── db.ts # SQLite database layer +│ │ ├── display.ts # Table rendering, currency conversion +│ │ └── prompts.ts # Input validation, prompt helpers +│ └── dist/ # Built output +├── docs/ # Documentation site (SvelteKit) +├── flake.nix # Nix devShell +└── pnpm-workspace.yaml +``` diff --git a/docs/src/routes/development/+page.svelte b/docs/src/routes/development/+page.svelte deleted file mode 100644 index 3919bfa..0000000 --- a/docs/src/routes/development/+page.svelte +++ /dev/null @@ -1,103 +0,0 @@ - - Development — subtrack - - - -
-

Development

- -

Repository setup

-
git clone https://github.com/nazozokc/subtrack.git
-cd subtrack
-pnpm install
- -

Nix devShell

-

- If you use Nix, a devShell is available with all required tools: -

-
nix develop
-

- This provides node, pnpm, typescript, - typos, and nixfmt. -

- -

Available commands

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CommandDescription
pnpm startRun subtrack directly from TypeScript source via tsx
pnpm buildBuild the CLI bundle to dist/index.mjs via tsdown
pnpm testRun tests with vitest
pnpm test:watchRun tests in watch mode
pnpm lint:typosCheck for spelling errors with typos
- -

Tech stack

- - - - - - - - - - - - - - - - - - - - -
CategoryChoice
RuntimeNode.js
LanguageTypeScript (strict mode, ESM)
CLI frameworkcommander
Interactive prompts@inquirer/prompts
Terminal outputconsola, picocolors, cli-table3
Databasesql.js (SQLite via WASM)
Exchange ratesopen.er-api.com
Build tooltsdown
Test frameworkvitest
Package managerpnpm
DocumentationSvelteKit (this site)
- -

Contributing

-

- See CONTRIBUTING.md - for branch policy, AI agent guidelines, and PR requirements. -

- -

Before opening a PR

-
pnpm build
-pnpm test
-

Make sure both pass and CI is green.

- -

Project structure

-
subtrack/
-├── subtrack/         # CLI tool (TypeScript/ESM)
-│   ├── src/
-│   │   ├── index.ts      # Entry point, commander setup
-│   │   ├── commands.ts   # Command handlers
-│   │   ├── db.ts         # SQLite database layer
-│   │   ├── display.ts    # Table rendering, currency conversion
-│   │   └── prompts.ts    # Input validation, prompt helpers
-│   └── dist/             # Built output
-├── docs/             # Documentation site (SvelteKit)
-├── flake.nix         # Nix devShell
-└── pnpm-workspace.yaml
-
diff --git a/docs/src/routes/faq/+page.md b/docs/src/routes/faq/+page.md new file mode 100644 index 0000000..c23cb19 --- /dev/null +++ b/docs/src/routes/faq/+page.md @@ -0,0 +1,66 @@ +--- +title: FAQ +description: Frequently asked questions and troubleshooting for subtrack. +--- + +## I lost my database. Can I recover it? + +If you have a backup (created with `subtrack backup`), copy it back to `~/.config/subtrack/subtrack.db`. Without a backup, the data cannot be recovered — the database is stored locally only. + +**Recommendation:** Set up regular automated backups via cron or Task Scheduler. See the [Data & Storage](/data) page for details. + +## Can I add support for more currencies? + +The supported currencies are defined in `src/prompts.ts` as `CURRENCY_CHOICES`. To add a new currency: + +1. Add it to the `CURRENCY_CHOICES` array +2. Add it to the `Currency` type in `src/db.ts` +3. Verify that [open.er-api.com](https://open.er-api.com) supports the currency + +Pull requests for additional currencies are welcome! + +## Does subtrack work offline? + +Yes. Listing, adding, deleting, and filtering all work fully offline. The only feature that requires internet is `--currency` conversion, which fetches live exchange rates from [open.er-api.com](https://open.er-api.com). + +When offline, `--currency` falls back to per-currency display without conversion. + +## How do I restore from a backup? + +Copy the backup file to the database location: + +```bash +cp ~/backups/subtrack_20260617_143000.db ~/.config/subtrack/subtrack.db +``` + +Ensure no subtrack processes are running during the restore. The database is flushed to disk after each command. + +## Is my data sent anywhere? + +**No.** All data is stored locally in a SQLite file on your machine. There are no accounts, no telemetry, and no cloud sync. The only external request subtrack makes is to [open.er-api.com](https://open.er-api.com) for currency exchange rates — and only when you use the `--currency` flag. + +## Can I use subtrack in Docker or CI? + +Yes. subtrack is a standard Node.js CLI tool and works in any environment with Node.js 18+. For Docker: + +```dockerfile +FROM node:22-alpine +RUN npm install -g subtrack +CMD ["subtrack", "list"] +``` + +Use `SUBSC_CLI_DB_DIR` to control where the database is stored in containerized environments. + +## How do I update subtrack? + +```bash +npm update -g subtrack +``` + +Or, if installed via pnpm: + +```bash +pnpm update -g subtrack +``` + +Your database will be preserved — updates only affect the CLI code, not your data. diff --git a/docs/src/routes/faq/+page.svelte b/docs/src/routes/faq/+page.svelte deleted file mode 100644 index e79ebd6..0000000 --- a/docs/src/routes/faq/+page.svelte +++ /dev/null @@ -1,102 +0,0 @@ - - FAQ — subtrack - - - -
-

FAQ & Troubleshooting

- - - -

I lost my database. Can I recover it?

-

- If you have a backup (created with subtrack backup), copy it - back to ~/.config/subtrack/subtrack.db. Without a backup, the - data cannot be recovered — the database is stored locally only. -

-

- Recommendation: Set up regular automated backups via cron - or Task Scheduler. See the Data & Storage page - for details. -

- - - -

Can I add support for more currencies?

-

- The supported currencies are defined in src/prompts.ts as - CURRENCY_CHOICES. To add a new currency: -

-
    -
  1. Add it to the CURRENCY_CHOICES array
  2. -
  3. Add it to the Currency type in src/db.ts
  4. -
  5. Verify that open.er-api.com supports the currency
  6. -
-

- Pull requests for additional currencies are welcome! -

- - - -

Does subtrack work offline?

-

- Yes. Listing, adding, deleting, and filtering all work fully offline. The - only feature that requires internet is --currency conversion, - which fetches live exchange rates from open.er-api.com. -

-

- When offline, --currency falls back to per-currency display - without conversion. -

- - - -

How do I restore from a backup?

-

- Copy the backup file to the database location: -

-
cp ~/backups/subtrack_20260617_143000.db ~/.config/subtrack/subtrack.db
-

- Ensure no subtrack processes are running during the restore. The database - is flushed to disk after each command. -

- - - -

Is my data sent anywhere?

-

- No. All data is stored locally in a SQLite file on your - machine. There are no accounts, no telemetry, and no cloud sync. The only - external request subtrack makes is to - open.er-api.com for currency exchange rates — and only when you use - the --currency flag. -

- - - -

Can I use subtrack in Docker or CI?

-

- Yes. subtrack is a standard Node.js CLI tool and works in any environment - with Node.js 18+. For Docker: -

-
FROM node:22-alpine
-RUN npm install -g subtrack
-CMD ["subtrack", "list"]
-

- Use SUBSC_CLI_DB_DIR to control where the database is stored - in containerized environments. -

- - - -

How do I update subtrack?

-
npm update -g subtrack
-

- Or, if installed via pnpm: -

-
pnpm update -g subtrack
-

- Your database will be preserved — updates only affect the CLI code, not - your data. -

-
diff --git a/docs/src/routes/guides/+page.md b/docs/src/routes/guides/+page.md new file mode 100644 index 0000000..8a22fb2 --- /dev/null +++ b/docs/src/routes/guides/+page.md @@ -0,0 +1,94 @@ +--- +title: Usage Guides +description: Practical usage examples and workflows for subtrack. +--- + +Practical workflows for managing your subscriptions with subtrack. + +## First-time setup & adding subscriptions + +After installation, start by adding your subscriptions. The interactive mode guides you through each field: + +```bash +subtrack add +``` + +You'll be prompted for: name, price, currency, billing cycle, and tags. Once added, verify with: + +```bash +subtrack list +``` + +### Example: Adding a Netflix subscription + +```bash +subtrack add \ + --name "Netflix Premium" \ + --price 1980 \ + --currency JPY \ + --cycle monthly \ + --tags "video,entertainment" +``` + +## Tag-based organization + +Tags are a powerful way to categorize subscriptions. Examples: + +- **By category:** `music`, `video`, `cloud`, `productivity` +- **By priority:** `essential`, `nice-to-have` +- **By payment method:** `credit-card`, `paypal` +- **By usage:** `personal`, `work`, `family` + +Filter by tags: + +```bash +# Find all music-related subscriptions +subtrack tags music + +# Find subscriptions used for both work and personal +subtrack tags work personal +``` + +Tags use AND logic — only subscriptions matching **all** specified tags are shown. + +## Understanding your spending + +Use `subtrack payment` to see your total spending across different periods: + +```bash +# How much per month? +subtrack payment + +# How much per year? +subtrack payment yearly + +# Weekly cost in USD +subtrack payment weekly --currency USD +``` + +`payment` automatically converts all billing cycles to the target period. A yearly subscription will be divided into monthly cost, and a weekly subscription will be multiplied accordingly. + +## Managing multi-currency subscriptions + +If you have subscriptions in different currencies (e.g., JPY for local services and USD for international ones), subtrack handles this natively: + +```bash +# See everything in your local currency +subtrack list --currency JPY + +# Compare spending across currencies +subtrack list +``` + +Without `--currency`, subscriptions are grouped by their original currency with per-group subtotals. + +## Regular backups + +Set up a cron job (or Task Scheduler on Windows) for automatic backups: + +```bash +# Example cron: daily backup at 3 AM +0 3 * * * subtrack backup ~/subtrack-backups +``` + +Backups are timestamped and will never overwrite previous files. See [Data & Storage](/data) for restore instructions. diff --git a/docs/src/routes/guides/+page.svelte b/docs/src/routes/guides/+page.svelte deleted file mode 100644 index b8d667c..0000000 --- a/docs/src/routes/guides/+page.svelte +++ /dev/null @@ -1,113 +0,0 @@ - - - - Usage Guides — subtrack - - - -
-

Usage Guides

-

- Practical workflows for managing your subscriptions with subtrack. -

- - - -

First-time setup & adding subscriptions

-

- After installation, start by adding your subscriptions. The interactive mode - guides you through each field: -

-
subtrack add
-

- You'll be prompted for: name, price, currency, billing cycle, and tags. - Once added, verify with: -

-
subtrack list
- -

Example: Adding a Netflix subscription

-
subtrack add \
-  --name "Netflix Premium" \
-  --price 1980 \
-  --currency JPY \
-  --cycle monthly \
-  --tags "video,entertainment"
- - - -

Tag-based organization

-

- Tags are a powerful way to categorize subscriptions. Examples: -

-
    -
  • By category: music, video, cloud, productivity
  • -
  • By priority: essential, nice-to-have
  • -
  • By payment method: credit-card, paypal
  • -
  • By usage: personal, work, family
  • -
- -

Filter by tags:

-
# Find all music-related subscriptions
-subtrack tags music
-
-# Find subscriptions used for both work and personal
-subtrack tags work personal
- -

- Tags use AND logic — only subscriptions matching all specified - tags are shown. -

- - - -

Understanding your spending

-

- Use subtrack payment to see your total spending across different - periods: -

-
# How much per month?
-subtrack payment
-
-# How much per year?
-subtrack payment yearly
-
-# Weekly cost in USD
-subtrack payment weekly --currency USD
-

- payment automatically converts all billing cycles to the target - period. A yearly subscription will be divided into monthly cost, and a weekly - subscription will be multiplied accordingly. -

- - - -

Managing multi-currency subscriptions

-

- If you have subscriptions in different currencies (e.g., JPY for local services - and USD for international ones), subtrack handles this natively: -

-
# See everything in your local currency
-subtrack list --currency JPY
-
-# Compare spending across currencies
-subtrack list
-

- Without --currency, subscriptions are grouped by their original - currency with per-group subtotals. -

- - - -

Regular backups

-

- Set up a cron job (or Task Scheduler on Windows) for automatic backups: -

-
# Example cron: daily backup at 3 AM
-0 3 * * * subtrack backup ~/subtrack-backups
-

- Backups are timestamped and will never overwrite previous files. See - Data & Storage for restore instructions. -

-
diff --git a/docs/src/routes/installation/+page.md b/docs/src/routes/installation/+page.md new file mode 100644 index 0000000..6470417 --- /dev/null +++ b/docs/src/routes/installation/+page.md @@ -0,0 +1,62 @@ +--- +title: Installation +description: Install subtrack via npm, pnpm, or build from source. +--- + +## Requirements + +**subtrack** requires **Node.js 18 or later**. It is a pure Node.js package and does not require any system-level dependencies. + +## Install via npm (recommended) + +```bash +npm install -g subtrack +``` + +## Install via pnpm + +```bash +pnpm add -g subtrack +``` + +## Install via bun + +```bash +bun add -g subtrack +``` + +## Run with npx (no install) + +If you prefer not to install globally, use `npx`: + +```bash +npx subtrack list +``` + +Note that `npx` will download the package on every first run, so global install is recommended for regular use. + +## Build from source + +Clone the repository and build locally: + +```bash +git clone https://github.com/nazozokc/subtrack.git +cd subtrack +pnpm install +pnpm build +pnpm link --global +``` + +This links the `subtrack` command to your global `node_modules`. Re-run `pnpm link --global` after pulling updates. + +
+ 💡 Tip: Run subtrack --help after installation to verify everything works. +
+ +## Verify installation + +```bash +subtrack --help +``` + +You should see the list of available commands. diff --git a/docs/src/routes/installation/+page.svelte b/docs/src/routes/installation/+page.svelte deleted file mode 100644 index d074c08..0000000 --- a/docs/src/routes/installation/+page.svelte +++ /dev/null @@ -1,55 +0,0 @@ - - - - Installation — subtrack - - - -
-

Installation

- -

Requirements

-

- subtrack requires Node.js 18 or later. - It is a pure Node.js package and does not require any system-level dependencies. -

- -

Install via npm (recommended)

-
npm install -g subtrack
- -

Install via pnpm

-
pnpm add -g subtrack
- -

Install via bun

-
bun add -g subtrack
- -

Run with npx (no install)

-

If you prefer not to install globally, use npx:

-
npx subtrack list
-

- Note that npx will download the package on every first run, - so global install is recommended for regular use. -

- -

Build from source

-

Clone the repository and build locally:

-
git clone https://github.com/nazozokc/subtrack.git
-cd subtrack
-pnpm install
-pnpm build
-pnpm link --global
-

- This links the subtrack command to your global node_modules. - Re-run pnpm link --global after pulling updates. -

- -
- 💡 Tip: Run subtrack --help after installation to verify everything works. -
- -

Verify installation

-
subtrack --help
-

You should see the list of available commands.

-
diff --git a/docs/src/routes/tui/+page.md b/docs/src/routes/tui/+page.md new file mode 100644 index 0000000..5f728be --- /dev/null +++ b/docs/src/routes/tui/+page.md @@ -0,0 +1,171 @@ +--- +title: Interactive TUI +description: Walkthrough of subtrack's interactive terminal UI — prompts, autocomplete, confirmations, and behaviors. +--- + +subtrack offers both **interactive** and **non-interactive** modes. The interactive mode uses [@inquirer/prompts](https://github.com/SBoudrias/Inquirer.js) to guide you through each operation with live validation, select menus, and autocomplete hints. + +| Command | Interactive | Non-interactive | +|---------|-------------|-----------------| +| `add` | ✅ (default) | ✅ (all flags provided) | +| `delete` | ✅ (always) | ❌ | +| `list` | ❌ | ✅ | +| `tags` | ❌ | ✅ | +| `payment` | ❌ | ✅ | +| `backup` | ❌ | ✅ | + +## `subtrack add` — interactive walkthrough + +Running `subtrack add` without flags starts a step-by-step prompt session. + +### 1. Name + +``` +? subscription name Netflix +``` + +- Text input with live validation +- Cannot be empty +- Max 100 characters +- Validation error on invalid input: + +``` +ⓧ Name cannot be empty +``` + +### 2. Price + +``` +? monthly payment amount 1980 +``` + +- Numeric input +- Validated as a non-negative integer +- Max 99,999,999 + +### 3. Currency + +``` +? currency (Use arrow keys) + JPY (日本円) +❯ USD (US Dollar) + EUR (Euro) + GBP (British Pound) + AUD (Australian Dollar) + CAD (Canadian Dollar) + KRW (South Korean Won) + CNY (Chinese Yuan) + SGD (Singapore Dollar) + HKD (Hong Kong Dollar) +``` + +- Select from 10 supported currencies +- Navigate with arrow keys, confirm with Enter + +### 4. Cycle + +``` +? cycle (Use arrow keys) + weekly + bi-weekly +❯ monthly + quarterly + semi-annual + yearly +``` + +- Select from 6 billing cycles + +### 5. Tags + +``` +? tags existing: music, video +``` + +- Text input with **existing tags shown as hints** +- If you have previously created tags, they appear as `existing: tag1, tag2` to remind you of available values +- Comma-separated, max 10 tags, each max 50 characters +- Can be left blank (press Enter to skip) + +### 6. Confirmation + +``` +? Save "Netflix" (¥1,980, monthly)? (Y/n) +``` + +- Shows a summary of the subscription +- **Default is `Yes`** — pressing Enter saves immediately +- Type `n` to cancel + +After confirmation: + +``` +✔ Added subscription: Netflix +``` + +### Full-flag mode (no prompts) + +When all flags are provided (`--name`, `--price`, `--currency`, `--cycle`, `--tags`), **no prompts appear** and the subscription is saved immediately without confirmation: + +```bash +subtrack add \ + --name Netflix \ + --price 1980 \ + --currency JPY \ + --cycle monthly \ + --tags "video,entertainment" +``` + +Useful for scripts and automation. + +### Partial-flag mode + +You can provide some flags and let the rest be prompted. For example: + +```bash +subtrack add --name Netflix +``` + +This prompts for price, currency, cycle, and tags — and shows the confirmation dialog since some fields were interactive. + +## `subtrack delete` — interactive walkthrough + +`delete` is **always interactive**. There is no non-interactive mode. + +### 1. Select subscriptions + +``` +? select subscriptions to delete (Use arrow keys to move, space to select) +❯◯ Netflix — ¥1,980/month [video, entertainment] + ◯ Spotify — ¥980/month [music] + ◯ AWS — $50/month [cloud] +``` + +- **Multi-select** with checkbox (space to toggle, enter to confirm) +- Each subscription shows: `name — price/cycle [tags]` +- If no subscriptions exist, shows "No subscriptions found" and exits + +### 2. Confirmation + +``` +? Delete 2 subscriptions? (Netflix, Spotify) (y/N) +``` + +- Shows count and names of selected subscriptions +- **Default is `No`** — pressing Enter does **not** delete +- Type `y` to confirm + +After deletion, each removed subscription is confirmed: + +``` +✔ Deleted: Netflix +✔ Deleted: Spotify +``` + +## Tips + +- **`add` confirmation defaults to `Yes`** for quick saves, but you can cancel with `n`. +- **`delete` confirmation defaults to `No`** to prevent accidental deletion. +- Use **arrow keys** for select menus, **space** for checkboxes, **Enter** to confirm. +- Tags from previous sessions appear as **hints** — use consistent tag names to get autocomplete-like suggestions. +- Validation errors are shown inline in prompts; invalid flag values in non-interactive mode print an error and abort the command. diff --git a/docs/svelte.config.js b/docs/svelte.config.js index 61ab2e4..24a3c9c 100644 --- a/docs/svelte.config.js +++ b/docs/svelte.config.js @@ -1,7 +1,17 @@ import adapter from "@sveltejs/adapter-static" +import { mdsvex } from "mdsvex" +import path from "node:path" +import { fileURLToPath } from "node:url" + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) /** @type {import("@sveltejs/kit").Config} */ const config = { + extensions: [".svelte", ".md"], + preprocess: mdsvex({ + extensions: [".md"], + layout: path.resolve(__dirname, "src/lib/layouts/MarkdownLayout.svelte"), + }), kit: { adapter: adapter({ fallback: "index.html", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 189d96f..568dee0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,6 +22,10 @@ importers: vite: specifier: ^6.3.5 version: 6.4.3(@types/node@25.9.3)(tsx@4.22.4) + devDependencies: + mdsvex: + specifier: ^0.12.7 + version: 0.12.7(svelte@5.56.3) subtrack: dependencies: @@ -866,6 +870,9 @@ packages: '@types/jsesc@2.5.1': resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/node@25.9.3': resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} @@ -875,6 +882,9 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@vitest/expect@4.1.8': resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} @@ -1091,6 +1101,11 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + mdsvex@0.12.7: + resolution: {integrity: sha512-gx4bReLCUvq+MPErHXYeyX+TEq1hsS2KfiZtEOMNTcbibSouFy8AHc5h04KbGCl+g5tLuo4/lbgRVYRnc7bJZw==} + peerDependencies: + svelte: ^3.56.0 || ^4.0.0 || ^5.0.0-next.120 + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -1122,6 +1137,13 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + prism-svelte@0.4.7: + resolution: {integrity: sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + quansync@1.0.0: resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} @@ -1280,6 +1302,21 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + unist-util-is@4.1.0: + resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + + unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + + unist-util-visit-parents@3.1.1: + resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + + unist-util-visit@2.0.3: + resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} + + vfile-message@2.0.4: + resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} + vite@6.4.3: resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -1914,6 +1951,10 @@ snapshots: '@types/jsesc@2.5.1': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 2.0.11 + '@types/node@25.9.3': dependencies: undici-types: 7.24.6 @@ -1925,6 +1966,8 @@ snapshots: '@types/trusted-types@2.0.7': {} + '@types/unist@2.0.11': {} + '@vitest/expect@4.1.8': dependencies: '@standard-schema/spec': 1.1.0 @@ -2139,6 +2182,16 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + mdsvex@0.12.7(svelte@5.56.3): + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 2.0.11 + prism-svelte: 0.4.7 + prismjs: 1.30.0 + svelte: 5.56.3 + unist-util-visit: 2.0.3 + vfile-message: 2.0.4 + mrmime@2.0.1: {} mute-stream@3.0.0: {} @@ -2159,6 +2212,10 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prism-svelte@0.4.7: {} + + prismjs@1.30.0: {} + quansync@1.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -2345,6 +2402,28 @@ snapshots: undici-types@7.24.6: {} + unist-util-is@4.1.0: {} + + unist-util-stringify-position@2.0.3: + dependencies: + '@types/unist': 2.0.11 + + unist-util-visit-parents@3.1.1: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 4.1.0 + + unist-util-visit@2.0.3: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 4.1.0 + unist-util-visit-parents: 3.1.1 + + vfile-message@2.0.4: + dependencies: + '@types/unist': 2.0.11 + unist-util-stringify-position: 2.0.3 + vite@6.4.3(@types/node@25.9.3)(tsx@4.22.4): dependencies: esbuild: 0.25.12