Merge cdf4b71b04 into ae2ac9d50f
This commit is contained in:
commit
9c40c9c53c
|
|
@ -14,6 +14,8 @@
|
|||
- Enhance: ウィジェットの表示設定をプレビューを見ながら行えるように
|
||||
- Enhance: ウィジェットの設定項目のラベルの多言語対応
|
||||
- Enhance: 画面幅が広いときにメディアを横並びで表示できるようにするオプションを追加
|
||||
- Enhance: 外部サイトへのリンクは移動の前に警告を表示するように
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/558 and https://github.com/MisskeyIO/misskey/commit/f7ec503b9ceb34d61a0dbd658858915eb7399c5d)
|
||||
- Enhance: パフォーマンスの向上
|
||||
- Fix: ドライブクリーナーでファイルを削除しても画面に反映されない問題を修正 #16061
|
||||
- Fix: 非ログイン時にログインを求めるダイアログが表示された後にダイアログのぼかしが解除されず操作不能になることがある問題を修正
|
||||
|
|
|
|||
|
|
@ -1260,6 +1260,8 @@ useGroupedNotifications: "通知をグルーピング"
|
|||
emailVerificationFailedError: "メールアドレスの確認中に問題が発生しました。リンクの有効期限が切れている可能性があります。"
|
||||
cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要です。"
|
||||
doReaction: "リアクションする"
|
||||
trustedLinkUrlPatterns: "外部サイトへのリンク警告 除外リスト"
|
||||
trustedLinkUrlPatternsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。スラッシュで囲むと正規表現になります。ドメイン名だけ書くと後方一致になります。"
|
||||
code: "コード"
|
||||
reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"
|
||||
remainingN: "残り: {n}"
|
||||
|
|
@ -1408,6 +1410,7 @@ frame: "フレーム"
|
|||
presets: "プリセット"
|
||||
zeroPadding: "ゼロ埋め"
|
||||
nothingToConfigure: "設定項目はありません"
|
||||
open: "開く"
|
||||
|
||||
_imageEditing:
|
||||
_vars:
|
||||
|
|
@ -3201,6 +3204,11 @@ _urlPreviewSetting:
|
|||
summaryProxyDescription: "Misskey本体ではなく、サマリープロキシを使用してプレビューを生成します。"
|
||||
summaryProxyDescription2: "プロキシには下記パラメータがクエリ文字列として連携されます。プロキシ側がこれらをサポートしない場合、設定値は無視されます。"
|
||||
|
||||
_externalNavigationWarning:
|
||||
title: "外部サイトに移動します"
|
||||
description: "{host}を離れて外部サイトに移動します"
|
||||
trustThisDomain: "このデバイスで今後このドメインを信頼する"
|
||||
|
||||
_mediaControls:
|
||||
pip: "ピクチャインピクチャ"
|
||||
playbackRate: "再生速度"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class ExternalWebsiteWarn1762172837314 {
|
||||
name = 'ExternalWebsiteWarn1762172837314'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "trustedLinkUrlPatterns" character varying(3072) array NOT NULL DEFAULT '{}'`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "trustedLinkUrlPatterns"`);
|
||||
}
|
||||
}
|
||||
|
|
@ -119,6 +119,7 @@ export class MetaEntityService {
|
|||
dayOfWeek: ad.dayOfWeek,
|
||||
isSensitive: ad.isSensitive ? true : undefined,
|
||||
})),
|
||||
trustedLinkUrlPatterns: instance.trustedLinkUrlPatterns,
|
||||
notesPerOneAd: instance.notesPerOneAd,
|
||||
enableEmail: instance.enableEmail,
|
||||
enableServiceWorker: instance.enableServiceWorker,
|
||||
|
|
|
|||
|
|
@ -639,6 +639,18 @@ export class MiMeta {
|
|||
})
|
||||
public urlPreviewRequireContentLength: boolean;
|
||||
|
||||
/**
|
||||
* An array of URL strings or regex that can be used to omit warnings about redirects to external sites.
|
||||
* Separate them with spaces to specify AND, and enclose them with slashes to specify regular expressions.
|
||||
* Each item is regarded as an OR.
|
||||
*/
|
||||
@Column('varchar', {
|
||||
length: 3072,
|
||||
array: true,
|
||||
default: '{}',
|
||||
})
|
||||
public trustedLinkUrlPatterns: string[];
|
||||
|
||||
@Column('varchar', {
|
||||
length: 1024,
|
||||
nullable: true,
|
||||
|
|
|
|||
|
|
@ -201,6 +201,14 @@ export const packedMetaLiteSchema = {
|
|||
},
|
||||
},
|
||||
},
|
||||
trustedLinkUrlPatterns: {
|
||||
type: 'array',
|
||||
optional: false, nullable: false,
|
||||
items: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
},
|
||||
notesPerOneAd: {
|
||||
type: 'number',
|
||||
optional: false, nullable: false,
|
||||
|
|
|
|||
|
|
@ -407,6 +407,14 @@ export const meta = {
|
|||
type: 'number',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
trustedLinkUrlPatterns: {
|
||||
type: 'array',
|
||||
optional: false, nullable: false,
|
||||
items: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
},
|
||||
backgroundImageUrl: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
|
|
@ -730,6 +738,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
perRemoteUserUserTimelineCacheMax: instance.perRemoteUserUserTimelineCacheMax,
|
||||
perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax,
|
||||
perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax,
|
||||
trustedLinkUrlPatterns: instance.trustedLinkUrlPatterns,
|
||||
enableReactionsBuffering: instance.enableReactionsBuffering,
|
||||
notesPerOneAd: instance.notesPerOneAd,
|
||||
summalyProxy: instance.urlPreviewSummaryProxyUrl,
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ export const paramDef = {
|
|||
type: 'string', nullable: true,
|
||||
description: '[Deprecated] Use "urlPreviewSummaryProxyUrl" instead.',
|
||||
},
|
||||
trustedLinkUrlPatterns: {
|
||||
type: 'array', nullable: true, items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
urlPreviewEnabled: { type: 'boolean' },
|
||||
urlPreviewAllowRedirect: { type: 'boolean' },
|
||||
urlPreviewTimeout: { type: 'integer' },
|
||||
|
|
@ -267,6 +272,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(ps.trustedLinkUrlPatterns)) {
|
||||
set.trustedLinkUrlPatterns = ps.trustedLinkUrlPatterns.filter(Boolean);
|
||||
}
|
||||
|
||||
if (Array.isArray(ps.mediaSilencedHosts)) {
|
||||
let lastValue = '';
|
||||
set.mediaSilencedHosts = ps.mediaSilencedHosts.sort().filter((h) => {
|
||||
|
|
@ -275,6 +285,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
|
||||
});
|
||||
}
|
||||
|
||||
if (ps.themeColor !== undefined) {
|
||||
set.themeColor = ps.themeColor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="maybeRelativeUrl" :rel="rel ?? 'nofollow noopener'" :target="target"
|
||||
:behavior="props.navigationBehavior"
|
||||
:title="url"
|
||||
@click="(ev: MouseEvent) => warningExternalWebsite(ev, props.url)"
|
||||
>
|
||||
<slot></slot>
|
||||
<i v-if="target === '_blank'" class="ti ti-external-link" :class="$style.icon"></i>
|
||||
|
|
@ -22,18 +23,20 @@ import type { MkABehavior } from '@/components/global/MkA.vue';
|
|||
import { useTooltip } from '@/composables/use-tooltip.js';
|
||||
import * as os from '@/os.js';
|
||||
import { isEnabledUrlPreview } from '@/utility/url-preview.js';
|
||||
import { warningExternalWebsite } from '@/utility/warning-external-website.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
url: string;
|
||||
rel?: null | string;
|
||||
navigationBehavior?: MkABehavior;
|
||||
}>(), {
|
||||
rel: 'nofollow noopener',
|
||||
});
|
||||
|
||||
const maybeRelativeUrl = maybeMakeRelative(props.url, local);
|
||||
const self = maybeRelativeUrl !== props.url;
|
||||
const attr = self ? 'to' : 'href';
|
||||
const target = self ? null : '_blank';
|
||||
const target = self ? undefined : '_blank';
|
||||
|
||||
const el = ref<HTMLElement | { $el: HTMLElement }>();
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</template>
|
||||
<div v-else>
|
||||
<component :is="self ? 'MkA' : 'a'" :class="[$style.link, { [$style.compact]: compact }]" :[attr]="maybeRelativeUrl" rel="nofollow noopener" :target="target" :title="url">
|
||||
<component
|
||||
:is="self ? 'MkA' : 'a'"
|
||||
:class="[$style.link, { [$style.compact]: compact }]"
|
||||
:[attr]="maybeRelativeUrl"
|
||||
rel="nofollow noopener"
|
||||
:target="target"
|
||||
:title="url"
|
||||
@click="(ev: MouseEvent) => warningExternalWebsite(ev, props.url)"
|
||||
>
|
||||
<div v-if="thumbnail && !sensitive" :class="$style.thumbnail" :style="prefer.s.dataSaver.urlPreviewThumbnail ? '' : { backgroundImage: `url('${thumbnail}')` }">
|
||||
</div>
|
||||
<article :class="$style.body">
|
||||
|
|
@ -94,6 +102,7 @@ import MkButton from '@/components/MkButton.vue';
|
|||
import { transformPlayerUrl } from '@/utility/url-preview.js';
|
||||
import { store } from '@/store.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { warningExternalWebsite } from '@/utility/warning-external-website.js';
|
||||
import { maybeMakeRelative } from '@@/js/url.js';
|
||||
|
||||
type SummalyResult = Awaited<ReturnType<typeof summaly>>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModal ref="modal" :preferType="'dialog'" :zPriority="'high'" @click="done(true)" @closed="emit('closed')">
|
||||
<div :class="$style.root" class="_gaps">
|
||||
<div class="_gaps_s">
|
||||
<div :class="$style.header">
|
||||
<div :class="$style.icon">
|
||||
<i class="ti ti-alert-triangle"></i>
|
||||
</div>
|
||||
<div :class="$style.title">{{ i18n.ts._externalNavigationWarning.title }}</div>
|
||||
</div>
|
||||
<div><Mfm :text="i18n.tsx._externalNavigationWarning.description({ host: instanceName })"/></div>
|
||||
<div class="_monospace" :class="$style.urlAddress">{{ url }}</div>
|
||||
<div>
|
||||
<MkSwitch v-model="trustThisDomain">{{ i18n.ts._externalNavigationWarning.trustThisDomain }}</MkSwitch>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.buttons">
|
||||
<MkButton inline rounded @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||
<MkButton inline primary rounded @click="ok"><i class="ti ti-external-link"></i> {{ i18n.ts.open }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
type Result = string | number | true | null;
|
||||
|
||||
export type MkUrlWarningDialogDoneEvent = { canceled: true } | { canceled: false, result: Result };
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from 'vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { store } from '@/store.js';
|
||||
import { instanceName } from '@@/js/config.js';
|
||||
|
||||
const props = defineProps<{
|
||||
url: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'done', v: MkUrlWarningDialogDoneEvent): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
const trustThisDomain = ref(false);
|
||||
|
||||
const domain = computed(() => new URL(props.url).hostname);
|
||||
|
||||
// overload function を使いたいので lint エラーを無視する
|
||||
function done(canceled: true): void;
|
||||
function done(canceled: false, result: Result): void; // eslint-disable-line no-redeclare
|
||||
|
||||
function done(canceled: boolean, result?: Result): void { // eslint-disable-line no-redeclare
|
||||
emit('done', { canceled, result } as MkUrlWarningDialogDoneEvent);
|
||||
modal.value?.close();
|
||||
}
|
||||
|
||||
async function ok() {
|
||||
const result = true;
|
||||
if (!store.s.trustedDomains.includes(domain.value) && trustThisDomain.value) {
|
||||
store.set('trustedDomains', store.s.trustedDomains.concat(domain.value));
|
||||
}
|
||||
done(false, result);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
done(true);
|
||||
}
|
||||
|
||||
/*
|
||||
function onBgClick() {
|
||||
if (props.cancelableByBgClick) cancel();
|
||||
}
|
||||
*/
|
||||
function onKeydown(evt: KeyboardEvent) {
|
||||
if (evt.key === 'Escape') cancel();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.document.addEventListener('keydown', onKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.document.removeEventListener('keydown', onKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
padding: 32px;
|
||||
width: 100%;
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
box-sizing: border-box;
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75em;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 18px;
|
||||
color: var(--MI_THEME-warn);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.urlAddress {
|
||||
padding: 10px 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--MI_THEME-divider);
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: right;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:is="self ? 'MkA' : 'a'" ref="el" :class="$style.root" class="_link" :[attr]="maybeRelativeUrl" :rel="rel ?? 'nofollow noopener'" :target="target"
|
||||
:behavior="props.navigationBehavior"
|
||||
@contextmenu.stop="() => {}"
|
||||
@click="(ev: MouseEvent) => warningExternalWebsite(ev, props.url)"
|
||||
>
|
||||
<template v-if="!self">
|
||||
<span :class="$style.schema">{{ schema }}//</span>
|
||||
|
|
@ -33,6 +34,7 @@ import type { MkABehavior } from '@/components/global/MkA.vue';
|
|||
import * as os from '@/os.js';
|
||||
import { useTooltip } from '@/composables/use-tooltip.js';
|
||||
import { isEnabledUrlPreview } from '@/utility/url-preview.js';
|
||||
import { warningExternalWebsite } from '@/utility/warning-external-website.js';
|
||||
|
||||
function safeURIDecode(str: string): string {
|
||||
try {
|
||||
|
|
@ -48,6 +50,7 @@ const props = withDefaults(defineProps<{
|
|||
showUrlPreview?: boolean;
|
||||
navigationBehavior?: MkABehavior;
|
||||
}>(), {
|
||||
rel: 'nofollow noopener',
|
||||
showUrlPreview: true,
|
||||
});
|
||||
|
||||
|
|
@ -76,7 +79,7 @@ const pathname = safeURIDecode(url.pathname);
|
|||
const query = safeURIDecode(url.search);
|
||||
const hash = safeURIDecode(url.hash);
|
||||
const attr = self ? 'to' : 'href';
|
||||
const target = self ? null : '_blank';
|
||||
const target = self ? undefined : '_blank';
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
|
|
|||
|
|
@ -106,6 +106,20 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['trust', 'url', 'link']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-link"></i></SearchIcon></template>
|
||||
<template #label><SearchLabel>{{ i18n.ts.trustedLinkUrlPatterns }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkTextarea v-model="trustedLinkUrlPatterns">
|
||||
<template #caption>{{ i18n.ts.trustedLinkUrlPatternsDescription }}</template>
|
||||
</MkTextarea>
|
||||
<MkButton primary @click="save_trustedLinkUrlPatterns">{{ i18n.ts.save }}</MkButton>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['silenced', 'servers', 'hosts']">
|
||||
<MkFolder>
|
||||
<template #icon><SearchIcon><i class="ti ti-eye-off"></i></SearchIcon></template>
|
||||
|
|
@ -194,6 +208,7 @@ 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'));
|
||||
const trustedLinkUrlPatterns = ref(meta.trustedLinkUrlPatterns.join('\n'));
|
||||
|
||||
async function onChange_enableRegistration(value: boolean) {
|
||||
if (value) {
|
||||
|
|
@ -269,6 +284,14 @@ function save_hiddenTags() {
|
|||
});
|
||||
}
|
||||
|
||||
function save_trustedLinkUrlPatterns() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
trustedLinkUrlPatterns: trustedLinkUrlPatterns.value.split('\n'),
|
||||
}).then(() => {
|
||||
fetchInstance(true);
|
||||
});
|
||||
}
|
||||
|
||||
function save_blockedHosts() {
|
||||
os.apiWithDialog('admin/update-meta', {
|
||||
blockedHosts: blockedHosts.value.split('\n') || [],
|
||||
|
|
|
|||
|
|
@ -119,6 +119,11 @@ export const store = markRaw(new Pizzax('base', {
|
|||
default: true,
|
||||
},
|
||||
|
||||
trustedDomains: {
|
||||
where: 'device',
|
||||
default: [] as string[],
|
||||
},
|
||||
|
||||
//#region TODO: そのうち消す (preferに移行済み)
|
||||
defaultWithReplies: {
|
||||
where: 'account',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { url as local } from '@@/js/config.js';
|
||||
import { extractDomain } from '@@/js/url.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { store } from '@/store.js';
|
||||
import * as os from '@/os.js';
|
||||
import MkUrlWarningDialog from '@/components/MkUrlWarningDialog.vue';
|
||||
import type { MkUrlWarningDialogDoneEvent } from '@/components/MkUrlWarningDialog.vue';
|
||||
|
||||
const isRegExp = /^\/(.+)\/(.*)$/;
|
||||
|
||||
export async function warningExternalWebsite(ev: MouseEvent, url: string) {
|
||||
const domain = extractDomain(url);
|
||||
const self = (domain == null || url.startsWith(local));
|
||||
|
||||
const isTrustedByInstance = self || instance.trustedLinkUrlPatterns.some(expression => {
|
||||
const r = isRegExp.exec(expression);
|
||||
if (r) {
|
||||
return new RegExp(r[1], r[2]).test(url);
|
||||
} else if (expression.includes(' ')) {
|
||||
return expression.split(' ').every(keyword => url.includes(keyword));
|
||||
} else {
|
||||
return domain.endsWith(expression);
|
||||
}
|
||||
});
|
||||
|
||||
const isTrustedByUser = domain != null && store.s.trustedDomains.includes(domain);
|
||||
|
||||
if (!self && !isTrustedByInstance && !isTrustedByUser) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const confirm = await new Promise<{ canceled: boolean }>(resolve => {
|
||||
const { dispose } = os.popup(MkUrlWarningDialog, {
|
||||
url,
|
||||
}, {
|
||||
done: (result: MkUrlWarningDialogDoneEvent) => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
},
|
||||
closed: () => {
|
||||
dispose();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (confirm.canceled) return false;
|
||||
|
||||
window.open(url, '_blank', 'noopener');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -5052,6 +5052,14 @@ export interface Locale extends ILocale {
|
|||
* リアクションする
|
||||
*/
|
||||
"doReaction": string;
|
||||
/**
|
||||
* 外部サイトへのリンク警告 除外リスト
|
||||
*/
|
||||
"trustedLinkUrlPatterns": string;
|
||||
/**
|
||||
* スペースで区切るとAND指定になり、改行で区切るとOR指定になります。スラッシュで囲むと正規表現になります。ドメイン名だけ書くと後方一致になります。
|
||||
*/
|
||||
"trustedLinkUrlPatternsDescription": string;
|
||||
/**
|
||||
* コード
|
||||
*/
|
||||
|
|
@ -5647,6 +5655,10 @@ export interface Locale extends ILocale {
|
|||
* 設定項目はありません
|
||||
*/
|
||||
"nothingToConfigure": string;
|
||||
/**
|
||||
* 開く
|
||||
*/
|
||||
"open": string;
|
||||
"_imageEditing": {
|
||||
"_vars": {
|
||||
/**
|
||||
|
|
@ -12023,6 +12035,20 @@ export interface Locale extends ILocale {
|
|||
*/
|
||||
"summaryProxyDescription2": string;
|
||||
};
|
||||
"_externalNavigationWarning": {
|
||||
/**
|
||||
* 外部サイトに移動します
|
||||
*/
|
||||
"title": string;
|
||||
/**
|
||||
* {host}を離れて外部サイトに移動します
|
||||
*/
|
||||
"description": ParameterizedString<"host">;
|
||||
/**
|
||||
* このデバイスで今後このドメインを信頼する
|
||||
*/
|
||||
"trustThisDomain": string;
|
||||
};
|
||||
"_mediaControls": {
|
||||
/**
|
||||
* ピクチャインピクチャ
|
||||
|
|
|
|||
|
|
@ -5479,6 +5479,7 @@ export type components = {
|
|||
dayOfWeek: number;
|
||||
isSensitive?: boolean;
|
||||
}[];
|
||||
trustedLinkUrlPatterns: string[];
|
||||
/** @default 0 */
|
||||
notesPerOneAd: number;
|
||||
enableEmail: boolean;
|
||||
|
|
@ -9469,6 +9470,7 @@ export interface operations {
|
|||
perUserListTimelineCacheMax: number;
|
||||
enableReactionsBuffering: boolean;
|
||||
notesPerOneAd: number;
|
||||
trustedLinkUrlPatterns: string[];
|
||||
backgroundImageUrl: string | null;
|
||||
deeplAuthKey: string | null;
|
||||
deeplIsPro: boolean;
|
||||
|
|
@ -12822,6 +12824,7 @@ export interface operations {
|
|||
mediaSilencedHosts?: string[] | null;
|
||||
/** @description [Deprecated] Use "urlPreviewSummaryProxyUrl" instead. */
|
||||
summalyProxy?: string | null;
|
||||
trustedLinkUrlPatterns?: string[] | null;
|
||||
urlPreviewEnabled?: boolean;
|
||||
urlPreviewAllowRedirect?: boolean;
|
||||
urlPreviewTimeout?: number;
|
||||
|
|
|
|||
Loading…
Reference in New Issue