Compare commits

...

161 Commits

Author SHA1 Message Date
fa4015580f README.md aktualisiert
All checks were successful
release-tag / release-image (push) Successful in 12m2s
2025-03-23 21:47:43 +00:00
e875eb9ca1 .gitea/workflows/registry.yml aktualisiert
Some checks failed
release-tag / release-image (push) Failing after 8m51s
2025-03-23 20:40:41 +00:00
42ce754a39 .gitea/workflows/registry.yml aktualisiert
Some checks failed
release-tag / release-image (push) Failing after 8m57s
2025-03-23 20:29:20 +00:00
9ee9d0e5f2 .gitea/workflows/registry.yml aktualisiert
Some checks failed
release-tag / release-image (push) Failing after 12m34s
2025-03-23 20:06:55 +00:00
eeeb3603b8 README.md aktualisiert
Some checks failed
release-tag / release-image (push) Failing after 45m9s
2025-03-23 18:30:00 +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
syuilo
dfab6b1b8d lint(frontend): force window prefix 2025-03-20 15:44:06 +09:00
syuilo
fac59d75e2 lint(frontend): relax id-denylist rule 2025-03-20 15:43:35 +09:00
syuilo
fd3e28812e clean up console 2025-03-20 15:15:46 +09:00
syuilo
6a90b7e04b add todo 2025-03-20 15:09:50 +09:00
syuilo
8d8414687a enhance(frontend): improve preference manager stability 2025-03-20 14:57:14 +09:00
syuilo
0c682dd027 🎨 2025-03-20 13:34:57 +09:00
syuilo
3399c786a8 refactor(frontend): refactor components 2025-03-20 13:33:01 +09:00
syuilo
64cf101fe7 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-03-20 13:16:23 +09:00
syuilo
8b6d90b7a4 🎨 2025-03-20 13:16:08 +09:00
github-actions[bot]
070749bdc8 Bump version to 2025.3.2-beta.6 2025-03-20 04:03:25 +00:00
syuilo
161706c5e2 New Crowdin updates (#15680)
* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Romanian)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Greek)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Dutch)

* New translations ja-jp.yml (Norwegian)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Swedish)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Uzbek)

* New translations ja-jp.yml (Lao)

