Skip to content

Commit 06c8de0

Browse files
authored
Merge pull request #16 from gitcommit90/fix/routes-open-in-place
fix: routes open as full-page editor
2 parents d242206 + 8823818 commit 06c8de0

5 files changed

Lines changed: 80 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ Release tags use the form `vX.Y.Z` and match `package.json`. GitHub Releases car
99

1010
## [Unreleased]
1111

12+
## [0.5.6] - 2026-07-20
13+
14+
### Fixed
15+
16+
- Opening a route now replaces the routes list with a full editor instead of burying an edit panel below the grid with no scroll.
17+
- Removed the laggy route-card lift animation so clicks feel immediate.
18+
1219
## [0.5.5] - 2026-07-20
1320

1421
### Added

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,7 +1,7 @@
11
{
22
"name": "@gitcommit90/rerouted",
33
"productName": "ReRouted",
4-
"version": "0.5.5",
4+
"version": "0.5.6",
55
"description": "A local AI router for connected accounts, models, named routes, and automatic fallback.",
66
"author": "gitcommit90",
77
"license": "MIT",

src/renderer/app.js

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,7 +1746,18 @@ function beginComboEdit(combo) {
17461746
model: member.model || member.upstreamModel,
17471747
})),
17481748
};
1749-
renderCombos();
1749+
renderCombos({ focusEditor: true });
1750+
}
1751+
1752+
function focusRouteEditor() {
1753+
const editorEl = view.querySelector(".route-editor");
1754+
if (!editorEl) return;
1755+
editorEl.scrollIntoView({ block: "start", behavior: "auto" });
1756+
const name = $("#c-name");
1757+
if (name) {
1758+
name.focus({ preventScroll: true });
1759+
if (typeof name.select === "function") name.select();
1760+
}
17501761
}
17511762

