diff --git a/src/components/dialog/PluginMarketSettingDialog.vue b/src/components/dialog/PluginMarketSettingDialog.vue index 73943741..00aec0db 100644 --- a/src/components/dialog/PluginMarketSettingDialog.vue +++ b/src/components/dialog/PluginMarketSettingDialog.vue @@ -24,11 +24,14 @@ const repoText = ref('') const newRepoUrl = ref('') const editingIndex = ref(null) const editingUrl = ref('') +const syncingWiki = ref(false) const emit = defineEmits(['save', 'close']) const parsedTextRepos = computed(() => parseRepoInput(repoText.value)) -const activeRepoCount = computed(() => (editorMode.value === 'text' ? parsedTextRepos.value.repos.length : repoList.value.length)) +const activeRepoCount = computed(() => + editorMode.value === 'text' ? parsedTextRepos.value.repos.length : repoList.value.length, +) const saveDisabled = computed( () => activeRepoCount.value === 0 || (editorMode.value === 'text' && parsedTextRepos.value.invalidRepos.length > 0), ) @@ -136,6 +139,35 @@ async function saveHandle() { } } +/** 从 Wiki 同步公开插件仓库清单并写入配置。 */ +async function syncWikiRepos() { + try { + syncingWiki.value = true + const result: { [key: string]: any } = await api.post('system/setting/PLUGIN_MARKET/sync-wiki', {}) + + if (result.success) { + const repos = Array.isArray(result.data?.repos) + ? result.data.repos + : parseRepoInput(result.data?.value || '').repos + repoList.value = repos + syncTextFromList() + $toast.success( + t('dialog.pluginMarketSetting.syncSuccess', { + added: result.data?.added_count ?? 0, + total: result.data?.total_count ?? repos.length, + }), + ) + } else { + $toast.error(t('dialog.pluginMarketSetting.syncFailed', { message: result?.message })) + } + } catch (error) { + console.log(error) + $toast.error(t('dialog.pluginMarketSetting.syncFailed', { message: error instanceof Error ? error.message : '' })) + } finally { + syncingWiki.value = false + } +} + /** 获取当前维护模式下可保存的仓库地址。 */ function normalizeCurrentRepos() { if (editorMode.value === 'text') { @@ -224,8 +256,8 @@ function formatRepoDisplay(url: string) { const pathSegments = parsedUrl.pathname.split('/').filter(Boolean) if ( - ['github.com', 'www.github.com', 'raw.githubusercontent.com'].includes(parsedUrl.hostname) - && pathSegments.length >= 2 + ['github.com', 'www.github.com', 'raw.githubusercontent.com'].includes(parsedUrl.hostname) && + pathSegments.length >= 2 ) { return `${pathSegments[0]}/${pathSegments[1].replace(/\.git$/, '')}` } @@ -261,22 +293,44 @@ onMounted(() => {
- - - {{ t('dialog.pluginMarketSetting.listMode') }} - - - {{ t('dialog.pluginMarketSetting.textMode') }} - - +
+ + {{ t('dialog.pluginMarketSetting.repoCountHint', { count: activeRepoCount }) }} +
+
+ + + + + + +
@@ -425,6 +479,16 @@ onMounted(() => { + + {{ t('dialog.pluginMarketSetting.syncWiki') }} + { .plugin-market-toolbar { display: flex; flex-shrink: 0; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + min-block-size: 2.25rem; } -.plugin-market-mode-toggle { - inline-size: 100%; +.plugin-market-toolbar-hint { + display: flex; + align-items: center; + border-radius: 0.375rem; + background: rgba(var(--v-theme-info), 0.08); + color: rgb(var(--v-theme-info)); + font-size: 0.875rem; + gap: 0.5rem; + min-inline-size: 0; + padding-block: 0.5rem; + padding-inline: 1rem; - :deep(.v-btn) { - flex: 1; - min-inline-size: 0; + span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} + +.plugin-market-mode-switch { + display: inline-flex; + padding: 0.125rem; + border: 1px solid rgba(var(--v-theme-on-surface), 0.08); + border-radius: 0.375rem; + background: rgba(var(--v-theme-surface), 0.72); + gap: 0.125rem; +} + +.plugin-market-mode-button { + display: flex; + align-items: center; + justify-content: center; + padding: 0; + border: 0; + border-radius: 0.375rem; + background: transparent; + block-size: 2.25rem; + color: rgba(var(--v-theme-on-surface), 0.68); + cursor: pointer; + font: inherit; + inline-size: 2.25rem; + transition: + background-color 0.16s ease, + color 0.16s ease; + + &:hover { + background: rgba(var(--v-theme-primary), 0.07); + color: rgb(var(--v-theme-on-surface)); + } + + &:focus-visible { + outline: 2px solid rgba(var(--v-theme-primary), 0.48); + outline-offset: 2px; + } + + &.is-active { + background: rgba(var(--v-theme-primary), 0.12); + color: rgb(var(--v-theme-primary)); } } @@ -529,8 +649,8 @@ onMounted(() => { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; - -webkit-line-clamp: 2; line-break: anywhere; + -webkit-line-clamp: 2; overflow-wrap: anywhere; white-space: normal; word-break: break-word; @@ -550,20 +670,22 @@ onMounted(() => { .plugin-market-empty { display: flex; + flex-direction: column; align-items: center; justify-content: center; - flex-direction: column; min-block-size: 14rem; } .plugin-market-textarea-field { position: relative; display: flex; + overflow: hidden; flex: 1; background: rgba(var(--v-theme-surface), 0.72); min-block-size: 0; - overflow: hidden; - transition: border-color 0.2s ease, box-shadow 0.2s ease; + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; &:focus-within { border-color: rgb(var(--v-theme-primary)); @@ -586,13 +708,14 @@ onMounted(() => { background: transparent; block-size: 100%; color: rgb(var(--v-theme-on-surface)); - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace; font-size: 1rem; line-height: 1.6; min-block-size: 0; outline: none; overflow-y: auto; - padding: 1rem 1rem 1rem 3.25rem; + padding-block: 1rem; + padding-inline: 3.25rem 1rem; resize: none; white-space: pre-wrap; word-break: break-word; @@ -615,16 +738,22 @@ onMounted(() => { .plugin-market-actions { flex: 0 0 auto; gap: 0.5rem; - padding: 0.75rem 1.5rem 1rem; + padding-block: 0.75rem 1rem; + padding-inline: 1.5rem; + + :deep(.v-btn) { + flex: 0 0 auto; + } } -@media (max-width: 600px) { +@media (width <= 600px) { .plugin-market-dialog-card { block-size: 100dvh; } .plugin-market-card-item { - padding: 0.75rem 1rem 0.625rem; + padding-block: 0.75rem 0.625rem; + padding-inline: 1rem; } .plugin-market-header { @@ -640,16 +769,22 @@ onMounted(() => { .plugin-market-dialog-body { gap: 0.625rem; - padding: 0.75rem 1rem !important; + padding-block: 0.75rem !important; + padding-inline: 1rem !important; } - .plugin-market-mode-toggle { - inline-size: 100%; + .plugin-market-toolbar { + flex-direction: row; + align-items: center; + justify-content: space-between; + } - :deep(.v-btn) { - flex: 1; - min-inline-size: 0; - } + .plugin-market-mode-switch { + flex: 0 0 auto; + } + + .plugin-market-toolbar-hint { + flex: 1 1 auto; } .plugin-market-list-panel, @@ -666,7 +801,18 @@ onMounted(() => { } .plugin-market-actions { - padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom)); + flex-wrap: wrap; + padding-block: 0.75rem calc(0.75rem + env(safe-area-inset-bottom)); + padding-inline: 1rem; + + :deep(.v-btn) { + flex: 1 1 9rem; + min-inline-size: 0; + } + + :deep(.v-spacer) { + display: none; + } } } diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index b0c50f4b..e56b4a26 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -2582,6 +2582,7 @@ export default { repoHint: 'Separate multiple URLs with new lines or commas', urlPlaceholder: 'Enter plugin repository URL', textPlaceholder: 'https://github.com/jxxghp/MoviePilot-Plugins/\nhttps://github.com/xxxx/xxxxxx/', + repoCountHint: '{count} plugin repository URLs maintained', listMode: 'List', textMode: 'Text', textHint: 'Paste repository URLs one per line or separated by commas.', @@ -2592,6 +2593,9 @@ export default { invalidText: 'There are {count} invalid URLs in the text. Fix them before saving.', invalidTextIgnored: '{count} invalid URLs ignored', duplicateTextIgnored: 'Duplicate URLs will be removed automatically when saving.', + syncWiki: 'Sync Wiki', + syncSuccess: 'Plugin repositories synced from Wiki. {added} added, {total} total.', + syncFailed: 'Failed to sync Wiki: {message}!', close: 'Close', save: 'Save', saveSuccess: 'Plugin repository saved successfully', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 66637d51..08c7166c 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -2534,6 +2534,7 @@ export default { repoHint: '多个地址可使用换行或英文逗号分隔', urlPlaceholder: '输入插件仓库地址', textPlaceholder: 'https://github.com/jxxghp/MoviePilot-Plugins/\nhttps://github.com/xxxx/xxxxxx/', + repoCountHint: '当前已维护 {count} 个插件仓库地址', listMode: '列表维护', textMode: '文本维护', textHint: '直接粘贴仓库地址串,一行一个或使用英文逗号分隔。', @@ -2544,6 +2545,9 @@ export default { invalidText: '文本中有 {count} 个无效地址,请修正后保存。', invalidTextIgnored: '已忽略 {count} 个无效地址', duplicateTextIgnored: '重复地址会在保存时自动去重。', + syncWiki: '同步 Wiki', + syncSuccess: '已从 Wiki 同步插件仓库,新增 {added} 个,共 {total} 个', + syncFailed: '同步 Wiki 失败:{message}!', close: '关闭', save: '保存', saveSuccess: '插件仓库保存成功', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 8c469832..141a416f 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -2535,6 +2535,7 @@ export default { repoHint: '多個地址可使用換行或英文逗號分隔', urlPlaceholder: '輸入插件倉庫地址', textPlaceholder: 'https://github.com/jxxghp/MoviePilot-Plugins/\nhttps://github.com/xxxx/xxxxxx/', + repoCountHint: '目前已維護 {count} 個插件倉庫地址', listMode: '列表維護', textMode: '文字維護', textHint: '直接貼上倉庫地址串,一行一個或使用英文逗號分隔。', @@ -2545,6 +2546,9 @@ export default { invalidText: '文字中有 {count} 個無效地址,請修正後儲存。', invalidTextIgnored: '已忽略 {count} 個無效地址', duplicateTextIgnored: '重複地址會在儲存時自動去重。', + syncWiki: '同步 Wiki', + syncSuccess: '已從 Wiki 同步插件倉庫,新增 {added} 個,共 {total} 個', + syncFailed: '同步 Wiki 失敗:{message}!', close: '關閉', save: '儲存', saveSuccess: '插件倉庫儲存成功',