Compare commits
81 Commits
2025.3.2-b
...
develop
Author | SHA1 | Date | |
---|---|---|---|
923cacb98a | |||
b1e564b562 | |||
![]() |
0471e457fe | ||
![]() |
260d35e2f0 | ||
![]() |
3ff9d9f4fd | ||
![]() |
27991a3bc8 | ||
![]() |
b5f86e5210 | ||
![]() |
16cde5568d | ||
![]() |
bf07796b6b | ||
![]() |
08b131ec33 | ||
![]() |
1312fe34c1 | ||
![]() |
97563910fa | ||
![]() |
96a7c4a568 | ||
![]() |
fee6f9fcc2 | ||
![]() |
50724b6ab8 | ||
![]() |
e61263cff0 | ||
![]() |
d073fe6b02 | ||
![]() |
ce858a676b | ||
![]() |
733a391d86 | ||
![]() |
0e25a0fb81 | ||
![]() |
1fd87bd2e4 | ||
![]() |
ebc54b1f82 | ||
![]() |
6015254e59 | ||
![]() |
c02f0b3b33 | ||
![]() |
abddd40c09 | ||
![]() |
a865a949b5 | ||
![]() |
0007723405 | ||
![]() |
71188b3463 | ||
![]() |
7f534a41a6 | ||
![]() |
f25963e2c2 | ||
![]() |
dfab6b1b8d | ||
![]() |
fac59d75e2 | ||
![]() |
fd3e28812e | ||
![]() |
6a90b7e04b | ||
![]() |
8d8414687a | ||
![]() |
0c682dd027 | ||
![]() |
3399c786a8 | ||
![]() |
64cf101fe7 | ||
![]() |
8b6d90b7a4 | ||
![]() |
070749bdc8 | ||
![]() |
161706c5e2 | ||
![]() |
596e517f77 | ||
![]() |
91670caca0 | ||
![]() |
fccaadacf0 | ||
![]() |
282caa0b7e | ||
![]() |
9529867630 | ||
![]() |
d06fadd095 | ||
![]() |
9dd13f364b | ||
![]() |
b067d4dcd6 | ||
![]() |
acac759d87 | ||
![]() |
b37622fa64 | ||
![]() |
c17a104de6 | ||
![]() |
4ab9f66356 | ||
![]() |
aed95a765d | ||
![]() |
71841e365e | ||
![]() |
3b20279e20 | ||
![]() |
21dc7aebe7 | ||
![]() |
aff03708a7 | ||
![]() |
69ad3cf89b | ||
![]() |
7b323031b7 | ||
![]() |
81ac71f7e5 | ||
![]() |
2c76018b7f | ||
![]() |
2dc2d2e4fe | ||
![]() |
409cd4fbd3 | ||
![]() |
7d4045e8b4 | ||
![]() |
bdc72e5817 | ||
![]() |
11378b17c5 | ||
![]() |
62bf0d53d3 | ||
![]() |
05391f59a5 | ||
![]() |
d609f41f61 | ||
![]() |
0a295e1bb0 | ||
![]() |
474155b677 | ||
![]() |
6c8f21b608 | ||
![]() |
7d5daa4b00 | ||
![]() |
1ce81f243e | ||
![]() |
a773f2976d | ||
![]() |
467c68fb98 | ||
![]() |
22b0ace8b4 | ||
![]() |
a814395127 | ||
![]() |
81a0cbd294 | ||
![]() |
32844e4775 |
51
.gitea/workflows/registry.yml
Normal file
51
.gitea/workflows/registry.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
name: release-tag
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
jobs:
|
||||
release-image:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_ORG: sendnrw
|
||||
DOCKER_LATEST: latest
|
||||
RUNNER_TOOL_CACHE: /toolcache
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker BuildX
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with: # replace it with your local IP
|
||||
config-inline: |
|
||||
[registry."git.send.nrw"]
|
||||
http = true
|
||||
insecure = true
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.send.nrw # replace it with your local IP
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Get Meta
|
||||
id: meta
|
||||
run: |
|
||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
||||
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: |
|
||||
linux/amd64
|
||||
push: true
|
||||
tags: | # replace it with your local IP and tags
|
||||
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
|
||||
git.send.nrw/${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
|
4
.github/workflows/storybook.yml
vendored
4
.github/workflows/storybook.yml
vendored
@@ -50,8 +50,8 @@ jobs:
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- name: Check pnpm-lock.yaml
|
||||
run: git diff --exit-code pnpm-lock.yaml
|
||||
- name: Build misskey-js
|
||||
run: pnpm --filter misskey-js build
|
||||
- name: Build dependent packages
|
||||
run: pnpm -F misskey-js -F misskey-bubble-game -F misskey-reversi build
|
||||
- name: Build storybook
|
||||
run: pnpm --filter frontend build-storybook
|
||||
- name: Publish to Chromatic
|
||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@@ -6,8 +6,24 @@
|
||||
|
||||
### Client
|
||||
- Feat: 設定の管理が強化されました
|
||||
- 自動でバックアップされるように
|
||||
- 内部処理が一新され、安定性とパフォーマンスが向上しました
|
||||
- 全てのクライアント設定がエクスポート(バックアップ)/インポート対象に含まれるようになりました
|
||||
- プラグイン、テーマ、クライアントに追加されたすべてのアカウント情報も含まれるようになりました
|
||||
- 自動で設定データをサーバーにバックアップできるように
|
||||
- 設定→設定のプロファイル→自動バックアップ で有効にできます
|
||||
- 新しいデバイスからログインしたり、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能)
|
||||
- 任意の設定項目をデバイス間で同期できるように
|
||||
- 設定項目の「...」メニュー→「デバイス間で同期」
|
||||
- 同期をオンにした際にサーバーに保存された値とローカルの値が競合する場合はどちらを優先するか選択できます
|
||||
- 任意の設定項目を初期値にリセットできるように
|
||||
- 設定項目の「...」メニュー→「初期値にリセット」
|
||||
- アカウントごとに設定値が分離される設定とそうでないクライアント設定が混在していた(かつ分離するかどうかを設定不可だった)のを、基本的に一律でクライアント全体に適用されるようにし、個別でアカウントごとに異なる設定を行えるように
|
||||
- 設定項目の「...」メニュー→「アカウントで上書き」をオンにすることで、設定値をそのアカウントでだけ適用するようにできます
|
||||
- ログアウトすると設定データもブラウザから消去されるようになりプライバシーが向上しました
|
||||
- 再度ログインすればサーバーのバックアップから設定データを復元可能です
|
||||
- エクスポートした設定データを他のサーバーでインポートして適用すること(設定の持ち運び)が可能になりました
|
||||
- Feat: 画面を重ねて表示するオプションを実装(実験的)
|
||||
- 設定 → その他 → 実験的機能 → Enable stacking router view
|
||||
- Enhance: プラグインの管理が強化されました
|
||||
- インストール/アンインストール/設定の変更時にリロード不要になりました
|
||||
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
|
||||
@@ -17,11 +33,14 @@
|
||||
- Enhance: 投稿フォームの設定メニューを改良
|
||||
- 投稿フォームをリセットできるように
|
||||
- 文字数カウントを復活
|
||||
- Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように
|
||||
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
|
||||
|
||||
### Server
|
||||
- Fix: プロフィール追加情報で無効なURLに入力された場合に照会エラーを出るのを修正
|
||||
- Fix: ActivityPubリクエストURLチェック実装は仕様に従っていないのを修正
|
||||
- Fix: 連合無しモードでも外部から照会可能だった問題を修正
|
||||
- Fix: テスト用WebHookのペイロードの`emojis`パラメータが実際のものと異なる問題を修正
|
||||
|
||||
## 2025.3.1
|
||||
|
||||
|
@@ -273,7 +273,6 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド
|
||||
query?: Record<string, string>;
|
||||
loginRequired?: boolean;
|
||||
hash?: string;
|
||||
globalCacheKey?: string;
|
||||
children?: RouteDef[];
|
||||
}
|
||||
```
|
||||
|
@@ -6,7 +6,7 @@
|
||||
**🌎 **Misskey** is an open source, federated social media platform that's free forever! 🚀**
|
||||
|
||||
[Learn more](https://misskey-hub.net/)
|
||||
|
||||
|
||||
---
|
||||
|
||||
<a href="https://misskey-hub.net/servers/">
|
||||
|
@@ -1013,6 +1013,7 @@ flip: "اقلب"
|
||||
lastNDays: "آخر {n} أيام"
|
||||
surrender: "ألغِ"
|
||||
postForm: "أنشئ ملاحظة"
|
||||
information: "عن"
|
||||
_delivery:
|
||||
stop: "مُعلّق"
|
||||
_initialAccountSetting:
|
||||
|
@@ -853,6 +853,7 @@ renotes: "রিনোট"
|
||||
sourceCode: "সোর্স কোড"
|
||||
flip: "উল্টান"
|
||||
postForm: "নোট লিখুন"
|
||||
information: "আপনার সম্পর্কে"
|
||||
_delivery:
|
||||
stop: "স্থগিত করা হয়েছে"
|
||||
_type:
|
||||
|
@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} està parlant sobre \"{word}\""
|
||||
makeActive: "Activar"
|
||||
display: "Veure"
|
||||
copy: "Copiar"
|
||||
copiedToClipboard: "Copiat al porta papers"
|
||||
metrics: "Mètriques"
|
||||
overview: "Visió General"
|
||||
logs: "Registres"
|
||||
@@ -1139,7 +1140,7 @@ channelArchiveConfirmDescription: "Un Canal arxivat no apareixerà a la llista d
|
||||
thisChannelArchived: "Aquest Canal ha sigut arxivat."
|
||||
displayOfNote: "Mostrar notes"
|
||||
initialAccountSetting: "Configuració del perfil"
|
||||
youFollowing: "Seguint"
|
||||
youFollowing: "Segueixes "
|
||||
preventAiLearning: "Descartar l'ús d'aprenentatge automàtic (IA Generativa)"
|
||||
preventAiLearningDescription: "Demanar els indexadors no fer servir els texts, imatges, etc. en cap conjunt de dades per alimentar l'aprenentatge automàtic (IA Predictiva/ Generativa). Això s'aconsegueix afegint la etiqueta \"noai\" com a resposta HTML al contingut corresponent. Prevenir aquest ús totalment pot ser que no sigui aconseguit, ja que molts indexadors poden obviar aquesta etiqueta."
|
||||
options: "Opcions"
|
||||
@@ -1332,6 +1333,8 @@ preferenceSyncConflictChoiceCancel: "Cancel·lar l'activació de la sincronitzac
|
||||
paste: "Pegar"
|
||||
emojiPalette: "Calaix d'emojis"
|
||||
postForm: "Formulari de publicació"
|
||||
textCount: "Nombre de caràcters "
|
||||
information: "Informació"
|
||||
_emojiPalette:
|
||||
palettes: "Calaixos d'emojis"
|
||||
enableSyncBetweenDevicesForPalettes: "Activa la sincronització dels calaixos d'emojis entre dispositius"
|
||||
@@ -1355,6 +1358,8 @@ _settings:
|
||||
appearanceBanner: "Pots configurar les preferències relacionades amb la visualització i l'aspecte del client segons el teu parer."
|
||||
soundsBanner: "Configuració dels sons que reproduirà el client."
|
||||
timelineAndNote: "Línia de temps i nota"
|
||||
makeEveryTextElementsSelectable: "Fes que tots els elements del text siguin seleccionables"
|
||||
makeEveryTextElementsSelectable_description: "L'activació pot reduir la usabilitat en determinades ocasions."
|
||||
_preferencesProfile:
|
||||
profileName: "Nom del perfil"
|
||||
profileNameDescription: "Estableix un nom que identifiqui aquest dispositiu."
|
||||
@@ -2519,6 +2524,7 @@ _notification:
|
||||
achievementEarned: "Assoliment desbloquejat"
|
||||
exportCompleted: "Exportació completada"
|
||||
login: "Iniciar sessió"
|
||||
createToken: "Creació de tokens d'accés "
|
||||
test: "Prova la notificació"
|
||||
app: "Notificacions d'aplicacions"
|
||||
_actions:
|
||||
@@ -2668,10 +2674,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "Assegura't que qui distribueix aquest recurs és fiable abans d'instal·lar-ho."
|
||||
_plugin:
|
||||
title: "Vols instal·lar aquest afegit?"
|
||||
metaTitle: "Informació de l'afegit "
|
||||
_theme:
|
||||
title: "Vols instal·lar aquest tema?"
|
||||
metaTitle: "Informació del tema"
|
||||
_meta:
|
||||
base: "Paleta de colors base"
|
||||
_vendorInfo:
|
||||
|
@@ -169,6 +169,9 @@ addAccount: "Přidat účet"
|
||||
reloadAccountsList: "Obnovit list účtů"
|
||||
loginFailed: "Přihlášení se nezdařilo."
|
||||
showOnRemote: "Více na původním profilu"
|
||||
continueOnRemote: "Pokračujte na původní profil"
|
||||
chooseServerOnMisskeyHub: "Vyberete si server z Misskey Hubu"
|
||||
inputHostName: "Zadejte doménu"
|
||||
general: "Obecně"
|
||||
wallpaper: "Obrázek na pozadí"
|
||||
setWallpaper: "Nastavení obrázku na pozadí"
|
||||
@@ -193,6 +196,7 @@ perHour: "za hodinu"
|
||||
perDay: "za den"
|
||||
stopActivityDelivery: "Přestat zasílat aktivitu"
|
||||
blockThisInstance: "Blokovat tuto instanci"
|
||||
silenceThisInstance: "Utišit tuto instanci"
|
||||
operations: "Operace"
|
||||
software: "Software"
|
||||
version: "Verze"
|
||||
@@ -1099,6 +1103,7 @@ flip: "Otočit"
|
||||
lastNDays: "Posledních {n} dnů"
|
||||
surrender: "Zrušit"
|
||||
postForm: "Formulář pro odeslání"
|
||||
information: "Informace"
|
||||
_delivery:
|
||||
stop: "Suspendováno"
|
||||
_type:
|
||||
|
@@ -1309,6 +1309,7 @@ availableRoles: "Verfügbare Rollen"
|
||||
federationSpecified: "Dieser Server arbeitet mit Whitelist-Föderation. Er kann nicht mit anderen als den vom Administrator angegebenen Servern interagieren."
|
||||
federationDisabled: "Föderation ist auf diesem Server deaktiviert. Es ist nicht möglich, mit Benutzern auf anderen Servern zu interagieren."
|
||||
postForm: "Notizfenster"
|
||||
information: "Über"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_accountSettings:
|
||||
@@ -2524,10 +2525,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "Überprüfe vor Installation die Vertrauenswürdigkeit des Vertreibers."
|
||||
_plugin:
|
||||
title: "Möchtest du dieses Plugin installieren?"
|
||||
metaTitle: "Plugininformation"
|
||||
_theme:
|
||||
title: "Möchten du dieses Farbschema installieren?"
|
||||
metaTitle: "Farbschemainfo"
|
||||
_meta:
|
||||
base: "Farbschemavorlage"
|
||||
_vendorInfo:
|
||||
|
@@ -289,6 +289,7 @@ icon: "Εικονίδιο"
|
||||
replies: "Απάντηση"
|
||||
renotes: "Κοινοποίηση σημειώματος"
|
||||
postForm: "Φόρμα δημοσίευσης"
|
||||
information: "Πληροφορίες"
|
||||
_email:
|
||||
_follow:
|
||||
title: "Έχετε ένα νέο ακόλουθο"
|
||||
|
@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} said something about \"{word}\""
|
||||
makeActive: "Activate"
|
||||
display: "Display"
|
||||
copy: "Copy"
|
||||
copiedToClipboard: "Copied to clipboard"
|
||||
metrics: "Metrics"
|
||||
overview: "Overview"
|
||||
logs: "Logs"
|
||||
@@ -1316,14 +1317,29 @@ unmarkAsSensitiveConfirm: "Do you want to remove the sensitive designation for t
|
||||
preferences: "Preferences"
|
||||
accessibility: "Accessibility"
|
||||
preferencesProfile: "Preferences profile"
|
||||
copyPreferenceId: "Copy the proference ID"
|
||||
copyPreferenceId: "Copy the preference ID"
|
||||
resetToDefaultValue: "Revert to default"
|
||||
overrideByAccount: "Override by the account"
|
||||
untitled: "Untitled"
|
||||
noName: "No name"
|
||||
skip: "Skip"
|
||||
restore: "Restore"
|
||||
syncBetweenDevices: "Sync between devices"
|
||||
preferenceSyncConflictTitle: "The configured value exists on the server."
|
||||
preferenceSyncConflictText: "The sync enabled settings will save their values to the server. However, there are existing values on the server. Which set of values would you like to overwrite?"
|
||||
preferenceSyncConflictChoiceServer: "Configured value on server"
|
||||
preferenceSyncConflictChoiceDevice: "Configured value on device"
|
||||
preferenceSyncConflictChoiceCancel: "Cancel enabling sync"
|
||||
paste: "Paste"
|
||||
emojiPalette: "Emoji palette"
|
||||
postForm: "Posting form"
|
||||
textCount: "Character count"
|
||||
information: "About"
|
||||
_emojiPalette:
|
||||
palettes: "Palette"
|
||||
enableSyncBetweenDevicesForPalettes: "Enable palette sync between devices"
|
||||
paletteForMain: "Main palette"
|
||||
paletteForReaction: "Reaction palette"
|
||||
_settings:
|
||||
driveBanner: "You can manage and configure the drive, check usage, and configure file upload settings."
|
||||
pluginBanner: "You can extend client features with plugins. You can install plugins, configure and manage individually."
|
||||
@@ -1341,6 +1357,9 @@ _settings:
|
||||
preferencesBanner: "You can configure the overall behavior of the client according to your preferences."
|
||||
appearanceBanner: "You can configure the appearance and display settings for the client according to your preferences."
|
||||
soundsBanner: "You can configure the sound settings for playback in the client."
|
||||
timelineAndNote: "Timeline and note"
|
||||
makeEveryTextElementsSelectable: "Make all text elements selectable"
|
||||
makeEveryTextElementsSelectable_description: "Enabling this may reduce usability in some situations."
|
||||
_preferencesProfile:
|
||||
profileName: "Profile name"
|
||||
profileNameDescription: "Set a name that identifies this device."
|
||||
@@ -2505,6 +2524,7 @@ _notification:
|
||||
achievementEarned: "Achievement unlocked"
|
||||
exportCompleted: "The export has been completed"
|
||||
login: "Sign In"
|
||||
createToken: "Create access token"
|
||||
test: "Notification test"
|
||||
app: "Notifications from linked apps"
|
||||
_actions:
|
||||
@@ -2532,6 +2552,7 @@ _deck:
|
||||
useSimpleUiForNonRootPages: "Use simple UI for navigated pages"
|
||||
usedAsMinWidthWhenFlexible: "Minimum width will be used for this when the \"Auto-adjust width\" option is enabled"
|
||||
flexible: "Auto-adjust width"
|
||||
enableSyncBetweenDevicesForProfiles: "Enable profile information sync between devices"
|
||||
_columns:
|
||||
main: "Main"
|
||||
widgets: "Widgets"
|
||||
@@ -2653,10 +2674,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "Make sure the distributor of this resource is trustworthy before installation."
|
||||
_plugin:
|
||||
title: "Do you want to install this plugin?"
|
||||
metaTitle: "Plugin information"
|
||||
_theme:
|
||||
title: "Do you want to install this theme?"
|
||||
metaTitle: "Theme information"
|
||||
_meta:
|
||||
base: "Base color scheme"
|
||||
_vendorInfo:
|
||||
@@ -2857,8 +2876,8 @@ _selfXssPrevention:
|
||||
description2: "If you do not understand exactly what you are trying to paste, %cstop working right now and close this window."
|
||||
description3: "For more information, please refer to this. {link}"
|
||||
_followRequest:
|
||||
recieved: "Received application"
|
||||
sent: "Sent application"
|
||||
recieved: "Received request"
|
||||
sent: "Sent request"
|
||||
_remoteLookupErrors:
|
||||
_federationNotAllowed:
|
||||
title: "Unable to communicate with this server"
|
||||
|
@@ -1300,6 +1300,7 @@ target: "Para"
|
||||
federationSpecified: "Este servidor opera en una federación de listas blancas. No puede interactuar con otros servidores que no sean los especificados por el administrador."
|
||||
federationDisabled: "La federación está desactivada en este servidor. No puede interactuar con usuarios de otros servidores"
|
||||
postForm: "Formulario"
|
||||
information: "Información"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_accountSettings:
|
||||
@@ -2521,10 +2522,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "Asegúrate de que el distribuidor de este recurso es de confianza antes de proceder a la instalación."
|
||||
_plugin:
|
||||
title: "¿Quieres instalar este plugin?"
|
||||
metaTitle: "Información del plugin"
|
||||
_theme:
|
||||
title: "¿Quieres instalar este tema?"
|
||||
metaTitle: "Información del tema"
|
||||
_meta:
|
||||
base: "Esquema de color base"
|
||||
_vendorInfo:
|
||||
|
@@ -1278,6 +1278,7 @@ lockdown: "Verrouiller"
|
||||
pleaseSelectAccount: "Sélectionner un compte"
|
||||
availableRoles: "Rôles disponibles"
|
||||
postForm: "Formulaire de publication"
|
||||
information: "Informations"
|
||||
_abuseUserReport:
|
||||
forward: "Transférer"
|
||||
forwardDescription: "Transférer le signalement vers une instance distante en tant qu'anonyme."
|
||||
@@ -2295,10 +2296,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "Veuillez confirmer que le distributeur est fiable avant l'installation."
|
||||
_plugin:
|
||||
title: "Voulez-vous installer cette extension ?"
|
||||
metaTitle: "Informations sur l'extension"
|
||||
_theme:
|
||||
title: "Voulez-vous installer ce thème ?"
|
||||
metaTitle: "Informations sur le thème"
|
||||
_meta:
|
||||
base: "Palette de couleurs de base"
|
||||
_vendorInfo:
|
||||
|
@@ -1262,6 +1262,7 @@ modified: "Diubah"
|
||||
thereAreNChanges: "Ada {n} perubahan"
|
||||
prohibitedWordsForNameOfUser: "Kata yang dilarang untuk nama pengguna"
|
||||
postForm: "Buat catatan"
|
||||
information: "Informasi"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_abuseUserReport:
|
||||
@@ -2492,10 +2493,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "Pastikan sumber dari sumber daya ini terpercaya sebelum melakukan pemasangan."
|
||||
_plugin:
|
||||
title: "Apakah kamu ingin memasang plugin ini?"
|
||||
metaTitle: "Informasi plugin"
|
||||
_theme:
|
||||
title: "Apakah kamu ingin memasang tema ini?"
|
||||
metaTitle: "Informasi tema"
|
||||
_meta:
|
||||
base: "Skema warna dasar"
|
||||
_vendorInfo:
|
||||
|
16
locales/index.d.ts
vendored
16
locales/index.d.ts
vendored
@@ -2810,6 +2810,10 @@ export interface Locale extends ILocale {
|
||||
* コピー
|
||||
*/
|
||||
"copy": string;
|
||||
/**
|
||||
* クリップボードにコピーされました
|
||||
*/
|
||||
"copiedToClipboard": string;
|
||||
/**
|
||||
* メトリクス
|
||||
*/
|
||||
@@ -5350,6 +5354,10 @@ export interface Locale extends ILocale {
|
||||
* 文字数
|
||||
*/
|
||||
"textCount": string;
|
||||
/**
|
||||
* 情報
|
||||
*/
|
||||
"information": string;
|
||||
"_emojiPalette": {
|
||||
/**
|
||||
* パレット
|
||||
@@ -10355,20 +10363,12 @@ export interface Locale extends ILocale {
|
||||
* このプラグインをインストールしますか?
|
||||
*/
|
||||
"title": string;
|
||||
/**
|
||||
* プラグイン情報
|
||||
*/
|
||||
"metaTitle": string;
|
||||
};
|
||||
"_theme": {
|
||||
/**
|
||||
* このテーマをインストールしますか?
|
||||
*/
|
||||
"title": string;
|
||||
/**
|
||||
* テーマ情報
|
||||
*/
|
||||
"metaTitle": string;
|
||||
};
|
||||
"_meta": {
|
||||
/**
|
||||
|
@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} ha Notato a riguardo di \"{word}\""
|
||||
makeActive: "Attiva"
|
||||
display: "Visualizza"
|
||||
copy: "Copia"
|
||||
copiedToClipboard: "Copiato negli appunti"
|
||||
metrics: "Statistiche"
|
||||
overview: "Anteprima"
|
||||
logs: "Log"
|
||||
@@ -973,7 +974,7 @@ check: "Verifica"
|
||||
driveCapOverrideLabel: "Modificare la capienza del Drive per questo profilo"
|
||||
driveCapOverrideCaption: "Se viene specificato meno di 0, viene annullato."
|
||||
requireAdminForView: "Per visualizzarli, è necessario aver effettuato l'accesso con un profilo amministratore."
|
||||
isSystemAccount: "Questi profili vengono creati e gestiti automaticamente dal sistema"
|
||||
isSystemAccount: "Si tratta di un profilo creato e gestito automaticamente dal sistema."
|
||||
typeToConfirm: "Digita {x} per continuare"
|
||||
deleteAccount: "Eliminazione profilo"
|
||||
document: "Documentazione"
|
||||
@@ -1323,7 +1324,22 @@ untitled: "Senza titolo"
|
||||
noName: "Senza nome"
|
||||
skip: "Salta"
|
||||
restore: "Ripristina"
|
||||
syncBetweenDevices: "Sincronizzazione tra i dispositivi"
|
||||
preferenceSyncConflictTitle: "Sul server esiste già il valore impostato"
|
||||
preferenceSyncConflictText: "Le impostazione sincronizzata salverà il valore sul server. Però, bada che esiste già un valore sul server. Quale vorresti sovrascrivere?"
|
||||
preferenceSyncConflictChoiceServer: "Valore del server"
|
||||
preferenceSyncConflictChoiceDevice: "Valore del dispositivo"
|
||||
preferenceSyncConflictChoiceCancel: "Annulla la sincronizzazione"
|
||||
paste: "Incolla"
|
||||
emojiPalette: "Tavolozza emoji"
|
||||
postForm: "Finestra di pubblicazione"
|
||||
textCount: "Il numero di caratteri"
|
||||
information: "Informazioni"
|
||||
_emojiPalette:
|
||||
palettes: "Tavolozza"
|
||||
enableSyncBetweenDevicesForPalettes: "Attiva la sincronizzazione tra dispositivi"
|
||||
paletteForMain: "Tavolozza principale"
|
||||
paletteForReaction: "Tavolozza per reazioni"
|
||||
_settings:
|
||||
driveBanner: "Permette di gestire e configurare il Drive, controllare il consumo di spazio e configurare il caricamento dei file."
|
||||
pluginBanner: "Consentono di migliorare le funzionalità. Le estensioni si possono configurare e gestire singolarmente."
|
||||
@@ -1341,6 +1357,9 @@ _settings:
|
||||
preferencesBanner: "Puoi personalizzare il comportamento del tuo dispositivo."
|
||||
appearanceBanner: "Puoi personalizzare l'aspetto nel dispositivo, in base alle tue preferenze."
|
||||
soundsBanner: "Puoi personalizzare i suoni emessi dagli eventi sul tuo dispositivo."
|
||||
timelineAndNote: "Note e Timeline"
|
||||
makeEveryTextElementsSelectable: "Imposta ogni elemento come selezionabile"
|
||||
makeEveryTextElementsSelectable_description: "Potrebbe ridurre l'usabilità in alcune situazioni."
|
||||
_preferencesProfile:
|
||||
profileName: "Nome del profilo"
|
||||
profileNameDescription: "Impostare il nome che indentifica questo dispositivo."
|
||||
@@ -2505,6 +2524,7 @@ _notification:
|
||||
achievementEarned: "Risultato raggiunto"
|
||||
exportCompleted: "Esportazione completata"
|
||||
login: "Accessi"
|
||||
createToken: "Creare un token di accesso"
|
||||
test: "Notifiche di test"
|
||||
app: "Notifiche da applicazioni"
|
||||
_actions:
|
||||
@@ -2532,6 +2552,7 @@ _deck:
|
||||
useSimpleUiForNonRootPages: "Visualizza sotto pagine con interfaccia web semplice"
|
||||
usedAsMinWidthWhenFlexible: "Se \"larghezza flessibile\" è abilitato, questa diventa la larghezza minima"
|
||||
flexible: "Larghezza flessibile"
|
||||
enableSyncBetweenDevicesForProfiles: "Abilita la sincronizzazione delle informazioni profilo tra dispositivi"
|
||||
_columns:
|
||||
main: "Principale"
|
||||
widgets: "Riquadri"
|
||||
@@ -2653,10 +2674,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "Prima di installare, assicurati che la fonte sia affidabile."
|
||||
_plugin:
|
||||
title: "Vuoi davvero installare questo componente aggiuntivo?"
|
||||
metaTitle: "Informazioni sul componente aggiuntivo"
|
||||
_theme:
|
||||
title: "Vuoi davvero installare questa variazione grafica?"
|
||||
metaTitle: "Informazioni sulla variazione grafica"
|
||||
_meta:
|
||||
base: "Combinazione base di colori"
|
||||
_vendorInfo:
|
||||
|
@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name}が「{word}」について何かを言いまし
|
||||
makeActive: "アクティブにする"
|
||||
display: "表示"
|
||||
copy: "コピー"
|
||||
copiedToClipboard: "クリップボードにコピーされました"
|
||||
metrics: "メトリクス"
|
||||
overview: "概要"
|
||||
logs: "ログ"
|
||||
@@ -1333,6 +1334,7 @@ paste: "ペースト"
|
||||
emojiPalette: "絵文字パレット"
|
||||
postForm: "投稿フォーム"
|
||||
textCount: "文字数"
|
||||
information: "情報"
|
||||
|
||||
_emojiPalette:
|
||||
palettes: "パレット"
|
||||
@@ -2748,10 +2750,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "配布元が信頼できるかを確認した上でインストールしてください。"
|
||||
_plugin:
|
||||
title: "このプラグインをインストールしますか?"
|
||||
metaTitle: "プラグイン情報"
|
||||
_theme:
|
||||
title: "このテーマをインストールしますか?"
|
||||
metaTitle: "テーマ情報"
|
||||
_meta:
|
||||
base: "基本のカラースキーム"
|
||||
_vendorInfo:
|
||||
|
@@ -1312,6 +1312,7 @@ federationDisabled: "このサーバーは連合が無効化されてるで。
|
||||
confirmOnReact: "ツッコむときに確認とる"
|
||||
reactAreYouSure: "\" {emoji} \" でツッコむ?"
|
||||
postForm: "投稿フォーム"
|
||||
information: "情報"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_accountSettings:
|
||||
@@ -2610,10 +2611,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "配ってるとこが信頼できるか確認した上でインストールしてな。"
|
||||
_plugin:
|
||||
title: "このプラグイン、インストールする?"
|
||||
metaTitle: "プラグイン情報"
|
||||
_theme:
|
||||
title: "このテーマインストールする?"
|
||||
metaTitle: "テーマ情報"
|
||||
_meta:
|
||||
base: ""
|
||||
_vendorInfo:
|
||||
|
@@ -655,6 +655,7 @@ replies: "답하기"
|
||||
renotes: "리노트"
|
||||
attach: "옇기"
|
||||
surrender: "아이예"
|
||||
information: "정보"
|
||||
_delivery:
|
||||
stop: "고만 보내예"
|
||||
_type:
|
||||
|
@@ -1330,6 +1330,7 @@ preferenceSyncConflictChoiceDevice: "장치 설정값"
|
||||
paste: "붙여넣기"
|
||||
emojiPalette: "이모지 팔레트"
|
||||
postForm: "글 입력란"
|
||||
information: "정보"
|
||||
_emojiPalette:
|
||||
palettes: "팔레트"
|
||||
paletteForMain: "메인으로 사용할 팔레트"
|
||||
@@ -2650,10 +2651,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "제공자를 신뢰할 수 있는 경우에만 설치하십시오."
|
||||
_plugin:
|
||||
title: "이 플러그인을 설치하시겠습니까?"
|
||||
metaTitle: "플러그인 정보"
|
||||
_theme:
|
||||
title: "이 테마를 설치하시겠습니까?"
|
||||
metaTitle: "테마 정보"
|
||||
_meta:
|
||||
base: "기본 컬러 스키마"
|
||||
_vendorInfo:
|
||||
|
@@ -394,6 +394,7 @@ searchByGoogle: "ຄົ້ນຫາ"
|
||||
file: "ໄຟລ໌"
|
||||
replies: "ຕອບກັບ"
|
||||
renotes: "Renote"
|
||||
information: "ກ່ຽວກັບ"
|
||||
_delivery:
|
||||
stop: "ໂຈະ"
|
||||
_type:
|
||||
|
@@ -462,6 +462,7 @@ loggedInAsBot: "Momenteel als bot ingelogd"
|
||||
icon: "Avatar"
|
||||
replies: "Antwoord"
|
||||
renotes: "Herdelen"
|
||||
information: "Over"
|
||||
_delivery:
|
||||
stop: "Opgeschort"
|
||||
_type:
|
||||
|
@@ -463,6 +463,7 @@ icon: "Avatar"
|
||||
replies: "Svar"
|
||||
renotes: "Renote"
|
||||
surrender: "Avbryt"
|
||||
information: "Informasjon"
|
||||
_delivery:
|
||||
stop: "Suspendert"
|
||||
_initialAccountSetting:
|
||||
|
@@ -1045,6 +1045,7 @@ lastNDays: "W ciągu ostatnich {n} dni"
|
||||
surrender: "Odrzuć"
|
||||
gameRetry: "Spróbuj ponownie"
|
||||
postForm: "Formularz tworzenia wpisu"
|
||||
information: "Informacje"
|
||||
_delivery:
|
||||
stop: "Zawieszono"
|
||||
_type:
|
||||
|
@@ -1302,6 +1302,7 @@ pleaseSelectAccount: "Selecione uma conta"
|
||||
availableRoles: "Cargos disponíveis"
|
||||
acknowledgeNotesAndEnable: "Ative após compreender as precauções."
|
||||
postForm: "Campo de postagem"
|
||||
information: "Informações"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_accountSettings:
|
||||
@@ -2598,10 +2599,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "Tenha certeza de que o distribuidor desse recurso é confiável antes da instalação."
|
||||
_plugin:
|
||||
title: "Deseja instalar esse plugin?"
|
||||
metaTitle: "Informações do plugin"
|
||||
_theme:
|
||||
title: "Deseja instalar esse tema?"
|
||||
metaTitle: "Informações do tema"
|
||||
_meta:
|
||||
base: "Paleta de cores base"
|
||||
_vendorInfo:
|
||||
|
@@ -646,6 +646,7 @@ show: "Arată"
|
||||
icon: "Avatar"
|
||||
replies: "Răspunde"
|
||||
renotes: "Re-notează"
|
||||
information: "Despre"
|
||||
_delivery:
|
||||
stop: "Suspendat"
|
||||
_type:
|
||||
|
@@ -1182,6 +1182,7 @@ alwaysConfirmFollow: "Всегда подтверждать подписку"
|
||||
inquiry: "Связаться"
|
||||
messageToFollower: "Сообщение подписчикам"
|
||||
postForm: "Форма отправки"
|
||||
information: "Описание"
|
||||
_settings:
|
||||
webhook: "Вебхук"
|
||||
_delivery:
|
||||
|
@@ -918,6 +918,7 @@ sourceCode: "Zdrojový kód"
|
||||
flip: "Preklopiť"
|
||||
lastNDays: "Posledných {n} dní"
|
||||
postForm: "Napísať poznámku"
|
||||
information: "Informácie"
|
||||
_delivery:
|
||||
stop: "Zmrazené"
|
||||
_type:
|
||||
|
@@ -562,6 +562,7 @@ inquiry: "Kontakt"
|
||||
tryAgain: "Försök igen senare"
|
||||
signinWithPasskey: "Logga in med nyckel"
|
||||
unknownWebAuthnKey: "Okänd nyckel"
|
||||
information: "Om"
|
||||
_delivery:
|
||||
stop: "Suspenderad"
|
||||
_type:
|
||||
|
@@ -1293,6 +1293,7 @@ prohibitedWordsForNameOfUserDescription: "หากมีสตริงใด
|
||||
yourNameContainsProhibitedWords: "ชื่อของคุณนั้นมีคำที่ต้องห้าม"
|
||||
yourNameContainsProhibitedWordsDescription: "ถ้าหากคุณต้องการใช้ชื่อนี้ กรุณาติดต่อผู้ดูแลระบบของเซิร์ฟเวอร์นะค่ะ"
|
||||
postForm: "แบบฟอร์มการโพสต์"
|
||||
information: "เกี่ยวกับ"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_abuseUserReport:
|
||||
@@ -2572,10 +2573,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "โปรดตรวจสอบให้แน่ใจว่าแหล่งแจกหน่ายมีความน่าเชื่อถือก่อนทำการติดตั้ง"
|
||||
_plugin:
|
||||
title: "ต้องการติดตั้งปลั๊กอินนี้ใช่ไหม?"
|
||||
metaTitle: "ข้อมูลส่วนเสริม"
|
||||
_theme:
|
||||
title: "ต้องการติดตั้งธีมนี้ใช่ไหม?"
|
||||
metaTitle: "ข้อมูลธีม"
|
||||
_meta:
|
||||
base: "โทนสีพื้นฐาน"
|
||||
_vendorInfo:
|
||||
|
@@ -910,6 +910,7 @@ sourceCode: "Вихідний код"
|
||||
flip: "Перевернути"
|
||||
lastNDays: "Останні {n} днів"
|
||||
postForm: "Створення нотатки"
|
||||
information: "Інформація"
|
||||
_delivery:
|
||||
stop: "Призупинено"
|
||||
_type:
|
||||
|
@@ -841,6 +841,7 @@ icon: "Avatar"
|
||||
replies: "Javob berish"
|
||||
renotes: "Qayta qayd etish"
|
||||
flip: "Teskari"
|
||||
information: "Haqida"
|
||||
_delivery:
|
||||
stop: "To'xtatilgan"
|
||||
_type:
|
||||
|
@@ -1120,6 +1120,7 @@ cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải
|
||||
lastNDays: "{n} ngày trước"
|
||||
surrender: "Từ chối"
|
||||
postForm: "Mẫu đăng"
|
||||
information: "Giới thiệu"
|
||||
_delivery:
|
||||
stop: "Đã vô hiệu hóa"
|
||||
_type:
|
||||
|
@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} 说了关于「{word}」的什么"
|
||||
makeActive: "启用"
|
||||
display: "显示"
|
||||
copy: "复制"
|
||||
copiedToClipboard: "已复制到剪贴板"
|
||||
metrics: "指标"
|
||||
overview: "概览"
|
||||
logs: "日志"
|
||||
@@ -1332,6 +1333,8 @@ preferenceSyncConflictChoiceCancel: "取消同步"
|
||||
paste: "粘贴"
|
||||
emojiPalette: "表情符号调色板"
|
||||
postForm: "投稿窗口"
|
||||
textCount: "字数"
|
||||
information: "关于"
|
||||
_emojiPalette:
|
||||
palettes: "调色板"
|
||||
enableSyncBetweenDevicesForPalettes: "启用调色板的设备间同步"
|
||||
@@ -2671,10 +2674,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "请在安装前确保来源可靠"
|
||||
_plugin:
|
||||
title: "要安装此插件吗?"
|
||||
metaTitle: "插件信息"
|
||||
_theme:
|
||||
title: "要安装此主题吗?"
|
||||
metaTitle: "主题信息"
|
||||
_meta:
|
||||
base: "基本配色方案"
|
||||
_vendorInfo:
|
||||
|
@@ -698,6 +698,7 @@ userSaysSomethingAbout: "{name} 說了一些關於「{word}」的話"
|
||||
makeActive: "啟用"
|
||||
display: "檢視"
|
||||
copy: "複製"
|
||||
copiedToClipboard: "已複製到剪貼簿"
|
||||
metrics: "指標"
|
||||
overview: "概覽"
|
||||
logs: "日誌"
|
||||
@@ -1332,6 +1333,8 @@ preferenceSyncConflictChoiceCancel: "取消啟用同步"
|
||||
paste: "貼上"
|
||||
emojiPalette: "表情符號調色盤"
|
||||
postForm: "發文視窗"
|
||||
textCount: "字數"
|
||||
information: "關於"
|
||||
_emojiPalette:
|
||||
palettes: "調色盤"
|
||||
enableSyncBetweenDevicesForPalettes: "啟用裝置與裝置之間的調色盤同步化"
|
||||
@@ -1355,6 +1358,8 @@ _settings:
|
||||
appearanceBanner: "您可以根據喜好設定與用戶端外觀和顯示方式相關的設定。"
|
||||
soundsBanner: "您可以調整用戶端播放的聲音設定。"
|
||||
timelineAndNote: "時間軸及貼文"
|
||||
makeEveryTextElementsSelectable: "允許選取所有文字"
|
||||
makeEveryTextElementsSelectable_description: "啟用此功能後,可能會在某些情境下降低可用性。"
|
||||
_preferencesProfile:
|
||||
profileName: "設定檔案名稱"
|
||||
profileNameDescription: "設定一個名稱來識別此裝置。"
|
||||
@@ -2519,6 +2524,7 @@ _notification:
|
||||
achievementEarned: "獲得成就"
|
||||
exportCompleted: "已完成匯出。"
|
||||
login: "登入"
|
||||
createToken: "建立存取權杖"
|
||||
test: "通知測試"
|
||||
app: "應用程式通知"
|
||||
_actions:
|
||||
@@ -2668,10 +2674,8 @@ _externalResourceInstaller:
|
||||
checkVendorBeforeInstall: "安裝前請確認提供者是可信賴的。"
|
||||
_plugin:
|
||||
title: "要安裝此外掛嘛?"
|
||||
metaTitle: "外掛資訊"
|
||||
_theme:
|
||||
title: "要安裝此佈景主題嗎?"
|
||||
metaTitle: "佈景主題資訊"
|
||||
_meta:
|
||||
base: "基本配色方案"
|
||||
_vendorInfo:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2025.3.2-beta.2",
|
||||
"version": "2025.3.2-beta.9",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -37,17 +37,17 @@
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-android-arm64": "1.3.11",
|
||||
"@swc/core-darwin-arm64": "1.10.16",
|
||||
"@swc/core-darwin-x64": "1.10.16",
|
||||
"@swc/core-darwin-arm64": "1.11.11",
|
||||
"@swc/core-darwin-x64": "1.11.11",
|
||||
"@swc/core-freebsd-x64": "1.3.11",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.10.16",
|
||||
"@swc/core-linux-arm64-gnu": "1.10.16",
|
||||
"@swc/core-linux-arm64-musl": "1.10.16",
|
||||
"@swc/core-linux-x64-gnu": "1.10.16",
|
||||
"@swc/core-linux-x64-musl": "1.10.16",
|
||||
"@swc/core-win32-arm64-msvc": "1.10.16",
|
||||
"@swc/core-win32-ia32-msvc": "1.10.16",
|
||||
"@swc/core-win32-x64-msvc": "1.10.16",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.11.11",
|
||||
"@swc/core-linux-arm64-gnu": "1.11.11",
|
||||
"@swc/core-linux-arm64-musl": "1.11.11",
|
||||
"@swc/core-linux-x64-gnu": "1.11.11",
|
||||
"@swc/core-linux-x64-musl": "1.11.11",
|
||||
"@swc/core-win32-arm64-msvc": "1.11.11",
|
||||
"@swc/core-win32-ia32-msvc": "1.11.11",
|
||||
"@swc/core-win32-x64-msvc": "1.11.11",
|
||||
"@tensorflow/tfjs": "4.22.0",
|
||||
"@tensorflow/tfjs-node": "4.22.0",
|
||||
"bufferutil": "4.0.9",
|
||||
@@ -67,23 +67,23 @@
|
||||
"utf-8-validate": "6.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.749.0",
|
||||
"@aws-sdk/lib-storage": "3.749.0",
|
||||
"@aws-sdk/client-s3": "3.772.0",
|
||||
"@aws-sdk/lib-storage": "3.772.0",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@fastify/accepts": "5.0.2",
|
||||
"@fastify/cookie": "11.0.2",
|
||||
"@fastify/cors": "10.0.2",
|
||||
"@fastify/cors": "10.1.0",
|
||||
"@fastify/express": "4.0.2",
|
||||
"@fastify/http-proxy": "10.0.2",
|
||||
"@fastify/multipart": "9.0.3",
|
||||
"@fastify/static": "8.1.0",
|
||||
"@fastify/static": "8.1.1",
|
||||
"@fastify/view": "10.0.2",
|
||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||
"@misskey-dev/summaly": "5.2.0",
|
||||
"@napi-rs/canvas": "0.1.67",
|
||||
"@nestjs/common": "11.0.9",
|
||||
"@nestjs/core": "11.0.9",
|
||||
"@nestjs/testing": "11.0.9",
|
||||
"@napi-rs/canvas": "0.1.68",
|
||||
"@nestjs/common": "11.0.12",
|
||||
"@nestjs/core": "11.0.12",
|
||||
"@nestjs/testing": "11.0.12",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@sentry/node": "8.55.0",
|
||||
"@sentry/profiling-node": "8.55.0",
|
||||
@@ -91,7 +91,7 @@
|
||||
"@sinonjs/fake-timers": "11.3.1",
|
||||
"@smithy/node-http-handler": "2.5.0",
|
||||
"@swc/cli": "0.6.0",
|
||||
"@swc/core": "1.10.16",
|
||||
"@swc/core": "1.11.11",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.17.1",
|
||||
@@ -100,7 +100,7 @@
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.3",
|
||||
"bullmq": "5.41.1",
|
||||
"bullmq": "5.44.1",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "9.0.2",
|
||||
"chalk": "5.4.1",
|
||||
@@ -122,7 +122,7 @@
|
||||
"hpagent": "1.2.0",
|
||||
"htmlescape": "1.1.1",
|
||||
"http-link-header": "1.1.3",
|
||||
"ioredis": "5.5.0",
|
||||
"ioredis": "5.6.0",
|
||||
"ip-cidr": "4.0.2",
|
||||
"ipaddr.js": "2.2.0",
|
||||
"is-svg": "5.1.0",
|
||||
@@ -131,26 +131,26 @@
|
||||
"json5": "2.2.3",
|
||||
"jsonld": "8.3.3",
|
||||
"jsrsasign": "11.1.0",
|
||||
"juice": "11.0.0",
|
||||
"meilisearch": "0.48.2",
|
||||
"juice": "11.0.1",
|
||||
"meilisearch": "0.49.0",
|
||||
"mfm-js": "0.24.0",
|
||||
"microformats-parser": "2.0.2",
|
||||
"mime-types": "2.1.35",
|
||||
"misskey-js": "workspace:*",
|
||||
"misskey-reversi": "workspace:*",
|
||||
"ms": "3.0.0-canary.1",
|
||||
"nanoid": "5.1.0",
|
||||
"nanoid": "5.1.5",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"nodemailer": "6.10.0",
|
||||
"nsfwjs": "4.2.0",
|
||||
"oauth": "0.10.0",
|
||||
"oauth": "0.10.2",
|
||||
"oauth2orize": "1.12.0",
|
||||
"oauth2orize-pkce": "0.1.2",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "9.3.6",
|
||||
"parse5": "7.2.1",
|
||||
"pg": "8.13.3",
|
||||
"pg": "8.14.1",
|
||||
"pkce-challenge": "4.1.0",
|
||||
"probe-image-size": "7.2.3",
|
||||
"promise-limit": "2.7.0",
|
||||
@@ -163,8 +163,8 @@
|
||||
"reflect-metadata": "0.2.2",
|
||||
"rename": "1.0.4",
|
||||
"rss-parser": "3.13.0",
|
||||
"rxjs": "7.8.1",
|
||||
"sanitize-html": "2.14.0",
|
||||
"rxjs": "7.8.2",
|
||||
"sanitize-html": "2.15.0",
|
||||
"secure-json-parse": "3.0.2",
|
||||
"sharp": "0.33.5",
|
||||
"slacc": "0.0.10",
|
||||
@@ -173,14 +173,14 @@
|
||||
"systeminformation": "5.25.11",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tmp": "0.2.3",
|
||||
"tsc-alias": "1.8.10",
|
||||
"tsc-alias": "1.8.11",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typeorm": "0.3.20",
|
||||
"typescript": "5.7.3",
|
||||
"ulid": "2.3.0",
|
||||
"typeorm": "0.3.21",
|
||||
"typescript": "5.8.2",
|
||||
"ulid": "2.4.0",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.6.7",
|
||||
"ws": "8.18.0",
|
||||
"ws": "8.18.1",
|
||||
"xev": "3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -204,7 +204,7 @@
|
||||
"@types/jsrsasign": "10.5.15",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/ms": "0.7.34",
|
||||
"@types/node": "22.13.4",
|
||||
"@types/node": "22.13.10",
|
||||
"@types/nodemailer": "6.4.17",
|
||||
"@types/oauth": "0.9.6",
|
||||
"@types/oauth2orize": "1.11.5",
|
||||
@@ -223,9 +223,9 @@
|
||||
"@types/tmp": "0.2.6",
|
||||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@types/ws": "8.5.14",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||
"@typescript-eslint/parser": "8.24.0",
|
||||
"@types/ws": "8.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"aws-sdk-client-mock": "4.1.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
|
@@ -7,42 +7,16 @@ import { Injectable } from '@nestjs/common';
|
||||
import { MiAbuseUserReport, MiNote, MiUser, MiWebhook } from '@/models/_.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { MiSystemWebhook, type SystemWebhookEventType } from '@/models/SystemWebhook.js';
|
||||
import { AbuseReportPayload, SystemWebhookPayload, SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
import { type AbuseReportPayload, SystemWebhookPayload, SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { type Packed } from '@/misc/json-schema.js';
|
||||
import { type WebhookEventTypes } from '@/models/Webhook.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { type UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
|
||||
|
||||
const oneDayMillis = 24 * 60 * 60 * 1000;
|
||||
|
||||
function generateAbuseReport(override?: Partial<MiAbuseUserReport>): AbuseReportPayload {
|
||||
const result: MiAbuseUserReport = {
|
||||
id: 'dummy-abuse-report1',
|
||||
targetUserId: 'dummy-target-user',
|
||||
targetUser: null,
|
||||
reporterId: 'dummy-reporter-user',
|
||||
reporter: null,
|
||||
assigneeId: null,
|
||||
assignee: null,
|
||||
resolved: false,
|
||||
forwarded: false,
|
||||
comment: 'This is a dummy report for testing purposes.',
|
||||
targetUserHost: null,
|
||||
reporterHost: null,
|
||||
resolvedAs: null,
|
||||
moderationNote: 'foo',
|
||||
...override,
|
||||
};
|
||||
|
||||
return {
|
||||
...result,
|
||||
targetUser: result.targetUser ? toPackedUserLite(result.targetUser) : null,
|
||||
reporter: result.reporter ? toPackedUserLite(result.reporter) : null,
|
||||
assignee: result.assignee ? toPackedUserLite(result.assignee) : null,
|
||||
};
|
||||
}
|
||||
|
||||
function generateDummyUser(override?: Partial<MiUser>): MiUser {
|
||||
return {
|
||||
id: 'dummy-user-1',
|
||||
@@ -134,124 +108,6 @@ function generateDummyNote(override?: Partial<MiNote>): MiNote {
|
||||
};
|
||||
}
|
||||
|
||||
function toPackedNote(note: MiNote, detail = true, override?: Packed<'Note'>): Packed<'Note'> {
|
||||
return {
|
||||
id: note.id,
|
||||
createdAt: new Date().toISOString(),
|
||||
deletedAt: null,
|
||||
text: note.text,
|
||||
cw: note.cw,
|
||||
userId: note.userId,
|
||||
user: toPackedUserLite(note.user ?? generateDummyUser()),
|
||||
replyId: note.replyId,
|
||||
renoteId: note.renoteId,
|
||||
isHidden: false,
|
||||
visibility: note.visibility,
|
||||
mentions: note.mentions,
|
||||
visibleUserIds: note.visibleUserIds,
|
||||
fileIds: note.fileIds,
|
||||
files: [],
|
||||
tags: note.tags,
|
||||
poll: null,
|
||||
emojis: note.emojis,
|
||||
channelId: note.channelId,
|
||||
channel: note.channel,
|
||||
localOnly: note.localOnly,
|
||||
reactionAcceptance: note.reactionAcceptance,
|
||||
reactionEmojis: {},
|
||||
reactions: {},
|
||||
reactionCount: 0,
|
||||
renoteCount: note.renoteCount,
|
||||
repliesCount: note.repliesCount,
|
||||
uri: note.uri ?? undefined,
|
||||
url: note.url ?? undefined,
|
||||
reactionAndUserPairCache: note.reactionAndUserPairCache,
|
||||
...(detail ? {
|
||||
clippedCount: note.clippedCount,
|
||||
reply: note.reply ? toPackedNote(note.reply, false) : null,
|
||||
renote: note.renote ? toPackedNote(note.renote, true) : null,
|
||||
myReaction: null,
|
||||
} : {}),
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
function toPackedUserLite(user: MiUser, override?: Packed<'UserLite'>): Packed<'UserLite'> {
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
host: user.host,
|
||||
avatarUrl: user.avatarUrl,
|
||||
avatarBlurhash: user.avatarBlurhash,
|
||||
avatarDecorations: user.avatarDecorations.map(it => ({
|
||||
id: it.id,
|
||||
angle: it.angle,
|
||||
flipH: it.flipH,
|
||||
url: 'https://example.com/dummy-image001.png',
|
||||
offsetX: it.offsetX,
|
||||
offsetY: it.offsetY,
|
||||
})),
|
||||
isBot: user.isBot,
|
||||
isCat: user.isCat,
|
||||
emojis: user.emojis,
|
||||
onlineStatus: 'active',
|
||||
badgeRoles: [],
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
function toPackedUserDetailedNotMe(user: MiUser, override?: Packed<'UserDetailedNotMe'>): Packed<'UserDetailedNotMe'> {
|
||||
return {
|
||||
...toPackedUserLite(user),
|
||||
url: null,
|
||||
uri: null,
|
||||
movedTo: null,
|
||||
alsoKnownAs: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: user.updatedAt?.toISOString() ?? null,
|
||||
lastFetchedAt: user.lastFetchedAt?.toISOString() ?? null,
|
||||
bannerUrl: user.bannerUrl,
|
||||
bannerBlurhash: user.bannerBlurhash,
|
||||
isLocked: user.isLocked,
|
||||
isSilenced: false,
|
||||
isSuspended: user.isSuspended,
|
||||
description: null,
|
||||
location: null,
|
||||
birthday: null,
|
||||
lang: null,
|
||||
fields: [],
|
||||
verifiedLinks: [],
|
||||
followersCount: user.followersCount,
|
||||
followingCount: user.followingCount,
|
||||
notesCount: user.notesCount,
|
||||
pinnedNoteIds: [],
|
||||
pinnedNotes: [],
|
||||
pinnedPageId: null,
|
||||
pinnedPage: null,
|
||||
publicReactions: true,
|
||||
followersVisibility: 'public',
|
||||
followingVisibility: 'public',
|
||||
twoFactorEnabled: false,
|
||||
usePasswordLessLogin: false,
|
||||
securityKeys: false,
|
||||
roles: [],
|
||||
memo: null,
|
||||
moderationNote: undefined,
|
||||
isFollowing: false,
|
||||
isFollowed: false,
|
||||
hasPendingFollowRequestFromYou: false,
|
||||
hasPendingFollowRequestToYou: false,
|
||||
isBlocking: false,
|
||||
isBlocked: false,
|
||||
isMuted: false,
|
||||
isRenoteMuted: false,
|
||||
notify: 'none',
|
||||
withReplies: true,
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
const dummyUser1 = generateDummyUser();
|
||||
const dummyUser2 = generateDummyUser({
|
||||
id: 'dummy-user-2',
|
||||
@@ -284,6 +140,7 @@ export class WebhookTestService {
|
||||
};
|
||||
|
||||
constructor(
|
||||
private customEmojiService: CustomEmojiService,
|
||||
private userWebhookService: UserWebhookService,
|
||||
private systemWebhookService: SystemWebhookService,
|
||||
private queueService: QueueService,
|
||||
@@ -354,31 +211,31 @@ export class WebhookTestService {
|
||||
|
||||
switch (params.type) {
|
||||
case 'note': {
|
||||
send('note', { note: toPackedNote(dummyNote1) });
|
||||
send('note', { note: await this.toPackedNote(dummyNote1) });
|
||||
break;
|
||||
}
|
||||
case 'reply': {
|
||||
send('reply', { note: toPackedNote(dummyReply1) });
|
||||
send('reply', { note: await this.toPackedNote(dummyReply1) });
|
||||
break;
|
||||
}
|
||||
case 'renote': {
|
||||
send('renote', { note: toPackedNote(dummyRenote1) });
|
||||
send('renote', { note: await this.toPackedNote(dummyRenote1) });
|
||||
break;
|
||||
}
|
||||
case 'mention': {
|
||||
send('mention', { note: toPackedNote(dummyMention1) });
|
||||
send('mention', { note: await this.toPackedNote(dummyMention1) });
|
||||
break;
|
||||
}
|
||||
case 'follow': {
|
||||
send('follow', { user: toPackedUserDetailedNotMe(dummyUser1) });
|
||||
send('follow', { user: await this.toPackedUserDetailedNotMe(dummyUser1) });
|
||||
break;
|
||||
}
|
||||
case 'followed': {
|
||||
send('followed', { user: toPackedUserLite(dummyUser2) });
|
||||
send('followed', { user: await this.toPackedUserLite(dummyUser2) });
|
||||
break;
|
||||
}
|
||||
case 'unfollow': {
|
||||
send('unfollow', { user: toPackedUserDetailedNotMe(dummyUser3) });
|
||||
send('unfollow', { user: await this.toPackedUserDetailedNotMe(dummyUser3) });
|
||||
break;
|
||||
}
|
||||
// まだ実装されていない (#9485)
|
||||
@@ -427,7 +284,7 @@ export class WebhookTestService {
|
||||
|
||||
switch (params.type) {
|
||||
case 'abuseReport': {
|
||||
send('abuseReport', generateAbuseReport({
|
||||
send('abuseReport', await this.generateAbuseReport({
|
||||
targetUserId: dummyUser1.id,
|
||||
targetUser: dummyUser1,
|
||||
reporterId: dummyUser2.id,
|
||||
@@ -436,7 +293,7 @@ export class WebhookTestService {
|
||||
break;
|
||||
}
|
||||
case 'abuseReportResolved': {
|
||||
send('abuseReportResolved', generateAbuseReport({
|
||||
send('abuseReportResolved', await this.generateAbuseReport({
|
||||
targetUserId: dummyUser1.id,
|
||||
targetUser: dummyUser1,
|
||||
reporterId: dummyUser2.id,
|
||||
@@ -448,7 +305,7 @@ export class WebhookTestService {
|
||||
break;
|
||||
}
|
||||
case 'userCreated': {
|
||||
send('userCreated', toPackedUserLite(dummyUser1));
|
||||
send('userCreated', await this.toPackedUserLite(dummyUser1));
|
||||
break;
|
||||
}
|
||||
case 'inactiveModeratorsWarning': {
|
||||
@@ -474,4 +331,153 @@ export class WebhookTestService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async generateAbuseReport(override?: Partial<MiAbuseUserReport>): Promise<AbuseReportPayload> {
|
||||
const result: MiAbuseUserReport = {
|
||||
id: 'dummy-abuse-report1',
|
||||
targetUserId: 'dummy-target-user',
|
||||
targetUser: null,
|
||||
reporterId: 'dummy-reporter-user',
|
||||
reporter: null,
|
||||
assigneeId: null,
|
||||
assignee: null,
|
||||
resolved: false,
|
||||
forwarded: false,
|
||||
comment: 'This is a dummy report for testing purposes.',
|
||||
targetUserHost: null,
|
||||
reporterHost: null,
|
||||
resolvedAs: null,
|
||||
moderationNote: 'foo',
|
||||
...override,
|
||||
};
|
||||
|
||||
return {
|
||||
...result,
|
||||
targetUser: result.targetUser ? await this.toPackedUserLite(result.targetUser) : null,
|
||||
reporter: result.reporter ? await this.toPackedUserLite(result.reporter) : null,
|
||||
assignee: result.assignee ? await this.toPackedUserLite(result.assignee) : null,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async toPackedNote(note: MiNote, detail = true, override?: Packed<'Note'>): Promise<Packed<'Note'>> {
|
||||
return {
|
||||
id: note.id,
|
||||
createdAt: new Date().toISOString(),
|
||||
deletedAt: null,
|
||||
text: note.text,
|
||||
cw: note.cw,
|
||||
userId: note.userId,
|
||||
user: await this.toPackedUserLite(note.user ?? generateDummyUser()),
|
||||
replyId: note.replyId,
|
||||
renoteId: note.renoteId,
|
||||
isHidden: false,
|
||||
visibility: note.visibility,
|
||||
mentions: note.mentions,
|
||||
visibleUserIds: note.visibleUserIds,
|
||||
fileIds: note.fileIds,
|
||||
files: [],
|
||||
tags: note.tags,
|
||||
poll: null,
|
||||
emojis: await this.customEmojiService.populateEmojis(note.emojis, note.userHost),
|
||||
channelId: note.channelId,
|
||||
channel: note.channel,
|
||||
localOnly: note.localOnly,
|
||||
reactionAcceptance: note.reactionAcceptance,
|
||||
reactionEmojis: {},
|
||||
reactions: {},
|
||||
reactionCount: 0,
|
||||
renoteCount: note.renoteCount,
|
||||
repliesCount: note.repliesCount,
|
||||
uri: note.uri ?? undefined,
|
||||
url: note.url ?? undefined,
|
||||
reactionAndUserPairCache: note.reactionAndUserPairCache,
|
||||
...(detail ? {
|
||||
clippedCount: note.clippedCount,
|
||||
reply: note.reply ? await this.toPackedNote(note.reply, false) : null,
|
||||
renote: note.renote ? await this.toPackedNote(note.renote, true) : null,
|
||||
myReaction: null,
|
||||
} : {}),
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async toPackedUserLite(user: MiUser, override?: Packed<'UserLite'>): Promise<Packed<'UserLite'>> {
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
host: user.host,
|
||||
avatarUrl: user.avatarUrl,
|
||||
avatarBlurhash: user.avatarBlurhash,
|
||||
avatarDecorations: user.avatarDecorations.map(it => ({
|
||||
id: it.id,
|
||||
angle: it.angle,
|
||||
flipH: it.flipH,
|
||||
url: 'https://example.com/dummy-image001.png',
|
||||
offsetX: it.offsetX,
|
||||
offsetY: it.offsetY,
|
||||
})),
|
||||
isBot: user.isBot,
|
||||
isCat: user.isCat,
|
||||
emojis: await this.customEmojiService.populateEmojis(user.emojis, user.host),
|
||||
onlineStatus: 'active',
|
||||
badgeRoles: [],
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async toPackedUserDetailedNotMe(user: MiUser, override?: Packed<'UserDetailedNotMe'>): Promise<Packed<'UserDetailedNotMe'>> {
|
||||
return {
|
||||
...await this.toPackedUserLite(user),
|
||||
url: null,
|
||||
uri: null,
|
||||
movedTo: null,
|
||||
alsoKnownAs: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: user.updatedAt?.toISOString() ?? null,
|
||||
lastFetchedAt: user.lastFetchedAt?.toISOString() ?? null,
|
||||
bannerUrl: user.bannerUrl,
|
||||
bannerBlurhash: user.bannerBlurhash,
|
||||
isLocked: user.isLocked,
|
||||
isSilenced: false,
|
||||
isSuspended: user.isSuspended,
|
||||
description: null,
|
||||
location: null,
|
||||
birthday: null,
|
||||
lang: null,
|
||||
fields: [],
|
||||
verifiedLinks: [],
|
||||
followersCount: user.followersCount,
|
||||
followingCount: user.followingCount,
|
||||
notesCount: user.notesCount,
|
||||
pinnedNoteIds: [],
|
||||
pinnedNotes: [],
|
||||
pinnedPageId: null,
|
||||
pinnedPage: null,
|
||||
publicReactions: true,
|
||||
followersVisibility: 'public',
|
||||
followingVisibility: 'public',
|
||||
twoFactorEnabled: false,
|
||||
usePasswordLessLogin: false,
|
||||
securityKeys: false,
|
||||
roles: [],
|
||||
memo: null,
|
||||
moderationNote: undefined,
|
||||
isFollowing: false,
|
||||
isFollowed: false,
|
||||
hasPendingFollowRequestFromYou: false,
|
||||
hasPendingFollowRequestToYou: false,
|
||||
isBlocking: false,
|
||||
isBlocked: false,
|
||||
isMuted: false,
|
||||
isRenoteMuted: false,
|
||||
notify: 'none',
|
||||
withReplies: true,
|
||||
...override,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'node:fs/promises';
|
||||
import { WritableStream } from 'node:stream/web';
|
||||
import type { PathLike } from 'node:fs';
|
||||
|
||||
/**
|
||||
|
@@ -166,6 +166,7 @@ export interface Schema extends OfSchema {
|
||||
readonly maximum?: number;
|
||||
readonly minimum?: number;
|
||||
readonly pattern?: string;
|
||||
readonly additionalProperties?: Schema | boolean;
|
||||
}
|
||||
|
||||
type RequiredPropertyNames<s extends Obj> = {
|
||||
@@ -217,6 +218,13 @@ type ObjectSchemaTypeDef<p extends Schema> =
|
||||
:
|
||||
p['anyOf'] extends ReadonlyArray<Schema> ? never : // see CONTRIBUTING.md
|
||||
p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> :
|
||||
p['additionalProperties'] extends true ? Record<string, any> :
|
||||
p['additionalProperties'] extends Schema ?
|
||||
p['additionalProperties'] extends infer AdditionalProperties ?
|
||||
AdditionalProperties extends Schema ?
|
||||
Record<string, SchemaType<AdditionalProperties>> :
|
||||
never :
|
||||
never :
|
||||
any;
|
||||
|
||||
type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>;
|
||||
|
@@ -6,6 +6,7 @@
|
||||
import * as fs from 'node:fs';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ZipReader } from 'slacc';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { EmojisRepository, DriveFilesRepository } from '@/models/_.js';
|
||||
import type Logger from '@/logger.js';
|
||||
@@ -86,6 +87,7 @@ export class ImportCustomEmojisProcessorService {
|
||||
const emojiPath = outputPath + '/' + record.fileName;
|
||||
await this.emojisRepository.delete({
|
||||
name: emojiInfo.name,
|
||||
host: IsNull(),
|
||||
});
|
||||
|
||||
try {
|
||||
|
@@ -13,7 +13,7 @@ import accepts from 'accepts';
|
||||
import vary from 'vary';
|
||||
import secureJson from 'secure-json-parse';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
|
||||
import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository, MiMeta } from '@/models/_.js';
|
||||
import * as url from '@/misc/prelude/url.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||
@@ -42,6 +42,9 @@ export class ActivityPubServerService {
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
|
||||
@Inject(DI.meta)
|
||||
private meta: MiMeta,
|
||||
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
@@ -102,6 +105,11 @@ export class ActivityPubServerService {
|
||||
|
||||
@bindThis
|
||||
private inbox(request: FastifyRequest, reply: FastifyReply) {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
let signature;
|
||||
|
||||
try {
|
||||
@@ -173,6 +181,11 @@ export class ActivityPubServerService {
|
||||
request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>,
|
||||
reply: FastifyReply,
|
||||
) {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = request.params.user;
|
||||
|
||||
const cursor = request.query.cursor;
|
||||
@@ -265,6 +278,11 @@ export class ActivityPubServerService {
|
||||
request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>,
|
||||
reply: FastifyReply,
|
||||
) {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = request.params.user;
|
||||
|
||||
const cursor = request.query.cursor;
|
||||
@@ -354,6 +372,11 @@ export class ActivityPubServerService {
|
||||
|
||||
@bindThis
|
||||
private async featured(request: FastifyRequest<{ Params: { user: string; }; }>, reply: FastifyReply) {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = request.params.user;
|
||||
|
||||
const user = await this.usersRepository.findOneBy({
|
||||
@@ -398,6 +421,11 @@ export class ActivityPubServerService {
|
||||
}>,
|
||||
reply: FastifyReply,
|
||||
) {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = request.params.user;
|
||||
|
||||
const sinceId = request.query.since_id;
|
||||
@@ -482,6 +510,11 @@ export class ActivityPubServerService {
|
||||
|
||||
@bindThis
|
||||
private async userInfo(request: FastifyRequest, reply: FastifyReply, user: MiUser | null) {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
reply.code(404);
|
||||
return;
|
||||
@@ -564,6 +597,11 @@ export class ActivityPubServerService {
|
||||
fastify.get<{ Params: { note: string; } }>('/notes/:note', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
|
||||
vary(reply.raw, 'Accept');
|
||||
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const note = await this.notesRepository.findOneBy({
|
||||
id: request.params.note,
|
||||
visibility: In(['public', 'home']),
|
||||
@@ -594,6 +632,11 @@ export class ActivityPubServerService {
|
||||
fastify.get<{ Params: { note: string; } }>('/notes/:note/activity', async (request, reply) => {
|
||||
vary(reply.raw, 'Accept');
|
||||
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const note = await this.notesRepository.findOneBy({
|
||||
id: request.params.note,
|
||||
userHost: IsNull(),
|
||||
@@ -634,6 +677,11 @@ export class ActivityPubServerService {
|
||||
|
||||
// publickey
|
||||
fastify.get<{ Params: { user: string; } }>('/users/:user/publickey', async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = request.params.user;
|
||||
|
||||
const user = await this.usersRepository.findOneBy({
|
||||
@@ -661,6 +709,11 @@ export class ActivityPubServerService {
|
||||
fastify.get<{ Params: { user: string; } }>('/users/:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
|
||||
vary(reply.raw, 'Accept');
|
||||
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = request.params.user;
|
||||
|
||||
const user = await this.usersRepository.findOneBy({
|
||||
@@ -674,6 +727,11 @@ export class ActivityPubServerService {
|
||||
fastify.get<{ Params: { acct: string; } }>('/@:acct', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
|
||||
vary(reply.raw, 'Accept');
|
||||
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const acct = Acct.parse(request.params.acct);
|
||||
|
||||
const user = await this.usersRepository.findOneBy({
|
||||
@@ -688,6 +746,11 @@ export class ActivityPubServerService {
|
||||
|
||||
// emoji
|
||||
fastify.get<{ Params: { emoji: string; } }>('/emojis/:emoji', async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const emoji = await this.emojisRepository.findOneBy({
|
||||
host: IsNull(),
|
||||
name: request.params.emoji,
|
||||
@@ -705,6 +768,11 @@ export class ActivityPubServerService {
|
||||
|
||||
// like
|
||||
fastify.get<{ Params: { like: string; } }>('/likes/:like', async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const reaction = await this.noteReactionsRepository.findOneBy({ id: request.params.like });
|
||||
|
||||
if (reaction == null) {
|
||||
@@ -726,6 +794,11 @@ export class ActivityPubServerService {
|
||||
|
||||
// follow
|
||||
fastify.get<{ Params: { follower: string; followee: string; } }>('/follows/:follower/:followee', async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
// This may be used before the follow is completed, so we do not
|
||||
// check if the following exists.
|
||||
|
||||
@@ -752,6 +825,11 @@ export class ActivityPubServerService {
|
||||
|
||||
// follow
|
||||
fastify.get<{ Params: { followRequestId: string; } }>('/follows/:followRequestId', async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
// This may be used before the follow is completed, so we do not
|
||||
// check if the following exists and only check if the follow request exists.
|
||||
|
||||
|
@@ -8,7 +8,7 @@ import { IsNull } from 'typeorm';
|
||||
import vary from 'vary';
|
||||
import fastifyAccepts from '@fastify/accepts';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { UsersRepository } from '@/models/_.js';
|
||||
import type { MiMeta, UsersRepository } from '@/models/_.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { escapeAttribute, escapeValue } from '@/misc/prelude/xml.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
@@ -26,6 +26,9 @@ export class WellKnownServerService {
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
|
||||
@Inject(DI.meta)
|
||||
private meta: MiMeta,
|
||||
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
@@ -66,6 +69,11 @@ export class WellKnownServerService {
|
||||
});
|
||||
|
||||
fastify.get('/.well-known/host-meta', async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
reply.header('Content-Type', xrd);
|
||||
return XRD({ element: 'Link', attributes: {
|
||||
rel: 'lrdd',
|
||||
@@ -75,6 +83,11 @@ export class WellKnownServerService {
|
||||
});
|
||||
|
||||
fastify.get('/.well-known/host-meta.json', async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
reply.header('Content-Type', 'application/json');
|
||||
return {
|
||||
links: [{
|
||||
@@ -86,6 +99,11 @@ export class WellKnownServerService {
|
||||
});
|
||||
|
||||
fastify.get('/.well-known/nodeinfo', async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
return { links: this.nodeinfoServerService.getLinks() };
|
||||
});
|
||||
|
||||
@@ -99,6 +117,11 @@ fastify.get('/.well-known/change-password', async (request, reply) => {
|
||||
*/
|
||||
|
||||
fastify.get<{ Querystring: { resource: string } }>(webFingerPath, async (request, reply) => {
|
||||
if (this.meta.federation === 'none') {
|
||||
reply.code(403);
|
||||
return;
|
||||
}
|
||||
|
||||
const fromId = (id: MiUser['id']): FindOptionsWhere<MiUser> => ({
|
||||
id,
|
||||
host: IsNull(),
|
||||
|
@@ -391,10 +391,10 @@ export class ApiCallService implements OnApplicationShutdown {
|
||||
}
|
||||
}
|
||||
|
||||
if (ep.meta.requireRolePolicy != null && (this.meta.rootUserId !== user!.id)) {
|
||||
if (ep.meta.requiredRolePolicy != null && (this.meta.rootUserId !== user!.id)) {
|
||||
const myRoles = await this.roleService.getUserRoles(user!.id);
|
||||
const policies = await this.roleService.getUserPolicies(user!.id);
|
||||
if (!policies[ep.meta.requireRolePolicy] && !myRoles.some(r => r.isAdministrator)) {
|
||||
if (!policies[ep.meta.requiredRolePolicy] && !myRoles.some(r => r.isAdministrator)) {
|
||||
throw new ApiError({
|
||||
message: 'You are not assigned to a required role.',
|
||||
code: 'ROLE_PERMISSION_DENIED',
|
||||
|
@@ -39,7 +39,7 @@ interface IEndpointMetaBase {
|
||||
*/
|
||||
readonly requireAdmin?: boolean;
|
||||
|
||||
readonly requireRolePolicy?: KeyOf<'RolePolicies'>;
|
||||
readonly requiredRolePolicy?: KeyOf<'RolePolicies'>;
|
||||
|
||||
/**
|
||||
* 引っ越し済みのユーザーによるリクエストを禁止するか
|
||||
|
@@ -12,7 +12,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageAvatarDecorations',
|
||||
requiredRolePolicy: 'canManageAvatarDecorations',
|
||||
kind: 'write:admin:avatar-decorations',
|
||||
|
||||
res: {
|
||||
|
@@ -13,7 +13,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageAvatarDecorations',
|
||||
requiredRolePolicy: 'canManageAvatarDecorations',
|
||||
kind: 'write:admin:avatar-decorations',
|
||||
errors: {
|
||||
},
|
||||
|
@@ -13,7 +13,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageAvatarDecorations',
|
||||
requiredRolePolicy: 'canManageAvatarDecorations',
|
||||
kind: 'read:admin:avatar-decorations',
|
||||
|
||||
res: {
|
||||
|
@@ -13,7 +13,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageAvatarDecorations',
|
||||
requiredRolePolicy: 'canManageAvatarDecorations',
|
||||
kind: 'write:admin:avatar-decorations',
|
||||
|
||||
errors: {
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
} as const;
|
||||
|
||||
|
@@ -16,7 +16,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
|
||||
errors: {
|
||||
|
@@ -17,7 +17,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
|
||||
errors: {
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
} as const;
|
||||
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
|
||||
errors: {
|
||||
|
@@ -10,7 +10,7 @@ import { QueueService } from '@/core/QueueService.js';
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -16,7 +16,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'read:admin:emoji',
|
||||
|
||||
res: {
|
||||
|
@@ -16,7 +16,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'read:admin:emoji',
|
||||
|
||||
res: {
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
} as const;
|
||||
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
} as const;
|
||||
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
} as const;
|
||||
|
||||
|
@@ -11,7 +11,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
} as const;
|
||||
|
||||
|
@@ -14,7 +14,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'write:admin:emoji',
|
||||
|
||||
errors: {
|
||||
|
@@ -16,7 +16,7 @@ import { ApiError } from '../../error.js';
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canImportAntennas',
|
||||
requiredRolePolicy: 'canImportAntennas',
|
||||
prohibitMoved: true,
|
||||
|
||||
limit: {
|
||||
|
@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canImportBlocking',
|
||||
requiredRolePolicy: 'canImportBlocking',
|
||||
prohibitMoved: true,
|
||||
|
||||
limit: {
|
||||
|
@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canImportFollowing',
|
||||
requiredRolePolicy: 'canImportFollowing',
|
||||
prohibitMoved: true,
|
||||
limit: {
|
||||
duration: ms('1hour'),
|
||||
|
@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canImportMuting',
|
||||
requiredRolePolicy: 'canImportMuting',
|
||||
prohibitMoved: true,
|
||||
|
||||
limit: {
|
||||
|
@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
|
||||
export const meta = {
|
||||
secure: true,
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canImportUserLists',
|
||||
requiredRolePolicy: 'canImportUserLists',
|
||||
prohibitMoved: true,
|
||||
limit: {
|
||||
duration: ms('1hour'),
|
||||
|
@@ -18,7 +18,7 @@ export const meta = {
|
||||
tags: ['meta'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canInvite',
|
||||
requiredRolePolicy: 'canInvite',
|
||||
kind: 'write:invite-codes',
|
||||
|
||||
errors: {
|
||||
|
@@ -14,7 +14,7 @@ export const meta = {
|
||||
tags: ['meta'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canInvite',
|
||||
requiredRolePolicy: 'canInvite',
|
||||
kind: 'write:invite-codes',
|
||||
|
||||
errors: {
|
||||
|
@@ -15,7 +15,7 @@ export const meta = {
|
||||
tags: ['meta'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canInvite',
|
||||
requiredRolePolicy: 'canInvite',
|
||||
kind: 'read:invite-codes',
|
||||
|
||||
res: {
|
||||
|
@@ -14,7 +14,7 @@ export const meta = {
|
||||
tags: ['meta'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canInvite',
|
||||
requiredRolePolicy: 'canInvite',
|
||||
kind: 'read:invite-codes',
|
||||
|
||||
res: {
|
||||
|
@@ -12,7 +12,7 @@ export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireRolePolicy: 'canManageCustomEmojis',
|
||||
requiredRolePolicy: 'canManageCustomEmojis',
|
||||
kind: 'read:admin:emoji',
|
||||
|
||||
res: {
|
||||
|
@@ -14,6 +14,7 @@ import { MiSystemWebhook, MiUser, MiWebhook, UserProfilesRepository, UsersReposi
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
|
||||
describe('WebhookTestService', () => {
|
||||
let app: TestingModule;
|
||||
@@ -56,6 +57,11 @@ describe('WebhookTestService', () => {
|
||||
providers: [
|
||||
WebhookTestService,
|
||||
IdService,
|
||||
{
|
||||
provide: CustomEmojiService, useFactory: () => ({
|
||||
populateEmojis: jest.fn(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
provide: QueueService, useFactory: () => ({
|
||||
systemWebhookDeliver: jest.fn(),
|
||||
|
@@ -16,7 +16,7 @@
|
||||
"@rollup/pluginutils": "5.1.4",
|
||||
"@tabler/icons-webfont": "3.31.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"@vitejs/plugin-vue": "5.2.1",
|
||||
"@vitejs/plugin-vue": "5.2.3",
|
||||
"@vue/compiler-sfc": "3.5.13",
|
||||
"astring": "1.9.0",
|
||||
"buraha": "0.0.1",
|
||||
@@ -25,16 +25,16 @@
|
||||
"misskey-js": "workspace:*",
|
||||
"frontend-shared": "workspace:*",
|
||||
"punycode.js": "2.3.1",
|
||||
"rollup": "4.34.9",
|
||||
"sass": "1.85.1",
|
||||
"shiki": "3.1.0",
|
||||
"rollup": "4.36.0",
|
||||
"sass": "1.86.0",
|
||||
"shiki": "3.2.1",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tsc-alias": "1.8.11",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "5.8.2",
|
||||
"uuid": "11.1.0",
|
||||
"json5": "2.2.3",
|
||||
"vite": "6.2.1",
|
||||
"vite": "6.2.2",
|
||||
"vue": "3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -42,26 +42,26 @@
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/estree": "1.0.6",
|
||||
"@types/micromatch": "4.0.9",
|
||||
"@types/node": "22.13.9",
|
||||
"@types/node": "22.13.11",
|
||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/ws": "8.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||
"@typescript-eslint/parser": "8.26.0",
|
||||
"@vitest/coverage-v8": "3.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@vitest/coverage-v8": "3.0.9",
|
||||
"@vue/runtime-core": "3.5.13",
|
||||
"acorn": "8.14.1",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-vue": "10.0.0",
|
||||
"fast-glob": "3.3.3",
|
||||
"happy-dom": "17.3.0",
|
||||
"happy-dom": "17.4.4",
|
||||
"intersection-observer": "0.12.2",
|
||||
"micromatch": "4.0.8",
|
||||
"msw": "2.7.3",
|
||||
"nodemon": "3.1.9",
|
||||
"prettier": "3.5.3",
|
||||
"start-server-and-test": "2.0.10",
|
||||
"start-server-and-test": "2.0.11",
|
||||
"vite-plugin-turbosnap": "1.0.3",
|
||||
"vue-component-type-helpers": "2.2.8",
|
||||
"vue-eslint-parser": "10.1.1",
|
||||
|
@@ -33,13 +33,11 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
|
||||
Math.min(navigator.hardwareConcurrency - 1, 4),
|
||||
);
|
||||
resolve(workers);
|
||||
if (_DEV_) console.log('WebGL2 in worker is supported!');
|
||||
} else {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
resolve(canvas);
|
||||
if (_DEV_) console.log('WebGL2 in worker is not supported...');
|
||||
}
|
||||
testWorker.terminate();
|
||||
});
|
||||
|
@@ -12,6 +12,7 @@ const siteName = document.querySelector<HTMLMetaElement>('meta[property="og:site
|
||||
export const host = address.host;
|
||||
export const hostname = address.hostname;
|
||||
export const url = address.origin;
|
||||
export const port = address.port;
|
||||
export const apiUrl = location.origin + '/api';
|
||||
export const wsOrigin = location.origin;
|
||||
export const lang = localStorage.getItem('lang') ?? 'en-US';
|
||||
|
@@ -109,12 +109,6 @@ export const ROLE_POLICIES = [
|
||||
'canImportUserLists',
|
||||
] as const;
|
||||
|
||||
// なんか動かない
|
||||
//export const CURRENT_STICKY_TOP = Symbol('CURRENT_STICKY_TOP');
|
||||
//export const CURRENT_STICKY_BOTTOM = Symbol('CURRENT_STICKY_BOTTOM');
|
||||
export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP';
|
||||
export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM';
|
||||
|
||||
export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg';
|
||||
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg';
|
||||
export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg';
|
||||
|
@@ -21,10 +21,10 @@
|
||||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "22.13.9",
|
||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||
"@typescript-eslint/parser": "8.26.0",
|
||||
"esbuild": "0.25.0",
|
||||
"@types/node": "22.13.11",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"esbuild": "0.25.1",
|
||||
"eslint-plugin-vue": "10.0.0",
|
||||
"nodemon": "3.1.9",
|
||||
"typescript": "5.8.2",
|
||||
|
@@ -131,7 +131,7 @@ export function imageDataUrl(options?: {
|
||||
alpha?: number,
|
||||
}
|
||||
}, seed?: string): string {
|
||||
const canvas = document.createElement('canvas');
|
||||
const canvas = window.document.createElement('canvas');
|
||||
canvas.width = options?.size?.width ?? 100;
|
||||
canvas.height = options?.size?.height ?? 100;
|
||||
|
||||
|
@@ -23,9 +23,9 @@ let misskeyOS = null;
|
||||
|
||||
function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) {
|
||||
unobserve();
|
||||
const theme = themes[document.documentElement.dataset.misskeyTheme];
|
||||
const theme = themes[window.document.documentElement.dataset.misskeyTheme];
|
||||
if (theme) {
|
||||
applyTheme(themes[document.documentElement.dataset.misskeyTheme]);
|
||||
applyTheme(themes[window.document.documentElement.dataset.misskeyTheme]);
|
||||
} else {
|
||||
applyTheme(themes['l-light']);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) {
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(document.documentElement, {
|
||||
observer.observe(window.document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-misskey-theme'],
|
||||
});
|
||||
@@ -64,12 +64,12 @@ initialize({
|
||||
initLocalStorage();
|
||||
queueMicrotask(() => {
|
||||
Promise.all([
|
||||
import('../src/components'),
|
||||
import('../src/directives'),
|
||||
import('../src/widgets'),
|
||||
import('../src/theme'),
|
||||
import('../src/preferences'),
|
||||
import('../src/os'),
|
||||
import('../src/components/index.js'),
|
||||
import('../src/directives/index.js'),
|
||||
import('../src/widgets/index.js'),
|
||||
import('../src/theme.js'),
|
||||
import('../src/preferences.js'),
|
||||
import('../src/os.js'),
|
||||
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os]) => {
|
||||
setup((app) => {
|
||||
moduleInitialized = true;
|
||||
|
@@ -50,9 +50,46 @@ export default [
|
||||
// defineExposeが誤検知されてしまう
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
|
||||
// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
|
||||
// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため
|
||||
'id-denylist': ['error', 'window', 'e'],
|
||||
// window ... グローバルスコープと衝突し、予期せぬ結果を招くため
|
||||
// e ... error や event など、複数のキーワードの頭文字であり分かりにくいため
|
||||
// close ... window.closeと衝突 or 紛らわしい
|
||||
// open ... window.openと衝突 or 紛らわしい
|
||||
// fetch ... window.fetchと衝突 or 紛らわしい
|
||||
// location ... window.locationと衝突 or 紛らわしい
|
||||
// document ... window.documentと衝突 or 紛らわしい
|
||||
// history ... window.historyと衝突 or 紛らわしい
|
||||
'id-denylist': ['warn', 'window', 'e', 'close', 'open', 'fetch', 'location', 'document', 'history'],
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{
|
||||
'name': 'open',
|
||||
'message': 'Use `window.open`.',
|
||||
},
|
||||
{
|
||||
'name': 'close',
|
||||
'message': 'Use `window.close`.',
|
||||
},
|
||||
{
|
||||
'name': 'fetch',
|
||||
'message': 'Use `window.fetch`.',
|
||||
},
|
||||
{
|
||||
'name': 'location',
|
||||
'message': 'Use `window.location`.',
|
||||
},
|
||||
{
|
||||
'name': 'document',
|
||||
'message': 'Use `window.document`.',
|
||||
},
|
||||
{
|
||||
'name': 'history',
|
||||
'message': 'Use `window.history`.',
|
||||
},
|
||||
{
|
||||
'name': 'name',
|
||||
'message': 'Use `window.name`. もしくは name という変数名を定義し忘れている',
|
||||
},
|
||||
],
|
||||
'no-shadow': ['warn'],
|
||||
'vue/attributes-order': ['error', {
|
||||
alphabetical: false,
|
||||
|
@@ -28,7 +28,7 @@
|
||||
"@syuilo/aiscript": "0.19.0",
|
||||
"@tabler/icons-webfont": "3.31.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"@vitejs/plugin-vue": "5.2.1",
|
||||
"@vitejs/plugin-vue": "5.2.3",
|
||||
"@vue/compiler-sfc": "3.5.13",
|
||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
|
||||
"analytics": "0.8.16",
|
||||
@@ -38,7 +38,7 @@
|
||||
"canvas-confetti": "1.9.3",
|
||||
"chart.js": "4.4.8",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
"chartjs-chart-matrix": "2.0.1",
|
||||
"chartjs-chart-matrix": "2.1.1",
|
||||
"chartjs-plugin-gradient": "0.6.1",
|
||||
"chartjs-plugin-zoom": "2.2.0",
|
||||
"chromatic": "11.27.0",
|
||||
@@ -60,10 +60,10 @@
|
||||
"misskey-reversi": "workspace:*",
|
||||
"photoswipe": "5.4.4",
|
||||
"punycode.js": "2.3.1",
|
||||
"rollup": "4.34.9",
|
||||
"sanitize-html": "2.14.0",
|
||||
"sass": "1.85.1",
|
||||
"shiki": "3.1.0",
|
||||
"rollup": "4.36.0",
|
||||
"sanitize-html": "2.15.0",
|
||||
"sass": "1.86.0",
|
||||
"shiki": "3.2.1",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.174.0",
|
||||
@@ -74,54 +74,54 @@
|
||||
"typescript": "5.8.2",
|
||||
"uuid": "11.1.0",
|
||||
"v-code-diff": "1.13.1",
|
||||
"vite": "6.2.1",
|
||||
"vite": "6.2.2",
|
||||
"vue": "3.5.13",
|
||||
"vuedraggable": "next",
|
||||
"wanakana": "5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/summaly": "5.2.0",
|
||||
"@storybook/addon-actions": "8.6.4",
|
||||
"@storybook/addon-essentials": "8.6.4",
|
||||
"@storybook/addon-interactions": "8.6.4",
|
||||
"@storybook/addon-links": "8.6.4",
|
||||
"@storybook/addon-mdx-gfm": "8.6.4",
|
||||
"@storybook/addon-storysource": "8.6.4",
|
||||
"@storybook/blocks": "8.6.4",
|
||||
"@storybook/components": "8.6.4",
|
||||
"@storybook/core-events": "8.6.4",
|
||||
"@storybook/manager-api": "8.6.4",
|
||||
"@storybook/preview-api": "8.6.4",
|
||||
"@storybook/react": "8.6.4",
|
||||
"@storybook/react-vite": "8.6.4",
|
||||
"@storybook/test": "8.6.4",
|
||||
"@storybook/theming": "8.6.4",
|
||||
"@storybook/types": "8.6.4",
|
||||
"@storybook/vue3": "8.6.4",
|
||||
"@storybook/vue3-vite": "8.6.4",
|
||||
"@storybook/addon-actions": "8.6.7",
|
||||
"@storybook/addon-essentials": "8.6.7",
|
||||
"@storybook/addon-interactions": "8.6.7",
|
||||
"@storybook/addon-links": "8.6.7",
|
||||
"@storybook/addon-mdx-gfm": "8.6.7",
|
||||
"@storybook/addon-storysource": "8.6.7",
|
||||
"@storybook/blocks": "8.6.7",
|
||||
"@storybook/components": "8.6.7",
|
||||
"@storybook/core-events": "8.6.7",
|
||||
"@storybook/manager-api": "8.6.7",
|
||||
"@storybook/preview-api": "8.6.7",
|
||||
"@storybook/react": "8.6.7",
|
||||
"@storybook/react-vite": "8.6.7",
|
||||
"@storybook/test": "8.6.7",
|
||||
"@storybook/theming": "8.6.7",
|
||||
"@storybook/types": "8.6.7",
|
||||
"@storybook/vue3": "8.6.7",
|
||||
"@storybook/vue3-vite": "8.6.7",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/canvas-confetti": "1.9.0",
|
||||
"@types/estree": "1.0.6",
|
||||
"@types/matter-js": "0.19.8",
|
||||
"@types/micromatch": "4.0.9",
|
||||
"@types/node": "22.13.9",
|
||||
"@types/node": "22.13.11",
|
||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||
"@types/sanitize-html": "2.13.0",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@types/throttle-debounce": "5.0.2",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/ws": "8.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||
"@typescript-eslint/parser": "8.26.0",
|
||||
"@vitest/coverage-v8": "3.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@vitest/coverage-v8": "3.0.9",
|
||||
"@vue/runtime-core": "3.5.13",
|
||||
"acorn": "8.14.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "14.1.0",
|
||||
"cypress": "14.2.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-vue": "10.0.0",
|
||||
"fast-glob": "3.3.3",
|
||||
"happy-dom": "17.3.0",
|
||||
"happy-dom": "17.4.4",
|
||||
"intersection-observer": "0.12.2",
|
||||
"micromatch": "4.0.8",
|
||||
"msw": "2.7.3",
|
||||
@@ -131,12 +131,12 @@
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"seedrandom": "3.0.5",
|
||||
"start-server-and-test": "2.0.10",
|
||||
"storybook": "8.6.4",
|
||||
"start-server-and-test": "2.0.11",
|
||||
"storybook": "8.6.7",
|
||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||
"vite-node": "3.0.8",
|
||||
"vite-node": "3.0.9",
|
||||
"vite-plugin-turbosnap": "1.0.3",
|
||||
"vitest": "3.0.8",
|
||||
"vitest": "3.0.9",
|
||||
"vitest-fetch-mock": "0.4.5",
|
||||
"vue-component-type-helpers": "2.2.8",
|
||||
"vue-eslint-parser": "10.1.1",
|
||||
|
@@ -12,9 +12,9 @@ import '@/style.scss';
|
||||
import { mainBoot } from '@/boot/main-boot.js';
|
||||
import { subBoot } from '@/boot/sub-boot.js';
|
||||
|
||||
const subBootPaths = ['/share', '/auth', '/miauth', '/oauth', '/signup-complete'];
|
||||
const subBootPaths = ['/share', '/auth', '/miauth', '/oauth', '/signup-complete', '/install-extensions'];
|
||||
|
||||
if (subBootPaths.some(i => location.pathname === i || location.pathname.startsWith(i + '/'))) {
|
||||
if (subBootPaths.some(i => window.location.pathname === i || window.location.pathname.startsWith(i + '/'))) {
|
||||
subBoot();
|
||||
} else {
|
||||
mainBoot();
|
||||
|
@@ -191,7 +191,7 @@ export async function login(token: AccountWithToken['token'], redirect?: string)
|
||||
// 他のタブは再読み込みするだけ
|
||||
reloadChannel.postMessage(null);
|
||||
// このページはredirectで指定された先に移動
|
||||
location.href = redirect;
|
||||
window.location.href = redirect;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -26,8 +26,6 @@ import { deckStore } from '@/ui/deck/deck-store.js';
|
||||
import { analytics, initAnalytics } from '@/analytics.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||
import { setupRouter } from '@/router/main.js';
|
||||
import { createMainRouter } from '@/router/definition.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { $i } from '@/i.js';
|
||||
|
||||
@@ -97,28 +95,28 @@ export async function common(createVue: () => App<Element>) {
|
||||
//#endregion
|
||||
|
||||
// タッチデバイスでCSSの:hoverを機能させる
|
||||
document.addEventListener('touchend', () => {}, { passive: true });
|
||||
window.document.addEventListener('touchend', () => {}, { passive: true });
|
||||
|
||||
// URLに#pswpを含む場合は取り除く
|
||||
if (location.hash === '#pswp') {
|
||||
history.replaceState(null, '', location.href.replace('#pswp', ''));
|
||||
if (window.location.hash === '#pswp') {
|
||||
window.history.replaceState(null, '', window.location.href.replace('#pswp', ''));
|
||||
}
|
||||
|
||||
// 一斉リロード
|
||||
reloadChannel.addEventListener('message', path => {
|
||||
if (path !== null) location.href = path;
|
||||
else location.reload();
|
||||
if (path !== null) window.location.href = path;
|
||||
else window.location.reload();
|
||||
});
|
||||
|
||||
// If mobile, insert the viewport meta tag
|
||||
if (['smartphone', 'tablet'].includes(deviceKind)) {
|
||||
const viewport = document.getElementsByName('viewport').item(0);
|
||||
const viewport = window.document.getElementsByName('viewport').item(0);
|
||||
viewport.setAttribute('content',
|
||||
`${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`);
|
||||
}
|
||||
|
||||
//#region Set lang attr
|
||||
const html = document.documentElement;
|
||||
const html = window.document.documentElement;
|
||||
html.setAttribute('lang', lang);
|
||||
//#endregion
|
||||
|
||||
@@ -132,11 +130,11 @@ export async function common(createVue: () => App<Element>) {
|
||||
});
|
||||
|
||||
//#region loginId
|
||||
const params = new URLSearchParams(location.search);
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const loginId = params.get('loginId');
|
||||
|
||||
if (loginId) {
|
||||
const target = getUrlWithoutLoginId(location.href);
|
||||
const target = getUrlWithoutLoginId(window.location.href);
|
||||
|
||||
if (!$i || $i.id !== loginId) {
|
||||
const account = await getAccountFromId(loginId);
|
||||
@@ -145,7 +143,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
}
|
||||
}
|
||||
|
||||
history.replaceState({ misskey: 'loginId' }, '', target);
|
||||
window.history.replaceState({ misskey: 'loginId' }, '', target);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@@ -157,7 +155,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
);
|
||||
}, { immediate: miLocalStorage.getItem('theme') == null });
|
||||
|
||||
document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light';
|
||||
window.document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light';
|
||||
|
||||
const darkTheme = prefer.model('darkTheme');
|
||||
const lightTheme = prefer.model('lightTheme');
|
||||
@@ -203,20 +201,20 @@ export async function common(createVue: () => App<Element>) {
|
||||
}, { immediate: true });
|
||||
|
||||
watch(prefer.r.useBlurEffectForModal, v => {
|
||||
document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none');
|
||||
window.document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none');
|
||||
}, { immediate: true });
|
||||
|
||||
watch(prefer.r.useBlurEffect, v => {
|
||||
if (v) {
|
||||
document.documentElement.style.removeProperty('--MI-blur');
|
||||
window.document.documentElement.style.removeProperty('--MI-blur');
|
||||
} else {
|
||||
document.documentElement.style.setProperty('--MI-blur', 'none');
|
||||
window.document.documentElement.style.setProperty('--MI-blur', 'none');
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// Keep screen on
|
||||
const onVisibilityChange = () => document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
const onVisibilityChange = () => window.document.addEventListener('visibilitychange', () => {
|
||||
if (window.document.visibilityState === 'visible') {
|
||||
navigator.wakeLock.request('screen');
|
||||
}
|
||||
});
|
||||
@@ -226,7 +224,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
.catch(() => {
|
||||
// On WebKit-based browsers, user activation is required to send wake lock request
|
||||
// https://webkit.org/blog/13862/the-user-activation-api/
|
||||
document.addEventListener(
|
||||
window.document.addEventListener(
|
||||
'click',
|
||||
() => navigator.wakeLock.request('screen').then(onVisibilityChange),
|
||||
{ once: true },
|
||||
@@ -235,7 +233,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
}
|
||||
|
||||
if (prefer.s.makeEveryTextElementsSelectable) {
|
||||
document.documentElement.classList.add('forceSelectableAll');
|
||||
window.document.documentElement.classList.add('forceSelectableAll');
|
||||
}
|
||||
|
||||
//#region Fetch user
|
||||
@@ -267,8 +265,6 @@ export async function common(createVue: () => App<Element>) {
|
||||
|
||||
const app = createVue();
|
||||
|
||||
setupRouter(app, createMainRouter);
|
||||
|
||||
if (_DEV_) {
|
||||
app.config.performance = true;
|
||||
}
|
||||
@@ -282,16 +278,16 @@ export async function common(createVue: () => App<Element>) {
|
||||
const rootEl = ((): HTMLElement => {
|
||||
const MISSKEY_MOUNT_DIV_ID = 'misskey_app';
|
||||
|
||||
const currentRoot = document.getElementById(MISSKEY_MOUNT_DIV_ID);
|
||||
const currentRoot = window.document.getElementById(MISSKEY_MOUNT_DIV_ID);
|
||||
|
||||
if (currentRoot) {
|
||||
console.warn('multiple import detected');
|
||||
return currentRoot;
|
||||
}
|
||||
|
||||
const root = document.createElement('div');
|
||||
const root = window.document.createElement('div');
|
||||
root.id = MISSKEY_MOUNT_DIV_ID;
|
||||
document.body.appendChild(root);
|
||||
window.document.body.appendChild(root);
|
||||
return root;
|
||||
})();
|
||||
|
||||
@@ -304,24 +300,26 @@ export async function common(createVue: () => App<Element>) {
|
||||
removeSplash();
|
||||
|
||||
//#region Self-XSS 対策メッセージ
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.warning}`,
|
||||
'color: #f00; background-color: #ff0; font-size: 36px; padding: 4px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.title}`,
|
||||
'color: #f00; font-weight: 900; font-family: "Hiragino Sans W9", "Hiragino Kaku Gothic ProN", sans-serif; font-size: 24px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description1}`,
|
||||
'font-size: 16px; font-weight: 700;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description2}`,
|
||||
'font-size: 16px;',
|
||||
'font-size: 20px; font-weight: 700; color: #f00;',
|
||||
);
|
||||
console.log(i18n.tsx._selfXssPrevention.description3({ link: 'https://misskey-hub.net/docs/for-users/resources/self-xss/' }));
|
||||
if (!_DEV_) {
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.warning}`,
|
||||
'color: #f00; background-color: #ff0; font-size: 36px; padding: 4px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.title}`,
|
||||
'color: #f00; font-weight: 900; font-family: "Hiragino Sans W9", "Hiragino Kaku Gothic ProN", sans-serif; font-size: 24px;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description1}`,
|
||||
'font-size: 16px; font-weight: 700;',
|
||||
);
|
||||
console.log(
|
||||
`%c${i18n.ts._selfXssPrevention.description2}`,
|
||||
'font-size: 16px;',
|
||||
'font-size: 20px; font-weight: 700; color: #f00;',
|
||||
);
|
||||
console.log(i18n.tsx._selfXssPrevention.description3({ link: 'https://misskey-hub.net/docs/for-users/resources/self-xss/' }));
|
||||
}
|
||||
//#endregion
|
||||
|
||||
return {
|
||||
@@ -332,7 +330,7 @@ export async function common(createVue: () => App<Element>) {
|
||||
}
|
||||
|
||||
function removeSplash() {
|
||||
const splash = document.getElementById('splash');
|
||||
const splash = window.document.getElementById('splash');
|
||||
if (splash) {
|
||||
splash.style.opacity = '0';
|
||||
splash.style.pointerEvents = 'none';
|
||||
|
@@ -24,7 +24,7 @@ import { miLocalStorage } from '@/local-storage.js';
|
||||
import { claimAchievement, claimedAchievements } from '@/utility/achievements.js';
|
||||
import { initializeSw } from '@/utility/initialize-sw.js';
|
||||
import { emojiPicker } from '@/utility/emoji-picker.js';
|
||||
import { mainRouter } from '@/router/main.js';
|
||||
import { mainRouter } from '@/router.js';
|
||||
import { makeHotkey } from '@/utility/hotkey.js';
|
||||
import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
@@ -43,7 +43,7 @@ export async function mainBoot() {
|
||||
if (!$i) uiStyle = 'visitor';
|
||||
|
||||
if (searchParams.has('zen')) uiStyle = 'zen';
|
||||
if (uiStyle === 'deck' && prefer.s['deck.useSimpleUiForNonRootPages'] && location.pathname !== '/') uiStyle = 'zen';
|
||||
if (uiStyle === 'deck' && prefer.s['deck.useSimpleUiForNonRootPages'] && window.location.pathname !== '/') uiStyle = 'zen';
|
||||
|
||||
if (searchParams.has('ui')) uiStyle = searchParams.get('ui');
|
||||
|
||||
@@ -201,6 +201,8 @@ export async function mainBoot() {
|
||||
prefer.commit('sound.on.noteMy', store.s.sound_noteMy as any);
|
||||
prefer.commit('sound.on.notification', store.s.sound_notification as any);
|
||||
prefer.commit('sound.on.reaction', store.s.sound_reaction as any);
|
||||
prefer.commit('defaultNoteVisibility', store.s.defaultNoteVisibility);
|
||||
prefer.commit('defaultNoteLocalOnly', store.s.defaultNoteLocalOnly);
|
||||
|
||||
window.setTimeout(() => {
|
||||
unisonReload();
|
||||
@@ -214,7 +216,7 @@ export async function mainBoot() {
|
||||
let reloadDialogShowing = false;
|
||||
stream.on('_disconnected_', async () => {
|
||||
if (prefer.s.serverDisconnectedBehavior === 'reload') {
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
} else if (prefer.s.serverDisconnectedBehavior === 'dialog') {
|
||||
if (reloadDialogShowing) return;
|
||||
reloadDialogShowing = true;
|
||||
@@ -225,7 +227,7 @@ export async function mainBoot() {
|
||||
});
|
||||
reloadDialogShowing = false;
|
||||
if (!canceled) {
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -396,7 +398,7 @@ export async function mainBoot() {
|
||||
let lastVisibilityChangedAt = Date.now();
|
||||
|
||||
function claimPlainLucky() {
|
||||
if (document.visibilityState !== 'visible') {
|
||||
if (window.document.visibilityState !== 'visible') {
|
||||
if (justPlainLuckyTimer != null) window.clearTimeout(justPlainLuckyTimer);
|
||||
return;
|
||||
}
|
||||
@@ -411,7 +413,7 @@ export async function mainBoot() {
|
||||
window.addEventListener('visibilitychange', () => {
|
||||
const now = Date.now();
|
||||
|
||||
if (document.visibilityState === 'visible') {
|
||||
if (window.document.visibilityState === 'visible') {
|
||||
// タブを高速で切り替えたら取得処理が何度も走るのを防ぐ
|
||||
if ((now - lastVisibilityChangedAt) < 1000 * 10) {
|
||||
justPlainLuckyTimer = window.setTimeout(claimPlainLucky, 1000 * 10);
|
||||
@@ -456,7 +458,7 @@ export async function mainBoot() {
|
||||
|
||||
const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt');
|
||||
const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo');
|
||||
if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) {
|
||||
if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !window.location.pathname.startsWith('/miauth')) {
|
||||
if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {
|
||||
closed: () => dispose(),
|
||||
@@ -552,7 +554,7 @@ export async function mainBoot() {
|
||||
mainRouter.push('/search');
|
||||
},
|
||||
} as const satisfies Keymap;
|
||||
document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
|
||||
window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
|
||||
|
||||
initializeSw();
|
||||
}
|
||||
|
@@ -88,9 +88,9 @@ import { i18n } from '@/i18n.js';
|
||||
import { dateString } from '@/filters/date.js';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import RouterView from '@/components/global/RouterView.vue';
|
||||
import { useRouterFactory } from '@/router/supplier';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
|
||||
import { createRouter } from '@/router.js';
|
||||
|
||||
const props = defineProps<{
|
||||
report: Misskey.entities.AdminAbuseUserReportsResponse[number];
|
||||
@@ -100,10 +100,9 @@ const emit = defineEmits<{
|
||||
(ev: 'resolved', reportId: string): void;
|
||||
}>();
|
||||
|
||||
const routerFactory = useRouterFactory();
|
||||
const targetRouter = routerFactory(`/admin/user/${props.report.targetUserId}`);
|
||||
const targetRouter = createRouter(`/admin/user/${props.report.targetUserId}`);
|
||||
targetRouter.init();
|
||||
const reporterRouter = routerFactory(`/admin/user/${props.report.reporterId}`);
|
||||
const reporterRouter = createRouter(`/admin/user/${props.report.reporterId}`);
|
||||
reporterRouter.init();
|
||||
|
||||
const moderationNote = ref(props.report.moderationNote ?? '');
|
||||
@@ -135,7 +134,7 @@ function forward() {
|
||||
|
||||
function showMenu(ev: MouseEvent) {
|
||||
os.popupMenu([{
|
||||
icon: 'ti ti-id',
|
||||
icon: 'ti ti-hash',
|
||||
text: 'Copy ID',
|
||||
action: () => {
|
||||
copyToClipboard(props.report.id);
|
||||
|
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, shallowRef } from 'vue';
|
||||
import { ref, useTemplateRef } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkWindow from '@/components/MkWindow.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
@@ -47,7 +47,7 @@ const emit = defineEmits<{
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const uiWindow = shallowRef<InstanceType<typeof MkWindow>>();
|
||||
const uiWindow = useTemplateRef('uiWindow');
|
||||
const comment = ref(props.initialComment ?? '');
|
||||
|
||||
function send() {
|
||||
|
@@ -192,7 +192,7 @@ function tick() {
|
||||
tick();
|
||||
|
||||
function calcColors() {
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const computedStyle = getComputedStyle(window.document.documentElement);
|
||||
const dark = tinycolor(computedStyle.getPropertyValue('--MI_THEME-bg')).isDark();
|
||||
const accent = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
|
||||
majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
||||
|
@@ -4,14 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<canvas ref="canvasEl" style="width: 100%; height: 100%; pointer-events: none;"></canvas>
|
||||
<canvas ref="canvasEl" style="display: block; width: 100%; height: 100%; pointer-events: none;"></canvas>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, shallowRef } from 'vue';
|
||||
import { onMounted, onUnmounted, useTemplateRef } from 'vue';
|
||||
import isChromatic from 'chromatic/isChromatic';
|
||||
|
||||
const canvasEl = shallowRef<HTMLCanvasElement>();
|
||||
const canvasEl = useTemplateRef('canvasEl');
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
scale?: number;
|
||||
|
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, shallowRef } from 'vue';
|
||||
import { onMounted, useTemplateRef } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
@@ -37,8 +37,8 @@ const props = withDefaults(defineProps<{
|
||||
}>(), {
|
||||
});
|
||||
|
||||
const rootEl = shallowRef<HTMLDivElement>();
|
||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
const rootEl = useTemplateRef('rootEl');
|
||||
const modal = useTemplateRef('modal');
|
||||
|
||||
async function ok() {
|
||||
if (props.announcement.needConfirmationToRead) {
|
||||
|
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef } from 'vue';
|
||||
import { useTemplateRef } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import XAntennaEditor from '@/components/MkAntennaEditor.vue';
|
||||
@@ -40,7 +40,7 @@ const emit = defineEmits<{
|
||||
(ev: 'closed'): void,
|
||||
}>();
|
||||
|
||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
const dialog = useTemplateRef('dialog');
|
||||
|
||||
function onAntennaCreated(newAntenna: Misskey.entities.Antenna) {
|
||||
emit('created', newAntenna);
|
||||
|
@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { markRaw, ref, shallowRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
|
||||
import { markRaw, ref, useTemplateRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import { emojilist, getEmojiName } from '@@/js/emojilist.js';
|
||||
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@@/js/emoji-base.js';
|
||||
@@ -139,7 +139,7 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const suggests = ref<Element>();
|
||||
const rootEl = shallowRef<HTMLDivElement>();
|
||||
const rootEl = useTemplateRef('rootEl');
|
||||
|
||||
const fetching = ref(true);
|
||||
const users = ref<any[]>([]);
|
||||
@@ -359,7 +359,7 @@ onMounted(() => {
|
||||
|
||||
props.textarea.addEventListener('keydown', onKeydown);
|
||||
|
||||
document.body.addEventListener('mousedown', onMousedown);
|
||||
window.document.body.addEventListener('mousedown', onMousedown);
|
||||
|
||||
nextTick(() => {
|
||||
exec();
|
||||
@@ -375,7 +375,7 @@ onMounted(() => {
|
||||
onBeforeUnmount(() => {
|
||||
props.textarea.removeEventListener('keydown', onKeydown);
|
||||
|
||||
document.body.removeEventListener('mousedown', onMousedown);
|
||||
window.document.body.removeEventListener('mousedown', onMousedown);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, shallowRef } from 'vue';
|
||||
import { nextTick, onMounted, useTemplateRef } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
type?: 'button' | 'submit' | 'reset';
|
||||
@@ -64,8 +64,8 @@ const emit = defineEmits<{
|
||||
(ev: 'click', payload: MouseEvent): void;
|
||||
}>();
|
||||
|
||||
const el = shallowRef<HTMLElement | null>(null);
|
||||
const ripples = shallowRef<HTMLElement | null>(null);
|
||||
const el = useTemplateRef('el');
|
||||
const ripples = useTemplateRef('ripples');
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
@@ -92,7 +92,7 @@ function onMousedown(evt: MouseEvent): void {
|
||||
const target = evt.target! as HTMLElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
|
||||
const ripple = document.createElement('div');
|
||||
const ripple = window.document.createElement('div');
|
||||
ripple.classList.add(ripples.value!.dataset.childrenClass!);
|
||||
ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
|
||||
ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';
|
||||
|
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue';
|
||||
import { ref, useTemplateRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue';
|
||||
import { store } from '@/store.js';
|
||||
|
||||
// APIs provided by Captcha services
|
||||
@@ -69,7 +69,7 @@ const emit = defineEmits<{
|
||||
|
||||
const available = ref(false);
|
||||
|
||||
const captchaEl = shallowRef<HTMLDivElement | undefined>();
|
||||
const captchaEl = useTemplateRef('captchaEl');
|
||||
const captchaWidgetId = ref<string | undefined>(undefined);
|
||||
const testcaptchaInput = ref('');
|
||||
const testcaptchaPassed = ref(false);
|
||||
@@ -112,7 +112,7 @@ watch(() => [props.instanceUrl, props.sitekey, props.secretKey], async () => {
|
||||
if (loaded || props.provider === 'mcaptcha' || props.provider === 'testcaptcha') {
|
||||
available.value = true;
|
||||
} else if (src.value !== null) {
|
||||
(document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), {
|
||||
(window.document.getElementById(scriptId.value) ?? window.document.head.appendChild(Object.assign(window.document.createElement('script'), {
|
||||
async: true,
|
||||
id: scriptId.value,
|
||||
src: src.value,
|
||||
@@ -149,7 +149,7 @@ async function requestRender() {
|
||||
if (captcha.value.render && captchaEl.value instanceof Element && props.sitekey) {
|
||||
// reCAPTCHAのレンダリング重複判定を回避するため、captchaEl配下に仮のdivを用意する.
|
||||
// (同じdivに対して複数回renderを呼び出すとreCAPTCHAはエラーを返すので)
|
||||
const elem = document.createElement('div');
|
||||
const elem = window.document.createElement('div');
|
||||
captchaEl.value.appendChild(elem);
|
||||
|
||||
captchaWidgetId.value = captcha.value.render(elem, {
|
||||
@@ -174,7 +174,7 @@ async function requestRender() {
|
||||
|
||||
function clearWidget() {
|
||||
if (props.provider === 'mcaptcha') {
|
||||
const container = document.getElementById('mcaptcha__widget-container');
|
||||
const container = window.document.getElementById('mcaptcha__widget-container');
|
||||
if (container) {
|
||||
container.innerHTML = '';
|
||||
}
|
||||
|
@@ -45,12 +45,8 @@ export type ChartSrc =
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
/* eslint-disable id-denylist --
|
||||
Chart.js has a `data` attribute in most chart definitions, which triggers the
|
||||
id-denylist violation when setting it. This is causing about 60+ lint issues.
|
||||
As this is part of Chart.js's API it makes sense to disable the check here.
|
||||
*/
|
||||
import { onMounted, ref, shallowRef, watch } from 'vue';
|
||||
|
||||
import { onMounted, ref, useTemplateRef, watch } from 'vue';
|
||||
import { Chart } from 'chart.js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { misskeyApiGet } from '@/utility/misskey-api.js';
|
||||
@@ -96,7 +92,7 @@ const props = withDefaults(defineProps<{
|
||||
nowForChromatic: undefined,
|
||||
});
|
||||
|
||||
const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
|
||||
const legendEl = useTemplateRef('legendEl');
|
||||
|
||||
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
||||
const negate = arr => arr.map(x => -x);
|
||||
@@ -134,7 +130,7 @@ let chartData: {
|
||||
bytes?: boolean;
|
||||
} | null = null;
|
||||
|
||||
const chartEl = shallowRef<HTMLCanvasElement | null>(null);
|
||||
const chartEl = useTemplateRef('chartEl');
|
||||
const fetching = ref(true);
|
||||
|
||||
const getDate = (ago: number) => {
|
||||
@@ -849,7 +845,7 @@ watch(() => [props.src, props.span], fetchAndRender);
|
||||
onMounted(() => {
|
||||
fetchAndRender();
|
||||
});
|
||||
/* eslint-enable id-denylist */
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
@@ -48,7 +48,6 @@ const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'))
|
||||
|
||||
function copy() {
|
||||
copyToClipboard(props.code);
|
||||
os.success();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, toRefs, shallowRef, nextTick } from 'vue';
|
||||
import { ref, watch, toRefs, useTemplateRef, nextTick } from 'vue';
|
||||
import { debounce } from 'throttle-debounce';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
@@ -61,7 +61,7 @@ const { modelValue } = toRefs(props);
|
||||
const v = ref<string>(modelValue.value ?? '');
|
||||
const focused = ref(false);
|
||||
const changed = ref(false);
|
||||
const inputEl = shallowRef<HTMLTextAreaElement>();
|
||||
const inputEl = useTemplateRef('inputEl');
|
||||
|
||||
const focus = () => inputEl.value?.focus();
|
||||
|
||||
|
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef, toRefs } from 'vue';
|
||||
import { ref, useTemplateRef, toRefs } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string | null;
|
||||
@@ -39,7 +39,7 @@ const emit = defineEmits<{
|
||||
|
||||
const { modelValue } = toRefs(props);
|
||||
const v = ref(modelValue.value);
|
||||
const inputEl = shallowRef<HTMLElement>();
|
||||
const inputEl = useTemplateRef('inputEl');
|
||||
|
||||
const onInput = () => {
|
||||
emit('update:modelValue', v.value ?? '');
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user