diff options
Diffstat (limited to 'packages/frontend/src')
34 files changed, 1371 insertions, 1138 deletions
diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index 5c89a6530d..dbc673333c 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -52,9 +52,9 @@ SPDX-License-Identifier: AGPL-3.0-only {{ item.label }} </template> <template v-else> - <span style="opacity: 0.7; font-size: 90%;">{{ item.parentLabels.join(' > ') }}</span> + <span style="opacity: 0.7; font-size: 90%; word-break: break-word;">{{ item.parentLabels.join(' > ') }}</span> <br> - <span>{{ item.label }}</span> + <span style="word-break: break-word;">{{ item.label }}</span> </template> </span> </MkA> @@ -95,7 +95,7 @@ export type SuperMenuDef = { <script lang="ts" setup> import { useTemplateRef, ref, watch, nextTick, computed } from 'vue'; import { getScrollContainer } from '@@/js/scroll.js'; -import type { SearchIndexItem } from '@/utility/settings-search-index.js'; +import type { SearchIndexItem } from '@/utility/inapp-search.js'; import MkInput from '@/components/MkInput.vue'; import { i18n } from '@/i18n.js'; import { useRouter } from '@/router.js'; @@ -165,12 +165,28 @@ watch(rawSearchQuery, (value) => { }); }; - for (const item of searchIndexItemById.values()) { - if ( - compareStringIncludes(item.label, value) || - item.keywords.some((x) => compareStringIncludes(x, value)) - ) { + // label, keywords, texts の順に優先して表示 + + let items = Array.from(searchIndexItemById.values()); + + for (const item of items) { + if (compareStringIncludes(item.label, value)) { + addSearchResult(item); + items = items.filter(i => i.id !== item.id); + } + } + + for (const item of items) { + if (item.keywords.some((x) => compareStringIncludes(x, value))) { + addSearchResult(item); + items = items.filter(i => i.id !== item.id); + } + } + + for (const item of items) { + if (item.texts.some((x) => compareStringIncludes(x, value))) { addSearchResult(item); + items = items.filter(i => i.id !== item.id); } } } diff --git a/packages/frontend/src/components/global/SearchText.vue b/packages/frontend/src/components/global/SearchText.vue new file mode 100644 index 0000000000..27a284faf0 --- /dev/null +++ b/packages/frontend/src/components/global/SearchText.vue @@ -0,0 +1,14 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<slot></slot> +</template> + +<script lang="ts" setup> +</script> + +<style lang="scss" module> +</style> diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index 19766e8575..6b1b80695f 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -31,7 +31,7 @@ import PageWithHeader from './global/PageWithHeader.vue'; import PageWithAnimBg from './global/PageWithAnimBg.vue'; import SearchMarker from './global/SearchMarker.vue'; import SearchLabel from './global/SearchLabel.vue'; -import SearchKeyword from './global/SearchKeyword.vue'; +import SearchText from './global/SearchText.vue'; import SearchIcon from './global/SearchIcon.vue'; import type { App } from 'vue'; @@ -71,7 +71,7 @@ export const components = { PageWithAnimBg: PageWithAnimBg, SearchMarker: SearchMarker, SearchLabel: SearchLabel, - SearchKeyword: SearchKeyword, + SearchText: SearchText, SearchIcon: SearchIcon, }; @@ -105,7 +105,7 @@ declare module '@vue/runtime-core' { PageWithAnimBg: typeof PageWithAnimBg; SearchMarker: typeof SearchMarker; SearchLabel: typeof SearchLabel; - SearchKeyword: typeof SearchKeyword; + SearchText: typeof SearchText; SearchIcon: typeof SearchIcon; } } diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index 6c580f87f1..bd919146f8 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -4,158 +4,161 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkFolder> - <template #icon><i class="ti ti-shield"></i></template> - <template #label>{{ i18n.ts.botProtection }}</template> - <template v-if="botProtectionForm.savedState.provider === 'hcaptcha'" #suffix>hCaptcha</template> - <template v-else-if="botProtectionForm.savedState.provider === 'mcaptcha'" #suffix>mCaptcha</template> - <template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template> - <template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template> - <template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template> - <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template> - <template #footer> - <MkFormFooter :canSaving="canSaving" :form="botProtectionForm"/> - </template> +<SearchMarker markerId="botProtection" :keywords="['bot', 'protection', 'captcha', 'hcaptcha', 'mcaptcha', 'recaptcha', 'turnstile']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-shield"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.botProtection }}</SearchLabel></template> + <template v-if="botProtectionForm.savedState.provider === 'hcaptcha'" #suffix>hCaptcha</template> + <template v-else-if="botProtectionForm.savedState.provider === 'mcaptcha'" #suffix>mCaptcha</template> + <template v-else-if="botProtectionForm.savedState.provider === 'recaptcha'" #suffix>reCAPTCHA</template> + <template v-else-if="botProtectionForm.savedState.provider === 'turnstile'" #suffix>Turnstile</template> + <template v-else-if="botProtectionForm.savedState.provider === 'testcaptcha'" #suffix>testCaptcha</template> + <template v-else #suffix>{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</template> + <template #footer> + <MkFormFooter :canSaving="canSaving" :form="botProtectionForm"/> + </template> - <div class="_gaps_m"> - <MkRadios v-model="botProtectionForm.state.provider"> - <option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option> - <option value="hcaptcha">hCaptcha</option> - <option value="mcaptcha">mCaptcha</option> - <option value="recaptcha">reCAPTCHA</option> - <option value="turnstile">Turnstile</option> - <option value="testcaptcha">testCaptcha</option> - </MkRadios> + <div class="_gaps_m"> + <MkRadios v-model="botProtectionForm.state.provider"> + <option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option> + <option value="hcaptcha">hCaptcha</option> + <option value="mcaptcha">mCaptcha</option> + <option value="recaptcha">reCAPTCHA</option> + <option value="turnstile">Turnstile</option> + <option value="testcaptcha">testCaptcha</option> + </MkRadios> - <template v-if="botProtectionForm.state.provider === 'hcaptcha'"> - <MkInput v-model="botProtectionForm.state.hcaptchaSiteKey" debounce> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.hcaptchaSiteKey }}</template> - </MkInput> - <MkInput v-model="botProtectionForm.state.hcaptchaSecretKey" debounce> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.hcaptchaSecretKey }}</template> - </MkInput> - <FormSlot v-if="botProtectionForm.state.hcaptchaSiteKey"> - <template #label>{{ i18n.ts._captcha.verify }}</template> - <MkCaptcha - v-model="captchaResult" - provider="hcaptcha" - :sitekey="botProtectionForm.state.hcaptchaSiteKey" - :secretKey="botProtectionForm.state.hcaptchaSecretKey" - /> - </FormSlot> - <MkInfo> - <div :class="$style.captchaInfoMsg"> - <div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div> - <div> - <span>ref: </span><a href="https://docs.hcaptcha.com/#integration-testing-test-keys" target="_blank">hCaptcha Developer Guide</a> + <template v-if="botProtectionForm.state.provider === 'hcaptcha'"> + <MkInput v-model="botProtectionForm.state.hcaptchaSiteKey" debounce> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.hcaptchaSiteKey }}</template> + </MkInput> + <MkInput v-model="botProtectionForm.state.hcaptchaSecretKey" debounce> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.hcaptchaSecretKey }}</template> + </MkInput> + <FormSlot v-if="botProtectionForm.state.hcaptchaSiteKey"> + <template #label>{{ i18n.ts._captcha.verify }}</template> + <MkCaptcha + v-model="captchaResult" + provider="hcaptcha" + :sitekey="botProtectionForm.state.hcaptchaSiteKey" + :secretKey="botProtectionForm.state.hcaptchaSecretKey" + /> + </FormSlot> + <MkInfo> + <div :class="$style.captchaInfoMsg"> + <div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div> + <div> + <span>ref: </span><a href="https://docs.hcaptcha.com/#integration-testing-test-keys" target="_blank">hCaptcha Developer Guide</a> + </div> </div> - </div> - </MkInfo> - </template> + </MkInfo> + </template> - <template v-else-if="botProtectionForm.state.provider === 'mcaptcha'"> - <MkInput v-model="botProtectionForm.state.mcaptchaSiteKey" debounce> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.mcaptchaSiteKey }}</template> - </MkInput> - <MkInput v-model="botProtectionForm.state.mcaptchaSecretKey" debounce> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.mcaptchaSecretKey }}</template> - </MkInput> - <MkInput v-model="botProtectionForm.state.mcaptchaInstanceUrl" debounce> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template> - </MkInput> - <FormSlot v-if="botProtectionForm.state.mcaptchaSiteKey && botProtectionForm.state.mcaptchaInstanceUrl"> - <template #label>{{ i18n.ts._captcha.verify }}</template> - <MkCaptcha - v-model="captchaResult" - provider="mcaptcha" - :sitekey="botProtectionForm.state.mcaptchaSiteKey" - :secretKey="botProtectionForm.state.mcaptchaSecretKey" - :instanceUrl="botProtectionForm.state.mcaptchaInstanceUrl" - /> - </FormSlot> - </template> + <template v-else-if="botProtectionForm.state.provider === 'mcaptcha'"> + <MkInput v-model="botProtectionForm.state.mcaptchaSiteKey" debounce> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.mcaptchaSiteKey }}</template> + </MkInput> + <MkInput v-model="botProtectionForm.state.mcaptchaSecretKey" debounce> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.mcaptchaSecretKey }}</template> + </MkInput> + <MkInput v-model="botProtectionForm.state.mcaptchaInstanceUrl" debounce> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.mcaptchaInstanceUrl }}</template> + </MkInput> + <FormSlot v-if="botProtectionForm.state.mcaptchaSiteKey && botProtectionForm.state.mcaptchaInstanceUrl"> + <template #label>{{ i18n.ts._captcha.verify }}</template> + <MkCaptcha + v-model="captchaResult" + provider="mcaptcha" + :sitekey="botProtectionForm.state.mcaptchaSiteKey" + :secretKey="botProtectionForm.state.mcaptchaSecretKey" + :instanceUrl="botProtectionForm.state.mcaptchaInstanceUrl" + /> + </FormSlot> + </template> - <template v-else-if="botProtectionForm.state.provider === 'recaptcha'"> - <MkInput v-model="botProtectionForm.state.recaptchaSiteKey" debounce> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.recaptchaSiteKey }}</template> - </MkInput> - <MkInput v-model="botProtectionForm.state.recaptchaSecretKey" debounce> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.recaptchaSecretKey }}</template> - </MkInput> - <FormSlot v-if="botProtectionForm.state.recaptchaSiteKey"> - <template #label>{{ i18n.ts._captcha.verify }}</template> - <MkCaptcha - v-model="captchaResult" - provider="recaptcha" - :sitekey="botProtectionForm.state.recaptchaSiteKey" - :secretKey="botProtectionForm.state.recaptchaSecretKey" - /> - </FormSlot> - <MkInfo> - <div :class="$style.captchaInfoMsg"> - <div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div> - <div> - <span>ref: </span> - <a - href="https://developers.google.com/recaptcha/docs/faq?hl=ja#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do" - target="_blank" - >reCAPTCHA FAQ</a> + <template v-else-if="botProtectionForm.state.provider === 'recaptcha'"> + <MkInput v-model="botProtectionForm.state.recaptchaSiteKey" debounce> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.recaptchaSiteKey }}</template> + </MkInput> + <MkInput v-model="botProtectionForm.state.recaptchaSecretKey" debounce> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.recaptchaSecretKey }}</template> + </MkInput> + <FormSlot v-if="botProtectionForm.state.recaptchaSiteKey"> + <template #label>{{ i18n.ts._captcha.verify }}</template> + <MkCaptcha + v-model="captchaResult" + provider="recaptcha" + :sitekey="botProtectionForm.state.recaptchaSiteKey" + :secretKey="botProtectionForm.state.recaptchaSecretKey" + /> + </FormSlot> + <MkInfo> + <div :class="$style.captchaInfoMsg"> + <div>{{ i18n.ts._captcha.testSiteKeyMessage }}</div> + <div> + <span>ref: </span> + <a + href="https://developers.google.com/recaptcha/docs/faq?hl=ja#id-like-to-run-automated-tests-with-recaptcha.-what-should-i-do" + target="_blank" + >reCAPTCHA FAQ</a> + </div> </div> - </div> - </MkInfo> - </template> + </MkInfo> + </template> - <template v-else-if="botProtectionForm.state.provider === 'turnstile'"> - <MkInput v-model="botProtectionForm.state.turnstileSiteKey" debounce> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.turnstileSiteKey }}</template> - </MkInput> - <MkInput v-model="botProtectionForm.state.turnstileSecretKey" debounce> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.turnstileSecretKey }}</template> - </MkInput> - <FormSlot v-if="botProtectionForm.state.turnstileSiteKey"> - <template #label>{{ i18n.ts._captcha.verify }}</template> - <MkCaptcha - v-model="captchaResult" - provider="turnstile" - :sitekey="botProtectionForm.state.turnstileSiteKey" - :secretKey="botProtectionForm.state.turnstileSecretKey" - /> - </FormSlot> - <MkInfo> - <div :class="$style.captchaInfoMsg"> - <div> - {{ i18n.ts._captcha.testSiteKeyMessage }} - </div> - <div> - <span>ref: </span><a href="https://developers.cloudflare.com/turnstile/troubleshooting/testing/" target="_blank">Cloudflare Docs</a> + <template v-else-if="botProtectionForm.state.provider === 'turnstile'"> + <MkInput v-model="botProtectionForm.state.turnstileSiteKey" debounce> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.turnstileSiteKey }}</template> + </MkInput> + <MkInput v-model="botProtectionForm.state.turnstileSecretKey" debounce> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.turnstileSecretKey }}</template> + </MkInput> + <FormSlot v-if="botProtectionForm.state.turnstileSiteKey"> + <template #label>{{ i18n.ts._captcha.verify }}</template> + <MkCaptcha + v-model="captchaResult" + provider="turnstile" + :sitekey="botProtectionForm.state.turnstileSiteKey" + :secretKey="botProtectionForm.state.turnstileSecretKey" + /> + </FormSlot> + <MkInfo> + <div :class="$style.captchaInfoMsg"> + <div> + {{ i18n.ts._captcha.testSiteKeyMessage }} + </div> + <div> + <span>ref: </span><a href="https://developers.cloudflare.com/turnstile/troubleshooting/testing/" target="_blank">Cloudflare Docs</a> + </div> </div> - </div> - </MkInfo> - </template> + </MkInfo> + </template> - <template v-else-if="botProtectionForm.state.provider === 'testcaptcha'"> - <MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo> - <FormSlot> - <template #label>{{ i18n.ts._captcha.verify }}</template> - <MkCaptcha v-model="captchaResult" provider="testcaptcha" :sitekey="null"/> - </FormSlot> - </template> - </div> -</MkFolder> + <template v-else-if="botProtectionForm.state.provider === 'testcaptcha'"> + <MkInfo warn><span v-html="i18n.ts.testCaptchaWarning"></span></MkInfo> + <FormSlot> + <template #label>{{ i18n.ts._captcha.verify }}</template> + <MkCaptcha v-model="captchaResult" provider="testcaptcha" :sitekey="null"/> + </FormSlot> + </template> + </div> + </MkFolder> +</SearchMarker> </template> <script lang="ts" setup> import { computed, defineAsyncComponent, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; +import type { ApiWithDialogCustomErrors } from '@/os.js'; import MkRadios from '@/components/MkRadios.vue'; import MkInput from '@/components/MkInput.vue'; import FormSlot from '@/components/form/slot.vue'; @@ -167,7 +170,6 @@ import { useForm } from '@/composables/use-form.js'; import MkFormFooter from '@/components/MkFormFooter.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkInfo from '@/components/MkInfo.vue'; -import type { ApiWithDialogCustomErrors } from '@/os.js'; const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue')); diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index 19258216f6..dc6495e06d 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -6,89 +6,117 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <FormSuspense :p="init"> + <SearchMarker path="/admin/branding" :label="i18n.ts.branding" :keywords="['branding']" icon="ti ti-paint"> <div class="_gaps_m"> - <MkInput v-model="iconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }}</template> - </MkInput> + <SearchMarker :keywords="['icon', 'image']"> + <MkInput v-model="iconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkInput v-model="app192IconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template> - <template #caption> - <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> - <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> - <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> - <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div> - </template> - </MkInput> + <SearchMarker :keywords="['icon', 'image']"> + <MkInput v-model="app192IconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</SearchLabel></template> + <template #caption> + <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> + <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> + <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> + <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '192x192px' }) }}</strong></div> + </template> + </MkInput> + </SearchMarker> - <MkInput v-model="app512IconUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template> - <template #caption> - <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> - <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> - <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> - <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div> - </template> - </MkInput> + <SearchMarker :keywords="['icon', 'image']"> + <MkInput v-model="app512IconUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</SearchLabel></template> + <template #caption> + <div>{{ i18n.tsx._serverSettings.appIconDescription({ host: instance.name ?? host }) }}</div> + <div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div> + <div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div> + <div><strong>{{ i18n.tsx._serverSettings.appIconResolutionMustBe({ resolution: '512x512px' }) }}</strong></div> + </template> + </MkInput> + </SearchMarker> - <MkInput v-model="bannerUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.bannerUrl }}</template> - </MkInput> + <SearchMarker :keywords="['banner', 'image']"> + <MkInput v-model="bannerUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts.bannerUrl }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkInput v-model="backgroundImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.backgroundImageUrl }}</template> - </MkInput> + <SearchMarker :keywords="['background', 'image']"> + <MkInput v-model="backgroundImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts.backgroundImageUrl }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkInput v-model="notFoundImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.notFoundDescription }}</template> - </MkInput> + <SearchMarker :keywords="['image']"> + <MkInput v-model="notFoundImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts.notFoundDescription }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkInput v-model="infoImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.nothing }}</template> - </MkInput> + <SearchMarker :keywords="['image']"> + <MkInput v-model="infoImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts.nothing }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkInput v-model="serverErrorImageUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.somethingHappened }}</template> - </MkInput> + <SearchMarker :keywords="['image']"> + <MkInput v-model="serverErrorImageUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts.somethingHappened }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkColorInput v-model="themeColor"> - <template #label>{{ i18n.ts.themeColor }}</template> - </MkColorInput> + <SearchMarker :keywords="['theme', 'color']"> + <MkColorInput v-model="themeColor"> + <template #label><SearchLabel>{{ i18n.ts.themeColor }}</SearchLabel></template> + </MkColorInput> + </SearchMarker> - <MkTextarea v-model="defaultLightTheme"> - <template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template> - <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> - </MkTextarea> + <SearchMarker :keywords="['theme', 'default', 'light']"> + <MkTextarea v-model="defaultLightTheme"> + <template #label><SearchLabel>{{ i18n.ts.instanceDefaultLightTheme }}</SearchLabel></template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> + </MkTextarea> + </SearchMarker> - <MkTextarea v-model="defaultDarkTheme"> - <template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template> - <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> - </MkTextarea> + <SearchMarker :keywords="['theme', 'default', 'dark']"> + <MkTextarea v-model="defaultDarkTheme"> + <template #label><SearchLabel>{{ i18n.ts.instanceDefaultDarkTheme }}</SearchLabel></template> + <template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template> + </MkTextarea> + </SearchMarker> - <MkInput v-model="repositoryUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.repositoryUrl }}</template> - </MkInput> + <SearchMarker> + <MkInput v-model="repositoryUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkInput v-model="feedbackUrl" type="url"> - <template #prefix><i class="ti ti-link"></i></template> - <template #label>{{ i18n.ts.feedbackUrl }}</template> - </MkInput> + <SearchMarker> + <MkInput v-model="feedbackUrl" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label><SearchLabel>{{ i18n.ts.feedbackUrl }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkTextarea v-model="manifestJsonOverride"> - <template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template> - </MkTextarea> + <SearchMarker> + <MkTextarea v-model="manifestJsonOverride"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.manifestJsonOverride }}</SearchLabel></template> + </MkTextarea> + </SearchMarker> </div> - </FormSuspense> + </SearchMarker> </div> <template #footer> <div :class="$style.footer"> @@ -106,7 +134,6 @@ import JSON5 from 'json5'; import { host } from '@@/js/config.js'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; -import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { instance, fetchInstance } from '@/instance.js'; @@ -115,38 +142,22 @@ import { definePage } from '@/page.js'; import MkButton from '@/components/MkButton.vue'; import MkColorInput from '@/components/MkColorInput.vue'; -const iconUrl = ref<string | null>(null); -const app192IconUrl = ref<string | null>(null); -const app512IconUrl = ref<string | null>(null); -const bannerUrl = ref<string | null>(null); -const backgroundImageUrl = ref<string | null>(null); -const themeColor = ref<string | null>(null); -const defaultLightTheme = ref<string | null>(null); -const defaultDarkTheme = ref<string | null>(null); -const serverErrorImageUrl = ref<string | null>(null); -const infoImageUrl = ref<string | null>(null); -const notFoundImageUrl = ref<string | null>(null); -const repositoryUrl = ref<string | null>(null); -const feedbackUrl = ref<string | null>(null); -const manifestJsonOverride = ref<string>('{}'); +const meta = await misskeyApi('admin/meta'); -async function init() { - const meta = await misskeyApi('admin/meta'); - iconUrl.value = meta.iconUrl; - app192IconUrl.value = meta.app192IconUrl; - app512IconUrl.value = meta.app512IconUrl; - bannerUrl.value = meta.bannerUrl; - backgroundImageUrl.value = meta.backgroundImageUrl; - themeColor.value = meta.themeColor; - defaultLightTheme.value = meta.defaultLightTheme; - defaultDarkTheme.value = meta.defaultDarkTheme; - serverErrorImageUrl.value = meta.serverErrorImageUrl; - infoImageUrl.value = meta.infoImageUrl; - notFoundImageUrl.value = meta.notFoundImageUrl; - repositoryUrl.value = meta.repositoryUrl; - feedbackUrl.value = meta.feedbackUrl; - manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'); -} +const iconUrl = ref(meta.iconUrl); +const app192IconUrl = ref(meta.app192IconUrl); +const app512IconUrl = ref(meta.app512IconUrl); +const bannerUrl = ref(meta.bannerUrl); +const backgroundImageUrl = ref(meta.backgroundImageUrl); +const themeColor = ref(meta.themeColor); +const defaultLightTheme = ref(meta.defaultLightTheme); +const defaultDarkTheme = ref(meta.defaultDarkTheme); +const serverErrorImageUrl = ref(meta.serverErrorImageUrl); +const infoImageUrl = ref(meta.infoImageUrl); +const notFoundImageUrl = ref(meta.notFoundImageUrl); +const repositoryUrl = ref(meta.repositoryUrl); +const feedbackUrl = ref(meta.feedbackUrl); +const manifestJsonOverride = ref(meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t')); function save() { os.apiWithDialog('admin/update-meta', { diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index 17f2f8b593..8eb403f94c 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -6,48 +6,67 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <FormSuspense :p="init"> + <SearchMarker path="/admin/email-settings" :label="i18n.ts.emailServer" :keywords="['email']" icon="ti ti-mail"> <div class="_gaps_m"> - <MkSwitch v-model="enableEmail"> - <template #label>{{ i18n.ts.enableEmail }} ({{ i18n.ts.recommended }})</template> - <template #caption>{{ i18n.ts.emailConfigInfo }}</template> - </MkSwitch> + <SearchMarker> + <MkSwitch v-model="enableEmail"> + <template #label><SearchLabel>{{ i18n.ts.enableEmail }}</SearchLabel> ({{ i18n.ts.recommended }})</template> + <template #caption><SearchText>{{ i18n.ts.emailConfigInfo }}</SearchText></template> + </MkSwitch> + </SearchMarker> <template v-if="enableEmail"> - <MkInput v-model="email" type="email"> - <template #label>{{ i18n.ts.emailAddress }}</template> - </MkInput> + <SearchMarker> + <MkInput v-model="email" type="email"> + <template #label><SearchLabel>{{ i18n.ts.emailAddress }}</SearchLabel></template> + </MkInput> + </SearchMarker> - <FormSection> - <template #label>{{ i18n.ts.smtpConfig }}</template> + <SearchMarker> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.smtpConfig }}</SearchLabel></template> - <div class="_gaps_m"> - <FormSplit :minWidth="280"> - <MkInput v-model="smtpHost"> - <template #label>{{ i18n.ts.smtpHost }}</template> - </MkInput> - <MkInput v-model="smtpPort" type="number"> - <template #label>{{ i18n.ts.smtpPort }}</template> - </MkInput> - </FormSplit> - <FormSplit :minWidth="280"> - <MkInput v-model="smtpUser"> - <template #label>{{ i18n.ts.smtpUser }}</template> - </MkInput> - <MkInput v-model="smtpPass" type="password"> - <template #label>{{ i18n.ts.smtpPass }}</template> - </MkInput> - </FormSplit> - <FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo> - <MkSwitch v-model="smtpSecure"> - <template #label>{{ i18n.ts.smtpSecure }}</template> - <template #caption>{{ i18n.ts.smtpSecureInfo }}</template> - </MkSwitch> - </div> - </FormSection> + <div class="_gaps_m"> + <FormSplit :minWidth="280"> + <SearchMarker> + <MkInput v-model="smtpHost"> + <template #label><SearchLabel>{{ i18n.ts.smtpHost }}</SearchLabel></template> + </MkInput> + </SearchMarker> + <SearchMarker> + <MkInput v-model="smtpPort" type="number"> + <template #label><SearchLabel>{{ i18n.ts.smtpPort }}</SearchLabel></template> + </MkInput> + </SearchMarker> + </FormSplit> + + <FormSplit :minWidth="280"> + <SearchMarker> + <MkInput v-model="smtpUser"> + <template #label><SearchLabel>{{ i18n.ts.smtpUser }}</SearchLabel></template> + </MkInput> + </SearchMarker> + <SearchMarker> + <MkInput v-model="smtpPass" type="password"> + <template #label><SearchLabel>{{ i18n.ts.smtpPass }}</SearchLabel></template> + </MkInput> + </SearchMarker> + </FormSplit> + + <FormInfo>{{ i18n.ts.emptyToDisableSmtpAuth }}</FormInfo> + + <SearchMarker> + <MkSwitch v-model="smtpSecure"> + <template #label><SearchLabel>{{ i18n.ts.smtpSecure }}</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts.smtpSecureInfo }}</SearchText></template> + </MkSwitch> + </SearchMarker> + </div> + </FormSection> + </SearchMarker> </template> </div> - </FormSuspense> + </SearchMarker> </div> <template #footer> <div :class="$style.footer"> @@ -67,7 +86,6 @@ import { ref, computed } from 'vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import FormInfo from '@/components/MkInfo.vue'; -import FormSuspense from '@/components/form/suspense.vue'; import FormSplit from '@/components/form/split.vue'; import FormSection from '@/components/form/section.vue'; import * as os from '@/os.js'; @@ -77,24 +95,15 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import MkButton from '@/components/MkButton.vue'; -const enableEmail = ref<boolean>(false); -const email = ref<string | null>(null); -const smtpSecure = ref<boolean>(false); -const smtpHost = ref<string>(''); -const smtpPort = ref<number>(0); -const smtpUser = ref<string>(''); -const smtpPass = ref<string>(''); +const meta = await misskeyApi('admin/meta'); -async function init() { - const meta = await misskeyApi('admin/meta'); - enableEmail.value = meta.enableEmail; - email.value = meta.email; - smtpSecure.value = meta.smtpSecure; - smtpHost.value = meta.smtpHost; - smtpPort.value = meta.smtpPort; - smtpUser.value = meta.smtpUser; - smtpPass.value = meta.smtpPass; -} +const enableEmail = ref(meta.enableEmail); +const email = ref(meta.email); +const smtpSecure = ref(meta.smtpSecure); +const smtpHost = ref(meta.smtpHost); +const smtpPort = ref(meta.smtpPort); +const smtpUser = ref(meta.smtpUser); +const smtpPass = ref(meta.smtpPass); async function testEmail() { const { canceled, result: destination } = await os.inputText({ diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index 845fb12c5d..3a4eb8c3c7 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -6,36 +6,49 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <FormSuspense :p="init"> + <SearchMarker path="/admin/external-services" :label="i18n.ts.externalServices" :keywords="['external', 'services', 'thirdparty']" icon="ti ti-link"> <div class="_gaps_m"> - <MkFolder> - <template #label>Google Analytics<span class="_beta">{{ i18n.ts.beta }}</span></template> + <SearchMarker v-slot="slotProps"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #label><SearchLabel>Google Analytics</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> - <div class="_gaps_m"> - <MkInput v-model="googleAnalyticsMeasurementId"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Measurement ID</template> - </MkInput> - <MkButton primary @click="save_googleAnalytics">Save</MkButton> - </div> - </MkFolder> + <div class="_gaps_m"> + <SearchMarker> + <MkInput v-model="googleAnalyticsMeasurementId"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label><SearchLabel>Measurement ID</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkFolder> - <template #label>DeepL Translation</template> + <MkButton primary @click="save_googleAnalytics">Save</MkButton> + </div> + </MkFolder> + </SearchMarker> - <div class="_gaps_m"> - <MkInput v-model="deeplAuthKey"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>DeepL Auth Key</template> - </MkInput> - <MkSwitch v-model="deeplIsPro"> - <template #label>Pro account</template> - </MkSwitch> - <MkButton primary @click="save_deepl">Save</MkButton> - </div> - </MkFolder> + <SearchMarker v-slot="slotProps"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #label><SearchLabel>DeepL Translation</SearchLabel></template> + + <div class="_gaps_m"> + <SearchMarker> + <MkInput v-model="deeplAuthKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label><SearchLabel>Auth Key</SearchLabel></template> + </MkInput> + </SearchMarker> + + <SearchMarker> + <MkSwitch v-model="deeplIsPro"> + <template #label><SearchLabel>Pro account</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <MkButton primary @click="save_deepl">Save</MkButton> + </div> + </MkFolder> + </SearchMarker> </div> - </FormSuspense> + </SearchMarker> </div> </PageWithHeader> </template> @@ -45,7 +58,6 @@ import { ref, computed } from 'vue'; import MkInput from '@/components/MkInput.vue'; import MkButton from '@/components/MkButton.vue'; import MkSwitch from '@/components/MkSwitch.vue'; -import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { fetchInstance } from '@/instance.js'; @@ -53,17 +65,11 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import MkFolder from '@/components/MkFolder.vue'; -const deeplAuthKey = ref<string>(''); -const deeplIsPro = ref<boolean>(false); +const meta = await misskeyApi('admin/meta'); -const googleAnalyticsMeasurementId = ref<string>(''); - -async function init() { - const meta = await misskeyApi('admin/meta'); - deeplAuthKey.value = meta.deeplAuthKey ?? ''; - deeplIsPro.value = meta.deeplIsPro; - googleAnalyticsMeasurementId.value = meta.googleAnalyticsMeasurementId ?? ''; -} +const deeplAuthKey = ref(meta.deeplAuthKey ?? ''); +const deeplIsPro = ref(meta.deeplIsPro); +const googleAnalyticsMeasurementId = ref(meta.googleAnalyticsMeasurementId ?? ''); function save_deepl() { os.apiWithDialog('admin/update-meta', { diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index a87028f008..94994dc94c 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo v-if="noEmailServer" warn>{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo> </div> - <MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu> + <MkSuperMenu :def="menuDef" :searchIndex="searchIndex" :grid="narrow"></MkSuperMenu> </div> </div> </div> @@ -44,6 +44,9 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import { lookupUser, lookupUserByEmail, lookupFile } from '@/utility/admin-lookup.js'; import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js'; import { useRouter } from '@/router.js'; +import { genSearchIndexes } from '@/utility/inapp-search.js'; + +const searchIndex = await import('search-index:admin').then(({ searchIndexes }) => genSearchIndexes(searchIndexes)); const isEmpty = (x: string | null) => x == null || x === ''; @@ -324,12 +327,6 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); definePage(() => INFO.value); - -defineExpose({ - header: { - title: i18n.ts.controlPanel, - }, -}); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index 819f229c10..83438bb1d9 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -6,140 +6,162 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <FormSuspense :p="init"> + <SearchMarker path="/admin/moderation" :label="i18n.ts.moderation" :keywords="['moderation']" icon="ti ti-shield" :inlining="['serverRules']"> <div class="_gaps_m"> - <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration"> - <template #label>{{ i18n.ts._serverSettings.openRegistration }}</template> - <template #caption> - <div>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</div> - <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.openRegistrationWarning }}</div> - </template> - </MkSwitch> + <SearchMarker :keywords="['open', 'registration']"> + <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.openRegistration }}</SearchLabel></template> + <template #caption> + <div><SearchText>{{ i18n.ts._serverSettings.thisSettingWillAutomaticallyOffWhenModeratorsInactive }}</SearchText></div> + <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> <SearchText>{{ i18n.ts._serverSettings.openRegistrationWarning }}</SearchText></div> + </template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup"> - <template #label>{{ i18n.ts.emailRequiredForSignup }} ({{ i18n.ts.recommended }})</template> - </MkSwitch> + <SearchMarker :keywords="['email', 'required', 'signup']"> + <MkSwitch v-model="emailRequiredForSignup" @change="onChange_emailRequiredForSignup"> + <template #label><SearchLabel>{{ i18n.ts.emailRequiredForSignup }}</SearchLabel> ({{ i18n.ts.recommended }})</template> + </MkSwitch> + </SearchMarker> - <MkSelect v-model="ugcVisibilityForVisitor" @update:modelValue="onChange_ugcVisibilityForVisitor"> - <template #label>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</template> - <option value="all">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all }}</option> - <option value="local">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly }} ({{ i18n.ts.recommended }})</option> - <option value="none">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none }}</option> - <template #caption> - <div>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</div> - <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description2 }}</div> - </template> - </MkSelect> + <SearchMarker :keywords="['ugc', 'content', 'visibility', 'visitor', 'guest']"> + <MkSelect v-model="ugcVisibilityForVisitor" @update:modelValue="onChange_ugcVisibilityForVisitor"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor }}</SearchLabel></template> + <option value="all">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.all }}</option> + <option value="local">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.localOnly }} ({{ i18n.ts.recommended }})</option> + <option value="none">{{ i18n.ts._serverSettings._userGeneratedContentsVisibilityForVisitor.none }}</option> + <template #caption> + <div><SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description }}</SearchText></div> + <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> <SearchText>{{ i18n.ts._serverSettings.userGeneratedContentsVisibilityForVisitor_description2 }}</SearchText></div> + </template> + </MkSelect> + </SearchMarker> - <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink> + <XServerRules/> - <MkFolder> - <template #icon><i class="ti ti-lock-star"></i></template> - <template #label>{{ i18n.ts.preservedUsernames }}</template> + <SearchMarker :keywords="['preserved', 'usernames']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-lock-star"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.preservedUsernames }}</SearchLabel></template> - <div class="_gaps"> - <MkTextarea v-model="preservedUsernames"> - <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="preservedUsernames"> + <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_preservedUsernames">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-message-exclamation"></i></template> - <template #label>{{ i18n.ts.sensitiveWords }}</template> + <SearchMarker :keywords="['sensitive', 'words']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-message-exclamation"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.sensitiveWords }}</SearchLabel></template> - <div class="_gaps"> - <MkTextarea v-model="sensitiveWords"> - <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="sensitiveWords"> + <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_sensitiveWords">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-message-x"></i></template> - <template #label>{{ i18n.ts.prohibitedWords }}</template> + <SearchMarker :keywords="['prohibited', 'words']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-message-x"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.prohibitedWords }}</SearchLabel></template> - <div class="_gaps"> - <MkTextarea v-model="prohibitedWords"> - <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="prohibitedWords"> + <template #caption>{{ i18n.ts.prohibitedWordsDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_prohibitedWords">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-user-x"></i></template> - <template #label>{{ i18n.ts.prohibitedWordsForNameOfUser }}</template> + <SearchMarker :keywords="['prohibited', 'name', 'user']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-user-x"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.prohibitedWordsForNameOfUser }}</SearchLabel></template> - <div class="_gaps"> - <MkTextarea v-model="prohibitedWordsForNameOfUser"> - <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> - </MkTextarea> - <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="prohibitedWordsForNameOfUser"> + <template #caption>{{ i18n.ts.prohibitedWordsForNameOfUserDescription }}<br>{{ i18n.ts.prohibitedWordsDescription2 }}</template> + </MkTextarea> + <MkButton primary @click="save_prohibitedWordsForNameOfUser">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.hiddenTags }}</template> + <SearchMarker :keywords="['hidden', 'tags', 'hashtags']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.hiddenTags }}</SearchLabel></template> - <div class="_gaps"> - <MkTextarea v-model="hiddenTags"> - <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="hiddenTags"> + <template #caption>{{ i18n.ts.hiddenTagsDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_hiddenTags">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.silencedInstances }}</template> + <SearchMarker :keywords="['silenced', 'servers', 'hosts']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.silencedInstances }}</SearchLabel></template> - <div class="_gaps"> - <MkTextarea v-model="silencedHosts"> - <template #caption>{{ i18n.ts.silencedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="silencedHosts"> + <template #caption>{{ i18n.ts.silencedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_silencedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.mediaSilencedInstances }}</template> + <SearchMarker :keywords="['media', 'silenced', 'servers', 'hosts']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.mediaSilencedInstances }}</SearchLabel></template> - <div class="_gaps"> - <MkTextarea v-model="mediaSilencedHosts"> - <template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="mediaSilencedHosts"> + <template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_mediaSilencedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-ban"></i></template> - <template #label>{{ i18n.ts.blockedInstances }}</template> + <SearchMarker :keywords="['blocked', 'servers', 'hosts']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-ban"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.blockedInstances }}</SearchLabel></template> - <div class="_gaps"> - <MkTextarea v-model="blockedHosts"> - <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> - </MkTextarea> - <MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton> - </div> - </MkFolder> + <div class="_gaps"> + <MkTextarea v-model="blockedHosts"> + <template #caption>{{ i18n.ts.blockedInstancesDescription }}</template> + </MkTextarea> + <MkButton primary @click="save_blockedHosts">{{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + </SearchMarker> </div> - </FormSuspense> + </SearchMarker> </div> </PageWithHeader> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; +import XServerRules from './server-rules.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; -import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { fetchInstance } from '@/instance.js'; @@ -150,32 +172,19 @@ import FormLink from '@/components/form/link.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSelect from '@/components/MkSelect.vue'; -const enableRegistration = ref<boolean>(false); -const emailRequiredForSignup = ref<boolean>(false); -const ugcVisibilityForVisitor = ref<string>('all'); -const sensitiveWords = ref<string>(''); -const prohibitedWords = ref<string>(''); -const prohibitedWordsForNameOfUser = ref<string>(''); -const hiddenTags = ref<string>(''); -const preservedUsernames = ref<string>(''); -const blockedHosts = ref<string>(''); -const silencedHosts = ref<string>(''); -const mediaSilencedHosts = ref<string>(''); +const meta = await misskeyApi('admin/meta'); -async function init() { - const meta = await misskeyApi('admin/meta'); - enableRegistration.value = !meta.disableRegistration; - emailRequiredForSignup.value = meta.emailRequiredForSignup; - ugcVisibilityForVisitor.value = meta.ugcVisibilityForVisitor; - sensitiveWords.value = meta.sensitiveWords.join('\n'); - prohibitedWords.value = meta.prohibitedWords.join('\n'); - prohibitedWordsForNameOfUser.value = meta.prohibitedWordsForNameOfUser.join('\n'); - hiddenTags.value = meta.hiddenTags.join('\n'); - preservedUsernames.value = meta.preservedUsernames.join('\n'); - blockedHosts.value = meta.blockedHosts.join('\n'); - silencedHosts.value = meta.silencedHosts?.join('\n') ?? ''; - mediaSilencedHosts.value = meta.mediaSilencedHosts.join('\n'); -} +const enableRegistration = ref(!meta.disableRegistration); +const emailRequiredForSignup = ref(meta.emailRequiredForSignup); +const ugcVisibilityForVisitor = ref(meta.ugcVisibilityForVisitor); +const sensitiveWords = ref(meta.sensitiveWords.join('\n')); +const prohibitedWords = ref(meta.prohibitedWords.join('\n')); +const prohibitedWordsForNameOfUser = ref(meta.prohibitedWordsForNameOfUser.join('\n')); +const hiddenTags = ref(meta.hiddenTags.join('\n')); +const preservedUsernames = ref(meta.preservedUsernames.join('\n')); +const blockedHosts = ref(meta.blockedHosts.join('\n')); +const silencedHosts = ref(meta.silencedHosts?.join('\n') ?? ''); +const mediaSilencedHosts = ref(meta.mediaSilencedHosts.join('\n')); async function onChange_enableRegistration(value: boolean) { if (value) { diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index 7a46ae41c6..d42c23a51e 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -6,70 +6,94 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <FormSuspense :p="init"> + <SearchMarker path="/admin/object-storage" :label="i18n.ts.objectStorage" :keywords="['objectStorage']" icon="ti ti-cloud"> <div class="_gaps_m"> - <MkSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</MkSwitch> + <SearchMarker> + <MkSwitch v-model="useObjectStorage"><SearchLabel>{{ i18n.ts.useObjectStorage }}</SearchLabel></MkSwitch> + </SearchMarker> <template v-if="useObjectStorage"> - <MkInput v-model="objectStorageBaseUrl" :placeholder="'https://example.com'" type="url"> - <template #label>{{ i18n.ts.objectStorageBaseUrl }}</template> - <template #caption>{{ i18n.ts.objectStorageBaseUrlDesc }}</template> - </MkInput> + <SearchMarker> + <MkInput v-model="objectStorageBaseUrl" :placeholder="'https://example.com'" type="url"> + <template #label><SearchLabel>{{ i18n.ts.objectStorageBaseUrl }}</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts.objectStorageBaseUrlDesc }}</SearchText></template> + </MkInput> + </SearchMarker> - <MkInput v-model="objectStorageBucket"> - <template #label>{{ i18n.ts.objectStorageBucket }}</template> - <template #caption>{{ i18n.ts.objectStorageBucketDesc }}</template> - </MkInput> + <SearchMarker> + <MkInput v-model="objectStorageBucket"> + <template #label><SearchLabel>{{ i18n.ts.objectStorageBucket }}</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts.objectStorageBucketDesc }}</SearchText></template> + </MkInput> + </SearchMarker> - <MkInput v-model="objectStoragePrefix"> - <template #label>{{ i18n.ts.objectStoragePrefix }}</template> - <template #caption>{{ i18n.ts.objectStoragePrefixDesc }}</template> - </MkInput> + <SearchMarker> + <MkInput v-model="objectStoragePrefix"> + <template #label><SearchLabel>{{ i18n.ts.objectStoragePrefix }}</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts.objectStoragePrefixDesc }}</SearchText></template> + </MkInput> + </SearchMarker> - <MkInput v-model="objectStorageEndpoint" :placeholder="'example.com'"> - <template #label>{{ i18n.ts.objectStorageEndpoint }}</template> - <template #prefix>https://</template> - <template #caption>{{ i18n.ts.objectStorageEndpointDesc }}</template> - </MkInput> + <SearchMarker> + <MkInput v-model="objectStorageEndpoint" :placeholder="'example.com'"> + <template #label><SearchLabel>{{ i18n.ts.objectStorageEndpoint }}</SearchLabel></template> + <template #prefix>https://</template> + <template #caption><SearchText>{{ i18n.ts.objectStorageEndpointDesc }}</SearchText></template> + </MkInput> + </SearchMarker> - <MkInput v-model="objectStorageRegion"> - <template #label>{{ i18n.ts.objectStorageRegion }}</template> - <template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template> - </MkInput> + <SearchMarker> + <MkInput v-model="objectStorageRegion"> + <template #label><SearchLabel>{{ i18n.ts.objectStorageRegion }}</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts.objectStorageRegionDesc }}</SearchText></template> + </MkInput> + </SearchMarker> <FormSplit :minWidth="280"> - <MkInput v-model="objectStorageAccessKey"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Access key</template> - </MkInput> + <SearchMarker> + <MkInput v-model="objectStorageAccessKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label><SearchLabel>Access key</SearchLabel></template> + </MkInput> + </SearchMarker> - <MkInput v-model="objectStorageSecretKey" type="password"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Secret key</template> - </MkInput> + <SearchMarker> + <MkInput v-model="objectStorageSecretKey" type="password"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label><SearchLabel>Secret key</SearchLabel></template> + </MkInput> + </SearchMarker> </FormSplit> - <MkSwitch v-model="objectStorageUseSSL"> - <template #label>{{ i18n.ts.objectStorageUseSSL }}</template> - <template #caption>{{ i18n.ts.objectStorageUseSSLDesc }}</template> - </MkSwitch> + <SearchMarker> + <MkSwitch v-model="objectStorageUseSSL"> + <template #label><SearchLabel>{{ i18n.ts.objectStorageUseSSL }}</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts.objectStorageUseSSLDesc }}</SearchText></template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="objectStorageUseProxy"> - <template #label>{{ i18n.ts.objectStorageUseProxy }}</template> - <template #caption>{{ i18n.ts.objectStorageUseProxyDesc }}</template> - </MkSwitch> + <SearchMarker> + <MkSwitch v-model="objectStorageUseProxy"> + <template #label><SearchLabel>{{ i18n.ts.objectStorageUseProxy }}</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts.objectStorageUseProxyDesc }}</SearchText></template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="objectStorageSetPublicRead"> - <template #label>{{ i18n.ts.objectStorageSetPublicRead }}</template> - </MkSwitch> + <SearchMarker> + <MkSwitch v-model="objectStorageSetPublicRead"> + <template #label><SearchLabel>{{ i18n.ts.objectStorageSetPublicRead }}</SearchLabel></template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="objectStorageS3ForcePathStyle"> - <template #label>s3ForcePathStyle</template> - <template #caption>{{ i18n.ts.s3ForcePathStyleDesc }}</template> - </MkSwitch> + <SearchMarker> + <MkSwitch v-model="objectStorageS3ForcePathStyle"> + <template #label><SearchLabel>s3ForcePathStyle</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts.s3ForcePathStyleDesc }}</SearchText></template> + </MkSwitch> + </SearchMarker> </template> </div> - </FormSuspense> + </SearchMarker> </div> <template #footer> <div :class="$style.footer"> @@ -94,36 +118,21 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import MkButton from '@/components/MkButton.vue'; -const useObjectStorage = ref<boolean>(false); -const objectStorageBaseUrl = ref<string | null>(null); -const objectStorageBucket = ref<string | null>(null); -const objectStoragePrefix = ref<string | null>(null); -const objectStorageEndpoint = ref<string | null>(null); -const objectStorageRegion = ref<string | null>(null); -const objectStoragePort = ref<number | null>(null); -const objectStorageAccessKey = ref<string | null>(null); -const objectStorageSecretKey = ref<string | null>(null); -const objectStorageUseSSL = ref<boolean>(false); -const objectStorageUseProxy = ref<boolean>(false); -const objectStorageSetPublicRead = ref<boolean>(false); -const objectStorageS3ForcePathStyle = ref<boolean>(true); +const meta = await misskeyApi('admin/meta'); -async function init() { - const meta = await misskeyApi('admin/meta'); - useObjectStorage.value = meta.useObjectStorage; - objectStorageBaseUrl.value = meta.objectStorageBaseUrl; - objectStorageBucket.value = meta.objectStorageBucket; - objectStoragePrefix.value = meta.objectStoragePrefix; - objectStorageEndpoint.value = meta.objectStorageEndpoint; - objectStorageRegion.value = meta.objectStorageRegion; - objectStoragePort.value = meta.objectStoragePort; - objectStorageAccessKey.value = meta.objectStorageAccessKey; - objectStorageSecretKey.value = meta.objectStorageSecretKey; - objectStorageUseSSL.value = meta.objectStorageUseSSL; - objectStorageUseProxy.value = meta.objectStorageUseProxy; - objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead; - objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle; -} +const useObjectStorage = ref(meta.useObjectStorage); +const objectStorageBaseUrl = ref(meta.objectStorageBaseUrl); +const objectStorageBucket = ref(meta.objectStorageBucket); +const objectStoragePrefix = ref(meta.objectStoragePrefix); +const objectStorageEndpoint = ref(meta.objectStorageEndpoint); +const objectStorageRegion = ref(meta.objectStorageRegion); +const objectStoragePort = ref(meta.objectStoragePort); +const objectStorageAccessKey = ref(meta.objectStorageAccessKey); +const objectStorageSecretKey = ref(meta.objectStorageSecretKey); +const objectStorageUseSSL = ref(meta.objectStorageUseSSL); +const objectStorageUseProxy = ref(meta.objectStorageUseProxy); +const objectStorageSetPublicRead = ref(meta.objectStorageSetPublicRead); +const objectStorageS3ForcePathStyle = ref(meta.objectStorageS3ForcePathStyle); function save() { os.apiWithDialog('admin/update-meta', { diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue index ff3a5b9d7f..e3021778e7 100644 --- a/packages/frontend/src/pages/admin/performance.vue +++ b/packages/frontend/src/pages/admin/performance.vue @@ -6,131 +6,163 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <div class="_gaps"> - <div class="_panel" style="padding: 16px;"> - <MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats"> - <template #label>{{ i18n.ts.enableServerMachineStats }}</template> - <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> - </MkSwitch> - </div> - - <div class="_panel" style="padding: 16px;"> - <MkSwitch v-model="enableIdenticonGeneration" @change="onChange_enableIdenticonGeneration"> - <template #label>{{ i18n.ts.enableIdenticonGeneration }}</template> - <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> - </MkSwitch> - </div> + <SearchMarker path="/admin/performance" :label="i18n.ts.performance" :keywords="['performance']" icon="ti ti-bolt"> + <div class="_gaps"> + <SearchMarker> + <div class="_panel" style="padding: 16px;"> + <MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats"> + <template #label><SearchLabel>{{ i18n.ts.enableServerMachineStats }}</SearchLabel></template> + <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> + </MkSwitch> + </div> + </SearchMarker> - <div class="_panel" style="padding: 16px;"> - <MkSwitch v-model="enableChartsForRemoteUser" @change="onChange_enableChartsForRemoteUser"> - <template #label>{{ i18n.ts.enableChartsForRemoteUser }}</template> - <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> - </MkSwitch> - </div> + <SearchMarker> + <div class="_panel" style="padding: 16px;"> + <MkSwitch v-model="enableIdenticonGeneration" @change="onChange_enableIdenticonGeneration"> + <template #label><SearchLabel>{{ i18n.ts.enableIdenticonGeneration }}</SearchLabel></template> + <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> + </MkSwitch> + </div> + </SearchMarker> - <div class="_panel" style="padding: 16px;"> - <MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances"> - <template #label>{{ i18n.ts.enableStatsForFederatedInstances }}</template> - <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> - </MkSwitch> - </div> + <SearchMarker> + <div class="_panel" style="padding: 16px;"> + <MkSwitch v-model="enableChartsForRemoteUser" @change="onChange_enableChartsForRemoteUser"> + <template #label><SearchLabel>{{ i18n.ts.enableChartsForRemoteUser }}</SearchLabel></template> + <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> + </MkSwitch> + </div> + </SearchMarker> - <div class="_panel" style="padding: 16px;"> - <MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances"> - <template #label>{{ i18n.ts.enableChartsForFederatedInstances }}</template> - <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> - </MkSwitch> - </div> + <SearchMarker> + <div class="_panel" style="padding: 16px;"> + <MkSwitch v-model="enableStatsForFederatedInstances" @change="onChange_enableStatsForFederatedInstances"> + <template #label><SearchLabel>{{ i18n.ts.enableStatsForFederatedInstances }}</SearchLabel></template> + <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> + </MkSwitch> + </div> + </SearchMarker> - <MkFolder :defaultOpen="true"> - <template #icon><i class="ti ti-bolt"></i></template> - <template #label>Misskey® Fan-out Timeline Technology™ (FTT)</template> - <template v-if="fttForm.savedState.enableFanoutTimeline" #suffix>Enabled</template> - <template v-else #suffix>Disabled</template> - <template v-if="fttForm.modified.value" #footer> - <MkFormFooter :form="fttForm"/> - </template> + <SearchMarker> + <div class="_panel" style="padding: 16px;"> + <MkSwitch v-model="enableChartsForFederatedInstances" @change="onChange_enableChartsForFederatedInstances"> + <template #label><SearchLabel>{{ i18n.ts.enableChartsForFederatedInstances }}</SearchLabel></template> + <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> + </MkSwitch> + </div> + </SearchMarker> - <div class="_gaps"> - <MkSwitch v-model="fttForm.state.enableFanoutTimeline"> - <template #label>{{ i18n.ts.enable }}<span v-if="fttForm.modifiedStates.enableFanoutTimeline" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption> - <div>{{ i18n.ts._serverSettings.fanoutTimelineDescription }}</div> - <div><MkLink target="_blank" url="https://misskey-hub.net/docs/for-admin/features/ftt/">{{ i18n.ts.details }}</MkLink></div> + <SearchMarker> + <MkFolder :defaultOpen="true"> + <template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template> + <template #label><SearchLabel>Misskey® Fan-out Timeline Technology™ (FTT)</SearchLabel></template> + <template v-if="fttForm.savedState.enableFanoutTimeline" #suffix>Enabled</template> + <template v-else #suffix>Disabled</template> + <template v-if="fttForm.modified.value" #footer> + <MkFormFooter :form="fttForm"/> </template> - </MkSwitch> - <template v-if="fttForm.state.enableFanoutTimeline"> - <MkSwitch v-model="fttForm.state.enableFanoutTimelineDbFallback"> - <template #label>{{ i18n.ts._serverSettings.fanoutTimelineDbFallback }}<span v-if="fttForm.modifiedStates.enableFanoutTimelineDbFallback" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.fanoutTimelineDbFallbackDescription }}</template> - </MkSwitch> + <div class="_gaps"> + <SearchMarker> + <MkSwitch v-model="fttForm.state.enableFanoutTimeline"> + <template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="fttForm.modifiedStates.enableFanoutTimeline" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption> + <div><SearchText>{{ i18n.ts._serverSettings.fanoutTimelineDescription }}</SearchText></div> + <div><MkLink target="_blank" url="https://misskey-hub.net/docs/for-admin/features/ftt/">{{ i18n.ts.details }}</MkLink></div> + </template> + </MkSwitch> + </SearchMarker> - <MkInput v-model="fttForm.state.perLocalUserUserTimelineCacheMax" type="number"> - <template #label>perLocalUserUserTimelineCacheMax<span v-if="fttForm.modifiedStates.perLocalUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> + <template v-if="fttForm.state.enableFanoutTimeline"> + <SearchMarker :keywords="['db', 'database', 'fallback']"> + <MkSwitch v-model="fttForm.state.enableFanoutTimelineDbFallback"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.fanoutTimelineDbFallback }}</SearchLabel><span v-if="fttForm.modifiedStates.enableFanoutTimelineDbFallback" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts._serverSettings.fanoutTimelineDbFallbackDescription }}</SearchText></template> + </MkSwitch> + </SearchMarker> - <MkInput v-model="fttForm.state.perRemoteUserUserTimelineCacheMax" type="number"> - <template #label>perRemoteUserUserTimelineCacheMax<span v-if="fttForm.modifiedStates.perRemoteUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> + <SearchMarker> + <MkInput v-model="fttForm.state.perLocalUserUserTimelineCacheMax" type="number"> + <template #label><SearchLabel>perLocalUserUserTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perLocalUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkInput> + </SearchMarker> - <MkInput v-model="fttForm.state.perUserHomeTimelineCacheMax" type="number"> - <template #label>perUserHomeTimelineCacheMax<span v-if="fttForm.modifiedStates.perUserHomeTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> + <SearchMarker> + <MkInput v-model="fttForm.state.perRemoteUserUserTimelineCacheMax" type="number"> + <template #label><SearchLabel>perRemoteUserUserTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perRemoteUserUserTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkInput> + </SearchMarker> - <MkInput v-model="fttForm.state.perUserListTimelineCacheMax" type="number"> - <template #label>perUserListTimelineCacheMax<span v-if="fttForm.modifiedStates.perUserListTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> - </template> - </div> - </MkFolder> + <SearchMarker> + <MkInput v-model="fttForm.state.perUserHomeTimelineCacheMax" type="number"> + <template #label><SearchLabel>perUserHomeTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perUserHomeTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkInput> + </SearchMarker> - <MkFolder :defaultOpen="true"> - <template #icon><i class="ti ti-bolt"></i></template> - <template #label>Misskey® Reactions Boost Technology™ (RBT)<span class="_beta">{{ i18n.ts.beta }}</span></template> - <template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template> - <template v-else #suffix>Disabled</template> - <template v-if="rbtForm.modified.value" #footer> - <MkFormFooter :form="rbtForm"/> - </template> + <SearchMarker> + <MkInput v-model="fttForm.state.perUserListTimelineCacheMax" type="number"> + <template #label><SearchLabel>perUserListTimelineCacheMax</SearchLabel><span v-if="fttForm.modifiedStates.perUserListTimelineCacheMax" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkInput> + </SearchMarker> + </template> + </div> + </MkFolder> + </SearchMarker> - <div class="_gaps_m"> - <MkSwitch v-model="rbtForm.state.enableReactionsBuffering"> - <template #label>{{ i18n.ts.enable }}<span v-if="rbtForm.modifiedStates.enableReactionsBuffering" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.reactionsBufferingDescription }}</template> - </MkSwitch> - </div> - </MkFolder> + <SearchMarker> + <MkFolder :defaultOpen="true"> + <template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template> + <template #label><SearchLabel>Misskey® Reactions Boost Technology™ (RBT)</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> + <template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template> + <template v-else #suffix>Disabled</template> + <template v-if="rbtForm.modified.value" #footer> + <MkFormFooter :form="rbtForm"/> + </template> + + <div class="_gaps_m"> + <SearchMarker> + <MkSwitch v-model="rbtForm.state.enableReactionsBuffering"> + <template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="rbtForm.modifiedStates.enableReactionsBuffering" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts._serverSettings.reactionsBufferingDescription }}</SearchText></template> + </MkSwitch> + </SearchMarker> + </div> + </MkFolder> + </SearchMarker> - <MkFolder :defaultOpen="true"> - <template #icon><i class="ti ti-recycle"></i></template> - <template #label>Remote Notes Cleaning (仮)</template> - <template v-if="remoteNotesCleaningForm.savedState.enableRemoteNotesCleaning" #suffix>Enabled</template> - <template v-else #suffix>Disabled</template> - <template v-if="remoteNotesCleaningForm.modified.value" #footer> - <MkFormFooter :form="remoteNotesCleaningForm"/> - </template> + <SearchMarker> + <MkFolder :defaultOpen="true"> + <template #icon><SearchIcon><i class="ti ti-recycle"></i></SearchIcon></template> + <template #label><SearchLabel>Remote Notes Cleaning (仮)</SearchLabel></template> + <template v-if="remoteNotesCleaningForm.savedState.enableRemoteNotesCleaning" #suffix>Enabled</template> + <template v-else #suffix>Disabled</template> + <template v-if="remoteNotesCleaningForm.modified.value" #footer> + <MkFormFooter :form="remoteNotesCleaningForm"/> + </template> - <div class="_gaps_m"> - <MkSwitch v-model="remoteNotesCleaningForm.state.enableRemoteNotesCleaning"> - <template #label>{{ i18n.ts.enable }}<span v-if="remoteNotesCleaningForm.modifiedStates.enableRemoteNotesCleaning" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.remoteNotesCleaning_description }}</template> - </MkSwitch> + <div class="_gaps_m"> + <MkSwitch v-model="remoteNotesCleaningForm.state.enableRemoteNotesCleaning"> + <template #label><SearchLabel>{{ i18n.ts.enable }}</SearchLabel><span v-if="remoteNotesCleaningForm.modifiedStates.enableRemoteNotesCleaning" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts._serverSettings.remoteNotesCleaning_description }}</SearchText></template> + </MkSwitch> - <template v-if="remoteNotesCleaningForm.state.enableRemoteNotesCleaning"> - <MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningExpiryDaysForEachNotes" type="number"> - <template #label>{{ i18n.ts._serverSettings.remoteNotesCleaningExpiryDaysForEachNotes }} ({{ i18n.ts.inDays }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningExpiryDaysForEachNotes" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #suffix>{{ i18n.ts._time.day }}</template> - </MkInput> + <template v-if="remoteNotesCleaningForm.state.enableRemoteNotesCleaning"> + <MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningExpiryDaysForEachNotes" type="number"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.remoteNotesCleaningExpiryDaysForEachNotes }}</SearchLabel> ({{ i18n.ts.inDays }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningExpiryDaysForEachNotes" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #suffix>{{ i18n.ts._time.day }}</template> + </MkInput> - <MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningMaxProcessingDurationInMinutes" type="number"> - <template #label>{{ i18n.ts._serverSettings.remoteNotesCleaningMaxProcessingDuration }} ({{ i18n.ts.inMinutes }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningMaxProcessingDurationInMinutes" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #suffix>{{ i18n.ts._time.minute }}</template> - </MkInput> - </template> - </div> - </MkFolder> - </div> + <MkInput v-model="remoteNotesCleaningForm.state.remoteNotesCleaningMaxProcessingDurationInMinutes" type="number"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.remoteNotesCleaningMaxProcessingDuration }}</SearchLabel> ({{ i18n.ts.inMinutes }})<span v-if="remoteNotesCleaningForm.modifiedStates.remoteNotesCleaningMaxProcessingDurationInMinutes" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #suffix>{{ i18n.ts._time.minute }}</template> + </MkInput> + </template> + </div> + </MkFolder> + </SearchMarker> + </div> + </SearchMarker> </div> </PageWithHeader> </template> @@ -243,7 +275,7 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); definePage(() => ({ - title: i18n.ts.other, - icon: 'ti ti-adjustments', + title: i18n.ts.performance, + icon: 'ti ti-bolt', })); </script> diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue index aabf64342e..fca9776c0a 100644 --- a/packages/frontend/src/pages/admin/relays.vue +++ b/packages/frontend/src/pages/admin/relays.vue @@ -6,18 +6,20 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 800px;"> - <div class="_gaps"> - <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;"> - <div>{{ relay.inbox }}</div> - <div style="margin: 8px 0;"> - <i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i> - <i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i> - <i v-else class="ti ti-clock" :class="$style.icon"></i> - <span>{{ i18n.ts._relayStatus[relay.status] }}</span> + <SearchMarker path="/admin/relays" :label="i18n.ts.relays" :keywords="['relays']" icon="ti ti-planet"> + <div class="_gaps"> + <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;"> + <div>{{ relay.inbox }}</div> + <div style="margin: 8px 0;"> + <i v-if="relay.status === 'accepted'" class="ti ti-check" :class="$style.icon" style="color: var(--MI_THEME-success);"></i> + <i v-else-if="relay.status === 'rejected'" class="ti ti-ban" :class="$style.icon" style="color: var(--MI_THEME-error);"></i> + <i v-else class="ti ti-clock" :class="$style.icon"></i> + <span>{{ i18n.ts._relayStatus[relay.status] }}</span> + </div> + <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </div> - <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </div> - </div> + </SearchMarker> </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 9e907a4469..27e35c7e69 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -6,115 +6,153 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <div class="_gaps_m"> - <XBotProtection/> + <SearchMarker path="/admin/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['botProtection']"> + <div class="_gaps_m"> + <XBotProtection/> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.sensitiveMediaDetection }}</template> - <template v-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template> - <template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'local'" #suffix>{{ i18n.ts.localOnly }}</template> - <template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template> - <template v-else #suffix>{{ i18n.ts.none }}</template> - <template v-if="sensitiveMediaDetectionForm.modified.value" #footer> - <MkFormFooter :form="sensitiveMediaDetectionForm"/> - </template> + <SearchMarker v-slot="slotProps" :keywords="['sensitive', 'media', 'detection']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.sensitiveMediaDetection }}</SearchLabel></template> + <template v-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'all'" #suffix>{{ i18n.ts.all }}</template> + <template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'local'" #suffix>{{ i18n.ts.localOnly }}</template> + <template v-else-if="sensitiveMediaDetectionForm.savedState.sensitiveMediaDetection === 'remote'" #suffix>{{ i18n.ts.remoteOnly }}</template> + <template v-else #suffix>{{ i18n.ts.none }}</template> + <template v-if="sensitiveMediaDetectionForm.modified.value" #footer> + <MkFormFooter :form="sensitiveMediaDetectionForm"/> + </template> - <div class="_gaps_m"> - <span>{{ i18n.ts._sensitiveMediaDetection.description }}</span> + <div class="_gaps_m"> + <div><SearchText>{{ i18n.ts._sensitiveMediaDetection.description }}</SearchText></div> - <MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection"> - <option value="none">{{ i18n.ts.none }}</option> - <option value="all">{{ i18n.ts.all }}</option> - <option value="local">{{ i18n.ts.localOnly }}</option> - <option value="remote">{{ i18n.ts.remoteOnly }}</option> - </MkRadios> + <MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection"> + <option value="none">{{ i18n.ts.none }}</option> + <option value="all">{{ i18n.ts.all }}</option> + <option value="local">{{ i18n.ts.localOnly }}</option> + <option value="remote">{{ i18n.ts.remoteOnly }}</option> + </MkRadios> - <MkRange v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :textConverter="(v) => `${v + 1}`"> - <template #label>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</template> - <template #caption>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</template> - </MkRange> + <SearchMarker :keywords="['sensitivity']"> + <MkRange v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetectionSensitivity" :min="0" :max="4" :step="1" :textConverter="(v) => `${v + 1}`"> + <template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.sensitivity }}</SearchLabel></template> + <template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.sensitivityDescription }}</SearchText></template> + </MkRange> + </SearchMarker> - <MkSwitch v-model="sensitiveMediaDetectionForm.state.enableSensitiveMediaDetectionForVideos"> - <template #label>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}<span class="_beta">{{ i18n.ts.beta }}</span></template> - <template #caption>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</template> - </MkSwitch> + <SearchMarker :keywords="['video', 'analyze']"> + <MkSwitch v-model="sensitiveMediaDetectionForm.state.enableSensitiveMediaDetectionForVideos"> + <template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.analyzeVideos }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> + <template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.analyzeVideosDescription }}</SearchText></template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="sensitiveMediaDetectionForm.state.setSensitiveFlagAutomatically"> - <template #label>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }} ({{ i18n.ts.notRecommended }})</template> - <template #caption>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</template> - </MkSwitch> + <SearchMarker :keywords="['flag', 'automatically']"> + <MkSwitch v-model="sensitiveMediaDetectionForm.state.setSensitiveFlagAutomatically"> + <template #label><SearchLabel>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomatically }}</SearchLabel> ({{ i18n.ts.notRecommended }})</template> + <template #caption><SearchText>{{ i18n.ts._sensitiveMediaDetection.setSensitiveFlagAutomaticallyDescription }}</SearchText></template> + </MkSwitch> + </SearchMarker> - <!-- 現状 false positive が多すぎて実用に耐えない + <!-- 現状 false positive が多すぎて実用に耐えない <MkSwitch v-model="disallowUploadWhenPredictedAsPorn"> <template #label>{{ i18n.ts._sensitiveMediaDetection.disallowUploadWhenPredictedAsPorn }}</template> </MkSwitch> --> - </div> - </MkFolder> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #label>Active Email Validation</template> - <template v-if="emailValidationForm.savedState.enableActiveEmailValidation" #suffix>Enabled</template> - <template v-else #suffix>Disabled</template> - <template v-if="emailValidationForm.modified.value" #footer> - <MkFormFooter :form="emailValidationForm"/> - </template> + <SearchMarker v-slot="slotProps" :keywords="['email', 'validation']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #label><SearchLabel>Active Email Validation</SearchLabel></template> + <template v-if="emailValidationForm.savedState.enableActiveEmailValidation" #suffix>Enabled</template> + <template v-else #suffix>Disabled</template> + <template v-if="emailValidationForm.modified.value" #footer> + <MkFormFooter :form="emailValidationForm"/> + </template> - <div class="_gaps_m"> - <span>{{ i18n.ts.activeEmailValidationDescription }}</span> - <MkSwitch v-model="emailValidationForm.state.enableActiveEmailValidation"> - <template #label>Enable</template> - </MkSwitch> - <MkSwitch v-model="emailValidationForm.state.enableVerifymailApi"> - <template #label>Use Verifymail.io API</template> - </MkSwitch> - <MkInput v-model="emailValidationForm.state.verifymailAuthKey"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Verifymail.io API Auth Key</template> - </MkInput> - <MkSwitch v-model="emailValidationForm.state.enableTruemailApi"> - <template #label>Use TrueMail API</template> - </MkSwitch> - <MkInput v-model="emailValidationForm.state.truemailInstance"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>TrueMail API Instance</template> - </MkInput> - <MkInput v-model="emailValidationForm.state.truemailAuthKey"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>TrueMail API Auth Key</template> - </MkInput> - </div> - </MkFolder> + <div class="_gaps_m"> + <div><SearchText>{{ i18n.ts.activeEmailValidationDescription }}</SearchText></div> - <MkFolder> - <template #label>Banned Email Domains</template> - <template v-if="bannedEmailDomainsForm.modified.value" #footer> - <MkFormFooter :form="bannedEmailDomainsForm"/> - </template> + <SearchMarker> + <MkSwitch v-model="emailValidationForm.state.enableActiveEmailValidation"> + <template #label><SearchLabel>Enable</SearchLabel></template> + </MkSwitch> + </SearchMarker> - <div class="_gaps_m"> - <MkTextarea v-model="bannedEmailDomainsForm.state.bannedEmailDomains"> - <template #label>Banned Email Domains List</template> - </MkTextarea> - </div> - </MkFolder> + <SearchMarker> + <MkSwitch v-model="emailValidationForm.state.enableVerifymailApi"> + <template #label><SearchLabel>Use Verifymail.io API</SearchLabel></template> + </MkSwitch> + </SearchMarker> - <MkFolder> - <template #label>Log IP address</template> - <template v-if="ipLoggingForm.savedState.enableIpLogging" #suffix>Enabled</template> - <template v-else #suffix>Disabled</template> - <template v-if="ipLoggingForm.modified.value" #footer> - <MkFormFooter :form="ipLoggingForm"/> - </template> + <SearchMarker> + <MkInput v-model="emailValidationForm.state.verifymailAuthKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label><SearchLabel>Verifymail.io API Auth Key</SearchLabel></template> + </MkInput> + </SearchMarker> - <div class="_gaps_m"> - <MkSwitch v-model="ipLoggingForm.state.enableIpLogging"> - <template #label>Enable</template> - </MkSwitch> - </div> - </MkFolder> - </div> + <SearchMarker> + <MkSwitch v-model="emailValidationForm.state.enableTruemailApi"> + <template #label><SearchLabel>Use TrueMail API</SearchLabel></template> + </MkSwitch> + </SearchMarker> + + <SearchMarker> + <MkInput v-model="emailValidationForm.state.truemailInstance"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label><SearchLabel>TrueMail API Instance</SearchLabel></template> + </MkInput> + </SearchMarker> + + <SearchMarker> + <MkInput v-model="emailValidationForm.state.truemailAuthKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label><SearchLabel>TrueMail API Auth Key</SearchLabel></template> + </MkInput> + </SearchMarker> + </div> + </MkFolder> + </SearchMarker> + + <SearchMarker v-slot="slotProps" :keywords="['banned', 'email', 'domains', 'blacklist']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #label><SearchLabel>Banned Email Domains</SearchLabel></template> + <template v-if="bannedEmailDomainsForm.modified.value" #footer> + <MkFormFooter :form="bannedEmailDomainsForm"/> + </template> + + <div class="_gaps_m"> + <SearchMarker> + <MkTextarea v-model="bannedEmailDomainsForm.state.bannedEmailDomains"> + <template #label><SearchLabel>Banned Email Domains List</SearchLabel></template> + </MkTextarea> + </SearchMarker> + </div> + </MkFolder> + </SearchMarker> + + <SearchMarker v-slot="slotProps" :keywords="['log', 'ipAddress']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #label><SearchLabel>Log IP address</SearchLabel></template> + <template v-if="ipLoggingForm.savedState.enableIpLogging" #suffix>Enabled</template> + <template v-else #suffix>Disabled</template> + <template v-if="ipLoggingForm.modified.value" #footer> + <MkFormFooter :form="ipLoggingForm"/> + </template> + + <div class="_gaps_m"> + <SearchMarker> + <MkSwitch v-model="ipLoggingForm.state.enableIpLogging"> + <template #label><SearchLabel>Enable</SearchLabel></template> + </MkSwitch> + </SearchMarker> + </div> + </MkFolder> + </SearchMarker> + </div> + </SearchMarker> </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue index 276a7590c4..d26f02b41c 100644 --- a/packages/frontend/src/pages/admin/server-rules.vue +++ b/packages/frontend/src/pages/admin/server-rules.vue @@ -4,10 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader :tabs="headerTabs"> - <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> +<SearchMarker markerId="serverRules" :keywords="['rules']"> + <MkFolder> + <template #icon><SearchIcon><i class="ti ti-checkbox"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.serverRules }}</SearchLabel></template> + <div class="_gaps_m"> - <div>{{ i18n.ts._serverRules.description }}</div> + <div><SearchText>{{ i18n.ts._serverRules.description }}</SearchText></div> + <Sortable v-model="serverRules" class="_gaps_m" @@ -33,8 +37,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> </div> </div> - </div> -</PageWithHeader> + </MkFolder> +</SearchMarker> </template> <script lang="ts" setup> @@ -42,9 +46,9 @@ import { defineAsyncComponent, ref, computed } from 'vue'; import * as os from '@/os.js'; import { fetchInstance, instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; -import { definePage } from '@/page.js'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; +import MkFolder from '@/components/MkFolder.vue'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -60,13 +64,6 @@ const save = async () => { const remove = (index: number): void => { serverRules.value.splice(index, 1); }; - -const headerTabs = computed(() => []); - -definePage(() => ({ - title: i18n.ts.serverRules, - icon: 'ti ti-checkbox', -})); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index d079b4cb0c..541ee7c0cd 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -6,292 +6,369 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> - <div class="_gaps_m"> - <MkFolder :defaultOpen="true"> - <template #icon><i class="ti ti-info-circle"></i></template> - <template #label>{{ i18n.ts.info }}</template> - <template v-if="infoForm.modified.value" #footer> - <MkFormFooter :form="infoForm"/> - </template> + <SearchMarker path="/admin/settings" :label="i18n.ts.general" :keywords="['general', 'settings']" icon="ti ti-settings"> + <div class="_gaps_m"> + <SearchMarker v-slot="slotProps" :keywords="['information', 'meta']"> + <MkFolder :defaultOpen="true"> + <template #icon><SearchIcon><i class="ti ti-info-circle"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.info }}</SearchLabel></template> + <template v-if="infoForm.modified.value" #footer> + <MkFormFooter :form="infoForm"/> + </template> - <div class="_gaps"> - <MkInput v-model="infoForm.state.name"> - <template #label>{{ i18n.ts.instanceName }}<span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> + <div class="_gaps"> + <SearchMarker :keywords="['name']"> + <MkInput v-model="infoForm.state.name"> + <template #label><SearchLabel>{{ i18n.ts.instanceName }}</SearchLabel><span v-if="infoForm.modifiedStates.name" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkInput> + </SearchMarker> - <MkInput v-model="infoForm.state.shortName"> - <template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template> - </MkInput> + <SearchMarker :keywords="['shortName']"> + <MkInput v-model="infoForm.state.shortName"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.shortName }}</SearchLabel> ({{ i18n.ts.optional }})<span v-if="infoForm.modifiedStates.shortName" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts._serverSettings.shortNameDescription }}</SearchText></template> + </MkInput> + </SearchMarker> - <MkTextarea v-model="infoForm.state.description"> - <template #label>{{ i18n.ts.instanceDescription }}<span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkTextarea> + <SearchMarker :keywords="['description']"> + <MkTextarea v-model="infoForm.state.description"> + <template #label><SearchLabel>{{ i18n.ts.instanceDescription }}</SearchLabel><span v-if="infoForm.modifiedStates.description" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkTextarea> + </SearchMarker> - <FormSplit :minWidth="300"> - <MkInput v-model="infoForm.state.maintainerName"> - <template #label>{{ i18n.ts.maintainerName }}<span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkInput> + <FormSplit :minWidth="300"> + <SearchMarker :keywords="['maintainer', 'name']"> + <MkInput v-model="infoForm.state.maintainerName"> + <template #label><SearchLabel>{{ i18n.ts.maintainerName }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerName" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkInput> + </SearchMarker> - <MkInput v-model="infoForm.state.maintainerEmail" type="email"> - <template #label>{{ i18n.ts.maintainerEmail }}<span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-mail"></i></template> - </MkInput> - </FormSplit> + <SearchMarker :keywords="['maintainer', 'email', 'contact']"> + <MkInput v-model="infoForm.state.maintainerEmail" type="email"> + <template #label><SearchLabel>{{ i18n.ts.maintainerEmail }}</SearchLabel><span v-if="infoForm.modifiedStates.maintainerEmail" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-mail"></i></template> + </MkInput> + </SearchMarker> + </FormSplit> - <MkInput v-model="infoForm.state.tosUrl" type="url"> - <template #label>{{ i18n.ts.tosUrl }}<span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <SearchMarker :keywords="['tos', 'termsOfService']"> + <MkInput v-model="infoForm.state.tosUrl" type="url"> + <template #label><SearchLabel>{{ i18n.ts.tosUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.tosUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> + </SearchMarker> - <MkInput v-model="infoForm.state.privacyPolicyUrl" type="url"> - <template #label>{{ i18n.ts.privacyPolicyUrl }}<span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <SearchMarker :keywords="['privacyPolicy']"> + <MkInput v-model="infoForm.state.privacyPolicyUrl" type="url"> + <template #label><SearchLabel>{{ i18n.ts.privacyPolicyUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.privacyPolicyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> + </SearchMarker> - <MkInput v-model="infoForm.state.inquiryUrl" type="url"> - <template #label>{{ i18n.ts._serverSettings.inquiryUrl }}<span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <SearchMarker :keywords="['inquiry', 'contact']"> + <MkInput v-model="infoForm.state.inquiryUrl" type="url"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.inquiryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.inquiryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</SearchText></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> + </SearchMarker> - <MkInput v-model="infoForm.state.repositoryUrl" type="url"> - <template #label>{{ i18n.ts.repositoryUrl }}<span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> + <SearchMarker :keywords="['repository', 'url']"> + <MkInput v-model="infoForm.state.repositoryUrl" type="url"> + <template #label><SearchLabel>{{ i18n.ts.repositoryUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.repositoryUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts.repositoryUrlDescription }}</SearchText></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> + </SearchMarker> - <MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn> - {{ i18n.ts.repositoryUrlOrTarballRequired }} - </MkInfo> + <MkInfo v-if="!instance.providesTarball && !infoForm.state.repositoryUrl" warn> + {{ i18n.ts.repositoryUrlOrTarballRequired }} + </MkInfo> - <MkInput v-model="infoForm.state.impressumUrl" type="url"> - <template #label>{{ i18n.ts.impressumUrl }}<span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.impressumDescription }}</template> - <template #prefix><i class="ti ti-link"></i></template> - </MkInput> - </div> - </MkFolder> + <SearchMarker :keywords="['impressum', 'legalNotice']"> + <MkInput v-model="infoForm.state.impressumUrl" type="url"> + <template #label><SearchLabel>{{ i18n.ts.impressumUrl }}</SearchLabel><span v-if="infoForm.modifiedStates.impressumUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts.impressumDescription }}</SearchText></template> + <template #prefix><i class="ti ti-link"></i></template> + </MkInput> + </SearchMarker> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-user-star"></i></template> - <template #label>{{ i18n.ts.pinnedUsers }}</template> - <template v-if="pinnedUsersForm.modified.value" #footer> - <MkFormFooter :form="pinnedUsersForm"/> - </template> + <SearchMarker v-slot="slotProps" :keywords="['pinned', 'users']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #icon><SearchIcon><i class="ti ti-user-star"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.pinnedUsers }}</SearchLabel></template> + <template v-if="pinnedUsersForm.modified.value" #footer> + <MkFormFooter :form="pinnedUsersForm"/> + </template> - <MkTextarea v-model="pinnedUsersForm.state.pinnedUsers"> - <template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.pinnedUsersDescription }}</template> - </MkTextarea> - </MkFolder> + <MkTextarea v-model="pinnedUsersForm.state.pinnedUsers"> + <template #label>{{ i18n.ts.pinnedUsers }}<span v-if="pinnedUsersForm.modifiedStates.pinnedUsers" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts.pinnedUsersDescription }}</SearchText></template> + </MkTextarea> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-world-cog"></i></template> - <template #label>ServiceWorker</template> - <template v-if="serviceWorkerForm.modified.value" #footer> - <MkFormFooter :form="serviceWorkerForm"/> - </template> + <SearchMarker v-slot="slotProps" :keywords="['serviceWorker']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #icon><SearchIcon><i class="ti ti-world-cog"></i></SearchIcon></template> + <template #label><SearchLabel>ServiceWorker</SearchLabel></template> + <template v-if="serviceWorkerForm.modified.value" #footer> + <MkFormFooter :form="serviceWorkerForm"/> + </template> - <div class="_gaps"> - <MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker"> - <template #label>{{ i18n.ts.enableServiceworker }}<span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.serviceworkerInfo }}</template> - </MkSwitch> + <div class="_gaps"> + <SearchMarker> + <MkSwitch v-model="serviceWorkerForm.state.enableServiceWorker"> + <template #label><SearchLabel>{{ i18n.ts.enableServiceworker }}</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.enableServiceWorker" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts.serviceworkerInfo }}</SearchText></template> + </MkSwitch> + </SearchMarker> - <template v-if="serviceWorkerForm.state.enableServiceWorker"> - <MkInput v-model="serviceWorkerForm.state.swPublicKey"> - <template #label>Public key<span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-key"></i></template> - </MkInput> + <template v-if="serviceWorkerForm.state.enableServiceWorker"> + <SearchMarker> + <MkInput v-model="serviceWorkerForm.state.swPublicKey"> + <template #label><SearchLabel>Public key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPublicKey" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-key"></i></template> + </MkInput> + </SearchMarker> - <MkInput v-model="serviceWorkerForm.state.swPrivateKey"> - <template #label>Private key<span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #prefix><i class="ti ti-key"></i></template> - </MkInput> - </template> - </div> - </MkFolder> + <SearchMarker> + <MkInput v-model="serviceWorkerForm.state.swPrivateKey"> + <template #label><SearchLabel>Private key</SearchLabel><span v-if="serviceWorkerForm.modifiedStates.swPrivateKey" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #prefix><i class="ti ti-key"></i></template> + </MkInput> + </SearchMarker> + </template> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-ad"></i></template> - <template #label>{{ i18n.ts._ad.adsSettings }}</template> - <template v-if="adForm.modified.value" #footer> - <MkFormFooter :form="adForm"/> - </template> + <SearchMarker v-slot="slotProps" :keywords="['ads']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #icon><SearchIcon><i class="ti ti-ad"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts._ad.adsSettings }}</SearchLabel></template> + <template v-if="adForm.modified.value" #footer> + <MkFormFooter :form="adForm"/> + </template> - <div class="_gaps"> - <div class="_gaps_s"> - <MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number"> - <template #label>{{ i18n.ts._ad.notesPerOneAd }}<span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template> - </MkInput> - <MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true"> - {{ i18n.ts._ad.adsTooClose }} - </MkInfo> - </div> - </div> - </MkFolder> + <div class="_gaps"> + <div class="_gaps_s"> + <SearchMarker> + <MkInput v-model="adForm.state.notesPerOneAd" :min="0" type="number"> + <template #label><SearchLabel>{{ i18n.ts._ad.notesPerOneAd }}</SearchLabel><span v-if="adForm.modifiedStates.notesPerOneAd" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._ad.setZeroToDisable }}</template> + </MkInput> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-world-search"></i></template> - <template #label>{{ i18n.ts._urlPreviewSetting.title }}</template> - <template v-if="urlPreviewForm.modified.value" #footer> - <MkFormFooter :form="urlPreviewForm"/> - </template> + <MkInfo v-if="adForm.state.notesPerOneAd > 0 && adForm.state.notesPerOneAd < 20" :warn="true"> + {{ i18n.ts._ad.adsTooClose }} + </MkInfo> + </div> + </div> + </MkFolder> + </SearchMarker> + + <SearchMarker v-slot="slotProps" :keywords="['url', 'preview']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #icon><SearchIcon><i class="ti ti-world-search"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.title }}</SearchLabel></template> + <template v-if="urlPreviewForm.modified.value" #footer> + <MkFormFooter :form="urlPreviewForm"/> + </template> - <div class="_gaps"> - <MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled"> - <template #label>{{ i18n.ts._urlPreviewSetting.enable }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template> - </MkSwitch> + <div class="_gaps"> + <SearchMarker> + <MkSwitch v-model="urlPreviewForm.state.urlPreviewEnabled"> + <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.enable }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewEnabled" class="_modified">{{ i18n.ts.modified }}</span></template> + </MkSwitch> + </SearchMarker> - <template v-if="urlPreviewForm.state.urlPreviewEnabled"> - <MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect"> - <template #label>{{ i18n.ts._urlPreviewSetting.allowRedirect }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template> - </MkSwitch> + <template v-if="urlPreviewForm.state.urlPreviewEnabled"> + <SearchMarker :keywords="['allow', 'redirect']"> + <MkSwitch v-model="urlPreviewForm.state.urlPreviewAllowRedirect"> + <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.allowRedirect }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewAllowRedirect" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.allowRedirectDescription }}</template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength"> - <template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template> - </MkSwitch> + <SearchMarker :keywords="['contentLength']"> + <MkSwitch v-model="urlPreviewForm.state.urlPreviewRequireContentLength"> + <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.requireContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewRequireContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template> + </MkSwitch> + </SearchMarker> - <MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number"> - <template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template> - </MkInput> + <SearchMarker :keywords="['contentLength']"> + <MkInput v-model="urlPreviewForm.state.urlPreviewMaximumContentLength" type="number"> + <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewMaximumContentLength" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template> + </MkInput> + </SearchMarker> - <MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number"> - <template #label>{{ i18n.ts._urlPreviewSetting.timeout }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template> - </MkInput> + <SearchMarker :keywords="['timeout']"> + <MkInput v-model="urlPreviewForm.state.urlPreviewTimeout" type="number"> + <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.timeout }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewTimeout" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template> + </MkInput> + </SearchMarker> - <MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text"> - <template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template> - </MkInput> + <SearchMarker :keywords="['userAgent']"> + <MkInput v-model="urlPreviewForm.state.urlPreviewUserAgent" type="text"> + <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.userAgent }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewUserAgent" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template> + </MkInput> + </SearchMarker> - <div> - <MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text"> - <template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}<span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template> - </MkInput> + <div> + <SearchMarker :keywords="['proxy']"> + <MkInput v-model="urlPreviewForm.state.urlPreviewSummaryProxyUrl" type="text"> + <template #label><SearchLabel>{{ i18n.ts._urlPreviewSetting.summaryProxy }}</SearchLabel><span v-if="urlPreviewForm.modifiedStates.urlPreviewSummaryProxyUrl" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template> + </MkInput> + </SearchMarker> - <div :class="$style.subCaption"> - {{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }} - <ul style="padding-left: 20px; margin: 4px 0"> - <li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li> - <li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li> - <li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li> - <li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li> - </ul> - </div> + <div :class="$style.subCaption"> + {{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }} + <ul style="padding-left: 20px; margin: 4px 0"> + <li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li> + <li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li> + <li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li> + <li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li> + </ul> + </div> + </div> + </template> </div> - </template> - </div> - </MkFolder> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-planet"></i></template> - <template #label>{{ i18n.ts.federation }}</template> - <template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template> - <template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template> - <template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template> - <template v-if="federationForm.modified.value" #footer> - <MkFormFooter :form="federationForm"/> - </template> + <SearchMarker v-slot="slotProps" :keywords="['federation']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #icon><SearchIcon><i class="ti ti-planet"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.federation }}</SearchLabel></template> + <template v-if="federationForm.savedState.federation === 'all'" #suffix>{{ i18n.ts.all }}</template> + <template v-else-if="federationForm.savedState.federation === 'specified'" #suffix>{{ i18n.ts.specifyHost }}</template> + <template v-else-if="federationForm.savedState.federation === 'none'" #suffix>{{ i18n.ts.none }}</template> + <template v-if="federationForm.modified.value" #footer> + <MkFormFooter :form="federationForm"/> + </template> - <div class="_gaps"> - <MkRadios v-model="federationForm.state.federation"> - <template #label>{{ i18n.ts.behavior }}<span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template> - <option value="all">{{ i18n.ts.all }}</option> - <option value="specified">{{ i18n.ts.specifyHost }}</option> - <option value="none">{{ i18n.ts.none }}</option> - </MkRadios> + <div class="_gaps"> + <SearchMarker> + <MkRadios v-model="federationForm.state.federation"> + <template #label><SearchLabel>{{ i18n.ts.behavior }}</SearchLabel><span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template> + <option value="all">{{ i18n.ts.all }}</option> + <option value="specified">{{ i18n.ts.specifyHost }}</option> + <option value="none">{{ i18n.ts.none }}</option> + </MkRadios> + </SearchMarker> - <MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts"> - <template #label>{{ i18n.ts.federationAllowedHosts }}<span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template> - </MkTextarea> + <SearchMarker :keywords="['hosts']"> + <MkTextarea v-if="federationForm.state.federation === 'specified'" v-model="federationForm.state.federationHosts"> + <template #label><SearchLabel>{{ i18n.ts.federationAllowedHosts }}</SearchLabel><span v-if="federationForm.modifiedStates.federationHosts" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption>{{ i18n.ts.federationAllowedHostsDescription }}</template> + </MkTextarea> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-list"></i></template> - <template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template> - <template #footer> - <div class="_buttons"> - <MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> - </div> - </template> + <SearchMarker :keywords="['suspended', 'software']"> + <MkFolder> + <template #icon><i class="ti ti-list"></i></template> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.deliverSuspendedSoftware }}</SearchLabel></template> + <template #footer> + <div class="_buttons"> + <MkButton @click="federationForm.state.deliverSuspendedSoftware.push({software: '', versionRange: ''})"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> + </div> + </template> - <div :class="$style.metadataRoot" class="_gaps_s"> - <MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo> - <div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem"> - <button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button> - <div :class="$style.dragItemForm"> - <FormSplit :minWidth="200"> - <MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName"> - </MkInput> - <MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version"> - </MkInput> - </FormSplit> - </div> - </div> - </div> - </MkFolder> + <div :class="$style.metadataRoot" class="_gaps_s"> + <MkInfo>{{ i18n.ts._serverSettings.deliverSuspendedSoftwareDescription }}</MkInfo> + <div v-for="(element, index) in federationForm.state.deliverSuspendedSoftware" :key="index" v-panel :class="$style.fieldDragItem"> + <button class="_button" :class="$style.dragItemRemove" @click="federationForm.state.deliverSuspendedSoftware.splice(index, 1)"><i class="ti ti-x"></i></button> + <div :class="$style.dragItemForm"> + <FormSplit :minWidth="200"> + <MkInput v-model="element.software" small :placeholder="i18n.ts.softwareName"> + </MkInput> + <MkInput v-model="element.versionRange" small :placeholder="i18n.ts.version"> + </MkInput> + </FormSplit> + </div> + </div> + </div> + </MkFolder> + </SearchMarker> - <MkSwitch v-model="federationForm.state.signToActivityPubGet"> - <template #label>{{ i18n.ts._serverSettings.signToActivityPubGet }}<span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</template> - </MkSwitch> + <SearchMarker :keywords="['sign', 'get']"> + <MkSwitch v-model="federationForm.state.signToActivityPubGet"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.signToActivityPubGet }}</SearchLabel><span v-if="federationForm.modifiedStates.signToActivityPubGet" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts._serverSettings.signToActivityPubGet_description }}</SearchText></template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="federationForm.state.proxyRemoteFiles"> - <template #label>{{ i18n.ts._serverSettings.proxyRemoteFiles }}<span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</template> - </MkSwitch> + <SearchMarker :keywords="['proxy', 'remote', 'files']"> + <MkSwitch v-model="federationForm.state.proxyRemoteFiles"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.proxyRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.proxyRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts._serverSettings.proxyRemoteFiles_description }}</SearchText></template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="federationForm.state.allowExternalApRedirect"> - <template #label>{{ i18n.ts._serverSettings.allowExternalApRedirect }}<span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption> - <div>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</div> - <div>{{ i18n.ts.needToRestartServerToApply }}</div> - </template> - </MkSwitch> + <SearchMarker :keywords="['allow', 'external', 'redirect']"> + <MkSwitch v-model="federationForm.state.allowExternalApRedirect"> + <template #label><SearchLabel>{{ i18n.ts._serverSettings.allowExternalApRedirect }}</SearchLabel><span v-if="federationForm.modifiedStates.allowExternalApRedirect" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption> + <div><SearchText>{{ i18n.ts._serverSettings.allowExternalApRedirect_description }}</SearchText></div> + <div>{{ i18n.ts.needToRestartServerToApply }}</div> + </template> + </MkSwitch> + </SearchMarker> - <MkSwitch v-model="federationForm.state.cacheRemoteFiles"> - <template #label>{{ i18n.ts.cacheRemoteFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}{{ i18n.ts.youCanCleanRemoteFilesCache }}</template> - </MkSwitch> + <SearchMarker :keywords="['cache', 'remote', 'files']"> + <MkSwitch v-model="federationForm.state.cacheRemoteFiles"> + <template #label><SearchLabel>{{ i18n.ts.cacheRemoteFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteFiles" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts.cacheRemoteFilesDescription }}</SearchText>{{ i18n.ts.youCanCleanRemoteFilesCache }}</template> + </MkSwitch> + </SearchMarker> - <template v-if="federationForm.state.cacheRemoteFiles"> - <MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles"> - <template #label>{{ i18n.ts.cacheRemoteSensitiveFiles }}<span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template> - <template #caption>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</template> - </MkSwitch> - </template> - </div> - </MkFolder> + <template v-if="federationForm.state.cacheRemoteFiles"> + <SearchMarker :keywords="['cache', 'remote', 'sensitive', 'files']"> + <MkSwitch v-model="federationForm.state.cacheRemoteSensitiveFiles"> + <template #label><SearchLabel>{{ i18n.ts.cacheRemoteSensitiveFiles }}</SearchLabel><span v-if="federationForm.modifiedStates.cacheRemoteSensitiveFiles" class="_modified">{{ i18n.ts.modified }}</span></template> + <template #caption><SearchText>{{ i18n.ts.cacheRemoteSensitiveFilesDescription }}</SearchText></template> + </MkSwitch> + </SearchMarker> + </template> + </div> + </MkFolder> + </SearchMarker> - <MkFolder> - <template #icon><i class="ti ti-ghost"></i></template> - <template #label>{{ i18n.ts.proxyAccount }}</template> - <template v-if="proxyAccountForm.modified.value" #footer> - <MkFormFooter :form="proxyAccountForm"/> - </template> + <SearchMarker v-slot="slotProps" :keywords="['proxy', 'account']"> + <MkFolder :defaultOpen="slotProps.isParentOfTarget"> + <template #icon><SearchIcon><i class="ti ti-ghost"></i></SearchIcon></template> + <template #label><SearchLabel>{{ i18n.ts.proxyAccount }}</SearchLabel></template> + <template v-if="proxyAccountForm.modified.value" #footer> + <MkFormFooter :form="proxyAccountForm"/> + </template> - <div class="_gaps"> - <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo> + <div class="_gaps"> + <MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo> - <MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true"> - <template #label>{{ i18n.ts._profile.description }}</template> - <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> - </MkTextarea> - </div> - </MkFolder> + <SearchMarker :keywords="['description']"> + <MkTextarea v-model="proxyAccountForm.state.description" :max="500" tall mfmAutocomplete :mfmPreview="true"> + <template #label><SearchLabel>{{ i18n.ts._profile.description }}</SearchLabel></template> + <template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> + </MkTextarea> + </SearchMarker> + </div> + </MkFolder> + </SearchMarker> - <MkButton primary @click="openSetupWizard"> - Open setup wizard - </MkButton> - </div> + <MkButton primary @click="openSetupWizard"> + Open setup wizard + </MkButton> + </div> + </SearchMarker> </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/system-webhook.vue b/packages/frontend/src/pages/admin/system-webhook.vue index d5402f608c..0fd255d5f6 100644 --- a/packages/frontend/src/pages/admin/system-webhook.vue +++ b/packages/frontend/src/pages/admin/system-webhook.vue @@ -6,17 +6,21 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> <div class="_spacer" style="--MI_SPACER-w: 900px;"> - <div class="_gaps_m"> - <MkButton primary @click="onCreateWebhookClicked"> - <i class="ti ti-plus"></i> {{ i18n.ts._webhookSettings.createWebhook }} - </MkButton> + <SearchMarker path="/admin/system-webhook" label="SystemWebhook" :keywords="['webhook']" icon="ti ti-webhook"> + <div class="_gaps_m"> + <SearchMarker> + <MkButton primary @click="onCreateWebhookClicked"> + <i class="ti ti-plus"></i> <SearchLabel>{{ i18n.ts._webhookSettings.createWebhook }}</SearchLabel> + </MkButton> + </SearchMarker> - <FormSection> - <div class="_gaps"> - <XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/> - </div> - </FormSection> - </div> + <FormSection> + <div class="_gaps"> + <XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/> + </div> + </FormSection> + </div> + </SearchMarker> </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue index 2f639cd090..1f98fab618 100644 --- a/packages/frontend/src/pages/settings/2fa.vue +++ b/packages/frontend/src/pages/settings/2fa.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFolder :defaultOpen="true"> <template #icon><i class="ti ti-shield-lock"></i></template> <template #label><SearchLabel>{{ i18n.ts.totp }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.totpDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.totpDescription }}</SearchText></template> <template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template> <div v-if="$i.twoFactorEnabled" class="_gaps_s"> @@ -74,7 +74,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker :keywords="['password', 'less', 'key', 'passkey', 'login', 'signin']"> <MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)"> <template #label><SearchLabel>{{ i18n.ts.passwordLessLogin }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.passwordLessLoginDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.passwordLessLoginDescription }}</SearchText></template> </MkSwitch> </SearchMarker> </div> diff --git a/packages/frontend/src/pages/settings/account-data.vue b/packages/frontend/src/pages/settings/account-data.vue index 5a00d7a9d7..c75667b06b 100644 --- a/packages/frontend/src/pages/settings/account-data.vue +++ b/packages/frontend/src/pages/settings/account-data.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/account-data" :label="i18n.ts._settings.accountData" :keywords="['import', 'export', 'data', 'archive']" icon="ti ti-package"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/package_3d.png" color="#ff9100"> - <SearchKeyword>{{ i18n.ts._settings.accountDataBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.accountDataBanner }}</SearchText> </MkFeatureBanner> <div class="_gaps_s"> diff --git a/packages/frontend/src/pages/settings/connect.vue b/packages/frontend/src/pages/settings/connect.vue index 1e701096c5..28579b915f 100644 --- a/packages/frontend/src/pages/settings/connect.vue +++ b/packages/frontend/src/pages/settings/connect.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/connect" :label="i18n.ts._settings.serviceConnection" :keywords="['app', 'service', 'connect', 'webhook', 'api', 'token']" icon="ti ti-link"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/link_3d.png" color="#ff0088"> - <SearchKeyword>{{ i18n.ts._settings.serviceConnectionBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.serviceConnectionBanner }}</SearchText> </MkFeatureBanner> <SearchMarker :keywords="['api', 'app', 'token', 'accessToken']"> diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index 1b99f6dea5..cfa4df18fc 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/drive" :label="i18n.ts.drive" :keywords="['drive']" icon="ti ti-cloud"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/cloud_3d.png" color="#0059ff"> - <SearchKeyword>{{ i18n.ts._settings.driveBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.driveBanner }}</SearchText> </MkFeatureBanner> <SearchMarker :keywords="['capacity', 'usage']"> @@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="keepOriginalFilename"> <MkSwitch v-model="keepOriginalFilename"> <template #label><SearchLabel>{{ i18n.ts.keepOriginalFilename }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.keepOriginalFilenameDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.keepOriginalFilenameDescription }}</SearchText></template> </MkSwitch> </MkPreferenceContainer> </SearchMarker> @@ -74,7 +74,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker :keywords="['auto', 'nsfw', 'sensitive', 'media', 'file']"> <MkSwitch v-model="autoSensitive" @update:modelValue="saveProfile()"> <template #label><SearchLabel>{{ i18n.ts.enableAutoSensitive }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> - <template #caption><SearchKeyword>{{ i18n.ts.enableAutoSensitiveDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.enableAutoSensitiveDescription }}</SearchText></template> </MkSwitch> </SearchMarker> </div> diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 61e3ca8b6c..eda9dfde7b 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div>{{ i18n.ts._preferencesBackup.autoPreferencesBackupIsNotEnabledForThisDevice }}</div> <div><button class="_textButton" @click="enableAutoBackup">{{ i18n.ts.enable }}</button> | <button class="_textButton" @click="skipAutoBackup">{{ i18n.ts.skip }}</button></div> </MkInfo> - <MkSuperMenu :def="menuDef" :grid="narrow" :searchIndex="SETTING_INDEX"></MkSuperMenu> + <MkSuperMenu :def="menuDef" :grid="narrow" :searchIndex="searchIndex"></MkSuperMenu> </div> </div> <div v-if="!(narrow && currentPage?.route.name == null)" class="main"> @@ -42,12 +42,12 @@ import { instance } from '@/instance.js'; import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js'; import * as os from '@/os.js'; import { useRouter } from '@/router.js'; -import { searchIndexes } from '@/utility/settings-search-index.js'; import { enableAutoBackup, getPreferencesProfileMenu } from '@/preferences/utility.js'; import { store } from '@/store.js'; import { signout } from '@/signout.js'; +import { genSearchIndexes } from '@/utility/inapp-search.js'; -const SETTING_INDEX = searchIndexes; // TODO: lazy load +const searchIndex = await import('search-index:settings').then(({ searchIndexes }) => genSearchIndexes(searchIndexes)); const indexInfo = { title: i18n.ts.settings, diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index 57aa30226b..3b05f0aa80 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/mute-block" :label="i18n.ts.muteAndBlock" icon="ti ti-ban" :keywords="['mute', 'block']"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/prohibited_3d.png" color="#ff2600"> - <SearchKeyword>{{ i18n.ts._settings.muteAndBlockBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.muteAndBlockBanner }}</SearchText> </MkFeatureBanner> <div class="_gaps_s"> diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index 4e8d88ab74..3ddfb81c33 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/notifications" :label="i18n.ts.notifications" :keywords="['notifications']" icon="ti ti-bell"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/bell_3d.png" color="#ffff00"> - <SearchKeyword>{{ i18n.ts._settings.notificationsBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.notificationsBanner }}</SearchText> </MkFeatureBanner> <FormSection first> diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index e0fb9b86bb..c896ee8232 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m"> <FormInfo warn>{{ i18n.ts._accountDelete.mayTakeTime }}</FormInfo> <FormInfo>{{ i18n.ts._accountDelete.sendEmail }}</FormInfo> - <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount"><SearchKeyword>{{ i18n.ts._accountDelete.requestAccountDelete }}</SearchKeyword></MkButton> + <MkButton v-if="!$i.isDeleted" danger @click="deleteAccount"><SearchText>{{ i18n.ts._accountDelete.requestAccountDelete }}</SearchText></MkButton> <MkButton v-else disabled>{{ i18n.ts._accountDelete.inProgress }}</MkButton> </div> </MkFolder> diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue index bff307ab7d..7c6ce90e7e 100644 --- a/packages/frontend/src/pages/settings/plugin.vue +++ b/packages/frontend/src/pages/settings/plugin.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/plugin" :label="i18n.ts.plugins" :keywords="['plugin', 'addon', 'extension']" icon="ti ti-plug"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/electric_plug_3d.png" color="#ffbb00"> - <SearchKeyword>{{ i18n.ts._settings.pluginBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.pluginBanner }}</SearchText> </MkFeatureBanner> <MkInfo v-if="isSafeMode" warn>{{ i18n.ts.pluginsAreDisabledBecauseSafeMode }}</MkInfo> diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 04f9b0512b..7ee5f151fa 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/preferences" :label="i18n.ts.preferences" :keywords="['general', 'preferences']" icon="ti ti-adjustments"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/gear_3d.png" color="#00ff9d"> - <SearchKeyword>{{ i18n.ts._settings.preferencesBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.preferencesBanner }}</SearchText> </MkFeatureBanner> <div class="_gaps_s"> @@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker :keywords="['realtimemode']"> <MkSwitch v-model="realtimeMode"> <template #label><i class="ti ti-bolt"></i> <SearchLabel>{{ i18n.ts.realtimeMode }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts._settings.realtimeMode_description }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts._settings.realtimeMode_description }}</SearchText></template> </MkSwitch> </SearchMarker> @@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="pollingInterval"> <MkRange v-model="pollingInterval" :min="1" :max="3" :step="1" easing :showTicks="true" :textConverter="(v) => v === 1 ? i18n.ts.low : v === 2 ? i18n.ts.middle : v === 3 ? i18n.ts.high : ''"> <template #label><SearchLabel>{{ i18n.ts._settings.contentsUpdateFrequency }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts._settings.contentsUpdateFrequency_description }}</SearchKeyword><br><SearchKeyword>{{ i18n.ts._settings.contentsUpdateFrequency_description2 }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts._settings.contentsUpdateFrequency_description }}</SearchText><br><SearchText>{{ i18n.ts._settings.contentsUpdateFrequency_description2 }}</SearchText></template> <template #prefix><i class="ti ti-player-play"></i></template> <template #suffix><i class="ti ti-player-track-next"></i></template> </MkRange> @@ -165,7 +165,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="collapseRenotes"> <MkSwitch v-model="collapseRenotes"> <template #label><SearchLabel>{{ i18n.ts.collapseRenotes }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.collapseRenotesDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.collapseRenotesDescription }}</SearchText></template> </MkSwitch> </MkPreferenceContainer> </SearchMarker> @@ -449,7 +449,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/mens_room_3d.png" color="#0011ff"> - <SearchKeyword>{{ i18n.ts._settings.accessibilityBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.accessibilityBanner }}</SearchText> </MkFeatureBanner> <div class="_gaps_s"> @@ -489,7 +489,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="enablePullToRefresh"> <MkSwitch v-model="enablePullToRefresh"> <template #label><SearchLabel>{{ i18n.ts._settings.enablePullToRefresh }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts._settings.enablePullToRefresh_description }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts._settings.enablePullToRefresh_description }}</SearchText></template> </MkSwitch> </MkPreferenceContainer> </SearchMarker> @@ -571,7 +571,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="animation"> <MkSwitch :modelValue="!reduceAnimation" @update:modelValue="v => reduceAnimation = !v"> <template #label><SearchLabel>{{ i18n.ts._settings.uiAnimations }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template> </MkSwitch> </MkPreferenceContainer> </SearchMarker> @@ -580,7 +580,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="useBlurEffect"> <MkSwitch v-model="useBlurEffect"> <template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template> </MkSwitch> </MkPreferenceContainer> </SearchMarker> @@ -589,7 +589,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="useBlurEffectForModal"> <MkSwitch v-model="useBlurEffectForModal"> <template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template> </MkSwitch> </MkPreferenceContainer> </SearchMarker> @@ -598,7 +598,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="enableHighQualityImagePlaceholders"> <MkSwitch v-model="enableHighQualityImagePlaceholders"> <template #label><SearchLabel>{{ i18n.ts._settings.enableHighQualityImagePlaceholders }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template> </MkSwitch> </MkPreferenceContainer> </SearchMarker> @@ -607,7 +607,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPreferenceContainer k="useStickyIcons"> <MkSwitch v-model="useStickyIcons"> <template #label><SearchLabel>{{ i18n.ts._settings.useStickyIcons }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText></template> </MkSwitch> </MkPreferenceContainer> </SearchMarker> diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 4e6425667e..3977359c54 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -7,13 +7,13 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/privacy" :label="i18n.ts.privacy" :keywords="['privacy']" icon="ti ti-lock-open"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/unlocked_3d.png" color="#aeff00"> - <SearchKeyword>{{ i18n.ts._settings.privacyBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.privacyBanner }}</SearchText> </MkFeatureBanner> <SearchMarker :keywords="['follow', 'lock']"> <MkSwitch v-model="isLocked" @update:modelValue="save()"> <template #label><SearchLabel>{{ i18n.ts.makeFollowManuallyApprove }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.lockedAccountInfo }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.lockedAccountInfo }}</SearchText></template> </MkSwitch> </SearchMarker> @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker :keywords="['reaction', 'public']"> <MkSwitch v-model="publicReactions" @update:modelValue="save()"> <template #label><SearchLabel>{{ i18n.ts.makeReactionsPublic }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.makeReactionsPublicDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.makeReactionsPublicDescription }}</SearchText></template> </MkSwitch> </SearchMarker> @@ -53,28 +53,28 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker :keywords="['online', 'status']"> <MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()"> <template #label><SearchLabel>{{ i18n.ts.hideOnlineStatus }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.hideOnlineStatusDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.hideOnlineStatusDescription }}</SearchText></template> </MkSwitch> </SearchMarker> <SearchMarker :keywords="['crawle', 'index', 'search']"> <MkSwitch v-model="noCrawle" @update:modelValue="save()"> <template #label><SearchLabel>{{ i18n.ts.noCrawle }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.noCrawleDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.noCrawleDescription }}</SearchText></template> </MkSwitch> </SearchMarker> <SearchMarker :keywords="['crawle', 'ai']"> <MkSwitch v-model="preventAiLearning" @update:modelValue="save()"> <template #label><SearchLabel>{{ i18n.ts.preventAiLearning }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.preventAiLearningDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.preventAiLearningDescription }}</SearchText></template> </MkSwitch> </SearchMarker> <SearchMarker :keywords="['explore']"> <MkSwitch v-model="isExplorable" @update:modelValue="save()"> <template #label><SearchLabel>{{ i18n.ts.makeExplorable }}</SearchLabel></template> - <template #caption><SearchKeyword>{{ i18n.ts.makeExplorableDescription }}</SearchKeyword></template> + <template #caption><SearchText>{{ i18n.ts.makeExplorableDescription }}</SearchText></template> </MkSwitch> </SearchMarker> @@ -146,7 +146,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <template #caption> - <div><SearchKeyword>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</SearchKeyword></div> + <div><SearchText>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</SearchText></div> </template> </FormSlot> </SearchMarker> @@ -183,7 +183,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <template #caption> - <div><SearchKeyword>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</SearchKeyword></div> + <div><SearchText>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</SearchText></div> </template> </FormSlot> </SearchMarker> diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index ce7f31cd23..e2679623ef 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -110,7 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false"> <template #label><SearchLabel>{{ i18n.ts._profile.followedMessage }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> <template #caption> - <div><SearchKeyword>{{ i18n.ts._profile.followedMessageDescription }}</SearchKeyword></div> + <div><SearchText>{{ i18n.ts._profile.followedMessageDescription }}</SearchText></div> <div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div> </template> </MkInput> diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index 2562993be3..c954b9dd5a 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/security" :label="i18n.ts.security" :keywords="['security']" icon="ti ti-lock" :inlining="['2fa']"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/locked_with_key_3d.png" color="#ffbf00"> - <SearchKeyword>{{ i18n.ts._settings.securityBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.securityBanner }}</SearchText> </MkFeatureBanner> <SearchMarker :keywords="['password']"> @@ -24,30 +24,34 @@ SPDX-License-Identifier: AGPL-3.0-only <X2fa/> - <FormSection> - <template #label>{{ i18n.ts.signinHistory }}</template> - <MkPagination :paginator="paginator" withControl> - <template #default="{items}"> - <div> - <div v-for="item in items" :key="item.id" v-panel class="timnmucd"> - <header> - <i v-if="item.success" class="ti ti-check icon succ"></i> - <i v-else class="ti ti-circle-x icon fail"></i> - <code class="ip _monospace">{{ item.ip }}</code> - <MkTime :time="item.createdAt" class="time"/> - </header> + <SearchMarker :keywords="['signin', 'login', 'history', 'log']"> + <FormSection> + <template #label><SearchLabel>{{ i18n.ts.signinHistory }}</SearchLabel></template> + <MkPagination :paginator="paginator" withControl> + <template #default="{items}"> + <div> + <div v-for="item in items" :key="item.id" v-panel class="timnmucd"> + <header> + <i v-if="item.success" class="ti ti-check icon succ"></i> + <i v-else class="ti ti-circle-x icon fail"></i> + <code class="ip _monospace">{{ item.ip }}</code> + <MkTime :time="item.createdAt" class="time"/> + </header> + </div> </div> - </div> - </template> - </MkPagination> - </FormSection> + </template> + </MkPagination> + </FormSection> + </SearchMarker> - <FormSection> - <FormSlot> - <MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> {{ i18n.ts.regenerateLoginToken }}</MkButton> - <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template> - </FormSlot> - </FormSection> + <SearchMarker :keywords="['regenerate', 'refresh', 'reset', 'token']"> + <FormSection> + <FormSlot> + <MkButton danger @click="regenerateToken"><i class="ti ti-refresh"></i> <SearchLabel>{{ i18n.ts.regenerateLoginToken }}</SearchLabel></MkButton> + <template #caption>{{ i18n.ts.regenerateLoginTokenDescription }}</template> + </FormSlot> + </FormSection> + </SearchMarker> </div> </SearchMarker> </template> diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index 590db19bca..ea5b347525 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <SearchMarker path="/settings/sounds" :label="i18n.ts.sounds" :keywords="['sounds']" icon="ti ti-music"> <div class="_gaps_m"> <MkFeatureBanner icon="/client-assets/speaker_high_volume_3d.png" color="#ff006f"> - <SearchKeyword>{{ i18n.ts._settings.soundsBanner }}</SearchKeyword> + <SearchText>{{ i18n.ts._settings.soundsBanner }}</SearchText> </MkFeatureBanner> <SearchMarker :keywords="['mute']"> diff --git a/packages/frontend/src/router.definition.ts b/packages/frontend/src/router.definition.ts index 7edc5ed9b7..57d9a860d6 100644 --- a/packages/frontend/src/router.definition.ts +++ b/packages/frontend/src/router.definition.ts @@ -492,10 +492,6 @@ export const ROUTE_DEF = [{ name: 'performance', component: page(() => import('@/pages/admin/performance.vue')), }, { - path: '/server-rules', - name: 'server-rules', - component: page(() => import('@/pages/admin/server-rules.vue')), - }, { path: '/invites', name: 'invites', component: page(() => import('@/pages/admin/invites.vue')), diff --git a/packages/frontend/src/utility/inapp-search.ts b/packages/frontend/src/utility/inapp-search.ts new file mode 100644 index 0000000000..cbc3d87ff8 --- /dev/null +++ b/packages/frontend/src/utility/inapp-search.ts @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type { GeneratedSearchIndexItem } from 'search-index'; + +export type SearchIndexItem = { + id: string; + parentId?: string; + path?: string; + label: string; + keywords: string[]; + texts: string[]; + icon?: string; +}; + +export function genSearchIndexes(generated: GeneratedSearchIndexItem[]): SearchIndexItem[] { + const rootMods = new Map(generated.map(item => [item.id, item])); + + // link inlining here + for (const item of generated) { + if (item.inlining) { + for (const id of item.inlining) { + const inline = rootMods.get(id); + if (inline) { + inline.parentId = item.id; + inline.path = item.path; + } else { + console.log('[Settings Search Index] Failed to inline', id); + } + } + } + } + + return generated; +} diff --git a/packages/frontend/src/utility/settings-search-index.ts b/packages/frontend/src/utility/settings-search-index.ts deleted file mode 100644 index 8506e4fe2f..0000000000 --- a/packages/frontend/src/utility/settings-search-index.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { searchIndexes as generated } from 'search-index:settings'; -import type { GeneratedSearchIndexItem } from 'search-index:settings'; - -export type SearchIndexItem = { - id: string; - parentId?: string; - path?: string; - label: string; - keywords: string[]; - icon?: string; -}; - -const rootMods = new Map(generated.map(item => [item.id, item])); - -// link inlining here -for (const item of generated) { - if (item.inlining) { - for (const id of item.inlining) { - const inline = rootMods.get(id); - if (inline) { - inline.parentId = item.id; - inline.path = item.path; - } else { - console.log('[Settings Search Index] Failed to inline', id); - } - } - } -} - -export const searchIndexes: SearchIndexItem[] = generated; - diff --git a/packages/frontend/src/utility/virtual.d.ts b/packages/frontend/src/utility/virtual.d.ts index 63dc4372b7..00f01992aa 100644 --- a/packages/frontend/src/utility/virtual.d.ts +++ b/packages/frontend/src/utility/virtual.d.ts @@ -3,16 +3,25 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +type XGeneratedSearchIndexItem = { + id: string; + parentId?: string; + path?: string; + label: string; + keywords: string[]; + texts: string[]; + icon?: string; + inlining?: string[]; +}; + +declare module 'search-index' { + export type GeneratedSearchIndexItem = XGeneratedSearchIndexItem; +} + declare module 'search-index:settings' { - export type GeneratedSearchIndexItem = { - id: string; - parentId?: string; - path?: string; - label: string; - keywords: string[]; - icon?: string; - inlining?: string[]; - }; + export const searchIndexes: XGeneratedSearchIndexItem[]; +} - export const searchIndexes: GeneratedSearchIndexItem[]; +declare module 'search-index:admin' { + export const searchIndexes: XGeneratedSearchIndexItem[]; } |