TwoQuarks commited on
Commit
3a7623c
·
verified ·
1 Parent(s): c5e42d3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -72
app.py CHANGED
@@ -21,92 +21,112 @@ STRANGE_RESULTS = STRANGE_ROOT / "results"
21
 
22
  STYLE_PATH = ROOT / "style.css"
23
 
24
-
25
  # ============================================================
26
  # CSS + JS (canvas + menu)
27
  # ============================================================
28
 
29
  CSS = STYLE_PATH.read_text(encoding="utf-8") if STYLE_PATH.exists() else ""
30
 
 
31
  JS = r"""
32
  () => {
33
- // prevent double init (Gradio can re-render)
34
- if (window.__tq_inited) return;
35
- window.__tq_inited = true;
36
-
37
- // --- Menu toggle ---
38
- const btn = document.getElementById("tqMenuBtn");
39
- const menu = document.getElementById("tqMenu");
40
- if (btn && menu) {
41
- btn.addEventListener("click", () => menu.classList.toggle("open"));
42
- document.addEventListener("click", (e) => {
43
- if (!menu.contains(e.target) && !btn.contains(e.target)) {
44
- menu.classList.remove("open");
45
- }
46
- });
47
- }
 
 
 
 
 
 
48
 
49
- // --- Starfield canvas ---
50
- const c = document.getElementById("quantumField");
51
- if (!c) return;
52
- const ctx = c.getContext("2d");
53
- if (!ctx) return;
54
 
55
- let w = 0, h = 0;
56
- const stars = [];
57
- const N = 170;
58
 
59
- function resize() {
60
- w = c.width = window.innerWidth;
61
- h = c.height = window.innerHeight;
62
- }
63
 
64
- function seed() {
65
- stars.length = 0;
66
- for (let i = 0; i < N; i++) {
67
- stars.push({
68
- x: Math.random() * w,
69
- y: Math.random() * h,
70
- z: 0.2 + Math.random() * 0.8,
71
- v: 0.15 + Math.random() * 0.55,
72
- });
 
73
  }
74
- }
75
 
76
- function tick() {
77
- ctx.clearRect(0, 0, w, h);
78
 
79
- // subtle vignette
80
- const g = ctx.createRadialGradient(w/2, h/2, 10, w/2, h/2, Math.max(w,h)/1.1);
81
- g.addColorStop(0, "rgba(0,0,0,0)");
82
- g.addColorStop(1, "rgba(0,0,0,0.55)");
83
- ctx.fillStyle = g;
84
- ctx.fillRect(0, 0, w, h);
85
 
86
- ctx.globalAlpha = 0.9;
 
87
 
88
- for (const s of stars) {
89
- s.y += s.v * (1.0 + (1.0 - s.z));
90
- if (s.y > h + 10) { s.y = -10; s.x = Math.random() * w; }
 
 
 
 
 
91
 
92
- const r = 0.6 + 1.6 * (1.0 - s.z);
93
- ctx.fillStyle = "rgba(255,255,255,0.75)";
94
- ctx.beginPath();
95
- ctx.arc(s.x, s.y, r, 0, Math.PI * 2);
96
- ctx.fill();
 
 
 
 
97
  }
98
 
99
- requestAnimationFrame(tick);
 
 
 
100
  }
101
 
102
- resize();
103
- seed();
104
- window.addEventListener("resize", () => { resize(); seed(); });
105
- tick();
 
 
 
 
106
  }
107
  """
108
 
109
-
110
  # ============================================================
111
  # Helpers
112
  # ============================================================
@@ -123,7 +143,8 @@ def _clean_pngs(folder: Path):
123
  def _collect_pngs(folder: Path):
124
  if not folder.exists():
125
  return []
126
- return sorted(str(p) for p in folder.rglob("*.png"))
 
127
 
128
 
129
  def _run_capture(cmd, cwd: Path, env: dict):
