Skip to content

Commit ebac63b

Browse files
committed
docs: add request orchestration graph links
1 parent f6dafed commit ebac63b

1 file changed

Lines changed: 138 additions & 8 deletions

File tree

docs-linhay/spaces/20260502-request-orchestration-menu/request-orchestration-design-v01.html

Lines changed: 138 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -413,12 +413,45 @@
413413
padding: 16px 32px 32px;
414414
}
415415

416+
.graph-stage {
417+
position: relative;
418+
min-width: 0;
419+
}
420+
421+
.connection-layer {
422+
position: absolute;
423+
inset: 0;
424+
width: 100%;
425+
height: 100%;
426+
pointer-events: none;
427+
z-index: 0;
428+
overflow: visible;
429+
}
430+
431+
.graph-line {
432+
fill: none;
433+
stroke: rgba(255, 255, 255, 0.22);
434+
stroke-width: 2.5;
435+
stroke-linecap: round;
436+
stroke-dasharray: 6 8;
437+
}
438+
439+
.graph-line-primary {
440+
stroke: rgba(243, 87, 45, 0.68);
441+
}
442+
443+
.graph-line-accent {
444+
stroke: rgba(101, 210, 123, 0.62);
445+
}
446+
416447
.lane-board {
417448
display: grid;
418449
grid-template-columns: repeat(4, minmax(280px, 1fr));
419450
gap: 20px;
420451
min-width: 0;
421452
align-items: start;
453+
position: relative;
454+
z-index: 1;
422455
}
423456

424457
.lane {
@@ -557,6 +590,7 @@
557590
padding: 14px;
558591
display: grid;
559592
gap: 10px;
593+
position: relative;
560594
}
561595

562596
.group-card.active,
@@ -712,6 +746,7 @@
712746
padding: 12px;
713747
display: grid;
714748
gap: 10px;
749+
position: relative;
715750
}
716751

717752
.route-row {
@@ -770,6 +805,25 @@
770805
color: var(--text-secondary);
771806
}
772807

808+
.graph-anchor-dot {
809+
position: absolute;
810+
top: 50%;
811+
width: 10px;
812+
height: 10px;
813+
border: 2px solid var(--accent);
814+
background: var(--bg-panel-soft);
815+
transform: translateY(-50%);
816+
box-shadow: 0 0 0 3px rgba(243, 87, 45, 0.12);
817+
}
818+
819+
.graph-anchor-dot.left {
820+
left: -7px;
821+
}
822+
823+
.graph-anchor-dot.right {
824+
right: -7px;
825+
}
826+
773827
.mapping-line,
774828
.reason-line {
775829
display: grid;
@@ -1098,7 +1152,9 @@
10981152
<div class="chain-bar" id="chain-bar" data-collaboration-id="RO.CHAIN"></div>
10991153

11001154
<div class="content-grid">
1101-
<section class="lane-board">
1155+
<div class="graph-stage" id="graph-stage">
1156+
<svg class="connection-layer" id="connection-layer" aria-hidden="true"></svg>
1157+
<section class="lane-board">
11021158
<article class="lane active" data-lane="entry" data-collaboration-id="RO.LANE.ENTRY">
11031159
<div class="lane-head">
11041160
<div class="lane-head-top">
@@ -1114,7 +1170,8 @@
11141170
</div>
11151171
</div>
11161172

1117-
<div class="lane-section">
1173+
<div class="lane-section" data-graph-anchor="entry">
1174+
<span class="graph-anchor-dot right" aria-hidden="true"></span>
11181175
<div class="section-kicker">请求入口</div>
11191176
<div class="segmented">
11201177
<button class="cli-token active" data-cli="codex" type="button">codex</button>
@@ -1185,14 +1242,15 @@
11851242
<span>结束</span>
11861243
<span class="locator">RO.LANE.END</span>
11871244
</div>
1188-
<div class="lane-copy">
1245+
<div class="lane-copy">
11891246
这里是链路收口区。确认当前请求会落到哪些账号, 走不走代理, 并允许直接发起测试。
11901247
</div>
11911248
</div>
11921249

11931250
<div class="lane-section" id="end-list" data-collaboration-id="RO.LANE.END.LIST"></div>
11941251
</article>
1195-
</section>
1252+
</section>
1253+
</div>
11961254

11971255
</div>
11981256

@@ -1325,11 +1383,14 @@
13251383
const defaultProxyBindings = Object.fromEntries(state.accounts.map((account) => [account.id, account.proxyId]));
13261384

13271385
const chainBar = document.getElementById("chain-bar");
1386+
const graphStage = document.getElementById("graph-stage");
1387+
const connectionLayer = document.getElementById("connection-layer");
13281388
const groupList = document.getElementById("group-list");
13291389
const proxyList = document.getElementById("proxy-list");
13301390
const endList = document.getElementById("end-list");
13311391
const copyToast = document.getElementById("copy-toast");
13321392
let copyToastTimer = null;
1393+
let connectionFrame = null;
13331394

13341395
function toLocatorSegment(value) {
13351396
return String(value).replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toUpperCase();
@@ -1367,6 +1428,53 @@
13671428
});
13681429
}
13691430

