feat(webui): polish agent output and app discovery
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user