Merge remote-tracking branch 'origin/report' into report

This commit is contained in:
mattyatea 2024-01-04 23:05:31 +09:00
commit eb261adca3
No known key found for this signature in database
GPG Key ID: 068E54E2C33BEF9A
40 changed files with 704 additions and 428 deletions

View File

@ -17,16 +17,32 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: daily interval: daily
# PNPM has an issue with dependabot. See: open-pull-requests-limit: 5
# https://github.com/dependabot/dependabot-core/issues/7258 # List dependencies required to be updated together, sharing the same version numbers.
# https://github.com/pnpm/pnpm/issues/6530 # Those who simply have the common owner (e.g. @fastify) don't need to be listed.
# TODO: Restore this when the issue is solved
open-pull-requests-limit: 0
groups: groups:
swc: aws-sdk:
patterns: patterns:
- "@swc/*" - "@aws-sdk/*"
bull-board:
patterns:
- "@bull-board/*"
nestjs:
patterns:
- "@nestjs/*"
slacc:
patterns:
- "slacc-*"
storybook: storybook:
patterns: patterns:
- "storybook*" - "storybook*"
- "@storybook/*" - "@storybook/*"
swc-core:
patterns:
- "@swc/core*"
typescript-eslint:
patterns:
- "@typescript-eslint/*"
tensorflow:
patterns:
- "@tensorflow/*"

View File

@ -12,8 +12,25 @@
--> -->
## 202x.x.x (Unreleased)
### Client
- Enhance: ハッシュタグ入力時に、本文の末尾の行に何も書かれていない場合は新たにスペースを追加しないように
## 2023.12.2
### General
- v2023.12.1でDockerを利用してサーバーを起動できない問題を修正
### Client
- Enhance: 検索画面においてEnterキー押下で検索できるように
## 2023.12.1 ## 2023.12.1
### Note
- アクセストークンの権限が再整理されたため、一部のAPIが古いAPIトークンでは動作しなくなりました。\
権限不足になる場合には権限を再設定して再生成してください。
### General ### General
- Enhance: ローカリゼーションの更新 - Enhance: ローカリゼーションの更新
- Fix: 自分のdirect noteがuser list timelineに追加されない - Fix: 自分のdirect noteがuser list timelineに追加されない

View File

@ -1,5 +1,5 @@
Unless otherwise stated this repository is Unless otherwise stated this repository is
Copyright © 2014-2023 syuilo and contributers Copyright © 2014-2024 syuilo and contributors
And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE. And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE.

View File

@ -51,6 +51,7 @@ WORKDIR /misskey
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
COPY --link ["scripts", "./scripts"] COPY --link ["scripts", "./scripts"]
COPY --link ["packages/backend/package.json", "./packages/backend/"] COPY --link ["packages/backend/package.json", "./packages/backend/"]
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
pnpm i --frozen-lockfile --aggregate-output pnpm i --frozen-lockfile --aggregate-output
@ -77,7 +78,9 @@ WORKDIR /misskey
COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
COPY --chown=misskey:misskey --from=native-builder /misskey/built ./built COPY --chown=misskey:misskey --from=native-builder /misskey/built ./built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/built ./packages/misskey-js/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
COPY --chown=misskey:misskey . ./ COPY --chown=misskey:misskey . ./

View File

@ -2,6 +2,7 @@
_lang_: "বাংলা" _lang_: "বাংলা"
headlineMisskey: "নোট ব্যাবহার করে সংযুক্ত নেটওয়ার্ক" headlineMisskey: "নোট ব্যাবহার করে সংযুক্ত নেটওয়ার্ক"
introMisskey: "স্বাগতম! মিসকি একটি ওপেন সোর্স, ডিসেন্ট্রালাইজড মাইক্রোব্লগিং পরিষেবা। \n\"নোট\" তৈরির মাধ্যমে যা ঘটছে তা সবার সাথে শেয়ার করুন 📡\n\"রিঅ্যাকশন\" গুলির মাধ্যমে যেকোনো নোট সম্পর্কে আপনার অনুভূতি ব্যাক্ত করতে পারেন 👍\nএকটি নতুন দুনিয়া ঘুরে দেখুন 🚀\n" introMisskey: "স্বাগতম! মিসকি একটি ওপেন সোর্স, ডিসেন্ট্রালাইজড মাইক্রোব্লগিং পরিষেবা। \n\"নোট\" তৈরির মাধ্যমে যা ঘটছে তা সবার সাথে শেয়ার করুন 📡\n\"রিঅ্যাকশন\" গুলির মাধ্যমে যেকোনো নোট সম্পর্কে আপনার অনুভূতি ব্যাক্ত করতে পারেন 👍\nএকটি নতুন দুনিয়া ঘুরে দেখুন 🚀\n"
poweredByMisskeyDescription: "{name} হল ওপেন সোর্স প্ল্যাটফর্ম <b>Misskey</b>-এর সার্ভারগুলির একটি৷"
monthAndDay: "{day}/{month}" monthAndDay: "{day}/{month}"
search: "খুঁজুন" search: "খুঁজুন"
notifications: "বিজ্ঞপ্তি" notifications: "বিজ্ঞপ্তি"
@ -12,12 +13,14 @@ fetchingAsApObject: "ফেডিভার্স থেকে খবর আন
ok: "ঠিক" ok: "ঠিক"
gotIt: "বুঝেছি" gotIt: "বুঝেছি"
cancel: "বাতিল" cancel: "বাতিল"
noThankYou: "না, ধন্যবাদ"
enterUsername: "ইউজারনেম লিখুন" enterUsername: "ইউজারনেম লিখুন"
renotedBy: "{user} রিনোট করেছেন" renotedBy: "{user} রিনোট করেছেন"
noNotes: "কোন নোট নেই" noNotes: "কোন নোট নেই"
noNotifications: "কোনো বিজ্ঞপ্তি নেই" noNotifications: "কোনো বিজ্ঞপ্তি নেই"
instance: "ইন্সট্যান্স" instance: "ইন্সট্যান্স"
settings: "সেটিংস" settings: "সেটিংস"
notificationSettings: "বিজ্ঞপ্তির সেটিংস"
basicSettings: "সাধারণ সেটিংস" basicSettings: "সাধারণ সেটিংস"
otherSettings: "অন্যান্য সেটিংস" otherSettings: "অন্যান্য সেটিংস"
openInWindow: "নতুন উইন্ডোতে খুলা" openInWindow: "নতুন উইন্ডোতে খুলা"
@ -42,12 +45,20 @@ pin: "পিন করা"
unpin: "পিন সরান" unpin: "পিন সরান"
copyContent: "বিষয়বস্তু কপি করুন" copyContent: "বিষয়বস্তু কপি করুন"
copyLink: "লিঙ্ক কপি করুন" copyLink: "লিঙ্ক কপি করুন"
copyLinkRenote: "রিনোট লিঙ্ক কপি করুন"
delete: "মুছুন" delete: "মুছুন"
deleteAndEdit: "মুছুন এবং সম্পাদনা করুন" deleteAndEdit: "মুছুন এবং সম্পাদনা করুন"
deleteAndEditConfirm: "আপনি কি এই নোটটি মুছে এটি সম্পাদনা করার বিষয়ে নিশ্চিত? আপনি এটির সমস্ত রিঅ্যাকশন, রিনোট এবং জবাব হারাবেন।" deleteAndEditConfirm: "আপনি কি এই নোটটি মুছে এটি সম্পাদনা করার বিষয়ে নিশ্চিত? আপনি এটির সমস্ত রিঅ্যাকশন, রিনোট এবং জবাব হারাবেন।"
addToList: "লিস্ট এ যোগ করুন" addToList: "লিস্ট এ যোগ করুন"
addToAntenna: "অ্যান্টেনা এ যোগ করুন"
sendMessage: "একটি বার্তা পাঠান" sendMessage: "একটি বার্তা পাঠান"
copyRSS: "RSS কপি করুন"
copyUsername: "ব্যবহারকারীর নাম কপি করুন" copyUsername: "ব্যবহারকারীর নাম কপি করুন"
copyUserId: "ব্যবহারকারীর ID কপি করুন"
copyNoteId: "নোটের ID কপি করুন"
copyFileId: "ফাইল ID কপি করুন"
copyFolderId: "ফোল্ডার ID কপি করুন"
copyProfileUrl: "প্রোফাইল URL কপি করুন"
searchUser: "ব্যবহারকারী খুঁজুন..." searchUser: "ব্যবহারকারী খুঁজুন..."
reply: "জবাব" reply: "জবাব"
loadMore: "আরও দেখুন" loadMore: "আরও দেখুন"
@ -100,6 +111,8 @@ renoted: "রিনোট করা হয়েছে"
cantRenote: "এই নোটটি রিনোট করা যাবে না।" cantRenote: "এই নোটটি রিনোট করা যাবে না।"
cantReRenote: "রিনোটকে রিনোট করা যাবে না।" cantReRenote: "রিনোটকে রিনোট করা যাবে না।"
quote: "উদ্ধৃতি" quote: "উদ্ধৃতি"
inChannelRenote: "চ্যানেলে রিনোট"
inChannelQuote: "চ্যানেলে উদ্ধৃতি"
pinnedNote: "পিন করা নোট" pinnedNote: "পিন করা নোট"
pinned: "পিন করা" pinned: "পিন করা"
you: "আপনি" you: "আপনি"
@ -108,6 +121,10 @@ sensitive: "সংবেদনশীল বিষয়বস্তু"
add: "যুক্ত করুন" add: "যুক্ত করুন"
reaction: "প্রতিক্রিয়া" reaction: "প্রতিক্রিয়া"
reactions: "প্রতিক্রিয়া" reactions: "প্রতিক্রিয়া"
emojiPicker: "ইমোজি পিকার"
pinnedEmojisForReactionSettingDescription: "রিঅ্যাকশন দেয়ার সময় আপনি ইমোজিটিকে পিন করা এবং প্রদর্শিত হওয়ার জন্য সেট করতে পারেন।"
pinnedEmojisSettingDescription: "ইমোজি ইনপুট দেয়ার সময় আপনি ইমোজিটিকে পিন করা এবং প্রদর্শিত হওয়ার জন্য সেট করতে পারেন।"
emojiPickerDisplay: "পিকার ডিসপ্লে"
reactionSettingDescription2: "পুনরায় সাজাতে টেনে আনুন, মুছতে ক্লিক করুন, যোগ করতে + টিপুন।" reactionSettingDescription2: "পুনরায় সাজাতে টেনে আনুন, মুছতে ক্লিক করুন, যোগ করতে + টিপুন।"
rememberNoteVisibility: "নোটের দৃশ্যমান্যতার সেটিংস মনে রাখুন" rememberNoteVisibility: "নোটের দৃশ্যমান্যতার সেটিংস মনে রাখুন"
attachCancel: "অ্যাটাচমেন্ট সরান " attachCancel: "অ্যাটাচমেন্ট সরান "
@ -1034,6 +1051,7 @@ _2fa:
step3: "অ্যাপে প্রদর্শিত টোকেনটি লিখুন এবং আপনার কাজ শেষ।" step3: "অ্যাপে প্রদর্শিত টোকেনটি লিখুন এবং আপনার কাজ শেষ।"
step4: "আপনাকে এখন থেকে লগ ইন করার সময়, এইভাবে টোকেন লিখতে হবে।" step4: "আপনাকে এখন থেকে লগ ইন করার সময়, এইভাবে টোকেন লিখতে হবে।"
securityKeyInfo: "আপনি একটি হার্ডওয়্যার সিকিউরিটি কী ব্যবহার করে লগ ইন করতে পারেন যা FIDO2 বা ডিভাইসের ফিঙ্গারপ্রিন্ট সেন্সর বা পিন সমর্থন করে৷" securityKeyInfo: "আপনি একটি হার্ডওয়্যার সিকিউরিটি কী ব্যবহার করে লগ ইন করতে পারেন যা FIDO2 বা ডিভাইসের ফিঙ্গারপ্রিন্ট সেন্সর বা পিন সমর্থন করে৷"
renewTOTPCancel: "না, ধন্যবাদ"
_permissions: _permissions:
"read:account": "অ্যাকাউন্টের তথ্য দেখুন" "read:account": "অ্যাকাউন্টের তথ্য দেখুন"
"write:account": "অ্যাকাউন্টের তথ্য সম্পাদন করুন" "write:account": "অ্যাকাউন্টের তথ্য সম্পাদন করুন"

