File size: 12,021 Bytes
f563372 2bdf2b8 f563372 9ba62b4 f563372 9ba62b4 f563372 9ba62b4 f563372 9ba62b4 f563372 2bdf2b8 f563372 9ba62b4 f563372 9ba62b4 f563372 2bdf2b8 9ba62b4 2bdf2b8 f563372 9ba62b4 f563372 9ba62b4 f563372 9ba62b4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | import json, textwrap, time
from datetime import datetime
import gradio as gr
# ----------------- SAMPLE WEEK (edit live on-air) -----------------
STORIES = [
{
"title": "Microsoft 365 Premium launches ($19.99/mo) with integrated Copilot",
"date": "2025-10-01",
"category": "Agents & Productivity",
"summary": "Microsoft bundles Office + Copilot Pro into a single individual plan with higher usage limits and advanced features (Researcher, Analyst, Actions). Copilot Pro sunsets. Agents require Azure + metered credits.",
"talk_track": [
"Copilot becomes the personal default.",
"Premium unlocks higher usage ceilings.",
"Agents: mind the Azure/Copilot credit metering."
],
"why_it_matters": "Mainstreaming AI assistants with clear pricing signals; agent workflows move into production norms.",
"sources": [
["Reuters", "https://www.reuters.com/technology/microsoft-launches-ai-powered-365-premium-bundle-1999-per-month-2025-10-01/"],
["Microsoft pricing", "https://www.microsoft.com/en-us/microsoft-365-copilot/pricing"]
]
},
{
"title": "Google 2K Nest cams for Gemini for Home + Home Premium subscription",
"date": "2025-10-01",
"category": "Edge & Devices",
"summary": "New Nest cams (wider FOV, better low light) and descriptive AI alerts + daily Home Brief. Google Home Premium replaces Nest Aware tiers.",
"talk_track": [
"First hardware built for Gemini for Home.",
"Events → summaries → routines.",
"Sub changes: Home Premium instead of Nest Aware."
],
"why_it_matters": "House-scale agents: sensor streams become structured inputs for daily briefings and automation.",
"sources": [
["The Verge", "https://www.theverge.com/"]
]
},
{
"title": "OpenAI updates GPT-5 Instant behavior for crisis-support signals",
"date": "2025-10-03",
"category": "Models & Alignment",
"summary": "Behavioral tuning for GPT-5 Instant improves distress detection and de-escalation, guided by experts.",
"talk_track": [
"Alignment over raw capability.",
"Important for support, education, first-touch contexts."
],
"why_it_matters": "Stability + safety polish for high-throughput deployments.",
"sources": [
["OpenAI release notes", "https://help.openai.com/en/articles/9624314-model-release-notes"]
]
},
{
"title": "Anthropic policy: personal Claude chats used for training (opt-out)",
"date": "2025-10-01",
"category": "Policy & Privacy",
"summary": "Non-enterprise Claude users are opted-in for training; opt-out available in settings. Enterprise/edu/gov excluded.",
"talk_track": [
"Flip the privacy toggle before sensitive work.",
"Audit retention + compliance posture."
],
"why_it_matters": "Data governance becomes a weekly habit, not a yearly policy read.",
"sources": [
["Tom’s Guide explainer", "https://www.tomsguide.com/"]
]
},
{
"title": "NVIDIA: Cosmos world models + Newton physics + Isaac GR00T updates",
"date": "2025-10-02",
"category": "Robotics & Physical AI",
"summary": "Open world-model family, open Newton physics in Isaac Lab, updated GR00T reasoning to accelerate robotics R&D.",
"talk_track": [
"Synthetic data + open physics to speed loops.",
"Bridging sim-to-real for smaller labs."
],
"why_it_matters": "Lower barrier to realistic robot learning → more pilots in 2026.",
"sources": [
["NVIDIA Newsroom", "https://nvidianews.nvidia.com/"]
]
}
]
# ----------------- Helpers -----------------
def all_categories():
return sorted(list({s["category"] for s in STORIES}))
def fmt_story(story: dict) -> str:
if not story:
return "No story selected."
date = datetime.fromisoformat(story["date"]).strftime("%b %d, %Y")
bullets = "\n".join([f"- {b}" for b in story.get("talk_track", [])])
srcs = "\n".join([f"- [{t}]({u})" for t, u in story.get("sources", [])])
return (
f"### {story['title']}\n"
f"**Date:** {date} **Category:** {story['category']}\n\n"
f"**Summary:** {story['summary']}\n\n"
f"**On-air notes:**\n{bullets}\n\n"
f"**Why it matters:** {story['why_it_matters']}\n\n"
f"**Sources:**\n{srcs}\n"
)
def teleprompter_text(story: dict) -> str:
text = f"{story['title']}\n\n{story['summary']}\n\nWhy it matters: {story['why_it_matters']}"
return textwrap.fill(text, width=90)
def list_rows(items):
return [[s["date"], s["category"], s["title"]] for s in items]
def filter_and_search(category, query):
items = STORIES
if category and category != "All":
items = [s for s in items if s["category"] == category]
if query:
q = query.lower().strip()
items = [s for s in items if q in s["title"].lower() or q in s["summary"].lower()]
return items
def build_ticker_html(text):
return (
'<link rel="stylesheet" href="style.css">'
'<div class="zen-ticker shell">'
f' <div class="zen-ticker__track" id="zenTicker" data-speed="38">{text}</div>'
"</div>"
'<script src="script.js"></script>'
)
def build_prompter_html(text, speed=40, is_playing=True):
playing = "true" if is_playing else "false"
safe = text.replace("&","&").replace("<","<").replace(">",">")
return (
'<link rel="stylesheet" href="style.css">'
'<div class="prompter">'
' <div class="prompter__viewport">'
' <div class="prompter__scroll" id="prompterScroll"'
f' data-speed="{speed}"'
f' data-playing="{playing}">'
f' <pre class="prompter__text">{safe}</pre>'
' </div>'
' </div>'
' <div class="prompter__controls">'
' <button id="pPlayPause" class="btn">⏯</button>'
' <button id="pSlower" class="btn">–</button>'
' <button id="pFaster" class="btn">+</button>'
' <button id="pFullscreen" class="btn">⤢</button>'
f' <span class="prompter__speed" id="pSpeed">{speed}px/s</span>'
' </div>'
'</div>'
'<script src="script.js"></script>'
)
def pick(idx, items):
if not items:
return ("No stories here.", build_prompter_html(""), build_ticker_html(""), 0)
i = max(0, min(int(idx), len(items) - 1))
story = items[i]
headlines = " • ".join([s["title"] for s in items[:12]])
return (
fmt_story(story),
build_prompter_html(teleprompter_text(story)),
build_ticker_html(headlines),
i
)
def add_story(json_blob):
# Robust try/except — this is where the earlier paste got cut
try:
obj = json.loads(json_blob)
if not isinstance(obj, dict):
raise ValueError("JSON must be an object")
if "title" not in obj or not obj["title"]:
raise ValueError("Missing 'title'")
if "summary" not in obj or not obj["summary"]:
raise ValueError("Missing 'summary'")
obj.setdefault("date", datetime.utcnow().date().isoformat())
obj.setdefault("category", "Misc")
obj.setdefault("talk_track", [])
obj.setdefault("why_it_matters", "")
obj.setdefault("sources", [])
STORIES.insert(0, obj)
return "✅ Added.", ["All"] + all_categories()
except Exception as e:
return f"❌ {e}", ["All"] + all_categories()
def countdown(minutes):
try:
total = int(float(minutes) * 60)
except Exception:
total = 0
total = max(0, min(total, 90 * 60))
for t in range(total, -1, -1):
mm, ss = divmod(t, 60)
yield f"{mm:02d}:{ss:02d}"
time.sleep(1)
# --------------------------- UI ----------------------------
with gr.Blocks(css="style.css", title="ZEN Weekly Live Radar PRO") as demo:
gr.HTML('<link rel="stylesheet" href="style.css"><script src="script.js"></script>')
with gr.Row(elem_classes="row-top"):
gr.Markdown("### 🛰️ ZEN Weekly Live Radar PRO — Paste stories, run teleprompter, scroll ticker")
with gr.Row():
with gr.Column(scale=1, elem_classes="card"):
category = gr.Dropdown(choices=["All"] + all_categories(), value="All", label="Category")
query = gr.Textbox(label="Search", placeholder="Filter titles & summaries…")
list_state = gr.State([])
table = gr.Dataframe(headers=["Date","Category","Title"], interactive=False)
slider = gr.Slider(label="Select", minimum=0, maximum=max(0, len(STORIES)-1), step=1, value=0)
refresh = gr.Button("Refresh")
gr.Markdown("#### Add a story (JSON)")
template = {
"title": "Sample: Your breaking item",
"date": datetime.utcnow().date().isoformat(),
"category": "Breaking",
"summary": "One-line explainer.",
"talk_track": ["Point 1", "Point 2"],
"why_it_matters": "The punchline.",
"sources": [["Source Name", "https://example.com"]]
}
gr.Code(value=json.dumps(template, indent=2), language="json", label="Template")
blob = gr.Textbox(lines=10, placeholder="Paste JSON here…")
add_btn = gr.Button("Add")
add_status = gr.Markdown()
gr.Markdown("#### On-air countdown")
mins = gr.Number(label="Minutes", value=5)
start = gr.Button("Start")
time_left = gr.Textbox(label="Time left", value="05:00", interactive=False)
with gr.Column(scale=2, elem_classes="card"):
story_md = gr.Markdown(fmt_story(STORIES[0]))
prompter_html = gr.HTML(build_prompter_html(teleprompter_text(STORIES[0])))
ticker_html = gr.HTML(build_ticker_html(" • ".join([s["title"] for s in STORIES[:12]])))
# --------------- Callbacks ----------------
def refresh_list(cat, q):
items = filter_and_search(cat, q)
rows = list_rows(items)
mx = max(0, len(items) - 1)
sel_md, prom_html, tick_html, _ = pick(0, items)
return (
items, # list_state
gr.update(value=rows), # table data
gr.update(maximum=mx, value=0), # slider props
sel_md, # story_md
prom_html, # prompter_html
tick_html # ticker_html
)
def on_select(i, cat, q, items):
items = filter_and_search(cat, q)
sel_md, prom_html, tick_html, _ = pick(i, items)
return sel_md, prom_html, tick_html
refresh.click(
refresh_list,
inputs=[category, query],
outputs=[list_state, table, slider, story_md, prompter_html, ticker_html]
)
category.change(
refresh_list,
inputs=[category, query],
outputs=[list_state, table, slider, story_md, prompter_html, ticker_html]
)
query.submit(
refresh_list,
inputs=[category, query],
outputs=[list_state, table, slider, story_md, prompter_html, ticker_html]
)
slider.release(
on_select,
inputs=[slider, category, query, list_state],
outputs=[story_md, prompter_html, ticker_html]
)
add_btn.click(add_story, [blob], [add_status, category]).then(
refresh_list, [category, query], [list_state, table, slider, story_md, prompter_html, ticker_html]
)
start.click(countdown, [mins], [time_left])
if __name__ == "__main__":
demo.queue().launch(ssr_mode=False) # disable SSR for widest compatibility
|