修复设置自动保存与标签清理边界
This commit is contained in:
@@ -1,9 +1,31 @@
|
||||
(() => {
|
||||
const PORTAL_BASE_URL = 'https://flowpilot.qlhazycoder.top';
|
||||
const CONTENT_SUMMARY_API_URL = `${PORTAL_BASE_URL}/api/content-summary`;
|
||||
const CACHE_KEY = 'multipage-contribution-content-summary-v1';
|
||||
const CACHE_KEY_PREFIX = 'multipage-contribution-content-summary-v2';
|
||||
const FETCH_TIMEOUT_MS = 6000;
|
||||
|
||||
function normalizeScopeId(value = '', fallback = '') {
|
||||
return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '-') || fallback;
|
||||
}
|
||||
|
||||
function normalizeScope(options = {}) {
|
||||
const flowId = normalizeScopeId(options.flowId || options.flow || options.activeFlowId, 'openai');
|
||||
const targetFallback = flowId === 'kiro' ? 'kiro-rs' : 'cpa';
|
||||
const targetId = normalizeScopeId(options.targetId || options.target || options.activeTargetId, targetFallback);
|
||||
return { flowId, targetId };
|
||||
}
|
||||
|
||||
function getCacheKey(scope = normalizeScope()) {
|
||||
return `${CACHE_KEY_PREFIX}:${scope.flowId}:${scope.targetId}`;
|
||||
}
|
||||
|
||||
function buildSummaryApiUrl(scope = normalizeScope()) {
|
||||
const url = new URL(CONTENT_SUMMARY_API_URL);
|
||||
url.searchParams.set('flow', scope.flowId);
|
||||
url.searchParams.set('target', scope.targetId);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function sanitizeItem(item = {}) {
|
||||
return {
|
||||
slug: String(item?.slug || '').trim(),
|
||||
@@ -14,13 +36,20 @@
|
||||
isVisible: Boolean(item?.is_visible ?? item?.isVisible),
|
||||
updatedAt: String(item?.updated_at ?? item?.updatedAt ?? '').trim(),
|
||||
updatedAtDisplay: String(item?.updated_at_display ?? item?.updatedAtDisplay ?? '').trim(),
|
||||
flowId: normalizeScopeId(item?.flow_id ?? item?.flowId, ''),
|
||||
targetId: normalizeScopeId(item?.target_id ?? item?.targetId, ''),
|
||||
scope: String(item?.scope || '').trim(),
|
||||
};
|
||||
}
|
||||
|
||||
function buildSnapshot(payload = {}) {
|
||||
function buildSnapshot(payload = {}, scope = normalizeScope()) {
|
||||
const items = Array.isArray(payload?.items)
|
||||
? payload.items.map(sanitizeItem).filter((item) => item.slug)
|
||||
: [];
|
||||
const responseScope = normalizeScope({
|
||||
flowId: payload?.flow_id || payload?.flowId || scope.flowId,
|
||||
targetId: payload?.target_id || payload?.targetId || scope.targetId,
|
||||
});
|
||||
const promptVersion = String(payload?.prompt_version || '').trim();
|
||||
const latestUpdatedAt = String(payload?.latest_updated_at || '').trim();
|
||||
const latestUpdatedAtDisplay = String(payload?.latest_updated_at_display || '').trim();
|
||||
@@ -33,15 +62,17 @@
|
||||
latestUpdatedAt,
|
||||
latestUpdatedAtDisplay,
|
||||
items,
|
||||
flowId: responseScope.flowId,
|
||||
targetId: responseScope.targetId,
|
||||
portalUrl: PORTAL_BASE_URL,
|
||||
apiUrl: CONTENT_SUMMARY_API_URL,
|
||||
apiUrl: buildSummaryApiUrl(responseScope),
|
||||
checkedAt: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
function readCache() {
|
||||
function readCache(scope = normalizeScope()) {
|
||||
try {
|
||||
const raw = localStorage.getItem(CACHE_KEY);
|
||||
const raw = localStorage.getItem(getCacheKey(scope));
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
@@ -57,7 +88,9 @@
|
||||
has_visible_updates: parsed.hasVisibleUpdates,
|
||||
latest_updated_at: parsed.latestUpdatedAt,
|
||||
latest_updated_at_display: parsed.latestUpdatedAtDisplay,
|
||||
});
|
||||
flow_id: parsed.flowId,
|
||||
target_id: parsed.targetId,
|
||||
}, scope);
|
||||
if (!Number.isFinite(parsed.checkedAt)) {
|
||||
return snapshot;
|
||||
}
|
||||
@@ -68,20 +101,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
function writeCache(snapshot) {
|
||||
function writeCache(snapshot, scope = normalizeScope(snapshot)) {
|
||||
try {
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify(snapshot));
|
||||
localStorage.setItem(getCacheKey(scope), JSON.stringify(snapshot));
|
||||
} catch (error) {
|
||||
// Ignore cache write failures.
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchContentSummary() {
|
||||
async function fetchContentSummary(options = {}) {
|
||||
const scope = normalizeScope(options);
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
||||
|
||||
try {
|
||||
const response = await fetch(CONTENT_SUMMARY_API_URL, {
|
||||
const response = await fetch(buildSummaryApiUrl(scope), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
@@ -99,8 +133,8 @@
|
||||
throw new Error('内容摘要返回格式异常');
|
||||
}
|
||||
|
||||
const snapshot = buildSnapshot(payload);
|
||||
writeCache(snapshot);
|
||||
const snapshot = buildSnapshot(payload, scope);
|
||||
writeCache(snapshot, scope);
|
||||
return snapshot;
|
||||
} catch (error) {
|
||||
if (error?.name === 'AbortError') {
|
||||
@@ -112,11 +146,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function getContentUpdateSnapshot() {
|
||||
async function getContentUpdateSnapshot(options = {}) {
|
||||
const scope = normalizeScope(options);
|
||||
try {
|
||||
return await fetchContentSummary();
|
||||
return await fetchContentSummary(scope);
|
||||
} catch (error) {
|
||||
const cached = readCache();
|
||||
const cached = readCache(scope);
|
||||
if (cached) {
|
||||
return {
|
||||
...cached,
|
||||
@@ -132,8 +167,10 @@
|
||||
latestUpdatedAt: '',
|
||||
latestUpdatedAtDisplay: '',
|
||||
items: [],
|
||||
flowId: scope.flowId,
|
||||
targetId: scope.targetId,
|
||||
portalUrl: PORTAL_BASE_URL,
|
||||
apiUrl: CONTENT_SUMMARY_API_URL,
|
||||
apiUrl: buildSummaryApiUrl(scope),
|
||||
checkedAt: Date.now(),
|
||||
errorMessage: error?.message || '内容摘要获取失败',
|
||||
};
|
||||
@@ -141,6 +178,7 @@
|
||||
}
|
||||
|
||||
window.SidepanelContributionContentService = {
|
||||
buildSummaryApiUrl,
|
||||
getContentUpdateSnapshot,
|
||||
portalUrl: PORTAL_BASE_URL,
|
||||
apiUrl: CONTENT_SUMMARY_API_URL,
|
||||
|
||||
+194
-52
@@ -81,11 +81,98 @@
|
||||
}
|
||||
|
||||
function getContributionSourceLabel(currentState = getLatestState()) {
|
||||
if (getActiveFlowId(currentState) !== 'openai') {
|
||||
const rootScope = typeof window !== 'undefined' ? window : globalThis;
|
||||
const registry = rootScope.MultiPageContributionRegistry || {};
|
||||
const adapter = typeof registry.getAdapterDefinition === 'function'
|
||||
? registry.getAdapterDefinition(currentState.contributionAdapterId || '', { flowId: getActiveFlowId(currentState) })
|
||||
: null;
|
||||
return normalizeString(adapter?.label) || '账号贡献';
|
||||
}
|
||||
return getContributionSource(currentState) === CONTRIBUTION_SOURCE_SUB2API ? 'SUB2API' : 'CPA';
|
||||
}
|
||||
|
||||
function getActiveFlowId(currentState = getLatestState()) {
|
||||
const selectedFlowId = typeof helpers.getSelectedFlowId === 'function'
|
||||
? normalizeString(helpers.getSelectedFlowId(currentState)).toLowerCase()
|
||||
: '';
|
||||
return selectedFlowId || normalizeString(currentState.activeFlowId || currentState.flowId).toLowerCase() || 'openai';
|
||||
}
|
||||
|
||||
function getActiveTargetId(currentState = getLatestState()) {
|
||||
const activeFlowId = getActiveFlowId(currentState);
|
||||
const selectedTargetId = typeof helpers.getSelectedTargetId === 'function'
|
||||
? normalizeString(helpers.getSelectedTargetId(activeFlowId, currentState)).toLowerCase()
|
||||
: '';
|
||||
if (selectedTargetId) {
|
||||
return selectedTargetId;
|
||||
}
|
||||
if (activeFlowId === 'kiro') {
|
||||
return normalizeString(currentState.kiroTargetId || currentState.targetId || 'kiro-rs').toLowerCase() || 'kiro-rs';
|
||||
}
|
||||
return normalizeString(currentState.openaiIntegrationTargetId || currentState.panelMode || currentState.targetId || 'cpa').toLowerCase() || 'cpa';
|
||||
}
|
||||
|
||||
function applySelectedFlowToState(nextState = {}, flowId = 'openai', targetId = '') {
|
||||
const selectedFlowId = normalizeString(flowId).toLowerCase() || 'openai';
|
||||
const selectedTargetId = normalizeString(targetId).toLowerCase();
|
||||
const baseState = nextState && typeof nextState === 'object' ? nextState : {};
|
||||
if (selectedFlowId === 'openai') {
|
||||
return {
|
||||
...baseState,
|
||||
activeFlowId: selectedFlowId,
|
||||
flowId: selectedFlowId,
|
||||
panelMode: selectedTargetId || normalizeString(baseState.panelMode).toLowerCase() || 'cpa',
|
||||
};
|
||||
}
|
||||
return {
|
||||
...baseState,
|
||||
activeFlowId: selectedFlowId,
|
||||
flowId: selectedFlowId,
|
||||
kiroTargetId: selectedTargetId || normalizeString(baseState.kiroTargetId || baseState.targetId).toLowerCase() || 'kiro-rs',
|
||||
};
|
||||
}
|
||||
|
||||
function getContributionTutorialEntry(currentState = getLatestState()) {
|
||||
const rootScope = typeof window !== 'undefined' ? window : globalThis;
|
||||
const registry = rootScope.MultiPageContributionRegistry || {};
|
||||
const activeFlowId = getActiveFlowId(currentState);
|
||||
if (typeof registry.getContributionTutorialEntry === 'function') {
|
||||
return registry.getContributionTutorialEntry(activeFlowId, {
|
||||
adapterId: currentState.contributionAdapterId,
|
||||
portalBaseUrl: contributionPortalUrl,
|
||||
targetId: getActiveTargetId(currentState),
|
||||
});
|
||||
}
|
||||
return {
|
||||
flowId: activeFlowId,
|
||||
targetId: getActiveTargetId(currentState),
|
||||
contributionAdapterId: normalizeString(currentState.contributionAdapterId),
|
||||
portalUrl: normalizeString(contributionPortalUrl),
|
||||
};
|
||||
}
|
||||
|
||||
function getContributionEntryAdapterId(currentState = getLatestState()) {
|
||||
return normalizeString(getContributionTutorialEntry(currentState)?.contributionAdapterId);
|
||||
}
|
||||
|
||||
function isContributionModeAvailable(currentState = getLatestState()) {
|
||||
const rootScope = typeof window !== 'undefined' ? window : globalThis;
|
||||
const registry = rootScope.MultiPageFlowCapabilities?.createFlowCapabilityRegistry?.({
|
||||
defaultFlowId: 'openai',
|
||||
}) || null;
|
||||
if (registry?.resolveSidepanelCapabilities) {
|
||||
return Boolean(registry.resolveSidepanelCapabilities({
|
||||
activeFlowId: getActiveFlowId(currentState),
|
||||
panelMode: currentState?.panelMode,
|
||||
state: currentState,
|
||||
})?.canShowContributionMode);
|
||||
}
|
||||
return Boolean(currentState?.supportsAccountContribution || getActiveFlowId(currentState) === 'openai');
|
||||
}
|
||||
|
||||
function isContributionModeEnabled(currentState = getLatestState()) {
|
||||
return Boolean(currentState.contributionMode);
|
||||
return isContributionModeAvailable(currentState) && Boolean(currentState.accountContributionEnabled);
|
||||
}
|
||||
|
||||
function hasActiveContributionSession(currentState = getLatestState()) {
|
||||
@@ -107,17 +194,22 @@
|
||||
});
|
||||
}
|
||||
|
||||
function syncContributionButton(enabled, blocked) {
|
||||
function syncContributionButton(enabled, blocked, available = true) {
|
||||
if (!dom.btnContributionMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom.btnContributionMode.classList.toggle('is-active', enabled);
|
||||
dom.btnContributionMode.setAttribute('aria-pressed', String(enabled));
|
||||
dom.btnContributionMode.disabled = enabled || blocked;
|
||||
dom.btnContributionMode.title = blocked
|
||||
? '当前流程运行中,暂时不能切换贡献模式'
|
||||
: (enabled ? '当前已在贡献模式' : '进入贡献模式');
|
||||
if (!available) {
|
||||
dom.btnContributionMode.disabled = true;
|
||||
dom.btnContributionMode.title = '当前 flow 不支持贡献模式';
|
||||
return;
|
||||
}
|
||||
dom.btnContributionMode.disabled = actionInFlight;
|
||||
dom.btnContributionMode.title = enabled
|
||||
? '打开当前 flow 教程;当前已在贡献模式'
|
||||
: (blocked ? '打开当前 flow 教程;当前流程运行中暂时不能进入贡献模式' : '打开当前 flow 教程并进入贡献模式');
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
@@ -150,6 +242,23 @@
|
||||
}
|
||||
|
||||
function getOauthStatusText(currentState = getLatestState()) {
|
||||
if (getActiveFlowId(currentState) !== 'openai') {
|
||||
const flowRuntime = getCurrentFlowContributionRuntime(currentState);
|
||||
const status = normalizeString(flowRuntime.status).toLowerCase();
|
||||
if (status === 'submitting') {
|
||||
return '正在提交账号产物';
|
||||
}
|
||||
if (status === 'submitted') {
|
||||
return '账号产物已提交';
|
||||
}
|
||||
if (status === 'skipped') {
|
||||
return '账号产物未就绪';
|
||||
}
|
||||
if (status === 'error') {
|
||||
return '账号产物提交失败';
|
||||
}
|
||||
return isContributionModeEnabled(currentState) ? '等待账号产物' : '未开启贡献模式';
|
||||
}
|
||||
const status = normalizeStatus(currentState.contributionStatus);
|
||||
const hasAuthUrl = Boolean(normalizeString(currentState.contributionAuthUrl));
|
||||
if (!normalizeString(currentState.contributionSessionId) || !hasAuthUrl) {
|
||||
@@ -171,6 +280,10 @@
|
||||
}
|
||||
|
||||
function getCallbackStatusText(currentState = getLatestState()) {
|
||||
if (getActiveFlowId(currentState) !== 'openai') {
|
||||
const flowRuntime = getCurrentFlowContributionRuntime(currentState);
|
||||
return normalizeString(flowRuntime.lastMessage || flowRuntime.error) || '账号产物就绪后会自动提交';
|
||||
}
|
||||
const status = normalizeCallbackStatus(currentState.contributionCallbackStatus);
|
||||
switch (status) {
|
||||
case 'captured':
|
||||
@@ -195,6 +308,9 @@
|
||||
if (statusMessage) {
|
||||
return statusMessage;
|
||||
}
|
||||
if (getActiveFlowId(currentState) !== 'openai') {
|
||||
return '当前账号将用于支持项目维护。扩展会按当前 flow 的贡献适配器收集并提交账号产物,提交过程不会依赖 OpenAI OAuth 配置。';
|
||||
}
|
||||
if (getContributionSource(currentState) === CONTRIBUTION_SOURCE_SUB2API) {
|
||||
const groupName = normalizeString(currentState.contributionTargetGroupName) || CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME;
|
||||
return `当前账号将用于支持项目维护。贡献会通过 SUB2API 完成,并固定写入 ${groupName} 分组;如检测到回调地址,扩展会自动提交并等待服务端确认。`;
|
||||
@@ -202,12 +318,21 @@
|
||||
return DEFAULT_COPY;
|
||||
}
|
||||
|
||||
function getCurrentFlowContributionRuntime(currentState = getLatestState()) {
|
||||
const runtime = currentState?.flowContributionRuntime;
|
||||
if (!runtime || typeof runtime !== 'object') {
|
||||
return {};
|
||||
}
|
||||
const flowRuntime = runtime[getActiveFlowId(currentState)];
|
||||
return flowRuntime && typeof flowRuntime === 'object' ? flowRuntime : {};
|
||||
}
|
||||
|
||||
function getContributionPortalPageUrl() {
|
||||
return normalizeString(contributionPortalUrl);
|
||||
return normalizeString(getContributionTutorialEntry()?.portalUrl || contributionPortalUrl);
|
||||
}
|
||||
|
||||
function getContributionUploadPageUrl() {
|
||||
return normalizeString(contributionUploadUrl);
|
||||
return normalizeString(contributionUploadUrl || contributionPortalUrl);
|
||||
}
|
||||
|
||||
function openContributionPortalPage() {
|
||||
@@ -227,28 +352,32 @@
|
||||
}
|
||||
|
||||
async function syncContributionProfile(partial = {}) {
|
||||
const payload = {
|
||||
nickname: normalizeString(partial.nickname),
|
||||
qq: normalizeString(partial.qq),
|
||||
};
|
||||
const response = await runtime.sendMessage({
|
||||
type: 'SET_CONTRIBUTION_PROFILE',
|
||||
source: 'sidepanel',
|
||||
payload,
|
||||
const nickname = normalizeString(partial.nickname);
|
||||
const qq = normalizeString(partial.qq);
|
||||
if (qq && !/^\d{1,20}$/.test(qq)) {
|
||||
throw new Error('QQ 只能填写数字,且长度不能超过 20 位。');
|
||||
}
|
||||
helpers.applySettingsState?.({
|
||||
...getLatestState(),
|
||||
contributionNickname: nickname,
|
||||
contributionQq: qq,
|
||||
});
|
||||
if (response?.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
if (response?.state) {
|
||||
helpers.applySettingsState?.(response.state);
|
||||
}
|
||||
}
|
||||
|
||||
async function requestContributionMode(enabled) {
|
||||
const selectedFlowId = getActiveFlowId();
|
||||
const selectedTargetId = getActiveTargetId();
|
||||
if (typeof helpers.persistCurrentSettingsForAction === 'function') {
|
||||
await helpers.persistCurrentSettingsForAction();
|
||||
}
|
||||
const response = await runtime.sendMessage({
|
||||
type: 'SET_CONTRIBUTION_MODE',
|
||||
type: 'SET_ACCOUNT_CONTRIBUTION_MODE',
|
||||
source: 'sidepanel',
|
||||
payload: { enabled: Boolean(enabled) },
|
||||
payload: {
|
||||
enabled: Boolean(enabled),
|
||||
flowId: selectedFlowId,
|
||||
adapterId: getContributionEntryAdapterId(),
|
||||
},
|
||||
});
|
||||
|
||||
if (response?.error) {
|
||||
@@ -258,8 +387,9 @@
|
||||
throw new Error('贡献模式切换后未返回最新状态。');
|
||||
}
|
||||
|
||||
helpers.applySettingsState?.(response.state);
|
||||
helpers.updateStatusDisplay?.(response.state);
|
||||
const nextState = applySelectedFlowToState(response.state, selectedFlowId, selectedTargetId);
|
||||
helpers.applySettingsState?.(nextState);
|
||||
helpers.updateStatusDisplay?.(nextState);
|
||||
render();
|
||||
}
|
||||
|
||||
@@ -274,7 +404,7 @@
|
||||
pollInFlight = true;
|
||||
try {
|
||||
const response = await runtime.sendMessage({
|
||||
type: 'POLL_CONTRIBUTION_STATUS',
|
||||
type: 'POLL_FLOW_CONTRIBUTION_STATUS',
|
||||
source: 'sidepanel',
|
||||
payload: {
|
||||
reason: options.reason || 'sidepanel_poll',
|
||||
@@ -299,7 +429,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function startContributionFlow() {
|
||||
async function startAccountContributionFlow() {
|
||||
if (typeof helpers.startContributionAutoRun !== 'function') {
|
||||
throw new Error('贡献模式尚未接入主自动流程启动能力。');
|
||||
}
|
||||
@@ -310,7 +440,6 @@
|
||||
throw new Error('QQ 只能填写数字,且长度不能超过 20 位。');
|
||||
}
|
||||
await syncContributionProfile(profile);
|
||||
|
||||
const started = await helpers.startContributionAutoRun();
|
||||
if (!started) {
|
||||
return;
|
||||
@@ -333,29 +462,28 @@
|
||||
|
||||
function render() {
|
||||
const currentState = getLatestState();
|
||||
const available = isContributionModeAvailable(currentState);
|
||||
const enabled = isContributionModeEnabled(currentState);
|
||||
const blocked = isModeSwitchBlocked();
|
||||
const activeFlowId = getActiveFlowId(currentState);
|
||||
const blocked = available ? isModeSwitchBlocked() : false;
|
||||
const activeElement = typeof document !== 'undefined' ? document.activeElement : null;
|
||||
const sourceLabel = getContributionSourceLabel(currentState);
|
||||
const sourceLabel = available ? getContributionSourceLabel(currentState) : '';
|
||||
|
||||
if (enabled && dom.selectPanelMode) {
|
||||
if (enabled && activeFlowId === 'openai' && dom.selectPanelMode) {
|
||||
dom.selectPanelMode.value = getContributionSource(currentState);
|
||||
}
|
||||
|
||||
helpers.updatePanelModeUI?.();
|
||||
helpers.updateAccountRunHistorySettingsUI?.();
|
||||
|
||||
if (dom.contributionModePanel) {
|
||||
dom.contributionModePanel.hidden = !enabled;
|
||||
if (dom.accountContributionPanel) {
|
||||
dom.accountContributionPanel.hidden = !available || !enabled;
|
||||
}
|
||||
if (dom.contributionModeText) {
|
||||
dom.contributionModeText.textContent = getSummaryText({
|
||||
contributionSource: currentState.contributionSource,
|
||||
contributionTargetGroupName: currentState.contributionTargetGroupName,
|
||||
});
|
||||
if (dom.accountContributionText) {
|
||||
dom.accountContributionText.textContent = getSummaryText(currentState);
|
||||
}
|
||||
if (dom.contributionModeBadge) {
|
||||
dom.contributionModeBadge.textContent = enabled ? sourceLabel : '';
|
||||
if (dom.accountContributionBadge) {
|
||||
dom.accountContributionBadge.textContent = enabled ? sourceLabel : '';
|
||||
}
|
||||
if (dom.inputContributionNickname && activeElement !== dom.inputContributionNickname) {
|
||||
const nextNickname = normalizeString(currentState.contributionNickname);
|
||||
@@ -372,30 +500,38 @@
|
||||
if (dom.contributionOauthStatus) {
|
||||
dom.contributionOauthStatus.textContent = getOauthStatusText(currentState);
|
||||
}
|
||||
if (dom.contributionPrimaryStatusLabel) {
|
||||
dom.contributionPrimaryStatusLabel.textContent = activeFlowId === 'openai' ? 'OAUTH' : '账号产物';
|
||||
}
|
||||
if (dom.contributionCallbackStatus) {
|
||||
dom.contributionCallbackStatus.textContent = getCallbackStatusText(currentState);
|
||||
}
|
||||
if (dom.contributionModeSummary) {
|
||||
dom.contributionModeSummary.textContent = getSummaryText(currentState);
|
||||
if (dom.contributionSecondaryStatusLabel) {
|
||||
dom.contributionSecondaryStatusLabel.textContent = activeFlowId === 'openai' ? '回调' : '提交';
|
||||
}
|
||||
if (dom.accountContributionSummary) {
|
||||
dom.accountContributionSummary.textContent = getSummaryText(currentState);
|
||||
}
|
||||
|
||||
syncContributionRows(enabled);
|
||||
syncContributionButton(enabled, blocked);
|
||||
syncContributionRows(enabled && activeFlowId === 'openai');
|
||||
syncContributionButton(enabled, blocked, available);
|
||||
|
||||
if (dom.selectPanelMode) {
|
||||
dom.selectPanelMode.disabled = enabled;
|
||||
dom.selectPanelMode.disabled = activeFlowId === 'openai' && available && enabled;
|
||||
}
|
||||
|
||||
if (dom.btnStartContribution) {
|
||||
dom.btnStartContribution.disabled = actionInFlight || blocked;
|
||||
dom.btnStartContribution.disabled = !available || actionInFlight || blocked;
|
||||
}
|
||||
|
||||
if (dom.btnOpenContributionUpload) {
|
||||
dom.btnOpenContributionUpload.disabled = false;
|
||||
dom.btnOpenContributionUpload.hidden = !available;
|
||||
dom.btnOpenContributionUpload.disabled = !available;
|
||||
dom.btnOpenContributionUpload.textContent = '已有认证文件?前往上传';
|
||||
}
|
||||
|
||||
if (dom.btnExitContributionMode) {
|
||||
dom.btnExitContributionMode.disabled = actionInFlight || blocked;
|
||||
dom.btnExitContributionMode.disabled = !available || actionInFlight || blocked;
|
||||
dom.btnExitContributionMode.title = blocked ? '当前流程运行中,暂时不能退出贡献模式' : '退出贡献模式';
|
||||
}
|
||||
|
||||
@@ -403,7 +539,7 @@
|
||||
dom.btnOpenAccountRecords.disabled = enabled;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (available && enabled) {
|
||||
helpers.closeConfigMenu?.();
|
||||
helpers.closeAccountRecordsPanel?.();
|
||||
ensurePolling();
|
||||
@@ -427,7 +563,13 @@
|
||||
}
|
||||
render();
|
||||
try {
|
||||
await enterContributionMode();
|
||||
if (isContributionModeEnabled()) {
|
||||
helpers.showToast?.('已打开当前 flow 教程。', 'info', 1800);
|
||||
} else if (isModeSwitchBlocked()) {
|
||||
helpers.showToast?.('已打开当前 flow 教程;当前流程运行中,暂时不能进入贡献模式。', 'warning', 2200);
|
||||
} else {
|
||||
await enterContributionMode();
|
||||
}
|
||||
} catch (error) {
|
||||
helpers.showToast?.(error.message, 'error');
|
||||
} finally {
|
||||
@@ -443,7 +585,7 @@
|
||||
actionInFlight = true;
|
||||
render();
|
||||
try {
|
||||
await startContributionFlow();
|
||||
await startAccountContributionFlow();
|
||||
} catch (error) {
|
||||
helpers.showToast?.(error.message, 'error');
|
||||
} finally {
|
||||
|
||||
+134
-6
@@ -652,6 +652,117 @@ header {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 34px;
|
||||
margin-bottom: 14px;
|
||||
padding: 7px 46px;
|
||||
border: 1px solid color-mix(in srgb, var(--amber) 24%, var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in srgb, var(--amber) 10%, var(--bg-base));
|
||||
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bg-base) 75%, transparent);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.auto-run-ad-badge {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 3px 7px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--amber) 22%, var(--bg-base));
|
||||
color: color-mix(in srgb, var(--amber) 80%, var(--text-primary));
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.auto-run-ad-badge--end {
|
||||
left: auto;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.auto-run-ad-viewport {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.auto-run-ad-track {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: max-content;
|
||||
min-width: 100%;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.auto-run-ad-text {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.auto-run-ad-text--clone {
|
||||
display: none;
|
||||
padding-left: var(--auto-run-ad-gap, 28px);
|
||||
}
|
||||
|
||||
.auto-run-ad-link {
|
||||
color: color-mix(in srgb, var(--amber) 88%, var(--text-primary));
|
||||
font-weight: 700;
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 2px;
|
||||
transition: color var(--transition);
|
||||
}
|
||||
|
||||
.auto-run-ad-link:hover {
|
||||
color: var(--link-hover);
|
||||
}
|
||||
|
||||
.auto-run-ad-link:focus-visible {
|
||||
outline: 2px solid color-mix(in srgb, var(--amber) 36%, var(--border));
|
||||
outline-offset: 2px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar.is-scrolling .auto-run-ad-track {
|
||||
justify-content: flex-start;
|
||||
min-width: max-content;
|
||||
animation: auto-run-ad-marquee var(--auto-run-ad-duration, 16s) linear infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar.is-scrolling:hover .auto-run-ad-track,
|
||||
.auto-run-ad-bar.is-scrolling:focus-within .auto-run-ad-track {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar.is-scrolling .auto-run-ad-text--clone {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes auto-run-ad-marquee {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(calc(-1 * var(--auto-run-ad-scroll-distance, 0px)));
|
||||
}
|
||||
}
|
||||
|
||||
.contribution-mode-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -2068,13 +2179,8 @@ header {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
#row-auto-delay-settings .setting-group-primary {
|
||||
flex: 0 0 auto;
|
||||
min-width: 116px;
|
||||
}
|
||||
|
||||
#row-auto-delay-settings .setting-group-secondary {
|
||||
margin-left: 0;
|
||||
margin-left: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -2082,6 +2188,23 @@ header {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#row-step6-cookie-settings .step6-cookie-cleanup-setting {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.step-execution-range-setting {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.step-range-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.step-range-input {
|
||||
width: 56px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.auto-run-delay-setting {
|
||||
margin-left: auto !important;
|
||||
}
|
||||
@@ -2704,6 +2827,9 @@ header {
|
||||
.step-row.skipped .step-indicator { border-color: var(--blue); background: var(--blue-soft); }
|
||||
.step-row.manual_completed .step-num,
|
||||
.step-row.skipped .step-num { color: var(--blue); }
|
||||
.step-row.disabled { opacity: 0.62; }
|
||||
.step-row.disabled .step-indicator { border-color: var(--border-subtle); background: var(--bg-base); }
|
||||
.step-row.disabled .step-num { color: var(--text-muted); }
|
||||
|
||||
/* Step Button */
|
||||
.step-btn {
|
||||
@@ -2730,6 +2856,7 @@ header {
|
||||
.step-row.stopped .step-btn { border-color: var(--cyan); color: var(--cyan); }
|
||||
.step-row.manual_completed .step-btn,
|
||||
.step-row.skipped .step-btn { border-color: rgba(37, 99, 235, 0.25); color: var(--blue); opacity: 0.82; }
|
||||
.step-row.disabled .step-btn { border-color: var(--border-subtle); color: var(--text-muted); opacity: 0.7; }
|
||||
|
||||
.step-actions {
|
||||
display: flex;
|
||||
@@ -2779,6 +2906,7 @@ header {
|
||||
.step-row.stopped .step-status { color: var(--cyan); }
|
||||
.step-row.manual_completed .step-status,
|
||||
.step-row.skipped .step-status { color: var(--blue); }
|
||||
.step-row.disabled .step-status { color: var(--text-muted); }
|
||||
|
||||
/* ============================================================
|
||||
Log / Console Section
|
||||
|
||||
+98
-24
@@ -80,6 +80,17 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="auto-run-ad-bar" class="auto-run-ad-bar" hidden aria-live="polite">
|
||||
<span class="auto-run-ad-badge">作者自营</span>
|
||||
<div id="auto-run-ad-viewport" class="auto-run-ad-viewport">
|
||||
<div id="auto-run-ad-track" class="auto-run-ad-track">
|
||||
<span id="auto-run-ad-text" class="auto-run-ad-text"></span>
|
||||
<span id="auto-run-ad-text-clone" class="auto-run-ad-text auto-run-ad-text--clone" aria-hidden="true"></span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="auto-run-ad-badge auto-run-ad-badge--end">售后保障</span>
|
||||
</div>
|
||||
|
||||
<div id="contribution-update-layer" class="contribution-update-layer" hidden>
|
||||
<div id="contribution-update-hint" class="contribution-update-hint" aria-live="polite" hidden>
|
||||
<p id="contribution-update-hint-text" class="contribution-update-hint-text">
|
||||
@@ -113,13 +124,12 @@
|
||||
<div class="data-row">
|
||||
<span class="data-label">注册</span>
|
||||
<select id="select-flow" class="data-select" aria-label="Flow 选择">
|
||||
<option value="codex" selected>codex</option>
|
||||
<option value="pending-1" disabled>kiro</option>
|
||||
<option value="pending-2" disabled>待添加</option>
|
||||
<option value="openai" selected>Codex / OpenAI</option>
|
||||
<option value="kiro">Kiro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="data-row">
|
||||
<span class="data-label">来源</span>
|
||||
<span id="label-source-selector" class="data-label">来源</span>
|
||||
<select id="select-panel-mode" class="data-select">
|
||||
<option value="cpa">CPA 面板</option>
|
||||
<option value="sub2api">SUB2API</option>
|
||||
@@ -144,11 +154,11 @@
|
||||
</div>
|
||||
<div class="contribution-mode-status-grid">
|
||||
<div class="contribution-mode-status-card">
|
||||
<span class="contribution-mode-status-label">OAUTH</span>
|
||||
<span id="contribution-primary-status-label" class="contribution-mode-status-label">OAUTH</span>
|
||||
<span id="contribution-oauth-status" class="contribution-mode-status-value">未生成登录地址</span>
|
||||
</div>
|
||||
<div class="contribution-mode-status-card">
|
||||
<span class="contribution-mode-status-label">回调</span>
|
||||
<span id="contribution-secondary-status-label" class="contribution-mode-status-label">回调</span>
|
||||
<span id="contribution-callback-status" class="contribution-mode-status-value">等待回调</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -242,11 +252,49 @@
|
||||
title="显示 Codex2API 管理密钥"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-kiro-rs-url" style="display:none;">
|
||||
<span class="data-label">kiro.rs</span>
|
||||
<div class="data-inline">
|
||||
<input type="text" id="input-kiro-rs-url" class="data-input"
|
||||
placeholder="请输入 kiro.rs 管理地址" />
|
||||
<button id="btn-open-kiro-rs-github" class="btn btn-ghost btn-xs data-inline-btn" type="button">GitHub</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-kiro-rs-key" style="display:none;">
|
||||
<span class="data-label">API Key</span>
|
||||
<div class="data-inline">
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-kiro-rs-key" class="data-input data-input-with-icon"
|
||||
placeholder="请输入 kiro.rs API Key" />
|
||||
<button id="btn-toggle-kiro-rs-key" class="input-icon-btn" type="button"
|
||||
data-password-toggle="input-kiro-rs-key" data-show-label="显示 kiro.rs API Key"
|
||||
data-hide-label="隐藏 kiro.rs API Key" aria-label="显示 kiro.rs API Key"
|
||||
title="显示 kiro.rs API Key"></button>
|
||||
</div>
|
||||
<button id="btn-test-kiro-rs" class="btn btn-ghost btn-xs data-inline-btn" type="button">测试</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-kiro-rs-test-status" style="display:none;">
|
||||
<span class="data-label">连接测试</span>
|
||||
<span id="display-kiro-rs-test-status" class="data-value mono">未测试</span>
|
||||
</div>
|
||||
<div class="data-row" id="row-kiro-web-status" style="display:none;">
|
||||
<span class="data-label">Kiro Web</span>
|
||||
<span id="display-kiro-web-status" class="data-value mono">未开始</span>
|
||||
</div>
|
||||
<div class="data-row" id="row-kiro-login-url" style="display:none;">
|
||||
<span class="data-label">注册入口</span>
|
||||
<span id="display-kiro-login-url" class="data-value mono">未打开</span>
|
||||
</div>
|
||||
<div class="data-row" id="row-kiro-upload-status" style="display:none;">
|
||||
<span class="data-label">上传状态</span>
|
||||
<span id="display-kiro-upload-status" class="data-value mono">未开始</span>
|
||||
</div>
|
||||
<div class="data-row module-divider-start" id="row-custom-password">
|
||||
<span class="data-label">账户密码</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-password" class="data-input data-input-with-icon"
|
||||
placeholder="codex密码,留空则自动生成" />
|
||||
placeholder="账户密码,留空则自动生成" />
|
||||
<button id="btn-toggle-password" class="input-icon-btn" type="button" aria-label="显示密码" title="显示密码"></button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -264,13 +312,26 @@
|
||||
<span class="setting-caption">Plus 订阅链路</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-plus-account-access-strategy" style="display:none;">
|
||||
<span class="data-label">账号接入策略</span>
|
||||
<div class="data-inline">
|
||||
<select id="select-plus-account-access-strategy" class="data-select">
|
||||
<option value="oauth">OAuth</option>
|
||||
<option value="cpa_codex_session">导入当前 ChatGPT 会话到 CPA</option>
|
||||
<option value="sub2api_codex_session">导入当前 ChatGPT 会话到 SUB2API</option>
|
||||
</select>
|
||||
<span class="setting-caption" id="plus-account-access-strategy-caption">当前来源仅支持 OAuth</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-plus-payment-method" style="display:none;">
|
||||
<span class="data-label">Plus 支付</span>
|
||||
<div class="data-inline">
|
||||
<select id="select-plus-payment-method" class="data-select">
|
||||
<option value="paypal">PayPal</option>
|
||||
<!--
|
||||
<option value="gopay">GoPay</option>
|
||||
<option value="gpc-helper">GPC</option>
|
||||
-->
|
||||
</select>
|
||||
<button id="btn-gpc-card-key-purchase" class="btn btn-outline btn-sm data-inline-btn" type="button" style="display:none;">购买卡密</button>
|
||||
<span class="setting-caption" id="plus-payment-method-caption">PayPal 订阅链路</span>
|
||||
@@ -565,9 +626,9 @@
|
||||
<button id="btn-fetch-email" class="btn btn-outline btn-sm data-inline-btn" type="button">获取</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row module-divider-start" id="row-auto-delay-settings">
|
||||
<div class="data-row" id="row-step6-cookie-settings">
|
||||
<span class="data-label">第六步</span>
|
||||
<div class="data-inline setting-pair auto-delay-setting-pair">
|
||||
<div class="data-inline">
|
||||
<div class="setting-group setting-group-primary step6-cookie-cleanup-setting">
|
||||
<span class="setting-caption setting-caption-left">清 Cookies</span>
|
||||
<label class="toggle-switch" for="input-step6-cookie-cleanup-enabled">
|
||||
@@ -577,6 +638,11 @@
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row module-divider-start" id="row-auto-delay-settings">
|
||||
<span class="data-label">启动前</span>
|
||||
<div class="data-inline setting-pair auto-delay-setting-pair">
|
||||
<div class="setting-group setting-group-secondary auto-run-delay-setting">
|
||||
<span class="setting-caption">延迟</span>
|
||||
<label class="toggle-switch" for="input-auto-delay-enabled">
|
||||
@@ -593,21 +659,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-operation-delay-settings">
|
||||
<span class="data-label">操作间延迟</span>
|
||||
<div class="data-inline setting-pair operation-delay-setting-pair">
|
||||
<div class="setting-group setting-group-primary operation-delay-setting">
|
||||
<label class="toggle-switch" for="input-operation-delay-enabled" title="开启后,页面输入、选择、点击、提交、继续、授权操作完成后等待 2 秒">
|
||||
<input type="checkbox" id="input-operation-delay-enabled" checked />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
<span>启用</span>
|
||||
</label>
|
||||
<span class="setting-caption">页面输入、选择、点击、提交、继续、授权后等待 2 秒</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row">
|
||||
<span class="data-label">自动重试</span>
|
||||
<div class="data-inline setting-pair">
|
||||
@@ -653,6 +704,26 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-step-execution-range">
|
||||
<span class="data-label">执行范围</span>
|
||||
<div class="data-inline setting-pair step-execution-range-setting">
|
||||
<div class="setting-group setting-group-primary">
|
||||
<label class="toggle-switch" for="input-step-execution-range-enabled" title="开启后,只执行指定起止步骤,范围外步骤禁用">
|
||||
<input type="checkbox" id="input-step-execution-range-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
<span>启用</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-group setting-group-secondary step-range-controls">
|
||||
<span class="setting-caption">节点</span>
|
||||
<select id="input-step-execution-range-from" class="data-select step-range-input" title="允许执行的起始节点"></select>
|
||||
<span class="data-unit">到</span>
|
||||
<select id="input-step-execution-range-to" class="data-select step-range-input" title="允许执行的结束节点"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row module-divider-start" id="row-oauth-display">
|
||||
<span class="data-label">OAuth</span>
|
||||
<span id="display-oauth-url" class="data-value mono truncate">等待中...</span>
|
||||
@@ -1789,6 +1860,9 @@
|
||||
<script src="../hotmail-utils.js"></script>
|
||||
<script src="../luckmail-utils.js"></script>
|
||||
<script src="../yyds-mail-utils.js"></script>
|
||||
<script src="../shared/flow-registry.js"></script>
|
||||
<script src="../shared/contribution-registry.js"></script>
|
||||
<script src="../shared/settings-schema.js"></script>
|
||||
<script src="../shared/flow-capabilities.js"></script>
|
||||
<script src="../data/step-definitions.js"></script>
|
||||
<script src="update-service.js"></script>
|
||||
|
||||
+1869
-233
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user