Python sandbox
- try/except/else/finally, raise (incl. re-raise), assert, for/while ... else
- Multi-generator and dict comprehensions
- Builtin exception hierarchy; user exceptions subclass it
- Inside try, missing keys/indexes, division by zero, attribute access on None
and unknown names raise Python exceptions; lookups stay lenient elsewhere
- int()/float() raise ValueError on non-numeric strings
ATL
- converter/atl.py: set/move (linear, ease, easein, easeout), pause, repeat,
parallel, choice, block, on show/hide and named transforms become a timeline
shipped as game.json "atl"; Move() becomes a linear slide
- player/atl.ts: timeline runner wired into the renderer; `on hide` plays
before the picture is removed
- function, time, contains, matrixcolor and expressions still block the import
Tests: exceptions/ATL fixtures and runtime tests, converter ATL tests; README
and ARCHITECTURE updated.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Converter: def/return/global/nonlocal, class (inheritance, property,
staticmethod, classmethod), lambda, *args/**kwargs and call-site unpacking.
Unsupported dunder methods, generators and unknown decorators are still refused
- Player: closures, super(), instances that survive save/load, isinstance,
hasattr/getattr/setattr, map/filter/sorted(key=), recursion limit
- audit_ir accepts calls to names, classes and methods the game defines itself;
unknown renpy.* calls still block the import. Volume/pan/pause/replay calls
are accepted as presentation-only no-ops; build.* settings are skipped
- Tests: IR fixture compiled from tests/fixtures/classes.py (tests/make_ir.py)
and run in the interpreter; converter tests updated
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add immutable revisions, PGVN editor, asset replacement, merge and rebase workflows, authenticated media delivery and reachability GC. Harden the script interpreter, pin unrpyc and remove tracked build metadata. Include current compatibility checks, Naninovel import and regression tests.
- games.main_version_id, version visibility (draft/published) and kind
(original/translation/mod), version_history; migration 0002
- permissions translations.edit and versions.publish
- "Translate" creates a translation draft; publishing merges the changed
languages into the main version in place (players keep their saves), the
previous manifest goes to the history and can be restored
- translation API: overview with per-state stats, entries with filters,
search and scene context, save, glossary, locale font, PO/CSV export/import
- translation editor page: languages and progress, lines and strings,
filters, autosave, approve/outdated/fuzzy actions, glossary highlighting,
"play from here" with the scene restored
- player: ?lang= and ?at= from the editor, per-locale font
(models.py and permissions.py for this landed earlier in 9ee3e1f)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tokens and components in styles.css, shared Ui (dialogs, toasts), icons,
bottom tab bar on mobile, keyboard/gamepad navigation, library shelf with
filters and conversion badges, hero game page, restyled player screens.
Games list now returns per-game version counts. Adds docs/UI_DESIGN.md.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- every script node gets a permanent id, say nodes a Ren'Py-style tid
- all languages from the game's tl/ are imported at conversion: dialogue blocks
matched by their source-location comment or by label + original text,
`translate strings` become a per-language strings table
- source language from config.language or detected from the dialogue
- manifest stores locales/<lang>/<module>.json and strings.json; game.json
lists sourceLang and available locales with progress
- player: language picker in main menu and settings, fallback to source,
__(), [var!t] and _preferences.language
- parser: `default` with init priority, and one bad statement no longer drops
the whole file
- docs/LOCALIZATION.md: design for translations, publishing and mods
Line endings of several converter/player files go back to LF: an earlier
commit accidentally stored them as CRLF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Checked against six real games (Ren'Py 7.5 and 8.x):
- nested labels and named menus become jump anchors inside label bodies
- call with arguments and labels with parameters (dynamically scoped)
- frame-by-frame ATL images, Movie frames, layeredimage, camera, Shake,
custom audio channels via register_channel, show text with expressions
- generator expressions in Python, no SyntaxWarning noise from decompiled code
Also:
- app.import_dir converts an unpacked game folder without changing it
- app.converter.analyze dry-runs the script part of the converter
- imports use their own "importing" status so a worker restart can't hijack them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FastAPI backend: admin bootstrap with forced credential change, admin-created
users with one-time generated passwords, roles as permission sets
- Games and versions with content-addressed blob storage; forks copy only the
manifest, so mods share assets with the original
- Worker: archive/RPA extraction, unrpyc for .rpyc, own .rpy parser, Python
subset translated via ast, media converted to WebP/Opus/WebM
- React panel (library search, game page, upload, users, roles) and a
framework-free web player with saves, history, skip/auto, transitions
- docker compose: postgres, backend, worker, nginx on a single HTTP port
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>