Compare commits

...

14 Commits

Author SHA1 Message Date
github-actions[bot] 0aaeb6d136 [skip ci] Update CHANGELOG.md (prepend template) 2025-04-09 02:17:33 +00:00
github-actions[bot] fb277501a0 Release: 2025.4.0 2025-04-09 02:17:29 +00:00
syuilo 718138509f Update CHANGELOG.md 2025-04-09 10:56:57 +09:00
github-actions[bot] a1ef38a606 Bump version to 2025.4.0-rc.5 2025-04-08 23:21:42 +00:00
zyoshoka 4a8b7ef23e
fix(ci): correct condition for skipping Chromatic build (#15783) 2025-04-09 08:10:17 +09:00
anatawa12 1f17161dfb
feat: show file name for warning / errors of [create-search-index] (#15785) 2025-04-09 08:10:04 +09:00
syuilo 2488708934
New Crowdin updates (#15781)
* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean)

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

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (German)

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Korean)
2025-04-09 08:09:43 +09:00
anatawa12 628a0c71a9
fix: direct does not work in nested route (#15784) 2025-04-09 08:09:08 +09:00
syuilo b40f5b9021 fix typo 2025-04-09 08:08:28 +09:00
syuilo 0f79db6a4d refactor 2025-04-09 08:05:27 +09:00
syuilo 910e097480 add missing SearchMarker 2025-04-09 08:04:00 +09:00
syuilo a8acbd6e68 refactor 2025-04-09 08:01:10 +09:00
syuilo c19f51a2ab resolve promise
https://github.com/misskey-dev/misskey/pull/15735#discussion_r2033387035
2025-04-09 07:56:20 +09:00
syuilo 1cf370882e add missing SearchMarker 2025-04-09 07:51:24 +09:00
22 changed files with 238 additions and 218 deletions

View File

@ -18,8 +18,8 @@ jobs:
# Neither Dependabot nor Renovate will change the actual behavior for components.
if: >-
github.repository == 'misskey-dev/misskey' &&
startsWith(github.ref, 'refs/heads/dependabot/') != true &&
startsWith(github.ref, 'refs/heads/renovate/') != true
startsWith(github.head_ref, 'refs/heads/dependabot/') != true &&
startsWith(github.head_ref, 'refs/heads/renovate/') != true
runs-on: ubuntu-latest
env:

View File

@ -1,3 +1,15 @@
## Unreleased
### General
-
### Client
-
### Server
-
## 2025.4.0
### General
@ -61,11 +73,13 @@
- Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように
- Enhance: 全体的なブラッシュアップ
- Enhance 全体的なパフォーマンス向上
- Enhance: ファイルのアップロードでデフォルトで圧縮するかどうかのオプションが廃止され、アップロード時に圧縮するかどうかを選択するようになりました
- 画像データの貼り付け、ドロップ時は圧縮されるようになりました
- Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
- Fix: iPadOSでdeck uiをマウスカーソルによってスクロールできない問題を修正
- NOTE: 構造上クラシックUIを新しいデザインシステムに移行することが困難なため、クラシックUIが削除されました
- デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置し、ナビゲーションバーを上部に表示することである程度クラシックUIを再現できます
- Fix: iPadOSでdeck uiをマウスカーソルによってスクロールできない問題を修正
### Server
- Enhance 全体的なパフォーマンス向上

View File

@ -424,7 +424,7 @@ antennaExcludeBots: "Exclou els bots"
antennaKeywordsDescription: "Separar amb espais per la condició AND o amb salts de línia per la condició OR."
notifyAntenna: "Notifica'm les publicacions noves"
withFileAntenna: "Només les publicacions amb fitxers"
hideNotesInSensitiveChannel: "Amaga les notes a canals sensibles "
excludeNotesInSensitiveChannel: "Excloure notes a canals sensibles"
enableServiceworker: "Activar les notificacions al navegador"
antennaUsersDescription: "Llistar un nom d'usuari per línia"
caseSensitive: "Sensible a majúscules i minúscules "

View File

@ -424,7 +424,7 @@ antennaExcludeBots: "Bot-Accounts ausschließen"
antennaKeywordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch trennen"
notifyAntenna: "Über neue Notizen benachrichtigen"
withFileAntenna: "Nur Notizen mit Dateien"
hideNotesInSensitiveChannel: "Verstecke Notizen von sensitive Kanäle"
excludeNotesInSensitiveChannel: "Schließe Notizen von sensitive Kanäle aus"
enableServiceworker: "Push-Benachrichtigungen im Browser aktivieren"
antennaUsersDescription: "Benutzernamen getrennt durch Zeilenumbrüche angeben"
caseSensitive: "Groß-/Kleinschreibung unterscheiden"
@ -1343,6 +1343,7 @@ top: "Oben"
embed: "Einbetten"
settingsMigrating: "Ihre Einstellungen werden gerade migriert, Bitte warten Sie einen Moment... (Sie können die Einstellungen später auch manuell migrieren, indem Sie zu Einstellungen → Sonstiges → Alte Einstellungen migrieren gehen)"
readonly: "Nur Lesezugriff"
goToDeck: "Zurück zum Deck"
_chat:
noMessagesYet: "Noch keine Nachrichten"
newMessage: "Neue Nachricht"

View File

@ -424,7 +424,7 @@ antennaExcludeBots: "Exclude bot accounts"
antennaKeywordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition."
notifyAntenna: "Notify about new notes"
withFileAntenna: "Only notes with files"
hideNotesInSensitiveChannel: "Hide notes from sensitive channels"
excludeNotesInSensitiveChannel: "Exclude notes from sensitive channels"
enableServiceworker: "Enable Push-Notifications for your Browser"
antennaUsersDescription: "List one username per line"
caseSensitive: "Case sensitive"
@ -1343,6 +1343,7 @@ top: "Top"
embed: "Embed"
settingsMigrating: "Settings are being migrated, please wait a moment... (You can also migrate manually later by going to Settings→Others→Migrate old settings)"
readonly: "Read only"
goToDeck: "Return to Deck"
_chat:
noMessagesYet: "No messages yet"
newMessage: "New message"

2
locales/index.d.ts vendored
View File

@ -1715,7 +1715,7 @@ export interface Locale extends ILocale {
*/
"withFileAntenna": string;
/**
*
*
*/
"excludeNotesInSensitiveChannel": string;
/**

View File

@ -424,7 +424,6 @@ antennaExcludeBots: "Escludere i Bot"
antennaKeywordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)."
notifyAntenna: "Invia notifiche delle nuove note"
withFileAntenna: "Solo note con file in allegato"
hideNotesInSensitiveChannel: "Nascondere le Note dai canali espliciti"
enableServiceworker: "Abilita ServiceWorker"
antennaUsersDescription: "Elenca un nome utente per riga"
caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole"

View File

@ -418,13 +418,13 @@ antennas: "안테나"
manageAntennas: "안테나 관리"
name: "이름"
antennaSource: "받을 소스"
antennaKeywords: "받을 검색어"
antennaExcludeKeywords: "제외할 검색어"
antennaKeywords: "받을 키워드"
antennaExcludeKeywords: "제외할 키워드"
antennaExcludeBots: "봇 계정 제외"
antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다"
notifyAntenna: "새로운 노트를 알림"
withFileAntenna: "파일이 첨부된 노트만"
hideNotesInSensitiveChannel: "민감한 채널의 노트 숨기기"
excludeNotesInSensitiveChannel: "민감한 채널의 노트 제외"
enableServiceworker: "ServiceWorker 사용"
antennaUsersDescription: "유저명을 한 줄에 한 명씩 적습니다"
caseSensitive: "대소문자를 구분"
@ -520,7 +520,7 @@ style: "스타일"
drawer: "서랍"
popup: "팝업"
showNoteActionsOnlyHover: "마우스가 올라간 때에만 노트 동작 버튼을 표시하기"
showReactionsCount: "노트의 반응 수를 표시하기"
showReactionsCount: "노트의 리액션 수를 표시하기"
noHistory: "기록이 없습니다"
signinHistory: "로그인 기록"
enableAdvancedMfm: "고급 MFM을 활성화"
@ -754,8 +754,8 @@ repliedCount: "받은 답글 수"
renotedCount: "받은 리노트 수"
followingCount: "팔로우 수"
followersCount: "팔로워 수"
sentReactionsCount: "반응 수"
receivedReactionsCount: "받은 반응 수"
sentReactionsCount: "리액션 수"
receivedReactionsCount: "받은 리액션 수"
pollVotesCount: "투표 수"
pollVotedCount: "받은 투표 수"
yes: "예"
@ -868,8 +868,8 @@ configure: "설정하기"
postToGallery: "갤러리에 업로드"
postToHashtag: "이 해시태그에 게시"
gallery: "갤러리"
recentPosts: "최근 포스트"
popularPosts: "인기 포스트"
recentPosts: "최근 게시물"
popularPosts: "인기 게시물"
shareWithNote: "노트로 공유"
ads: "광고"
expiration: "기한"
@ -1056,7 +1056,7 @@ thisPostMayBeAnnoyingHome: "홈에 게시"
thisPostMayBeAnnoyingCancel: "그만두기"
thisPostMayBeAnnoyingIgnore: "이대로 게시"
collapseRenotes: "이미 본 리노트를 간략화하기"
collapseRenotesDescription: "반응이나 리노트를 한 노트를 접어서 표시합니다."
collapseRenotesDescription: "리액션이나 리노트를 한 노트를 접어서 표시합니다."
internalServerError: "내부 서버 오류"
internalServerErrorDescription: "내부 서버에서 예기치 않은 오류가 발생했습니다."
copyErrorInfo: "오류 정보 복사"
@ -1080,8 +1080,8 @@ resetPasswordConfirm: "비밀번호를 재설정하시겠습니까?"
sensitiveWords: "민감한 단어"
sensitiveWordsDescription: "설정한 단어가 포함된 노트의 공개 범위를 '홈'으로 강제합니다. 개행으로 구분하여 여러 개를 지정할 수 있습니다."
sensitiveWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다."
prohibitedWords: "금지 워드"
prohibitedWordsDescription: "설정된 워드가 포함되는 노트를 작성하려고 하면, 에러가 발생하도록 합니다. 줄바꿈으로 구분지어 복수 설정할 수 있습니다."
prohibitedWords: "금지 단어"
prohibitedWordsDescription: "설정된 단어가 포함되는 노트를 작성하려고 하면, 오류가 발생하도록 합니다. 줄바꿈으로 구분지어 복수 설정할 수 있습니다."
prohibitedWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다."
hiddenTags: "숨긴 해시태그"
hiddenTagsDescription: "설정한 태그를 트렌드에 표시하지 않도록 합니다. 줄 바꿈으로 하나씩 나눠서 설정할 수 있습니다."
@ -1616,53 +1616,53 @@ _achievements:
_types:
_notes1:
title: "미스키 계정 만들었어요"
description: "첫 노트를 작성했습니다"
description: "첫 노트를 게시했다"
flavor: "Misskey에 어서 오세요!"
_notes10:
title: "몇 가지 노트"
description: "10개의 노트를 작성했습니다"
description: "10개의 노트를 게시했다"
_notes100:
title: "많은 노트"
description: "100개의 노트를 작성했습니다"
description: "100개의 노트를 게시했다"
_notes500:
title: "노트 범벅"
description: "500개의 노트를 작성했습니다"
description: "500개의 노트를 게시했다"
_notes1000:
title: "노트가 산더미"
description: "1,000개의 노트를 작성했습니다"
description: "1,000개의 노트를 게시했다"
_notes5000:
title: "솟아나는 노트"
description: "5,000개의 노트를 작성했습니다"
description: "5,000개의 노트를 게시했다"
_notes10000:
title: "슈퍼 노트"
description: "10,000개의 노트를 작성했습니다"
description: "10,000개의 노트를 게시했다"
_notes20000:
title: "노트가 필요해요"
description: "20,000개의 노트를 작성했습니다"
title: "노트가 필요해요"
description: "20,000개의 노트를 게시했다"
_notes30000:
title: "노트노트노트"
description: "30,000개의 노트를 작성했습니다"
description: "30,000개의 노트를 게시했다"
_notes40000:
title: "노트 공장"
description: "40,000개의 노트를 작성했습니다"
description: "40,000개의 노트를 게시했다"
_notes50000:
title: "노트 행성"
description: "50,000개의 노트를 작성했습니다"
description: "50,000개의 노트를 게시했다"
_notes60000:
title: "노트 퀘이사"
description: "60,000개의 노트를 작성했습니다"
description: "60,000개의 노트를 게시했다"
_notes70000:
title: "노트 블랙홀"
description: "70,000개의 노트를 작성했습니다"
description: "70,000개의 노트를 게시했다"
_notes80000:
title: "노트 은하"
description: "80,000개의 노트를 작성했습니다"
description: "80,000개의 노트를 게시했다"
_notes90000:
title: "노트 우주"
description: "90,000개의 노트를 작성했습니다"
description: "90,000개의 노트를 게시했다"
_notes100000:
title: "ALL YOUR NOTE ARE BELONG TO US"
description: "100,000개의 노트를 작성했습니다"
description: "100,000개의 노트를 게시했다"
flavor: "이렇게나 쓸 게 있어요?"
_login3:
title: "초보자 I"
@ -1687,175 +1687,175 @@ _achievements:
flavor: "그 유저, 미스키스트이다"
_login200:
title: "단골 I"
description: "총 200일간 로그인했습니다"
description: "총 로그인한 날이 200일"
_login300:
title: "단골 II"
description: "총 300일간 로그인했습니다"
description: "총 로그인한 날이 300일"
_login400:
title: "단골 III"
description: "총 400일간 로그인했습니다"
description: "총 로그인한 날이 400일"
_login500:
title: "베테랑 I"
description: "총 500일간 로그인했습니다"
description: "총 로그인한 날이 500일"
flavor: "제군, 나는 노트가 좋다"
_login600:
title: "베테랑 II"
description: "총 600일간 로그인했습니다"
description: "총 로그인한 날이 600일"
_login700:
title: "베테랑 III"
description: "총 700일간 로그인했습니다"
description: "총 로그인한 날이 700일"
_login800:
title: "노트 마스터 I"
description: "총 800일간 로그인했습니다"
description: "총 로그인한 날이 800일"
_login900:
title: "노트 마스터 II"
description: "총 900일간 로그인했습니다"
description: "총 로그인한 날이 900일"
_login1000:
title: "노트 마스터 III"
description: "총 1,000일간 로그인했습니다"
description: "총 로그인한 날이 1,000일"
flavor: "Misskey를 사용해 주셔서 감사합니다!"
_noteClipped1:
title: "클립할 수밖에 없었어"
description: "처음으로 노트를 클립했습니다"
description: "처음으로 노트를 클립했다"
_noteFavorited1:
title: "별을 바라보는 자"
description: "처음으로 노트를 즐겨찾기했습니다"
description: "처음으로 노트를 즐겨찾기했다"
_myNoteFavorited1:
title: "별을 원하는 자"
description: "다른 사람이 당신의 노트를 즐겨찾기했습니다"
description: "다른 사람이 당신의 노트를 즐겨찾기했다"
_profileFilled:
title: "준비 완료"
description: "프로필 설정을 완료했습니다"
description: "프로필 설정을 완료했다"
_markedAsCat:
title: "나는 고양이다냥!"
description: "계정을 고양이로 설정했습니다냥"
description: "계정을 고양이로 설정했다냥"
flavor: "냐냐냐냐냐냐아아아아앙!"
_following1:
title: "첫 팔로우"
description: "유저를 처음으로 팔로우했습니다"
description: "유저를 처음으로 팔로우했다"
_following10:
title: "팔로우, 팔로우"
description: "10명의 유저를 팔로우했습니다"
description: "10명의 유저를 팔로우했다"
_following50:
title: "친구 잔뜩"
description: "50명의 유저를 팔로우했습니다"
description: "50명의 유저를 팔로우했다"
_following100:
title: "주소록 한 권으론 부족해"
description: "100명의 유저를 팔로우했습니다"
description: "100명의 유저를 팔로우했다"
_following300:
title: "친구가 넘쳐나"
description: "300명의 유저를 팔로우했습니다"
description: "300명의 유저를 팔로우했다"
_followers1:
title: "첫 팔로워"
description: "유저가 처음으로 팔로잉했습니다"
description: "유저가 처음으로 팔로잉했다"
_followers10:
title: "팔로우 미!"
description: "10명의 유저가 팔로우했습니다"
description: "10명의 유저가 팔로우했다"
_followers50:
title: "이곳저곳"
description: "50명의 유저가 팔로우했습니다"
description: "50명의 유저가 팔로우했다"
_followers100:
title: "인기왕"
description: "100명의 유저가 팔로우했습니다"
description: "100명의 유저가 팔로우했다"
_followers300:
title: "줄 좀 서봐요"
description: "100명의 유저가 팔로우했습니다"
description: "100명의 유저가 팔로우했다"
_followers500:
title: "기지국"
description: "500명의 유저가 팔로우했습니다"
description: "500명의 유저가 팔로우했다"
_followers1000:
title: "유명인사"
description: "1,000명의 유저가 팔로우했습니다"
description: "1,000명의 유저가 팔로우했다"
_collectAchievements30:
title: "도전 과제 콜렉터"
description: "30개의 도전과제를 획득했습니다"
description: "30개의 도전과제를 획득했다"
_viewAchievements3min:
title: "저 도전과제 좋아해요"
description: "도전 과제 목록을 3분 이상 쳐다봤습니다"
description: "도전 과제 목록을 3분 이상 쳐다봤다"
_iLoveMisskey:
title: "I Love Misskey"
description: "\"I ❤ #Misskey\"를 포스트했습니다"
description: "\"I ❤ #Misskey\"를 게시했다"
flavor: "Misskey를 이용해 주셔서 감사합니다! ― 개발 팀"
_foundTreasure:
title: "보물찾기"
description: "숨겨진 보물을 발견했습니다"
description: "숨겨진 보물을 발견했다"
_client30min:
title: "잠시 쉬어요"
description: "클라이언트를 시작하고 30분이 경과하였습니다"
description: "클라이언트를 시작하고 30분이 경과다"
_client60min:
title: "No \"Miss\" in Misskey"
description: "클라이언트를 시작하고 60분이 경과하였습니다"
description: "클라이언트를 시작하고 60분이 경과다"
_noteDeletedWithin1min:
title: "있었는데요 없었습니다"
description: "노트를 포스트한 후 1분 이내에 삭제했습니다"
description: "노트를 게시한 후 1분 이내에 삭제했다"
_postedAtLateNight:
title: "올빼미"
description: "한밤중에 노트를 포스트했습니다"
description: "한밤중에 노트를 게시했다"
flavor: "잠 좀 자세요. 걱정돼요."
_postedAt0min0sec:
title: "정각"
description: "0분 0초 정각에 노트를 작성했습니다"
description: "0분 0초 정각에 노트를 게시했다"
flavor: "째깍 째깍 째깍 땡!"
_selfQuote:
title: "혼잣말"
description: "자기 노트를 인용했습니다"
description: "자기 노트를 인용했다"
_htl20npm:
title: "타임라인 폭주 중"
description: "1분 사이에 홈 타임라인에 노트가 20개 넘게 생성되었습니다"
description: "1분 사이에 홈 타임라인에 노트가 20개 넘게 생성되었다"
_viewInstanceChart:
title: "애널리스트"
description: "서버의 차트를 열었습니다"
description: "서버의 차트를 열었다"
_outputHelloWorldOnScratchpad:
title: "Hello, world!"
description: "스크래치패드에서 hello world를 출력했습니다"
description: "스크래치패드에서 hello world를 출력했다"
_open3windows:
title: "멀티 윈도우"
description: "3개 이상의 창을 열었습니다"
description: "3개 이상의 창을 열었다"
_driveFolderCircularReference:
title: "순환 참조"
description: "드라이브 폴더에 스스로를 넣게 했습니다"
description: "드라이브 폴더에 스스로를 넣게 했다"
_reactWithoutRead:
title: "읽고 답하긴 하시는 건가요?"
description: "100자가 넘는 노트를 작성한 지 3초 안에 반응했어요"
description: "100자가 넘는 노트를 작성한 지 3초 안에 리액션했다"
_clickedClickHere:
title: "여기를 누르세요"
description: "여기를 눌렀습니다"
title: "여길 눌러보세요"
description: "여기를 눌렀다"
_justPlainLucky:
title: "그냥 운이 좋았어"
description: "매 10초마다 0.01%의 확률로 달성됩니다"
description: "매 10초마다 0.01%의 확률로 달성다"
_setNameToSyuilo:
title: "신 콤플렉스"
description: "이름을 syuilo로 설정했습니다"
description: "이름을 syuilo로 설정했다"
_passedSinceAccountCreated1:
title: "1주년"
description: "계정을 생성하고 1년이 지났습니다"
description: "계정을 생성하고 1년이 지났다"
_passedSinceAccountCreated2:
title: "2주년"
description: "계정을 생성하고 2년이 지났습니다"
description: "계정을 생성하고 2년이 지났다"
_passedSinceAccountCreated3:
title: "3주년"
description: "계정을 생성하고 3년이 지났습니다"
description: "계정을 생성하고 3년이 지났다"
_loggedInOnBirthday:
title: "생일 축하합니다!"
description: "생일에 로그인했습니다"
description: "생일에 로그인했다"
_loggedInOnNewYearsDay:
title: "새해 복 많이 받으세요"
description: "새해 첫 날에 로그인했습니다"
description: "새해 첫 날에 로그인했다"
flavor: "올해에도 저희 서버에 관심을 가져 주셔서 감사합니다"
_cookieClicked:
title: "쿠키를 클릭하는 게임"
description: "쿠키를 클릭했습니다"
flavor: "소프트웨어 착각하지 않으셨나요?"
description: "쿠키를 클릭했다"
flavor: "소프트웨어 착각하지 않았어?"
_brainDiver:
title: "Brain Diver"
description: "Brain Diver로의 링크를 첨부했습니다"
description: "Brain Diver로의 링크를 첨부했다"
flavor: "Misskey-Misskey La-Tu-Ma"
_smashTestNotificationButton:
title: "테스트 과잉"
description: "매우 짧은 시간 안에 알림 테스트를 여러 번 수행했습니다"
description: "매우 짧은 시간 안에 알림 테스트를 여러 번 수행했다"
_tutorialCompleted:
title: "Misskey 입문자 과정 수료증"
description: "튜토리얼을 완료했습니다"
description: "튜토리얼을 완료했다"
_bubbleGameExplodingHead:
title: "🤯"
description: "버블 게임에서 가장 큰 물건을 내놓았다"
@ -2565,7 +2565,7 @@ _notification:
checkNotificationBehavior: "알림 표시를 체크하기"
sendTestNotification: "테스트 알림 보내기"
notificationWillBeDisplayedLikeThis: "알림이 이렇게 표시됩니다"
reactedBySomeUsers: "{n}명이 반응했습니다"
reactedBySomeUsers: "{n}명이 리액션했습니다"
likedBySomeUsers: "{n}명이 좋아요를 했습니다"
renotedBySomeUsers: "{n}명이 리노트했습니다"
followedBySomeUsers: "{n}명에게 팔로우됨"
@ -2729,7 +2729,7 @@ _moderationLogTypes:
deleteAccount: "계정을 삭제"
deletePage: "페이지를 삭제"
deleteFlash: "Play를 삭제"
deleteGalleryPost: "갤러리 포스트를 삭제"
deleteGalleryPost: "갤러리 게시물을 삭제"
deleteChatRoom: "채팅 룸 삭제"
updateProxyAccountDescription: "프록시 계정의 설명 업데이트"
_fileViewer:
@ -2976,8 +2976,8 @@ _captcha:
title: "CAPTCHA 검증을 실패했습니다."
text: "설정이 올바른지 다시 한 번 확인해보세요."
_unknown:
title: "CAPTCHA 에러"
text: "알 수 없는 에러가 발생했습니다."
title: "CAPTCHA 오류"
text: "알 수 없는 오류가 발생했습니다."
_bootErrors:
title: "로딩이 실패함"
serverError: "잠시 기다렸다가 다시 로드해도 여전히 문제가 해결되지 않으면 아래 Error ID와 함께 서버 관리자에게 연락해 주세요."

View File

@ -424,7 +424,7 @@ antennaExcludeBots: "排除机器人账户"
antennaKeywordsDescription: "AND 条件用空格分隔OR 条件用换行符分隔。"
notifyAntenna: "开启通知"
withFileAntenna: "仅带有附件的帖子"
hideNotesInSensitiveChannel: "隐藏敏感频道内的帖子"
excludeNotesInSensitiveChannel: "排除敏感频道内的帖子"
enableServiceworker: "启用 ServiceWorker"
antennaUsersDescription: "指定用户名,一行一个"
caseSensitive: "区分大小写"

View File

@ -424,7 +424,6 @@ antennaExcludeBots: "排除機器人帳戶"
antennaKeywordsDescription: "空格代表「以及」AND換行代表「或者」OR"
notifyAntenna: "通知有新貼文"
withFileAntenna: "僅帶有附件的貼文"
hideNotesInSensitiveChannel: "隱藏敏感頻道的貼文"
enableServiceworker: "啟用瀏覽器的推播通知"
antennaUsersDescription: "填寫使用者名稱,以換行分隔"
caseSensitive: "區分大小寫"

View File

@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2025.4.0-rc.4",
"version": "2025.4.0",
"codename": "nasubi",
"repository": {
"type": "git",

View File

@ -173,21 +173,21 @@ function customStringify(obj: unknown): string {
/**
*
*/
function extractElementText(node: ElementNode): string | null {
return extractElementTextChecked(node, node.tag);
function extractElementText(node: ElementNode, id: string): string | null {
return extractElementTextChecked(node, node.tag, id);
}
function extractElementTextChecked(node: ElementNode, processingNodeName: string): string | null {
function extractElementTextChecked(node: ElementNode, processingNodeName: string, id: string): string | null {
const result: string[] = [];
for (const child of node.children) {
const text = extractElementText2Inner(child, processingNodeName);
const text = extractElementText2Inner(child, processingNodeName, id);
if (text == null) return null;
result.push(text);
}
return result.join('');
}
function extractElementText2Inner(node: TemplateChildNode, processingNodeName: string): string | null {
function extractElementText2Inner(node: TemplateChildNode, processingNodeName: string, id: string): string | null {
if (node.type === NodeTypes.COMPOUND_EXPRESSION) throw new Error("Unexpected COMPOUND_EXPRESSION");
switch (node.type) {
@ -196,16 +196,16 @@ function extractElementText2Inner(node: TemplateChildNode, processingNodeName: s
if (expr.type === NodeTypes.COMPOUND_EXPRESSION) throw new Error(`Unexpected COMPOUND_EXPRESSION`);
const exprResult = evalExpression(expr.content);
if (typeof exprResult !== 'string') {
logger.error(`Result of interpolation node is not string at line ${node.loc.start.line}`);
logger.error(`Result of interpolation node is not string at line ${id}:${node.loc.start.line}`);
return null;
}
return exprResult;
}
case NodeTypes.ELEMENT:
if (node.tagType === ElementTypes.ELEMENT) {
return extractElementTextChecked(node, processingNodeName);
return extractElementTextChecked(node, processingNodeName, id);
} else {
logger.error(`Unexpected ${node.tag} extracting text of ${processingNodeName} ${node.loc.start.line}`);
logger.error(`Unexpected ${node.tag} extracting text of ${processingNodeName} ${id}:${node.loc.start.line}`);
return null;
}
case NodeTypes.TEXT:
@ -217,7 +217,7 @@ function extractElementText2Inner(node: TemplateChildNode, processingNodeName: s
case NodeTypes.IF_BRANCH:
case NodeTypes.FOR:
case NodeTypes.TEXT_CALL:
logger.error(`Unexpected controlflow element extracting text of ${processingNodeName} ${node.loc.start.line}`);
logger.error(`Unexpected controlflow element extracting text of ${processingNodeName} ${id}:${node.loc.start.line}`);
return null;
}
}
@ -229,7 +229,7 @@ function extractElementText2Inner(node: TemplateChildNode, processingNodeName: s
/**
* SearchLabel/SearchKeyword/SearchIconを探して抽出する関数
*/
function extractSugarTags(nodes: TemplateChildNode[]): { label: string | null, keywords: string[], icon: string | null } {
function extractSugarTags(nodes: TemplateChildNode[], id: string): { label: string | null, keywords: string[], icon: string | null } {
let label: string | null | undefined = undefined;
let icon: string | null | undefined = undefined;
const keywords: string[] = [];
@ -242,35 +242,35 @@ function extractSugarTags(nodes: TemplateChildNode[]): { label: string | null, k
return false; // SearchMarkerはスキップ
case 'SearchLabel':
if (label !== undefined) {
logger.warn(`Duplicate SearchLabel found, ignoring the second one at ${node.loc.start.line}`);
logger.warn(`Duplicate SearchLabel found, ignoring the second one at ${id}:${node.loc.start.line}`);
break; // 2つ目のSearchLabelは無視
}
label = extractElementText(node);
label = extractElementText(node, id);
return;
case 'SearchKeyword':
const content = extractElementText(node);
const content = extractElementText(node, id);
if (content) {
keywords.push(content);
}
return;
case 'SearchIcon':
if (icon !== undefined) {
logger.warn(`Duplicate SearchIcon found, ignoring the second one at ${node.loc.start.line}`);
logger.warn(`Duplicate SearchIcon found, ignoring the second one at ${id}:${node.loc.start.line}`);
break; // 2つ目のSearchIconは無視
}
if (node.children.length !== 1) {
logger.error(`SearchIcon must have exactly one child at ${node.loc.start.line}`);
logger.error(`SearchIcon must have exactly one child at ${id}:${node.loc.start.line}`);
return;
}
const iconNode = node.children[0];
if (iconNode.type !== NodeTypes.ELEMENT) {
logger.error(`SearchIcon must have a child element at ${node.loc.start.line}`);
logger.error(`SearchIcon must have a child element at ${id}:${node.loc.start.line}`);
return;
}
icon = getStringProp(findAttribute(iconNode.props, 'class'));
icon = getStringProp(findAttribute(iconNode.props, 'class'), id);
return;
}
@ -282,7 +282,7 @@ function extractSugarTags(nodes: TemplateChildNode[]): { label: string | null, k
return { label: label ?? null, keywords, icon: icon ?? null };
}
function getStringProp(attr: AttributeNode | DirectiveNode | null): string | null {
function getStringProp(attr: AttributeNode | DirectiveNode | null, id: string): string | null {
switch (attr?.type) {
case null:
case undefined:
@ -294,27 +294,27 @@ function getStringProp(attr: AttributeNode | DirectiveNode | null): string | nul
if (attr.exp.type === NodeTypes.COMPOUND_EXPRESSION) throw new Error('Unexpected COMPOUND_EXPRESSION');
const value = evalExpression(attr.exp.content ?? '');
if (typeof value !== 'string') {
logger.error(`Expected string value, got ${typeof value} at line ${attr.loc.start.line}`);
logger.error(`Expected string value, got ${typeof value} at ${id}:${attr.loc.start.line}`);
return null;
}
return value;
}
}
function getStringArrayProp(attr: AttributeNode | DirectiveNode | null): string[] | null {
function getStringArrayProp(attr: AttributeNode | DirectiveNode | null, id: string): string[] | null {
switch (attr?.type) {
case null:
case undefined:
return null;
case NodeTypes.ATTRIBUTE:
logger.error(`Expected directive, got attribute at line ${attr.loc.start.line}`);
logger.error(`Expected directive, got attribute at ${id}:${attr.loc.start.line}`);
return null;
case NodeTypes.DIRECTIVE:
if (attr.exp == null) return null;
if (attr.exp.type === NodeTypes.COMPOUND_EXPRESSION) throw new Error('Unexpected COMPOUND_EXPRESSION');
const value = evalExpression(attr.exp.content ?? '');
if (!Array.isArray(value) || !value.every(x => typeof x === 'string')) {
logger.error(`Expected string array value, got ${typeof value} at line ${attr.loc.start.line}`);
logger.error(`Expected string array value, got ${typeof value} at ${id}:${attr.loc.start.line}`);
return null;
}
return value;
@ -354,11 +354,11 @@ function extractUsageInfoFromTemplateAst(
};
// バインドプロパティを取得
const path = getStringProp(findAttribute(node.props, 'path'))
const icon = getStringProp(findAttribute(node.props, 'icon'))
const label = getStringProp(findAttribute(node.props, 'label'))
const inlining = getStringArrayProp(findAttribute(node.props, 'inlining'))
const keywords = getStringArrayProp(findAttribute(node.props, 'keywords'))
const path = getStringProp(findAttribute(node.props, 'path'), id)
const icon = getStringProp(findAttribute(node.props, 'icon'), id)
const label = getStringProp(findAttribute(node.props, 'label'), id)
const inlining = getStringArrayProp(findAttribute(node.props, 'inlining'), id)
const keywords = getStringArrayProp(findAttribute(node.props, 'keywords'), id)
if (path) markerInfo.path = path;
if (icon) markerInfo.icon = icon;
@ -373,7 +373,7 @@ function extractUsageInfoFromTemplateAst(
// SearchLabelとSearchKeywordを抽出 (AST全体を探索)
{
const extracted = extractSugarTags(node.children);
const extracted = extractSugarTags(node.children, id);
if (extracted.label && markerInfo.label) logger.warn(`Duplicate label found for ${markerId} at ${id}:${node.loc.start.line}`);
if (extracted.icon && markerInfo.icon) logger.warn(`Duplicate icon found for ${markerId} at ${id}:${node.loc.start.line}`);
markerInfo.label = extracted.label ?? markerInfo.label ?? '';
@ -585,7 +585,7 @@ export class MarkerIdAssigner {
}
// AST で :children 属性が検出された場合、それを更新
const childrenValue = getStringArrayProp(childrenProp);
const childrenValue = getStringArrayProp(childrenProp, id);
if (childrenValue == null) continue;
const newValue: string[] = [...childrenValue];
@ -740,7 +740,7 @@ export function pluginCreateSearchIndexVirtualModule(options: Options, asigner:
this.addWatchFile(searchIndexFilePath);
const code = await asigner.getOrLoad(searchIndexFilePath);
return generateJavaScriptCode(collectFileMarkers(id, code));
return generateJavaScriptCode(collectFileMarkers(searchIndexFilePath, code));
}
return null;
},

View File

@ -266,18 +266,20 @@ export class Nirax<DEF extends RouteDef[]> extends EventEmitter<RouterEvents> {
throw new Error('no route found for: ' + fullPath);
}
if ('redirect' in res.route) {
let redirectPath: string;
if (typeof res.route.redirect === 'function') {
redirectPath = res.route.redirect(res.props);
} else {
redirectPath = res.route.redirect + (res._parsedRoute.queryString ? '?' + res._parsedRoute.queryString : '') + (res._parsedRoute.hash ? '#' + res._parsedRoute.hash : '');
for (let current: PathResolvedResult | undefined = res; current; current = current.child) {
if ('redirect' in current.route) {
let redirectPath: string;
if (typeof current.route.redirect === 'function') {
redirectPath = current.route.redirect(current.props);
} else {
redirectPath = current.route.redirect + (current._parsedRoute.queryString ? '?' + current._parsedRoute.queryString : '') + (current._parsedRoute.hash ? '#' + current._parsedRoute.hash : '');
}
if (_DEV_) console.log('Redirecting to: ', redirectPath);
if (_redirected && this.redirectCount++ > 10) {
throw new Error('redirect loop detected');
}
return this.navigate(redirectPath, emitChange, true);
}
if (_DEV_) console.log('Redirecting to: ', redirectPath);
if (_redirected && this.redirectCount++ > 10) {
throw new Error('redirect loop detected');
}
return this.navigate(redirectPath, emitChange, true);
}
if (res.route.loginRequired && !this.isLoggedIn) {

View File

@ -103,7 +103,6 @@ function removeItem(index: number) {
async function save() {
prefer.commit('menu', items.value.map(x => x.type));
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
}
function reset() {

View File

@ -4,64 +4,66 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/bell_3d.png" color="#ffff00">
<SearchKeyword>{{ i18n.ts._settings.notificationsBanner }}</SearchKeyword>
</MkFeatureBanner>
<SearchMarker path="/settings/notifications" :label="i18n.ts.notifications" :keywords="['notifications']" icon="ti ti-bell">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/bell_3d.png" color="#ffff00">
<SearchKeyword>{{ i18n.ts._settings.notificationsBanner }}</SearchKeyword>
</MkFeatureBanner>
<FormSection first>
<template #label>{{ i18n.ts.notificationRecieveConfig }}</template>
<div class="_gaps_s">
<MkFolder v-for="type in notificationTypes.filter(x => !nonConfigurableNotificationTypes.includes(x))" :key="type">
<template #label>{{ i18n.ts._notification._types[type] }}</template>
<template #suffix>
{{
$i.notificationRecieveConfig[type]?.type === 'never' ? i18n.ts.none :
$i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following :
$i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers :
$i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow :
$i.notificationRecieveConfig[type]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower :
$i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList :
i18n.ts.all
}}
</template>
<FormSection first>
<template #label>{{ i18n.ts.notificationRecieveConfig }}</template>
<div class="_gaps_s">
<MkFolder v-for="type in notificationTypes.filter(x => !nonConfigurableNotificationTypes.includes(x))" :key="type">
<template #label>{{ i18n.ts._notification._types[type] }}</template>
<template #suffix>
{{
$i.notificationRecieveConfig[type]?.type === 'never' ? i18n.ts.none :
$i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following :
$i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers :
$i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow :
$i.notificationRecieveConfig[type]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower :
$i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList :
i18n.ts.all
}}
</template>
<XNotificationConfig
:userLists="userLists"
:value="$i.notificationRecieveConfig[type] ?? { type: 'all' }"
:configurableTypes="onlyOnOrOffNotificationTypes.includes(type) ? ['all', 'never'] : undefined"
@update="(res) => updateReceiveConfig(type, res)"
/>
</MkFolder>
</div>
</FormSection>
<FormSection>
<div class="_gaps_m">
<FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
</div>
</FormSection>
<FormSection>
<div class="_gaps_m">
<FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
<FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink>
</div>
</FormSection>
<FormSection>
<template #label>{{ i18n.ts.pushNotification }}</template>
<XNotificationConfig
:userLists="userLists"
:value="$i.notificationRecieveConfig[type] ?? { type: 'all' }"
:configurableTypes="onlyOnOrOffNotificationTypes.includes(type) ? ['all', 'never'] : undefined"
@update="(res) => updateReceiveConfig(type, res)"
/>
</MkFolder>
</div>
</FormSection>
<FormSection>
<div class="_gaps_m">
<FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
</div>
</FormSection>
<FormSection>
<div class="_gaps_m">
<FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
<FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink>
</div>
</FormSection>
<FormSection>
<template #label>{{ i18n.ts.pushNotification }}</template>
<div class="_gaps_m">
<MkPushNotificationAllowButton ref="allowButton"/>
<MkSwitch :disabled="!pushRegistrationInServer" :modelValue="sendReadMessage" @update:modelValue="onChangeSendReadMessage">
<template #label>{{ i18n.ts.sendPushNotificationReadMessage }}</template>
<template #caption>
<I18n :src="i18n.ts.sendPushNotificationReadMessageCaption">
<template #emptyPushNotificationMessage>{{ i18n.ts._notification.emptyPushNotificationMessage }}</template>
</I18n>
</template>
</MkSwitch>
</div>
</FormSection>
</div>
<div class="_gaps_m">
<MkPushNotificationAllowButton ref="allowButton"/>
<MkSwitch :disabled="!pushRegistrationInServer" :modelValue="sendReadMessage" @update:modelValue="onChangeSendReadMessage">
<template #label>{{ i18n.ts.sendPushNotificationReadMessage }}</template>
<template #caption>
<I18n :src="i18n.ts.sendPushNotificationReadMessageCaption">
<template #emptyPushNotificationMessage>{{ i18n.ts._notification.emptyPushNotificationMessage }}</template>
</I18n>
</template>
</MkSwitch>
</div>
</FormSection>
</div>
</SearchMarker>
</template>
<script lang="ts" setup>

View File

@ -33,16 +33,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
</MkKeyValue>
<MkFolder>
<template #icon><i class="ti ti-badges"></i></template>
<template #label><SearchLabel>{{ i18n.ts._role.policies }}</SearchLabel></template>
<SearchMarker :keywords="['role', 'policy']">
<MkFolder>
<template #icon><i class="ti ti-badges"></i></template>
<template #label><SearchLabel>{{ i18n.ts._role.policies }}</SearchLabel></template>
<div class="_gaps_s">
<div v-for="policy in Object.keys($i.policies)" :key="policy">
{{ policy }} ... {{ $i.policies[policy] }}
<div class="_gaps_s">
<div v-for="policy in Object.keys($i.policies)" :key="policy">
{{ policy }} ... {{ $i.policies[policy] }}
</div>
</div>
</div>
</MkFolder>
</MkFolder>
</SearchMarker>
</div>
</MkFolder>
</SearchMarker>

View File

@ -540,7 +540,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="useBlurEffect">
<MkSwitch v-model="useBlurEffect">
<template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template>
<template #caption><SearchLabel>{{ i18n.ts.turnOffToImprovePerformance }}</SearchLabel></template>
<template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@ -549,7 +549,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="useBlurEffectForModal">
<MkSwitch v-model="useBlurEffectForModal">
<template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template>
<template #caption><SearchLabel>{{ i18n.ts.turnOffToImprovePerformance }}</SearchLabel></template>
<template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@ -558,7 +558,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPreferenceContainer k="useStickyIcons">
<MkSwitch v-model="useStickyIcons">
<template #label><SearchLabel>{{ i18n.ts._settings.useStickyIcons }}</SearchLabel></template>
<template #caption><SearchLabel>{{ i18n.ts.turnOffToImprovePerformance }}</SearchLabel></template>
<template #caption><SearchKeyword>{{ i18n.ts.turnOffToImprovePerformance }}</SearchKeyword></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>

View File

@ -19,8 +19,10 @@ export async function signout() {
localStorage.clear();
defaultMemoryStorage.clear();
const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => {
indexedDB.deleteDatabase(name);
const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise<void>((res, rej) => {
const delidb = indexedDB.deleteDatabase(name);
delidb.onsuccess = () => res();
delidb.onerror = e => rej(e);
}));
await Promise.all(idbPromises);

View File

@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkA :class="$style.item" :activeClass="$style.active" to="/" exact>
<i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span>
</MkA>
<template v-for="item in menu">
<template v-for="item in prefer.r.menu.value">
<div v-if="item === '-'" :class="$style.divider"></div>
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]" :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span>
@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, defineAsyncComponent, toRef } from 'vue';
import { computed, defineAsyncComponent } from 'vue';
import { openInstanceMenu } from './common.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
@ -59,10 +59,9 @@ import { instance } from '@/instance.js';
import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
import { $i } from '@/i.js';
const menu = toRef(prefer.s, 'menu');
const otherMenuItemIndicated = computed(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;
if (prefer.r.menu.value.includes(def)) continue;
if (navbarItemDef[def].indicated) return true;
}
return false;

View File

@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact>
<i :class="$style.itemIcon" class="ti ti-home ti-fw" style="viewTransitionName: navbar-homeIcon;"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span>
</MkA>
<template v-for="item in menu">
<template v-for="item in prefer.r.menu.value">
<div v-if="item === '-'" :class="$style.divider"></div>
<component
:is="navbarItemDef[item].to ? 'MkA' : 'button'"
@ -120,10 +120,9 @@ const iconOnly = computed(() => {
return forceIconOnly.value || (store.r.menuDisplay.value === 'sideIcon');
});
const menu = computed(() => prefer.s.menu);
const otherMenuItemIndicated = computed(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;
if (prefer.r.menu.value.includes(def)) continue;
if (navbarItemDef[def].indicated) return true;
}
return false;

View File

@ -65,10 +65,11 @@ const hyphens = [
];
const hyphensCodePoints = hyphens.map(code => `\\u{${code.toString(16).padStart(4, '0')}}`);
const hyphensRegex = new RegExp(`[${hyphensCodePoints.join('')}]`, 'ug');
/** ハイフンを統一(ローマ字半角入力時に`ー`と`-`が判定できない問題の調整) */
export function normalizeHyphens(str: string) {
return str.replace(new RegExp(`[${hyphensCodePoints.join('')}]`, 'ug'), '\u002d');
return str.replace(hyphensRegex, '\u002d');
}
/**

View File

@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2025.4.0-rc.4",
"version": "2025.4.0",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",