Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
b5435bb
Merge branch 'release/v0.2.0' into develop
Addi90 Aug 22, 2026
4e3a4d3
feat: support custom modifier pipeline order in config
Addi90 Aug 23, 2026
5945fd1
feat: drag-and-drop modifier ordering in sidebar
Addi90 Aug 23, 2026
7087744
docs: document adjustable modifier order
Addi90 Aug 23, 2026
f70e132
fix: use children snippet instead of deprecated slot in ModifierCard
Addi90 Aug 23, 2026
4e40732
fix: mark insertion slot (between cards) while dragging modifiers
Addi90 Aug 23, 2026
f0621af
fix: keep drop markers of the first modifier card visible
Addi90 Aug 23, 2026
ae978b4
feat: show reset-order button when the modifier order was changed
Addi90 Aug 23, 2026
16bbe1d
Merge pull request #4 from Addi90/feat/flexible-modifier-order
Addi90 Aug 23, 2026
2cd912d
test: add vitest + testing-library scaffolding for frontend
Addi90 Aug 24, 2026
3ec8f66
test: cover config sanitize and i18n key parity
Addi90 Aug 24, 2026
b64e173
test: unit test store actions with mocked api
Addi90 Aug 24, 2026
676b3fb
test: smoke test FileList, Dialog, RenameButton
Addi90 Aug 24, 2026
c1d22e5
docs: document frontend test workflow
Addi90 Aug 24, 2026
7c5ad7f
build: run frontend vitest suite in 'do test'
Addi90 Aug 24, 2026
0072531
test: move pytest suite to tests/backend/, keep frontend tests co-loc…
Addi90 Aug 24, 2026
48a7f15
Merge pull request #5 from Addi90/feat/frontend-tests
Addi90 Aug 24, 2026
09c3465
ci: add GitHub Actions development pipeline (pytest, vitest, SPA buil…
Addi90 Aug 26, 2026
194a8d0
ci: add release workflow (do bump + do tag on master merge) and build…
Addi90 Aug 26, 2026
10cf22e
docs: document CI workflows (ci/release/build) in README + AGENTS.md
Addi90 Aug 26, 2026
ce385a2
fix(ci): add httpx2 to dev requirements (starlette TestClient needs i…
Addi90 Aug 26, 2026
dd1274a
Merge pull request #8 from Addi90/feat/github-actions-for-development…
Addi90 Aug 26, 2026
020a12f
feat(engine): support renaming directories as extension-less entries
Addi90 Aug 27, 2026
c237921
feat(api): list directories with type, accept dirs in preview/check/r…
Addi90 Aug 27, 2026
7400b3a
feat(frontend): view toggles for files and directories in the store
Addi90 Aug 27, 2026
83ebeed
feat(frontend): directory rows with type badge, view toggles, tree re…
Addi90 Aug 27, 2026
972cafb
docs: document directory renaming and list view toggles
Addi90 Aug 27, 2026
90eb526
fix(engine): treat dot-files as extension-less (whole name is the base)
Addi90 Aug 27, 2026
f53235d
fix(frontend): resolve a11y warnings in the drag-and-drop modifier card
Addi90 Aug 27, 2026
4b4ce6c
docs: trim AGENTS.md to the necessary — fix stale spots, drop filler
Addi90 Aug 27, 2026
6770216
Merge pull request #9 from Addi90/feat/folder-editing
Addi90 Aug 27, 2026
28a3913
feat(frontend): add app logo and favicon (rounded purple folder + Hel…
Addi90 Aug 27, 2026
76820a6
feat(build): use the app logo as the native app icon (macOS .app + Wi…
Addi90 Aug 27, 2026
7af3628
fix(ui): anchor the header subtitle to the title baseline
Addi90 Aug 28, 2026
f5305c4
Merge pull request #10 from Addi90/feat/add-logos-and-favicon
Addi90 Aug 28, 2026
c9c7c0a
fix(build): never fail the build on icon (re)generation
Addi90 Aug 28, 2026
ee02e25
fix(ui): directory tree no longer freezes at one level
Addi90 Aug 28, 2026
66c520d
fix(api): return a friendly 403 for unreadable directories
Addi90 Aug 28, 2026
f0ffdef
feat(ui): dismissible error banner
Addi90 Aug 28, 2026
bcea52c
Merge pull request #11 from Addi90/fix/icon-build-fallback
Addi90 Aug 28, 2026
ad3f2b9
Merge pull request #12 from Addi90/fix/tree-depth-and-permissions
Addi90 Aug 28, 2026
a46e57d
Merge branch 'master' into develop
Aug 28, 2026
9585999
docs: align AGENTS.md and README with v0.3.0
Aug 28, 2026
b34d4ef
Merge pull request #19 from Addi90/docs/v0.3.0-doc-sync
Addi90 Aug 28, 2026
adb7108
feat: modern UI — design tokens, dark mode, APCA-tuned contrast
Aug 29, 2026
b459dcf
fix: stray ellipsis next to file-list checkboxes
Aug 29, 2026
14556ed
Merge pull request #21 from Addi90/port/modern-ui
Addi90 Aug 30, 2026
649af8b
test: tighten frontend tests to their actual contracts
Aug 28, 2026
403e97c
test: add missing frontend coverage (api client + modifier panels)
Aug 29, 2026
3dc5a92
docs: sync AGENTS.md vitest counts to the new test suite
Aug 29, 2026
3973c83
fix: use a real disabled state for disabled modifier panels
Aug 29, 2026
7c5e32a
fix: place Remove's range toggle on its own row
Aug 29, 2026
b95d940
docs: sync AGENTS.md, README and DESIGN.md with the modern UI branch
Aug 30, 2026
ba21121
Merge pull request #20 from Addi90/test/frontend-hardening
Addi90 Aug 30, 2026
f85e7df
fix: silence two svelte build warnings
Aug 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@ jobs:
run: |
set -euo pipefail

git push origin master --tags
git push origin master --tags
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ build/smoke/
.DS_Store
.idea/
.vscode/
outputs/
graphify-out/
650 changes: 229 additions & 421 deletions AGENTS.md

Large diffs are not rendered by default.

242 changes: 242 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# A-Renamer — Design System

Dark-first desktop web utility (pywebview / WKWebView, mouse + keyboard). Batch file &
directory renamer: directory tree · file list with live previews · 7-step modifier
pipeline. Accent family is derived from the product logo (`frontend/public/favicon.svg`,
purple folder gradient `#a855f7 → #6d28d9`).

Reference implementation of these tokens: `frontend/src/index.css` (the committed SPA's stylesheet).

```yaml
name: A-Renamer
description: >
Dark-first desktop renaming utility. Layered dark surfaces, one logo-purple accent,
mono filenames, dense 13px working grid, numbered modifier pipeline.
colors:
bg: "#0e0f12" # light: #f2f3f6
surface: "#16181d" # light: #ffffff
surface-2: "#1d2026" # light: #f6f7f9
surface-3: "#242830" # light: #eceef2
border: "#262b33" # light: #e4e7ee
border-strong: "#333945" # light: #d3d8e2
primary: "#8b36e8" # filled button bg; light: #7c3aed
primary-hover: "#6d28d9" # both themes
primary-contrast: "#ffffff" # both themes
accent: "#a855f7" # light: #7c3aed
accent-deep: "#8b36e8" # light: #6d28d9
accent-bright: "#e9d5ff" # light: #6523d6
accent-contrast: "#0e0f12" # light: #ffffff
accent-soft: "#a855f724" # 14% alpha; light: #7c3aed17 (9%)
sel: "#a855f71a" # 10% alpha; light: #7c3aed0f (6%)
row-hover: "#ffffff09" # 3.5% alpha; light: #14182809
success: "#70e4a1" # light: #157a49
danger: "#ffc1b8" # light: #b42c21
text: "#edeff2" # light: #181b21
muted: "#d0d6df" # light: #545e6e
faint: "#7c8694" # light: #4e5767
typography:
font-family: [system-ui, -apple-system, Segoe UI, Roboto, sans-serif]
mono: [ui-monospace, SF Mono, Menlo, monospace]
text-scale: [10.5, 11, 11.5, 12, 12.5, 13, 14] # dense utility grid (not 1.25 modular)
rounded:
sm: 8px
md: 10px
lg: 14px
pill: 99px
spacing:
unit: 4px # grid 4/8/12/16/20; 2px subgrid for tight control padding
components:
button-primary:
backgroundColor: "{colors.primary}"
textColor: "{colors.accent-contrast}"
rounded: "{rounded.sm}"
padding: 7px 14px
button-ghost:
backgroundColor: "{colors.surface}"
textColor: "{colors.muted}"
rounded: "{rounded.sm}"
padding: 6px 12px
chip-on:
backgroundColor: "{colors.accent-soft}"
textColor: "{colors.accent-bright}"
rounded: "{rounded.sm}"
input:
backgroundColor: "{colors.bg}"
textColor: "{colors.text}"
rounded: "{rounded.sm}"
pane:
backgroundColor: "{colors.surface}"
textColor: "{colors.text}"
rounded: "{rounded.lg}"
step-card:
backgroundColor: "{colors.surface-2}"
textColor: "{colors.text}"
rounded: "{rounded.md}"
step-number-chip:
backgroundColor: "{colors.surface-3}"
textColor: "{colors.muted}"
rounded: "{rounded.pill}"
step-number-chip-active:
backgroundColor: "{colors.primary}"
textColor: "{colors.primary-contrast}"
rounded: "{rounded.pill}"
badge:
backgroundColor: "{colors.accent-soft}"
textColor: "{colors.accent-bright}"
rounded: "{rounded.pill}"
hint:
textColor: "{colors.faint}"
warning:
textColor: "{colors.danger}"
changed-name:
textColor: "{colors.success}"
row-hover:
backgroundColor: "{colors.row-hover}"
row-selected:
backgroundColor: "{colors.sel}"
```

## Colors

Two themes, same token names. All values live in `:root` / `[data-theme]` blocks;
components use `var(--token)` only.

| Token | Dark | Light | Role |
|---|---|---|---|
| `--bg` | `#0e0f12` | `#f2f3f6` | page background |
| `--surface` | `#16181d` | `#ffffff` | panes, cards |
| `--surface-2` | `#1d2026` | `#f6f7f9` | inactive step cards, segment wells |
| `--surface-3` | `#242830` | `#eceef2` | step number chips (off) |
| `--border` | `#262b33` | `#e4e7ee` | hairlines, card borders |
| `--border-strong` | `#333945` | `#d3d8e2` | inactive status dots |
| `--text` / `--muted` / `--faint` | see frontmatter | see frontmatter | 3 text tiers |
| `--primary` / `--primary-hover` | `#8b36e8` / `#6d28d9` | `#7c3aed` / `#6d28d9` | filled primary button + hover (white text, APCA 82/89) |
| `--accent` / `--accent-deep` | `#a855f7` / `#8b36e8` | `#7c3aed` / `#6d28d9` | logo gradient stops |
| `--accent-bright` | `#e9d5ff` | `#6523d6` | accent text on soft fills (APCA ≥75 in both themes) |
| `--accent-soft` / `--sel` | 14% / 10% | 9% / 6% | tints, selection |
| `--success` | `#70e4a1` | `#157a49` | changed-name highlight |
| `--danger` | `#ffc1b8` | `#b42c21` | errors, warnings |

**Contrast (measured, gate: WCAG AA 4.5 hard floor, APCA 75 target for primary text):**

| Pair | Dark | Light |
|---|---|---|
| body (`--text` on `--surface`) | WCAG 15.4 · APCA 96 ✓ | WCAG 17.3 · APCA 104 ✓ |
| `--muted` on `--surface` | WCAG 12.1 · APCA 80 ✓ | WCAG 6.6 · APCA 82.5 ✓ |
| `--faint` on `--surface` | WCAG 4.8 · APCA 36.1* | WCAG 7.3 · APCA 85 ✓ (worst fill `--surface-3`: 75.1) |
| crumb (`--muted` on `--bg`) | WCAG 13.1 · APCA 81 ✓ | WCAG 5.9 · APCA 75 ✓ |
| primary button (white on `--primary`) | WCAG 5.5 · APCA 81.9 ✓ | WCAG 5.7 · APCA 82.8 ✓ |
| primary hover (white on `--primary-hover`) | WCAG 7.1 · APCA 88.3 ✓ | WCAG 7.3 · APCA 89.4 ✓ |
| changed name (`--success`) | WCAG 11.2 · APCA 75.8 ✓ | WCAG 5.4 · APCA 76.3 ✓ |
| badge (`--accent-bright` on soft) | WCAG 10.3 · APCA 81.7 ✓ | WCAG 6.6 · APCA 75.1 ✓ |
| `--danger` | WCAG 11.5 · APCA 76.9 ✓ | WCAG 6.3 · APCA 80.0 ✓ |

\* One documented deviation remains: dark `--faint` (APCA 36.1, WCAG 4.8 ✓).
It is the metadata tier (table headers, step numbers, 10.5–11.5px labels,
placeholders, hints) and only reads on `--surface`/`--surface-2`. Raising it to
APCA 75 would flatten it into `--muted` (APCA 80) and kill the 3-tier hierarchy —
the known APCA dark-mode tradeoff. (Duplicate-row *hover* is transient: danger
text on the 12% danger tint reaches APCA 67–73, WCAG ≥4.5 — accepted.) Every
other pair in both themes passes APCA 75 at the sizes where it is used.

**Known ceiling:** `data:` URI glyphs (e.g. the `<select>` chevron) cannot use
`var()` — their stroke color duplicates `--muted` per theme as a hardcoded value.
Kept to exactly that one exception.

**`@google/design.md lint` notes (0 errors, 5 accepted warnings):** the frontmatter
models one flat palette (dark values + light in comments), so (a) `chip-on`/`badge`
contrast warnings composite dark-theme ink over an alpha tint on a white canvas — the
per-theme measured values in the table above are the real ones; (b) `--border`/`--border-strong`
are reported orphaned because the schema's component sub-tokens (`backgroundColor`,
`textColor`, …) cannot express borders — they are used throughout per the Elevation rules; (c) `accent-deep`
is the logo's second gradient stop (`favicon.svg`) — brand vocabulary, kept for completeness.
`--primary-hover` is now an explicit token (both themes share `#6d28d9`).

## Typography

- UI: system stack. Filenames & paths: mono. Never a third family.
- Dense utility scale (14 base): 10.5 (field labels) · 11 (section headings, uppercase) ·
11.5 (hints) · 12 (checkboxes) · 12.5 (mono names, crumbs) · 13 (rows, buttons) ·
14 (default). We deliberately do not use a 1.25 modular scale — a renaming tool's
value is rows-per-screen, not display typography.
- Uppercase + `letter-spacing: 0.06–0.08em` reserved for section headings only.
- Tabular figures (`font-variant-numeric: tabular-nums`) for counts.

## Layout

Three panes: tree (232px fixed) · file list (flex) · pipeline (332px fixed), 12px gutters,
12/16/20px outer padding. Spacing snaps to the 4px grid (4/8/12/16/20); tight
control-internal padding may use the 2px subgrid. Below 980px the
panes stack. The window never scrolls vertically; panes scroll internally.

## Elevation

Three named levels, no ad-hoc shadows:

| Level | Recipe (dark) | Use |
|---|---|---|
| `--shadow` (resting) | `inset 0 1px 0 rgba(255,255,255,.04), 0 8px 28px rgba(0,0,0,.38)` | panes, dialogs |
| `--shadow-btn` (raised) | `0 1px 2px rgba(0,0,0,.4), inset 0 0 0 1px rgba(255,255,255,.06)` | primary button |
| focus ring | `0 0 0 3px var(--accent-soft)` | focused inputs, active status dots |

Light theme uses `rgba(16,24,40,…)` at 5–12%. Separation order: whitespace → background
shift (`--surface-2`) → elevation. A border is the last resort; panes combine a 1px
`--border` with `--shadow` (border + elevation together is the pane recipe, never a
border alone as a card).

## Shapes

`--r-sm: 8px` (inputs, buttons, chips) · `--r-md: 10px` (step cards) · `--r-lg: 14px`
(panes, dialogs) · `99px` (pills, dots, badges). No other radii.

## Components & states

Every interactive element declares the full state set. Global contract:

```css
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
:disabled { opacity: 0.5; cursor: not-allowed; }
.primary:active { transform: translateY(1px); } /* pressed: physical dip on pill controls */
.ghost:active, .icon-btn:active, .chip:active, .lang:active { transform: translateY(1px); }
.crumb:active, .node:active { background: var(--surface-3); } /* flat surfaces deepen instead */
```

Inputs override the outline with the 3px `--accent-soft` ring (their focus-visible
treatment); everything else keeps the outline.

- **Button (primary)** — `--primary` / `--primary-contrast` (white in both themes) /
`--r-sm` / `--shadow-btn`; hover → `--primary-hover`. The dark button uses
`--accent-deep` as its base stop: white on `--accent` is APCA 37, on `--accent-deep`
82. Disabled → 45% opacity; selection count in a pill (dark: `--text` bg / `--bg`
text, APCA 96; light: `--accent-contrast` bg / `--accent` text, 77.5).
- **Button (ghost)** — `--surface` + `--border` / `--muted`; hover → `--surface-2` +
`--text`; disabled → 45%.
- **Chip (view toggle)** — off: `--surface`/`--faint`; on: `--accent-soft` fill,
`--accent-bright`, border mixed 45% accent.
- **Step card (modifier)** — off: `--surface-2`, collapsed to one line, status dot
`--border-strong`, number chip `--surface-3`/`--faint`; on: `--surface`, expanded,
dot `--accent` + 3px `--accent-soft` ring, number chip `--accent`/`--accent-contrast`.
- **Table row** — hover `--row-hover`; selected `--sel` (hover: `--accent-soft`);
changed names `--success` semibold + 5px dot indicator.
- **Tree node** — `--muted`; hover → `--text`; current → `--accent-soft` + `--text`.
- **Input/Select** — `--bg` field on `--surface` cards (the one deliberate inversion,
light field on light card); focus ring per contract.
- **Dialog** — `--surface`, `--r-lg`, `--shadow`; confirm → primary, abort → ghost;
warning icon/title `--danger`.

## Do's & Don'ts

Do:
- Use tokens only; two themes are the whole palette story.
- Mono for anything that is a filename or a path.
- Hierarchy by tier (`text` → `muted` → `faint`) and weight, not by color.
- Semantic color only: `--success` = name changed, `--danger` = error/warning, accent =
interactive/selected — nothing else.

Don't:
- No gradients except the logo. No glassmorphism, no glow (the one allowed "glow" is
the 3px focus/soft ring on status dots).
- No shadows as texture — only `--shadow`, `--shadow-btn`, focus ring.
- No off-scale sizes, no off-grid spacing, no new accent hues.
- No 1px-border-only cards; no borders in place of a background shift.
- Don't introduce a light-default reflex: dark is the default theme.
38 changes: 26 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<p align="center"><img src="build/icons/arenamer.png" width="144" alt="A-Renamer Tool logo"></p>

# A-Renamer Tool

The A (or Adrian's) – Renamer is a tool for bulk renaming files **and directories** in various ways. Its graphical user interface lets the user easily select a file directory and then select the entries (files and/or folders) to bulk rename. In the file selection view it also provides a live preview of the new name for each selected entry, and Files/Directories toggles control which entry types are shown (default: files only, the historical view).
Expand All @@ -14,43 +16,50 @@ The user has various ways of adding, removing and replacing content in a filenam
- **Date** — add a date (created, last modified, today, or custom) in several formats
- **Counting** — add a running number (start value, zero-padding) in list order

Modifiers are applied in a fixed pipeline order (`Replace → Case → If-Then → Remove → Add → Counting → Date`), each independently toggleable, with an instant per-file preview. Renaming is safe: a duplicate check runs first (blocking warning if any new name would clobber an existing entry — cross-type, so renaming a folder onto a file or vice versa is caught too), followed by a confirmation dialog. Directories are treated as *extension-less* entries: a folder named `backup.tar` renames to `x_backup.tar` (nothing is stripped). The UI is internationalized (German + English) with a runtime language switcher.
Modifiers are applied in a pipeline (`Replace → Case → If-Then → Remove → Add → Counting → Date`) whose order you can also customize by dragging the modifier cards, each modifier independently toggleable, with an instant per-entry preview. Renaming is safe: a duplicate check runs first (blocking warning if any new name would clobber an existing entry — cross-type, so renaming a folder onto a file or vice versa is caught too), followed by a confirmation dialog. Directories are treated as *extension-less* entries: a folder named `backup.tar` renames to `x_backup.tar` (nothing is stripped). The UI is internationalized (German + English) with a runtime language switcher, and offers a light/dark theme toggle (dark is the default).

## Quick start

```sh
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt # fastapi, uvicorn, pywebview, pytest
pip install -r requirements-dev.txt # fastapi, uvicorn, pywebview, pytest (+httpx2 for the API tests)
python run.py # opens a desktop window (or web fallback)
```

(Or `pip install -e .` instead of the requirements files — `pyproject.toml` makes the
project pip-installable and provides the `arenamer` command.)

`run.py` starts the backend and tries to open a native desktop window; in headless environments it falls back to serving the web UI at `http://127.0.0.1:<port>`.

## Development

```sh
# Backend / engine tests (the rename engine is the verified core)
python3 -m pytest tests/ -v
# Both test suites in one command (pytest engine + API, then vitest frontend)
./do test

# Frontend (Svelte + Vite)
# Or individually:
python3 -m pytest tests/ -v # backend / engine (the verified core)
cd frontend && npm install
npm run dev # dev server on :5173, proxies /api -> :8000
npm run build # emits to ../backend/static for the desktop app
npm run test # frontend vitest suite
```

`do` is a small stdlib-only release tool (`do bump` / `do tag` / `do changelog` / `do build` / `do test`), also used by the CI workflows.

## Packaging (distributable desktop app)

The app is bundled with PyInstaller into a self-contained desktop application (no Python required by end users):

```sh
pip install -r requirements-build.txt # pyinstaller
python build/build.py # frontend -> PyInstaller -> versioned archive in dist/
pip install -r requirements-build.txt # pyinstaller (+ cairosvg/pillow for optional icon regen)
python build/build.py # frontend -> icons -> PyInstaller -> versioned archive in dist/
```

| OS | Artifact in `dist/` | Web engine | End-user requirement |
|----|---------------------|------------|----------------------|
| macOS | `A-Renamer.app` + `-macOS.zip` | WKWebView (cocoa) | none; unsigned → right-click → Open past Gatekeeper |
| Windows | `A-Renamer/` + `-win64.zip` | WebView2 (edgechromium) | Edge WebView2 Runtime (preinstalled on Win 10/11) |
| Windows | `A-Renamer/` + `-windows.zip` | WebView2 (edgechromium) | Edge WebView2 Runtime (preinstalled on Win 10/11) |
| Linux | `A-Renamer/` + `-linux.tar.gz` | WebKit2GTK (gtk) | `libwebkit2gtk-4.x` system libraries |

PyInstaller cannot cross-compile, so run the build on each target OS to get that OS's artifact.
Expand All @@ -68,14 +77,19 @@ So a release is simply: branch `release/vX.Y.Z` off `develop` → pull request t
```
arenamer/
├── run.py # one-command launcher (desktop window, or web fallback)
├── do # stdlib-only release tooling (bump/tag/changelog/build/test)
├── pyproject.toml # single source of truth for name + version
├── DESIGN.md # design system: tokens, themes, elevation, state contract
├── changelog.md # per-version sections (written by `do bump`)
├── backend/
│ ├── main.py # FastAPI app: static mount, /api/* routes, pywebview bootstrap
│ ├── api/ # API routes + Pydantic schemas (list/dirs/preview/check/rename)
│ ├── engine/ # PURE rename engine (no web deps) — the correctness core
│ └── static/ # built SPA (gitignored; `npm run build` output)
├── frontend/ # Svelte SPA (Vite): file list, directory tree, modifier panels
├── build/ # PyInstaller packaging (spec + orchestrator)
└── tests/ # pytest suites for engine and API
├── build/ # PyInstaller packaging (spec, orchestrator, icons)
├── tests/ # pytest suites for engine and API
└── .github/workflows/ # ci.yml, release.yml, build.yml
```

See [AGENTS.md](AGENTS.md) for the full feature checklist, behavior decisions vs. the original, and architecture details.
Expand All @@ -88,5 +102,5 @@ This project is licensed under the [GNU General Public License v3](LICENSE) —

---

*This project (v0.1.0) was entirely ported from Qt/C++ by Qwen 3.8 27B running locally, development continues exclusively with the use of local LLMs.*
*Setup: Apple MacBook Pro M5 Pro 48GB with Opencode, LMStudio or oMLX*
*This project (v0.1.0) was entirely ported from Qt/C++ by Qwen 3.8 27B running locally, development continues using local LLMs.*
*Setup: Apple MacBook Pro M5 Pro 48GB with Pi Agent, LMStudio or oMLX*
8 changes: 7 additions & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
<!doctype html>
<html lang="en">
<html lang="en" data-theme="dark">
<head>
<script>
// Pre-paint theme (dark default) so there is no flash on load.
// App.svelte owns the toggle + persistence; this only reads what it saved.
try { document.documentElement.dataset.theme = localStorage.getItem("arenamer.theme") || "dark"; } catch {}
</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>A-Renamer Tool</title>
<meta name="color-scheme" content="dark light" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
Expand Down
Loading
Loading