View File

@ -121,6 +121,10 @@ sensitive: "Konten sensitif"
add: "Tambahkan" add: "Tambahkan"
reaction: "Reaksi" reaction: "Reaksi"
reactions: "Reaksi" reactions: "Reaksi"
emojiPicker: "Emoji Picker"
pinnedEmojisForReactionSettingDescription: "Atur sematan emoji pada reaksi"
pinnedEmojisSettingDescription: "Atur sematan emoji pada masukan emoji"
emojiPickerDisplay: "Tampilan Emoji Picker"
reactionSettingDescription2: "Geser untuk memindah urutan emoji, klik untuk menghapus, tekan \"+\" untuk menambahkan" reactionSettingDescription2: "Geser untuk memindah urutan emoji, klik untuk menghapus, tekan \"+\" untuk menambahkan"
rememberNoteVisibility: "Ingat pengaturan visibilitas catatan" rememberNoteVisibility: "Ingat pengaturan visibilitas catatan"
attachCancel: "Hapus lampiran" attachCancel: "Hapus lampiran"
@ -641,6 +645,7 @@ smtpSecure: "Gunakan SSL/TLS implisit untuk koneksi SMTP"
smtpSecureInfo: "Matikan ini ketika menggunakan STARTTLS" smtpSecureInfo: "Matikan ini ketika menggunakan STARTTLS"
testEmail: "Tes pengiriman surel" testEmail: "Tes pengiriman surel"
wordMute: "Bisukan kata" wordMute: "Bisukan kata"
hardWordMute: "Pembisuan kata keras"
regexpError: "Kesalahan ekspresi reguler" regexpError: "Kesalahan ekspresi reguler"
regexpErrorDescription: "Galat terjadi pada baris {line} ekspresi reguler dari {tab} kata yang dibisukan:" regexpErrorDescription: "Galat terjadi pada baris {line} ekspresi reguler dari {tab} kata yang dibisukan:"
instanceMute: "Bisukan instansi" instanceMute: "Bisukan instansi"
@ -1154,6 +1159,7 @@ tosAndPrivacyPolicy: "Syarat dan Ketentuan serta Kebijakan Privasi"
avatarDecorations: "Dekorasi avatar" avatarDecorations: "Dekorasi avatar"
attach: "Lampirkan" attach: "Lampirkan"
detach: "Hapus" detach: "Hapus"
detachAll: "Lepas Semua"
angle: "Sudut" angle: "Sudut"
flip: "Balik" flip: "Balik"
showAvatarDecorations: "Tampilkan dekorasi avatar" showAvatarDecorations: "Tampilkan dekorasi avatar"
@ -1168,6 +1174,7 @@ doReaction: "Tambahkan reaksi"
code: "Kode" code: "Kode"
reloadRequiredToApplySettings: "Muat ulang diperlukan untuk menerapkan pengaturan." reloadRequiredToApplySettings: "Muat ulang diperlukan untuk menerapkan pengaturan."
remainingN: "Sisa : {n}" remainingN: "Sisa : {n}"
decorate: "Dekor"
_announcement: _announcement:
forExistingUsers: "Hanya pengguna yang telah ada" forExistingUsers: "Hanya pengguna yang telah ada"
forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya." forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya."
@ -1215,6 +1222,7 @@ _initialTutorial:
followers: "Perlihatkan ke pengikut saja. Hanya pengikut yang dapat melihat postinganmu dan tidak dapat direnote oleh siapapun." followers: "Perlihatkan ke pengikut saja. Hanya pengikut yang dapat melihat postinganmu dan tidak dapat direnote oleh siapapun."
direct: "Hanya perlihatkan ke pengguna spesifik dan penerima akan diberi tahu. Dapat juga digunakan sebagai alternatif dari pesan langsung." direct: "Hanya perlihatkan ke pengguna spesifik dan penerima akan diberi tahu. Dapat juga digunakan sebagai alternatif dari pesan langsung."
_cw: _cw:
title: "Peringatan Konten (CW)"
_exampleNote: _exampleNote:
cw: "Peringatan: Bikin Lapar!" cw: "Peringatan: Bikin Lapar!"
note: "Baru aja makan donat berlapis coklat 🍩😋" note: "Baru aja makan donat berlapis coklat 🍩😋"

