Skip to content

Commit b66feb2

Browse files
authored
Merge pull request #1154 from dbraendle/feat/dashboard-icons-fallback
feat: add dashboard-icons as icon fallback source
2 parents 1a34b5f + 1ec5a33 commit b66feb2

1 file changed

Lines changed: 53 additions & 2 deletions

File tree

bin/options/icon.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,26 @@ function generateIconServiceUrls(domain: string): string[] {
398398
];
399399
}
400400

401+
/**
402+
* Generates dashboard-icons URLs for an app name.
403+
* Uses walkxcode/dashboard-icons as a final fallback for selfhosted apps.
404+
* Keeps matching conservative to avoid overriding valid site-specific icons.
405+
*/
406+
function generateDashboardIconUrls(appName: string): string[] {
407+
const baseUrl = 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png';
408+
const name = appName.toLowerCase().trim();
409+
const slugs = new Set<string>();
410+
411+
// Exact name
412+
slugs.add(name);
413+
// Replace spaces with hyphens
414+
slugs.add(name.replace(/\s+/g, '-'));
415+
416+
return [...slugs]
417+
.filter((s) => s.length > 0)
418+
.map((slug) => `${baseUrl}/${slug}.png`);
419+
}
420+
401421
/**
402422
* Attempts to fetch favicon from website
403423
*/
@@ -409,14 +429,14 @@ async function tryGetFavicon(
409429
const domain = new URL(url).hostname;
410430
const spinner = getSpinner(`Fetching icon from ${domain}...`);
411431

412-
const serviceUrls = generateIconServiceUrls(domain);
413-
414432
const isCI =
415433
process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
416434
const downloadTimeout = isCI
417435
? ICON_CONFIG.downloadTimeout.ci
418436
: ICON_CONFIG.downloadTimeout.default;
419437

438+
const serviceUrls = generateIconServiceUrls(domain);
439+
420440
for (const serviceUrl of serviceUrls) {
421441
try {
422442
const faviconPath = await downloadIcon(
@@ -445,6 +465,37 @@ async function tryGetFavicon(
445465
}
446466
}
447467

468+
// Final fallback for selfhosted apps behind auth where domain-based
469+
// services cannot access the site favicon.
470+
if (appName) {
471+
const dashboardIconUrls = generateDashboardIconUrls(appName);
472+
for (const iconUrl of dashboardIconUrls) {
473+
try {
474+
const iconPath = await downloadIcon(iconUrl, false, downloadTimeout);
475+
if (!iconPath) continue;
476+
477+
const convertedPath = await convertIconFormat(iconPath, appName);
478+
if (convertedPath) {
479+
const finalPath = await copyWindowsIconIfNeeded(
480+
convertedPath,
481+
appName,
482+
);
483+
spinner.succeed(
484+
chalk.green(
485+
`Icon found via dashboard-icons fallback for "${appName}"!`,
486+
),
487+
);
488+
return finalPath;
489+
}
490+
} catch (error: unknown) {
491+
if (error instanceof Error) {
492+
logger.debug(`Dashboard icon ${iconUrl} failed: ${error.message}`);
493+
}
494+
continue;
495+
}
496+
}
497+
}
498+
448499
spinner.warn(`No favicon found for ${domain}. Using default.`);
449500
return null;
450501
} catch (error) {

0 commit comments

Comments
 (0)