summaryrefslogtreecommitdiff
path: root/packages/frontend/src/scripts
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2024-02-17 15:05:47 +0900
committerGitHub <noreply@github.com>2024-02-17 15:05:47 +0900
commit96c7c85ad008a71fb03198a708c8531aacbb39e0 (patch)
tree8f9f72a49e62b3a6151f79f0eb7576492c529a4f /packages/frontend/src/scripts
parentMerge pull request #12828 from misskey-dev/develop (diff)
parentfix: allow moderators see role assigned users; fix #13301 (#13315) (diff)
downloadsharkey-96c7c85ad008a71fb03198a708c8531aacbb39e0.tar.gz
sharkey-96c7c85ad008a71fb03198a708c8531aacbb39e0.tar.bz2
sharkey-96c7c85ad008a71fb03198a708c8531aacbb39e0.zip
Merge pull request #13045 from misskey-dev/develop
* perf(drop-and-fusion): remove root Transition component for improve performance * refactor(drop-and-fusion): some refactors * clean up * enhance(drop-and-fusion): some tweaks * Feat(frontend): リアクション・ノート内絵文字・/about#emojisで絵文字詳細が見られるように (#12984) * リアクション・ノート内絵文字・/about#emojisで絵文字詳細が見られるように * update CHANGELOG.md * fix locale & type errors * fix locale etc * fix * fix type * lint fixes * lint fixes(2) * tweak * fix(backend): 虚無ノートを投稿できる問題の修正と `api.json` の OpenAPI Specification 3.1.0 への対応 (#12969) * fix(backend): `text: null`だけのノートは投稿できないように * add test * Update CHANGELOG.md * chore: bump OpenAPI Specification from 3.0.0 to 3.1.0 * chore: テストがすでにコメントで記述されていたのでそっちを使うことにする * fix test * fix(backend): prohibit posting whitespace-only notes * Update CHANGELOG.md * fix(backend): `renoteId`または`fileIds`(`mediaIds`)または`poll`が`null`でない場合に、`text が空白文字のみで構成されたリクエストになることを許可して、結果は`text: null`を返すように * test(backend): 引用renoteで空白文字のみで構成されたtextにするとレスポンスが`text: null`になることをチェックするテストを追加 * fix(frontend): `text`が`null`であって`renoteId`と`replyId`が`null`でないようなノートは引用リノートとして表示するように * fix(misskey-js): OpenAPI 3.1に対応 * fix(misskey-js): 型生成をOpenAPI Specification 3.1.0に対応 * fix(ci): `validate-api.json`をOpenAPI Specification 3.1.0に対応 * fix(ci): スキーマ書き換えの際のミスを修正 * Revert "fix(frontend): `text`が`null`であって`renoteId`と`replyId`が`null`でないようなノートは引用リノートとして表示するように" This reverts commit a9ca55343df6ea1679599acbc4801f78aa3a242b. * fix(misskey-js): `build-misskey-js-with-types`時は`api.json`のGETをスキップするように * Revert "fix(misskey-js): `build-misskey-js-with-types`時は`api.json`のGETをスキップするように" This reverts commit 865458989f9ddacc38d1bb3743a41ea828dbf324. * fix(misskey-js): `openapi-parser`で`validate`のかわりに`parse`を用いるように * Update CHANGELOG.md * fix type * enhance(drop-and-fusion): refactor and new mode(wip) * feat: 枠線をつけるMFMを追加 (#12981) * Update MkMisskeyFlavoredMarkdown.ts * Update const.ts * Update MkMisskeyFlavoredMarkdown.ts * Update MkMisskeyFlavoredMarkdown.ts * Update CHANGELOG.md * feat(CI): CHANGELOG.mdの追記個所をチェックするCIを追加 (#12963) * feat(CI): CHANGELOG.mdの追記個所をチェックするCIを追加 * fix * remove strategy * fix * fix * enhance(drop-and-fusion): sweets mode * 完成 (#12980) * enhance(frontend): Playの説明欄にMFMを使えるように (#12899) * (enhance) Playの説明欄にMFMを使えるように * Update Changelog * use class for mfm component * Update packages/frontend/src/pages/flash/flash-edit.vue Co-authored-by: 1Step621 <86859447+1STEP621@users.noreply.github.com> * Update flash.vue * Update CHANGELOG.md --------- Co-authored-by: 1Step621 <86859447+1STEP621@users.noreply.github.com> * fix: isPrivateIpで検証時にipバージョンが一致するかを確認するように (#12988) * fix: isPrivateIpで検証時にipバージョンが一致するかを確認するように * Update CHANGELOG.md * Update CHANGELOG.md * enhance(frontend) 日本語の拡張絵文字辞書を追加 (#12855) * Create ja-JP.json * Update general.vue * Update ja-JP.json * Update ja-JP.json * Update ja-JP.json * fix * fix design * (Add) ひらがな [wip] * fix lint * Apply suggestions from code review Co-authored-by: 1Step621 <86859447+1STEP621@users.noreply.github.com> * (add) ja-JP_hira Co-authored-by: 1Step621 <86859447+1STEP621@users.noreply.github.com> * (enhance) 言語名をちゃんと表示するように --------- Co-authored-by: 1Step621 <86859447+1STEP621@users.noreply.github.com> * refactor: noteテーブルのインデックス整理と配列カラムへのクエリでインデックスを使うように (#12993) * Optimize note model index * enhance(backend): ANY()をやめる (MisskeyIO#239) * add small e2e test drive endpoint --------- Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> * enhance(frontend): dedicated games page * enhance: 動画・音声周りのUIと動作改良 (#12925) * wip * (fix) `/files` をバイトレンジリクエストに対応させる * video * audio * fix * fix * spdx * fix (rangeRequest) * fix * Update CHANGELOG.md * (add) ボリュームを保存できるように * (fix) ミュート復帰時に音量が固定される * named export * tweak design * Add sensitive class for audio component * Refactor seekbar styles * Refactor hms * Revert "(add) ボリュームを保存できるように" This reverts commit 6271f9493b63f96d0dd9915207e97fe120ef9037. * Revert "(fix) ミュート復帰時に音量が固定される" This reverts commit a65002b56ecdcb10f76bcc2debbe38593a69643f. * revert revert changes --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * (style) sticky系フッターのデザイン調整 (#13005) * enhance(frontend): ページ遷移時にPlayerを閉じるように (#13013) * なんかできた * update changelog.md * onDeactivatedを使うように * Enhance(frontend): MkCustomEmojiDetailedDialogを調整 (#13015) * MkEmojiDetailedDialogを調整 * 絵文字ライセンスでMFMを使えるように * <a> -> <MkLink> * 入力ボックスでmfmのオートコンプリートを効かせる * enhance(frontend): チャンネルノートの場合はその前後を見れるように (#13019) * チャンネルノートの場合はその前後を見れるように * Update Changelog * $[border ...]にクリッピング機能を追加 (#13002) * Update MkMisskeyFlavoredMarkdown.ts * Update MkMisskeyFlavoredMarkdown.ts * Update CHANGELOG.md * Set clipping as default * Fix: properly handle cc followers (#13009) * Fix: properly handle cc followers Fix #13001 * Update CHANGELOG.md * Fix syntax error * enhance(drop-and-fusion): ゲームバランスの調整など * MkCodeにコピーボタンを追加 (#12999) * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update MkCode.vue * Update CHANGELOG.md --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * chore(drop-and-fusion): bump version * refactor: MkCodeをブロックとインラインで別コンポーネント化する (#13026) * Create MkCodeInline.vue * Update MkCode.vue * Update MkMisskeyFlavoredMarkdown.ts * Update flash.vue * Update MkCodeInline.vue * fix(frontend/MediaVideo): 再生シークバーの当たり判定を調整 (#13027) * fix(frontend/MediaVideo): 再生シークバーの当たり判定を調整 * fix * feat(frontend): 横スワイプでタブを切り替える機能 (#13011) * (add) 横スワイプでタブを切り替える機能 * Change Changelog * y方向の移動が一定量を超えたらスワイプを中断するように * Update swipe distance thresholds * Remove console.log * adjust threshold * rename, use v-model * fix * Update MkHorizontalSwipe.vue Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * use css module --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * refactor: fully typed locales (#13033) * refactor: fully typed locales * refactor: hide parameterized locale strings from type data in ts access * refactor: missing assertions * docs: annotation * refactor: style * fix(frontend/HorizontalSwipe): ページの要素がはみ出る問題を修正 (#13036) * 「外部サイトからインストール」のパスを /install-extensions に変更 (#12991) * /install-extensionsに変更 * CHANGELOG.mdに追記 * 旧パスも利用できるように * fix: Some fixes for #12850 (#12862) - refinement the error message when trueMail validation fails - the settings of trueMail are not displayed after saving - changing how `Active Email Validation` is saved * Enhance(frontend): MFMの属性にオートコンプリートが利用できるように (#12803) * MFMのパラメータでオートコンプリートできるように * tweak conditions & refactor * ファイル末尾の改行忘れ * remove console.log & refactor * 型付けに敗北 * fix * update CHANGELOG.md * tweak conditions * CHANGELOGの様式ミス * CHANGELOGを書く場所を間違えていたので修正 * move changelog * move changelog * typeof MFM_TAGS[number] Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * $[border.noclip ]対応 * Update const.ts --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * feat: reversi Resolve #12962 * refactor: deprecate i18n.t (#13039) * refactor: deprecate i18n.t * revert: deprecate i18n.t This reverts commit 7dbf873a2f745040ee723df5db659acacff84e12. * chore: reimpl * refactor: extract bubble-game engine as independent package * lint fix * lint fixes * tweak reversi map * fix lint * fix(dev): fix workspace settings * fix(dev): fix pnpm dev * enhance(reversi): tweak reversi * refactor: migrate to ESM * fix api-extractor * add missing ext * enhance(reversi): tweak reversi * :art: * Fix(frontend): 日本語のUnicode絵文字追加辞書をインストールすると絵文字ピッカーでUnicode絵文字を検索できなくなるのを修正 (#13046) * 絵文字辞書のサロゲートペアを修正 * update CHANGELOG.md * Revert "update CHANGELOG.md" This reverts commit 7c24fa611a533bb74ef7979a0356b83f3410a056. * enhance(reversi): tweak reversi Resolve #13048 * Update Dockerfile * enhance(reversi): tweak reversi * enhance(frontend): ノート作成画面の添付メニューから直接ファイルを消せるように (#12858) * (enhance) 添付画面から直接ファイルを消せるように * Update Changelog --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * enhance(reversi): tweak reversi * enhance(sw): オフライン表示のデザインを改善 (#13052) * enhance(sw): オフライン表示のデザインを改善 * Update Changelog * fix * fix * fix * 言語が取得できなかった場合のフォールバックを追加 * (change) translation key * enhance(reversi): tweak reversi * enhance(reversi): tweak reversi * fix(frontend): MkHorizontalSwipeでメニューを閉じるのに2回クリックが必要になる問題を修正 #13055 * return a `Vary: Accept` header for all dual-format endpoints #365 (#13044) `/users/:user`, `/@:user`, `/notes/:note` return different responses depending on the request's `Accept:` header. If we don't consistently return a `Vary: Accept` header, browsers and caching proxies will get confused, and return AP representations when HTML was requested, or vice versa. Co-authored-by: dakkar <dakkar@thenautilus.net> Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * enhance(frontend): タイムラインフィルターの設定を保持+センシティブなノートを隠せるように (#12848) * (enhance) タイムラインフィルターの状態を記憶するように * fix * (enhance) センシティブな投稿をミュート形式で表示する(TLのみ) * fix * Update Changelog * Fix changelog * Lintエラーを潰す * Update locales/ja-JP.yml * hideSensitive -> withSensitive * Update CHANGELOG.md * Update ja-JP.yml --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * Enhance(frontend): 絵文字編集ダイアログをウィンドウにする (#13047) * 絵文字編集ダイアログをウィンドウにする * update CHANGELOG.md * update deps * New Crowdin updates (#12845) * New translations ja-jp.yml (French) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Lao) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Lao) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (French) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Lao) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Romanian) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Arabic) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Czech) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Dutch) * New translations ja-jp.yml (Polish) * New translations ja-jp.yml (Portuguese) * New translations ja-jp.yml (Russian) * 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 (English) * New translations ja-jp.yml (Vietnamese) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Bengali) * New translations ja-jp.yml (Uzbek) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Korean (Gyeongsang)) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (French) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Arabic) * New translations ja-jp.yml (Catalan) * 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 (Korean) * New translations ja-jp.yml (Polish) * New translations ja-jp.yml (Portuguese) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Slovak) * New translations ja-jp.yml (Ukrainian) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Vietnamese) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Bengali) * New translations ja-jp.yml (Uzbek) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Korean (Gyeongsang)) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (French) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Arabic) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Czech) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Portuguese) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Slovak) * New translations ja-jp.yml (Ukrainian) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Vietnamese) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (French) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Romanian) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Arabic) * New translations ja-jp.yml (Catalan) * 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 (Korean) * New translations ja-jp.yml (Polish) * New translations ja-jp.yml (Portuguese) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Slovak) * New translations ja-jp.yml (Ukrainian) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Vietnamese) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Bengali) * New translations ja-jp.yml (Uzbek) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Korean (Gyeongsang)) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (French) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Chinese Simplified) * chore(deps-dev): bump vite in /scripts/changelog-checker (#13040) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.11 to 5.0.12. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.0.12/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.0.12/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * enhance(frontend): 季節に応じた画面の演出を南半球に対応させる (#12838) * (enhance) 季節に応じた画面の演出を南半球に対応させる * Update Changelog * (add) 半球の簡易自動判定 --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * enhance(frontend): リファクタリングなど * perf(reversi): improve performance of reversi backend * 2024.2.0-beta.1 * fix(frontend/pizzax): デフォルト値が適用できないことがあるのを修正 (#13057) * fix(frontend/pizzax): デフォルト値が適用できないことがあるのを修正 * fix * いらんプロパティをけす * refactor(reversi): refactoring of reversi backend * New Crowdin updates (#13056) * New translations ja-jp.yml (French) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Lao) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Romanian) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Arabic) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Czech) * New translations ja-jp.yml (Danish) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Greek) * New translations ja-jp.yml (Hungarian) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Dutch) * New translations ja-jp.yml (Norwegian) * New translations ja-jp.yml (Polish) * New translations ja-jp.yml (Portuguese) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Slovak) * New translations ja-jp.yml (Swedish) * New translations ja-jp.yml (Turkish) * New translations ja-jp.yml (Ukrainian) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Vietnamese) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Bengali) * New translations ja-jp.yml (Croatian) * New translations ja-jp.yml (Uyghur) * New translations ja-jp.yml (Lojban) * New translations ja-jp.yml (Sinhala) * New translations ja-jp.yml (Uzbek) * New translations ja-jp.yml (Kannada) * New translations ja-jp.yml (Haitian Creole) * New translations ja-jp.yml (Kabyle) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Korean (Gyeongsang)) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) * 2024.2.0-beta.2 * enhance(reversi): some tweaks * perf(reversi): improve performance of reversi backend * fix lint * enhance(reversi): render ogp * fix lint * fix: 2024-01-22 10:50時点のdevelopにてCIがコケている (#13060) * fix: バブルゲームのビルド失敗修正 * fix: api.jsonの定義誤りを修正 * fix: lint.yml(typecheck) * fix: fix eslint error * fix: frontend vitest version * fix: frontend vitest version * fix: * fix: cypress * fix: misskey-js test * fix: misskey-js tsd(tsdはpakcage.jsonのexportsをサポートしない?) * fix: conflict * fix: 間違えて上書きしたところを修正 * fix: 再 * fix: api.json * fix: api.json * fix: タイムアウト延長 * Update packages/misskey-js/jest.config.cjs Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> * :art: * fix lint * 2024.2.0-beta.3 * chore: publish misskey-js automatically (#13014) * chore: publish @misskey-dev/misskey-js * remove @misskey-dev/ * ?? * correct version * version * fix of #13014 (misskey-js publish) * 修正できたかも (#13066) * perf: (productionの)dependenciesから@typesを削除、reversi/bubble-gameをesbuildにする (#13067) * perf: (productionの)dependenciesから@typesを削除、reversi/bubble-gameをesbuildにする * fix * fix * fix(build): スクリプトの名前の変更漏れ (#13068) * fix(build): スクリプトの名前の変更漏れ * 漏れの漏れ * :art: * enhance(reversi): improve desync handling * New Crowdin updates (#13061) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Korean) * 2024.2.0-beta.4 * fix(frontend/HorizontalSwipe): スワイプ・UIアニメーションが無効の際はトランジションを行わないように (#13076) * fix(frontend/HorizontalSwipe): アニメーションを減らすが考慮されるように * fix * fix * revert unused change * fix * :art: * enhance(reversi): 準備中の自分の対局も一覧に表示するように * enhance(reversi): more robust matching process * fix of 65557d5f27044bd90c538266fde1e6b91b696f80 * enhance(reversi): 開始時に対局をシェアできるように * enhance(reversi): improve stability * New translations ja-jp.yml (Japanese, Kansai) (#13074) * enhance(reversi): improve game setting flow * enhance(reversi): tweak MATCHING_TIMEOUT_MS * perf(reversi): set expire matchSpecific and matchAny * fix(reversi): wait redis operation to improve stability * 2024.2.0-beta.5 * fix(frontend/pizzax): オブジェクトにnullがある場合に正しくマージされないのを修正 (#13073) * fix(frontend/pizzax): オブジェクトにnullがある場合に正しくマージされない * fix types * マージを内製 * fix(frontend/reversi): fix game preview * enhance(reversi): improve matching system * New translations ja-jp.yml (Japanese, Kansai) (#13077) * 2024.2.0-beta.6 * enhance(reversi): 変則なしマッチングを可能に * fix(reversi/backend): refactor and fixes * Create deploy-test-environment.yml (#13079) * test * Revert "Create deploy-test-environment.yml (#13079)" This reverts commit 4de14fb5cf8c0aa2078ebbedc242a65e042ded54. * New Crowdin updates (#13080) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) * 2024.2.0-beta.7 * New Crowdin updates (#13082) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (German) * New translations ja-jp.yml (English) * New translations ja-jp.yml (German) * New translations ja-jp.yml (English) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Japanese, Kansai) * fix(dev): pnpm devで依存関係更新が一部反映されない (#13091) * fix misskey-js version * refactor(frontend/MediaPlayer): cssの重複を削除 (#13094) * Update MkMediaAudio.vue * Update MkMediaVideo.vue * enhance(frontend): リモートのユーザーはメニューから直接リモートで表示できるように (#13087) * enhance(frontend): リモートのユーザーはメニューから直接リモートで表示できるように * change changelog * Apply suggestions from code review Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * fix(backend): Fix typos in job configurations (#13086) * Fix typos * Update CHANGELOG * Update CHANGELOG.md * feat(frontend/nirax): リダイレクトを設定できるように (#13030) * feat(frontend/nirax): リダイレクトを設定できるように * revert demonstrative changes * fix * revert unrelated changes * リダイレクトの際にパスが変わらない問題を修正 * リダイレクトが必要なrouteを設定 * fix lint * router向けe2eテストの追加 * fix --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> Co-authored-by: samunohito <46447427+samunohito@users.noreply.github.com> * fix(i18n): ストック情報とフロー情報の文言をわかりやすく変更 (#13085) * fix(i18n): ストック情報とフロー情報をわかりやすく書き直す * Update ja-JP.yml * Update ja-JP.yml * test(frontend): load default config to start vite (#12867) Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com> * iOSで大きな画像を変換してアップロードできない問題を修正 (#13109) Fix https://github.com/misskey-dev/misskey/issues/12026 * refactor: frontendのcomponentsの型エラーを改善 (#12926) * add: safeFloatParserを追加 * fix: 欠けていた型を追加 * refactor: pageBlockTypesをjson-schemaに移植 * refactor: components/global内の型エラーが出ている箇所を修正 * lint: fix null check style * refactor: fix type error * refactor: fix some type errors * fix: 翻訳が抜けていた箇所を修正 * refactor: getJsonSchemaで正しいスキーマが返されるように修正 * fix: MkChartの型エラーとbytesオプションが機能していない問題を修正 * fix(misskey-js): `drive`->`folderUpdated`のpayloadの型が間違っていたのを修正 * refactor: fix some type errors * change: Captcha読み込み中の文言をLoadingに変更 * refactor(backend/misskey-js): MainEventの型を改善 * refactor: chartjs-plugin-gradientが二重でpluginに登録されていたのを修正 * update: misskey-js.api.md * refactor: fix some type errors * fix: backendのtypecheckが落ちていたのを修正 * update: misskey-js.api.md * add: json-schemaのnoteにpollの型定義を追加 * refactor: noteのjson-schemaの型を改善 * refactor: MkPoll * refactor: fix some type errors * change: UserLiteにisLockedを持たせるように * fix: notificationスキーマにroleが含まれていないのを修正 * Revert "change: UserLiteにisLockedを持たせるように" This reverts commit 1bb0c8e7a9b19a4e9f21bf7381712b98f27672a5. * fix: フォロー通知から鍵垢へのフォローを行うと処理中のまま止まってしまう問題を修正 * refactor: noteスキーマのvisibilityにenumを追加 * change: deepCloneのCloneableTypeにundefinedを追加 * refactor: fix some type errors * refactor: `allowEmpty: false`を使用していた箇所を`minLength: 1`に置き換え * enhance: API 'retension' のresponseの型を追加 * fix: Chart関連のtooltipが正しい位置に表示されない問題を修正 * refactor: fix some type errors * fix: 型情報が不足していたのを修正 * enhance: announcementスキーマにenumを追加 * enhance: ロールポリシーの型定義をRoleServiceからjson-schemaに移植 * refactor: policiesを`ref: RolePolicies`に統一 * fix: API `meta` のレスポンスの型にpoliciesが含まれていないのを修正 * refactor: fix some type errors * fix: backendのlintが落ちているのを修正 * fix: MkFoldableSectionの開閉時のanimationが適用されていない問題を修正 * fix: backendのtypecheckが落ちているのを修正 * update: run build-misskey-js-with-types * fix: MkDialogのmount時に文字数制限の判定が行われない問題を修正 * update: CHANGELOG.md * refactor: MkUserSelectDialogの型を改善 * fix: deepCloneでundefinedはcloneしないように (#9207) * change: frontendのcloneをbackend側にも反映 * update: CHANGELOG.md * fix: RoleServiceからPackを通して型RolePoliciesに依存させないように * Update packages/frontend/src/scripts/get-note-summary.ts * revert RoleService.ts changes * change: optional chaining -> non-null assertion * remove: unused import * fix: propsで渡されたuserがUserLiteの場合に意図しない動作になってしまうのを修正 * change: fix null check style * refactor: fix type error * change: fix null check style * Update packages/frontend/src/components/MkDrive.vue Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * refactor: css moduleでglobalを使わないように * refactor: roleのiconUrlは必ず存在するものとして扱うように * enhance: MenuButtonのactiveにcomputedを受け付けられるように * Update packages/frontend/src/components/MkNotePreview.vue * Update MkWindow.vue * refactor: notification.noteは必ず存在するものとして扱うように * Update packages/frontend/src/components/MkNotification.vue Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * fix: MkSignupDialogでdoneのemit時にresを含んでいなかったのを修正 * Update packages/frontend/src/scripts/clone.ts Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * refactor: 不要な返り値の型を削除 * refactor: 不要なnullチェックを削除 * update: misskey-js-autogen * update: clone.ts * refactor * Update MkNotification.vue * Update MkNotification.vue * :v: * Update MkNotification.vue * Update MkNotification.vue * Update MkNotification.vue * Update MkNotifications.vue * Update MkUserSetupDialog.Profile.vue * Update MkUserCardMini.vue * :v: * Update MkMenu.vue --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * fix/refactor(reversi): 既存のバグを修正・型定義を強化 (#13105) * 既存のバグを修正 * fix types * fix misskey-js autogen * Update index.d.ts --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * update deps * 2024.2.0-beta.8 * Revert "Revert "Create deploy-test-environment.yml (#13079)"" This reverts commit 4553d6426bfa6a54754d5cf477d04782d5e05860. * refactor(frontend): global/router -> router * refactor(backend): User関連のスキーマ/型の指定を強くする (#12808) * refactor(backend): User関連のスキーマ/型の指定を強くする * refactor(backend): `pack()`の引数にスキーマを指定するように * chore: fix ci * fix: 変更漏れ * fix ci --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * fix(frontend): styleの指定方法を変更 (#13120) * fix(ci): `misskey-js` のバージョンチェックをトリガーする条件の修正 (#13116) * fix(misskey-js): バージョンチェックのトリガー条件を修正 * chore(misskey-js): 2024.2.0-beta.8 * Fix(frontend): リバーシで自分自信を招待できるのを修正 & os.selectUser()のincludeSelfが機能していないのを修正 (#13117) * リバーシで自分自信を招待できるのを修正 & os.selectUser()のincludeSelfが機能していないのを修正 * lint fix * enhance(frontend): :cherry_blossom: * chore(deps): bump codecov/codecov-action from 3 to 4 (#13125) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Hide reactions of all remote users / feat: moderators can see reactions of all users (#13128) * fix: Hide reactions of all remote users https://github.com/misskey-dev/misskey/issues/12964 * feat: Moderators can see reactions of all users https://github.com/misskey-dev/misskey/issues/13127 * modify CHANGELOG.md * fix iAmModerator * chore(deps): bump peter-evans/slash-command-dispatch from 3 to 4 (#13124) Bumps [peter-evans/slash-command-dispatch](https://github.com/peter-evans/slash-command-dispatch) from 3 to 4. - [Release notes](https://github.com/peter-evans/slash-command-dispatch/releases) - [Commits](https://github.com/peter-evans/slash-command-dispatch/compare/v3...v4) --- updated-dependencies: - dependency-name: peter-evans/slash-command-dispatch dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * 「見たことのあるリノートを省略して表示」が効いていない問題を修正 (#13133) * fix: 「見たことのあるリノートを省略して表示」が効いていない問題を修正 fix #13131 * add a comment * fix(backend): "誰でも新規登録できるようにする"の初期値をOFFにする (#13130) * fix(backend): "誰でも新規登録できるようにする"の初期値をOFFにする * fix CHANGELOG.md * fix * Update deploy-test-environment.yml (#13136) * fix: api-docが開けない問題を修正 (#13132) * refactor: 自己参照を使用している箇所に`selfRef`を持たせるように * feat: スキーマ生成時に自己参照を含むかどうかを指定できるように * fix: api.jsonにselfRefが含まれているのを修正 * refactor: 他の箇所と同様にselfRefの除去を行うように * remove: 不要なimportを削除 * refactor(frontend): `os.popup()`の`props`の型チェックを有効化 (#13140) * refactor(frontend): `os.popup()`の`props`の型チェックを有効化 * refactor: `ComponentProps`に書き換え * refacor: `import type` * enhance(frontend): shiki v1に移行 (#13138) * enhance(frontend): shiki v1に移行 * optimize chunks, エラーを握りつぶす * wasmを分離 * バンドルサイズの警告の最小値を650kBに引き上げ * optimize * fix(frontend): アバターデコレーションのアニメーションが止まらない (#13139) * fix(frontend): アバターデコレーションのアニメーションが止まらない * Update Changelog * i -> index * key * revert lint fixes * fix(frontend): selectUserのパラメータを調整 (#13142) * fix(frontend): selectUserのパラメータを調整 * ついでに軽微なスタイルの修正 * fix(frontend): チャートのlegendがクリックに反応しない問題を修正 これにより発生 https://github.com/misskey-dev/misskey/pull/12926 * New Crowdin updates (#13090) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Korean (Gyeongsang)) * 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 (German) * New translations ja-jp.yml (English) * New translations ja-jp.yml (German) * New translations ja-jp.yml (French) * 2024.2.0-beta.9 * fix(backend): メール配信機能が無効ならばメールを送ることのないように (#13152) Do not send email if email delivery is disabled * ignore `instance.actor` when checking if there are local users (#13146) * ignore `instance.actor` when checking if there are local users We've seen this happen a few times: * there was some AP software at $some_domain * it gets replaced by Misskey * before the first user can be created, an AP activity comes in * Misskey resolves the activity * to do this, it creates the `instance.actor` to sign its request * now there *is* a local user, so the `meta` endpoint returns `requireSetup:false` * the admin is very confused This commit factors out the check, and doesn't count the `instance.actor` as a real user. * autogen bits * keep cached avatar&banner when refresh fails to get new values (#13145) * keep cached avatar&banner when refresh fails to get new values when the remote explicitly tells us a user image is gone, we remove our cached value, but if we fail to get the image, we keep whatever value we already have this should minimise the problem of avatars randomly disappearing * autogen bits * pnpm run build-misskey-js-with-types --------- Co-authored-by: tamaina <tamaina@hotmail.co.jp> * update patrons * New Crowdin updates (#13156) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Korean (Gyeongsang)) * New translations ja-jp.yml (Korean (Gyeongsang)) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * Fix(frontend): クロップ後の解像度が異様に低くなる問題の修正&クロップに失敗する問題&コメントにnullという文字列が入る問題の修正 (#13162) * Fix(frontend): Fix resolution of cropped image (misskey-dev#11489) * CHANGELOG * Fix(frontend): クロップの際、folderIdがnullだと文字列のnullが送られ検索できない問題 * Fix: キャプションが存在しないときにクロップすると'null'がキャプションに入ってしまう問題 (misskey-dev#11813) * Update CHANGELOG * refactor(frontend): `os.popup()`の`events`の型チェックを有効化 (#13165) * 2024.2.0-beta.10 * enhance(frontend): シンタックスハイライトにテーマを適用できるように (#13175) * enhance(frontend): シンタックスハイライトにテーマを適用できるように * Update Changelog * こっちも * テーマの値がディープマージされるように * 常にテーマ設定に準じるように * テーマ更新時に新しいshikiテーマを読み込むように * enhance(frontend): KeepAliveのページキャッシュを削除できるように (#13180) * enhance(frontend): 内部のページキャッシュを削除できるように * Update Changelog * Enhance(frontend): フロント側でもリアクション権限のチェックをするように (#13134) * フロント側でもリアクション権限のチェックをするように * update CHANGELOG.md * lint fixes * remove unrelated diffs * deny -> reject denyは「(信用しないことを理由に)拒否する」という意味らしい * allow -> accept * EmojiSimpleにlocalOnlyを含めるように * リアクション権限のない絵文字は打てないように(ダイアログを出すのではなく) * regenerate type definitions * lint fix * remove unused locales * remove unnecessary async * fix(frontend): エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正 (#13172) Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> * enhance(frontend): リモートへの引用リノートと同一のリンクにはリンクプレビューを表示しないように (#13178) * enhance(frontend): リモートへの引用リノートと同一のリンクにはリンクプレビューを表示しないように * Update Changelog --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * AP Key の JSON-LD 表現を修正 (#13170) * CHANGELOGを修正 (#13181) * chore(frontend): reword possible typo (#13182) * fix(bubble-game): 共有用画像のコメントにnullが入る問題を修正 (#13183) * fix(misskey-js): 自動生成物の冒頭からバージョンと日付を削除 (#13185) * Enhance: 連合向けのノート配信を軽量化 (#13192) * AP HTML表現をシンプルに * a * CHANGELOG * リンク * Fix(frontend): MkCodeEditorで行がずれていくのを修正 (#13188) * MkCodeEditorで行がずれていくのを修正 * update CHANGELOG.md * 正しい 2024.2.0-beta.10 改版手順? (#13173) * 正しい 2024.2.0-beta.10 改版手順? * run build-misskey-js-with-types * enhance(frontend/HorizontalSwipe): 操作性の改善 (#13038) * Update swipe thresholds and touch-action * スワイプ中にPullToRefreshが反応しないように * 横スワイプに関与する可能性のある要素がある場合はスワイプを発火しないように * update threshold * isSwipingを外部化 * rename --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * typo * Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 (#13196) * Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 * CHANGELOG * test(frontend): migrate MSW in Storybook to v2 (#13195) * fix(frontend) misskey-js type (#13202) * refactor(backend): exist -> exists (#13203) * refactor(backend): exist -> exists * fix * fix(frontend): aiscriptのコードブロックでのハイライト指定を修正 (#13208) * chore: use vite@5.1.0 / pnpm@8.15.1 * fix: 特定文字列を含むノートを投稿できないようにする管理画面用設定項目を追加 (#13210) * fix: 特定文字列を含むノートを投稿できないようにする管理画面用設定項目を追加 * Serviceでチェックするように変更 * perf(frontend): splash screenのdomが消えない場合があるのを修正 https://github.com/misskey-dev/misskey/issues/10805 * chore(deps): bump pnpm/action-setup from 2 to 3 (#13215) Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 2 to 3. - [Release notes](https://github.com/pnpm/action-setup/releases) - [Commits](https://github.com/pnpm/action-setup/compare/v2...v3) --- updated-dependencies: - dependency-name: pnpm/action-setup dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * New Crowdin updates (#13179) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Czech) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Russian) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Indonesian) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Japanese, Kansai) * New translations ja-jp.yml (Catalan) * update deps * 2024.2.0-beta.11 * fix misskey-js version * dev: Update misskey-tga deploy-test-environment.yml (#13221) * fix: misskey-jsの型定義生成時にバックエンドの依存パッケージもビルドするように (#13249) * fix(frontend): vue v3.4.16でタイムラインが正常に表示できない問題を修正 * type * fix: misskey-jsの型定義生成時にバックエンドの依存パッケージもビルドするように * Revert "type" This reverts commit bac0951bd1608cbd97ba809a76a664590ce5efb3. * Revert "fix(frontend): vue v3.4.16でタイムラインが正常に表示できない問題を修正" This reverts commit 92b21658287932515cb061ddc2ab5c83b4b78ca2. * Update about-misskey.vue * New Crowdin updates (#13216) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Italian) * feat: provide tarball (#13260) * feat: provide tarball * build: pack on build-assets * chore: use ignore-walk * chore: debug * build: dependencies * New translations ja-jp.yml (Spanish) (#13261) * update SPDX-FileCopyrightText * refactor(msjs): avoid any (part 1) (#13247) * refactor(msjs): avoid any * run api extractor --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Co-authored-by: kakkokari-gtyih <daisho7308+f@gmail.com> * ci(test-frontend): Cypressのテストの失敗時、永遠に止まらない問題を回避 (MisskeyIO#434) (#13274) 失敗しないようタイムアウトの延長・15分で止まるように * chore: 以前の開発環境(backendにアクセスする方式)を立ち上げられるように (#13220) * chore: 以前の開発環境(backendにアクセスする方式)を立ち上げられるように * Update scripts/dev.mjs Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> * fix: downgrade vue to 3.4.15 * enhance: 禁止ワードはリモートノートも対象に (#13280) Resolve #13279 * Update CHANGELOG.md (#13282) #13281 に対応していることを強調 * perf: omit search for immutable static requests (#13265) * perf: omit search for immutable static requests * perf: also applies to /files * fix: exclude /proxy * /files/:key/*を301 redirectに --------- Co-authored-by: tamaina <tamaina@hotmail.co.jp> * Revert "update SPDX-FileCopyrightText" This reverts commit 9b5aeb76d8c9372d67058c512597152b6bf222f2. * (re) update SPDX-FileCopyrightText Fix #13290 * fix(frontend): エラーページのトラブルシューティングがリンク切れしている問題 (#176) (#13288) * fix: TypeAssertionExpression breaks Storybook builds * build: upgrade Storybook to 8 beta (#13297) * chore: upgrade Storybook to 8 * ci: restore Storybook workflow * build: createRequire * ci: TurboSnap life extension * dev: Update misskey-tga (#13223) * Update deploy-test-environment.yml * Update .github/workflows/deploy-test-environment.yml Co-authored-by: anatawa12 <anatawa12@icloud.com> * Update deploy-test-environment.yml * Update deploy-test-environment.yml --------- Co-authored-by: anatawa12 <anatawa12@icloud.com> * fix(ci): publish docker image fails (#13325) * fix(ci): publish docker image fails * fix: `docker.yml` * refactor: remove inaccurate name * fix: match version * feat(backend): likeOnlyなどでハートにフォールバックする際異体字セレクタがない方に揃える (#13299) * feat(backend): likeOnlyなどでハートにフォールバックする際異体字セレクタがない方に揃える close #13298 * Update ReactionService.ts * chore(backend): prefer single quote for string literal * fix(backend): add missing schemas and fix incorrect schemas (#13295) * fix(backend): add missing schemas and fix incorrect schemas * fix: ci * fix: ci (本命) * fix: run `pnpm build-misskey-js-with-types` * fix: typos * fix: role-condition-formula-value contains `id` * fix: incorrect schema * リモートユーザーが復活してもキャッシュにより該当ユーザーのActivityが受け入れられないのを修正 Fix #13273 (#13275) * リモートユーザーが復活してもキャッシュにより該当ユーザーのActivityが受け入れられないのを修正 Fix #13273 * CHAGELOG * Use Redis event --------- Co-authored-by: tamaina <tamaina@hotmail.co.jp> * refactor(backend): misc/cacheをシンプルな実装に戻した * fix * fix type * Update CHANGELOG.md * New Crowdin updates (#13267) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (French) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Chinese Traditional) * fix(frontend): vue v3.4.16以降でタイムラインが正常に表示できない問題を修正 (#13248) * fix(frontend): vue v3.4.16でタイムラインが正常に表示できない問題を修正 * type * Revert "fix: downgrade vue to 3.4.15" This reverts commit e12369ac13a906321397dfee5142f2af4b12f5b6. * Update pnpm-lock.yaml --------- Co-authored-by: tamaina <tamaina@hotmail.co.jp> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * 2024.2.0-beta.12 * fix(ci): publish docker image fails (3) (#13327) * fix(ci): publish docker image fails (3) * fix: set `tags` * fix(frontend/pageMetadata): ページタイトルが更新されない問題 (#13289) Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> * chore(deps): bump actions/github-script from 6.4.0 to 7.0.1 (#13311) Bumps [actions/github-script](https://github.com/actions/github-script) from 6.4.0 to 7.0.1. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v6.4.0...v7.0.1) --- updated-dependencies: - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump pnpm/action-setup from 2 to 3 (#13310) Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 2 to 3. - [Release notes](https://github.com/pnpm/action-setup/releases) - [Commits](https://github.com/pnpm/action-setup/compare/v2.0.0...v3) --- updated-dependencies: - dependency-name: pnpm/action-setup dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/checkout from 3.6.0 to 4.1.1 (#13309) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.1.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.6.0...v4.1.1) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/upload-artifact from 3 to 4 (#13308) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump actions/setup-node from 3.8.1 to 4.0.2 (#13307) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.8.1 to 4.0.2. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3.8.1...v4.0.2) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build: docker buildのpnpm i実行時にNODE_ENV=productionが指定されるようにする (#13329) * fix of #13330 (#13330) * build: docker buildのpnpm i実行時にNODE_ENV=productionが指定されるようにする * build: 消す行間違ってたのを修正 * fix(dev): devコマンドの実装を修正 (#13336) * fix misskey-js version * refactor(backend): remove/replace deprecated type deps (#13252) * Update CHANGELOG.md * 2024.2.0-beta.13 * Merge pull request from GHSA-qqrm-9grj-6v32 * maybe ok * fix * test wip * :v: * fix * if (res.ok) * validateContentTypeSetAsJsonLD * 条件を考慮し直す * その他の+json接尾辞が付いているメディアタイプも受け容れる * https://github.com/misskey-dev/misskey-ghsa-qqrm-9grj-6v32/pull/1#discussion_r1490999009 * add `; profile="https://www.w3.org/ns/activitystreams"` * application/ld+json; * feat: add link to local note in initial comment of abuse note (#13347) * feat: add link to local note in initial comment of abuse note * docs(changelog): ノートの通報時にリモートのノートであっても自インスタンスにおけるノートのリンクを含むように * feat: license violation protection (#13285) * spec(frontend): aboutページにリポジトリ・フィードバックのURLを表示させる Cherry-picked from MisskeyIO#441 Cherry-picked from MisskeyIO#438 * feat: license violation protection * build: fix typo * build: fix typo * fix: farewell to the static type land * fix: key typo * fix: import typo * fix: properly interpret `prominently` * docs: add disclaimer * docs: update CHANGELOG * chore: add gap --------- Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: 1Step621 <86859447+1STEP621@users.noreply.github.com> Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Co-authored-by: FineArchs <133759614+FineArchs@users.noreply.github.com> Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com> Co-authored-by: ikasoba <57828948+ikasoba@users.noreply.github.com> Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Co-authored-by: GrapeApple0 <84321396+GrapeApple0@users.noreply.github.com> Co-authored-by: YS <47836716+yszkst@users.noreply.github.com> Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> Co-authored-by: a <a@trwnh.com> Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> Co-authored-by: Korange <hi@korange.work> Co-authored-by: AsukaMari <2037177696@qq.com> Co-authored-by: dakkar <dakkar@thenautilus.net> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com> Co-authored-by: tamaina <tamaina@hotmail.co.jp> Co-authored-by: Srgr0 <66754887+Srgr0@users.noreply.github.com> Co-authored-by: woxtu <woxtup@gmail.com> Co-authored-by: Kagami Sascha Rosylight <saschanaz@outlook.com> Co-authored-by: yukineko <27853966+hideki0403@users.noreply.github.com> Co-authored-by: atsuchan <83960488+atsu1125@users.noreply.github.com> Co-authored-by: taichan <40626578+tai-cha@users.noreply.github.com> Co-authored-by: Soli <personal@str08.net> Co-authored-by: MeiMei <30769358+mei23@users.noreply.github.com> Co-authored-by: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Co-authored-by: kakkokari-gtyih <daisho7308+f@gmail.com> Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com> Co-authored-by: anatawa12 <anatawa12@icloud.com>
Diffstat (limited to 'packages/frontend/src/scripts')
-rw-r--r--packages/frontend/src/scripts/achievements.ts18
-rw-r--r--packages/frontend/src/scripts/aiscript/api.ts15
-rw-r--r--packages/frontend/src/scripts/aiscript/ui.ts6
-rw-r--r--packages/frontend/src/scripts/array.ts2
-rw-r--r--packages/frontend/src/scripts/autocomplete.ts45
-rw-r--r--packages/frontend/src/scripts/cache.ts2
-rw-r--r--packages/frontend/src/scripts/chart-legend.ts2
-rw-r--r--packages/frontend/src/scripts/chart-vline.ts2
-rw-r--r--packages/frontend/src/scripts/check-reaction-permissions.ts8
-rw-r--r--packages/frontend/src/scripts/check-word-mute.ts2
-rw-r--r--packages/frontend/src/scripts/clicker-game.ts8
-rw-r--r--packages/frontend/src/scripts/clone.ts8
-rw-r--r--packages/frontend/src/scripts/code-highlighter.ts85
-rw-r--r--packages/frontend/src/scripts/collapsed.ts2
-rw-r--r--packages/frontend/src/scripts/collect-page-vars.ts2
-rw-r--r--packages/frontend/src/scripts/color.ts2
-rw-r--r--packages/frontend/src/scripts/confetti.ts2
-rw-r--r--packages/frontend/src/scripts/contains.ts2
-rw-r--r--packages/frontend/src/scripts/copy-to-clipboard.ts2
-rw-r--r--packages/frontend/src/scripts/device-kind.ts9
-rw-r--r--packages/frontend/src/scripts/emoji-base.ts2
-rw-r--r--packages/frontend/src/scripts/emoji-picker.ts2
-rw-r--r--packages/frontend/src/scripts/emojilist.ts9
-rw-r--r--packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts2
-rw-r--r--packages/frontend/src/scripts/extract-mentions.ts2
-rw-r--r--packages/frontend/src/scripts/extract-url-from-mfm.ts2
-rw-r--r--packages/frontend/src/scripts/focus.ts2
-rw-r--r--packages/frontend/src/scripts/form.ts20
-rw-r--r--packages/frontend/src/scripts/format-time-string.ts2
-rw-r--r--packages/frontend/src/scripts/gen-search-query.ts4
-rw-r--r--packages/frontend/src/scripts/get-account-from-id.ts2
-rw-r--r--packages/frontend/src/scripts/get-drive-file-menu.ts13
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts85
-rw-r--r--packages/frontend/src/scripts/get-note-summary.ts10
-rw-r--r--packages/frontend/src/scripts/get-user-menu.ts20
-rw-r--r--packages/frontend/src/scripts/get-user-name.ts2
-rw-r--r--packages/frontend/src/scripts/hotkey.ts2
-rw-r--r--packages/frontend/src/scripts/i18n.ts294
-rw-r--r--packages/frontend/src/scripts/idb-proxy.ts2
-rw-r--r--packages/frontend/src/scripts/idle-render.ts2
-rw-r--r--packages/frontend/src/scripts/init-chart.ts2
-rw-r--r--packages/frontend/src/scripts/initialize-sw.ts2
-rw-r--r--packages/frontend/src/scripts/install-plugin.ts5
-rw-r--r--packages/frontend/src/scripts/install-theme.ts2
-rw-r--r--packages/frontend/src/scripts/intl-const.ts6
-rw-r--r--packages/frontend/src/scripts/is-device-darkmode.ts2
-rw-r--r--packages/frontend/src/scripts/isFfVisibleForMe.ts2
-rw-r--r--packages/frontend/src/scripts/keycode.ts2
-rw-r--r--packages/frontend/src/scripts/langmap.ts2
-rw-r--r--packages/frontend/src/scripts/login-id.ts2
-rw-r--r--packages/frontend/src/scripts/lookup-user.ts7
-rw-r--r--packages/frontend/src/scripts/lookup.ts7
-rw-r--r--packages/frontend/src/scripts/media-proxy.ts2
-rw-r--r--packages/frontend/src/scripts/merge.ts35
-rw-r--r--packages/frontend/src/scripts/mfm-function-picker.ts2
-rw-r--r--packages/frontend/src/scripts/misskey-api.ts (renamed from packages/frontend/src/scripts/api.ts)28
-rw-r--r--packages/frontend/src/scripts/navigator.ts2
-rw-r--r--packages/frontend/src/scripts/nyaize.ts2
-rw-r--r--packages/frontend/src/scripts/page-metadata.ts74
-rw-r--r--packages/frontend/src/scripts/physics.ts2
-rw-r--r--packages/frontend/src/scripts/please-login.ts2
-rw-r--r--packages/frontend/src/scripts/popout.ts2
-rw-r--r--packages/frontend/src/scripts/popup-position.ts4
-rw-r--r--packages/frontend/src/scripts/post-message.ts2
-rw-r--r--packages/frontend/src/scripts/reaction-picker.ts8
-rw-r--r--packages/frontend/src/scripts/safe-parse.ts11
-rw-r--r--packages/frontend/src/scripts/safe-uri-decode.ts2
-rw-r--r--packages/frontend/src/scripts/scroll.ts2
-rw-r--r--packages/frontend/src/scripts/select-file.ts5
-rw-r--r--packages/frontend/src/scripts/show-moved-dialog.ts2
-rw-r--r--packages/frontend/src/scripts/show-suspended-dialog.ts2
-rw-r--r--packages/frontend/src/scripts/shuffle.ts2
-rw-r--r--packages/frontend/src/scripts/snowfall-effect.ts26
-rw-r--r--packages/frontend/src/scripts/sound.ts109
-rw-r--r--packages/frontend/src/scripts/sticky-sidebar.ts2
-rw-r--r--packages/frontend/src/scripts/test-utils.ts2
-rw-r--r--packages/frontend/src/scripts/theme-editor.ts2
-rw-r--r--packages/frontend/src/scripts/theme.ts12
-rw-r--r--packages/frontend/src/scripts/time.ts2
-rw-r--r--packages/frontend/src/scripts/timezones.ts2
-rw-r--r--packages/frontend/src/scripts/touch.ts6
-rw-r--r--packages/frontend/src/scripts/unison-reload.ts2
-rw-r--r--packages/frontend/src/scripts/upload.ts4
-rw-r--r--packages/frontend/src/scripts/upload/compress-config.ts6
-rw-r--r--packages/frontend/src/scripts/upload/isWebpSupported.ts2
-rw-r--r--packages/frontend/src/scripts/url.ts2
-rw-r--r--packages/frontend/src/scripts/use-chart-tooltip.ts2
-rw-r--r--packages/frontend/src/scripts/use-document-visibility.ts2
-rw-r--r--packages/frontend/src/scripts/use-interval.ts14
-rw-r--r--packages/frontend/src/scripts/use-leave-guard.ts2
-rw-r--r--packages/frontend/src/scripts/use-note-capture.ts8
-rw-r--r--packages/frontend/src/scripts/use-tooltip.ts2
-rw-r--r--packages/frontend/src/scripts/worker-multi-dispatch.ts2
93 files changed, 866 insertions, 280 deletions
diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts
index e7585fcf81..f5d0ab559f 100644
--- a/packages/frontend/src/scripts/achievements.ts
+++ b/packages/frontend/src/scripts/achievements.ts
@@ -1,9 +1,9 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
import { $i } from '@/account.js';
export const ACHIEVEMENT_TYPES = [
@@ -83,6 +83,8 @@ export const ACHIEVEMENT_TYPES = [
'brainDiver',
'smashTestNotificationButton',
'tutorialCompleted',
+ 'bubbleGameExplodingHead',
+ 'bubbleGameDoubleExplodingHead',
] as const;
export const ACHIEVEMENT_BADGES = {
@@ -466,6 +468,16 @@ export const ACHIEVEMENT_BADGES = {
bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))',
frame: 'bronze',
},
+ 'bubbleGameExplodingHead': {
+ img: '/fluent-emoji/1f92f.png',
+ bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))',
+ frame: 'bronze',
+ },
+ 'bubbleGameDoubleExplodingHead': {
+ img: '/fluent-emoji/1f92f.png',
+ bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))',
+ frame: 'silver',
+ },
/* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107>
} as const satisfies Record<typeof ACHIEVEMENT_TYPES[number], {
img: string;
@@ -489,7 +501,7 @@ export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) {
window.setTimeout(() => {
claimingQueue.delete(type);
}, 500);
- os.api('i/claim-achievement', { name: type });
+ misskeyApi('i/claim-achievement', { name: type });
}
if (_DEV_) {
diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts
index 038ae23109..98a0c61752 100644
--- a/packages/frontend/src/scripts/aiscript/api.ts
+++ b/packages/frontend/src/scripts/aiscript/api.ts
@@ -1,16 +1,27 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { utils, values } from '@syuilo/aiscript';
import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
import { $i } from '@/account.js';
import { miLocalStorage } from '@/local-storage.js';
import { customEmojis } from '@/custom-emojis.js';
import { url, lang } from '@/config.js';
import { nyaize } from '@/scripts/nyaize.js';
+export function aiScriptReadline(q: string): Promise<string> {
+ return new Promise(ok => {
+ os.inputText({
+ title: q,
+ }).then(({ result: a }) => {
+ ok(a ?? '');
+ });
+ });
+}
+
export function createAiScriptEnv(opts) {
return {
USER_ID: $i ? values.STR($i.id) : values.NULL,
@@ -44,7 +55,7 @@ export function createAiScriptEnv(opts) {
if (typeof token.value !== 'string') throw new Error('invalid token');
}
const actualToken: string|null = token?.value ?? opts.token ?? null;
- return os.api(ep.value, utils.valToJs(param), actualToken).then(res => {
+ return misskeyApi(ep.value, utils.valToJs(param), actualToken).then(res => {
return utils.jsToVal(res);
}, err => {
return values.ERROR('request_failed', utils.jsToVal(err));
diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts
index 08ba1e6d9b..f2493264d3 100644
--- a/packages/frontend/src/scripts/aiscript/ui.ts
+++ b/packages/frontend/src/scripts/aiscript/ui.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -218,7 +218,7 @@ function getTextOptions(def: values.Value | undefined): Omit<AsUiText, 'id' | 't
};
}
-function getMfmOptions(def: values.Value | undefined): Omit<AsUiMfm, 'id' | 'type'> {
+function getMfmOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiMfm, 'id' | 'type'> {
utils.assertObject(def);
const text = def.value.get('text');
@@ -241,7 +241,7 @@ function getMfmOptions(def: values.Value | undefined): Omit<AsUiMfm, 'id' | 'typ
color: color?.value,
font: font?.value,
onClickEv: (evId: string) => {
- if (onClickEv) call(onClickEv, values.STR(evId));
+ if (onClickEv) call(onClickEv, [values.STR(evId)]);
},
};
}
diff --git a/packages/frontend/src/scripts/array.ts b/packages/frontend/src/scripts/array.ts
index 082703a450..b3d76e149f 100644
--- a/packages/frontend/src/scripts/array.ts
+++ b/packages/frontend/src/scripts/array.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index 2a9a42ace5..fe515d81a1 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -8,18 +8,18 @@ import getCaretCoordinates from 'textarea-caret';
import { toASCII } from 'punycode/';
import { popup } from '@/os.js';
-export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag';
+export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag' | 'mfmParam';
export class Autocomplete {
private suggestion: {
x: Ref<number>;
y: Ref<number>;
- q: Ref<string | null>;
+ q: Ref<any>;
close: () => void;
} | null;
private textarea: HTMLInputElement | HTMLTextAreaElement;
private currentType: string;
- private textRef: Ref<string>;
+ private textRef: Ref<string | number | null>;
private opening: boolean;
private onlyType: SuggestionType[];
@@ -38,7 +38,7 @@ export class Autocomplete {
/**
* 対象のテキストエリアを与えてインスタンスを初期化します。
*/
- constructor(textarea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, onlyType?: SuggestionType[]) {
+ constructor(textarea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string | number | null>, onlyType?: SuggestionType[]) {
//#region BIND
this.onInput = this.onInput.bind(this);
this.complete = this.complete.bind(this);
@@ -49,7 +49,7 @@ export class Autocomplete {
this.textarea = textarea;
this.textRef = textRef;
this.opening = false;
- this.onlyType = onlyType ?? ['user', 'hashtag', 'emoji', 'mfmTag'];
+ this.onlyType = onlyType ?? ['user', 'hashtag', 'emoji', 'mfmTag', 'mfmParam'];
this.attach();
}
@@ -80,6 +80,7 @@ export class Autocomplete {
const hashtagIndex = text.lastIndexOf('#');
const emojiIndex = text.lastIndexOf(':');
const mfmTagIndex = text.lastIndexOf('$');
+ const mfmParamIndex = text.lastIndexOf('.');
const max = Math.max(
mentionIndex,
@@ -94,7 +95,8 @@ export class Autocomplete {
const isMention = mentionIndex !== -1;
const isHashtag = hashtagIndex !== -1;
- const isMfmTag = mfmTagIndex !== -1;
+ const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.');
+ const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
let opened = false;
@@ -134,6 +136,17 @@ export class Autocomplete {
}
}
+ if (isMfmParam && !opened && this.onlyType.includes('mfmParam')) {
+ const mfmParam = text.substring(mfmParamIndex + 1);
+ if (!mfmParam.includes(' ')) {
+ this.open('mfmParam', {
+ tag: text.substring(mfmTagIndex + 2, mfmParamIndex),
+ params: mfmParam.split(','),
+ });
+ opened = true;
+ }
+ }
+
if (!opened) {
this.close();
}
@@ -142,7 +155,7 @@ export class Autocomplete {
/**
* サジェストを提示します。
*/
- private async open(type: string, q: string | null) {
+ private async open(type: string, q: any) {
if (type !== this.currentType) {
this.close();
}
@@ -280,6 +293,22 @@ export class Autocomplete {
const pos = trimmedBefore.length + (value.length + 3);
this.textarea.setSelectionRange(pos, pos);
});
+ } else if (type === 'mfmParam') {
+ const source = this.text;
+
+ const before = source.substring(0, caret);
+ const trimmedBefore = before.substring(0, before.lastIndexOf('.'));
+ const after = source.substring(caret);
+
+ // 挿入
+ this.text = `${trimmedBefore}.${value}${after}`;
+
+ // キャレットを戻す
+ nextTick(() => {
+ this.textarea.focus();
+ const pos = trimmedBefore.length + (value.length + 1);
+ this.textarea.setSelectionRange(pos, pos);
+ });
}
}
}
diff --git a/packages/frontend/src/scripts/cache.ts b/packages/frontend/src/scripts/cache.ts
index 12347cf4b1..0fbdf34d5d 100644
--- a/packages/frontend/src/scripts/cache.ts
+++ b/packages/frontend/src/scripts/cache.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/chart-legend.ts b/packages/frontend/src/scripts/chart-legend.ts
index e91908e0cb..2d534f60c1 100644
--- a/packages/frontend/src/scripts/chart-legend.ts
+++ b/packages/frontend/src/scripts/chart-legend.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/chart-vline.ts b/packages/frontend/src/scripts/chart-vline.ts
index 336ec6cfbb..24e41245e7 100644
--- a/packages/frontend/src/scripts/chart-vline.ts
+++ b/packages/frontend/src/scripts/chart-vline.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/check-reaction-permissions.ts b/packages/frontend/src/scripts/check-reaction-permissions.ts
new file mode 100644
index 0000000000..c9d2a5bfc6
--- /dev/null
+++ b/packages/frontend/src/scripts/check-reaction-permissions.ts
@@ -0,0 +1,8 @@
+import * as Misskey from 'misskey-js';
+
+export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple): boolean {
+ const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
+ return !(emoji.localOnly && note.user.host !== me.host)
+ && !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote'))
+ && (roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id)));
+}
diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts
index 5ac19c8d5b..67e896b4b9 100644
--- a/packages/frontend/src/scripts/check-word-mute.ts
+++ b/packages/frontend/src/scripts/check-word-mute.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/clicker-game.ts b/packages/frontend/src/scripts/clicker-game.ts
index 5ad076e5ef..f9c4bc1829 100644
--- a/packages/frontend/src/scripts/clicker-game.ts
+++ b/packages/frontend/src/scripts/clicker-game.ts
@@ -1,10 +1,10 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ref, computed } from 'vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
type SaveData = {
gameVersion: number;
@@ -23,7 +23,7 @@ let prev = '';
export async function load() {
try {
- saveData.value = await os.api('i/registry/get', {
+ saveData.value = await misskeyApi('i/registry/get', {
scope: ['clickerGame'],
key: 'saveData',
});
@@ -63,7 +63,7 @@ export async function save() {
const current = JSON.stringify(saveData.value);
if (current === prev) return;
- await os.api('i/registry/set', {
+ await misskeyApi('i/registry/set', {
scope: ['clickerGame'],
key: 'saveData',
value: saveData.value,
diff --git a/packages/frontend/src/scripts/clone.ts b/packages/frontend/src/scripts/clone.ts
index 96b53684f3..ea8eea14b5 100644
--- a/packages/frontend/src/scripts/clone.ts
+++ b/packages/frontend/src/scripts/clone.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -8,15 +8,15 @@
// あと、Vue RefをIndexedDBに保存しようとしてstructredCloneを使ったらエラーになった
// https://github.com/misskey-dev/misskey/pull/8098#issuecomment-1114144045
-type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[];
+export type Cloneable = string | number | boolean | null | undefined | { [key: string]: Cloneable } | { [key: number]: Cloneable } | { [key: symbol]: Cloneable } | Cloneable[];
export function deepClone<T extends Cloneable>(x: T): T {
if (typeof x === 'object') {
if (x === null) return x;
if (Array.isArray(x)) return x.map(deepClone) as T;
- const obj = {} as Record<string, Cloneable>;
+ const obj = {} as Record<string | number | symbol, Cloneable>;
for (const [k, v] of Object.entries(x)) {
- obj[k] = deepClone(v);
+ obj[k] = v === undefined ? undefined : deepClone(v);
}
return obj as T;
} else {
diff --git a/packages/frontend/src/scripts/code-highlighter.ts b/packages/frontend/src/scripts/code-highlighter.ts
index 957669122e..2733897bab 100644
--- a/packages/frontend/src/scripts/code-highlighter.ts
+++ b/packages/frontend/src/scripts/code-highlighter.ts
@@ -1,10 +1,51 @@
-import { setWasm, setCDN, Highlighter, getHighlighter as _getHighlighter } from 'shiki';
-
-setWasm('/assets/shiki/dist/onig.wasm');
-setCDN('/assets/shiki/');
+import { bundledThemesInfo } from 'shiki';
+import { getHighlighterCore, loadWasm } from 'shiki/core';
+import darkPlus from 'shiki/themes/dark-plus.mjs';
+import { unique } from './array.js';
+import { deepClone } from './clone.js';
+import { deepMerge } from './merge.js';
+import type { Highlighter, LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from 'shiki';
+import { ColdDeviceStorage } from '@/store.js';
+import lightTheme from '@/themes/_light.json5';
+import darkTheme from '@/themes/_dark.json5';
let _highlighter: Highlighter | null = null;
+export async function getTheme(mode: 'light' | 'dark', getName: true): Promise<string>;
+export async function getTheme(mode: 'light' | 'dark', getName?: false): Promise<ThemeRegistration | ThemeRegistrationRaw>;
+export async function getTheme(mode: 'light' | 'dark', getName = false): Promise<ThemeRegistration | ThemeRegistrationRaw | string | null> {
+ const theme = deepClone(ColdDeviceStorage.get(mode === 'light' ? 'lightTheme' : 'darkTheme'));
+
+ if (theme.base) {
+ const base = [lightTheme, darkTheme].find(x => x.id === theme.base);
+ if (base && base.codeHighlighter) theme.codeHighlighter = Object.assign({}, base.codeHighlighter, theme.codeHighlighter);
+ }
+
+ if (theme.codeHighlighter) {
+ let _res: ThemeRegistration = {};
+ if (theme.codeHighlighter.base === '_none_') {
+ _res = deepClone(theme.codeHighlighter.overrides);
+ } else {
+ const base = await bundledThemesInfo.find(t => t.id === theme.codeHighlighter!.base)?.import() ?? darkPlus;
+ _res = deepMerge(theme.codeHighlighter.overrides ?? {}, 'default' in base ? base.default : base);
+ }
+ if (_res.name == null) {
+ _res.name = theme.id;
+ }
+ _res.type = mode;
+
+ if (getName) {
+ return _res.name;
+ }
+ return _res;
+ }
+
+ if (getName) {
+ return 'dark-plus';
+ }
+ return darkPlus;
+}
+
export async function getHighlighter(): Promise<Highlighter> {
if (!_highlighter) {
return await initHighlighter();
@@ -13,16 +54,36 @@ export async function getHighlighter(): Promise<Highlighter> {
}
export async function initHighlighter() {
- const highlighter = await _getHighlighter({
- theme: 'dark-plus',
- langs: ['js'],
+ const aiScriptGrammar = await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json');
+
+ await loadWasm(import('shiki/onig.wasm?init'));
+
+ // テーマの重複を消す
+ const themes = unique([
+ darkPlus,
+ ...(await Promise.all([getTheme('light'), getTheme('dark')])),
+ ]);
+
+ const highlighter = await getHighlighterCore({
+ themes,
+ langs: [
+ import('shiki/langs/javascript.mjs'),
+ aiScriptGrammar.default as unknown as LanguageRegistration,
+ ],
+ });
+
+ ColdDeviceStorage.watch('lightTheme', async () => {
+ const newTheme = await getTheme('light');
+ if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) {
+ highlighter.loadTheme(newTheme);
+ }
});
- await highlighter.loadLanguage({
- path: 'languages/aiscript.tmLanguage.json',
- id: 'aiscript',
- scopeName: 'source.aiscript',
- aliases: ['is', 'ais'],
+ ColdDeviceStorage.watch('darkTheme', async () => {
+ const newTheme = await getTheme('dark');
+ if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) {
+ highlighter.loadTheme(newTheme);
+ }
});
_highlighter = highlighter;
diff --git a/packages/frontend/src/scripts/collapsed.ts b/packages/frontend/src/scripts/collapsed.ts
index 57e6ecf5b5..237bd37c7a 100644
--- a/packages/frontend/src/scripts/collapsed.ts
+++ b/packages/frontend/src/scripts/collapsed.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/collect-page-vars.ts b/packages/frontend/src/scripts/collect-page-vars.ts
index 79356e60eb..5096c0669e 100644
--- a/packages/frontend/src/scripts/collect-page-vars.ts
+++ b/packages/frontend/src/scripts/collect-page-vars.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/color.ts b/packages/frontend/src/scripts/color.ts
index 25ef41d9b7..a11255ffd1 100644
--- a/packages/frontend/src/scripts/color.ts
+++ b/packages/frontend/src/scripts/color.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/confetti.ts b/packages/frontend/src/scripts/confetti.ts
index b394ba3e2a..8e53a6ceeb 100644
--- a/packages/frontend/src/scripts/confetti.ts
+++ b/packages/frontend/src/scripts/confetti.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/contains.ts b/packages/frontend/src/scripts/contains.ts
index b50ce4128c..6137c06e85 100644
--- a/packages/frontend/src/scripts/contains.ts
+++ b/packages/frontend/src/scripts/contains.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/copy-to-clipboard.ts b/packages/frontend/src/scripts/copy-to-clipboard.ts
index 3884d4a20a..216c0464b3 100644
--- a/packages/frontend/src/scripts/copy-to-clipboard.ts
+++ b/packages/frontend/src/scripts/copy-to-clipboard.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/device-kind.ts b/packages/frontend/src/scripts/device-kind.ts
index 3843052a24..7c33f8ccee 100644
--- a/packages/frontend/src/scripts/device-kind.ts
+++ b/packages/frontend/src/scripts/device-kind.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -11,6 +11,13 @@ const ua = navigator.userAgent.toLowerCase();
const isTablet = /ipad/.test(ua) || (/mobile|iphone|android/.test(ua) && window.innerWidth > 700);
const isSmartphone = !isTablet && /mobile|iphone|android/.test(ua);
+const isIPhone = /iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1;
+// navigator.platform may be deprecated but this check is still required
+const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;
+const isIos = /ipad|iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1;
+
+export const isFullscreenNotSupported = isIPhone || isIos;
+
export const deviceKind: 'smartphone' | 'tablet' | 'desktop' = defaultStore.state.overridedDeviceKind ? defaultStore.state.overridedDeviceKind
: isSmartphone ? 'smartphone'
: isTablet ? 'tablet'
diff --git a/packages/frontend/src/scripts/emoji-base.ts b/packages/frontend/src/scripts/emoji-base.ts
index 46a13462a1..a01540a3e4 100644
--- a/packages/frontend/src/scripts/emoji-base.ts
+++ b/packages/frontend/src/scripts/emoji-base.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/emoji-picker.ts b/packages/frontend/src/scripts/emoji-picker.ts
index f87c3f6fb2..14b5cbf35e 100644
--- a/packages/frontend/src/scripts/emoji-picker.ts
+++ b/packages/frontend/src/scripts/emoji-picker.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index 8885bf4b7f..54d45e025f 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -36,7 +36,8 @@ for (let i = 0; i < emojilist.length; i++) {
export const emojiCharByCategory = _charGroupByCategory;
export function getEmojiName(char: string): string | null {
- const idx = _indexByChar.get(char);
+ // Colorize it because emojilist.json assumes that
+ const idx = _indexByChar.get(colorizeEmoji(char));
if (idx == null) {
return null;
} else {
@@ -44,6 +45,10 @@ export function getEmojiName(char: string): string | null {
}
}
+export function colorizeEmoji(char: string) {
+ return char.length === 1 ? `${char}\uFE0F` : char;
+}
+
export interface CustomEmojiFolderTree {
value: string;
category: string;
diff --git a/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts b/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts
index 57b296ab2a..992f6e9a16 100644
--- a/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts
+++ b/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/extract-mentions.ts b/packages/frontend/src/scripts/extract-mentions.ts
index 74ce45d324..d518562053 100644
--- a/packages/frontend/src/scripts/extract-mentions.ts
+++ b/packages/frontend/src/scripts/extract-mentions.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/extract-url-from-mfm.ts b/packages/frontend/src/scripts/extract-url-from-mfm.ts
index c1ed9338f8..d5654ba850 100644
--- a/packages/frontend/src/scripts/extract-url-from-mfm.ts
+++ b/packages/frontend/src/scripts/extract-url-from-mfm.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/focus.ts b/packages/frontend/src/scripts/focus.ts
index 6a31ebd431..ea6ee61c88 100644
--- a/packages/frontend/src/scripts/focus.ts
+++ b/packages/frontend/src/scripts/focus.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts
index 222fd9b0b7..26a027f461 100644
--- a/packages/frontend/src/scripts/form.ts
+++ b/packages/frontend/src/scripts/form.ts
@@ -1,9 +1,13 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-type EnumItem = string | {label: string; value: string;};
+type EnumItem = string | {
+ label: string;
+ value: string;
+};
+
export type FormItem = {
label?: string;
type: 'string';
@@ -38,14 +42,21 @@ export type FormItem = {
}[];
} | {
label?: string;
+ type: 'range';
+ default: number | null;
+ step: number;
+ min: number;
+ max: number;
+} | {
+ label?: string;
type: 'object';
default: Record<string, unknown> | null;
- hidden: true;
+ hidden: boolean;
} | {
label?: string;
type: 'array';
default: unknown[] | null;
- hidden: true;
+ hidden: boolean;
};
export type Form = Record<string, FormItem>;
@@ -55,6 +66,7 @@ type GetItemType<Item extends FormItem> =
Item['type'] extends 'number' ? number :
Item['type'] extends 'boolean' ? boolean :
Item['type'] extends 'radio' ? unknown :
+ Item['type'] extends 'range' ? number :
Item['type'] extends 'enum' ? string :
Item['type'] extends 'array' ? unknown[] :
Item['type'] extends 'object' ? Record<string, unknown>
diff --git a/packages/frontend/src/scripts/format-time-string.ts b/packages/frontend/src/scripts/format-time-string.ts
index 918996dd10..35ad77d982 100644
--- a/packages/frontend/src/scripts/format-time-string.ts
+++ b/packages/frontend/src/scripts/format-time-string.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/gen-search-query.ts b/packages/frontend/src/scripts/gen-search-query.ts
index 54654980f2..60884d08d3 100644
--- a/packages/frontend/src/scripts/gen-search-query.ts
+++ b/packages/frontend/src/scripts/gen-search-query.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -18,7 +18,7 @@ export async function genSearchQuery(v: any, q: string) {
host = at;
}
} else {
- const user = await v.os.api('users/show', Misskey.acct.parse(at)).catch(x => null);
+ const user = await v.api('users/show', Misskey.acct.parse(at)).catch(x => null);
if (user) {
userId = user.id;
} else {
diff --git a/packages/frontend/src/scripts/get-account-from-id.ts b/packages/frontend/src/scripts/get-account-from-id.ts
index 346d283572..40afa10f2d 100644
--- a/packages/frontend/src/scripts/get-account-from-id.ts
+++ b/packages/frontend/src/scripts/get-account-from-id.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index f8496f0711..7aca5f83b2 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -8,6 +8,7 @@ import { defineAsyncComponent } from 'vue';
import { i18n } from '@/i18n.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
import { MenuItem } from '@/types/menu.js';
import { defaultStore } from '@/store.js';
@@ -18,7 +19,7 @@ function rename(file: Misskey.entities.DriveFile) {
default: file.name,
}).then(({ canceled, result: name }) => {
if (canceled) return;
- os.api('drive/files/update', {
+ misskeyApi('drive/files/update', {
fileId: file.id,
name: name,
});
@@ -31,7 +32,7 @@ function describe(file: Misskey.entities.DriveFile) {
file: file,
}, {
done: caption => {
- os.api('drive/files/update', {
+ misskeyApi('drive/files/update', {
fileId: file.id,
comment: caption.length === 0 ? null : caption,
});
@@ -40,7 +41,7 @@ function describe(file: Misskey.entities.DriveFile) {
}
function toggleSensitive(file: Misskey.entities.DriveFile) {
- os.api('drive/files/update', {
+ misskeyApi('drive/files/update', {
fileId: file.id,
isSensitive: !file.isSensitive,
}).catch(err => {
@@ -65,11 +66,11 @@ function addApp() {
async function deleteFile(file: Misskey.entities.DriveFile) {
const { canceled } = await os.confirm({
type: 'warning',
- text: i18n.t('driveFileDeleteConfirm', { name: file.name }),
+ text: i18n.tsx.driveFileDeleteConfirm({ name: file.name }),
});
if (canceled) return;
- os.api('drive/files/delete', {
+ misskeyApi('drive/files/delete', {
fileId: file.id,
});
}
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 7130e69279..b273bd36f3 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -1,15 +1,16 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { defineAsyncComponent, Ref } from 'vue';
+import { defineAsyncComponent, Ref, ShallowRef } from 'vue';
import * as Misskey from 'misskey-js';
import { claimAchievement } from './achievements.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { url } from '@/config.js';
import { defaultStore, noteActions } from '@/store.js';
@@ -35,18 +36,18 @@ export async function getNoteClipMenu(props: {
const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
const clips = await clipsCache.fetch();
- return [...clips.map(clip => ({
+ const menu: MenuItem[] = [...clips.map(clip => ({
text: clip.name,
action: () => {
claimAchievement('noteClipped1');
os.promiseDialog(
- os.api('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
+ misskeyApi('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
null,
async (err) => {
if (err.id === '734806c4-542c-463a-9311-15c512803965') {
const confirm = await os.confirm({
type: 'warning',
- text: i18n.t('confirmToUnclipAlreadyClippedNote', { name: clip.name }),
+ text: i18n.tsx.confirmToUnclipAlreadyClippedNote({ name: clip.name }),
});
if (!confirm.canceled) {
os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
@@ -92,6 +93,8 @@ export async function getNoteClipMenu(props: {
os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id });
},
}];
+
+ return menu;
}
export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): MenuItem {
@@ -99,10 +102,13 @@ export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): Men
icon: 'ti ti-exclamation-circle',
text,
action: (): void => {
- const u = note.url ?? note.uri ?? `${url}/notes/${note.id}`;
+ const localUrl = `${url}/notes/${note.id}`;
+ let noteInfo = '';
+ if (note.url ?? note.uri != null) noteInfo = `Note: ${note.url ?? note.uri}\n`;
+ noteInfo += `Local Note: ${localUrl}\n`;
os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
user: note.user,
- initialComment: `Note: ${u}\n-----\n`,
+ initialComment: `${noteInfo}-----\n`,
}, {}, 'closed');
},
};
@@ -121,7 +127,6 @@ export function getCopyNoteLinkMenu(note: Misskey.entities.Note, text: string):
export function getNoteMenu(props: {
note: Misskey.entities.Note;
- menuButton: Ref<HTMLElement>;
translation: Ref<Misskey.entities.NotesTranslateResponse | null>;
translating: Ref<boolean>;
isDeleted: Ref<boolean>;
@@ -145,7 +150,7 @@ export function getNoteMenu(props: {
}).then(({ canceled }) => {
if (canceled) return;
- os.api('notes/delete', {
+ misskeyApi('notes/delete', {
noteId: appearNote.id,
});
@@ -162,7 +167,7 @@ export function getNoteMenu(props: {
}).then(({ canceled }) => {
if (canceled) return;
- os.api('notes/delete', {
+ misskeyApi('notes/delete', {
noteId: appearNote.id,
});
@@ -230,7 +235,7 @@ export function getNoteMenu(props: {
function share(): void {
navigator.share({
- title: i18n.t('noteOf', { user: appearNote.user.name }),
+ title: i18n.tsx.noteOf({ user: appearNote.user.name }),
text: appearNote.text,
url: `${url}/notes/${appearNote.id}`,
});
@@ -243,7 +248,7 @@ export function getNoteMenu(props: {
async function translate(): Promise<void> {
if (props.translation.value != null) return;
props.translating.value = true;
- const res = await os.api('notes/translate', {
+ const res = await misskeyApi('notes/translate', {
noteId: appearNote.id,
targetLang: miLocalStorage.getItem('lang') ?? navigator.language,
});
@@ -253,7 +258,7 @@ export function getNoteMenu(props: {
let menu: MenuItem[];
if ($i) {
- const statePromise = os.api('notes/state', {
+ const statePromise = misskeyApi('notes/state', {
noteId: appearNote.id,
});
@@ -330,7 +335,7 @@ export function getNoteMenu(props: {
icon: 'ti ti-user',
text: i18n.ts.user,
children: async () => {
- const user = appearNote.userId === $i?.id ? $i : await os.api('users/show', { userId: appearNote.userId });
+ const user = appearNote.userId === $i?.id ? $i : await misskeyApi('users/show', { userId: appearNote.userId });
const { menu, cleanup } = getUserMenu(user);
cleanups.push(cleanup);
return menu;
@@ -352,6 +357,42 @@ export function getNoteMenu(props: {
]
: []
),
+ ...(appearNote.channel && (appearNote.channel.userId === $i.id || $i.isModerator || $i.isAdmin) ? [
+ { type: 'divider' },
+ {
+ type: 'parent' as const,
+ icon: 'ti ti-device-tv',
+ text: i18n.ts.channel,
+ children: async () => {
+ const channelChildMenu = [] as MenuItem[];
+
+ const channel = await misskeyApi('channels/show', { channelId: appearNote.channel!.id });
+
+ if (channel.pinnedNoteIds.includes(appearNote.id)) {
+ channelChildMenu.push({
+ icon: 'ti ti-pinned-off',
+ text: i18n.ts.unpin,
+ action: () => os.apiWithDialog('channels/update', {
+ channelId: appearNote.channel!.id,
+ pinnedNoteIds: channel.pinnedNoteIds.filter(id => id !== appearNote.id),
+ }),
+ });
+ } else {
+ channelChildMenu.push({
+ icon: 'ti ti-pin',
+ text: i18n.ts.pin,
+ action: () => os.apiWithDialog('channels/update', {
+ channelId: appearNote.channel!.id,
+ pinnedNoteIds: [...channel.pinnedNoteIds, appearNote.id],
+ }),
+ });
+ }
+ return channelChildMenu;
+ },
+ },
+ ]
+ : []
+ ),
...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
{ type: 'divider' },
appearNote.userId === $i.id ? {
@@ -389,7 +430,7 @@ export function getNoteMenu(props: {
}
if (noteActions.length > 0) {
- menu = menu.concat([{ type: "divider" }, ...noteActions.map(action => ({
+ menu = menu.concat([{ type: 'divider' }, ...noteActions.map(action => ({
icon: 'ti ti-plug',
text: action.title,
action: () => {
@@ -399,7 +440,7 @@ export function getNoteMenu(props: {
}
if (defaultStore.state.devMode) {
- menu = menu.concat([{ type: "divider" }, {
+ menu = menu.concat([{ type: 'divider' }, {
icon: 'ti ti-id',
text: i18n.ts.copyNoteId,
action: () => {
@@ -434,7 +475,7 @@ function smallerVisibility(a: Visibility | string, b: Visibility | string): Visi
export function getRenoteMenu(props: {
note: Misskey.entities.Note;
- renoteButton: Ref<HTMLElement>;
+ renoteButton: ShallowRef<HTMLElement | undefined>;
mock?: boolean;
}) {
const isRenote = (
@@ -454,7 +495,7 @@ export function getRenoteMenu(props: {
text: i18n.ts.inChannelRenote,
icon: 'ti ti-repeat',
action: () => {
- const el = props.renoteButton.value as HTMLElement | null | undefined;
+ const el = props.renoteButton.value;
if (el) {
const rect = el.getBoundingClientRect();
const x = rect.left + (el.offsetWidth / 2);
@@ -463,7 +504,7 @@ export function getRenoteMenu(props: {
}
if (!props.mock) {
- os.api('notes/create', {
+ misskeyApi('notes/create', {
renoteId: appearNote.id,
channelId: appearNote.channelId,
}).then(() => {
@@ -490,7 +531,7 @@ export function getRenoteMenu(props: {
text: i18n.ts.renote,
icon: 'ti ti-repeat',
action: () => {
- const el = props.renoteButton.value as HTMLElement | null | undefined;
+ const el = props.renoteButton.value;
if (el) {
const rect = el.getBoundingClientRect();
const x = rect.left + (el.offsetWidth / 2);
@@ -508,7 +549,7 @@ export function getRenoteMenu(props: {
}
if (!props.mock) {
- os.api('notes/create', {
+ misskeyApi('notes/create', {
localOnly,
visibility,
renoteId: appearNote.id,
@@ -530,7 +571,7 @@ export function getRenoteMenu(props: {
const renoteItems = [
...normalRenoteItems,
- ...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [{ type: 'divider' }] : [],
+ ...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [{ type: 'divider' }] as MenuItem[] : [],
...channelRenoteItems,
];
diff --git a/packages/frontend/src/scripts/get-note-summary.ts b/packages/frontend/src/scripts/get-note-summary.ts
index 1fd9f04d46..6fd9947ac1 100644
--- a/packages/frontend/src/scripts/get-note-summary.ts
+++ b/packages/frontend/src/scripts/get-note-summary.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -10,7 +10,11 @@ import { i18n } from '@/i18n.js';
* 投稿を表す文字列を取得します。
* @param {*} note (packされた)投稿
*/
-export const getNoteSummary = (note: Misskey.entities.Note): string => {
+export const getNoteSummary = (note?: Misskey.entities.Note | null): string => {
+ if (note == null) {
+ return '';
+ }
+
if (note.deletedAt) {
return `(${i18n.ts.deletedNote})`;
}
@@ -30,7 +34,7 @@ export const getNoteSummary = (note: Misskey.entities.Note): string => {
// ファイルが添付されているとき
if ((note.files || []).length !== 0) {
- summary += ` (${i18n.t('withNFiles', { n: note.files.length })})`;
+ summary += ` (${i18n.tsx.withNFiles({ n: note.files.length })})`;
}
// 投票が添付されているとき
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 6e5c689d97..c14f75f382 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -10,13 +10,14 @@ import { i18n } from '@/i18n.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { host, url } from '@/config.js';
import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
import { defaultStore, userActions } from '@/store.js';
import { $i, iAmModerator } from '@/account.js';
-import { mainRouter } from '@/router.js';
-import { Router } from '@/nirax.js';
+import { IRouter } from '@/nirax.js';
import { antennasCache, rolesCache, userListsCache } from '@/cache.js';
+import { mainRouter } from '@/router/main.js';
-export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router = mainRouter) {
+export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter = mainRouter) {
const meId = $i ? $i.id : null;
const cleanups = [] as (() => void)[];
@@ -131,7 +132,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
}
async function editMemo(): Promise<void> {
- const userDetailed = await os.api('users/show', {
+ const userDetailed = await misskeyApi('users/show', {
userId: user.id,
});
const { canceled, result } = await os.form(i18n.ts.editMemo, {
@@ -169,7 +170,14 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
action: () => {
copyToClipboard(`${user.host ?? host}/@${user.username}.atom`);
},
- }, {
+ }, ...(user.host != null && user.url != null ? [{
+ icon: 'ti ti-external-link',
+ text: i18n.ts.showOnRemote,
+ action: () => {
+ if (user.url == null) return;
+ window.open(user.url, '_blank', 'noopener');
+ },
+ }] : []), {
icon: 'ti ti-share',
text: i18n.ts.copyProfileUrl,
action: () => {
diff --git a/packages/frontend/src/scripts/get-user-name.ts b/packages/frontend/src/scripts/get-user-name.ts
index 3ae80d7fc3..56e91abba0 100644
--- a/packages/frontend/src/scripts/get-user-name.ts
+++ b/packages/frontend/src/scripts/get-user-name.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/hotkey.ts b/packages/frontend/src/scripts/hotkey.ts
index 48c80c066b..0600bff893 100644
--- a/packages/frontend/src/scripts/hotkey.ts
+++ b/packages/frontend/src/scripts/hotkey.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/i18n.ts b/packages/frontend/src/scripts/i18n.ts
index 8e5f17f38a..c2f44a33cc 100644
--- a/packages/frontend/src/scripts/i18n.ts
+++ b/packages/frontend/src/scripts/i18n.ts
@@ -1,34 +1,294 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import type { ILocale, ParameterizedString } from '../../../../locales/index.js';
-export class I18n<T extends Record<string, any>> {
- public ts: T;
+type FlattenKeys<T extends ILocale, TPrediction> = keyof {
+ [K in keyof T as T[K] extends ILocale
+ ? FlattenKeys<T[K], TPrediction> extends infer C extends string
+ ? `${K & string}.${C}`
+ : never
+ : T[K] extends TPrediction
+ ? K
+ : never]: T[K];
+};
- constructor(locale: T) {
- this.ts = locale;
+type ParametersOf<T extends ILocale, TKey extends FlattenKeys<T, ParameterizedString>> = TKey extends `${infer K}.${infer C}`
+ // @ts-expect-error -- C は明らかに FlattenKeys<T[K], ParameterizedString> になるが、型システムはここでは TKey がドット区切りであることのコンテキストを持たないので、型システムに合法にて示すことはできない。
+ ? ParametersOf<T[K], C>
+ : TKey extends keyof T
+ ? T[TKey] extends ParameterizedString<infer P>
+ ? P
+ : never
+ : never;
+type Tsx<T extends ILocale> = {
+ readonly [K in keyof T as T[K] extends string ? never : K]: T[K] extends ParameterizedString<infer P>
+ ? (arg: { readonly [_ in P]: string | number }) => string
+ // @ts-expect-error -- 証明省略
+ : Tsx<T[K]>;
+};
+
+export class I18n<T extends ILocale> {
+ private tsxCache?: Tsx<T>;
+
+ constructor(public locale: T) {
//#region BIND
this.t = this.t.bind(this);
//#endregion
}
- // string にしているのは、ドット区切りでのパス指定を許可するため
- // なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
- public t(key: string, args?: Record<string, string | number>): string {
- try {
- let str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string;
+ public get ts(): T {
+ if (_DEV_) {
+ class Handler<TTarget extends ILocale> implements ProxyHandler<TTarget> {
+ get(target: TTarget, p: string | symbol): unknown {
+ const value = target[p as keyof TTarget];
+
+ if (typeof value === 'object') {
+ return new Proxy(value, new Handler<TTarget[keyof TTarget] & ILocale>());
+ }
+
+ if (typeof value === 'string') {
+ const parameters = Array.from(value.matchAll(/\{(\w+)\}/g), ([, parameter]) => parameter);
+
+ if (parameters.length) {
+ console.error(`Missing locale parameters: ${parameters.join(', ')} at ${String(p)}`);
+ }
+
+ return value;
+ }
+
+ console.error(`Unexpected locale key: ${String(p)}`);
+
+ return p;
+ }
+ }
+
+ return new Proxy(this.locale, new Handler());
+ }
+
+ return this.locale;
+ }
+
+ public get tsx(): Tsx<T> {
+ if (_DEV_) {
+ if (this.tsxCache) {
+ return this.tsxCache;
+ }
+
+ class Handler<TTarget extends ILocale> implements ProxyHandler<TTarget> {
+ get(target: TTarget, p: string | symbol): unknown {
+ const value = target[p as keyof TTarget];
+
+ if (typeof value === 'object') {
+ return new Proxy(value, new Handler<TTarget[keyof TTarget] & ILocale>());
+ }
+
+ if (typeof value === 'string') {
+ const quasis: string[] = [];
+ const expressions: string[] = [];
+ let cursor = 0;
+
+ while (~cursor) {
+ const start = value.indexOf('{', cursor);
+
+ if (!~start) {
+ quasis.push(value.slice(cursor));
+ break;
+ }
+
+ quasis.push(value.slice(cursor, start));
+
+ const end = value.indexOf('}', start);
+
+ expressions.push(value.slice(start + 1, end));
+
+ cursor = end + 1;
+ }
+
+ if (!expressions.length) {
+ console.error(`Unexpected locale key: ${String(p)}`);
+
+ return () => value;
+ }
+
+ return (arg) => {
+ let str = quasis[0];
+
+ for (let i = 0; i < expressions.length; i++) {
+ if (!Object.hasOwn(arg, expressions[i])) {
+ console.error(`Missing locale parameters: ${expressions[i]} at ${String(p)}`);
+ }
+
+ str += arg[expressions[i]] + quasis[i + 1];
+ }
+
+ return str;
+ };
+ }
+
+ console.error(`Unexpected locale key: ${String(p)}`);
+
+ return p;
+ }
+ }
+
+ return this.tsxCache = new Proxy(this.locale, new Handler()) as unknown as Tsx<T>;
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (this.tsxCache) {
+ return this.tsxCache;
+ }
+
+ function build(target: ILocale): Tsx<T> {
+ const result = {} as Tsx<T>;
+
+ for (const k in target) {
+ if (!Object.hasOwn(target, k)) {
+ continue;
+ }
+
+ const value = target[k as keyof typeof target];
+
+ if (typeof value === 'object') {
+ result[k] = build(value as ILocale);
+ } else if (typeof value === 'string') {
+ const quasis: string[] = [];
+ const expressions: string[] = [];
+ let cursor = 0;
+
+ while (~cursor) {
+ const start = value.indexOf('{', cursor);
+
+ if (!~start) {
+ quasis.push(value.slice(cursor));
+ break;
+ }
+
+ quasis.push(value.slice(cursor, start));
+
+ const end = value.indexOf('}', start);
+
+ expressions.push(value.slice(start + 1, end));
+
+ cursor = end + 1;
+ }
+
+ if (!expressions.length) {
+ continue;
+ }
+
+ result[k] = (arg) => {
+ let str = quasis[0];
+
+ for (let i = 0; i < expressions.length; i++) {
+ str += arg[expressions[i]] + quasis[i + 1];
+ }
+
+ return str;
+ };
+ }
+ }
+ return result;
+ }
+
+ return this.tsxCache = build(this.locale);
+ }
+
+ /**
+ * @deprecated なるべくこのメソッド使うよりも ts 直接参照の方が vue のキャッシュ効いてパフォーマンスが良いかも
+ */
+ public t<TKey extends FlattenKeys<T, string>>(key: TKey): string;
+ /**
+ * @deprecated なるべくこのメソッド使うよりも tsx 直接参照の方が vue のキャッシュ効いてパフォーマンスが良いかも
+ */
+ public t<TKey extends FlattenKeys<T, ParameterizedString>>(key: TKey, args: { readonly [_ in ParametersOf<T, TKey>]: string | number }): string;
+ public t(key: string, args?: { readonly [_: string]: string | number }) {
+ let str: string | ParameterizedString | ILocale = this.locale;
+
+ for (const k of key.split('.')) {
+ str = str[k];
- if (args) {
- for (const [k, v] of Object.entries(args)) {
- str = str.replace(`{${k}}`, v.toString());
+ if (_DEV_) {
+ if (typeof str === 'undefined') {
+ console.error(`Unexpected locale key: ${key}`);
+ return key;
}
}
- return str;
- } catch (err) {
- console.warn(`missing localization '${key}'`);
- return key;
}
+
+ if (args) {
+ if (_DEV_) {
+ const missing = Array.from((str as string).matchAll(/\{(\w+)\}/g), ([, parameter]) => parameter).filter(parameter => !Object.hasOwn(args, parameter));
+
+ if (missing.length) {
+ console.error(`Missing locale parameters: ${missing.join(', ')} at ${key}`);
+ }
+ }
+
+ for (const [k, v] of Object.entries(args)) {
+ const search = `{${k}}`;
+
+ if (_DEV_) {
+ if (!(str as string).includes(search)) {
+ console.error(`Unexpected locale parameter: ${k} at ${key}`);
+ }
+ }
+
+ str = (str as string).replace(search, v.toString());
+ }
+ }
+
+ return str;
}
}
+
+if (import.meta.vitest) {
+ const { describe, expect, it } = import.meta.vitest;
+
+ describe('i18n', () => {
+ it('t', () => {
+ const i18n = new I18n({
+ foo: 'foo',
+ bar: {
+ baz: 'baz',
+ qux: 'qux {0}' as unknown as ParameterizedString<'0'>,
+ quux: 'quux {0} {1}' as unknown as ParameterizedString<'0' | '1'>,
+ },
+ });
+
+ expect(i18n.t('foo')).toBe('foo');
+ expect(i18n.t('bar.baz')).toBe('baz');
+ expect(i18n.tsx.bar.qux({ 0: 'hoge' })).toBe('qux hoge');
+ expect(i18n.tsx.bar.quux({ 0: 'hoge', 1: 'fuga' })).toBe('quux hoge fuga');
+ });
+ it('ts', () => {
+ const i18n = new I18n({
+ foo: 'foo',
+ bar: {
+ baz: 'baz',
+ qux: 'qux {0}' as unknown as ParameterizedString<'0'>,
+ quux: 'quux {0} {1}' as unknown as ParameterizedString<'0' | '1'>,
+ },
+ });
+
+ expect(i18n.ts.foo).toBe('foo');
+ expect(i18n.ts.bar.baz).toBe('baz');
+ });
+ it('tsx', () => {
+ const i18n = new I18n({
+ foo: 'foo',
+ bar: {
+ baz: 'baz',
+ qux: 'qux {0}' as unknown as ParameterizedString<'0'>,
+ quux: 'quux {0} {1}' as unknown as ParameterizedString<'0' | '1'>,
+ },
+ });
+
+ expect(i18n.tsx.bar.qux({ 0: 'hoge' })).toBe('qux hoge');
+ expect(i18n.tsx.bar.quux({ 0: 'hoge', 1: 'fuga' })).toBe('quux hoge fuga');
+ });
+ });
+}
diff --git a/packages/frontend/src/scripts/idb-proxy.ts b/packages/frontend/src/scripts/idb-proxy.ts
index a20cfcb1d0..1ca0990ba9 100644
--- a/packages/frontend/src/scripts/idb-proxy.ts
+++ b/packages/frontend/src/scripts/idb-proxy.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/idle-render.ts b/packages/frontend/src/scripts/idle-render.ts
index ac1be50c73..6adfedcb9f 100644
--- a/packages/frontend/src/scripts/idle-render.ts
+++ b/packages/frontend/src/scripts/idle-render.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/init-chart.ts b/packages/frontend/src/scripts/init-chart.ts
index ebf27667d7..2465a14703 100644
--- a/packages/frontend/src/scripts/init-chart.ts
+++ b/packages/frontend/src/scripts/init-chart.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/initialize-sw.ts b/packages/frontend/src/scripts/initialize-sw.ts
index 007fc0f2f7..1517e4e1e8 100644
--- a/packages/frontend/src/scripts/initialize-sw.ts
+++ b/packages/frontend/src/scripts/initialize-sw.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/install-plugin.ts b/packages/frontend/src/scripts/install-plugin.ts
index 1310a0dc73..d0a8675b19 100644
--- a/packages/frontend/src/scripts/install-plugin.ts
+++ b/packages/frontend/src/scripts/install-plugin.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -10,6 +10,7 @@ import { Interpreter, Parser, utils } from '@syuilo/aiscript';
import type { Plugin } from '@/store.js';
import { ColdDeviceStorage } from '@/store.js';
import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
export type AiScriptPluginMeta = {
@@ -110,7 +111,7 @@ export async function installPlugin(code: string, meta?: AiScriptPluginMeta) {
}, {
done: async result => {
const { name, permissions } = result;
- const { token } = await os.api('miauth/gen-token', {
+ const { token } = await misskeyApi('miauth/gen-token', {
session: null,
name: name,
permission: permissions,
diff --git a/packages/frontend/src/scripts/install-theme.ts b/packages/frontend/src/scripts/install-theme.ts
index 394b642bf4..866f1225bf 100644
--- a/packages/frontend/src/scripts/install-theme.ts
+++ b/packages/frontend/src/scripts/install-theme.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/intl-const.ts b/packages/frontend/src/scripts/intl-const.ts
index ea16c9c2ae..aaa4f0a86e 100644
--- a/packages/frontend/src/scripts/intl-const.ts
+++ b/packages/frontend/src/scripts/intl-const.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -33,6 +33,10 @@ try {
}
export const dateTimeFormat = _dateTimeFormat;
+export const timeZone = dateTimeFormat.resolvedOptions().timeZone;
+
+export const hemisphere = /^(australia|pacific|antarctica|indian)\//i.test(timeZone) ? 'S' : 'N';
+
let _numberFormat: Intl.NumberFormat;
try {
_numberFormat = new Intl.NumberFormat(versatileLang);
diff --git a/packages/frontend/src/scripts/is-device-darkmode.ts b/packages/frontend/src/scripts/is-device-darkmode.ts
index badc295726..4f487c7cb9 100644
--- a/packages/frontend/src/scripts/is-device-darkmode.ts
+++ b/packages/frontend/src/scripts/is-device-darkmode.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/isFfVisibleForMe.ts b/packages/frontend/src/scripts/isFfVisibleForMe.ts
index dc0e90d20a..406404c462 100644
--- a/packages/frontend/src/scripts/isFfVisibleForMe.ts
+++ b/packages/frontend/src/scripts/isFfVisibleForMe.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/keycode.ts b/packages/frontend/src/scripts/keycode.ts
index 57bc4d19ba..bc1f485f5e 100644
--- a/packages/frontend/src/scripts/keycode.ts
+++ b/packages/frontend/src/scripts/keycode.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/langmap.ts b/packages/frontend/src/scripts/langmap.ts
index 3912d58d82..b32de15963 100644
--- a/packages/frontend/src/scripts/langmap.ts
+++ b/packages/frontend/src/scripts/langmap.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/login-id.ts b/packages/frontend/src/scripts/login-id.ts
index fe0e17e66e..b52735caa0 100644
--- a/packages/frontend/src/scripts/login-id.ts
+++ b/packages/frontend/src/scripts/login-id.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/lookup-user.ts b/packages/frontend/src/scripts/lookup-user.ts
index a35fe898e4..efc9132e75 100644
--- a/packages/frontend/src/scripts/lookup-user.ts
+++ b/packages/frontend/src/scripts/lookup-user.ts
@@ -1,11 +1,12 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Misskey from 'misskey-js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
export async function lookupUser() {
const { canceled, result } = await os.inputText({
@@ -17,8 +18,8 @@ export async function lookupUser() {
os.pageWindow(`/admin/user/${user.id}`);
};
- const usernamePromise = os.api('users/show', Misskey.acct.parse(result));
- const idPromise = os.api('users/show', { userId: result });
+ const usernamePromise = misskeyApi('users/show', Misskey.acct.parse(result));
+ const idPromise = misskeyApi('users/show', { userId: result });
let _notFound = false;
const notFound = () => {
if (_notFound) {
diff --git a/packages/frontend/src/scripts/lookup.ts b/packages/frontend/src/scripts/lookup.ts
index 979f40f038..7f020b15cc 100644
--- a/packages/frontend/src/scripts/lookup.ts
+++ b/packages/frontend/src/scripts/lookup.ts
@@ -1,12 +1,13 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
-import { mainRouter } from '@/router.js';
import { Router } from '@/nirax.js';
+import { mainRouter } from '@/router/main.js';
export async function lookup(router?: Router) {
const _router = router ?? mainRouter;
@@ -28,7 +29,7 @@ export async function lookup(router?: Router) {
}
if (query.startsWith('https://')) {
- const promise = os.api('ap/show', {
+ const promise = misskeyApi('ap/show', {
uri: query,
});
diff --git a/packages/frontend/src/scripts/media-proxy.ts b/packages/frontend/src/scripts/media-proxy.ts
index 559e61211d..099a22163a 100644
--- a/packages/frontend/src/scripts/media-proxy.ts
+++ b/packages/frontend/src/scripts/media-proxy.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/merge.ts b/packages/frontend/src/scripts/merge.ts
new file mode 100644
index 0000000000..4e39a0fa06
--- /dev/null
+++ b/packages/frontend/src/scripts/merge.ts
@@ -0,0 +1,35 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { deepClone } from './clone.js';
+import type { Cloneable } from './clone.js';
+
+type DeepPartial<T> = {
+ [P in keyof T]?: T[P] extends Record<string | number | symbol, unknown> ? DeepPartial<T[P]> : T[P];
+};
+
+function isPureObject(value: unknown): value is Record<string | number | symbol, unknown> {
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
+}
+
+/**
+ * valueにないキーをdefからもらう(再帰的)\
+ * nullはそのまま、undefinedはdefの値
+ **/
+export function deepMerge<X extends Record<string | number | symbol, unknown>>(value: DeepPartial<X>, def: X): X {
+ if (isPureObject(value) && isPureObject(def)) {
+ const result = deepClone(value as Cloneable) as X;
+ for (const [k, v] of Object.entries(def) as [keyof X, X[keyof X]][]) {
+ if (!Object.prototype.hasOwnProperty.call(value, k) || value[k] === undefined) {
+ result[k] = v;
+ } else if (isPureObject(v) && isPureObject(result[k])) {
+ const child = deepClone(result[k] as Cloneable) as DeepPartial<X[keyof X] & Record<string | number | symbol, unknown>>;
+ result[k] = deepMerge<typeof v>(child, v);
+ }
+ }
+ return result;
+ }
+ throw new Error('deepMerge: value and def must be pure objects');
+}
diff --git a/packages/frontend/src/scripts/mfm-function-picker.ts b/packages/frontend/src/scripts/mfm-function-picker.ts
index 465926fe04..8867a8c50f 100644
--- a/packages/frontend/src/scripts/mfm-function-picker.ts
+++ b/packages/frontend/src/scripts/mfm-function-picker.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/misskey-api.ts
index 8f3a163938..49fb6f9e59 100644
--- a/packages/frontend/src/scripts/api.ts
+++ b/packages/frontend/src/scripts/misskey-api.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -10,12 +10,17 @@ import { $i } from '@/account.js';
export const pendingApiRequestsCount = ref(0);
// Implements Misskey.api.ApiClient.request
-export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
+export function misskeyApi<
+ ResT = void,
+ E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints,
+ P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req'],
+ _ResT = ResT extends void ? Misskey.api.SwitchCaseResponseType<E, P> : ResT,
+>(
endpoint: E,
data: P = {} as any,
token?: string | null | undefined,
signal?: AbortSignal,
-): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
+): Promise<_ResT> {
if (endpoint.includes('://')) throw new Error('invalid endpoint');
pendingApiRequestsCount.value++;
@@ -23,7 +28,7 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin
pendingApiRequestsCount.value--;
};
- const promise = new Promise<Misskey.Endpoints[E]['res'] | void>((resolve, reject) => {
+ const promise = new Promise<_ResT>((resolve, reject) => {
// Append a credential
if ($i) (data as any).i = $i.token;
if (token !== undefined) (data as any).i = token;
@@ -44,7 +49,7 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin
if (res.status === 200) {
resolve(body);
} else if (res.status === 204) {
- resolve();
+ resolve(undefined as _ResT); // void -> undefined
} else {
reject(body.error);
}
@@ -57,10 +62,15 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin
}
// Implements Misskey.api.ApiClient.request
-export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
+export function misskeyApiGet<
+ ResT = void,
+ E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints,
+ P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req'],
+ _ResT = ResT extends void ? Misskey.api.SwitchCaseResponseType<E, P> : ResT,
+>(
endpoint: E,
data: P = {} as any,
-): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
+): Promise<_ResT> {
pendingApiRequestsCount.value++;
const onFinally = () => {
@@ -69,7 +79,7 @@ export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endp
const query = new URLSearchParams(data as any);
- const promise = new Promise<Misskey.Endpoints[E]['res'] | void>((resolve, reject) => {
+ const promise = new Promise<_ResT>((resolve, reject) => {
// Send request
window.fetch(`${apiUrl}/${endpoint}?${query}`, {
method: 'GET',
@@ -81,7 +91,7 @@ export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endp
if (res.status === 200) {
resolve(body);
} else if (res.status === 204) {
- resolve();
+ resolve(undefined as _ResT); // void -> undefined
} else {
reject(body.error);
}
diff --git a/packages/frontend/src/scripts/navigator.ts b/packages/frontend/src/scripts/navigator.ts
index b13186a10e..ffc0a457f4 100644
--- a/packages/frontend/src/scripts/navigator.ts
+++ b/packages/frontend/src/scripts/navigator.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/nyaize.ts b/packages/frontend/src/scripts/nyaize.ts
index 62833b4de3..abc8ada461 100644
--- a/packages/frontend/src/scripts/nyaize.ts
+++ b/packages/frontend/src/scripts/nyaize.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/page-metadata.ts b/packages/frontend/src/scripts/page-metadata.ts
index 369e46aae1..0e3b093ecf 100644
--- a/packages/frontend/src/scripts/page-metadata.ts
+++ b/packages/frontend/src/scripts/page-metadata.ts
@@ -1,13 +1,10 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Misskey from 'misskey-js';
-import { ComputedRef, inject, isRef, onActivated, onMounted, provide, ref, Ref } from 'vue';
-
-export const setPageMetadata = Symbol('setPageMetadata');
-export const pageMetadataProvider = Symbol('pageMetadataProvider');
+import { MaybeRefOrGetter, Ref, inject, isRef, onActivated, onBeforeUnmount, provide, ref, toValue, watch } from 'vue';
export type PageMetadata = {
title: string;
@@ -18,29 +15,56 @@ export type PageMetadata = {
needWideArea?: boolean;
};
-export function definePageMetadata(metadata: PageMetadata | null | Ref<PageMetadata | null> | ComputedRef<PageMetadata | null>): void {
- const _metadata = isRef(metadata) ? metadata : ref(metadata);
+type PageMetadataGetter = () => PageMetadata;
+type PageMetadataReceiver = (getter: PageMetadataGetter) => void;
- provide(pageMetadataProvider, _metadata);
+const RECEIVER_KEY = Symbol('ReceiverKey');
+const setReceiver = (v: PageMetadataReceiver): void => {
+ provide<PageMetadataReceiver>(RECEIVER_KEY, v);
+};
+const getReceiver = (): PageMetadataReceiver | undefined => {
+ return inject<PageMetadataReceiver>(RECEIVER_KEY);
+};
- const set = inject(setPageMetadata) as any;
- if (set) {
- set(_metadata);
+const METADATA_KEY = Symbol('MetadataKey');
+const setMetadata = (v: Ref<PageMetadata | null>): void => {
+ provide<Ref<PageMetadata | null>>(METADATA_KEY, v);
+};
+const getMetadata = (): Ref<PageMetadata | null> | undefined => {
+ return inject<Ref<PageMetadata | null>>(METADATA_KEY);
+};
- onMounted(() => {
- set(_metadata);
- });
+export const definePageMetadata = (maybeRefOrGetterMetadata: MaybeRefOrGetter<PageMetadata>): void => {
+ const metadataRef = ref(toValue(maybeRefOrGetterMetadata));
+ const metadataGetter = () => metadataRef.value;
+ const receiver = getReceiver();
- onActivated(() => {
- set(_metadata);
- });
- }
-}
+ // setup handler
+ receiver?.(metadataGetter);
-export function provideMetadataReceiver(callback: (info: ComputedRef<PageMetadata>) => void): void {
- provide(setPageMetadata, callback);
-}
+ // update handler
+ onBeforeUnmount(watch(
+ () => toValue(maybeRefOrGetterMetadata),
+ (metadata) => {
+ metadataRef.value = metadata;
+ receiver?.(metadataGetter);
+ },
+ { deep: true },
+ ));
+ onActivated(() => {
+ receiver?.(metadataGetter);
+ });
+};
-export function injectPageMetadata(): PageMetadata | undefined {
- return inject(pageMetadataProvider);
-}
+export const provideMetadataReceiver = (receiver: PageMetadataReceiver): void => {
+ setReceiver(receiver);
+};
+
+export const provideReactiveMetadata = (metadataRef: Ref<PageMetadata | null>): void => {
+ setMetadata(metadataRef);
+};
+
+export const injectReactiveMetadata = (): Ref<PageMetadata | null> => {
+ const metadataRef = getMetadata();
+ return isRef(metadataRef) ? metadataRef : ref(null);
+};
diff --git a/packages/frontend/src/scripts/physics.ts b/packages/frontend/src/scripts/physics.ts
index cf9fad70eb..8a4e9319b3 100644
--- a/packages/frontend/src/scripts/physics.ts
+++ b/packages/frontend/src/scripts/physics.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts
index e6c08dfbc0..9e51272791 100644
--- a/packages/frontend/src/scripts/please-login.ts
+++ b/packages/frontend/src/scripts/please-login.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/popout.ts b/packages/frontend/src/scripts/popout.ts
index 0c2ff16992..1caa2dfc21 100644
--- a/packages/frontend/src/scripts/popout.ts
+++ b/packages/frontend/src/scripts/popout.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/popup-position.ts b/packages/frontend/src/scripts/popup-position.ts
index 0a799c5665..8c9e3c02c3 100644
--- a/packages/frontend/src/scripts/popup-position.ts
+++ b/packages/frontend/src/scripts/popup-position.ts
@@ -1,10 +1,10 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export function calcPopupPosition(el: HTMLElement, props: {
- anchorElement: HTMLElement | null;
+ anchorElement?: HTMLElement | null;
innerMargin: number;
direction: 'top' | 'bottom' | 'left' | 'right';
align: 'top' | 'bottom' | 'left' | 'right' | 'center';
diff --git a/packages/frontend/src/scripts/post-message.ts b/packages/frontend/src/scripts/post-message.ts
index 80441caf15..31a9ac1ad9 100644
--- a/packages/frontend/src/scripts/post-message.ts
+++ b/packages/frontend/src/scripts/post-message.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/reaction-picker.ts b/packages/frontend/src/scripts/reaction-picker.ts
index 9b13e794f5..7aec05c0cf 100644
--- a/packages/frontend/src/scripts/reaction-picker.ts
+++ b/packages/frontend/src/scripts/reaction-picker.ts
@@ -1,8 +1,9 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import * as Misskey from 'misskey-js';
import { defineAsyncComponent, Ref, ref } from 'vue';
import { popup } from '@/os.js';
import { defaultStore } from '@/store.js';
@@ -10,6 +11,7 @@ import { defaultStore } from '@/store.js';
class ReactionPicker {
private src: Ref<HTMLElement | null> = ref(null);
private manualShowing = ref(false);
+ private targetNote: Ref<Misskey.entities.Note | null> = ref(null);
private onChosen?: (reaction: string) => void;
private onClosed?: () => void;
@@ -23,6 +25,7 @@ class ReactionPicker {
src: this.src,
pinnedEmojis: reactionsRef,
asReactionPicker: true,
+ targetNote: this.targetNote,
manualShowing: this.manualShowing,
}, {
done: reaction => {
@@ -38,8 +41,9 @@ class ReactionPicker {
});
}
- public show(src: HTMLElement, onChosen?: ReactionPicker['onChosen'], onClosed?: ReactionPicker['onClosed']) {
+ public show(src: HTMLElement | null, targetNote: Misskey.entities.Note | null, onChosen?: ReactionPicker['onChosen'], onClosed?: ReactionPicker['onClosed']) {
this.src.value = src;
+ this.targetNote.value = targetNote;
this.manualShowing.value = true;
this.onChosen = onChosen;
this.onClosed = onClosed;
diff --git a/packages/frontend/src/scripts/safe-parse.ts b/packages/frontend/src/scripts/safe-parse.ts
new file mode 100644
index 0000000000..6bfcef6c36
--- /dev/null
+++ b/packages/frontend/src/scripts/safe-parse.ts
@@ -0,0 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export function safeParseFloat(str: unknown): number | null {
+ if (typeof str !== 'string' || str === '') return null;
+ const num = parseFloat(str);
+ if (isNaN(num)) return null;
+ return num;
+}
diff --git a/packages/frontend/src/scripts/safe-uri-decode.ts b/packages/frontend/src/scripts/safe-uri-decode.ts
index 625d8c34a7..0edf4e9eba 100644
--- a/packages/frontend/src/scripts/safe-uri-decode.ts
+++ b/packages/frontend/src/scripts/safe-uri-decode.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts
index 1f626e4c0d..8edb6fca05 100644
--- a/packages/frontend/src/scripts/scroll.ts
+++ b/packages/frontend/src/scripts/scroll.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts
index 53e2cd5b16..9aa38178b2 100644
--- a/packages/frontend/src/scripts/select-file.ts
+++ b/packages/frontend/src/scripts/select-file.ts
@@ -1,11 +1,12 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
@@ -65,7 +66,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> {
}
});
- os.api('drive/files/upload-from-url', {
+ misskeyApi('drive/files/upload-from-url', {
url: url,
folderId: defaultStore.state.uploadFolder,
marker,
diff --git a/packages/frontend/src/scripts/show-moved-dialog.ts b/packages/frontend/src/scripts/show-moved-dialog.ts
index b4defbfe7d..35b3ef79d8 100644
--- a/packages/frontend/src/scripts/show-moved-dialog.ts
+++ b/packages/frontend/src/scripts/show-moved-dialog.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/show-suspended-dialog.ts b/packages/frontend/src/scripts/show-suspended-dialog.ts
index a2fd5db453..8b89dbb936 100644
--- a/packages/frontend/src/scripts/show-suspended-dialog.ts
+++ b/packages/frontend/src/scripts/show-suspended-dialog.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/shuffle.ts b/packages/frontend/src/scripts/shuffle.ts
index d9d5bb1037..fed16bc71c 100644
--- a/packages/frontend/src/scripts/shuffle.ts
+++ b/packages/frontend/src/scripts/shuffle.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/snowfall-effect.ts b/packages/frontend/src/scripts/snowfall-effect.ts
index a09f02cec0..11fcaa0716 100644
--- a/packages/frontend/src/scripts/snowfall-effect.ts
+++ b/packages/frontend/src/scripts/snowfall-effect.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -17,20 +17,20 @@ export class SnowfallEffect {
uniform vec3 u_worldSize;
uniform float u_gravity;
uniform float u_wind;
+ uniform float u_spin_factor;
+ uniform float u_turbulence;
void main() {
v_color = a_color;
- v_rotation = a_rotation.x + u_time * a_rotation.y;
+ v_rotation = a_rotation.x + (u_time * u_spin_factor) * a_rotation.y;
vec3 pos = a_position.xyz;
- float turbulence = 1.0;
-
pos.x = mod(pos.x + u_time + u_wind * a_speed.x, u_worldSize.x * 2.0) - u_worldSize.x;
pos.y = mod(pos.y - u_time * a_speed.y * u_gravity, u_worldSize.y * 2.0) - u_worldSize.y;
- pos.x += sin(u_time * a_speed.z * turbulence) * a_rotation.z;
- pos.z += cos(u_time * a_speed.z * turbulence) * a_rotation.z;
+ pos.x += sin(u_time * a_speed.z * u_turbulence) * a_rotation.z;
+ pos.z += cos(u_time * a_speed.z * u_turbulence) * a_rotation.z;
gl_Position = u_projection * vec4(pos.xyz, a_position.w);
gl_PointSize = (a_size / gl_Position.w) * 100.0;
@@ -105,6 +105,7 @@ export class SnowfallEffect {
private opacity = 1;
private size = 4;
private snowflake = 'data:image/png;base64,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';
+ private mode = 'snow';
private INITIAL_BUFFERS = () => ({
position: { size: 3, value: [] },
@@ -119,6 +120,8 @@ export class SnowfallEffect {
worldSize: { type: 'vec3', value: [0, 0, 0] },
gravity: { type: 'float', value: this.gravity },
wind: { type: 'float', value: 0 },
+ spin_factor: { type: 'float', value: this.mode === 'sakura' ? 8 : 1 },
+ turbulence: { type: 'float', value: this.mode === 'sakura' ? 2 : 1 },
projection: {
type: 'mat4',
value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
@@ -153,7 +156,16 @@ export class SnowfallEffect {
easing: 0.0005,
};
- constructor() {
+ constructor(options: {
+ sakura?: boolean;
+ }) {
+ if (options.sakura) {
+ this.mode = 'sakura';
+ this.snowflake = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAFEmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgeG1wOkNyZWF0ZURhdGU9IjIwMjQtMDItMDFUMTQ6Mzk6NTYrMDkwMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDItMDFUMTQ6NDU6MzQrMDk6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQtMDItMDFUMTQ6NDU6MzQrMDk6MDAiCiAgIHBob3Rvc2hvcDpEYXRlQ3JlYXRlZD0iMjAyNC0wMi0wMVQxNDozOTo1NiswOTAwIgogICBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIgogICBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSI2NCIKICAgZXhpZjpQaXhlbFlEaW1l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';
+ this.size = 10;
+ this.density = 1 / 280;
+ }
+
const canvas = this.initCanvas();
const gl = canvas.getContext('webgl2', { antialias: true });
if (gl == null) throw new Error('Failed to get WebGL context');
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 2f7545ef0d..9555579e0d 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -1,11 +1,10 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { SoundStore } from '@/store.js';
import { defaultStore } from '@/store.js';
-import * as os from '@/os.js';
let ctx: AudioContext;
const cache = new Map<string, AudioBuffer>();
@@ -89,63 +88,33 @@ export type OperationType = typeof operationTypes[number];
/**
* 音声を読み込む
- * @param soundStore サウンド設定
+ * @param url url
* @param options `useCache`: デフォルトは`true` 一度再生した音声はキャッシュする
*/
-export async function loadAudio(soundStore: SoundStore, options?: { useCache?: boolean; }) {
- if (_DEV_) console.log('loading audio. opts:', options);
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
- return;
- }
+export async function loadAudio(url: string, options?: { useCache?: boolean; }) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (ctx == null) {
ctx = new AudioContext();
}
if (options?.useCache ?? true) {
- if (soundStore.type === '_driveFile_' && cache.has(soundStore.fileId)) {
- if (_DEV_) console.log('use cache');
- return cache.get(soundStore.fileId) as AudioBuffer;
- } else if (cache.has(soundStore.type)) {
- if (_DEV_) console.log('use cache');
- return cache.get(soundStore.type) as AudioBuffer;
+ if (cache.has(url)) {
+ return cache.get(url) as AudioBuffer;
}
}
let response: Response;
- if (soundStore.type === '_driveFile_') {
- try {
- response = await fetch(soundStore.fileUrl);
- } catch (err) {
- try {
- // URLが変わっている可能性があるのでドライブ側からURLを取得するフォールバック
- const apiRes = await os.api('drive/files/show', {
- fileId: soundStore.fileId,
- });
- response = await fetch(apiRes.url);
- } catch (fbErr) {
- // それでも無理なら諦める
- return;
- }
- }
- } else {
- try {
- response = await fetch(`/client-assets/sounds/${soundStore.type}.mp3`);
- } catch (err) {
- return;
- }
+ try {
+ response = await fetch(url);
+ } catch (err) {
+ return;
}
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await ctx.decodeAudioData(arrayBuffer);
if (options?.useCache ?? true) {
- if (soundStore.type === '_driveFile_') {
- cache.set(soundStore.fileId, audioBuffer);
- } else {
- cache.set(soundStore.type, audioBuffer);
- }
+ cache.set(url, audioBuffer);
}
return audioBuffer;
@@ -155,13 +124,12 @@ export async function loadAudio(soundStore: SoundStore, options?: { useCache?: b
* 既定のスプライトを再生する
* @param type スプライトの種類を指定
*/
-export function play(operationType: OperationType) {
+export function playMisskeySfx(operationType: OperationType) {
const sound = defaultStore.state[`sound_${operationType}`];
- if (_DEV_) console.log('play', operationType, sound);
if (sound.type == null || !canPlay) return;
canPlay = false;
- playFile(sound).finally(() => {
+ playMisskeySfxFile(sound).finally(() => {
// ごく短時間に音が重複しないように
setTimeout(() => {
canPlay = true;
@@ -173,26 +141,59 @@ export function play(operationType: OperationType) {
* サウンド設定形式で指定された音声を再生する
* @param soundStore サウンド設定
*/
-export async function playFile(soundStore: SoundStore) {
- const buffer = await loadAudio(soundStore);
+export async function playMisskeySfxFile(soundStore: SoundStore) {
+ if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
+ return;
+ }
+ const masterVolume = defaultStore.state.sound_masterVolume;
+ if (isMute() || masterVolume === 0 || soundStore.volume === 0) {
+ return;
+ }
+ const url = soundStore.type === '_driveFile_' ? soundStore.fileUrl : `/client-assets/sounds/${soundStore.type}.mp3`;
+ const buffer = await loadAudio(url);
if (!buffer) return;
- createSourceNode(buffer, soundStore.volume)?.start();
+ const volume = soundStore.volume * masterVolume;
+ createSourceNode(buffer, { volume }).soundSource.start();
}
-export function createSourceNode(buffer: AudioBuffer, volume: number) : AudioBufferSourceNode | null {
- const masterVolume = defaultStore.state.sound_masterVolume;
- if (isMute() || masterVolume === 0 || volume === 0) {
- return null;
+export async function playUrl(url: string, opts: {
+ volume?: number;
+ pan?: number;
+ playbackRate?: number;
+}) {
+ if (opts.volume === 0) {
+ return;
}
+ const buffer = await loadAudio(url);
+ if (!buffer) return;
+ createSourceNode(buffer, opts).soundSource.start();
+}
+
+export function createSourceNode(buffer: AudioBuffer, opts: {
+ volume?: number;
+ pan?: number;
+ playbackRate?: number;
+}): {
+ soundSource: AudioBufferSourceNode;
+ panNode: StereoPannerNode;
+ gainNode: GainNode;
+} {
+ const panNode = ctx.createStereoPanner();
+ panNode.pan.value = opts.pan ?? 0;
const gainNode = ctx.createGain();
- gainNode.gain.value = masterVolume * volume;
+
+ gainNode.gain.value = opts.volume ?? 1;
const soundSource = ctx.createBufferSource();
soundSource.buffer = buffer;
- soundSource.connect(gainNode).connect(ctx.destination);
+ soundSource.playbackRate.value = opts.playbackRate ?? 1;
+ soundSource
+ .connect(panNode)
+ .connect(gainNode)
+ .connect(ctx.destination);
- return soundSource;
+ return { soundSource, panNode, gainNode };
}
/**
diff --git a/packages/frontend/src/scripts/sticky-sidebar.ts b/packages/frontend/src/scripts/sticky-sidebar.ts
index f233c3648e..50f1e6ecc8 100644
--- a/packages/frontend/src/scripts/sticky-sidebar.ts
+++ b/packages/frontend/src/scripts/sticky-sidebar.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/test-utils.ts b/packages/frontend/src/scripts/test-utils.ts
index 1b42811faa..52bb2d94e0 100644
--- a/packages/frontend/src/scripts/test-utils.ts
+++ b/packages/frontend/src/scripts/test-utils.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/theme-editor.ts b/packages/frontend/src/scripts/theme-editor.ts
index 275f4bcdaa..0092af1640 100644
--- a/packages/frontend/src/scripts/theme-editor.ts
+++ b/packages/frontend/src/scripts/theme-editor.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index 21ef85fe7a..5f7e88bd9f 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -1,11 +1,12 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ref } from 'vue';
import tinycolor from 'tinycolor2';
import { deepClone } from './clone.js';
+import type { BuiltinTheme } from 'shiki';
import { globalEvents } from '@/events.js';
import lightTheme from '@/themes/_light.json5';
import darkTheme from '@/themes/_dark.json5';
@@ -18,6 +19,13 @@ export type Theme = {
desc?: string;
base?: 'dark' | 'light';
props: Record<string, string>;
+ codeHighlighter?: {
+ base: BuiltinTheme;
+ overrides?: Record<string, any>;
+ } | {
+ base: '_none_';
+ overrides: Record<string, any>;
+ };
};
export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
@@ -53,7 +61,7 @@ export const getBuiltinThemesRef = () => {
return builtinThemes;
};
-let timeout = null;
+let timeout: number | null = null;
export function applyTheme(theme: Theme, persist = true) {
if (timeout) window.clearTimeout(timeout);
diff --git a/packages/frontend/src/scripts/time.ts b/packages/frontend/src/scripts/time.ts
index 4479db1081..275b67ed00 100644
--- a/packages/frontend/src/scripts/time.ts
+++ b/packages/frontend/src/scripts/time.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/timezones.ts b/packages/frontend/src/scripts/timezones.ts
index 55f9be393f..c7582e06da 100644
--- a/packages/frontend/src/scripts/timezones.ts
+++ b/packages/frontend/src/scripts/timezones.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/touch.ts b/packages/frontend/src/scripts/touch.ts
index 05f379e4aa..13c9d648dc 100644
--- a/packages/frontend/src/scripts/touch.ts
+++ b/packages/frontend/src/scripts/touch.ts
@@ -1,8 +1,9 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import { ref } from 'vue';
import { deviceKind } from '@/scripts/device-kind.js';
const isTouchSupported = 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
@@ -16,3 +17,6 @@ if (isTouchSupported && !isTouchUsing) {
isTouchUsing = true;
}, { passive: true });
}
+
+/** (MkHorizontalSwipe) 横スワイプ中か? */
+export const isHorizontalSwipeSwiping = ref(false);
diff --git a/packages/frontend/src/scripts/unison-reload.ts b/packages/frontend/src/scripts/unison-reload.ts
index 65fc090888..a24941d02e 100644
--- a/packages/frontend/src/scripts/unison-reload.ts
+++ b/packages/frontend/src/scripts/unison-reload.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts
index b896376ec8..6c46b2bc1b 100644
--- a/packages/frontend/src/scripts/upload.ts
+++ b/packages/frontend/src/scripts/upload.ts
@@ -1,11 +1,11 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { reactive, ref } from 'vue';
import * as Misskey from 'misskey-js';
-import { readAndCompressImage } from 'browser-image-resizer';
+import { readAndCompressImage } from '@misskey-dev/browser-image-resizer';
import { getCompressionConfig } from './upload/compress-config.js';
import { defaultStore } from '@/store.js';
import { apiUrl } from '@/config.js';
diff --git a/packages/frontend/src/scripts/upload/compress-config.ts b/packages/frontend/src/scripts/upload/compress-config.ts
index 2deb9cbb81..3046b7f518 100644
--- a/packages/frontend/src/scripts/upload/compress-config.ts
+++ b/packages/frontend/src/scripts/upload/compress-config.ts
@@ -1,11 +1,11 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import isAnimated from 'is-file-animated';
import { isWebpSupported } from './isWebpSupported.js';
-import type { BrowserImageResizerConfig } from 'browser-image-resizer';
+import type { BrowserImageResizerConfigWithConvertedOutput } from '@misskey-dev/browser-image-resizer';
const compressTypeMap = {
'image/jpeg': { quality: 0.90, mimeType: 'image/webp' },
@@ -21,7 +21,7 @@ const compressTypeMapFallback = {
'image/svg+xml': { quality: 1, mimeType: 'image/png' },
} as const;
-export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfig | undefined> {
+export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfigWithConvertedOutput | undefined> {
const imgConfig = (isWebpSupported() ? compressTypeMap : compressTypeMapFallback)[file.type];
if (!imgConfig || await isAnimated(file)) {
return;
diff --git a/packages/frontend/src/scripts/upload/isWebpSupported.ts b/packages/frontend/src/scripts/upload/isWebpSupported.ts
index 185c3e6b40..2511236ecc 100644
--- a/packages/frontend/src/scripts/upload/isWebpSupported.ts
+++ b/packages/frontend/src/scripts/upload/isWebpSupported.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/url.ts b/packages/frontend/src/scripts/url.ts
index 625f4ce057..e3072b3b7d 100644
--- a/packages/frontend/src/scripts/url.ts
+++ b/packages/frontend/src/scripts/url.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/use-chart-tooltip.ts b/packages/frontend/src/scripts/use-chart-tooltip.ts
index 3d6489c3b8..7e4bf5c9c6 100644
--- a/packages/frontend/src/scripts/use-chart-tooltip.ts
+++ b/packages/frontend/src/scripts/use-chart-tooltip.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/use-document-visibility.ts b/packages/frontend/src/scripts/use-document-visibility.ts
index a9e2512eb3..a8f4d5e03a 100644
--- a/packages/frontend/src/scripts/use-document-visibility.ts
+++ b/packages/frontend/src/scripts/use-document-visibility.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/use-interval.ts b/packages/frontend/src/scripts/use-interval.ts
index b8c5431fb6..b50e78c3cc 100644
--- a/packages/frontend/src/scripts/use-interval.ts
+++ b/packages/frontend/src/scripts/use-interval.ts
@@ -1,9 +1,9 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { onMounted, onUnmounted } from 'vue';
+import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
export function useInterval(fn: () => void, interval: number, options: {
immediate: boolean;
@@ -28,6 +28,16 @@ export function useInterval(fn: () => void, interval: number, options: {
intervalId = null;
};
+ onActivated(() => {
+ if (intervalId) return;
+ if (options.immediate) fn();
+ intervalId = window.setInterval(fn, interval);
+ });
+
+ onDeactivated(() => {
+ clear();
+ });
+
onUnmounted(() => {
clear();
});
diff --git a/packages/frontend/src/scripts/use-leave-guard.ts b/packages/frontend/src/scripts/use-leave-guard.ts
index c9750c3923..5f7e56e8a9 100644
--- a/packages/frontend/src/scripts/use-leave-guard.ts
+++ b/packages/frontend/src/scripts/use-leave-guard.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts
index bda9c04ea4..524ac5d3fe 100644
--- a/packages/frontend/src/scripts/use-note-capture.ts
+++ b/packages/frontend/src/scripts/use-note-capture.ts
@@ -1,15 +1,15 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { onUnmounted, Ref } from 'vue';
+import { onUnmounted, Ref, ShallowRef } from 'vue';
import * as Misskey from 'misskey-js';
import { useStream } from '@/stream.js';
import { $i } from '@/account.js';
export function useNoteCapture(props: {
- rootEl: Ref<HTMLElement>;
+ rootEl: ShallowRef<HTMLElement | undefined>;
note: Ref<Misskey.entities.Note>;
pureNote: Ref<Misskey.entities.Note>;
isDeletedRef: Ref<boolean>;
@@ -83,7 +83,7 @@ export function useNoteCapture(props: {
function capture(withHandler = false): void {
if (connection) {
// TODO: このノートがストリーミング経由で流れてきた場合のみ sr する
- connection.send(document.body.contains(props.rootEl.value) ? 'sr' : 's', { id: note.value.id });
+ connection.send(document.body.contains(props.rootEl.value ?? null as Node | null) ? 'sr' : 's', { id: note.value.id });
if (pureNote.value.id !== note.value.id) connection.send('s', { id: pureNote.value.id });
if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated);
}
diff --git a/packages/frontend/src/scripts/use-tooltip.ts b/packages/frontend/src/scripts/use-tooltip.ts
index aaf0a0285a..a26d08cce7 100644
--- a/packages/frontend/src/scripts/use-tooltip.ts
+++ b/packages/frontend/src/scripts/use-tooltip.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
diff --git a/packages/frontend/src/scripts/worker-multi-dispatch.ts b/packages/frontend/src/scripts/worker-multi-dispatch.ts
index 7686b687c5..6b3fcd9383 100644
--- a/packages/frontend/src/scripts/worker-multi-dispatch.ts
+++ b/packages/frontend/src/scripts/worker-multi-dispatch.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/