- pagerite/segments.py replaces masking.py: a fragment is parsed with the
project's own markdown-it (markdown.make_md(verbatim=True), byte-identical
tokens) and split into pure-prose segments with source spans; only the
segments plus per-segment context surrounds cross the wire (Job.texts /
contexts / Result.texts) and translations splice back by offset — markup
can no longer break, it never leaves the server. Count/empty/non-prose
results are rejected and skipped for the run.
- Localization machinery out of app.py: the translator dispatcher (clients,
job pipeline, validation skip-list) moves into translate.Dispatcher;
translated-edit recording moves into i18n (add_patch, set_title_translation,
clear_translations). app.py keeps only the routes.
- Structure editor localized: flag strip switches the language titles are
shown/edited in (GET /_api/pages?lang= flags translated rows, originals
dimmed); retitling in a translation writes a per-language title fragment
via StructureOp.lang — slugs, order and hierarchy stay language-independent.
- Localization tab: refresh-all button (DELETE /_api/translations) drops
machine translations, keeps user patches and clears the skip-list so the
dispatcher re-translates everything.
- PageEditor always opens in the primary language; editor socket gets
reconnect/doc-mismatch logging. Reference translator: per-segment calls
with context prompts, deterministic punctuation matching and the "<"
markup-bleed cut.
- pagerite/masking.py: technical spans (code, URLs, {placeholders}, attrs,
footnote/link labels, container names, HTML tags) become numbered sentinels
for the LLM round trip; results are restored by number and rejected when a
sentinel is mangled (skipped for the rest of the run, stays pending).
Chunks with no prose left after masking are never dispatched.
- Data.translate_key -> translate_keys dict (key -> name); the first key is
generated at bootstrap, result transactions record the key as user=, and
startup logs the service URL(s) via translate.log_service_urls.
- Fix /_translate proxying through the Vite dev server (missing slash).
Uses Playwright to drive Chromium through real internal link clicks,
so pagerite.js analytics pings create normal visits. Also fires HTTP
GETs with crawler user-agents to record crawler hits.
Features:
- script-local deps via uv add --script (playwright, httpx)
- rotating pool of real public IPs via X-Forwarded-For
- Poisson inter-arrival delays between sessions/hits
- configurable browsers, crawlers, clicks, and dwell time
- Move API routes from /_/api/ to /_api/
- Move uploaded files from /_/f/ to /_f/
- Move built assets from /_/assets/ to /_assets/
- Move admin shell from /_/admin to /_admin
- Update Vite proxy/build config, frontend fetches/WebSockets, devserver health URL, seed content, and docs
- Fix README database name to pagerite.kantadb
FastAPI backend rendering HTML with html5tagger, content persisted in a
kanta database and rendered per request. Vue only for the editing tools
(page editor over a WebSocket, site/structure editor); public pages are
plain HTML with fetch navigation. No auth: single trusted author.