"""Pure renderers for the game view: everything that turns (scenario, game) into the strings and component updates the player sees. Nothing here creates a Gradio component — gameview.py builds the layout and handlers on top of these. """ import html import gradio as gr from art import avatar_uri, scene_uri from engine import final_verdict, new_game # The end screen carries the verdict as colour, not iconography: the whole recap card # tints gold on a win and crimson on a loss, so how it ended is obvious even scrolled # past the verdict word — and no skull presumes a loss means death. WIN_TINT = ("#7a5800", "rgba(212,160,30,.55)", "rgba(212,160,30,.12)") LOSE_TINT = ("#8b1a1a", "rgba(139,26,26,.55)", "rgba(139,26,26,.10)") # the player-facing verb: the input label, the act button, and the turn status line PLAYER_VERB = "Say" def esc(text): """Neutralise raw HTML in model/player text. The transcript renders with the HTML sanitizer OFF (so our SVG-avatar tags survive), so dynamic text is escaped here instead — markdown (*, _, ---) still works, injected tags don't.""" return html.escape(text or "", quote=False) # A scenario-agnostic cue to act, shown once at the opening (intros set the scene but don't # all end on a hook). The player is "you" here; the model channel never sees this line. # Rendered as a narration beat, so the italics come from the CSS register, not markdown. PROMPT_TO_SPEAK = "The room turns to you. What do you say?" # Inline rewind links on each of the player's past lines. Each turn index has its own hidden # button (`.rtr-regen-N` / `.rtr-edit-N`, wired in gameview.py), so the link just confirms and # clicks that button — no value passes through the frontend. These prefixes are the contract # between the links here and the hidden buttons gameview builds. REGEN_CLS = "rtr-regen" EDIT_CLS = "rtr-edit" def _click_hidden(cls, confirm): """JS for an inline link: confirm, then click THIS game's hidden `.cls` button (scoped to the nearest `.rtr-game`, so the right game answers even with several game tabs in the page).""" return ( f"if(confirm('{confirm}'))" f"{{var t=this.closest('.rtr-game').querySelector('.{cls}');" # elem_classes lands on the