feat(webui): polish agent output and app discovery

This commit is contained in:
Xubin Ren
2026-07-22 22:42:31 +08:00
parent b189a37648
commit aa8387fb4d
87 changed files with 6225 additions and 2379 deletions
+36 -1
View File
@@ -17,6 +17,10 @@ function googleFaviconUrl(domain: string): string {
return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=64`;
}
function faviconImUrl(domain: string): string {
return `https://favicon.im/${encodeURIComponent(domain)}?larger=true`;
}
export function faviconUrls(domain: string): string[] {
const faviconDomain = faviconDomainFromValue(domain);
return [
@@ -26,6 +30,22 @@ export function faviconUrls(domain: string): string[] {
];
}
/**
* Cross-origin page favicons commonly opt into same-origin resource policy.
* Prefer image proxies for arbitrary links while retaining the official icon
* as a final fallback. Explicit first-party brand assets remain first when a
* provider supplies them.
*/
export function browserSafeFaviconUrls(domain: string): string[] {
const faviconDomain = faviconDomainFromValue(domain);
return [
faviconImUrl(faviconDomain),
googleFaviconUrl(domain),
duckDuckGoFaviconUrl(faviconDomain),
officialFaviconUrl(faviconDomain),
];
}
function brand(
domain: string,
color: string,
@@ -33,7 +53,7 @@ function brand(
logoOverrides: string[] = [],
): ProviderBrand {
const logoUrls = [...logoOverrides];
faviconUrls(domain).forEach((url) => addUniqueLogoUrl(logoUrls, url));
browserSafeFaviconUrls(domain).forEach((url) => addUniqueLogoUrl(logoUrls, url));
return {
logoUrl: logoUrls[0],
logoUrls,
@@ -60,12 +80,27 @@ function domainFromLogoUrl(url: string): string | null {
const match = parsed.pathname.match(/^\/ip3\/(.+)\.ico$/);
return match ? decodeURIComponent(match[1]) : null;
}
if (host === "favicon.im") {
return decodeURIComponent(parsed.pathname.replace(/^\//, "")) || null;
}
return host.replace(/^www\./, "");
} catch {
return null;
}
}
/**
* A repository favicon identifies the hosting service, not the app itself.
* Apps backed by GitHub repositories should keep their distinct initials
* instead of appearing to share one GitHub identity.
*/
export function isGenericRepositoryLogoUrl(logoUrl: string | null | undefined): boolean {
const value = logoUrl?.trim();
if (!value) return false;
const domain = domainFromLogoUrl(value)?.toLowerCase();
return domain === "github.com" || domain?.startsWith("github.com/") === true;
}
function faviconDomainFromValue(value: string): string {
const host = value.split("/")[0]?.trim();
return host || value;