Spaces:
Running on Zero
Running on Zero
GitHub Actions
feat: WebLLM browser agent with PeerJS mesh, HybridRAG, news signals, and easter-egg ticker
78cc96f | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>HearthNet — Browser Agent + Mesh</title> | |
| <style> | |
| :root { | |
| --bg:#0b1020; --card:#111827; --line:#334155; --fg:#eef2ff; --muted:#94a3b8; | |
| --accent:#6366f1; --accent-soft:#1e1b4b; --ok:#34d399; --warn:#fbbf24; --err:#f87171; | |
| } | |
| * { box-sizing:border-box; } | |
| body { margin:0; font-family:system-ui,-apple-system,Segoe UI,sans-serif; background:var(--bg); color:var(--fg); } | |
| a { color:#a5b4fc; } | |
| .shell { max-width:1400px; margin:0 auto; padding:16px; } | |
| header { display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:12px; } | |
| header h1 { font-size:18px; margin:0; font-weight:600; } | |
| header .spacer { flex:1; } | |
| select, input, button { font-family:inherit; font-size:14px; } | |
| select, input { background:var(--card); color:var(--fg); border:1px solid var(--line); border-radius:8px; padding:8px 10px; } | |
| button { background:#1f2937; color:var(--fg); border:1px solid var(--line); border-radius:8px; padding:8px 14px; cursor:pointer; } | |
| button:hover:not(:disabled) { border-color:var(--accent); } | |
| button.primary { background:var(--accent); border-color:var(--accent); color:#fff; } | |
| button:disabled { opacity:.5; cursor:not-allowed; } | |
| #model-progress { font-size:12px; color:var(--muted); } | |
| #model-progress.ok { color:var(--ok); } #model-progress.warn { color:var(--warn); } | |
| .tabs { display:flex; gap:6px; margin-bottom:12px; flex-wrap:wrap; } | |
| .tab { padding:8px 14px; border-radius:10px; background:var(--card); border:1px solid var(--line); cursor:pointer; } | |
| .tab.active { background:var(--accent-soft); border-color:var(--accent); } | |
| .pane { display:none; } | |
| .pane.active { display:block; } | |
| .panel { border:1px solid var(--line); background:var(--card); border-radius:14px; padding:14px; margin-bottom:12px; } | |
| .panel-title { font-weight:700; margin-bottom:10px; color:#a5b4fc; } | |
| .muted { color:var(--muted); } | |
| .mono { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; } | |
| pre { white-space:pre-wrap; word-break:break-word; margin:0; } | |
| .pill { display:inline-block; padding:2px 8px; border-radius:10px; font-size:12px; background:var(--accent-soft); color:#c7d2fe; } | |
| .pill.ok { background:#064e3b; color:var(--ok); } .pill.warn { background:#451a03; color:var(--warn); } .pill.err { background:#450a0a; color:var(--err); } | |
| .row { display:flex; gap:8px; align-items:center; flex-wrap:wrap; } | |
| .row input { flex:1; min-width:120px; } | |
| /* agent */ | |
| .agent-top { display:flex; gap:8px; margin-bottom:12px; } | |
| .agent-top input { flex:1; } | |
| .agent-grid { display:grid; grid-template-columns:1fr 320px; gap:12px; } | |
| .agent-main, .agent-side .panel { min-height:60vh; } | |
| .agent-main { border:1px solid var(--line); background:var(--card); border-radius:14px; padding:14px; } | |
| /* news */ | |
| .news-ticker { overflow:hidden; white-space:nowrap; border:1px solid var(--line); background:var(--card); border-radius:10px; padding:8px 0; margin-bottom:12px; } | |
| .news-ticker-track { display:inline-block; padding-left:100%; animation:scroll 60s linear infinite; } | |
| .news-ticker-track:hover { animation-play-state:paused; } | |
| @keyframes scroll { to { transform:translateX(-100%); } } | |
| .tick { padding:0 14px; } .tick-sep { color:var(--muted); } | |
| .news-grid { display:grid; grid-template-columns:1fr 340px; gap:12px; } | |
| .news-item { padding:10px 0; border-bottom:1px solid var(--line); } | |
| .news-item a { font-weight:600; text-decoration:none; } | |
| .news-meta { font-size:12px; color:var(--muted); margin:2px 0; } | |
| .news-sum { font-size:13px; color:#cbd5e1; } | |
| .signal { padding:6px 0; border-bottom:1px solid var(--line); } | |
| .signal.on { } .signal-hits { font-size:11px; color:var(--muted); margin-top:4px; } | |
| .alert-row, .peer-row { padding:6px 0; border-bottom:1px solid var(--line); font-size:13px; } | |
| /* mesh */ | |
| .mesh-grid { display:grid; grid-template-columns:240px 1fr; gap:12px; } | |
| .mesh-chat { height:300px; overflow:auto; border:1px solid var(--line); border-radius:8px; padding:8px; background:#0f1729; font-size:13px; } | |
| .msg { padding:2px 0; } .msg.self b { color:var(--ok); } .msg.sys { color:var(--muted); font-style:italic; } | |
| /* easter-egg live ticker (press "e") */ | |
| .egg-ticker { position:fixed; left:0; right:0; bottom:0; z-index:9999; display:flex; align-items:center; | |
| background:linear-gradient(90deg,#111827,#1e1b4b); border-top:2px solid var(--accent); | |
| box-shadow:0 -8px 24px rgba(0,0,0,.5); overflow:hidden; transform:translateY(0); transition:transform .25s ease; } | |
| .egg-ticker.hidden { transform:translateY(110%); } | |
| .egg-label { flex:0 0 auto; padding:8px 14px; font-weight:800; font-size:12px; letter-spacing:.08em; | |
| color:#fff; background:var(--accent); } | |
| .egg-ticker-track { white-space:nowrap; padding:8px 0; animation:scroll 50s linear infinite; } | |
| .egg-ticker:hover .egg-ticker-track { animation-play-state:paused; } | |
| .etk { padding:0 16px; } .etk b { color:#a5b4fc; } .esep { color:var(--muted); } | |
| @media (max-width:900px) { .agent-grid,.news-grid,.mesh-grid { grid-template-columns:1fr; } } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="shell"> | |
| <header> | |
| <h1>🔥 HearthNet</h1> | |
| <span class="muted">browser-local agent · news · WebRTC mesh</span> | |
| <span class="spacer"></span> | |
| <span class="muted" style="opacity:.55;font-size:11px">press <kbd>e</kbd> for live ticker</span> | |
| <label class="muted">Model</label> | |
| <select id="model"></select> | |
| <span id="model-progress">select a model — it loads on first chat</span> | |
| </header> | |
| <div class="tabs"> | |
| <div class="tab active" data-tab="agent">Agent</div> | |
| <div class="tab" data-tab="news">News</div> | |
| <div class="tab" data-tab="mesh">Mesh</div> | |
| </div> | |
| <div id="pane-agent" class="pane active"></div> | |
| <div id="pane-news" class="pane"></div> | |
| <div id="pane-mesh" class="pane"> | |
| <div class="row" style="margin-bottom:12px"> | |
| <button id="share-signals">Share active signals to mesh</button> | |
| <span class="muted">broadcasts News-tab signals to all connected browsers</span> | |
| </div> | |
| <div id="mesh-mount"></div> | |
| </div> | |
| </div> | |
| <!-- easter egg: press "e" to toggle a global live news ticker --> | |
| <div id="egg-ticker" class="egg-ticker hidden"> | |
| <span class="egg-label">⚡ LIVE</span> | |
| <div class="egg-ticker-track" id="egg-track">press “e” again to hide…</div> | |
| </div> | |
| <!-- PeerJS (WebRTC signaling broker) — JavaScript only, no server of ours --> | |
| <script src="https://unpkg.com/peerjs@1.5.4/dist/peerjs.min.js"></script> | |
| <script type="module" src="./index.js"></script> | |
| </body> | |
| </html> | |