Compare commits
18 Commits
2025.3.2-b
...
feat/bundl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f066247988 | ||
|
|
6015254e59 | ||
|
|
c02f0b3b33 | ||
|
|
abddd40c09 | ||
|
|
a865a949b5 | ||
|
|
0007723405 | ||
|
|
71188b3463 | ||
|
|
7f534a41a6 | ||
|
|
f25963e2c2 | ||
|
|
dfab6b1b8d | ||
|
|
fac59d75e2 | ||
|
|
fd3e28812e | ||
|
|
6a90b7e04b | ||
|
|
8d8414687a | ||
|
|
0c682dd027 | ||
|
|
3399c786a8 | ||
|
|
64cf101fe7 | ||
|
|
8b6d90b7a4 |
@@ -235,6 +235,7 @@
|
||||
"jest-mock": "29.7.0",
|
||||
"nodemon": "3.1.9",
|
||||
"pid-port": "1.0.2",
|
||||
"simple-oauth2": "5.1.0"
|
||||
"simple-oauth2": "5.1.0",
|
||||
"vite": "6.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
|
||||
import * as fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import locale from '../../../locales/index.js';
|
||||
import type { RedisOptions } from 'ioredis';
|
||||
import type { Manifest, ManifestChunk } from 'vite';
|
||||
import type { ILocale } from '../../../locales/index.js';
|
||||
|
||||
type RedisOptionsSource = Partial<RedisOptions> & {
|
||||
host: string;
|
||||
@@ -185,9 +188,12 @@ export type Config = {
|
||||
authUrl: string;
|
||||
driveUrl: string;
|
||||
userAgent: string;
|
||||
frontendEntry: string;
|
||||
localeEntries: Record<string, string>;
|
||||
errorLocaleMessages: Record<string, ILocale>;
|
||||
configEntry: ManifestChunk;
|
||||
frontendEntry: ManifestChunk;
|
||||
frontendManifestExists: boolean;
|
||||
frontendEmbedEntry: string;
|
||||
frontendEmbedEntry: ManifestChunk;
|
||||
frontendEmbedManifestExists: boolean;
|
||||
mediaProxy: string;
|
||||
externalMediaProxyEnabled: boolean;
|
||||
@@ -229,12 +235,23 @@ export function loadConfig(): Config {
|
||||
|
||||
const frontendManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_vite_/manifest.json');
|
||||
const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json');
|
||||
const frontendManifest = frontendManifestExists ?
|
||||
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8'))
|
||||
: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
|
||||
const frontendEmbedManifest = frontendEmbedManifestExists ?
|
||||
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8'))
|
||||
: { 'src/boot.ts': { file: 'src/boot.ts' } };
|
||||
const frontendManifest: Manifest = frontendManifestExists
|
||||
? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8'))
|
||||
: Object.entries(locale).reduce<Record<string, ManifestChunk>>((a, [k]) => {
|
||||
a[`locale:${k}`] = { file: `locale:${k}` };
|
||||
return a;
|
||||
}, {
|
||||
'src/_boot_.ts': { file: 'src/_boot_.ts' },
|
||||
'../frontend-shared/js/config.ts': { file: join('@fs', _dirname.slice(1), '../../frontend-shared/js/config.ts') },
|
||||
});
|
||||
const frontendEmbedManifest: Manifest = frontendEmbedManifestExists
|
||||
? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8'))
|
||||
: Object.entries(locale).reduce<Record<string, ManifestChunk>>((a, [k]) => {
|
||||
a[`locale:${k}`] = { file: `locale:${k}` };
|
||||
return a;
|
||||
}, {
|
||||
'src/boot.ts': { file: 'src/boot.ts' },
|
||||
});
|
||||
|
||||
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
|
||||
|
||||
@@ -310,6 +327,20 @@ export function loadConfig(): Config {
|
||||
config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator
|
||||
: null,
|
||||
userAgent: `Misskey/${version} (${config.url})`,
|
||||
localeEntries: Object.entries<ManifestChunk>(frontendManifest).reduce<Record<string, string>>((a, [k, v]) => {
|
||||
if (k.startsWith('locale:')) {
|
||||
a[k.slice('locale:'.length)] = v.file;
|
||||
}
|
||||
return a;
|
||||
}, {}),
|
||||
errorLocaleMessages: Object.entries(locale).reduce<Record<string, ILocale>>((a, [k, v]) => {
|
||||
a[k] = {
|
||||
_bootErrors: v._bootErrors,
|
||||
reload: v.reload,
|
||||
};
|
||||
return a;
|
||||
}, {}),
|
||||
configEntry: frontendManifest['../frontend-shared/js/config.ts'],
|
||||
frontendEntry: frontendManifest['src/_boot_.ts'],
|
||||
frontendManifestExists: frontendManifestExists,
|
||||
frontendEmbedEntry: frontendEmbedManifest['src/boot.ts'],
|
||||
|
||||
@@ -32,56 +32,24 @@
|
||||
}
|
||||
|
||||
//#region Detect language & fetch translations
|
||||
if (!localStorage.hasOwnProperty('locale')) {
|
||||
const supportedLangs = LANGS;
|
||||
let lang = localStorage.getItem('lang');
|
||||
if (lang == null || !supportedLangs.includes(lang)) {
|
||||
if (supportedLangs.includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
||||
|
||||
// Fallback
|
||||
if (lang == null) lang = 'en-US';
|
||||
}
|
||||
}
|
||||
|
||||
const metaRes = await window.fetch('/api/meta', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({}),
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (metaRes.status !== 200) {
|
||||
renderError('META_FETCH');
|
||||
return;
|
||||
}
|
||||
const meta = await metaRes.json();
|
||||
const v = meta.version;
|
||||
if (v == null) {
|
||||
renderError('META_FETCH_V');
|
||||
return;
|
||||
}
|
||||
|
||||
// for https://github.com/misskey-dev/misskey/issues/10202
|
||||
if (lang == null || lang.toString == null || lang.toString() === 'null') {
|
||||
console.error('invalid lang value detected!!!', typeof lang, lang);
|
||||
lang = 'en-US';
|
||||
}
|
||||
|
||||
const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
|
||||
if (localRes.status === 200) {
|
||||
localStorage.setItem('lang', lang);
|
||||
localStorage.setItem('locale', await localRes.text());
|
||||
localStorage.setItem('localeVersion', v);
|
||||
const supportedLangs = LANGS;
|
||||
let lang = localStorage.getItem('lang');
|
||||
if (!supportedLangs.includes(lang)) {
|
||||
if (supportedLangs.includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
renderError('LOCALE_FETCH');
|
||||
return;
|
||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
||||
|
||||
// Fallback
|
||||
if (lang == null) lang = 'en-US';
|
||||
}
|
||||
}
|
||||
|
||||
await import(`/vite/${LOCALES[lang]}`)
|
||||
.catch(async e => {
|
||||
console.error(e);
|
||||
renderError('LOCALE_FETCH', e);
|
||||
});
|
||||
//#endregion
|
||||
|
||||
//#region Script
|
||||
@@ -115,10 +83,21 @@
|
||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
||||
}
|
||||
|
||||
const locale = JSON.parse(localStorage.getItem('locale') || '{}');
|
||||
const supportedLangs = LANGS;
|
||||
let lang = localStorage.getItem('lang');
|
||||
if (!supportedLangs.includes(lang)) {
|
||||
if (supportedLangs.includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
||||
|
||||
const title = locale?._bootErrors?.title || 'Failed to initialize Misskey';
|
||||
const reload = locale?.reload || 'Reload';
|
||||
// Fallback
|
||||
if (lang == null) lang = 'en-US';
|
||||
}
|
||||
}
|
||||
const { locale } = await import(`/vite/${CONFIG_ENTRY}`).catch(() => ({ locale: {} }));
|
||||
const title = locale._bootErrors.title;
|
||||
const reload = locale.reload;
|
||||
|
||||
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
|
||||
<div class="message">${title}</div>
|
||||
|
||||
@@ -23,56 +23,24 @@
|
||||
}
|
||||
|
||||
//#region Detect language & fetch translations
|
||||
if (!localStorage.hasOwnProperty('locale')) {
|
||||
const supportedLangs = LANGS;
|
||||
let lang = localStorage.getItem('lang');
|
||||
if (lang == null || !supportedLangs.includes(lang)) {
|
||||
if (supportedLangs.includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
||||
|
||||
// Fallback
|
||||
if (lang == null) lang = 'en-US';
|
||||
}
|
||||
}
|
||||
|
||||
const metaRes = await window.fetch('/api/meta', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({}),
|
||||
credentials: 'omit',
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (metaRes.status !== 200) {
|
||||
renderError('META_FETCH');
|
||||
return;
|
||||
}
|
||||
const meta = await metaRes.json();
|
||||
const v = meta.version;
|
||||
if (v == null) {
|
||||
renderError('META_FETCH_V');
|
||||
return;
|
||||
}
|
||||
|
||||
// for https://github.com/misskey-dev/misskey/issues/10202
|
||||
if (lang == null || lang.toString == null || lang.toString() === 'null') {
|
||||
console.error('invalid lang value detected!!!', typeof lang, lang);
|
||||
lang = 'en-US';
|
||||
}
|
||||
|
||||
const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
|
||||
if (localRes.status === 200) {
|
||||
localStorage.setItem('lang', lang);
|
||||
localStorage.setItem('locale', await localRes.text());
|
||||
localStorage.setItem('localeVersion', v);
|
||||
const supportedLangs = LANGS;
|
||||
let lang = localStorage.getItem('lang');
|
||||
if (!supportedLangs.includes(lang)) {
|
||||
if (supportedLangs.includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
renderError('LOCALE_FETCH');
|
||||
return;
|
||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
||||
|
||||
// Fallback
|
||||
if (lang == null) lang = 'en-US';
|
||||
}
|
||||
}
|
||||
|
||||
await import(`/vite/${LOCALES[lang]}`)
|
||||
.catch(async e => {
|
||||
console.error(e);
|
||||
renderError('LOCALE_FETCH', e);
|
||||
});
|
||||
//#endregion
|
||||
|
||||
//#region Script
|
||||
@@ -151,21 +119,25 @@
|
||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
||||
}
|
||||
|
||||
const locale = JSON.parse(localStorage.getItem('locale') || '{}');
|
||||
const supportedLangs = LANGS;
|
||||
let lang = localStorage.getItem('lang');
|
||||
if (!supportedLangs.includes(lang)) {
|
||||
if (supportedLangs.includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
||||
|
||||
const messages = Object.assign({
|
||||
title: 'Failed to initialize Misskey',
|
||||
solution: 'The following actions may solve the problem.',
|
||||
solution1: 'Update your os and browser',
|
||||
solution2: 'Disable an adblocker',
|
||||
solution3: 'Clear the browser cache',
|
||||
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
|
||||
otherOption: 'Other options',
|
||||
otherOption1: 'Clear preferences and cache',
|
||||
otherOption2: 'Start the simple client',
|
||||
otherOption3: 'Start the repair tool',
|
||||
}, locale?._bootErrors || {});
|
||||
const reload = locale?.reload || 'Reload';
|
||||
// Fallback
|
||||
if (lang == null) lang = 'en-US';
|
||||
}
|
||||
}
|
||||
const { locale } = await import(`/vite/${CONFIG_ENTRY}`).catch(() => ({
|
||||
locale: {
|
||||
_bootErrors: {},
|
||||
},
|
||||
}));
|
||||
const messages = locale._bootErrors;
|
||||
const reload = locale.reload;
|
||||
|
||||
let errorsElement = document.getElementById('errors');
|
||||
|
||||
|
||||
@@ -6,23 +6,22 @@
|
||||
'use strict';
|
||||
|
||||
(() => {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const locale = JSON.parse(localStorage.getItem('locale') || '{}');
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const supportedLangs = LANGS;
|
||||
let lang = localStorage.getItem('lang');
|
||||
if (!supportedLangs.includes(lang)) {
|
||||
if (supportedLangs.includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
||||
|
||||
const messages = Object.assign({
|
||||
title: 'Failed to initialize Misskey',
|
||||
serverError: 'If reloading after a period of time does not resolve the problem, contact the server administrator with the following ERROR ID.',
|
||||
solution: 'The following actions may solve the problem.',
|
||||
solution1: 'Update your os and browser',
|
||||
solution2: 'Disable an adblocker',
|
||||
solution3: 'Clear the browser cache',
|
||||
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
|
||||
otherOption: 'Other options',
|
||||
otherOption1: 'Clear preferences and cache',
|
||||
otherOption2: 'Start the simple client',
|
||||
otherOption3: 'Start the repair tool',
|
||||
}, locale?._bootErrors || {});
|
||||
const reload = locale?.reload || 'Reload';
|
||||
// Fallback
|
||||
if (lang == null) lang = 'en-US';
|
||||
}
|
||||
}
|
||||
const locale = ERROR_MESSAGES[lang];
|
||||
const messages = locale._bootErrors;
|
||||
const reload = locale.reload;
|
||||
|
||||
const reloadEls = document.querySelectorAll('[data-i18n-reload]');
|
||||
for (const el of reloadEls) {
|
||||
|
||||
@@ -41,6 +41,8 @@ html(class='embed')
|
||||
script.
|
||||
var VERSION = "#{version}";
|
||||
var CLIENT_ENTRY = "#{entry.file}";
|
||||
var CONFIG_ENTRY = "#{config.configEntry.file}";
|
||||
var LOCALES = JSON.parse(`!{JSON.stringify(config.localeEntries)}`);
|
||||
|
||||
script(type='application/json' id='misskey_meta' data-generated-at=now)
|
||||
!= metaJson
|
||||
|
||||
@@ -70,6 +70,8 @@ html
|
||||
script.
|
||||
var VERSION = "#{version}";
|
||||
var CLIENT_ENTRY = "#{entry.file}";
|
||||
var CONFIG_ENTRY = "#{config.configEntry.file}";
|
||||
var LOCALES = JSON.parse(`!{JSON.stringify(config.localeEntries)}`);
|
||||
|
||||
script(type='application/json' id='misskey_meta' data-generated-at=now)
|
||||
!= metaJson
|
||||
|
||||
@@ -27,6 +27,9 @@ html
|
||||
style
|
||||
include ../error.css
|
||||
|
||||
script.
|
||||
var ERROR_MESSAGES = JSON.parse(`!{JSON.stringify(config.errorLocaleMessages)}`);
|
||||
|
||||
script
|
||||
include ../error.js
|
||||
|
||||
|
||||
@@ -13,18 +13,17 @@ import { createApp, defineAsyncComponent } from 'vue';
|
||||
import defaultLightTheme from '@@/themes/l-light.json5';
|
||||
import defaultDarkTheme from '@@/themes/d-dark.json5';
|
||||
import { MediaProxy } from '@@/js/media-proxy.js';
|
||||
import { url, version, locale, lang, updateLocale } from '@@/js/config.js';
|
||||
import { parseEmbedParams } from '@@/js/embed-page.js';
|
||||
import type { Theme } from '@/theme.js';
|
||||
import { applyTheme, assertIsTheme } from '@/theme.js';
|
||||
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||
import { DI } from '@/di.js';
|
||||
import { serverMetadata } from '@/server-metadata.js';
|
||||
import { url, version, locale, lang, updateLocale } from '@@/js/config.js';
|
||||
import { parseEmbedParams } from '@@/js/embed-page.js';
|
||||
import { postMessageToParentWindow, setIframeId } from '@/post-message.js';
|
||||
import { serverContext } from '@/server-context.js';
|
||||
import { i18n, updateI18n } from '@/i18n.js';
|
||||
|
||||
import type { Theme } from '@/theme.js';
|
||||
|
||||
console.log('Misskey Embed');
|
||||
|
||||
//#region Embedパラメータの取得・パース
|
||||
@@ -71,22 +70,6 @@ if (embedParams.colorMode === 'dark') {
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Detect language & fetch translations
|
||||
const localeVersion = localStorage.getItem('localeVersion');
|
||||
const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null);
|
||||
if (localeOutdated) {
|
||||
const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
|
||||
if (res.status === 200) {
|
||||
const newLocale = await res.text();
|
||||
const parsedNewLocale = JSON.parse(newLocale);
|
||||
localStorage.setItem('locale', newLocale);
|
||||
localStorage.setItem('localeVersion', version);
|
||||
updateLocale(parsedNewLocale);
|
||||
updateI18n(parsedNewLocale);
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
// サイズの制限
|
||||
document.documentElement.style.maxWidth = '500px';
|
||||
|
||||
|
||||
@@ -33,13 +33,11 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
|
||||
Math.min(navigator.hardwareConcurrency - 1, 4),
|
||||
);
|
||||
resolve(workers);
|
||||
if (_DEV_) console.log('WebGL2 in worker is supported!');
|
||||
} else {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
resolve(canvas);
|
||||
if (_DEV_) console.log('WebGL2 in worker is not supported...');
|
||||
}
|
||||
testWorker.terminate();
|
||||
});
|
||||
|
||||
@@ -17,8 +17,7 @@ export const apiUrl = location.origin + '/api';
|
||||
export const wsOrigin = location.origin;
|
||||
export const lang = localStorage.getItem('lang') ?? 'en-US';
|
||||
export const langs = _LANGS_;
|
||||
const preParseLocale = localStorage.getItem('locale');
|
||||
export let locale: Locale = preParseLocale ? JSON.parse(preParseLocale) : null;
|
||||
export let locale: Locale;
|
||||
export const version = _VERSION_;
|
||||
export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName;
|
||||
export const ui = localStorage.getItem('ui');
|
||||
|
||||
@@ -131,7 +131,7 @@ export function imageDataUrl(options?: {
|
||||
alpha?: number,
|
||||
}
|
||||
}, seed?: string): string {
|
||||
const canvas = document.createElement('canvas');
|
||||
const canvas = window.document.createElement('canvas');
|
||||
canvas.width = options?.size?.width ?? 100;
|
||||
canvas.height = options?.size?.height ?? 100;
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ let misskeyOS = null;
|
||||
|
||||
function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) {
|
||||
unobserve();
|
||||
const theme = themes[document.documentElement.dataset.misskeyTheme];
|
||||
const theme = themes[window.document.documentElement.dataset.misskeyTheme];
|
||||
if (theme) {
|
||||
applyTheme(themes[document.documentElement.dataset.misskeyTheme]);
|
||||
applyTheme(themes[window.document.documentElement.dataset.misskeyTheme]);
|
||||
} else {
|
||||
applyTheme(themes['l-light']);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) {
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(document.documentElement, {
|
||||
observer.observe(window.document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-misskey-theme'],
|
||||
});
|
||||
@@ -55,7 +55,6 @@ function initLocalStorage() {
|
||||
...userDetailed(),
|
||||
policies: {},
|
||||
}));
|
||||
localStorage.setItem('locale', JSON.stringify(locale));
|
||||
}
|
||||
|
||||
initialize({
|
||||
@@ -70,13 +69,17 @@ queueMicrotask(() => {
|
||||
import('../src/theme.js'),
|
||||
import('../src/preferences.js'),
|
||||
import('../src/os.js'),
|
||||
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os]) => {
|
||||
import('../src/i18n.js'),
|
||||
import('../../frontend-shared/js/config.js'),
|
||||
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os, { updateI18n }, { updateLocale }]) => {
|
||||
setup((app) => {
|
||||
moduleInitialized = true;
|
||||
if (app[appInitialized]) {
|
||||
return;
|
||||
}
|
||||
app[appInitialized] = true;
|
||||
updateLocale(locale);
|
||||
updateI18n(locale);
|
||||
loadTheme(applyTheme);
|
||||
components(app);
|
||||
directives(app);
|
||||
|
||||
@@ -50,13 +50,46 @@ export default [
|
||||
// defineExposeが誤検知されてしまう
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
|
||||
// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
|
||||
// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため
|
||||
// window ... グローバルスコープと衝突し、予期せぬ結果を招くため
|
||||
// e ... error や event など、複数のキーワードの頭文字であり分かりにくいため
|
||||
// close ... window.closeと衝突 or 紛らわしい
|
||||
// open ... window.openと衝突 or 紛らわしい
|
||||
// fetch ... window.fetchと衝突 or 紛らわしい
|
||||
// location ... window.locationと衝突 or 紛らわしい
|
||||
'id-denylist': ['error', 'window', 'e', 'close', 'open', 'fetch', 'location'],
|
||||
// document ... window.documentと衝突 or 紛らわしい
|
||||
// history ... window.historyと衝突 or 紛らわしい
|
||||
'id-denylist': ['warn', 'window', 'e', 'close', 'open', 'fetch', 'location', 'document', 'history'],
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{
|
||||
'name': 'open',
|
||||
'message': 'Use `window.open`.',
|
||||
},
|
||||
{
|
||||
'name': 'close',
|
||||
'message': 'Use `window.close`.',
|
||||
},
|
||||
{
|
||||
'name': 'fetch',
|
||||
'message': 'Use `window.fetch`.',
|
||||
},
|
||||
{
|
||||
'name': 'location',
|
||||
'message': 'Use `window.location`.',
|
||||
},
|
||||
{
|
||||
'name': 'document',
|
||||
'message': 'Use `window.document`.',
|
||||
},
|
||||
{
|
||||
'name': 'history',
|
||||
'message': 'Use `window.history`.',
|
||||
},
|
||||
{
|
||||
'name': 'name',
|
||||
'message': 'Use `window.name`. もしくは name という変数名を定義し忘れている',
|
||||
},
|
||||
],
|
||||
'no-shadow': ['warn'],
|
||||
'vue/attributes-order': ['error', {
|
||||
alphabetical: false,
|
||||
|
||||
@@ -14,7 +14,7 @@ import { subBoot } from '@/boot/sub-boot.js';
|
||||
|
||||
const subBootPaths = ['/share', '/auth', '/miauth', '/oauth', '/signup-complete', '/install-extensions'];
|
||||
|
||||
if (subBootPaths.some(i => location.pathname === i || location.pathname.startsWith(i + '/'))) {
|
||||
if (subBootPaths.some(i => window.location.pathname === i || window.location.pathname.startsWith(i + '/'))) {
|
||||
subBoot();
|
||||
} else {
|
||||
mainBoot();
|
||||
|
||||
@@ -191,7 +191,7 @@ export async function login(token: AccountWithToken['token'], redirect?: string)
|
||||
// 他のタブは再読み込みするだけ
|
||||
reloadChannel.postMessage(null);
|
||||
// このページはredirectで指定された先に移動
|
||||
location.href = redirect;
|
||||
window.location.href = redirect;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,45 +78,29 @@ export async function common(createVue: () => App<Element>) {
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Detect language & fetch translations
|
||||
const localeVersion = miLocalStorage.getItem('localeVersion');
|
||||
const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null);
|
||||
if (localeOutdated) {
|
||||
const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
|
||||
if (res.status === 200) {
|
||||
const newLocale = await res.text();
|
||||
const parsedNewLocale = JSON.parse(newLocale);
|
||||
miLocalStorage.setItem('locale', newLocale);
|
||||
miLocalStorage.setItem('localeVersion', version);
|
||||
updateLocale(parsedNewLocale);
|
||||
updateI18n(parsedNewLocale);
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
// タッチデバイスでCSSの:hoverを機能させる
|
||||
document.addEventListener('touchend', () => {}, { passive: true });
|
||||
window.document.addEventListener('touchend', () => {}, { passive: true });
|
||||
|
||||
// URLに#pswpを含む場合は取り除く
|
||||
if (location.hash === '#pswp') {
|
||||
history.replaceState(null, '', location.href.replace('#pswp', ''));
|
||||
if (window.location.hash === '#pswp') {
|
||||
window.history.replaceState(null, '', window.location.href.replace('#pswp', ''));
|
||||
}
|
||||
|
||||
// 一斉リロード
|
||||
reloadChannel.addEventListener('message', path => {
|
||||
if (path !== null) location.href = path;
|
||||
else location.reload();
|
||||
if (path !== null) window.location.href = path;
|
||||
else window.location.reload();
|
||||
});
|
||||
|
||||
// If mobile, insert the viewport meta tag
|
||||
if (['smartphone', 'tablet'].includes(deviceKind)) {
|
||||
const viewport = document.getElementsByName('viewport').item(0);
|
||||
const viewport = window.document.getElementsByName('viewport').item(0);
|
||||
viewport.setAttribute('content',
|
||||
`${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`);
|
||||
}
|
||||
|
||||
//#region Set lang attr
|
||||
const html = document.documentElement;
|
||||
const html = window.document.documentElement;
|
||||
html.setAttribute('lang', lang);
|
||||
//#endregion
|
||||
|
||||
@@ -130,11 +114,11 @@ export async function common(createVue: () => App<Element>) {
|
||||
});
|
||||
|
||||
//#region loginId
|
||||
const params = new URLSearchParams(location.search);
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const loginId = params.get('loginId');
|
||||
|
||||
if (loginId) {
|
||||
const target = getUrlWithoutLoginId(location.href);
|
||||
const target = getUrlWithoutLoginId(window.location.href);
|
||||
|
||||
if (!$i || $i.id !== loginId) {
|
||||
const account = await getAccountFromId(loginId);
|
||||
@@ -143,7 +127,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
}
|
||||
}
|
||||
|
||||
history.replaceState({ misskey: 'loginId' }, '', target);
|
||||
window.history.replaceState({ misskey: 'loginId' }, '', target);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@@ -155,7 +139,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
);
|
||||
}, { immediate: miLocalStorage.getItem('theme') == null });
|
||||
|
||||
document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light';
|
||||
window.document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light';
|
||||
|
||||
const darkTheme = prefer.model('darkTheme');
|
||||
const lightTheme = prefer.model('lightTheme');
|
||||
@@ -201,20 +185,20 @@ export async function common(createVue: () => App<Element>) {
|
||||
}, { immediate: true });
|
||||
|
||||
watch(prefer.r.useBlurEffectForModal, v => {
|
||||
document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none');
|
||||
window.document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none');
|
||||
}, { immediate: true });
|
||||
|
||||
watch(prefer.r.useBlurEffect, v => {
|
||||
if (v) {
|
||||
document.documentElement.style.removeProperty('--MI-blur');
|
||||
window.document.documentElement.style.removeProperty('--MI-blur');
|
||||
} else {
|
||||
document.documentElement.style.setProperty('--MI-blur', 'none');
|
||||
window.document.documentElement.style.setProperty('--MI-blur', 'none');
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// Keep screen on
|
||||
const onVisibilityChange = () => document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
const onVisibilityChange = () => window.document.addEventListener('visibilitychange', () => {
|
||||
if (window.document.visibilityState === 'visible') {
|
||||
navigator.wakeLock.request('screen');
|
||||
}
|
||||
});
|
||||
@@ -224,7 +208,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
.catch(() => {
|
||||
// On WebKit-based browsers, user activation is required to send wake lock request
|
||||
// https://webkit.org/blog/13862/the-user-activation-api/
|
||||
document.addEventListener(
|
||||
window.document.addEventListener(
|
||||
'click',
|
||||
() => navigator.wakeLock.request('screen').then(onVisibilityChange),
|
||||
{ once: true },
|
||||
@@ -233,7 +217,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
}
|
||||
|
||||
if (prefer.s.makeEveryTextElementsSelectable) {
|
||||
document.documentElement.classList.add('forceSelectableAll');
|
||||
window.document.documentElement.classList.add('forceSelectableAll');
|
||||
}
|
||||
|
||||
//#region Fetch user
|
||||
@@ -278,16 +262,16 @@ export async function common(createVue: () => App<Element>) {
|
||||
const rootEl = ((): HTMLElement => {
|
||||
const MISSKEY_MOUNT_DIV_ID = 'misskey_app';
|
||||
|
||||
const currentRoot = document.getElementById(MISSKEY_MOUNT_DIV_ID);
|
||||
const currentRoot = window.document.getElementById(MISSKEY_MOUNT_DIV_ID);
|
||||
|
||||
if (currentRoot) {
|
||||
console.warn('multiple import detected');
|
||||
return currentRoot;
|
||||
}
|
||||
|
||||
const root = document.createElement('div');
|
||||
const root = window.document.createElement('div');
|
||||
root.id = MISSKEY_MOUNT_DIV_ID;
|
||||
document.body.appendChild(root);
|
||||
window.document.body.appendChild(root);
|
||||
return root;
|
||||
})();
|
||||
|
||||
@@ -300,24 +284,26 @@ export async function common(createVue: () => App<Element>) {
|
||||
removeSplash();
|
||||
|
||||
//#region Self-XSS 対策メッセージ
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.warning}`,
|
||||
'color: #f00; background-color: #ff0; font-size: 36px; padding: 4px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.title}`,
|
||||
'color: #f00; font-weight: 900; font-family: "Hiragino Sans W9", "Hiragino Kaku Gothic ProN", sans-serif; font-size: 24px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description1}`,
|
||||
'font-size: 16px; font-weight: 700;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description2}`,
|
||||
'font-size: 16px;',
|
||||
'font-size: 20px; font-weight: 700; color: #f00;',
|
||||
);
|
||||
console.log(i18n.tsx._selfXssPrevention.description3({ link: 'https://misskey-hub.net/docs/for-users/resources/self-xss/' }));
|
||||
if (!_DEV_) {
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.warning}`,
|
||||
'color: #f00; background-color: #ff0; font-size: 36px; padding: 4px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.title}`,
|
||||
'color: #f00; font-weight: 900; font-family: "Hiragino Sans W9", "Hiragino Kaku Gothic ProN", sans-serif; font-size: 24px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description1}`,
|
||||
'font-size: 16px; font-weight: 700;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description2}`,
|
||||
'font-size: 16px;',
|
||||
'font-size: 20px; font-weight: 700; color: #f00;',
|
||||
);
|
||||
console.log(i18n.tsx._selfXssPrevention.description3({ link: 'https://misskey-hub.net/docs/for-users/resources/self-xss/' }));
|
||||
}
|
||||
//#endregion
|
||||
|
||||
return {
|
||||
@@ -328,7 +314,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
}
|
||||
|
||||
function removeSplash() {
|
||||
const splash = document.getElementById('splash');
|
||||
const splash = window.document.getElementById('splash');
|
||||
if (splash) {
|
||||
splash.style.opacity = '0';
|
||||
splash.style.pointerEvents = 'none';
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function mainBoot() {
|
||||
if (!$i) uiStyle = 'visitor';
|
||||
|
||||
if (searchParams.has('zen')) uiStyle = 'zen';
|
||||
if (uiStyle === 'deck' && prefer.s['deck.useSimpleUiForNonRootPages'] && location.pathname !== '/') uiStyle = 'zen';
|
||||
if (uiStyle === 'deck' && prefer.s['deck.useSimpleUiForNonRootPages'] && window.location.pathname !== '/') uiStyle = 'zen';
|
||||
|
||||
if (searchParams.has('ui')) uiStyle = searchParams.get('ui');
|
||||
|
||||
@@ -216,7 +216,7 @@ export async function mainBoot() {
|
||||
let reloadDialogShowing = false;
|
||||
stream.on('_disconnected_', async () => {
|
||||
if (prefer.s.serverDisconnectedBehavior === 'reload') {
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
} else if (prefer.s.serverDisconnectedBehavior === 'dialog') {
|
||||
if (reloadDialogShowing) return;
|
||||
reloadDialogShowing = true;
|
||||
@@ -227,7 +227,7 @@ export async function mainBoot() {
|
||||
});
|
||||
reloadDialogShowing = false;
|
||||
if (!canceled) {
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -398,7 +398,7 @@ export async function mainBoot() {
|
||||
let lastVisibilityChangedAt = Date.now();
|
||||
|
||||
function claimPlainLucky() {
|
||||
if (document.visibilityState !== 'visible') {
|
||||
if (window.document.visibilityState !== 'visible') {
|
||||
if (justPlainLuckyTimer != null) window.clearTimeout(justPlainLuckyTimer);
|
||||
return;
|
||||
}
|
||||
@@ -413,7 +413,7 @@ export async function mainBoot() {
|
||||
window.addEventListener('visibilitychange', () => {
|
||||
const now = Date.now();
|
||||
|
||||
if (document.visibilityState === 'visible') {
|
||||
if (window.document.visibilityState === 'visible') {
|
||||
// タブを高速で切り替えたら取得処理が何度も走るのを防ぐ
|
||||
if ((now - lastVisibilityChangedAt) < 1000 * 10) {
|
||||
justPlainLuckyTimer = window.setTimeout(claimPlainLucky, 1000 * 10);
|
||||
@@ -458,7 +458,7 @@ export async function mainBoot() {
|
||||
|
||||
const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt');
|
||||
const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo');
|
||||
if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) {
|
||||
if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !window.location.pathname.startsWith('/miauth')) {
|
||||
if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {
|
||||
closed: () => dispose(),
|
||||
@@ -554,7 +554,7 @@ export async function mainBoot() {
|
||||
mainRouter.push('/search');
|
||||
},
|
||||
} as const satisfies Keymap;
|
||||
document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
|
||||
window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
|
||||
|
||||
initializeSw();
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ function tick() {
|
||||
tick();
|
||||
|
||||
function calcColors() {
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const computedStyle = getComputedStyle(window.document.documentElement);
|
||||
const dark = tinycolor(computedStyle.getPropertyValue('--MI_THEME-bg')).isDark();
|
||||
const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
|
||||
majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
||||
|
||||
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<canvas ref="canvasEl" style="width: 100%; height: 100%; pointer-events: none;"></canvas>
|
||||
<canvas ref="canvasEl" style="display: block; width: 100%; height: 100%; pointer-events: none;"></canvas>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -359,7 +359,7 @@ onMounted(() => {
|
||||
|
||||
props.textarea.addEventListener('keydown', onKeydown);
|
||||
|
||||
document.body.addEventListener('mousedown', onMousedown);
|
||||
window.document.body.addEventListener('mousedown', onMousedown);
|
||||
|
||||
nextTick(() => {
|
||||
exec();
|
||||
@@ -375,7 +375,7 @@ onMounted(() => {
|
||||
onBeforeUnmount(() => {
|
||||
props.textarea.removeEventListener('keydown', onKeydown);
|
||||
|
||||
document.body.removeEventListener('mousedown', onMousedown);
|
||||
window.document.body.removeEventListener('mousedown', onMousedown);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ function onMousedown(evt: MouseEvent): void {
|
||||
const target = evt.target! as HTMLElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
|
||||
const ripple = document.createElement('div');
|
||||
const ripple = window.document.createElement('div');
|
||||
ripple.classList.add(ripples.value!.dataset.childrenClass!);
|
||||
ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
|
||||
ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';
|
||||
|
||||
@@ -112,7 +112,7 @@ watch(() => [props.instanceUrl, props.sitekey, props.secretKey], async () => {
|
||||
if (loaded || props.provider === 'mcaptcha' || props.provider === 'testcaptcha') {
|
||||
available.value = true;
|
||||
} else if (src.value !== null) {
|
||||
(document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), {
|
||||
(window.document.getElementById(scriptId.value) ?? window.document.head.appendChild(Object.assign(window.document.createElement('script'), {
|
||||
async: true,
|
||||
id: scriptId.value,
|
||||
src: src.value,
|
||||
@@ -149,7 +149,7 @@ async function requestRender() {
|
||||
if (captcha.value.render && captchaEl.value instanceof Element && props.sitekey) {
|
||||
// reCAPTCHAのレンダリング重複判定を回避するため、captchaEl配下に仮のdivを用意する.
|
||||
// (同じdivに対して複数回renderを呼び出すとreCAPTCHAはエラーを返すので)
|
||||
const elem = document.createElement('div');
|
||||
const elem = window.document.createElement('div');
|
||||
captchaEl.value.appendChild(elem);
|
||||
|
||||
captchaWidgetId.value = captcha.value.render(elem, {
|
||||
@@ -174,7 +174,7 @@ async function requestRender() {
|
||||
|
||||
function clearWidget() {
|
||||
if (props.provider === 'mcaptcha') {
|
||||
const container = document.getElementById('mcaptcha__widget-container');
|
||||
const container = window.document.getElementById('mcaptcha__widget-container');
|
||||
if (container) {
|
||||
container.innerHTML = '';
|
||||
}
|
||||
|
||||
@@ -68,11 +68,11 @@ onMounted(() => {
|
||||
rootEl.value.style.left = `${left}px`;
|
||||
}
|
||||
|
||||
document.body.addEventListener('mousedown', onMousedown);
|
||||
window.document.body.addEventListener('mousedown', onMousedown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.body.removeEventListener('mousedown', onMousedown);
|
||||
window.document.body.removeEventListener('mousedown', onMousedown);
|
||||
});
|
||||
|
||||
function onMousedown(evt: Event) {
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable import/no-default-export */
|
||||
import type { StoryObj } from '@storybook/vue3';
|
||||
import { HttpResponse, http } from 'msw';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { file } from '../../.storybook/fakes.js';
|
||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||
import MkCropperDialog from './MkCropperDialog.vue';
|
||||
import type { StoryObj } from '@storybook/vue3';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
@@ -55,7 +53,7 @@ export const Default = {
|
||||
http.get('/proxy/image.webp', async ({ request }) => {
|
||||
const url = new URL(request.url).searchParams.get('url');
|
||||
if (url === 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true') {
|
||||
const image = await (await fetch('client-assets/fedi.jpg')).blob();
|
||||
const image = await (await window.fetch('client-assets/fedi.jpg')).blob();
|
||||
return new HttpResponse(image, {
|
||||
headers: {
|
||||
'Content-Type': 'image/jpeg',
|
||||
|
||||
@@ -122,7 +122,7 @@ onMounted(() => {
|
||||
cropper = new Cropper(imgEl.value!, {
|
||||
});
|
||||
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const computedStyle = getComputedStyle(window.document.documentElement);
|
||||
|
||||
const selection = cropper.getCropperSelection()!;
|
||||
selection.themeColor = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
|
||||
|
||||
@@ -180,7 +180,7 @@ function applyToPreview() {
|
||||
nextTick(() => {
|
||||
if (currentPreviewUrl === embedPreviewUrl.value) {
|
||||
// URLが変わらなくてもリロード
|
||||
iframeEl.value?.contentWindow?.location.reload();
|
||||
iframeEl.value?.contentWindow?.window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ function toggle() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const computedStyle = getComputedStyle(window.document.documentElement);
|
||||
const parentBg = getBgColor(rootEl.value?.parentElement) ?? 'transparent';
|
||||
const myBg = computedStyle.getPropertyValue('--MI_THEME-panel');
|
||||
bgSame.value = parentBg === myBg;
|
||||
|
||||
@@ -55,7 +55,7 @@ import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurha
|
||||
const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => {
|
||||
// テスト環境で Web Worker インスタンスは作成できない
|
||||
if (import.meta.env.MODE === 'test') {
|
||||
const canvas = document.createElement('canvas');
|
||||
const canvas = window.document.createElement('canvas');
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
resolve(canvas);
|
||||
@@ -69,13 +69,11 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
|
||||
Math.min(navigator.hardwareConcurrency - 1, 4),
|
||||
);
|
||||
resolve(workers);
|
||||
if (_DEV_) console.log('WebGL2 in worker is supported!');
|
||||
} else {
|
||||
const canvas = document.createElement('canvas');
|
||||
const canvas = window.document.createElement('canvas');
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
resolve(canvas);
|
||||
if (_DEV_) console.log('WebGL2 in worker is not supported...');
|
||||
}
|
||||
testWorker.terminate();
|
||||
});
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import type { StoryObj } from '@storybook/vue3';
|
||||
import { HttpResponse, http } from 'msw';
|
||||
import { federationInstance } from '../../.storybook/fakes.js';
|
||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||
import { getChartResolver } from '../../.storybook/charts.js';
|
||||
import MkInstanceCardMini from './MkInstanceCardMini.vue';
|
||||
import type { StoryObj } from '@storybook/vue3';
|
||||
|
||||
export const Default = {
|
||||
render(args) {
|
||||
@@ -48,7 +47,7 @@ export const Default = {
|
||||
const url = new URL(urlStr);
|
||||
|
||||
if (url.href.startsWith('https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/')) {
|
||||
const image = await (await fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob();
|
||||
const image = await (await window.fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob();
|
||||
return new HttpResponse(image, {
|
||||
headers: {
|
||||
'Content-Type': 'image/jpeg',
|
||||
|
||||
@@ -126,7 +126,7 @@ function createDoughnut(chartEl, tooltip, data) {
|
||||
labels: data.map(x => x.name),
|
||||
datasets: [{
|
||||
backgroundColor: data.map(x => x.color),
|
||||
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'),
|
||||
borderColor: getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'),
|
||||
borderWidth: 2,
|
||||
hoverOffset: 0,
|
||||
data: data.map(x => x.value),
|
||||
|
||||
@@ -148,7 +148,7 @@ const keymap = {
|
||||
// PlayerElもしくはその子要素にフォーカスがあるかどうか
|
||||
function hasFocus() {
|
||||
if (!playerEl.value) return false;
|
||||
return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement);
|
||||
return playerEl.value === window.document.activeElement || playerEl.value.contains(window.document.activeElement);
|
||||
}
|
||||
|
||||
const playerEl = useTemplateRef('playerEl');
|
||||
|
||||
@@ -48,7 +48,7 @@ const props = defineProps<{
|
||||
|
||||
const gallery = useTemplateRef('gallery');
|
||||
const pswpZIndex = os.claimZIndex('middle');
|
||||
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
||||
window.document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
||||
const count = computed(() => props.mediaList.filter(media => previewable(media)).length);
|
||||
let lightbox: PhotoSwipeLightbox | null = null;
|
||||
|
||||
@@ -166,7 +166,7 @@ onMounted(() => {
|
||||
className: 'pswp__alt-text-container',
|
||||
appendTo: 'wrapper',
|
||||
onInit: (el, pswp) => {
|
||||
const textBox = document.createElement('p');
|
||||
const textBox = window.document.createElement('p');
|
||||
textBox.className = 'pswp__alt-text _acrylic';
|
||||
el.appendChild(textBox);
|
||||
|
||||
@@ -178,19 +178,19 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
lightbox.on('afterInit', () => {
|
||||
activeEl = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
||||
activeEl = window.document.activeElement instanceof HTMLElement ? window.document.activeElement : null;
|
||||
focusParent(activeEl, true, true);
|
||||
lightbox?.pswp?.element?.focus({
|
||||
preventScroll: true,
|
||||
});
|
||||
history.pushState(null, '', '#pswp');
|
||||
window.history.pushState(null, '', '#pswp');
|
||||
});
|
||||
|
||||
lightbox.on('destroy', () => {
|
||||
focusParent(activeEl, true, false);
|
||||
activeEl = null;
|
||||
if (window.location.hash === '#pswp') {
|
||||
history.back();
|
||||
window.history.back();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ const keymap = {
|
||||
// PlayerElもしくはその子要素にフォーカスがあるかどうか
|
||||
function hasFocus() {
|
||||
if (!playerEl.value) return false;
|
||||
return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement);
|
||||
return playerEl.value === window.document.activeElement || playerEl.value.contains(window.document.activeElement);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
|
||||
@@ -216,7 +216,7 @@ function showMenu(ev: MouseEvent) {
|
||||
'2.0x': 2,
|
||||
},
|
||||
},
|
||||
...(document.pictureInPictureEnabled ? [{
|
||||
...(window.document.pictureInPictureEnabled ? [{
|
||||
text: i18n.ts._mediaControls.pip,
|
||||
icon: 'ti ti-picture-in-picture',
|
||||
action: togglePictureInPicture,
|
||||
@@ -384,8 +384,8 @@ function toggleFullscreen() {
|
||||
|
||||
function togglePictureInPicture() {
|
||||
if (videoEl.value) {
|
||||
if (document.pictureInPictureElement) {
|
||||
document.exitPictureInPicture();
|
||||
if (window.document.pictureInPictureElement) {
|
||||
window.document.exitPictureInPicture();
|
||||
} else {
|
||||
videoEl.value.requestPictureInPicture();
|
||||
}
|
||||
|
||||
@@ -358,10 +358,10 @@ function switchItem(item: MenuSwitch & { ref: any }) {
|
||||
|
||||
function focusUp() {
|
||||
if (disposed) return;
|
||||
if (!itemsEl.value?.contains(document.activeElement)) return;
|
||||
if (!itemsEl.value?.contains(window.document.activeElement)) return;
|
||||
|
||||
const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable);
|
||||
const activeIndex = focusableElements.findIndex(el => el === document.activeElement);
|
||||
const activeIndex = focusableElements.findIndex(el => el === window.document.activeElement);
|
||||
const targetIndex = (activeIndex !== -1 && activeIndex !== 0) ? (activeIndex - 1) : (focusableElements.length - 1);
|
||||
const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
|
||||
|
||||
@@ -370,10 +370,10 @@ function focusUp() {
|
||||
|
||||
function focusDown() {
|
||||
if (disposed) return;
|
||||
if (!itemsEl.value?.contains(document.activeElement)) return;
|
||||
if (!itemsEl.value?.contains(window.document.activeElement)) return;
|
||||
|
||||
const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable);
|
||||
const activeIndex = focusableElements.findIndex(el => el === document.activeElement);
|
||||
const activeIndex = focusableElements.findIndex(el => el === window.document.activeElement);
|
||||
const targetIndex = (activeIndex !== -1 && activeIndex !== (focusableElements.length - 1)) ? (activeIndex + 1) : 0;
|
||||
const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
|
||||
|
||||
@@ -400,9 +400,9 @@ const onGlobalMousedown = (ev: MouseEvent) => {
|
||||
|
||||
const setupHandlers = () => {
|
||||
if (!isNestingMenu) {
|
||||
document.addEventListener('focusin', onGlobalFocusin, { passive: true });
|
||||
window.document.addEventListener('focusin', onGlobalFocusin, { passive: true });
|
||||
}
|
||||
document.addEventListener('mousedown', onGlobalMousedown, { passive: true });
|
||||
window.document.addEventListener('mousedown', onGlobalMousedown, { passive: true });
|
||||
};
|
||||
|
||||
let disposed = false;
|
||||
@@ -410,9 +410,9 @@ let disposed = false;
|
||||
const disposeHandlers = () => {
|
||||
disposed = true;
|
||||
if (!isNestingMenu) {
|
||||
document.removeEventListener('focusin', onGlobalFocusin);
|
||||
window.document.removeEventListener('focusin', onGlobalFocusin);
|
||||
}
|
||||
document.removeEventListener('mousedown', onGlobalMousedown);
|
||||
window.document.removeEventListener('mousedown', onGlobalMousedown);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -48,7 +48,7 @@ const polygonPoints = ref('');
|
||||
const headX = ref<number | null>(null);
|
||||
const headY = ref<number | null>(null);
|
||||
const clock = ref<number | null>(null);
|
||||
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-accent'));
|
||||
const accent = tinycolor(getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-accent'));
|
||||
const color = accent.toRgbString();
|
||||
|
||||
function draw(): void {
|
||||
|
||||
@@ -59,7 +59,7 @@ const pagination = computed(() => prefer.r.useGroupedNotifications.value ? {
|
||||
|
||||
function onNotification(notification) {
|
||||
const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false;
|
||||
if (isMuted || document.visibilityState === 'visible') {
|
||||
if (isMuted || window.document.visibilityState === 'visible') {
|
||||
useStream().send('readNotification');
|
||||
}
|
||||
|
||||
|
||||
21
packages/frontend/src/components/MkPageWithAnimBg.vue
Normal file
21
packages/frontend/src/components/MkPageWithAnimBg.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<MkAnimBg style="position: absolute;"/>
|
||||
<div class="_pageScrollable" style="position: absolute; top: 0; width: 100%; height: 100%;">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MkAnimBg from '@/components/MkAnimBg.vue';
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
</style>
|
||||
@@ -142,7 +142,7 @@ const {
|
||||
} = prefer.r;
|
||||
|
||||
const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value);
|
||||
const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : document.body);
|
||||
const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : window.document.body);
|
||||
|
||||
const visibility = useDocumentVisibility();
|
||||
|
||||
|
||||
@@ -151,9 +151,9 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
|
||||
closed: () => dispose(),
|
||||
});
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.appendChild(document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }'));
|
||||
document.head.appendChild(style);
|
||||
const style = window.document.createElement('style');
|
||||
style.appendChild(window.document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }'));
|
||||
window.document.head.appendChild(style);
|
||||
|
||||
const thumbWidth = getThumbWidth();
|
||||
|
||||
@@ -172,7 +172,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
|
||||
let beforeValue = finalValue.value;
|
||||
|
||||
const onMouseup = () => {
|
||||
document.head.removeChild(style);
|
||||
window.document.head.removeChild(style);
|
||||
tooltipForDragShowing.value = false;
|
||||
window.removeEventListener('mousemove', onDrag);
|
||||
window.removeEventListener('touchmove', onDrag);
|
||||
|
||||
@@ -136,7 +136,7 @@ async function menu(ev) {
|
||||
}
|
||||
|
||||
function anime() {
|
||||
if (document.hidden || !prefer.s.animation || buttonEl.value == null) return;
|
||||
if (window.document.hidden || !prefer.s.animation || buttonEl.value == null) return;
|
||||
|
||||
const rect = buttonEl.value.getBoundingClientRect();
|
||||
const x = rect.left + 16;
|
||||
|
||||
@@ -44,7 +44,7 @@ onMounted(async () => {
|
||||
|
||||
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||
|
||||
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-accent'));
|
||||
const accent = tinycolor(getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-accent'));
|
||||
const color = accent.toHex();
|
||||
|
||||
if (chartEl.value == null) return;
|
||||
|
||||
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
:width="400"
|
||||
:height="500"
|
||||
@close="onCloseModalWindow"
|
||||
@closed="console.log('MkRoleSelectDialog: closed') ; $emit('dispose')"
|
||||
@closed="emit('closed')"
|
||||
>
|
||||
<template #header>{{ title }}</template>
|
||||
<MkSpacer :marginMin="20" :marginMax="28">
|
||||
@@ -58,7 +58,7 @@ import MkLoading from '@/components/global/MkLoading.vue';
|
||||
const emit = defineEmits<{
|
||||
(ev: 'done', value: Misskey.entities.Role[]),
|
||||
(ev: 'close'),
|
||||
(ev: 'dispose'),
|
||||
(ev: 'closed'),
|
||||
}>();
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
|
||||
@@ -267,7 +267,7 @@ async function onSubmit(): Promise<void> {
|
||||
'testcaptcha-response': testcaptchaResponse.value,
|
||||
};
|
||||
|
||||
const res = await fetch(`${config.apiUrl}/signup`, {
|
||||
const res = await window.fetch(`${config.apiUrl}/signup`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -20,7 +20,7 @@ import tinycolor from 'tinycolor2';
|
||||
|
||||
const loaded = !!window.TagCanvas;
|
||||
const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const computedStyle = getComputedStyle(window.document.documentElement);
|
||||
const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||
const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||
const available = ref(false);
|
||||
@@ -57,7 +57,7 @@ onMounted(() => {
|
||||
if (loaded) {
|
||||
available.value = true;
|
||||
} else {
|
||||
document.head.appendChild(Object.assign(document.createElement('script'), {
|
||||
window.document.head.appendChild(Object.assign(window.document.createElement('script'), {
|
||||
async: true,
|
||||
src: '/client-assets/tagcanvas.min.js',
|
||||
})).addEventListener('load', () => available.value = true);
|
||||
|
||||
@@ -61,7 +61,7 @@ async function renderChart() {
|
||||
|
||||
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const computedStyle = getComputedStyle(window.document.documentElement);
|
||||
const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
|
||||
|
||||
const colorRead = accent;
|
||||
|
||||
@@ -240,7 +240,7 @@ function onHeaderMousedown(evt: MouseEvent | TouchEvent) {
|
||||
const main = rootEl.value;
|
||||
if (main == null) return;
|
||||
|
||||
if (!contains(main, document.activeElement)) main.focus();
|
||||
if (!contains(main, window.document.activeElement)) main.focus();
|
||||
|
||||
const position = main.getBoundingClientRect();
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ function openWindow() {
|
||||
|
||||
function nav(ev: MouseEvent) {
|
||||
if (behavior === 'browser') {
|
||||
location.href = props.to;
|
||||
window.location.href = props.to;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ onMounted(() => {
|
||||
|
||||
if (props.rootEl) {
|
||||
ro2 = new ResizeObserver((entries, observer) => {
|
||||
if (document.body.contains(el.value as HTMLElement)) {
|
||||
if (window.document.body.contains(el.value as HTMLElement)) {
|
||||
nextTick(() => renderTab());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -69,6 +69,8 @@ const emit = defineEmits<{
|
||||
(ev: 'update:tab', key: string);
|
||||
}>();
|
||||
|
||||
const viewId = inject(DI.viewId);
|
||||
const viewTransitionName = computed(() => `${viewId}---pageHeader`);
|
||||
const injectedPageMetadata = inject(DI.pageMetadata);
|
||||
const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value);
|
||||
|
||||
@@ -106,7 +108,7 @@ function onTabClick(): void {
|
||||
|
||||
const calcBg = () => {
|
||||
const rawBg = 'var(--MI_THEME-bg)';
|
||||
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
||||
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(window.document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
||||
tinyBg.setAlpha(0.85);
|
||||
bg.value = tinyBg.toRgbString();
|
||||
};
|
||||
@@ -120,7 +122,7 @@ onMounted(() => {
|
||||
if (el.value && el.value.parentElement) {
|
||||
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||
ro = new ResizeObserver((entries, observer) => {
|
||||
if (el.value && el.value.parentElement && document.body.contains(el.value as HTMLElement)) {
|
||||
if (el.value && el.value.parentElement && window.document.body.contains(el.value as HTMLElement)) {
|
||||
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||
}
|
||||
});
|
||||
@@ -140,6 +142,7 @@ onUnmounted(() => {
|
||||
backdrop-filter: var(--MI-blur, blur(15px));
|
||||
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||
width: 100%;
|
||||
view-transition-name: v-bind(viewTransitionName);
|
||||
}
|
||||
|
||||
.upper,
|
||||
|
||||
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="_pageContainer" style="height: 100%;">
|
||||
<div ref="rootEl" class="_pageContainer" :class="$style.root">
|
||||
<KeepAlive :max="prefer.s.numberOfPageCache">
|
||||
<Suspense :timeout="0">
|
||||
<component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/>
|
||||
@@ -18,11 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, provide, ref, shallowRef } from 'vue';
|
||||
import { inject, nextTick, onMounted, provide, ref, shallowRef, useTemplateRef } from 'vue';
|
||||
import type { Router } from '@/router.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import MkLoadingPage from '@/pages/_loading_.vue';
|
||||
import { DI } from '@/di.js';
|
||||
import { randomId } from '@/utility/random-id.js';
|
||||
import { deepEqual } from '@/utility/deep-equal.js';
|
||||
|
||||
const props = defineProps<{
|
||||
router?: Router;
|
||||
@@ -34,18 +36,76 @@ if (router == null) {
|
||||
throw new Error('no router provided');
|
||||
}
|
||||
|
||||
const viewId = randomId();
|
||||
provide(DI.viewId, viewId);
|
||||
|
||||
const currentDepth = inject(DI.routerCurrentDepth, 0);
|
||||
provide(DI.routerCurrentDepth, currentDepth + 1);
|
||||
|
||||
const rootEl = useTemplateRef('rootEl');
|
||||
onMounted(() => {
|
||||
rootEl.value.style.viewTransitionName = viewId; // view-transition-nameにcss varが使えないっぽいため直接代入
|
||||
});
|
||||
|
||||
// view-transition-newなどの<pt-name-selector>にはcss varが使えず、v-bindできないため直接スタイルを生成
|
||||
const viewTransitionStylesTag = window.document.createElement('style');
|
||||
viewTransitionStylesTag.textContent = `
|
||||
@keyframes ${viewId}-old {
|
||||
to { transform: scale(0.95); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes ${viewId}-new {
|
||||
from { transform: scale(0.95); opacity: 0; }
|
||||
}
|
||||
|
||||
::view-transition-old(${viewId}) {
|
||||
animation-duration: 0.2s;
|
||||
animation-name: ${viewId}-old;
|
||||
}
|
||||
|
||||
::view-transition-new(${viewId}) {
|
||||
animation-duration: 0.2s;
|
||||
animation-name: ${viewId}-new;
|
||||
}
|
||||
`;
|
||||
|
||||
window.document.head.appendChild(viewTransitionStylesTag);
|
||||
|
||||
const current = router.current!;
|
||||
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
|
||||
const currentPageProps = ref(current.props);
|
||||
let currentRoutePath = current.route.path;
|
||||
const key = ref(router.getCurrentFullPath());
|
||||
|
||||
router.useListener('change', ({ resolved }) => {
|
||||
if (resolved == null || 'redirect' in resolved.route) return;
|
||||
currentPageComponent.value = resolved.route.component;
|
||||
currentPageProps.value = resolved.props;
|
||||
key.value = router.getCurrentFullPath();
|
||||
if (resolved.route.path === currentRoutePath && deepEqual(resolved.props, currentPageProps.value)) return;
|
||||
|
||||
function _() {
|
||||
currentPageComponent.value = resolved.route.component;
|
||||
currentPageProps.value = resolved.props;
|
||||
key.value = router.getCurrentFullPath();
|
||||
currentRoutePath = resolved.route.path;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (prefer.s.animation && window.document.startViewTransition) {
|
||||
window.document.startViewTransition(() => new Promise((res) => {
|
||||
_();
|
||||
nextTick(() => {
|
||||
res();
|
||||
//setTimeout(res, 100);
|
||||
});
|
||||
}));
|
||||
} else {
|
||||
_();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
height: 100%;
|
||||
background-color: var(--MI_THEME-bg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,7 +42,7 @@ const highlighted = ref(props.markerId === searchMarkerId.value);
|
||||
|
||||
function checkChildren() {
|
||||
if (props.children?.includes(searchMarkerId.value)) {
|
||||
const el = document.querySelector(`[data-in-app-search-marker-id="${searchMarkerId.value}"]`);
|
||||
const el = window.document.querySelector(`[data-in-app-search-marker-id="${searchMarkerId.value}"]`);
|
||||
highlighted.value = el == null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,5 @@ export const DI = {
|
||||
router: Symbol() as InjectionKey<Router>,
|
||||
mock: Symbol() as InjectionKey<boolean>,
|
||||
pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>,
|
||||
viewId: Symbol() as InjectionKey<string>,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
el._keyHandler = makeHotkey(binding.value);
|
||||
|
||||
if (el._hotkey_global) {
|
||||
document.addEventListener('keydown', el._keyHandler, { passive: false });
|
||||
window.document.addEventListener('keydown', el._keyHandler, { passive: false });
|
||||
} else {
|
||||
el.addEventListener('keydown', el._keyHandler, { passive: false });
|
||||
}
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
|
||||
unmounted(el) {
|
||||
if (el._hotkey_global) {
|
||||
document.removeEventListener('keydown', el._keyHandler);
|
||||
window.document.removeEventListener('keydown', el._keyHandler);
|
||||
} else {
|
||||
el.removeEventListener('keydown', el._keyHandler);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export default {
|
||||
mounted(src, binding, vn) {
|
||||
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
|
||||
|
||||
const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel');
|
||||
const myBg = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel');
|
||||
|
||||
if (parentBg === myBg) {
|
||||
src.style.backgroundColor = 'var(--MI_THEME-bg)';
|
||||
|
||||
@@ -47,7 +47,7 @@ export default {
|
||||
}
|
||||
|
||||
self.show = () => {
|
||||
if (!document.body.contains(el)) return;
|
||||
if (!window.document.body.contains(el)) return;
|
||||
if (self._close) return;
|
||||
if (self.text == null) return;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export class UserPreview {
|
||||
}
|
||||
|
||||
private show() {
|
||||
if (!document.body.contains(this.el)) return;
|
||||
if (!window.document.body.contains(this.el)) return;
|
||||
if (this.promise) return;
|
||||
|
||||
const showing = ref(true);
|
||||
@@ -58,7 +58,7 @@ export class UserPreview {
|
||||
};
|
||||
|
||||
this.checkTimer = window.setInterval(() => {
|
||||
if (!document.body.contains(this.el)) {
|
||||
if (!window.document.body.contains(this.el)) {
|
||||
window.clearTimeout(this.showTimer);
|
||||
window.clearTimeout(this.hideTimer);
|
||||
this.close();
|
||||
|
||||
@@ -12,7 +12,7 @@ import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERR
|
||||
// TODO: 他のタブと永続化されたstateを同期
|
||||
|
||||
//#region loader
|
||||
const providedMetaEl = document.getElementById('misskey_meta');
|
||||
const providedMetaEl = window.document.getElementById('misskey_meta');
|
||||
|
||||
let cachedMeta = miLocalStorage.getItem('instance') ? JSON.parse(miLocalStorage.getItem('instance')!) : null;
|
||||
let cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;
|
||||
|
||||
@@ -320,7 +320,7 @@ export class Nirax<DEF extends RouteDef[]> extends EventEmitter<RouterEvents> {
|
||||
}
|
||||
const res = this.navigate(fullPath);
|
||||
if (res.route.path === '/:(*)') {
|
||||
location.href = fullPath;
|
||||
window.location.href = fullPath;
|
||||
} else {
|
||||
this.emit('push', {
|
||||
beforeFullPath,
|
||||
|
||||
@@ -23,8 +23,8 @@ export type Keys = (
|
||||
'fontSize' |
|
||||
'ui' |
|
||||
'ui_temp' |
|
||||
'locale' |
|
||||
'localeVersion' |
|
||||
'locale' | // DEPRECATED
|
||||
'localeVersion' | // DEPRECATED
|
||||
'theme' |
|
||||
'themeId' |
|
||||
'customCss' |
|
||||
|
||||
@@ -167,7 +167,7 @@ export const navbarItemDef = reactive({
|
||||
title: i18n.ts.reload,
|
||||
icon: 'ti ti-refresh',
|
||||
action: (ev) => {
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
},
|
||||
},
|
||||
profile: {
|
||||
|
||||
@@ -21,10 +21,10 @@ import MkWaitingDialog from '@/components/MkWaitingDialog.vue';
|
||||
import MkPageWindow from '@/components/MkPageWindow.vue';
|
||||
import MkToast from '@/components/MkToast.vue';
|
||||
import MkDialog from '@/components/MkDialog.vue';
|
||||
import MkPasswordDialog from '@/components/MkPasswordDialog.vue';
|
||||
import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue';
|
||||
import MkPopupMenu from '@/components/MkPopupMenu.vue';
|
||||
import MkContextMenu from '@/components/MkContextMenu.vue';
|
||||
import type MkRoleSelectDialog_TypeReferenceOnly from '@/components/MkRoleSelectDialog.vue';
|
||||
import type MkEmojiPickerDialog_TypeReferenceOnly from '@/components/MkEmojiPickerDialog.vue';
|
||||
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||
import { pleaseLogin } from '@/utility/please-login.js';
|
||||
import { showMovedDialog } from '@/utility/show-moved-dialog.js';
|
||||
@@ -181,7 +181,7 @@ type EmitsExtractor<T> = {
|
||||
export function popup<T extends Component>(
|
||||
component: T,
|
||||
props: ComponentProps<T>,
|
||||
events: ComponentEmit<T> = {} as ComponentEmit<T>,
|
||||
events: Partial<ComponentEmit<T>> = {},
|
||||
): { dispose: () => void } {
|
||||
markRaw(component);
|
||||
|
||||
@@ -460,7 +460,7 @@ export function authenticateDialog(): Promise<{
|
||||
canceled: false; result: { password: string; token: string | null; };
|
||||
}> {
|
||||
return new Promise(resolve => {
|
||||
const { dispose } = popup(MkPasswordDialog, {}, {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkPasswordDialog.vue')), {}, {
|
||||
done: result => {
|
||||
resolve(result ? { canceled: false, result } : { canceled: true, result: undefined });
|
||||
},
|
||||
@@ -617,30 +617,26 @@ export async function selectDriveFolder(multiple: boolean): Promise<Misskey.enti
|
||||
});
|
||||
}
|
||||
|
||||
export async function selectRole(params: {
|
||||
initialRoleIds?: string[],
|
||||
title?: string,
|
||||
infoMessage?: string,
|
||||
publicOnly?: boolean,
|
||||
}): Promise<
|
||||
export async function selectRole(params: ComponentProps<typeof MkRoleSelectDialog_TypeReferenceOnly>): Promise<
|
||||
{ canceled: true; result: undefined; } |
|
||||
{ canceled: false; result: Misskey.entities.Role[] }
|
||||
> {
|
||||
> {
|
||||
return new Promise((resolve) => {
|
||||
popup(defineAsyncComponent(() => import('@/components/MkRoleSelectDialog.vue')), params, {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkRoleSelectDialog.vue')), params, {
|
||||
done: roles => {
|
||||
resolve({ canceled: false, result: roles });
|
||||
},
|
||||
close: () => {
|
||||
resolve({ canceled: true, result: undefined });
|
||||
},
|
||||
}, 'dispose');
|
||||
closed: () => dispose(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function pickEmoji(src: HTMLElement, opts: ComponentProps<typeof MkEmojiPickerDialog>): Promise<string> {
|
||||
export async function pickEmoji(src: HTMLElement, opts: ComponentProps<typeof MkEmojiPickerDialog_TypeReferenceOnly>): Promise<string> {
|
||||
return new Promise(resolve => {
|
||||
const { dispose } = popup(MkEmojiPickerDialog, {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), {
|
||||
src,
|
||||
...opts,
|
||||
}, {
|
||||
@@ -675,7 +671,11 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n
|
||||
width?: number;
|
||||
onClosing?: () => void;
|
||||
}): Promise<void> {
|
||||
let returnFocusTo = getHTMLElementOrNull(src) ?? getHTMLElementOrNull(document.activeElement);
|
||||
if (!(src instanceof HTMLElement)) {
|
||||
src = null;
|
||||
}
|
||||
|
||||
let returnFocusTo = getHTMLElementOrNull(src) ?? getHTMLElementOrNull(window.document.activeElement);
|
||||
return new Promise(resolve => nextTick(() => {
|
||||
const { dispose } = popup(MkPopupMenu, {
|
||||
items,
|
||||
@@ -704,7 +704,7 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let returnFocusTo = getHTMLElementOrNull(ev.currentTarget ?? ev.target) ?? getHTMLElementOrNull(document.activeElement);
|
||||
let returnFocusTo = getHTMLElementOrNull(ev.currentTarget ?? ev.target) ?? getHTMLElementOrNull(window.document.activeElement);
|
||||
ev.preventDefault();
|
||||
return new Promise(resolve => nextTick(() => {
|
||||
const { dispose } = popup(MkContextMenu, {
|
||||
|
||||
@@ -120,7 +120,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void {
|
||||
|
||||
const calcBg = () => {
|
||||
const rawBg = pageMetadata.value.bg ?? 'var(--MI_THEME-bg)';
|
||||
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
||||
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(window.document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
||||
tinyBg.setAlpha(0.85);
|
||||
bg.value = tinyBg.toRgbString();
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ onMounted(() => {
|
||||
labels: props.data.map(x => x.name),
|
||||
datasets: [{
|
||||
backgroundColor: props.data.map(x => x.color),
|
||||
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'),
|
||||
borderColor: getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'),
|
||||
borderWidth: 2,
|
||||
hoverOffset: 0,
|
||||
data: props.data.map(x => x.value),
|
||||
|
||||
@@ -38,7 +38,7 @@ const emit = defineEmits<{
|
||||
const app = computed(() => props.session.app);
|
||||
|
||||
const name = computed(() => {
|
||||
const el = document.createElement('div');
|
||||
const el = window.document.createElement('div');
|
||||
el.textContent = app.value.name;
|
||||
return el.innerHTML;
|
||||
});
|
||||
|
||||
@@ -64,7 +64,7 @@ function accepted() {
|
||||
if (session.value && session.value.app.callbackUrl) {
|
||||
const url = new URL(session.value.app.callbackUrl);
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(url.protocol)) throw new Error('invalid url');
|
||||
location.href = `${session.value.app.callbackUrl}?token=${session.value.token}`;
|
||||
window.location.href = `${session.value.app.callbackUrl}?token=${session.value.token}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -632,7 +632,7 @@ function loadMonoTextures() {
|
||||
src = URL.createObjectURL(monoTextures[mono.img]);
|
||||
monoTextureUrls[mono.img] = src;
|
||||
} else {
|
||||
const res = await fetch(mono.img);
|
||||
const res = await window.fetch(mono.img);
|
||||
const blob = await res.blob();
|
||||
monoTextures[mono.img] = blob;
|
||||
src = URL.createObjectURL(blob);
|
||||
@@ -875,7 +875,7 @@ function loadImage(url: string) {
|
||||
|
||||
function getGameImageDriveFile() {
|
||||
return new Promise<Misskey.entities.DriveFile | null>(res => {
|
||||
const dcanvas = document.createElement('canvas');
|
||||
const dcanvas = window.document.createElement('canvas');
|
||||
dcanvas.width = game.GAME_WIDTH;
|
||||
dcanvas.height = game.GAME_HEIGHT;
|
||||
const ctx = dcanvas.getContext('2d');
|
||||
|
||||
@@ -4,9 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||
<MkSpacer :contentMax="550">
|
||||
<MkPageWithAnimBg>
|
||||
<MkSpacer :contentMax="550" :marginMax="50">
|
||||
<MkLoading v-if="uiPhase === 'fetching'"/>
|
||||
<MkExtensionInstaller v-else-if="uiPhase === 'confirm' && data" :extension="data" @confirm="install()" @cancel="close_()">
|
||||
<template #additionalInfo>
|
||||
@@ -38,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</MkStickyContainer>
|
||||
</MkPageWithAnimBg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -58,6 +57,7 @@ import { parseThemeCode, installTheme } from '@/theme.js';
|
||||
import { unisonReload } from '@/utility/unison-reload.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue';
|
||||
|
||||
const uiPhase = ref<'fetching' | 'confirm' | 'error'>('fetching');
|
||||
const errorKV = ref<{
|
||||
@@ -232,10 +232,6 @@ url.value = urlParams.get('url');
|
||||
hash.value = urlParams.get('hash');
|
||||
fetch();
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
definePage(() => ({
|
||||
title: i18n.ts._externalResourceInstaller.title,
|
||||
icon: 'ti ti-download',
|
||||
|
||||
@@ -31,7 +31,7 @@ import MkButton from '@/components/MkButton.vue';
|
||||
const state = ref<'fetching' | 'done'>('fetching');
|
||||
|
||||
function fetch() {
|
||||
const params = new URL(location.href).searchParams;
|
||||
const params = new URL(window.location.href).searchParams;
|
||||
|
||||
// acctのほうはdeprecated
|
||||
let uri = params.get('uri') ?? params.get('acct');
|
||||
@@ -76,12 +76,12 @@ function close(): void {
|
||||
|
||||
// 閉じなければ100ms後タイムラインに
|
||||
window.setTimeout(() => {
|
||||
location.href = '/';
|
||||
window.location.href = '/';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function goToMisskey(): void {
|
||||
location.href = '/';
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
fetch();
|
||||
|
||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<MkAnimBg style="position: fixed; top: 0;"/>
|
||||
<MkPageWithAnimBg>
|
||||
<div :class="$style.formContainer">
|
||||
<div :class="$style.form">
|
||||
<MkAuthConfirm
|
||||
@@ -25,16 +24,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkAuthConfirm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkPageWithAnimBg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, useTemplateRef } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
|
||||
import MkAnimBg from '@/components/MkAnimBg.vue';
|
||||
import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue';
|
||||
import MkAuthConfirm from '@/components/MkAuthConfirm.vue';
|
||||
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { definePage } from '@/page.js';
|
||||
@@ -64,7 +61,7 @@ async function onAccept(token: string) {
|
||||
const cbUrl = new URL(props.callback);
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(cbUrl.protocol)) throw new Error('invalid url');
|
||||
cbUrl.searchParams.set('session', props.session);
|
||||
location.href = cbUrl.toString();
|
||||
window.location.href = cbUrl.toString();
|
||||
} else {
|
||||
authRoot.value?.showUI('success');
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<MkAnimBg style="position: fixed; top: 0;"/>
|
||||
<MkPageWithAnimBg>
|
||||
<div :class="$style.formContainer">
|
||||
<div :class="$style.form">
|
||||
<MkAuthConfirm
|
||||
@@ -19,51 +18,51 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MkPageWithAnimBg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkAnimBg from '@/components/MkAnimBg.vue';
|
||||
import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue';
|
||||
import { definePage } from '@/page.js';
|
||||
import MkAuthConfirm from '@/components/MkAuthConfirm.vue';
|
||||
|
||||
const transactionIdMeta = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:transaction-id"]');
|
||||
const transactionIdMeta = window.document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:transaction-id"]');
|
||||
if (transactionIdMeta) {
|
||||
transactionIdMeta.remove();
|
||||
}
|
||||
|
||||
const name = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content;
|
||||
const logo = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-logo"]')?.content;
|
||||
const permissions = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:scope"]')?.content.split(' ').filter((p): p is typeof Misskey.permissions[number] => (Misskey.permissions as readonly string[]).includes(p)) ?? [];
|
||||
const name = window.document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content;
|
||||
const logo = window.document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-logo"]')?.content;
|
||||
const permissions = window.document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:scope"]')?.content.split(' ').filter((p): p is typeof Misskey.permissions[number] => (Misskey.permissions as readonly string[]).includes(p)) ?? [];
|
||||
|
||||
function doPost(token: string, decision: 'accept' | 'deny') {
|
||||
const form = document.createElement('form');
|
||||
const form = window.document.createElement('form');
|
||||
form.action = '/oauth/decision';
|
||||
form.method = 'post';
|
||||
form.acceptCharset = 'utf-8';
|
||||
|
||||
const loginToken = document.createElement('input');
|
||||
const loginToken = window.document.createElement('input');
|
||||
loginToken.type = 'hidden';
|
||||
loginToken.name = 'login_token';
|
||||
loginToken.value = token;
|
||||
form.appendChild(loginToken);
|
||||
|
||||
const transactionId = document.createElement('input');
|
||||
const transactionId = window.document.createElement('input');
|
||||
transactionId.type = 'hidden';
|
||||
transactionId.name = 'transaction_id';
|
||||
transactionId.value = transactionIdMeta?.content ?? '';
|
||||
form.appendChild(transactionId);
|
||||
|
||||
if (decision === 'deny') {
|
||||
const cancel = document.createElement('input');
|
||||
const cancel = window.document.createElement('input');
|
||||
cancel.type = 'hidden';
|
||||
cancel.name = 'cancel';
|
||||
cancel.value = 'cancel';
|
||||
form.appendChild(cancel);
|
||||
}
|
||||
|
||||
document.body.appendChild(form);
|
||||
window.document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ async function tokenDone() {
|
||||
function downloadBackupCodes() {
|
||||
if (backupCodes.value !== undefined) {
|
||||
const txtBlob = new Blob([backupCodes.value.join('\n')], { type: 'text/plain' });
|
||||
const dummya = document.createElement('a');
|
||||
const dummya = window.document.createElement('a');
|
||||
dummya.href = URL.createObjectURL(txtBlob);
|
||||
dummya.download = `${$i.username}@${hostname}` + (port !== '' ? `_${port}` : '') + '-2fa-backup-codes.txt';
|
||||
dummya.click();
|
||||
|
||||
@@ -606,8 +606,6 @@ const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies');
|
||||
|
||||
watch(lang, () => {
|
||||
miLocalStorage.setItem('lang', lang.value as string);
|
||||
miLocalStorage.removeItem('locale');
|
||||
miLocalStorage.removeItem('localeVersion');
|
||||
});
|
||||
|
||||
watch([
|
||||
|
||||
@@ -182,12 +182,12 @@ function close(): void {
|
||||
|
||||
// 閉じなければ100ms後タイムラインに
|
||||
window.setTimeout(() => {
|
||||
location.href = '/';
|
||||
window.location.href = '/';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function goToMisskey(): void {
|
||||
location.href = '/';
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
function onPosted(): void {
|
||||
|
||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<MkAnimBg style="position: fixed; top: 0;"/>
|
||||
<MkPageWithAnimBg>
|
||||
<div :class="$style.formContainer">
|
||||
<form :class="$style.form" class="_panel" @submit.prevent="submit()">
|
||||
<div :class="$style.banner">
|
||||
@@ -21,13 +20,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</MkPageWithAnimBg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkAnimBg from '@/components/MkAnimBg.vue';
|
||||
import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
@@ -64,8 +63,8 @@ function submit() {
|
||||
min-height: 100svh;
|
||||
padding: 32px 32px 64px 32px;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
.form {
|
||||
|
||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<MkAnimBg style="position: fixed; top: 0;"/>
|
||||
<MkPageWithAnimBg>
|
||||
<div :class="$style.formContainer">
|
||||
<form :class="$style.form" class="_panel" @submit.prevent="submit()">
|
||||
<div :class="$style.title">
|
||||
@@ -35,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</MkPageWithAnimBg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -46,7 +45,7 @@ import MkInput from '@/components/MkInput.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkAnimBg from '@/components/MkAnimBg.vue';
|
||||
import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue';
|
||||
import { login } from '@/accounts.js';
|
||||
|
||||
const username = ref('');
|
||||
|
||||
@@ -125,8 +125,8 @@ function syncBetweenTabs() {
|
||||
|
||||
window.setInterval(syncBetweenTabs, 5000);
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
window.document.addEventListener('visibilitychange', () => {
|
||||
if (window.document.visibilityState === 'visible') {
|
||||
syncBetweenTabs();
|
||||
}
|
||||
});
|
||||
@@ -136,7 +136,7 @@ let latestBackupAt = 0;
|
||||
window.setInterval(() => {
|
||||
if ($i == null) return;
|
||||
if (!store.s.enablePreferencesAutoCloudBackup) return;
|
||||
if (document.visibilityState !== 'visible') return; // 同期されていない古い値がバックアップされるのを防ぐ
|
||||
if (window.document.visibilityState !== 'visible') return; // 同期されていない古い値がバックアップされるのを防ぐ
|
||||
if (prefer.profile.modifiedAt <= latestBackupAt) return;
|
||||
|
||||
cloudBackup().then(() => {
|
||||
|
||||
@@ -32,6 +32,8 @@ export type SoundStore = {
|
||||
// NOTE: デフォルト値は他の設定の状態に依存してはならない(依存していた場合、ユーザーがその設定項目単体で「初期値にリセット」した場合不具合の原因になる)
|
||||
|
||||
export const PREF_DEF = {
|
||||
// TODO: 持つのはホストやユーザーID、ユーザー名など最低限にしといて、その他のプロフィール情報はpreferences外で管理した方が綺麗そう
|
||||
// 現状だと、updateCurrentAccount/updateCurrentAccountPartialが呼ばれるたびに「設定」へのcommitが行われて不自然(明らかに設定の更新とは捉えにくい)だし
|
||||
accounts: {
|
||||
default: [] as [host: string, user: Misskey.entities.User][],
|
||||
},
|
||||
|
||||
@@ -139,9 +139,16 @@ export class PreferencesManager {
|
||||
}
|
||||
|
||||
public commit<K extends keyof PREF>(key: K, value: ValueOf<K>) {
|
||||
console.log('prefer:commit', key, value);
|
||||
const v = JSON.parse(JSON.stringify(value)); // deep copy 兼 vueのプロキシ解除
|
||||
|
||||
this.rewriteRawState(key, value);
|
||||
if (deepEqual(this.s[key], v)) {
|
||||
console.log('(skip) prefer:commit', key, v);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('prefer:commit', key, v);
|
||||
|
||||
this.rewriteRawState(key, v);
|
||||
|
||||
const record = this.getMatchedRecordOf(key);
|
||||
|
||||
@@ -149,7 +156,7 @@ export class PreferencesManager {
|
||||
this.profile.preferences[key].push([makeScope({
|
||||
server: host,
|
||||
account: $i!.id,
|
||||
}), value, {}]);
|
||||
}), v, {}]);
|
||||
this.save();
|
||||
return;
|
||||
}
|
||||
@@ -157,12 +164,12 @@ export class PreferencesManager {
|
||||
if (parseScope(record[0]).server == null && this.isServerDependentKey(key)) {
|
||||
this.profile.preferences[key].push([makeScope({
|
||||
server: host,
|
||||
}), value, {}]);
|
||||
}), v, {}]);
|
||||
this.save();
|
||||
return;
|
||||
}
|
||||
|
||||
record[1] = value;
|
||||
record[1] = v;
|
||||
this.save();
|
||||
|
||||
if (record[2].sync) {
|
||||
|
||||
@@ -106,14 +106,14 @@ async function renameProfile() {
|
||||
function exportCurrentProfile() {
|
||||
const p = prefer.profile;
|
||||
const txtBlob = new Blob([JSON.stringify(p)], { type: 'text/plain' });
|
||||
const dummya = document.createElement('a');
|
||||
const dummya = window.document.createElement('a');
|
||||
dummya.href = URL.createObjectURL(txtBlob);
|
||||
dummya.download = `${p.name || p.id}.misskeypreferences`;
|
||||
dummya.click();
|
||||
}
|
||||
|
||||
function importProfile() {
|
||||
const input = document.createElement('input');
|
||||
const input = window.document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.misskeypreferences';
|
||||
input.onchange = async () => {
|
||||
|
||||
@@ -17,10 +17,10 @@ export function createRouter(fullPath: string): Router {
|
||||
return new Nirax(ROUTE_DEF, fullPath, !!$i, page(() => import('@/pages/not-found.vue')));
|
||||
}
|
||||
|
||||
export const mainRouter = createRouter(location.pathname + location.search + location.hash);
|
||||
export const mainRouter = createRouter(window.location.pathname + window.location.search + window.location.hash);
|
||||
|
||||
window.addEventListener('popstate', (event) => {
|
||||
mainRouter.replace(location.pathname + location.search + location.hash);
|
||||
mainRouter.replace(window.location.pathname + window.location.search + window.location.hash);
|
||||
});
|
||||
|
||||
mainRouter.addListener('push', ctx => {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as Misskey from 'misskey-js';
|
||||
|
||||
const providedContextEl = document.getElementById('misskey_clientCtx');
|
||||
const providedContextEl = window.document.getElementById('misskey_clientCtx');
|
||||
|
||||
export type ServerContext = {
|
||||
clip?: Misskey.entities.Clip;
|
||||
|
||||
@@ -29,10 +29,10 @@ export function useStream(): Misskey.IStream {
|
||||
timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL);
|
||||
|
||||
// send heartbeat right now when last send time is over HEART_BEAT_INTERVAL
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
window.document.addEventListener('visibilitychange', () => {
|
||||
if (
|
||||
!stream
|
||||
|| document.visibilityState !== 'visible'
|
||||
|| window.document.visibilityState !== 'visible'
|
||||
|| Date.now() - lastHeartbeatCall < HEART_BEAT_INTERVAL
|
||||
) return;
|
||||
heartbeat();
|
||||
@@ -42,7 +42,7 @@ export function useStream(): Misskey.IStream {
|
||||
}
|
||||
|
||||
function heartbeat(): void {
|
||||
if (stream != null && document.visibilityState === 'visible') {
|
||||
if (stream != null && window.document.visibilityState === 'visible') {
|
||||
stream.heartbeat();
|
||||
}
|
||||
lastHeartbeatCall = Date.now();
|
||||
|
||||
@@ -68,10 +68,10 @@ let timeout: number | null = null;
|
||||
export function applyTheme(theme: Theme, persist = true) {
|
||||
if (timeout) window.clearTimeout(timeout);
|
||||
|
||||
document.documentElement.classList.add('_themeChanging_');
|
||||
window.document.documentElement.classList.add('_themeChanging_');
|
||||
|
||||
timeout = window.setTimeout(() => {
|
||||
document.documentElement.classList.remove('_themeChanging_');
|
||||
window.document.documentElement.classList.remove('_themeChanging_');
|
||||
|
||||
// 色計算など再度行えるようにクライアント全体に通知
|
||||
globalEvents.emit('themeChanged');
|
||||
@@ -79,7 +79,7 @@ export function applyTheme(theme: Theme, persist = true) {
|
||||
|
||||
const colorScheme = theme.base === 'dark' ? 'dark' : 'light';
|
||||
|
||||
document.documentElement.dataset.colorScheme = colorScheme;
|
||||
window.document.documentElement.dataset.colorScheme = colorScheme;
|
||||
|
||||
// Deep copy
|
||||
const _theme = deepClone(theme);
|
||||
@@ -91,7 +91,7 @@ export function applyTheme(theme: Theme, persist = true) {
|
||||
|
||||
const props = compile(_theme);
|
||||
|
||||
for (const tag of document.head.children) {
|
||||
for (const tag of window.document.head.children) {
|
||||
if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
|
||||
tag.setAttribute('content', props['htmlThemeColor']);
|
||||
break;
|
||||
@@ -99,10 +99,10 @@ export function applyTheme(theme: Theme, persist = true) {
|
||||
}
|
||||
|
||||
for (const [k, v] of Object.entries(props)) {
|
||||
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
|
||||
window.document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
|
||||
}
|
||||
|
||||
document.documentElement.style.setProperty('color-scheme', colorScheme);
|
||||
window.document.documentElement.style.setProperty('color-scheme', colorScheme);
|
||||
|
||||
if (persist) {
|
||||
miLocalStorage.setItem('theme', JSON.stringify(props));
|
||||
|
||||
@@ -67,7 +67,7 @@ const dev = _DEV_;
|
||||
const notifications = ref<Misskey.entities.Notification[]>([]);
|
||||
|
||||
function onNotification(notification: Misskey.entities.Notification, isClient = false) {
|
||||
if (document.visibilityState === 'visible') {
|
||||
if (window.document.visibilityState === 'visible') {
|
||||
if (!isClient && notification.type !== 'test') {
|
||||
// サーバーサイドのテスト通知の際は自動で既読をつけない(テストできないので)
|
||||
useStream().send('readNotification');
|
||||
|
||||
@@ -129,8 +129,8 @@ watch(store.r.menuDisplay, () => {
|
||||
});
|
||||
|
||||
function toggleIconOnly() {
|
||||
if (document.startViewTransition && prefer.s.animation) {
|
||||
document.startViewTransition(() => {
|
||||
if (window.document.startViewTransition && prefer.s.animation) {
|
||||
window.document.startViewTransition(() => {
|
||||
store.set('menuDisplay', iconOnly.value ? 'sideFull' : 'sideIcon');
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,7 @@ function resetDisconnected() {
|
||||
}
|
||||
|
||||
function reload() {
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
useStream().on('_disconnected_', onDisconnected);
|
||||
|
||||
@@ -87,9 +87,9 @@ provideMetadataReceiver((metadataGetter) => {
|
||||
pageMetadata.value = info;
|
||||
if (pageMetadata.value) {
|
||||
if (isRoot.value && pageMetadata.value.title === instanceName) {
|
||||
document.title = pageMetadata.value.title;
|
||||
window.document.title = pageMetadata.value.title;
|
||||
} else {
|
||||
document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -139,10 +139,10 @@ if (window.innerWidth < 1024) {
|
||||
const currentUI = miLocalStorage.getItem('ui');
|
||||
miLocalStorage.setItem('ui_temp', currentUI ?? 'default');
|
||||
miLocalStorage.setItem('ui', 'default');
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
document.documentElement.style.overflowY = 'scroll';
|
||||
window.document.documentElement.style.overflowY = 'scroll';
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
|
||||
@@ -202,8 +202,8 @@ function onWheel(ev: WheelEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
document.documentElement.style.overflowY = 'hidden';
|
||||
document.documentElement.style.scrollBehavior = 'auto';
|
||||
window.document.documentElement.style.overflowY = 'hidden';
|
||||
window.document.documentElement.style.scrollBehavior = 'auto';
|
||||
|
||||
async function deleteProfile() {
|
||||
if (prefer.s['deck.profile'] == null) return;
|
||||
|
||||
@@ -30,9 +30,9 @@ provideMetadataReceiver((metadataGetter) => {
|
||||
pageMetadata.value = info;
|
||||
if (pageMetadata.value) {
|
||||
if (isRoot.value && pageMetadata.value.title === instanceName) {
|
||||
document.title = pageMetadata.value.title;
|
||||
window.document.title = pageMetadata.value.title;
|
||||
} else {
|
||||
document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -143,9 +143,9 @@ provideMetadataReceiver((metadataGetter) => {
|
||||
pageMetadata.value = info;
|
||||
if (pageMetadata.value) {
|
||||
if (isRoot.value && pageMetadata.value.title === instanceName) {
|
||||
document.title = pageMetadata.value.title;
|
||||
window.document.title = pageMetadata.value.title;
|
||||
} else {
|
||||
document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -170,7 +170,7 @@ if (window.innerWidth > 1024) {
|
||||
if (tempUI) {
|
||||
miLocalStorage.setItem('ui', tempUI);
|
||||
miLocalStorage.removeItem('ui_temp');
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,12 +205,12 @@ provide<Ref<number>>(CURRENT_STICKY_BOTTOM, navFooterHeight);
|
||||
watch(navFooter, () => {
|
||||
if (navFooter.value) {
|
||||
navFooterHeight.value = navFooter.value.offsetHeight;
|
||||
document.body.style.setProperty('--MI-stickyBottom', `${navFooterHeight.value}px`);
|
||||
document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)');
|
||||
window.document.body.style.setProperty('--MI-stickyBottom', `${navFooterHeight.value}px`);
|
||||
window.document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)');
|
||||
} else {
|
||||
navFooterHeight.value = 0;
|
||||
document.body.style.setProperty('--MI-stickyBottom', '0px');
|
||||
document.body.style.setProperty('--MI-minBottomSpacing', '0px');
|
||||
window.document.body.style.setProperty('--MI-stickyBottom', '0px');
|
||||
window.document.body.style.setProperty('--MI-minBottomSpacing', '0px');
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
|
||||
@@ -51,9 +51,9 @@ provideMetadataReceiver((metadataGetter) => {
|
||||
pageMetadata.value = info;
|
||||
if (pageMetadata.value) {
|
||||
if (isRoot.value && pageMetadata.value.title === instanceName) {
|
||||
document.title = pageMetadata.value.title;
|
||||
window.document.title = pageMetadata.value.title;
|
||||
} else {
|
||||
document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
|
||||
|
||||
const pageMetadata = ref<null | PageMetadata>(null);
|
||||
|
||||
const showBottom = !(new URLSearchParams(location.search)).has('zen') && ui === 'deck';
|
||||
const showBottom = !(new URLSearchParams(window.location.search)).has('zen') && ui === 'deck';
|
||||
|
||||
provide(DI.router, mainRouter);
|
||||
provideMetadataReceiver((metadataGetter) => {
|
||||
@@ -45,9 +45,9 @@ provideMetadataReceiver((metadataGetter) => {
|
||||
pageMetadata.value = info;
|
||||
if (pageMetadata.value) {
|
||||
if (isRoot.value && pageMetadata.value.title === instanceName) {
|
||||
document.title = pageMetadata.value.title;
|
||||
window.document.title = pageMetadata.value.title;
|
||||
} else {
|
||||
document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -86,7 +86,7 @@ export function useNoteCapture(props: {
|
||||
function capture(withHandler = false): void {
|
||||
if (connection) {
|
||||
// TODO: このノートがストリーミング経由で流れてきた場合のみ sr する
|
||||
connection.send(document.body.contains(props.rootEl.value ?? null as Node | null) ? 'sr' : 's', { id: note.value.id });
|
||||
connection.send(window.document.body.contains(props.rootEl.value ?? null as Node | null) ? 'sr' : 's', { id: note.value.id });
|
||||
if (pureNote.value.id !== note.value.id) connection.send('s', { id: pureNote.value.id });
|
||||
if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function useTooltip(
|
||||
if (!isHovering) return;
|
||||
if (elRef.value == null) return;
|
||||
const el = elRef.value instanceof Element ? elRef.value : elRef.value.$el;
|
||||
if (!document.body.contains(el)) return; // openしようとしたときに既に元要素がDOMから消えている場合があるため
|
||||
if (!window.document.body.contains(el)) return; // openしようとしたときに既に元要素がDOMから消えている場合があるため
|
||||
|
||||
const showing = ref(true);
|
||||
onShow(showing);
|
||||
@@ -38,7 +38,7 @@ export function useTooltip(
|
||||
};
|
||||
|
||||
autoHidingTimer = window.setInterval(() => {
|
||||
if (elRef.value == null || !document.body.contains(elRef.value instanceof Element ? elRef.value : elRef.value.$el)) {
|
||||
if (elRef.value == null || !window.document.body.contains(elRef.value instanceof Element ? elRef.value : elRef.value.$el)) {
|
||||
if (!isHovering) return;
|
||||
isHovering = false;
|
||||
window.clearTimeout(timeoutId);
|
||||
|
||||
@@ -13,8 +13,10 @@ export async function clearCache() {
|
||||
os.waiting();
|
||||
miLocalStorage.removeItem('instance');
|
||||
miLocalStorage.removeItem('instanceCachedAt');
|
||||
//#region deprecated
|
||||
miLocalStorage.removeItem('locale');
|
||||
miLocalStorage.removeItem('localeVersion');
|
||||
//#endregion
|
||||
miLocalStorage.removeItem('theme');
|
||||
miLocalStorage.removeItem('emojis');
|
||||
miLocalStorage.removeItem('lastEmojisFetchedAt');
|
||||
|
||||
@@ -50,7 +50,7 @@ function releaseFocusTrap(el: HTMLElement): void {
|
||||
|
||||
const highestZIndexElement = getHighestZIndexElement();
|
||||
|
||||
if (el.parentElement != null && el !== document.body) {
|
||||
if (el.parentElement != null && el !== window.document.body) {
|
||||
el.parentElement.childNodes.forEach((siblingNode) => {
|
||||
const siblingEl = getHTMLElementOrNull(siblingNode);
|
||||
if (!siblingEl) return;
|
||||
@@ -104,7 +104,7 @@ export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEl
|
||||
el.inert = false;
|
||||
}
|
||||
|
||||
if (el.parentElement != null && el !== document.body) {
|
||||
if (el.parentElement != null && el !== window.document.body) {
|
||||
el.parentElement.childNodes.forEach((siblingNode) => {
|
||||
const siblingEl = getHTMLElementOrNull(siblingNode);
|
||||
if (!siblingEl) return;
|
||||
|
||||
@@ -58,7 +58,7 @@ export const focusParent = (input: MaybeHTMLElement | null | undefined, self = f
|
||||
|
||||
const focusOrScroll = (element: HTMLElement, scroll: boolean) => {
|
||||
if (scroll) {
|
||||
const scrollContainer = getScrollContainer(element) ?? document.documentElement;
|
||||
const scrollContainer = getScrollContainer(element) ?? window.document.documentElement;
|
||||
const scrollContainerTop = getScrollPosition(scrollContainer);
|
||||
const stickyTop = getStickyTop(element, scrollContainer);
|
||||
const stickyBottom = getStickyBottom(element, scrollContainer);
|
||||
@@ -74,7 +74,7 @@ const focusOrScroll = (element: HTMLElement, scroll: boolean) => {
|
||||
scrollContainer.scrollTo({ top: scrollTo, behavior: 'instant' });
|
||||
}
|
||||
|
||||
if (document.activeElement !== element) {
|
||||
if (window.document.activeElement !== element) {
|
||||
element.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,8 +35,8 @@ export const requestFullscreen = ({ videoEl, playerEl, options }: RequestFullscr
|
||||
|
||||
export const exitFullscreen = ({ videoEl }: ExitFullscreenProps) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (document.exitFullscreen != null) {
|
||||
document.exitFullscreen();
|
||||
if (window.document.exitFullscreen != null) {
|
||||
window.document.exitFullscreen();
|
||||
return;
|
||||
}
|
||||
if (videoEl.webkitExitFullscreen != null) {
|
||||
|
||||
@@ -54,9 +54,9 @@ export const makeHotkey = (keymap: Keymap) => {
|
||||
const actions = parseKeymap(keymap);
|
||||
return (ev: KeyboardEvent) => {
|
||||
if ('pswp' in window && window.pswp != null) return;
|
||||
if (document.activeElement != null) {
|
||||
if (IGNORE_ELEMENTS.includes(document.activeElement.tagName.toLowerCase())) return;
|
||||
if (getHTMLElementOrNull(document.activeElement)?.isContentEditable) return;
|
||||
if (window.document.activeElement != null) {
|
||||
if (IGNORE_ELEMENTS.includes(window.document.activeElement.tagName.toLowerCase())) return;
|
||||
if (getHTMLElementOrNull(window.document.activeElement)?.isContentEditable) return;
|
||||
}
|
||||
for (const action of actions) {
|
||||
if (matchPatterns(ev, action)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user