adelevett commited on
Commit
fe40bed
·
1 Parent(s): e9d4821

provenance

Browse files
Files changed (3) hide show
  1. app.py +24 -36
  2. sample/demo_chat_33.json +472 -0
  3. theater_template.html +28 -3
app.py CHANGED
@@ -5,7 +5,7 @@ import gradio as gr
5
  import pymupdf4llm
6
  from pipeline import run_livestream_pipeline, extract_youtube_video_id, DEFAULT_HF_TOKEN
7
 
8
- def load_template_with_data(video_id: str, chat_data: list) -> str:
9
  template_path = os.path.join(os.path.dirname(__file__), "theater_template.html")
10
  with open(template_path, "r", encoding="utf-8") as f:
11
  template = f.read()
@@ -14,12 +14,15 @@ def load_template_with_data(video_id: str, chat_data: list) -> str:
14
  inner_html = template.replace("{{VIDEO_ID}}", video_id)
15
  inner_html = inner_html.replace("{{CHAT_DATA_JSON}}", json.dumps(chat_data, ensure_ascii=False))
16
 
 
 
 
17
  # Escape HTML to be safely embedded inside srcdoc attribute of an iframe
18
  escaped_inner_html = html.escape(inner_html)
19
 
20
  # Wrap in iframe to ensure scripts execute correctly in Gradio 6+ without innerHTML restrictions
21
  iframe_code = (
22
- f'<iframe srcdoc="{escaped_inner_html}" style="width: 100%; height: 620px; '
23
  f'border: none; border-radius: 12px; background-color: #0b0c10; box-shadow: 0 4px 12px rgba(0,0,0,0.45);"></iframe>'
24
  )
25
  return iframe_code
@@ -31,7 +34,7 @@ def get_demo_html() -> str:
31
  chat_data = json.load(f)
32
  else:
33
  chat_data = []
34
- return load_template_with_data("xLljoibgUvk", chat_data)
35
 
36
  def get_demo_2_html() -> str:
37
  demo_json_path = os.path.join(os.path.dirname(__file__), "sample", "demo_chat_2.json")
@@ -40,7 +43,7 @@ def get_demo_2_html() -> str:
40
  chat_data = json.load(f)
41
  else:
42
  chat_data = []
43
- return load_template_with_data("NtRf4icqE7o", chat_data)
44
 
45
  def get_demo_3_html() -> str:
46
  demo_json_path = os.path.join(os.path.dirname(__file__), "sample", "demo_chat_3.json")
@@ -49,7 +52,7 @@ def get_demo_3_html() -> str:
49
  chat_data = json.load(f)
50
  else:
51
  chat_data = []
52
- return load_template_with_data("g6eQMrA1_-I", chat_data)
53
 
54
  def get_demo_4_html() -> str:
55
  demo_json_path = os.path.join(os.path.dirname(__file__), "sample", "demo_chat_4.json")
@@ -58,7 +61,7 @@ def get_demo_4_html() -> str:
58
  chat_data = json.load(f)
59
  else:
60
  chat_data = []
61
- return load_template_with_data("hJP5GqnTrNo", chat_data)
62
 
63
  # Global store for custom simulations
