Commit Graph
34 Commits
Author SHA1 Message Date
mrleo1nidandClaude Sonnet 5.5 cc71050362 feat: _() strings and screen texts become translatable strings; search looks in all states; stutter hint
CI / frontend (push) Failing after 14s
CI / backend (push) Failing after 44s
show text _("...") and literal screen texts are listed in Names and menu; typing in search switches the filter from To do to All; the translator is told how stutters are spelled.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 21:09:55 +03:00
mrleo1nidandClaude Sonnet 5.5 262db2687e feat: lines with style tags inside words are translated without them and marked fuzzy
CI / frontend (push) Failing after 24s
CI / backend (push) Failing after 45s
Decorative Ren'Py tags cannot survive translation mid-word; retry without them when the translator rejects the answer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 20:57:46 +03:00
mrleo1nidandClaude Sonnet 5.5 fb03204726 feat: machine translation saves lines as they finish and shows progress at once
CI / frontend (push) Failing after 15s
CI / backend (push) Failing after 44s
Total shown right away, lines saved per poll, retry reasons in the card; layout fix of the options row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 20:50:51 +03:00
mrleo1nidandClaude Sonnet 5.5 68b66a1d94 feat: machine translation resumes after a page reload
CI / frontend (push) Failing after 14s
CI / backend (push) Failing after 49s
The run is recorded at every page and picked up from the translator's finished job; one run across tabs via Web Locks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 20:39:37 +03:00
mrleo1nidandClaude Sonnet 5.5 7fa88e4025 feat: language picker with flags, paging and follow mode, translation run in the floating window
CI / frontend (push) Failing after 27s
CI / backend (push) Failing after 49s
Language picker with search and SVG flags, real pagination with locate, one global machine translation run shown next to uploads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 19:24:20 +03:00
mrleo1nidandClaude Sonnet 5.5 5f6bd4508e feat: per-line translate buttons, auto-translate follows the visible list and fills rows live
CI / frontend (push) Failing after 21s
CI / backend (push) Successful in 47s
Shared translator session, game description/genres as optional context, live header progress.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 19:11:27 +03:00
mrleo1nidandClaude Sonnet 5.5 e8d6bf4646 feat: machine translation through a local translator running in the browser's machine
CI / frontend (push) Failing after 17s
CI / backend (push) Successful in 47s
Translator panel in the translation editor and a /translator setup page. The browser talks to
local-translator directly; the server only receives lines marked as machine.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 18:59:27 +03:00
mrleo1nidandClaude Sonnet 5.5 a1f5ef8e2d feat: language dropdowns on versions and on the game page
CI / frontend (push) Failing after 20s
CI / backend (push) Successful in 51s
- Each ready version gets a "Languages" dropdown next to Play: the original language
  first, then translations with their completion; picking one starts the version in
  that language
- The game header gets the same dropdown, aggregated over published, ready versions.
  Each language points at the version that has it best (most complete, the main
  version on a tie); an original language counts as complete
- The old row of language tags linking to the translation editor is replaced
- Backend: versions carry source_name (the name of the original language)
- pages/languages.ts holds the aggregation logic with tests; LanguageMenu closes on
  outside click and Escape

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 18:41:26 +03:00
mrleo1nidandClaude Sonnet 5.5 a93f33c93d feat: shared volume settings with a per-game override
CI / frontend (push) Successful in 41s
CI / backend (push) Successful in 1m41s
- Music, sound and voice volumes are shared by all games by default; a game that
  has never been launched with its own volumes plays with the shared ones
- The sound section of the player settings has a "own volume for this game" switch:
  turning it on starts from the current values and then the sliders change only this
  game, turning it off removes the override and returns to the shared volumes
- The override belongs to the game (game_id from /api/versions/{id}), so forks and mods
  share it; it falls back to the version id when the id is unavailable
