mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-23 19:14:19 +02:00
Merge branch 'develop' into copilot/add-user-mute-settings
This commit is contained in:
@@ -13,17 +13,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkPagination>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts" setup generic="P extends IPaginator">
|
||||
import * as Misskey from 'misskey-js';
|
||||
import type { IPaginator } from '@/utility/paginator.js';
|
||||
import type { IPaginator, ExtractorFunction } from '@/utility/paginator.js';
|
||||
import MkChannelPreview from '@/components/MkChannelPreview.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
paginator: IPaginator;
|
||||
paginator: P;
|
||||
noGap?: boolean;
|
||||
extractor?: (item: any) => Misskey.entities.Channel;
|
||||
extractor?: ExtractorFunction<P, Misskey.entities.Channel>;
|
||||
}>(), {
|
||||
extractor: (item) => item,
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ export const Default = {
|
||||
};
|
||||
},
|
||||
args: {
|
||||
imageFile: file(),
|
||||
imageFile: new File([], 'image.webp', { type: 'image/webp' }),
|
||||
aspectRatio: NaN,
|
||||
},
|
||||
parameters: {
|
||||
|
||||
@@ -60,7 +60,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<button class="_buttonPrimary" style="padding: 4px; border-radius: 8px;" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<MkInfo v-if="!store.r.tips.value.postForm" :class="$style.showHowToUse"><button class="_textButton" @click="showTour">{{ i18n.ts._postForm.showHowToUse }}</button></MkInfo>
|
||||
<MkInfo v-if="!store.r.tips.value.postForm" :class="$style.showHowToUse" closable @close="closeTip('postForm')">
|
||||
<button class="_textButton" @click="showTour">{{ i18n.ts._postForm.showHowToUse }}</button>
|
||||
</MkInfo>
|
||||
<MkInfo v-if="scheduledAt != null" :class="$style.scheduledAt">
|
||||
<I18n :src="i18n.ts.scheduleToPostOnX" tag="span">
|
||||
<template #x>
|
||||
|
||||
@@ -120,6 +120,7 @@ function onPasskeyLogin(): void {
|
||||
.then((res) => {
|
||||
passkeyContext.value = res.context ?? '';
|
||||
credentialRequest.value = parseRequestOptionsFromJSON({
|
||||
// @ts-expect-error TODO: misskey-js由来の型(@simplewebauthn/types)とフロントエンド由来の型(@github/webauthn-json)が合わない
|
||||
publicKey: res.option,
|
||||
});
|
||||
|
||||
@@ -134,7 +135,7 @@ function onPasskeyDone(credential: AuthenticationPublicKeyCredential): void {
|
||||
waiting.value = true;
|
||||
|
||||
if (doingPasskeyFromInputPage.value) {
|
||||
misskeyApi('signin-with-passkey', {
|
||||
misskeyApi<Misskey.entities.SigninWithPasskeyResponse>('signin-with-passkey', {
|
||||
credential: credential.toJSON(),
|
||||
context: passkeyContext.value,
|
||||
}).then((res) => {
|
||||
@@ -149,6 +150,7 @@ function onPasskeyDone(credential: AuthenticationPublicKeyCredential): void {
|
||||
tryLogin({
|
||||
username: userInfo.value.username,
|
||||
password: password.value,
|
||||
// @ts-expect-error TODO: misskey-js由来の型(@simplewebauthn/types)とフロントエンド由来の型(@github/webauthn-json)が合わない
|
||||
credential: credential.toJSON(),
|
||||
});
|
||||
}
|
||||
@@ -253,6 +255,7 @@ async function tryLogin(req: Partial<Misskey.entities.SigninFlowRequest>): Promi
|
||||
case 'passkey': {
|
||||
if (webAuthnSupported()) {
|
||||
credentialRequest.value = parseRequestOptionsFromJSON({
|
||||
// @ts-expect-error TODO: misskey-js由来の型(@simplewebauthn/types)とフロントエンド由来の型(@github/webauthn-json)が合わない
|
||||
publicKey: res.authRequest,
|
||||
});
|
||||
page.value = 'passkey';
|
||||
|
||||
@@ -15,17 +15,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkPagination>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts" setup generic="P extends IPaginator">
|
||||
import * as Misskey from 'misskey-js';
|
||||
import type { IPaginator } from '@/utility/paginator.js';
|
||||
import type { IPaginator, ExtractorFunction } from '@/utility/paginator.js';
|
||||
import MkUserInfo from '@/components/MkUserInfo.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
paginator: IPaginator;
|
||||
paginator: P;
|
||||
noGap?: boolean;
|
||||
extractor?: (item: any) => Misskey.entities.UserDetailed;
|
||||
extractor?: ExtractorFunction<P, Misskey.entities.UserDetailed>;
|
||||
}>(), {
|
||||
extractor: (item) => item,
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<h1 :class="$style.mainTitle">
|
||||
<!-- 背景色によってはロゴが見えなくなるのでとりあえず無効に -->
|
||||
<!-- <img class="logo" v-if="instance.logoImageUrl" :src="instance.logoImageUrl"><span v-else class="text">{{ instanceName }}</span> -->
|
||||
<span>{{ instanceName }}</span>
|
||||
<MkA to="/">{{ instanceName }}</MkA>
|
||||
</h1>
|
||||
<div :class="$style.mainAbout">
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
|
||||
@@ -19,10 +19,13 @@ export function useForm<T extends Record<string, any>>(initialState: T, save: (n
|
||||
const currentState = reactive<T>(copy(initialState));
|
||||
const previousState = reactive<T>(copy(initialState));
|
||||
|
||||
const modifiedStates = reactive<Record<keyof T, boolean>>({} as any);
|
||||
for (const key in currentState) {
|
||||
modifiedStates[key] = false;
|
||||
}
|
||||
const modifiedStates = reactive<Record<keyof T, boolean>>((() => {
|
||||
const obj: Record<keyof T, boolean> = {} as Record<keyof T, boolean>;
|
||||
for (const key in initialState) {
|
||||
obj[key] = false;
|
||||
}
|
||||
return obj;
|
||||
})());
|
||||
const modified = computed(() => Object.values(modifiedStates).some(v => v));
|
||||
const modifiedCount = computed(() => Object.values(modifiedStates).filter(v => v).length);
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
*/
|
||||
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
import type { PageMetadata } from '@/page.js';
|
||||
import type { Router } from '@/router.js';
|
||||
|
||||
export const DI = {
|
||||
routerCurrentDepth: Symbol() as InjectionKey<number>,
|
||||
router: Symbol() as InjectionKey<Router>,
|
||||
mock: Symbol() as InjectionKey<boolean>,
|
||||
pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any> | null>>,
|
||||
pageMetadata: Symbol() as InjectionKey<Ref<PageMetadata | null>>,
|
||||
viewId: Symbol() as InjectionKey<string>,
|
||||
currentStickyTop: Symbol() as InjectionKey<Ref<number>>,
|
||||
currentStickyBottom: Symbol() as InjectionKey<Ref<number>>,
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { Directive } from 'vue';
|
||||
import { getBgColor } from '@/utility/get-bg-color.js';
|
||||
import { globalEvents } from '@/events.js';
|
||||
|
||||
const handlerMap = new WeakMap<any, any>();
|
||||
const handlerMap = new WeakMap<HTMLElement, () => void>();
|
||||
|
||||
export const adaptiveBorderDirective = {
|
||||
mounted(src) {
|
||||
|
||||
@@ -119,9 +119,9 @@ export const userPreviewDirective = {
|
||||
|
||||
// TODO: 新たにプロパティを作るのをやめMapを使う
|
||||
// ただメモリ的には↓の方が省メモリかもしれないので検討中
|
||||
const self = (el as any)._userPreviewDirective_ = {} as any;
|
||||
|
||||
self.preview = new UserPreview(el, binding.value);
|
||||
el._userPreviewDirective_ = {
|
||||
preview: new UserPreview(el, binding.value),
|
||||
};
|
||||
},
|
||||
|
||||
unmounted(el, binding) {
|
||||
|
||||
@@ -59,7 +59,7 @@ export class Pizzax<T extends StateDef> {
|
||||
private pizzaxChannel: BroadcastChannel<PizzaxChannelMessage<T>>;
|
||||
|
||||
// 簡易的にキューイングして占有ロックとする
|
||||
private currentIdbJob: Promise<any> = Promise.resolve();
|
||||
private currentIdbJob: Promise<unknown> = Promise.resolve();
|
||||
private addIdbSetJob<T>(job: () => Promise<T>) {
|
||||
const promise = this.currentIdbJob.then(job, err => {
|
||||
console.error('Pizzax failed to save data to idb!', err);
|
||||
|
||||
@@ -257,7 +257,7 @@ const {
|
||||
const user = ref(result.user);
|
||||
const info = ref(result.info);
|
||||
const ips = ref(result.ips);
|
||||
const ap = ref<any>(null);
|
||||
const ap = ref<Misskey.entities.ApGetResponse | null>(null);
|
||||
const moderator = ref(info.value.isModerator);
|
||||
const silenced = ref(info.value.isSilenced);
|
||||
const suspended = ref(info.value.isSuspended);
|
||||
|
||||
@@ -44,7 +44,7 @@ async function addRelay() {
|
||||
if (canceled || inbox == null) return;
|
||||
misskeyApi('admin/relays/add', {
|
||||
inbox,
|
||||
}).then((relay: any) => {
|
||||
}).then(() => {
|
||||
refresh();
|
||||
}).catch((err: any) => {
|
||||
os.alert({
|
||||
|
||||
@@ -161,7 +161,11 @@ async function _fetch_() {
|
||||
},
|
||||
raw: res.data,
|
||||
};
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
switch (err.message.toLowerCase()) {
|
||||
case 'this theme is already installed':
|
||||
errorKV.value = {
|
||||
|
||||
@@ -36,7 +36,17 @@ misskeyApi('antennas/show', { antennaId: props.antennaId }).then((antennaRespons
|
||||
antenna.value = antennaResponse;
|
||||
});
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
const headerActions = computed(() => antenna.value ? [{
|
||||
icon: 'ti ti-timeline',
|
||||
text: i18n.ts.timeline,
|
||||
handler: () => {
|
||||
router.push('/timeline/antenna/:antennaId', {
|
||||
params: {
|
||||
antennaId: antenna.value!.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
}] : []);
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => ({
|
||||
|
||||
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkButton :link="true" to="/my/antennas/create" primary :class="$style.add"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||
|
||||
<div v-if="antennas.length > 0" class="_gaps">
|
||||
<MkA v-for="antenna in antennas" :key="antenna.id" :class="$style.antenna" :to="`/my/antennas/${antenna.id}`">
|
||||
<MkA v-for="antenna in antennas" :key="antenna.id" :class="$style.antenna" :to="`/timeline/antenna/${antenna.id}`">
|
||||
<div class="name">{{ antenna.name }}</div>
|
||||
</MkA>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkButton primary rounded style="margin: 0 auto;" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.createList }}</MkButton>
|
||||
|
||||
<div v-if="items.length > 0" class="_gaps">
|
||||
<MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/my/lists/${ list.id }`">
|
||||
<MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/timeline/list/${list.id}`">
|
||||
<div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.tsx.nUsers({ n: `${list.userIds!.length}/${$i.policies['userEachUserListsLimit']}` }) }})</span></div>
|
||||
<MkAvatars :userIds="list.userIds!" :limit="10"/>
|
||||
</MkA>
|
||||
|
||||
@@ -26,10 +26,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #label>{{ i18n.ts.members }}</template>
|
||||
<template #caption>{{ i18n.tsx.nUsers({ n: `${list.userIds!.length}/${$i.policies['userEachUserListsLimit']}` }) }}</template>
|
||||
|
||||
<div class="_gaps_s">
|
||||
<MkButton rounded primary style="margin: 0 auto;" @click="addUser()">{{ i18n.ts.addUser }}</MkButton>
|
||||
<div class="_gaps">
|
||||
<MkButton rounded primary style="margin: 0 auto;" @click="addUser()"><i class="ti ti-plus"></i> {{ i18n.ts.addUser }}</MkButton>
|
||||
|
||||
<MkPagination :paginator="membershipsPaginator" withControl>
|
||||
<MkPagination :paginator="membershipsPaginator">
|
||||
<template #default="{ items }">
|
||||
<div class="_gaps_s">
|
||||
<div v-for="item in items" :key="item.id">
|
||||
@@ -67,12 +67,13 @@ import MkInput from '@/components/MkInput.vue';
|
||||
import { userListsCache } from '@/cache.js';
|
||||
import { ensureSignin } from '@/i.js';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import { mainRouter } from '@/router.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { useRouter } from '@/router.js';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
|
||||
const $i = ensureSignin();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
listId: string;
|
||||
}>();
|
||||
@@ -162,7 +163,7 @@ async function deleteList() {
|
||||
listId: list.value.id,
|
||||
});
|
||||
userListsCache.delete();
|
||||
mainRouter.push('/my/lists');
|
||||
router.push('/my/lists');
|
||||
}
|
||||
|
||||
async function updateSettings() {
|
||||
@@ -181,7 +182,17 @@ async function updateSettings() {
|
||||
|
||||
watch(() => props.listId, fetchList, { immediate: true });
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
const headerActions = computed(() => list.value ? [{
|
||||
icon: 'ti ti-timeline',
|
||||
text: i18n.ts.timeline,
|
||||
handler: () => {
|
||||
router.push('/timeline/list/:listId', {
|
||||
params: {
|
||||
listId: list.value!.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
}] : []);
|
||||
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ import { Interpreter, Parser, utils } from '@syuilo/aiscript';
|
||||
import type { Ref } from 'vue';
|
||||
import type { AsUiComponent } from '@/aiscript/ui.js';
|
||||
import type { AsUiRoot } from '@/aiscript/ui.js';
|
||||
import type { Value } from '@syuilo/aiscript/interpreter/value.js';
|
||||
import MkContainer from '@/components/MkContainer.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
@@ -141,7 +142,7 @@ async function run() {
|
||||
switch (type) {
|
||||
case 'end': logs.value.push({
|
||||
id: Math.random(),
|
||||
text: utils.valToString(params.val, true),
|
||||
text: utils.valToString(params.val as Value, true),
|
||||
print: false,
|
||||
}); break;
|
||||
default: break;
|
||||
|
||||
@@ -603,6 +603,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif']">
|
||||
<MkPreferenceContainer k="disableShowingAnimatedImages">
|
||||
<MkSwitch :modelValue="!disableShowingAnimatedImages" @update:modelValue="v => disableShowingAnimatedImages = !v">
|
||||
<template #label><SearchLabel>{{ i18n.ts._settings.enableAnimatedImages }}</SearchLabel></template>
|
||||
<template #caption>
|
||||
<SearchText>{{ i18n.ts.turnOffToImprovePerformance }}</SearchText>
|
||||
<div>{{ i18n.ts.disableShowingAnimatedImages_caption }}</div>
|
||||
</template>
|
||||
</MkSwitch>
|
||||
</MkPreferenceContainer>
|
||||
</SearchMarker>
|
||||
|
||||
<SearchMarker :keywords="['blur']">
|
||||
<MkPreferenceContainer k="useBlurEffect">
|
||||
<MkSwitch v-model="useBlurEffect">
|
||||
|
||||
@@ -7,8 +7,6 @@ import * as Misskey from 'misskey-js';
|
||||
import { markRaw } from 'vue';
|
||||
import { $i } from '@/i.js';
|
||||
import { wsOrigin } from '@@/js/config.js';
|
||||
// TODO: No WebsocketモードでStreamMockが使えそう
|
||||
//import { StreamMock } from '@/utility/stream-mock.js';
|
||||
|
||||
// heart beat interval in ms
|
||||
const HEART_BEAT_INTERVAL = 1000 * 60;
|
||||
|
||||
@@ -8,16 +8,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<a v-if="isRoot" href="https://github.com/misskey-dev/misskey" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--MI_THEME-panel); color:var(--MI_THEME-fg); position: fixed; z-index: 10; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
|
||||
|
||||
<div v-if="!narrow && !isRoot" :class="$style.side">
|
||||
<div :class="$style.banner" :style="{ backgroundImage: instance.backgroundImageUrl ? `url(${ instance.backgroundImageUrl })` : 'none' }"></div>
|
||||
<div :class="$style.dashboard">
|
||||
<div :class="$style.sideBanner" :style="{ backgroundImage: instance.backgroundImageUrl ? `url(${ instance.backgroundImageUrl })` : 'none' }"></div>
|
||||
<div :class="$style.sideDashboard">
|
||||
<MkVisitorDashboard/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="$style.main">
|
||||
<button v-if="!isRoot" :class="$style.homeButton" class="_button" @click="goHome">
|
||||
<i class="ti ti-home"></i>
|
||||
</button>
|
||||
<div v-if="narrow && !isRoot" :class="$style.header">
|
||||
<img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.headerIcon"/>
|
||||
<MkA to="/" :class="$style.headerTitle">{{ instanceName }}</MkA>
|
||||
<MkButton primary rounded :class="$style.headerButton" @click="goHome">{{ i18n.ts.signup }}</MkButton>
|
||||
</div>
|
||||
<div :class="$style.content">
|
||||
<RouterView/>
|
||||
</div>
|
||||
@@ -38,6 +40,7 @@ import { i18n } from '@/i18n.js';
|
||||
import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
|
||||
import { mainRouter } from '@/router.js';
|
||||
import { DI } from '@/di.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
||||
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
|
||||
|
||||
@@ -93,16 +96,26 @@ onMounted(() => {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.homeButton {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
.header {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--MI_THEME-panel);
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.headerIcon {
|
||||
width: 48px;
|
||||
vertical-align: bottom;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.headerTitle {
|
||||
margin: 0 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.headerButton {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.side {
|
||||
@@ -112,7 +125,7 @@ onMounted(() => {
|
||||
background: var(--MI_THEME-accent);
|
||||
}
|
||||
|
||||
.banner {
|
||||
.sideBanner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -124,7 +137,7 @@ onMounted(() => {
|
||||
mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
.sideDashboard {
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ export function uploadFile(file: File | Blob, options: {
|
||||
const driveFile = JSON.parse(ev.target.response);
|
||||
globalEvents.emit('driveFileCreated', driveFile);
|
||||
resolve(driveFile);
|
||||
}) as (ev: ProgressEvent<EventTarget>) => any;
|
||||
}) as (ev: ProgressEvent<EventTarget>) => void;
|
||||
|
||||
if (options.onProgress) {
|
||||
xhr.upload.onprogress = ev => {
|
||||
|
||||
@@ -13,7 +13,7 @@ interface CommonParamDef {
|
||||
type: string;
|
||||
label?: string;
|
||||
caption?: string;
|
||||
default: any;
|
||||
default: unknown;
|
||||
}
|
||||
|
||||
interface NumberParamDef extends CommonParamDef {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { ref, shallowRef, triggerRef } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import type { ComputedRef, Ref, ShallowRef } from 'vue';
|
||||
import type { ComputedRef, Ref, ShallowRef, UnwrapRef } from 'vue';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
|
||||
const MAX_ITEMS = 30;
|
||||
@@ -19,7 +19,12 @@ export type MisskeyEntity = {
|
||||
_shouldInsertAd_?: boolean;
|
||||
};
|
||||
|
||||
type FilterByEpRes<E extends Record<string, any>> = {
|
||||
type AbsEndpointType = {
|
||||
req: unknown;
|
||||
res: unknown;
|
||||
};
|
||||
|
||||
type FilterByEpRes<E extends Record<string, AbsEndpointType>> = {
|
||||
[K in keyof E]: E[K]['res'] extends Array<{ id: string }> ? K : never
|
||||
}[keyof E];
|
||||
export type PaginatorCompatibleEndpointPaths = FilterByEpRes<Misskey.Endpoints>;
|
||||
@@ -27,6 +32,8 @@ export type PaginatorCompatibleEndpoints = {
|
||||
[K in PaginatorCompatibleEndpointPaths]: Misskey.Endpoints[K];
|
||||
};
|
||||
|
||||
export type ExtractorFunction<P extends IPaginator, T> = (item: UnwrapRef<P['items']>[number]) => T;
|
||||
|
||||
export interface IPaginator<T = unknown, _T = T & MisskeyEntity> {
|
||||
/**
|
||||
* 外部から直接操作しないでください
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import type { Channels, StreamEvents, IStream, IChannelConnection } from 'misskey-js';
|
||||
|
||||
type AnyOf<T extends Record<any, any>> = T[keyof T];
|
||||
type OmitFirst<T extends any[]> = T extends [any, ...infer R] ? R : never;
|
||||
|
||||
/**
|
||||
* Websocket無効化時に使うStreamのモック(なにもしない)
|
||||
*/
|
||||
export class StreamMock extends EventEmitter<StreamEvents> implements IStream {
|
||||
public readonly state = 'initializing';
|
||||
|
||||
constructor(...args: ConstructorParameters<typeof Misskey.Stream>) {
|
||||
super();
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public useChannel<C extends keyof Channels>(channel: C, params?: Channels[C]['params'], name?: string): ChannelConnectionMock<Channels[C]> {
|
||||
return new ChannelConnectionMock(this, channel, name);
|
||||
}
|
||||
|
||||
public removeSharedConnection(connection: any): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public removeSharedConnectionPool(pool: any): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public disconnectToChannel(): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public send(typeOrPayload: string): void;
|
||||
public send(typeOrPayload: string, payload: any): void;
|
||||
public send(typeOrPayload: Record<string, any> | any[]): void;
|
||||
public send(typeOrPayload: string | Record<string, any> | any[], payload?: any): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public ping(): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public heartbeat(): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelConnectionMock<Channel extends AnyOf<Channels> = any> extends EventEmitter<Channel['events']> implements IChannelConnection<Channel> {
|
||||
public id = '';
|
||||
public name?: string; // for debug
|
||||
public inCount = 0; // for debug
|
||||
public outCount = 0; // for debug
|
||||
public channel: string;
|
||||
|
||||
constructor(stream: IStream, ...args: OmitFirst<ConstructorParameters<typeof Misskey.ChannelConnection<Channel>>>) {
|
||||
super();
|
||||
|
||||
this.channel = args[0];
|
||||
this.name = args[1];
|
||||
}
|
||||
|
||||
public send<T extends keyof Channel['receives']>(type: T, body: Channel['receives'][T]): void {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
@@ -222,7 +222,8 @@ export class WatermarkRenderer {
|
||||
},
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Unrecognized layer type: ${(layer as any).type}`);
|
||||
// @ts-expect-error Should be unreachable
|
||||
throw new Error(`Unrecognized layer type: ${layer.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import { Interpreter, Parser, utils } from '@syuilo/aiscript';
|
||||
import { useWidgetPropsManager } from './widget.js';
|
||||
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
|
||||
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
|
||||
import type { Value } from '@syuilo/aiscript/interpreter/value.js';
|
||||
import * as os from '@/os.js';
|
||||
import MkContainer from '@/components/MkContainer.vue';
|
||||
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
|
||||
@@ -83,7 +84,7 @@ const run = async () => {
|
||||
switch (type) {
|
||||
case 'end': logs.value.push({
|
||||
id: genId(),
|
||||
text: utils.valToString(params.val, true),
|
||||
text: utils.valToString(params.val as Value, true),
|
||||
print: false,
|
||||
}); break;
|
||||
default: break;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"declaration": false,
|
||||
"sourceMap": false,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "Bundler",
|
||||
"removeComments": false,
|
||||
"noLib": false,
|
||||
|
||||
Reference in New Issue
Block a user