64
  custom_simulation_store = {
@@ -97,10 +100,8 @@ def get_provenance_html(selection: str) -> str:
97
  if selection in apa_citations:
98
  citation = apa_citations[selection]
99
  return (
100
- f"<div style='margin-top: 16px; padding: 14px 18px; background-color: #161a23; "
101
- f"border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 8px; font-family: sans-serif; "
102
- f"color: #94a1b2; font-size: 0.9rem; line-height: 1.5;'>"
103
- f"<span style='color: #fffffe; font-weight: 600; display: block; margin-bottom: 4px;'>📄 Provenance</span>"
104
  f"Comments generated using AI from published works: {citation}"
105
  f"</div>"
106
  )
@@ -115,10 +116,8 @@ def get_provenance_html(selection: str) -> str:
115
 
116
  if source_info:
117
  return (
118
- f"<div style='margin-top: 16px; padding: 14px 18px; background-color: #161a23; "
119
- f"border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 8px; font-family: sans-serif; "
120
- f"color: #94a1b2; font-size: 0.9rem; line-height: 1.5;'>"
121
- f"<span style='color: #fffffe; font-weight: 600; display: block; margin-bottom: 4px;'>📄 Provenance</span>"
122
  f"Comments generated using AI from {source_info}."
123
  f"</div>"
124
  )
@@ -126,23 +125,21 @@ def get_provenance_html(selection: str) -> str:
126
  return ""
127
 
128
 
129
- def play_simulation_selection(selection: str) -> tuple[str, str]:
130
- provenance_html = get_provenance_html(selection)
131
  if selection == "Steve Jobs 1983 Speech (Demo)":
132
- return get_demo_html(), provenance_html
133
  elif selection == "Carl Sagan Demon-Haunted World (Demo)":
134
- return get_demo_2_html(), provenance_html
135
  elif selection == "Edward Teller - Schrödinger's Cat (Demo)":
136
- return get_demo_3_html(), provenance_html
137
  elif selection == "Sal Khan - Khanmigo AI Tutor (Demo)":
138
- return get_demo_4_html(), provenance_html
139
  elif selection == "My Custom Simulation":
140
  if custom_simulation_store["video_id"] and custom_simulation_store["chat_data"]:
141
- player_html = load_template_with_data(custom_simulation_store["video_id"], custom_simulation_store["chat_data"])
142
  else:
143
- player_html = "<div style='color:#ff0055; text-align:center; padding:50px; font-family:sans-serif;'>No custom simulation has been generated yet. Please choose 'Configure Custom Solution'.</div>"
144
- return player_html, provenance_html
145
- return "", ""
146
 
147
 
148
  def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_token: str, use_ocr: bool = False):
@@ -150,7 +147,6 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
150
  video_id = extract_youtube_video_id(yt_url)
151
  if not video_id:
