|
351 | 351 | box-shadow: 4px 4px 0 var(--shadow); |
352 | 352 | } |
353 | 353 |
|
354 | | - .chain-bar { |
355 | | - display: none; |
356 | | - } |
357 | | - |
358 | | - .chain-node { |
359 | | - min-height: 72px; |
360 | | - border: 2px solid var(--line); |
361 | | - background: var(--bg-panel); |
362 | | - box-shadow: 4px 4px 0 var(--shadow); |
363 | | - display: flex; |
364 | | - align-items: center; |
365 | | - gap: 14px; |
366 | | - padding: 0 16px; |
367 | | - position: relative; |
368 | | - } |
369 | | - |
370 | | - .chain-node.active { |
371 | | - border-color: var(--accent); |
372 | | - background: linear-gradient(180deg, rgba(243, 87, 45, 0.18), rgba(243, 87, 45, 0.03)); |
373 | | - } |
374 | | - |
375 | | - .chain-index { |
376 | | - width: 32px; |
377 | | - height: 32px; |
378 | | - border: 2px solid var(--line-strong); |
379 | | - display: inline-flex; |
380 | | - align-items: center; |
381 | | - justify-content: center; |
382 | | - font-family: var(--mono); |
383 | | - font-size: 13px; |
384 | | - font-weight: 700; |
385 | | - color: var(--text-secondary); |
386 | | - flex-shrink: 0; |
387 | | - } |
388 | | - |
389 | | - .chain-node.active .chain-index { |
390 | | - border-color: var(--accent); |
391 | | - color: var(--text-primary); |
392 | | - } |
393 | | - |
394 | | - .chain-copy strong { |
395 | | - display: block; |
396 | | - font-size: 13px; |
397 | | - text-transform: uppercase; |
398 | | - letter-spacing: 0.08em; |
399 | | - } |
400 | | - |
401 | | - .chain-copy span { |
402 | | - display: block; |
403 | | - color: var(--text-secondary); |
404 | | - font-size: 12px; |
405 | | - line-height: 1.45; |
406 | | - margin-top: 4px; |
407 | | - } |
408 | | - |
409 | 354 | .content-grid { |
410 | 355 | display: grid; |
411 | 356 | grid-template-columns: minmax(0, 1fr); |
|
416 | 361 | .graph-stage { |
417 | 362 | position: relative; |
418 | 363 | min-width: 0; |
| 364 | + border: 2px solid var(--line); |
| 365 | + background: rgba(14, 16, 18, 0.72); |
| 366 | + box-shadow: 8px 8px 0 var(--shadow); |
| 367 | + padding: 18px; |
419 | 368 | } |
420 | 369 |
|
421 | 370 | .connection-layer { |
|
466 | 415 | } |
467 | 416 |
|
468 | 417 | .lane-head { |
469 | | - padding: 8px 2px 10px; |
| 418 | + padding: 14px; |
| 419 | + border: 2px solid var(--line); |
| 420 | + background: rgba(255, 255, 255, 0.03); |
| 421 | + box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.2); |
470 | 422 | display: grid; |
471 | 423 | gap: 8px; |
472 | 424 | } |
|
502 | 454 |
|
503 | 455 | .lane-copy { |
504 | 456 | color: var(--text-secondary); |
505 | | - font-size: 12px; |
506 | | - line-height: 1.55; |
| 457 | + font-size: 11px; |
| 458 | + line-height: 1.45; |
507 | 459 | } |
508 | 460 |
|
509 | 461 | .lane-section { |
|
513 | 465 | border-bottom: 0; |
514 | 466 | } |
515 | 467 |
|
| 468 | + .lane[data-lane="entry"] .lane-section { |
| 469 | + border: 2px solid var(--line); |
| 470 | + background: rgba(255, 255, 255, 0.03); |
| 471 | + padding: 14px; |
| 472 | + } |
| 473 | + |
516 | 474 | .section-kicker { |
517 | 475 | font-size: 10px; |
518 | 476 | font-family: var(--mono); |
|
1149 | 1107 | </div> |
1150 | 1108 | </header> |
1151 | 1109 |
|
1152 | | - <div class="chain-bar" id="chain-bar" data-collaboration-id="RO.CHAIN"></div> |
1153 | | - |
1154 | 1110 | <div class="content-grid"> |
1155 | 1111 | <div class="graph-stage" id="graph-stage"> |
1156 | 1112 | <svg class="connection-layer" id="connection-layer" aria-hidden="true"></svg> |
1157 | 1113 | <section class="lane-board"> |
1158 | | - <article class="lane active" data-lane="entry" data-collaboration-id="RO.LANE.ENTRY"> |
1159 | | - <div class="lane-head"> |
1160 | | - <div class="lane-head-top"> |
1161 | | - <div class="lane-number">node 01</div> |
1162 | | - <button class="lane-toggle" type="button" data-focus="entry">+</button> |
1163 | | - </div> |
1164 | | - <div class="lane-title"> |
1165 | | - <span>请求入口</span> |
1166 | | - <span class="locator">RO.LANE.ENTRY</span> |
1167 | | - </div> |
1168 | | - <div class="lane-copy"> |
1169 | | - 节点只保留两个入口: `codex` 与 `claude code`。 |
| 1114 | + <article class="lane active" data-lane="entry" data-collaboration-id="RO.LANE.ENTRY"> |
| 1115 | + <div class="lane-head"> |
| 1116 | + <div class="lane-head-top"> |
| 1117 | + <div class="lane-number">node 01</div> |
| 1118 | + <button class="lane-toggle" type="button" data-focus="entry">+</button> |
| 1119 | + </div> |
| 1120 | + <div class="lane-title"> |
| 1121 | + <span>请求入口</span> |
| 1122 | + <span class="locator">RO.LANE.ENTRY</span> |
| 1123 | + </div> |
| 1124 | + <div class="lane-copy">只保留 `codex` 与 `claude code` 两种入口。</div> |
1170 | 1125 | </div> |
1171 | | - </div> |
1172 | 1126 |
|
1173 | | - <div class="lane-section" data-graph-anchor="entry"> |
1174 | | - <span class="graph-anchor-dot right" aria-hidden="true"></span> |
1175 | | - <div class="section-kicker">请求入口</div> |
1176 | | - <div class="segmented"> |
1177 | | - <button class="cli-token active" data-cli="codex" type="button">codex</button> |
1178 | | - <button class="cli-token" data-cli="claude-code" type="button">claude code</button> |
| 1127 | + <div class="lane-section" data-graph-anchor="entry"> |
| 1128 | + <span class="graph-anchor-dot right" aria-hidden="true"></span> |
| 1129 | + <div class="section-kicker">请求入口</div> |
| 1130 | + <div class="segmented"> |
| 1131 | + <button class="cli-token active" data-cli="codex" type="button">codex</button> |
| 1132 | + <button class="cli-token" data-cli="claude-code" type="button">claude code</button> |
| 1133 | + </div> |
1179 | 1134 | </div> |
1180 | | - </div> |
1181 | 1135 |
|
1182 | | - <div class="lane-section"> |
1183 | | - <div class="section-kicker">入口说明</div> |
1184 | | - <div class="metric-pair"> |
1185 | | - <span class="key">current client</span> |
1186 | | - <span class="value" id="entry-strategy">codex</span> |
1187 | | - </div> |
1188 | | - <div class="metric-pair"> |
1189 | | - <span class="key">scope</span> |
1190 | | - <span class="value">仅允许 codex / claude code</span> |
1191 | | - </div> |
1192 | | - <div class="metric-pair"> |
1193 | | - <span class="key">dispatch</span> |
1194 | | - <span class="value">从入口进入账号组选择</span> |
| 1136 | + <div class="lane-section"> |
| 1137 | + <div class="section-kicker">节点说明</div> |
| 1138 | + <div class="metric-pair"> |
| 1139 | + <span class="key">current client</span> |
| 1140 | + <span class="value" id="entry-strategy">codex</span> |
| 1141 | + </div> |
| 1142 | + <div class="metric-pair"> |
| 1143 | + <span class="key">scope</span> |
| 1144 | + <span class="value">仅允许两种 CLI</span> |
| 1145 | + </div> |
| 1146 | + <div class="metric-pair"> |
| 1147 | + <span class="key">next</span> |
| 1148 | + <span class="value">进入账号组节点</span> |
| 1149 | + </div> |
1195 | 1150 | </div> |
1196 | | - </div> |
1197 | | - </article> |
| 1151 | + </article> |
1198 | 1152 |
|
1199 | | - <article class="lane" data-lane="groups" data-collaboration-id="RO.LANE.GROUPS"> |
1200 | | - <div class="lane-head"> |
1201 | | - <div class="lane-head-top"> |
1202 | | - <div class="lane-number">node 02</div> |
1203 | | - <button class="lane-toggle" type="button" data-focus="groups">+</button> |
1204 | | - </div> |
1205 | | - <div class="lane-title"> |
1206 | | - <span>账号组</span> |
1207 | | - <span class="locator">RO.LANE.GROUPS</span> |
1208 | | - </div> |
1209 | | - <div class="lane-copy"> |
1210 | | - 这里展示的是账号池里已经存在的账号组。当前链路只会从其中选一组继续向右推进。 |
| 1153 | + <article class="lane" data-lane="groups" data-collaboration-id="RO.LANE.GROUPS"> |
| 1154 | + <div class="lane-head"> |
| 1155 | + <div class="lane-head-top"> |
| 1156 | + <div class="lane-number">node 02</div> |
| 1157 | + <button class="lane-toggle" type="button" data-focus="groups">+</button> |
| 1158 | + </div> |
| 1159 | + <div class="lane-title"> |
| 1160 | + <span>账号组</span> |
| 1161 | + <span class="locator">RO.LANE.GROUPS</span> |
| 1162 | + </div> |
| 1163 | + <div class="lane-copy">从账号池中选当前请求要经过的账号组。</div> |
1211 | 1164 | </div> |
1212 | | - </div> |
1213 | 1165 |
|
1214 | | - <div class="lane-section" id="group-list" data-collaboration-id="RO.LANE.GROUPS.LIST"></div> |
1215 | | - </article> |
| 1166 | + <div class="lane-section" id="group-list" data-collaboration-id="RO.LANE.GROUPS.LIST"></div> |
| 1167 | + </article> |
1216 | 1168 |
|
1217 | | - <article class="lane" data-lane="proxies" data-collaboration-id="RO.LANE.PROXIES"> |
1218 | | - <div class="lane-head"> |
1219 | | - <div class="lane-head-top"> |
1220 | | - <div class="lane-number">node 03</div> |
1221 | | - <button class="lane-toggle" type="button" data-focus="proxies">+</button> |
1222 | | - </div> |
1223 | | - <div class="lane-title"> |
1224 | | - <span>代理池</span> |
1225 | | - <span class="locator">RO.LANE.PROXIES</span> |
1226 | | - </div> |
1227 | | - <div class="lane-copy"> |
1228 | | - 账号在这里决定是否挂到代理池。连到某个代理节点表示走代理, 未挂接则保持直连。 |
| 1169 | + <article class="lane" data-lane="proxies" data-collaboration-id="RO.LANE.PROXIES"> |
| 1170 | + <div class="lane-head"> |
| 1171 | + <div class="lane-head-top"> |
| 1172 | + <div class="lane-number">node 03</div> |
| 1173 | + <button class="lane-toggle" type="button" data-focus="proxies">+</button> |
| 1174 | + </div> |
| 1175 | + <div class="lane-title"> |
| 1176 | + <span>代理池</span> |
| 1177 | + <span class="locator">RO.LANE.PROXIES</span> |
| 1178 | + </div> |
| 1179 | + <div class="lane-copy">账号可直连,也可挂到具体代理节点。</div> |
1229 | 1180 | </div> |
1230 | | - </div> |
1231 | 1181 |
|
1232 | | - <div class="lane-section" id="proxy-list" data-collaboration-id="RO.LANE.PROXIES.LIST"></div> |
1233 | | - </article> |
| 1182 | + <div class="lane-section" id="proxy-list" data-collaboration-id="RO.LANE.PROXIES.LIST"></div> |
| 1183 | + </article> |
1234 | 1184 |
|
1235 | | - <article class="lane" data-lane="end" data-collaboration-id="RO.LANE.END"> |
1236 | | - <div class="lane-head"> |
1237 | | - <div class="lane-head-top"> |
1238 | | - <div class="lane-number">node 04</div> |
1239 | | - <button class="lane-toggle" type="button" data-focus="end">+</button> |
1240 | | - </div> |
1241 | | - <div class="lane-title"> |
1242 | | - <span>结束</span> |
1243 | | - <span class="locator">RO.LANE.END</span> |
1244 | | - </div> |
1245 | | - <div class="lane-copy"> |
1246 | | - 这里是链路收口区。确认当前请求会落到哪些账号, 走不走代理, 并允许直接发起测试。 |
| 1185 | + <article class="lane" data-lane="end" data-collaboration-id="RO.LANE.END"> |
| 1186 | + <div class="lane-head"> |
| 1187 | + <div class="lane-head-top"> |
| 1188 | + <div class="lane-number">node 04</div> |
| 1189 | + <button class="lane-toggle" type="button" data-focus="end">+</button> |
| 1190 | + </div> |
| 1191 | + <div class="lane-title"> |
| 1192 | + <span>结束</span> |
| 1193 | + <span class="locator">RO.LANE.END</span> |
| 1194 | + </div> |
| 1195 | + <div class="lane-copy">收口当前链路,并直接发起测试。</div> |
1247 | 1196 | </div> |
1248 | | - </div> |
1249 | 1197 |
|
1250 | | - <div class="lane-section" id="end-list" data-collaboration-id="RO.LANE.END.LIST"></div> |
1251 | | - </article> |
| 1198 | + <div class="lane-section" id="end-list" data-collaboration-id="RO.LANE.END.LIST"></div> |
| 1199 | + </article> |
1252 | 1200 | </section> |
1253 | 1201 | </div> |
1254 | 1202 |
|
|
1263 | 1211 | <div class="copy-toast" id="copy-toast" aria-live="polite"></div> |
1264 | 1212 |
|
1265 | 1213 | <script> |
1266 | | - const stageMeta = { |
1267 | | - entry: { |
1268 | | - label: "请求入口", |
1269 | | - hint: "只允许 codex 或 claude code 两种来源进入编排。", |
1270 | | - focusCopy: |
1271 | | - "入口只定义这次请求来自哪种 CLI 语义。后续账号组里哪些账号能继续参与, 会直接受这里的客户端类型影响。", |
1272 | | - focusTitle: "入口先定客户端, 再进入账号组", |
1273 | | - focusDetail: |
1274 | | - "这期设计里不再展开模型映射。入口只保留 `codex` 和 `claude code` 两个选项, 让编排画布先聚焦在链路结构本身。", |
1275 | | - }, |
1276 | | - groups: { |
1277 | | - label: "账号组", |
1278 | | - hint: "账号组来自账号池, 当前链路只选择其中一组继续执行。", |
1279 | | - focusCopy: |
1280 | | - "这里看到的不是新建分组, 而是账号池里已经存在的账号集合。请求从入口进来后, 会先选中一组账号作为候选执行面。", |
1281 | | - focusTitle: "账号组是账号池的投影, 不是孤立配置", |
1282 | | - focusDetail: |
1283 | | - "每个组都会展示组内账号和它们支持的入口类型。切换组时, 右侧代理池和结束区会立刻跟着刷新。", |
1284 | | - }, |
1285 | | - proxies: { |
1286 | | - label: "代理池", |
1287 | | - hint: "账号连到代理节点表示走代理, 不连则保持直连。", |
1288 | | - focusCopy: |
1289 | | - "这里是代理编排面。你不再改账号属性本身, 而是把当前组里的账号挂接到代理池节点上, 形成这条请求链路的出站方式。", |
1290 | | - focusTitle: "代理池负责挂接关系, 不是账号详情页", |
1291 | | - focusDetail: |
1292 | | - "一个账号在这里有两种状态: 保持直连, 或挂到某个代理节点。页面会同时展示路由编辑区和代理池摘要区。", |
1293 | | - }, |
1294 | | - end: { |
1295 | | - label: "结束", |
1296 | | - hint: "链路在这里收口, 并允许发起测试。", |
1297 | | - focusCopy: |
1298 | | - "结束区只做收口和验证。它把当前入口、选中的账号组和代理挂接结果汇总起来, 并提供一个直接的测试动作。", |
1299 | | - focusTitle: "结束区负责确认与测试", |
1300 | | - focusDetail: |
1301 | | - "你应该能在这里明确看到: 哪些账号会执行、哪些走代理、哪些保持直连, 然后直接点测试按钮验证整条链路。", |
1302 | | - }, |
1303 | | - }; |
1304 | | - |
1305 | 1214 | const state = { |
1306 | 1215 | cli: "codex", |
1307 | 1216 | activeGroupId: "codex", |
|
1382 | 1291 |
|
1383 | 1292 | const defaultProxyBindings = Object.fromEntries(state.accounts.map((account) => [account.id, account.proxyId])); |
1384 | 1293 |
|
1385 | | - const chainBar = document.getElementById("chain-bar"); |
1386 | 1294 | const graphStage = document.getElementById("graph-stage"); |
1387 | 1295 | const connectionLayer = document.getElementById("connection-layer"); |
1388 | 1296 | const groupList = document.getElementById("group-list"); |
|
1497 | 1405 | }); |
1498 | 1406 | } |
1499 | 1407 |
|
1500 | | - function renderChainBar() { |
1501 | | - const stages = ["entry", "groups", "proxies", "end"]; |
1502 | | - chainBar.innerHTML = stages |
1503 | | - .map((key, index) => { |
1504 | | - const meta = stageMeta[key]; |
1505 | | - const active = state.focus === key ? "active" : ""; |
1506 | | - return ` |
1507 | | - <button class="chain-node ${active}" type="button" data-focus="${key}" data-collaboration-id="RO.CHAIN.${toLocatorSegment(key)}"> |
1508 | | - <span class="chain-index">0${index + 1}</span> |
1509 | | - <span class="chain-copy"> |
1510 | | - <strong>${meta.label}</strong> |
1511 | | - <span class="locator">RO.CHAIN.${toLocatorSegment(key)}</span> |
1512 | | - <span>${meta.hint}</span> |
1513 | | - </span> |
1514 | | - </button> |
1515 | | - `; |
1516 | | - }) |
1517 | | - .join(""); |
1518 | | - } |
1519 | | - |
1520 | 1408 | function renderGroups(computed) { |
1521 | 1409 | groupList.innerHTML = state.groups |
1522 | 1410 | .map((group) => { |
|
1789 | 1677 | const computed = computeAccounts(); |
1790 | 1678 | renderButtons(); |
1791 | 1679 | renderMeta(computed); |
1792 | | - renderChainBar(); |
1793 | 1680 | renderGroups(computed); |
1794 | 1681 | renderProxies(computed); |
1795 | 1682 | renderEnd(computed); |
|
0 commit comments