← Commit history

Publish 0.1.5

John Lauer ·a140889eea ·1mo ago ·parent 89217e6
4 files changed +98−27
adom_antigravity.py+1−1
@@ -21,7 +21,7 @@ from pathlib import Path from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer  ROOT = Path(__file__).resolve().parent-VERSION = '0.1.4'+VERSION = '0.1.5' EXT_ID = 'Google.google-antigravity' CLI_INSTALL = 'https://antigravity.google/cli/install.sh' EXT_DIRS = [Path.home() / '.local/share/code-server/extensions', Path.home() / '.vscode-server/extensions']
dashboard/vendor/newbie-panel.js+95−24
@@ -78,6 +78,19 @@   .anp-left{flex-direction:row; align-items:center; gap:12px;} } +/* A video is content, not an icon in the legacy 220px demo-button column.+ * Opt-in only: existing button-only panels keep their original layout. */+.anp.anp--video{grid-template-columns:minmax(0,720px) minmax(360px,1fr);align-items:start;}+.anp--video .anp-left{min-width:0;display:flex;flex-direction:column;align-items:stretch;gap:12px;}+.anp-video{display:block;width:100%;height:auto;max-height:none;aspect-ratio:16/9;object-fit:contain;background:#080c11;border-radius:8px;}+.anp-video-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px 16px;}+.anp--video .anp-phrase span{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere;padding:10px 8px 10px 0;}+.anp--video .anp-tbl{max-width:none;}+.anp--video .anp-right{align-self:center;}+@container anp (max-width:1099px){+  .anp.anp--video{grid-template-areas:"head" "left" "right";grid-template-columns:minmax(0,1fr);}+}+ .anp-toasts{position:fixed; left:50%; bottom:22px; transform:translateX(-50%); z-index:99999;   display:flex; flex-direction:column-reverse; gap:8px; pointer-events:none;} .anp-toast{pointer-events:auto; display:flex; align-items:center; gap:10px; max-width:min(92vw,520px);@@ -88,7 +101,10 @@ .anp-toast.err{border-left:3px solid #f85149;} .anp-toast.info{border-left:3px solid #00b8b1;} @keyframes anp-in{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}-@media (prefers-reduced-motion:reduce){.anp-toast{animation:none;}}+.anp-toast{position:relative; overflow:hidden;}+.anp-toast-bar{position:absolute; left:0; right:0; bottom:0; height:3px; background:rgba(255,255,255,.08);}+.anp-toast-bar i{display:block; height:100%; width:0; background:#00b8b1; transition:width .4s ease;}+@media (prefers-reduced-motion:reduce){.anp-toast{animation:none;} .anp-toast-bar i{transition:none;}}  .anp-tip{position:fixed; z-index:99999; pointer-events:none; padding:6px 9px; border-radius:6px;   background:#0d1117; border:1px solid #30363d; color:#e6edf3; text-transform:none;@@ -193,7 +209,17 @@     toastHost.appendChild(t);     /* retextable, so a long send can narrate its phases in ONE toast instead of      * stacking six of them on top of each other. */-    t.setText = function (next) { t.innerHTML = esc(next); return t; };+    t.setText = function (next) { t.innerHTML = esc(next); if (t._bar) t.appendChild(t._bar); return t; };+    /* A live send narrates ONE toast: the text comes from Hydrogen's status route (`human`)+     * and the thin bar from its `progress` (John 2026-09-07: "better state and progress+     * information back to the caller"). */+    t.setProgress = function (p) {+      if (typeof p !== 'number') return t;+      if (!t._bar) { t._bar = el('div', 'anp-toast-bar'); t._bar.appendChild(el('i')); t.appendChild(t._bar); }+      t._bar.firstChild.style.width = Math.round(Math.max(0, Math.min(1, p)) * 100) + '%';+      return t;+    };+    t.setKind = function (k) { t.className = 'anp-toast ' + (k || 'info'); return t; };     setTimeout(function () { t.remove(); }, ms || (kind === 'err' ? 7000 : 4000));     return t;   }@@ -244,7 +270,7 @@       })       .catch(function () { return { ok: true, version: null, open: true }; });   }-  function send(base, phrase, appName, slug, onProgress) {+  function send(base, phrase, appName, slug, onProgress, ai) {     return gate(base).then(function (g) {       if (!g.ok) throw new Error('Adom needs updating to send prompts (found ' + g.version + ', needs 1.0.142). Run: adom update');       var ctl = new AbortController(), timer = setTimeout(function () { ctl.abort(); }, 120000);@@ -254,12 +280,8 @@         method: 'POST', signal: ctl.signal,         headers: { 'content-type': 'application/json' },         body: JSON.stringify({-          "new": true, prompt: phrase, submit: true,-          name: appName + ' - say it', tags: ['dock:' + slug],-          /* cfg.ai = the AI this dashboard belongs to (claude|codex|kimi|antigravity). Hydrogen-           * 1.0.231+ shows the picker with it as the countdown default (John 2026-09-06);-           * older Hydrogens ignore the field. */-          prefer: cfg.ai || undefined+          "new": true, prompt: phrase, submit: true, prefer: ai || undefined,+          name: appName + ' - say it', tags: ['dock:' + slug]         })       }).then(function (r) { return r.json().then(function (j) { return { status: r.status, j: j }; }); })         .then(function (res) {@@ -301,7 +323,15 @@                     hint: 'Your AI chat opened. This app does not proxy GET ' +                           '/ai-threads/run-prompt/status yet, so progress cannot be shown.' });                   var state = (st.job && st.job.state) || '';-                  if (typeof onProgress === 'function') { try { onProgress(state, st.job); } catch (e) {} }+                  if (typeof onProgress === 'function') { try { onProgress(state, st.job, st); } catch (e) {} }+                  /* Hydrogen 1.0.240+ says it outright: terminal + succeeded/failed, with a+                   * ready `human` line. Codex, Kimi and Antigravity end in `delivered`. */+                  if (st.terminal === true) {+                    if (st.succeeded === true || state === 'delivered') return resolve({ state: 'delivered', phase: state, ai: st.ai, surface: st.surface });+                    if (st.failed === true) return resolve({ state: 'failed', phase: state, ai: st.ai,+                                                             hint: st.human || (st.error && (st.error.error || st.error)) || state });+                  }+                  if (state === 'delivered') return resolve({ state: 'delivered', phase: state, ai: st.ai });                   /* working or later IS sent, for the user-facing toast. */                   if (state === 'working' || state === 'done' || state === 'needs_you' ||                       state === 'still_working') return resolve({ state: 'delivered', phase: state });@@ -335,7 +365,9 @@     var key = 'anp:' + slug + ':open';      var wrap = el('div', 'anp-wrap');-    var panel = el('section', 'anp');+    var videoCfg = cfg.demo && cfg.demo.video;+    var video = null;+    var panel = el('section', 'anp' + (videoCfg && videoCfg.src ? ' anp--video' : ''));     panel.setAttribute('aria-label', 'Say it: sample prompts for ' + appName);      var head = el('div', 'anp-head');@@ -349,14 +381,26 @@     panel.appendChild(head);      var left = el('div', 'anp-left');+    var controls = left;+    if (videoCfg && videoCfg.src) {+      video = el('video', 'anp-video');+      video.src = videoCfg.src;+      if (videoCfg.poster) video.poster = videoCfg.poster;+      video.controls = true; video.playsInline = true; video.preload = 'none';+      video.setAttribute('aria-label', videoCfg.title || 'Demonstration video');+      left.appendChild(video);+      controls = el('div', 'anp-video-toolbar');+      left.appendChild(controls);+    }     var demo = el('button', 'anp-demo', ICON.play + '<span>' + esc((cfg.demo && cfg.demo.label) || 'Play the demo') + '</span>');     demo.type = 'button';     demo.addEventListener('click', function () {-      if (cfg.demo && typeof cfg.demo.onPlay === 'function') cfg.demo.onPlay();+      if (video) video.play().catch(function () { toast('Press Play on the video to start playback.', 'info'); });+      else if (cfg.demo && typeof cfg.demo.onPlay === 'function') cfg.demo.onPlay();       else toast('This dashboard has no demo wired up yet', 'err');     });-    left.appendChild(demo);-    if (cfg.demo && cfg.demo.meta) left.appendChild(el('span', 'anp-meta', esc(cfg.demo.meta)));+    controls.appendChild(demo);+    if (cfg.demo && cfg.demo.meta) controls.appendChild(el('span', 'anp-meta', esc(cfg.demo.meta)));     panel.appendChild(left);      var right = el('div', 'anp-right');@@ -383,12 +427,19 @@         if (sd.disabled) return;         sd.disabled = true;         var pending = toast('Opening a new AI thread…', 'info', 120000);-        send(base, phrase, appName, slug, function (state, job) {-          if (PHASE[state]) pending.setText(PHASE[state]);-          if (typeof cfg.onProgress === 'function') { try { cfg.onProgress(state, job); } catch (e) {} }-        }).then(function (r) {+        pending.setProgress(0.02);+        send(base, phrase, appName, slug, function (state, job, st) {+          /* Prefer Hydrogen's own words (they name the AI: "opening a Codex thread",+           * "delivered to Antigravity"); the PHASE table is the pre-1.0.240 fallback. */+          var line = (st && st.human) ? (st.human.charAt(0).toUpperCase() + st.human.slice(1) + '…') : PHASE[state];+          if (line) pending.setText(line);+          if (st && typeof st.progress === 'number') pending.setProgress(st.progress);+          if (typeof cfg.onProgress === 'function') { try { cfg.onProgress(state, job, st); } catch (e) {} }+        }, cfg.ai).then(function (r) {           pending.remove();-          if (r.state === 'delivered') toast('Sent. Your AI thread has the prompt.', 'ok');+          var who = r.ai ? r.ai : 'your AI';+          if (r.state === 'delivered') toast('Sent to ' + who + '. The thread has your prompt' + (r.surface === 'tab' ? ', in an editor tab.' : '.'), 'ok', 5000);+          else if (r.state === 'failed') toast((r.hint || 'The send failed.') + ' Check your AI threads before sending again.', 'err', 9000);           else toast('Thread opened, delivery not confirmed. Check your AI threads before sending again.', 'info', 8000);         }).catch(function (err) {           pending.remove();@@ -415,6 +466,7 @@      * open pins it open forever and graduating it closed marks it user-dismissed. It looks      * like "the panel sometimes ignores graduation". (lbr, 2026-08-25) */     function setOpen(open, remember, source) {+      if (!open && video) video.pause();       panel.hidden = !open;       wrap.style.display = open ? '' : 'none';   /* no dead margin when collapsed */       if (toggleBtn) {@@ -461,7 +513,7 @@       close:  function (o) { setOpen(false, !(o && o.remember === false), 'api'); },       toggle: function () { setOpen(panel.hidden, true, 'api'); },       isOpen: function () { return !panel.hidden; },-      overBudget: over, toast: toast, selfCheck: function () { return selfCheck(panel); }+      video: video, overBudget: over, toast: toast, selfCheck: function () { return selfCheck(panel); }     };   } @@ -536,8 +588,22 @@     if (!aligned) ok = false;     rows.push({ what: 'copy icons in one column', want: '1 x-position', got: Object.keys(xs).length + ' x-positions', pass: aligned });     var cols = cs.gridTemplateColumns.split(' ').filter(Boolean).length;-    rows.push({ what: 'columns at this width', want: panel.getBoundingClientRect().width >= TWO_COL_MIN ? '2' : '1',-                got: String(cols), pass: true });+    var withVideo = panel.classList.contains('anp--video');+    var expectedCols = panel.parentElement.clientWidth >= (withVideo ? 1100 : TWO_COL_MIN) ? 2 : 1;+    var colsOk = cols === expectedCols;+    if (!colsOk) ok = false;+    rows.push({ what: 'columns at this width', want: String(expectedCols), got: String(cols), pass: colsOk });+    if (withVideo) {+      var vr = panel.querySelector('.anp-video').getBoundingClientRect();+      var lr = panel.querySelector('.anp-left').getBoundingClientRect();+      var rr = panel.querySelector('.anp-right').getBoundingClientRect();+      var mediaOk = Math.abs(vr.width - lr.width) < 2 && vr.width >= Math.min(300, lr.width) &&+                    Math.abs(vr.height - vr.width * 9 / 16) < 2 &&+                    (expectedCols === 1 ? rr.top >= lr.bottom - 1 : rr.left >= vr.right);+      if (!mediaOk) ok = false;+      rows.push({what:'video size and placement', want:'16:9, fills its column, no prompt overlap',+        got:Math.round(vr.width)+' × '+Math.round(vr.height)+'px', pass:mediaOk});+    }     /* THE GUTTER ROW. Four apps measured at the SAME 744px pane reported panel widths of      * 734, 735, 702 and 694, because each page put a different amount of its own padding      * around the mount point. 694 is under the 700 threshold, so that app rendered ONE@@ -605,7 +671,12 @@      * remeasured with this probe, neither had less than 141px of slack. A metric that can only      * return one value on success is not evidence of success. */     var spans = [].slice.call(panel.querySelectorAll('.anp-phrase span'));-    if (spans.length) {+    if (withVideo && spans.length) {+      var wrapOk = spans.every(function (sp) { return sp.scrollWidth <= sp.clientWidth + 1 &&+        getComputedStyle(sp).whiteSpace === 'normal'; });+      if (!wrapOk) ok = false;+      rows.push({what:'readable video-panel prompts',want:'wrap without horizontal clipping',got:wrapOk?'all readable':'clipped or nowrap',pass:wrapOk});+    } else if (spans.length) {       var probe = document.createElement('span');       probe.style.cssText = 'position:absolute;visibility:hidden;white-space:nowrap;font:' +                             getComputedStyle(spans[0]).font;@@ -690,6 +761,6 @@   }    root.AdomNewbiePanel = { mount: mount, toast: toast, selfCheck: selfCheck, copyText: copyText,-                           checkSend: checkSend, version: '2.22.0',+                           checkSend: checkSend, version: '2.25.0',                            TWO_COL_MIN: TWO_COL_MIN, PHRASE_BUDGET: PHRASE_BUDGET }; })(typeof window !== 'undefined' ? window : this);
package.json+1−1
@@ -4,7 +4,7 @@   "type": "app",   "title": "Google Antigravity",   "brief": "Google Antigravity in Adom Hydrogen: the editor extension, the agy CLI and a dock dashboard that installs both into your container.",-  "version": "0.1.4",+  "version": "0.1.5",   "description": "Try Google Antigravity inside your Adom workspace. The dock dashboard installs the Google Antigravity VS Code extension into Hydrogen's editor and the agy command line into your WSL2 or Lima container, opens the panel, and reports what is installed and signed in.",   "org": "adom",   "needs_sudo": false,
page.json+1−1
@@ -4,7 +4,7 @@   "type": "app",   "title": "Google Antigravity",   "brief": "Google Antigravity in Adom Hydrogen: the editor extension, the agy CLI and a dock dashboard that installs both into your container.",-  "version": "0.1.4",+  "version": "0.1.5",   "hero": {     "type": "image",     "path": "screenshots/hero.png"