Repair release-branch CI after folding dev onto main
The release branch inherited a few merge-shaped regressions after the large dev snapshot was layered onto the latest main snapshot. This commit restores the main-compatible stats and TokenRoutes behavior the CI suite expects and adds the missing multi-brand helper export so the release branch reflects a coherent post-merge tree instead of a partially regressed hybrid. Constraint: Must keep the release branch mergeable to main while preserving the current publish snapshot shape Rejected: Re-open the whole release merge and hand-resolve every file again | too broad for targeted CI repair Confidence: medium Scope-risk: narrow Reversibility: clean Directive: When dev and main have drifted heavily, re-run the main-facing CI tests after any conflict normalization pass Tested: targeted vitest suite for failing CI jobs plus update-center coverage; typecheck Not-tested: Full npm test still not representative locally because of unrelated untracked tmp/sub2api suites outside the PR branch content
This commit is contained in:
+1483
-1204
File diff suppressed because it is too large
Load Diff
@@ -777,30 +777,46 @@ export function getAllBrandNames(): string[] {
|
||||
return BRAND_DEFINITIONS.map((brand) => brand.name);
|
||||
}
|
||||
|
||||
export function getBrand(modelName: string): BrandInfo | null {
|
||||
function toBrandInfo(brand: BrandDefinition): BrandInfo {
|
||||
return {
|
||||
name: brand.name,
|
||||
icon: brand.icon,
|
||||
color: brand.color,
|
||||
};
|
||||
}
|
||||
|
||||
export function getMatchingBrands(modelName: string): BrandInfo[] {
|
||||
const context = buildMatchContext(modelName);
|
||||
const matches: BrandInfo[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
const add = (brand: BrandDefinition) => {
|
||||
if (seen.has(brand.name)) return;
|
||||
seen.add(brand.name);
|
||||
matches.push(toBrandInfo(brand));
|
||||
};
|
||||
|
||||
for (const definition of BRAND_DEFINITIONS) {
|
||||
if (definition.rules.some((rule) => matchesRule(context, rule))) {
|
||||
return {
|
||||
name: definition.name,
|
||||
icon: definition.icon,
|
||||
color: definition.color,
|
||||
};
|
||||
add(definition);
|
||||
}
|
||||
}
|
||||
|
||||
for (const candidate of context.candidates) {
|
||||
for (const rule of BRAND_FALLBACK_BOUNDARY_RULES) {
|
||||
if (rule.boundaryRegex.test(candidate)) {
|
||||
return {
|
||||
name: rule.brand.name,
|
||||
icon: rule.brand.icon,
|
||||
color: rule.brand.color,
|
||||
};
|
||||
add(rule.brand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return matches;
|
||||
}
|
||||
|
||||
export function getMatchingBrandNames(modelName: string): string[] {
|
||||
return getMatchingBrands(modelName).map((brand) => brand.name);
|
||||
}
|
||||
|
||||
export function getBrand(modelName: string): BrandInfo | null {
|
||||
return getMatchingBrands(modelName)[0] || null;
|
||||
}
|
||||
|
||||
@@ -93,8 +93,14 @@ const EMPTY_ROUTE_FORM: RouteEditorForm = {
|
||||
sourceRouteIds: [],
|
||||
advancedOpen: false,
|
||||
};
|
||||
const DESKTOP_DETAIL_ENTER_MS = 260;
|
||||
const DESKTOP_DETAIL_COLLAPSE_MS = 200;
|
||||
|
||||
function prefersReducedMotion(): boolean {
|
||||
return typeof globalThis.matchMedia === 'function'
|
||||
&& globalThis.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
}
|
||||
|
||||
function getRouteRoutingStrategySuccessMessage(value: RouteRoutingStrategy): string {
|
||||
if (value === 'round_robin') return '已切换为轮询策略';
|
||||
if (value === 'stable_first') return '已切换为稳定优先策略';
|
||||
@@ -114,30 +120,44 @@ export function DesktopDetailPanelPresence({
|
||||
}) {
|
||||
const [shouldRender, setShouldRender] = useState(open);
|
||||
const [isOpen, setIsOpen] = useState(open);
|
||||
const [isEntering, setIsEntering] = useState(false);
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
const hasEverOpenedRef = useRef(open);
|
||||
|
||||
useEffect(() => {
|
||||
const reduceMotion = prefersReducedMotion();
|
||||
|
||||
if (open) {
|
||||
hasEverOpenedRef.current = true;
|
||||
setShouldRender(true);
|
||||
setIsClosing(false);
|
||||
if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
|
||||
const rafId = window.requestAnimationFrame(() => setIsOpen(true));
|
||||
return () => window.cancelAnimationFrame(rafId);
|
||||
}
|
||||
setIsOpen(true);
|
||||
return undefined;
|
||||
setIsClosing(false);
|
||||
if (reduceMotion) {
|
||||
setIsEntering(false);
|
||||
return undefined;
|
||||
}
|
||||
setIsEntering(true);
|
||||
const enterTimerId = globalThis.setTimeout(() => {
|
||||
setIsEntering(false);
|
||||
}, DESKTOP_DETAIL_ENTER_MS);
|
||||
return () => globalThis.clearTimeout(enterTimerId);
|
||||
}
|
||||
|
||||
if (!hasEverOpenedRef.current) {
|
||||
setShouldRender(false);
|
||||
setIsOpen(false);
|
||||
setIsEntering(false);
|
||||
setIsClosing(false);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setIsOpen(false);
|
||||
setIsEntering(false);
|
||||
if (reduceMotion) {
|
||||
setShouldRender(false);
|
||||
setIsClosing(false);
|
||||
return undefined;
|
||||
}
|
||||
setIsClosing(true);
|
||||
const timerId = globalThis.setTimeout(() => {
|
||||
setShouldRender(false);
|
||||
@@ -150,7 +170,7 @@ export function DesktopDetailPanelPresence({
|
||||
if (!shouldRender) return null;
|
||||
return (
|
||||
<div
|
||||
className={`route-detail-panel-presence ${isOpen ? 'is-open' : ''} ${isClosing ? 'is-closing' : ''}`.trim()}
|
||||
className={`route-detail-panel-presence ${isOpen ? 'is-open' : ''} ${isEntering ? 'is-entering' : ''} ${isClosing ? 'is-closing' : ''}`.trim()}
|
||||
style={{ gridColumn: '1 / -1' }}
|
||||
>
|
||||
{children(isClosing)}
|
||||
@@ -1280,15 +1300,20 @@ export default function TokenRoutes() {
|
||||
const isCurrentlyExpanded = expandedRouteIds.includes(routeId);
|
||||
if (isCurrentlyExpanded) {
|
||||
if (!isMobile) {
|
||||
setClosingDesktopDetailRouteIds((prev) => (prev.includes(routeId) ? prev : [...prev, routeId]));
|
||||
const existingTimer = desktopDetailCloseTimersRef.current[routeId];
|
||||
if (existingTimer) {
|
||||
globalThis.clearTimeout(existingTimer);
|
||||
}
|
||||
desktopDetailCloseTimersRef.current[routeId] = globalThis.setTimeout(() => {
|
||||
const reduceMotion = prefersReducedMotion();
|
||||
if (reduceMotion) {
|
||||
setClosingDesktopDetailRouteIds((prev) => prev.filter((id) => id !== routeId));
|
||||
delete desktopDetailCloseTimersRef.current[routeId];
|
||||
}, DESKTOP_DETAIL_COLLAPSE_MS);
|
||||
} else {
|
||||
setClosingDesktopDetailRouteIds((prev) => (prev.includes(routeId) ? prev : [...prev, routeId]));
|
||||
const existingTimer = desktopDetailCloseTimersRef.current[routeId];
|
||||
if (existingTimer) {
|
||||
globalThis.clearTimeout(existingTimer);
|
||||
}
|
||||
desktopDetailCloseTimersRef.current[routeId] = globalThis.setTimeout(() => {
|
||||
setClosingDesktopDetailRouteIds((prev) => prev.filter((id) => id !== routeId));
|
||||
delete desktopDetailCloseTimersRef.current[routeId];
|
||||
}, DESKTOP_DETAIL_COLLAPSE_MS);
|
||||
}
|
||||
}
|
||||
setExpandedRouteIds((prev) => prev.filter((id) => id !== routeId));
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user