View File

@ -260,6 +260,7 @@ removed: "뭉캣십니다"
removeAreYouSure: "{x}(얼)럴 뭉캡니꺼?" removeAreYouSure: "{x}(얼)럴 뭉캡니꺼?"
deleteAreYouSure: "{x}(얼)럴 뭉캡니꺼?" deleteAreYouSure: "{x}(얼)럴 뭉캡니꺼?"
resetAreYouSure: "아시로 데돌립니꺼?" resetAreYouSure: "아시로 데돌립니꺼?"
areYouSure: "갠찮십니꺼?"
saved: "저장햇십니다" saved: "저장햇십니다"
messaging: "대화" messaging: "대화"
upload: "올리기" upload: "올리기"
@ -458,6 +459,7 @@ noMessagesYet: "아직 대화가 없십니다"
newMessageExists: "새 메시지가 있십니다" newMessageExists: "새 메시지가 있십니다"
onlyOneFileCanBeAttached: "메시지엔 파일 하나까제밖에 몬 넣십니다" onlyOneFileCanBeAttached: "메시지엔 파일 하나까제밖에 몬 넣십니다"
invitations: "초대하기" invitations: "초대하기"
invitationCode: "초대장"
checking: "학인하고 잇십니다" checking: "학인하고 잇십니다"
passwordMatched: "맞십니다" passwordMatched: "맞십니다"
passwordNotMatched: "안 맞십니다" passwordNotMatched: "안 맞십니다"
@ -564,6 +566,11 @@ removeAllFollowing: "팔로잉 말캉 무루기"
removeAllFollowingDescription: "{host} 서버랑 걸어놓은 모든 팔로잉을 무룹니다. 고 서버가 아예 없어지삐맀든가, 그런 경우에 하이소." removeAllFollowingDescription: "{host} 서버랑 걸어놓은 모든 팔로잉을 무룹니다. 고 서버가 아예 없어지삐맀든가, 그런 경우에 하이소."
userSuspended: "요 게정은... 얼어 있십니다." userSuspended: "요 게정은... 얼어 있십니다."
userSilenced: "요 게정은... 수ᇚ혀 있십니다." userSilenced: "요 게정은... 수ᇚ혀 있십니다."
relays: "릴레이"
addRelay: "릴레이 옇기"
addedRelays: "옇은 릴레이"
enableInfiniteScroll: "알아서 더 보기"
author: "맨던 사람"
manage: "간리" manage: "간리"
emailServer: "전자우펜 서버" emailServer: "전자우펜 서버"
email: "전자우펜" email: "전자우펜"
@ -572,6 +579,8 @@ smtpHost: "호스트 이럼"
smtpPort: "포트" smtpPort: "포트"
smtpUser: "사용자 이럼" smtpUser: "사용자 이럼"
smtpPass: "비밀번호" smtpPass: "비밀번호"
display: "보기"
create: "맨걸기"
abuseReports: "신고하기" abuseReports: "신고하기"
reportAbuse: "신고하기" reportAbuse: "신고하기"
reportAbuseRenote: "리노트 신고하기" reportAbuseRenote: "리노트 신고하기"
@ -583,6 +592,7 @@ forwardReport: "웬겍 서버에 신고 보내기"
random: "무작이" random: "무작이"
system: "시스템" system: "시스템"
clip: "클립 맨걸기" clip: "클립 맨걸기"
createNew: "새로 맨걸기"
notesCount: "노트 수" notesCount: "노트 수"
renotesCount: "리노트한 수" renotesCount: "리노트한 수"
renotedCount: "리노트덴 수" renotedCount: "리노트덴 수"
@ -608,6 +618,7 @@ tools: "도구"
like: "좋네예!" like: "좋네예!"
unlike: "좋네예 무루기" unlike: "좋네예 무루기"
numberOfLikes: "좋네예 수" numberOfLikes: "좋네예 수"
show: "보기"
roles: "옉할" roles: "옉할"
role: "옉할" role: "옉할"
noRole: "옉할이 없십니다" noRole: "옉할이 없십니다"
@ -637,6 +648,8 @@ _gallery:
_email: _email:
_follow: _follow:
title: "새 팔로워가 잇십니다" title: "새 팔로워가 잇십니다"
_serverDisconnectedBehavior:
reload: "알아서 새로곤침"
_channel: _channel:
removeBanner: "배너 뭉캐기" removeBanner: "배너 뭉캐기"
_theme: _theme:

View File

