relay-companion 0.1.310 → 0.1.312
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.
- package/overlay/inbox.html +60 -20
- package/package.json +1 -1
package/overlay/inbox.html
CHANGED
|
@@ -4617,6 +4617,7 @@
|
|
|
4617
4617
|
removeQueuedRunMessage(key, messageId);
|
|
4618
4618
|
requestWorkDrafts.delete(key);
|
|
4619
4619
|
setRowNote(key, steerNow ? "Steered into the current turn." : "Queued follow-up started.", "ok");
|
|
4620
|
+
fadeRowNoteLater(key, 3000);
|
|
4620
4621
|
renderAll();
|
|
4621
4622
|
return;
|
|
4622
4623
|
}
|
|
@@ -5103,11 +5104,13 @@
|
|
|
5103
5104
|
const rt = routeFor(id);
|
|
5104
5105
|
const note = String((b.closest(".ta-dock")?.querySelector(".ta-note") || {}).value || "").trim();
|
|
5105
5106
|
const ranAlready = requestHasNativeSession(requestRows().find((row) => row.id === id));
|
|
5106
|
-
setRowNote(id, ranAlready
|
|
5107
|
-
? `Opening this exact session in ${rt.app}…`
|
|
5108
|
-
: `Opening a new ${rt.app} session with the Relay documents${note ? " and your draft note" : ""}.`, "ok");
|
|
5109
5107
|
if (ranAlready && window.relay.openInCurrent) window.relay.openInCurrent(id, hostKeyFor(rt.app));
|
|
5110
5108
|
else openRelayFromUI(id, "relay", "fresh", hostKeyFor(rt.app), note);
|
|
5109
|
+
// AFTER the open, never before: openRelayFromUI clears the row's note
|
|
5110
|
+
// first (a stale failure must not outlive the retry), so a note written
|
|
5111
|
+
// above this line vanished the same instant — the trap already found on
|
|
5112
|
+
// the per-relay rows.
|
|
5113
|
+
setOpeningNote(id, `Opening in ${rt.app}…`);
|
|
5111
5114
|
if (onDone) onDone("open-in");
|
|
5112
5115
|
});
|
|
5113
5116
|
}
|
|
@@ -5122,7 +5125,7 @@
|
|
|
5122
5125
|
const res = await window.relay.openRunSession(id).catch((error) => ({ ok: false, error: String(error?.message || error) }));
|
|
5123
5126
|
b.disabled = false;
|
|
5124
5127
|
if (!res?.ok) setRowNote(id, res?.error || "That Claude session is not ready to open yet.", "err");
|
|
5125
|
-
else setRowNote(id, "Opened
|
|
5128
|
+
else { setRowNote(id, "Opened in Claude Code.", "ok"); fadeRowNoteLater(id, 3000); }
|
|
5126
5129
|
if (res?.ok && onDone) onDone("open-in");
|
|
5127
5130
|
});
|
|
5128
5131
|
}
|
|
@@ -5295,6 +5298,7 @@
|
|
|
5295
5298
|
if (box) { box.value = ""; clearStagedFiles(box); }
|
|
5296
5299
|
requestWorkDrafts.delete(key);
|
|
5297
5300
|
setRowNote(id, "Queued for when the current turn finishes.", "ok");
|
|
5301
|
+
fadeRowNoteLater(id, 5000);
|
|
5298
5302
|
renderAll();
|
|
5299
5303
|
return;
|
|
5300
5304
|
}
|
|
@@ -5308,6 +5312,7 @@
|
|
|
5308
5312
|
if (box) { box.value = ""; clearStagedFiles(box); }
|
|
5309
5313
|
requestWorkDrafts.delete(key);
|
|
5310
5314
|
setRowNote(id, "Follow-up sent into the session.", "ok");
|
|
5315
|
+
fadeRowNoteLater(id, 3000);
|
|
5311
5316
|
renderAll();
|
|
5312
5317
|
}
|
|
5313
5318
|
else {
|
|
@@ -6016,6 +6021,7 @@
|
|
|
6016
6021
|
input.value = "";
|
|
6017
6022
|
readerHumanDrafts.delete(String(r.id));
|
|
6018
6023
|
setRowNote(r.id, `Sent to ${sender}.`, "ok");
|
|
6024
|
+
fadeRowNoteLater(r.id, 3000);
|
|
6019
6025
|
renderReader();
|
|
6020
6026
|
};
|
|
6021
6027
|
send.addEventListener("click", doSend);
|
|
@@ -6028,12 +6034,15 @@
|
|
|
6028
6034
|
const n = whole ? threadMessages().filter((m) => m.threadId === r.threadId).length : 0;
|
|
6029
6035
|
// Continuity is judged BEFORE the hand-off marks the conversation.
|
|
6030
6036
|
const knows = threadHanded(r.threadId || r.id);
|
|
6031
|
-
|
|
6037
|
+
// An in-chat open answers with its own note (staged / delivered / woken),
|
|
6038
|
+
// which supersedes this one; the paths that answer with nothing used to
|
|
6039
|
+
// leave this line up for good, so it retires with the open too.
|
|
6040
|
+
openRelayFromUI(r.id, r.outbound ? "sent" : "relay", "current");
|
|
6041
|
+
setOpeningNote(r.id, whole
|
|
6032
6042
|
? (knows ? `Opening the whole conversation (${n} relays) in the ${agentAppName()} chat that knows it — staged…`
|
|
6033
6043
|
: `Opening ${agentAppName()} on this whole conversation (${n} relays) — staged, nothing sends until you do…`)
|
|
6034
6044
|
: (knows ? `Reopening the ${agentAppName()} chat that knows this conversation…`
|
|
6035
|
-
: `Opening ${agentAppName()} on this conversation — staged, nothing sends until you do…`)
|
|
6036
|
-
openRelayFromUI(r.id, r.outbound ? "sent" : "relay", "current");
|
|
6045
|
+
: `Opening ${agentAppName()} on this conversation — staged, nothing sends until you do…`));
|
|
6037
6046
|
renderReader();
|
|
6038
6047
|
});
|
|
6039
6048
|
// The reader's old "open in a window" hatch pointed at preview.html too;
|
|
@@ -6355,6 +6364,45 @@
|
|
|
6355
6364
|
el.classList.remove("ok");
|
|
6356
6365
|
}
|
|
6357
6366
|
}
|
|
6367
|
+
// A GREEN NOTE IS TRANSIENT; AN ERROR IS NOT. Every "ok" note narrates an
|
|
6368
|
+
// action the user just took, so it retires when that action lands — main
|
|
6369
|
+
// reports the open home (openDone), or a few seconds pass. An error stays: it
|
|
6370
|
+
// is the only record of a click that went nowhere. Both timers are guarded by
|
|
6371
|
+
// the text that scheduled them, so anything written over the note since is
|
|
6372
|
+
// the newer truth and survives (David, live, on a finished open: the green
|
|
6373
|
+
// line just sat there).
|
|
6374
|
+
const noteTimers = new Map(); // id -> { kind, text, shownAt, timer }
|
|
6375
|
+
const OPENING_NOTE_FALLBACK_MS = 6000; // the safety the row spinner already keeps
|
|
6376
|
+
const OPENING_NOTE_MIN_MS = 900; // a note nobody can read is worse than none
|
|
6377
|
+
function noteStillReads(id, text) { return rowNotes.get(id)?.text === text; }
|
|
6378
|
+
function cancelNoteTimer(id) {
|
|
6379
|
+
const entry = noteTimers.get(id);
|
|
6380
|
+
if (!entry) return;
|
|
6381
|
+
clearTimeout(entry.timer);
|
|
6382
|
+
noteTimers.delete(id);
|
|
6383
|
+
}
|
|
6384
|
+
function scheduleNoteClear(id, { kind, text, shownAt, ms }) {
|
|
6385
|
+
cancelNoteTimer(id);
|
|
6386
|
+
noteTimers.set(id, { kind, text, shownAt, timer: setTimeout(() => {
|
|
6387
|
+
cancelNoteTimer(id);
|
|
6388
|
+
if (noteStillReads(id, text)) clearRowNote(id);
|
|
6389
|
+
}, ms) });
|
|
6390
|
+
}
|
|
6391
|
+
// "Opening in {app}…" — up until the open lands, never longer.
|
|
6392
|
+
function setOpeningNote(id, text) {
|
|
6393
|
+
setRowNote(id, text, "ok");
|
|
6394
|
+
scheduleNoteClear(id, { kind: "opening", text, shownAt: performance.now(), ms: OPENING_NOTE_FALLBACK_MS });
|
|
6395
|
+
}
|
|
6396
|
+
// The open landed: let the line finish being readable, then take it away.
|
|
6397
|
+
function retireOpeningNote(id) {
|
|
6398
|
+
const entry = noteTimers.get(id);
|
|
6399
|
+
if (!entry || entry.kind !== "opening" || !noteStillReads(id, entry.text)) return;
|
|
6400
|
+
scheduleNoteClear(id, { ...entry, ms: Math.max(0, OPENING_NOTE_MIN_MS - (performance.now() - entry.shownAt)) });
|
|
6401
|
+
}
|
|
6402
|
+
// A note that reports a FINISHED action: read it, then it goes.
|
|
6403
|
+
function fadeRowNoteLater(id, ms) {
|
|
6404
|
+
scheduleNoteClear(id, { kind: "fade", text: rowNotes.get(id)?.text, shownAt: performance.now(), ms });
|
|
6405
|
+
}
|
|
6358
6406
|
|
|
6359
6407
|
async function runRowAction(id, btn, fn) {
|
|
6360
6408
|
if (!btn || btn.disabled) return;
|
|
@@ -8480,7 +8528,7 @@
|
|
|
8480
8528
|
// outlive the retry), so the answer to THIS click is written after it —
|
|
8481
8529
|
// written before, it vanished the same instant (found on the clone).
|
|
8482
8530
|
openRelayFromUI(id, source, "open", host);
|
|
8483
|
-
|
|
8531
|
+
setOpeningNote(id, `Opening in ${app}…`);
|
|
8484
8532
|
});
|
|
8485
8533
|
}
|
|
8486
8534
|
}
|
|
@@ -10165,7 +10213,7 @@
|
|
|
10165
10213
|
// The OS window was just re-shown and main re-asserted click-through; reset the
|
|
10166
10214
|
// local flag so the next mousemove re-runs the interactivity handshake cleanly.
|
|
10167
10215
|
if (window.relay.onShown) window.relay.onShown(() => { interactive = false; });
|
|
10168
|
-
if (window.relay.onOpenDone) window.relay.onOpenDone((id) => stopOpening(id));
|
|
10216
|
+
if (window.relay.onOpenDone) window.relay.onOpenDone((id) => { stopOpening(id); retireOpeningNote(id); });
|
|
10169
10217
|
// Visible confirmation for "Open in current chat". Two honest states:
|
|
10170
10218
|
// waiting — staged for an idle chat; ONLY that chat's next turn can ingest
|
|
10171
10219
|
// it, so the note stays up (surviving re-renders) until delivery
|
|
@@ -10173,25 +10221,17 @@
|
|
|
10173
10221
|
// delivered — main watched the consume-once injection file disappear; the
|
|
10174
10222
|
// instruction is IN the chat. Green check, then fade.
|
|
10175
10223
|
// A Codex bridge/automation hand-off keeps the classic brief confirmation.
|
|
10176
|
-
const injectedNoteTimers = new Map();
|
|
10177
10224
|
const deliveredChatNames = new Map(); // packetId -> chat the injection was staged for
|
|
10178
|
-
function fadeRowNoteLater(id, ms) {
|
|
10179
|
-
if (injectedNoteTimers.has(id)) clearTimeout(injectedNoteTimers.get(id));
|
|
10180
|
-
injectedNoteTimers.set(id, setTimeout(() => {
|
|
10181
|
-
injectedNoteTimers.delete(id);
|
|
10182
|
-
clearRowNote(id);
|
|
10183
|
-
}, ms));
|
|
10184
|
-
}
|
|
10185
10225
|
if (window.relay.onInjected) window.relay.onInjected((id, info) => {
|
|
10186
10226
|
if (info.channel) {
|
|
10187
10227
|
// Wake path: a channel event starts a real turn even in an idle chat.
|
|
10188
|
-
|
|
10228
|
+
cancelNoteTimer(id);
|
|
10189
10229
|
setRowNote(id, "Pushed to your current Claude chat — waking it now…", "ok");
|
|
10190
10230
|
return;
|
|
10191
10231
|
}
|
|
10192
10232
|
if (info.awaitingTurn) {
|
|
10193
10233
|
// Persistent until delivery / the auto-fresh hop — never a silent limbo.
|
|
10194
|
-
|
|
10234
|
+
cancelNoteTimer(id);
|
|
10195
10235
|
// Name the destination chat when we know it: "your \u201cgranular-relay\u201d chat"
|
|
10196
10236
|
// tells the user WHERE to look; "your current chat" leaves them hunting
|
|
10197
10237
|
// (field report: the injection landed in a chat they never found).
|
|
@@ -10219,7 +10259,7 @@
|
|
|
10219
10259
|
// the explanation while the user is reading it. Cleared by the next open of the row.
|
|
10220
10260
|
if (window.relay.onOpenError) window.relay.onOpenError((id, message) => {
|
|
10221
10261
|
if (!message) return;
|
|
10222
|
-
|
|
10262
|
+
cancelNoteTimer(id);
|
|
10223
10263
|
setRowNote(id, message, "err");
|
|
10224
10264
|
});
|
|
10225
10265
|
window.relay.refresh().then((p) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relay-companion",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.312",
|
|
4
4
|
"description": "Companion CLI for Relay (sendrelays.com): pairs this machine with your Relay account so coding agents like Claude Code and Codex can send and receive Relay messages.",
|
|
5
5
|
"homepage": "https://sendrelays.com",
|
|
6
6
|
"repository": {
|