Find a file
Tony Frazier 76082458b7
Clean the Style Reviewer's input of narration markup (#560)
## Summary

The Style Reviewer judges prose habits, and its findings are injected
into the narrator's system prompt as `<style_guidance>`. Given raw
entries, it also judged the markup (`<pic>` prompts, Visual Prose HTML,
`### Time | Place` headers, `---` rules), so a finding about markup
became an instruction to the narrator.

This adds one configurable narration cleaner, puts the reviewer and the
classifier on it, and adds a **Clean Input** switch for the reviewer.
The reviewer now reads plain prose, and the classifier still reads the
header text that carries the time and place.

The HTML stage parses with the DOM. The narrator's markup renders as
real HTML (`marked` passes well-formed tags through raw; Visual Prose
goes through `template.innerHTML`), so the cleaner reproduces what the
reader sees: `<Kael>` is an invisible unknown element on screen and
vanishes in the cleaned text too, and entities decode as the browser
decodes them. A DOM parse also handles tag case, hyphenated names,
unclosed tags and entities without a hand-kept tag list.

Design, per-option behaviour and the experiment behind it:
`docs/architecture/narration-cleaning.md`.

## Changes

- **`utils/narrationClean.ts`** (new): per-markup modes (`keep` /
`unwrap` / `remove`) and presets `CLEAN_NONE`, `CLEAN_FOR_REVIEW`,
`CLEAN_FOR_CLASSIFICATION`.
- The HTML stage parses with `template.innerHTML` (inert: scripts don't
run and nothing loads) and walks the tree. Unknown and custom elements
are unwrapped, `<style>`, `<script>` and comments are dropped, `<em>` /
`<strong>` become `*` / `**`, block ends break lines, and table cells
are separated by a space.
- A `<` that does not begin a well-formed tag is escaped before the
parse, copying `marked`'s inline `tag` rule, so `3 < 5`, `x<y and then…`
and `<https://example.com>` stay text. A test runs a table of inputs
through `marked`'s `Lexer.lexInline` to keep the two in agreement across
upgrades.
- `<pic>` tags (`<pic … />` and `<pic …></pic>`) are removed by a regex
stage that runs first.
- **Style Reviewer**: `buildReviewPassages` cleans each entry, skips one
that cleans to nothing, and numbers the rest. **Clean Input** (Advanced
Settings, `styleReviewer.cleanInput`, default on) switches to
`CLEAN_NONE` through the same path.
- **Classifier**: moved onto `CLEAN_FOR_CLASSIFICATION`, which unwraps
heading and bold-line text for the time and place fields.
`stripNarratorMarkup` is removed.
- **New devDependency: `jsdom`.** The default test environment stays
`node`; a file that reaches the HTML stage opts in with `//
@vitest-environment jsdom` (`narrationClean.test.ts`,
`styleReviewPassages.test.ts`). `devEngines` moves to `^24.15.0`, the
floor jsdom 30 needs; `.nvmrc` resolves to the latest 24.x.
- CRLF blank lines collapse in the final pass.
- Docs: `narration-cleaning.md` (new), `testing.md`, the docs index and
the `CLAUDE.md` table.

## Behaviour to know about

- The HTML stage needs a `document` when the text contains `<` or `&`.
The app always has one (SPA, `ssr = false`); under `node` the stage
throws a `ReferenceError`.
- In Visual Prose the browser parses the raw content and is stricter
than `marked`, so a malformed tag there hides text on screen while the
cleaner keeps it. The Visual Prose prompt asks for valid HTML, and the
cleaner shows the reviewer the extra text.
- `pic: 'keep'` combined with `html: 'unwrap'` drops the tag as an
unknown element and keeps the prose around it.
- A whole-line bold speaker or name label (`**Kael**`) is removed as a
heading under `boldLines: 'remove'`; **Clean Input** switches that off.
- jsdom stands in for the WebView's parser in tests, and the manual
check below covers the real WebView.

## Test plan