@ -425,9 +425,9 @@ setupOf2fa: "2단계 인증 설정"
totp: "인증 앱" totp: "인증 앱"
totpDescription: "인증 앱을 사용하여 일회성 비밀번호 입력" totpDescription: "인증 앱을 사용하여 일회성 비밀번호 입력"
moderator: "모더레이터" moderator: "모더레이터"
moderation: "모더레이션" moderation: "조정"
moderationNote: "모더레이션 노트" moderationNote: "조정 기록"
addModerationNote: "모더레이션 노트 추가하기" addModerationNote: "조정 기록 추가하기"
moderationLogs: "모더레이션 로그" moderationLogs: "모더레이션 로그"
nUsersMentioned: "{n}명이 언급함" nUsersMentioned: "{n}명이 언급함"
securityKeyAndPasskey: "보안 키 또는 패스 키" securityKeyAndPasskey: "보안 키 또는 패스 키"
@ -513,7 +513,7 @@ dayOverDayChanges: "어제보다"
appearance: "모양" appearance: "모양"
clientSettings: "클라이언트 설정" clientSettings: "클라이언트 설정"
accountSettings: "계정 설정" accountSettings: "계정 설정"
promotion: "프로모션" promotion: "홍보"
promote: "프로모션하기" promote: "프로모션하기"
numberOfDays: "며칠동안" numberOfDays: "며칠동안"
hideThisNote: "이 노트를 숨기기" hideThisNote: "이 노트를 숨기기"
@ -863,8 +863,8 @@ devMode: "개발자 모드"
keepCw: "CW 유지하기" keepCw: "CW 유지하기"
pubSub: "Pub/Sub 계정" pubSub: "Pub/Sub 계정"
lastCommunication: "마지막 통신" lastCommunication: "마지막 통신"
resolved: "해결됨" resolved: "처리함"
unresolved: "해결되지 않음" unresolved: "처리되지 않음"
breakFollow: "팔로워 해제" breakFollow: "팔로워 해제"
breakFollowConfirm: "팔로우를 해제하시겠습니까?" breakFollowConfirm: "팔로우를 해제하시겠습니까?"
itsOn: "켜져 있습니다" itsOn: "켜져 있습니다"
@ -1181,6 +1181,8 @@ remainingN: "나머지: {n}"
overwriteContentConfirm: "현재 내용을 덮어쓰기 합니다. 계속 진행하시겠습니까?" overwriteContentConfirm: "현재 내용을 덮어쓰기 합니다. 계속 진행하시겠습니까?"
seasonalScreenEffect: "계절에 따른 효과 보이기" seasonalScreenEffect: "계절에 따른 효과 보이기"
decorate: "장식하기" decorate: "장식하기"
addMfmFunction: "장식 추가하기"
enableQuickAddMfmFunction: "상급자용 MFM 선택기 표시하기"
_announcement: _announcement:
forExistingUsers: "기존 유저에게만 알림" forExistingUsers: "기존 유저에게만 알림"
forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다." forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다."
@ -1557,7 +1559,7 @@ _role:
name: "역할 이름" name: "역할 이름"
description: "역할 설명" description: "역할 설명"
permission: "역할 권한" permission: "역할 권한"
descriptionOfPermission: "<b>모더레이터</b>는 기본적인 중재와 관련된 작업을 수행할 수 있습니다.\n<b>관리자</b>는 서버의 모든 설정을 변경할 수 있습니다." descriptionOfPermission: "<b>조정자</b>는 기본적인 조정 작업을 진행할 수 있습니다.\n<b>관리자</b>는 서버의 모든 설정을 변경할 수 있습니다."
assignTarget: "할당 대상" assignTarget: "할당 대상"
descriptionOfAssignTarget: "<b>수동</b>을 선택하면 누가 이 역할에 포함되는지를 수동으로 관리할 수 있습니다.\n<b>조건부</b>를 선택하면 조건을 설정해 일치하는 사용자를 자동으로 포함되게 할 수 있습니다." descriptionOfAssignTarget: "<b>수동</b>을 선택하면 누가 이 역할에 포함되는지를 수동으로 관리할 수 있습니다.\n<b>조건부</b>를 선택하면 조건을 설정해 일치하는 사용자를 자동으로 포함되게 할 수 있습니다."
manual: "수동" manual: "수동"
@ -1628,7 +1630,7 @@ _role:
or: "다음을 하나라도 만족" or: "다음을 하나라도 만족"
not: "다음을 만족하지 않음" not: "다음을 만족하지 않음"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다." description: "기계 학습으로 민감한 미디어를 알아서 찾아내어 조정에 참고하도록 합니다. 서버가 부하를 다소 받습니다."
sensitivity: "탐지 민감도" sensitivity: "탐지 민감도"
sensitivityDescription: "민감도가 낮을수록 안전한 미디어가 잘못 탐지될 확률이 줄어들며, 높을수록 민감한 미디어가 탐지되지 않을 확률이 줄어듭니다." sensitivityDescription: "민감도가 낮을수록 안전한 미디어가 잘못 탐지될 확률이 줄어들며, 높을수록 민감한 미디어가 탐지되지 않을 확률이 줄어듭니다."
setSensitiveFlagAutomatically: "자동으로 NSFW로 설정하기" setSensitiveFlagAutomatically: "자동으로 NSFW로 설정하기"
@ -1933,6 +1935,55 @@ _permissions:
"write:flash": "Play를 조작합니다" "write:flash": "Play를 조작합니다"
"read:flash-likes": "Play의 좋아요를 봅니다" "read:flash-likes": "Play의 좋아요를 봅니다"
"write:flash-likes": "Play의 좋아요를 조작합니다" "write:flash-likes": "Play의 좋아요를 조작합니다"
"read:admin:abuse-user-reports": "사용자 신고 보기"
"write:admin:delete-account": "사용자 계정 삭제하기"
"write:admin:delete-all-files-of-a-user": "모든 사용자 파일 삭제하기"
"read:admin:index-stats": "데이터베이스 색인 정보 보기"
"read:admin:table-stats": "데이터베이스 테이블 정보 보기"
"read:admin:user-ips": "사용자 IP 주소 보기"
"read:admin:meta": "인스턴스 메타데이터 보기"
"write:admin:reset-password": "사용자 비밀번호 재설정하기"
"write:admin:resolve-abuse-user-report": "사용자 신고 처리하기"
"write:admin:send-email": "이메일 보내기"
"read:admin:server-info": "서버 정보 보기"
"read:admin:show-moderation-log": "조정 기록 보기"
"read:admin:show-user": "사용자 개인정보 보기"
"read:admin:show-users": "사용자 개인정보 보기"
"write:admin:suspend-user": "사용자 정지하기"
"write:admin:unset-user-avatar": "사용자 아바타 삭제하기"
"write:admin:unset-user-banner": "사용자 배너 삭제하기"
"write:admin:unsuspend-user": "사용자 정지 해제하기"
"write:admin:meta": "인스턴스 메타데이터 수정하기"
"write:admin:user-note": "조정 기록 수정하기"
"write:admin:roles": "역할 수정하기"
"read:admin:roles": "역할 보기"
"write:admin:relays": "릴레이 수정하기"
"read:admin:relays": "릴레이 보기"
"write:admin:invite-codes": "초대 코드 수정하기"
"read:admin:invite-codes": "초대 코드 보기"
"write:admin:announcements": "공지사항 수정하기"
"read:admin:announcements": "공지사항 보기"
"write:admin:avatar-decorations": "아바타 꾸미기 수정하기"
"read:admin:avatar-decorations": "아바타 꾸미기 보기"
"write:admin:federation": "연합 정보 수정하기"
"write:admin:account": "사용자 계정 수정하기"
"read:admin:account": "사용자 정보 보기"
"write:admin:emoji": "이모지 수정하기"
"read:admin:emoji": "이모지 보기"
"write:admin:queue": "작업 대기열 수정하기"
"read:admin:queue": "작업 대기열 정보 보기"
"write:admin:promo": "홍보 기록 수정하기"
"write:admin:drive": "사용자 드라이브 수정하기"
"read:admin:drive": "사용자 드라이브 정보 보기"
"read:admin:stream": "관리자용 Websocket API 사용하기"
"write:admin:ad": "광고 수정하기"
"read:admin:ad": "광고 보기"
"write:invite-codes": "초대 코드 만들기"
"read:invite-codes": "초대 코드 불러오기"
"write:clip-favorite": "클립의 좋아요 수정하기"
"read:clip-favorite": "클립의 좋아요 보기"
"read:federation": "연합 정보 불러오기"
"write:report-abuse": "위반 내용 신고하기"
_auth: _auth:
shareAccessTitle: "어플리케이션의 접근 허가" shareAccessTitle: "어플리케이션의 접근 허가"
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?" shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
@ -2267,21 +2318,21 @@ _moderationLogTypes:
updateCustomEmoji: "커스텀 이모지 수정" updateCustomEmoji: "커스텀 이모지 수정"
deleteCustomEmoji: "커스텀 이모지 삭제" deleteCustomEmoji: "커스텀 이모지 삭제"
updateServerSettings: "서버 설정 갱신" updateServerSettings: "서버 설정 갱신"
updateUserNote: "모더레이션 노트 갱신" updateUserNote: "조정 기록 갱신"
deleteDriveFile: "파일 삭제" deleteDriveFile: "파일 삭제"
deleteNote: "노트 삭제" deleteNote: "노트 삭제"
createGlobalAnnouncement: "전역 공지사항 생성" createGlobalAnnouncement: "모든 공지사항 만들기"
createUserAnnouncement: "유저 공지사항 생성" createUserAnnouncement: "사용자 공지사항 만들기"
updateGlobalAnnouncement: "전역 공지사항 수정" updateGlobalAnnouncement: "모든 공지사항 수정"
updateUserAnnouncement: "유저 공지사항 수정" updateUserAnnouncement: "사용자 공지사항 수정"
deleteGlobalAnnouncement: "전역 공지사항 삭제" deleteGlobalAnnouncement: "모든 공지사항 삭제"
deleteUserAnnouncement: "유저 공지사항 삭제" deleteUserAnnouncement: "사용자 공지사항 삭제"
resetPassword: "비밀번호 재설정" resetPassword: "비밀번호 재설정"
suspendRemoteInstance: "리모트 서버를 정지" suspendRemoteInstance: "리모트 서버를 정지"
unsuspendRemoteInstance: "리모트 서버의 정지를 해제" unsuspendRemoteInstance: "리모트 서버의 정지를 해제"
markSensitiveDriveFile: "파일에 열람주의를 설정" markSensitiveDriveFile: "파일에 열람주의를 설정"
unmarkSensitiveDriveFile: "파일에 열람주의를 해제" unmarkSensitiveDriveFile: "파일에 열람주의를 해제"
resolveAbuseReport: "신고 해결" resolveAbuseReport: "신고 처리"
createInvitation: "초대 코드 생성" createInvitation: "초대 코드 생성"
createAd: "광고 생성" createAd: "광고 생성"
deleteAd: "광고 삭제" deleteAd: "광고 삭제"

View File

