relay-companion 0.1.65 → 0.1.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -301,6 +301,8 @@
301
301
  .row-note { font-size:11.5px; color:var(--muted-2); margin-top:9px; line-height:1.45; }
302
302
  .row-err { font-size:11.5px; color:#b4332a; margin-top:8px; line-height:1.4; }
303
303
  .row-err:empty { display:none; }
304
+ /* Positive variant: injection confirmations reuse the same slot in green. */
305
+ .row-err.ok { color:#1a7f37; }
304
306
 
305
307
  /* ---- task items — also flat, divided by one hairline ---- */
306
308
  .task {
@@ -2629,6 +2631,22 @@
2629
2631
  // local flag so the next mousemove re-runs the interactivity handshake cleanly.
2630
2632
  if (window.relay.onShown) window.relay.onShown(() => { interactive = false; });
2631
2633
  if (window.relay.onOpenDone) window.relay.onOpenDone((id) => stopOpening(id));
2634
+ // Visible confirmation for "Open in current chat": the injection is staged,
2635
+ // but the agent only ingests it at its next checkpoint (tool boundary / turn
2636
+ // end — or your next message if it's idle). Say so, briefly, in green.
2637
+ const injectedNoteTimers = new Map();
2638
+ if (window.relay.onInjected) window.relay.onInjected((id, info) => {
2639
+ const hostName = info.host === "codex" ? "Codex" : "Claude";
2640
+ for (const el of document.querySelectorAll(`[data-err="${CSS.escape(id)}"]`)) {
2641
+ el.textContent = `✓ Sent to your current ${hostName} chat — it will pick this up at its next checkpoint (or your next message if it's idle).`;
2642
+ el.classList.add("ok");
2643
+ if (injectedNoteTimers.has(id)) clearTimeout(injectedNoteTimers.get(id));
2644
+ injectedNoteTimers.set(id, setTimeout(() => {
2645
+ injectedNoteTimers.delete(id);
2646
+ if (el.isConnected) { el.textContent = ""; el.classList.remove("ok"); }
2647
+ }, 12000));
2648
+ }
2649
+ });
2632
2650
  window.relay.refresh().then((p) => {
2633
2651
  onPayload(p);
2634
2652
  console.log("[overlay] booted ok, relays=" + (p && p.relays ? p.relays.length : 0) + " sent=" + (p && p.sent ? p.sent.length : 0));
package/overlay/main.cjs CHANGED
@@ -1533,6 +1533,11 @@ async function openPacketInCurrent(packetId) {
1533
1533
  ackPacket(packetId);
1534
1534
  if (win && !win.isDestroyed()) win.webContents.send("openDone", packetId); // stop the row spinner
1535
1535
  };
1536
+ // Confirm the staging in the UI: without this, a successful injection is
1537
+ // indistinguishable from a dead click until the agent's next checkpoint.
1538
+ const confirmInjected = (host) => {
1539
+ if (win && !win.isDestroyed()) win.webContents.send("injected", packetId, { host });
1540
+ };
1536
1541
  const fallbackFresh = () => {
1537
1542
  openPacket(packetId, { fresh: true }).catch((error) =>
1538
1543
  console.error("[overlay] openInCurrent fresh fallback failed:", error && error.message),
@@ -1562,7 +1567,10 @@ async function openPacketInCurrent(packetId) {
1562
1567
  desktop,
1563
1568
  codexRunning,
1564
1569
  activateHost: () => activateHost("codex", bundle),
1565
- finish,
1570
+ finish: () => {
1571
+ confirmInjected("codex");
1572
+ finish();
1573
+ },
1566
1574
  fallbackFresh,
1567
1575
  },
1568
1576
  ),
@@ -1600,6 +1608,7 @@ async function openPacketInCurrent(packetId) {
1600
1608
  // Raise Claude Desktop so the user watches the instruction land; a
1601
1609
  // terminal-only session stays where it is (its window is not ours to raise).
1602
1610
  if (target.source === "desktop") activateHost("claude", bundle);
1611
+ confirmInjected("claude");
1603
1612
  finish();
1604
1613
  });
1605
1614
  }
@@ -5,6 +5,9 @@ contextBridge.exposeInMainWorld("relay", {
5
5
  onInbox: (cb) => ipcRenderer.on("inbox", (_e, payload) => cb(payload)),
6
6
  onNewRelay: (cb) => ipcRenderer.on("newRelay", (_e, relay, opts) => cb(relay, opts || {})),
7
7
  onOpenDone: (cb) => ipcRenderer.on("openDone", (_e, id) => cb(id)),
8
+ // "Open in current chat" staged an injection: the UI confirms it so the
9
+ // click has visible feedback (delivery is checkpoint-based, not instant).
10
+ onInjected: (cb) => ipcRenderer.on("injected", (_e, id, info) => cb(id, info || {})),
8
11
  onOpenFull: (cb) => ipcRenderer.on("openFull", () => cb()), // status-area icon clicked
9
12
  onShown: (cb) => ipcRenderer.on("shown", () => cb()), // window re-shown; reset hover handshake
10
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relay-companion",
3
- "version": "0.1.65",
3
+ "version": "0.1.66",
4
4
  "description": "Relay companion for ordinary messages, with dormant coordination features available only by explicit opt-in.",
5
5
  "type": "module",
6
6
  "bin": {