Compare commits

..

30 Commits

Author SHA1 Message Date
923cacb98a README.md aktualisiert 2025-03-23 18:29:44 +00:00
b1e564b562 .gitea/workflows/registry.yml hinzugefügt 2025-03-23 18:28:46 +00:00
syuilo
0471e457fe fix(frontend): fix broken styles 2025-03-23 21:23:52 +09:00
syuilo
260d35e2f0 Update CHANGELOG.md 2025-03-22 18:37:41 +09:00
syuilo
3ff9d9f4fd Update CHANGELOG.md 2025-03-22 18:34:56 +09:00
syuilo
27991a3bc8 Update CHANGELOG.md 2025-03-22 18:28:51 +09:00
syuilo
b5f86e5210 refactor(frontend): refactor page styles 2025-03-22 18:25:45 +09:00
syuilo
16cde5568d Update CHANGELOG.md 2025-03-22 15:12:24 +09:00
syuilo
bf07796b6b Update CHANGELOG.md 2025-03-22 15:08:19 +09:00
syuilo
08b131ec33 refactor(backend): better prop name 2025-03-22 08:16:15 +09:00
syuilo
1312fe34c1 Update CHANGELOG.md 2025-03-22 08:12:32 +09:00
github-actions[bot]
97563910fa Bump version to 2025.3.2-beta.9 2025-03-21 12:05:52 +00:00
syuilo
96a7c4a568 fix(frontend): チャンネルのフッターが表示されない問題を修正 2025-03-21 21:04:38 +09:00
syuilo
fee6f9fcc2 Update def.ts 2025-03-21 21:04:17 +09:00
renovate[bot]
50724b6ab8 fix(deps): update [frontend] update dependencies (#15625)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-21 19:41:26 +09:00
renovate[bot]
e61263cff0 fix(deps): update [backend] update dependencies (#15596)
* fix(deps): update [backend] update dependencies

* fix(backend/types): ensure to use nodejs-provided stream api

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-03-21 19:38:11 +09:00
github-actions[bot]
d073fe6b02 Bump version to 2025.3.2-beta.8 2025-03-20 12:18:50 +00:00
syuilo
ce858a676b refactor(frontend): use PageWithHeader instead of MkStickyContainer+MkPageHeader combination 2025-03-20 21:04:37 +09:00
syuilo
733a391d86 follow up of 1fd87bd2e4
コミットし忘れ
2025-03-20 20:29:46 +09:00
syuilo
0e25a0fb81 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-03-20 20:28:29 +09:00
syuilo
1fd87bd2e4 fix(frontend): prevent transition glitch of MkHorizontalSwipe 2025-03-20 20:28:16 +09:00
github-actions[bot]
ebc54b1f82 Bump version to 2025.3.2-beta.7 2025-03-20 10:07:37 +00: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
210 changed files with 2351 additions and 1959 deletions

View File

@@ -0,0 +1,51 @@
name: release-tag
on:
push:
branches:
- 'main'
jobs:
release-image:
runs-on: ubuntu-latest
env:
DOCKER_ORG: sendnrw
DOCKER_LATEST: latest
RUNNER_TOOL_CACHE: /toolcache
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v2
with: # replace it with your local IP
config-inline: |
[registry."git.send.nrw"]
http = true
insecure = true
- name: Login to DockerHub
uses: docker/login-action@v2
with:
registry: git.send.nrw # replace it with your local IP
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get Meta
id: meta
run: |
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
platforms: |
linux/amd64
push: true
tags: | # replace it with your local IP and tags
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}

View File

@@ -6,9 +6,24 @@
### Client
- Feat: 設定の管理が強化されました
- 自動でバックアップされるように
- 内部処理が一新され、安定性とパフォーマンスが向上しました
- 全てのクライアント設定がエクスポート(バックアップ)/インポート対象に含まれるようになりました
- プラグイン、テーマ、クライアントに追加されたすべてのアカウント情報も含まれるようになりました
- 自動で設定データをサーバーにバックアップできるように
- 設定→設定のプロファイル→自動バックアップ で有効にできます
- 新しいデバイスからログインしたり、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能)
- 任意の設定項目をデバイス間で同期できるように
- 設定項目の「...」メニュー→「デバイス間で同期」
- 同期をオンにした際にサーバーに保存された値とローカルの値が競合する場合はどちらを優先するか選択できます
- 任意の設定項目を初期値にリセットできるように
- 設定項目の「...」メニュー→「初期値にリセット」
- アカウントごとに設定値が分離される設定とそうでないクライアント設定が混在していた(かつ分離するかどうかを設定不可だった)のを、基本的に一律でクライアント全体に適用されるようにし、個別でアカウントごとに異なる設定を行えるように
- 設定項目の「...」メニュー→「アカウントで上書き」をオンにすることで、設定値をそのアカウントでだけ適用するようにできます
- ログアウトすると設定データもブラウザから消去されるようになりプライバシーが向上しました
- 再度ログインすればサーバーのバックアップから設定データを復元可能です
- エクスポートした設定データを他のサーバーでインポートして適用すること(設定の持ち運び)が可能になりました
- Feat: 画面を重ねて表示するオプションを実装(実験的)
- 設定 → その他 → 実験的機能 → Enable stacking router view
- Enhance: プラグインの管理が強化されました
- インストール/アンインストール/設定の変更時にリロード不要になりました
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように

