Skip to content

Commit 5ca5ce1

Browse files
committed
Merge provider-family live route fix
2 parents b5e3b81 + 8edc35d commit 5ca5ce1

7 files changed

Lines changed: 59 additions & 37 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rerouted",
3-
"version": "0.4.11",
3+
"version": "0.4.12",
44
"description": "A macOS menu-bar router for accounts, models, and automatic fallback.",
55
"author": "gitcommit90",
66
"license": "MIT",

scripts/capture-ui.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ app.whenReady().then(async () => {
743743
!routeStage ||
744744
!path ||
745745
routeStage.querySelectorAll(".live-request-path").length !== 2 ||
746-
routeStage.querySelectorAll(".live-provider").length !== 5 ||
746+
routeStage.querySelectorAll(".live-provider").length !== 4 ||
747747
!copyButton
748748
) {
749749
throw new Error("Status persistence controls did not render");

src/renderer/app.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const { accountDisplayName, accountIdentityLabel, maskAccountEmail } =
55
window.ReroutedAccountIdentity;
66
const { compactNumber: fmtNum } = window.ReroutedNumberFormat;
77
const { createLatestRequestGate, guardSensitiveRender } = window.ReroutedRendererLockState;
8-
const { buildProviderCatalog, canonicalProviderType } = window.ReroutedProviderCatalog;
8+
const { buildEnabledProviderGroups, buildProviderCatalog, canonicalProviderType } =
9+
window.ReroutedProviderCatalog;
910
const { buildRouteAccountOptions, modelsForRouteAccount } = window.ReroutedRoutePicker;
1011
const { oauthPrompt } = window.ReroutedOAuthPrompt;
1112
const $ = (sel, el = document) => el.querySelector(sel);
@@ -110,6 +111,7 @@ const PROVIDER_LABELS = {
110111
cloudflare: "Cloudflare",
111112
glm: "GLM Coding",
112113
"openai-compat": "API key",
114+
custom: "Custom",
113115
};
114116

115117
const PROVIDER_PRESENTATION = {
@@ -956,15 +958,14 @@ function homeRecentHtml(recent) {
956958
}
957959

958960
function liveProviders() {
959-
return (state?.providers || []).filter(
960-
(provider) => provider.enabled !== false && provider.hasToken
961-
);
961+
return buildEnabledProviderGroups(state?.providers || []).map((provider) => ({
962+
...provider,
963+
name: providerLabel(provider.id),
964+
}));
962965
}
963966

964967
function liveProviderKey(providers) {
965-
return providers
966-
.map((provider) => `${provider.id}:${provider.enabled}:${provider.hasToken}:${provider.name}`)
967-
.join("|");
968+
return providers.map((provider) => provider.id).join("|");
968969
}
969970

970971
function liveProviderHtml(providers) {
@@ -974,15 +975,13 @@ function liveProviderHtml(providers) {
974975
return providers
975976
.map((provider, index) => {
976977
const presentation = providerPresentation(provider);
977-
const account = aliasLabel(provider.accountAlias);
978-
const name = provider.name || providerLabel(canonicalProviderType(provider.type));
978+
const name = provider.name || providerLabel(provider.id);
979979
const center = (providers.length - 1) / 2;
980980
const archY = Math.round(Math.abs(index - center) * 1.8);
981981
return `<span class="live-provider" data-live-provider-id="${esc(provider.id)}" title="${esc(
982-
account ? `${name} · ${account}` : name
982+
name
983983
)}" style="--provider-order:${index};--arch-y:${archY}px">
984984
<img src="assets/providers/${esc(presentation.logo)}" alt="" />
985-
${account ? `<small>${esc(account.replace("Account ", ""))}</small>` : ""}
986985
</span>`;
987986
})
988987
.join("");
@@ -1014,7 +1013,10 @@ function drawLiveRequestPaths({ force = false } = {}) {
10141013
const stageRect = stage.getBoundingClientRect();
10151014
if (!stageRect.width || !stageRect.height) return;
10161015
const pathKey = `${Math.round(stageRect.width)}x${Math.round(stageRect.height)}|${requests
1017-
.map((request) => `${request.id}:${request.providerId || "pending"}`)
1016+
.map(
1017+
(request) =>
1018+
`${request.id}:${canonicalProviderType(request.providerType) || "pending"}`
1019+
)
10181020
.join("|")}`;
10191021
if (!force && pathKey === liveRequestPathKey) return;
10201022
liveRequestPathKey = pathKey;
@@ -1039,7 +1041,7 @@ function drawLiveRequestPaths({ force = false } = {}) {
10391041
const count = requests.length;
10401042
requests.forEach((request, index) => {
10411043
const spread = (index - (count - 1) / 2) * 3.2;
1042-
const targetNode = providerNodes.get(request.providerId);
1044+
const targetNode = providerNodes.get(canonicalProviderType(request.providerType));
10431045
if (targetNode) targetNode.classList.add("is-active");
10441046
const target = targetNode
10451047
? liveRoutePoint(targetNode, stageRect, "bottom")
@@ -1065,7 +1067,7 @@ function drawLiveRequestPaths({ force = false } = {}) {
10651067
}
10661068
const routeText = stage.querySelector("[data-live-route-status]");
10671069
if (routeText) {
1068-
const routed = requests.filter((request) => request.providerId).length;
1070+
const routed = requests.filter((request) => request.providerType).length;
10691071
routeText.textContent = count
10701072
? routed === count
10711073
? `${count} routing live`
@@ -1117,7 +1119,7 @@ function renderHome() {
11171119
<section class="hero-surface live-router-card" data-live-router-card>
11181120
<div class="live-router-head">
11191121
<div class="gateway-state"><span class="status-node ${online ? "" : "off"}" data-home-status-node></span><span data-home-gateway>${online ? "Gateway live" : "Gateway stopped"}</span></div>
1120-
<span class="live-provider-count">${providers.length} connected</span>
1122+
<span class="live-provider-count">${providers.length} provider${providers.length === 1 ? "" : "s"}</span>
11211123
</div>
11221124
<div class="live-router-stage" data-live-router-stage aria-label="${esc(
11231125
activeRequests.length

src/renderer/provider-catalog.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,26 @@
9595
return entries;
9696
}
9797

98-
const api = { buildProviderCatalog, canonicalProviderType };
98+
function buildEnabledProviderGroups(providers = []) {
99+
const groups = [];
100+
const groupsById = new Map();
101+
102+
for (const account of providers) {
103+
if (account?.enabled === false || !account?.hasToken) continue;
104+
const id = canonicalProviderType(account.type) || "unknown";
105+
let group = groupsById.get(id);
106+
if (!group) {
107+
group = { id, type: id, accounts: [] };
108+
groups.push(group);
109+
groupsById.set(id, group);
110+
}
111+
group.accounts.push(account);
112+
}
113+
114+
return groups;
115+
}
116+
117+
const api = { buildProviderCatalog, buildEnabledProviderGroups, canonicalProviderType };
99118
if (typeof module !== "undefined" && module.exports) module.exports = api;
100119
if (root) root.ReroutedProviderCatalog = api;
101120
})(typeof window !== "undefined" ? window : null);

src/renderer/styles.css

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -664,23 +664,6 @@ summary:focus-visible,
664664
object-fit: contain;
665665
}
666666

667-
.live-provider small {
668-
position: absolute;
669-
right: -2px;
670-
bottom: -3px;
671-
display: grid;
672-
min-width: 12px;
673-
height: 12px;
674-
padding: 0 3px;
675-
place-items: center;
676-
border: 1px solid rgba(255, 255, 255, 0.7);
677-
border-radius: 999px;
678-
color: #fff;
679-
background: var(--night-soft);
680-
font-size: 7px;
681-
font-weight: 750;
682-
}
683-
684667
.live-provider.is-active {
685668
border-color: rgba(239, 91, 42, 0.95);
686669
box-shadow: 0 0 0 3px rgba(239, 91, 42, 0.16), 0 8px 22px rgba(0, 0, 0, 0.22);

tests/provider-catalog.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const assert = require("node:assert/strict");
44
const { describe, it } = require("node:test");
55
const {
6+
buildEnabledProviderGroups,
67
buildProviderCatalog,
78
canonicalProviderType,
89
} = require("../src/renderer/provider-catalog");
@@ -104,4 +105,21 @@ describe("provider-first catalog", () => {
104105
assert.equal(catalog[2].name, "Mystery Provider");
105106
assert.deepEqual(catalog[2].accounts, providers.slice(2));
106107
});
108+
109+
it("builds one enabled live node per canonical provider, not per account", () => {
110+
const providers = [
111+
{ id: "chatgpt_one", type: "chatgpt", enabled: true, hasToken: true },
112+
{ id: "chatgpt_two", type: "codex", enabled: true, hasToken: true },
113+
{ id: "claude_disabled", type: "claude", enabled: false, hasToken: true },
114+
{ id: "xai_signed_out", type: "xai", enabled: true, hasToken: false },
115+
{ id: "custom_one", type: "openai-compat", enabled: true, hasToken: true },
116+
{ id: "custom_two", type: "custom", enabled: true, hasToken: true },
117+
];
118+
119+
const groups = buildEnabledProviderGroups(providers);
120+
121+
assert.deepEqual(groups.map((group) => group.id), ["chatgpt", "custom"]);
122+
assert.deepEqual(groups[0].accounts, providers.slice(0, 2));
123+
assert.deepEqual(groups[1].accounts, providers.slice(4));
124+
});
107125
});

0 commit comments

Comments
 (0)