152
  return (
153
- gr.update(),
154
  gr.update(),
155
  "### ❌ Error\nInvalid YouTube URL. Please provide a valid YouTube link or 11-character Video ID.",
156
  gr.update()
@@ -169,7 +165,6 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
169
  has_pasted_text = True
170
  else:
171
  return (
172
- gr.update(),
173
  gr.update(),
174
  "### ❌ Error\nPlease upload a PDF/text file or paste some reference document text.",
175
  gr.update()
@@ -179,7 +174,6 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
179
  manual_transcript = srt_text.strip() if srt_text.strip() else None
180
  if not manual_transcript:
181
  return (
182
- gr.update(),
183
  gr.update(),
184
  "### ❌ Error\nPlease paste the timestamped video transcript. (Auto-fetching is disabled due to server IP blocks).",
185
  gr.update()
@@ -219,12 +213,10 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
219
  )
220
 
221
  # Create updated HTML player
222
- new_html = load_template_with_data(video_id, chat_data)
223
- provenance_html = get_provenance_html("My Custom Simulation")
224
 
225
  return (
226
  new_html,
227
- provenance_html,
228
  success_msg,
229
  gr.update(choices=["Steve Jobs 1983 Speech (Demo)", "Carl Sagan Demon-Haunted World (Demo)", "Edward Teller - Schrödinger's Cat (Demo)", "Sal Khan - Khanmigo AI Tutor (Demo)", "My Custom Simulation"], value="My Custom Simulation")
230
  )
@@ -237,13 +229,13 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
237
  "document or a different video."
238
  )
239
  return (
240
- gr.update(),
241
  gr.update(),
242
  error_msg,
243
  gr.update()
244
  )
245
 
246
 
 
247
  # Gradio Theme
248
  custom_theme = gr.themes.Default(
249
  primary_hue="purple",
@@ -365,14 +357,11 @@ with gr.Blocks(title="ReadStream") as demo:
365
  # The player frame
366
  player_frame = gr.HTML(value=get_demo_html())
367
 
368
- # The provenance/citation frame
369
- provenance_frame = gr.HTML(value=get_provenance_html("Steve Jobs 1983 Speech (Demo)"))
370
-
371
  # Trigger updates when selection changes
372
  sim_selector.change(
373
  fn=play_simulation_selection,
374
  inputs=[sim_selector],
375
- outputs=[player_frame, provenance_frame]
376
  )
377
 
378
  # Tab 2: Generator Config
@@ -437,7 +426,6 @@ with gr.Blocks(title="ReadStream") as demo:
437
  ],
438
  outputs=[
439
  player_frame,
440
- provenance_frame,
441
  status_output,
442
  sim_selector
443
  ]
 
5
  import pymupdf4llm
6
  from pipeline import run_livestream_pipeline, extract_youtube_video_id, DEFAULT_HF_TOKEN
7
 
8
+ def load_template_with_data(video_id: str, chat_data: list, selection: str) -> str:
9
  template_path = os.path.join(os.path.dirname(__file__), "theater_template.html")
10
  with open(template_path, "r", encoding="utf-8") as f:
11
  template = f.read()
 
14
  inner_html = template.replace("{{VIDEO_ID}}", video_id)
15
  inner_html = inner_html.replace("{{CHAT_DATA_JSON}}", json.dumps(chat_data, ensure_ascii=False))
16
 
17
+ provenance_html = get_provenance_html(selection)
18
+ inner_html = inner_html.replace("{{PROVENANCE_HTML}}", provenance_html)
19
+
20
  # Escape HTML to be safely embedded inside srcdoc attribute of an iframe
21
  escaped_inner_html = html.escape(inner_html)
22
 
23
  # Wrap in iframe to ensure scripts execute correctly in Gradio 6+ without innerHTML restrictions
24
  iframe_code = (
25
+ f'<iframe srcdoc="{escaped_inner_html}" style="width: 100%; height: 650px; '
26
  f'border: none; border-radius: 12px; background-color: #0b0c10; box-shadow: 0 4px 12px rgba(0,0,0,0.45);"></iframe>'
27
  )
28
  return iframe_code
 
34
  chat_data = json.load(f)
35
  else:
36
  chat_data = []
37
+ return load_template_with_data("xLljoibgUvk", chat_data, "Steve Jobs 1983 Speech (Demo)")
38
 
39
  def get_demo_2_html() -> str:
40
  demo_json_path = os.path.join(os.path.dirname(__file__), "sample", "demo_chat_2.json")
 
43
  chat_data = json.load(f)
44
  else:
45
  chat_data = []
46
+ return load_template_with_data("NtRf4icqE7o", chat_data, "Carl Sagan Demon-Haunted World (Demo)")
47
 
48
  def get_demo_3_html() -> str:
49
  demo_json_path = os.path.join(os.path.dirname(__file__), "sample", "demo_chat_3.json")
 
52
  chat_data = json.load(f)
53
  else:
54
  chat_data = []
55
+ return load_template_with_data("g6eQMrA1_-I", chat_data, "Edward Teller - Schrödinger's Cat (Demo)")
56
 
57
  def get_demo_4_html() -> str:
58
  demo_json_path = os.path.join(os.path.dirname(__file__), "sample", "demo_chat_4.json")
 
61
  chat_data = json.load(f)
62
  else:
63
  chat_data = []
64
+ return load_template_with_data("hJP5GqnTrNo", chat_data, "Sal Khan - Khanmigo AI Tutor (Demo)")
65
 
66
  # Global store for custom simulations
67
  custom_simulation_store = {
 
100
  if selection in apa_citations:
101
  citation = apa_citations[selection]
102
  return (
103
+ f"<div class='provenance-container'>"
104
+ f"<span class='provenance-title'>📄 Provenance</span>"
 
 
105
  f"Comments generated using AI from published works: {citation}"
106
  f"</div>"
107
  )
 
116
 
117
  if source_info:
118
  return (
119
+ f"<div class='provenance-container'>"
120
+ f"<span class='provenance-title'>📄 Provenance</span>"
 
 
121
  f"Comments generated using AI from {source_info}."
122
  f"</div>"
123
  )
 
125
  return ""
126
 
127
 
128
+ def play_simulation_selection(selection: str) -> str:
 
129
  if selection == "Steve Jobs 1983 Speech (Demo)":
130
+ return get_demo_html()
131
  elif selection == "Carl Sagan Demon-Haunted World (Demo)":
132
+ return get_demo_2_html()
133
  elif selection == "Edward Teller - Schrödinger's Cat (Demo)":
134
+ return get_demo_3_html()
135
  elif selection == "Sal Khan - Khanmigo AI Tutor (Demo)":
136
+ return get_demo_4_html()
137
  elif selection == "My Custom Simulation":
138
  if custom_simulation_store["video_id"] and custom_simulation_store["chat_data"]:
139
+ return load_template_with_data(custom_simulation_store["video_id"], custom_simulation_store["chat_data"], selection)
140
  else:
141
+ return "<div style='color:#ff0055; text-align:center; padding:50px; font-family:sans-serif;'>No custom simulation has been generated yet. Please choose 'Configure Custom Solution'.</div>"
142
+ return ""
 
143
 
144
 
145
  def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_token: str, use_ocr: bool = False):
 
147
  video_id = extract_youtube_video_id(yt_url)
148
  if not video_id:
149
  return (
 
150
  gr.update(),
151
  "### ❌ Error\nInvalid YouTube URL. Please provide a valid YouTube link or 11-character Video ID.",
152
  gr.update()
 
165
  has_pasted_text = True
166
  else:
167
  return (
 
168
  gr.update(),
169
  "### ❌ Error\nPlease upload a PDF/text file or paste some reference document text.",
170
  gr.update()
 
174
  manual_transcript = srt_text.strip() if srt_text.strip() else None
175
  if not manual_transcript:
176
  return (
 
177
  gr.update(),
178
  "### ❌ Error\nPlease paste the timestamped video transcript. (Auto-fetching is disabled due to server IP blocks).",
179
  gr.update()
 
213
  )
214
 
215
  # Create updated HTML player
216
+ new_html = load_template_with_data(video_id, chat_data, "My Custom Simulation")
 
217
 
218
  return (
219
  new_html,
 
220
  success_msg,
221
  gr.update(choices=["Steve Jobs 1983 Speech (Demo)", "Carl Sagan Demon-Haunted World (Demo)", "Edward Teller - Schrödinger's Cat (Demo)", "Sal Khan - Khanmigo AI Tutor (Demo)", "My Custom Simulation"], value="My Custom Simulation")
222
  )
 
229
  "document or a different video."
230
  )
231
  return (
 
232
  gr.update(),
233
  error_msg,
234
  gr.update()
235
  )
236
 
237
 
238
+
239
  # Gradio Theme
240
  custom_theme = gr.themes.Default(
241
  primary_hue="purple",
 
357
  # The player frame
358
  player_frame = gr.HTML(value=get_demo_html())
359
 
 
 
 
360
  # Trigger updates when selection changes
361
  sim_selector.change(
362
  fn=play_simulation_selection,
363
  inputs=[sim_selector],
364
+ outputs=[player_frame]
365
  )
366
 
367
  # Tab 2: Generator Config
 
426
  ],
427
  outputs=[
428
  player_frame,
 
429
  status_output,
430
  sim_selector
431
  ]
sample/demo_chat_33.json ADDED
@@ -0,0 +1,472 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "m7",
4
+ "username": "PhysicsNerd",
5
+ "text": "Whoa, Teller himself—the father of the H‑bomb.",
6
+ "anchor_id": "anc_0",
7
+ "displayTime": 1.7044073470653067
8
+ },
9
+ {
10
+ "id": "m5",
11
+ "username": "HistoryBuff",
12
+ "text": "Web of Stories uploads are pure gold.",
13
+ "anchor_id": "anc_0",
14
+ "displayTime": 1.7560328159750458
15
+ },
16
+ {
17
+ "id": "m1",
18
+ "username": "StreamStart",
19
+ "text": "Hey all! Just getting settled.",
20
+ "anchor_id": "anc_0",
21
+ "displayTime": 1.8508130510712666
22
+ },
23
+ {
24
+ "id": "m9",
25
+ "username": "NewViewer",
26
+ "text": "First time catching this live—excited!",
27
+ "anchor_id": "anc_0",
28
+ "displayTime": 5.426041785860398
29
+ },
30
+ {
31
+ "id": "m2",
32
+ "username": "QuantumFan",
33
+ "text": "Is that Edward Teller? Title says so!",
34
+ "anchor_id": "anc_0",
35
+ "displayTime": 7.287732397946086
36
+ },
37
+ {
38
+ "id": "m6",
39
+ "username": "JustJoining",
40
+ "text": "Checking in—let's roll!",
41
+ "anchor_id": "anc_1",
42
+ "displayTime": 11.560509043329397
43
+ },
44
+ {
45
+ "id": "m8",
46
+ "username": "CasualVibe",
47
+ "text": "Here for the vibes + science.",
48
+ "anchor_id": "anc_1",
49
+ "displayTime": 12.18646232115536
50
+ },
51
+ {
52
+ "id": "m4",
53
+ "username": "AudioCheck",
54
+ "text": "Audio's a bit low, can anyone hear?",
55
+ "anchor_id": "anc_1",
56
+ "displayTime": 14.393131422360284
57
+ },
58
+ {
59
+ "id": "m10",
60
+ "username": "Bot01",
61
+ "text": "Stream started at 0s.",
62
+ "anchor_id": "anc_1",
63
+ "displayTime": 14.670082545828391
64
+ },
65
+ {
66
+ "id": "m3",
67
+ "username": "ChatNormie",
68
+ "text": "What are we watching today? 🤔",
69
+ "anchor_id": "anc_1",
70
+ "displayTime": 16.30379060401677
71
+ },
72
+ {
73
+ "id": "m12",
74
+ "username": "SkepticSam",
75
+ "text": "Probabilities are the only certainty, lol 😂",
76
+ "anchor_id": "anc_2",
77
+ "displayTime": 36.45109351519112
78
+ },
79
+ {
80
+ "id": "m11",
81
+ "username": "ParticleMan",
82
+ "text": "So we can only predict probabilities? Classic QM vibes 🤔",
83
+ "anchor_id": "anc_2",
84
+ "displayTime": 37.0899897925436
85
+ },
86
+ {
87
+ "id": "m18",
88
+ "username": "Student101",
89
+ "text": "Wait, weren’t we told the future’s unknowable? 🤯",
90
+ "anchor_id": "anc_2",
91
+ "displayTime": 37.33139184788705
92
+ },
93
+ {
94
+ "id": "m17",
95
+ "username": "HeisenbergFan",
96
+ "text": "Heisenberg strikes again! 😎",
97
+ "anchor_id": "anc_2",
98
+ "displayTime": 37.40101850077151
99
+ },
100
+ {
101
+ "id": "m14",
102
+ "username": "CuriousMind",
103
+ "text": "Where's the particle headed? That's the million‑dollar question 🤷",
104
+ "anchor_id": "anc_3",
105
+ "displayTime": 53.25153527417958
106
+ },
107
+ {
108
+ "id": "m13",
109
+ "username": "ScienceTeacher",
110
+ "text": "He’s nailing the basics of position here! 👍",
111
+ "anchor_id": "anc_3",
112
+ "displayTime": 57.776361860010795
113
+ },
114
+ {
115
+ "id": "m15",
116
+ "username": "MemeLord",
117
+ "text": "Did he just say 'place' AND 'position'? Redundant much? 🤔",
118
+ "anchor_id": "anc_4",
119
+ "displayTime": 62.56361385264804
120
+ },
121
+ {
122
+ "id": "m16",
123
+ "username": "WaveFunction",
124
+ "text": "We need to know both where it is AND where it’s going! 🔍",
125
+ "anchor_id": "anc_4",
126
+ "displayTime": 65.98394598622109
127
+ },
128
+ {
129
+ "id": "m23",
130
+ "username": "DeepThinker",
131
+ "text": "Formalism of front mechanisms? 🤔 That's new to me.",
132
+ "anchor_id": "anc_5",
133
+ "displayTime": 73.60726208836788
134
+ },
135
+ {
136
+ "id": "m21",
137
+ "username": "PhysicsGeek",
138
+ "text": "Here comes the Uncertainty Principle! 🔬",
139
+ "anchor_id": "anc_6",
140
+ "displayTime": 81.14965317486786
141
+ },
142
+ {
143
+ "id": "m27",
144
+ "username": "ObserverEffect",
145
+ "text": "All about the wave behavior, folks.",
146
+ "anchor_id": "anc_6",
147
+ "displayTime": 81.40064237332483
148
+ },
149
+ {
150
+ "id": "m25",
151
+ "username": "WaveWatcher",
152
+ "text": "Wave‑particle duality strikes! ⚡",
153
+ "anchor_id": "anc_6",
154
+ "displayTime": 83.05429321499436
155
+ },
156
+ {
157
+ "id": "m26",
158
+ "username": "MathNerd",
159
+ "text": "Momentum & position? Pick one, please!",
160
+ "anchor_id": "anc_7",
161
+ "displayTime": 89.12294537206856
162
+ },
163
+ {
164
+ "id": "m22",
165
+ "username": "MomentumMover",
166
+ "text": "You can't know both at once—classic!",
167
+ "anchor_id": "anc_7",
168
+ "displayTime": 89.35757327197463
169
+ },
170
+ {
171
+ "id": "m28",
172
+ "username": "TheoryCrafter",
173
+ "text": "The idea hasn't been generally accepted? By whom?",
174
+ "anchor_id": "anc_8",
175
+ "displayTime": 95.28519694025123
176
+ },
177
+ {
178
+ "id": "m24",
179
+ "username": "ConfusedViewer",
180
+ "text": "Please explain in detail, I get confused easily 😅",
181
+ "anchor_id": "anc_8",
182
+ "displayTime": 96.78080347453259
183
+ },
184
+ {
185
+ "id": "m31",
186
+ "username": "EinsteinLover",
187
+ "text": "Einstein was never happy—God doesn’t play dice. 🤔",
188
+ "anchor_id": "anc_9",
189
+ "displayTime": 108.7847471511717
190
+ },
191
+ {
192
+ "id": "m35",
193
+ "username": "QuantumCat",
194
+ "text": "Einstein loathed that QM piece.",
195
+ "anchor_id": "anc_9",
196
+ "displayTime": 110.86567959369393
197
+ },
198
+ {
199
+ "id": "m38",
200
+ "username": "ScienceFan",
201
+ "text": "Vigdan? Definitely Wigner. :)",
202
+ "anchor_id": "anc_10",
203
+ "displayTime": 117.6248017653613
204
+ },
205
+ {
206
+ "id": "m36",
207
+ "username": "HistorianBuff",
208
+ "text": "Teller surrounded himself with great minds.",
209
+ "anchor_id": "anc_10",
210
+ "displayTime": 118.83227697903055
211
+ },
212
+ {
213
+ "id": "m32",
214
+ "username": "HungarianHistory",
215
+ "text": "Hungarian friends? You mean Wigner?",
216
+ "anchor_id": "anc_10",
217
+ "displayTime": 119.27222982289787
218
+ },
219
+ {
220
+ "id": "m33",
221
+ "username": "Objector",
222
+ "text": "The objection usually targets the observer, right?",
223
+ "anchor_id": "anc_11",
224
+ "displayTime": 126.74444970089813
225
+ },
226
+ {
227
+ "id": "m37",
228
+ "username": "SkepticalEye",
229
+ "text": "Is the observer really the culprit? 🤔",
230
+ "anchor_id": "anc_12",
231
+ "displayTime": 133.8760741718684
232
+ },
233
+ {
234
+ "id": "m34",
235
+ "username": "PhD_Student",
236
+ "text": "Observer disturbance = the key. 🔬",
237
+ "anchor_id": "anc_12",
238
+ "displayTime": 137.67091362080828
239
+ },
240
+ {
241
+ "id": "m42",
242
+ "username": "TranscriptPolice",
243
+ "text": "Is 'Viklav' actually Wigner? Audio’s a mess.",
244
+ "anchor_id": "anc_14",
245
+ "displayTime": 154.06959799738488
246
+ },
247
+ {
248
+ "id": "m47",
249
+ "username": "MetaPhysicist",
250
+ "text": "If I don’t know myself, can I even be an observer? 🤯",
251
+ "anchor_id": "anc_15",
252
+ "displayTime": 160.04850612940567
253
+ },
254
+ {
255
+ "id": "m49",
256
+ "username": "AudioFixer",
257
+ "text": "Yep, definitely Wigner. 👍",
258
+ "anchor_id": "anc_14",
259
+ "reply_to": "m42",
260
+ "displayTime": 161.13860733944296
261
+ },
262
+ {
263
+ "id": "m41",
264
+ "username": "PhilosopherKing",
265
+ "text": "What even is an observer? 🤔 Deep.",
266
+ "anchor_id": "anc_15",
267
+ "displayTime": 162.11222951445424
268
+ },
269
+ {
270
+ "id": "m44",
271
+ "username": "GossipGirl",
272
+ "text": "Tea time: gossip on Eugene Wigner 😂",
273
+ "anchor_id": "anc_16",
274
+ "displayTime": 176.259440186899
275
+ },
276
+ {
277
+ "id": "m45",
278
+ "username": "DeepThought",
279
+ "text": "Sharp objection incoming… brace yourselves.",
280
+ "anchor_id": "anc_18",
281
+ "displayTime": 197.88219601893857
282
+ },
283
+ {
284
+ "id": "m46",
285
+ "username": "CatLover99",
286
+ "text": "Everyone knows the cat 😼",
287
+ "anchor_id": "anc_20",
288
+ "displayTime": 215.4792674271316
289
+ },
290
+ {
291
+ "id": "m43",
292
+ "username": "SchrodingerFan",
293
+ "text": "Schrödinger’s cat—finally! 🎉",
294
+ "anchor_id": "anc_20",
295
+ "displayTime": 217.0800292542282
296
+ },
297
+ {
298
+ "id": "m48",
299
+ "username": "ScienceComms",
300
+ "text": "The cat’s the real star of the show, for sure.",
301
+ "anchor_id": "anc_20",
302
+ "displayTime": 217.7914877999608
303
+ },
304
+ {
305
+ "id": "m51",
306
+ "username": "MadScientist",
307
+ "text": "Setting the scene! 🎬",
308
+ "anchor_id": "anc_21",
309
+ "displayTime": 230.28898388768386
310
+ },
311
+ {
312
+ "id": "m52",
313
+ "username": "RadioactiveRalph",
314
+ "text": "Radioactive stuff—dangerous! ☢️",
315
+ "anchor_id": "anc_22",
316
+ "displayTime": 237.69146661505562
317
+ },
318
+ {
319
+ "id": "m57",
320
+ "username": "ParticlePhysicist",
321
+ "text": "Alpha particles??",
322
+ "anchor_id": "anc_22",
323
+ "displayTime": 242.5563821294065
324
+ },
325
+ {
326
+ "id": "m55",
327
+ "username": "LabRat",
328
+ "text": "Why close the counter? 🤔",
329
+ "anchor_id": "anc_23",
330
+ "displayTime": 250.3788946697037
331
+ },
332
+ {
333
+ "id": "m54",
334
+ "username": "StatsGuy",
335
+ "text": "50/50 chance, folks.",
336
+ "anchor_id": "anc_24",
337
+ "displayTime": 257.8528758912121
338
+ },
339
+ {
340
+ "id": "m58",
341
+ "username": "Gambler",
342
+ "text": "1/2 chance—quantum roulette! 🎲",
343
+ "anchor_id": "anc_24",
344
+ "displayTime": 261.07468852972573
345
+ },
346
+ {
347
+ "id": "m56",
348
+ "username": "AnimalControl",
349
+ "text": "Aw, poor cat! 😿",
350
+ "anchor_id": "anc_25",
351
+ "displayTime": 271.08823857839616
352
+ },
353
+ {
354
+ "id": "m53",
355
+ "username": "DangerZone",
356
+ "text": "A nasty poison door? Classic move. 😆",
357
+ "anchor_id": "anc_25",
358
+ "displayTime": 272.1478990220569
359
+ },
360
+ {
361
+ "id": "m68",
362
+ "username": "ProbabilityPro",
363
+ "text": "Just a probability distro, folks.",
364
+ "anchor_id": "anc_27",
365
+ "displayTime": 288.0731226736326
366
+ },
367
+ {
368
+ "id": "m61",
369
+ "username": "WaveFunctionFan",
370
+ "text": "The cat's wavefunction, lol 🤔",
371
+ "anchor_id": "anc_27",
372
+ "displayTime": 288.5014844760779
373
+ },
374
+ {
375
+ "id": "m62",
376
+ "username": "DeadOrAlive",
377
+ "text": "50% alive, 50% dead 😅",
378
+ "anchor_id": "anc_28",
379
+ "displayTime": 296.0870052113439
380
+ },
381
+ {
382
+ "id": "m66",
383
+ "username": "TranscriptChecker",
384
+ "text": "Big debt half? Probably a transcript slip—means 50%.",
385
+ "anchor_id": "anc_28",
386
+ "displayTime": 296.5118867595247
387
+ },
388
+ {
389
+ "id": "m63",
390
+ "username": "Realist",
391
+ "text": "Accurate: I have no clue. That's the point.",
392
+ "anchor_id": "anc_29",
393
+ "displayTime": 303.04106823107577
394
+ },
395
+ {
396
+ "id": "m65",
397
+ "username": "ObserverEffect",
398
+ "text": "Observer = wavefunction collapse 👀",
399
+ "anchor_id": "anc_29",
400
+ "displayTime": 306.91754865296963
401
+ },
402
+ {
403
+ "id": "m78",
404
+ "username": "EndingCredits",
405
+ "text": "And that wraps up the cat. 🐱",
406
+ "anchor_id": "anc_30",
407
+ "displayTime": 312.55215484797293
408
+ },
409
+ {
410
+ "id": "m64",
411
+ "username": "MagicTrick",
412
+ "text": "Reviving it? That's not the usual trick.",
413
+ "anchor_id": "anc_30",
414
+ "displayTime": 315.884434339336
415
+ },
416
+ {
417
+ "id": "m67",
418
+ "username": "ColdHearted",
419
+ "text": "Just kill the cat, end of story 😈",
420
+ "anchor_id": "anc_30",
421
+ "displayTime": 318.4129909973275
422
+ },
423
+ {
424
+ "id": "m75",
425
+ "username": "CritiqueMaster",
426
+ "text": "Teller's roasting the thought experiment. 🔥",
427
+ "anchor_id": "anc_31",
428
+ "displayTime": 322.9903872565834
429
+ },
430
+ {
431
+ "id": "m72",
432
+ "username": "SkepticalViewer",
433
+ "text": "He can't believe that was a serious objection. 🤨",
434
+ "anchor_id": "anc_31",
435
+ "displayTime": 326.75891442169757
436
+ },
437
+ {
438
+ "id": "m73",
439
+ "username": "OldSchoolPhysicist",
440
+ "text": "Age‑old indeed. 🕰️",
441
+ "anchor_id": "anc_32",
442
+ "displayTime": 333.34605285350625
443
+ },
444
+ {
445
+ "id": "m74",
446
+ "username": "BoldStatement",
447
+ "text": "That's his take? Just ignoring the observer? 🤔",
448
+ "anchor_id": "anc_33",
449
+ "displayTime": 343.00763121136214
450
+ },
451
+ {
452
+ "id": "m76",
453
+ "username": "FinalThought",
454
+ "text": "I don't need to look… interesting perspective. 🤔",
455
+ "anchor_id": "anc_33",
456
+ "displayTime": 343.59067613724466
457
+ },
458
+ {
459
+ "id": "m77",
460
+ "username": "ScienceHistory",
461
+ "text": "He really disagrees with Copenhagen here. 🤷‍♂️",
462
+ "anchor_id": "anc_33",
463
+ "displayTime": 345.21474456628954
464
+ },
465
+ {
466
+ "id": "m71",
467
+ "username": "MicDrop",
468
+ "text": "I don't need to look—mic drop. 🎤",
469
+ "anchor_id": "anc_33",
470
+ "displayTime": 345.7304280552546
471
+ }
472
+ ]
theater_template.html CHANGED
@@ -33,10 +33,10 @@
33
  display: flex;
