Pabloler21 Claude Fable 5 commited on
Commit
da6f3c4
·
1 Parent(s): 23a340b

feat: render build mode with looped heartbeat + red pulse

Browse files

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Files changed (2) hide show
  1. render.py +22 -2
  2. tests/test_render.py +13 -0
render.py CHANGED
@@ -2,7 +2,8 @@ import base64
2
  import html
3
  from pathlib import Path
4
 
5
- from sting import chime_wav_bytes, sting_wav_bytes
 
6
 
7
  _PLACEHOLDER = "...nothing yet. tell me something true."
8
 
@@ -44,6 +45,9 @@ _IMG = {name: _load_image(name)
44
 
45
  _STING = base64.b64encode(sting_wav_bytes()).decode()
46
  _CHIMES = [base64.b64encode(chime_wav_bytes(i)).decode() for i in range(3)]
 
 
 
47
 
48
 
49
  def _src(name: str) -> str:
@@ -79,7 +83,23 @@ def render_entity(affinity: int, mode: str = "idle", seq: int = 0) -> str:
79
 
80
  ghost = ""
81
  tint = ""
82
- if mode == "rage":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  figure = (
84
  '<div class="entity-figure">'
85
  f'<img class="entity-img entity-rage" src="{_src("rage")}" '
 
2
  import html
3
  from pathlib import Path
4
 
5
+ from sting import (chime_wav_bytes, flatline_wav_bytes, heartbeat_wav_bytes,
6
+ sigh_wav_bytes, sting_wav_bytes)
7
 
8
  _PLACEHOLDER = "...nothing yet. tell me something true."
9
 
 
45
 
46
  _STING = base64.b64encode(sting_wav_bytes()).decode()
47
  _CHIMES = [base64.b64encode(chime_wav_bytes(i)).decode() for i in range(3)]
48
+ _HEARTBEAT = base64.b64encode(heartbeat_wav_bytes()).decode()
49
+ _SIGH = base64.b64encode(sigh_wav_bytes()).decode()
50
+ _FLATLINE = base64.b64encode(flatline_wav_bytes()).decode()
51
 
52
 
53
  def _src(name: str) -> str:
 
83
 
84
  ghost = ""
85
  tint = ""
86
+ if mode == "build":
87
+ # finale suspense bed: the child stays the materialized smudge while a
88
+ # heartbeat loops and a red pulse throbs. The HTML is byte-identical
89
+ # across recital steps, so Gradio doesn't remount it and the looped
90
+ # <audio> plays continuously (no JS) until the climax swaps the mode.
91
+ figure = (
92
+ '<div class="entity-figure">'
93
+ f'<img class="entity-img" src="{_src("base")}" style="{look}">'
94
+ "</div>"
95
+ )
96
+ flash = ""
97
+ tint = '<div class="entity-redpulse"></div>'
98
+ ghost = (
99
+ f'<audio autoplay loop src="data:audio/wav;base64,{_HEARTBEAT}">'
100
+ "</audio>"
101
+ )
102
+ elif mode == "rage":
103
  figure = (
104
  '<div class="entity-figure">'
105
  f'<img class="entity-img entity-rage" src="{_src("rage")}" '
tests/test_render.py CHANGED
@@ -255,3 +255,16 @@ class TestReplyChime:
255
  def test_frenzy_keeps_only_the_sting(self):
256
  out = render_entity(12, "frenzy")
257
  assert out.count("<audio autoplay") == 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  def test_frenzy_keeps_only_the_sting(self):
256
  out = render_entity(12, "frenzy")
257
  assert out.count("<audio autoplay") == 1
258
+
259
+
260
+ class TestBuildMode:
261
+ def test_build_loops_the_heartbeat_and_not_the_chime(self):
262
+ html = render_entity(50, "build", seq=4)
263
+ assert "<audio autoplay loop" in html # heartbeat bed loops
264
+ assert "entity-redpulse" in html # red suspense pulse
265
+ # the per-turn music-box chime must NOT play during a finale build
266
+ assert html.count("<audio") == 1
267
+
268
+ def test_build_keeps_the_child_visible(self):
269
+ html = render_entity(50, "build", seq=4)
270
+ assert "entity-img" in html