@@ -229,22 +250,17 @@ def build_ui():
229
  gr.HTML(
230
  """
231
  <canvas id="quantumField"></canvas>
232
-
233
  <nav id="tqNav">
234
  <div class="nav-inner">
235
  <div class="brand">TwoQuarks</div>
236
-
237
  <div class="nav-links">
238
  <a href="#down">Down</a>
239
  <a href="#strange">Strange</a>
240
  </div>
241
-
242
  <div class="spacer"></div>
243
-
244
  <div class="menu-btn" id="tqMenuBtn" aria-label="menu">
245
  <span></span>
246
  </div>
247
-
248
  <div class="menu" id="tqMenu">
249
  <a href="#down">DOWN / AntiDown</a>
250
  <div class="sep"></div>
@@ -289,13 +305,13 @@ designed to expose and analyze failure modes under deceptive reward conditions.
289
  label="Episodes per phase",
290
  )
291
 
292
- run_down_btn = gr.Button("Run DOWN Model", elem_classes=["tq-primary"])
293
  down_meta = gr.JSON(label="Run status")
294
 
295
- # elem_id is REQUIRED for carousel CSS
296
  down_gallery = gr.Gallery(
297
  label="Generated plots",
298
- columns=2,
299
  height="auto",
300
  elem_id="down_gallery",
301
  )
@@ -321,12 +337,12 @@ designed to study failure under hidden regime shifts and phase transitions.
321
  """
322
  )
323
 
324
- run_strange_btn = gr.Button("Run STRANGE Model", elem_classes=["tq-primary"])
325
  strange_meta = gr.JSON(label="Run status")
326
 
327
  strange_gallery = gr.Gallery(
328
  label="Generated plots",
329
- columns=2,
330
  height="auto",
331
  elem_id="strange_gallery",
332
  )
 
21
 
22
  STYLE_PATH = ROOT / "style.css"
23
 
 
24
  # ============================================================
25
  # CSS + JS (canvas + menu)
26
  # ============================================================
27
 
28
  CSS = STYLE_PATH.read_text(encoding="utf-8") if STYLE_PATH.exists() else ""
29
 
