Merge tag '2023.10.2' into art/bump-2023.10.2

This commit is contained in:
Zel9278 2023-10-29 12:41:11 +09:00
commit 377914bd73
1863 changed files with 39210 additions and 20256 deletions

View file

@ -21,9 +21,6 @@ module.exports = {
'allowSingleExtends': true,
},
],
'@typescript-eslint/prefer-nullish-coalescing': [
'error',
],
// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため
'id-denylist': ['error', 'window', 'e'],

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import path from 'node:path';

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { entities } from 'misskey-js'
export function abuseUserReport() {
@ -84,6 +89,7 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi
value: 'https://misskey-hub.net',
},
],
verifiedLinks: [],
followersCount: 1024,
followingCount: 16,
hasPendingFollowRequestFromYou: false,
@ -110,9 +116,11 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi
publicReactions: false,
securityKeys: false,
twoFactorEnabled: false,
twoFactorBackupCodesStock: 'none',
updatedAt: null,
uri: null,
url: null,
notify: 'none',
};
}

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { existsSync, readFileSync } from 'node:fs';
import { writeFile } from 'node:fs/promises';
import { basename, dirname } from 'node:path/posix';

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { StorybookConfig } from '@storybook/vue3-vite';

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { addons } from '@storybook/manager-api';
import { create } from '@storybook/theming/create';

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { type SharedOptions, rest } from 'msw';
export const onUnhandledRequest = ((req, print) => {

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { writeFile } from 'node:fs/promises';
import locales from '../../../locales/index.js';

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { readFile, writeFile } from 'node:fs/promises';
import JSON5 from 'json5';

View file

@ -1,6 +1,6 @@
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.21.0/tabler-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.37.0/tabler-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
<style>
html {

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { addons } from '@storybook/addons';
import { FORCE_REMOUNT } from '@storybook/core-events';
import { type Preview, setup } from '@storybook/vue3';

View file

@ -6,6 +6,7 @@
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"exactOptionalPropertyTypes": true,
"noEmitOnError": false,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
type FIXME = any;
declare const _LANGS_: string[][];

View file

@ -1,5 +1,10 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare module '@/themes/*.json5' {
import { Theme } from "@/scripts/theme";
import { Theme } from '@/scripts/theme.js';
const theme: Theme;

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { parse } from 'acorn';
import { generate } from 'astring';
import { describe, expect, it } from 'vitest';

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { generate } from 'astring';
import * as estree from 'estree';
import { walk } from '../node_modules/estree-walker/src/index.js';

View file

@ -1,11 +1,12 @@
{
"name": "frontend",
"private": true,
"type": "module",
"scripts": {
"watch": "vite",
"build": "vite build",
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
"build-storybook-pre": "tsc -p .storybook && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
"build-storybook": "pnpm build-storybook-pre && storybook build",
"chromatic": "chromatic",
"test": "vitest --run",
@ -16,32 +17,33 @@
},
"dependencies": {
"@discordapp/twemoji": "14.1.2",
"@rollup/plugin-alias": "5.0.0",
"@rollup/plugin-json": "6.0.0",
"@rollup/plugin-replace": "5.0.2",
"@rollup/pluginutils": "5.0.2",
"@syuilo/aiscript": "0.15.0",
"@tabler/icons-webfont": "2.25.0",
"@vitejs/plugin-vue": "4.2.3",
"@vue-macros/reactivity-transform": "0.3.15",
"@vue/compiler-sfc": "3.3.4",
"@github/webauthn-json": "2.1.1",
"@rollup/plugin-alias": "5.0.1",
"@rollup/plugin-json": "6.0.1",
"@rollup/plugin-replace": "5.0.4",
"@rollup/pluginutils": "5.0.5",
"@syuilo/aiscript": "0.16.0",
"@tabler/icons-webfont": "2.37.0",
"@vitejs/plugin-vue": "4.4.0",
"@vue-macros/reactivity-transform": "0.3.23",
"@vue/compiler-sfc": "3.3.5",
"astring": "1.8.6",
"autosize": "6.0.1",
"broadcast-channel": "5.1.0",
"broadcast-channel": "5.5.0",
"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
"buraha": "0.0.1",
"canvas-confetti": "1.6.0",
"chart.js": "4.3.0",
"canvas-confetti": "1.6.1",
"chart.js": "4.4.0",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "2.0.1",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.0.1",
"chromatic": "6.19.9",
"compare-versions": "5.0.3",
"cropperjs": "2.0.0-beta.3",
"chromatic": "7.4.0",
"compare-versions": "6.1.0",
"cropperjs": "2.0.0-beta.4",
"date-fns": "2.30.0",
"escape-regexp": "0.0.1",
"estree-walker": "^3.0.3",
"estree-walker": "3.0.3",
"eventemitter3": "5.0.1",
"gsap": "3.12.2",
"idb-keyval": "6.2.1",
@ -51,94 +53,89 @@
"matter-js": "0.19.0",
"mfm-js": "0.23.3",
"misskey-js": "workspace:*",
"photoswipe": "5.3.8",
"photoswipe": "5.4.2",
"prismjs": "1.29.0",
"punycode": "2.3.0",
"querystring": "0.2.1",
"rollup": "3.26.3",
"s-age": "1.1.2",
"rollup": "4.1.4",
"sanitize-html": "2.11.0",
"sass": "1.63.6",
"sass": "1.69.4",
"strict-event-emitter-types": "2.0.0",
"syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0",
"three": "0.154.0",
"three": "0.157.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.7",
"tsc-alias": "1.8.8",
"tsconfig-paths": "4.2.0",
"twemoji-parser": "14.0.0",
"typescript": "5.1.6",
"uuid": "9.0.0",
"vanilla-tilt": "1.8.0",
"vite": "4.4.4",
"vue": "3.3.4",
"typescript": "5.2.2",
"uuid": "9.0.1",
"v-code-diff": "1.7.1",
"vanilla-tilt": "1.8.1",
"vite": "4.5.0",
"vue": "3.3.5",
"vue-prism-editor": "2.0.0-alpha.2",
"vuedraggable": "next"
},
"devDependencies": {
"@storybook/addon-actions": "7.0.27",
"@storybook/addon-essentials": "7.0.27",
"@storybook/addon-interactions": "7.0.27",
"@storybook/addon-links": "7.0.27",
"@storybook/addon-storysource": "7.0.27",
"@storybook/addons": "7.0.27",
"@storybook/blocks": "7.0.27",
"@storybook/core-events": "7.0.27",
"@storybook/jest": "0.1.0",
"@storybook/manager-api": "7.0.27",
"@storybook/preview-api": "7.0.27",
"@storybook/react": "7.0.27",
"@storybook/react-vite": "7.0.27",
"@storybook/testing-library": "0.2.0",
"@storybook/theming": "7.0.27",
"@storybook/types": "7.0.27",
"@storybook/vue3": "7.0.27",
"@storybook/vue3-vite": "7.0.27",
"@testing-library/jest-dom": "5.16.5",
"@storybook/addon-actions": "7.5.1",
"@storybook/addon-essentials": "7.5.1",
"@storybook/addon-interactions": "7.5.1",
"@storybook/addon-links": "7.5.1",
"@storybook/addon-storysource": "7.5.1",
"@storybook/addons": "7.5.1",
"@storybook/blocks": "7.5.1",
"@storybook/core-events": "7.5.1",
"@storybook/jest": "0.2.3",
"@storybook/manager-api": "7.5.1",
"@storybook/preview-api": "7.5.1",
"@storybook/react": "7.5.1",
"@storybook/react-vite": "7.5.1",
"@storybook/testing-library": "0.2.2",
"@storybook/theming": "7.5.1",
"@storybook/types": "7.5.1",
"@storybook/vue3": "7.5.1",
"@storybook/vue3-vite": "7.5.1",
"@testing-library/vue": "7.0.0",
"@types/escape-regexp": "0.0.1",
"@types/estree": "1.0.1",
"@types/gulp": "4.0.13",
"@types/gulp-rename": "2.0.2",
"@types/matter-js": "0.18.5",
"@types/micromatch": "4.0.2",
"@types/node": "20.4.2",
"@types/punycode": "2.1.0",
"@types/sanitize-html": "2.9.0",
"@types/testing-library__jest-dom": "5.14.8",
"@types/throttle-debounce": "5.0.0",
"@types/tinycolor2": "1.4.3",
"@types/uuid": "9.0.2",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.5",
"@typescript-eslint/eslint-plugin": "5.61.0",
"@typescript-eslint/parser": "5.61.0",
"@vitest/coverage-v8": "0.33.0",
"@vue/runtime-core": "3.3.4",
"@types/escape-regexp": "0.0.2",
"@types/estree": "1.0.3",
"@types/matter-js": "0.19.2",
"@types/micromatch": "4.0.4",
"@types/node": "20.8.7",
"@types/punycode": "2.1.1",
"@types/sanitize-html": "2.9.3",
"@types/throttle-debounce": "5.0.1",
"@types/tinycolor2": "1.4.5",
"@types/uuid": "9.0.6",
"@types/websocket": "1.0.8",
"@types/ws": "8.5.8",
"@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "6.8.0",
"@vitest/coverage-v8": "0.34.6",
"@vue/runtime-core": "3.3.5",
"acorn": "8.10.0",
"cross-env": "7.0.3",
"cypress": "12.17.1",
"eslint": "8.45.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-vue": "9.15.1",
"fast-glob": "3.3.0",
"cypress": "13.3.2",
"eslint": "8.51.0",
"eslint-plugin-import": "2.28.1",
"eslint-plugin-vue": "9.17.0",
"fast-glob": "3.3.1",
"happy-dom": "10.0.3",
"micromatch": "4.0.5",
"msw": "1.2.2",
"msw-storybook-addon": "1.8.0",
"msw": "1.3.2",
"msw-storybook-addon": "1.9.0",
"nodemon": "3.0.1",
"prettier": "3.0.0",
"prettier": "3.0.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"start-server-and-test": "2.0.0",
"storybook": "7.0.27",
"start-server-and-test": "2.0.1",
"storybook": "7.5.1",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"summaly": "github:misskey-dev/summaly",
"vite-plugin-turbosnap": "1.0.2",
"vitest": "0.33.0",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.6",
"vitest-fetch-mock": "0.2.2",
"vue-eslint-parser": "9.3.1",
"vue-tsc": "1.8.5"
"vue-eslint-parser": "9.3.2",
"vue-tsc": "1.8.19"
}
}

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable */
/* tslint:disable */

View file

@ -1,9 +1,14 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
// https://vitejs.dev/config/build-options.html#build-modulepreload
import 'vite/modulepreload-polyfill';
import '@/style.scss';
import { mainBoot } from './boot/main-boot';
import { subBoot } from './boot/sub-boot';
import { mainBoot } from '@/boot/main-boot.js';
import { subBoot } from '@/boot/sub-boot.js';
const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete'];

View file

@ -1,17 +1,22 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { defineAsyncComponent, reactive, ref } from 'vue';
import * as misskey from 'misskey-js';
import { showSuspendedDialog } from './scripts/show-suspended-dialog';
import { i18n } from './i18n';
import { miLocalStorage } from './local-storage';
import { MenuButton } from './types/menu';
import { del, get, set } from '@/scripts/idb-proxy';
import { apiUrl } from '@/config';
import { waiting, api, popup, popupMenu, success, alert } from '@/os';
import { unisonReload, reloadChannel } from '@/scripts/unison-reload';
import * as Misskey from 'misskey-js';
import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js';
import { MenuButton } from '@/types/menu.js';
import { del, get, set } from '@/scripts/idb-proxy.js';
import { apiUrl } from '@/config.js';
import { waiting, api, popup, popupMenu, success, alert } from '@/os.js';
import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
// TODO: 他のタブと永続化されたstateを同期
type Account = misskey.entities.MeDetailed;
type Account = Misskey.entities.MeDetailed;
const accountData = miLocalStorage.getItem('account');
@ -91,7 +96,6 @@ export async function removeAccount(idOrToken: Account['id']) {
function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Promise<Account> {
return new Promise((done, fail) => {
// Fetch user
window.fetch(`${apiUrl}/i`, {
method: 'POST',
body: JSON.stringify({
@ -103,8 +107,8 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr
})
.then(res => new Promise<Account | { error: Record<string, any> }>((done2, fail2) => {
if (res.status >= 500 && res.status < 600) {
// サーバーエラー(5xx)の場合をrejectとする
// 認証エラーなど4xxはresolve
// サーバーエラー(5xx)の場合をrejectとする
// 認証エラーなど4xxはresolve
return fail2(res);
}
res.json().then(done2, fail2);
@ -112,13 +116,13 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr
.then(async res => {
if (res.error) {
if (res.error.id === 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370') {
// SUSPENDED
// SUSPENDED
if (forceShowDialog || $i && (token === $i.token || id === $i.id)) {
await showSuspendedDialog();
}
} else if (res.error.id === 'e5b3b9f0-2b8f-4b9f-9c1f-8c5c1b2e1b1a') {
// USER_IS_DELETED
// アカウントが削除されている
// USER_IS_DELETED
// アカウントが削除されている
if (forceShowDialog || $i && (token === $i.token || id === $i.id)) {
await alert({
type: 'error',
@ -127,8 +131,8 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr
});
}
} else if (res.error.id === 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14') {
// AUTHENTICATION_FAILED
// トークンが無効化されていたりアカウントが削除されたりしている
// AUTHENTICATION_FAILED
// トークンが無効化されていたりアカウントが削除されたりしている
if (forceShowDialog || $i && (token === $i.token || id === $i.id)) {
await alert({
type: 'error',
@ -207,8 +211,8 @@ export async function login(token: Account['token'], redirect?: string) {
export async function openAccountMenu(opts: {
includeCurrentAccount?: boolean;
withExtraOperation: boolean;
active?: misskey.entities.UserDetailed['id'];
onChoose?: (account: misskey.entities.UserDetailed) => void;
active?: Misskey.entities.UserDetailed['id'];
onChoose?: (account: Misskey.entities.UserDetailed) => void;
}, ev: MouseEvent) {
if (!$i) return;
@ -230,7 +234,7 @@ export async function openAccountMenu(opts: {
}, 'closed');
}
async function switchAccount(account: misskey.entities.UserDetailed) {
async function switchAccount(account: Misskey.entities.UserDetailed) {
const storedAccounts = await getAccounts();
const found = storedAccounts.find(x => x.id === account.id);
if (found == null) return;
@ -244,7 +248,7 @@ export async function openAccountMenu(opts: {
const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id));
const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) });
function createItem(account: misskey.entities.UserDetailed) {
function createItem(account: Misskey.entities.UserDetailed) {
return {
type: 'user' as const,
user: account,

View file

@ -1,25 +1,30 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent, App } from 'vue';
import { compareVersions } from 'compare-versions';
import widgets from '@/widgets';
import directives from '@/directives';
import components from '@/components';
import { version, ui, lang, updateLocale } from '@/config';
import { applyTheme } from '@/scripts/theme';
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
import { i18n, updateI18n } from '@/i18n';
import { confirm, alert, post, popup, toast } from '@/os';
import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
import { defaultStore, ColdDeviceStorage } from '@/store';
import { fetchInstance, instance } from '@/instance';
import { deviceKind } from '@/scripts/device-kind';
import { reloadChannel } from '@/scripts/unison-reload';
import { reactionPicker } from '@/scripts/reaction-picker';
import { getUrlWithoutLoginId } from '@/scripts/login-id';
import { getAccountFromId } from '@/scripts/get-account-from-id';
import { deckStore } from '@/ui/deck/deck-store';
import { miLocalStorage } from '@/local-storage';
import { fetchCustomEmojis } from '@/custom-emojis';
import { mainRouter } from '@/router';
import widgets from '@/widgets/index.js';
import directives from '@/directives/index.js';
import components from '@/components/index.js';
import { version, ui, lang, updateLocale } from '@/config.js';
import { applyTheme } from '@/scripts/theme.js';
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
import { i18n, updateI18n } from '@/i18n.js';
import { confirm, alert, post, popup, toast } from '@/os.js';
import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
import { defaultStore, ColdDeviceStorage } from '@/store.js';
import { fetchInstance, instance } from '@/instance.js';
import { deviceKind } from '@/scripts/device-kind.js';
import { reloadChannel } from '@/scripts/unison-reload.js';
import { reactionPicker } from '@/scripts/reaction-picker.js';
import { getUrlWithoutLoginId } from '@/scripts/login-id.js';
import { getAccountFromId } from '@/scripts/get-account-from-id.js';
import { deckStore } from '@/ui/deck/deck-store.js';
import { miLocalStorage } from '@/local-storage.js';
import { fetchCustomEmojis } from '@/custom-emojis.js';
import { mainRouter } from '@/router.js';
export async function common(createVue: () => App<Element>) {
console.info(`Misskey v${version}`);
@ -197,6 +202,18 @@ export async function common(createVue: () => App<Element>) {
}
}, { immediate: true });
if (defaultStore.state.keepScreenOn) {
if ('wakeLock' in navigator) {
navigator.wakeLock.request('screen');
document.addEventListener('visibilitychange', async () => {
if (document.visibilityState === 'visible') {
navigator.wakeLock.request('screen');
}
});
}
}
//#region Fetch user
if ($i && $i.token) {
if (_DEV_) {

View file

@ -1,19 +1,24 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
import { common } from './common';
import { version, ui, lang, updateLocale } from '@/config';
import { i18n, updateI18n } from '@/i18n';
import { confirm, alert, post, popup, toast } from '@/os';
import { useStream } from '@/stream';
import * as sound from '@/scripts/sound';
import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
import { defaultStore, ColdDeviceStorage } from '@/store';
import { makeHotkey } from '@/scripts/hotkey';
import { reactionPicker } from '@/scripts/reaction-picker';
import { miLocalStorage } from '@/local-storage';
import { claimAchievement, claimedAchievements } from '@/scripts/achievements';
import { mainRouter } from '@/router';
import { initializeSw } from '@/scripts/initialize-sw';
import { deckStore } from '@/ui/deck/deck-store';
import { common } from './common.js';
import { version, ui, lang, updateLocale } from '@/config.js';
import { i18n, updateI18n } from '@/i18n.js';
import { confirm, alert, post, popup, toast } from '@/os.js';
import { useStream } from '@/stream.js';
import * as sound from '@/scripts/sound.js';
import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
import { defaultStore, ColdDeviceStorage } from '@/store.js';
import { makeHotkey } from '@/scripts/hotkey.js';
import { reactionPicker } from '@/scripts/reaction-picker.js';
import { miLocalStorage } from '@/local-storage.js';
import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js';
import { mainRouter } from '@/router.js';
import { initializeSw } from '@/scripts/initialize-sw.js';
import { deckStore } from '@/ui/deck/deck-store.js';
export async function mainBoot() {
const { isClientUpdated } = await common(() => createApp(
@ -78,6 +83,21 @@ export async function mainBoot() {
}
});
for (const announcement of ($i.unreadAnnouncements ?? []).filter(x => x.display === 'dialog')) {
popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), {
announcement,
}, {}, 'closed');
}
stream.on('announcementCreated', (ev) => {
const announcement = ev.announcement;
if (announcement.display === 'dialog') {
popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), {
announcement,
}, {}, 'closed');
}
});
if ($i.isDeleted) {
alert({
type: 'warning',

View file

@ -1,5 +1,10 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
import { common } from './common';
import { common } from './common.js';
export async function subBoot() {
const { isClientUpdated } = await common(() => createApp(

View file

@ -1,7 +1,13 @@
import * as misskey from 'misskey-js';
import { Cache } from '@/scripts/cache';
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export const clipsCache = new Cache<misskey.entities.Clip[]>(Infinity);
export const rolesCache = new Cache(Infinity);
export const userListsCache = new Cache<misskey.entities.UserList[]>(Infinity);
export const antennasCache = new Cache<misskey.entities.Antenna[]>(Infinity);
import * as Misskey from 'misskey-js';
import { Cache } from '@/scripts/cache.js';
import { api } from '@/os.js';
export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => api('clips/list'));
export const rolesCache = new Cache(1000 * 60 * 30, () => api('admin/roles/list'));
export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => api('users/lists/list'));
export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => api('antennas/list'));

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions';
import { StoryObj } from '@storybook/vue3';

View file

@ -1,7 +1,12 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="bcekxzvu _margin _panel">
<div class="target">
<MkA v-user-preview="report.targetUserId" class="info" :to="`/user-info/${report.targetUserId}`">
<MkA v-user-preview="report.targetUserId" class="info" :to="`/admin/user/${report.targetUserId}`">
<MkAvatar class="avatar" :user="report.targetUser" indicator/>
<div class="names">
<MkUserName class="name" :user="report.targetUser"/>
@ -39,9 +44,9 @@
import MkButton from '@/components/MkButton.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { dateString } from '@/filters/date';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { dateString } from '@/filters/date.js';
const props = defineProps<{
report: any;

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions';
import { StoryObj } from '@storybook/vue3';

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkWindow ref="uiWindow" :initialWidth="400" :initialHeight="500" :canResize="true" @closed="emit('closed')">
<template #header>
@ -30,8 +35,8 @@ import * as Misskey from 'misskey-js';
import MkWindow from '@/components/MkWindow.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
const props = defineProps<{
user: Misskey.entities.User;

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { userDetailed } from '../../.storybook/fakes';

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div v-if="user" :class="$style.root">
<i class="ti ti-plane-departure" style="margin-right: 8px;"></i>
@ -8,13 +13,13 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { UserLite } from 'misskey-js/built/entities';
import * as Misskey from 'misskey-js';
import MkMention from './MkMention.vue';
import { i18n } from '@/i18n';
import { host as localHost } from '@/config';
import { api } from '@/os';
import { i18n } from '@/i18n.js';
import { host as localHost } from '@/config.js';
import { api } from '@/os.js';
const user = ref<UserLite>();
const user = ref<Misskey.entities.UserLite>();
const props = defineProps<{
movedTo: string; // user id

View file

@ -1,10 +1,15 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
import { userDetailed } from '../../.storybook/fakes';
import { commonHandlers } from '../../.storybook/mocks';
import MkAchievements from './MkAchievements.vue';
import { ACHIEVEMENT_TYPES } from '@/scripts/achievements';
import { ACHIEVEMENT_TYPES } from '@/scripts/achievements.js';
export const Empty = {
render(args) {
return {

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<div v-if="achievements" :class="$style.root">
@ -47,14 +52,14 @@
</template>
<script lang="ts" setup>
import * as misskey from 'misskey-js';
import * as Misskey from 'misskey-js';
import { onMounted } from 'vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js';
const props = withDefaults(defineProps<{
user: misskey.entities.User;
user: Misskey.entities.User;
withLocked: boolean;
withDescription: boolean;
}>(), {

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import isChromatic from 'chromatic/isChromatic';

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<svg :class="$style.root" viewBox="0 0 10 10" preserveAspectRatio="none">
<template v-if="props.graduations === 'dots'">

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<canvas ref="canvasEl" style="width: 100%; height: 100%; pointer-events: none;"></canvas>
</template>

View file

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import MkAnnouncementDialog from './MkAnnouncementDialog.vue';
export const Default = {
render(args) {
return {
components: {
MkAnnouncementDialog,
},
setup() {
return {
args,
};
},
computed: {
props() {
return {
...this.args,
};
},
},
template: '<MkAnnouncementDialog v-bind="props" />',
};
},
args: {
announcement: {
id: '1',
title: 'Title',
text: 'Text',
createdAt: new Date().toISOString(),
updatedAt: null,
icon: 'info',
imageUrl: null,
display: 'dialog',
needConfirmationToRead: false,
forYou: true,
},
},
parameters: {
layout: 'centered',
},
} satisfies StoryObj<typeof MkAnnouncementDialog>;

View file

@ -0,0 +1,104 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModal ref="modal" :zPriority="'middle'" @closed="$emit('closed')" @click="onBgClick">
<div ref="rootEl" :class="$style.root">
<div :class="$style.header">
<span :class="$style.icon">
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
<i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
<i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
<i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
</span>
<span :class="$style.title">{{ announcement.title }}</span>
</div>
<div :class="$style.text"><Mfm :text="announcement.text"/></div>
<MkButton primary full @click="ok">{{ i18n.ts.ok }}</MkButton>
</div>
</MkModal>
</template>
<script lang="ts" setup>
import { onMounted, shallowRef } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os.js';
import MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { $i, updateAccount } from '@/account.js';
const props = withDefaults(defineProps<{
announcement: Misskey.entities.Announcement;
}>(), {
});
const rootEl = shallowRef<HTMLDivElement>();
const modal = shallowRef<InstanceType<typeof MkModal>>();
async function ok() {
if (props.announcement.needConfirmationToRead) {
const confirm = await os.confirm({
type: 'question',
title: i18n.ts._announcement.readConfirmTitle,
text: i18n.t('_announcement.readConfirmText', { title: props.announcement.title }),
});
if (confirm.canceled) return;
}
modal.value.close();
os.api('i/read-announcement', { announcementId: props.announcement.id });
updateAccount({
unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id),
});
}
function onBgClick() {
rootEl.value.animate([{
offset: 0,
transform: 'scale(1)',
}, {
offset: 0.5,
transform: 'scale(1.1)',
}, {
offset: 1,
transform: 'scale(1)',
}], {
duration: 100,
});
}
onMounted(() => {
});
</script>
<style lang="scss" module>
.root {
margin: auto;
position: relative;
padding: 32px;
min-width: 320px;
max-width: 480px;
box-sizing: border-box;
background: var(--panel);
border-radius: var(--radius);
}
.header {
font-size: 120%;
}
.icon {
margin-right: 0.5em;
}
.title {
font-weight: bold;
}
.text {
margin: 1em 0;
}
</style>

View file

@ -1,2 +1,7 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import MkAsUi from './MkAsUi.vue';
void MkAsUi;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<div v-if="c.type === 'root'" :class="$style.root">
@ -33,6 +38,13 @@
<option v-for="item in c.items" :key="item.value" :value="item.value">{{ item.text }}</option>
</MkSelect>
<MkButton v-else-if="c.type === 'postFormButton'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" inline @click="openPostForm">{{ c.text }}</MkButton>
<div v-else-if="c.type === 'postForm'" :class="$style.postForm">
<MkPostForm
fixed
:instant="true"
:initialText="c.form.text"
/>
</div>
<MkFolder v-else-if="c.type === 'folder'" :defaultOpen="c.opened">
<template #label>{{ c.title }}</template>
<template v-for="child in c.children" :key="child">
@ -49,14 +61,15 @@
<script lang="ts" setup>
import { Ref } from 'vue';
import * as os from '@/os';
import * as os from '@/os.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkSelect from '@/components/MkSelect.vue';
import { AsUiComponent } from '@/scripts/aiscript/ui';
import { AsUiComponent } from '@/scripts/aiscript/ui.js';
import MkFolder from '@/components/MkFolder.vue';
import MkPostForm from '@/components/MkPostForm.vue';
const props = withDefaults(defineProps<{
component: AsUiComponent;
@ -109,4 +122,9 @@ function openPostForm() {
.fontMonospace {
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
}
.postForm {
background: var(--bg);
border-radius: 8px;
}
</style>

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions';
import { expect } from '@storybook/jest';
@ -8,7 +13,7 @@ import { userDetailed } from '../../.storybook/fakes';
import { commonHandlers } from '../../.storybook/mocks';
import MkAutocomplete from './MkAutocomplete.vue';
import MkInput from './MkInput.vue';
import { tick } from '@/scripts/test-utils';
import { tick } from '@/scripts/test-utils.js';
const common = {
render(args) {
return {

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div ref="rootEl" :class="$style.root" class="_popup _shadow" :style="{ zIndex }" @contextmenu.prevent="() => {}">
<ol v-if="type === 'user'" ref="suggests" :class="$style.list">
@ -36,16 +41,16 @@
<script lang="ts">
import { markRaw, ref, shallowRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import sanitizeHtml from 'sanitize-html';
import contains from '@/scripts/contains';
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base';
import { acct } from '@/filters/user';
import * as os from '@/os';
import { MFM_TAGS } from '@/scripts/mfm-tags';
import { defaultStore } from '@/store';
import { emojilist, getEmojiName } from '@/scripts/emojilist';
import { i18n } from '@/i18n';
import { miLocalStorage } from '@/local-storage';
import { customEmojis } from '@/custom-emojis';
import contains from '@/scripts/contains.js';
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js';
import { acct } from '@/filters/user.js';
import * as os from '@/os.js';
import { MFM_TAGS } from '@/scripts/mfm-tags.js';
import { defaultStore } from '@/store.js';
import { emojilist, getEmojiName } from '@/scripts/emojilist.js';
import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js';
import { customEmojis } from '@/custom-emojis.js';
type EmojiDef = {
emoji: string;

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<div v-for="user in users.slice(0, limit)" :key="user.id" style="display:inline-block;width:32px;height:32px;margin-right:8px;">
@ -9,8 +14,8 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import * as os from '@/os';
import { UserLite } from 'misskey-js/built/entities';
import * as Misskey from 'misskey-js';
import * as os from '@/os.js';
const props = withDefaults(defineProps<{
userIds: string[];
@ -19,11 +24,11 @@ const props = withDefaults(defineProps<{
limit: Infinity,
});
const users = ref<UserLite[]>([]);
const users = ref<Misskey.entities.UserLite[]>([]);
onMounted(async () => {
users.value = await os.api('users/show', {
userIds: props.userIds,
}) as unknown as UserLite[];
}) as unknown as Misskey.entities.UserLite[];
});
</script>

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
import { action } from '@storybook/addon-actions';

View file

@ -1,9 +1,16 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<button
v-if="!link"
ref="el" class="_button"
:class="[$style.root, { [$style.inline]: inline, [$style.primary]: primary, [$style.gradate]: gradate, [$style.danger]: danger, [$style.rounded]: rounded, [$style.full]: full, [$style.small]: small, [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike }]"
:type="type"
:name="name"
:value="value"
@click="emit('click', $event)"
@mousedown="onMousedown"
>
@ -44,6 +51,8 @@ const props = defineProps<{
large?: boolean;
transparent?: boolean;
asLike?: boolean;
name?: string;
value?: string;
}>();
const emit = defineEmits<{

View file

@ -1,2 +1,7 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import MkCaptcha from './MkCaptcha.vue';
void MkCaptcha;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<span v-if="!available">{{ i18n.ts.waiting }}<MkEllipsis/></span>
@ -7,8 +12,8 @@
<script lang="ts" setup>
import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch } from 'vue';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
// APIs provided by Captcha services
export type Captcha = {

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<button
class="_button"
@ -21,8 +26,8 @@
<script lang="ts" setup>
import { ref } from 'vue';
import * as os from '@/os';
import { i18n } from '@/i18n';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
channel: Record<string, any>;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkPagination :pagination="pagination">
<template #empty>
@ -16,8 +21,8 @@
<script lang="ts" setup>
import MkChannelPreview from '@/components/MkChannelPreview.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import { i18n } from '@/i18n';
import { infoImageUrl } from '@/instance';
import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js';
const props = withDefaults(defineProps<{
pagination: Paging;

View file

@ -1,8 +1,14 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
<div class="banner" :style="bannerStyle">
<div class="fade"></div>
<div class="name"><i class="ti ti-device-tv"></i> {{ channel.name }}</div>
<div v-if="channel.isSensitive" class="sensitiveIndicator">{{ i18n.ts.sensitive }}</div>
<div class="status">
<div>
<i class="ti ti-users ti-fw"></i>
@ -35,7 +41,7 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { i18n } from '@/i18n';
import { i18n } from '@/i18n.js';
const props = defineProps<{
channel: Record<string, any>;
@ -97,6 +103,19 @@ const bannerStyle = computed(() => {
border-radius: 6px;
color: #fff;
}
> .sensitiveIndicator {
position: absolute;
z-index: 1;
bottom: 16px;
left: 16px;
background: rgba(0, 0, 0, 0.7);
color: var(--warn);
border-radius: 6px;
font-weight: bold;
font-size: 1em;
padding: 4px 7px;
}
}
> article {

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root">
<canvas ref="chartEl"></canvas>
@ -17,14 +22,14 @@
import { onMounted, ref, shallowRef, watch, PropType } from 'vue';
import { Chart } from 'chart.js';
import gradient from 'chartjs-plugin-gradient';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { chartVLine } from '@/scripts/chart-vline';
import { alpha } from '@/scripts/color';
import date from '@/filters/date';
import { initChart } from '@/scripts/init-chart';
import { chartLegend } from '@/scripts/chart-legend';
import * as os from '@/os.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { chartVLine } from '@/scripts/chart-vline.js';
import { alpha } from '@/scripts/color.js';
import date from '@/filters/date.js';
import { initChart } from '@/scripts/init-chart.js';
import { chartLegend } from '@/scripts/chart-legend.js';
import MkChartLegend from '@/components/MkChartLegend.vue';
initChart();

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root">
<button v-for="item in items" class="_button item" :class="{ disabled: item.hidden }" @click="onClick(item)">

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkTooltip ref="tooltip" :showing="showing" :x="x" :y="y" :maxWidth="340" :direction="'top'" :innerMargin="16" @closed="emit('closed')">
<div v-if="title || series">

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<div v-if="game.ready" :class="$style.game">
@ -16,11 +21,11 @@
<script lang="ts" setup>
import { computed, onMounted, onUnmounted } from 'vue';
import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
import * as game from '@/scripts/clicker-game';
import number from '@/filters/number';
import { claimAchievement } from '@/scripts/achievements';
import * as os from '@/os.js';
import { useInterval } from '@/scripts/use-interval.js';
import * as game from '@/scripts/clicker-game.js';
import number from '@/filters/number.js';
import { claimAchievement } from '@/scripts/achievements.js';
const saveData = game.saveData;
const cookies = computed(() => saveData.value?.cookies);

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root" class="_panel">
<b>{{ clip.name }}</b>
@ -10,7 +15,7 @@
</template>
<script lang="ts" setup>
import { i18n } from '@/i18n';
import { i18n } from '@/i18n.js';
defineProps<{
clip: any;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<!-- eslint-disable vue/no-v-html -->
<template>
<code v-if="inline" :class="`language-${prismLang}`" style="overflow-wrap: anywhere;" v-html="html"></code>

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<XCode :code="code" :lang="lang" :inline="inline"/>
</template>

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<div :class="$style.label"><slot name="label"></slot></div>
@ -20,7 +25,7 @@
<script lang="ts" setup>
import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
import { i18n } from '@/i18n';
import { i18n } from '@/i18n.js';
const props = defineProps<{
modelValue: string | null;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div ref="rootEl" class="_panel" :class="[$style.root, { [$style.naked]: naked, [$style.thin]: thin, [$style.scrollable]: scrollable }]">
<header v-if="showHeader" ref="headerEl" :class="$style.header">
@ -35,8 +40,8 @@
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
showHeader?: boolean;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<Transition
appear
@ -16,9 +21,9 @@
import { onMounted, onBeforeUnmount } from 'vue';
import MkMenu from './MkMenu.vue';
import { MenuItem } from './types/menu.vue';
import contains from '@/scripts/contains';
import { defaultStore } from '@/store';
import * as os from '@/os';
import contains from '@/scripts/contains.js';
import { defaultStore } from '@/store.js';
import * as os from '@/os.js';
const props = defineProps<{
items: MenuItem[];

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow
ref="dialogEl"
@ -27,25 +32,25 @@
<script lang="ts" setup>
import { onMounted } from 'vue';
import * as misskey from 'misskey-js';
import * as Misskey from 'misskey-js';
import Cropper from 'cropperjs';
import tinycolor from 'tinycolor2';
import MkModalWindow from '@/components/MkModalWindow.vue';
import * as os from '@/os';
import { $i } from '@/account';
import { defaultStore } from '@/store';
import { apiUrl } from '@/config';
import { i18n } from '@/i18n';
import { getProxiedImageUrl } from '@/scripts/media-proxy';
import * as os from '@/os.js';
import { $i } from '@/account.js';
import { defaultStore } from '@/store.js';
import { apiUrl } from '@/config.js';
import { i18n } from '@/i18n.js';
import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
const emit = defineEmits<{
(ev: 'ok', cropped: misskey.entities.DriveFile): void;
(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
(ev: 'cancel'): void;
(ev: 'closed'): void;
}>();
const props = defineProps<{
file: misskey.entities.DriveFile;
file: Misskey.entities.DriveFile;
aspectRatio: number;
uploadFolder?: string | null;
}>();
@ -57,7 +62,7 @@ let cropper: Cropper | null = null;
let loading = $ref(true);
const ok = async () => {
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
const promise = new Promise<Misskey.entities.DriveFile>(async (res) => {
const croppedCanvas = await cropper?.getCropperSelection()?.$toCanvas();
croppedCanvas?.toBlob(blob => {
if (!blob) return;

View file

@ -1,19 +1,22 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<button class="_button" :class="$style.root" @mousedown="toggle">
<b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b>
<span v-if="!modelValue" :class="$style.label">{{ label }}</span>
</button>
<MkButton rounded full small @click="toggle"><b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b><span v-if="!modelValue" :class="$style.label">{{ label }}</span></MkButton>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import * as misskey from 'misskey-js';
import { concat } from '@/scripts/array';
import { i18n } from '@/i18n';
import * as Misskey from 'misskey-js';
import { concat } from '@/scripts/array.js';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
const props = defineProps<{
modelValue: boolean;
note: misskey.entities.Note;
note: Misskey.entities.Note;
}>();
const emit = defineEmits<{
@ -28,25 +31,12 @@ const label = computed(() => {
] as string[][]).join(' / ');
});
const toggle = () => {
function toggle() {
emit('update:modelValue', !props.modelValue);
};
}
</script>
<style lang="scss" module>
.root {
display: inline-block;
padding: 4px 8px;
font-size: 0.7em;
color: var(--cwFg);
background: var(--cwBg);
border-radius: 2px;
&:hover {
background: var(--cwHoverBg);
}
}
.label {
margin-left: 4px;

View file

@ -1,10 +1,15 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<script lang="ts">
import { defineComponent, h, PropType, TransitionGroup, useCssModule } from 'vue';
import MkAd from '@/components/global/MkAd.vue';
import { isDebuggerEnabled, stackTraceInstances } from '@/debug';
import { i18n } from '@/i18n';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { defaultStore } from '@/store.js';
import { MisskeyEntity } from '@/types/date-separated-list';
export default defineComponent({
@ -163,10 +168,10 @@ export default defineComponent({
> *:empty {
display: none;
}
> *:not(:last-child) {
margin-bottom: var(--margin);
}
&:not(.date-separated-list-nogap) > *:not(:last-child) {
margin-bottom: var(--margin);
}
}

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
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">
@ -56,7 +61,7 @@ import MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import { i18n } from '@/i18n';
import { i18n } from '@/i18n.js';
type Input = {
type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local';

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import isChromatic from 'chromatic/isChromatic';

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<span>
<span v-text="hh"></span>

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="_panel _shadow" :class="$style.root">
<!-- TODO: インスタンス運営者が任意のテキストとリンクを設定できるようにする -->
@ -33,11 +38,11 @@
<script lang="ts" setup>
import MkButton from '@/components/MkButton.vue';
import MkLink from '@/components/MkLink.vue';
import { host } from '@/config';
import { i18n } from '@/i18n';
import * as os from '@/os';
import { miLocalStorage } from '@/local-storage';
import { instance } from '@/instance';
import { host } from '@/config.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { miLocalStorage } from '@/local-storage.js';
import { instance } from '@/instance.js';
const emit = defineEmits<{
(ev: 'closed'): void;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div
:class="[$style.root, { [$style.isSelected]: isSelected }]"
@ -36,11 +41,14 @@
import { computed, ref } from 'vue';
import * as Misskey from 'misskey-js';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import bytes from '@/filters/bytes';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { $i } from '@/account';
import { getDriveFileMenu } from '@/scripts/get-drive-file-menu';
import bytes from '@/filters/bytes.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
import { useRouter } from '@/router.js';
import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js';
const router = useRouter();
const props = withDefaults(defineProps<{
file: Misskey.entities.DriveFile;
@ -66,7 +74,7 @@ function onClick(ev: MouseEvent) {
if (props.selectMode) {
emit('chosen', props.file);
} else {
os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
router.push(`/my/drive/file/${props.file.id}`);
}
}

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div
:class="[$style.root, { [$style.draghover]: draghover }]"
@ -29,11 +34,11 @@
<script lang="ts" setup>
import { computed, defineAsyncComponent, ref } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { claimAchievement } from '@/scripts/achievements';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import * as os from '@/os.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';
const props = withDefaults(defineProps<{
folder: Misskey.entities.DriveFolder;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div
:class="[$style.root, { [$style.draghover]: draghover }]"
@ -15,8 +20,8 @@
<script lang="ts" setup>
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os';
import { i18n } from '@/i18n';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
const props = defineProps<{
folder?: Misskey.entities.DriveFolder;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root">
<nav :class="$style.nav">
@ -96,12 +101,12 @@ import MkButton from './MkButton.vue';
import XNavFolder from '@/components/MkDrive.navFolder.vue';
import XFolder from '@/components/MkDrive.folder.vue';
import XFile from '@/components/MkDrive.file.vue';
import * as os from '@/os';
import { useStream } from '@/stream';
import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
import { uploadFile, uploads } from '@/scripts/upload';
import { claimAchievement } from '@/scripts/achievements';
import * as os from '@/os.js';
import { useStream } from '@/stream.js';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import { uploadFile, uploads } from '@/scripts/upload.js';
import { claimAchievement } from '@/scripts/achievements.js';
const props = withDefaults(defineProps<{
initialFolder?: Misskey.entities.DriveFolder;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div ref="thumbnail" :class="$style.root">
<ImgWithBlurhash v-if="isThumbnailAvailable" :hash="file.blurhash" :src="file.thumbnailUrl" :alt="file.name" :title="file.name" :cover="fit !== 'contain'"/>

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow
ref="dialog"
@ -23,8 +28,8 @@ import { ref, shallowRef } from 'vue';
import * as Misskey from 'misskey-js';
import XDrive from '@/components/MkDrive.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import number from '@/filters/number';
import { i18n } from '@/i18n';
import number from '@/filters/number.js';
import { i18n } from '@/i18n.js';
withDefaults(defineProps<{
type?: 'file' | 'folder';

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkWindow
ref="window"
@ -18,7 +23,7 @@ import { } from 'vue';
import * as Misskey from 'misskey-js';
import XDrive from '@/components/MkDrive.vue';
import MkWindow from '@/components/MkWindow.vue';
import { i18n } from '@/i18n';
import { i18n } from '@/i18n.js';
defineProps<{
initialFolder?: Misskey.entities.DriveFolder;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
<section>
@ -22,7 +27,7 @@
<script lang="ts" setup>
import { ref, computed, Ref } from 'vue';
import { getEmojiName } from '@/scripts/emojilist';
import { getEmojiName } from '@/scripts/emojilist.js';
const props = defineProps<{
emojis: string[] | Ref<string[]>;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }">
<input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter">
@ -95,15 +100,15 @@
import { ref, shallowRef, computed, watch, onMounted } from 'vue';
import * as Misskey from 'misskey-js';
import XSection from '@/components/MkEmojiPicker.section.vue';
import { emojilist, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, getEmojiName } from '@/scripts/emojilist';
import { emojilist, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, getEmojiName } from '@/scripts/emojilist.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch';
import { deviceKind } from '@/scripts/device-kind';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis';
import { $i } from '@/account';
import * as os from '@/os.js';
import { isTouchUsing } from '@/scripts/touch.js';
import { deviceKind } from '@/scripts/device-kind.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js';
import { $i } from '@/account.js';
const props = withDefaults(defineProps<{
showPinned?: boolean;
@ -151,7 +156,7 @@ watch(q, () => {
const newQ = q.value.replace(/:/g, '').toLowerCase();
const searchCustom = () => {
const max = 8;
const max = 100;
const emojis = customEmojis.value;
const matches = new Set<Misskey.entities.CustomEmoji>();
@ -214,7 +219,7 @@ watch(q, () => {
};
const searchUnicode = () => {
const max = 8;
const max = 100;
const emojis = emojilist;
const matches = new Set<UnicodeEmojiDef>();
@ -610,6 +615,8 @@ defineExpose({
height: 1.25em;
vertical-align: -.25em;
pointer-events: none;
width: 100%;
object-fit: contain;
}
}
}

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModal
ref="modal"
@ -29,7 +34,7 @@
import { shallowRef } from 'vue';
import MkModal from '@/components/MkModal.vue';
import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
import { defaultStore } from '@/store';
import { defaultStore } from '@/store.js';
withDefaults(defineProps<{
manualShowing?: boolean | null;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkWindow
ref="window"

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div v-if="meta" :class="$style.root" :style="{ backgroundImage: `url(${ meta.backgroundImageUrl })` }"></div>
</template>
@ -5,7 +10,7 @@
<script lang="ts" setup>
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os';
import * as os from '@/os.js';
const meta = ref<Misskey.entities.DetailedInstanceMetadata>();

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow
ref="dialog"
@ -25,7 +30,7 @@ import * as Misskey from 'misskey-js';
import MkModalWindow from '@/components/MkModalWindow.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import { i18n } from '@/i18n';
import { i18n } from '@/i18n.js';
const props = defineProps<{
file: Misskey.entities.DriveFile;

View file

@ -1,10 +1,15 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<MkPagination v-slot="{items}" :pagination="pagination" class="urempief" :class="{ grid: viewMode === 'grid' }">
<MkA
v-for="file in items"
:key="file.id"
v-tooltip.mfm="`${file.type}\n${bytes(file.size)}\n${dateString(file.createdAt)}\nby ${file.user ? '@' + Acct.toString(file.user) : 'system'}`"
v-tooltip.mfm="`${file.type}\n${bytes(file.size)}\n${dateString(file.createdAt)}\nby ${file.user ? '@' + Misskey.acct.toString(file.user) : 'system'}`"
:to="`/admin/file/${file.id}`"
class="file _button"
>
@ -32,12 +37,12 @@
</template>
<script lang="ts" setup>
import * as Acct from 'misskey-js/built/acct';
import * as Misskey from 'misskey-js';
import MkPagination from '@/components/MkPagination.vue';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import bytes from '@/filters/bytes';
import { i18n } from '@/i18n';
import { dateString } from '@/filters/date';
import bytes from '@/filters/bytes.js';
import { i18n } from '@/i18n.js';
import { dateString } from '@/filters/date.js';
const props = defineProps<{
pagination: any;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkA :to="`/play/${flash.id}`" class="vhpxefrk _panel" tabindex="-1">
<article>
@ -15,10 +20,10 @@
<script lang="ts" setup>
import { } from 'vue';
import { userName } from '@/filters/user';
import { userName } from '@/filters/user.js';
const props = defineProps<{
//flash: misskey.entities.Flash;
//flash: Misskey.entities.Flash;
flash: any;
}>();
</script>

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div ref="el" :class="$style.root">
<header :class="$style.header" class="_button" :style="{ background: bg }" @click="showBody = !showBody">
@ -25,8 +30,8 @@
<script lang="ts" setup>
import { onMounted, ref, shallowRef, watch } from 'vue';
import tinycolor from 'tinycolor2';
import { miLocalStorage } from '@/local-storage';
import { defaultStore } from '@/store';
import { miLocalStorage } from '@/local-storage.js';
import { defaultStore } from '@/store.js';
const miLocalStoragePrefix = 'ui:folder:' as const;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div ref="rootEl" :class="$style.root" role="group" :aria-expanded="opened">
<MkStickyContainer>
@ -46,7 +51,7 @@
<script lang="ts" setup>
import { nextTick, onMounted } from 'vue';
import { defaultStore } from '@/store';
import { defaultStore } from '@/store.js';
const props = withDefaults(defineProps<{
defaultOpen?: boolean;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<button
class="_button"
@ -32,11 +37,12 @@
<script lang="ts" setup>
import { onBeforeUnmount, onMounted } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os';
import { useStream } from '@/stream';
import { i18n } from '@/i18n';
import { claimAchievement } from '@/scripts/achievements';
import { $i } from '@/account';
import * as os from '@/os.js';
import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { $i } from '@/account.js';
import { defaultStore } from "@/store.js";
const props = withDefaults(defineProps<{
user: Misskey.entities.UserDetailed,
@ -47,6 +53,10 @@ const props = withDefaults(defineProps<{
large: false,
});
const emit = defineEmits<{
(_: 'update:user', value: Misskey.entities.UserDetailed): void
}>();
let isFollowing = $ref(props.user.isFollowing);
let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou);
let wait = $ref(false);
@ -90,6 +100,11 @@ async function onClick() {
} else {
await os.api('following/create', {
userId: props.user.id,
withReplies: defaultStore.state.defaultWithReplies,
});
emit('update:user', {
...props.user,
withReplies: defaultStore.state.defaultWithReplies
});
hasPendingFollowRequestFromYou = true;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow
ref="dialog"
@ -39,9 +44,9 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkInfo from '@/components/MkInfo.vue';
import * as os from '@/os';
import { instance } from '@/instance';
import { i18n } from '@/i18n';
import * as os from '@/os.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
const emit = defineEmits<{
(ev: 'done'): void;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkModalWindow
ref="dialog"
@ -64,7 +69,7 @@ import MkRange from './MkRange.vue';
import MkButton from './MkButton.vue';
import MkRadios from './MkRadios.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n';
import { i18n } from '@/i18n.js';
const props = defineProps<{
title: string;

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { expect } from '@storybook/jest';
import { userEvent, waitFor, within } from '@storybook/testing-library';

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkA :to="`/gallery/${post.id}`" class="ttasepnz _panel" tabindex="-1" @pointerenter="enterHover" @pointerleave="leaveHover">
<div class="thumbnail">
@ -27,13 +32,13 @@
</template>
<script lang="ts" setup>
import * as misskey from 'misskey-js';
import * as Misskey from 'misskey-js';
import { computed, ref } from 'vue';
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
import { defaultStore } from '@/store';
import { defaultStore } from '@/store.js';
const props = defineProps<{
post: misskey.entities.GalleryPost;
post: Misskey.entities.GalleryPost;
}>();
const hover = ref(false);

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root">
<input v-model="query" :class="$style.input" type="search" :placeholder="q">
@ -7,7 +12,7 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { i18n } from '@/i18n';
import { i18n } from '@/i18n.js';
const props = defineProps<{
q: string;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div ref="rootEl">
<MkLoading v-if="fetching"/>
@ -10,11 +15,11 @@
<script lang="ts" setup>
import { onMounted, nextTick, watch } from 'vue';
import { Chart } from 'chart.js';
import * as os from '@/os';
import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import { alpha } from '@/scripts/color';
import { initChart } from '@/scripts/init-chart';
import * as os from '@/os.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { alpha } from '@/scripts/color.js';
import { initChart } from '@/scripts/init-chart.js';
initChart();

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div ref="root" :class="['chromatic-ignore', $style.root, { [$style.cover]: cover }]" :title="title ?? ''">
<TransitionGroup
@ -19,8 +24,8 @@
import { $ref } from 'vue/macros';
import DrawBlurhash from '@/workers/draw-blurhash?worker';
import TestWebGL2 from '@/workers/test-webgl2?worker';
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch';
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js';
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash.js';
const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => {
// Web Worker
@ -56,7 +61,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
import { v4 as uuid } from 'uuid';
import { render } from 'buraha';
import { defaultStore } from '@/store';
import { defaultStore } from '@/store.js';
const props = withDefaults(defineProps<{
transition?: {

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="[$style.root, { [$style.warn]: warn }]">
<i v-if="warn" class="ti ti-alert-triangle" :class="$style.i"></i>

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div>
<div :class="$style.label" @click="focus"><slot name="label"></slot></div>
@ -18,6 +23,8 @@
:spellcheck="spellcheck"
:step="step"
:list="id"
:min="min"
:max="max"
@focus="focused = true"
@blur="focused = false"
@keydown="onKeydown($event)"
@ -38,8 +45,8 @@
import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
import { debounce } from 'throttle-debounce';
import MkButton from '@/components/MkButton.vue';
import { useInterval } from '@/scripts/use-interval';
import { i18n } from '@/i18n';
import { useInterval } from '@/scripts/use-interval.js';
import { i18n } from '@/i18n.js';
const props = defineProps<{
modelValue: string | number | null;
@ -54,6 +61,8 @@ const props = defineProps<{
spellcheck?: boolean;
step?: any;
datalist?: string[];
min?: number;
max?: number;
inline?: boolean;
debounce?: boolean;
manualSave?: boolean;
@ -150,6 +159,10 @@ onMounted(() => {
}
});
});
defineExpose({
focus,
});
</script>
<style lang="scss" module>

View file

@ -1,5 +1,10 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="[$style.root, { yellow: instance.isNotResponding, red: instance.isBlocked, gray: instance.isSuspended }]">
<div :class="[$style.root, { yellow: instance.isNotResponding, red: instance.isBlocked, gray: instance.isSuspended, blue: instance.isSilenced }]">
<img class="icon" :src="getInstanceIcon(instance)" alt="" loading="lazy"/>
<div class="body">
<span class="host">{{ instance.name ?? instance.host }}</span>
@ -10,13 +15,13 @@
</template>
<script lang="ts" setup>
import * as misskey from 'misskey-js';
import * as Misskey from 'misskey-js';
import MkMiniChart from '@/components/MkMiniChart.vue';
import * as os from '@/os';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
import * as os from '@/os.js';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
const props = defineProps<{
instance: misskey.entities.Instance;
instance: Misskey.entities.Instance;
}>();
let chartValues = $ref<number[] | null>(null);
@ -84,6 +89,12 @@ function getInstanceIcon(instance): string {
height: 30px;
}
&:global(.blue) {
--c: rgba(0, 42, 255, 0.15);
background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);
background-size: 16px 16px;
}
&:global(.yellow) {
--c: rgb(255 196 0 / 15%);
background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%);

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root">
<MkFoldableSection class="item">
@ -83,14 +88,14 @@ import { onMounted } from 'vue';
import { Chart } from 'chart.js';
import MkSelect from '@/components/MkSelect.vue';
import MkChart from '@/components/MkChart.vue';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import MkHeatmap from '@/components/MkHeatmap.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue';
import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue';
import { initChart } from '@/scripts/init-chart';
import { initChart } from '@/scripts/init-chart.js';
initChart();

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root" :style="bg">
<img v-if="faviconUrl" :class="$style.icon" :src="faviconUrl"/>
@ -7,9 +12,9 @@
<script lang="ts" setup>
import { } from 'vue';
import { instanceName } from '@/config';
import { instance as Instance } from '@/instance';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
import { instanceName } from '@/config.js';
import { instance as Instance } from '@/instance.js';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
const props = defineProps<{
instance?: {

View file

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import { rest } from 'msw';
@ -48,13 +53,13 @@ export const Default = {
export const Used = {
...Default,
args: {
invite: inviteCode(true) as any
invite: inviteCode(true) as any,
},
} satisfies StoryObj<typeof MkInviteCode>;
export const Expired = {
...Default,
args: {
invite: inviteCode(false, true, true) as any
invite: inviteCode(false, true, true) as any,
},
} satisfies StoryObj<typeof MkInviteCode>;

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkFolder>
<template #label>{{ invite.code }}</template>
@ -54,15 +59,15 @@
<script lang="ts" setup>
import { computed } from 'vue';
import * as misskey from 'misskey-js';
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';
import { i18n } from '@/i18n';
import * as os from '@/os';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
const props = defineProps<{
invite: misskey.entities.Invite;
invite: Misskey.entities.Invite;
moderator?: boolean;
}>();

View file

@ -1,3 +1,8 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="[$style.root, { [$style.oneline]: oneline }]">
<div :class="$style.key">
@ -12,9 +17,9 @@
<script lang="ts" setup>
import { } from 'vue';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import * as os from '@/os';
import { i18n } from '@/i18n';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
copy?: string | null;

Some files were not shown because too many files have changed in this diff Show more