@ -1181,6 +1181,8 @@ remainingN: "剩餘:{n}"
overwriteContentConfirm: "確定要覆蓋目前的內容嗎?" overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
seasonalScreenEffect: "隨季節變換畫面的呈現" seasonalScreenEffect: "隨季節變換畫面的呈現"
decorate: "設置頭像裝飾" decorate: "設置頭像裝飾"
addMfmFunction: "插入MFM功能語法"
enableQuickAddMfmFunction: "顯示高級MFM選擇器"
_announcement: _announcement:
forExistingUsers: "僅限既有的使用者" forExistingUsers: "僅限既有的使用者"
forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。" forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"

View File

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2023.12.1", "version": "2023.12.2",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",
@ -18,7 +18,7 @@
"build-assets": "node ./scripts/build-assets.mjs", "build-assets": "node ./scripts/build-assets.mjs",
"build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
"build-storybook": "pnpm --filter frontend build-storybook", "build-storybook": "pnpm --filter frontend build-storybook",
"build-misskey-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build", "build-misskey-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
"init": "pnpm migrate", "init": "pnpm migrate",

View File

@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class SupportTrueMailApi1703658526000 {
name = 'SupportTrueMailApi1703658526000'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "truemailInstance" character varying(1024)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "truemailAuthKey" character varying(1024)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "enableTruemailApi" boolean NOT NULL DEFAULT false`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableTruemailApi"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "truemailInstance"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "truemailAuthKey"`);
}
}

View File

@ -74,6 +74,8 @@
"@fastify/multipart": "8.0.0", "@fastify/multipart": "8.0.0",
"@fastify/static": "6.12.0", "@fastify/static": "6.12.0",
"@fastify/view": "8.2.0", "@fastify/view": "8.2.0",
"@misskey-dev/sharp-read-bmp": "^1.1.1",
"@misskey-dev/summaly": "^5.0.3",
"@nestjs/common": "10.2.10", "@nestjs/common": "10.2.10",
"@nestjs/core": "10.2.10", "@nestjs/core": "10.2.10",
"@nestjs/testing": "10.2.10", "@nestjs/testing": "10.2.10",
@ -157,11 +159,9 @@
"sanitize-html": "2.11.0", "sanitize-html": "2.11.0",
"secure-json-parse": "2.7.0", "secure-json-parse": "2.7.0",
"sharp": "0.32.6", "sharp": "0.32.6",
"sharp-read-bmp": "github:misskey-dev/sharp-read-bmp",
"slacc": "0.0.10", "slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"summaly": "github:misskey-dev/summaly",
"systeminformation": "5.21.20", "systeminformation": "5.21.20",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tmp": "0.2.1", "tmp": "0.2.1",
@ -177,6 +177,7 @@
}, },
"devDependencies": { "devDependencies": {
"@jest/globals": "29.7.0", "@jest/globals": "29.7.0",
"@misskey-dev/eslint-plugin": "^1.0.0",
"@simplewebauthn/typescript-types": "8.3.4", "@simplewebauthn/typescript-types": "8.3.4",
"@swc/jest": "0.2.29", "@swc/jest": "0.2.29",
"@types/accepts": "1.3.7", "@types/accepts": "1.3.7",

View File

@ -7,7 +7,7 @@ import { randomUUID } from 'node:crypto';
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import sharp from 'sharp'; import sharp from 'sharp';
import { sharpBmp } from 'sharp-read-bmp'; import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
import { DeleteObjectCommandInput, PutObjectCommandInput, NoSuchKey } from '@aws-sdk/client-s3'; import { DeleteObjectCommandInput, PutObjectCommandInput, NoSuchKey } from '@aws-sdk/client-s3';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';

View File

@ -156,7 +156,7 @@ export class EmailService {
@bindThis @bindThis
public async validateEmailForAccount(emailAddress: string): Promise<{ public async validateEmailForAccount(emailAddress: string): Promise<{
available: boolean; available: boolean;
reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned'; reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned' | 'network' | 'blacklist';
}> { }> {
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
@ -173,6 +173,8 @@ export class EmailService {
if (meta.enableActiveEmailValidation) { if (meta.enableActiveEmailValidation) {
if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) { if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) {
validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey); validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
} else if (meta.enableTruemailApi && meta.truemailInstance && meta.truemailAuthKey != null) {
validated = await this.trueMail(meta.truemailInstance, emailAddress, meta.truemailAuthKey);
} else { } else {
validated = await validateEmail({ validated = await validateEmail({
email: emailAddress, email: emailAddress,
@ -201,6 +203,8 @@ export class EmailService {
validated.reason === 'disposable' ? 'disposable' : validated.reason === 'disposable' ? 'disposable' :
validated.reason === 'mx' ? 'mx' : validated.reason === 'mx' ? 'mx' :
validated.reason === 'smtp' ? 'smtp' : validated.reason === 'smtp' ? 'smtp' :
validated.reason === 'network' ? 'network' :
validated.reason === 'blacklist' ? 'blacklist' :
null, null,
}; };
} }
@ -265,4 +269,67 @@ export class EmailService {
reason: null, reason: null,
}; };
} }
private async trueMail<T>(truemailInstance: string, emailAddress: string, truemailAuthKey: string): Promise<{
valid: boolean;
reason: 'used' | 'format' | 'blacklist' | 'mx' | 'smtp' | 'network' | T | null;
}> {
const endpoint = truemailInstance + '?email=' + emailAddress;
try {
const res = await this.httpRequestService.send(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: truemailAuthKey
},
});
const json = (await res.json()) as {
email: string;
success: boolean;
errors?: {
list_match?: string;
regex?: string;
mx?: string;
smtp?: string;
} | null;
};
if (json.email === undefined || (json.email !== undefined && json.errors?.regex)) {
return {
valid: false,
reason: 'format',
};
}
if (json.errors?.smtp) {
return {
valid: false,
reason: 'smtp',
};
}
if (json.errors?.mx) {
return {
valid: false,
reason: 'mx',
};
}
if (!json.success) {
return {
valid: false,
reason: json.errors?.list_match as T || 'blacklist',
};
}
return {
valid: true,
reason: null,
};
} catch (error) {
return {
valid: false,
reason: 'network',
};
}
}
} }

View File

@ -71,8 +71,11 @@ export default class Logger {
let log = `${l} ${worker}\t[${contexts.join(' ')}]\t${m}`; let log = `${l} ${worker}\t[${contexts.join(' ')}]\t${m}`;
if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log; if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log;
console.log(important ? chalk.bold(log) : log); const args: unknown[] = [important ? chalk.bold(log) : log];
if (level === 'error' && data) console.log(data); if (data != null) {
args.push(data);
}
console.log(...args);
} }
@bindThis @bindThis

View File

@ -457,6 +457,23 @@ export class MiMeta {
}) })
public verifymailAuthKey: string | null; public verifymailAuthKey: string | null;
@Column('boolean', {
default: false,
})
public enableTruemailApi: boolean;
@Column('varchar', {
length: 1024,
nullable: true,
})
public truemailInstance: string | null;
@Column('varchar', {
length: 1024,
nullable: true,
})
public truemailAuthKey: string | null;
@Column('boolean', { @Column('boolean', {
default: true, default: true,
}) })

View File

@ -9,7 +9,7 @@ import { dirname } from 'node:path';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import rename from 'rename'; import rename from 'rename';
import sharp from 'sharp'; import sharp from 'sharp';
import { sharpBmp } from 'sharp-read-bmp'; import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js'; import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';

View File