30
+ # JS: robust init (Gradio can run JS before HTML exists)
31
  JS = r"""
32
  () => {
33
+ // already running animation? bail
34
+ if (window.__tq_starfield_running) return;
35
+
36
+ function initOnceDomReady() {
37
+ const canvas = document.getElementById("quantumField");
38
+ const btn = document.getElementById("tqMenuBtn");
39
+ const menu = document.getElementById("tqMenu");
40
+
41
+ // DOM not ready yet -> retry shortly
42
+ if (!canvas) return false;
43
+
44
+ // ---- Menu toggle (idempotent) ----
45
+ if (btn && menu && !btn.__tq_bound) {
46
+ btn.__tq_bound = true;
47
+ btn.addEventListener("click", () => menu.classList.toggle("open"));
48
+ document.addEventListener("click", (e) => {
49
+ if (!menu.contains(e.target) && !btn.contains(e.target)) {
50
+ menu.classList.remove("open");
51
+ }
52
+ });
53
+ }
54
 
55
+ // ---- Starfield canvas ----
56
+ const ctx = canvas.getContext("2d");
57
+ if (!ctx) return true;
 
 
58
 
59
+ let w = 0, h = 0;
60
+ const stars = [];
61
+ const N = 170;
62
 
63
+ function resize() {
64
+ w = canvas.width = window.innerWidth;
65
+ h = canvas.height = window.innerHeight;
66
+ }
67
 
68
+ function seed() {
69
+ stars.length = 0;
70
+ for (let i = 0; i < N; i++) {
71
+ stars.push({
72
+ x: Math.random() * w,
73
+ y: Math.random() * h,
74
+ z: 0.2 + Math.random() * 0.8,
75
+ v: 0.15 + Math.random() * 0.55,
76
+ });
77
+ }
78
  }
 
79
 
80
+ function tick() {
81
+ ctx.clearRect(0, 0, w, h);
82
 
83
+ // subtle vignette
84
+ const g = ctx.createRadialGradient(w/2, h/2, 10, w/2, h/2, Math.max(w,h)/1.1);
85
+ g.addColorStop(0, "rgba(0,0,0,0)");
86
+ g.addColorStop(1, "rgba(0,0,0,0.55)");
87
+ ctx.fillStyle = g;
88
+ ctx.fillRect(0, 0, w, h);
89
 
90
+ ctx.globalAlpha = 0.9;
91
+ ctx.fillStyle = "rgba(255,255,255,0.75)";
92
 
93
+ for (const s of stars) {
94
+ s.y += s.v * (1.0 + (1.0 - s.z));
95
+ if (s.y > h + 10) { s.y = -10; s.x = Math.random() * w; }
96
+ const r = 0.6 + 1.6 * (1.0 - s.z);
97
+ ctx.beginPath();
98
+ ctx.arc(s.x, s.y, r, 0, Math.PI * 2);
99
+ ctx.fill();
100
+ }
101
 
102
+ requestAnimationFrame(tick);
103
+ }
104
+
105
+ resize();
106
+ seed();
107
+
108
+ if (!window.__tq_resize_bound) {
109
+ window.__tq_resize_bound = true;
110
+ window.addEventListener("resize", () => { resize(); seed(); });
111
  }
112
 
113
+ window.__tq_starfield_running = true;
114
+ tick();
115
+
116
+ return true;
117
  }
118
 
119
+ // Try a few times to catch Gradio timing
120
+ let tries = 0;
121
+ const maxTries = 30;
122
+ const timer = setInterval(() => {
123
+ tries += 1;
124
+ const ok = initOnceDomReady();
125
+ if (ok || tries >= maxTries) clearInterval(timer);
126
+ }, 120);
127
  }
128
  """
129
 
 
130
  # ============================================================
131
  # Helpers
132
  # ============================================================
 
143
  def _collect_pngs(folder: Path):
144
  if not folder.exists():
145
  return []
146
+ # Return absolute paths so Gradio can resolve correctly
147
+ return sorted(str(p.resolve()) for p in folder.rglob("*.png"))
148
 
149
 
150
  def _run_capture(cmd, cwd: Path, env: dict):
 
250
  gr.HTML(
251
  """
252
  <canvas id="quantumField"></canvas>
 
253
  <nav id="tqNav">
254
  <div class="nav-inner">
255
  <div class="brand">TwoQuarks</div>
 
256
  <div class="nav-links">
257
  <a href="#down">Down</a>
258
  <a href="#strange">Strange</a>
259
  </div>
 
260
  <div class="spacer"></div>
 
261
  <div class="menu-btn" id="tqMenuBtn" aria-label="menu">
262
  <span></span>
263
  </div>
 
264
  <div class="menu" id="tqMenu">
265
  <a href="#down">DOWN / AntiDown</a>
266
  <div class="sep"></div>
 
305
  label="Episodes per phase",
306
  )
307
 
308
+ run_down_btn = gr.Button("Run DOWN Model", elem_id="btn_down", elem_classes=["tq-primary"])
309
  down_meta = gr.JSON(label="Run status")
310
 
311
+ # Force 1 column; carousel is handled by CSS
312
  down_gallery = gr.Gallery(
313
  label="Generated plots",
314
+ columns=1,
315
  height="auto",
316
  elem_id="down_gallery",
317
  )
 
337
  """
338
  )
339
 
340
+ run_strange_btn = gr.Button("Run STRANGE Model", elem_id="btn_strange", elem_classes=["tq-primary"])
341
  strange_meta = gr.JSON(label="Run status")
342
 
343
  strange_gallery = gr.Gallery(
344
  label="Generated plots",
345
+ columns=1,
346
  height="auto",
347
  elem_id="strange_gallery",
348
  )