mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
✨ Feature(custom): add plugin browser dialog
This commit is contained in:
@@ -2,15 +2,16 @@
|
||||
|
||||
### ✨ 新增功能
|
||||
|
||||
- windows下新增便携模式,无需安装即可运行,数据存储在程序目录下的`data`文件夹中,支持自动更新
|
||||
- windows下新增便携模式,无需安装即可运行,数据存储在程序目录下的`data`文件夹中,支持自动更新;Linux下新增`rpm`安装包
|
||||
- 新增自定义主题功能,主题仓库[PicList ThemeHub](https://github.com/Kuingsmile/PicList-ThemeHub)
|
||||
- 12个内置主题供选择,如bilibili、二次元、极夜紫、gemini等风格
|
||||
- 相册页面多项优化,支持显示已选择图片数量,匹配的url列表和记忆过滤器打开状态
|
||||
- 插件页面现在可以浏览所有插件列表,查看详情和安装
|
||||
- 新增教学引导页面,首次运行时会自动弹出
|
||||
- 现在支持关闭GPU加速,解决部分兼容性问题
|
||||
- 新增高级动画设置,开启后可获得更好的UI体验
|
||||
- Linux下新增rpm安装包
|
||||
- 优化了相册页面的加载速度
|
||||
|
||||
- 优化了多个页面的加载速度和浏览性能
|
||||
|
||||
### 🐛 问题修复
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
- Added portable mode on Windows, allowing the program to run without installation. Data is stored in the `data` folder within the program directory, and automatic updates are supported.
|
||||
- Added portable mode on Windows, allowing the program to run without installation. Data is stored in the `data` folder within the program directory, and automatic updates are supported. Added `rpm` installation package for Linux
|
||||
- Added custom theme functionality, with a theme repository available at [PicList ThemeHub](https://github.com/Kuingsmile/PicList-ThemeHub)
|
||||
- 12 built-in themes available, such as bilibili, ACG, Night Purple, gemini styles
|
||||
- Multiple optimizations on the album page, supporting display of the number of selected images, matching URL list, and remembering filter open state
|
||||
- Plugin page now allows browsing of all plugin lists, viewing details, and installation
|
||||
- Added tutorial guide page, which automatically pops up on first run
|
||||
- Now supports disabling GPU acceleration to resolve some compatibility issues
|
||||
- Added advanced animation settings for a better UI experience
|
||||
- Added rpm installation package for Linux
|
||||
- Optimized the loading speed of the album page
|
||||
- Optimized the loading speed of multiple pages and browsing performance
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"app": { "title": "PicList" },
|
||||
"app": {
|
||||
"title": "PicList"
|
||||
},
|
||||
"common": {
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
@@ -664,6 +666,7 @@
|
||||
"viewDocFailed": "View Document Failed"
|
||||
},
|
||||
"plugin": {
|
||||
"browseAllPlugins": "Browse All Plugins",
|
||||
"browsePlugins": "Browse Plugins",
|
||||
"configThing": "Config {c}",
|
||||
"description": "PicList Plugin Management",
|
||||
@@ -676,12 +679,15 @@
|
||||
"installPluginsToGetStarted": "Please install plugins to get started",
|
||||
"list": "Plugin List",
|
||||
"loading": "Loading...",
|
||||
"loadingPlugins": "Loading plugins...",
|
||||
"needRestart": "Need Restart to Take Effect",
|
||||
"noPluginsFound": "No Plugins Found",
|
||||
"NoPluginsInstalled": "No Plugins Installed",
|
||||
"notGuiImplement": "This plugin does not have a GUI implementation, continue?",
|
||||
"openRemoteList": "Open Remote Plugin List",
|
||||
"pluginList": "Plugin List",
|
||||
"restartApp": "Restart Application",
|
||||
"searchInBrowse": "Search in Browse",
|
||||
"searchPlaceholder": "Search for PicGo plugins on npm, or click the button above to view the excellent plugin list",
|
||||
"setResult": "Set Result",
|
||||
"setSuccess": "Set Success",
|
||||
@@ -800,7 +806,10 @@
|
||||
"syncConfigProxy": "Proxy",
|
||||
"syncConfiguration": "Sync Platform",
|
||||
"syncEndpointConfig": "Endpoint Configuration",
|
||||
"syncResult": { "failed": "Sync Failed", "success": "Sync Successful" },
|
||||
"syncResult": {
|
||||
"failed": "Sync Failed",
|
||||
"success": "Sync Successful"
|
||||
},
|
||||
"title": "Sync",
|
||||
"upDownloadDesc": "Sync configuration files and gallery data to cloud",
|
||||
"upDownloadSettings": "Sync Config & Gallery",
|
||||
@@ -1098,7 +1107,11 @@
|
||||
"uploadHint": "All formats are supported, but images are recommended",
|
||||
"uploadingMultipleUrls": "Uploading {count} URLs...",
|
||||
"uploadViewHint": "Click to open picbeds settings",
|
||||
"urlType": { "normal": "Long Link", "short": "Short Link", "title": "Link Type" },
|
||||
"urlType": {
|
||||
"normal": "Long Link",
|
||||
"short": "Short Link",
|
||||
"title": "Link Type"
|
||||
},
|
||||
"urlUpload": "URL Upload"
|
||||
},
|
||||
"uploaderConfig": {
|
||||
@@ -1135,5 +1148,10 @@
|
||||
"toggle": "Toggle Theme"
|
||||
}
|
||||
},
|
||||
"titleBar": { "alwaysOnTop": "Always On Top", "close": "Close", "minimize": "Minimize", "miniWindow": "Mini Window" }
|
||||
"titleBar": {
|
||||
"alwaysOnTop": "Always On Top",
|
||||
"close": "Close",
|
||||
"minimize": "Minimize",
|
||||
"miniWindow": "Mini Window"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"app": { "title": "PicList" },
|
||||
"app": {
|
||||
"title": "PicList"
|
||||
},
|
||||
"common": {
|
||||
"cancel": "取消",
|
||||
"close": "关闭",
|
||||
@@ -664,6 +666,7 @@
|
||||
"viewDocFailed": "查看文档失败"
|
||||
},
|
||||
"plugin": {
|
||||
"browseAllPlugins": "浏览所有插件",
|
||||
"browsePlugins": "浏览插件",
|
||||
"configThing": "配置 {c}",
|
||||
"description": "PicList 插件管理页面",
|
||||
@@ -676,12 +679,15 @@
|
||||
"installPluginsToGetStarted": "请先安装插件以开始使用",
|
||||
"list": "插件列表",
|
||||
"loading": "加载中...",
|
||||
"loadingPlugins": "正在加载插件...",
|
||||
"needRestart": "需要重启生效",
|
||||
"noPluginsFound": "未找到插件",
|
||||
"NoPluginsInstalled": "暂无已安装插件",
|
||||
"notGuiImplement": "该插件未对可视化界面进行优化, 是否继续安装?",
|
||||
"openRemoteList": "打开远程插件列表",
|
||||
"pluginList": "插件列表",
|
||||
"restartApp": "重启应用",
|
||||
"searchInBrowse": "搜索插件",
|
||||
"searchPlaceholder": "搜索 npm 上的 PicGo 插件,或者点击上方按钮查看优秀插件列表",
|
||||
"setResult": "设置结果",
|
||||
"setSuccess": "设置成功",
|
||||
@@ -763,7 +769,12 @@
|
||||
"downloadSettings": "下载配置",
|
||||
"fileManagement": "文件管理",
|
||||
"galleryDB": "相册数据库同步",
|
||||
"gitea": { "branch": "分支名", "repo": "仓库名", "token": "访问令牌", "username": "用户名" },
|
||||
"gitea": {
|
||||
"branch": "分支名",
|
||||
"repo": "仓库名",
|
||||
"token": "访问令牌",
|
||||
"username": "用户名"
|
||||
},
|
||||
"giteaHost": "Gitea 地址",
|
||||
"gitee": {
|
||||
"branch": "Gitee 分支名",
|
||||
@@ -795,7 +806,10 @@
|
||||
"syncConfigProxy": "代理",
|
||||
"syncConfiguration": "同步平台",
|
||||
"syncEndpointConfig": "平台设置",
|
||||
"syncResult": { "failed": "同步失败", "success": "同步成功" },
|
||||
"syncResult": {
|
||||
"failed": "同步失败",
|
||||
"success": "同步成功"
|
||||
},
|
||||
"title": "同步",
|
||||
"upDownloadDesc": "同步配置文件和相册数据到云端",
|
||||
"upDownloadSettings": "同步配置和相册",
|
||||
@@ -1093,7 +1107,11 @@
|
||||
"uploadHint": "支持所有文件类型,但推荐仅上传图片",
|
||||
"uploadingMultipleUrls": "正在上传 {count} 个URL...",
|
||||
"uploadViewHint": "点击打开图床设置",
|
||||
"urlType": { "normal": "长链接", "short": "短链接", "title": "链接类型" },
|
||||
"urlType": {
|
||||
"normal": "长链接",
|
||||
"short": "短链接",
|
||||
"title": "链接类型"
|
||||
},
|
||||
"urlUpload": "URL上传"
|
||||
},
|
||||
"uploaderConfig": {
|
||||
@@ -1130,5 +1148,10 @@
|
||||
"toggle": "切换主题"
|
||||
}
|
||||
},
|
||||
"titleBar": { "alwaysOnTop": "置顶", "close": "关闭", "minimize": "最小化", "miniWindow": "迷你窗口" }
|
||||
"titleBar": {
|
||||
"alwaysOnTop": "置顶",
|
||||
"close": "关闭",
|
||||
"minimize": "最小化",
|
||||
"miniWindow": "迷你窗口"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"app": { "title": "PicList" },
|
||||
"app": {
|
||||
"title": "PicList"
|
||||
},
|
||||
"common": {
|
||||
"cancel": "取消",
|
||||
"close": "關閉",
|
||||
@@ -664,6 +666,7 @@
|
||||
"viewDocFailed": "查看文件失敗"
|
||||
},
|
||||
"plugin": {
|
||||
"browseAllPlugins": "瀏覽所有插件",
|
||||
"browsePlugins": "瀏覽插件",
|
||||
"configThing": "配置 {c}",
|
||||
"description": "PicList 插件管理頁面",
|
||||
@@ -676,12 +679,15 @@
|
||||
"installPluginsToGetStarted": "請先安裝插件以開始使用",
|
||||
"list": "插件列表",
|
||||
"loading": "載入中...",
|
||||
"needRestart": "需要重啟生效",
|
||||
"loadingPlugins": "正在加載插件...",
|
||||
"needRestart": "需要重啟才能生效",
|
||||
"noPluginsFound": "未找到插件",
|
||||
"NoPluginsInstalled": "尚未安裝任何插件",
|
||||
"notGuiImplement": "該插件未針對圖形介面進行優化,是否繼續安裝?",
|
||||
"openRemoteList": "打開遠程插件列表",
|
||||
"pluginList": "插件列表",
|
||||
"restartApp": "重啟應用",
|
||||
"searchInBrowse": "在瀏覽中搜索",
|
||||
"searchPlaceholder": "搜尋 npm 上的 PicGo 插件,或者點擊上方按鈕查看優秀插件列表",
|
||||
"setResult": "設定結果",
|
||||
"setSuccess": "設定成功",
|
||||
@@ -763,7 +769,12 @@
|
||||
"downloadSettings": "下載配置",
|
||||
"fileManagement": "文件管理",
|
||||
"galleryDB": "相冊數據庫同步",
|
||||
"gitea": { "branch": "分支名", "repo": "倉庫名", "token": "訪問令牌", "username": "用戶名" },
|
||||
"gitea": {
|
||||
"branch": "分支名",
|
||||
"repo": "倉庫名",
|
||||
"token": "訪問令牌",
|
||||
"username": "用戶名"
|
||||
},
|
||||
"giteaHost": "Gitea 地址",
|
||||
"gitee": {
|
||||
"branch": "Gitee 分支名",
|
||||
@@ -795,7 +806,10 @@
|
||||
"syncConfigProxy": "代理",
|
||||
"syncConfiguration": "同步平台",
|
||||
"syncEndpointConfig": "平台配置",
|
||||
"syncResult": { "failed": "同步失敗", "success": "同步成功" },
|
||||
"syncResult": {
|
||||
"failed": "同步失敗",
|
||||
"success": "同步成功"
|
||||
},
|
||||
"title": "同步",
|
||||
"upDownloadDesc": "同步配置文件和相冊數據到雲端",
|
||||
"upDownloadSettings": "同步配置和相冊",
|
||||
@@ -1093,7 +1107,11 @@
|
||||
"uploadHint": "支持所有文件類型,但推薦僅上傳圖片",
|
||||
"uploadingMultipleUrls": "正在上傳 {count} 個URL...",
|
||||
"uploadViewHint": "點擊打開圖床設定",
|
||||
"urlType": { "normal": "長連結", "short": "短連結", "title": "連結類型" },
|
||||
"urlType": {
|
||||
"normal": "長連結",
|
||||
"short": "短連結",
|
||||
"title": "連結類型"
|
||||
},
|
||||
"urlUpload": "URL上傳"
|
||||
},
|
||||
"uploaderConfig": {
|
||||
@@ -1130,5 +1148,10 @@
|
||||
"toggle": "切換主題"
|
||||
}
|
||||
},
|
||||
"titleBar": { "alwaysOnTop": "置頂", "close": "關閉", "minimize": "最小化", "miniWindow": "迷你視窗" }
|
||||
"titleBar": {
|
||||
"alwaysOnTop": "置頂",
|
||||
"close": "關閉",
|
||||
"minimize": "最小化",
|
||||
"miniWindow": "迷你視窗"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="plugin-container">
|
||||
<!-- Header Card -->
|
||||
<div class="plugin-card header-card">
|
||||
<div class="header-card">
|
||||
<div class="card-header">
|
||||
<div class="header-content">
|
||||
<div class="header-icon">
|
||||
@@ -25,10 +25,22 @@
|
||||
<RefreshCwIcon :size="16" />
|
||||
{{ t('pages.plugin.updateAll') }}
|
||||
</button>
|
||||
<button class="action-button" :title="t('pages.plugin.pluginList')" @click="goAwesomeList">
|
||||
<ExternalLinkIcon :size="16" />
|
||||
{{ t('pages.plugin.list') }}
|
||||
</button>
|
||||
<div class="dropdown-wrapper">
|
||||
<button class="action-button" :title="t('pages.plugin.pluginList')" @click="togglePluginListMenu">
|
||||
<ExternalLinkIcon :size="16" />
|
||||
{{ t('pages.plugin.list') }}
|
||||
</button>
|
||||
<div v-if="showPluginListMenu" class="dropdown-menu">
|
||||
<button class="dropdown-item" @click="goAwesomeList">
|
||||
<ExternalLinkIcon :size="16" />
|
||||
{{ t('pages.plugin.openRemoteList') }}
|
||||
</button>
|
||||
<button class="dropdown-item" @click="openBrowsePluginsDialog">
|
||||
<SearchIcon :size="16" />
|
||||
{{ t('pages.plugin.browseAllPlugins') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -202,6 +214,91 @@
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- Browse All Plugins Modal -->
|
||||
<transition name="modal">
|
||||
<div v-if="showBrowseDialog" class="modal-overlay" :class="advancedAnimation">
|
||||
<div class="modal-container browse-modal" @click.stop>
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">
|
||||
{{ t('pages.plugin.browseAllPlugins') }}
|
||||
</h2>
|
||||
<button class="modal-close" @click="closeBrowseDialog">
|
||||
<XIcon :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-content browse-content">
|
||||
<div class="browse-search">
|
||||
<div class="search-input-wrapper">
|
||||
<SearchIcon class="search-icon" :size="20" />
|
||||
<input
|
||||
v-model="browseSearchText"
|
||||
type="text"
|
||||
class="search-input"
|
||||
:placeholder="t('pages.plugin.searchInBrowse')"
|
||||
/>
|
||||
<button v-if="browseSearchText" class="clear-button" @click="browseSearchText = ''">
|
||||
<XIcon :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="loadingBrowse" class="browse-loading">
|
||||
<div class="loading-spinner" />
|
||||
<span class="loading-text">{{ t('pages.plugin.loadingPlugins') }}</span>
|
||||
</div>
|
||||
<div v-else class="browse-plugin-grid">
|
||||
<div v-for="item in filteredBrowsePlugins" :key="item.fullName" class="browse-plugin-item">
|
||||
<!-- Plugin Badge -->
|
||||
|
||||
<div class="plugin-header">
|
||||
<img class="plugin-logo" :src="item.logo" :onerror="setSrc" alt="" />
|
||||
<div class="plugin-info">
|
||||
<h3 class="plugin-name" @click="openHomepage(item.homepage)">
|
||||
{{ item.name }}
|
||||
<span class="plugin-version">v{{ item.version }}</span>
|
||||
<div v-if="!item.gui" class="cli-badge-browser">CLI</div>
|
||||
</h3>
|
||||
<p class="plugin-author">
|
||||
{{ item.author }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plugin-description">
|
||||
<p :title="item.description">
|
||||
{{ item.description }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="plugin-actions">
|
||||
<template v-if="!item.hasInstall">
|
||||
<button
|
||||
v-if="!item.ing"
|
||||
class="plugin-button install-button"
|
||||
@click="installPluginFromBrowse(item)"
|
||||
>
|
||||
<DownloadIcon :size="16" />
|
||||
{{ t('pages.plugin.install') }}
|
||||
</button>
|
||||
<button v-else class="plugin-button installing-button" disabled>
|
||||
<div class="button-spinner" />
|
||||
{{ t('pages.plugin.installing') }}
|
||||
</button>
|
||||
</template>
|
||||
<button v-else class="plugin-button installed-button" disabled>
|
||||
<CheckIcon :size="16" />
|
||||
{{ t('pages.plugin.installed') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!loadingBrowse && filteredBrowsePlugins.length === 0" class="empty-content">
|
||||
<PackageIcon class="empty-icon" :size="48" />
|
||||
<h3>{{ t('pages.plugin.noPluginsFound') }}</h3>
|
||||
<p>{{ t('pages.plugin.tryDifferentSearch') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -252,6 +349,11 @@ const enableAdvancedAnimation = ref(false)
|
||||
const latestVersionMap = reactive<Record<string, string>>({})
|
||||
const $configForm = useTemplateRef('$configForm')
|
||||
const strictSearch = useStorage('plugin-strict-search', true)
|
||||
const showPluginListMenu = ref(false)
|
||||
const showBrowseDialog = ref(false)
|
||||
const browseSearchText = ref('')
|
||||
const browsePlugins = ref<IPicGoPlugin[]>([])
|
||||
const loadingBrowse = ref(false)
|
||||
|
||||
function setSrc(e: Event) {
|
||||
const target = e.target as HTMLImageElement
|
||||
@@ -274,6 +376,21 @@ const npmSearchText = computed(() => {
|
||||
: searchText.value
|
||||
})
|
||||
|
||||
const filteredBrowsePlugins = computed(() => {
|
||||
if (!browseSearchText.value) {
|
||||
return browsePlugins.value
|
||||
}
|
||||
const search = browseSearchText.value.toLowerCase()
|
||||
return browsePlugins.value.filter(plugin => {
|
||||
return (
|
||||
plugin.name.toLowerCase().includes(search) ||
|
||||
plugin.fullName.toLowerCase().includes(search) ||
|
||||
plugin.description?.toLowerCase().includes(search) ||
|
||||
plugin.author?.toLowerCase().includes(search)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
let getSearchResult: DebouncedFunc<(val: string) => void>
|
||||
|
||||
watch(npmSearchText, (val: string) => {
|
||||
@@ -293,6 +410,14 @@ watch(dialogVisible, (val: boolean) => {
|
||||
}
|
||||
})
|
||||
|
||||
watch(showBrowseDialog, (val: boolean) => {
|
||||
if (val) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
} else {
|
||||
document.body.style.overflow = 'auto'
|
||||
}
|
||||
})
|
||||
|
||||
async function getLatestVersionOfPlugIn(pluginName: string) {
|
||||
try {
|
||||
const res = await fetch(`https://registry.npmjs.com/${pluginName}`)
|
||||
@@ -333,6 +458,13 @@ const installPluginHandler = ({ success, body }: { success: boolean; body: strin
|
||||
item.hasInstall = success
|
||||
}
|
||||
})
|
||||
// Update browse dialog if open
|
||||
browsePlugins.value.forEach(item => {
|
||||
if (item.fullName === body) {
|
||||
item.ing = false
|
||||
item.hasInstall = success
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const updateSuccessHandler = (plugin: string) => {
|
||||
@@ -543,9 +675,60 @@ function openHomepage(url: string) {
|
||||
}
|
||||
|
||||
function goAwesomeList() {
|
||||
showPluginListMenu.value = false
|
||||
window.electron.sendRPC(IRPCActionType.OPEN_URL, 'https://github.com/PicGo/Awesome-PicGo')
|
||||
}
|
||||
|
||||
function togglePluginListMenu() {
|
||||
showPluginListMenu.value = !showPluginListMenu.value
|
||||
}
|
||||
|
||||
async function openBrowsePluginsDialog() {
|
||||
showPluginListMenu.value = false
|
||||
showBrowseDialog.value = true
|
||||
browseSearchText.value = ''
|
||||
await fetchAllPlugins()
|
||||
}
|
||||
|
||||
function closeBrowseDialog() {
|
||||
showBrowseDialog.value = false
|
||||
browseSearchText.value = ''
|
||||
}
|
||||
|
||||
async function fetchAllPlugins() {
|
||||
loadingBrowse.value = true
|
||||
try {
|
||||
const res = await fetch('https://registry.npmjs.com/-/v1/search?text=picgo-plugin-&size=250')
|
||||
const data = await res.json()
|
||||
browsePlugins.value = data.objects
|
||||
.filter((item: INPMSearchResultObject) => {
|
||||
return item.package.name.startsWith('picgo-plugin-')
|
||||
})
|
||||
.map((item: INPMSearchResultObject) => {
|
||||
return handleSearchResult(item)
|
||||
})
|
||||
.sort((a: IPicGoPlugin, b: IPicGoPlugin) => {
|
||||
return b.fullName.localeCompare(a.fullName)
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch plugins:', err)
|
||||
} finally {
|
||||
loadingBrowse.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function installPluginFromBrowse(item: IPicGoPlugin) {
|
||||
if (!item.gui) {
|
||||
if (confirm(t('pages.plugin.notGuiImplement'))) {
|
||||
item.ing = true
|
||||
window.electron.sendRPC(IRPCActionType.PLUGIN_INSTALL, item.fullName)
|
||||
}
|
||||
} else {
|
||||
item.ing = true
|
||||
window.electron.sendRPC(IRPCActionType.PLUGIN_INSTALL, item.fullName)
|
||||
}
|
||||
}
|
||||
|
||||
function handleImportLocalPlugin() {
|
||||
window.electron.sendRPC(IRPCActionType.PLUGIN_IMPORT_LOCAL)
|
||||
loading.value = true
|
||||
@@ -569,6 +752,14 @@ onBeforeMount(async () => {
|
||||
getSearchResult = debounce(_getSearchResult, 50)
|
||||
initConf()
|
||||
needReload.value = (await getConfig<boolean>(configPaths.needReload)) || false
|
||||
|
||||
// Close dropdown menu when clicking outside
|
||||
document.addEventListener('click', (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement
|
||||
if (!target.closest('.dropdown-wrapper')) {
|
||||
showPluginListMenu.value = false
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
|
||||
@@ -33,6 +33,15 @@ html, body {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.header-card {
|
||||
overflow: visible;
|
||||
border: 1px solid var(--color-border-secondary);
|
||||
border-radius: var(--radius-xl);
|
||||
background: var(--color-background-secondary);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: var(--transition-medium);
|
||||
}
|
||||
|
||||
/* Header Card */
|
||||
.header-card .card-header {
|
||||
display: flex;
|
||||
@@ -353,8 +362,8 @@ html, body {
|
||||
}
|
||||
|
||||
.cli-badge {
|
||||
color: var(--color-blue-common);
|
||||
background: var(--color-info);
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-accent);
|
||||
}
|
||||
|
||||
.update-badge {
|
||||
@@ -380,6 +389,7 @@ html, body {
|
||||
}
|
||||
|
||||
.plugin-info {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -575,7 +585,7 @@ html, body {
|
||||
width: 100%;
|
||||
max-width: 70vw;
|
||||
max-height: 80vh;
|
||||
background: var(--color-surface);
|
||||
background: var(--color-background-tertiary);
|
||||
box-shadow: var(--shadow-xl);
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -783,3 +793,117 @@ html, body {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Dropdown Menu */
|
||||
.dropdown-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.5rem);
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
min-width: 200px;
|
||||
border: 1px solid var(--color-border-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-background-tertiary);
|
||||
box-shadow: var(--shadow-lg);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.75rem 1rem;
|
||||
width: 100%;
|
||||
font-size: 0.875rem;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-primary);
|
||||
background: transparent;
|
||||
transition: var(--transition-fast);
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background: var(--color-background-hover);
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.dropdown-item svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Browse Modal */
|
||||
.browse-modal {
|
||||
max-width: 900px;
|
||||
max-height: 80vh;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.browse-content {
|
||||
display: flex;
|
||||
max-height: 600px;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.browse-search {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.browse-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 3rem 1rem;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.browse-plugin-grid {
|
||||
display: grid;
|
||||
overflow-y: auto;
|
||||
padding: 0.5rem;
|
||||
gap: 1rem;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
}
|
||||
|
||||
.browse-plugin-item {
|
||||
display: flex;
|
||||
border: 1px solid var(--color-border-secondary);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1rem;
|
||||
background: var(--color-background-primary);
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
transition: var(--transition-medium);
|
||||
}
|
||||
|
||||
.browse-plugin-item:hover {
|
||||
border-color: var(--color-accent);
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.browse-plugin-item .plugin-actions {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.cli-badge-browser {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-accent);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user