加入favicon

This commit is contained in:
yinjun.chen1
2026-09-18 10:02:00 +08:00
parent de705acfeb
commit 7e931a4372
8 changed files with 56 additions and 3 deletions
+8
View File
@@ -808,6 +808,14 @@ h2 {
min-width: 0;
}
.config-favicon {
width: 16px;
height: 16px;
flex-shrink: 0;
border-radius: 3px;
margin-right: 2px;
}
.config-domain {
font-weight: 600;
color: #e94560;
+1
View File
@@ -139,6 +139,7 @@ export function initBookmarkImport(ctx: PopupCtx) {
submitSelector: null,
enabled: true,
delayMs: null,
favicon: null,
});
added++;
}
+2 -1
View File
@@ -3,7 +3,7 @@
* 通过 ctx.openEditModal(cfg | null) 打开:cfg 为 null 表示新增。
*/
import { t } from "../i18n.js";
import { resolveAlias } from "../ui-utils.js";
import { resolveAlias, fetchFaviconByDomain } from "../ui-utils.js";
import type { DomainConfig } from "../types.js";
import { $ } from "./dom";
import type { PopupCtx } from "./context";
@@ -92,6 +92,7 @@ export function initEditConfigModal(ctx: PopupCtx) {
submitSelector: editSubmitSelectorInput.value.trim() || null,
enabled: editEnabledCheckbox.checked,
delayMs: editDelayMsInput.value ? Number(editDelayMsInput.value) : null,
favicon: (await fetchFaviconByDomain(domain)) || null,
};
const configs = await EnvStore.loadConfigs();
+2 -1
View File
@@ -3,7 +3,7 @@
* 添加按钮提交、"填入当前域名"按钮。
*/
import { t } from "../i18n.js";
import { resolveAlias } from "../ui-utils.js";
import { resolveAlias, fetchFaviconByDomain } from "../ui-utils.js";
import type { DomainConfig } from "../types.js";
import { $ } from "./dom";
import type { PopupCtx } from "./context";
@@ -120,6 +120,7 @@ export function initConfigForm(ctx: PopupCtx) {
submitSelector: submitSelectorInput.value.trim() || null,
enabled: enabledCheckbox.checked,
delayMs: delayMsInput.value ? Number(delayMsInput.value) : null,
favicon: (await fetchFaviconByDomain(domain)) || null,
};
const configs = await EnvStore.loadConfigs();
+14 -1
View File
@@ -3,7 +3,7 @@
* 空状态与"添加区/列表区"位置互换。
*/
import { t } from "../i18n.js";
import { closeAllMenus, openConfigUrl } from "../ui-utils.js";
import { closeAllMenus, openConfigUrl, faviconFallbackUrl } from "../ui-utils.js";
import { $ } from "./dom";
import type { PopupCtx } from "./context";
@@ -137,6 +137,19 @@ export function initConfigList(ctx: PopupCtx) {
<div class="config-domain">${displayName} ${enabledTag} ${authTag}</div>
`;
// favicon
const faviconSrc = cfg.favicon || faviconFallbackUrl(cfg.domain);
if (faviconSrc) {
const favicon = document.createElement("img");
favicon.className = "config-favicon";
favicon.src = faviconSrc;
favicon.alt = "";
favicon.addEventListener("error", () => {
favicon.style.display = "none";
});
item.appendChild(favicon);
}
if (needsAuth) {
const tag = info.querySelector(".tag-unauthorized");
if (tag) {
+1
View File
@@ -148,6 +148,7 @@ export function initTransfer(ctx: PopupCtx) {
submitSelector: cfg.submitSelector || null,
enabled: cfg.enabled !== false,
delayMs: cfg.delayMs || null,
favicon: cfg.favicon || null,
});
// 分支 A:导入 v3 环境结构 —— 同名环境合并配置(按 domain 去重),新环境创建
+1
View File
@@ -11,6 +11,7 @@ export interface DomainConfig {
submitSelector: string | null;
enabled: boolean;
delayMs: number | null;
favicon: string | null;
}
/** 环境 */
+27
View File
@@ -38,3 +38,30 @@ export async function resolveAlias(domain: string): Promise<string | null> {
export function closeAllMenus() {
document.querySelectorAll(".config-menu.open").forEach((m) => m.classList.remove("open"));
}
/**
* 从已打开的标签页里找匹配域名的页面,取其 favIconUrl。
* 找不到时返回 null,渲染时用 faviconFallbackUrl 兜底。
*/
export async function fetchFaviconByDomain(domain: string): Promise<string | null> {
try {
const base = /^https?:\/\//i.test(domain) ? domain : "*://" + domain;
const patterns = [base];
if (!base.endsWith("*")) patterns.push(base + "/*");
const tabs = await chrome.tabs.query({ url: patterns });
for (const tab of tabs) {
if (tab.url && tab.favIconUrl && isDomainMatch(tab.url, domain)) return tab.favIconUrl;
}
} catch (e) {}
return null;
}
/** 从 domain 构造 /favicon.ico 兜底 URL */
export function faviconFallbackUrl(domain: string): string | null {
try {
const withProto = /^https?:\/\//i.test(domain) ? domain : "https://" + domain;
const u = new URL(withProto);
return u.origin + "/favicon.ico";
} catch (e) {
return null;
}
}