Compare commits

..

9 Commits

Author SHA1 Message Date
Acid Chicken (硫酸鶏)
f066247988 feat: bundled locale 2025-03-20 19:03:53 +09:00
syuilo
6015254e59 lint fixes 2025-03-20 19:00:09 +09:00
syuilo
c02f0b3b33 Update eslint.config.js 2025-03-20 18:59:10 +09:00
syuilo
abddd40c09 enhance(frontend): 通常のRouterViewにTransitionを追加 2025-03-20 18:55:32 +09:00
かっこかり
a865a949b5 fix(frontend): MkRoleSelectDialogでのpopupの使い方が誤っているのを修正 (#15683) 2025-03-20 16:36:37 +09:00
syuilo
0007723405 fix lint 2025-03-20 16:34:50 +09:00
syuilo
71188b3463 fix lint 2025-03-20 16:10:38 +09:00
syuilo
7f534a41a6 fix lint 2025-03-20 16:07:52 +09:00
syuilo
f25963e2c2 Update eslint.config.js 2025-03-20 16:06:32 +09:00
111 changed files with 495 additions and 441 deletions

View File

@@ -9,8 +9,6 @@ reset: "Obnovit"
notifications: "Oznámení" notifications: "Oznámení"
username: "Uživatelské jméno" username: "Uživatelské jméno"
password: "Heslo" password: "Heslo"
initialPasswordForSetup: "Počáteční heslo pro nastavení"
initialPasswordIsIncorrect: "Počáteční heslo pro nastavení je nesprávné"
forgotPassword: "Zapomenuté heslo" forgotPassword: "Zapomenuté heslo"
fetchingAsApObject: "Načítám data z Fediversu..." fetchingAsApObject: "Načítám data z Fediversu..."
ok: "Potvrdit" ok: "Potvrdit"
@@ -480,8 +478,6 @@ uiLanguage: "Jazyk uživatelského rozhraní"
aboutX: "O {x}" aboutX: "O {x}"
emojiStyle: "Styl emoji" emojiStyle: "Styl emoji"
native: "Výchozí" native: "Výchozí"
style: "Vzhled"
popup: "Vyskakovací okno"
showNoteActionsOnlyHover: "Zobrazit akce poznámky jenom při naběhnutí myši" showNoteActionsOnlyHover: "Zobrazit akce poznámky jenom při naběhnutí myši"
noHistory: "Žádná historie" noHistory: "Žádná historie"
signinHistory: "Historie přihlášení" signinHistory: "Historie přihlášení"

View File

@@ -1144,7 +1144,7 @@ preventAiLearning: "Verwendung in machinellem Lernen (Generative bzw. Prediktive
preventAiLearningDescription: "Fordert Crawler auf, gepostetes Text- oder Bildmaterial usw. nicht in Datensätzen für maschinelles Lernen (Generative bzw. Prediktive AI/KI) zu verwenden. Dies wird durch das Hinzufügen einer \"noai\"-Flag in der HTML-Antwort des jeweiligen Inhalts erreicht. Da diese Flag jedoch ignoriert werden kann, ist eine vollständige Verhinderung hierdurch nicht möglich." preventAiLearningDescription: "Fordert Crawler auf, gepostetes Text- oder Bildmaterial usw. nicht in Datensätzen für maschinelles Lernen (Generative bzw. Prediktive AI/KI) zu verwenden. Dies wird durch das Hinzufügen einer \"noai\"-Flag in der HTML-Antwort des jeweiligen Inhalts erreicht. Da diese Flag jedoch ignoriert werden kann, ist eine vollständige Verhinderung hierdurch nicht möglich."
options: "Optionen" options: "Optionen"
specifyUser: "Spezifischer Benutzer" specifyUser: "Spezifischer Benutzer"
lookupConfirm: "Bist du sicher, dass du das nachschlagen möchtest?" lookupConfirm: "Zustimmen?"
openTagPageConfirm: "Hashtag Seite wirklich öffnen?" openTagPageConfirm: "Hashtag Seite wirklich öffnen?"
specifyHost: "Host" specifyHost: "Host"
failedToPreviewUrl: "Vorschau nicht anzeigbar" failedToPreviewUrl: "Vorschau nicht anzeigbar"

View File

@@ -1392,7 +1392,7 @@ _abuseUserReport:
resolve: "Resolve" resolve: "Resolve"
accept: "Accept" accept: "Accept"
reject: "Reject" reject: "Reject"
resolveTutorial: "If the report's content is legitimate, select \"Accept\" to mark it as resolved.\nIf the report's content is illegitimate, select \"Reject\" to ignore it." resolveTutorial: "If the report is legitimate in content, select \"Accept\" to mark the case as resolved in the affirmative.\nIf the content of the report is not legitimate, select \"Reject\" to mark the case as resolved in the negative."
_delivery: _delivery:
status: "Delivery status" status: "Delivery status"
stop: "Suspended" stop: "Suspended"
@@ -2598,7 +2598,7 @@ _webhookSettings:
testRemarks: "Click the button to the right of the switch to send a test Webhook with dummy data." testRemarks: "Click the button to the right of the switch to send a test Webhook with dummy data."
_abuseReport: _abuseReport:
_notificationRecipient: _notificationRecipient:
createRecipient: "Add recipient for reports" createRecipient: "Add a recipient for reports"
modifyRecipient: "Edit a recipient for reports" modifyRecipient: "Edit a recipient for reports"
recipientType: "Notification type" recipientType: "Notification type"
_recipientType: _recipientType:
@@ -2828,7 +2828,7 @@ _customEmojisManager:
confirmImportEmojisTitle: "Import Emojis" confirmImportEmojisTitle: "Import Emojis"
confirmImportEmojisDescription: "Import {count} Emoji(s) received from the remote server. Please pay close attention to the license of the Emoji. Are you sure to continue?" confirmImportEmojisDescription: "Import {count} Emoji(s) received from the remote server. Please pay close attention to the license of the Emoji. Are you sure to continue?"
_local: _local:
tabTitleList: "Registered emojis" tabTitleList: "List of registered Emojis"
tabTitleRegister: "Emoji registration" tabTitleRegister: "Emoji registration"
_list: _list:
emojisNothing: "There are no registered Emojis." emojisNothing: "There are no registered Emojis."

View File

@@ -698,7 +698,6 @@ userSaysSomethingAbout: "{name}님이 \"{word}\"를 언급했습니다."
makeActive: "활성화" makeActive: "활성화"
display: "보기" display: "보기"
copy: "복사" copy: "복사"
copiedToClipboard: "클립보드에 복사되었습니다."
metrics: "통계" metrics: "통계"
overview: "요약" overview: "요약"
logs: "로그" logs: "로그"
@@ -1295,7 +1294,7 @@ thereAreNChanges: "{n}건 변경이 있습니다."
signinWithPasskey: "패스키로 로그인" signinWithPasskey: "패스키로 로그인"
unknownWebAuthnKey: "등록되지 않은 패스키입니다." unknownWebAuthnKey: "등록되지 않은 패스키입니다."
passkeyVerificationFailed: "패스키 검증을 실패했습니다." passkeyVerificationFailed: "패스키 검증을 실패했습니다."
passkeyVerificationSucceededButPasswordlessLoginDisabled: "입력된 패스키는 정상적이나, 비밀번호 없이 로그인 하는 기능이 비활성화 되어있습니다." passkeyVerificationSucceededButPasswordlessLoginDisabled: "패스키를 검증했으나, 비밀번호 없이 로그인하기가 꺼져 있습니다."
messageToFollower: "팔로워에게 보낼 메시지" messageToFollower: "팔로워에게 보낼 메시지"
target: "대상" target: "대상"
testCaptchaWarning: "CAPTCHA를 테스트하기 위한 기능입니다. <strong>실제 환경에서는 사용하지 마세요.</strong>" testCaptchaWarning: "CAPTCHA를 테스트하기 위한 기능입니다. <strong>실제 환경에서는 사용하지 마세요.</strong>"
@@ -1326,40 +1325,21 @@ skip: "건너뛰기"
restore: "복원" restore: "복원"
syncBetweenDevices: "장치간 동기화" syncBetweenDevices: "장치간 동기화"
preferenceSyncConflictTitle: "서버에 설정값이 존재합니다." preferenceSyncConflictTitle: "서버에 설정값이 존재합니다."
preferenceSyncConflictText: "동기화를 활성화 한 항목의 설정 값은 서버에 저장되지만, 해당 항목은 이미 서버에 설정 값이 저장되어져 있습니다. 어느 쪽의 설정 값을 덮어씌울까요?"
preferenceSyncConflictChoiceServer: "서버 설정값" preferenceSyncConflictChoiceServer: "서버 설정값"
preferenceSyncConflictChoiceDevice: "장치 설정값" preferenceSyncConflictChoiceDevice: "장치 설정값"
preferenceSyncConflictChoiceCancel: "동기화 취소"
paste: "붙여넣기" paste: "붙여넣기"
emojiPalette: "이모지 팔레트" emojiPalette: "이모지 팔레트"
postForm: "글 입력란" postForm: "글 입력란"
textCount: "문자 수"
information: "정보" information: "정보"
_emojiPalette: _emojiPalette:
palettes: "팔레트" palettes: "팔레트"
enableSyncBetweenDevicesForPalettes: "팔레트의 디바이스 간 동기화를 활성화"
paletteForMain: "메인으로 사용할 팔레트" paletteForMain: "메인으로 사용할 팔레트"
paletteForReaction: "리액션으로 사용할 팔레트" paletteForReaction: "리액션으로 사용할 팔레트"
_settings: _settings:
driveBanner: "드라이브 관리, 사용량 확인, 파일 업로드에 관한 설정을 합니다."
pluginBanner: "플러그인을 사용하면 클라이언트 기능을 확장할 수 있습니다. 플러그인 설치와 개별적인 설정을 합니다."
notificationsBanner: "서버에서 받는 알림의 종류 및 범위, 푸시 알림 설정을 합니다."
api: "API" api: "API"
webhook: "Webhook" webhook: "Webhook"
serviceConnection: "서비스 연동" serviceConnection: "서비스 연동"
serviceConnectionBanner: "외부 앱, 서비스와 연결하기 위한 액세스 토큰과 웹 훅 관리 설정을 합니다."
accountData: "계정 데이터" accountData: "계정 데이터"
accountDataBanner: "계정 데이터의 아카이브를 추출하기/가져오기 하여 관리할 수 있습니다."
muteAndBlockBanner: "숨길 컨텐츠의 설정과, 특정 유저의 리액션을 제한하는 설정을 관리합니다."
accessibilityBanner: "좀 더 쾌적하게 사용할 수 있도록 클라이언트의 시각 및 움직임에 관한 개인화 설정을 합니다."
privacyBanner: "컨텐츠, 계정의 발견 범위, 팔로우 승인제 등의 계정의 프라이버시에 관한 설정을 합니다."
securityBanner: "비밀번호, 로그인 방법, OTP, 패스 키 등의 계정의 보안에 관련된 설정을 합니다."
preferencesBanner: "취향에 알맞는 클라이언트의 전체적인 동작을 설정합니다."
appearanceBanner: "취향에 알맞는 클라이언트의 디스플레이, 표시 방법에 관한 설정을 합니다."
soundsBanner: "클라이언트에서 재생할 소리에 대한 설정을 합니다."
timelineAndNote: "타임라인과 노트"
makeEveryTextElementsSelectable: "모든 텍스트 요소를 선택할 수 있도록 함"
makeEveryTextElementsSelectable_description: "활성화 시, 일부 동작에서 사용자의 접근성이 나빠질 수도 있습니다."
_preferencesProfile: _preferencesProfile:
profileName: "프로필 이름" profileName: "프로필 이름"
profileNameDescription: "이 디바이스를 식별할 이름을 설정해 주세요." profileNameDescription: "이 디바이스를 식별할 이름을 설정해 주세요."
@@ -1383,7 +1363,6 @@ _accountSettings:
makeNotesHiddenBefore: "과거 노트 비공개로 전환하기" makeNotesHiddenBefore: "과거 노트 비공개로 전환하기"
makeNotesHiddenBeforeDescription: "이 기능이 활성화되어 있는 동안 설정한 날짜 및 시간보다 과거 또는 설정한 시간이 지난 노트는 본인만 볼 수 있게(비공개로 전환) 됩니다. 비활성화하면 노트의 공개 상태도 원래대로 돌아갑니다." makeNotesHiddenBeforeDescription: "이 기능이 활성화되어 있는 동안 설정한 날짜 및 시간보다 과거 또는 설정한 시간이 지난 노트는 본인만 볼 수 있게(비공개로 전환) 됩니다. 비활성화하면 노트의 공개 상태도 원래대로 돌아갑니다."
mayNotEffectForFederatedNotes: "원격 서버에 연합된 노트에는 효과가 없을 수도 있습니다." mayNotEffectForFederatedNotes: "원격 서버에 연합된 노트에는 효과가 없을 수도 있습니다."
mayNotEffectSomeSituations: "여기서 설정하는 제한은 모더레이션이나 리모트 서버에서 볼 때 등 일부 환경에서는 적용되지 않을 수도 있습니다."
notesHavePassedSpecifiedPeriod: "지정한 시간이 경과된 노트" notesHavePassedSpecifiedPeriod: "지정한 시간이 경과된 노트"
notesOlderThanSpecifiedDateAndTime: "지정된 날짜 및 시간 이전의 노트" notesOlderThanSpecifiedDateAndTime: "지정된 날짜 및 시간 이전의 노트"
_abuseUserReport: _abuseUserReport:
@@ -2524,7 +2503,6 @@ _notification:
achievementEarned: "도전 과제 획득" achievementEarned: "도전 과제 획득"
exportCompleted: "추출을 성공함" exportCompleted: "추출을 성공함"
login: "로그인" login: "로그인"
createToken: "액세스 토큰 만들기"
test: "알림 테스트" test: "알림 테스트"
app: "연동된 앱을 통한 알림" app: "연동된 앱을 통한 알림"
_actions: _actions:
@@ -2552,7 +2530,6 @@ _deck:
useSimpleUiForNonRootPages: "루트 이외의 페이지로 접속한 경우 UI 간략화하기" useSimpleUiForNonRootPages: "루트 이외의 페이지로 접속한 경우 UI 간략화하기"
usedAsMinWidthWhenFlexible: "'폭 자동 조정'이 활성화된 경우 최소 폭으로 사용됩니다" usedAsMinWidthWhenFlexible: "'폭 자동 조정'이 활성화된 경우 최소 폭으로 사용됩니다"
flexible: "폭 자동 조정" flexible: "폭 자동 조정"
enableSyncBetweenDevicesForProfiles: "프로파일 정보의 디바이스 간 동기화를 활성화"
_columns: _columns:
main: "메인" main: "메인"
widgets: "위젯" widgets: "위젯"

View File

@@ -235,6 +235,7 @@
"jest-mock": "29.7.0", "jest-mock": "29.7.0",
"nodemon": "3.1.9", "nodemon": "3.1.9",
"pid-port": "1.0.2", "pid-port": "1.0.2",
"simple-oauth2": "5.1.0" "simple-oauth2": "5.1.0",
"vite": "6.2.1"
} }
} }

