diff --git a/apps/admin-web/src/components/PublishArticleModal.vue b/apps/admin-web/src/components/PublishArticleModal.vue index 271ca61..485838f 100644 --- a/apps/admin-web/src/components/PublishArticleModal.vue +++ b/apps/admin-web/src/components/PublishArticleModal.vue @@ -1,9 +1,15 @@ @@ -596,7 +598,7 @@ function toneClass(tone: CardTone): string { top: 4px; right: -8px; background: #f5f5f5; - color: #bfbfbf; + color: f3f#737373; font-size: 10px; padding: 2px 6px; border-radius: 4px; @@ -716,12 +718,12 @@ function toneClass(tone: CardTone): string { .media-card__identity-text p { margin: 2px 0 0; - color: #bfbfbf; + color: #737373; font-size: 11px; } .more-icon { - color: #bfbfbf; + color: #737373; font-size: 18px; cursor: pointer; padding: 0 4px; @@ -750,7 +752,7 @@ function toneClass(tone: CardTone): string { .media-card__account-unbound { font-size: 13px; - color: #bfbfbf; + color: #737373; } .media-card__footer { diff --git a/apps/admin-web/src/views/WorkspaceView.vue b/apps/admin-web/src/views/WorkspaceView.vue index d6a91f3..3f275f9 100644 --- a/apps/admin-web/src/views/WorkspaceView.vue +++ b/apps/admin-web/src/views/WorkspaceView.vue @@ -501,7 +501,7 @@ function refreshDashboard(): void { align-items: center; gap: 6px; } -.muted { color: #bfbfbf; font-size: 11px; } +.muted { color: #737373; font-size: 11px; } .muted-dot { color: #d9d9d9; font-size: 10px; } /* Status Badges */ diff --git a/apps/browser-extension/entrypoints/background.ts b/apps/browser-extension/entrypoints/background.ts index 2bf7de2..0b1cc03 100644 --- a/apps/browser-extension/entrypoints/background.ts +++ b/apps/browser-extension/entrypoints/background.ts @@ -4,6 +4,16 @@ import { defineBackground } from "wxt/utils/define-background"; import { handlePublisherAction } from "../src/runtime"; import { getExtensionState } from "../src/storage"; +type BackgroundSuccessResponse = { + ok: true; + data: Awaited>; +}; + +type BackgroundErrorResponse = { + ok: false; + error: string; +}; + export default defineBackground(() => { void getExtensionState(); @@ -21,11 +31,25 @@ export default defineBackground(() => { await getExtensionState(); }); - browser.runtime.onMessage.addListener((message) => { + chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { if (!message || message.type !== "geo.publisher.request") { return undefined; } - return handlePublisherAction(message.action, message.payload); + void handlePublisherAction(message.action, message.payload) + .then((data) => { + sendResponse({ + ok: true, + data, + } satisfies BackgroundSuccessResponse); + }) + .catch((error: unknown) => { + sendResponse({ + ok: false, + error: error instanceof Error ? error.message : "publisher_plugin_unknown_error", + } satisfies BackgroundErrorResponse); + }); + + return true; }); }); diff --git a/apps/browser-extension/entrypoints/content.ts b/apps/browser-extension/entrypoints/content.ts index 10a5b6c..5b153ac 100644 --- a/apps/browser-extension/entrypoints/content.ts +++ b/apps/browser-extension/entrypoints/content.ts @@ -9,6 +9,20 @@ type PublisherRequestMessage = { payload?: unknown; }; +type BackgroundSuccessResponse = { + ok: true; + data: unknown; +}; + +type BackgroundErrorResponse = { + ok: false; + error?: string; +}; + +function isBackgroundResponse(value: unknown): value is BackgroundSuccessResponse | BackgroundErrorResponse { + return typeof value === "object" && value !== null && "ok" in value; +} + export default defineContentScript({ matches: ["http://*/*", "https://*/*"], main() { @@ -23,12 +37,25 @@ export default defineContentScript({ const { requestId, action, payload } = event.data; try { - const data = await browser.runtime.sendMessage({ + const response = await browser.runtime.sendMessage({ type: "geo.publisher.request", action, payload, }); + const data = isBackgroundResponse(response) + ? (() => { + if (!response.ok) { + throw new Error(response.error || "publisher_plugin_unknown_error"); + } + return response.data; + })() + : response; + + if (typeof data === "undefined") { + throw new Error("publisher_plugin_empty_response"); + } + window.postMessage( { source: "geo-extension", diff --git a/apps/browser-extension/entrypoints/popup/App.vue b/apps/browser-extension/entrypoints/popup/App.vue index be0b90f..ce4763b 100644 --- a/apps/browser-extension/entrypoints/popup/App.vue +++ b/apps/browser-extension/entrypoints/popup/App.vue @@ -2,14 +2,22 @@ import { computed, onMounted, ref } from "vue"; import { browser } from "wxt/browser"; -import { getExtensionState, resetPlatformStates, updatePlatformState } from "../../src/storage"; +import { normalizeRemoteUrl } from "../../src/adapters/common"; +import { getExtensionState } from "../../src/storage"; import type { StoredPlatformState } from "../../src/platforms"; +type BackgroundSuccessResponse = { + ok: true; + data: unknown; +}; + +type BackgroundErrorResponse = { + ok: false; + error?: string; +}; + const loading = ref(true); const refreshing = ref(false); -const resetting = ref(false); -const savingId = ref(null); -const expandedId = ref(null); const installationKey = ref(""); const installationId = ref(null); @@ -33,6 +41,10 @@ function clonePlatforms(items: StoredPlatformState[]): StoredPlatformState[] { return items.map((platform) => ({ ...platform })); } +function isBackgroundResponse(value: unknown): value is BackgroundSuccessResponse | BackgroundErrorResponse { + return typeof value === "object" && value !== null && "ok" in value; +} + async function loadState(): Promise { loading.value = true; try { @@ -64,8 +76,9 @@ function uidFor(platform: StoredPlatformState): string { } function avatarFor(platform: StoredPlatformState): string | null { - if (platform.avatar_url?.trim()) { - return platform.avatar_url.trim(); + const normalizedAvatar = normalizeRemoteUrl(platform.avatar_url); + if (normalizedAvatar) { + return normalizedAvatar; } if (platform.connected) { return `https://api.dicebear.com/9.x/initials/svg?seed=${encodeURIComponent(titleFor(platform))}`; @@ -73,13 +86,6 @@ function avatarFor(platform: StoredPlatformState): string | null { return null; } -function statusLabel(platform: StoredPlatformState): string { - if (refreshing.value || loading.value) { - return "检测中..."; - } - return platform.connected ? "已登录" : "未登录"; -} - function statusTone(platform: StoredPlatformState): "success" | "danger" | "pending" { if (refreshing.value || loading.value) { return "pending"; @@ -87,36 +93,18 @@ function statusTone(platform: StoredPlatformState): "success" | "danger" | "pend return platform.connected ? "success" : "danger"; } -function fillDemo(platform: StoredPlatformState): void { - platform.connected = true; - platform.nickname = platform.nickname?.trim() || `${platform.platform_name} Demo`; - platform.platform_uid = (platform.platform_uid != null ? String(platform.platform_uid).trim() : "") || `${platform.platform_id}-demo`; - platform.avatar_url = - platform.avatar_url?.trim() || - `https://api.dicebear.com/9.x/initials/svg?seed=${encodeURIComponent(platform.nickname || platform.platform_name)}`; - platform.message = null; -} - -function toggleExpanded(platformId: string): void { - expandedId.value = expandedId.value === platformId ? null : platformId; -} - -async function savePlatform(platform: StoredPlatformState): Promise { - savingId.value = platform.platform_id; - try { - const state = await updatePlatformState(platform.platform_id, { - connected: platform.connected, - nickname: platform.nickname?.trim() || null, - platform_uid: platform.platform_uid != null ? String(platform.platform_uid).trim() || null : null, - avatar_url: platform.avatar_url?.trim() || null, - message: platform.connected ? null : "Sign in required", - }); - installationId.value = state.plugin_installation_id; - apiBaseUrl.value = state.api_base_url; - platforms.value = clonePlatforms(state.platforms); - } finally { - savingId.value = null; +function loginButtonLabel(platform: StoredPlatformState): string { + if (refreshing.value || loading.value) { + return "检测中..."; } + return "未登录"; +} + +async function openLoginPage(platform: StoredPlatformState): Promise { + if (!platform.login_url || refreshing.value || loading.value) { + return; + } + await browser.tabs.create({ url: platform.login_url }); } async function simulateDetect(): Promise { @@ -126,10 +114,13 @@ async function simulateDetect(): Promise { async function refreshDetectedPlatforms(): Promise { refreshing.value = true; try { - await browser.runtime.sendMessage({ + const response = await browser.runtime.sendMessage({ type: "geo.publisher.request", action: "detectPlatforms", }); + if (isBackgroundResponse(response) && !response.ok) { + throw new Error(response.error || "publisher_plugin_unknown_error"); + } const state = await getExtensionState(); installationId.value = state.plugin_installation_id; apiBaseUrl.value = state.api_base_url; @@ -138,17 +129,6 @@ async function refreshDetectedPlatforms(): Promise { refreshing.value = false; } } - -async function handleReset(): Promise { - resetting.value = true; - try { - const state = await resetPlatformStates(); - platforms.value = clonePlatforms(state.platforms); - expandedId.value = null; - } finally { - resetting.value = false; - } -}