- Reading speed and auto delay stay shared
- volume-store.ts keeps the storage logic (damaged data ignored, values clamped,
  unavailable storage tolerated) and has its own tests

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 18:27:39 +03:00
mrleo1nidandClaude Sonnet 5.5 52f6e7c18f feat: full math module and small renpy helpers
CI / frontend (push) Successful in 34s
CI / backend (push) Successful in 57s
- math: sin/cos/tan/asin/acos/atan/atan2, exp, log, log10, fabs, fmod, modf, trunc,
  hypot, degrees, radians, isnan, isinf, copysign, e
- renpy.curry, get_showing_tags, run(action), pop_call; _preferences.get_mute/get_volume
- The compatibility check knows these names

Removes a few more blockers in TheFixer and That_New_Teacher; the rest are custom
systems of those games (AlphaMask wardrobe, custom statements) and authoring defects.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 14:47:34 +03:00
mrleo1nidandClaude Sonnet 5.5 3ac4f0371f feat: eval/exec of strings, gallery Replay, callable actions; iNSight and SomethingBetter now pass
- pyparse.ts: parser for expressions and simple statements built at run time, producing
  the sandbox's own IR; backs eval() and exec() (e.g. exec("persistent." + var + " = 0"))
- Replay/EndReplay and renpy.end_replay: the engine stashes the full game state, runs the
  label with the given scope and restores everything when it ends; locked replays are
  insensitive. Save loading shared as applySave()
- Screen actions are callable (MainMenu(confirm=False)())
- jump main_menu_screen is Ren'Py's own label: it opens the player's main menu
- Screens: a closing quote ends the word before a letter ("text"size 15) but not before
  an operator ("a"+b); Text(...) pictures in image blocks
- README: current capabilities and limits of the Ren'Py support

