diff --git a/locales/en-US.yml b/locales/en-US.yml index c442d41c1e..23884aa043 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -752,6 +752,10 @@ noCrawleDescription: "Ask search engines to not index your profile page, notes, lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", your notes will be visible to anyone, even if you require followers to be manually approved." alwaysMarkSensitive: "Mark as sensitive by default" loadRawImages: "Load original images instead of showing thumbnails" +searchEngine: "Search Engine For Search MFM" +searchEngineOther: "Other" +searchEngineCustomURIDescription: "The custom URI must be input in the format like \"https://www.google.com/search?q={query}\" or \"https://www.google.com/search?q=%s\"." +searchEngineCusomURI: "Custom URI" disableShowingAnimatedImages: "Don't play animated images" highlightSensitiveMedia: "Highlight sensitive media" verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification." diff --git a/locales/index.d.ts b/locales/index.d.ts index 6efd5b33b9..fe36e2dfd0 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -3020,6 +3020,22 @@ export interface Locale extends ILocale { * 添付画像のサムネイルをオリジナル画質にする */ "loadRawImages": string; + /** + * 検索MFMの検索エンジン + */ + "searchEngine": string; + /** + * 他 + */ + "searchEngineOther": string; + /** + * カスタム URI は、"https://www.google.com/search?q={query}" や "https://www.google.com/search?q=%s" のような形式で入力する必要があります。 + */ + "searchEngineCustomURIDescription": ParameterizedString<"query">; + /** + * カスタム URI + */ + "searchEngineCusomURI": string; /** * アニメーション画像を再生しない */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 68e4091a88..293f8ec380 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -751,6 +751,10 @@ noCrawleDescription: "外部の検索エンジンにあなたのユーザーペ lockedAccountInfo: "フォローを承認制にしても、ノートの公開範囲を「フォロワー」にしない限り、誰でもあなたのノートを見ることができます。" alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする" loadRawImages: "添付画像のサムネイルをオリジナル画質にする" +searchEngine: "検索MFMの検索エンジン" +searchEngineOther: "他" +searchEngineCustomURIDescription: "カスタム URI は、\"https://www.google.com/search?q={query}\" や \"https://www.google.com/search?q=%s\" のような形式で入力する必要があります。" +searchEngineCusomURI: "カスタム URI" disableShowingAnimatedImages: "アニメーション画像を再生しない" highlightSensitiveMedia: "メディアがセンシティブであることを分かりやすく表示" verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。" diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue index c92a49d32a..d1809d1073 100644 --- a/packages/frontend/src/components/MkGoogle.vue +++ b/packages/frontend/src/components/MkGoogle.vue @@ -13,6 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import { i18n } from '@/i18n.js'; +import { defaultStore } from '@/store'; const props = defineProps<{ q: string; @@ -21,9 +22,10 @@ const props = defineProps<{ const query = ref(props.q); const search = () => { - const sp = new URLSearchParams(); - sp.append('q', query.value); - window.open(`https://www.google.com/search?${sp.toString()}`, '_blank', 'noopener'); + const searchQuery = encodeURIComponent(query.value); + const searchUrl = defaultStore.state.searchEngine.replace(/{query}|%s\b/g, searchQuery); + + window.open(searchUrl, '_blank', 'noopener'); }; </script> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 1e4e815d5d..c589a29001 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -63,6 +63,27 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch> <MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch> <MkSwitch v-model="showTickerOnReplies">Show instance ticker on replies</MkSwitch> + <MkSelect v-model="searchEngine" placeholder="Other"> + <template #label>{{ i18n.ts.searchEngine }}</template> + <option + v-for="[key, value] in Object.entries(searchEngineMap)" :key="key" :value="key" + > + {{ value }} + </option> + <!-- If the user is on Other and enters a domain add this one so that the dropdown doesnt go blank --> + <option v-if="!Object.keys(searchEngineMap).includes(searchEngine)" :value="searchEngine"> + {{ i18n.ts.searchEngineOther }} + </option> + <!-- If one of the other options is selected show this as a blank other --> <option v-if="Object.keys(searchEngineMap).includes(searchEngine)" value="">{{ i18n.ts.searchEngineOther }}</option> + </MkSelect> + + <div v-if="!Object.keys(searchEngineMap).includes(searchEngine)"> + <MkInput v-model="searchEngine" :max="300"> + <template #label>{{ i18n.ts.searchEngineCusomURI }}</template> + <template #caption>{{ i18n.ts.searchEngineCustomURIDescription }}</template> + </MkInput> + </div> + <MkRadios v-model="reactionsDisplaySize"> <template #label>{{ i18n.ts.reactionsDisplaySize }}</template> <option value="small">{{ i18n.ts.small }}</option> @@ -271,11 +292,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, ref, watch } from 'vue'; +import { computed, reactive, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkSwitch from '@/components/MkSwitch.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkRadios from '@/components/MkRadios.vue'; +import MkInput from '@/components/MkInput.vue'; import MkRange from '@/components/MkRange.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkButton from '@/components/MkButton.vue'; @@ -284,6 +306,7 @@ import FormLink from '@/components/form/link.vue'; import MkLink from '@/components/MkLink.vue'; import MkInfo from '@/components/MkInfo.vue'; import { langs } from '@/config.js'; +import { searchEngineMap } from '@/scripts/search-engine-map.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; @@ -355,6 +378,9 @@ const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); const showTickerOnReplies = computed(defaultStore.makeGetterSetter('showTickerOnReplies')); +//const searchEngine = computed(defaultStore.makeGetterSetter('searchEngine')); +const searchEngine = computed(defaultStore.makeGetterSetter('searchEngine')); + const noteDesign = computed(defaultStore.makeGetterSetter('noteDesign')); const uncollapseCW = computed(defaultStore.makeGetterSetter('uncollapseCW')); const expandLongNote = computed(defaultStore.makeGetterSetter('expandLongNote')); diff --git a/packages/frontend/src/scripts/search-engine-map.ts b/packages/frontend/src/scripts/search-engine-map.ts new file mode 100644 index 0000000000..925d16aafe --- /dev/null +++ b/packages/frontend/src/scripts/search-engine-map.ts @@ -0,0 +1,12 @@ +//store the URL and if its none of these its a custom one +export const searchEngineMap = { + //The first one is the default search engine + 'https://www.google.com/search?q={query}': 'Google', + 'https://duckduckgo.com?q={query}': 'Duckduckgo', + 'https://www.bing.com/search?q={query}': 'Bing', + 'https://search.yahoo.com/search?p={query}': 'Yahoo', + 'https://www.ecosia.org/search?q={query}': 'Ecosia', + 'https://www.qwant.com?q={query}': 'Qwant', + 'https://search.aol.com/aol/search?q={query}': 'AOL', + 'https://yandex.com/search/?text={query}': 'Yandex', +}; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 2cf17b27c5..ffd63f71fc 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -6,6 +6,7 @@ import { markRaw, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { miLocalStorage } from './local-storage.js'; +import { searchEngineMap } from './scripts/search-engine-map.js'; import type { SoundType } from '@/scripts/sound.js'; import type { BuiltinTheme as ShikiBuiltinTheme } from 'shiki'; import { Storage } from '@/pizzax.js'; @@ -304,6 +305,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + searchEngine: { + where: 'device', + default: Object.keys(searchEngineMap)[0], + }, noteDesign: { where: 'device', default: 'sharkey' as 'sharkey' | 'misskey',