17521763
function syncComboDraft() {
@@ -1767,7 +1778,7 @@ function routeAccountLabel(account) {
17671778
return account.name;
17681779
}
17691780

1770-
function renderCombos() {
1781+
function renderCombos(options = {}) {
17711782
if (blockSensitiveRenderIfLocked()) return;
17721783
const combos = state.combos || [];
17731784
const models = flatModels();
@@ -1787,15 +1798,14 @@ function renderCombos() {
17871798
editor.pickerModelId = null;
17881799
}
17891800

1790-
view.innerHTML = `
1791-
${pageHeader("Routing", "Routes", "Give clients one memorable model ID while ReRouted handles account and provider failover.", '<button type="button" class="btn btn-primary btn-sm" id="btn-new-route">New route</button>')}
1801+
const listMarkup = `
17921802
<div class="route-card-grid">
17931803
${
17941804
combos.length
17951805
? combos
17961806
.map(
17971807
(c, index) => `
1798-
<article class="route-card">
1808+
<article class="route-card${editor && (editor.id === (c.storageId || c.id) || (!editor.id && !c.storageId && !c.id)) ? " is-active" : ""}">
17991809
<button type="button" class="route-card-hit" data-edit-index="${index}" aria-label="Edit route ${esc(comboRouteId(c))}"></button>
18001810
<div class="route-card-top">
18011811
<span class="strategy-badge">${c.strategy === "round-robin" ? "Round robin" : "Fallback"}</span>
@@ -1804,17 +1814,21 @@ function renderCombos() {
18041814
<div class="route-name">${esc(comboRouteId(c))}</div>
18051815
<div class="route-summary">${c.strategy === "round-robin" ? "Rotates every request" : "Fills in order"} · ${(c.members || []).length} member${(c.members || []).length === 1 ? "" : "s"}</div>
18061816
<div class="route-nodes" aria-hidden="true">${(c.members || []).slice(0, 5).map((_, index) => `${index ? '<span class="route-node-line"></span>' : ""}<span class="route-node">${index + 1}</span>`).join("")}${(c.members || []).length > 5 ? '<span class="route-node-line"></span><span class="route-node">+</span>' : ""}</div>
1807-
<div class="route-card-action" aria-hidden="true">Edit route <span>→</span></div>
1817+
<div class="route-card-action" aria-hidden="true">${editor && (editor.id === (c.storageId || c.id)) ? "Editing" : "Open"} <span>→</span></div>
18081818
</article>`
18091819
)
18101820
.join("")
18111821
: `<div class="empty">No routes yet. Create a memorable model ID and choose where requests should go.</div>`
18121822
}
1813-
</div>
1814-
${
1815-
editor
1816-
? `<section class="action-panel route-editor">
1817-
<div class="action-panel-head"><div class="eyebrow">${editor.id ? "Edit route" : "New route"}</div><div class="action-panel-title">${editor.id ? esc(editor.name || "Route") : "Build a route"}</div><div class="action-panel-sub">The model ID below is what clients see in <span class="mono">/v1/models</span>.</div></div>
1823+
</div>`;
1824+
1825+
const editorMarkup = editor
1826+
? `<section class="action-panel route-editor" id="route-editor">
1827+
<div class="action-panel-head">
1828+
<div class="eyebrow">${editor.id ? "Edit route" : "New route"}</div>
1829+
<div class="action-panel-title">${editor.id ? esc(editor.name || "Route") : "Build a route"}</div>
1830+
<div class="action-panel-sub">The model ID below is what clients see in <span class="mono">/v1/models</span>.</div>
1831+
</div>
18181832
<div class="label">Model ID</div>
18191833
<input class="input" id="c-name" placeholder="coding-fast" value="${esc(editor.name)}" />
18201834
<div class="label">Routing behavior</div>
@@ -1845,17 +1859,39 @@ function renderCombos() {
18451859
<label class="route-picker-field" for="c-add-model"><span class="label">Model</span><select class="select" id="c-add-model" ${editor.pickerAccountId ? "" : "disabled"}><option value="">${editor.pickerAccountId ? "Choose a model…" : "Choose an account first…"}</option>${pickerModels.map((model) => `<option value="${esc(model.upstreamModel)}" ${model.upstreamModel === editor.pickerModelId ? "selected" : ""}>${esc(model.name)} · ${esc(model.upstreamModel)}</option>`).join("")}</select></label>
18461860
<button type="button" class="btn btn-secondary btn-sm route-picker-add" id="btn-add-member" ${editor.pickerAccountId && editor.pickerModelId ? "" : "disabled"}>Add to route</button>
18471861
</div>
1848-
<div class="btn-row"><button type="button" class="btn btn-secondary" id="btn-cancel-edit">Cancel</button><button type="button" class="btn btn-primary" id="btn-create">${editor.id ? "Save route" : "Create route"}</button></div>
1862+
<div class="btn-row"><button type="button" class="btn btn-secondary" id="btn-cancel-edit">Back to routes</button><button type="button" class="btn btn-primary" id="btn-create">${editor.id ? "Save route" : "Create route"}</button></div>
18491863
</section>`
1850-
: ""
1851-
}
1864+
: "";
1865+
1866+
// When editing, replace the list with the editor (full page take-over). No buried bottom panel.
1867+
view.innerHTML = `
1868+
${pageHeader(
1869+
"Routing",
1870+
editor ? (editor.id ? editor.name || "Edit route" : "New route") : "Routes",
1871+
editor
1872+
? "Change the model ID, order, and failover for this route."
1873+
: "Give clients one memorable model ID while ReRouted handles account and provider failover.",
1874+
editor
1875+
? '<button type="button" class="btn btn-secondary btn-sm" id="btn-back-routes">← Routes</button>'
1876+
: '<button type="button" class="btn btn-primary btn-sm" id="btn-new-route">New route</button>'
1877+
)}
1878+
${editor ? editorMarkup : listMarkup}
18521879
`;
1853-
$("#btn-new-route").onclick = () => beginComboEdit(null);
1880+
1881+
const backToList = () => {
1882+
comboDraft = null;
1883+
renderCombos();
1884+
};
1885+
const backBtn = $("#btn-back-routes");
1886+
if (backBtn) backBtn.onclick = backToList;
1887+
const newBtn = $("#btn-new-route");
1888+
if (newBtn) newBtn.onclick = () => beginComboEdit(null);
18541889
view.querySelectorAll("button[data-edit-index]").forEach((btn) => {
18551890
btn.onclick = () => beginComboEdit(combos[Number(btn.dataset.editIndex)]);
18561891
});
18571892
view.querySelectorAll("button[data-del-index]").forEach((btn) => {
1858-
btn.onclick = async () => {
1893+
btn.onclick = async (event) => {
1894+
event.stopPropagation();
18591895
const combo = combos[Number(btn.dataset.delIndex)];
18601896
const id = combo?.storageId || combo?.id;
18611897
if (!id) return;
@@ -1984,6 +2020,9 @@ function renderCombos() {
19842020
await refresh();
19852021
renderCombos();
19862022
};
2023+
if (options.focusEditor) {
2024+
requestAnimationFrame(() => focusRouteEditor());
2025+
}
19872026
}
19882027

19892028
let statsPeriod = "24h";

src/renderer/styles.css

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,14 +1593,18 @@ summary:focus-visible,
15931593
background:
15941594
linear-gradient(145deg, rgba(239, 91, 42, 0.08), transparent 58%),
15951595
var(--paper-strong);
1596-
transition: transform 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
1596+
transition: border-color 80ms ease, box-shadow 80ms ease;
15971597
}
15981598

15991599
.route-card:hover,
16001600
.route-card:focus-within {
16011601
border-color: rgba(239, 91, 42, 0.55);
16021602
box-shadow: 0 8px 20px rgba(58, 49, 35, 0.08);
1603-
transform: translateY(-1px);
1603+
}
1604+
1605+
.route-card:active {
1606+
border-color: rgba(239, 91, 42, 0.75);
1607+
box-shadow: none;
16041608
}
16051609

16061610
.route-card-hit {
@@ -1613,6 +1617,15 @@ summary:focus-visible,
16131617
cursor: pointer;
16141618
}
16151619

1620+
.route-card-hit:active {
1621+
background: rgba(239, 91, 42, 0.04);
1622+
}
1623+
1624+
.route-editor {
1625+
margin: 0 0 0 7px;
1626+
animation: none;
1627+
}
1628+
16161629
.route-card-top {
16171630
display: flex;
16181631
position: relative;

0 commit comments

Comments
 (0)