From b84732e5060085a81e97fc11aede45ba307da3ad Mon Sep 17 00:00:00 2001
From: QLHazyCoder <2825305047@qq.com>
Date: Thu, 28 May 2026 22:23:53 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E4=BE=A7=E8=BE=B9?=
=?UTF-8?q?=E6=A0=8F=E6=8C=89=E9=92=AE=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=9B=AE?=
=?UTF-8?q?=E6=A0=87=E4=BB=93=E5=BA=93=E9=93=BE=E6=8E=A5=E5=92=8C=E6=A0=B7?=
=?UTF-8?q?=E5=BC=8F=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
sidepanel/sidepanel.css | 37 +++++-------
sidepanel/sidepanel.html | 10 ++--
sidepanel/sidepanel.js | 57 +++++++++++++++---
tests/sidepanel-flow-source-registry.test.js | 61 ++++++++++++++++++--
tests/sidepanel-form-layout.test.js | 6 ++
5 files changed, 129 insertions(+), 42 deletions(-)
diff --git a/sidepanel/sidepanel.css b/sidepanel/sidepanel.css
index 1d277dd..7d62ee4 100644
--- a/sidepanel/sidepanel.css
+++ b/sidepanel/sidepanel.css
@@ -412,12 +412,15 @@ header {
.btn {
display: inline-flex;
align-items: center;
+ justify-content: center;
gap: 6px;
- padding: 7px 14px;
+ min-height: var(--data-field-height);
+ padding: 0 12px;
font-family: inherit;
font-size: 13px;
font-weight: 600;
- border: 1px solid transparent;
+ line-height: 1;
+ border: 1px solid var(--border);
border-radius: var(--radius-sm);
cursor: pointer;
transition: all var(--transition);
@@ -433,12 +436,14 @@ header {
.btn-success {
background: var(--green);
color: #fff;
+ border-color: var(--green);
}
.btn-success:hover { opacity: 0.9; box-shadow: 0 2px 8px var(--green-soft); }
.btn-danger {
background: var(--red);
color: #fff;
+ border-color: var(--red);
}
.btn-danger:hover { opacity: 0.9; box-shadow: 0 2px 8px var(--red-soft); }
@@ -468,10 +473,9 @@ header {
.run-count-input:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-ghost {
- background: transparent;
+ background: var(--bg-field);
color: var(--text-secondary);
- padding: 6px;
- border-radius: var(--radius-sm);
+ border-color: var(--border);
}
.btn-ghost:hover { background: var(--bg-hover); color: var(--text-primary); }
@@ -482,8 +486,8 @@ header {
}
.btn-outline:hover { border-color: var(--blue); color: var(--blue); background: var(--blue-soft); }
-.btn-sm { padding: 5px 12px; font-size: 12px; }
-.btn-xs { padding: 4px 10px; font-size: 11px; }
+.btn-sm { padding-inline: 12px; font-size: 12px; }
+.btn-xs { padding-inline: 10px; font-size: 11px; }
/* ============================================================
Extension Updates
@@ -1547,14 +1551,10 @@ header {
color: var(--text-secondary);
}
-#btn-fetch-email {
- padding-inline: 10px;
- flex-shrink: 0;
-}
-
.data-inline-btn {
flex: 0 0 auto;
min-width: var(--data-inline-action-min-width);
+ min-height: var(--data-field-height);
justify-content: center;
}
@@ -2437,10 +2437,7 @@ header {
.hero-sms-country-menu-btn {
width: 100%;
- height: 33px;
- min-height: 33px;
- padding-top: 0;
- padding-bottom: 0;
+ min-height: var(--data-field-height);
justify-content: flex-start;
overflow: hidden;
text-overflow: ellipsis;
@@ -2543,8 +2540,7 @@ header {
flex: 1 1 auto;
min-width: 0;
width: 100%;
- height: 33px;
- min-height: 33px;
+ min-height: var(--data-field-height);
}
.hero-sms-free-phone-cell {
@@ -2583,10 +2579,7 @@ header {
}
#btn-hero-sms-price-preview {
- height: 33px;
- min-height: 33px;
- padding-top: 0;
- padding-bottom: 0;
+ min-height: var(--data-field-height);
align-self: flex-start;
}
diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html
index fe7cfee..97ba281 100644
--- a/sidepanel/sidepanel.html
+++ b/sidepanel/sidepanel.html
@@ -137,7 +137,8 @@
-
+
API Key
diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js
index 20ca9e2..4c1370c 100644
--- a/sidepanel/sidepanel.js
+++ b/sidepanel/sidepanel.js
@@ -89,7 +89,7 @@ const btnImportSettings = document.getElementById('btn-import-settings');
const inputImportSettingsFile = document.getElementById('input-import-settings-file');
const labelSourceSelector = document.getElementById('label-source-selector');
const selectPanelMode = document.getElementById('select-panel-mode');
-const btnOpenWebchat2ApiGithub = document.getElementById('btn-open-webchat2api-github');
+const btnOpenTargetRepository = document.getElementById('btn-open-target-repository');
const rowVpsUrl = document.getElementById('row-vps-url');
const inputVpsUrl = document.getElementById('input-vps-url');
const rowVpsPassword = document.getElementById('row-vps-password');
@@ -179,7 +179,6 @@ const rowCodex2ApiAdminKey = document.getElementById('row-codex2api-admin-key');
const inputCodex2ApiAdminKey = document.getElementById('input-codex2api-admin-key');
const rowKiroRsUrl = document.getElementById('row-kiro-rs-url');
const inputKiroRsUrl = document.getElementById('input-kiro-rs-url');
-const btnOpenKiroRsGithub = document.getElementById('btn-open-kiro-rs-github');
const rowKiroRsKey = document.getElementById('row-kiro-rs-key');
const inputKiroRsKey = document.getElementById('input-kiro-rs-key');
const btnTestKiroRs = document.getElementById('btn-test-kiro-rs');
@@ -1620,6 +1619,18 @@ let customEmailPoolEntriesState = [];
const EYE_OPEN_ICON = '
';
const EYE_CLOSED_ICON = '
';
+const TARGET_REPOSITORY_URLS = Object.freeze({
+ openai: Object.freeze({
+ cpa: 'https://github.com/router-for-me/CLIProxyAPI',
+ sub2api: 'https://github.com/Wei-Shaw/sub2api',
+ }),
+ kiro: Object.freeze({
+ 'kiro-rs': 'https://github.com/QLHazyCoder/kiro.rs',
+ }),
+ grok: Object.freeze({
+ webchat2api: 'https://github.com/zqbxdev/webchat2api',
+ }),
+});
const PRIVACY_MASKED_INPUT_IDS = Object.freeze([
'input-contribution-qq',
'input-sub2api-url',
@@ -9390,6 +9401,33 @@ function getSelectedTargetId(flowId = getSelectedFlowId()) {
);
}
+function getTargetRepositoryUrl(flowId = getSelectedFlowId(), targetId = getSelectedTargetId(flowId)) {
+ const normalizedFlowId = normalizeFlowId(flowId);
+ const normalizedTargetId = normalizeTargetIdForFlow(
+ normalizedFlowId,
+ targetId,
+ getDefaultTargetIdForFlow(normalizedFlowId)
+ );
+ return TARGET_REPOSITORY_URLS[normalizedFlowId]?.[normalizedTargetId] || '';
+}
+
+function updateTargetRepositoryButton(flowId = getSelectedFlowId(), targetId = getSelectedTargetId(flowId)) {
+ if (!btnOpenTargetRepository) {
+ return;
+ }
+ const repositoryUrl = getTargetRepositoryUrl(flowId, targetId);
+ const hasRepositoryUrl = Boolean(repositoryUrl);
+ btnOpenTargetRepository.hidden = !hasRepositoryUrl;
+ btnOpenTargetRepository.disabled = !hasRepositoryUrl;
+ btnOpenTargetRepository.dataset.repositoryUrl = repositoryUrl;
+ const targetLabel = getFlowRegistry()?.getTargetLabel?.(flowId, targetId) || targetId;
+ const title = hasRepositoryUrl
+ ? `打开 ${targetLabel} 项目 GitHub 仓库`
+ : `${targetLabel} 暂未配置项目仓库`;
+ btnOpenTargetRepository.setAttribute('aria-label', title);
+ btnOpenTargetRepository.title = title;
+}
+
function renderFlowSelectorOptions(selectedFlowId = getSelectedFlowId()) {
if (!selectFlow) {
return [];
@@ -13941,6 +13979,9 @@ function updatePanelModeUI() {
if (selectPanelMode) {
selectPanelMode.value = effectiveTargetId;
}
+ if (typeof updateTargetRepositoryButton === 'function') {
+ updateTargetRepositoryButton(activeFlowId, effectiveTargetId);
+ }
const visibleGroupIds = Array.isArray(capabilityState?.visibleGroupIds)
? capabilityState.visibleGroupIds
: [];
@@ -15820,12 +15861,12 @@ btnGpcHelperConvertApiKey?.addEventListener('click', () => {
openExternalUrl(GPC_HELPER_PORTAL_URL);
});
-btnOpenKiroRsGithub?.addEventListener('click', () => {
- openExternalUrl('https://github.com/QLHazyCoder/kiro.rs');
-});
-
-btnOpenWebchat2ApiGithub?.addEventListener('click', () => {
- openExternalUrl('https://github.com/zqbxdev/webchat2api');
+btnOpenTargetRepository?.addEventListener('click', () => {
+ const repositoryUrl = btnOpenTargetRepository.dataset.repositoryUrl
+ || getTargetRepositoryUrl(getSelectedFlowId(), getSelectedTargetId());
+ if (repositoryUrl) {
+ openExternalUrl(repositoryUrl);
+ }
});
btnGpcHelperBalance?.addEventListener('click', async () => {
diff --git a/tests/sidepanel-flow-source-registry.test.js b/tests/sidepanel-flow-source-registry.test.js
index 8dccd4d..70cc8d9 100644
--- a/tests/sidepanel-flow-source-registry.test.js
+++ b/tests/sidepanel-flow-source-registry.test.js
@@ -38,13 +38,12 @@ test('sidepanel html exposes flow selector and kiro source fields', () => {
'id="select-flow"',
'
',
'id="label-source-selector"',
- 'id="btn-open-webchat2api-github"',
+ 'id="btn-open-target-repository"',
'id="row-step6-cookie-settings"',
'id="row-shared-auto-run"',
'id="row-auto-run-thread-interval"',
'id="row-oauth-callback"',
'id="row-kiro-rs-url"',
- 'id="btn-open-kiro-rs-github"',
'id="row-kiro-rs-key"',
'id="btn-test-kiro-rs"',
'id="row-kiro-rs-test-status"',
@@ -64,6 +63,12 @@ test('sidepanel html exposes flow selector and kiro source fields', () => {
assert.match(sidepanelHtml, new RegExp(snippet.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
});
assert.doesNotMatch(sidepanelHtml, /id="btn-export-grok-sso"/);
+ assert.match(
+ sidepanelHtml,
+ /id="btn-open-target-repository"[^>]*class="btn btn-outline btn-sm data-inline-btn"[^>]*>GitHub<\/button>/
+ );
+ const repositoryButtonTag = sidepanelHtml.match(/