View File

@@ -5,10 +5,13 @@
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url'; 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 yaml from 'js-yaml';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import locale from '../../../locales/index.js';
import type { RedisOptions } from 'ioredis'; import type { RedisOptions } from 'ioredis';
import type { Manifest, ManifestChunk } from 'vite';
import type { ILocale } from '../../../locales/index.js';
type RedisOptionsSource = Partial<RedisOptions> & { type RedisOptionsSource = Partial<RedisOptions> & {
host: string; host: string;
@@ -185,9 +188,12 @@ export type Config = {
authUrl: string; authUrl: string;
driveUrl: string; driveUrl: string;
userAgent: string; userAgent: string;
frontendEntry: string; localeEntries: Record<string, string>;
errorLocaleMessages: Record<string, ILocale>;
configEntry: ManifestChunk;
frontendEntry: ManifestChunk;
frontendManifestExists: boolean; frontendManifestExists: boolean;
frontendEmbedEntry: string; frontendEmbedEntry: ManifestChunk;
frontendEmbedManifestExists: boolean; frontendEmbedManifestExists: boolean;
mediaProxy: string; mediaProxy: string;
externalMediaProxyEnabled: boolean; externalMediaProxyEnabled: boolean;
@@ -229,12 +235,23 @@ export function loadConfig(): Config {
const frontendManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_vite_/manifest.json'); const frontendManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_vite_/manifest.json');
const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json'); const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json');
const frontendManifest = frontendManifestExists ? const frontendManifest: Manifest = frontendManifestExists
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8')) ? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8'))
: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } }; : Object.entries(locale).reduce<Record<string, ManifestChunk>>((a, [k]) => {
const frontendEmbedManifest = frontendEmbedManifestExists ? a[`locale:${k}`] = { file: `locale:${k}` };
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8')) return a;
: { 'src/boot.ts': { file: 'src/boot.ts' } }; }, {
'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; 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 config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator
: null, : null,
userAgent: `Misskey/${version} (${config.url})`, 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'], frontendEntry: frontendManifest['src/_boot_.ts'],
frontendManifestExists: frontendManifestExists, frontendManifestExists: frontendManifestExists,
frontendEmbedEntry: frontendEmbedManifest['src/boot.ts'], frontendEmbedEntry: frontendEmbedManifest['src/boot.ts'],

View File

@@ -32,56 +32,24 @@
} }
//#region Detect language & fetch translations //#region Detect language & fetch translations
if (!localStorage.hasOwnProperty('locale')) { const supportedLangs = LANGS;
const supportedLangs = LANGS; let lang = localStorage.getItem('lang');
let lang = localStorage.getItem('lang'); if (!supportedLangs.includes(lang)) {
if (lang == null || !supportedLangs.includes(lang)) { if (supportedLangs.includes(navigator.language)) {
if (supportedLangs.includes(navigator.language)) { lang = 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);
} else { } else {
renderError('LOCALE_FETCH'); lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
return;
// Fallback
if (lang == null) lang = 'en-US';
} }
} }
await import(`/vite/${LOCALES[lang]}`)
.catch(async e => {
console.error(e);
renderError('LOCALE_FETCH', e);
});
//#endregion //#endregion
//#region Script //#region Script
@@ -115,10 +83,21 @@
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve)); 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'; // Fallback
const reload = locale?.reload || 'Reload'; 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> 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> <div class="message">${title}</div>

View File

@@ -23,56 +23,24 @@
} }
//#region Detect language & fetch translations //#region Detect language & fetch translations
if (!localStorage.hasOwnProperty('locale')) { const supportedLangs = LANGS;
const supportedLangs = LANGS; let lang = localStorage.getItem('lang');
let lang = localStorage.getItem('lang'); if (!supportedLangs.includes(lang)) {
if (lang == null || !supportedLangs.includes(lang)) { if (supportedLangs.includes(navigator.language)) {
if (supportedLangs.includes(navigator.language)) { lang = 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);
} else { } else {
renderError('LOCALE_FETCH'); lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
return;
// Fallback
if (lang == null) lang = 'en-US';
} }
} }
await import(`/vite/${LOCALES[lang]}`)
.catch(async e => {
console.error(e);
renderError('LOCALE_FETCH', e);
});
//#endregion //#endregion
//#region Script //#region Script
@@ -151,21 +119,25 @@
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve)); 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({ // Fallback
title: 'Failed to initialize Misskey', if (lang == null) lang = 'en-US';
solution: 'The following actions may solve the problem.', }
solution1: 'Update your os and browser', }
solution2: 'Disable an adblocker', const { locale } = await import(`/vite/${CONFIG_ENTRY}`).catch(() => ({
solution3: 'Clear the browser cache', locale: {
solution4: '(Tor Browser) Set dom.webaudio.enabled to true', _bootErrors: {},
otherOption: 'Other options', },
otherOption1: 'Clear preferences and cache', }));
otherOption2: 'Start the simple client', const messages = locale._bootErrors;
otherOption3: 'Start the repair tool', const reload = locale.reload;
}, locale?._bootErrors || {});
const reload = locale?.reload || 'Reload';
let errorsElement = document.getElementById('errors'); let errorsElement = document.getElementById('errors');

View File