* New translations ja-jp.yml (Korean (Gyeongsang))
2025-03-20 13:01:20 +09:00
syuilo
596e517f77 fix(frontend): minimum uiが表示できない 2025-03-20 12:58:18 +09:00
syuilo
91670caca0 enhance(frontend): tweak install-extensions behaviour 2025-03-20 12:58:01 +09:00
syuilo
fccaadacf0 lint(frontend): improve id-denylist rule 2025-03-20 12:47:51 +09:00
syuilo
282caa0b7e 🎨 for install-extensions 2025-03-20 12:36:48 +09:00
syuilo
9529867630 fix(frontend): dev buildでpreferencesのタブ同期が不必要に行われるのを修正 2025-03-20 12:02:50 +09:00
github-actions[bot]
d06fadd095 Bump version to 2025.3.2-beta.5 2025-03-20 00:06:04 +00:00
zyoshoka
9dd13f364b fix(backend): mismatch in emojis param of test WebHook payload (#15675)
* fix(backend): mismatch in `emojis` param of test WebHook payload

* fix: test

* fix: type
2025-03-20 09:00:58 +09:00
syuilo
b067d4dcd6 follow up of 7b323031b7 2025-03-20 08:59:54 +09:00
syuilo
acac759d87 fix(frontend): モバイルレイアウト時にホームを押しても最上部へスクロールされない
Fix #15679
2025-03-20 08:35:45 +09:00
syuilo
b37622fa64 🎨 2025-03-19 20:54:07 +09:00
syuilo
c17a104de6 fix(frontend): router view stacking有効時にinstall-extensionsが動かない 2025-03-19 20:53:48 +09:00
syuilo
4ab9f66356 Update deep-equal.ts 2025-03-19 20:32:15 +09:00
zyoshoka
aed95a765d chore(storybook): fix storybook build (#15678) 2025-03-19 10:52:05 +00:00
Sayamame-beans
71841e365e Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように (#15677)
* enhance(frontend): include server hostname and port in 2fa recovery code filename

* chore(frontend): fix mistake(use `@` for indicate server hostname)

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-03-19 19:34:57 +09:00
github-actions[bot]
3b20279e20 Bump version to 2025.3.2-beta.4 2025-03-19 10:27:14 +00:00
Yuri Lee
21dc7aebe7 Fix: Don't delete remote emoji when import zip (#15674)
* Fix: Don't delete remote emoji when import zip

* Update packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>

---------

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
2025-03-19 10:23:50 +00:00
syuilo
aff03708a7 New Crowdin updates (#15667)
* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Catalan)
2025-03-19 19:18:14 +09:00
syuilo
69ad3cf89b Update CHANGELOG.md 2025-03-19 19:17:33 +09:00
syuilo
7b323031b7 refactor(frontend): use useTemplateRef for DOM referencing 2025-03-19 18:46:03 +09:00
syuilo
81ac71f7e5 refactor(frontend): router refactoring 2025-03-19 18:06:22 +09:00
syuilo
2c76018b7f better import paths 2025-03-19 17:27:18 +09:00
syuilo
2dc2d2e4fe refactor 2025-03-19 16:04:01 +09:00
syuilo
409cd4fbd3 refactor(frontend): router refactoring 2025-03-19 15:54:30 +09:00
syuilo
7d4045e8b4 refactor(frontend): router refactoring 2025-03-19 15:24:56 +09:00
syuilo
bdc72e5817 clean up 2025-03-19 15:17:41 +09:00
syuilo
11378b17c5 🎨 2025-03-19 09:31:01 +09:00
syuilo
62bf0d53d3 🎨 2025-03-18 22:21:28 +09:00
syuilo
05391f59a5 enhance(frontend): improve StackingRouterView 2025-03-18 20:55:19 +09:00
syuilo
d609f41f61 🎨 2025-03-18 17:31:25 +09:00
syuilo
0a295e1bb0 🎨 2025-03-18 15:23:50 +09:00
syuilo
474155b677 follow up of 6c8f21b608 2025-03-17 13:27:47 +09:00
syuilo
6c8f21b608 fix(backend): 連合無しモードでも外部から照会可能だった問題を修正 2025-03-17 13:21:09 +09:00
ikasoba
7d5daa4b00 fix (#15671) 2025-03-17 03:40:10 +00:00
syuilo
1ce81f243e Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-03-16 19:04:16 +09:00
syuilo
a773f2976d refactor(frontend): signinRequired -> ensureSignin 2025-03-16 19:04:14 +09:00
github-actions[bot]
467c68fb98 Bump version to 2025.3.2-beta.3 2025-03-16 08:51:58 +00:00
syuilo
22b0ace8b4 enhance(frontend): 投稿フォームの設定メニューを改良 (改)
This reverts commit a814395127.
2025-03-16 17:48:16 +09:00
syuilo
a814395127 Revert "enhance(frontend): 投稿フォームの設定メニューを改良 (#14804)"
This reverts commit ce6b2448ce.
2025-03-16 17:21:20 +09:00
syuilo
81a0cbd294 chore(frontend): use toast to show message when call copyToClipboard 2025-03-16 15:04:38 +09:00
syuilo
32844e4775 🎨 2025-03-16 14:56:27 +09:00
github-actions[bot]
fbd9f47182 Bump version to 2025.3.2-beta.2 2025-03-16 05:18:29 +00:00
syuilo
1c9e25470a refactor 2025-03-16 14:15:09 +09:00
かっこかり
ce6b2448ce enhance(frontend): 投稿フォームの設定メニューを改良 (#14804)
* enhance(frontend): 投稿フォームの設定メニューを改良

* Update Changelog

* indent

* MkMenuのitemを切り出して共通化

* remove unused expose

* fix: ドロワーなどのOptionが当たらない問題を修正

* 他のpopupMenuの項目選択時と挙動をあわせる

* チュートリアルで詰む問題を修正

* Revert "MkMenuのitemを切り出して共通化"

This reverts commit ce3679798c.

* enhance: slotで共通化

* Update MkPostFormOtherMenu.vue

* remove duplicated locale key

* refactor: メニューの定義をMkPostForm側で行うように

* Update CHANGELOG.md

* [ci skip] Update MkPostFormOtherMenu.vue

* Update MkPostForm.vue

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-03-16 05:05:58 +00:00
syuilo
7d44b47fdf New Crowdin updates (#15662)
* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)
2025-03-16 13:59:22 +09:00
syuilo
dca42fd6e6 enhance(frontend): improve ux for touch devices 2025-03-16 13:59:08 +09:00
syuilo
43153311c6 🎨 2025-03-16 13:43:47 +09:00
syuilo
2b23c7e7f5 chore(frontend): remove duplicated warn 2025-03-16 13:30:08 +09:00
syuilo
2d4e85b466 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-03-16 13:03:04 +09:00
syuilo
7bfada9792 enhance: remove bull-board support 2025-03-16 13:03:02 +09:00
lqvp
4fef9c670a fix(i18n): 通知タイプのcreateTokenが抜けていたのを修正 (#15663) 2025-03-16 02:27:41 +00:00
syuilo
9599261fc3 fix(frontend): fix settings-search-index of webhook 2025-03-16 11:02:54 +09:00
syuilo
c2940fd77c enhance(frontend): improve usability on touch device 2025-03-16 10:58:06 +09:00
syuilo
2ddedd0ce6 refactor 2025-03-14 19:54:30 +09:00
syuilo
c88f5f5195 Update CHANGELOG.md 2025-03-14 17:00:02 +09:00
github-actions[bot]
30de6d80bb Bump version to 2025.3.2-beta.1 2025-03-14 07:39:14 +00:00
syuilo
63993dace6 fix(frontend): fix pref migration
Fix #15661
2025-03-14 16:37:25 +09:00
syuilo
0929410d36 enhance(frontend): improve pref manager 2025-03-14 15:43:56 +09:00
github-actions[bot]
96866d37cd Bump version to 2025.3.2-beta.0 2025-03-14 03:49:32 +00:00
syuilo
35b66276ff New Crowdin updates (#15621)
* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Greek)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Traditional)
2025-03-14 12:09:38 +09:00
syuilo
daa16d184f 🎨 2025-03-14 11:58:41 +09:00
syuilo
833a232262 enhance(frontend): clear all client data when logout 2025-03-14 11:55:42 +09:00
github-actions[bot]
bcb891e4fd Bump version to 2025.3.2-alpha.11 2025-03-14 01:29:38 +00:00
syuilo
152660fcf2 enhance(frontend): re-organize settings page 2025-03-14 09:53:44 +09:00
syuilo
a1204f5e3e 🎨 2025-03-14 09:42:53 +09:00
かっこかり
7acd3d1a88 fix(frontend): fix test 2025-03-13 22:32:26 +09:00
syuilo
8c9ec5827f enhance(frontend): improve accounts management 2025-03-13 22:12:23 +09:00
syuilo
44073736de enhance(frontend): improve preferences 2025-03-13 19:44:23 +09:00
syuilo
0126dba475 enhance(frontend): re-organize settings page 2025-03-13 19:30:35 +09:00
github-actions[bot]
3280a3d661 Bump version to 2025.3.2-alpha.10 2025-03-13 09:07:37 +00:00
syuilo
bdf80c49d8 fix(frontend): better migration detection
Fix #15656
2025-03-13 18:05:44 +09:00
syuilo
59169a6450 🎨 2025-03-13 17:42:35 +09:00
syuilo
5d228fb0f3 enhance(frontend): re-organize settings page 2025-03-13 17:39:53 +09:00
syuilo
10b67e1b3a enhance(frontend): improve emoji picker settings 2025-03-13 16:56:47 +09:00
syuilo
3ced310f77 refactor(frontend): organize use functions 2025-03-13 14:05:04 +09:00
syuilo
010ec113c2 refactor(frontend): cond -> scope 2025-03-13 13:45:23 +09:00
syuilo
30005ba959 enhance(frontend): tweak search index 2025-03-13 09:26:04 +09:00
syuilo
6b69588c03 enhance(frontend): improve deck setting page 2025-03-13 09:24:15 +09:00
syuilo
8593aa1418 refactor 2025-03-13 09:10:09 +09:00
syuilo
9876ff9a7a Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-03-13 09:07:25 +09:00
syuilo
ce6eba77d9 🎨 2025-03-13 09:07:22 +09:00
syuilo
9b2af53025 enhance(frontend): improve pref manager 2025-03-13 09:02:38 +09:00
syuilo
7b6ff19ea3 Update CHANGELOG.md 2025-03-12 21:49:23 +09:00
github-actions[bot]
c9fa95429a Bump version to 2025.3.2-alpha.9 2025-03-12 12:45:35 +00:00
饺子w (Yumechi)
e5d117dc98 fix(backend): tighten an overly relaxed criteria and remove capability of matching multiple final URLs in URL authority checking (#15655)
Signed-off-by: eternal-flame-AD <yume@yumechi.jp>
2025-03-12 12:39:24 +00:00
syuilo
4a73feb041 enhance(frontend): make deck profiles syncable 2025-03-12 21:12:08 +09:00
syuilo
a06b9eefaa enhance(frontend): suppress needless confirmation when turn on pref sync 2025-03-12 21:05:39 +09:00
syuilo
3129fcf164 fix(frontend): fix type errors 2025-03-12 20:17:54 +09:00
syuilo
35a4544477 add todo 2025-03-12 18:54:36 +09:00
zyoshoka
aa1cc2f817 fix(storybook): use type-only imports in generated stories (#15654) 2025-03-12 16:51:10 +09:00
github-actions[bot]
15685be4cc Bump version to 2025.3.2-alpha.8 2025-03-12 06:10:35 +00:00
syuilo
8508c4dadc refactor 2025-03-12 15:07:45 +09:00
かっこかり
e594fb0037 enhance(dev): frontendの検索インデックス作成を単独のコマンドで行えるように (#15653) 2025-03-12 14:37:57 +09:00
syuilo
a369721791 remove todo 2025-03-12 14:35:22 +09:00
syuilo
f8e244f48d enhance(frontend): アカウントオーバーライド設定とデバイス間同期の併用に対応 2025-03-12 14:34:10 +09:00
syuilo
8410611512 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-03-12 13:04:44 +09:00
syuilo
caab1ec7c3 🎨 2025-03-12 13:04:41 +09:00
github-actions[bot]
ffade9740e Bump version to 2025.3.2-alpha.7 2025-03-12 03:03:37 +00:00
syuilo
b03bcf26cd enhance(frontend): 設定値の同期を実装(実験的) 2025-03-12 11:39:05 +09:00
syuilo
ddbc83b2e4 chore(frontend): tweak settings page 2025-03-11 20:42:06 +09:00
syuilo
d185785f20 enhance(frontend): improve settings page 2025-03-11 14:52:04 +09:00
syuilo
02d7fbefc4 🎨 2025-03-11 12:08:15 +09:00
syuilo
f7ea92c68c chore: remove unused files 2025-03-11 12:02:41 +09:00
syuilo
e891d5c5d3 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-03-11 11:44:34 +09:00
syuilo
57a6b630b7 chore: add note 2025-03-11 11:44:25 +09:00
github-actions[bot]
eda768a08c Bump version to 2025.3.2-alpha.6 2025-03-11 02:43:27 +00:00
syuilo
1f345eb839 enhance(frontend): deckをpreferences管理に 2025-03-11 11:14:55 +09:00
syuilo
1f2801af02 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-03-10 21:42:30 +09:00
syuilo
a4ba096e2a chore(frontend): improve preference store stability 2025-03-10 21:42:17 +09:00
ろむねこ
6841cdfa76 enhance(frontend): CWの注釈テキストが入力されていない場合はPostボタンを非アクティブに (#15639)
* add condition to disable post button when CW text is empty

* standardize condition by using 1<= inserted of 0<

* unify CW text length condition to improve readability

* add missing CW state check

* fix state check, add empty/null check, improve max length validation

* simplify CW validation by removing minimum length check

* Update CHANGELOG

* remove CW text validation in post()

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-03-10 10:35:37 +00:00
github-actions[bot]
794f360bc2 Bump version to 2025.3.2-alpha.5 2025-03-10 09:40:41 +00:00
かっこかり
f797765b1d enhance(frontend): テーマ設定で簡易プレビューを表示するように (#15643)
* enhance(frontend): テーマ設定で簡易プレビューを表示するように

* Update Changelog

* fix lint

* 🎨

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-03-10 09:35:51 +00:00
syuilo
9dce512fbb enhance(frontend): add navbar transition animation 2025-03-10 15:47:00 +09:00
syuilo
9e91f85370 refactor(frontend): use Symbol for vue provide/inject 2025-03-10 15:08:40 +09:00
syuilo
9998cb84e8 refactor(frontend): page-metadata -> page 2025-03-10 13:47:38 +09:00
renovate[bot]
5ed1101bbd chore(deps): update [root] update dependencies (#15624)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 12:30:37 +09:00
syuilo
6c9153300d chore(frontend): tweak lockdown setting 2025-03-10 12:03:54 +09:00
545 changed files with 9934 additions and 8043 deletions

View File

@@ -0,0 +1,51 @@
name: release-tag
on:
push:
branches:
- 'main'
jobs:
release-image:
runs-on: ubuntu-fast
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

@@ -24,9 +24,6 @@ updates:
aws-sdk:
patterns:
- "@aws-sdk/*"
bull-board:
patterns:
- "@bull-board/*"
nestjs:
patterns:
- "@nestjs/*"

View File

@@ -50,8 +50,8 @@ jobs:
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Build misskey-js
run: pnpm --filter misskey-js build
- name: Build dependent packages
run: pnpm -F misskey-js -F misskey-bubble-game -F misskey-reversi build
- name: Build storybook
run: pnpm --filter frontend build-storybook
- name: Publish to Chromatic

View File

@@ -1,17 +1,46 @@
## 2025.3.2
### General
-
- セキュリティを強化するため、ジョブキューのダッシュボード(bull-board)統合が削除されました。
- Misskeyネイティブでダッシュボードを実装予定です
### Client
- Feat: 設定の管理が強化されました
- 自動でバックアップされるように
- 内部処理が一新され、安定性とパフォーマンスが向上しました
- 全てのクライアント設定がエクスポート(バックアップ)/インポート対象に含まれるようになりました
- プラグイン、テーマ、クライアントに追加されたすべてのアカウント情報も含まれるようになりました
- 自動で設定データをサーバーにバックアップできるように
- 設定→設定のプロファイル→自動バックアップ で有効にできます
- 新しいデバイスからログインしたり、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能)
- 任意の設定項目をデバイス間で同期できるように
- 設定項目の「...」メニュー→「デバイス間で同期」
- 同期をオンにした際にサーバーに保存された値とローカルの値が競合する場合はどちらを優先するか選択できます
- 任意の設定項目を初期値にリセットできるように
- 設定項目の「...」メニュー→「初期値にリセット」
- アカウントごとに設定値が分離される設定とそうでないクライアント設定が混在していた(かつ分離するかどうかを設定不可だった)のを、基本的に一律でクライアント全体に適用されるようにし、個別でアカウントごとに異なる設定を行えるように
- 設定項目の「...」メニュー→「アカウントで上書き」をオンにすることで、設定値をそのアカウントでだけ適用するようにできます
- ログアウトすると設定データもブラウザから消去されるようになりプライバシーが向上しました
- 再度ログインすればサーバーのバックアップから設定データを復元可能です
- エクスポートした設定データを他のサーバーでインポートして適用すること(設定の持ち運び)が可能になりました
- Feat: 画面を重ねて表示するオプションを実装(実験的)
- 設定 → その他 → 実験的機能 → Enable stacking router view
- Enhance: プラグインの管理が強化されました
- インストール/アンインストール/設定の変更時にリロード不要になりました
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
- Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに
- Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように
- Enhance: テーマ設定画面のデザインを改善
- Enhance: 投稿フォームの設定メニューを改良
- 投稿フォームをリセットできるように
- 文字数カウントを復活
- Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
### Server
- Fix: プロフィール追加情報で無効なURLに入力された場合に照会エラーを出るのを修正
- Fix: ActivityPubリクエストURLチェック実装は仕様に従っていないのを修正
- Fix: 連合無しモードでも外部から照会可能だった問題を修正
- Fix: テスト用WebHookのペイロードの`emojis`パラメータが実際のものと異なる問題を修正
## 2025.3.1

View File

@@ -273,7 +273,6 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド
query?: Record<string, string>;
loginRequired?: boolean;
hash?: string;
globalCacheKey?: string;
children?: RouteDef[];
}
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

View File

@@ -1012,6 +1012,8 @@ sourceCode: "الشفرة المصدرية"
flip: "اقلب"
lastNDays: "آخر {n} أيام"
surrender: "ألغِ"
postForm: "أنشئ ملاحظة"
information: "عن"
_delivery:
stop: "مُعلّق"
_initialAccountSetting:

View File

@@ -852,6 +852,8 @@ replies: "জবাব"
renotes: "রিনোট"
sourceCode: "সোর্স কোড"
flip: "উল্টান"
postForm: "নোট লিখুন"
information: "আপনার সম্পর্কে"
_delivery:
stop: "স্থগিত করা হয়েছে"
_type:

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} està parlant sobre \"{word}\""
makeActive: "Activar"
display: "Veure"
copy: "Copiar"
copiedToClipboard: "Copiat al porta papers"
metrics: "Mètriques"
overview: "Visió General"
logs: "Registres"
@@ -1139,7 +1140,7 @@ channelArchiveConfirmDescription: "Un Canal arxivat no apareixerà a la llista d
thisChannelArchived: "Aquest Canal ha sigut arxivat."
displayOfNote: "Mostrar notes"
initialAccountSetting: "Configuració del perfil"
youFollowing: "Seguint"
youFollowing: "Segueixes "
preventAiLearning: "Descartar l'ús d'aprenentatge automàtic (IA Generativa)"
preventAiLearningDescription: "Demanar els indexadors no fer servir els texts, imatges, etc. en cap conjunt de dades per alimentar l'aprenentatge automàtic (IA Predictiva/ Generativa). Això s'aconsegueix afegint la etiqueta \"noai\" com a resposta HTML al contingut corresponent. Prevenir aquest ús totalment pot ser que no sigui aconseguit, ja que molts indexadors poden obviar aquesta etiqueta."
options: "Opcions"
@@ -1190,7 +1191,7 @@ pastAnnouncements: "Informes passats"
youHaveUnreadAnnouncements: "Tens informes per llegir."
useSecurityKey: "Segueix les instruccions del teu navegador O dispositiu per fer servir el teu passkey."
replies: "Respostes"
renotes: "Impulsar"
renotes: "Impulsos"
loadReplies: "Mostrar les respostes"
loadConversation: "Mostrar la conversació "
pinnedList: "Llista fixada"
@@ -1313,6 +1314,65 @@ confirmOnReact: "Confirmar en reaccionar"
reactAreYouSure: "Vols reaccionar amb \"{emoji}\"?"
markAsSensitiveConfirm: "Vols marcar aquest contingut com a sensible?"
unmarkAsSensitiveConfirm: "Vols deixar de marcar com a sensible aquest contingut?"
preferences: "Preferències "
accessibility: "Accessibilitat "
preferencesProfile: "Perfil de configuració "
copyPreferenceId: "Copiar l'ID de la configuració "
resetToDefaultValue: "Restaura al valor per defecte "
overrideByAccount: "Anul·lar per compte"
untitled: "Sense títol "
noName: "No hi ha un nom disponible "
skip: "Ometre "
restore: "Restaurar "
syncBetweenDevices: "Sincronització entre dispositius"
preferenceSyncConflictTitle: "Els valors de la configuració ja existeixen al dispositiu"
preferenceSyncConflictText: "Un element de la configuració amb sincronització activada desa els seus valors al servidor, però s'ha trobat un valor a la configuració desat al servidor per aquest element de la configuració. Quin valor us sobreescriure?"
preferenceSyncConflictChoiceServer: "Valors de configuració del servidor"
preferenceSyncConflictChoiceDevice: "Punts d'ajustos del dispositiu "
preferenceSyncConflictChoiceCancel: "Cancel·lar l'activació de la sincronització "
paste: "Pegar"
emojiPalette: "Calaix d'emojis"
postForm: "Formulari de publicació"
textCount: "Nombre de caràcters "
information: "Informació"
_emojiPalette:
palettes: "Calaixos d'emojis"
enableSyncBetweenDevicesForPalettes: "Activa la sincronització dels calaixos d'emojis entre dispositius"
paletteForMain: "Calaix d'emojis principal"
paletteForReaction: "Calaix d'emojis per reaccions"
_settings:
driveBanner: "Pots gestionar i configurar el Disc, comprovar el seu ús i establir una configuració per a la càrrega d'arxius."
pluginBanner: "Els complements poden fer-se servir per ampliar les funcionalitats del client. Els complements poden instal·lar-se, configurar-se individualment i gestionar-se."
notificationsBanner: "Pots configurar el tipus i l'abast de les notificacions que es rebran del servidor, també les notificacions emergents."
api: "API"
webhook: "Webhook"
serviceConnection: "Relació entre serveis"
serviceConnectionBanner: "Pots configurar i gestionar tokens d'accés i webhooks per integrar serveis i aplicacions externes."
accountData: "Dades del compte"
accountDataBanner: "Exportació/Importació i gestió d'arxius amb dades del compte."
muteAndBlockBanner: "Pots configurar i gestionar els continguts que desitges amagar i restringir les accions de determinats usuaris."
accessibilityBanner: "Els clients poden personalitzar-se i configurar-se per un ús òptim en funció de la seva visió i comportament."
privacyBanner: "Pots establir la configuració de privacitat del compte, com el grau de visibilitat del teu contingut, la facilitat per trobar-ho i si es pot aprovar els seguidors."
securityBanner: "Configura les opcions relacionades amb la seguretat del teu compte com ara contrasenyes, mètodes per iniciar sessió, aplicacions d'autentificació i claus d'accés."
preferencesBanner: "Pots configurar el comportament general del client segons les teves preferències."
appearanceBanner: "Pots configurar les preferències relacionades amb la visualització i l'aspecte del client segons el teu parer."
soundsBanner: "Configuració dels sons que reproduirà el client."
timelineAndNote: "Línia de temps i nota"
makeEveryTextElementsSelectable: "Fes que tots els elements del text siguin seleccionables"
makeEveryTextElementsSelectable_description: "L'activació pot reduir la usabilitat en determinades ocasions."
_preferencesProfile:
profileName: "Nom del perfil"
profileNameDescription: "Estableix un nom que identifiqui aquest dispositiu."
profileNameDescription2: "Per exemple: \"PC Principal\", \"Smartphone\", etc"
_preferencesBackup:
autoBackup: "Còpia de seguretat automàtica "
restoreFromBackup: "Restaurar des d'una còpia de seguretat"
noBackupsFoundTitle: "No s'ha trobat cap còpia de seguretat"
noBackupsFoundDescription: "No s'han trobat còpies de seguretat creades automàticament, però si has desat, manualment, un arxiu de còpia de seguretat, pots importar-lo i carregar-lo."
selectBackupToRestore: "Seleccionar la còpia de seguretat que vols restaurar"
youNeedToNameYourProfileToEnableAutoBackup: "Has de posar-li un nom al teu perfil per poder activar les còpies de seguretat automàtiques."
autoPreferencesBackupIsNotEnabledForThisDevice: "La còpia de seguretat automàtica no es troba activada en aquest dispositiu."
backupFound: "Còpia de seguretat de la configuració trobada"
_accountSettings:
requireSigninToViewContents: "És obligatori l'inici de sessió per poder veure el contingut"
requireSigninToViewContentsDescription1: "Es requereix l'inici de sessió per poder veure totes les notes i el contingut que has creat. Amb això esperem evitar que els rastrejadors recopilin informació."
@@ -1323,6 +1383,7 @@ _accountSettings:
makeNotesHiddenBefore: "Fes que les notes antigues siguin privades"
makeNotesHiddenBeforeDescription: "Mentres aquesta funció estigui activada les notes que hagin superat una data i hora fixada o hagi passat el temps establert només seran visibles per a tu. Si la desactives es restablirà també l'estat públic de les notes."
mayNotEffectForFederatedNotes: "Això pot ser que no afecti les notes federades."
mayNotEffectSomeSituations: "Aquestes restriccions són simplificades. Pot ser que no s'apliquin en determinades situacions, com quan es modera o visualitza un servidor remot."
notesHavePassedSpecifiedPeriod: "Notes publicades durant un període de temps especificat."
notesOlderThanSpecifiedDateAndTime: "Notes més antigues de la data i temps especificat "
_abuseUserReport:
@@ -1971,6 +2032,7 @@ _theme:
installed: "{name} Instal·lat "
installedThemes: "Temes instal·lats "
builtinThemes: "Temes integrats"
instanceTheme: "Tema de la instància "
alreadyInstalled: "Aquest tema ja es troba instal·lat "
invalid: "El format d'aquest tema no és correcte"
make: "Crear un tema"
@@ -2010,7 +2072,7 @@ _theme:
hashtag: "Etiqueta"
mention: "Menció"
mentionMe: "Mencions (jo)"
renote: "Renotar"
renote: "Impulsar"
modalBg: "Fons del modal"
divider: "Divisor"
scrollbarHandle: "Maneta de la barra de desplaçament"
@@ -2452,7 +2514,7 @@ _notification:
follow: "Segueix-me"
mention: "Menció"
reply: "Respostes"
renote: "Impulsar"
renote: "Impulsos"
quote: "Citar"
reaction: "Reaccions"
pollEnded: "Enquesta terminada"
@@ -2462,12 +2524,13 @@ _notification:
achievementEarned: "Assoliment desbloquejat"
exportCompleted: "Exportació completada"
login: "Iniciar sessió"
createToken: "Creació de tokens d'accés "
test: "Prova la notificació"
app: "Notificacions d'aplicacions"
_actions:
followBack: "També et segueix"
reply: "Respondre"
renote: "Impulsos"
renote: "Impulsar"
_deck:
alwaysShowMainColumn: "Mostrar sempre la columna principal"
columnAlign: "Alinea les columnes"
@@ -2489,6 +2552,7 @@ _deck:
useSimpleUiForNonRootPages: "Usa una interfície senzilla per a les pàgines navegades"
usedAsMinWidthWhenFlexible: "L'amplada mínima es farà servir quan \"Ajust automàtic de l'amplada\" estigui activat"
flexible: "Ajust automàtic de l'amplada"
enableSyncBetweenDevicesForProfiles: "Activar la sincronització de la informació de perfils de dispositiu a dispositiu"
_columns:
main: "Principal"
widgets: "Ginys"
@@ -2610,10 +2674,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "Assegura't que qui distribueix aquest recurs és fiable abans d'instal·lar-ho."
_plugin:
title: "Vols instal·lar aquest afegit?"
metaTitle: "Informació de l'afegit "
_theme:
title: "Vols instal·lar aquest tema?"
metaTitle: "Informació del tema"
_meta:
base: "Paleta de colors base"
_vendorInfo:

View File

@@ -169,6 +169,9 @@ addAccount: "Přidat účet"
reloadAccountsList: "Obnovit list účtů"
loginFailed: "Přihlášení se nezdařilo."
showOnRemote: "Více na původním profilu"
continueOnRemote: "Pokračujte na původní profil"
chooseServerOnMisskeyHub: "Vyberete si server z Misskey Hubu"
inputHostName: "Zadejte doménu"
general: "Obecně"
wallpaper: "Obrázek na pozadí"
setWallpaper: "Nastavení obrázku na pozadí"
@@ -193,6 +196,7 @@ perHour: "za hodinu"
perDay: "za den"
stopActivityDelivery: "Přestat zasílat aktivitu"
blockThisInstance: "Blokovat tuto instanci"
silenceThisInstance: "Utišit tuto instanci"
operations: "Operace"
software: "Software"
version: "Verze"
@@ -1098,6 +1102,8 @@ sourceCode: "Zdrojový kód"
flip: "Otočit"
lastNDays: "Posledních {n} dnů"
surrender: "Zrušit"
postForm: "Formulář pro odeslání"
information: "Informace"
_delivery:
stop: "Suspendováno"
_type:

View File

@@ -49,7 +49,7 @@ pin: "An dein Profil anheften"
unpin: "Von deinem Profil lösen"
copyContent: "Inhalt kopieren"
copyLink: "Link kopieren"
copyRemoteLink: "Renote-Link kopieren"
copyRemoteLink: "Remote-Link kopieren"
copyLinkRenote: "Renote-Link kopieren"
delete: "Löschen"
deleteAndEdit: "Löschen und Bearbeiten"
@@ -863,7 +863,7 @@ administration: "Verwaltung"
accounts: "Benutzerkonten"
switch: "Wechseln"
noMaintainerInformationWarning: "Betreiberinformationen sind nicht konfiguriert."
noInquiryUrlWarning: "Keine gültige URL."
noInquiryUrlWarning: "Keine gültige Kontakt-URL."
noBotProtectionWarning: "Schutz vor Bots ist nicht konfiguriert."
configure: "Konfigurieren"
postToGallery: "Neuen Galeriebeitrag erstellen"
@@ -1308,6 +1308,10 @@ pleaseSelectAccount: "Bitte Konto auswählen"
availableRoles: "Verfügbare Rollen"
federationSpecified: "Dieser Server arbeitet mit Whitelist-Föderation. Er kann nicht mit anderen als den vom Administrator angegebenen Servern interagieren."
federationDisabled: "Föderation ist auf diesem Server deaktiviert. Es ist nicht möglich, mit Benutzern auf anderen Servern zu interagieren."
postForm: "Notizfenster"
information: "Über"
_settings:
webhook: "Webhook"
_accountSettings:
requireSigninToViewContents: "Anmeldung erfordern, um Inhalte anzuzeigen"
requireSigninToViewContentsDescription1: "Erfordere eine Anmeldung, um alle Notizen und andere Inhalte anzuzeigen, die du erstellt hast. Dadurch wird verhindert, dass Crawler deine Informationen sammeln."
@@ -2521,10 +2525,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "Überprüfe vor Installation die Vertrauenswürdigkeit des Vertreibers."
_plugin:
title: "Möchtest du dieses Plugin installieren?"
metaTitle: "Plugininformation"
_theme:
title: "Möchten du dieses Farbschema installieren?"
metaTitle: "Farbschemainfo"
_meta:
base: "Farbschemavorlage"
_vendorInfo:

View File

@@ -288,6 +288,8 @@ cannotUploadBecauseNoFreeSpace: "Το ανέβασμα απέτυχε λόγω
icon: "Εικονίδιο"
replies: "Απάντηση"
renotes: "Κοινοποίηση σημειώματος"
postForm: "Φόρμα δημοσίευσης"
information: "Πληροφορίες"
_email:
_follow:
title: "Έχετε ένα νέο ακόλουθο"

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} said something about \"{word}\""
makeActive: "Activate"
display: "Display"
copy: "Copy"
copiedToClipboard: "Copied to clipboard"
metrics: "Metrics"
overview: "Overview"
logs: "Logs"
@@ -1313,6 +1314,65 @@ confirmOnReact: "Confirm when reacting"
reactAreYouSure: "Would you like to add a \"{emoji}\" reaction?"
markAsSensitiveConfirm: "Do you want to set this media as sensitive?"
unmarkAsSensitiveConfirm: "Do you want to remove the sensitive designation for this media?"
preferences: "Preferences"
accessibility: "Accessibility"
preferencesProfile: "Preferences profile"
copyPreferenceId: "Copy the preference ID"
resetToDefaultValue: "Revert to default"
overrideByAccount: "Override by the account"
untitled: "Untitled"
noName: "No name"
skip: "Skip"
restore: "Restore"
syncBetweenDevices: "Sync between devices"
preferenceSyncConflictTitle: "The configured value exists on the server."
preferenceSyncConflictText: "The sync enabled settings will save their values to the server. However, there are existing values on the server. Which set of values would you like to overwrite?"
preferenceSyncConflictChoiceServer: "Configured value on server"
preferenceSyncConflictChoiceDevice: "Configured value on device"
preferenceSyncConflictChoiceCancel: "Cancel enabling sync"
paste: "Paste"
emojiPalette: "Emoji palette"
postForm: "Posting form"
textCount: "Character count"
information: "About"
_emojiPalette:
palettes: "Palette"
enableSyncBetweenDevicesForPalettes: "Enable palette sync between devices"
paletteForMain: "Main palette"
paletteForReaction: "Reaction palette"
_settings:
driveBanner: "You can manage and configure the drive, check usage, and configure file upload settings."
pluginBanner: "You can extend client features with plugins. You can install plugins, configure and manage individually."
notificationsBanner: "You can configure the types and range of notifications from the server and push notifications."
api: "API"
webhook: "Webhook"
serviceConnection: "Service integration"
serviceConnectionBanner: "Manage and configure access tokens and Webhooks to integrate with external apps or services."
accountData: "Account data"
accountDataBanner: "Export and import to manage account data."
muteAndBlockBanner: "You can configure and manage settings to hide content and restrict actions from specific users."
accessibilityBanner: "You can personalize the client's visuals and behavior, and configure settings to optimize usage."
privacyBanner: "You can configure settings related to account privacy, such as content visibility, discoverability, and follow approval."
securityBanner: "You can configure settings related to account security, such as password, login methods, authentication apps, and Passkeys."
preferencesBanner: "You can configure the overall behavior of the client according to your preferences."
appearanceBanner: "You can configure the appearance and display settings for the client according to your preferences."
soundsBanner: "You can configure the sound settings for playback in the client."
timelineAndNote: "Timeline and note"
makeEveryTextElementsSelectable: "Make all text elements selectable"
makeEveryTextElementsSelectable_description: "Enabling this may reduce usability in some situations."
_preferencesProfile:
profileName: "Profile name"
profileNameDescription: "Set a name that identifies this device."
profileNameDescription2: "Example: \"Main PC\", \"Smartphone\""
_preferencesBackup:
autoBackup: "Auto backup"
restoreFromBackup: "Restore from backup"
noBackupsFoundTitle: "No backups found"
noBackupsFoundDescription: "No auto-created backups were found, but if you have manually saved a backup file, you can import and restore it."
selectBackupToRestore: "Select a backup to restore"
youNeedToNameYourProfileToEnableAutoBackup: "A profile name must be set to enable auto backup."
autoPreferencesBackupIsNotEnabledForThisDevice: "Settings auto backup is not enabled on this device."
backupFound: "Settings backup is found"
_accountSettings:
requireSigninToViewContents: "Require sign-in to view contents"
requireSigninToViewContentsDescription1: "Require login to view all notes and other content you have created. This will have the effect of preventing crawlers from collecting your information."
@@ -1323,6 +1383,7 @@ _accountSettings:
makeNotesHiddenBefore: "Make past notes private"
makeNotesHiddenBeforeDescription: "While this feature is enabled, notes that are past the set date and time or have been visible only to you. When it is deactivated, the note publication status will also be restored."
mayNotEffectForFederatedNotes: "Notes federated to a remote server may not be affected."
mayNotEffectSomeSituations: "These restrictions are simplified. They may not apply in some situations, such as when viewing on a remote server or during moderation."
notesHavePassedSpecifiedPeriod: "Note that the specified time has passed"
notesOlderThanSpecifiedDateAndTime: "Notes before the specified date and time"
_abuseUserReport:
@@ -1971,6 +2032,7 @@ _theme:
installed: "{name} has been installed"
installedThemes: "Installed themes"
builtinThemes: "Built-in themes"
instanceTheme: "Server theme"
alreadyInstalled: "This theme is already installed"
invalid: "The format of this theme is invalid"
make: "Make a theme"
@@ -2462,6 +2524,7 @@ _notification:
achievementEarned: "Achievement unlocked"
exportCompleted: "The export has been completed"
login: "Sign In"
createToken: "Create access token"
test: "Notification test"
app: "Notifications from linked apps"
_actions:
@@ -2489,6 +2552,7 @@ _deck:
useSimpleUiForNonRootPages: "Use simple UI for navigated pages"
usedAsMinWidthWhenFlexible: "Minimum width will be used for this when the \"Auto-adjust width\" option is enabled"
flexible: "Auto-adjust width"
enableSyncBetweenDevicesForProfiles: "Enable profile information sync between devices"
_columns:
main: "Main"
widgets: "Widgets"
@@ -2610,10 +2674,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "Make sure the distributor of this resource is trustworthy before installation."
_plugin:
title: "Do you want to install this plugin?"
metaTitle: "Plugin information"
_theme:
title: "Do you want to install this theme?"
metaTitle: "Theme information"
_meta:
base: "Base color scheme"
_vendorInfo:
@@ -2744,7 +2806,7 @@ _roleSelectDialog:
_customEmojisManager:
_gridCommon:
copySelectionRows: "Copy selected rows"
copySelectionRanges: "Copy selected ranges"
copySelectionRanges: "Copy selection"
deleteSelectionRows: "Delete selected rows"
deleteSelectionRanges: "Delete rows in the selection"
searchSettings: "Search settings"
@@ -2814,8 +2876,8 @@ _selfXssPrevention:
description2: "If you do not understand exactly what you are trying to paste, %cstop working right now and close this window."
description3: "For more information, please refer to this. {link}"
_followRequest:
recieved: "Received application"
sent: "Sent application"
recieved: "Received request"
sent: "Sent request"
_remoteLookupErrors:
_federationNotAllowed:
title: "Unable to communicate with this server"

View File

@@ -1299,6 +1299,10 @@ messageToFollower: "Mensaje a seguidores"
target: "Para"
federationSpecified: "Este servidor opera en una federación de listas blancas. No puede interactuar con otros servidores que no sean los especificados por el administrador."
federationDisabled: "La federación está desactivada en este servidor. No puede interactuar con usuarios de otros servidores"
postForm: "Formulario"
information: "Información"
_settings:
webhook: "Webhook"
_accountSettings:
requireSigninToViewContents: "Se requiere iniciar sesión para ver el contenido"
requireSigninToViewContentsDescription1: "Requiere iniciar sesión para ver todas las notas y otros contenidos que hayas creado. Se espera que esto evite que los rastreadores recopilen información."
@@ -2518,10 +2522,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "Asegúrate de que el distribuidor de este recurso es de confianza antes de proceder a la instalación."
_plugin:
title: "¿Quieres instalar este plugin?"
metaTitle: "Información del plugin"
_theme:
title: "¿Quieres instalar este tema?"
metaTitle: "Información del tema"
_meta:
base: "Esquema de color base"
_vendorInfo:

View File

@@ -1277,6 +1277,8 @@ prohibitedWordsForNameOfUser: "Mots interdits pour les noms d'utilisateur·rices
lockdown: "Verrouiller"
pleaseSelectAccount: "Sélectionner un compte"
availableRoles: "Rôles disponibles"
postForm: "Formulaire de publication"
information: "Informations"
_abuseUserReport:
forward: "Transférer"
forwardDescription: "Transférer le signalement vers une instance distante en tant qu'anonyme."
@@ -2294,10 +2296,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "Veuillez confirmer que le distributeur est fiable avant l'installation."
_plugin:
title: "Voulez-vous installer cette extension ?"
metaTitle: "Informations sur l'extension"
_theme:
title: "Voulez-vous installer ce thème ?"
metaTitle: "Informations sur le thème"
_meta:
base: "Palette de couleurs de base"
_vendorInfo:

View File

@@ -1261,6 +1261,10 @@ performance: "Kinerja"
modified: "Diubah"
thereAreNChanges: "Ada {n} perubahan"
prohibitedWordsForNameOfUser: "Kata yang dilarang untuk nama pengguna"
postForm: "Buat catatan"
information: "Informasi"
_settings:
webhook: "Webhook"
_abuseUserReport:
accept: "Setuju"
reject: "Tolak"
@@ -2489,10 +2493,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "Pastikan sumber dari sumber daya ini terpercaya sebelum melakukan pemasangan."
_plugin:
title: "Apakah kamu ingin memasang plugin ini?"
metaTitle: "Informasi plugin"
_theme:
title: "Apakah kamu ingin memasang tema ini?"
metaTitle: "Informasi tema"
_meta:
base: "Skema warna dasar"
_vendorInfo:

168
locales/index.d.ts vendored
View File

@@ -2810,6 +2810,10 @@ export interface Locale extends ILocale {
* コピー
*/
"copy": string;
/**
* クリップボードにコピーされました
*/
"copiedToClipboard": string;
/**
* メトリクス
*/
@@ -5310,6 +5314,146 @@ export interface Locale extends ILocale {
* 復元
*/
"restore": string;
/**
* デバイス間で同期
*/
"syncBetweenDevices": string;
/**
* サーバーに設定値が存在します
*/
"preferenceSyncConflictTitle": string;
/**
* 同期が有効にされた設定項目は設定値をサーバーに保存しますが、この設定項目のサーバーに保存された設定値が見つかりました。どちらの設定値で上書きしますか?
*/
"preferenceSyncConflictText": string;
/**
* サーバーの設定値
*/
"preferenceSyncConflictChoiceServer": string;
/**
* デバイスの設定値
*/
"preferenceSyncConflictChoiceDevice": string;
/**
* 同期の有効化をキャンセル
*/
"preferenceSyncConflictChoiceCancel": string;
/**
* ペースト
*/
"paste": string;
/**
* 絵文字パレット
*/
"emojiPalette": string;
/**
* 投稿フォーム
*/
"postForm": string;
/**
* 文字数
*/
"textCount": string;
/**
* 情報
*/
"information": string;
"_emojiPalette": {
/**
* パレット
*/
"palettes": string;
/**
* パレットのデバイス間同期を有効にする
*/
"enableSyncBetweenDevicesForPalettes": string;
/**
* メインで使用するパレット
*/
"paletteForMain": string;
/**
* リアクションで使用するパレット
*/
"paletteForReaction": string;
};
"_settings": {
/**
* ドライブの管理と設定、使用量の確認、ファイルをアップロードする際の設定を行えます。
*/
"driveBanner": string;
/**
* プラグインを利用するとクライアントの機能を拡張することができます。プラグインのインストール、個別の設定と管理が行えます。
*/
"pluginBanner": string;
/**
* サーバーからの受信する通知の種類と範囲や、プッシュ通知の設定が行えます。
*/
"notificationsBanner": string;
/**
* API
*/
"api": string;
/**
* Webhook
*/
"webhook": string;
/**
* サービス連携
*/
"serviceConnection": string;
/**
* 外部のアプリ・サービスと連携するためのアクセストークンやWebhookの管理と設定が行えます。
*/
"serviceConnectionBanner": string;
/**
* アカウントのデータ
*/
"accountData": string;
/**
* アカウントデータのアーカイブをエクスポート/インポートして管理できます。
*/
"accountDataBanner": string;
/**
* 非表示にするコンテンツの設定や、特定のユーザーからのアクションを制限する設定と管理を行えます。
*/
"muteAndBlockBanner": string;
/**
* クライアントの視覚や動作に関するパーソナライズを行い、より最適に使用できるように設定できます。
*/
"accessibilityBanner": string;
/**
* コンテンツの公開範囲、見つけやすさ、フォローの承認制などアカウントのプライバシーに関する設定を行えます。
*/
"privacyBanner": string;
/**
* パスワード、ログイン方法、認証アプリ、パスキーなどアカウントのセキュリティに関する設定を行えます。
*/
"securityBanner": string;
/**
* 好みに応じた、クライアントの全体的な動作の設定が行えます。
*/
"preferencesBanner": string;
/**
* 好みに応じた、クライアントの見た目・表示方法に関する設定が行えます。
*/
"appearanceBanner": string;
/**
* クライアントで再生するサウンドの設定が行えます。
*/
"soundsBanner": string;
/**
* タイムラインとノート
*/
"timelineAndNote": string;
/**
* 全てのテキスト要素を選択可能にする
*/
"makeEveryTextElementsSelectable": string;
/**
* 有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。
*/
"makeEveryTextElementsSelectable_description": string;
};
"_preferencesProfile": {
/**
* プロファイル名
@@ -5395,6 +5539,10 @@ export interface Locale extends ILocale {
* リモートサーバーに連合されたノートには効果が及ばない場合があります。
*/
"mayNotEffectForFederatedNotes": string;
/**
* これらの制限は簡易的なものです。リモートサーバーでの閲覧やモデレーション時など、一部のシチュエーションでは適用されない場合があります。
*/
"mayNotEffectSomeSituations": string;
/**
* 指定した時間を経過しているノート
*/
@@ -7742,6 +7890,10 @@ export interface Locale extends ILocale {
* 標準のテーマ
*/
"builtinThemes": string;
/**
* サーバーのテーマ
*/
"instanceTheme": string;
/**
* そのテーマは既にインストールされています
*/
@@ -9645,6 +9797,10 @@ export interface Locale extends ILocale {
* ログイン
*/
"login": string;
/**
* アクセストークンの作成
*/
"createToken": string;
/**
* 通知のテスト
*/
@@ -9750,6 +9906,10 @@ export interface Locale extends ILocale {
* 幅を自動調整
*/
"flexible": string;
/**
* プロファイル情報のデバイス間同期を有効にする
*/
"enableSyncBetweenDevicesForProfiles": string;
"_columns": {
/**
* メイン
@@ -10203,20 +10363,12 @@ export interface Locale extends ILocale {
* このプラグインをインストールしますか?
*/
"title": string;
/**
* プラグイン情報
*/
"metaTitle": string;
};
"_theme": {
/**
* このテーマをインストールしますか?
*/
"title": string;
/**
* テーマ情報
*/
"metaTitle": string;
};
"_meta": {
/**

View File

@@ -606,7 +606,7 @@ scratchpad: "ScratchPad"
scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScript. È possibile scrivere, eseguire e confermare i risultati dell'interazione del codice con Misskey."
uiInspector: "UI Inspector"
uiInspectorDescription: "Puoi visualizzare un elenco di elementi UI presenti in memoria. I componenti dell'interfaccia utente vengono generati dalle funzioni Ui:C:."
output: "Uscita"
output: "Output"
script: "Script"
disablePagesScript: "Disabilita AiScript nelle pagine"
updateRemoteUser: "Aggiorna dati dal profilo remoto"
@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} ha Notato a riguardo di \"{word}\""
makeActive: "Attiva"
display: "Visualizza"
copy: "Copia"
copiedToClipboard: "Copiato negli appunti"
metrics: "Statistiche"
overview: "Anteprima"
logs: "Log"
@@ -766,7 +767,7 @@ driveUsage: "Utilizzazione del Drive"
noCrawle: "Rifiuta l'indicizzazione dai robot."
noCrawleDescription: "Richiedi che i motori di ricerca non indicizzino la tua pagina di profilo, le tue note, pagine, ecc."
lockedAccountInfo: "A meno che non imposti la visibilità delle tue note su \"Solo ai follower\", le tue note sono visibili da tutti, anche se hai configurato l'account per confermare manualmente le richieste di follow."
alwaysMarkSensitive: "Segnare gli allegati come espliciti come opzione predefinita"
alwaysMarkSensitive: "Segnare automaticamente come espliciti gli allegati"
loadRawImages: "Visualizza le intere immagini allegate invece delle miniature."
disableShowingAnimatedImages: "Disabilita le immagini animate"
highlightSensitiveMedia: "Evidenzia i media espliciti"
@@ -973,7 +974,7 @@ check: "Verifica"
driveCapOverrideLabel: "Modificare la capienza del Drive per questo profilo"
driveCapOverrideCaption: "Se viene specificato meno di 0, viene annullato."
requireAdminForView: "Per visualizzarli, è necessario aver effettuato l'accesso con un profilo amministratore."
isSystemAccount: "Questi profili vengono creati e gestiti automaticamente dal sistema"
isSystemAccount: "Si tratta di un profilo creato e gestito automaticamente dal sistema."
typeToConfirm: "Digita {x} per continuare"
deleteAccount: "Eliminazione profilo"
document: "Documentazione"
@@ -1090,7 +1091,7 @@ notesSearchNotAvailable: "Non è possibile cercare tra le Note."
license: "Licenza"
unfavoriteConfirm: "Vuoi davvero rimuovere la preferenza?"
myClips: "Le mie Clip"
drivecleaner: "Drive cleaner"
drivecleaner: "Pulizia del Drive"
retryAllQueuesNow: "Ritenta di consumare tutte le code"
retryAllQueuesConfirmTitle: "Vuoi ritentare adesso?"
retryAllQueuesConfirmText: "Potrebbe sovraccaricare il server temporaneamente."
@@ -1315,6 +1316,63 @@ markAsSensitiveConfirm: "Vuoi davvero indicare questo contenuto multimediale com
unmarkAsSensitiveConfirm: "Vuoi davvero indicare come non esplicito il contenuto multimediale?"
preferences: "Preferenze"
accessibility: "Accessibilità"
preferencesProfile: "Profilo preferenze"
copyPreferenceId: "Copia ID preferenze"
resetToDefaultValue: "Ripristina a predefinito"
overrideByAccount: "Sovrascrivere col profilo"
untitled: "Senza titolo"
noName: "Senza nome"
skip: "Salta"
restore: "Ripristina"
syncBetweenDevices: "Sincronizzazione tra i dispositivi"
preferenceSyncConflictTitle: "Sul server esiste già il valore impostato"
preferenceSyncConflictText: "Le impostazione sincronizzata salverà il valore sul server. Però, bada che esiste già un valore sul server. Quale vorresti sovrascrivere?"
preferenceSyncConflictChoiceServer: "Valore del server"
preferenceSyncConflictChoiceDevice: "Valore del dispositivo"
preferenceSyncConflictChoiceCancel: "Annulla la sincronizzazione"
paste: "Incolla"
emojiPalette: "Tavolozza emoji"
postForm: "Finestra di pubblicazione"
textCount: "Il numero di caratteri"
information: "Informazioni"
_emojiPalette:
palettes: "Tavolozza"
enableSyncBetweenDevicesForPalettes: "Attiva la sincronizzazione tra dispositivi"
paletteForMain: "Tavolozza principale"
paletteForReaction: "Tavolozza per reazioni"
_settings:
driveBanner: "Permette di gestire e configurare il Drive, controllare il consumo di spazio e configurare il caricamento dei file."
pluginBanner: "Consentono di migliorare le funzionalità. Le estensioni si possono configurare e gestire singolarmente."
notificationsBanner: "Puoi impostare il tipo di notifiche da ricevere dal server e anche le notifiche push."
api: "API"
webhook: "Webhook"
serviceConnection: "Integrazione servizi"
serviceConnectionBanner: "Puoi gestire i codici di accesso e i Webhook per collegare App o servizi esterni."
accountData: "Dati del profilo"
accountDataBanner: "Puoi gestire i dati del tuo profilo, esportando e importando."
muteAndBlockBanner: "Puoi configurare la visibiltà dei contenuti e limitare le attività provenienti da profili specifici."
accessibilityBanner: "Puoi personalizzare e migliorare la lettura sul tuo dispositivo in modo che sia più chiaro e reattivo."
privacyBanner: "Puoi configurare la privacy del tuo profilo, come la visibilità delle Note, la visibilità del profilo nelle ricerche e l'approvazione delle relazioni tra profili."
securityBanner: "Puoi gestire la sicurezza del tuo account, la password, i modi di accesso, la generazione di codici OTP per accesso multi fattore (MFA/2FA) e la passkey."
preferencesBanner: "Puoi personalizzare il comportamento del tuo dispositivo."
appearanceBanner: "Puoi personalizzare l'aspetto nel dispositivo, in base alle tue preferenze."
soundsBanner: "Puoi personalizzare i suoni emessi dagli eventi sul tuo dispositivo."
timelineAndNote: "Note e Timeline"
makeEveryTextElementsSelectable: "Imposta ogni elemento come selezionabile"
makeEveryTextElementsSelectable_description: "Potrebbe ridurre l'usabilità in alcune situazioni."
_preferencesProfile:
profileName: "Nome del profilo"
profileNameDescription: "Impostare il nome che indentifica questo dispositivo."
profileNameDescription2: "Es: \"PC principale\" o \"Cellulare\""
_preferencesBackup:
autoBackup: "Backup automatico"
restoreFromBackup: "Ripristinare da backup"
noBackupsFoundTitle: "Nessun backup trovato"
noBackupsFoundDescription: "Impossibile trovare un backup creato automaticamente. Se se hai salvato il file di backup manualmente, puoi importarlo e ripristinarlo."
selectBackupToRestore: "Seleziona un backup da ripristinare"
youNeedToNameYourProfileToEnableAutoBackup: "Per abilitare i backup automatici, è necessario indicare il nome del profilo."
autoPreferencesBackupIsNotEnabledForThisDevice: "Su questo dispositivo non è stato attivato il backup automatico delle preferenze."
backupFound: "Esiste il Backup delle preferenze"
_accountSettings:
requireSigninToViewContents: "Per vedere il contenuto, è necessaria l'iscrizione"
requireSigninToViewContentsDescription1: "Richiedere l'iscrizione per visualizzare tutte le Note e gli altri contenuti che hai creato. Probabilmente l'effetto è impedire la raccolta di informazioni da parte dei bot crawler."
@@ -1325,6 +1383,7 @@ _accountSettings:
makeNotesHiddenBefore: "Nascondi le Note pubblicate in precedenza"
makeNotesHiddenBeforeDescription: "Mentre questa funzione è abilitata, le Note antecedenti al momento impostato, saranno visibili soltanto a te (private). Disabilitandola nuovamente, verrà ripristinata anche la visibilità pubblica della Nota."
mayNotEffectForFederatedNotes: "Le Note già federate su server remoti potrebbero non essere modificate."
mayNotEffectSomeSituations: "Queste restrizioni sono semplificate. In alcuni casi, potrebbero anche non avvenire. Ad esempio visionando un server remoto o durante la moderazione."
notesHavePassedSpecifiedPeriod: "Note antecedenti al periodo specificato"
notesOlderThanSpecifiedDateAndTime: "Note antecedenti al momento specificato"
_abuseUserReport:
@@ -1973,6 +2032,7 @@ _theme:
installed: "{name} è installato"
installedThemes: "Temi installati"
builtinThemes: "Temi integrati"
instanceTheme: "Tema dell'istanza"
alreadyInstalled: "Questo tema è già installato"
invalid: "Il formato tema non è valido"
make: "Crea un tema"
@@ -2464,6 +2524,7 @@ _notification:
achievementEarned: "Risultato raggiunto"
exportCompleted: "Esportazione completata"
login: "Accessi"
createToken: "Creare un token di accesso"
test: "Notifiche di test"
app: "Notifiche da applicazioni"
_actions:
@@ -2491,6 +2552,7 @@ _deck:
useSimpleUiForNonRootPages: "Visualizza sotto pagine con interfaccia web semplice"
usedAsMinWidthWhenFlexible: "Se \"larghezza flessibile\" è abilitato, questa diventa la larghezza minima"
flexible: "Larghezza flessibile"
enableSyncBetweenDevicesForProfiles: "Abilita la sincronizzazione delle informazioni profilo tra dispositivi"
_columns:
main: "Principale"
widgets: "Riquadri"
@@ -2509,8 +2571,8 @@ _disabledTimeline:
title: "Timeline disabilitata"
description: "Il ruolo in cui sei non ti permette di leggere questa timeline"
_drivecleaner:
orderBySizeDesc: "Dal più grande al più piccolo"
orderByCreatedAtAsc: "Dal più vecchio al più recente"
orderBySizeDesc: "Dal file più grosso al più piccolo"
orderByCreatedAtAsc: "Dal file più vecchio al più recente"
_webhookSettings:
createWebhook: "Creazione Webhook"
modifyWebhook: "Modifica Webhook"
@@ -2612,10 +2674,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "Prima di installare, assicurati che la fonte sia affidabile."
_plugin:
title: "Vuoi davvero installare questo componente aggiuntivo?"
metaTitle: "Informazioni sul componente aggiuntivo"
_theme:
title: "Vuoi davvero installare questa variazione grafica?"
metaTitle: "Informazioni sulla variazione grafica"
_meta:
base: "Combinazione base di colori"
_vendorInfo:

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name}が「{word}」について何かを言いまし
makeActive: "アクティブにする"
display: "表示"
copy: "コピー"
copiedToClipboard: "クリップボードにコピーされました"
metrics: "メトリクス"
overview: "概要"
logs: "ログ"
@@ -1323,6 +1324,44 @@ untitled: "無題"
noName: "名前はありません"
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: "外部のアプリ・サービスと連携するためのアクセストークンやWebhookの管理と設定が行えます。"
accountData: "アカウントのデータ"
accountDataBanner: "アカウントデータのアーカイブをエクスポート/インポートして管理できます。"
muteAndBlockBanner: "非表示にするコンテンツの設定や、特定のユーザーからのアクションを制限する設定と管理を行えます。"
accessibilityBanner: "クライアントの視覚や動作に関するパーソナライズを行い、より最適に使用できるように設定できます。"
privacyBanner: "コンテンツの公開範囲、見つけやすさ、フォローの承認制などアカウントのプライバシーに関する設定を行えます。"
securityBanner: "パスワード、ログイン方法、認証アプリ、パスキーなどアカウントのセキュリティに関する設定を行えます。"
preferencesBanner: "好みに応じた、クライアントの全体的な動作の設定が行えます。"
appearanceBanner: "好みに応じた、クライアントの見た目・表示方法に関する設定が行えます。"
soundsBanner: "クライアントで再生するサウンドの設定が行えます。"
timelineAndNote: "タイムラインとノート"
makeEveryTextElementsSelectable: "全てのテキスト要素を選択可能にする"
makeEveryTextElementsSelectable_description: "有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。"
_preferencesProfile:
profileName: "プロファイル名"
@@ -1349,6 +1388,7 @@ _accountSettings:
makeNotesHiddenBefore: "過去のノートを非公開化する"
makeNotesHiddenBeforeDescription: "この機能が有効になっている間、設定された日時より過去、または設定された時間を経過しているノートが自分のみ表示可能(非公開化)になります。無効に戻すと、ノートの公開状態も元に戻ります。"
mayNotEffectForFederatedNotes: "リモートサーバーに連合されたノートには効果が及ばない場合があります。"
mayNotEffectSomeSituations: "これらの制限は簡易的なものです。リモートサーバーでの閲覧やモデレーション時など、一部のシチュエーションでは適用されない場合があります。"
notesHavePassedSpecifiedPeriod: "指定した時間を経過しているノート"
notesOlderThanSpecifiedDateAndTime: "指定した日時より前のノート"
@@ -2030,6 +2070,7 @@ _theme:
installed: "{name}をインストールしました"
installedThemes: "インストールされたテーマ"
builtinThemes: "標準のテーマ"
instanceTheme: "サーバーのテーマ"
alreadyInstalled: "そのテーマは既にインストールされています"
invalid: "テーマの形式が間違っています"
make: "テーマを作る"
@@ -2548,6 +2589,7 @@ _notification:
achievementEarned: "実績の獲得"
exportCompleted: "エクスポートが完了した"
login: "ログイン"
createToken: "アクセストークンの作成"
test: "通知のテスト"
app: "連携アプリからの通知"
@@ -2577,6 +2619,7 @@ _deck:
useSimpleUiForNonRootPages: "非ルートページは簡易UIで表示"
usedAsMinWidthWhenFlexible: "「幅を自動調整」が有効の場合、これが幅の最小値となります"
flexible: "幅を自動調整"
enableSyncBetweenDevicesForProfiles: "プロファイル情報のデバイス間同期を有効にする"
_columns:
main: "メイン"
@@ -2707,10 +2750,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "配布元が信頼できるかを確認した上でインストールしてください。"
_plugin:
title: "このプラグインをインストールしますか?"
metaTitle: "プラグイン情報"
_theme:
title: "このテーマをインストールしますか?"
metaTitle: "テーマ情報"
_meta:
base: "基本のカラースキーム"
_vendorInfo:

View File

@@ -1311,6 +1311,10 @@ federationSpecified: "このサーバーはホワイトリスト連合で運用
federationDisabled: "このサーバーは連合が無効化されてるで。他のサーバーのユーザーとやり取りすることはできひんで。"
confirmOnReact: "ツッコむときに確認とる"
reactAreYouSure: "\" {emoji} \" でツッコむ?"
postForm: "投稿フォーム"
information: "情報"
_settings:
webhook: "Webhook"
_accountSettings:
requireSigninToViewContents: "ログインしてもらってからコンテンツ見てもらう"
requireSigninToViewContentsDescription1: "あなたが作成した全部のノートとかのコンテンツを見れるようにするのにログインがいるようにするで。クローラーにいろいろ収集されるんを防げるかもしれん。"
@@ -2607,10 +2611,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "配ってるとこが信頼できるか確認した上でインストールしてな。"
_plugin:
title: "このプラグイン、インストールする?"
metaTitle: "プラグイン情報"
_theme:
title: "このテーマインストールする?"
metaTitle: "テーマ情報"
_meta:
base: ""
_vendorInfo:

View File

@@ -655,6 +655,7 @@ replies: "답하기"
renotes: "리노트"
attach: "옇기"
surrender: "아이예"
information: "정보"
_delivery:
stop: "고만 보내예"
_type:

View File

@@ -1309,6 +1309,50 @@ availableRoles: "사용 가능한 역할"
acknowledgeNotesAndEnable: "활성화 하기 전에 주의 사항을 확인했습니다."
federationSpecified: "이 서버는 화이트 리스트 제도로 운영 중 입니다. 정해진 리모트 서버가 아닌 경우 연합되지 않습니다."
federationDisabled: "이 서버는 연합을 하지 않고 있습니다. 리모트 서버 유저와 통신을 할 수 없습니다."
confirmOnReact: "리액션할 때 확인"
reactAreYouSure: "\" {emoji} \"로 리액션하시겠습니까?"
markAsSensitiveConfirm: "이 미디어를 민감한 미디어로 설정하시겠습니까?"
unmarkAsSensitiveConfirm: "이 미디어의 민감한 미디어 지정을 해제하시겠습니까?"
preferences: "환경설정"
accessibility: "접근성"
preferencesProfile: "설정 프로필"
copyPreferenceId: "설정한 ID를 복사"
resetToDefaultValue: "기본값으로 되돌리기"
overrideByAccount: "계정으로 덮어쓰기"
untitled: "제목 없음"
noName: "이름이 없습니다."
skip: "건너뛰기"
restore: "복원"
syncBetweenDevices: "장치간 동기화"
preferenceSyncConflictTitle: "서버에 설정값이 존재합니다."
preferenceSyncConflictChoiceServer: "서버 설정값"
preferenceSyncConflictChoiceDevice: "장치 설정값"
paste: "붙여넣기"
emojiPalette: "이모지 팔레트"
postForm: "글 입력란"
information: "정보"
_emojiPalette:
palettes: "팔레트"
paletteForMain: "메인으로 사용할 팔레트"
paletteForReaction: "리액션으로 사용할 팔레트"
_settings:
api: "API"
webhook: "Webhook"
serviceConnection: "서비스 연동"
accountData: "계정 데이터"
_preferencesProfile:
profileName: "프로필 이름"
profileNameDescription: "이 디바이스를 식별할 이름을 설정해 주세요."
profileNameDescription2: "예: '메인PC', '스마트폰' 등"
_preferencesBackup:
autoBackup: "자동 백업"
restoreFromBackup: "백업으로 복구"
noBackupsFoundTitle: "백업을 찾을 수 없습니다"
noBackupsFoundDescription: "자동으로 생성된 백업은 찾을 수 없었지만, 수동으로 백업 파일을 저장한 경우 해당 파일을 가져와 복원할 수 있습니다."
selectBackupToRestore: "복원할 백업을 선택하세요"
youNeedToNameYourProfileToEnableAutoBackup: "자동 백업을 활성화하려면 프로필 이름을 설정해야 합니다."
autoPreferencesBackupIsNotEnabledForThisDevice: "이 장치에서 설정 자동 백업이 활성화되어 있지 않습니다."
backupFound: "설정 백업이 발견되었습니다"
_accountSettings:
requireSigninToViewContents: "콘텐츠 열람을 위해 로그인을 필수로 설정하기"
requireSigninToViewContentsDescription1: "자신이 작성한 모든 노트 등의 콘텐츠를 보기 위해 로그인을 필수로 설정합니다. 크롤러가 정보 수집하는 것을 방지하는 효과를 기대할 수 있습니다."
@@ -1967,6 +2011,7 @@ _theme:
installed: "{name} 테마가 설치되었습니다"
installedThemes: "설치된 테마"
builtinThemes: "표준 테마"
instanceTheme: "서버 테마"
alreadyInstalled: "이미 설치된 테마입니다"
invalid: "테마 형식이 올바르지 않습니다"
make: "테마 만들기"
@@ -2440,6 +2485,8 @@ _notification:
flushNotification: "알림 이력을 초기화"
exportOfXCompleted: "{x} 추출에 성공했습니다."
login: "로그인 알림이 있습니다"
createToken: "액세스 토큰이 생성되었습니다"
createTokenDescription: "만약 기억이 나지 않는다면 '{text}'를 통해 액세스 토큰을 삭제해 주세요."
_types:
all: "전부"
note: "사용자의 새 글"
@@ -2590,6 +2637,7 @@ _moderationLogTypes:
deletePage: "페이지를 삭제"
deleteFlash: "Play를 삭제"
deleteGalleryPost: "갤러리 포스트를 삭제"
updateProxyAccountDescription: "프록시 계정의 설명 업데이트"
_fileViewer:
title: "파일 상세"
type: "파일 유형"
@@ -2603,10 +2651,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "제공자를 신뢰할 수 있는 경우에만 설치하십시오."
_plugin:
title: "이 플러그인을 설치하시겠습니까?"
metaTitle: "플러그인 정보"
_theme:
title: "이 테마를 설치하시겠습니까?"
metaTitle: "테마 정보"
_meta:
base: "기본 컬러 스키마"
_vendorInfo:
@@ -2840,8 +2886,21 @@ _captcha:
text: "알 수 없는 에러가 발생했습니다."
_bootErrors:
title: "로딩이 실패함"
serverError: "잠시 기다렸다가 다시 로드해도 여전히 문제가 해결되지 않으면 아래 Error ID와 함께 서버 관리자에게 연락해 주세요."
solution: "다음과 같은 방법으로 해결할 수 있습니다."
solution1: "브라우저 및 OS를 최신 버전으로 업데이트하기"
solution2: "광고 차단 비활성화하기"
solution3: "브라우저 캐시 지우기"
solution4: "(Tor Browser) dom.webaudio.enabled를 true로 설정하세요"
otherOption: "기타 옵션"
otherOption1: "클라이언트 설정 및 캐시 삭제"
otherOption2: "간편 클라이언트 실행"
otherOption3: "복구 툴 실행"
_search:
searchScopeAll: "전체"
searchScopeLocal: "로컬"
searchScopeServer: "서버 지정"
searchScopeUser: "사용자 지정"
pleaseEnterServerHost: "서버의 호스트를 입력해 주세요."
pleaseSelectUser: "유저를 선택해주세요"
serverHostPlaceholder: "예: misskey.example.com"

View File

@@ -394,6 +394,7 @@ searchByGoogle: "ຄົ້ນຫາ"
file: "ໄຟລ໌"
replies: "ຕອບ​ກັບ"
renotes: "Renote"
information: "ກ່ຽວກັບ"
_delivery:
stop: "ໂຈະ"
_type:

View File

@@ -462,6 +462,7 @@ loggedInAsBot: "Momenteel als bot ingelogd"
icon: "Avatar"
replies: "Antwoord"
renotes: "Herdelen"
information: "Over"
_delivery:
stop: "Opgeschort"
_type:

View File

@@ -463,6 +463,7 @@ icon: "Avatar"
replies: "Svar"
renotes: "Renote"
surrender: "Avbryt"
information: "Informasjon"
_delivery:
stop: "Suspendert"
_initialAccountSetting:

View File

@@ -1044,6 +1044,8 @@ flip: "Odwróć"
lastNDays: "W ciągu ostatnich {n} dni"
surrender: "Odrzuć"
gameRetry: "Spróbuj ponownie"
postForm: "Formularz tworzenia wpisu"
information: "Informacje"
_delivery:
stop: "Zawieszono"
_type:

View File

@@ -1301,6 +1301,10 @@ lockdown: "Lockdown"
pleaseSelectAccount: "Selecione uma conta"
availableRoles: "Cargos disponíveis"
acknowledgeNotesAndEnable: "Ative após compreender as precauções."
postForm: "Campo de postagem"
information: "Informações"
_settings:
webhook: "Webhook"
_accountSettings:
requireSigninToViewContents: "Exigir cadastro para ver o conteúdo"
requireSigninToViewContentsDescription1: "Exigir cadastro para ver todas as notas e outro conteúdo que você criou. Isso previne 'crawlers' de coletar os seus dados."
@@ -2595,10 +2599,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "Tenha certeza de que o distribuidor desse recurso é confiável antes da instalação."
_plugin:
title: "Deseja instalar esse plugin?"
metaTitle: "Informações do plugin"
_theme:
title: "Deseja instalar esse tema?"
metaTitle: "Informações do tema"
_meta:
base: "Paleta de cores base"
_vendorInfo:

View File

@@ -646,6 +646,7 @@ show: "Arată"
icon: "Avatar"
replies: "Răspunde"
renotes: "Re-notează"
information: "Despre"
_delivery:
stop: "Suspendat"
_type:

View File

@@ -1181,6 +1181,10 @@ keepOriginalFilenameDescription: "Если вы выключите данную
alwaysConfirmFollow: "Всегда подтверждать подписку"
inquiry: "Связаться"
messageToFollower: "Сообщение подписчикам"
postForm: "Форма отправки"
information: "Описание"
_settings:
webhook: "Вебхук"
_delivery:
stop: "Заморожено"
_type:

View File

@@ -917,6 +917,8 @@ renotes: "Preposlať"
sourceCode: "Zdrojový kód"
flip: "Preklopiť"
lastNDays: "Posledných {n} dní"
postForm: "Napísať poznámku"
information: "Informácie"
_delivery:
stop: "Zmrazené"
_type:

View File

@@ -562,6 +562,7 @@ inquiry: "Kontakt"
tryAgain: "Försök igen senare"
signinWithPasskey: "Logga in med nyckel"
unknownWebAuthnKey: "Okänd nyckel"
information: "Om"
_delivery:
stop: "Suspenderad"
_type:

View File

@@ -1292,6 +1292,10 @@ prohibitedWordsForNameOfUser: "คำนี้ไม่สามารถใช
prohibitedWordsForNameOfUserDescription: "หากมีสตริงใดๆ ในรายการนี้ปรากฏอยู่ในชื่อของผู้ใช้ ชื่อนั้นจะถูกปฏิเสธ ผู้ใช้ที่มีสิทธิ์แต่ผู้ดูแลระบบนั้นจะไม่ได้รับผลกระทบใดๆจากข้อจำกัดนี้ค่ะ"
yourNameContainsProhibitedWords: "ชื่อของคุณนั้นมีคำที่ต้องห้าม"
yourNameContainsProhibitedWordsDescription: "ถ้าหากคุณต้องการใช้ชื่อนี้ กรุณาติดต่อผู้ดูแลระบบของเซิร์ฟเวอร์นะค่ะ"
postForm: "แบบฟอร์มการโพสต์"
information: "เกี่ยวกับ"
_settings:
webhook: "Webhook"
_abuseUserReport:
forward: "ส่ง​ต่อ"
forwardDescription: "ส่งรายงานไปยังเซิร์ฟเวอร์ระยะไกลโดยใช้บัญชีระบบที่ไม่ระบุตัวตน"
@@ -2569,10 +2573,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "โปรดตรวจสอบให้แน่ใจว่าแหล่งแจกหน่ายมีความน่าเชื่อถือก่อนทำการติดตั้ง"
_plugin:
title: "ต้องการติดตั้งปลั๊กอินนี้ใช่ไหม?"
metaTitle: "ข้อมูลส่วนเสริม"
_theme:
title: "ต้องการติดตั้งธีมนี้ใช่ไหม?"
metaTitle: "ข้อมูลธีม"
_meta:
base: "โทนสีพื้นฐาน"
_vendorInfo:

View File

@@ -909,6 +909,8 @@ renotes: "Поширити"
sourceCode: "Вихідний код"
flip: "Перевернути"
lastNDays: "Останні {n} днів"
postForm: "Створення нотатки"
information: "Інформація"
_delivery:
stop: "Призупинено"
_type:

View File

@@ -841,6 +841,7 @@ icon: "Avatar"
replies: "Javob berish"
renotes: "Qayta qayd etish"
flip: "Teskari"
information: "Haqida"
_delivery:
stop: "To'xtatilgan"
_type:

View File

@@ -1119,6 +1119,8 @@ pullDownToRefresh: "Kéo xuống để làm mới"
cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải có chú thích."
lastNDays: "{n} ngày trước"
surrender: "Từ chối"
postForm: "Mẫu đăng"
information: "Giới thiệu"
_delivery:
stop: "Đã vô hiệu hóa"
_type:

View File

@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} 说了关于「{word}」的什么"
makeActive: "启用"
display: "显示"
copy: "复制"
copiedToClipboard: "已复制到剪贴板"
metrics: "指标"
overview: "概览"
logs: "日志"
@@ -746,7 +747,7 @@ confirmToUnclipAlreadyClippedNote: "本帖已包含在便签 \"{name}\" 里。
public: "公开"
private: "私密"
i18nInfo: "Misskey 已经被志愿者们翻译成了各种语言。如果你也有兴趣,可以通过 {link} 帮助翻译。"
manageAccessTokens: "管理 Access Tokens"
manageAccessTokens: "管理访问令牌"
accountInfo: "账户信息"
notesCount: "帖子数量"
repliesCount: "回复数量"
@@ -1315,6 +1316,63 @@ markAsSensitiveConfirm: "要将此媒体标记为敏感吗?"
unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?"
preferences: "设置"
accessibility: "辅助功能"
preferencesProfile: "设置的配置"
copyPreferenceId: "复制设置 ID"
resetToDefaultValue: "重置为默认值"
overrideByAccount: "用账户覆盖"
untitled: "未命名"
noName: "没有名字"
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: "可在此管理用于连接外部应用或服务的访问令牌及 Webhook。"
accountData: "账户数据"
accountDataBanner: "可在此导入或导出帐户数据的存档。"
muteAndBlockBanner: "可在此设置隐藏内容,或限制指定用户能进行的操作。"
accessibilityBanner: "可在此设置客户端的显示及动态效果等辅助设置。"
privacyBanner: "可在此设置如内容可见性、可发现性、批准关注请求等账户隐私设置。"
securityBanner: "可在此设置如密码、登入方式、验证器、Passkey 等账户安全性设置。"
preferencesBanner: "可在此设置客户端的整体运作行为。"
appearanceBanner: "可在此设置客户端的外观及显示方式。"
soundsBanner: "可在此设置客户端播放的声音。"
timelineAndNote: "时间线和帖子"
makeEveryTextElementsSelectable: "使所有的文字均可选择"
makeEveryTextElementsSelectable_description: "若开启,在某些情况下可能降低用户体验。"
_preferencesProfile:
profileName: "配置名"
profileNameDescription: "请指定用于识别此设备的名称"
profileNameDescription2: "如「PC」、「手机」等"
_preferencesBackup:
autoBackup: "自动备份"
restoreFromBackup: "从备份恢复"
noBackupsFoundTitle: "没有找到备份"
noBackupsFoundDescription: "没有找到自动备份。若有手动保存备份文件,可将其导入来恢复。"
selectBackupToRestore: "请选择要恢复的备份"
youNeedToNameYourProfileToEnableAutoBackup: "需指定配置名以开启自动备份。"
autoPreferencesBackupIsNotEnabledForThisDevice: "此设备未开启自动备份"
backupFound: "已找到备份"
_accountSettings:
requireSigninToViewContents: "需要登录才能显示内容"
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
@@ -1325,6 +1383,7 @@ _accountSettings:
makeNotesHiddenBefore: "将过去的帖子设为私密"
makeNotesHiddenBeforeDescription: "开启此设定时,超过设定的时间或日期后,帖子将变为仅自己可见。关闭后帖子的公开状态将恢复成原本的设定。"
mayNotEffectForFederatedNotes: "与远程服务器联合的帖子在远端可能会没有效果。"
mayNotEffectSomeSituations: "此限制功能非常简单,在与远程服务器联合等情形时可能不适用。"
notesHavePassedSpecifiedPeriod: "超过指定时间的帖子"
notesOlderThanSpecifiedDateAndTime: "指定日期前的帖子"
_abuseUserReport:
@@ -1973,6 +2032,7 @@ _theme:
installed: "{name} 已安装"
installedThemes: "已安装的主题"
builtinThemes: "标准主题"
instanceTheme: "服务器主题"
alreadyInstalled: "此主题已经安装"
invalid: "主题格式错误"
make: "制作主题"
@@ -2464,6 +2524,7 @@ _notification:
achievementEarned: "取得的成就"
exportCompleted: "已完成导出"
login: "登录"
createToken: "创建访问令牌"
test: "测试通知"
app: "关联应用的通知"
_actions:
@@ -2491,6 +2552,7 @@ _deck:
useSimpleUiForNonRootPages: "用简易UI表示非根页面"
usedAsMinWidthWhenFlexible: "「自适应宽度」被启用的时候,这就是最小的宽度"
flexible: "自适应宽度"
enableSyncBetweenDevicesForProfiles: "启用个人资料信息跨设备同步"
_columns:
main: "主列"
widgets: "小工具"
@@ -2598,7 +2660,7 @@ _moderationLogTypes:
deletePage: "删除了页面"
deleteFlash: "删除了 Play"
deleteGalleryPost: "删除了图库稿件"
updateProxyAccountDescription: "更新代理账户的说明"
updateProxyAccountDescription: "更新代理账户的简介"
_fileViewer:
title: "文件信息"
type: "文件类型"
@@ -2612,10 +2674,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "请在安装前确保来源可靠"
_plugin:
title: "要安装此插件吗?"
metaTitle: "插件信息"
_theme:
title: "要安装此主题吗?"
metaTitle: "主题信息"
_meta:
base: "基本配色方案"
_vendorInfo:

View File

@@ -103,7 +103,7 @@ serverIsDead: "伺服器沒有回應。請稍等片刻再試。"
youShouldUpgradeClient: "請重新載入以使用新版客戶端顯示此頁面。"
enterListName: "輸入清單名稱"
privacy: "隱私"
makeFollowManuallyApprove: "手動審核追隨請求"
makeFollowManuallyApprove: "追隨需要核准"
defaultNoteVisibility: "預設可見性"
follow: "追隨"
followRequest: "追隨請求"
@@ -459,13 +459,13 @@ moderationNoteDescription: "您可以編寫僅在審查員之間共用的註解
addModerationNote: "新增管理筆記"
moderationLogs: "管理日誌"
nUsersMentioned: "被 {n} 個人提及"
securityKeyAndPasskey: "安全金鑰、Passkey"
securityKeyAndPasskey: "安全金鑰、通行金鑰"
securityKey: "安全金鑰"
lastUsed: "上次使用"
lastUsedAt: "上次使用:{t}"
unregister: "註銷"
passwordLessLogin: "無密碼登入"
passwordLessLoginDescription: "不使用密碼,以安全金鑰或 Passkey 登入"
passwordLessLoginDescription: "不使用密碼,以安全金鑰或通行金鑰登入"
resetPassword: "重設密碼"
newPasswordIs: "新密碼為「{password}」"
reduceUiAnimation: "減少介面的動態視覺"
@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} 說了一些關於「{word}」的話"
makeActive: "啟用"
display: "檢視"
copy: "複製"
copiedToClipboard: "已複製到剪貼簿"
metrics: "指標"
overview: "概覽"
logs: "日誌"
@@ -765,7 +766,7 @@ driveFilesCount: "雲端硬碟檔案數量"
driveUsage: "雲端硬碟使用量"
noCrawle: "拒絕搜尋引擎索引"
noCrawleDescription: "要求網路搜尋引擎不要索引你的個人資料頁、貼文及頁面等。"
lockedAccountInfo: "即使你通過了追隨者請求,除非你將貼文的可見性設定為 「追隨者」,否則任何人都能看見你的貼文。"
lockedAccountInfo: "即使追隨需要核准,除非你將貼文的可見性設定為 「追隨者」,否則任何人都能看見你的貼文。"
alwaysMarkSensitive: "預設標記檔案為敏感內容"
loadRawImages: "以原始圖檔顯示附件圖檔的縮圖"
disableShowingAnimatedImages: "不播放動態圖檔"
@@ -1188,7 +1189,7 @@ forYou: "給您"
currentAnnouncements: "最新公告"
pastAnnouncements: "歷史公告"
youHaveUnreadAnnouncements: "有未讀的公告。"
useSecurityKey: "請按照瀏覽器或裝置上的說明來使用安全金鑰或 Passkey。"
useSecurityKey: "請按照瀏覽器或裝置上的說明來使用安全金鑰或通行金鑰。"
replies: "回覆"
renotes: "轉發"
loadReplies: "閱覽回覆"
@@ -1205,7 +1206,7 @@ showRenotes: "顯示其他人的轉發貼文"
edited: "已編輯"
notificationRecieveConfig: "接受通知的設定"
mutualFollow: "互相追隨"
followingOrFollower: "追隨中或追隨者"
followingOrFollower: "追隨中或追隨者"
fileAttachedOnly: "只顯示包含附件的貼文"
showRepliesToOthersInTimeline: "在時間軸上顯示給其他人的回覆"
hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆"
@@ -1291,10 +1292,10 @@ performance: "性能"
modified: "已變更"
discard: "取消"
thereAreNChanges: "有 {n} 處的變更"
signinWithPasskey: "使用密碼金鑰登入"
unknownWebAuthnKey: "未註冊的金鑰。"
passkeyVerificationFailed: "驗證金鑰失敗。"
passkeyVerificationSucceededButPasswordlessLoginDisabled: "雖然驗證金鑰成功,但是無密碼登入的方式是停用的。"
signinWithPasskey: "使用通行金鑰登入"
unknownWebAuthnKey: "未註冊的通行金鑰。"
passkeyVerificationFailed: "驗證通行金鑰失敗。"
passkeyVerificationSucceededButPasswordlessLoginDisabled: "雖然驗證通行金鑰成功,但是無密碼登入的方式是停用的。"
messageToFollower: "給追隨者的訊息"
target: "目標 "
testCaptchaWarning: "此功能用於 CAPTCHA 的測試。<strong>請勿在正式環境中使用。</strong>"
@@ -1315,6 +1316,63 @@ markAsSensitiveConfirm: "要將這個媒體設定為敏感嗎?"
unmarkAsSensitiveConfirm: "要解除這個媒體的敏感設定嗎?"
preferences: "環境設定"
accessibility: "輔助工具"
preferencesProfile: "設定檔案"
copyPreferenceId: "複製設定 ID"
resetToDefaultValue: "還原成預設值"
overrideByAccount: "覆寫帳號"
untitled: "無標題"
noName: "沒有名稱"
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: "您可以管理和設定存取權杖與 Webhooks以便與外部應用程式和服務整合。"
accountData: "帳戶資料"
accountDataBanner: "您可以管理帳戶資料的匯出 / 匯入。"
muteAndBlockBanner: "您可以設定和管理要隱藏的內容,並限制特定使用者的行動。"
accessibilityBanner: "可針對客戶端的視覺和行為進行個人化設定,以達到更佳的使用效果。"
privacyBanner: "您可以調整帳戶的隱私設定,例如內容的可見性、尋找內容的容易程度,以及追隨是否需要核准。"
securityBanner: "您可以設定與帳戶安全性相關的設定,例如密碼、登入方式、驗證應用程式和通行金鑰。"
preferencesBanner: "您可以根據喜好設定用戶端的整體行為。"
appearanceBanner: "您可以根據喜好設定與用戶端外觀和顯示方式相關的設定。"
soundsBanner: "您可以調整用戶端播放的聲音設定。"
timelineAndNote: "時間軸及貼文"
makeEveryTextElementsSelectable: "允許選取所有文字"
makeEveryTextElementsSelectable_description: "啟用此功能後,可能會在某些情境下降低可用性。"
_preferencesProfile:
profileName: "設定檔案名稱"
profileNameDescription: "設定一個名稱來識別此裝置。"
profileNameDescription2: "例如:「主要個人電腦」、「智慧型手機」等"
_preferencesBackup:
autoBackup: "自動備份"
restoreFromBackup: "從備份還原"
noBackupsFoundTitle: "找不到備份檔"
noBackupsFoundDescription: "沒有找到自動建立的備份,但如果您手動儲存了備份檔案,則可以匯入並還原。"
selectBackupToRestore: "選擇要還原的備份"
youNeedToNameYourProfileToEnableAutoBackup: "要啟用自動備份,必須設定檔案名稱。"
autoPreferencesBackupIsNotEnabledForThisDevice: "此裝置未啟用自動備份設定。"
backupFound: "找到設定的備份"
_accountSettings:
requireSigninToViewContents: "須登入以顯示內容"
requireSigninToViewContentsDescription1: "必須登入才會顯示您建立的貼文等內容。可望有效防止資訊被爬蟲蒐集。"
@@ -1325,6 +1383,7 @@ _accountSettings:
makeNotesHiddenBefore: "隱藏過去的貼文"
makeNotesHiddenBeforeDescription: "啟用此功能後,超過設定的日期和時間或超過設定時間的貼文將僅對自己顯示(私密化)。 如果您再次停用它,貼文的公開狀態也會恢復原狀。"
mayNotEffectForFederatedNotes: "聯邦發送至遠端伺服器的貼文可能會不受影響。"
mayNotEffectSomeSituations: "這些限制已經簡化。它們可能不適用於某些情況,例如在遠端伺服器上檢視或管理時。"
notesHavePassedSpecifiedPeriod: "早於指定時間的貼文"
notesOlderThanSpecifiedDateAndTime: "指定時間和日期之前的貼文"
_abuseUserReport:
@@ -1973,6 +2032,7 @@ _theme:
installed: "{name}已安裝"
installedThemes: "已經安裝的佈景主題"
builtinThemes: "標準佈景主題"
instanceTheme: "伺服器的主題"
alreadyInstalled: "已安裝此佈景主題"
invalid: "佈景主題格式錯誤"
make: "製作佈景主題"
@@ -2082,11 +2142,11 @@ _2fa:
setupCompleted: "設定完成"
step4: "從現在開始,任何登入操作都將要求您提供權杖。"
securityKeyNotSupported: "您的瀏覽器不支援安全金鑰。"
registerTOTPBeforeKey: "如要註冊安全金鑰或 Passkey,請先設定驗證應用程式。"
securityKeyInfo: "您可以設定使用支援 FIDO2 的硬體安全金鑰以及裝置上的生物辨識、PIN 碼和密碼等來登入。"
registerSecurityKey: "註冊安全金鑰或 Passkey"
registerTOTPBeforeKey: "如要註冊安全金鑰或通行金鑰,請先設定驗證應用程式。"
securityKeyInfo: "註冊 WebAuthn 衍生的金鑰,例如支援 FIDO2 的硬體安全金鑰、裝置生物識別、PIN 鎖和通行金鑰。"
registerSecurityKey: "註冊安全金鑰或通行金鑰"
securityKeyName: "輸入金鑰名稱"
tapSecurityKey: "按照瀏覽器的說明註冊安全金鑰或 Passkey。"
tapSecurityKey: "按照瀏覽器的說明註冊安全金鑰或通行金鑰。"
removeKey: "刪除安全金鑰"
removeKeyConfirm: "要刪除{name}嗎?"
whyTOTPOnlyRenew: "如果註冊了安全金鑰,則無法解除驗證應用程式的設定。"
@@ -2308,7 +2368,7 @@ _profile:
avatarDecorationMax: "最多可以設置 {max} 個裝飾。"
followedMessage: "被追隨時的訊息"
followedMessageDescription: "可以設定被追隨時顯示給對方的訊息。"
followedMessageDescriptionForLockedAccount: "如果追隨需要核的話,在允許追隨請求之後顯示。"
followedMessageDescriptionForLockedAccount: "如果追隨需要核的話,將在通過追隨請求之後顯示。"
_exportOrImport:
allNotes: "所有貼文"
favoritedNotes: "「我的最愛」貼文"
@@ -2428,7 +2488,7 @@ _notification:
youRenoted: "{name} 轉發了你的貼文"
youWereFollowed: "您有新的追隨者"
youReceivedFollowRequest: "您有新的追隨請求"
yourFollowRequestAccepted: "您的追隨請求已通過"
yourFollowRequestAccepted: "您的追隨請求已被核准"
pollEnded: "問卷調查已產生結果"
newNote: "新的貼文"
unreadAntennaNote: "天線 {name}"
@@ -2464,6 +2524,7 @@ _notification:
achievementEarned: "獲得成就"
exportCompleted: "已完成匯出。"
login: "登入"
createToken: "建立存取權杖"
test: "通知測試"
app: "應用程式通知"
_actions:
@@ -2491,6 +2552,7 @@ _deck:
useSimpleUiForNonRootPages: "用簡易介面顯示非根頁面"
usedAsMinWidthWhenFlexible: "如果啟用「自動調整寬度」,此為最小寬度"
flexible: "自動調整寬度"
enableSyncBetweenDevicesForProfiles: "啟用裝置與裝置之間的設定檔資料同步化"
_columns:
main: "主列"
widgets: "小工具"
@@ -2612,10 +2674,8 @@ _externalResourceInstaller:
checkVendorBeforeInstall: "安裝前請確認提供者是可信賴的。"
_plugin:
title: "要安裝此外掛嘛?"
metaTitle: "外掛資訊"
_theme:
title: "要安裝此佈景主題嗎?"
metaTitle: "佈景主題資訊"
_meta:
base: "基本配色方案"
_vendorInfo:

View File

@@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2025.3.2-alpha.4",
"version": "2025.3.2-beta.9",
"codename": "nasubi",
"repository": {
"type": "git",
@@ -24,6 +24,7 @@
"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
"build-storybook": "pnpm --filter frontend build-storybook",
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
"build-frontend-search-index": "pnpm --filter frontend build-search-index",
"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
"init": "pnpm migrate",
@@ -65,12 +66,12 @@
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "2.1.0",
"@types/node": "22.13.9",
"@types/node": "22.13.10",
"@typescript-eslint/eslint-plugin": "8.26.0",
"@typescript-eslint/parser": "8.26.0",
"cross-env": "7.0.3",
"cypress": "14.1.0",
"eslint": "9.21.0",
"eslint": "9.22.0",
"globals": "16.0.0",
"ncp": "2.0.0",
"pnpm": "10.6.1",

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,26 +67,23 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
"@aws-sdk/client-s3": "3.749.0",
"@aws-sdk/lib-storage": "3.749.0",
"@bull-board/api": "6.7.7",
"@bull-board/fastify": "6.7.7",
"@bull-board/ui": "6.7.7",
"@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",
@@ -94,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",
@@ -103,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",
@@ -125,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",
@@ -134,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",
@@ -166,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",
@@ -176,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": {
@@ -207,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",
@@ -226,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

@@ -16,7 +16,7 @@ import type { Config } from '@/config.js';
import { StatusError } from '@/misc/status-error.js';
import { bindThis } from '@/decorators.js';
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
import { assertActivityMatchesUrls, FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
import { assertActivityMatchesUrl, FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
import type { IObject } from '@/core/activitypub/type.js';
import type { Response } from 'node-fetch';
import type { URL } from 'node:url';
@@ -265,7 +265,7 @@ export class HttpRequestService {
const finalUrl = res.url; // redirects may have been involved
const activity = await res.json() as IObject;
assertActivityMatchesUrls(url, activity, [finalUrl], allowSoftfail);
assertActivityMatchesUrl(url, activity, finalUrl, allowSoftfail);
return activity;
}

View File

@@ -7,42 +7,16 @@ import { Injectable } from '@nestjs/common';
import { MiAbuseUserReport, MiNote, MiUser, MiWebhook } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { MiSystemWebhook, type SystemWebhookEventType } from '@/models/SystemWebhook.js';
import { AbuseReportPayload, SystemWebhookPayload, SystemWebhookService } from '@/core/SystemWebhookService.js';
import { Packed } from '@/misc/json-schema.js';
import { type AbuseReportPayload, SystemWebhookPayload, SystemWebhookService } from '@/core/SystemWebhookService.js';
import { type Packed } from '@/misc/json-schema.js';
import { type WebhookEventTypes } from '@/models/Webhook.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { type UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js';
import { QueueService } from '@/core/QueueService.js';
import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
const oneDayMillis = 24 * 60 * 60 * 1000;
function generateAbuseReport(override?: Partial<MiAbuseUserReport>): AbuseReportPayload {
const result: MiAbuseUserReport = {
id: 'dummy-abuse-report1',
targetUserId: 'dummy-target-user',
targetUser: null,
reporterId: 'dummy-reporter-user',
reporter: null,
assigneeId: null,
assignee: null,
resolved: false,
forwarded: false,
comment: 'This is a dummy report for testing purposes.',
targetUserHost: null,
reporterHost: null,
resolvedAs: null,
moderationNote: 'foo',
...override,
};
return {
...result,
targetUser: result.targetUser ? toPackedUserLite(result.targetUser) : null,
reporter: result.reporter ? toPackedUserLite(result.reporter) : null,
assignee: result.assignee ? toPackedUserLite(result.assignee) : null,
};
}
function generateDummyUser(override?: Partial<MiUser>): MiUser {
return {
id: 'dummy-user-1',
@@ -134,124 +108,6 @@ function generateDummyNote(override?: Partial<MiNote>): MiNote {
};
}
function toPackedNote(note: MiNote, detail = true, override?: Packed<'Note'>): Packed<'Note'> {
return {
id: note.id,
createdAt: new Date().toISOString(),
deletedAt: null,
text: note.text,
cw: note.cw,
userId: note.userId,
user: toPackedUserLite(note.user ?? generateDummyUser()),
replyId: note.replyId,
renoteId: note.renoteId,
isHidden: false,
visibility: note.visibility,
mentions: note.mentions,
visibleUserIds: note.visibleUserIds,
fileIds: note.fileIds,
files: [],
tags: note.tags,
poll: null,
emojis: note.emojis,
channelId: note.channelId,
channel: note.channel,
localOnly: note.localOnly,
reactionAcceptance: note.reactionAcceptance,
reactionEmojis: {},
reactions: {},
reactionCount: 0,
renoteCount: note.renoteCount,
repliesCount: note.repliesCount,
uri: note.uri ?? undefined,
url: note.url ?? undefined,
reactionAndUserPairCache: note.reactionAndUserPairCache,
...(detail ? {
clippedCount: note.clippedCount,
reply: note.reply ? toPackedNote(note.reply, false) : null,
renote: note.renote ? toPackedNote(note.renote, true) : null,
myReaction: null,
} : {}),
...override,
};
}
function toPackedUserLite(user: MiUser, override?: Packed<'UserLite'>): Packed<'UserLite'> {
return {
id: user.id,
name: user.name,
username: user.username,
host: user.host,
avatarUrl: user.avatarUrl,
avatarBlurhash: user.avatarBlurhash,
avatarDecorations: user.avatarDecorations.map(it => ({
id: it.id,
angle: it.angle,
flipH: it.flipH,
url: 'https://example.com/dummy-image001.png',
offsetX: it.offsetX,
offsetY: it.offsetY,
})),
isBot: user.isBot,
isCat: user.isCat,
emojis: user.emojis,
onlineStatus: 'active',
badgeRoles: [],
...override,
};
}
function toPackedUserDetailedNotMe(user: MiUser, override?: Packed<'UserDetailedNotMe'>): Packed<'UserDetailedNotMe'> {
return {
...toPackedUserLite(user),
url: null,
uri: null,
movedTo: null,
alsoKnownAs: [],
createdAt: new Date().toISOString(),
updatedAt: user.updatedAt?.toISOString() ?? null,
lastFetchedAt: user.lastFetchedAt?.toISOString() ?? null,
bannerUrl: user.bannerUrl,
bannerBlurhash: user.bannerBlurhash,
isLocked: user.isLocked,
isSilenced: false,
isSuspended: user.isSuspended,
description: null,
location: null,
birthday: null,
lang: null,
fields: [],
verifiedLinks: [],
followersCount: user.followersCount,
followingCount: user.followingCount,
notesCount: user.notesCount,
pinnedNoteIds: [],
pinnedNotes: [],
pinnedPageId: null,
pinnedPage: null,
publicReactions: true,
followersVisibility: 'public',
followingVisibility: 'public',
twoFactorEnabled: false,
usePasswordLessLogin: false,
securityKeys: false,
roles: [],
memo: null,
moderationNote: undefined,
isFollowing: false,
isFollowed: false,
hasPendingFollowRequestFromYou: false,
hasPendingFollowRequestToYou: false,
isBlocking: false,
isBlocked: false,
isMuted: false,
isRenoteMuted: false,
notify: 'none',
withReplies: true,
...override,
};
}
const dummyUser1 = generateDummyUser();
const dummyUser2 = generateDummyUser({
id: 'dummy-user-2',
@@ -284,6 +140,7 @@ export class WebhookTestService {
};
constructor(
private customEmojiService: CustomEmojiService,
private userWebhookService: UserWebhookService,
private systemWebhookService: SystemWebhookService,
private queueService: QueueService,
@@ -354,31 +211,31 @@ export class WebhookTestService {
switch (params.type) {
case 'note': {
send('note', { note: toPackedNote(dummyNote1) });
send('note', { note: await this.toPackedNote(dummyNote1) });
break;
}
case 'reply': {
send('reply', { note: toPackedNote(dummyReply1) });
send('reply', { note: await this.toPackedNote(dummyReply1) });
break;
}
case 'renote': {
send('renote', { note: toPackedNote(dummyRenote1) });
send('renote', { note: await this.toPackedNote(dummyRenote1) });
break;
}
case 'mention': {
send('mention', { note: toPackedNote(dummyMention1) });
send('mention', { note: await this.toPackedNote(dummyMention1) });
break;
}
case 'follow': {
send('follow', { user: toPackedUserDetailedNotMe(dummyUser1) });
send('follow', { user: await this.toPackedUserDetailedNotMe(dummyUser1) });
break;
}
case 'followed': {
send('followed', { user: toPackedUserLite(dummyUser2) });
send('followed', { user: await this.toPackedUserLite(dummyUser2) });
break;
}
case 'unfollow': {
send('unfollow', { user: toPackedUserDetailedNotMe(dummyUser3) });
send('unfollow', { user: await this.toPackedUserDetailedNotMe(dummyUser3) });
break;
}
// まだ実装されていない (#9485)
@@ -427,7 +284,7 @@ export class WebhookTestService {
switch (params.type) {
case 'abuseReport': {
send('abuseReport', generateAbuseReport({
send('abuseReport', await this.generateAbuseReport({
targetUserId: dummyUser1.id,
targetUser: dummyUser1,
reporterId: dummyUser2.id,
@@ -436,7 +293,7 @@ export class WebhookTestService {
break;
}
case 'abuseReportResolved': {
send('abuseReportResolved', generateAbuseReport({
send('abuseReportResolved', await this.generateAbuseReport({
targetUserId: dummyUser1.id,
targetUser: dummyUser1,
reporterId: dummyUser2.id,
@@ -448,7 +305,7 @@ export class WebhookTestService {
break;
}
case 'userCreated': {
send('userCreated', toPackedUserLite(dummyUser1));
send('userCreated', await this.toPackedUserLite(dummyUser1));
break;
}
case 'inactiveModeratorsWarning': {
@@ -474,4 +331,153 @@ export class WebhookTestService {
}
}
}
@bindThis
private async generateAbuseReport(override?: Partial<MiAbuseUserReport>): Promise<AbuseReportPayload> {
const result: MiAbuseUserReport = {
id: 'dummy-abuse-report1',
targetUserId: 'dummy-target-user',
targetUser: null,
reporterId: 'dummy-reporter-user',
reporter: null,
assigneeId: null,
assignee: null,
resolved: false,
forwarded: false,
comment: 'This is a dummy report for testing purposes.',
targetUserHost: null,
reporterHost: null,
resolvedAs: null,
moderationNote: 'foo',
...override,
};
return {
...result,
targetUser: result.targetUser ? await this.toPackedUserLite(result.targetUser) : null,
reporter: result.reporter ? await this.toPackedUserLite(result.reporter) : null,
assignee: result.assignee ? await this.toPackedUserLite(result.assignee) : null,
};
}
@bindThis
private async toPackedNote(note: MiNote, detail = true, override?: Packed<'Note'>): Promise<Packed<'Note'>> {
return {
id: note.id,
createdAt: new Date().toISOString(),
deletedAt: null,
text: note.text,
cw: note.cw,
userId: note.userId,
user: await this.toPackedUserLite(note.user ?? generateDummyUser()),
replyId: note.replyId,
renoteId: note.renoteId,
isHidden: false,
visibility: note.visibility,
mentions: note.mentions,
visibleUserIds: note.visibleUserIds,
fileIds: note.fileIds,
files: [],
tags: note.tags,
poll: null,
emojis: await this.customEmojiService.populateEmojis(note.emojis, note.userHost),
channelId: note.channelId,
channel: note.channel,
localOnly: note.localOnly,
reactionAcceptance: note.reactionAcceptance,
reactionEmojis: {},
reactions: {},
reactionCount: 0,
renoteCount: note.renoteCount,
repliesCount: note.repliesCount,
uri: note.uri ?? undefined,
url: note.url ?? undefined,
reactionAndUserPairCache: note.reactionAndUserPairCache,
...(detail ? {
clippedCount: note.clippedCount,
reply: note.reply ? await this.toPackedNote(note.reply, false) : null,
renote: note.renote ? await this.toPackedNote(note.renote, true) : null,
myReaction: null,
} : {}),
...override,
};
}
@bindThis
private async toPackedUserLite(user: MiUser, override?: Packed<'UserLite'>): Promise<Packed<'UserLite'>> {
return {
id: user.id,
name: user.name,
username: user.username,
host: user.host,
avatarUrl: user.avatarUrl,
avatarBlurhash: user.avatarBlurhash,
avatarDecorations: user.avatarDecorations.map(it => ({
id: it.id,
angle: it.angle,
flipH: it.flipH,
url: 'https://example.com/dummy-image001.png',
offsetX: it.offsetX,
offsetY: it.offsetY,
})),
isBot: user.isBot,
isCat: user.isCat,
emojis: await this.customEmojiService.populateEmojis(user.emojis, user.host),
onlineStatus: 'active',
badgeRoles: [],
...override,
};
}
@bindThis
private async toPackedUserDetailedNotMe(user: MiUser, override?: Packed<'UserDetailedNotMe'>): Promise<Packed<'UserDetailedNotMe'>> {
return {
...await this.toPackedUserLite(user),
url: null,
uri: null,
movedTo: null,
alsoKnownAs: [],
createdAt: new Date().toISOString(),
updatedAt: user.updatedAt?.toISOString() ?? null,
lastFetchedAt: user.lastFetchedAt?.toISOString() ?? null,
bannerUrl: user.bannerUrl,
bannerBlurhash: user.bannerBlurhash,
isLocked: user.isLocked,
isSilenced: false,
isSuspended: user.isSuspended,
description: null,
location: null,
birthday: null,
lang: null,
fields: [],
verifiedLinks: [],
followersCount: user.followersCount,
followingCount: user.followingCount,
notesCount: user.notesCount,
pinnedNoteIds: [],
pinnedNotes: [],
pinnedPageId: null,
pinnedPage: null,
publicReactions: true,
followersVisibility: 'public',
followingVisibility: 'public',
twoFactorEnabled: false,
usePasswordLessLogin: false,
securityKeys: false,
roles: [],
memo: null,
moderationNote: undefined,
isFollowing: false,
isFollowed: false,
hasPendingFollowRequestFromYou: false,
hasPendingFollowRequestToYou: false,
isBlocking: false,
isBlocked: false,
isMuted: false,
isRenoteMuted: false,
notify: 'none',
withReplies: true,
...override,
};
}
}

View File

@@ -17,7 +17,7 @@ import { LoggerService } from '@/core/LoggerService.js';
import { bindThis } from '@/decorators.js';
import type Logger from '@/logger.js';
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
import { assertActivityMatchesUrls, FetchAllowSoftFailMask as FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
import { assertActivityMatchesUrl, FetchAllowSoftFailMask as FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
import type { IObject } from './type.js';
type Request = {
@@ -258,7 +258,7 @@ export class ApRequestService {
const finalUrl = res.url; // redirects may have been involved
const activity = await res.json() as IObject;
assertActivityMatchesUrls(url, activity, [finalUrl], allowSoftfail);
assertActivityMatchesUrl(url, activity, finalUrl, allowSoftfail);
return activity;
}

View File

@@ -75,7 +75,7 @@ function normalizeSynonymousSubdomain(url: URL | string): URL {
return new URL(urlParsed.toString().replace(host, normalizedHost));
}
export function assertActivityMatchesUrls(requestUrl: string | URL, activity: IObject, candidateUrls: (string | URL)[], allowSoftfail: FetchAllowSoftFailMask): FetchAllowSoftFailMask {
export function assertActivityMatchesUrl(requestUrl: string | URL, activity: IObject, finalUrl: string | URL, allowSoftfail: FetchAllowSoftFailMask): FetchAllowSoftFailMask {
// must have a unique identifier to verify authority
if (!activity.id) {
throw new Error('bad Activity: missing id field');
@@ -95,26 +95,32 @@ export function assertActivityMatchesUrls(requestUrl: string | URL, activity: IO
const requestUrlParsed = normalizeSynonymousSubdomain(requestUrl);
const idParsed = normalizeSynonymousSubdomain(activity.id);
const candidateUrlsParsed = candidateUrls.map(it => normalizeSynonymousSubdomain(it));
const finalUrlParsed = normalizeSynonymousSubdomain(finalUrl);
// mastodon sends activities with hash in the URL
// currently it only happens with likes, deletes etc.
// but object ID never has hash
requestUrlParsed.hash = '';
finalUrlParsed.hash = '';
const requestUrlSecure = requestUrlParsed.protocol === 'https:';
const finalUrlSecure = candidateUrlsParsed.every(it => it.protocol === 'https:');
const finalUrlSecure = finalUrlParsed.protocol === 'https:';
if (requestUrlSecure && !finalUrlSecure) {
throw new Error(`bad Activity: id(${activity.id}) is not allowed to have http:// in the url`);
}
// Compare final URL to the ID
if (!candidateUrlsParsed.some(it => it.href === idParsed.href)) {
requireSoftfail(FetchAllowSoftFailMask.NonCanonicalId, `bad Activity: id(${activity.id}) does not match response url(${candidateUrlsParsed.map(it => it.toString())})`);
if (finalUrlParsed.href !== idParsed.href) {
requireSoftfail(FetchAllowSoftFailMask.NonCanonicalId, `bad Activity: id(${activity.id}) does not match response url(${finalUrlParsed.toString()})`);
// at lease host need to match exactly (ActivityPub requirement)
if (!candidateUrlsParsed.some(it => idParsed.host === it.host)) {
throw new Error(`bad Activity: id(${activity.id}) does not match response host(${candidateUrlsParsed.map(it => it.host)})`);
if (idParsed.host !== finalUrlParsed.host) {
throw new Error(`bad Activity: id(${activity.id}) does not match response host(${finalUrlParsed.host})`);
}
}
// Compare request URL to the ID
if (!requestUrlParsed.href.includes(idParsed.href)) {
if (requestUrlParsed.href !== idParsed.href) {
requireSoftfail(FetchAllowSoftFailMask.NonCanonicalId, `bad Activity: id(${activity.id}) does not match request url(${requestUrlParsed.toString()})`);
// if cross-origin lookup is allowed, we can accept some variation between the original request URL to the final object ID (but not between the final URL and the object ID)

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

@@ -166,6 +166,7 @@ export interface Schema extends OfSchema {
readonly maximum?: number;
readonly minimum?: number;
readonly pattern?: string;
readonly additionalProperties?: Schema | boolean;
}
type RequiredPropertyNames<s extends Obj> = {
@@ -217,6 +218,13 @@ type ObjectSchemaTypeDef<p extends Schema> =
:
p['anyOf'] extends ReadonlyArray<Schema> ? never : // see CONTRIBUTING.md
p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> :
p['additionalProperties'] extends true ? Record<string, any> :
p['additionalProperties'] extends Schema ?
p['additionalProperties'] extends infer AdditionalProperties ?
AdditionalProperties extends Schema ?
Record<string, SchemaType<AdditionalProperties>> :
never :
never :
any;
type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>;

View File

@@ -6,6 +6,7 @@
import * as fs from 'node:fs';
import { Inject, Injectable } from '@nestjs/common';
import { ZipReader } from 'slacc';
import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { EmojisRepository, DriveFilesRepository } from '@/models/_.js';
import type Logger from '@/logger.js';
@@ -86,6 +87,7 @@ export class ImportCustomEmojisProcessorService {
const emojiPath = outputPath + '/' + record.fileName;
await this.emojisRepository.delete({
name: emojiInfo.name,
host: IsNull(),
});
try {

View File

@@ -13,7 +13,7 @@ import accepts from 'accepts';
import vary from 'vary';
import secureJson from 'secure-json-parse';
import { DI } from '@/di-symbols.js';
import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository, MiMeta } from '@/models/_.js';
import * as url from '@/misc/prelude/url.js';
import type { Config } from '@/config.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
@@ -42,6 +42,9 @@ export class ActivityPubServerService {
@Inject(DI.config)
private config: Config,
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -102,6 +105,11 @@ export class ActivityPubServerService {
@bindThis
private inbox(request: FastifyRequest, reply: FastifyReply) {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
let signature;
try {
@@ -173,6 +181,11 @@ export class ActivityPubServerService {
request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>,
reply: FastifyReply,
) {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const userId = request.params.user;
const cursor = request.query.cursor;
@@ -265,6 +278,11 @@ export class ActivityPubServerService {
request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>,
reply: FastifyReply,
) {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const userId = request.params.user;
const cursor = request.query.cursor;
@@ -354,6 +372,11 @@ export class ActivityPubServerService {
@bindThis
private async featured(request: FastifyRequest<{ Params: { user: string; }; }>, reply: FastifyReply) {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const userId = request.params.user;
const user = await this.usersRepository.findOneBy({
@@ -398,6 +421,11 @@ export class ActivityPubServerService {
}>,
reply: FastifyReply,
) {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const userId = request.params.user;
const sinceId = request.query.since_id;
@@ -482,6 +510,11 @@ export class ActivityPubServerService {
@bindThis
private async userInfo(request: FastifyRequest, reply: FastifyReply, user: MiUser | null) {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
if (user == null) {
reply.code(404);
return;
@@ -564,6 +597,11 @@ export class ActivityPubServerService {
fastify.get<{ Params: { note: string; } }>('/notes/:note', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
vary(reply.raw, 'Accept');
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const note = await this.notesRepository.findOneBy({
id: request.params.note,
visibility: In(['public', 'home']),
@@ -594,6 +632,11 @@ export class ActivityPubServerService {
fastify.get<{ Params: { note: string; } }>('/notes/:note/activity', async (request, reply) => {
vary(reply.raw, 'Accept');
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const note = await this.notesRepository.findOneBy({
id: request.params.note,
userHost: IsNull(),
@@ -634,6 +677,11 @@ export class ActivityPubServerService {
// publickey
fastify.get<{ Params: { user: string; } }>('/users/:user/publickey', async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const userId = request.params.user;
const user = await this.usersRepository.findOneBy({
@@ -661,6 +709,11 @@ export class ActivityPubServerService {
fastify.get<{ Params: { user: string; } }>('/users/:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
vary(reply.raw, 'Accept');
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const userId = request.params.user;
const user = await this.usersRepository.findOneBy({
@@ -674,6 +727,11 @@ export class ActivityPubServerService {
fastify.get<{ Params: { acct: string; } }>('/@:acct', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
vary(reply.raw, 'Accept');
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const acct = Acct.parse(request.params.acct);
const user = await this.usersRepository.findOneBy({
@@ -688,6 +746,11 @@ export class ActivityPubServerService {
// emoji
fastify.get<{ Params: { emoji: string; } }>('/emojis/:emoji', async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const emoji = await this.emojisRepository.findOneBy({
host: IsNull(),
name: request.params.emoji,
@@ -705,6 +768,11 @@ export class ActivityPubServerService {
// like
fastify.get<{ Params: { like: string; } }>('/likes/:like', async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const reaction = await this.noteReactionsRepository.findOneBy({ id: request.params.like });
if (reaction == null) {
@@ -726,6 +794,11 @@ export class ActivityPubServerService {
// follow
fastify.get<{ Params: { follower: string; followee: string; } }>('/follows/:follower/:followee', async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
// This may be used before the follow is completed, so we do not
// check if the following exists.
@@ -752,6 +825,11 @@ export class ActivityPubServerService {
// follow
fastify.get<{ Params: { followRequestId: string; } }>('/follows/:followRequestId', async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
// This may be used before the follow is completed, so we do not
// check if the following exists and only check if the follow request exists.

View File

@@ -8,7 +8,7 @@ import { IsNull } from 'typeorm';
import vary from 'vary';
import fastifyAccepts from '@fastify/accepts';
import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/_.js';
import type { MiMeta, UsersRepository } from '@/models/_.js';
import type { Config } from '@/config.js';
import { escapeAttribute, escapeValue } from '@/misc/prelude/xml.js';
import type { MiUser } from '@/models/User.js';
@@ -26,6 +26,9 @@ export class WellKnownServerService {
@Inject(DI.config)
private config: Config,
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -66,6 +69,11 @@ export class WellKnownServerService {
});
fastify.get('/.well-known/host-meta', async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
reply.header('Content-Type', xrd);
return XRD({ element: 'Link', attributes: {
rel: 'lrdd',
@@ -75,6 +83,11 @@ export class WellKnownServerService {
});
fastify.get('/.well-known/host-meta.json', async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
reply.header('Content-Type', 'application/json');
return {
links: [{
@@ -86,6 +99,11 @@ export class WellKnownServerService {
});
fastify.get('/.well-known/nodeinfo', async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
return { links: this.nodeinfoServerService.getLinks() };
});
@@ -99,6 +117,11 @@ fastify.get('/.well-known/change-password', async (request, reply) => {
*/
fastify.get<{ Querystring: { resource: string } }>(webFingerPath, async (request, reply) => {
if (this.meta.federation === 'none') {
reply.code(403);
return;
}
const fromId = (id: MiUser['id']): FindOptionsWhere<MiUser> => ({
id,
host: IsNull(),

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

@@ -6,7 +6,6 @@
import { Inject, Injectable } from '@nestjs/common';
import cors from '@fastify/cors';
import multipart from '@fastify/multipart';
import fastifyCookie from '@fastify/cookie';
import { ModuleRef } from '@nestjs/core';
import { AuthenticationResponseJSON } from '@simplewebauthn/types';
import type { Config } from '@/config.js';
@@ -57,8 +56,6 @@ export class ApiServerService {
},
});
fastify.register(fastifyCookie, {});
// Prevent cache
fastify.addHook('onRequest', (request, reply, done) => {
reply.header('Cache-Control', 'private, max-age=0, must-revalidate');

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

@@ -7,16 +7,12 @@ import { randomUUID } from 'node:crypto';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Inject, Injectable } from '@nestjs/common';
import { createBullBoard } from '@bull-board/api';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter.js';
import { FastifyAdapter as BullBoardFastifyAdapter } from '@bull-board/fastify';
import ms from 'ms';
import sharp from 'sharp';
import pug from 'pug';
import { In, IsNull } from 'typeorm';
import fastifyStatic from '@fastify/static';
import fastifyView from '@fastify/view';
import fastifyCookie from '@fastify/cookie';
import fastifyProxy from '@fastify/http-proxy';
import vary from 'vary';
import htmlSafeJsonStringify from 'htmlescape';
@@ -221,64 +217,6 @@ export class ClientServerService {
@bindThis
public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
fastify.register(fastifyCookie, {});
//#region Bull Dashboard
const bullBoardPath = '/queue';
// Authenticate
fastify.addHook('onRequest', async (request, reply) => {
if (request.routeOptions.url == null) {
reply.code(404).send('Not found');
return;
}
// %71ueueとかでリクエストされたら困るため
const url = decodeURI(request.routeOptions.url);
if (url === bullBoardPath || url.startsWith(bullBoardPath + '/')) {
if (!url.startsWith(bullBoardPath + '/static/')) {
reply.header('Cache-Control', 'private, max-age=0, must-revalidate');
}
const token = request.cookies.token;
if (token == null) {
reply.code(401).send('Login required');
return;
}
const user = await this.usersRepository.findOneBy({ token });
if (user == null) {
reply.code(403).send('No such user');
return;
}
const isAdministrator = await this.roleService.isAdministrator(user);
if (!isAdministrator) {
reply.code(403).send('Access denied');
return;
}
}
});
const bullBoardServerAdapter = new BullBoardFastifyAdapter();
createBullBoard({
queues: [
this.systemQueue,
this.endedPollNotificationQueue,
this.deliverQueue,
this.inboxQueue,
this.dbQueue,
this.relationshipQueue,
this.objectStorageQueue,
this.userWebhookDeliverQueue,
this.systemWebhookDeliverQueue,
].map(q => new BullMQAdapter(q)),
serverAdapter: bullBoardServerAdapter,
});
bullBoardServerAdapter.setBasePath(bullBoardPath);
(fastify.register as any)(bullBoardServerAdapter.registerPlugin(), { prefix: bullBoardPath });
//#endregion
fastify.register(fastifyView, {
root: _dirname + '/views',
engine: {

View File

@@ -6,7 +6,7 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { channel, clip, cookie, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
import { channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
import type { SimpleGetResponse } from '../utils.js';
import type * as misskey from 'misskey-js';
@@ -156,20 +156,20 @@ describe('Webリソース', () => {
describe(' has entry such ', () => {
beforeEach(() => {
post(alice, { text: "**a**" })
post(alice, { text: '**a**' });
});
test('MFMを含まない。', async () => {
const content = await simpleGet(path(alice.username), "*/*", undefined, res => res.text());
const content = await simpleGet(path(alice.username), '*/*', undefined, res => res.text());
const _body: unknown = content.body;
// JSONフィードのときは改めて文字列化する
const body: string = typeof (_body) === "object" ? JSON.stringify(_body) : _body as string;
const body: string = typeof (_body) === 'object' ? JSON.stringify(_body) : _body as string;
if (body.includes("**a**")) {
throw new Error("MFM shouldn't be included");
if (body.includes('**a**')) {
throw new Error('MFM shouldn\'t be included');
}
});
})
});
});
describe.each([{ path: '/api/foo' }])('$path', ({ path }) => {
@@ -180,24 +180,6 @@ describe('Webリソース', () => {
}));
});
describe.each([{ path: '/queue' }])('$path', ({ path }) => {
test('はログインしないとGETできない。', async () => await notOk({
path,
status: 401,
}));
test('はadminでなければGETできない。', async () => await notOk({
path,
cookie: cookie(bob),
status: 403,
}));
test('はadminならGETできる。', async () => await ok({
path,
cookie: cookie(alice),
}));
});
describe.each([{ path: '/streaming' }])('$path', ({ path }) => {
test('はGETできない。', async () => await notOk({
path,

View File

@@ -14,6 +14,7 @@ import { MiSystemWebhook, MiUser, MiWebhook, UserProfilesRepository, UsersReposi
import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js';
import { QueueService } from '@/core/QueueService.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
describe('WebhookTestService', () => {
let app: TestingModule;
@@ -56,6 +57,11 @@ describe('WebhookTestService', () => {
providers: [
WebhookTestService,
IdService,
{
provide: CustomEmojiService, useFactory: () => ({
populateEmojis: jest.fn(),
}),
},
{
provide: QueueService, useFactory: () => ({
systemWebhookDeliver: jest.fn(),

View File

@@ -8,7 +8,7 @@ import httpSignature from '@peertube/http-signature';
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
import { assertActivityMatchesUrls, FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
import { assertActivityMatchesUrl, FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
import { IObject } from '@/core/activitypub/type.js';
export const buildParsedSignature = (signingString: string, signature: string, algorithm: string) => {
@@ -66,23 +66,26 @@ describe('ap-request', () => {
});
test('rejects non matching domain', () => {
assert.doesNotThrow(() => assertActivityMatchesUrls(
assert.doesNotThrow(() => assertActivityMatchesUrl(
'https://alice.example.com/abc',
{ id: 'https://alice.example.com/abc' } as IObject,
[
'https://alice.example.com/abc',
],
'https://alice.example.com/abc',
FetchAllowSoftFailMask.Strict,
), 'validation should pass base case');
assert.throws(() => assertActivityMatchesUrls(
assert.throws(() => assertActivityMatchesUrl(
'https://alice.example.com/abc',
{ id: 'https://bob.example.com/abc' } as IObject,
[
'https://alice.example.com/abc',
],
'https://alice.example.com/abc',
FetchAllowSoftFailMask.Any,
), 'validation should fail no matter what if the response URL is inconsistent with the object ID');
assert.doesNotThrow(() => assertActivityMatchesUrl(
'https://alice.example.com/abc#test',
{ id: 'https://alice.example.com/abc' } as IObject,
'https://alice.example.com/abc',
FetchAllowSoftFailMask.Strict,
), 'validation should pass with hash in request URL');
// fix issues like threads
// https://github.com/misskey-dev/misskey/issues/15039
const withOrWithoutWWW = [
@@ -97,89 +100,71 @@ describe('ap-request', () => {
),
withOrWithoutWWW,
).forEach(([[a, b], c]) => {
assert.doesNotThrow(() => assertActivityMatchesUrls(
assert.doesNotThrow(() => assertActivityMatchesUrl(
a,
{ id: b } as IObject,
[
c,
],
c,
FetchAllowSoftFailMask.Strict,
), 'validation should pass with or without www. subdomain');
});
});
test('cross origin lookup', () => {
assert.doesNotThrow(() => assertActivityMatchesUrls(
assert.doesNotThrow(() => assertActivityMatchesUrl(
'https://alice.example.com/abc',
{ id: 'https://bob.example.com/abc' } as IObject,
[
'https://bob.example.com/abc',
],
'https://bob.example.com/abc',
FetchAllowSoftFailMask.CrossOrigin | FetchAllowSoftFailMask.NonCanonicalId,
), 'validation should pass if the response is otherwise consistent and cross-origin is allowed');
assert.throws(() => assertActivityMatchesUrls(
assert.throws(() => assertActivityMatchesUrl(
'https://alice.example.com/abc',
{ id: 'https://bob.example.com/abc' } as IObject,
[
'https://bob.example.com/abc',
],
'https://bob.example.com/abc',
FetchAllowSoftFailMask.Strict,
), 'validation should fail if the response is otherwise consistent and cross-origin is not allowed');
});
test('rejects non-canonical ID', () => {
assert.throws(() => assertActivityMatchesUrls(
assert.throws(() => assertActivityMatchesUrl(
'https://alice.example.com/@alice',
{ id: 'https://alice.example.com/users/alice' } as IObject,
[
'https://alice.example.com/users/alice'
],
'https://alice.example.com/users/alice',
FetchAllowSoftFailMask.Strict,
), 'throws if the response ID did not exactly match the expected ID');
assert.doesNotThrow(() => assertActivityMatchesUrls(
assert.doesNotThrow(() => assertActivityMatchesUrl(
'https://alice.example.com/@alice',
{ id: 'https://alice.example.com/users/alice' } as IObject,
[
'https://alice.example.com/users/alice',
],
'https://alice.example.com/users/alice',
FetchAllowSoftFailMask.NonCanonicalId,
), 'does not throw if non-canonical ID is allowed');
});
test('origin relaxed alignment', () => {
assert.doesNotThrow(() => assertActivityMatchesUrls(
assert.doesNotThrow(() => assertActivityMatchesUrl(
'https://alice.example.com/abc',
{ id: 'https://ap.alice.example.com/abc' } as IObject,
[
'https://ap.alice.example.com/abc',
],
'https://ap.alice.example.com/abc',
FetchAllowSoftFailMask.MisalignedOrigin | FetchAllowSoftFailMask.NonCanonicalId,
), 'validation should pass if response is a subdomain of the expected origin');
assert.throws(() => assertActivityMatchesUrls(
assert.throws(() => assertActivityMatchesUrl(
'https://alice.multi-tenant.example.com/abc',
{ id: 'https://alice.multi-tenant.example.com/abc' } as IObject,
[
'https://bob.multi-tenant.example.com/abc',
],
'https://bob.multi-tenant.example.com/abc',
FetchAllowSoftFailMask.MisalignedOrigin | FetchAllowSoftFailMask.NonCanonicalId,
), 'validation should fail if response is a disjoint domain of the expected origin');
assert.throws(() => assertActivityMatchesUrls(
assert.throws(() => assertActivityMatchesUrl(
'https://alice.example.com/abc',
{ id: 'https://ap.alice.example.com/abc' } as IObject,
[
'https://ap.alice.example.com/abc',
],
'https://ap.alice.example.com/abc',
FetchAllowSoftFailMask.Strict,
), 'throws if relaxed origin is forbidden');
});
test('resist HTTP downgrade', () => {
assert.throws(() => assertActivityMatchesUrls(
assert.throws(() => assertActivityMatchesUrl(
'https://alice.example.com/abc',
{ id: 'https://alice.example.com/abc' } as IObject,
[
'http://alice.example.com/abc',
],
'http://alice.example.com/abc',
FetchAllowSoftFailMask.Strict,
), 'throws if HTTP downgrade is detected');
});

View File

@@ -35,7 +35,7 @@ export type SystemWebhookPayload = {
createdAt: string;
type: string;
body: any;
}
};
const config = loadConfig();
export const port = config.port;
@@ -45,10 +45,6 @@ export const host = new URL(config.url).host;
export const WEBHOOK_HOST = 'http://localhost:15080';
export const WEBHOOK_PORT = 15080;
export const cookie = (me: UserToken): string => {
return `token=${me.token};`;
};
export type ApiRequest<E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req'] = misskey.Endpoints[E]['req']> = {
endpoint: E,
parameters: P,

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

@@ -33,13 +33,11 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
Math.min(navigator.hardwareConcurrency - 1, 4),
);
resolve(workers);
if (_DEV_) console.log('WebGL2 in worker is supported!');
} else {
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
resolve(canvas);
if (_DEV_) console.log('WebGL2 in worker is not supported...');
}
testWorker.terminate();
});

View File

@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
import { inject, watch, ref } from 'vue';
import { watch, ref } from 'vue';
import XReaction from '@/components/EmReactionsViewer.reaction.vue';
const props = withDefaults(defineProps<{
@@ -22,12 +22,6 @@ const props = withDefaults(defineProps<{
maxNumber: Infinity,
});
const mock = inject<boolean>('mock', false);
const emit = defineEmits<{
(ev: 'mockUpdateMyReaction', emoji: string, delta: number): void;
}>();
const initialReactions = new Set(Object.keys(props.note.reactions));
const reactions = ref<[string, number][]>([]);
@@ -38,12 +32,8 @@ if (props.note.myReaction && !Object.keys(reactions.value).includes(props.note.m
}
function onMockToggleReaction(emoji: string, count: number) {
if (!mock) return;
const i = reactions.value.findIndex((item) => item[0] === emoji);
if (i < 0) return;
emit('mockUpdateMyReaction', emoji, (count - reactions.value[i][1]));
}
watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => {

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
<div class="_fullinfo">
<img :src="notFoundImageUrl" class="_ghost"/>
<img :src="notFoundImageUrl" draggable="false"/>
<div>{{ i18n.ts.notFoundDescription }}</div>
</div>
</div>
@@ -20,5 +20,5 @@ import { i18n } from '@/i18n.js';
const serverMetadata = inject(DI.serverMetadata)!;
const notFoundImageUrl = computed(() => serverMetadata?.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);
const notFoundImageUrl = computed(() => serverMetadata.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);
</script>

View File

@@ -12,6 +12,7 @@ const siteName = document.querySelector<HTMLMetaElement>('meta[property="og:site
export const host = address.host;
export const hostname = address.hostname;
export const url = address.origin;
export const port = address.port;
export const apiUrl = location.origin + '/api';
export const wsOrigin = location.origin;
export const lang = localStorage.getItem('lang') ?? 'en-US';

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

@@ -17,8 +17,52 @@ interface SatisfiesExpression extends estree.BaseExpression {
reference: estree.Identifier;
}
interface ImportDeclaration extends estree.ImportDeclaration {
kind?: 'type';
}
const generator = {
...GENERATOR,
ImportDeclaration(node: ImportDeclaration, state: State) {
state.write('import ');
if (node.kind === 'type') state.write('type ');
const { specifiers } = node;
if (specifiers.length > 0) {
let i = 0;
for (; i < specifiers.length; i++) {
if (i > 0) {
state.write(', ');
}
const specifier = specifiers[i]!;
if (specifier.type === 'ImportDefaultSpecifier') {
state.write(specifier.local.name, specifier);
} else if (specifier.type === 'ImportNamespaceSpecifier') {
state.write(`* as ${specifier.local.name}`, specifier);
} else {
break;
}
}
if (i < specifiers.length) {
state.write('{');
for (; i < specifiers.length; i++) {
const specifier = specifiers[i]! as estree.ImportSpecifier;
const { name } = specifier.imported as estree.Identifier;
state.write(name, specifier);
if (name !== specifier.local.name) {
state.write(` as ${specifier.local.name}`);
}
if (i < specifiers.length - 1) {
state.write(', ');
}
}
state.write('}');
}
state.write(' from ');
}
this.Literal(node.source, state);
state.write(';');
},
SatisfiesExpression(node: SatisfiesExpression, state: State) {
switch (node.expression.type) {
case 'ArrowFunctionExpression': {
@@ -62,7 +106,7 @@ type ToKebab<T extends readonly string[]> = T extends readonly [
: T extends readonly [
infer XH extends string,
...infer XR extends readonly string[]
]
]
? `${XH}${XR extends readonly string[] ? `-${ToKebab<XR>}` : ''}`
: '';
@@ -132,7 +176,7 @@ function toStories(component: string): Promise<string> {
kind={'init' as const}
shorthand
/> as estree.Property,
]
]
: []),
]}
/> as estree.ObjectExpression;
@@ -155,7 +199,8 @@ function toStories(component: string): Promise<string> {
/> as estree.ImportSpecifier,
]),
]}
/> as estree.ImportDeclaration,
kind={'type'}
/> as ImportDeclaration,
...(hasMsw
? [
<import-declaration
@@ -165,8 +210,8 @@ function toStories(component: string): Promise<string> {
local={<identifier name='msw' /> as estree.Identifier}
/> as estree.ImportNamespaceSpecifier,
]}
/> as estree.ImportDeclaration,
]
/> as ImportDeclaration,
]
: []),
...(hasImplStories
? []
@@ -176,8 +221,8 @@ function toStories(component: string): Promise<string> {
specifiers={[
<import-default-specifier local={identifier} /> as estree.ImportDefaultSpecifier,
]}
/> as estree.ImportDeclaration,
]),
/> as ImportDeclaration,
]),
...(hasMetaStories
? [
<import-declaration
@@ -187,7 +232,7 @@ function toStories(component: string): Promise<string> {
local={<identifier name='storiesMeta' /> as estree.Identifier}
/> as estree.ImportNamespaceSpecifier,
]}
/> as estree.ImportDeclaration,
/> as ImportDeclaration,
]
: []),
<variable-declaration

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'],
});
@@ -64,12 +64,12 @@ initialize({
initLocalStorage();
queueMicrotask(() => {
Promise.all([
import('../src/components'),
import('../src/directives'),
import('../src/widgets'),
import('../src/theme'),
import('../src/preferences'),
import('../src/os'),
import('../src/components/index.js'),
import('../src/directives/index.js'),
import('../src/widgets/index.js'),
import('../src/theme.js'),
import('../src/preferences.js'),
import('../src/os.js'),
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os]) => {
setup((app) => {
moduleInitialized = true;

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

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