From 547588c6700a8db8363516526f0d8dd8ab2279f6 Mon Sep 17 00:00:00 2001 From: Kuingsmile <96409857+Kuingsmile@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:52:11 +0800 Subject: [PATCH] :sparkles: Feature(custom): optimize plugin page --- .../components/ImageProcessSetting.vue | 8 +- .../components/common/customModal.vue | 6 +- src/renderer/i18n/locales/en.json | 2 +- src/renderer/i18n/locales/zh-CN.json | 2 +- src/renderer/i18n/locales/zh-TW.json | 2 +- src/renderer/pages/Gallery.vue | 12 +- src/renderer/pages/NavigationPage.vue | 1 - src/renderer/pages/PicGoSetting.vue | 20 +- src/renderer/pages/Plugin.vue | 639 ++++++------ src/renderer/pages/Upload.vue | 2 +- src/renderer/pages/css/PicgoSetting.css | 1 - src/renderer/pages/css/PluginPage.css | 909 ------------------ 12 files changed, 382 insertions(+), 1222 deletions(-) delete mode 100644 src/renderer/pages/css/PluginPage.css diff --git a/src/renderer/components/ImageProcessSetting.vue b/src/renderer/components/ImageProcessSetting.vue index 1654a6d7..8039817e 100644 --- a/src/renderer/components/ImageProcessSetting.vue +++ b/src/renderer/components/ImageProcessSetting.vue @@ -1011,10 +1011,10 @@ import type { import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, toRaw, useTemplateRef, watch } from 'vue' import { useI18n } from 'vue-i18n' -import customRadioOption from '@/components/common/customRadioOption.vue' -import customRange from '@/components/common/customRange.vue' -import customSwitch from '@/components/common/customSwitch.vue' -import placeholderTable from '@/components/common/placeholderTable.vue' +import customRadioOption from '@/components/common/CustomRadioOption.vue' +import customRange from '@/components/common/CustomRange.vue' +import customSwitch from '@/components/common/CustomSwitch.vue' +import placeholderTable from '@/components/common/PlaceholderTable.vue' import PerPicbedSetting from '@/components/PerPicbedSetting.vue' import { getRawData } from '@/utils/common' import { configPaths } from '@/utils/configPaths' diff --git a/src/renderer/components/common/customModal.vue b/src/renderer/components/common/customModal.vue index 4746efa2..a54a8dec 100644 --- a/src/renderer/components/common/customModal.vue +++ b/src/renderer/components/common/customModal.vue @@ -24,7 +24,7 @@ @@ -50,6 +50,10 @@ import { getConfig } from '@/utils/dataSender' const visible = defineModel('visible') +function handleClose() { + visible.value = false +} + const enableAdvancedAnimation = ref(false) const { diff --git a/src/renderer/i18n/locales/en.json b/src/renderer/i18n/locales/en.json index 15e43b71..cd720e0d 100644 --- a/src/renderer/i18n/locales/en.json +++ b/src/renderer/i18n/locales/en.json @@ -685,7 +685,7 @@ "noPluginsFound": "No Plugins Found", "NoPluginsInstalled": "No Plugins Installed", "notGuiImplement": "This plugin does not have a GUI implementation, continue?", - "openRemoteList": "Open Remote Plugin List", + "openRemoteList": "Remote List", "pluginList": "Plugin List", "restartApp": "Restart Application", "searchInBrowse": "Search in Browse", diff --git a/src/renderer/i18n/locales/zh-CN.json b/src/renderer/i18n/locales/zh-CN.json index 5dd569e6..e0389de8 100644 --- a/src/renderer/i18n/locales/zh-CN.json +++ b/src/renderer/i18n/locales/zh-CN.json @@ -685,7 +685,7 @@ "noPluginsFound": "未找到插件", "NoPluginsInstalled": "暂无已安装插件", "notGuiImplement": "该插件未对可视化界面进行优化, 是否继续安装?", - "openRemoteList": "打开远程插件列表", + "openRemoteList": "远程列表", "pluginList": "插件列表", "restartApp": "重启应用", "searchInBrowse": "搜索插件", diff --git a/src/renderer/i18n/locales/zh-TW.json b/src/renderer/i18n/locales/zh-TW.json index 474a7838..17cf1af3 100644 --- a/src/renderer/i18n/locales/zh-TW.json +++ b/src/renderer/i18n/locales/zh-TW.json @@ -685,7 +685,7 @@ "noPluginsFound": "未找到插件", "NoPluginsInstalled": "尚未安裝任何插件", "notGuiImplement": "該插件未針對圖形介面進行優化,是否繼續安裝?", - "openRemoteList": "打開遠程插件列表", + "openRemoteList": "遠程列表", "pluginList": "插件列表", "restartApp": "重啟應用", "searchInBrowse": "在瀏覽中搜索", diff --git a/src/renderer/pages/Gallery.vue b/src/renderer/pages/Gallery.vue index 2bd88e69..8755b603 100644 --- a/src/renderer/pages/Gallery.vue +++ b/src/renderer/pages/Gallery.vue @@ -8,9 +8,7 @@ class="flex w-full items-center justify-between gap-4 rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5" >
-
- -
+

{{ t('pages.gallery.title') }}

@@ -559,10 +557,10 @@ import { useI18n } from 'vue-i18n' import { onBeforeRouteUpdate } from 'vue-router' import ALLApi from '@/apis/allApi' -import CustomButton from '@/components/common/customButton.vue' -import CustomModal from '@/components/common/customModal.vue' -import MultiSelect from '@/components/common/multiSelect.vue' -import SingleSelect from '@/components/common/singleSelect.vue' +import CustomButton from '@/components/common/CustomButton.vue' +import CustomModal from '@/components/common/CustomModal.vue' +import MultiSelect from '@/components/common/MultiSelect.vue' +import SingleSelect from '@/components/common/SingleSelect.vue' import VirtualScroller from '@/components/VirtualScroller.vue' import useConfirm from '@/hooks/useConfirm' import { usePicBed } from '@/hooks/useGlobal' diff --git a/src/renderer/pages/NavigationPage.vue b/src/renderer/pages/NavigationPage.vue index 319adf6c..6ae82a7e 100644 --- a/src/renderer/pages/NavigationPage.vue +++ b/src/renderer/pages/NavigationPage.vue @@ -311,7 +311,6 @@ function isPathActive(path: string): boolean { } function isPicBedPathActive(type: string): boolean { - console.log('type:', type, 'route.params.type:', route.params.type) return route.name === routerConfig.UPLOADER_CONFIG_PAGE && route.params.type === type } diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue index 32ad8291..ba1429a4 100644 --- a/src/renderer/pages/PicGoSetting.vue +++ b/src/renderer/pages/PicGoSetting.vue @@ -1242,16 +1242,16 @@ import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, toR import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router' -import CustomButton from '@/components/common/customButton.vue' -import CustomInput from '@/components/common/customInput.vue' -import CustomModal from '@/components/common/customModal.vue' -import CustomNavCard from '@/components/common/customNavCard.vue' -import CustomSelect from '@/components/common/customSelect.vue' -import CustomSwitch from '@/components/common/customSwitch.vue' -import MultiSelect from '@/components/common/multiSelect.vue' -import placeholderTable from '@/components/common/placeholderTable.vue' -import SettingCard from '@/components/common/settingCard.vue' -import SettingSection from '@/components/common/settingSection.vue' +import CustomButton from '@/components/common/CustomButton.vue' +import CustomInput from '@/components/common/CustomInput.vue' +import CustomModal from '@/components/common/CustomModal.vue' +import CustomNavCard from '@/components/common/CustomNavCard.vue' +import CustomSelect from '@/components/common/CustomSelect.vue' +import CustomSwitch from '@/components/common/CustomSwitch.vue' +import MultiSelect from '@/components/common/MultiSelect.vue' +import placeholderTable from '@/components/common/PlaceholderTable.vue' +import SettingCard from '@/components/common/SettingCard.vue' +import SettingSection from '@/components/common/SettingSection.vue' import ImageProcessSetting from '@/components/ImageProcessSetting.vue' import useConfirm from '@/hooks/useConfirm' import { osGlobal, usePicBed } from '@/hooks/useGlobal' diff --git a/src/renderer/pages/Plugin.vue b/src/renderer/pages/Plugin.vue index a09a1acb..88f0a0e7 100644 --- a/src/renderer/pages/Plugin.vue +++ b/src/renderer/pages/Plugin.vue @@ -1,303 +1,410 @@ @@ -321,6 +428,8 @@ import { import { computed, onBeforeMount, onBeforeUnmount, reactive, ref, toRaw, useTemplateRef, watch } from 'vue' import { useI18n } from 'vue-i18n' +import CustomButton from '@/components/common/CustomButton.vue' +import CustomModal from '@/components/common/CustomModal.vue' import ConfigForm from '@/components/UnifiedConfigForm.vue' import { usePicBed } from '@/hooks/useGlobal' import { getRawData, handleStreamlinePluginName } from '@/utils/common' @@ -345,11 +454,9 @@ const dialogVisible = ref(false) const pluginNameList = ref([]) const loading = ref(true) const needReload = ref(false) -const enableAdvancedAnimation = ref(false) const latestVersionMap = reactive>({}) 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([]) @@ -360,14 +467,6 @@ function setSrc(e: Event) { target.src = import.meta.env.BASE_URL + 'roundLogo.png' } -async function initConf() { - enableAdvancedAnimation.value = (await getConfig(configPaths.settings.isCustomMiniIcon)) || false -} - -const advancedAnimation = computed(() => ({ - advancedAnimation: enableAdvancedAnimation.value, -})) - const npmSearchText = computed(() => { return searchText.value.match('picgo-plugin-') ? searchText.value @@ -402,14 +501,6 @@ watch(npmSearchText, (val: string) => { } }) -watch(dialogVisible, (val: boolean) => { - if (val) { - document.body.style.overflow = 'hidden' - } else { - document.body.style.overflow = 'auto' - } -}) - watch(showBrowseDialog, (val: boolean) => { if (val) { document.body.style.overflow = 'hidden' @@ -675,26 +766,15 @@ 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 { @@ -750,16 +830,7 @@ onBeforeMount(async () => { window.electron.ipcRendererOn(PICGO_TOGGLE_PLUGIN, picgoTogglePluginHandler) getPluginList() getSearchResult = debounce(_getSearchResult, 50) - initConf() needReload.value = (await getConfig(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(() => { @@ -783,5 +854,3 @@ export default { name: 'PluginPage', } - - diff --git a/src/renderer/pages/Upload.vue b/src/renderer/pages/Upload.vue index 4c2627ff..9d8439ad 100644 --- a/src/renderer/pages/Upload.vue +++ b/src/renderer/pages/Upload.vue @@ -676,7 +676,7 @@ import { computed, onBeforeMount, onBeforeUnmount, reactive, ref, useTemplateRef import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router' -import CustomModal from '@/components/common/customModal.vue' +import CustomModal from '@/components/common/CustomModal.vue' import ImageProcessSetting from '@/components/ImageProcessSetting.vue' import { usePicBed } from '@/hooks/useGlobal' import useMessage from '@/hooks/useMessage' diff --git a/src/renderer/pages/css/PicgoSetting.css b/src/renderer/pages/css/PicgoSetting.css index 4bd1d26e..59f3ee60 100644 --- a/src/renderer/pages/css/PicgoSetting.css +++ b/src/renderer/pages/css/PicgoSetting.css @@ -1,5 +1,4 @@ /* stylelint-disable selector-pseudo-class-no-unknown */ -@import url('./common/advancedAnimation.css'); @import 'tailwindcss' reference; @import '../../assets/css/theme.css' reference; @import '../../assets/css/utilities.css' reference; diff --git a/src/renderer/pages/css/PluginPage.css b/src/renderer/pages/css/PluginPage.css deleted file mode 100644 index e2c48b9c..00000000 --- a/src/renderer/pages/css/PluginPage.css +++ /dev/null @@ -1,909 +0,0 @@ -@import url('./common/advancedAnimation.css'); - -/* Global scrolling behavior */ -html, body { - overflow-x: hidden; -} - -/* Container */ -.plugin-container { - display: flex; - overflow-y: auto; - margin: 0; - padding: 1rem; - width: 100%; - min-height: 100vh; - flex-direction: column; - gap: 1.25rem; - box-sizing: border-box; -} - -/* Card Base */ -.plugin-card { - overflow: hidden; - 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); -} - -.plugin-card:hover { - border-color: var(--color-border); - 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; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid var(--color-border-secondary); - padding: 1rem 1.5rem; - flex-wrap: wrap; - gap: 1rem; -} - -.header-content { - display: flex; - align-items: center; - gap: 1rem; - flex: 1; -} - -.header-icon { - display: flex; - align-items: center; - color: var(--color-accent); -} - -.header-content h1 { - margin: 0; - font-size: 1.5rem; - font-weight: 600; - color: var(--color-text-primary); - letter-spacing: -0.025em; -} - -.header-content p { - margin: 0; - font-size: 0.875rem; - color: var(--color-text-secondary); -} - -.header-actions { - display: flex; - align-items: center; - gap: 0.75rem; - flex-wrap: wrap; -} - -.action-button { - display: flex; - align-items: center; - border: none; - border-radius: var(--radius-md); - padding: 0.625rem 1rem; - font-size: 0.875rem; - font-family: inherit; - font-weight: 500; - color: white; - background: var(--color-accent); - transition: var(--transition-fast); - gap: 0.5rem; - cursor: pointer; -} - -.action-button:hover { - background: var(--color-accent-hover); - transform: translateY(-1px); - box-shadow: var(--shadow-md); -} - -.action-button.secondary { - border: 1px solid var(--color-border); - color: var(--color-text-primary); - background: var(--color-background-secondary); -} - -.action-button.secondary:hover { - border-color: var(--color-accent); - color: var(--color-accent); - background: var(--color-surface); -} - -.action-button.small { - padding: 0.5rem 0.75rem; - font-size: 0.75rem; -} - -/* Search Card */ -.search-card { - border-radius: var(--radius-lg); -} - -.search-container { - display: flex; - padding: 1rem 1.5rem; - flex-direction: column; - gap: 1rem; -} - -.search-input-wrapper { - position: relative; - display: flex; - align-items: center; -} - -.search-icon { - position: absolute; - left: 1rem; - z-index: 1; - color: var(--color-text-secondary); -} - -.search-input { - border: 1px solid var(--color-border); - border-radius: var(--radius-lg); - padding: 0.75rem 1rem 0.75rem 3rem; - width: 100%; - font-size: 0.875rem; - font-family: inherit; - color: var(--color-text-primary); - background: var(--color-background-secondary); - transition: var(--transition-fast); -} - -.search-input:focus { - border-color: var(--color-accent); - background: var(--color-background-tertiary); - outline: none; - box-shadow: 0 0 0 2px rgb(0 122 255 / 20%); -} - -.search-input::placeholder { - color: var(--color-text-secondary); -} - -.clear-button { - position: absolute; - right: 0.5rem; - display: flex; - align-items: center; - border: none; - border-radius: var(--radius-md); - padding: 0.5rem; - color: var(--color-text-secondary); - background: transparent; - transition: var(--transition-fast); - cursor: pointer; -} - -.clear-button:hover { - color: var(--color-text-primary); - background: var(--color-surface); -} - -/* Search Options */ -.search-options { - display: flex; - align-items: flex-start; - border-top: 1px solid var(--color-border-secondary); - padding-top: 0.5rem; -} - -.strict-search-checkbox { - display: flex; - flex-direction: column; - gap: 0.25rem; - cursor: pointer; - user-select: none; -} - -.checkbox-input { - display: none; -} - -.checkbox-label { - position: relative; - display: flex; - align-items: center; - font-size: 0.875rem; - font-weight: 500; - color: var(--color-text-primary); - gap: 0.5rem; -} - -.checkbox-label::before { - border: 2px solid var(--color-border); - border-radius: 3px; - width: 16px; - height: 16px; - background: var(--color-surface); - transition: all 0.2s ease; - content: ''; - flex-shrink: 0; -} - -.checkbox-input:checked + .checkbox-label::before { - border-color: var(--color-accent); - background: var(--color-accent); -} - -.checkbox-input:checked + .checkbox-label::after { - position: absolute; - left: 3px; - font-size: 10px; - font-weight: bold; - color: white; - content: '✓'; -} - -.checkbox-label:hover::before { - border-color: var(--color-accent); -} - -.checkbox-description { - margin-left: 24px; - font-size: 0.75rem; - color: var(--color-text-secondary); - line-height: 1.4; -} - -/* Notice Card */ -.notice-card { - border-color: var(--color-warning); - border-radius: var(--radius-lg); - background: linear-gradient(135deg, rgb(255 193 7 / 10%) 0%, var(--color-surface) 100%); -} - -.notice-content { - display: flex; - align-items: center; - gap: 1rem; - padding: 1rem 1.5rem; -} - -.notice-icon { - color: var(--color-warning); - flex-shrink: 0; -} - -.notice-text { - flex: 1; - font-size: 0.875rem; - font-weight: 500; - color: var(--color-text-primary); -} - -/* Loading */ -.loading-overlay { - position: absolute; - inset: 0; - z-index: 100; - display: flex; - justify-content: center; - align-items: center; - border-radius: var(--radius-xl); - background: rgb(0 0 0 / 50%); - flex-direction: column; - gap: 1rem; -} - -.loading-spinner { - border: 3px solid var(--color-border); - border-top: 3px solid var(--color-accent); - border-radius: var(--radius-round); - width: 40px; - height: 40px; - animation: spin 1s linear infinite; -} - -.loading-text { - font-size: 0.875rem; - font-weight: 500; - color: white; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -/* Plugin Grid */ -.plugin-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); - gap: 1.25rem; - align-items: start; -} - -/* Plugin Item Card */ -.plugin-item-card { - position: relative; - display: flex; - border-radius: var(--radius-lg); - padding: 1.5rem; - height: auto; - min-height: 200px; - transition: var(--transition-medium); - flex-direction: column; -} - -.plugin-item-card:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-lg); -} - -.plugin-item-card.disabled { - opacity: 0.7; -} - -/* Plugin Badges */ -.cli-badge, -.update-badge { - position: absolute; - top: 1rem; - right: 1rem; - z-index: 1; - border-radius: var(--radius-sm); - padding: 0.25rem 0.5rem; - font-size: 0.75rem; - font-weight: 600; -} - -.cli-badge { - color: var(--color-text-primary); - background: var(--color-accent); -} - -.update-badge { - right: 3.5rem; - color: white; - background: var(--color-success); -} - -/* Plugin Header */ -.plugin-header { - display: flex; - align-items: flex-start; - gap: 1rem; - margin-bottom: 1rem; -} - -.plugin-logo { - border-radius: var(--radius-lg); - width: 48px; - height: 48px; - object-fit: cover; - flex-shrink: 0; -} - -.plugin-info { - position: relative; - flex: 1; - min-width: 0; -} - -.plugin-name { - display: flex; - align-items: center; - overflow: hidden; - margin: 0 0 0.25rem; - font-size: 1rem; - font-weight: 600; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--color-text-primary); - transition: var(--transition-fast); - cursor: pointer; - gap: 0.5rem; -} - -.plugin-name:hover { - color: var(--color-accent); -} - -.plugin-version { - border-radius: var(--radius-sm); - padding: 0.125rem 0.375rem; - font-size: 0.75rem; - font-weight: 400; - color: var(--color-text-secondary); - background: var(--color-background-tertiary); -} - -.plugin-author { - overflow: hidden; - margin: 0; - font-size: 0.875rem; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--color-text-secondary); -} - -/* Plugin Description */ -.plugin-description { - display: flex; - align-items: flex-start; - margin-bottom: 1.5rem; - flex: 1; -} - -.plugin-description p { - display: -webkit-box; - overflow: hidden; - margin: 0; - min-height: 2.6rem; - font-size: 0.875rem; - text-overflow: ellipsis; - color: var(--color-text-primary); - line-height: 1.5; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -} - -/* Plugin Actions */ -.plugin-actions { - margin-top: auto; - padding-top: 1rem; -} - -.plugin-button { - display: flex; - align-items: center; - border: none; - border-radius: var(--radius-md); - padding: 0.75rem 1rem; - width: 100%; - font-size: 0.875rem; - font-family: inherit; - font-weight: 500; - transition: var(--transition-fast); - gap: 0.5rem; - cursor: pointer; -} - -.plugin-button:disabled { - cursor: not-allowed; - opacity: 0.6; -} - -.install-button { - color: white; - background: var(--color-success); -} - -.install-button:hover:not(:disabled) { - transform: translateY(-1px); -} - -.installing-button, -.processing-button { - border: 1px solid var(--color-border); - color: var(--color-text-secondary); - background: var(--color-surface-elevated); -} - -.installed-button { - border: 1px solid var(--color-success); - color: var(--color-success); - background: var(--color-success-light); -} - -.settings-button { - border: 1px solid var(--color-border); - color: var(--color-text-primary); - background: var(--color-background-secondary); -} - -.settings-button:hover:not(:disabled) { - background: var(--color-accent); - transform: translateY(-1px); -} - -.disabled-button { - border: 1px solid var(--color-border); - color: var(--color-text-secondary); - background: var(--color-background-secondary); -} - -.disabled-button:hover:not(:disabled) { - border-color: var(--color-warning); - color: var(--color-warning); - background: var(--color-surface); -} - -/* Button Spinner */ -.button-spinner { - border: 2px solid transparent; - border-top: 2px solid currentcolor; - border-radius: var(--radius-round); - width: 16px; - height: 16px; - animation: spin 1s linear infinite; -} - -/* Empty State */ -.empty-state { - border-radius: var(--radius-lg); - padding: 3rem 2rem; -} - -.empty-content { - display: flex; - flex-direction: column; - align-items: center; - gap: 1rem; - text-align: center; -} - -.empty-icon { - color: var(--color-text-secondary); - opacity: 0.5; -} - -.empty-content h3 { - margin: 0; - font-size: 1.125rem; - font-weight: 600; - color: var(--color-text-primary); -} - -.empty-content p { - margin: 0; - max-width: 400px; - font-size: 0.875rem; - color: var(--color-text-secondary); -} - -/* Modal */ -.modal-overlay { - position: fixed; - inset: 0; - z-index: 1000; - display: flex; - justify-content: center; - align-items: center; - background: rgb(0 0 0 / 50%); -} - -.modal-container { - display: flex; - margin: 2rem; - border: 1px solid var(--color-border); - border-radius: var(--radius-xl); - width: 100%; - max-width: 70vw; - max-height: 80vh; - background: var(--color-background-tertiary); - box-shadow: var(--shadow-xl); - flex-direction: column; -} - -.modal-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1.5rem 1.5rem 0; -} - -.modal-title { - margin: 0; - font-size: 1.25rem; - font-weight: 600; - color: var(--color-text-primary); -} - -.modal-close { - display: flex; - justify-content: center; - align-items: center; - border: none; - border-radius: var(--radius-md); - width: 2rem; - height: 2rem; - color: var(--color-text-secondary); - background: transparent; - transition: var(--transition-fast); - cursor: pointer; -} - -.modal-close:hover { - color: var(--color-text-primary); - background: var(--color-surface-elevated); -} - -.modal-content { - overflow-y: auto; - padding: 1.5rem; - flex: 1; -} - -.modal-content::-webkit-scrollbar { - width: 6px; -} - -.modal-content::-webkit-scrollbar-track { - border-radius: 3px; - background: var(--color-surface-elevated); -} - -.modal-content::-webkit-scrollbar-thumb { - border-radius: 3px; - background: var(--color-border); -} - -.modal-content::-webkit-scrollbar-thumb:hover { - background: var(--color-text-secondary); -} - -.modal-footer { - display: flex; - justify-content: flex-end; - align-items: center; - border-top: 1px solid var(--color-border-secondary); - padding: 1.5rem; - gap: 0.75rem; -} - -/* Buttons */ -.btn { - display: flex; - align-items: center; - border: none; - border-radius: var(--radius-md); - padding: 0.75rem 1.5rem; - font-size: 0.875rem; - font-family: inherit; - font-weight: 500; - transition: var(--transition-fast); - gap: 0.5rem; - cursor: pointer; -} - -.btn:disabled { - cursor: not-allowed; - opacity: 0.8; -} - -.btn:hover:not(:disabled) { - transform: translateY(-1px); - box-shadow: var(--shadow-sm); -} - -.btn-primary { - color: white; - background: var(--color-accent); -} - -.btn-primary:hover:not(:disabled) { - background: var(--color-accent-hover); -} - -.btn-secondary { - border: 1px solid var(--color-border); - color: var(--color-text-primary); - background: var(--color-surface-elevated); -} - -.btn-secondary:hover:not(:disabled) { - border-color: var(--color-accent); - background: var(--color-surface); -} - -/* Transitions */ -.notice-enter-active, -.notice-leave-active { - transition: all var(--transition-medium); -} - -.notice-enter-from, -.notice-leave-to { - opacity: 0; - transform: translateY(-1rem); -} - -/* Responsive Design */ -@media (width <= 768px) { - .plugin-container { - padding: 0.75rem; - gap: 1rem; - } - - .header-card .card-header { - flex-direction: column; - align-items: flex-start; - gap: 1rem; - } - - .header-actions { - justify-content: flex-start; - width: 100%; - flex-wrap: wrap; - } - - .plugin-grid { - grid-template-columns: 1fr; - } - - .modal-container { - margin: 1rem; - max-width: 95vw; - } - - .action-button { - justify-content: center; - min-width: 120px; - } -} - -@media (width <= 480px) { - .plugin-container { - padding: 0.5rem; - } - - .plugin-item-card { - padding: 1rem; - } - - .header-actions { - flex-direction: column; - gap: 0.5rem; - width: 100%; - } - - .action-button { - justify-content: center; - width: 100%; - } -} - -/* Accessibility */ -@media (prefers-reduced-motion: reduce) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} - -/* Focus styles for keyboard navigation */ -.action-button:focus-visible, -.plugin-button:focus-visible, -.btn:focus-visible, -.search-input:focus-visible, -.clear-button:focus-visible, -.modal-close:focus-visible { - outline: 2px solid var(--color-accent); - outline-offset: 2px; -} - -.plugin-name:focus-visible { - border-radius: var(--radius-sm); - 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); -}