1431+
function getAnchorPoint(name, side) {
1432+
const node = document.querySelector(`[data-graph-anchor="${name}"]`);
1433+
if (!node || !graphStage) return null;
1434+
const nodeRect = node.getBoundingClientRect();
1435+
const stageRect = graphStage.getBoundingClientRect();
1436+
const x = side === "left" ? nodeRect.left - stageRect.left : nodeRect.right - stageRect.left;
1437+
const y = nodeRect.top - stageRect.top + nodeRect.height / 2;
1438+
return { x, y };
1439+
}
1440+
1441+
function buildConnectionPath(from, to) {
1442+
const delta = Math.max(48, (to.x - from.x) * 0.35);
1443+
return `M ${from.x} ${from.y} C ${from.x + delta} ${from.y}, ${to.x - delta} ${to.y}, ${to.x} ${to.y}`;
1444+
}
1445+
1446+
function renderConnections(computed) {
1447+
if (!graphStage || !connectionLayer) return;
1448+
1449+
const activeAccounts = computed.filter((item) => item.active);
1450+
const destinations = [
1451+
...new Set(activeAccounts.map((account) => (account.proxyId ? `proxy-${account.proxyId}` : "proxy-direct"))),
1452+
];
1453+
const segments = [];
1454+
1455+
const entryPoint = getAnchorPoint("entry", "right");
1456+
const groupPointLeft = getAnchorPoint("group-selected", "left");
1457+
const groupPointRight = getAnchorPoint("group-selected", "right");
1458+
const endPoint = getAnchorPoint("end", "left");
1459+
1460+
if (entryPoint && groupPointLeft) {
1461+
segments.push(`<path d="${buildConnectionPath(entryPoint, groupPointLeft)}" class="graph-line graph-line-primary" />`);
1462+
}
1463+
1464+
destinations.forEach((destination) => {
1465+
const destinationLeft = getAnchorPoint(destination, "left");
1466+
const destinationRight = getAnchorPoint(destination, "right");
1467+
if (groupPointRight && destinationLeft) {
1468+
segments.push(`<path d="${buildConnectionPath(groupPointRight, destinationLeft)}" class="graph-line" />`);
1469+
}
1470+
if (destinationRight && endPoint) {
1471+
segments.push(`<path d="${buildConnectionPath(destinationRight, endPoint)}" class="graph-line graph-line-accent" />`);
1472+
}
1473+
});
1474+
1475+
connectionLayer.innerHTML = segments.join("");
1476+
}
1477+
13701478
function computeAccounts() {
13711479
return state.accounts.map((account) => {
13721480
const groupSelected = account.group === state.activeGroupId;
@@ -1419,7 +1527,12 @@
14191527
const locatorId = `RO.GROUP.${toLocatorSegment(group.id)}`;
14201528
if (selected) cardClasses.push("unique");
14211529
return `
1422-
<div class="${cardClasses.join(" ")}" data-collaboration-id="${locatorId}">
1530+
<div
1531+
class="${cardClasses.join(" ")}"
1532+
data-collaboration-id="${locatorId}"
1533+
${selected ? 'data-graph-anchor="group-selected"' : ""}
1534+
>
1535+
${selected ? '<span class="graph-anchor-dot left" aria-hidden="true"></span><span class="graph-anchor-dot right" aria-hidden="true"></span>' : ""}
14231536
<div class="group-top">
14241537
<div>
14251538
<div class="group-name">${group.label}</div>
@@ -1521,7 +1634,9 @@
15211634
`;
15221635

15231636
const directBlock = `
1524-
<div class="group-card" data-collaboration-id="RO.PROXY.DIRECT">
1637+
<div class="group-card" data-collaboration-id="RO.PROXY.DIRECT" data-graph-anchor="proxy-direct">
1638+
<span class="graph-anchor-dot left" aria-hidden="true"></span>
1639+
<span class="graph-anchor-dot right" aria-hidden="true"></span>
15251640
<div class="group-top">
15261641
<div>
15271642
<div class="group-name">直连</div>
@@ -1543,7 +1658,13 @@
15431658
const linkedAccounts = activeAccounts.filter((account) => account.proxyId === proxy.id);
15441659
const locatorId = `RO.PROXY.${toLocatorSegment(proxy.id)}`;
15451660
return `
1546-
<div class="group-card ${linkedAccounts.length > 0 ? "active" : ""}" data-collaboration-id="${locatorId}">
1661+
<div
1662+
class="group-card ${linkedAccounts.length > 0 ? "active" : ""}"
1663+
data-collaboration-id="${locatorId}"
1664+
data-graph-anchor="proxy-${proxy.id}"
1665+
>
1666+
<span class="graph-anchor-dot left" aria-hidden="true"></span>
1667+
<span class="graph-anchor-dot right" aria-hidden="true"></span>
15471668
<div class="group-top">
15481669
<div>
15491670
<div class="group-name">${proxy.label}</div>
@@ -1572,7 +1693,8 @@
15721693
const proxyCount = activeAccounts.filter((item) => item.proxyId).length;
15731694

15741695
endList.innerHTML = `
1575-
<div class="outlet-block primary" data-collaboration-id="RO.END.TEST">
1696+
<div class="outlet-block primary" data-collaboration-id="RO.END.TEST" data-graph-anchor="end">
1697+
<span class="graph-anchor-dot left" aria-hidden="true"></span>
15761698
<div class="outlet-top">
15771699
<div>
15781700
<div class="outlet-name">结束与测试</div>
@@ -1673,6 +1795,8 @@
16731795
renderEnd(computed);
16741796
renderLaneFocus();
16751797
hydrateLocators();
1798+
window.cancelAnimationFrame(connectionFrame);
1799+
connectionFrame = window.requestAnimationFrame(() => renderConnections(computed));
16761800
}
16771801

16781802
document.addEventListener("click", async (event) => {
@@ -1765,6 +1889,12 @@
17651889
}
17661890
});
17671891

1892+
window.addEventListener("resize", () => {
1893+
const computed = computeAccounts();
1894+
window.cancelAnimationFrame(connectionFrame);
1895+
connectionFrame = window.requestAnimationFrame(() => renderConnections(computed));
1896+
});
1897+
17681898
render();
17691899
</script>
17701900
</body>

0 commit comments

Comments
 (0)