View File

@@ -9,8 +9,6 @@ reset: "Obnovit"
notifications: "Oznámení"
username: "Uživatelské jméno"
password: "Heslo"
initialPasswordForSetup: "Počáteční heslo pro nastavení"
initialPasswordIsIncorrect: "Počáteční heslo pro nastavení je nesprávné"
forgotPassword: "Zapomenuté heslo"
fetchingAsApObject: "Načítám data z Fediversu..."
ok: "Potvrdit"
@@ -480,8 +478,6 @@ uiLanguage: "Jazyk uživatelského rozhraní"
aboutX: "O {x}"
emojiStyle: "Styl emoji"
native: "Výchozí"
style: "Vzhled"
popup: "Vyskakovací okno"
showNoteActionsOnlyHover: "Zobrazit akce poznámky jenom při naběhnutí myši"
noHistory: "Žádná historie"
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."
options: "Optionen"
specifyUser: "Spezifischer Benutzer"
lookupConfirm: "Bist du sicher, dass du das nachschlagen möchtest?"
lookupConfirm: "Zustimmen?"
openTagPageConfirm: "Hashtag Seite wirklich öffnen?"
specifyHost: "Host"
failedToPreviewUrl: "Vorschau nicht anzeigbar"

View File

@@ -1392,7 +1392,7 @@ _abuseUserReport:
resolve: "Resolve"
accept: "Accept"
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:
status: "Delivery status"
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."
_abuseReport:
_notificationRecipient:
createRecipient: "Add recipient for reports"
createRecipient: "Add a recipient for reports"
modifyRecipient: "Edit a recipient for reports"
recipientType: "Notification type"
_recipientType:
@@ -2828,7 +2828,7 @@ _customEmojisManager:
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?"
_local:
tabTitleList: "Registered emojis"
tabTitleList: "List of registered Emojis"
tabTitleRegister: "Emoji registration"
_list:
emojisNothing: "There are no registered Emojis."

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2025.3.2-beta.6",
"version": "2025.3.2-beta.9",
"codename": "nasubi",
"repository": {
"type": "git",

View File

@@ -37,17 +37,17 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.10.16",
"@swc/core-darwin-x64": "1.10.16",
"@swc/core-darwin-arm64": "1.11.11",
"@swc/core-darwin-x64": "1.11.11",
"@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.10.16",
"@swc/core-linux-arm64-gnu": "1.10.16",
"@swc/core-linux-arm64-musl": "1.10.16",
"@swc/core-linux-x64-gnu": "1.10.16",
"@swc/core-linux-x64-musl": "1.10.16",
"@swc/core-win32-arm64-msvc": "1.10.16",
"@swc/core-win32-ia32-msvc": "1.10.16",
"@swc/core-win32-x64-msvc": "1.10.16",
"@swc/core-linux-arm-gnueabihf": "1.11.11",
"@swc/core-linux-arm64-gnu": "1.11.11",
"@swc/core-linux-arm64-musl": "1.11.11",
"@swc/core-linux-x64-gnu": "1.11.11",
"@swc/core-linux-x64-musl": "1.11.11",
"@swc/core-win32-arm64-msvc": "1.11.11",
"@swc/core-win32-ia32-msvc": "1.11.11",
"@swc/core-win32-x64-msvc": "1.11.11",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9",
@@ -67,23 +67,23 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
"@aws-sdk/client-s3": "3.749.0",
"@aws-sdk/lib-storage": "3.749.0",
"@aws-sdk/client-s3": "3.772.0",
"@aws-sdk/lib-storage": "3.772.0",
"@discordapp/twemoji": "15.1.0",
"@fastify/accepts": "5.0.2",
"@fastify/cookie": "11.0.2",
"@fastify/cors": "10.0.2",
"@fastify/cors": "10.1.0",
"@fastify/express": "4.0.2",
"@fastify/http-proxy": "10.0.2",
"@fastify/multipart": "9.0.3",
"@fastify/static": "8.1.0",
"@fastify/static": "8.1.1",
"@fastify/view": "10.0.2",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.0",
"@napi-rs/canvas": "0.1.67",
"@nestjs/common": "11.0.9",
"@nestjs/core": "11.0.9",
"@nestjs/testing": "11.0.9",
"@napi-rs/canvas": "0.1.68",
"@nestjs/common": "11.0.12",
"@nestjs/core": "11.0.12",
"@nestjs/testing": "11.0.12",
"@peertube/http-signature": "1.7.0",
"@sentry/node": "8.55.0",
"@sentry/profiling-node": "8.55.0",
@@ -91,7 +91,7 @@
"@sinonjs/fake-timers": "11.3.1",
"@smithy/node-http-handler": "2.5.0",
"@swc/cli": "0.6.0",
"@swc/core": "1.10.16",
"@swc/core": "1.11.11",
"@twemoji/parser": "15.1.1",
"accepts": "1.3.8",
"ajv": "8.17.1",
@@ -100,7 +100,7 @@
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "1.20.3",
"bullmq": "5.41.1",
"bullmq": "5.44.1",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.2",
"chalk": "5.4.1",
@@ -122,7 +122,7 @@
"hpagent": "1.2.0",
"htmlescape": "1.1.1",
"http-link-header": "1.1.3",
"ioredis": "5.5.0",
"ioredis": "5.6.0",
"ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0",
"is-svg": "5.1.0",
@@ -131,26 +131,26 @@
"json5": "2.2.3",
"jsonld": "8.3.3",
"jsrsasign": "11.1.0",
"juice": "11.0.0",
"meilisearch": "0.48.2",
"juice": "11.0.1",
"meilisearch": "0.49.0",
"mfm-js": "0.24.0",
"microformats-parser": "2.0.2",
"mime-types": "2.1.35",
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.1",
"nanoid": "5.1.0",
"nanoid": "5.1.5",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"nodemailer": "6.10.0",
"nsfwjs": "4.2.0",
"oauth": "0.10.0",
"oauth": "0.10.2",
"oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
"otpauth": "9.3.6",
"parse5": "7.2.1",
"pg": "8.13.3",
"pg": "8.14.1",
"pkce-challenge": "4.1.0",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
@@ -163,8 +163,8 @@
"reflect-metadata": "0.2.2",
"rename": "1.0.4",
"rss-parser": "3.13.0",
"rxjs": "7.8.1",
"sanitize-html": "2.14.0",
"rxjs": "7.8.2",
"sanitize-html": "2.15.0",
"secure-json-parse": "3.0.2",
"sharp": "0.33.5",
"slacc": "0.0.10",
@@ -173,14 +173,14 @@
"systeminformation": "5.25.11",
"tinycolor2": "1.6.0",
"tmp": "0.2.3",
"tsc-alias": "1.8.10",
"tsc-alias": "1.8.11",
"tsconfig-paths": "4.2.0",
"typeorm": "0.3.20",
"typescript": "5.7.3",
"ulid": "2.3.0",
"typeorm": "0.3.21",
"typescript": "5.8.2",
"ulid": "2.4.0",
"vary": "1.1.2",
"web-push": "3.6.7",
"ws": "8.18.0",
"ws": "8.18.1",
"xev": "3.0.2"
},
"devDependencies": {
@@ -204,7 +204,7 @@
"@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4",
"@types/ms": "0.7.34",
"@types/node": "22.13.4",
"@types/node": "22.13.10",
"@types/nodemailer": "6.4.17",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5",
@@ -223,9 +223,9 @@
"@types/tmp": "0.2.6",
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.5.14",
"@typescript-eslint/eslint-plugin": "8.24.0",
"@typescript-eslint/parser": "8.24.0",
"@types/ws": "8.18.0",
"@typescript-eslint/eslint-plugin": "8.27.0",
"@typescript-eslint/parser": "8.27.0",
"aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3",
"eslint-plugin-import": "2.31.0",

View File

@@ -4,6 +4,7 @@
*/
import * as fs from 'node:fs/promises';
import { WritableStream } from 'node:stream/web';
import type { PathLike } from 'node:fs';
/**

View File

@@ -391,10 +391,10 @@ export class ApiCallService implements OnApplicationShutdown {
}
}
if (ep.meta.requireRolePolicy != null && (this.meta.rootUserId !== user!.id)) {
if (ep.meta.requiredRolePolicy != null && (this.meta.rootUserId !== user!.id)) {
const myRoles = await this.roleService.getUserRoles(user!.id);
const policies = await this.roleService.getUserPolicies(user!.id);
if (!policies[ep.meta.requireRolePolicy] && !myRoles.some(r => r.isAdministrator)) {
if (!policies[ep.meta.requiredRolePolicy] && !myRoles.some(r => r.isAdministrator)) {
throw new ApiError({
message: 'You are not assigned to a required role.',
code: 'ROLE_PERMISSION_DENIED',

View File

@@ -39,7 +39,7 @@ interface IEndpointMetaBase {
*/
readonly requireAdmin?: boolean;
readonly requireRolePolicy?: KeyOf<'RolePolicies'>;
readonly requiredRolePolicy?: KeyOf<'RolePolicies'>;
/**
* 引っ越し済みのユーザーによるリクエストを禁止するか

View File

@@ -12,7 +12,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageAvatarDecorations',
requiredRolePolicy: 'canManageAvatarDecorations',
kind: 'write:admin:avatar-decorations',
res: {

View File

@@ -13,7 +13,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageAvatarDecorations',
requiredRolePolicy: 'canManageAvatarDecorations',
kind: 'write:admin:avatar-decorations',
errors: {
},

View File

@@ -13,7 +13,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageAvatarDecorations',
requiredRolePolicy: 'canManageAvatarDecorations',
kind: 'read:admin:avatar-decorations',
res: {

View File

@@ -13,7 +13,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageAvatarDecorations',
requiredRolePolicy: 'canManageAvatarDecorations',
kind: 'write:admin:avatar-decorations',
errors: {

View File

@@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
} as const;

View File

@@ -16,7 +16,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
errors: {

View File

@@ -17,7 +17,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
errors: {

View File

@@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
} as const;

View File

@@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
errors: {

View File

@@ -10,7 +10,7 @@ import { QueueService } from '@/core/QueueService.js';
export const meta = {
secure: true,
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
} as const;
export const paramDef = {

View File

@@ -16,7 +16,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'read:admin:emoji',
res: {

View File

@@ -16,7 +16,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'read:admin:emoji',
res: {

View File

@@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
} as const;

View File

@@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
} as const;

View File

@@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
} as const;

View File

@@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
} as const;

View File

@@ -14,7 +14,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'write:admin:emoji',
errors: {

View File

@@ -16,7 +16,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
requireRolePolicy: 'canImportAntennas',
requiredRolePolicy: 'canImportAntennas',
prohibitMoved: true,
limit: {

View File

@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
requireRolePolicy: 'canImportBlocking',
requiredRolePolicy: 'canImportBlocking',
prohibitMoved: true,
limit: {

View File

@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
requireRolePolicy: 'canImportFollowing',
requiredRolePolicy: 'canImportFollowing',
prohibitMoved: true,
limit: {
duration: ms('1hour'),

View File

@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
requireRolePolicy: 'canImportMuting',
requiredRolePolicy: 'canImportMuting',
prohibitMoved: true,
limit: {

View File

@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
export const meta = {
secure: true,
requireCredential: true,
requireRolePolicy: 'canImportUserLists',
requiredRolePolicy: 'canImportUserLists',
prohibitMoved: true,
limit: {
duration: ms('1hour'),

View File

@@ -18,7 +18,7 @@ export const meta = {
tags: ['meta'],
requireCredential: true,
requireRolePolicy: 'canInvite',
requiredRolePolicy: 'canInvite',
kind: 'write:invite-codes',
errors: {

View File

@@ -14,7 +14,7 @@ export const meta = {
tags: ['meta'],
requireCredential: true,
requireRolePolicy: 'canInvite',
requiredRolePolicy: 'canInvite',
kind: 'write:invite-codes',
errors: {

View File

@@ -15,7 +15,7 @@ export const meta = {
tags: ['meta'],
requireCredential: true,
requireRolePolicy: 'canInvite',
requiredRolePolicy: 'canInvite',
kind: 'read:invite-codes',
res: {

View File

@@ -14,7 +14,7 @@ export const meta = {
tags: ['meta'],
requireCredential: true,
requireRolePolicy: 'canInvite',
requiredRolePolicy: 'canInvite',
kind: 'read:invite-codes',
res: {

View File

@@ -12,7 +12,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireRolePolicy: 'canManageCustomEmojis',
requiredRolePolicy: 'canManageCustomEmojis',
kind: 'read:admin:emoji',
res: {

View File

@@ -16,7 +16,7 @@
"@rollup/pluginutils": "5.1.4",
"@tabler/icons-webfont": "3.31.0",
"@twemoji/parser": "15.1.1",
"@vitejs/plugin-vue": "5.2.1",
"@vitejs/plugin-vue": "5.2.3",
"@vue/compiler-sfc": "3.5.13",
"astring": "1.9.0",
"buraha": "0.0.1",
@@ -25,16 +25,16 @@
"misskey-js": "workspace:*",
"frontend-shared": "workspace:*",
"punycode.js": "2.3.1",
"rollup": "4.34.9",
"sass": "1.85.1",
"shiki": "3.1.0",
"rollup": "4.36.0",
"sass": "1.86.0",
"shiki": "3.2.1",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.11",
"tsconfig-paths": "4.2.0",
"typescript": "5.8.2",
"uuid": "11.1.0",
"json5": "2.2.3",
"vite": "6.2.1",
"vite": "6.2.2",
"vue": "3.5.13"
},
"devDependencies": {
@@ -42,26 +42,26 @@
"@testing-library/vue": "8.1.0",
"@types/estree": "1.0.6",
"@types/micromatch": "4.0.9",
"@types/node": "22.13.9",
"@types/node": "22.13.11",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.0",
"@typescript-eslint/eslint-plugin": "8.26.0",
"@typescript-eslint/parser": "8.26.0",
"@vitest/coverage-v8": "3.0.8",
"@typescript-eslint/eslint-plugin": "8.27.0",
"@typescript-eslint/parser": "8.27.0",
"@vitest/coverage-v8": "3.0.9",
"@vue/runtime-core": "3.5.13",
"acorn": "8.14.1",
"cross-env": "7.0.3",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-vue": "10.0.0",
"fast-glob": "3.3.3",
"happy-dom": "17.3.0",
"happy-dom": "17.4.4",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"msw": "2.7.3",
"nodemon": "3.1.9",
"prettier": "3.5.3",
"start-server-and-test": "2.0.10",
"start-server-and-test": "2.0.11",
"vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "2.2.8",
"vue-eslint-parser": "10.1.1",

View File

@@ -109,12 +109,6 @@ export const ROLE_POLICIES = [
'canImportUserLists',
] as const;
// なんか動かない
//export const CURRENT_STICKY_TOP = Symbol('CURRENT_STICKY_TOP');
//export const CURRENT_STICKY_BOTTOM = Symbol('CURRENT_STICKY_BOTTOM');
export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP';
export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM';
export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg';
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg';
export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg';

View File

@@ -21,10 +21,10 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/node": "22.13.9",
"@typescript-eslint/eslint-plugin": "8.26.0",
"@typescript-eslint/parser": "8.26.0",
"esbuild": "0.25.0",
"@types/node": "22.13.11",
"@typescript-eslint/eslint-plugin": "8.27.0",
"@typescript-eslint/parser": "8.27.0",
"esbuild": "0.25.1",
"eslint-plugin-vue": "10.0.0",
"nodemon": "3.1.9",
"typescript": "5.8.2",

View File

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

View File

@@ -23,9 +23,9 @@ let misskeyOS = null;
function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) {
unobserve();
const theme = themes[document.documentElement.dataset.misskeyTheme];
const theme = themes[window.document.documentElement.dataset.misskeyTheme];
if (theme) {
applyTheme(themes[document.documentElement.dataset.misskeyTheme]);
applyTheme(themes[window.document.documentElement.dataset.misskeyTheme]);
} else {
applyTheme(themes['l-light']);
}
@@ -42,7 +42,7 @@ function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) {
}
}
});
observer.observe(document.documentElement, {
observer.observe(window.document.documentElement, {
attributes: true,
attributeFilter: ['data-misskey-theme'],
});

View File

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

View File

@@ -28,7 +28,7 @@
"@syuilo/aiscript": "0.19.0",
"@tabler/icons-webfont": "3.31.0",
"@twemoji/parser": "15.1.1",
"@vitejs/plugin-vue": "5.2.1",
"@vitejs/plugin-vue": "5.2.3",
"@vue/compiler-sfc": "3.5.13",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
"analytics": "0.8.16",
@@ -38,7 +38,7 @@
"canvas-confetti": "1.9.3",
"chart.js": "4.4.8",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "2.0.1",
"chartjs-chart-matrix": "2.1.1",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.2.0",
"chromatic": "11.27.0",
@@ -60,10 +60,10 @@
"misskey-reversi": "workspace:*",
"photoswipe": "5.4.4",
"punycode.js": "2.3.1",
"rollup": "4.34.9",
"sanitize-html": "2.14.0",
"sass": "1.85.1",
"shiki": "3.1.0",
"rollup": "4.36.0",
"sanitize-html": "2.15.0",
"sass": "1.86.0",
"shiki": "3.2.1",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
"three": "0.174.0",
@@ -74,54 +74,54 @@
"typescript": "5.8.2",
"uuid": "11.1.0",
"v-code-diff": "1.13.1",
"vite": "6.2.1",
"vite": "6.2.2",
"vue": "3.5.13",
"vuedraggable": "next",
"wanakana": "5.3.1"
},
"devDependencies": {
"@misskey-dev/summaly": "5.2.0",
"@storybook/addon-actions": "8.6.4",
"@storybook/addon-essentials": "8.6.4",
"@storybook/addon-interactions": "8.6.4",
"@storybook/addon-links": "8.6.4",
"@storybook/addon-mdx-gfm": "8.6.4",
"@storybook/addon-storysource": "8.6.4",
"@storybook/blocks": "8.6.4",
"@storybook/components": "8.6.4",
"@storybook/core-events": "8.6.4",
"@storybook/manager-api": "8.6.4",
"@storybook/preview-api": "8.6.4",
"@storybook/react": "8.6.4",
"@storybook/react-vite": "8.6.4",
"@storybook/test": "8.6.4",
"@storybook/theming": "8.6.4",
"@storybook/types": "8.6.4",
"@storybook/vue3": "8.6.4",
"@storybook/vue3-vite": "8.6.4",
"@storybook/addon-actions": "8.6.7",
"@storybook/addon-essentials": "8.6.7",
"@storybook/addon-interactions": "8.6.7",
"@storybook/addon-links": "8.6.7",
"@storybook/addon-mdx-gfm": "8.6.7",
"@storybook/addon-storysource": "8.6.7",
"@storybook/blocks": "8.6.7",
"@storybook/components": "8.6.7",
"@storybook/core-events": "8.6.7",
"@storybook/manager-api": "8.6.7",
"@storybook/preview-api": "8.6.7",
"@storybook/react": "8.6.7",
"@storybook/react-vite": "8.6.7",
"@storybook/test": "8.6.7",
"@storybook/theming": "8.6.7",
"@storybook/types": "8.6.7",
"@storybook/vue3": "8.6.7",
"@storybook/vue3-vite": "8.6.7",
"@testing-library/vue": "8.1.0",
"@types/canvas-confetti": "1.9.0",
"@types/estree": "1.0.6",
"@types/matter-js": "0.19.8",
"@types/micromatch": "4.0.9",
"@types/node": "22.13.9",
"@types/node": "22.13.11",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.13.0",
"@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.0",
"@typescript-eslint/eslint-plugin": "8.26.0",
"@typescript-eslint/parser": "8.26.0",
"@vitest/coverage-v8": "3.0.8",
"@typescript-eslint/eslint-plugin": "8.27.0",
"@typescript-eslint/parser": "8.27.0",
"@vitest/coverage-v8": "3.0.9",
"@vue/runtime-core": "3.5.13",
"acorn": "8.14.1",
"cross-env": "7.0.3",
"cypress": "14.1.0",
"cypress": "14.2.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-vue": "10.0.0",
"fast-glob": "3.3.3",
"happy-dom": "17.3.0",
"happy-dom": "17.4.4",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"msw": "2.7.3",
@@ -131,12 +131,12 @@
"react": "19.0.0",
"react-dom": "19.0.0",
"seedrandom": "3.0.5",
"start-server-and-test": "2.0.10",
"storybook": "8.6.4",
"start-server-and-test": "2.0.11",
"storybook": "8.6.7",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"vite-node": "3.0.8",
"vite-node": "3.0.9",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "3.0.8",
"vitest": "3.0.9",
"vitest-fetch-mock": "0.4.5",
"vue-component-type-helpers": "2.2.8",
"vue-eslint-parser": "10.1.1",

View File

@@ -14,7 +14,7 @@ import { subBoot } from '@/boot/sub-boot.js';
const subBootPaths = ['/share', '/auth', '/miauth', '/oauth', '/signup-complete', '/install-extensions'];
if (subBootPaths.some(i => location.pathname === i || location.pathname.startsWith(i + '/'))) {
if (subBootPaths.some(i => window.location.pathname === i || window.location.pathname.startsWith(i + '/'))) {
subBoot();
} else {
mainBoot();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,14 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { action } from '@storybook/addon-actions';
import { file } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkCropperDialog from './MkCropperDialog.vue';
import type { StoryObj } from '@storybook/vue3';
export const Default = {
render(args) {
return {
@@ -55,7 +53,7 @@ export const Default = {
http.get('/proxy/image.webp', async ({ request }) => {
const url = new URL(request.url).searchParams.get('url');
if (url === 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true') {
const image = await (await fetch('client-assets/fedi.jpg')).blob();
const image = await (await window.fetch('client-assets/fedi.jpg')).blob();
return new HttpResponse(image, {
headers: {
'Content-Type': 'image/jpeg',

View File

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

View File

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

View File

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

View File

@@ -19,9 +19,9 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveToClass="transitionName === 'swipeAnimationLeft' ? $style.swipeAnimationLeft_leaveTo : $style.swipeAnimationRight_leaveTo"
:style="`--swipe: ${pullDistance}px;`"
>
<!-- 注意slot内の最上位要素に動的にkeyを設定すること -->
<!-- 各最上位要素にユニークなkeyの指定がないとTransitionがうまく動きません -->
<div :key="tabModel">
<slot></slot>
</div>
</Transition>
</div>
</template>

View File

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

View File

@@ -3,13 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { federationInstance } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import { getChartResolver } from '../../.storybook/charts.js';
import MkInstanceCardMini from './MkInstanceCardMini.vue';
import type { StoryObj } from '@storybook/vue3';
export const Default = {
render(args) {
@@ -48,7 +47,7 @@ export const Default = {
const url = new URL(urlStr);
if (url.href.startsWith('https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/')) {
const image = await (await fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob();
const image = await (await window.fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob();
return new HttpResponse(image, {
headers: {
'Content-Type': 'image/jpeg',

View File

@@ -126,7 +126,7 @@ function createDoughnut(chartEl, tooltip, data) {
labels: data.map(x => x.name),
datasets: [{
backgroundColor: data.map(x => x.color),
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'),
borderColor: getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'),
borderWidth: 2,
hoverOffset: 0,
data: data.map(x => x.value),

View File

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

View File

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

View File

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

View File

@@ -358,10 +358,10 @@ function switchItem(item: MenuSwitch & { ref: any }) {
function focusUp() {
if (disposed) return;
if (!itemsEl.value?.contains(document.activeElement)) return;
if (!itemsEl.value?.contains(window.document.activeElement)) return;
const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable);
const activeIndex = focusableElements.findIndex(el => el === document.activeElement);
const activeIndex = focusableElements.findIndex(el => el === window.document.activeElement);
const targetIndex = (activeIndex !== -1 && activeIndex !== 0) ? (activeIndex - 1) : (focusableElements.length - 1);
const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
@@ -370,10 +370,10 @@ function focusUp() {
function focusDown() {
if (disposed) return;
if (!itemsEl.value?.contains(document.activeElement)) return;
if (!itemsEl.value?.contains(window.document.activeElement)) return;
const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable);
const activeIndex = focusableElements.findIndex(el => el === document.activeElement);
const activeIndex = focusableElements.findIndex(el => el === window.document.activeElement);
const targetIndex = (activeIndex !== -1 && activeIndex !== (focusableElements.length - 1)) ? (activeIndex + 1) : 0;
const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
@@ -400,9 +400,9 @@ const onGlobalMousedown = (ev: MouseEvent) => {
const setupHandlers = () => {
if (!isNestingMenu) {
document.addEventListener('focusin', onGlobalFocusin, { passive: true });
window.document.addEventListener('focusin', onGlobalFocusin, { passive: true });
}
document.addEventListener('mousedown', onGlobalMousedown, { passive: true });
window.document.addEventListener('mousedown', onGlobalMousedown, { passive: true });
};
let disposed = false;
@@ -410,9 +410,9 @@ let disposed = false;
const disposeHandlers = () => {
disposed = true;
if (!isNestingMenu) {
document.removeEventListener('focusin', onGlobalFocusin);
window.document.removeEventListener('focusin', onGlobalFocusin);
}
document.removeEventListener('mousedown', onGlobalMousedown);
window.document.removeEventListener('mousedown', onGlobalMousedown);
};
onMounted(() => {

View File

@@ -48,7 +48,7 @@ const polygonPoints = ref('');
const headX = ref<number | null>(null);
const headY = ref<number | null>(null);
const clock = ref<number | null>(null);
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-accent'));
const accent = tinycolor(getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-accent'));
const color = accent.toRgbString();
function draw(): void {

View File

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

View File

@@ -142,7 +142,7 @@ const {
} = prefer.r;
const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value);
const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : document.body);
const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : window.document.body);
const visibility = useDocumentVisibility();

View File

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

View File

@@ -136,7 +136,7 @@ async function menu(ev) {
}
function anime() {
if (document.hidden || !prefer.s.animation || buttonEl.value == null) return;
if (window.document.hidden || !prefer.s.animation || buttonEl.value == null) return;
const rect = buttonEl.value.getBoundingClientRect();
const x = rect.left + 16;

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 accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-accent'));
const accent = tinycolor(getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-accent'));
const color = accent.toHex();
if (chartEl.value == null) return;

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,8 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, onUnmounted, provide, inject, ref, watch, useTemplateRef } from 'vue';
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@@/js/const.js';
import type { Ref } from 'vue';
import { DI } from '@/di.js';
const rootEl = useTemplateRef('rootEl');
const headerEl = useTemplateRef('headerEl');
@@ -32,13 +31,13 @@ const footerEl = useTemplateRef('footerEl');
const headerHeight = ref<string | undefined>();
const childStickyTop = ref(0);
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
provide(CURRENT_STICKY_TOP, childStickyTop);
const parentStickyTop = inject(DI.currentStickyTop, ref(0));
provide(DI.currentStickyTop, childStickyTop);
const footerHeight = ref<string | undefined>();
const childStickyBottom = ref(0);
const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0));
provide(CURRENT_STICKY_BOTTOM, childStickyBottom);
const parentStickyBottom = inject(DI.currentStickyBottom, ref(0));
provide(DI.currentStickyBottom, childStickyBottom);
const calc = () => {
// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる

View File

@@ -0,0 +1,44 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="[$style.root, reversed ? '_pageScrollableReversed' : '_pageScrollable']">
<MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="actions" :tabs="tabs"/></template>
<div :class="$style.body">
<slot></slot>
</div>
<template #footer><slot name="footer"></slot></template>
</MkStickyContainer>
</div>
</template>
<script lang="ts" setup>
import type { PageHeaderItem } from '@/types/page-header.js';
import type { Tab } from './MkPageHeader.tabs.vue';
const props = withDefaults(defineProps<{
tabs?: Tab[];
actions?: PageHeaderItem[] | null;
thin?: boolean;
hideTitle?: boolean;
displayMyAvatar?: boolean;
reversed?: boolean;
}>(), {
tabs: () => ([] as Tab[]),
});
const tab = defineModel<string>('tab');
</script>
<style lang="scss" module>
.root {
}
.body {
min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
}
</style>

View File

@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="_pageContainer" style="height: 100%;">
<div ref="rootEl" class="_pageContainer" :class="$style.root">
<KeepAlive :max="prefer.s.numberOfPageCache">
<Suspense :timeout="0">
<component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/>
@@ -18,11 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { inject, provide, ref, shallowRef } from 'vue';
import { inject, nextTick, onMounted, provide, ref, shallowRef, useTemplateRef } from 'vue';
import type { Router } from '@/router.js';
import { prefer } from '@/preferences.js';
import MkLoadingPage from '@/pages/_loading_.vue';
import { DI } from '@/di.js';
import { randomId } from '@/utility/random-id.js';
import { deepEqual } from '@/utility/deep-equal.js';
const props = defineProps<{
router?: Router;
@@ -34,18 +36,76 @@ if (router == null) {
throw new Error('no router provided');
}
const viewId = randomId();
provide(DI.viewId, viewId);
const currentDepth = inject(DI.routerCurrentDepth, 0);
provide(DI.routerCurrentDepth, currentDepth + 1);
const rootEl = useTemplateRef('rootEl');
onMounted(() => {
rootEl.value.style.viewTransitionName = viewId; // view-transition-nameにcss varが使えないっぽいため直接代入
});
// view-transition-newなどの<pt-name-selector>にはcss varが使えず、v-bindできないため直接スタイルを生成
const viewTransitionStylesTag = window.document.createElement('style');
viewTransitionStylesTag.textContent = `
@keyframes ${viewId}-old {
to { transform: scale(0.95); opacity: 0; }
}
@keyframes ${viewId}-new {
from { transform: scale(0.95); opacity: 0; }
}
::view-transition-old(${viewId}) {
animation-duration: 0.2s;
animation-name: ${viewId}-old;
}
::view-transition-new(${viewId}) {
animation-duration: 0.2s;
animation-name: ${viewId}-new;
}
`;
window.document.head.appendChild(viewTransitionStylesTag);
const current = router.current!;
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
const currentPageProps = ref(current.props);
let currentRoutePath = current.route.path;
const key = ref(router.getCurrentFullPath());
router.useListener('change', ({ resolved }) => {
if (resolved == null || 'redirect' in resolved.route) return;
if (resolved.route.path === currentRoutePath && deepEqual(resolved.props, currentPageProps.value)) return;
function _() {
currentPageComponent.value = resolved.route.component;
currentPageProps.value = resolved.props;
key.value = router.getCurrentFullPath();
currentRoutePath = resolved.route.path;
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (prefer.s.animation && window.document.startViewTransition) {
window.document.startViewTransition(() => new Promise((res) => {
_();
nextTick(() => {
res();
//setTimeout(res, 100);
});
}));
} else {
_();
}
});
</script>
<style lang="scss" module>
.root {
height: 100%;
background-color: var(--MI_THEME-bg);
}
</style>

View File

@@ -42,7 +42,7 @@ const highlighted = ref(props.markerId === searchMarkerId.value);
function checkChildren() {
if (props.children?.includes(searchMarkerId.value)) {
const el = document.querySelector(`[data-in-app-search-marker-id="${searchMarkerId.value}"]`);
const el = window.document.querySelector(`[data-in-app-search-marker-id="${searchMarkerId.value}"]`);
highlighted.value = el == null;
}
}

View File

@@ -25,6 +25,8 @@ import MkPageHeader from './global/MkPageHeader.vue';
import MkSpacer from './global/MkSpacer.vue';
import MkStickyContainer from './global/MkStickyContainer.vue';
import MkLazy from './global/MkLazy.vue';
import PageWithHeader from './global/PageWithHeader.vue';
import PageWithAnimBg from './global/PageWithAnimBg.vue';
import SearchMarker from './global/SearchMarker.vue';
import SearchLabel from './global/SearchLabel.vue';
import SearchKeyword from './global/SearchKeyword.vue';
@@ -60,6 +62,8 @@ export const components = {
MkSpacer: MkSpacer,
MkStickyContainer: MkStickyContainer,
MkLazy: MkLazy,
PageWithHeader: PageWithHeader,
PageWithAnimBg: PageWithAnimBg,
SearchMarker: SearchMarker,
SearchLabel: SearchLabel,
SearchKeyword: SearchKeyword,
@@ -89,6 +93,8 @@ declare module '@vue/runtime-core' {
MkSpacer: typeof MkSpacer;
MkStickyContainer: typeof MkStickyContainer;
MkLazy: typeof MkLazy;
PageWithHeader: typeof PageWithHeader;
PageWithAnimBg: typeof PageWithAnimBg;
SearchMarker: typeof SearchMarker;
SearchLabel: typeof SearchLabel;
SearchKeyword: typeof SearchKeyword;

View File

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

View File

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

View File

@@ -10,7 +10,7 @@ export default {
mounted(src, binding, vn) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel');
const myBg = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel');
if (parentBg === myBg) {
src.style.backgroundColor = 'var(--MI_THEME-bg)';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
<div style="overflow: clip;">
<MkSpacer :contentMax="600" :marginMin="20">
<div class="_gaps_m znqjceqz">
@@ -130,7 +129,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkSpacer>
</div>
</MkStickyContainer>
</PageWithHeader>
</template>
<script lang="ts" setup>

View File

@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
<MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20">
<XOverview/>
@@ -20,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInstanceStats/>
</MkSpacer>
</MkHorizontalSwipe>
</MkStickyContainer>
</PageWithHeader>
</template>
<script lang="ts" setup>

View File

@@ -4,12 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkStickyContainer>
<template #header><MkPageHeader/></template>
<PageWithHeader>
<MkSpacer :contentMax="1200">
<MkAchievements :user="$i"/>
</MkSpacer>
</MkStickyContainer>
</PageWithHeader>
</template>
<script lang="ts" setup>

View File

@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
<MkSpacer v-if="file" :contentMax="600" :marginMin="16" :marginMax="32">
<div v-if="tab === 'overview'" class="cxqhhsmd _gaps_m">
<a class="thumbnail" :href="file.url" target="_blank">
@@ -67,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkObjectView>
</div>
</MkSpacer>
</MkStickyContainer>
</PageWithHeader>
</template>
<script lang="ts" setup>

View File

@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
<MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
<FormSuspense :p="init">
<div v-if="tab === 'overview'" class="_gaps_m">
@@ -208,7 +207,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</FormSuspense>
</MkSpacer>
</MkStickyContainer>
</PageWithHeader>
</template>
<script lang="ts" setup>

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