relay-companion 0.1.89 → 0.1.90
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 +49 -5
- package/overlay/main.cjs +13 -6
- package/package.json +1 -1
package/overlay/inbox.html
CHANGED
|
@@ -703,7 +703,8 @@
|
|
|
703
703
|
the reused .open-actions light-built panel — never from borders. */
|
|
704
704
|
.sv-head { display:flex; align-items:center; justify-content:space-between; padding:10px 16px 8px; }
|
|
705
705
|
.sv-title { font-family:var(--serif); font-size:16px; font-weight:500; color:var(--ink); }
|
|
706
|
-
|
|
706
|
+
/* First element in the pane now (no header above), so it owns the top inset. */
|
|
707
|
+
.sv-account { display:flex; align-items:center; gap:12px; padding:16px 16px 0; }
|
|
707
708
|
.sv-avatar {
|
|
708
709
|
flex:0 0 auto; width:38px; height:38px; border-radius:50%;
|
|
709
710
|
background:var(--accent-soft); color:var(--accent);
|
|
@@ -1561,6 +1562,10 @@
|
|
|
1561
1562
|
// The relay row whose open-actions card is expanded. Like openingIds, this
|
|
1562
1563
|
// survives the innerHTML rebuilds renderRelays performs on every push.
|
|
1563
1564
|
let expandedRelayId = null;
|
|
1565
|
+
// A conversation expands its actions IN PLACE, exactly like a single relay.
|
|
1566
|
+
// Taking over the whole pane (the old thread-detail push) lost the user's
|
|
1567
|
+
// place in the list for what is really a three-item menu.
|
|
1568
|
+
let expandedThreadId = null;
|
|
1564
1569
|
function openingTargets(id) {
|
|
1565
1570
|
return [...document.querySelectorAll("[data-opening-id]")].filter((e) => e.getAttribute("data-opening-id") === id);
|
|
1566
1571
|
}
|
|
@@ -1676,8 +1681,13 @@
|
|
|
1676
1681
|
if (t.msgs.length === 1 && inbound.length === 1 && byId.has(inbound[0].id)) {
|
|
1677
1682
|
return relayRowHtml(byId.get(inbound[0].id), seen);
|
|
1678
1683
|
}
|
|
1684
|
+
const tExpanded = t.threadId === expandedThreadId;
|
|
1685
|
+
const newestInbound = [...t.msgs].reverse().find((m) => m.direction === "in") || t.msgs[t.msgs.length - 1];
|
|
1686
|
+
const tMenu = tExpanded && newestInbound
|
|
1687
|
+
? openActionsHtml(newestInbound.id, { thread: true, threadId: t.threadId, messageCount: t.msgs.length })
|
|
1688
|
+
: "";
|
|
1679
1689
|
return `
|
|
1680
|
-
<div class="th-item${t.unreadCount ? " unread" : ""} deletable" role="button" tabindex="0" data-thread="${esc(t.threadId)}" data-party="${esc(t.party)}">
|
|
1690
|
+
<div class="th-item${t.unreadCount ? " unread" : ""}${tExpanded ? " expanded" : ""} deletable" role="button" tabindex="0" data-thread="${esc(t.threadId)}" data-party="${esc(t.party)}">
|
|
1681
1691
|
${threadDeleteButton(t)}
|
|
1682
1692
|
<span class="th-top">
|
|
1683
1693
|
${t.unreadCount ? '<span class="th-unread"></span>' : ""}
|
|
@@ -1686,13 +1696,20 @@
|
|
|
1686
1696
|
<span class="th-time">${esc(timeAgo(t.latest.at))}</span>
|
|
1687
1697
|
</span>
|
|
1688
1698
|
<span class="th-title">${esc(t.latest.title)}</span>
|
|
1699
|
+
${tMenu}
|
|
1689
1700
|
</div>`;
|
|
1690
1701
|
}).join("");
|
|
1691
1702
|
prevRelayIds = new Set(allRows.map((r) => r.id));
|
|
1692
1703
|
wireRelayRows();
|
|
1693
1704
|
for (const el of relaysListEl.querySelectorAll(".th-item")) {
|
|
1694
|
-
el.addEventListener("click", () =>
|
|
1705
|
+
el.addEventListener("click", () => {
|
|
1706
|
+
const id = el.getAttribute("data-thread");
|
|
1707
|
+
expandedThreadId = expandedThreadId === id ? null : id;
|
|
1708
|
+
expandedRelayId = null; // only one menu open at a time
|
|
1709
|
+
renderRelays();
|
|
1710
|
+
});
|
|
1695
1711
|
}
|
|
1712
|
+
wireInlineThreadActions();
|
|
1696
1713
|
}
|
|
1697
1714
|
|
|
1698
1715
|
// Grow the window as the user scrolls toward the end. Cheap: each extension
|
|
@@ -1711,7 +1728,7 @@
|
|
|
1711
1728
|
// Preview plus the two host-open actions, as one small text-first menu.
|
|
1712
1729
|
// Preview is the primary, non-navigation path; the other actions hand the
|
|
1713
1730
|
// relay to a current or fresh host session.
|
|
1714
|
-
function openActionsHtml(id, { thread, previewable = true } = {}) {
|
|
1731
|
+
function openActionsHtml(id, { thread, previewable = true, threadId = "", messageCount = 0 } = {}) {
|
|
1715
1732
|
const preview = thread ? `data-thread-preview="${esc(id)}"` : `data-preview="${esc(id)}"`;
|
|
1716
1733
|
const cur = thread ? `data-thread-open-current="${esc(id)}"` : `data-open-current="${esc(id)}"`;
|
|
1717
1734
|
const fresh = thread ? `data-thread-open-fresh="${esc(id)}"` : `data-open-fresh="${esc(id)}"`;
|
|
@@ -1724,9 +1741,37 @@
|
|
|
1724
1741
|
<button class="oa-item${previewable ? "" : " oa-primary"}" type="button" role="menuitem" ${cur}>${thread ? "Open Thread in Current Chat" : "Open in Current Chat"}</button>
|
|
1725
1742
|
<div class="oa-sep" role="separator"></div>
|
|
1726
1743
|
<button class="oa-item" type="button" role="menuitem" ${fresh}>${thread ? "Open Thread in New Chat" : "Open in New Chat"}</button>
|
|
1744
|
+
${threadId ? `<div class="oa-sep" role="separator"></div>
|
|
1745
|
+
<button class="oa-item" type="button" role="menuitem" data-thread-history="${esc(threadId)}">Read the Conversation${messageCount ? ` (${messageCount})` : ""}</button>` : ""}
|
|
1727
1746
|
</div>`;
|
|
1728
1747
|
}
|
|
1729
1748
|
|
|
1749
|
+
|
|
1750
|
+
// The inline conversation menu reuses the thread actions; scoped to the list so
|
|
1751
|
+
// the detail view's own copy keeps working unchanged.
|
|
1752
|
+
function wireInlineThreadActions() {
|
|
1753
|
+
for (const btn of relaysListEl.querySelectorAll("[data-thread-preview]")) {
|
|
1754
|
+
const id = btn.getAttribute("data-thread-preview");
|
|
1755
|
+
btn.addEventListener("click", () => {
|
|
1756
|
+
expandedThreadId = null;
|
|
1757
|
+
window.relay.preview(id);
|
|
1758
|
+
renderRelays();
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
for (const btn of relaysListEl.querySelectorAll("[data-thread-open-current]")) {
|
|
1762
|
+
btn.addEventListener("click", () => openRelayFromUI(btn.getAttribute("data-thread-open-current"), "relay", "current"));
|
|
1763
|
+
}
|
|
1764
|
+
for (const btn of relaysListEl.querySelectorAll("[data-thread-open-fresh]")) {
|
|
1765
|
+
btn.addEventListener("click", () => openRelayFromUI(btn.getAttribute("data-thread-open-fresh"), "relay", "fresh"));
|
|
1766
|
+
}
|
|
1767
|
+
for (const btn of relaysListEl.querySelectorAll("[data-thread-history]")) {
|
|
1768
|
+
btn.addEventListener("click", () => {
|
|
1769
|
+
const el = btn.closest(".th-item");
|
|
1770
|
+
openThreadDetail(btn.getAttribute("data-thread-history"), el ? el.getAttribute("data-party") || "" : "");
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1730
1775
|
// Conversations get the same trash affordance as single relays — the unified
|
|
1731
1776
|
// list made that inconsistent (a 1-message conversation renders as a relay row
|
|
1732
1777
|
// WITH delete; a 2-message one rendered as a conversation row without). One
|
|
@@ -3065,7 +3110,6 @@
|
|
|
3065
3110
|
if (info.paired) {
|
|
3066
3111
|
const name = info.name || info.email || "Relay account";
|
|
3067
3112
|
html += `
|
|
3068
|
-
<div class="sv-head"><span class="sv-title">Your account.</span></div>
|
|
3069
3113
|
<div class="sv-account">
|
|
3070
3114
|
<span class="sv-avatar">${esc(cvInitials(info.name, info.email))}</span>
|
|
3071
3115
|
<span class="sv-id">
|
package/overlay/main.cjs
CHANGED
|
@@ -2330,12 +2330,19 @@ function anchorTopRight() {
|
|
|
2330
2330
|
const display = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()) || screen.getPrimaryDisplay();
|
|
2331
2331
|
const wa = display.workArea;
|
|
2332
2332
|
const anchor = { x: wa.x + wa.width - WIN.width - MARGIN, y: wa.y + MARGIN, width: WIN.width, height: WIN.height };
|
|
2333
|
-
if (process.env.RELAY_OVERLAY_TEST === "1") {
|
|
2334
|
-
//
|
|
2335
|
-
//
|
|
2336
|
-
//
|
|
2337
|
-
//
|
|
2338
|
-
//
|
|
2333
|
+
if (process.env.RELAY_OVERLAY_TEST === "1" || process.env.RELAY_OVERLAY_PERF === "1") {
|
|
2334
|
+
// A harness run must not take over the developer's screen. Park sandbox
|
|
2335
|
+
// windows just off the bottom-right of the work area: still a REAL composited
|
|
2336
|
+
// window on the same display — so paint timing, dwell timers and hit tests
|
|
2337
|
+
// measure exactly what production does — but out of sight while the suite
|
|
2338
|
+
// runs, and never under the user's parked cursor (a pointer resting on the
|
|
2339
|
+
// card holds notifications open BY DESIGN, which wedged dwell scenarios).
|
|
2340
|
+
// RELAY_OVERLAY_TEST_ONSCREEN=1 puts it back on screen for eyeball debugging.
|
|
2341
|
+
if (process.env.RELAY_OVERLAY_TEST_ONSCREEN !== "1") {
|
|
2342
|
+
anchor.x = wa.x + wa.width - 8;
|
|
2343
|
+
anchor.y = wa.y + wa.height - 8;
|
|
2344
|
+
return anchor;
|
|
2345
|
+
}
|
|
2339
2346
|
try {
|
|
2340
2347
|
const p = screen.getCursorScreenPoint();
|
|
2341
2348
|
const overlaps =
|