@@ -6,23 +6,22 @@
'use strict'; 'use strict';
(() => { (() => {
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', async () => {
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({ // Fallback
title: 'Failed to initialize Misskey', if (lang == null) lang = 'en-US';
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', const locale = ERROR_MESSAGES[lang];
solution2: 'Disable an adblocker', const messages = locale._bootErrors;
solution3: 'Clear the browser cache', const reload = locale.reload;
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';
const reloadEls = document.querySelectorAll('[data-i18n-reload]'); const reloadEls = document.querySelectorAll('[data-i18n-reload]');
for (const el of reloadEls) { for (const el of reloadEls) {

View File

@@ -41,6 +41,8 @@ html(class='embed')
script. script.
var VERSION = "#{version}"; var VERSION = "#{version}";
var CLIENT_ENTRY = "#{entry.file}"; 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) script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson != metaJson

View File

@@ -70,6 +70,8 @@ html
script. script.
var VERSION = "#{version}"; var VERSION = "#{version}";
var CLIENT_ENTRY = "#{entry.file}"; 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) script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson != metaJson

View File

@@ -27,6 +27,9 @@ html
style style
include ../error.css include ../error.css
script.
var ERROR_MESSAGES = JSON.parse(`!{JSON.stringify(config.errorLocaleMessages)}`);
script script
include ../error.js include ../error.js

View File

@@ -13,18 +13,17 @@ import { createApp, defineAsyncComponent } from 'vue';
import defaultLightTheme from '@@/themes/l-light.json5'; import defaultLightTheme from '@@/themes/l-light.json5';
import defaultDarkTheme from '@@/themes/d-dark.json5'; import defaultDarkTheme from '@@/themes/d-dark.json5';
import { MediaProxy } from '@@/js/media-proxy.js'; 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 { applyTheme, assertIsTheme } from '@/theme.js';
import { fetchCustomEmojis } from '@/custom-emojis.js'; import { fetchCustomEmojis } from '@/custom-emojis.js';
import { DI } from '@/di.js'; import { DI } from '@/di.js';
import { serverMetadata } from '@/server-metadata.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 { postMessageToParentWindow, setIframeId } from '@/post-message.js';
import { serverContext } from '@/server-context.js'; import { serverContext } from '@/server-context.js';
import { i18n, updateI18n } from '@/i18n.js'; import { i18n, updateI18n } from '@/i18n.js';
import type { Theme } from '@/theme.js';
console.log('Misskey Embed'); console.log('Misskey Embed');
//#region Embedパラメータの取得・パース //#region Embedパラメータの取得・パース
@@ -71,22 +70,6 @@ if (embedParams.colorMode === 'dark') {
} }
//#endregion //#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'; document.documentElement.style.maxWidth = '500px';

View File

@@ -17,8 +17,7 @@ export const apiUrl = location.origin + '/api';
export const wsOrigin = location.origin; export const wsOrigin = location.origin;
export const lang = localStorage.getItem('lang') ?? 'en-US'; export const lang = localStorage.getItem('lang') ?? 'en-US';
export const langs = _LANGS_; export const langs = _LANGS_;
const preParseLocale = localStorage.getItem('locale'); export let locale: Locale;
export let locale: Locale = preParseLocale ? JSON.parse(preParseLocale) : null;
export const version = _VERSION_; export const version = _VERSION_;
export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName; export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName;
export const ui = localStorage.getItem('ui'); export const ui = localStorage.getItem('ui');

View File

@@ -131,7 +131,7 @@ export function imageDataUrl(options?: {
alpha?: number, alpha?: number,
} }
}, seed?: string): string { }, seed?: string): string {
const canvas = document.createElement('canvas'); const canvas = window.document.createElement('canvas');
canvas.width = options?.size?.width ?? 100; canvas.width = options?.size?.width ?? 100;
canvas.height = options?.size?.height ?? 100; canvas.height = options?.size?.height ?? 100;

View File

@@ -23,9 +23,9 @@ let misskeyOS = null;
function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) { function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) {
unobserve(); unobserve();
const theme = themes[document.documentElement.dataset.misskeyTheme]; const theme = themes[window.document.documentElement.dataset.misskeyTheme];
if (theme) { if (theme) {
applyTheme(themes[document.documentElement.dataset.misskeyTheme]); applyTheme(themes[window.document.documentElement.dataset.misskeyTheme]);
} else { } else {
applyTheme(themes['l-light']); 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, attributes: true,
attributeFilter: ['data-misskey-theme'], attributeFilter: ['data-misskey-theme'],
}); });
@@ -55,7 +55,6 @@ function initLocalStorage() {
...userDetailed(), ...userDetailed(),
policies: {}, policies: {},
})); }));
localStorage.setItem('locale', JSON.stringify(locale));
} }
initialize({ initialize({
@@ -70,13 +69,17 @@ queueMicrotask(() => {
import('../src/theme.js'), import('../src/theme.js'),
import('../src/preferences.js'), import('../src/preferences.js'),
import('../src/os.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) => { setup((app) => {
moduleInitialized = true; moduleInitialized = true;
if (app[appInitialized]) { if (app[appInitialized]) {
return; return;
} }
app[appInitialized] = true; app[appInitialized] = true;
updateLocale(locale);
updateI18n(locale);
loadTheme(applyTheme); loadTheme(applyTheme);
components(app); components(app);
directives(app); directives(app);

View File

@@ -56,7 +56,9 @@ export default [
// open ... window.openと衝突 or 紛らわしい // open ... window.openと衝突 or 紛らわしい
// fetch ... window.fetchと衝突 or 紛らわしい // fetch ... window.fetchと衝突 or 紛らわしい
// location ... window.locationと衝突 or 紛らわしい // location ... window.locationと衝突 or 紛らわしい
'id-denylist': ['warn', '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': [ 'no-restricted-globals': [
'error', 'error',
{ {
@@ -75,10 +77,18 @@ export default [
'name': 'location', 'name': 'location',
'message': 'Use `window.location`.', 'message': 'Use `window.location`.',
}, },
{
'name': 'document',
'message': 'Use `window.document`.',
},
{ {
'name': 'history', 'name': 'history',
'message': 'Use `window.history`.', 'message': 'Use `window.history`.',
}, },
{
'name': 'name',
'message': 'Use `window.name`. もしくは name という変数名を定義し忘れている',
},
], ],
'no-shadow': ['warn'], 'no-shadow': ['warn'],
'vue/attributes-order': ['error', { 'vue/attributes-order': ['error', {

View File

@@ -14,7 +14,7 @@ import { subBoot } from '@/boot/sub-boot.js';
const subBootPaths = ['/share', '/auth', '/miauth', '/oauth', '/signup-complete', '/install-extensions']; 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(); subBoot();
} else { } else {
mainBoot(); mainBoot();

View File

@@ -191,7 +191,7 @@ export async function login(token: AccountWithToken['token'], redirect?: string)
// 他のタブは再読み込みするだけ // 他のタブは再読み込みするだけ
reloadChannel.postMessage(null); reloadChannel.postMessage(null);
// このページはredirectで指定された先に移動 // このページはredirectで指定された先に移動
location.href = redirect; window.location.href = redirect;
return; return;
} }

View File

@@ -78,45 +78,29 @@ export async function common(createVue: () => App<Element>) {
} }
//#endregion //#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を機能させる // タッチデバイスでCSSの:hoverを機能させる
document.addEventListener('touchend', () => {}, { passive: true }); window.document.addEventListener('touchend', () => {}, { passive: true });
// URLに#pswpを含む場合は取り除く // URLに#pswpを含む場合は取り除く
if (location.hash === '#pswp') { if (window.location.hash === '#pswp') {
history.replaceState(null, '', location.href.replace('#pswp', '')); window.history.replaceState(null, '', window.location.href.replace('#pswp', ''));
} }
// 一斉リロード // 一斉リロード
reloadChannel.addEventListener('message', path => { reloadChannel.addEventListener('message', path => {
if (path !== null) location.href = path; if (path !== null) window.location.href = path;
else location.reload(); else window.location.reload();
}); });
// If mobile, insert the viewport meta tag // If mobile, insert the viewport meta tag
if (['smartphone', 'tablet'].includes(deviceKind)) { if (['smartphone', 'tablet'].includes(deviceKind)) {
const viewport = document.getElementsByName('viewport').item(0); const viewport = window.document.getElementsByName('viewport').item(0);
viewport.setAttribute('content', viewport.setAttribute('content',
`${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`); `${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`);
} }
//#region Set lang attr //#region Set lang attr
const html = document.documentElement; const html = window.document.documentElement;
html.setAttribute('lang', lang); html.setAttribute('lang', lang);
//#endregion //#endregion
@@ -130,11 +114,11 @@ export async function common(createVue: () => App<Element>) {
}); });
//#region loginId //#region loginId
const params = new URLSearchParams(location.search); const params = new URLSearchParams(window.location.search);
const loginId = params.get('loginId'); const loginId = params.get('loginId');
if (loginId) { if (loginId) {
const target = getUrlWithoutLoginId(location.href); const target = getUrlWithoutLoginId(window.location.href);
if (!$i || $i.id !== loginId) { if (!$i || $i.id !== loginId) {
const account = await getAccountFromId(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 //#endregion
@@ -155,7 +139,7 @@ export async function common(createVue: () => App<Element>) {
); );
}, { immediate: miLocalStorage.getItem('theme') == null }); }, { 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 darkTheme = prefer.model('darkTheme');
const lightTheme = prefer.model('lightTheme'); const lightTheme = prefer.model('lightTheme');
@@ -201,20 +185,20 @@ export async function common(createVue: () => App<Element>) {
}, { immediate: true }); }, { immediate: true });
watch(prefer.r.useBlurEffectForModal, v => { 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 }); }, { immediate: true });
watch(prefer.r.useBlurEffect, v => { watch(prefer.r.useBlurEffect, v => {
if (v) { if (v) {
document.documentElement.style.removeProperty('--MI-blur'); window.document.documentElement.style.removeProperty('--MI-blur');
} else { } else {
document.documentElement.style.setProperty('--MI-blur', 'none'); window.document.documentElement.style.setProperty('--MI-blur', 'none');
} }
}, { immediate: true }); }, { immediate: true });
// Keep screen on // Keep screen on
const onVisibilityChange = () => document.addEventListener('visibilitychange', () => { const onVisibilityChange = () => window.document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') { if (window.document.visibilityState === 'visible') {
navigator.wakeLock.request('screen'); navigator.wakeLock.request('screen');
} }
}); });
@@ -224,7 +208,7 @@ export async function common(createVue: () => App<Element>) {
.catch(() => { .catch(() => {
// On WebKit-based browsers, user activation is required to send wake lock request // On WebKit-based browsers, user activation is required to send wake lock request
// https://webkit.org/blog/13862/the-user-activation-api/ // https://webkit.org/blog/13862/the-user-activation-api/
document.addEventListener( window.document.addEventListener(
'click', 'click',
() => navigator.wakeLock.request('screen').then(onVisibilityChange), () => navigator.wakeLock.request('screen').then(onVisibilityChange),
{ once: true }, { once: true },
@@ -233,7 +217,7 @@ export async function common(createVue: () => App<Element>) {
} }
if (prefer.s.makeEveryTextElementsSelectable) { if (prefer.s.makeEveryTextElementsSelectable) {
document.documentElement.classList.add('forceSelectableAll'); window.document.documentElement.classList.add('forceSelectableAll');
} }
//#region Fetch user //#region Fetch user
@@ -278,16 +262,16 @@ export async function common(createVue: () => App<Element>) {
const rootEl = ((): HTMLElement => { const rootEl = ((): HTMLElement => {
const MISSKEY_MOUNT_DIV_ID = 'misskey_app'; 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) { if (currentRoot) {
console.warn('multiple import detected'); console.warn('multiple import detected');
return currentRoot; return currentRoot;
} }
const root = document.createElement('div'); const root = window.document.createElement('div');
root.id = MISSKEY_MOUNT_DIV_ID; root.id = MISSKEY_MOUNT_DIV_ID;
document.body.appendChild(root); window.document.body.appendChild(root);
return root; return root;
})(); })();
@@ -330,7 +314,7 @@ export async function common(createVue: () => App<Element>) {
} }
function removeSplash() { function removeSplash() {
const splash = document.getElementById('splash'); const splash = window.document.getElementById('splash');
if (splash) { if (splash) {
splash.style.opacity = '0'; splash.style.opacity = '0';
splash.style.pointerEvents = 'none'; splash.style.pointerEvents = 'none';

View File

@@ -43,7 +43,7 @@ export async function mainBoot() {
if (!$i) uiStyle = 'visitor'; if (!$i) uiStyle = 'visitor';
if (searchParams.has('zen')) uiStyle = 'zen'; 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'); if (searchParams.has('ui')) uiStyle = searchParams.get('ui');
@@ -216,7 +216,7 @@ export async function mainBoot() {
let reloadDialogShowing = false; let reloadDialogShowing = false;
stream.on('_disconnected_', async () => { stream.on('_disconnected_', async () => {
if (prefer.s.serverDisconnectedBehavior === 'reload') { if (prefer.s.serverDisconnectedBehavior === 'reload') {
location.reload(); window.location.reload();
} else if (prefer.s.serverDisconnectedBehavior === 'dialog') { } else if (prefer.s.serverDisconnectedBehavior === 'dialog') {
if (reloadDialogShowing) return; if (reloadDialogShowing) return;
reloadDialogShowing = true; reloadDialogShowing = true;
@@ -227,7 +227,7 @@ export async function mainBoot() {
}); });
reloadDialogShowing = false; reloadDialogShowing = false;
if (!canceled) { if (!canceled) {
location.reload(); window.location.reload();
} }
} }
}); });
@@ -398,7 +398,7 @@ export async function mainBoot() {
let lastVisibilityChangedAt = Date.now(); let lastVisibilityChangedAt = Date.now();
function claimPlainLucky() { function claimPlainLucky() {
if (document.visibilityState !== 'visible') { if (window.document.visibilityState !== 'visible') {
if (justPlainLuckyTimer != null) window.clearTimeout(justPlainLuckyTimer); if (justPlainLuckyTimer != null) window.clearTimeout(justPlainLuckyTimer);
return; return;
} }
@@ -413,7 +413,7 @@ export async function mainBoot() {
window.addEventListener('visibilitychange', () => { window.addEventListener('visibilitychange', () => {
const now = Date.now(); const now = Date.now();
if (document.visibilityState === 'visible') { if (window.document.visibilityState === 'visible') {
// タブを高速で切り替えたら取得処理が何度も走るのを防ぐ // タブを高速で切り替えたら取得処理が何度も走るのを防ぐ
if ((now - lastVisibilityChangedAt) < 1000 * 10) { if ((now - lastVisibilityChangedAt) < 1000 * 10) {
justPlainLuckyTimer = window.setTimeout(claimPlainLucky, 1000 * 10); justPlainLuckyTimer = window.setTimeout(claimPlainLucky, 1000 * 10);
@@ -458,7 +458,7 @@ export async function mainBoot() {
const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt'); const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt');
const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo'); 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)))) { if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) {
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {
closed: () => dispose(), closed: () => dispose(),
@@ -554,7 +554,7 @@ export async function mainBoot() {
mainRouter.push('/search'); mainRouter.push('/search');
}, },
} as const satisfies Keymap; } as const satisfies Keymap;
document.addEventListener('keydown', makeHotkey(keymap), { passive: false }); window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
initializeSw(); initializeSw();
} }

View File

@@ -192,7 +192,7 @@ function tick() {
tick(); tick();
function calcColors() { function calcColors() {
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(window.document.documentElement);
const dark = tinycolor(computedStyle.getPropertyValue('--MI_THEME-bg')).isDark(); const dark = tinycolor(computedStyle.getPropertyValue('--MI_THEME-bg')).isDark();
const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString(); const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)'; majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';

View File

@@ -359,7 +359,7 @@ onMounted(() => {
props.textarea.addEventListener('keydown', onKeydown); props.textarea.addEventListener('keydown', onKeydown);
document.body.addEventListener('mousedown', onMousedown); window.document.body.addEventListener('mousedown', onMousedown);
nextTick(() => { nextTick(() => {
exec(); exec();
@@ -375,7 +375,7 @@ onMounted(() => {
onBeforeUnmount(() => { onBeforeUnmount(() => {
props.textarea.removeEventListener('keydown', onKeydown); props.textarea.removeEventListener('keydown', onKeydown);
document.body.removeEventListener('mousedown', onMousedown); window.document.body.removeEventListener('mousedown', onMousedown);
}); });
</script> </script>

View File

@@ -92,7 +92,7 @@ function onMousedown(evt: MouseEvent): void {
const target = evt.target! as HTMLElement; const target = evt.target! as HTMLElement;
const rect = target.getBoundingClientRect(); const rect = target.getBoundingClientRect();
const ripple = document.createElement('div'); const ripple = window.document.createElement('div');
ripple.classList.add(ripples.value!.dataset.childrenClass!); ripple.classList.add(ripples.value!.dataset.childrenClass!);
ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px'; ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px'; ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';

View File

@@ -112,7 +112,7 @@ watch(() => [props.instanceUrl, props.sitekey, props.secretKey], async () => {
if (loaded || props.provider === 'mcaptcha' || props.provider === 'testcaptcha') { if (loaded || props.provider === 'mcaptcha' || props.provider === 'testcaptcha') {
available.value = true; available.value = true;
} else if (src.value !== null) { } 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, async: true,
id: scriptId.value, id: scriptId.value,
src: src.value, src: src.value,
@@ -149,7 +149,7 @@ async function requestRender() {
if (captcha.value.render && captchaEl.value instanceof Element && props.sitekey) { if (captcha.value.render && captchaEl.value instanceof Element && props.sitekey) {
// reCAPTCHAのレンダリング重複判定を回避するため、captchaEl配下に仮のdivを用意する. // reCAPTCHAのレンダリング重複判定を回避するため、captchaEl配下に仮のdivを用意する.
// 同じdivに対して複数回renderを呼び出すとreCAPTCHAはエラーを返すので // 同じdivに対して複数回renderを呼び出すとreCAPTCHAはエラーを返すので
const elem = document.createElement('div'); const elem = window.document.createElement('div');
captchaEl.value.appendChild(elem); captchaEl.value.appendChild(elem);
captchaWidgetId.value = captcha.value.render(elem, { captchaWidgetId.value = captcha.value.render(elem, {
@@ -174,7 +174,7 @@ async function requestRender() {
function clearWidget() { function clearWidget() {
if (props.provider === 'mcaptcha') { if (props.provider === 'mcaptcha') {
const container = document.getElementById('mcaptcha__widget-container'); const container = window.document.getElementById('mcaptcha__widget-container');
if (container) { if (container) {
container.innerHTML = ''; container.innerHTML = '';
} }

View File

@@ -68,11 +68,11 @@ onMounted(() => {
rootEl.value.style.left = `${left}px`; rootEl.value.style.left = `${left}px`;
} }
document.body.addEventListener('mousedown', onMousedown); window.document.body.addEventListener('mousedown', onMousedown);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
document.body.removeEventListener('mousedown', onMousedown); window.document.body.removeEventListener('mousedown', onMousedown);
}); });
function onMousedown(evt: Event) { function onMousedown(evt: Event) {

View File

@@ -3,14 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only * 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 { HttpResponse, http } from 'msw';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { file } from '../../.storybook/fakes.js'; import { file } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';
import MkCropperDialog from './MkCropperDialog.vue'; import MkCropperDialog from './MkCropperDialog.vue';
import type { StoryObj } from '@storybook/vue3';
export const Default = { export const Default = {
render(args) { render(args) {
return { return {
@@ -55,7 +53,7 @@ export const Default = {
http.get('/proxy/image.webp', async ({ request }) => { http.get('/proxy/image.webp', async ({ request }) => {
const url = new URL(request.url).searchParams.get('url'); 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') { 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, { return new HttpResponse(image, {
headers: { headers: {
'Content-Type': 'image/jpeg', 'Content-Type': 'image/jpeg',

View File

@@ -122,7 +122,7 @@ onMounted(() => {
cropper = new Cropper(imgEl.value!, { cropper = new Cropper(imgEl.value!, {
}); });
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(window.document.documentElement);
const selection = cropper.getCropperSelection()!; const selection = cropper.getCropperSelection()!;
selection.themeColor = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString(); selection.themeColor = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();

View File

@@ -180,7 +180,7 @@ function applyToPreview() {
nextTick(() => { nextTick(() => {
if (currentPreviewUrl === embedPreviewUrl.value) { if (currentPreviewUrl === embedPreviewUrl.value) {
// URLが変わらなくてもリロード // URLが変わらなくてもリロード
iframeEl.value?.contentWindow?.location.reload(); iframeEl.value?.contentWindow?.window.location.reload();
} }
}); });
} }

View File

@@ -116,7 +116,7 @@ function toggle() {
} }
onMounted(() => { onMounted(() => {
const computedStyle = getComputedStyle(document.documentElement); const computedStyle = getComputedStyle(window.document.documentElement);
const parentBg = getBgColor(rootEl.value?.parentElement) ?? 'transparent'; const parentBg = getBgColor(rootEl.value?.parentElement) ?? 'transparent';
const myBg = computedStyle.getPropertyValue('--MI_THEME-panel'); const myBg = computedStyle.getPropertyValue('--MI_THEME-panel');
bgSame.value = parentBg === myBg; bgSame.value = parentBg === myBg;

View File

@@ -55,7 +55,7 @@ import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurha
const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => { const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => {
// テスト環境で Web Worker インスタンスは作成できない // テスト環境で Web Worker インスタンスは作成できない
if (import.meta.env.MODE === 'test') { if (import.meta.env.MODE === 'test') {
const canvas = document.createElement('canvas'); const canvas = window.document.createElement('canvas');
canvas.width = 64; canvas.width = 64;
canvas.height = 64; canvas.height = 64;
resolve(canvas); resolve(canvas);
@@ -70,7 +70,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
); );
resolve(workers); resolve(workers);
} else { } else {
const canvas = document.createElement('canvas'); const canvas = window.document.createElement('canvas');
canvas.width = 64; canvas.width = 64;
canvas.height = 64; canvas.height = 64;
resolve(canvas); resolve(canvas);

View File

@@ -3,13 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only * 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 { HttpResponse, http } from 'msw';
import { federationInstance } from '../../.storybook/fakes.js'; import { federationInstance } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js'; import { commonHandlers } from '../../.storybook/mocks.js';
import { getChartResolver } from '../../.storybook/charts.js'; import { getChartResolver } from '../../.storybook/charts.js';
import MkInstanceCardMini from './MkInstanceCardMini.vue'; import MkInstanceCardMini from './MkInstanceCardMini.vue';
import type { StoryObj } from '@storybook/vue3';
export const Default = { export const Default = {
render(args) { render(args) {
@@ -48,7 +47,7 @@ export const Default = {
const url = new URL(urlStr); const url = new URL(urlStr);
if (url.href.startsWith('https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/')) { 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, { return new HttpResponse(image, {
headers: { headers: {
'Content-Type': 'image/jpeg', 'Content-Type': 'image/jpeg',

View File

@@ -126,7 +126,7 @@ function createDoughnut(chartEl, tooltip, data) {
labels: data.map(x => x.name), labels: data.map(x => x.name),
datasets: [{ datasets: [{
backgroundColor: data.map(x => x.color), 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, borderWidth: 2,
hoverOffset: 0, hoverOffset: 0,
data: data.map(x => x.value), data: data.map(x => x.value),

View File

@@ -148,7 +148,7 @@ const keymap = {
// PlayerElもしくはその子要素にフォーカスがあるかどうか // PlayerElもしくはその子要素にフォーカスがあるかどうか
function hasFocus() { function hasFocus() {
if (!playerEl.value) return false; 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'); const playerEl = useTemplateRef('playerEl');

View File

@@ -48,7 +48,7 @@ const props = defineProps<{
const gallery = useTemplateRef('gallery'); const gallery = useTemplateRef('gallery');
const pswpZIndex = os.claimZIndex('middle'); 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); const count = computed(() => props.mediaList.filter(media => previewable(media)).length);
let lightbox: PhotoSwipeLightbox | null = null; let lightbox: PhotoSwipeLightbox | null = null;
@@ -166,7 +166,7 @@ onMounted(() => {
className: 'pswp__alt-text-container', className: 'pswp__alt-text-container',
appendTo: 'wrapper', appendTo: 'wrapper',
onInit: (el, pswp) => { onInit: (el, pswp) => {
const textBox = document.createElement('p'); const textBox = window.document.createElement('p');
textBox.className = 'pswp__alt-text _acrylic'; textBox.className = 'pswp__alt-text _acrylic';
el.appendChild(textBox); el.appendChild(textBox);
@@ -178,19 +178,19 @@ onMounted(() => {
}); });
lightbox.on('afterInit', () => { 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); focusParent(activeEl, true, true);
lightbox?.pswp?.element?.focus({ lightbox?.pswp?.element?.focus({
preventScroll: true, preventScroll: true,
}); });
history.pushState(null, '', '#pswp'); window.history.pushState(null, '', '#pswp');
}); });
lightbox.on('destroy', () => { lightbox.on('destroy', () => {
focusParent(activeEl, true, false); focusParent(activeEl, true, false);
activeEl = null; activeEl = null;
if (window.location.hash === '#pswp') { if (window.location.hash === '#pswp') {
history.back(); window.history.back();
} }
}); });

View File

@@ -171,7 +171,7 @@ const keymap = {
// PlayerElもしくはその子要素にフォーカスがあるかどうか // PlayerElもしくはその子要素にフォーカスがあるかどうか
function hasFocus() { function hasFocus() {
if (!playerEl.value) return false; 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 // eslint-disable-next-line vue/no-setup-props-reactivity-loss
@@ -216,7 +216,7 @@ function showMenu(ev: MouseEvent) {
'2.0x': 2, '2.0x': 2,
}, },
}, },
...(document.pictureInPictureEnabled ? [{ ...(window.document.pictureInPictureEnabled ? [{
text: i18n.ts._mediaControls.pip, text: i18n.ts._mediaControls.pip,
icon: 'ti ti-picture-in-picture', icon: 'ti ti-picture-in-picture',
action: togglePictureInPicture, action: togglePictureInPicture,
@@ -384,8 +384,8 @@ function toggleFullscreen() {
function togglePictureInPicture() { function togglePictureInPicture() {
if (videoEl.value) { if (videoEl.value) {
if (document.pictureInPictureElement) { if (window.document.pictureInPictureElement) {
document.exitPictureInPicture(); window.document.exitPictureInPicture();
} else { } else {
videoEl.value.requestPictureInPicture(); videoEl.value.requestPictureInPicture();
} }

View File

@@ -358,10 +358,10 @@ function switchItem(item: MenuSwitch & { ref: any }) {
function focusUp() { function focusUp() {
if (disposed) return; 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 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 targetIndex = (activeIndex !== -1 && activeIndex !== 0) ? (activeIndex - 1) : (focusableElements.length - 1);
const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value; const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
@@ -370,10 +370,10 @@ function focusUp() {
function focusDown() { function focusDown() {
if (disposed) return; 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 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 targetIndex = (activeIndex !== -1 && activeIndex !== (focusableElements.length - 1)) ? (activeIndex + 1) : 0;
const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value; const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
@@ -400,9 +400,9 @@ const onGlobalMousedown = (ev: MouseEvent) => {
const setupHandlers = () => { const setupHandlers = () => {
if (!isNestingMenu) { 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; let disposed = false;
@@ -410,9 +410,9 @@ let disposed = false;
const disposeHandlers = () => { const disposeHandlers = () => {
disposed = true; disposed = true;
if (!isNestingMenu) { if (!isNestingMenu) {
document.removeEventListener('focusin', onGlobalFocusin); window.document.removeEventListener('focusin', onGlobalFocusin);
} }
document.removeEventListener('mousedown', onGlobalMousedown); window.document.removeEventListener('mousedown', onGlobalMousedown);
}; };
onMounted(() => { onMounted(() => {

View File

@@ -48,7 +48,7 @@ const polygonPoints = ref('');
const headX = ref<number | null>(null); const headX = ref<number | null>(null);
const headY = ref<number | null>(null); const headY = ref<number | null>(null);
const clock = 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(); const color = accent.toRgbString();
function draw(): void { function draw(): void {

View File

@@ -59,7 +59,7 @@ const pagination = computed(() => prefer.r.useGroupedNotifications.value ? {
function onNotification(notification) { function onNotification(notification) {
const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false; const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false;
if (isMuted || document.visibilityState === 'visible') { if (isMuted || window.document.visibilityState === 'visible') {
useStream().send('readNotification'); useStream().send('readNotification');
} }

View File

@@ -142,7 +142,7 @@ const {
} = prefer.r; } = prefer.r;
const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value); 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(); const visibility = useDocumentVisibility();

View File

@@ -151,9 +151,9 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
closed: () => dispose(), closed: () => dispose(),
}); });
const style = document.createElement('style'); const style = window.document.createElement('style');
style.appendChild(document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }')); style.appendChild(window.document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }'));
document.head.appendChild(style); window.document.head.appendChild(style);
const thumbWidth = getThumbWidth(); const thumbWidth = getThumbWidth();
@@ -172,7 +172,7 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
let beforeValue = finalValue.value; let beforeValue = finalValue.value;
const onMouseup = () => { const onMouseup = () => {
document.head.removeChild(style); window.document.head.removeChild(style);
tooltipForDragShowing.value = false; tooltipForDragShowing.value = false;
window.removeEventListener('mousemove', onDrag); window.removeEventListener('mousemove', onDrag);
window.removeEventListener('touchmove', onDrag); window.removeEventListener('touchmove', onDrag);

View File

@@ -136,7 +136,7 @@ async function menu(ev) {
} }
function anime() { 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 rect = buttonEl.value.getBoundingClientRect();
const x = rect.left + 16; const x = rect.left + 16;

View File

@@ -44,7 +44,7 @@ onMounted(async () => {
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; 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(); const color = accent.toHex();
if (chartEl.value == null) return; if (chartEl.value == null) return;

View File

@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:width="400" :width="400"
:height="500" :height="500"
@close="onCloseModalWindow" @close="onCloseModalWindow"
@closed="console.log('MkRoleSelectDialog: closed') ; $emit('dispose')" @closed="emit('closed')"
> >
<template #header>{{ title }}</template> <template #header>{{ title }}</template>
<MkSpacer :marginMin="20" :marginMax="28"> <MkSpacer :marginMin="20" :marginMax="28">
@@ -58,7 +58,7 @@ import MkLoading from '@/components/global/MkLoading.vue';
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'done', value: Misskey.entities.Role[]), (ev: 'done', value: Misskey.entities.Role[]),
(ev: 'close'), (ev: 'close'),
(ev: 'dispose'), (ev: 'closed'),
}>(); }>();
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{

View File

@@ -267,7 +267,7 @@ async function onSubmit(): Promise<void> {
'testcaptcha-response': testcaptchaResponse.value, 'testcaptcha-response': testcaptchaResponse.value,
}; };
const res = await fetch(`${config.apiUrl}/signup`, { const res = await window.fetch(`${config.apiUrl}/signup`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@@ -20,7 +20,7 @@ import tinycolor from 'tinycolor2';
const loaded = !!window.TagCanvas; const loaded = !!window.TagCanvas;
const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz'; 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 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 idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
const available = ref(false); const available = ref(false);
@@ -57,7 +57,7 @@ onMounted(() => {
if (loaded) { if (loaded) {
available.value = true; available.value = true;
} else { } else {
document.head.appendChild(Object.assign(document.createElement('script'), { window.document.head.appendChild(Object.assign(window.document.createElement('script'), {
async: true, async: true,
src: '/client-assets/tagcanvas.min.js', src: '/client-assets/tagcanvas.min.js',
})).addEventListener('load', () => available.value = true); })).addEventListener('load', () => available.value = true);

View File

@@ -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 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 accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
const colorRead = accent; const colorRead = accent;

View File

@@ -240,7 +240,7 @@ function onHeaderMousedown(evt: MouseEvent | TouchEvent) {
const main = rootEl.value; const main = rootEl.value;
if (main == null) return; if (main == null) return;
if (!contains(main, document.activeElement)) main.focus(); if (!contains(main, window.document.activeElement)) main.focus();
const position = main.getBoundingClientRect(); const position = main.getBoundingClientRect();

View File

@@ -87,7 +87,7 @@ function openWindow() {
function nav(ev: MouseEvent) { function nav(ev: MouseEvent) {
if (behavior === 'browser') { if (behavior === 'browser') {
location.href = props.to; window.location.href = props.to;
return; return;
} }

View File

@@ -170,7 +170,7 @@ onMounted(() => {
if (props.rootEl) { if (props.rootEl) {
ro2 = new ResizeObserver((entries, observer) => { ro2 = new ResizeObserver((entries, observer) => {
if (document.body.contains(el.value as HTMLElement)) { if (window.document.body.contains(el.value as HTMLElement)) {
nextTick(() => renderTab()); nextTick(() => renderTab());
} }
}); });

View File

@@ -69,6 +69,8 @@ const emit = defineEmits<{
(ev: 'update:tab', key: string); (ev: 'update:tab', key: string);
}>(); }>();
const viewId = inject(DI.viewId);
const viewTransitionName = computed(() => `${viewId}---pageHeader`);
const injectedPageMetadata = inject(DI.pageMetadata); const injectedPageMetadata = inject(DI.pageMetadata);
const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value); const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value);
@@ -106,7 +108,7 @@ function onTabClick(): void {
const calcBg = () => { const calcBg = () => {
const rawBg = 'var(--MI_THEME-bg)'; 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); tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString(); bg.value = tinyBg.toRgbString();
}; };
@@ -120,7 +122,7 @@ onMounted(() => {
if (el.value && el.value.parentElement) { if (el.value && el.value.parentElement) {
narrow.value = el.value.parentElement.offsetWidth < 500; narrow.value = el.value.parentElement.offsetWidth < 500;
ro = new ResizeObserver((entries, observer) => { 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; narrow.value = el.value.parentElement.offsetWidth < 500;
} }
}); });
@@ -140,6 +142,7 @@ onUnmounted(() => {
backdrop-filter: var(--MI-blur, blur(15px)); backdrop-filter: var(--MI-blur, blur(15px));
border-bottom: solid 0.5px var(--MI_THEME-divider); border-bottom: solid 0.5px var(--MI_THEME-divider);
width: 100%; width: 100%;
view-transition-name: v-bind(viewTransitionName);
} }
.upper, .upper,

View File

@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
--> -->
<template> <template>
<div class="_pageContainer" style="height: 100%;"> <div ref="rootEl" class="_pageContainer" :class="$style.root">
<KeepAlive :max="prefer.s.numberOfPageCache"> <KeepAlive :max="prefer.s.numberOfPageCache">
<Suspense :timeout="0"> <Suspense :timeout="0">
<component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/>
@@ -18,11 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <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 type { Router } from '@/router.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import MkLoadingPage from '@/pages/_loading_.vue'; import MkLoadingPage from '@/pages/_loading_.vue';
import { DI } from '@/di.js'; import { DI } from '@/di.js';
import { randomId } from '@/utility/random-id.js';
import { deepEqual } from '@/utility/deep-equal.js';
const props = defineProps<{ const props = defineProps<{
router?: Router; router?: Router;
@@ -34,18 +36,76 @@ if (router == null) {
throw new Error('no router provided'); throw new Error('no router provided');
} }
const viewId = randomId();
provide(DI.viewId, viewId);
const currentDepth = inject(DI.routerCurrentDepth, 0); const currentDepth = inject(DI.routerCurrentDepth, 0);
provide(DI.routerCurrentDepth, currentDepth + 1); 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 current = router.current!;
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
const currentPageProps = ref(current.props); const currentPageProps = ref(current.props);
let currentRoutePath = current.route.path;
const key = ref(router.getCurrentFullPath()); const key = ref(router.getCurrentFullPath());
router.useListener('change', ({ resolved }) => { router.useListener('change', ({ resolved }) => {
if (resolved == null || 'redirect' in resolved.route) return; if (resolved == null || 'redirect' in resolved.route) return;
currentPageComponent.value = resolved.route.component; if (resolved.route.path === currentRoutePath && deepEqual(resolved.props, currentPageProps.value)) return;
currentPageProps.value = resolved.props;
key.value = router.getCurrentFullPath(); 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> </script>
<style lang="scss" module>
.root {
height: 100%;
background-color: var(--MI_THEME-bg);
}
</style>

View File

@@ -42,7 +42,7 @@ const highlighted = ref(props.markerId === searchMarkerId.value);
function checkChildren() { function checkChildren() {
if (props.children?.includes(searchMarkerId.value)) { 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; highlighted.value = el == null;
} }
} }

View File

@@ -11,4 +11,5 @@ export const DI = {
router: Symbol() as InjectionKey<Router>, router: Symbol() as InjectionKey<Router>,
mock: Symbol() as InjectionKey<boolean>, mock: Symbol() as InjectionKey<boolean>,
pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>, pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>,
viewId: Symbol() as InjectionKey<string>,
}; };

View File

@@ -13,7 +13,7 @@ export default {
el._keyHandler = makeHotkey(binding.value); el._keyHandler = makeHotkey(binding.value);
if (el._hotkey_global) { if (el._hotkey_global) {
document.addEventListener('keydown', el._keyHandler, { passive: false }); window.document.addEventListener('keydown', el._keyHandler, { passive: false });
} else { } else {
el.addEventListener('keydown', el._keyHandler, { passive: false }); el.addEventListener('keydown', el._keyHandler, { passive: false });
} }
@@ -21,7 +21,7 @@ export default {
unmounted(el) { unmounted(el) {
if (el._hotkey_global) { if (el._hotkey_global) {
document.removeEventListener('keydown', el._keyHandler); window.document.removeEventListener('keydown', el._keyHandler);
} else { } else {
el.removeEventListener('keydown', el._keyHandler); el.removeEventListener('keydown', el._keyHandler);
} }

View File

@@ -10,7 +10,7 @@ export default {
mounted(src, binding, vn) { mounted(src, binding, vn) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent'; 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) { if (parentBg === myBg) {
src.style.backgroundColor = 'var(--MI_THEME-bg)'; src.style.backgroundColor = 'var(--MI_THEME-bg)';

View File

@@ -47,7 +47,7 @@ export default {
} }
self.show = () => { self.show = () => {
if (!document.body.contains(el)) return; if (!window.document.body.contains(el)) return;
if (self._close) return; if (self._close) return;
if (self.text == null) return; if (self.text == null) return;

View File

@@ -31,7 +31,7 @@ export class UserPreview {
} }
private show() { private show() {
if (!document.body.contains(this.el)) return; if (!window.document.body.contains(this.el)) return;
if (this.promise) return; if (this.promise) return;
const showing = ref(true); const showing = ref(true);
@@ -58,7 +58,7 @@ export class UserPreview {
}; };
this.checkTimer = window.setInterval(() => { this.checkTimer = window.setInterval(() => {
if (!document.body.contains(this.el)) { if (!window.document.body.contains(this.el)) {
window.clearTimeout(this.showTimer); window.clearTimeout(this.showTimer);
window.clearTimeout(this.hideTimer); window.clearTimeout(this.hideTimer);
this.close(); this.close();

View File

@@ -12,7 +12,7 @@ import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERR
// TODO: 他のタブと永続化されたstateを同期 // TODO: 他のタブと永続化されたstateを同期
//#region loader //#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 cachedMeta = miLocalStorage.getItem('instance') ? JSON.parse(miLocalStorage.getItem('instance')!) : null;
let cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0; let cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;

View File

@@ -320,7 +320,7 @@ export class Nirax<DEF extends RouteDef[]> extends EventEmitter<RouterEvents> {
} }
const res = this.navigate(fullPath); const res = this.navigate(fullPath);
if (res.route.path === '/:(*)') { if (res.route.path === '/:(*)') {
location.href = fullPath; window.location.href = fullPath;
} else { } else {
this.emit('push', { this.emit('push', {
beforeFullPath, beforeFullPath,

View File

@@ -23,8 +23,8 @@ export type Keys = (
'fontSize' | 'fontSize' |
'ui' | 'ui' |
'ui_temp' | 'ui_temp' |
'locale' | 'locale' | // DEPRECATED
'localeVersion' | 'localeVersion' | // DEPRECATED
'theme' | 'theme' |
'themeId' | 'themeId' |
'customCss' | 'customCss' |

View File

@@ -167,7 +167,7 @@ export const navbarItemDef = reactive({
title: i18n.ts.reload, title: i18n.ts.reload,
icon: 'ti ti-refresh', icon: 'ti ti-refresh',
action: (ev) => { action: (ev) => {
location.reload(); window.location.reload();
}, },
}, },
profile: { profile: {

View File

@@ -21,10 +21,10 @@ import MkWaitingDialog from '@/components/MkWaitingDialog.vue';
import MkPageWindow from '@/components/MkPageWindow.vue'; import MkPageWindow from '@/components/MkPageWindow.vue';
import MkToast from '@/components/MkToast.vue'; import MkToast from '@/components/MkToast.vue';
import MkDialog from '@/components/MkDialog.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 MkPopupMenu from '@/components/MkPopupMenu.vue';
import MkContextMenu from '@/components/MkContextMenu.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 { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { pleaseLogin } from '@/utility/please-login.js'; import { pleaseLogin } from '@/utility/please-login.js';
import { showMovedDialog } from '@/utility/show-moved-dialog.js'; import { showMovedDialog } from '@/utility/show-moved-dialog.js';
@@ -181,7 +181,7 @@ type EmitsExtractor<T> = {
export function popup<T extends Component>( export function popup<T extends Component>(
component: T, component: T,
props: ComponentProps<T>, props: ComponentProps<T>,
events: ComponentEmit<T> = {} as ComponentEmit<T>, events: Partial<ComponentEmit<T>> = {},
): { dispose: () => void } { ): { dispose: () => void } {
markRaw(component); markRaw(component);
@@ -460,7 +460,7 @@ export function authenticateDialog(): Promise<{
canceled: false; result: { password: string; token: string | null; }; canceled: false; result: { password: string; token: string | null; };
}> { }> {
return new Promise(resolve => { return new Promise(resolve => {
const { dispose } = popup(MkPasswordDialog, {}, { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkPasswordDialog.vue')), {}, {
done: result => { done: result => {
resolve(result ? { canceled: false, result } : { canceled: true, result: undefined }); 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: { export async function selectRole(params: ComponentProps<typeof MkRoleSelectDialog_TypeReferenceOnly>): Promise<
initialRoleIds?: string[],
title?: string,
infoMessage?: string,
publicOnly?: boolean,
}): Promise<
{ canceled: true; result: undefined; } | { canceled: true; result: undefined; } |
{ canceled: false; result: Misskey.entities.Role[] } { canceled: false; result: Misskey.entities.Role[] }
> { > {
return new Promise((resolve) => { return new Promise((resolve) => {
popup(defineAsyncComponent(() => import('@/components/MkRoleSelectDialog.vue')), params, { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkRoleSelectDialog.vue')), params, {
done: roles => { done: roles => {
resolve({ canceled: false, result: roles }); resolve({ canceled: false, result: roles });
}, },
close: () => { close: () => {
resolve({ canceled: true, result: undefined }); 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 => { return new Promise(resolve => {
const { dispose } = popup(MkEmojiPickerDialog, { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), {
src, src,
...opts, ...opts,
}, { }, {
@@ -675,7 +671,11 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n
width?: number; width?: number;
onClosing?: () => void; onClosing?: () => void;
}): Promise<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(() => { return new Promise(resolve => nextTick(() => {
const { dispose } = popup(MkPopupMenu, { const { dispose } = popup(MkPopupMenu, {
items, items,
@@ -704,7 +704,7 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
return Promise.resolve(); 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(); ev.preventDefault();
return new Promise(resolve => nextTick(() => { return new Promise(resolve => nextTick(() => {
const { dispose } = popup(MkContextMenu, { const { dispose } = popup(MkContextMenu, {

View File

@@ -120,7 +120,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void {
const calcBg = () => { const calcBg = () => {
const rawBg = pageMetadata.value.bg ?? 'var(--MI_THEME-bg)'; 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); tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString(); bg.value = tinyBg.toRgbString();
}; };

View File

@@ -41,7 +41,7 @@ onMounted(() => {
labels: props.data.map(x => x.name), labels: props.data.map(x => x.name),
datasets: [{ datasets: [{
backgroundColor: props.data.map(x => x.color), 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, borderWidth: 2,
hoverOffset: 0, hoverOffset: 0,
data: props.data.map(x => x.value), data: props.data.map(x => x.value),

View File

@@ -38,7 +38,7 @@ const emit = defineEmits<{
const app = computed(() => props.session.app); const app = computed(() => props.session.app);
const name = computed(() => { const name = computed(() => {
const el = document.createElement('div'); const el = window.document.createElement('div');
el.textContent = app.value.name; el.textContent = app.value.name;
return el.innerHTML; return el.innerHTML;
}); });

View File

@@ -64,7 +64,7 @@ function accepted() {
if (session.value && session.value.app.callbackUrl) { if (session.value && session.value.app.callbackUrl) {
const url = new URL(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'); 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}`;
} }
} }

View File

@@ -632,7 +632,7 @@ function loadMonoTextures() {
src = URL.createObjectURL(monoTextures[mono.img]); src = URL.createObjectURL(monoTextures[mono.img]);
monoTextureUrls[mono.img] = src; monoTextureUrls[mono.img] = src;
} else { } else {
const res = await fetch(mono.img); const res = await window.fetch(mono.img);
const blob = await res.blob(); const blob = await res.blob();
monoTextures[mono.img] = blob; monoTextures[mono.img] = blob;
src = URL.createObjectURL(blob); src = URL.createObjectURL(blob);
@@ -875,7 +875,7 @@ function loadImage(url: string) {
function getGameImageDriveFile() { function getGameImageDriveFile() {
return new Promise<Misskey.entities.DriveFile | null>(res => { 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.width = game.GAME_WIDTH;
dcanvas.height = game.GAME_HEIGHT; dcanvas.height = game.GAME_HEIGHT;
const ctx = dcanvas.getContext('2d'); const ctx = dcanvas.getContext('2d');

View File

@@ -31,7 +31,7 @@ import MkButton from '@/components/MkButton.vue';
const state = ref<'fetching' | 'done'>('fetching'); const state = ref<'fetching' | 'done'>('fetching');
function fetch() { function fetch() {
const params = new URL(location.href).searchParams; const params = new URL(window.location.href).searchParams;
// acctのほうはdeprecated // acctのほうはdeprecated
let uri = params.get('uri') ?? params.get('acct'); let uri = params.get('uri') ?? params.get('acct');
@@ -76,12 +76,12 @@ function close(): void {
// 閉じなければ100ms後タイムラインに // 閉じなければ100ms後タイムラインに
window.setTimeout(() => { window.setTimeout(() => {
location.href = '/'; window.location.href = '/';
}, 100); }, 100);
} }
function goToMisskey(): void { function goToMisskey(): void {
location.href = '/'; window.location.href = '/';
} }
fetch(); fetch();

View File

@@ -61,7 +61,7 @@ async function onAccept(token: string) {
const cbUrl = new URL(props.callback); const cbUrl = new URL(props.callback);
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(cbUrl.protocol)) throw new Error('invalid url'); if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(cbUrl.protocol)) throw new Error('invalid url');
cbUrl.searchParams.set('session', props.session); cbUrl.searchParams.set('session', props.session);
location.href = cbUrl.toString(); window.location.href = cbUrl.toString();
} else { } else {
authRoot.value?.showUI('success'); authRoot.value?.showUI('success');
} }

View File

@@ -27,42 +27,42 @@ import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue';
import { definePage } from '@/page.js'; import { definePage } from '@/page.js';
import MkAuthConfirm from '@/components/MkAuthConfirm.vue'; 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) { if (transactionIdMeta) {
transactionIdMeta.remove(); transactionIdMeta.remove();
} }
const name = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content; const name = window.document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content;
const logo = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-logo"]')?.content; const logo = window.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 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') { function doPost(token: string, decision: 'accept' | 'deny') {
const form = document.createElement('form'); const form = window.document.createElement('form');
form.action = '/oauth/decision'; form.action = '/oauth/decision';
form.method = 'post'; form.method = 'post';
form.acceptCharset = 'utf-8'; form.acceptCharset = 'utf-8';
const loginToken = document.createElement('input'); const loginToken = window.document.createElement('input');
loginToken.type = 'hidden'; loginToken.type = 'hidden';
loginToken.name = 'login_token'; loginToken.name = 'login_token';
loginToken.value = token; loginToken.value = token;
form.appendChild(loginToken); form.appendChild(loginToken);
const transactionId = document.createElement('input'); const transactionId = window.document.createElement('input');
transactionId.type = 'hidden'; transactionId.type = 'hidden';
transactionId.name = 'transaction_id'; transactionId.name = 'transaction_id';
transactionId.value = transactionIdMeta?.content ?? ''; transactionId.value = transactionIdMeta?.content ?? '';
form.appendChild(transactionId); form.appendChild(transactionId);
if (decision === 'deny') { if (decision === 'deny') {
const cancel = document.createElement('input'); const cancel = window.document.createElement('input');
cancel.type = 'hidden'; cancel.type = 'hidden';
cancel.name = 'cancel'; cancel.name = 'cancel';
cancel.value = 'cancel'; cancel.value = 'cancel';
form.appendChild(cancel); form.appendChild(cancel);
} }
document.body.appendChild(form); window.document.body.appendChild(form);
form.submit(); form.submit();
} }

View File

@@ -160,7 +160,7 @@ async function tokenDone() {
function downloadBackupCodes() { function downloadBackupCodes() {
if (backupCodes.value !== undefined) { if (backupCodes.value !== undefined) {
const txtBlob = new Blob([backupCodes.value.join('\n')], { type: 'text/plain' }); 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.href = URL.createObjectURL(txtBlob);
dummya.download = `${$i.username}@${hostname}` + (port !== '' ? `_${port}` : '') + '-2fa-backup-codes.txt'; dummya.download = `${$i.username}@${hostname}` + (port !== '' ? `_${port}` : '') + '-2fa-backup-codes.txt';
dummya.click(); dummya.click();

View File

@@ -606,8 +606,6 @@ const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies');
watch(lang, () => { watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.setItem('lang', lang.value as string);
miLocalStorage.removeItem('locale');
miLocalStorage.removeItem('localeVersion');
}); });
watch([ watch([

View File

@@ -182,12 +182,12 @@ function close(): void {
// 閉じなければ100ms後タイムラインに // 閉じなければ100ms後タイムラインに
window.setTimeout(() => { window.setTimeout(() => {
location.href = '/'; window.location.href = '/';
}, 100); }, 100);
} }
function goToMisskey(): void { function goToMisskey(): void {
location.href = '/'; window.location.href = '/';
} }
function onPosted(): void { function onPosted(): void {

View File

@@ -125,8 +125,8 @@ function syncBetweenTabs() {
window.setInterval(syncBetweenTabs, 5000); window.setInterval(syncBetweenTabs, 5000);
document.addEventListener('visibilitychange', () => { window.document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') { if (window.document.visibilityState === 'visible') {
syncBetweenTabs(); syncBetweenTabs();
} }
}); });
@@ -136,7 +136,7 @@ let latestBackupAt = 0;
window.setInterval(() => { window.setInterval(() => {
if ($i == null) return; if ($i == null) return;
if (!store.s.enablePreferencesAutoCloudBackup) return; if (!store.s.enablePreferencesAutoCloudBackup) return;
if (document.visibilityState !== 'visible') return; // 同期されていない古い値がバックアップされるのを防ぐ if (window.document.visibilityState !== 'visible') return; // 同期されていない古い値がバックアップされるのを防ぐ
if (prefer.profile.modifiedAt <= latestBackupAt) return; if (prefer.profile.modifiedAt <= latestBackupAt) return;
cloudBackup().then(() => { cloudBackup().then(() => {

View File

@@ -106,14 +106,14 @@ async function renameProfile() {
function exportCurrentProfile() { function exportCurrentProfile() {
const p = prefer.profile; const p = prefer.profile;
const txtBlob = new Blob([JSON.stringify(p)], { type: 'text/plain' }); 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.href = URL.createObjectURL(txtBlob);
dummya.download = `${p.name || p.id}.misskeypreferences`; dummya.download = `${p.name || p.id}.misskeypreferences`;
dummya.click(); dummya.click();
} }
function importProfile() { function importProfile() {
const input = document.createElement('input'); const input = window.document.createElement('input');
input.type = 'file'; input.type = 'file';
input.accept = '.misskeypreferences'; input.accept = '.misskeypreferences';
input.onchange = async () => { input.onchange = async () => {

View File

@@ -17,10 +17,10 @@ export function createRouter(fullPath: string): Router {
return new Nirax(ROUTE_DEF, fullPath, !!$i, page(() => import('@/pages/not-found.vue'))); 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) => { 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 => { mainRouter.addListener('push', ctx => {

View File

@@ -5,7 +5,7 @@
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
const providedContextEl = document.getElementById('misskey_clientCtx'); const providedContextEl = window.document.getElementById('misskey_clientCtx');
export type ServerContext = { export type ServerContext = {
clip?: Misskey.entities.Clip; clip?: Misskey.entities.Clip;

View File

@@ -29,10 +29,10 @@ export function useStream(): Misskey.IStream {
timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL); timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL);
// send heartbeat right now when last send time is over HEART_BEAT_INTERVAL // send heartbeat right now when last send time is over HEART_BEAT_INTERVAL
document.addEventListener('visibilitychange', () => { window.document.addEventListener('visibilitychange', () => {
if ( if (
!stream !stream
|| document.visibilityState !== 'visible' || window.document.visibilityState !== 'visible'
|| Date.now() - lastHeartbeatCall < HEART_BEAT_INTERVAL || Date.now() - lastHeartbeatCall < HEART_BEAT_INTERVAL
) return; ) return;
heartbeat(); heartbeat();
@@ -42,7 +42,7 @@ export function useStream(): Misskey.IStream {
} }
function heartbeat(): void { function heartbeat(): void {
if (stream != null && document.visibilityState === 'visible') { if (stream != null && window.document.visibilityState === 'visible') {
stream.heartbeat(); stream.heartbeat();
} }
lastHeartbeatCall = Date.now(); lastHeartbeatCall = Date.now();

View File

@@ -68,10 +68,10 @@ let timeout: number | null = null;
export function applyTheme(theme: Theme, persist = true) { export function applyTheme(theme: Theme, persist = true) {
if (timeout) window.clearTimeout(timeout); if (timeout) window.clearTimeout(timeout);
document.documentElement.classList.add('_themeChanging_'); window.document.documentElement.classList.add('_themeChanging_');
timeout = window.setTimeout(() => { timeout = window.setTimeout(() => {
document.documentElement.classList.remove('_themeChanging_'); window.document.documentElement.classList.remove('_themeChanging_');
// 色計算など再度行えるようにクライアント全体に通知 // 色計算など再度行えるようにクライアント全体に通知
globalEvents.emit('themeChanged'); globalEvents.emit('themeChanged');
@@ -79,7 +79,7 @@ export function applyTheme(theme: Theme, persist = true) {
const colorScheme = theme.base === 'dark' ? 'dark' : 'light'; const colorScheme = theme.base === 'dark' ? 'dark' : 'light';
document.documentElement.dataset.colorScheme = colorScheme; window.document.documentElement.dataset.colorScheme = colorScheme;
// Deep copy // Deep copy
const _theme = deepClone(theme); const _theme = deepClone(theme);
@@ -91,7 +91,7 @@ export function applyTheme(theme: Theme, persist = true) {
const props = compile(_theme); 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') { if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
tag.setAttribute('content', props['htmlThemeColor']); tag.setAttribute('content', props['htmlThemeColor']);
break; break;
@@ -99,10 +99,10 @@ export function applyTheme(theme: Theme, persist = true) {
} }
for (const [k, v] of Object.entries(props)) { 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) { if (persist) {
miLocalStorage.setItem('theme', JSON.stringify(props)); miLocalStorage.setItem('theme', JSON.stringify(props));

View File

@@ -67,7 +67,7 @@ const dev = _DEV_;
const notifications = ref<Misskey.entities.Notification[]>([]); const notifications = ref<Misskey.entities.Notification[]>([]);
function onNotification(notification: Misskey.entities.Notification, isClient = false) { function onNotification(notification: Misskey.entities.Notification, isClient = false) {
if (document.visibilityState === 'visible') { if (window.document.visibilityState === 'visible') {
if (!isClient && notification.type !== 'test') { if (!isClient && notification.type !== 'test') {
// サーバーサイドのテスト通知の際は自動で既読をつけない(テストできないので) // サーバーサイドのテスト通知の際は自動で既読をつけない(テストできないので)
useStream().send('readNotification'); useStream().send('readNotification');

View File

@@ -129,8 +129,8 @@ watch(store.r.menuDisplay, () => {
}); });
function toggleIconOnly() { function toggleIconOnly() {
if (document.startViewTransition && prefer.s.animation) { if (window.document.startViewTransition && prefer.s.animation) {
document.startViewTransition(() => { window.document.startViewTransition(() => {
store.set('menuDisplay', iconOnly.value ? 'sideFull' : 'sideIcon'); store.set('menuDisplay', iconOnly.value ? 'sideFull' : 'sideIcon');
}); });
} else { } else {

View File

@@ -34,7 +34,7 @@ function resetDisconnected() {
} }
function reload() { function reload() {
location.reload(); window.location.reload();
} }
useStream().on('_disconnected_', onDisconnected); useStream().on('_disconnected_', onDisconnected);

View File

@@ -87,9 +87,9 @@ provideMetadataReceiver((metadataGetter) => {
pageMetadata.value = info; pageMetadata.value = info;
if (pageMetadata.value) { if (pageMetadata.value) {
if (isRoot.value && pageMetadata.value.title === instanceName) { if (isRoot.value && pageMetadata.value.title === instanceName) {
document.title = pageMetadata.value.title; window.document.title = pageMetadata.value.title;
} else { } 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'); const currentUI = miLocalStorage.getItem('ui');
miLocalStorage.setItem('ui_temp', currentUI ?? 'default'); miLocalStorage.setItem('ui_temp', currentUI ?? 'default');
miLocalStorage.setItem('ui', 'default'); miLocalStorage.setItem('ui', 'default');
location.reload(); window.location.reload();
} }
document.documentElement.style.overflowY = 'scroll'; window.document.documentElement.style.overflowY = 'scroll';
onMounted(() => { onMounted(() => {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {

View File

@@ -202,8 +202,8 @@ function onWheel(ev: WheelEvent) {
} }
} }
document.documentElement.style.overflowY = 'hidden'; window.document.documentElement.style.overflowY = 'hidden';
document.documentElement.style.scrollBehavior = 'auto'; window.document.documentElement.style.scrollBehavior = 'auto';
async function deleteProfile() { async function deleteProfile() {
if (prefer.s['deck.profile'] == null) return; if (prefer.s['deck.profile'] == null) return;

View File

@@ -30,9 +30,9 @@ provideMetadataReceiver((metadataGetter) => {
pageMetadata.value = info; pageMetadata.value = info;
if (pageMetadata.value) { if (pageMetadata.value) {
if (isRoot.value && pageMetadata.value.title === instanceName) { if (isRoot.value && pageMetadata.value.title === instanceName) {
document.title = pageMetadata.value.title; window.document.title = pageMetadata.value.title;
} else { } else {
document.title = `${pageMetadata.value.title} | ${instanceName}`; window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
} }
} }
}); });

View File

@@ -143,9 +143,9 @@ provideMetadataReceiver((metadataGetter) => {
pageMetadata.value = info; pageMetadata.value = info;
if (pageMetadata.value) { if (pageMetadata.value) {
if (isRoot.value && pageMetadata.value.title === instanceName) { if (isRoot.value && pageMetadata.value.title === instanceName) {
document.title = pageMetadata.value.title; window.document.title = pageMetadata.value.title;
} else { } else {
document.title = `${pageMetadata.value.title} | ${instanceName}`; window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
} }
} }
}); });
@@ -170,7 +170,7 @@ if (window.innerWidth > 1024) {
if (tempUI) { if (tempUI) {
miLocalStorage.setItem('ui', tempUI); miLocalStorage.setItem('ui', tempUI);
miLocalStorage.removeItem('ui_temp'); miLocalStorage.removeItem('ui_temp');
location.reload(); window.location.reload();
} }
} }
@@ -205,12 +205,12 @@ provide<Ref<number>>(CURRENT_STICKY_BOTTOM, navFooterHeight);
watch(navFooter, () => { watch(navFooter, () => {
if (navFooter.value) { if (navFooter.value) {
navFooterHeight.value = navFooter.value.offsetHeight; navFooterHeight.value = navFooter.value.offsetHeight;
document.body.style.setProperty('--MI-stickyBottom', `${navFooterHeight.value}px`); window.document.body.style.setProperty('--MI-stickyBottom', `${navFooterHeight.value}px`);
document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)'); window.document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)');
} else { } else {
navFooterHeight.value = 0; navFooterHeight.value = 0;
document.body.style.setProperty('--MI-stickyBottom', '0px'); window.document.body.style.setProperty('--MI-stickyBottom', '0px');
document.body.style.setProperty('--MI-minBottomSpacing', '0px'); window.document.body.style.setProperty('--MI-minBottomSpacing', '0px');
} }
}, { }, {
immediate: true, immediate: true,

View File

@@ -51,9 +51,9 @@ provideMetadataReceiver((metadataGetter) => {
pageMetadata.value = info; pageMetadata.value = info;
if (pageMetadata.value) { if (pageMetadata.value) {
if (isRoot.value && pageMetadata.value.title === instanceName) { if (isRoot.value && pageMetadata.value.title === instanceName) {
document.title = pageMetadata.value.title; window.document.title = pageMetadata.value.title;
} else { } else {
document.title = `${pageMetadata.value.title} | ${instanceName}`; window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
} }
} }
}); });

View File

@@ -37,7 +37,7 @@ const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
const pageMetadata = ref<null | PageMetadata>(null); 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); provide(DI.router, mainRouter);
provideMetadataReceiver((metadataGetter) => { provideMetadataReceiver((metadataGetter) => {
@@ -45,9 +45,9 @@ provideMetadataReceiver((metadataGetter) => {
pageMetadata.value = info; pageMetadata.value = info;
if (pageMetadata.value) { if (pageMetadata.value) {
if (isRoot.value && pageMetadata.value.title === instanceName) { if (isRoot.value && pageMetadata.value.title === instanceName) {
document.title = pageMetadata.value.title; window.document.title = pageMetadata.value.title;
} else { } else {
document.title = `${pageMetadata.value.title} | ${instanceName}`; window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
} }
} }
}); });

View File

@@ -86,7 +86,7 @@ export function useNoteCapture(props: {
function capture(withHandler = false): void { function capture(withHandler = false): void {
if (connection) { if (connection) {
// TODO: このノートがストリーミング経由で流れてきた場合のみ sr する // 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 (pureNote.value.id !== note.value.id) connection.send('s', { id: pureNote.value.id });
if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated); if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated);
} }

View File

@@ -29,7 +29,7 @@ export function useTooltip(
if (!isHovering) return; if (!isHovering) return;
if (elRef.value == null) return; if (elRef.value == null) return;
const el = elRef.value instanceof Element ? elRef.value : elRef.value.$el; 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); const showing = ref(true);
onShow(showing); onShow(showing);
@@ -38,7 +38,7 @@ export function useTooltip(
}; };
autoHidingTimer = window.setInterval(() => { 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; if (!isHovering) return;
isHovering = false; isHovering = false;
window.clearTimeout(timeoutId); window.clearTimeout(timeoutId);

View File

@@ -13,8 +13,10 @@ export async function clearCache() {
os.waiting(); os.waiting();
miLocalStorage.removeItem('instance'); miLocalStorage.removeItem('instance');
miLocalStorage.removeItem('instanceCachedAt'); miLocalStorage.removeItem('instanceCachedAt');
//#region deprecated
miLocalStorage.removeItem('locale'); miLocalStorage.removeItem('locale');
miLocalStorage.removeItem('localeVersion'); miLocalStorage.removeItem('localeVersion');
//#endregion
miLocalStorage.removeItem('theme'); miLocalStorage.removeItem('theme');
miLocalStorage.removeItem('emojis'); miLocalStorage.removeItem('emojis');
miLocalStorage.removeItem('lastEmojisFetchedAt'); miLocalStorage.removeItem('lastEmojisFetchedAt');

View File

@@ -50,7 +50,7 @@ function releaseFocusTrap(el: HTMLElement): void {
const highestZIndexElement = getHighestZIndexElement(); const highestZIndexElement = getHighestZIndexElement();
if (el.parentElement != null && el !== document.body) { if (el.parentElement != null && el !== window.document.body) {
el.parentElement.childNodes.forEach((siblingNode) => { el.parentElement.childNodes.forEach((siblingNode) => {
const siblingEl = getHTMLElementOrNull(siblingNode); const siblingEl = getHTMLElementOrNull(siblingNode);
if (!siblingEl) return; if (!siblingEl) return;
@@ -104,7 +104,7 @@ export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEl
el.inert = false; el.inert = false;
} }
if (el.parentElement != null && el !== document.body) { if (el.parentElement != null && el !== window.document.body) {
el.parentElement.childNodes.forEach((siblingNode) => { el.parentElement.childNodes.forEach((siblingNode) => {
const siblingEl = getHTMLElementOrNull(siblingNode); const siblingEl = getHTMLElementOrNull(siblingNode);
if (!siblingEl) return; if (!siblingEl) return;

View File

@@ -58,7 +58,7 @@ export const focusParent = (input: MaybeHTMLElement | null | undefined, self = f
const focusOrScroll = (element: HTMLElement, scroll: boolean) => { const focusOrScroll = (element: HTMLElement, scroll: boolean) => {
if (scroll) { if (scroll) {
const scrollContainer = getScrollContainer(element) ?? document.documentElement; const scrollContainer = getScrollContainer(element) ?? window.document.documentElement;
const scrollContainerTop = getScrollPosition(scrollContainer); const scrollContainerTop = getScrollPosition(scrollContainer);
const stickyTop = getStickyTop(element, scrollContainer); const stickyTop = getStickyTop(element, scrollContainer);
const stickyBottom = getStickyBottom(element, scrollContainer); const stickyBottom = getStickyBottom(element, scrollContainer);
@@ -74,7 +74,7 @@ const focusOrScroll = (element: HTMLElement, scroll: boolean) => {
scrollContainer.scrollTo({ top: scrollTo, behavior: 'instant' }); scrollContainer.scrollTo({ top: scrollTo, behavior: 'instant' });
} }
if (document.activeElement !== element) { if (window.document.activeElement !== element) {
element.focus({ preventScroll: true }); element.focus({ preventScroll: true });
} }
}; };

View File

@@ -35,8 +35,8 @@ export const requestFullscreen = ({ videoEl, playerEl, options }: RequestFullscr
export const exitFullscreen = ({ videoEl }: ExitFullscreenProps) => { export const exitFullscreen = ({ videoEl }: ExitFullscreenProps) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (document.exitFullscreen != null) { if (window.document.exitFullscreen != null) {
document.exitFullscreen(); window.document.exitFullscreen();
return; return;
} }
if (videoEl.webkitExitFullscreen != null) { if (videoEl.webkitExitFullscreen != null) {

View File

@@ -54,9 +54,9 @@ export const makeHotkey = (keymap: Keymap) => {
const actions = parseKeymap(keymap); const actions = parseKeymap(keymap);
return (ev: KeyboardEvent) => { return (ev: KeyboardEvent) => {
if ('pswp' in window && window.pswp != null) return; if ('pswp' in window && window.pswp != null) return;
if (document.activeElement != null) { if (window.document.activeElement != null) {
if (IGNORE_ELEMENTS.includes(document.activeElement.tagName.toLowerCase())) return; if (IGNORE_ELEMENTS.includes(window.document.activeElement.tagName.toLowerCase())) return;
if (getHTMLElementOrNull(document.activeElement)?.isContentEditable) return; if (getHTMLElementOrNull(window.document.activeElement)?.isContentEditable) return;
} }
for (const action of actions) { for (const action of actions) {
if (matchPatterns(ev, action)) { if (matchPatterns(ev, action)) {

View File

@@ -50,7 +50,7 @@ export function initChart() {
); );
// フォントカラー // フォントカラー
Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-fg'); Chart.defaults.color = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-fg');
Chart.defaults.borderColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; Chart.defaults.borderColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';

View File

@@ -28,7 +28,7 @@ export function physics(container: HTMLElement) {
// create renderer // create renderer
const render = Matter.Render.create({ const render = Matter.Render.create({
engine: engine, engine: engine,
//element: document.getElementById('debug'), //element: window.document.getElementById('debug'),
options: { options: {
width: containerWidth, width: containerWidth,
height: containerHeight, height: containerHeight,

View File

@@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
const CHARS = 'abcdefghijklmnopqrstuvwxyz'; // CSSの<custom-ident>などで使われることもあるのでa-z以外使うな
export function randomId(length = 32, characters = CHARS) {
let result = '';
const charactersLength = characters.length;
for ( let i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}

View File

@@ -35,6 +35,6 @@ export async function reloadAsk(opts: {
if (opts.unison) { if (opts.unison) {
unisonReload(); unisonReload();
} else { } else {
location.reload(); window.location.reload();
} }
} }

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