34
  flex-direction: column;
35
  align-items: center;
36
- justify-content: center;
37
  width: 100%;
38
  height: 100vh;
39
- overflow: hidden;
40
  padding: 10px;
41
  }
42
 
@@ -45,7 +45,7 @@
45
  grid-template-columns: 1.8fr 1fr;
46
  width: 100%;
47
  max-width: 1350px;
48
- height: 90vh;
49
  gap: 16px;
50
  background: rgba(15, 15, 20, 0.4);
51
  border-radius: 16px;
@@ -284,6 +284,29 @@
284
  cursor: not-allowed;
285
  opacity: 0.6;
286
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  </style>
288
  </head>
289
  <body>
@@ -317,6 +340,8 @@
317
  </div>
318
  </div>
319
 
 
 
320
  <script>
321
  // Embedded Chat Data Placeholder (Replaced by Python script)
322
  const chatData = {{CHAT_DATA_JSON}};
 
33
  display: flex;
34
  flex-direction: column;
35
  align-items: center;
36
+ justify-content: flex-start;
37
  width: 100%;
38
  height: 100vh;
39
+ overflow-y: auto;
40
  padding: 10px;
41
  }
42
 
 
45
  grid-template-columns: 1.8fr 1fr;
46
  width: 100%;
47
  max-width: 1350px;
