From 4c80d16e2635f2e51b0cba761c1f451e0e71a9cf Mon Sep 17 00:00:00 2001 From: zipg Date: Tue, 15 Sep 2026 22:13:13 +0800 Subject: [PATCH] feat(plugins): polish plugin marketplace listing display --- .../plugins/PluginContributionsPanel.vue | 44 ++++++++++++++----- .../src/lib/plugins/pluginMarketplace.spec.ts | 7 ++- .../src/lib/plugins/pluginMarketplace.ts | 25 +++++++++++ 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/apps/desktop/src/components/plugins/PluginContributionsPanel.vue b/apps/desktop/src/components/plugins/PluginContributionsPanel.vue index 8011b14909..6bf66666a1 100644 --- a/apps/desktop/src/components/plugins/PluginContributionsPanel.vue +++ b/apps/desktop/src/components/plugins/PluginContributionsPanel.vue @@ -8,6 +8,7 @@ import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { useToast } from "@/composables/useToast"; import PluginIcon from "@/components/plugins/PluginIcon.vue"; import * as api from "@/lib/backend/api"; @@ -15,13 +16,14 @@ import { clearPluginIconCache } from "@/lib/plugins/pluginIconResolver"; import { isTauriRuntime } from "@/lib/backend/tauriRuntime"; import { physicalDropPositionInsideRect } from "@/lib/ai/aiAttachments"; import { createFrontendPluginRegistry, pluginConnectionProviderIcon } from "@/lib/plugins/frontendPlugin"; -import { beaconPluginInstall, buildMarketplacePluginListings, filterMarketplacePluginListings, listingRepositoryCanVerify, type MarketplacePluginListing } from "@/lib/plugins/pluginMarketplace"; +import { beaconPluginInstall, buildMarketplacePluginListings, filterMarketplacePluginListings, listingRepositoryCanVerify, marketplaceHomepageUrl, type MarketplacePluginListing } from "@/lib/plugins/pluginMarketplace"; import { formatBytes } from "@/lib/database/serverMetrics"; import type { PluginCenterFocus } from "@/lib/plugins/pluginCenterNavigation"; import { useConnectionStore } from "@/stores/connectionStore"; import { useQueryStore } from "@/stores/queryStore"; import type { InstalledPlugin, PluginInstallResult, PluginRepository, PluginRepositoryCatalogResult, PluginTrustedKey } from "@/types/database"; import { useI18n } from "vue-i18n"; +import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage"; const props = defineProps<{ focusTarget?: PluginCenterFocus | null; @@ -47,6 +49,7 @@ const GithubIcon = defineComponent({ }); const PLUGIN_ALLOW_UNSIGNED_STORAGE_KEY = "dbx-plugin-allow-unsigned"; +const MARKETPLACE_VIEW_MODE_STORAGE_KEY = "dbx-plugin-marketplace-view-mode"; type TauriFileDropPayload = { type: "enter"; paths: string[]; position: { x: number; y: number } } | { type: "over"; position: { x: number; y: number } } | { type: "drop"; paths: string[]; position: { x: number; y: number } } | { type: "leave" }; @@ -87,7 +90,7 @@ const repositoryName = ref(""); const repositoryCatalogUrl = ref(""); const marketplaceQuery = ref(""); const marketplaceRepositoryId = ref("all"); -const marketplaceViewMode = ref<"grid" | "list">("grid"); +const marketplaceViewMode = ref<"grid" | "list">(safeLocalStorageGet(MARKETPLACE_VIEW_MODE_STORAGE_KEY) === "list" ? "list" : "grid"); const webFileInput = ref(null); const panelRootRef = ref(null); const draggingPackage = ref(false); @@ -113,6 +116,12 @@ const customRepositories = computed(() => repositories.value.filter((repository) const showCustomRepositoryTrustSettings = computed(() => customRepositories.value.length > 0 || trustedKeys.value.length > 0); const pluginDevelopmentDocsUrl = computed(() => `https://dbxio.com/${appLocale.value.startsWith("zh") ? "cn" : "en"}/docs/plugin-development`); +function marketplaceActionClass(listing: MarketplacePluginListing): string { + if (listing.status === "update") return "text-blue-600 hover:bg-gray-200 dark:text-blue-400 dark:hover:bg-gray-700"; + if (listing.status === "install") return "text-foreground hover:bg-gray-200 dark:hover:bg-gray-700"; + return "cursor-default text-gray-600 dark:text-gray-400"; +} + function openExternal(url?: string) { const target = url?.trim(); if (!target) return; @@ -578,6 +587,7 @@ onMounted(() => { void refresh(); if (isTauriRuntime()) document.addEventListener("dbx:tauri-file-drop", onTauriPluginDrop); }); +watch(marketplaceViewMode, (mode) => safeLocalStorageSet(MARKETPLACE_VIEW_MODE_STORAGE_KEY, mode)); onBeforeUnmount(() => { if (isTauriRuntime()) document.removeEventListener("dbx:tauri-file-drop", onTauriPluginDrop); }); @@ -681,12 +691,12 @@ onBeforeUnmount(() => { @@ -697,7 +707,12 @@ onBeforeUnmount(() => { {{ tag }} {{ t("pluginPlatform.permissionsCount", { count: listing.plugin.permissions.length }) }} -

{{ listing.description || t("pluginPlatform.noDescription") }}

+ + +

{{ listing.description || t("pluginPlatform.noDescription") }}

+
+ {{ listing.description || t("pluginPlatform.noDescription") }} +
{{ t("pluginPlatform.unsupportedTarget", { target: listing.target }) }} @@ -707,7 +722,7 @@ onBeforeUnmount(() => { @@ -749,7 +764,12 @@ onBeforeUnmount(() => { {{ listing.plugin.publisher }} ยท {{ listing.repository.name }}
-

{{ listing.description || t("pluginPlatform.noDescription") }}

+ + +

{{ listing.description || t("pluginPlatform.noDescription") }}

+
+ {{ listing.description || t("pluginPlatform.noDescription") }} +