@ -284,6 +284,18 @@ export const meta = {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
}, },
enableTruemailApi: {
type: 'boolean',
optional: false, nullable: false,
},
truemailInstance: {
type: 'string',
optional: false, nullable: true,
},
truemailAuthKey: {
type: 'string',
optional: false, nullable: true,
},
enableChartsForRemoteUser: { enableChartsForRemoteUser: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
@ -524,6 +536,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
enableActiveEmailValidation: instance.enableActiveEmailValidation, enableActiveEmailValidation: instance.enableActiveEmailValidation,
enableVerifymailApi: instance.enableVerifymailApi, enableVerifymailApi: instance.enableVerifymailApi,
verifymailAuthKey: instance.verifymailAuthKey, verifymailAuthKey: instance.verifymailAuthKey,
enableTruemailApi: instance.enableTruemailApi,
truemailInstance: instance.truemailInstance,
truemailAuthKey: instance.truemailAuthKey,
enableChartsForRemoteUser: instance.enableChartsForRemoteUser, enableChartsForRemoteUser: instance.enableChartsForRemoteUser,
enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances, enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances,
enableServerMachineStats: instance.enableServerMachineStats, enableServerMachineStats: instance.enableServerMachineStats,

View File

@ -116,6 +116,9 @@ export const paramDef = {
enableActiveEmailValidation: { type: 'boolean' }, enableActiveEmailValidation: { type: 'boolean' },
enableVerifymailApi: { type: 'boolean' }, enableVerifymailApi: { type: 'boolean' },
verifymailAuthKey: { type: 'string', nullable: true }, verifymailAuthKey: { type: 'string', nullable: true },
enableTruemailApi: { type: 'boolean' },
truemailInstance: { type: 'string', nullable: true },
truemailAuthKey: { type: 'string', nullable: true },
enableChartsForRemoteUser: { type: 'boolean' }, enableChartsForRemoteUser: { type: 'boolean' },
enableChartsForFederatedInstances: { type: 'boolean' }, enableChartsForFederatedInstances: { type: 'boolean' },
enableServerMachineStats: { type: 'boolean' }, enableServerMachineStats: { type: 'boolean' },
@ -474,6 +477,26 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.verifymailAuthKey = ps.verifymailAuthKey; set.verifymailAuthKey = ps.verifymailAuthKey;
} }
} }
if (ps.enableTruemailApi !== undefined) {
set.enableTruemailApi = ps.enableTruemailApi;
}
if (ps.truemailInstance !== undefined) {
if (ps.truemailInstance === '') {
set.truemailInstance = null;
} else {
set.truemailInstance = ps.truemailInstance;
}
}
if (ps.truemailAuthKey !== undefined) {
if (ps.truemailAuthKey === '') {
set.truemailAuthKey = null;
} else {
set.truemailAuthKey = ps.truemailAuthKey;
}
}
if (ps.enableChartsForRemoteUser !== undefined) { if (ps.enableChartsForRemoteUser !== undefined) {
set.enableChartsForRemoteUser = ps.enableChartsForRemoteUser; set.enableChartsForRemoteUser = ps.enableChartsForRemoteUser;

View File

@ -4,7 +4,7 @@
*/ */
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { summaly } from 'summaly'; import { summaly } from '@misskey-dev/summaly';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';

View File

@ -19,18 +19,18 @@
"dependencies": { "dependencies": {
"@discordapp/twemoji": "15.0.2", "@discordapp/twemoji": "15.0.2",
"@github/webauthn-json": "2.1.1", "@github/webauthn-json": "2.1.1",
"@misskey-dev/browser-image-resizer": "2.2.1-misskey.10",
"@rollup/plugin-json": "6.1.0", "@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "5.0.5", "@rollup/plugin-replace": "5.0.5",
"@rollup/pluginutils": "5.1.0", "@rollup/pluginutils": "5.1.0",
"@syuilo/aiscript": "0.16.0", "@syuilo/aiscript": "0.16.0",
"@tabler/icons-webfont": "2.44.0", "@tabler/icons-webfont": "2.44.0",
"@twemoji/parser": "15.0.0", "@twemoji/parser": "15.0.0",
"@vitejs/plugin-vue": "4.5.2", "@vitejs/plugin-vue": "5.0.2",
"@vue/compiler-sfc": "3.3.12", "@vue/compiler-sfc": "3.4.3",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
"astring": "1.8.6", "astring": "1.8.6",
"broadcast-channel": "7.0.0", "broadcast-channel": "7.0.0",
"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
"buraha": "0.0.1", "buraha": "0.0.1",
"canvas-confetti": "1.6.1", "canvas-confetti": "1.6.1",
"chart.js": "4.4.1", "chart.js": "4.4.1",
@ -45,7 +45,6 @@
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"estree-walker": "3.0.3", "estree-walker": "3.0.3",
"eventemitter3": "5.0.1", "eventemitter3": "5.0.1",
"gsap": "3.12.4",
"idb-keyval": "6.2.1", "idb-keyval": "6.2.1",
"insert-text-at-cursor": "0.3.0", "insert-text-at-cursor": "0.3.0",
"is-file-animated": "1.0.2", "is-file-animated": "1.0.2",
@ -70,10 +69,12 @@
"uuid": "9.0.1", "uuid": "9.0.1",
"v-code-diff": "1.7.2", "v-code-diff": "1.7.2",
"vite": "5.0.10", "vite": "5.0.10",
"vue": "3.3.12", "vue": "3.4.3",
"vuedraggable": "next" "vuedraggable": "next"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/eslint-plugin": "^1.0.0",
"@misskey-dev/summaly": "^5.0.3",
"@storybook/addon-actions": "7.6.5", "@storybook/addon-actions": "7.6.5",
"@storybook/addon-essentials": "7.6.5", "@storybook/addon-essentials": "7.6.5",
"@storybook/addon-interactions": "7.6.5", "@storybook/addon-interactions": "7.6.5",
@ -107,7 +108,7 @@
"@typescript-eslint/eslint-plugin": "6.14.0", "@typescript-eslint/eslint-plugin": "6.14.0",
"@typescript-eslint/parser": "6.14.0", "@typescript-eslint/parser": "6.14.0",
"@vitest/coverage-v8": "0.34.6", "@vitest/coverage-v8": "0.34.6",
"@vue/runtime-core": "3.3.12", "@vue/runtime-core": "3.4.3",
"acorn": "8.11.2", "acorn": "8.11.2",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "13.6.1", "cypress": "13.6.1",
@ -127,11 +128,10 @@
"start-server-and-test": "2.0.3", "start-server-and-test": "2.0.3",
"storybook": "7.6.5", "storybook": "7.6.5",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"summaly": "github:misskey-dev/summaly",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.6", "vitest": "0.34.6",
"vitest-fetch-mock": "0.2.2", "vitest-fetch-mock": "0.2.2",
"vue-eslint-parser": "9.3.2", "vue-eslint-parser": "9.3.2",
"vue-tsc": "1.8.25" "vue-tsc": "1.8.27"
} }
} }

View File