48
+ height: 480px;
49
  gap: 16px;
50
  background: rgba(15, 15, 20, 0.4);
51
  border-radius: 16px;
 
284
  cursor: not-allowed;
285
  opacity: 0.6;
286
  }
287
+
288
+ .provenance-container {
289
+ width: 100%;
290
+ max-width: 1350px;
291
+ margin-top: 12px;
292
+ padding: 10px 14px;
293
+ background: rgba(22, 26, 35, 0.65);
294
+ border: 1px solid var(--border-color);
295
+ border-radius: 10px;
296
+ font-size: 12px;
297
+ color: var(--text-muted);
298
+ line-height: 1.5;
299
+ box-shadow: 0 4px 12px rgba(0,0,0,0.25);
300
+ }
301
+ .provenance-title {
302
+ color: var(--text-color);
303
+ font-weight: 600;
304
+ display: block;
305
+ margin-bottom: 4px;
306
+ text-transform: uppercase;
307
+ font-size: 10px;
308
+ letter-spacing: 0.8px;
309
+ }
310
  </style>
311
  </head>
312
  <body>
 
340
  </div>
341
  </div>
342
 
343
+ {{PROVENANCE_HTML}}
344
+
345
  <script>
346
  // Embedded Chat Data Placeholder (Replaced by Python script)
347
  const chatData = {{CHAT_DATA_JSON}};