- [x] `check`, `test` and `lint:fix` pass (2012 tests)
- [ ] In `tauri dev`, with Visual Prose on and inline images on, trigger
a style review and check the dev log: the rendered `passages` variable
is plain prose, and the prose after a `<pic>` tag is kept
- [ ] A narration with a literal `<Kael>` reads the same to the reviewer
as it does in the story view
- [ ] The classifier fills time and place from a `### Time | Place`
header and from a Visual Prose passage
- [ ] The **Clean Input** switch turns cleaning off

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01YHkuCEJRUTBnsZaUnhfR9N


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added a “Clean Input” setting for Style Reviewer, enabled by default.
When on, it cleans narration before review; when off, original text is
retained.
* Improved narration cleanup for reviews and classification, including
handling of markup, images, and layout.
* **Chores**
  * Updated the minimum required Node.js version.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:01:03 -04:00
.cargo Add Incognito Keyboard option for Android (#467) 2026-08-26 20:45:30 -04:00
.github Add iOS as a build target (#541) 2026-09-27 18:27:49 -04:00
docs Clean the Style Reviewer's input of narration markup (#560) 2026-10-01 15:01:03 -04:00
scripts Route dev/build/release scripts through aube when it installed node_modules (#552) 2026-09-28 14:34:27 -04:00
src Clean the Style Reviewer's input of narration markup (#560) 2026-10-01 15:01:03 -04:00
src-tauri Route dev/build/release scripts through aube when it installed node_modules (#552) 2026-09-28 14:34:27 -04:00
static switched font for logo 2026-01-19 14:48:22 -06:00
third-party-licenses Added harper 2026-01-02 05:08:39 -07:00
.gitattributes Fix CRLF Migrations (#162) 2026-02-22 15:46:59 -05:00
.gitignore Overhaul CI/CD: Node 24, shared release workflows, faster cached builds (#518) 2026-09-16 22:38:32 -04:00
.nvmrc Overhaul CI/CD: Node 24, shared release workflows, faster cached builds (#518) 2026-09-16 22:38:32 -04:00
.prettierrc fix: prettier, eslint, pipeline (#71) 2026-02-06 00:49:36 +01:00
CLAUDE.md Clean the Style Reviewer's input of narration markup (#560) 2026-10-01 15:01:03 -04:00
compileApk.sh Android build hygiene: minSdk 26, lint in CI, resource cleanup (#468) 2026-08-29 21:52:01 -04:00
components.json fix: prettier, eslint, pipeline (#71) 2026-02-06 00:49:36 +01:00
eslint.config.js Validated release script, extracted version arithmetic, lint config fixes (#416) 2026-08-08 19:55:23 +02:00
lefthook.yml Route dev/build/release scripts through aube when it installed node_modules (#552) 2026-09-28 14:34:27 -04:00
LICENSE.md Db rework (#140) (#141) 2026-02-13 17:37:27 +05:30
logo.png switched font for logo 2026-01-19 14:48:22 -06:00
package-lock.json Clean the Style Reviewer's input of narration markup (#560) 2026-10-01 15:01:03 -04:00
package.json Clean the Style Reviewer's input of narration markup (#560) 2026-10-01 15:01:03 -04:00
README.md Add iOS as a build target (#541) 2026-09-27 18:27:49 -04:00
sun4.jpg Use custom icon for android 2026-01-03 07:08:45 -07:00
sun17.jpg Use custom icon for android 2026-01-03 07:08:45 -07:00
sun2022.png Use custom icon for android 2026-01-03 07:08:45 -07:00
svelte.config.js fix: prettier, eslint, pipeline (#71) 2026-02-06 00:49:36 +01:00
tsconfig.json Initial commit 2025-12-30 16:08:22 -07:00
vite.config.js fix: prettier, eslint, pipeline (#71) 2026-02-06 00:49:36 +01:00
vitest.config.ts Validated release script, extracted version arithmetic, lint config fixes (#416) 2026-08-08 19:55:23 +02:00

Aventuras

Interactive fiction with an AI that remembers. Play a text adventure or write collaboratively, on desktop and Android, with any LLM provider you like — cloud or running on your own machine.

Aventuras keeps the story straight: chapters are summarized as you play, a lorebook feeds the right entries into the right scene, an agent tidies that lorebook behind you, and the world state — who is present, where you are, what you carry — is tracked turn by turn.

Highlights

  • Two modes — Adventure, with multiple-choice actions and world tracking; Creative Writing, freeform with AI suggestions. First/second/third person, past or present tense.
  • Any provider — OpenRouter, OpenAI, Anthropic, Google, xAI, Groq, DeepSeek, Mistral, Z.AI, NanoGPT, Chutes, Pollinations, NVIDIA NIM, plus local llama.cpp, LM Studio and Ollama. Streaming, extended thinking, and per-subsystem Agent Profiles so retrieval, classification and narration can each use a different model.
  • Memory that scales — automatic chapter summarization, and a retrieval step that goes and finds what the current scene needs from everything that came before.
  • Lorebook — characters, locations, items, factions, concepts and events, with aliases, secrets, keyword and relevance-based injection, SillyTavern import, and an autonomous agent that consolidates duplicates and fills gaps.
  • The Vault — a cross-story library of reusable characters, lorebooks and scenarios, with a tool-calling assistant that edits them and shows you a diff before anything lands.
  • Every prompt is editable — they are Liquid templates, all of them, in an in-app editor.
  • Images — inline <pic> illustrations, scene backgrounds and character portraits, across nine backends including local ComfyUI and A1111.
  • Writing tools — local grammar checking (Harper/WASM), style analysis for repetition, suggestions that match how you write.
  • Yours to keep — SQLite on your machine, full backup/restore, .avt story export, LAN sync between devices over QR pairing, and an optional translation layer over the whole app.
  • 26 themes, adjustable text size, dialogue highlighting.

Install

Pre-built binaries are on the Releases page:

Platform Download
Windows Aventuras_vX.Y.Z_x64-setup.exe
macOS Aventuras_vX.Y.Z_x64.dmg / _aarch64.dmg
Linux Aventuras_vX.Y.Z_amd64.deb / .AppImage, or _x86_64.rpm
Android Aventuras_vX.Y.Z.apk
iOS Aventuras_vX.Y.Z_ios-arm64-unsigned.ipa (sideload; unsigned)

No API keys in config files — providers are set up in the app, under Settings → API Settings.

Build from source

Requires the Node.js version in .nvmrc, the latest stable Rust, (for Android) the Android SDK, NDK r27d and JDK 21–24, and (for iOS) a Mac with Xcode.

git clone https://github.com/AventurasTeam/Aventuras.git
cd Aventuras
npm install
npx tauri dev
Script Does
npm run dev Vite dev server (frontend only)
npx tauri dev full app, with hot reload
npm run build production build
npm run check type checking (svelte-check)
npm test test suite (Vitest)
npm run lint ESLint — lint:fix to fix
npm run format Prettier
npm run release version bump, tag and push

Desktop builds are npx tauri build; Android and iOS are documented in docs/development/release.md. iOS produces an unsigned device .ipa (no Apple certificates or signing secrets — see docs/development/ios-build-target.md for the full account of how the target was added and how the unsigned CI pipeline works).

Tech stack

TypeScript (strict) · SvelteKit 2 · Svelte 5 runes · Tauri 2 (Rust) · Tailwind + shadcn-svelte · SQLite (tauri-plugin-sql / sqlx) · Vercel AI SDK · LiquidJS · CodeMirror 6 · Zod · Harper.js (WASM) · Vitest

Contributing

Start with docs/ — the architecture is documented per area, and CLAUDE.md has the conventions. CI runs lint, type-check, tests and a build on every PR against master, develop or dev.

Acknowledgments

Tauri · SvelteKit · OpenRouter · Harper · Lucide

License

AGPL-3.0