fix(plugins): adapt marketplace cards to narrow and short windows

This commit is contained in:
Lan-zk
2026-09-28 13:50:22 +08:00
committed by GitHub
parent 50834a0eb9
commit 52eaf32233
10 changed files with 218 additions and 42 deletions
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { computed, defineComponent, h, onBeforeUnmount, onMounted, ref, watch, TransitionGroup } from "vue";
import { ArrowUp, BadgeCheck, Check, ChevronRight, CircleAlert, Download, ExternalLink, FileUp, FolderTree, Globe, Info, LayoutGrid, Link2, List, Loader2, PackageCheck, Pencil, Pin, PinOff, Plus, RefreshCw, RotateCcw, Search, Settings2, ShieldCheck, Store, Trash2 } from "@lucide/vue";
import { Badge } from "@/components/ui/badge";
import { isSensitivePluginPermission } from "@/lib/plugins/pluginPermissions";
@@ -213,6 +213,26 @@ function marketplaceActionClass(listing: MarketplacePluginListing): string {
return "cursor-default text-gray-600 dark:text-gray-400";
}
// 紧凑档(窄卡)下 "+N" 折叠标签的展开状态;只影响展示,重开面板自然复位。
const expandedTagKeys = ref(new Set<string>());
function toggleTagExpansion(key: string) {
const next = new Set(expandedTagKeys.value);
if (next.has(key)) next.delete(key);
else next.add(key);
expandedTagKeys.value = next;
}
// 安装/更新完成后的短促确认窗口:动作按钮上的对勾弹入几百毫秒即退场。
const recentlyCompletedKey = ref("");
let recentlyCompletedTimer = 0;
function markRecentlyCompleted(key: string) {
recentlyCompletedKey.value = key;
window.clearTimeout(recentlyCompletedTimer);
recentlyCompletedTimer = window.setTimeout(() => {
if (recentlyCompletedKey.value === key) recentlyCompletedKey.value = "";
}, 900);
}
function openExternal(url?: string) {
const target = url?.trim();
if (!target) return;
@@ -310,6 +330,7 @@ async function installMarketplaceListing(listing: MarketplacePluginListing, opti
marketplaceInstallingKey.value = listing.key;
try {
const result = await installListing(listing, options.allowSourceChange === true);
markRecentlyCompleted(listing.key);
toast(t(listing.status === "update" ? "pluginPlatform.updateSuccess" : "pluginPlatform.installSuccess", { name: result.plugin.manifest.name, version: result.plugin.manifest.version }));
// The COMPONENT_PLUGINS_UPDATED_EVENT handler does the panel-side refresh (icon cache +
// installed list); notifyComponentUpdatesChanged drives the App-level update-center badge.
@@ -920,6 +941,7 @@ onMounted(() => {
watch(marketplaceViewMode, (mode) => safeLocalStorageSet(MARKETPLACE_VIEW_MODE_STORAGE_KEY, mode));
watch(marketplaceSortMode, (mode) => safeLocalStorageSet(MARKETPLACE_SORT_MODE_STORAGE_KEY, mode));
onBeforeUnmount(() => {
window.clearTimeout(recentlyCompletedTimer);
window.removeEventListener(COMPONENT_PLUGINS_UPDATED_EVENT, handleComponentPluginsUpdated);
if (isTauriRuntime()) document.removeEventListener("dbx:tauri-file-drop", onTauriPluginDrop);
});
@@ -940,16 +962,17 @@ onBeforeUnmount(() => {
<TabsTrigger value="settings" class="gap-1.5 text-xs"><Settings2 class="size-3.5" />{{ t("pluginPlatform.settings") }}</TabsTrigger>
</TabsList>
<TabsContent value="marketplace" class="m-0 min-h-0 flex-1 overflow-y-auto">
<TabsContent value="marketplace" class="@container m-0 min-h-0 flex-1 overflow-y-auto">
<div class="flex min-h-full w-full flex-col gap-4 pb-2">
<div class="flex items-start gap-2.5 rounded-xl border bg-card/70 px-4 py-3">
<Info class="mt-0.5 size-3.5 shrink-0 text-primary" />
<div class="min-w-0 flex-1 text-xs leading-5 text-muted-foreground">
<div class="min-w-0 flex-1 marketplace-guide-text text-xs leading-5 text-muted-foreground">
<span class="font-medium text-foreground">{{ t("pluginPlatform.marketplaceGuideTitle") }}</span>
<span class="mx-1.5 text-border">·</span>{{ t("pluginPlatform.marketplaceGuideDescription") }}
</div>
</div>
<div class="flex w-full flex-col gap-2 rounded-xl border bg-card/70 p-3 sm:flex-row sm:items-center">
<!-- 吸顶:矮窗滚动时搜索/排序控件始终可达 -->
<div class="sticky top-0 z-20 flex w-full flex-col gap-2 rounded-xl border bg-background/95 p-3 backdrop-blur-sm sm:flex-row sm:items-center">
<div class="relative">
<Search class="pointer-events-none absolute left-2.5 top-2.5 size-3.5 text-muted-foreground" />
<Input data-plugin-marketplace-search v-model="marketplaceQuery" class="h-8 min-w-0 pl-8 text-xs sm:w-[min(100%,28rem)]" :placeholder="t('pluginPlatform.searchMarketplace')" />
@@ -1020,8 +1043,9 @@ onBeforeUnmount(() => {
<div class="mt-3 text-sm font-medium">{{ t("pluginPlatform.noMarketplacePlugins") }}</div>
<div class="mt-1 text-xs text-muted-foreground">{{ t("pluginPlatform.noMarketplacePluginsDescription") }}</div>
</div>
<div v-else-if="marketplaceViewMode === 'grid'" class="grid w-full grid-cols-1 gap-3 md:grid-cols-3">
<article v-for="listing in sortedMarketplaceListings" :key="listing.key" class="group flex min-w-0 min-h-48 flex-col rounded-xl border bg-card p-4 transition-colors hover:border-primary/40">
<!-- 列数交给面板实际宽度(容器内 auto-fill),不再跟随窗口断点;TransitionGroup 提供过滤/排序的 FLIP 连续性 -->
<TransitionGroup v-else-if="marketplaceViewMode === 'grid'" name="marketplace-cards" tag="div" class="grid w-full grid-cols-[repeat(auto-fill,minmax(min(100%,19rem),1fr))] gap-3">
<article v-for="listing in sortedMarketplaceListings" :key="listing.key" class="group @container marketplace-card flex min-w-0 min-h-48 flex-col rounded-xl border bg-card p-4 transition-colors hover:border-primary/40">
<div class="flex flex-wrap items-start gap-3">
<button
v-if="batchMode && isBatchSelectableListing(listing.status)"
@@ -1044,8 +1068,14 @@ onBeforeUnmount(() => {
<BadgeCheck v-if="listingRepositoryCanVerify(listing.repository)" class="size-3.5 shrink-0 text-emerald-600 dark:text-emerald-400" :title="t('pluginPlatform.verified')" :aria-label="t('pluginPlatform.verified')" />
<span class="truncate">{{ listing.plugin.publisher }} · {{ listing.repository.name }}</span>
</div>
<!-- 发布信息行(紧凑档专属):日期+版本降为身份块的第三行纯文本,与标签行的
内容/风险元数据分类;纯文本而非徽标,形态本身完成信息类别区分 -->
<div class="mt-0.5 hidden truncate text-[10px] leading-3.5 text-muted-foreground @max-[21rem]:block">
v{{ listing.plugin.latestVersion }}<span v-if="listing.latestVersionReleasedAt"> · {{ formatMarketplaceReleasedDate(listing.latestVersionReleasedAt, appLocale) }}</span>
</div>
<div class="flex shrink-0 items-center gap-1">
</div>
<div class="flex shrink-0 items-center gap-1.5 @max-[21rem]:gap-0.5">
<span class="inline-flex items-center gap-1">
<button
v-if="listing.plugin.source"
type="button"
@@ -1066,17 +1096,43 @@ onBeforeUnmount(() => {
>
<Globe class="size-3.5" />
</button>
<span v-if="listing.latestVersionReleasedAt" class="shrink-0 text-[10px] text-muted-foreground">{{ formatMarketplaceReleasedDate(listing.latestVersionReleasedAt, appLocale) }}</span>
</span>
<!-- 头部元数据芯片(日期+版本):标准档显示在这里;紧凑档(卡宽 <21rem)隐藏,
由标题块下方的发布信息行接管 —— 纯 CSS 无法跨容器移动元素,双份渲染按档位二选一 -->
<span v-if="listing.latestVersionReleasedAt" class="hidden shrink-0 items-center gap-1 @min-[21rem]:flex">
<span class="shrink-0 text-[10px] text-muted-foreground">{{ formatMarketplaceReleasedDate(listing.latestVersionReleasedAt, appLocale) }}</span>
<Badge variant="outline" class="h-5 shrink-0 px-1.5 text-[10px]">v{{ listing.plugin.latestVersion }}</Badge>
</span>
<span v-else class="hidden shrink-0 items-center @min-[21rem]:flex">
<Badge variant="outline" class="h-5 shrink-0 px-1.5 text-[10px]">v{{ listing.plugin.latestVersion }}</Badge>
</span>
</div>
</div>
<div class="mt-3 flex min-w-0 flex-wrap gap-1.5">
<Tooltip v-for="tag in listing.plugin.tags.slice(0, 3)" :key="tag" :delay-duration="500">
<div class="marketplace-tags mt-3 flex min-w-0 flex-wrap items-center gap-1.5 @max-[21rem]:mt-2.5">
<!-- 紧凑档(卡宽 <21rem)只露第 1 个标签 + "+N" 折叠;标准档由下方 t-extra 全显 -->
<Tooltip v-if="listing.plugin.tags.length" :delay-duration="500">
<TooltipTrigger as-child>
<Badge variant="outline" class="h-5 min-w-0 max-w-full truncate px-1.5 text-[10px]" :title="listing.plugin.tags[0]">{{ listing.plugin.tags[0] }}</Badge>
</TooltipTrigger>
<TooltipContent side="bottom" class="max-w-md break-all text-[11px]">{{ listing.plugin.tags[0] }}</TooltipContent>
</Tooltip>
<span v-if="listing.plugin.tags.length > 1" class="hidden flex-wrap gap-1.5 @min-[21rem]:flex">
<Tooltip v-for="tag in listing.plugin.tags.slice(1, 3)" :key="tag" :delay-duration="500">
<TooltipTrigger as-child>
<Badge variant="outline" class="h-5 min-w-0 max-w-full truncate px-1.5 text-[10px]" :title="tag">{{ tag }}</Badge>
</TooltipTrigger>
<TooltipContent side="bottom" class="max-w-md break-all text-[11px]">{{ tag }}</TooltipContent>
</Tooltip>
</span>
<button
v-if="listing.plugin.tags.length > 1"
type="button"
class="hidden h-5 items-center rounded-full border border-dashed px-1.5 text-[10px] text-muted-foreground transition-colors hover:border-muted-foreground/60 hover:text-foreground @max-[21rem]:inline-flex"
:aria-expanded="expandedTagKeys.has(listing.key)"
@click.stop="toggleTagExpansion(listing.key)"
>
{{ expandedTagKeys.has(listing.key) ? t("pluginPlatform.showFewerTags") : `+${Math.min(listing.plugin.tags.length - 1, 2)}` }}
</button>
<!-- Keep each permission visible as its own wrapping badge. Sensitive permissions
use the destructive variant so the risk surface remains obvious. -->
<Tooltip v-for="permission in listing.plugin.permissions" :key="permission" :delay-duration="300">
@@ -1088,14 +1144,28 @@ onBeforeUnmount(() => {
<TooltipContent side="bottom" class="max-w-md break-all font-mono text-[11px]">{{ permission }}</TooltipContent>
</Tooltip>
</div>
<!-- "+N" 展开区:块级兄弟节点而非标签行内联项 —— 收起时 0fr 高度、零占位;
间距放在被裁剪的子元素内(pt-1.5),收起时随高度一起归零,不留幻影间隙 -->
<div v-if="listing.plugin.tags.length > 1" class="marketplace-tags-more" :data-open="expandedTagKeys.has(listing.key) ? 'true' : 'false'">
<div class="overflow-hidden">
<div class="flex flex-wrap gap-1.5 pt-1.5">
<Tooltip v-for="tag in listing.plugin.tags.slice(1, 3)" :key="`more-${tag}`" :delay-duration="500">
<TooltipTrigger as-child>
<Badge variant="outline" class="h-5 min-w-0 max-w-full truncate px-1.5 text-[10px]" :title="tag">{{ tag }}</Badge>
</TooltipTrigger>
<TooltipContent side="bottom" class="max-w-md break-all text-[11px]">{{ tag }}</TooltipContent>
</Tooltip>
</div>
</div>
</div>
<Tooltip :delay-duration="700">
<TooltipTrigger as-child>
<p class="mt-3 line-clamp-3 cursor-help text-xs leading-5 text-muted-foreground">{{ listing.description || t("pluginPlatform.noDescription") }}</p>
<p class="marketplace-desc mt-3 line-clamp-3 cursor-help text-xs leading-5 text-muted-foreground">{{ listing.description || t("pluginPlatform.noDescription") }}</p>
</TooltipTrigger>
<TooltipContent side="bottom" class="max-w-md whitespace-pre-wrap break-words">{{ listing.description || t("pluginPlatform.noDescription") }}</TooltipContent>
</Tooltip>
<div class="mt-auto flex items-center justify-between gap-3 pt-4">
<div class="text-[11px] text-muted-foreground">
<div class="marketplace-footer mt-auto flex items-center justify-between gap-3 pt-4">
<div class="min-w-0 flex-1 truncate text-[11px] text-muted-foreground">
<span v-if="listing.status === 'unsupported'">{{ t("pluginPlatform.unsupportedTarget", { target: listing.target }) }}</span>
<!-- In the update state the left line states both versions: the badge above shows the
catalog latest version, which otherwise reads as the installed one. -->
@@ -1111,12 +1181,13 @@ onBeforeUnmount(() => {
@click="installMarketplaceListing(listing)"
>
<Loader2 v-if="marketplaceInstallingKey === listing.key" class="size-3.5 animate-spin" />
<Check v-else-if="listing.status === 'installed' && recentlyCompletedKey === listing.key" class="marketplace-check-pop size-3.5" />
{{ t(`pluginPlatform.marketplaceStatus.${listing.status}`) }}
</button>
</div>
</article>
</div>
<div v-else class="flex w-full flex-col gap-2">
</TransitionGroup>
<TransitionGroup v-else name="marketplace-cards" tag="div" class="flex w-full flex-col gap-2">
<article v-for="listing in sortedMarketplaceListings" :key="listing.key" class="flex items-center gap-3 rounded-xl border bg-card p-3 transition-colors hover:border-primary/40">
<button
v-if="batchMode && isBatchSelectableListing(listing.status)"
@@ -1154,7 +1225,7 @@ onBeforeUnmount(() => {
>
<Globe class="size-3.5" />
</button>
<span v-if="listing.latestVersionReleasedAt" class="shrink-0 text-[10px] text-muted-foreground">{{ formatMarketplaceReleasedDate(listing.latestVersionReleasedAt, appLocale) }}</span>
<span v-if="listing.latestVersionReleasedAt" class="shrink-0 text-[10px] text-muted-foreground @max-[38.75rem]:hidden">{{ formatMarketplaceReleasedDate(listing.latestVersionReleasedAt, appLocale) }}</span>
<Badge variant="outline" class="h-5 shrink-0 px-1.5 text-[10px]">v{{ listing.plugin.latestVersion }}</Badge>
</div>
<div class="mt-0.5 flex min-w-0 items-center gap-1 text-xs text-muted-foreground">
@@ -1168,7 +1239,7 @@ onBeforeUnmount(() => {
<TooltipContent side="bottom" class="max-w-md whitespace-pre-wrap break-words">{{ listing.description || t("pluginPlatform.noDescription") }}</TooltipContent>
</Tooltip>
</div>
<div class="hidden min-w-0 max-w-52 shrink-0 flex-wrap gap-1.5 lg:flex">
<div class="hidden min-w-0 max-w-52 shrink-0 flex-wrap gap-1.5 @min-[64rem]:flex">
<Tooltip v-for="tag in listing.plugin.tags.slice(0, 3)" :key="tag" :delay-duration="500">
<TooltipTrigger as-child>
<Badge variant="outline" class="h-5 min-w-0 max-w-full truncate px-1.5 text-[10px]" :title="tag">{{ tag }}</Badge>
@@ -1184,10 +1255,11 @@ onBeforeUnmount(() => {
@click="installMarketplaceListing(listing)"
>
<Loader2 v-if="marketplaceInstallingKey === listing.key" class="size-3.5 animate-spin" />
<span class="hidden sm:inline">{{ t(`pluginPlatform.marketplaceStatus.${listing.status}`) }}</span>
<Check v-else-if="listing.status === 'installed' && recentlyCompletedKey === listing.key" class="marketplace-check-pop size-3.5" />
<span class="hidden @min-[40rem]:inline">{{ t(`pluginPlatform.marketplaceStatus.${listing.status}`) }}</span>
</button>
</article>
</div>
</TransitionGroup>
</div>
</TabsContent>
@@ -1608,3 +1680,98 @@ onBeforeUnmount(() => {
</div>
</div>
</template>
<style scoped>
/*
* 商店卡片的响应式补充样式。布局分级主体走 Tailwind 容器变体(卡片自身 @container,
* 阈值 21rem),这里只放三件事:TransitionGroup 的 FLIP 过渡、"+N" 展开区、矮窗降密度。
* 所有动效集中在 prefers-reduced-motion: no-preference 下,减弱动效的用户自动退化为静态。
*/
@media (prefers-reduced-motion: no-preference) {
.marketplace-cards-move {
/* 与卡片自身 transition-colors 同为 transition 简写,须 !important 保证 FLIP 位移生效 */
transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1) !important;
}
.marketplace-cards-enter-active {
transition: opacity 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
.marketplace-cards-leave-active {
transition: opacity 0.12s ease-out;
}
.marketplace-tags-more {
transition: grid-template-rows 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.marketplace-check-pop {
animation: marketplace-check-pop 0.12s cubic-bezier(0.16, 1, 0.3, 1);
}
}
.marketplace-cards-enter-from,
.marketplace-cards-leave-to {
opacity: 0;
}
/* "+N" 展开区:块级兄弟节点而非标签行内联项(内联时宽度仍按内容计算,收起会占位)。
0fr 收起 + overflow hidden 清零子项内容贡献;标准档不参与渲染,避免与全显标签重复。 */
.marketplace-tags-more {
display: none;
grid-template-rows: 0fr;
}
.marketplace-tags-more[data-open="true"] {
grid-template-rows: 1fr;
}
@container (width < 21rem) {
.marketplace-tags-more {
display: grid;
}
}
@keyframes marketplace-check-pop {
from {
transform: scale(0.4);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* 矮窗(窗口高 ≤700px)降密度:描述收 2 行、内边距收紧,更多内容交给滚动区 */
@media (max-height: 700px) {
.marketplace-guide-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.marketplace-card {
min-height: 10rem;
padding: 0.75rem;
}
.marketplace-tags {
margin-top: 0.5rem;
}
.marketplace-desc {
margin-top: 0.5rem;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
}
.marketplace-footer {
padding-top: 0.625rem;
}
}
</style>
+1
View File
@@ -223,6 +223,7 @@ export default {
sortByUpdatesFirst: "Updates first",
allRepositories: "All repositories",
marketplaceGuideTitle: "Plugins are maintained by community developers",
showFewerTags: "Show less",
marketplaceGuideDescription: "For plugin issues or feature requests, please open an issue in the plugin's own repository (use the source repository link on the plugin card); the main DBX repository only handles issues with the app itself.",
loadingMarketplace: "Loading plugin marketplace",
noMarketplacePlugins: "No plugins to show",
+1
View File
@@ -226,6 +226,7 @@ export default withEnglishFallback({
sortByUpdatesFirst: "Actualizaciones primero",
allRepositories: "Todos los repositorios",
marketplaceGuideTitle: "Los plugins los mantienen desarrolladores de la comunidad",
showFewerTags: "Menos",
marketplaceGuideDescription: "Para problemas o sugerencias sobre un plugin, abre una issue en el repositorio del propio plugin (usa el enlace del repositorio de origen en la tarjeta); el repositorio principal de DBX solo atiende problemas de la propia aplicación.",
loadingMarketplace: "Cargando marketplace de plugins",
noMarketplacePlugins: "No hay plugins para mostrar",
+1
View File
@@ -226,6 +226,7 @@ export default withEnglishFallback({
sortByUpdatesFirst: "Prima gli aggiornamenti",
allRepositories: "Tutti i repository",
marketplaceGuideTitle: "I plugin sono mantenuti dagli sviluppatori della community",
showFewerTags: "Meno",
marketplaceGuideDescription: "Per problemi o suggerimenti su un plugin, apri una issue nel repository del plugin stesso (usa il link del repository sorgente sulla scheda); il repository principale di DBX gestisce solo i problemi dell'applicazione.",
loadingMarketplace: "Caricamento marketplace plugin",
noMarketplacePlugins: "Nessun plugin da mostrare",
+1
View File
@@ -226,6 +226,7 @@ export default withEnglishFallback({
sortByUpdatesFirst: "更新ありを優先",
allRepositories: "すべてのリポジトリ",
marketplaceGuideTitle: "プラグインはコミュニティ開発者がメンテナンスしています",
showFewerTags: "折りたたむ",
marketplaceGuideDescription: "プラグインの問題や機能要望は、プラグインカードのソースリポジトリリンクから該当リポジトリに Issue を投稿してください。DBX メインリポジトリはアプリ本体の問題のみを受け付けています。",
loadingMarketplace: "プラグインマーケットプレイスを読み込み中",
noMarketplacePlugins: "表示できるプラグインがありません",
+1
View File
@@ -225,6 +225,7 @@ export default withEnglishFallback({
sortByUpdatesFirst: "업데이트 우선",
allRepositories: "모든 저장소",
marketplaceGuideTitle: "플러그인은 커뮤니티 개발자가 유지 관리합니다",
showFewerTags: "접기",
marketplaceGuideDescription: "플러그인 문제나 기능 제안은 플러그인 카드의 소스 저장소 링크를 통해 해당 저장소에 Issue를 등록해 주세요. DBX 메인 저장소는 앱 자체의 문제만 처리합니다.",
loadingMarketplace: "플러그인 마켓플레이스 로드 중",
noMarketplacePlugins: "표시할 플러그인이 없습니다",
+1
View File
@@ -226,6 +226,7 @@ export default withEnglishFallback({
sortByUpdatesFirst: "Atualizações primeiro",
allRepositories: "Todos os repositórios",
marketplaceGuideTitle: "Os plugins são mantidos por desenvolvedores da comunidade",
showFewerTags: "Mostrar menos",
marketplaceGuideDescription: "Para problemas ou sugestões sobre um plugin, abra uma issue no repositório do próprio plugin (use o link do repositório de origem no cartão); o repositório principal do DBX trata apenas de problemas do próprio aplicativo.",
loadingMarketplace: "Carregando marketplace de plugins",
noMarketplacePlugins: "Nenhum plugin para exibir",
+1
View File
@@ -173,6 +173,7 @@ export default withEnglishFallback({
sortByUpdatesFirst: "Сначала с обновлениями",
allRepositories: "Все репозитории",
marketplaceGuideTitle: "Плагины поддерживаются сообществом разработчиков",
showFewerTags: "Свернуть",
marketplaceGuideDescription: "По вопросам о плагинах или для запроса функций создайте issue в собственном репозитории плагина (ссылка на исходный репозиторий есть на карточке плагина); основной репозиторий DBX принимает только вопросы о самом приложении.",
loadingMarketplace: "Загрузка магазина плагинов",
noMarketplacePlugins: "Нет плагинов для отображения",
+1
View File
@@ -148,6 +148,7 @@ export default withEnglishFallback({
sortByUpdatesFirst: "有更新优先",
allRepositories: "全部仓库",
marketplaceGuideTitle: "插件由社区开发者维护",
showFewerTags: "收起",
marketplaceGuideDescription: "插件问题或功能建议请通过插件卡片上的源码仓库入口,前往对应插件仓库提交 Issue;DBX 主仓库仅受理应用本身的问题。",
loadingMarketplace: "正在加载插件商店",
noMarketplacePlugins: "暂无可展示的插件",
+1
View File
@@ -226,6 +226,7 @@ export default withEnglishFallback({
sortByUpdatesFirst: "有更新優先",
allRepositories: "全部倉庫",
marketplaceGuideTitle: "外掛由社群開發者維護",
showFewerTags: "收合",
marketplaceGuideDescription: "外掛問題或功能建議請透過外掛卡片上的原始碼倉庫入口,前往對應外掛倉庫提交 Issue;DBX 主倉庫僅受理應用程式本身的問題。",
loadingMarketplace: "正在載入外掛商店",
noMarketplacePlugins: "沒有可顯示的外掛",