← Commit history
bin/nb-dashboard+38−11@@ -20,7 +20,7 @@ const { execFile, spawn } = require('child_process'); const fs = require('fs'); const crypto = require('crypto'); -const VERSION = '0.10.2';+const VERSION = '0.10.3'; // SHIP-NIGHT PARK (dock-bar thread relaying John's ruling, 2026-08-23): /ai-threads/run-prompt has an // unverified crash fix (it has killed the whole ah process) and ANY /ai-threads call wakes ah's parked // AI backend - so "Try it" could crash the app the user is standing in. Parked, not removed: flip this@@ -274,6 +274,12 @@ function page() { .footer-note { font-size:.75rem; color:#8b949e; } .footer-note code { font-size:11px; } .restore-btn { width:fit-content; padding:.4rem .85rem; background:transparent; border:1px solid #30363d; border-radius:.375rem; color:#c9d1d9; font-size:.8125rem; font-weight:600; cursor:pointer; } .restore-btn:hover { background:#1c2128; } .restore-btn.armed { color:#f85149; border-color:rgba(255,107,107,.5); background:rgba(255,107,107,.08); }+ .toast { position:fixed; left:50%; bottom:28px; transform:translate(-50%, 16px); display:flex; align-items:center; gap:10px;+ background:#132420; color:#e8eef4; border:1px solid rgba(63,185,80,.55); border-left:3px solid #3fb950; border-radius:10px;+ padding:10px 16px; font-size:13.5px; opacity:0; pointer-events:none; transition:opacity .18s ease, transform .18s ease; z-index:50; max-width:90vw; }+ .toast.show { opacity:1; transform:translate(-50%, 0); }+ .toast.warn { border-color:rgba(243,178,62,.55); border-left-color:var(--amber); background:#241f13; }+ .toast-ico { font-size:15px; color:#3fb950; } .toast.warn .toast-ico { color:var(--amber); } .explain { margin-top:12px; border:1px solid rgba(0,206,194,.35); border-radius:10px; padding:12px 14px; background:rgba(0,206,194,.05); } table { width:100%; border-collapse:collapse; margin-top:10px; font-size:13px; } th { text-align:left; color:var(--dim); font-weight:500; font-size:11.5px; text-transform:uppercase; letter-spacing:.08em; padding:4px 8px; border-bottom:1px solid var(--edge); }@@ -372,21 +378,42 @@ async function loadSay(){ try { const r = await fetch('api/say'); const d = await r.json(); document.getElementById('say-row').innerHTML = (d.say||[]).map((p,i)=>- '<span class="chip"><span class="ph" title="click to copy" onclick="copySay('+i+',this)">'+hiNoun(p)+'</span>'+(d.tryit?'<button class="try" onclick="trySay('+i+',this)">Try it</button>':'')+'</span>').join('');+ '<span class="chip"><span class="ph" title="click to copy" onclick="copySay('+i+',this)">'+hiNoun(p)+'</span><button class="try" onclick="trySay('+i+',this)">Try it</button></span>').join(''); window.__say = d.say || []; } catch(e){} } function copySay(i, el){ try { navigator.clipboard.writeText(window.__say[i]); const o=el.innerHTML; el.textContent='copied'; setTimeout(()=>{el.innerHTML=o;},900); } catch(e){} }-async function trySay(i, btn){- const phrase = window.__say[i]; const note = document.getElementById('say-note');- btn.textContent = '...';+function writeClipboard(text){+ // navigator.clipboard needs a secure context + permission; webviews sometimes deny it silently.+ // The textarea/execCommand fallback works everywhere a page can focus itself.+ return new Promise((resolve) => {+ try { navigator.clipboard.writeText(text).then(() => resolve(true), () => resolve(fallbackCopy(text))); }+ catch(e) { resolve(fallbackCopy(text)); }+ });+}+function fallbackCopy(text){ try {- const r = await fetch('api/try', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ phrase }) });- const d = await r.json();- if (d.staged) { btn.textContent = 'Staged'; note.innerHTML = 'Staged <em>"'+phrase+'"</em> into your nb AI thread, unsent - review it and press enter.'; }- else { try { navigator.clipboard.writeText(phrase); } catch(e){} btn.textContent = 'Copied'; note.innerHTML = 'Copied <em>"'+phrase+'"</em>. Could not stage it into a thread ('+(d.reason||('status '+d.status))+'), so paste it to your AI.'; }- } catch(e) { btn.textContent = 'Try it'; }- setTimeout(()=>{ btn.textContent='Try it'; }, 2500);+ const ta = document.createElement('textarea');+ ta.value = text; ta.setAttribute('readonly',''); ta.style.position='fixed'; ta.style.left='-9999px';+ document.body.appendChild(ta); ta.select(); const ok = document.execCommand('copy'); ta.remove(); return ok;+ } catch(e){ return false; }+}+let _toastT = null;+function toast(msg, ok){+ let t = document.getElementById('toast');+ if (!t) { t = document.createElement('div'); t.id='toast'; document.body.appendChild(t); }+ t.className = 'toast ' + (ok ? 'ok' : 'warn');+ t.innerHTML = '<span class="toast-ico">' + (ok ? '✓' : '⚠') + '</span>' + msg;+ t.classList.add('show');+ clearTimeout(_toastT); _toastT = setTimeout(() => t.classList.remove('show'), 4000);+}+async function trySay(i, btn){+ // SHIP UX (John, 2026-08-23): Try it copies the prompt and SAYS SO - no AI-thread calls of any kind+ // (that path is parked server-side too; the staging code sleeps behind TRYIT_PARKED).+ const phrase = window.__say[i];+ const ok = await writeClipboard(phrase);+ if (ok) toast('Copied to clipboard. Paste it into any AI thread.', true);+ else toast('Could not copy automatically - select the phrase and copy it yourself.', false); } loadSay(); // ---- settings modal ----
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "nb-bridge",- "version": "0.10.2",+ "version": "0.10.3", "type": "app", "title": "NB - Native Browser Bridge", "description": "This package installs the SKILLS into your container so your AI knows how to drive your real signed-in Chrome and Edge; Bridge loads the bridge runtime itself from the release zip.",
page.json+70−6@@ -16,10 +16,10 @@ "chrome", "edge", "nbrowser",- "ai-agent",- "developer-tools",- "vibe-coding",- "native-messaging"+ "pup",+ "native-messaging",+ "credentials",+ "chip-fetcher" ], "status": "live", "discovery_triggers": [@@ -70,5 +70,69 @@ }, "visibility": "public", "brief": "Your real browser, driven by AI.",- "discovery_pitch": "nbe (the Adom Native Browser Extension, layer 2 of nb) drives the user's REAL signed-in Chrome/Edge via nbrowser_* verbs: use it for LOGGING IN, filling forms, and downloading behind auth with their OWN saved passwords - pup cannot (it is a fresh Chrome-for-Testing with no logins). Next moves: run 'adom-bridge bridge_list' to check it is installed; if not, OFFER to install it (see the installing-the-extension skill) - never make the user do it. If this container lacks the skills, 'adom-wiki pkg install adom/nb-bridge'. Check readiness with 'adom-bridge nbrowser_readiness' (state:needs-extension means offer the install)."-}+ "discovery_pitch": "nbe (the Adom Native Browser Extension, layer 2 of nb) drives the user's REAL signed-in Chrome/Edge via nbrowser_* verbs: use it for LOGGING IN, filling forms, and downloading behind auth with their OWN saved passwords - pup cannot (it is a fresh Chrome-for-Testing with no logins). Next moves: run 'adom-bridge bridge_list' to check it is installed; if not, OFFER to install it (see the installing-the-extension skill) - never make the user do it. If this container lacks the skills, 'adom-wiki pkg install adom/nb-bridge'. Check readiness with 'adom-bridge nbrowser_readiness' (state:needs-extension means offer the install).",+ "version": "0.10.3",+ "type": "app",+ "description": "This package installs the SKILLS into your container so your AI knows how to drive your real signed-in Chrome and Edge; Bridge loads the bridge runtime itself from the release zip.",+ "install": {+ "binary_name": "nb-bridge"+ },+ "files": [+ "screenshots/hero.png",+ "skills/adom-native-browser/SKILL.md",+ "skills/driving-sites/SKILL.md",+ "skills/driving-the-desktop/SKILL.md",+ "skills/driving-the-extension/SKILL.md",+ "skills/driving-the-extension-demo/SKILL.md",+ "skills/installing-the-extension/SKILL.md",+ "skills/native-browser-recording/SKILL.md",+ "skills/native-browser-safety/SKILL.md",+ "skills/reading-google-messages/SKILL.md",+ "skills/uninstalling-the-extension/SKILL.md",+ "bin/nb-dashboard",+ "docs/img/dock-nb.svg",+ "docs/img/settings/overlay-on.png",+ "docs/img/settings/overlay-off.png",+ "docs/img/settings/activity-progress.png",+ "docs/img/settings/activity-flash.png",+ "docs/img/settings/activity-off.png",+ "dockbar.json",+ "install.sh",+ "uninstall.sh",+ "package.json"+ ],+ "scripts": {+ "install": "./install.sh",+ "uninstall": "./uninstall.sh"+ },+ "dependencies": {+ "adom/adom-bridge": "^2.0.3"+ },+ "sample_prompts": [+ {+ "label": "Open in my browser",+ "prompt": "open the wiki in my real chrome work profile"+ },+ {+ "label": "Log in for me",+ "prompt": "log into digikey for me using my saved password"+ },+ {+ "label": "Form behind a login",+ "prompt": "fill out this form on the site I'm already signed into"+ },+ {+ "label": "Download behind auth",+ "prompt": "download that datasheet, it's behind a login"+ },+ {+ "label": "Install the extension",+ "prompt": "install the adom native browser extension in my chrome"+ }+ ],+ "org": "adom",+ "author": {+ "name": "John Lauer",+ "email": "[email protected]"+ }+}