2026-04-28 15:47:57 -04:00
|
|
|
|
# ASCIInator — CLAUDE.md
|
|
|
|
|
|
|
|
|
|
|
|
> Project spec for Claude Code. Read this fully before touching any file.
|
|
|
|
|
|
> Stack: Vue 3 + Vite + Tailwind (no component lib) + Fastify + execa
|
|
|
|
|
|
> Purpose: Headless GUI front-end for system ASCII conversion binaries.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 1. Project Overview
|
|
|
|
|
|
|
|
|
|
|
|
ASCIInator converts images to ASCII art by orchestrating system binaries
|
|
|
|
|
|
(chafa, jp2a, ascii-image-converter, img2txt) via ImageMagick preprocessing.
|
|
|
|
|
|
It is a **pure front-end UI** that talks to a **local Fastify API server**
|
|
|
|
|
|
which shells out to the binaries via `execa`.
|
|
|
|
|
|
|
|
|
|
|
|
Output format: **plain text**
|
|
|
|
|
|
Input methods: file upload, clipboard paste, drag-and-drop
|
|
|
|
|
|
Theme: dark default, respects `prefers-color-scheme`
|
|
|
|
|
|
CSS: Tailwind utility classes only — no component libraries, no Vuetify, no PrimeVue
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2. Repository Structure
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
asciinator/
|
|
|
|
|
|
├── CLAUDE.md ← you are here
|
|
|
|
|
|
├── package.json ← root (workspaces)
|
|
|
|
|
|
├── vite.config.js
|
|
|
|
|
|
├── tailwind.config.js
|
|
|
|
|
|
├── index.html
|
|
|
|
|
|
├── src/
|
|
|
|
|
|
│ ├── main.js
|
|
|
|
|
|
│ ├── App.vue
|
|
|
|
|
|
│ ├── assets/
|
|
|
|
|
|
│ │ └── main.css ← Tailwind base + custom properties
|
|
|
|
|
|
│ └── components/
|
|
|
|
|
|
│ ├── ImageInput.vue ← Component 1: input (upload / paste / drag-drop)
|
|
|
|
|
|
│ ├── ToolSelector.vue ← Component 2: ASCII tool picker + per-tool options
|
|
|
|
|
|
│ ├── ShellBridge.vue ← Component 3: translates GUI state → API call → shell
|
|
|
|
|
|
│ ├── OutputDisplay.vue ← Component 4: renders plain-text ASCII result
|
|
|
|
|
|
│ ├── ErrorLog.vue ← Component 5: watches and surfaces stderr / API errors
|
|
|
|
|
|
│ └── options/ ← Per-tool flag panels (extracted from ToolSelector)
|
|
|
|
|
|
│ ├── ChafaOptions.vue
|
|
|
|
|
|
│ ├── Jp2aOptions.vue
|
|
|
|
|
|
│ ├── AsciiOptions.vue
|
|
|
|
|
|
│ └── ImgTxtOptions.vue
|
|
|
|
|
|
└── server/
|
|
|
|
|
|
├── index.js ← Fastify server entry
|
|
|
|
|
|
├── routes/
|
|
|
|
|
|
│ └── convert.js ← POST /convert route
|
|
|
|
|
|
└── lib/
|
|
|
|
|
|
├── imagemagick.js ← ImageMagick pre/post processing via execa
|
|
|
|
|
|
└── converters.js ← per-tool execa invocations
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 3. Component Architecture
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 `ImageInput.vue`
|
|
|
|
|
|
- Accepts: file picker, drag-and-drop onto drop zone, clipboard paste (`Ctrl+V`)
|
|
|
|
|
|
- Emits: `image-ready` with a `{ dataUrl, mimeType, filename }` payload
|
|
|
|
|
|
- Sends raw file to server as `multipart/form-data`
|
|
|
|
|
|
- Shows image preview thumbnail before conversion
|
|
|
|
|
|
- Validates: image/* MIME types only; max 20 MB
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 `ToolSelector.vue`
|
|
|
|
|
|
- Provides a segmented control to choose between:
|
|
|
|
|
|
- `chafa` (default)
|
|
|
|
|
|
- `jp2a`
|
|
|
|
|
|
- `ascii-image-converter`
|
|
|
|
|
|
- `img2txt`
|
|
|
|
|
|
- Each tool reveals its own option panel when selected
|
|
|
|
|
|
- **chafa options** (full flag exposure):
|
|
|
|
|
|
- `--width` / `--height` (integer inputs — combined server-side into `--size=WxH`)
|
|
|
|
|
|
- `--colors` (none | 2 | 8 | 16/8 | 16 | 240 | 256 | full)
|
|
|
|
|
|
- `--symbols` (text input, e.g. `block+border+extra`)
|
|
|
|
|
|
- `--dither` (none | ordered | diffusion | noise)
|
|
|
|
|
|
- `--threshold` (0.0–1.0 slider)
|
|
|
|
|
|
- `--font-ratio` (float input)
|
|
|
|
|
|
- **jp2a options**: `--width`, `--height`, `--chars`, `--background`
|
|
|
|
|
|
- **ascii-image-converter options**: `--width`, `--height`, `--color`, `--braille`, `--threshold`
|
|
|
|
|
|
- **img2txt options**: `--width`, `--height`, `--format` (ansi/utf8/html), `--dither`, `--gamma`
|
|
|
|
|
|
- Emits: `tool-config` with `{ tool: string, flags: Record<string, string|number|boolean> }`
|
|
|
|
|
|
|
|
|
|
|
|
### 3.3 `ShellBridge.vue`
|
|
|
|
|
|
- Renderless component (`<template><slot /></template>`)
|
|
|
|
|
|
- Props: `image` (`{ dataUrl, mimeType, filename }`), `toolConfig` (`{ tool, flags }`)
|
|
|
|
|
|
- Exposes: `convert()` — called via template ref from App.vue on button click
|
|
|
|
|
|
- Converts `dataUrl` back to Blob via `fetch()`, sends as `multipart/form-data`
|
feat: full GUI, test API, auto-convert, aspect-ratio sliders
- Auto-convert on image load (0ms) and flag change (400ms debounce)
- SizeControl: linked width/height sliders with aspect-ratio lock and 0.5 font correction factor
- Full flag exposure for all 4 tools (chafa, jp2a, ascii-image-converter, img2txt)
- ChafaOptions: symbols/fill dropdowns, dither controls, work/threshold/font-ratio sliders, format select, toggles
- Jp2aOptions: color-depth, RGB weight sliders, edge controls, 8 toggles
- ImgTxtOptions: dither select with valid libcaca values, gamma slider
- OutputDisplay: ansi-to-html rendering for colored chafa output
- ShellBridge: abort-previous pattern, conversion-start/end lifecycle events
- Test API (ENABLE_TEST_API=true): /test/health, /test/convert, /test/flags/:tool, /test/imagemagick
- buildArgs: space-separated args (not = format); full schemas in SCHEMAS export
- runChafa: width/height destructured and combined into --size WxH
- Port changed to 3050; Vite on 0.0.0.0 with allowedHosts for production domain
- 98 unit tests passing across 12 test files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 06:29:45 -04:00
|
|
|
|
- POSTs to `/api/convert` (proxied by Vite dev server to `http://localhost:3050/convert`)
|
2026-04-28 15:47:57 -04:00
|
|
|
|
- Emits: `conversion-result` (stdout string) | `conversion-error` (stderr string)
|
|
|
|
|
|
- Handles: 30s timeout via `AbortController`, network errors, non-2xx responses
|
|
|
|
|
|
|
|
|
|
|
|
### 3.4 `OutputDisplay.vue`
|
|
|
|
|
|
- Receives plain-text ASCII output string as prop
|
|
|
|
|
|
- Renders in a `<pre>` block with monospace font (JetBrains Mono or Fira Code via CDN)
|
|
|
|
|
|
- Controls: copy-to-clipboard button, download as `.txt`, font-size slider
|
|
|
|
|
|
- Preserves whitespace exactly — never reformat or trim output
|
|
|
|
|
|
|
|
|
|
|
|
### 3.5 `ErrorLog.vue`
|
|
|
|
|
|
- Receives array of `{ timestamp, source, message }` error objects as prop
|
|
|
|
|
|
- Collapsible panel, pinned to bottom of viewport
|
|
|
|
|
|
- Color-coded: yellow = warning, red = error
|
|
|
|
|
|
- Shows last 50 entries, auto-scrolls to latest
|
|
|
|
|
|
- Clear button
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 4. Fastify Server (`server/`)
|
|
|
|
|
|
|
|
|
|
|
|
### Entry: `server/index.js`
|
|
|
|
|
|
```js
|
|
|
|
|
|
// Fastify + @fastify/multipart
|
feat: full GUI, test API, auto-convert, aspect-ratio sliders
- Auto-convert on image load (0ms) and flag change (400ms debounce)
- SizeControl: linked width/height sliders with aspect-ratio lock and 0.5 font correction factor
- Full flag exposure for all 4 tools (chafa, jp2a, ascii-image-converter, img2txt)
- ChafaOptions: symbols/fill dropdowns, dither controls, work/threshold/font-ratio sliders, format select, toggles
- Jp2aOptions: color-depth, RGB weight sliders, edge controls, 8 toggles
- ImgTxtOptions: dither select with valid libcaca values, gamma slider
- OutputDisplay: ansi-to-html rendering for colored chafa output
- ShellBridge: abort-previous pattern, conversion-start/end lifecycle events
- Test API (ENABLE_TEST_API=true): /test/health, /test/convert, /test/flags/:tool, /test/imagemagick
- buildArgs: space-separated args (not = format); full schemas in SCHEMAS export
- runChafa: width/height destructured and combined into --size WxH
- Port changed to 3050; Vite on 0.0.0.0 with allowedHosts for production domain
- 98 unit tests passing across 12 test files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 06:29:45 -04:00
|
|
|
|
// Listens on localhost:3050
|
2026-04-28 15:47:57 -04:00
|
|
|
|
// Single route: POST /convert
|
|
|
|
|
|
// CORS: origin: true (reflects requester — permissive for local dev)
|
|
|
|
|
|
// Logger: warn level only (startup logged via console.log)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Route: `POST /convert`
|
|
|
|
|
|
1. Parse multipart body — extract image file + JSON fields (`tool`, `flags`)
|
|
|
|
|
|
2. Write image to temp file (`/tmp/asciinator-<uuid>.<ext>`)
|
|
|
|
|
|
3. Pass through ImageMagick preprocessing (see §4.1)
|
|
|
|
|
|
4. Invoke selected tool via `converters.js`
|
|
|
|
|
|
5. Stream stdout back as `text/plain`
|
|
|
|
|
|
6. Clean up temp files in `finally`
|
|
|
|
|
|
7. On non-zero exit: return 422 with stderr body
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 ImageMagick Pipeline (`lib/imagemagick.js`)
|
|
|
|
|
|
**Preprocessing** (always runs before ASCII tool):
|
|
|
|
|
|
- Convert to PNG by default, JPEG for jp2a (`magick input.x output.{png,jpg}`)
|
|
|
|
|
|
- Strip metadata (`-strip`)
|
|
|
|
|
|
- Auto-orient (`-auto-orient`)
|
|
|
|
|
|
- Resize to max 2000px on longest side if larger (`-resize 2000x2000>`)
|
|
|
|
|
|
- `jp2a` receives JPEG — it links against libjpeg and rejects PNG input
|
|
|
|
|
|
|
|
|
|
|
|
**Post-processing** (optional, future hook — stub it now):
|
|
|
|
|
|
- Reserved for potential brightness/contrast adjustment on output
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 Converters (`lib/converters.js`)
|
|
|
|
|
|
Each converter is a function:
|
|
|
|
|
|
```js
|
|
|
|
|
|
async function runChafa(imagePath, flags) → { stdout, stderr }
|
|
|
|
|
|
async function runJp2a(imagePath, flags) → { stdout, stderr }
|
|
|
|
|
|
async function runAsciiImageConverter(imagePath, flags) → { stdout, stderr }
|
|
|
|
|
|
async function runImgToTxt(imagePath, flags) → { stdout, stderr }
|
|
|
|
|
|
```
|
|
|
|
|
|
- Use `execa` for all invocations
|
|
|
|
|
|
- Map the `flags` object to CLI args (e.g. `{ width: 80 }` → `['--width', '80']`)
|
|
|
|
|
|
- Validate flag values before constructing args — reject unknown flags
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 5. State Management
|
|
|
|
|
|
|
|
|
|
|
|
Use Vue 3 `reactive()` in `App.vue` — no Pinia for this scale.
|
|
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
|
const state = reactive({
|
|
|
|
|
|
image: null, // { dataUrl, mimeType, filename }
|
|
|
|
|
|
toolConfig: { tool: 'chafa', flags: {} },
|
|
|
|
|
|
result: '', // plain text ASCII output
|
|
|
|
|
|
errors: [], // [{ timestamp, source, message }]
|
|
|
|
|
|
isConverting: false,
|
|
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Pass state slices as props, bubble events up. No global store.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 6. Styling Conventions
|
|
|
|
|
|
|
|
|
|
|
|
- **Tailwind only** — no inline styles, no external CSS except `src/assets/main.css`
|
|
|
|
|
|
- Always dark — `class="dark"` hardcoded on `<html>`. Light mode support planned (needs CSS variable overrides)
|
|
|
|
|
|
- CSS custom properties in `main.css` for brand tokens:
|
|
|
|
|
|
```css
|
|
|
|
|
|
:root {
|
|
|
|
|
|
--ascii-green: #39ff14; /* neon accent */
|
|
|
|
|
|
--ascii-bg: #0d0d0d;
|
|
|
|
|
|
--ascii-surface: #1a1a1a;
|
|
|
|
|
|
--ascii-border: #2a2a2a;
|
|
|
|
|
|
--ascii-text: #e0e0e0;
|
|
|
|
|
|
--ascii-error: #ff4444;
|
|
|
|
|
|
--ascii-warn: #ffaa00;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
- Monospace font for all output: `font-family: 'JetBrains Mono', 'Fira Code', monospace`
|
|
|
|
|
|
- UI font: `font-family: 'IBM Plex Sans', sans-serif`
|
|
|
|
|
|
- Load both via Google Fonts / Bunny Fonts in `index.html`
|
|
|
|
|
|
|
|
|
|
|
|
**`@layer components` utility classes** (defined in `main.css` — use these in templates):
|
|
|
|
|
|
|
|
|
|
|
|
| Class | Purpose |
|
|
|
|
|
|
|-------|---------|
|
|
|
|
|
|
| `.ascii-input` | Dark-themed text input or select — full width, bordered, focus ring |
|
|
|
|
|
|
| `.ascii-label` | Column-flex label wrapper with muted text — wraps a `.ascii-input` |
|
|
|
|
|
|
| `.ascii-btn` | Ghost button — transparent bg, border, hover turns neon green |
|
|
|
|
|
|
| `.ascii-btn-primary` | Solid neon-green button — used for the main Convert action |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 7. Neovim IDE v2 Setup
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 LSP & Tooling (expected config)
|
|
|
|
|
|
| Tool | Role |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| `volar` | Vue 3 LSP (use Takeover mode — disable `tsserver` for `.vue` files) |
|
|
|
|
|
|
| `eslint-lsp` | Linting via `eslint_d` or native LSP client |
|
|
|
|
|
|
| `prettier` | Formatting — `.prettierrc` at root |
|
|
|
|
|
|
| `emmet-ls` | HTML/template expansion in `.vue` files |
|
|
|
|
|
|
| `cssls` | Tailwind class intellisense (pair with `tailwindcss-language-server`) |
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 `.prettierrc`
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"semi": false,
|
|
|
|
|
|
"singleQuote": true,
|
|
|
|
|
|
"tabWidth": 2,
|
|
|
|
|
|
"printWidth": 100,
|
|
|
|
|
|
"vueIndentScriptAndStyle": false
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 7.3 `.eslintrc.cjs`
|
|
|
|
|
|
```js
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
|
extends: ['plugin:vue/vue3-recommended', 'prettier'],
|
|
|
|
|
|
rules: {
|
|
|
|
|
|
'vue/multi-word-component-names': 'off'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 7.4 Neovim v2 Test Notes
|
|
|
|
|
|
This project is a test bed for **Neovim IDE v2.1.x**. Log observations here:
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] Volar Takeover mode stability with Vite HMR running
|
|
|
|
|
|
- [ ] Tailwind intellisense triggering inside `class=""` in `.vue` templates
|
|
|
|
|
|
- [ ] `emmet-ls` expansion in `<template>` blocks
|
|
|
|
|
|
- [ ] ESLint auto-fix on save (`BufWritePre` autocmd)
|
|
|
|
|
|
- [ ] Prettier format on save — confirm it doesn't fight ESLint
|
|
|
|
|
|
- [ ] `gd` (go-to-definition) across `.vue` SFC boundaries
|
|
|
|
|
|
- [ ] Telescope file search respecting `.gitignore` (node_modules, dist)
|
|
|
|
|
|
- [ ] DAP (debugger) attach to Vite dev server — note any issues
|
|
|
|
|
|
- [ ] Terminal split for running Fastify server alongside Vite
|
|
|
|
|
|
|
|
|
|
|
|
**Add test results below as you work:**
|
|
|
|
|
|
```
|
|
|
|
|
|
DATE | COMPONENT | OBSERVATION | PASS/FAIL
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 8. Dev Workflow
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Install
|
|
|
|
|
|
npm install
|
|
|
|
|
|
|
|
|
|
|
|
# Start Fastify server (terminal 1)
|
|
|
|
|
|
node server/index.js
|
|
|
|
|
|
|
|
|
|
|
|
# Start Vite dev server (terminal 2)
|
|
|
|
|
|
npm run dev
|
|
|
|
|
|
|
|
|
|
|
|
# Both should be running simultaneously during development
|
|
|
|
|
|
# Vue: http://localhost:5173
|
feat: full GUI, test API, auto-convert, aspect-ratio sliders
- Auto-convert on image load (0ms) and flag change (400ms debounce)
- SizeControl: linked width/height sliders with aspect-ratio lock and 0.5 font correction factor
- Full flag exposure for all 4 tools (chafa, jp2a, ascii-image-converter, img2txt)
- ChafaOptions: symbols/fill dropdowns, dither controls, work/threshold/font-ratio sliders, format select, toggles
- Jp2aOptions: color-depth, RGB weight sliders, edge controls, 8 toggles
- ImgTxtOptions: dither select with valid libcaca values, gamma slider
- OutputDisplay: ansi-to-html rendering for colored chafa output
- ShellBridge: abort-previous pattern, conversion-start/end lifecycle events
- Test API (ENABLE_TEST_API=true): /test/health, /test/convert, /test/flags/:tool, /test/imagemagick
- buildArgs: space-separated args (not = format); full schemas in SCHEMAS export
- runChafa: width/height destructured and combined into --size WxH
- Port changed to 3050; Vite on 0.0.0.0 with allowedHosts for production domain
- 98 unit tests passing across 12 test files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 06:29:45 -04:00
|
|
|
|
# Fastify API: http://localhost:3050
|
2026-04-28 15:47:57 -04:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### npm scripts (`package.json`)
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"scripts": {
|
|
|
|
|
|
"dev": "vite",
|
|
|
|
|
|
"build": "vite build",
|
|
|
|
|
|
"preview": "vite preview",
|
|
|
|
|
|
"server": "node server/index.js",
|
|
|
|
|
|
"server:dev": "node --watch server/index.js",
|
|
|
|
|
|
"test:unit": "vitest",
|
|
|
|
|
|
"test:e2e": "playwright test",
|
|
|
|
|
|
"lint": "run-s lint:oxlint lint:eslint",
|
|
|
|
|
|
"format": "prettier --write --experimental-cli src/"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 9. Key Constraints for Claude Code
|
|
|
|
|
|
|
|
|
|
|
|
- **Never** install a Vue component library (no Vuetify, Headless UI, PrimeVue, etc.)
|
|
|
|
|
|
- **Never** add TypeScript — this is a JS project
|
|
|
|
|
|
- **Never** use `Options API` — Composition API with `<script setup>` only
|
|
|
|
|
|
- **Never** add Pinia — use `reactive()` in App.vue
|
|
|
|
|
|
- **Never** use `localStorage` for anything
|
|
|
|
|
|
- **Always** use `execa` for shell invocations — never `exec()` or raw `spawn()`
|
|
|
|
|
|
- **Always** sanitize/validate flag values before building CLI args
|
|
|
|
|
|
- **Always** clean up `/tmp/asciinator-*` files after each conversion
|
|
|
|
|
|
- **Always** use `<script setup>` SFC format
|
|
|
|
|
|
- **Always** keep components under ~150 lines — extract composables if growing
|
|
|
|
|
|
- **Never** remove Vitest or Playwright — they were scaffolded by Vite and are part of the test bed
|
|
|
|
|
|
- **Unit tests** live in `src/__tests__/` (App) and `src/components/__tests__/` (components) — Vitest, not Jest
|
|
|
|
|
|
- **e2e tests** live in `e2e/` — Playwright
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 10. Dependencies
|
|
|
|
|
|
|
|
|
|
|
|
### All in root `package.json` (single package, no workspaces)
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"dependencies": {
|
|
|
|
|
|
"vue": "^3.5",
|
|
|
|
|
|
"fastify": "^5.x",
|
|
|
|
|
|
"@fastify/multipart": "^10.x",
|
|
|
|
|
|
"@fastify/cors": "^11.x",
|
|
|
|
|
|
"execa": "^9.x"
|
|
|
|
|
|
},
|
|
|
|
|
|
"devDependencies": {
|
|
|
|
|
|
"vite": "^8.x",
|
|
|
|
|
|
"@vitejs/plugin-vue": "^6.x",
|
|
|
|
|
|
"tailwindcss": "^3.x",
|
|
|
|
|
|
"autoprefixer": "^10.x",
|
|
|
|
|
|
"postcss": "^8.x",
|
|
|
|
|
|
"vitest": "^4.x",
|
|
|
|
|
|
"@playwright/test": "^1.x",
|
|
|
|
|
|
"@vue/test-utils": "^2.x",
|
|
|
|
|
|
"eslint": "^10.x",
|
|
|
|
|
|
"eslint-plugin-vue": "^10.x",
|
|
|
|
|
|
"oxlint": "^1.x",
|
|
|
|
|
|
"prettier": "^3.x"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
*Last updated: 2026-04-28 — ASCIInator v0.1 (all components built)*
|