Strict compatibility check on the local collection: Tomie, Twisted-Thoughts, Hooked,
iNSight-of-you and SomethingBetter pass; TheFixer (custom AlphaMask wardrobe, missing
labels) and That_New_Teacher (custom statements, Render API) do not.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 14:40:36 +03:00
mrleo1nidandClaude Sonnet 5.5 3923ee72fb feat: generator interpreter for Python that waits for the player (Ren'Py)
Python containing renpy.input, narrator("..."), character calls, renpy.say,
renpy.pause, renpy.display_menu, renpy.with_statement or renpy.call_screen now
runs through loops, try, functions, methods and expressions.

Player
- python.ts: execG/evalG/callG run only the code that may wait (mayBlock);
  everything else stays on the synchronous fast path. A waiting call returns a
  BlockingCall; outside the generator interpreter it is an explicit error. The
  interpreter scope is set aside while the player decides
- engine: drives the generator and answers requests (dialogue, text input with
  length/allow/exclude, pause, choice menu, transition, call screen)
- renpy.show/hide/scene, transitions and transforms as Python values,
  Character(...) objects, Borders, set_tag_attributes

Converter
- blocking_names() lists game functions that may wait, transitively, and ships
  them as game.json "blocking"; character calls are allowed
- hide screen of an unknown screen is harmless; images defined by a parenthesised
  expression are supported

Hooked now passes the strict compatibility check (with Tomie and Twisted-Thoughts).
Tests: waiting.py fixture run through the generator interpreter; blocking-name analysis.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 14:22:38 +03:00
mrleo1nidandClaude Sonnet 5.5 ea03cebf6a feat: ATL pictures/contains/function/time, colour matrices and more Python for Ren'Py
ATL
- Image blocks become timelines: picture steps with dissolve, parallel, contains,
  finite repeat, time, function f(trans, st, at), calls of other transforms with
  arguments, on hover/idle events for buttons. Animations stop when their element
  leaves the page
- Pictures defined by an expression (get_face_path("base")) are drawn at show time
- Purely graphical hints (shader, mesh, gl_*, u_*) are accepted and dropped

Colour
- matrixcolor and im.matrix.* as 5x5 matrices combined with `*`, applied through SVG
  filters; im.MatrixColor/Scale/Crop/Blur/Composite and Text/Image as values

Python
- match statement, re/copy/time/datetime/defaultdict/functools.partial, more
  str/dict methods, Character(...) created by code
- narrator("..."), Character calls, renpy.say and renpy.show/hide with expressions in
  Python blocks become script nodes
- Audit follows reachability: functions and classes nothing refers to (custom
  statement lexers, unused tools) no longer block an import

Docs: README and ARCHITECTURE now describe a multi-engine library (GameMaker and
QSP planned). Tests for the new runtime pieces.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 14:05:14 +03:00
mrleo1nidandClaude Sonnet 5.5 62ece25902 feat: Ren'Py screens (screen/call screen/show screen) with actions and styles
Converter
- screens.py parses the screen language: containers (fixed, frame, vbox, hbox,
  grid, vpgrid, viewport, button, has), widgets (text, textbutton, imagebutton,
  add, bar ...), if/for/use/transclude, default, python, key, timer, on, inline ATL,
  conditional properties, and decompiled forms (unparenthesised operators,
  glued property values, comma separated actions)
- style statements, call/show/hide screen operators and Python-level
  renpy.show_screen are translated; only screens the game shows are audited,
  unused stock screens no longer block an import; undefined screens do
- ATL values may be computed at run time and transforms take parameters
  (at blink(0.2, 0.3)); blur property

Player
- screens.ts builds the DOM from the IR and rebuilds it on state changes;
  buttons with idle/hover/selected/insensitive states, tooltips, sounds, modal
  screens, timers and keys that survive re-rendering, saved with the save file
- Actions: Jump, Call, Return, SetVariable, Toggle*, SetScreenVariable, Show, Hide,
  ToggleScreen, Function, Play, Notify, If, Language, ShowMenu, OpenURL ...
- Crop/LiveCrop/Solid/Frame/Transform/DynamicDisplayable values, crop property,
  globals(), renpy.variant/change_language/seen_image, transition stubs

Tests: screen parser and runtime (linkedom DOM), screens fixture compiled from
tests/fixtures/screens.rpy. Docs: docs/SCREENS.md.

Twisted-Thoughts now passes the strict compatibility check.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 13:17:11 +03:00
mrleo1nidandClaude Sonnet 5.5 47fc121e7d feat: local browser saves with opt-in cloud sync behind saves.cloud permission
CI / frontend (push) Successful in 21s
CI / backend (push) Successful in 47s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 12:44:42 +03:00
mrleo1nidandClaude Sonnet 5.5 4418da47eb feat: report ATL problems only for used transforms; animate moving images
CI / frontend (push) Successful in 23s
CI / backend (push) Successful in 1m55s
- Unsupported ATL constructs in a transform now block the import only when the
  transform is used (at clause, another transform, even before its definition).
  Stock screens.rpy transforms for unused screens no longer fail games
- image blocks with one picture plus linear/ease/parallel/repeat keep their
  animation (image-level ATL); the renderer also starts it
- Python: skip @gui.variant/@renpy.variant functions (inactive variants), treat
  @renpy.pure as transparent, read renpy.store.x as x
- Accept renpy.transition, end_replay, block_rollback and music set_pause/set_pan
  as presentation-only no-ops, and implement them in the player (set_pause and
  set_pan were allowed by the converter but missing at runtime)
- Skip the 0x52-URM add-on directory (in-game console / save editor) when
  parsing scripts and media
- Tests for lazy ATL warnings, moving images and variants

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 12:20:39 +03:00
mrleo1nidandClaude Sonnet 5.5 ff82d882c0 feat: add ATL animation timelines and try/raise to the Ren'Py player
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>
2026-10-25 12:11:07 +03:00
mrleo1nidandClaude Sonnet 5.5 962595c925 feat: support user-defined functions and classes in Ren'Py python
- 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>
2026-10-25 12:01:09 +03:00
mrleo1nidandClaude Sonnet 5.5 12ec82c7e6 refactor: move game-specific import logic into profiles; unblock Tomie import
- Add app/games: GameProfile base, registry, ReaderHooks and renpy/naninovel/
  tomie profiles. pipeline, preflight, detect and mods dispatch via the registry
- Move tomie_* converter modules to app/games/tomie and split out reader hooks,
  import, mod validation and known defects
- Tolerate the 31 authoring defects of the pinned Tomie build as report
  warnings (exact list, per-script counts); any new defect still blocks import
- Frontend: runtime registry (runtimes.ts) with lazy Tomie launcher
- Add profile/registry tests and docs/GAME_PROFILES.md; update IMPORT.md

Full media export of the original Tomie build is not yet verified.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 11:35:23 +03:00
mrleo1nidandClaude Sonnet 5.5 bbbcc6a5d8 chore: add CI, lint config, login rate limit and split large modules
- Rate-limit failed logins (8 per 15 min per IP or username, 429 + Retry-After)
- Add ruff/mypy config and GitHub Actions workflow (ruff, pytest, tsc, tests, build)
- Apply ruff autofixes (import order, pyupgrade)
- Extract rpy_helpers.py, engine-util.ts, TranslationParts.tsx and
  translation-types.ts from oversized modules
- Add dependency-free React UI tests
- Refresh README and ARCHITECTURE (Naninovel/Tomie, status and plans)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 11:23:22 +03:00
mrleo1nid 57a4edcbfd feat: add native Tomie adapter and strict import compatibility checks 2026-10-25 11:02:52 +03:00
mrleo1nid c8d83b3fe7 feat: add mod editing, revision merges and secure game storage
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.
2026-10-25 04:42:48 +03:00
mrleo1nid 06ff19ac4a fix: apply audio volume during fades and redesign player settings 2026-10-25 04:31:29 +03:00
mrleo1nid fa82c99a52 fix: preserve upload progress across page navigation 2026-10-25 04:13:11 +03:00
mrleo1nidandClaude Sonnet 5.5 ae8e175376 Pull title, description and tags from an F95zone thread
Admin/uploader pastes a thread link and presses "Подтянуть" on the new
game and edit forms; the backend reads the public first post (no login).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 03:57:28 +03:00
mrleo1nidandClaude Opus 5.5 ac164d1782 Localization stages 2-3: drafts, publishing, translation editor
- 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>
2026-10-25 03:56:19 +03:00
mrleo1nidandClaude Sonnet 5.5 4e047f3aa0 UI: wrap long game titles, shorten library search placeholder
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 03:48:57 +03:00
mrleo1nidandClaude Sonnet 5.5 9ee3e1ff6e UI: unified design system, light/dark themes, redesigned pages and player UI
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>
2026-10-25 03:43:53 +03:00
mrleo1nidandClaude Opus 5.5 51a7e77e56 Add .gitattributes: LF in the repository, CRLF for batch files
Normalize the files that had been committed with CRLF (line endings only,
no content changes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 03:41:24 +03:00
mrleo1nidandClaude Opus 5.5 e1d7b29f77 Localization stage 1: stable IDs, import of tl/ translations, language picker
- 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>
2026-10-25 03:39:40 +03:00
mrleo1nidandClaude Opus 5.5 5d9ddb6376 Upload: clearer proxy 413 error, clean up after interrupted uploads
- Show a readable message when a reverse proxy rejects the archive with 413
- Delete the freshly created game if its archive upload fails
- Turn ClientDisconnect during upload into a short 400 instead of a traceback

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 03:30:43 +03:00
mrleo1nidandClaude Opus 5.5 a97b46079e Converter: cover real Ren'Py games better, add folder import and dry run
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>
2026-10-25 03:23:20 +03:00
mrleo1nidandClaude Opus 5.5 35033fb86c Add start-test.bat for an isolated Docker test stand
Runs a separate compose project (pgames-test) on port 8090 with its own
.env.test and data-test/, starts Docker Desktop if needed and generates a
sample game to upload. env_file in compose is now overridable via ENV_FILE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 02:44:50 +03:00
mrleo1nidandClaude Opus 5.5 364b312064 Initial MVP: local visual novel library with Ren'Py → PGVN converter
- 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>
2026-10-25 02:41:32 +03:00