bootstrap
Adom Hydrogen (platform base)
Public Made by Adomby adom
Layer Hydrogen's workspace onto a standard Adom workstation.
← Commit history
skills/hd-eval/SKILL.mddeleted−159@@ -1,159 +0,0 @@-name: hd-eval-description: >- Inject JavaScript into ANY surface of the Hydrogen Desktop window from inside- the workspace — the app shell (top nav, dialogs), the VS Code workbench, the- wiki panel, or the Claude chat webview. Backed by the control API's POST- /eval-in, which connects directly to the right CDP target and returns the JS- value. Use it to build add-ons (e.g. a usage meter in the top bar), demo- overlays (a moving arrow that points at things), highlight elements, read DOM- state, or drive the UI. Works in dev AND release builds. Trigger words:- eval-in, inject ui, eval into hd, hd overlay, demo arrow, highlight element,- add-on, addon, usage meter, inject script, target shell, target workbench,- target claude, find probe, eval-in targets, point at element, nav widget.--# hd-eval — inject JS into any HD surface (`POST /eval-in`)--The HD window is **not one page**. It's a tree of cross-origin surfaces, each a-separate Chrome DevTools Protocol (CDP) target/context:--| friendly target | what it is | typical use |-|---|---|---|-| `shell` | the HD app shell (Svelte): top nav, logo menu, dialogs, the floating layer | inject add-ons/overlays that float over everything |-| `workbench` | the VS Code editor (code-server) | activity bar, tabs, the "Claude Code: Open" button |-| `wiki` | the wiki panel iframe | wiki DOM |-| `claude` | the Claude Code chat **webview** (only exists while the panel is open) | the chat input + send button |--`/eval-in` figures out where a surface lives (it differs between dev and release —-see "Why two paths" below) and runs your JS there, returning the value.--## Finding the control port + reaching it from the workspace--The control API port is **dynamic** (auto-resolves conflicts per launch). From inside-the workspace, reach HD on `127.0.0.1` (WSL2 mirrored networking shares loopback with the-Windows host). Read the live control URL from the discovery file `~/.adom/hd-control-url`-(your non-interactive Bash shells don't source `.bashrc`/`profile.d`, so the env var alone-is unreliable):--```bash-BASE="$(cat ~/.adom/hd-control-url)" # http://127.0.0.1:<dynamic>-```--(Never hardcode the port — read the live URL from `~/.adom/hd-control-url`.)--## Discover the surfaces--```bash-curl -sf $BASE/targets | jq .-# each surface has a friendly alias (shell|workbench|wiki|claude), url, id.-# the claude target (Claude chat webview) appears only when that panel is open.-```--`POST /eval-in` with no `target` (or no `js`) also returns the surface list, so an-agent can ask "what can I inject into?" and pick.--## The call--```-POST /eval-in-{- "target": "shell" | "workbench" | "wiki" | "claude" | "<url-substring>" | "<id>",- "js": "<javascript; value of last expression is returned>",- "find": "<OPTIONAL JS expression>", // disambiguate among same-origin contexts- "frame": "<url-substring | index>", // OPTIONAL nested-context selector- "awaitPromise": true // OPTIONAL (default true)-}-→ { "ok": true, "value": <result>, "target": "<matched url>", "contextId": N, "frames": [...] }-```--### `find` — the important one--A surface can expose **several same-origin nested contexts** (the workbench-exposes ~6 `localhost:7380` contexts). Origin alone is ambiguous, so pass `find` —-a JS **expression** (e.g. `document.querySelector('...')`, NOT a bare CSS selector).-`/eval-in` evals it in each candidate context and runs your `js` in the **first-one where `find` is truthy**.--## Recipes--### Add-on: a widget in the top nav (e.g. a usage meter) — target `shell`-Inject once; it persists until reload. Append your element to `nav` in the shell.--### Demo overlay: a red arrow over everything that you reposition — target `shell`-Inject a `position:fixed; z-index:2147483647; pointer-events:none` element with a-CSS `transition` into the shell `body`, then move it with cheap follow-up evals-(it glides). The shell floats over all iframes, so window coords work everywhere.--### Highlight an element (even deep in the workbench / Claude webview)-Eval **inside the element's own surface** and set `outline` — no coordinate math.-Use `find` to land in the right context, e.g.-`find: document.querySelector("a[aria-label='Claude Code: Open']")`.--### Type into & submit the Claude chat (panel must be open)-Stable selectors (NOT the CSS-module hash classes like `.messageInput_xxxx`,-which change per build):-- input = `[aria-label='Message input']` (a `contenteditable` div) — focus it,- then `document.execCommand('insertText', false, text)`.-- submit = `button[type='submit'][data-permission-mode]` (disabled until text) —- `.click()` it; fallback = dispatch an `Enter` keydown on the input.--## Why two paths (dev vs release) — you don't have to care--- **Dev build:** shell is `localhost:1420`; the workbench (`localhost:7380`) is- same-process, so it's a context INSIDE the shell page (not a separate target).- `/eval-in` falls back to the shell and filters contexts by origin.-- **Release build:** shell is `tauri.localhost`; the workbench is a different site →- an out-of-process iframe (OOPIF) → its own top-level target. `/eval-in` connects- directly.--You just pass `target:"workbench"` (or `claude`) and `/eval-in` picks the right-path. Prefer it over the older `/eval` (shell only) and `/iframe-eval`-(page-context walk, breaks in release).--## Verify-then-fallback (recommended)--`/eval-in` returns the JS value, so always verify: after an action, eval a probe-and check it; if it fails, try a second technique. The HD "eval-in demo" floating-toolbar does exactly this and logs endpoint + params + verify + fallback per step.--## Don'ts-- Don't hardcode the control port — read `/ports`.-- Don't pass `find` as a bare CSS selector — it's a JS expression.-- Don't select by CSS-module hash classes — use stable attributes (`aria-label`, `data-*`, `role`).-- Don't expect the `claude` webview to exist unless the Claude panel is open.-- Don't use `/eval` for workbench/Claude — it only reaches the shell page.--## Cross-references-- **hd-api** — the full control API surface-- **hd-ui** / **hd-adom-menu** — where UI elements live-- **hd-self-screenshot** — capture the result of your injection--## Recipe: hide/show activity-bar icons (the RIGHT way — persisted, overflow-proof)--Do NOT hide activity-bar icons with `display:none` on `.action-item` — that misses-icons collapsed into the "…" (Additional Views) **overflow** and doesn't persist.-Instead write VS Code's own source-of-truth state, then reload the workbench:--- **DB:** `vscode-web-state-db-global` → store `ItemTable` → key `workbench.activity.pinnedViewlets2`-- It's an array of `{id, pinned, visible, order}`. Set **`pinned:false` to hide** an- icon from the activity bar (works even if it's in the overflow), **`pinned:true` to restore**.-- **IDs:** Explorer `workbench.view.explorer` · Search `workbench.view.search` ·- Source Control `workbench.view.scm` · Run&Debug `workbench.view.debug` ·- Extensions `workbench.view.extensions` (extension views: `workbench.view.extension.<id>`).-- IndexedDB is **per-origin**, so read/write from **any** `localhost:7380` context- (target `workbench`, `find: "(typeof indexedDB!=='undefined')"`) — don't require the- workbench main doc (it's absent during a reload).-- After writing, **reload the workbench** (`iframe.src=iframe.src` on the `shell`, or- `POST /reload-vscode`) so VS Code re-reads `pinnedViewlets2`.--```jsonc-// hide Search / Source Control / Run&Debug (then reload)-{ "target":"workbench", "find":"(typeof indexedDB!=='undefined')", "awaitPromise":true,- "js":"(async function(){function g(d,k){return new Promise(function(r){var o=indexedDB.open(d);o.onsuccess=function(){var b=o.result;var q=b.transaction('ItemTable','readonly').objectStore('ItemTable').get(k);q.onsuccess=function(){b.close();r(q.result)}}})}function p(d,k,v){return new Promise(function(r){var o=indexedDB.open(d);o.onsuccess=function(){var b=o.result;var t=b.transaction('ItemTable','readwrite');t.objectStore('ItemTable').put(v,k);t.oncomplete=function(){b.close();r(true)}}})}var DB='vscode-web-state-db-global',K='workbench.activity.pinnedViewlets2';var a=JSON.parse(await g(DB,K));var ids=['workbench.view.search','workbench.view.scm','workbench.view.debug'];a.forEach(function(e){if(ids.indexOf(e.id)>-1)e.pinned=false});await p(DB,K,JSON.stringify(a));return 'hidden';})()" }-```--The HD demo toolbar's **Hide / Restore activity icons** buttons (`/demo/run-{action:"hide-activitybar"|"show-activitybar"}`) do exactly this. The setup-steps-`configure-vscode` step should hide icons this way too (persisted across the-mid-setup reload), not via CSS or transient `:8821` layout calls.