@ -131,6 +131,10 @@ function onMousedown(evt: MouseEvent): void {
box-sizing: border-box; box-sizing: border-box;
transition: background 0.1s ease; transition: background 0.1s ease;
&:hover {
text-decoration: none;
}
&:not(:disabled):hover { &:not(:disabled):hover {
background: var(--buttonHoverBg); background: var(--buttonHoverBg);
} }

View File

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.codeEditorScroller"> <div :class="$style.codeEditorScroller">
<textarea <textarea
ref="inputEl" ref="inputEl"
v-model="vModel" v-model="v"
:class="[$style.textarea]" :class="[$style.textarea]"
:disabled="disabled" :disabled="disabled"
:required="required" :required="required"
@ -58,7 +58,6 @@ const emit = defineEmits<{
}>(); }>();
const { modelValue } = toRefs(props); const { modelValue } = toRefs(props);
const vModel = ref<string>(modelValue.value ?? '');
const v = ref<string>(modelValue.value ?? ''); const v = ref<string>(modelValue.value ?? '');
const focused = ref(false); const focused = ref(false);
const changed = ref(false); const changed = ref(false);
@ -79,15 +78,14 @@ const onKeydown = (ev: KeyboardEvent) => {
if (ev.code === 'Enter') { if (ev.code === 'Enter') {
const pos = inputEl.value?.selectionStart ?? 0; const pos = inputEl.value?.selectionStart ?? 0;
const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length; const posEnd = inputEl.value?.selectionEnd ?? v.value.length;
if (pos === posEnd) { if (pos === posEnd) {
const lines = vModel.value.slice(0, pos).split('\n'); const lines = v.value.slice(0, pos).split('\n');
const currentLine = lines[lines.length - 1]; const currentLine = lines[lines.length - 1];
const currentLineSpaces = currentLine.match(/^\s+/); const currentLineSpaces = currentLine.match(/^\s+/);
const posDelta = currentLineSpaces ? currentLineSpaces[0].length : 0; const posDelta = currentLineSpaces ? currentLineSpaces[0].length : 0;
ev.preventDefault(); ev.preventDefault();
vModel.value = vModel.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + vModel.value.slice(pos); v.value = v.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + v.value.slice(pos);
v.value = vModel.value;
nextTick(() => { nextTick(() => {
inputEl.value?.setSelectionRange(pos + 1 + posDelta, pos + 1 + posDelta); inputEl.value?.setSelectionRange(pos + 1 + posDelta, pos + 1 + posDelta);
}); });
@ -97,9 +95,8 @@ const onKeydown = (ev: KeyboardEvent) => {
if (ev.key === 'Tab') { if (ev.key === 'Tab') {
const pos = inputEl.value?.selectionStart ?? 0; const pos = inputEl.value?.selectionStart ?? 0;
const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length; const posEnd = inputEl.value?.selectionEnd ?? v.value.length;
vModel.value = vModel.value.slice(0, pos) + '\t' + vModel.value.slice(posEnd); v.value = v.value.slice(0, pos) + '\t' + v.value.slice(posEnd);
v.value = vModel.value;
nextTick(() => { nextTick(() => {
inputEl.value?.setSelectionRange(pos + 1, pos + 1); inputEl.value?.setSelectionRange(pos + 1, pos + 1);
}); });

View File

@ -9,7 +9,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, watch } from 'vue'; import { reactive, watch } from 'vue';
import gsap from 'gsap';
import number from '@/filters/number.js'; import number from '@/filters/number.js';
const props = defineProps<{ const props = defineProps<{
@ -20,8 +19,24 @@ const tweened = reactive({
number: 0, number: 0,
}); });
watch(() => props.value, (n) => { watch(() => props.value, (to, from) => {
gsap.to(tweened, { duration: 1, number: Number(n) || 0 }); // requestAnimationFrame500msfromto1
let start: number | null = null;
function step(timestamp: number) {
if (start === null) {
start = timestamp;
}
const elapsed = timestamp - start;
tweened.number = (from ?? 0) + (to - (from ?? 0)) * elapsed / 500;
if (elapsed < 500) {
window.requestAnimationFrame(step);
} else {
tweened.number = to;
}
}
window.requestAnimationFrame(step);
}, { }, {
immediate: true, immediate: true,
}); });

View File

@ -752,7 +752,17 @@ async function post(ev?: MouseEvent) {
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') { if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; if (!postData.text) {
postData.text = hashtags_;
} else {
const postTextLines = postData.text.split('\n');
if (postTextLines[postTextLines.length - 1].trim() === '') {
postTextLines[postTextLines.length - 1] += hashtags_;
} else {
postTextLines[postTextLines.length - 1] += ' ' + hashtags_;
}
postData.text = postTextLines.join('\n');
}
} }
// plugin // plugin

View File

@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, onUnmounted, ref } from 'vue'; import { defineAsyncComponent, onUnmounted, ref } from 'vue';
import type { summaly } from 'summaly'; import type { summaly } from '@misskey-dev/summaly';
import { url as local } from '@/config.js'; import { url as local } from '@/config.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import * as os from '@/os.js'; import * as os from '@/os.js';

View File

@ -80,6 +80,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #prefix><i class="ti ti-key"></i></template> <template #prefix><i class="ti ti-key"></i></template>
<template #label>Verifymail.io API Auth Key</template> <template #label>Verifymail.io API Auth Key</template>
</MkInput> </MkInput>
<MkSwitch v-model="enableTruemailApi" @update:modelValue="save">
<template #label>Use TrueMail API</template>
</MkSwitch>
<MkInput v-model="truemailInstance" @update:modelValue="save">
<template #prefix><i class="ti ti-key"></i></template>
<template #label>TrueMail API Instance</template>
</MkInput>
<MkInput v-model="truemailAuthKey" @update:modelValue="save">
<template #prefix><i class="ti ti-key"></i></template>
<template #label>TrueMail API Auth Key</template>
</MkInput>
</div> </div>
</MkFolder> </MkFolder>
@ -153,6 +164,9 @@ const enableIpLogging = ref<boolean>(false);
const enableActiveEmailValidation = ref<boolean>(false); const enableActiveEmailValidation = ref<boolean>(false);
const enableVerifymailApi = ref<boolean>(false); const enableVerifymailApi = ref<boolean>(false);
const verifymailAuthKey = ref<string | null>(null); const verifymailAuthKey = ref<string | null>(null);
const enableTruemailApi = ref<boolean>(false);
const truemailInstance = ref<string | null>(null);
const truemailAuthKey = ref<string | null>(null);
const bannedEmailDomains = ref<string>(''); const bannedEmailDomains = ref<string>('');
async function init() { async function init() {
@ -194,6 +208,9 @@ function save() {
enableActiveEmailValidation: enableActiveEmailValidation.value, enableActiveEmailValidation: enableActiveEmailValidation.value,
enableVerifymailApi: enableVerifymailApi.value, enableVerifymailApi: enableVerifymailApi.value,
verifymailAuthKey: verifymailAuthKey.value, verifymailAuthKey: verifymailAuthKey.value,
enableTruemailApi: enableTruemailApi.value,
truemailInstance: truemailInstance.value,
truemailAuthKey: truemailAuthKey.value,
bannedEmailDomains: bannedEmailDomains.value.split('\n'), bannedEmailDomains: bannedEmailDomains.value.split('\n'),
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();

View File

@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-else-if="tab === 'search'"> <div v-else-if="tab === 'search'">
<div class="_gaps"> <div class="_gaps">
<div> <div>
<MkInput v-model="searchQuery"> <MkInput v-model="searchQuery" @enter="search()">
<template #prefix><i class="ti ti-search"></i></template> <template #prefix><i class="ti ti-search"></i></template>
</MkInput> </MkInput>
<MkButton primary rounded style="margin-top: 8px;" @click="search()">{{ i18n.ts.search }}</MkButton> <MkButton primary rounded style="margin-top: 8px;" @click="search()">{{ i18n.ts.search }}</MkButton>

View File

@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :contentMax="700"> <MkSpacer :contentMax="700">
<div v-if="tab === 'search'"> <div v-if="tab === 'search'">
<div class="_gaps"> <div class="_gaps">
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search"> <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
<template #prefix><i class="ti ti-search"></i></template> <template #prefix><i class="ti ti-search"></i></template>
</MkInput> </MkInput>
<MkRadios v-model="searchType" @update:modelValue="search()"> <MkRadios v-model="searchType" @update:modelValue="search()">

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div class="_gaps"> <div class="_gaps">
<div class="_gaps"> <div class="_gaps">
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search"> <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
<template #prefix><i class="ti ti-search"></i></template> <template #prefix><i class="ti ti-search"></i></template>
</MkInput> </MkInput>
<MkFolder> <MkFolder>

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div class="_gaps"> <div class="_gaps">
<div class="_gaps"> <div class="_gaps">
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search"> <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
<template #prefix><i class="ti ti-search"></i></template> <template #prefix><i class="ti ti-search"></i></template>
</MkInput> </MkInput>
<MkRadios v-model="searchOrigin" @update:modelValue="search()"> <MkRadios v-model="searchOrigin" @update:modelValue="search()">

View File

@ -168,7 +168,7 @@ export class Storage<T extends StateDef> {
this.reactiveState[key].value = this.state[key] = rawValue; this.reactiveState[key].value = this.state[key] = rawValue;
return this.addIdbSetJob(async () => { return this.addIdbSetJob(async () => {
if (_DEV_) console.log(`set ${key} start`); if (_DEV_) console.log(`set ${String(key)} start`);
switch (this.def[key].where) { switch (this.def[key].where) {
case 'device': { case 'device': {
this.pizzaxChannel.postMessage({ this.pizzaxChannel.postMessage({
@ -207,7 +207,7 @@ export class Storage<T extends StateDef> {
break; break;
} }
} }
if (_DEV_) console.log(`set ${key} complete`); if (_DEV_) console.log(`set ${String(key)} complete`);
}); });
} }

View File

@ -5,7 +5,7 @@
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { readAndCompressImage } from 'browser-image-resizer'; import { readAndCompressImage } from '@misskey-dev/browser-image-resizer';
import { getCompressionConfig } from './upload/compress-config.js'; import { getCompressionConfig } from './upload/compress-config.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
import { apiUrl } from '@/config.js'; import { apiUrl } from '@/config.js';

View File

@ -5,7 +5,7 @@
import isAnimated from 'is-file-animated'; import isAnimated from 'is-file-animated';
import { isWebpSupported } from './isWebpSupported.js'; import { isWebpSupported } from './isWebpSupported.js';
import type { BrowserImageResizerConfig } from 'browser-image-resizer'; import type { BrowserImageResizerConfigWithConvertedOutput } from '@misskey-dev/browser-image-resizer';
const compressTypeMap = { const compressTypeMap = {
'image/jpeg': { quality: 0.90, mimeType: 'image/webp' }, 'image/jpeg': { quality: 0.90, mimeType: 'image/webp' },
@ -21,7 +21,7 @@ const compressTypeMapFallback = {
'image/svg+xml': { quality: 1, mimeType: 'image/png' }, 'image/svg+xml': { quality: 1, mimeType: 'image/png' },
} as const; } as const;
export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfig | undefined> { export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfigWithConvertedOutput | undefined> {
const imgConfig = (isWebpSupported() ? compressTypeMap : compressTypeMapFallback)[file.type]; const imgConfig = (isWebpSupported() ? compressTypeMap : compressTypeMapFallback)[file.type];
if (!imgConfig || await isAnimated(file)) { if (!imgConfig || await isAnimated(file)) {
return; return;

View File

@ -6,7 +6,7 @@
import { describe, test, assert, afterEach } from 'vitest'; import { describe, test, assert, afterEach } from 'vitest';
import { render, cleanup, type RenderResult } from '@testing-library/vue'; import { render, cleanup, type RenderResult } from '@testing-library/vue';
import './init'; import './init';
import type { summaly } from 'summaly'; import type { summaly } from '@misskey-dev/summaly';
import { components } from '@/components/index.js'; import { components } from '@/components/index.js';
import { directives } from '@/directives/index.js'; import { directives } from '@/directives/index.js';
import MkUrlPreview from '@/components/MkUrlPreview.vue'; import MkUrlPreview from '@/components/MkUrlPreview.vue';

View File

@ -8,15 +8,16 @@
}, },
"devDependencies": { "devDependencies": {
"@apidevtools/swagger-parser": "10.1.0", "@apidevtools/swagger-parser": "10.1.0",
"@misskey-dev/eslint-plugin": "^1.0.0",
"@types/node": "20.9.1", "@types/node": "20.9.1",
"@typescript-eslint/eslint-plugin": "6.11.0", "@typescript-eslint/eslint-plugin": "6.11.0",
"@typescript-eslint/parser": "6.11.0", "@typescript-eslint/parser": "6.11.0",
"eslint": "8.53.0", "eslint": "8.53.0",
"typescript": "5.3.3",
"tsx": "4.4.0",
"ts-case-convert": "2.0.2",
"openapi-types": "12.1.3", "openapi-types": "12.1.3",
"openapi-typescript": "6.7.1" "openapi-typescript": "6.7.1",
"ts-case-convert": "2.0.2",
"tsx": "4.4.0",
"typescript": "5.3.3"
}, },
"files": [ "files": [
"built" "built"

View File

@ -23,6 +23,7 @@
}, },
"devDependencies": { "devDependencies": {
"@microsoft/api-extractor": "7.38.5", "@microsoft/api-extractor": "7.38.5",
"@misskey-dev/eslint-plugin": "^1.0.0",
"@swc/jest": "0.2.29", "@swc/jest": "0.2.29",
"@types/jest": "29.5.11", "@types/jest": "29.5.11",
"@types/node": "20.10.5", "@types/node": "20.10.5",

View File

@ -1,118 +1,7 @@
module.exports = { module.exports = {
root: true, root: true,
parser: '@typescript-eslint/parser', ignorePatterns: ['**/.eslintrc.cjs'],
plugins: [
'@typescript-eslint',
'import'
],
extends: [ extends: [
'eslint:recommended', 'plugin:@misskey-dev/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript'
], ],
rules: {
'indent': ['warn', 'tab', {
'SwitchCase': 1,
'MemberExpression': 1,
'flatTernaryExpressions': true,
'ArrayExpression': 'first',
'ObjectExpression': 'first',
}],
'eol-last': ['error', 'always'],
'semi': ['error', 'always'],
'semi-spacing': ['error', { 'before': false, 'after': true }],
'quotes': ['warn', 'single'],
'comma-dangle': ['warn', 'always-multiline'],
'comma-spacing': ['error', { 'before': false, 'after': true }],
'array-bracket-spacing': ['error', 'never'],
'keyword-spacing': ['error', {
'before': true,
'after': true,
}],
'key-spacing': ['error', {
'beforeColon': false,
'afterColon': true,
}],
'arrow-spacing': ['error', {
'before': true,
'after': true,
}],
'brace-style': ['error', '1tbs', {
'allowSingleLine': true,
}],
'padded-blocks': ['error', 'never'],
/* TODO: path alias使warn
'no-restricted-imports': ['warn', {
'patterns': [
]
}],
*/
'eqeqeq': ['error', 'always', { 'null': 'ignore' }],
'no-multi-spaces': ['error'],
'no-var': ['error'],
'prefer-arrow-callback': ['error'],
'no-throw-literal': ['error'],
'no-param-reassign': ['warn'],
'no-constant-condition': ['warn'],
'no-empty-pattern': ['warn'],
'no-async-promise-executor': ['off'],
'no-useless-escape': ['off'],
'no-multiple-empty-lines': ['error', { 'max': 1 }],
'no-control-regex': ['warn'],
'no-empty': ['warn'],
'no-inner-declarations': ['off'],
'no-sparse-arrays': ['off'],
'nonblock-statement-body-position': ['error', 'beside'],
'object-curly-spacing': ['error', 'always'],
'space-infix-ops': ['error'],
'space-before-blocks': ['error', 'always'],
'padding-line-between-statements': [
'error',
{ 'blankLine': 'always', 'prev': 'function', 'next': '*' },
{ 'blankLine': 'always', 'prev': '*', 'next': 'function' },
],
"lines-between-class-members": "off",
/* typescript-eslint enforce
'@typescript-eslint/lines-between-class-members': ['error', {
enforce: [{
blankLine: 'always',
prev: 'method',
next: '*',
}]
}],
*/
'@typescript-eslint/func-call-spacing': ['error', 'never'],
'@typescript-eslint/no-explicit-any': ['warn'],
'@typescript-eslint/no-unused-vars': ['warn'],
'@typescript-eslint/no-unnecessary-condition': ['warn'],
'@typescript-eslint/no-var-requires': ['warn'],
'@typescript-eslint/no-inferrable-types': ['warn'],
'@typescript-eslint/no-empty-function': ['off'],
'@typescript-eslint/no-non-null-assertion': ['warn'],
'@typescript-eslint/explicit-function-return-type': ['off'],
'@typescript-eslint/no-misused-promises': ['error', {
'checksVoidReturn': false,
}],
'@typescript-eslint/consistent-type-imports': 'off',
'@typescript-eslint/prefer-nullish-coalescing': [
'warn',
],
'@typescript-eslint/naming-convention': [
'error',
{
"selector": "typeLike",
"format": ["PascalCase"]
},
{
"selector": "typeParameter",
"format": []
}
],
'import/no-unresolved': ['off'],
'import/no-default-export': ['warn'],
'import/order': ['warn', {
'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
}]
},
}; };

View File

@ -14,6 +14,7 @@
"misskey-js": "workspace:*" "misskey-js": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/eslint-plugin": "^1.0.0",
"@typescript-eslint/parser": "6.14.0", "@typescript-eslint/parser": "6.14.0",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
"eslint": "8.56.0", "eslint": "8.56.0",

File diff suppressed because it is too large Load Diff