Merge branch 'develop' into feat-1714

This commit is contained in:
かっこかり
2024-07-18 18:58:36 +09:00
committed by GitHub
97 changed files with 1920 additions and 1128 deletions

View File

@@ -53,7 +53,6 @@ await fs.readFile(
'../../assets/**',
'../../fluent-emojis/**',
'../../locales/ja-JP.yml',
'../../misskey-assets/**',
'assets/**',
'public/**',
'../../pnpm-lock.yaml',

View File

@@ -24,7 +24,7 @@
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "5.0.7",
"@rollup/pluginutils": "5.1.0",
"@syuilo/aiscript": "0.18.0",
"@syuilo/aiscript": "0.19.0",
"@tabler/icons-webfont": "3.3.0",
"@twemoji/parser": "15.1.1",
"@vitejs/plugin-vue": "5.0.5",

View File

@@ -30,7 +30,7 @@ import * as os from '@/os.js';
import MkLoading from '@/components/global/MkLoading.vue';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
const props = withDefaults(defineProps<{
code: string;

View File

@@ -39,7 +39,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
import { claimAchievement } from '@/scripts/achievements.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { MenuItem } from '@/types/menu.js';
const props = withDefaults(defineProps<{

View File

@@ -62,7 +62,7 @@ import { computed } from 'vue';
import * as Misskey from 'misskey-js';
import MkFolder from '@/components/MkFolder.vue';
import MkButton from '@/components/MkButton.vue';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';

View File

@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { } from 'vue';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';

View File

@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
}"
:style="{
width: (width && !asDrawer) ? `${width}px` : '',
maxHeight: maxHeight ? `${maxHeight}px` : '',
maxHeight: maxHeight ? `min(${maxHeight}px, calc(100dvh - 32px))` : 'calc(100dvh - 32px)',
}"
@keydown.stop="() => {}"
@contextmenu.self.prevent="() => {}"

View File

@@ -33,7 +33,7 @@ import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue'
import RouterView from '@/components/global/RouterView.vue';
import MkWindow from '@/components/MkWindow.vue';
import { popout as _popout } from '@/scripts/popout.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { url } from '@/config.js';
import { useScrollPositionManager } from '@/nirax.js';
import { i18n } from '@/i18n.js';

View File

@@ -87,7 +87,7 @@ const host = ref(toUnicode(configHost));
const totpLogin = ref(false);
const isBackupCode = ref(false);
const queryingKey = ref(false);
const credentialRequest = ref<CredentialRequestOptions | null>(null);
let credentialRequest: CredentialRequestOptions | null = null;
const emit = defineEmits<{
(ev: 'login', v: any): void;
@@ -122,14 +122,14 @@ function onLogin(res: any): Promise<void> | void {
}
async function queryKey(): Promise<void> {
if (credentialRequest.value == null) return;
if (credentialRequest == null) return;
queryingKey.value = true;
await webAuthnRequest(credentialRequest.value)
await webAuthnRequest(credentialRequest)
.catch(() => {
queryingKey.value = false;
return Promise.reject(null);
}).then(credential => {
credentialRequest.value = null;
credentialRequest = null;
queryingKey.value = false;
signing.value = true;
return misskeyApi('signin', {
@@ -160,7 +160,7 @@ function onSubmit(): void {
}).then(res => {
totpLogin.value = true;
signing.value = false;
credentialRequest.value = parseRequestOptionsFromJSON({
credentialRequest = parseRequestOptionsFromJSON({
publicKey: res,
});
})

View File

@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
scrolling="no"
:allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')"
:class="$style.playerIframe"
:src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')"
:src="transformPlayerUrl(player.url)"
:style="{ border: 0 }"
></iframe>
<span v-else>invalid url</span>
@@ -92,6 +92,7 @@ import * as os from '@/os.js';
import { deviceKind } from '@/scripts/device-kind.js';
import MkButton from '@/components/MkButton.vue';
import { versatileLang } from '@/scripts/intl-const.js';
import { transformPlayerUrl } from '@/scripts/player-url-transform.js';
import { defaultStore } from '@/store.js';
type SummalyResult = Awaited<ReturnType<typeof summaly>>;

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_panel" :class="$style.root">
<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(user.bannerUrl) : user.bannerUrl})` : ''"></div>
<MkAvatar :class="$style.avatar" :user="user" indicator/>
<div :class="$style.title">
<MkA :class="$style.name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
@@ -41,6 +41,8 @@ import { userPage } from '@/filters/user.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
import { defaultStore } from '@/store.js';
defineProps<{
user: Misskey.entities.UserDetailed;

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<div v-if="showing" :class="$style.root" class="_popup _shadow" :style="{ zIndex, top: top + 'px', left: left + 'px' }" @mouseover="() => { emit('mouseover'); }" @mouseleave="() => { emit('mouseleave'); }">
<div v-if="user != null">
<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''">
<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(user.bannerUrl) : user.bannerUrl})` : ''">
<span v-if="$i && $i.id != user.id && user.isFollowed" :class="$style.followed">{{ i18n.ts.followsYou }}</span>
</div>
<svg viewBox="0 0 128 128" :class="$style.avatarBack">
@@ -67,6 +67,7 @@ import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
import { $i } from '@/account.js';
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
const props = defineProps<{
showing: boolean;

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="poamfof">
<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
<div v-if="player.url && (player.url.startsWith('http://') || player.url.startsWith('https://'))" class="player">
<iframe v-if="!fetching" :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen/>
<iframe v-if="!fetching" :src="transformPlayerUrl(player.url)" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
</div>
<span v-else>invalid url</span>
</Transition>
@@ -27,6 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref } from 'vue';
import MkWindow from '@/components/MkWindow.vue';
import { versatileLang } from '@/scripts/intl-const.js';
import { transformPlayerUrl } from '@/scripts/player-url-transform.js';
import { defaultStore } from '@/store.js';
const props = defineProps<{

View File

@@ -16,7 +16,7 @@ export type MkABehavior = 'window' | 'browser' | null;
<script lang="ts" setup>
import { computed, inject, shallowRef } from 'vue';
import * as os from '@/os.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { url } from '@/config.js';
import { i18n } from '@/i18n.js';
import { useRouter } from '@/router/supplier.js';

View File

@@ -31,7 +31,7 @@ import { defaultStore } from '@/store.js';
import { customEmojisMap } from '@/custom-emojis.js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import * as sound from '@/scripts/sound.js';
import { i18n } from '@/i18n.js';
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';

View File

@@ -14,7 +14,7 @@ import { char2fluentEmojiFilePath, char2twemojiFilePath } from '@/scripts/emoji-
import { defaultStore } from '@/store.js';
import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
import * as os from '@/os.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import * as sound from '@/scripts/sound.js';
import { i18n } from '@/i18n.js';

View File

@@ -22,7 +22,7 @@ import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue';
import MkPopupMenu from '@/components/MkPopupMenu.vue';
import MkContextMenu from '@/components/MkContextMenu.vue';
import { MenuItem } from '@/types/menu.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { pleaseLogin } from '@/scripts/please-login.js';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import { embedPage } from '@/config.js';

View File

@@ -93,7 +93,7 @@ import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
import { PageHeaderItem } from '@/types/page-header.js';
import { isSupportShare } from '@/scripts/navigator.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { miLocalStorage } from '@/local-storage.js';
import { useRouter } from '@/router/supplier.js';

View File

@@ -43,7 +43,7 @@ import { url } from '@/config.js';
import MkButton from '@/components/MkButton.vue';
import { clipsCache } from '@/cache.js';
import { isSupportShare } from '@/scripts/navigator.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { copyEmbedCode } from '@/scripts/get-embed-code.js';
const props = defineProps<{

View File

@@ -210,7 +210,7 @@ import { apiUrl } from '@/config.js';
import { $i } from '@/account.js';
import * as sound from '@/scripts/sound.js';
import MkRange from '@/components/MkRange.vue';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
type FrontendMonoDefinition = {
id: string;

View File

@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import * as Misskey from 'misskey-js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { i18n } from '@/i18n.js';
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';

View File

@@ -37,6 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { AISCRIPT_VERSION } from '@syuilo/aiscript';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
@@ -48,7 +49,7 @@ import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import { useRouter } from '@/router/supplier.js';
const PRESET_DEFAULT = `/// @ 0.18.0
const PRESET_DEFAULT = `/// @ ${AISCRIPT_VERSION}
var name = ""
@@ -66,7 +67,7 @@ Ui:render([
])
`;
const PRESET_OMIKUJI = `/// @ 0.18.0
const PRESET_OMIKUJI = `/// @ ${AISCRIPT_VERSION}
// ユーザーごとに日替わりのおみくじのプリセット
// 選択肢
@@ -109,7 +110,7 @@ Ui:render([
])
`;
const PRESET_SHUFFLE = `/// @ 0.18.0
const PRESET_SHUFFLE = `/// @ ${AISCRIPT_VERSION}
// 巻き戻し可能な文字シャッフルのプリセット
let string = "ペペロンチーノ"
@@ -188,7 +189,7 @@ var cursor = 0
do()
`;
const PRESET_QUIZ = `/// @ 0.18.0
const PRESET_QUIZ = `/// @ ${AISCRIPT_VERSION}
let title = '地理クイズ'
let qas = [{
@@ -301,7 +302,7 @@ qaEls.push(Ui:C:container({
Ui:render(qaEls)
`;
const PRESET_TIMELINE = `/// @ 0.18.0
const PRESET_TIMELINE = `/// @ ${AISCRIPT_VERSION}
// APIリクエストを行いローカルタイムラインを表示するプリセット
@fetch() {

View File

@@ -78,7 +78,7 @@ import MkCode from '@/components/MkCode.vue';
import { defaultStore } from '@/store.js';
import { $i } from '@/account.js';
import { isSupportShare } from '@/scripts/navigator.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { pleaseLogin } from '@/scripts/please-login.js';
const props = defineProps<{

View File

@@ -77,7 +77,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import { defaultStore } from '@/store.js';
import { $i } from '@/account.js';
import { isSupportShare } from '@/scripts/navigator.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { useRouter } from '@/router/supplier.js';
const router = useRouter();

View File

@@ -125,7 +125,7 @@ import { $i } from '@/account.js';
import { isSupportShare } from '@/scripts/navigator.js';
import { instance } from '@/instance.js';
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
const props = defineProps<{
pageName: string;

View File

@@ -82,7 +82,7 @@ import MkCode from '@/components/MkCode.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import * as os from '@/os.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { ColdDeviceStorage } from '@/store.js';
import { unisonReload } from '@/scripts/unison-reload.js';
import { i18n } from '@/i18n.js';

View File

@@ -38,7 +38,7 @@ import MkSelect from '@/components/MkSelect.vue';
import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue';
import { Theme, getBuiltinThemesRef } from '@/scripts/theme.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import * as os from '@/os.js';
import { getThemes, removeTheme } from '@/theme-store.js';
import { i18n } from '@/i18n.js';

View File

@@ -167,12 +167,14 @@ import number from '@/filters/number.js';
import { userPage } from '@/filters/user.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
import { $i, iAmModerator } from '@/account.js';
import { dateString } from '@/filters/date.js';
import { confetti } from '@/scripts/confetti.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
import { useRouter } from '@/router/supplier.js';
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
function calcAge(birthdate: string): number {
const date = new Date(birthdate);
@@ -220,8 +222,14 @@ watch(moderationNote, async () => {
const style = computed(() => {
if (props.user.bannerUrl == null) return {};
return {
backgroundImage: `url(${ props.user.bannerUrl })`,
if (defaultStore.state.disableShowingAnimatedImages) {
return {
backgroundImage: `url(${ getStaticImageUrl(props.user.bannerUrl) })`,
};
} else {
return {
backgroundImage: `url(${ props.user.bannerUrl })`,
};
};
});

View File

@@ -6,33 +6,6 @@
/**
* Clipboardに値をコピー(TODO: 文字列以外も対応)
*/
export default val => {
// 空div 生成
const tmp = document.createElement('div');
// 選択用のタグ生成
const pre = document.createElement('pre');
// 親要素のCSSで user-select: none だとコピーできないので書き換える
pre.style.webkitUserSelect = 'auto';
pre.style.userSelect = 'auto';
tmp.appendChild(pre).textContent = val;
// 要素を画面外へ
const s = tmp.style;
s.position = 'fixed';
s.right = '200%';
// body に追加
document.body.appendChild(tmp);
// 要素を選択
document.getSelection().selectAllChildren(tmp);
// クリップボードにコピー
const result = document.execCommand('copy');
// 要素削除
document.body.removeChild(tmp);
return result;
export function copyToClipboard(input: string | null) {
if (input) navigator.clipboard.writeText(input);
};

View File

@@ -6,7 +6,7 @@
import * as Misskey from 'misskey-js';
import { defineAsyncComponent } from 'vue';
import { i18n } from '@/i18n.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { MenuItem } from '@/types/menu.js';

View File

@@ -11,7 +11,7 @@ import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { url } from '@/config.js';
import { defaultStore, noteActions } from '@/store.js';
import { miLocalStorage } from '@/local-storage.js';

View File

@@ -7,7 +7,7 @@ import { toUnicode } from 'punycode';
import { defineAsyncComponent, ref, watch } from 'vue';
import * as Misskey from 'misskey-js';
import { i18n } from '@/i18n.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { host, url } from '@/config.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';

View File

@@ -7,29 +7,24 @@ import { Ref, nextTick } from 'vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { MFM_TAGS } from '@/const.js';
import type { MenuItem } from '@/types/menu.js';
/**
* MFMの装飾のリストを表示する
*/
export function mfmFunctionPicker(src: any, textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) {
return new Promise((res, rej) => {
os.popupMenu([{
text: i18n.ts.addMfmFunction,
type: 'label',
}, ...getFunctionList(textArea, textRef)], src);
});
export function mfmFunctionPicker(src: HTMLElement | EventTarget | null, textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) {
os.popupMenu([{
text: i18n.ts.addMfmFunction,
type: 'label',
}, ...getFunctionList(textArea, textRef)], src);
}
function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) : object[] {
const ret: object[] = [];
MFM_TAGS.forEach(tag => {
ret.push({
text: tag,
icon: 'ti ti-icons',
action: () => add(textArea, textRef, tag),
});
});
return ret;
function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>): MenuItem[] {
return MFM_TAGS.map(tag => ({
text: tag,
icon: 'ti ti-icons',
action: () => add(textArea, textRef, tag),
}));
}
function add(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, type: string) {

View File

@@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { hostname } from '@/config.js';
export function transformPlayerUrl(url: string): string {
const urlObj = new URL(url);
if (!['https:', 'http:'].includes(urlObj.protocol)) throw new Error('Invalid protocol');
const urlParams = new URLSearchParams(urlObj.search);
if (urlObj.hostname === 'player.twitch.tv') {
// TwitchはCSPの制約あり
// https://dev.twitch.tv/docs/embed/video-and-clips/
urlParams.set('parent', hostname);
urlParams.set('allowfullscreen', '');
urlParams.set('autoplay', 'true');
} else {
urlParams.set('autoplay', '1');
urlParams.set('auto_play', '1');
}
urlObj.search = urlParams.toString();
return urlObj.toString();
}