Compare commits

...

36 Commits

Author SHA1 Message Date
b58dd8c704 12.3.0 2020-02-08 18:37:00 +09:00
09c96286f9 🎨 2020-02-08 18:35:42 +09:00
f2d2089c21 New Crowdin translations (#5849)
* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

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

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

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

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

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

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

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-02-08 18:03:56 +09:00
79c366d1f2 Update container.vue 2020-02-08 18:02:28 +09:00
c97ce5255f i18n 2020-02-08 17:52:36 +09:00
1fb66254a4 🎨 2020-02-08 17:49:54 +09:00
2d74f0507b 🎨 2020-02-08 17:36:23 +09:00
9c06544c46 wip 2020-02-08 16:51:27 +09:00
641dad586f 🎨 2020-02-08 15:50:04 +09:00
016144b960 wip 2020-02-08 15:47:16 +09:00
4d6c8efe44 wip 2020-02-08 15:17:35 +09:00
860a7d1eeb wip 2020-02-08 15:11:12 +09:00
2389857be8 🎨 2020-02-08 14:31:51 +09:00
18458f418f [wip] フォルダー名の変更と削除機能を実装 (#5874)
* フォルダーの削除機能を実装

* フォルダ名の変更を実装

* ダイアログの削除(v11準拠)とエラーメッセージを表示するように

* ダイアログのテキストのkeypathを変更
2020-02-08 13:09:38 +09:00
e812d054bc Clean up 2020-02-08 13:06:42 +09:00
44d2c0195a 翻訳の抜けを修正 (#5875)
* missing translation

* fix

* fix

* ✌️
2020-02-08 13:06:09 +09:00
42b4949b7f Update app.vue 2020-02-08 12:49:22 +09:00
d915ae0807 Resolve #5879 2020-02-08 12:46:14 +09:00
8eec8ea35f Fix #5844 2020-02-08 12:13:15 +09:00
023e0ba7aa Revert "Better page transition"
This reverts commit d0d3b70c73.
2020-02-08 11:54:39 +09:00
d0d3b70c73 Better page transition 2020-02-08 11:33:32 +09:00
a509045b25 Fix bug 2020-02-08 11:33:25 +09:00
7be6501571 EmojiReaction => EmojiReact (#5877) 2020-02-07 21:37:24 +09:00
bb4c35d481 fix #5854 2020-02-07 20:25:49 +09:00
47ea84957d docs 2020-02-07 19:45:15 +09:00
fc76f7874e Docs 2020-02-07 19:43:37 +09:00
77a778acf1 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-07 19:15:12 +09:00
ff059d1268 wip docs 2020-02-07 19:15:08 +09:00
53bb5012b9 非ログイン時にエラーが発生していたのを修正 (#5872) 2020-02-07 11:39:44 +09:00
09a3a977d7 👀 (#5869) 2020-02-07 09:43:26 +09:00
04db5944d1 Fix #5854 2020-02-07 02:38:02 +09:00
9c97bb431c markdown 2020-02-07 01:20:04 +09:00
38215f2cf9 Fix wrong url on list page (#5865) 2020-02-07 00:19:40 +09:00
01e7a01daf Clean up 2020-02-06 23:27:47 +09:00
c2a8e29ef9 Not found page 2020-02-06 23:20:59 +09:00
15a41e31b0 Fix #5856 2020-02-06 23:12:27 +09:00
51 changed files with 1544 additions and 849 deletions

View File

@ -1,6 +1,21 @@
ChangeLog ChangeLog
========= =========
12.3.0 (2020/02/08)
--------------------
### ✨Improvements
* グループ実装
* /share実装
* 指定したURLのページが見つからなかった時のページを実装
* ドキュメント実装
* AP: EmojiReaction => EmojiReact
### 🐛Fixes
* 画面の縦の幅が狭いとメニューが一部隠れる問題を修正
* リストの設定ページが開けなかった問題を修正
* drive-file-thumbnailのicon-subがおかしい問題を修正
* ドライブのフォルダー名の変更と削除ができない問題を修正
12.2.0 (2020/02/06) 12.2.0 (2020/02/06)
-------------------- --------------------
### ✨Improvements ### ✨Improvements

View File

@ -74,9 +74,17 @@ gulp.task('copy:client', () =>
.pipe(gulp.dest('./built/client/assets/')) .pipe(gulp.dest('./built/client/assets/'))
); );
gulp.task('copy:docs', () =>
gulp.src([
'./src/docs/**/*',
])
.pipe(gulp.dest('./built/client/assets/docs/'))
);
gulp.task('build:client', gulp.parallel( gulp.task('build:client', gulp.parallel(
'build:client:styles', 'build:client:styles',
'copy:client' 'copy:client',
'copy:docs'
)); ));
gulp.task('build', gulp.parallel( gulp.task('build', gulp.parallel(

View File

@ -65,12 +65,13 @@ import: "Import"
export: "Export" export: "Export"
files: "Files" files: "Files"
download: "Download" download: "Download"
driveFileDeleteConfirm: "Are you sure that you want to delete file \"{name}\"? Notes with this file attached will also be deleted." driveFileDeleteConfirm: "Are you sure you want to delete the file \"{name}\"? Notes with this file attached will also be deleted."
unfollowConfirm: "Are you sure that you want to unfollow {name}?" unfollowConfirm: "Are you sure that you want to unfollow {name}?"
exportRequested: "You have requested an export. This may take a while. After the export is complete, the resulting file will be added to the drive." exportRequested: "You have requested an export. This may take a while. After the export is complete, the resulting file will be added to the drive."
importRequested: "You requested an import. This may take a while." importRequested: "You requested an import. This may take a while."
lists: "Lists" lists: "Lists"
noLists: "You don't have any lists" noLists: "You don't have any lists"
note: "Notes"
notes: "Notes" notes: "Notes"
following: "Following" following: "Following"
followers: "Followers" followers: "Followers"
@ -80,8 +81,6 @@ manageLists: "Manage lists"
error: "Something happened :(" error: "Something happened :("
retry: "Retry" retry: "Retry"
enterListName: "List name" enterListName: "List name"
renameList: "Rename list"
deleteList: "Delete list"
privacy: "Privacy" privacy: "Privacy"
makeFollowManuallyApprove: "Follow requests require approval" makeFollowManuallyApprove: "Follow requests require approval"
defaultNoteVisibility: "Default visibility" defaultNoteVisibility: "Default visibility"
@ -92,8 +91,9 @@ unfollow: "Unfollow"
followRequestPending: "Pending follow request" followRequestPending: "Pending follow request"
enterEmoji: "Enter an emoji" enterEmoji: "Enter an emoji"
renote: "Renote" renote: "Renote"
unrenote: "Unrenote"
quote: "Quote" quote: "Quote"
pinnedNote: "Pinned note(s)" pinnedNote: "Pinned note"
you: "You" you: "You"
clickToShow: "Click to show" clickToShow: "Click to show"
sensitive: "NSFW" sensitive: "NSFW"
@ -177,7 +177,7 @@ mutedUsers: "Muted users"
blockedUsers: "Blocked users" blockedUsers: "Blocked users"
noUsers: "There are no users" noUsers: "There are no users"
editProfile: "Edit profile" editProfile: "Edit profile"
noteDeleteConfirm: "Are you sure that you want to delete this note?" noteDeleteConfirm: "Are you sure you want to delete this note?"
pinLimitExceeded: "You cannot pin any more notes." pinLimitExceeded: "You cannot pin any more notes."
intro: "Installation of Misskey has been finished! Please create an admin user." intro: "Installation of Misskey has been finished! Please create an admin user."
done: "Done" done: "Done"
@ -302,8 +302,8 @@ name: "Name"
antennaSource: "Antenna source" antennaSource: "Antenna source"
antennaKeywords: "Antenna keywords" antennaKeywords: "Antenna keywords"
antennaKeywordsDescription: "Separate with spaces for AND condition. Separate with line breaks for OR." antennaKeywordsDescription: "Separate with spaces for AND condition. Separate with line breaks for OR."
notifyAntenna: "Get notification for new notes" notifyAntenna: "Notify newer notes"
withFileAntenna: "Notes with attachment only" withFileAntenna: "Filter only notes with file attached"
serviceworker: "ServiceWorker" serviceworker: "ServiceWorker"
enableServiceworker: "Enable ServiceWorker" enableServiceworker: "Enable ServiceWorker"
antennaUsersDescription: "List one username per line" antennaUsersDescription: "List one username per line"
@ -311,7 +311,7 @@ caseSensitive: "Case sensitive"
withReplies: "Include replies" withReplies: "Include replies"
connectedTo: "Following account(s) are connected" connectedTo: "Following account(s) are connected"
notesAndReplies: "Notes and replies" notesAndReplies: "Notes and replies"
withFiles: "Attach file(s)" withFiles: "Media"
silence: "Silence" silence: "Silence"
silenceConfirm: "Are you sure that you want to silence this user?" silenceConfirm: "Are you sure that you want to silence this user?"
unsilenceConfirm: "Are you sure that you want to undo silence of this user?" unsilenceConfirm: "Are you sure that you want to undo silence of this user?"
@ -346,13 +346,37 @@ resetPassword: "Reste password"
newPasswordIs: "The new password is \"{password}\"" newPasswordIs: "The new password is \"{password}\""
post: "Notes" post: "Notes"
posted: "Posted!" posted: "Posted!"
autoReloadWhenDisconnected: "Auto reload when disconnected with server"
autoNoteWatch: "Watch note automatically"
reduceUiAnimation: "Reduce animations of User Interface"
share: "Share"
notFound: "Not found"
notFoundDescription: "There was no page corresponding to the specified URL."
uploadFolder: "Default Upload location"
cacheClear: "Clear cache"
markAsReadAllNotifications: "Mark all notifications as read"
markAsReadAllUnreadNotes: "Mark all notes as read"
markAsReadAllTalkMessages: "Mark all conversations as read"
help: "Help"
inputMessageHere: "Enter message here"
close: "Close"
groups: "Groups"
createGroup: "Create a group"
ownedGroups: "Owned Groups"
joinedGroups: "Membership in groups"
invites: "Invite"
groupName: "Group name"
members: "Members"
transfer: "Transfer"
_2fa: _2fa:
alreadyRegistered: "You have already registered 2-factor authentication device."
registerDevice: "Register a new device" registerDevice: "Register a new device"
registerKey: "Register a new Security Key"
step1: "First, install an authentication app (such as {a} or {b}) on your device." step1: "First, install an authentication app (such as {a} or {b}) on your device."
step2: "Then, scan the QR code on the screen." step2: "Then, scan the QR code on the screen."
step3: "Enter the token provided by your app to finish setup." step3: "Enter the token provided by your app to finish setup."
step4: "From now, any login attempt will ask for your login token." step4: "From now, any login attempt will ask for your login token."
securityKeyInfo: "You can set up a hardware security key (must support FIDO2) to further secure your login." securityKeyInfo: "You can setup WebAuthN authentication to further secure the log-in process with not only hardware security key which supports FIDO2, but also fingerprint or PIN authentication on your device."
_permissions: _permissions:
"read:account": "View your account information" "read:account": "View your account information"
"write:account": "Edit your account information" "write:account": "Edit your account information"
@ -386,7 +410,7 @@ _auth:
_antennaSources: _antennaSources:
all: "All notes" all: "All notes"
homeTimeline: "Notes from following users" homeTimeline: "Notes from following users"
users: "Notes from specific user(s)" users: "Notes from specific users"
userList: "Notes from specific list" userList: "Notes from specific list"
_weekday: _weekday:
sunday: "Sunday" sunday: "Sunday"
@ -403,6 +427,7 @@ _widgets:
calendar: "Calendar" calendar: "Calendar"
trends: "Trending" trends: "Trending"
clock: "Clock" clock: "Clock"
rss: "RSS reader"
_cw: _cw:
hide: "Hide" hide: "Hide"
show: "Load more" show: "Load more"
@ -441,8 +466,8 @@ _visibility:
specified: "Direct" specified: "Direct"
specifiedDescription: "Post to specified users only" specifiedDescription: "Post to specified users only"
_postForm: _postForm:
replyPlaceholder: "Reply to this note" replyPlaceholder: "Reply to this note..."
quotePlaceholder: "Quote this post..." quotePlaceholder: "Quote this note..."
_placeholders: _placeholders:
a: "What are you up to?" a: "What are you up to?"
b: "What's happening around you?" b: "What's happening around you?"
@ -471,7 +496,7 @@ _charts:
usersTotal: "Total # of users" usersTotal: "Total # of users"
activeUsers: "Active users" activeUsers: "Active users"
notesIncDec: "Difference in # of notes" notesIncDec: "Difference in # of notes"
localNotesIncDec: "Total # of local notes" localNotesIncDec: "Difference in # of local notes"
remoteNotesIncDec: "Difference in # of remote notes" remoteNotesIncDec: "Difference in # of remote notes"
notesTotal: "Total # of notes" notesTotal: "Total # of notes"
filesIncDec: "Difference in # of files" filesIncDec: "Difference in # of files"

View File

@ -71,6 +71,7 @@ exportRequested: "Se ha solicitado la exportación. Puede tomar un tiempo. Cuand
importRequested: "Se ha solicitado la importación. Puede tomar un tiempo." importRequested: "Se ha solicitado la importación. Puede tomar un tiempo."
lists: "Listas" lists: "Listas"
noLists: "No tiene listas" noLists: "No tiene listas"
note: "Notas"
notes: "Notas" notes: "Notas"
following: "Sigue" following: "Sigue"
followers: "Seguidores" followers: "Seguidores"
@ -80,8 +81,6 @@ manageLists: "Administrar listas"
error: "Ocurrió un problema" error: "Ocurrió un problema"
retry: "Reintentar" retry: "Reintentar"
enterListName: "Ingrese nombre de lista" enterListName: "Ingrese nombre de lista"
renameList: "Renombrar lista"
deleteList: "Borrar lista"
privacy: "Privacidad" privacy: "Privacidad"
makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento" makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento"
defaultNoteVisibility: "Visibilidad por defecto" defaultNoteVisibility: "Visibilidad por defecto"
@ -92,6 +91,7 @@ unfollow: "Dejar de seguir"
followRequestPending: "Solicitudes de seguimiento pendientes" followRequestPending: "Solicitudes de seguimiento pendientes"
enterEmoji: "Ingresar emojis" enterEmoji: "Ingresar emojis"
renote: "Renotar" renote: "Renotar"
unrenote: "Quitar renota"
quote: "Citar" quote: "Citar"
pinnedNote: "Nota fijada" pinnedNote: "Nota fijada"
you: "Tú" you: "Tú"
@ -302,8 +302,8 @@ name: "Nombre"
antennaSource: "Origen de la antena" antennaSource: "Origen de la antena"
antennaKeywords: "Palabras clave de la antena" antennaKeywords: "Palabras clave de la antena"
antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar con una linea nueva es una declaración OR" antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar con una linea nueva es una declaración OR"
notifyAntenna: "Notificar notas nuevas" notifyAntenna: "Notificar nueva nota"
withFileAntenna: "Solo notas con adjuntos" withFileAntenna: "Sólo notas con archivos adjuntados"
serviceworker: "ServiceWorker" serviceworker: "ServiceWorker"
enableServiceworker: "Activar ServiceWorker" enableServiceworker: "Activar ServiceWorker"
antennaUsersDescription: "Elegir nombres de usuarios separados por una linea nueva" antennaUsersDescription: "Elegir nombres de usuarios separados por una linea nueva"
@ -346,13 +346,20 @@ resetPassword: "Resetear contraseña"
newPasswordIs: "La nueva contraseña es \"{password}\"" newPasswordIs: "La nueva contraseña es \"{password}\""
post: "Nota" post: "Nota"
posted: "Posteado" posted: "Posteado"
autoReloadWhenDisconnected: "Recargar automáticamente cuando el servidor está desconectado"
autoNoteWatch: "Ver nota automáticamente"
reduceUiAnimation: "Reducir la animación de la UI"
share: "Compartir"
notFound: "No se encuentra"
notFoundDescription: "No se encontró la página correspondiente a la URL elegida"
help: "Ayuda"
invites: "Invitar"
_2fa: _2fa:
registerDevice: "Registrar dispositivo" registerDevice: "Registrar dispositivo"
step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra." step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra."
step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla." step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla."
step3: "Para terminar, ingrese el token mostrado en la aplicación." step3: "Para terminar, ingrese el token mostrado en la aplicación."
step4: "Ahora cuando inicie sesión, ingrese el mismo token" step4: "Ahora cuando inicie sesión, ingrese el mismo token"
securityKeyInfo: "Se puede configurar para que se inicie sesión usando una clave de seguridad de hardware que soporte FIDO2."
_permissions: _permissions:
"read:account": "Ver información de la cuenta" "read:account": "Ver información de la cuenta"
"write:account": "Editar información de la cuenta" "write:account": "Editar información de la cuenta"
@ -368,7 +375,7 @@ _permissions:
"write:messaging": "Administrar coversación" "write:messaging": "Administrar coversación"
"read:mutes": "Ver usuarios silenciados" "read:mutes": "Ver usuarios silenciados"
"write:mutes": "Administrar usuarios silenciados" "write:mutes": "Administrar usuarios silenciados"
"write:notes": "Crear o borrar notas" "write:notes": "Crear/borrar notas"
"read:notifications": "Ver notificaciones" "read:notifications": "Ver notificaciones"
"write:notifications": "Administrar notificaciones" "write:notifications": "Administrar notificaciones"
"read:reactions": "Ver reacciones" "read:reactions": "Ver reacciones"
@ -386,8 +393,8 @@ _auth:
_antennaSources: _antennaSources:
all: "Todas las notas" all: "Todas las notas"
homeTimeline: "Notas de los usuarios que sigues" homeTimeline: "Notas de los usuarios que sigues"
users: "Solo notas de determinados usuarios" users: "Notas de un usuario o varios"
userList: "Solo notas de usuarios de una lista" userList: "Notas de los usuarios de una lista"
_weekday: _weekday:
sunday: "Domingo" sunday: "Domingo"
monday: "Lunes" monday: "Lunes"
@ -403,6 +410,7 @@ _widgets:
calendar: "Calendario" calendar: "Calendario"
trends: "Tendencias" trends: "Tendencias"
clock: "Reloj" clock: "Reloj"
rss: "Lector RSS"
_cw: _cw:
hide: "Ocultar" hide: "Ocultar"
show: "Ver más" show: "Ver más"
@ -470,9 +478,9 @@ _charts:
usersIncDec: "Variación de usuarios" usersIncDec: "Variación de usuarios"
usersTotal: "Total de usuarios" usersTotal: "Total de usuarios"
activeUsers: "Cantidad de usuarios activos" activeUsers: "Cantidad de usuarios activos"
notesIncDec: "Variación de cantidad de notas" notesIncDec: "Variación de la cantidad de notas"
localNotesIncDec: "Variación de cantidad de notas locales" localNotesIncDec: "Variación de la cantidad de notas locales"
remoteNotesIncDec: "Variación de cantidad de notas remotas" remoteNotesIncDec: "Variación de la cantidad de notas remotas"
notesTotal: "Total de notas" notesTotal: "Total de notas"
filesIncDec: "Variación de cantidad de archivos" filesIncDec: "Variación de cantidad de archivos"
filesTotal: "Total de archivos" filesTotal: "Total de archivos"
@ -482,8 +490,8 @@ _instanceCharts:
requests: "Pedidos" requests: "Pedidos"
users: "Variación de usuarios" users: "Variación de usuarios"
usersTotal: "Total de usuarios" usersTotal: "Total de usuarios"
notes: "Variación de cantidad de notas" notes: "Variación de la cantidad de notas"
notesTotal: "Total de notas" notesTotal: "Estimación de notas"
ff: "Variación de cantidad de seguidos/seguidores" ff: "Variación de cantidad de seguidos/seguidores"
ffTotal: "Total de seguidos/seguidores" ffTotal: "Total de seguidos/seguidores"
cacheSize: "Variación del tamaño de la caché" cacheSize: "Variación del tamaño de la caché"

View File

@ -28,7 +28,7 @@ gotIt: "わかった"
cancel: "キャンセル" cancel: "キャンセル"
enterUsername: "ユーザー名を入力" enterUsername: "ユーザー名を入力"
renotedBy: "{user}がRenote" renotedBy: "{user}がRenote"
noNotes: ノートはありません" noNotes: "ノートはありません"
noNotifications: "通知はありません" noNotifications: "通知はありません"
instance: "インスタンス" instance: "インスタンス"
settings: "設定" settings: "設定"
@ -72,6 +72,7 @@ exportRequested: "エクスポートをリクエストしました。これに
importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。" importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。"
lists: "リスト" lists: "リスト"
noLists: "リストはありません" noLists: "リストはありません"
note: "ノート"
notes: "ノート" notes: "ノート"
following: "フォロー" following: "フォロー"
followers: "フォロワー" followers: "フォロワー"
@ -81,8 +82,6 @@ manageLists: "リストの管理"
error: "問題が発生しました" error: "問題が発生しました"
retry: "再試行" retry: "再試行"
enterListName: "リスト名を入力" enterListName: "リスト名を入力"
renameList: "リスト名を変更"
deleteList: "リストを削除"
privacy: "プライバシー" privacy: "プライバシー"
makeFollowManuallyApprove: "フォローを承認制にする" makeFollowManuallyApprove: "フォローを承認制にする"
defaultNoteVisibility: "デフォルトの公開範囲" defaultNoteVisibility: "デフォルトの公開範囲"
@ -351,14 +350,35 @@ posted: "投稿しました"
autoReloadWhenDisconnected: "サーバー切断時に自動リロード" autoReloadWhenDisconnected: "サーバー切断時に自動リロード"
autoNoteWatch: "ノートの自動ウォッチ" autoNoteWatch: "ノートの自動ウォッチ"
reduceUiAnimation: "UIのアニメーションを減らす" reduceUiAnimation: "UIのアニメーションを減らす"
share: "共有"
notFound: "見つかりません"
notFoundDescription: "指定されたURLに該当するページはありませんでした。"
uploadFolder: "既定アップロード先"
cacheClear: "キャッシュを削除"
markAsReadAllNotifications: "すべての通知を既読にする"
markAsReadAllUnreadNotes: "すべての投稿を既読にする"
markAsReadAllTalkMessages: "すべてのトークを既読にする"
help: "ヘルプ"
inputMessageHere: "ここにメッセージを入力"
close: "閉じる"
groups: "グループ"
createGroup: "グループを作成"
ownedGroups: "所有グループ"
joinedGroups: "参加しているグループ"
invites: "招待"
groupName: "グループ名"
members: "メンバー"
transfer: "譲渡"
_2fa: _2fa:
alreadyRegistered: "既に設定は完了しています。"
registerDevice: "デバイスを登録" registerDevice: "デバイスを登録"
registerKey: "キーを登録"
step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。" step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。"
step2: "次に、表示されているQRコードをアプリでスキャンします。" step2: "次に、表示されているQRコードをアプリでスキャンします。"
step3: "アプリに表示されているトークンを入力して完了です。" step3: "アプリに表示されているトークンを入力して完了です。"
step4: "これからログインするときも、同じようにトークンを入力します。" step4: "これからログインするときも、同じようにトークンを入力します。"
securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキーを使用してログインするように設定できます。" securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキーもしくは端末の指紋認証やPINを使用してログインするように設定できます。"
_permissions: _permissions:
"read:account": "アカウントの情報を見る" "read:account": "アカウントの情報を見る"

View File

@ -26,8 +26,8 @@ ok: "OK"
gotIt: "알겠어요" gotIt: "알겠어요"
cancel: "취소" cancel: "취소"
enterUsername: "유저명 입력" enterUsername: "유저명 입력"
renotedBy: "{user}님이 리노트" renotedBy: "{user}님이 Renote"
noNotes: "게시물이 없습니다" noNotes: "노트가 없습니다"
noNotifications: "표시할 알림이 없습니다" noNotifications: "표시할 알림이 없습니다"
instance: "인스턴스" instance: "인스턴스"
settings: "설정" settings: "설정"
@ -71,6 +71,7 @@ exportRequested: "내보내기를 요청하였습니다. 이 작업은 시간이
importRequested: "가져오기를 요청하였습니다. 이 작업에는 시간이 걸릴 수 있습니다." importRequested: "가져오기를 요청하였습니다. 이 작업에는 시간이 걸릴 수 있습니다."
lists: "리스트" lists: "리스트"
noLists: "리스트가 없습니다" noLists: "리스트가 없습니다"
note: "노트"
notes: "노트" notes: "노트"
following: "팔로잉" following: "팔로잉"
followers: "팔로워" followers: "팔로워"
@ -80,8 +81,6 @@ manageLists: "리스트 관리"
error: "오류가 발생했습니다" error: "오류가 발생했습니다"
retry: "다시 시도" retry: "다시 시도"
enterListName: "리스트 이름을 입력" enterListName: "리스트 이름을 입력"
renameList: "리스트 이름 바꾸기"
deleteList: "리스트 삭제"
privacy: "프라이버시" privacy: "프라이버시"
makeFollowManuallyApprove: "팔로우를 수동으로 승인" makeFollowManuallyApprove: "팔로우를 수동으로 승인"
defaultNoteVisibility: "기본 공개 범위" defaultNoteVisibility: "기본 공개 범위"
@ -91,9 +90,10 @@ followRequests: "팔로우 요청"
unfollow: "팔로우 해제" unfollow: "팔로우 해제"
followRequestPending: "팔로우 허가 대기중" followRequestPending: "팔로우 허가 대기중"
enterEmoji: "이모지 입력" enterEmoji: "이모지 입력"
renote: "리노트" renote: "Renote"
unrenote: "Renote 취소"
quote: "인용" quote: "인용"
pinnedNote: "고정 노트" pinnedNote: "고정해놓은 노트"
you: "당신" you: "당신"
clickToShow: "클릭하여 보기" clickToShow: "클릭하여 보기"
sensitive: "열람주의" sensitive: "열람주의"
@ -257,8 +257,8 @@ banner: "배너"
nsfw: "열람주의" nsfw: "열람주의"
disconnectedFromServer: "서버와의 연결이 끊어졌습니다" disconnectedFromServer: "서버와의 연결이 끊어졌습니다"
reloadConfirm: "새로고침 하시겠습니까?" reloadConfirm: "새로고침 하시겠습니까?"
watch: "알림 받기" watch: "지켜보기"
unwatch: "알림 받기 해제" unwatch: "지켜보기 해제"
accept: "허가" accept: "허가"
reject: "거부" reject: "거부"
instanceName: "인스턴스 이름" instanceName: "인스턴스 이름"
@ -280,7 +280,7 @@ enableLocalTimeline: "로컬 타임라인 활성화"
enableGlobalTimeline: "글로벌 타임라인 활성화" enableGlobalTimeline: "글로벌 타임라인 활성화"
disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다." disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다."
registration: "등록" registration: "등록"
enableRegistration: "신규 사용자 등록을 활성화" enableRegistration: "신규 회원가입을 활성화"
invite: "초대" invite: "초대"
proxyRemoteFiles: "원격 파일 프록시" proxyRemoteFiles: "원격 파일 프록시"
proxyRemoteFilesDescription: "이 설정을 활성화할 경우, 저장되지 않았거나 저장용량 초과로 삭제된 원격 파일을 로컬에서 프록시하여 썸네일을 생성하게 됩니다. 서버의 스토리지에는 영향을 주지 않습니다." proxyRemoteFilesDescription: "이 설정을 활성화할 경우, 저장되지 않았거나 저장용량 초과로 삭제된 원격 파일을 로컬에서 프록시하여 썸네일을 생성하게 됩니다. 서버의 스토리지에는 영향을 주지 않습니다."
@ -302,8 +302,8 @@ name: "이름"
antennaSource: "받을 소스" antennaSource: "받을 소스"
antennaKeywords: "받을 키워드" antennaKeywords: "받을 키워드"
antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다" antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다"
notifyAntenna: "새로운 글이 게시될 때 알림 받기" notifyAntenna: "새로운 노트를 알림"
withFileAntenna: "파일이 첨부된 게시물만" withFileAntenna: "파일이 첨부된 노트만"
serviceworker: "ServiceWorker" serviceworker: "ServiceWorker"
enableServiceworker: "ServiceWorker 사용" enableServiceworker: "ServiceWorker 사용"
antennaUsersDescription: "유저명을 한 줄에 한 명씩 적습니다" antennaUsersDescription: "유저명을 한 줄에 한 명씩 적습니다"
@ -311,7 +311,7 @@ caseSensitive: "대소문자를 구분"
withReplies: "답글 포함" withReplies: "답글 포함"
connectedTo: "다음 계정에 연결되어 있습니다" connectedTo: "다음 계정에 연결되어 있습니다"
notesAndReplies: "글과 답글" notesAndReplies: "글과 답글"
withFiles: "파일 첨부" withFiles: "미디어"
silence: "사일런스" silence: "사일런스"
silenceConfirm: "이 계정을 사일런스로 설정하시겠습니까?" silenceConfirm: "이 계정을 사일런스로 설정하시겠습니까?"
unsilenceConfirm: "이 계정의 사일런스를 해제하시겠습니까?" unsilenceConfirm: "이 계정의 사일런스를 해제하시겠습니까?"
@ -346,13 +346,37 @@ resetPassword: "비밀번호 재설정"
newPasswordIs: "새로운 비밀번호는 \"{password}\" 입니다" newPasswordIs: "새로운 비밀번호는 \"{password}\" 입니다"
post: "작성" post: "작성"
posted: "게시하였습니다" posted: "게시하였습니다"
autoReloadWhenDisconnected: "서버와의 연결이 끊기면 자동 새로고침"
autoNoteWatch: "노트를 자동으로 지켜보기"
reduceUiAnimation: "UI의 애니메이션을 줄이기"
share: "공유"
notFound: "찾을 수 없습니다"
notFoundDescription: "지정한 URL에 해당하는 페이지가 존재하지 않습니다."
uploadFolder: "기본 업로드 위치"
cacheClear: "캐시 지우기"
markAsReadAllNotifications: "모든 알림을 읽은 상태로 표시"
markAsReadAllUnreadNotes: "모든 글을 읽은 상태로 표시"
markAsReadAllTalkMessages: "모든 대화를 읽은 상태로 표시"
help: "도움말"
inputMessageHere: "여기에 메시지를 입력하세요"
close: "닫기"
groups: "그룹"
createGroup: "그룹 만들기"
ownedGroups: "소유 그룹"
joinedGroups: "참여중인 그룹"
invites: "초대"
groupName: "그룹명"
members: "멤버"
transfer: "양도"
_2fa: _2fa:
alreadyRegistered: "이미 설정이 완료되었습니다."
registerDevice: "디바이스 등록" registerDevice: "디바이스 등록"
registerKey: "키를 등록"
step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다." step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다."
step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다." step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다."
step3: "앱에 표시된 토큰을 입력하시면 완료됩니다." step3: "앱에 표시된 토큰을 입력하시면 완료됩니다."
step4: "다음 로그인부터는 토큰을 입력해야 합니다." step4: "다음 로그인부터는 토큰을 입력해야 합니다."
securityKeyInfo: "FIDO2를 지원하는 하드웨어 보안 키를 사용하여 로그인하도록 설정할 수 있습니다." securityKeyInfo: "FIDO2를 지원하는 하드웨어 시큐리티 키 혹은 휴대전화의 지문인식이나 화면잠금 PIN을 이용해서 로그인하도록 설정할 수 있습니다."
_permissions: _permissions:
"read:account": "계정 정보 보기" "read:account": "계정 정보 보기"
"write:account": "계정 정보 변경" "write:account": "계정 정보 변경"
@ -368,7 +392,7 @@ _permissions:
"write:messaging": "대화 수정" "write:messaging": "대화 수정"
"read:mutes": "뮤트 보기" "read:mutes": "뮤트 보기"
"write:mutes": "뮤트 수정" "write:mutes": "뮤트 수정"
"write:notes": "노트 작성 삭제" "write:notes": "노트 작성하거나 삭제"
"read:notifications": "알림 보기" "read:notifications": "알림 보기"
"write:notifications": "알림 수정" "write:notifications": "알림 수정"
"read:reactions": "리액션 보기" "read:reactions": "리액션 보기"
@ -384,10 +408,10 @@ _auth:
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?" shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
permissionAsk: "이 앱은 다음의 권한을 요청합니다" permissionAsk: "이 앱은 다음의 권한을 요청합니다"
_antennaSources: _antennaSources:
all: "모든 게시물" all: "모든 노트"
homeTimeline: "팔로우중인 유저의 게시물" homeTimeline: "팔로우중인 유저의 노트"
users: "지정한 유저(들)의 게시물" users: "지정한 한 명 혹은 여러 명의 유저의 노트"
userList: "지정한 리스트에 속한 유저의 게시물" userList: "지정한 리스트에 속한 유저의 노트"
_weekday: _weekday:
sunday: "일요일" sunday: "일요일"
monday: "월요일" monday: "월요일"
@ -403,6 +427,7 @@ _widgets:
calendar: "달력" calendar: "달력"
trends: "트렌드" trends: "트렌드"
clock: "시계" clock: "시계"
rss: "RSS 리더"
_cw: _cw:
hide: "숨기기" hide: "숨기기"
show: "더 보기" show: "더 보기"
@ -441,8 +466,8 @@ _visibility:
specified: "다이렉트" specified: "다이렉트"
specifiedDescription: "지정한 유저에게만 공개" specifiedDescription: "지정한 유저에게만 공개"
_postForm: _postForm:
replyPlaceholder: "이 에 답글..." replyPlaceholder: "이 노트에 답글..."
quotePlaceholder: "이 글을 인용..." quotePlaceholder: "이 노트를 인용..."
_placeholders: _placeholders:
a: "지금 무엇을 하고 있나요?" a: "지금 무엇을 하고 있나요?"
b: "무슨 일이 일어나고 있나요?" b: "무슨 일이 일어나고 있나요?"
@ -459,7 +484,7 @@ _profile:
metadataLabel: "라벨" metadataLabel: "라벨"
metadataContent: "내용" metadataContent: "내용"
_exportOrImport: _exportOrImport:
allNotes: "모든 게시물" allNotes: "모든 노트"
followingList: "팔로잉" followingList: "팔로잉"
muteList: "뮤트" muteList: "뮤트"
blockingList: "차단" blockingList: "차단"
@ -467,12 +492,12 @@ _exportOrImport:
_charts: _charts:
federationInstancesIncDec: "연합 인스턴스 수 증감" federationInstancesIncDec: "연합 인스턴스 수 증감"
federationInstancesTotal: "총 연합 인스턴스 수" federationInstancesTotal: "총 연합 인스턴스 수"
usersIncDec: "유저 증감" usersIncDec: "유저 증감"
usersTotal: "유저 합계" usersTotal: "유저 합계"
activeUsers: "활성 유저 수" activeUsers: "활성 유저 수"
notesIncDec: "노트 수 증감" notesIncDec: "노트 수 증감"
localNotesIncDec: "로컬 노트 수 증감" localNotesIncDec: "로컬 노트 수 증감"
remoteNotesIncDec: "외부 노트 수 증감" remoteNotesIncDec: "리모트 노트 수 증감"
notesTotal: "총 노트 수" notesTotal: "총 노트 수"
filesIncDec: "파일 수 증감" filesIncDec: "파일 수 증감"
filesTotal: "총 파일 수" filesTotal: "총 파일 수"
@ -480,10 +505,10 @@ _charts:
storageUsageTotal: "총 스토리지 사용량" storageUsageTotal: "총 스토리지 사용량"
_instanceCharts: _instanceCharts:
requests: "요청" requests: "요청"
users: "유저 증감" users: "유저 증감"
usersTotal: "누적 사용자 수" usersTotal: "누적 유저 수"
notes: "노트 수 증감" notes: "노트 수 증감"
notesTotal: "누적 노트 수" notesTotal: " 노트 수"
ff: "팔로잉/팔로워 증감" ff: "팔로잉/팔로워 증감"
ffTotal: "팔로잉/팔로워 누적" ffTotal: "팔로잉/팔로워 누적"
cacheSize: "캐시 용량 증감" cacheSize: "캐시 용량 증감"

View File

@ -1 +1,265 @@
--- ---
_ago:
unknown: "未知"
future: "未来"
justNow: "最近"
secondsAgo: "{n}秒前"
minutesAgo: "{n}分前"
hoursAgo: "{n}小时前"
daysAgo: "{n}日前"
weeksAgo: "{n}周前"
monthsAgo: "{n}月前"
yearsAgo: "{n}年前"
_time:
second: "秒"
minute: "分"
hour: "小时"
day: "日"
introMisskey: "欢迎Misskey是一个开源的分散型SNS服务。\n通过「Note」来分享现在发生的事情吧📡\n「反应」工能也可以让你快速的对大家的「Note」来表达感情👍\n一起来探索新的世界吧🚀"
monthAndDay: "{month}月 {day}日"
search: "搜索"
notifications: "通知"
username: "用户名"
password: "密码"
fetchingAsApObject: "联合查询中"
ok: "OK"
gotIt: "我明白了"
cancel: "取消"
enterUsername: "输入用户名"
renotedBy: "由 {user} 转推"
noNotifications: "无通知"
instance: "实例"
settings: "设置"
profile: "个人资料"
timeline: "时间线"
noAccountDescription: "这个人很懒,没有写自我介绍"
login: "登录"
loggingIn: "正在登录..."
logout: "登出"
signup: "新用户注册"
uploading: "正在上传"
save: "保存"
users: "用户"
addUser: "添加用户"
favorite: "收藏"
favorites: "收藏"
unfavorite: "取消收藏"
pin: "置顶"
unpin: "取消置顶"
copyContent: "复制内容"
copyLink: "复制链接"
delete: "删除"
addToList: "添加至列表"
sendMessage: "发送"
copyUsername: "复制用户名"
reply: "回复"
loadMore: "查看更多"
importAndExport: "导入和导出"
import: "导入"
export: "导出"
files: "文件"
download: "下载"
lists: "列表"
noLists: "列表为空"
following: "关注中"
followers: "关注者"
followsYou: "关注了你"
error: "有点小问题"
retry: "重试"
follow: "关注"
followRequest: "关注申请"
followRequests: "关注申请"
unfollow: "取消关注"
followRequestPending: "发送关注申请"
enterEmoji: "输入Emoji"
renote: "转发"
unrenote: "取消转发"
quote: "引用"
you: "您"
clickToShow: "点击以显示"
sensitive: "阅读注意"
add: "添加"
reaction: "反应"
reactionSettingDescription: "快速选择回应中的自定义表情符号,以换行符分隔。"
renameFile: "重命名文件"
attachCancel: "删除附件"
markAsSensitive: "阅读注意"
enterFileName: "请输入文件名"
mute: "屏蔽"
unmute: "解除屏蔽"
block: "屏蔽"
unblock: "取消屏蔽"
suspend: "冻结"
unsuspend: "解除冻结"
blockConfirm: "确定要屏蔽吗?"
unblockConfirm: "确定要解除屏蔽吗?"
suspendConfirm: "要冻结吗?"
unsuspendConfirm: "要解除冻结吗?"
selectList: "选择列表"
customEmojis: "自定义Emoji"
emojiName: "Emoji 名称"
emojiUrl: "emoji 地址"
addEmoji: "添加Emoji"
cacheRemoteFiles: "远程文件缓存"
addAcount: "添加账户"
loginFailed: "登录失败"
general: "常规设置"
wallpaper: "壁纸"
removeWallpaper: "移除壁纸"
searchWith: "搜索:{q}"
federation: "联合"
instances: "实例"
latestRequestSentAt: "上次发送的请求"
latestRequestReceivedAt: "上次收到的请求"
storageUsage: "已用存储"
perHour: "每小时"
perDay: "每天"
operations: "操作"
monitor: "监视器"
jobQueue: "作业队列"
cpuAndMemory: "CPU使用量"
network: "网络"
disk: "存储"
statistics: "统计"
clearQueue: "清除队列"
mutedUsers: "禁言用户"
blockedUsers: "已屏蔽用户"
noUsers: "无用户"
editProfile: "编辑资料"
done: "完成"
processing: "处理中"
preview: "预览"
noCustomEmojis: "无自定义Emoji"
notResponding: "没有响应"
instanceFollowing: "关注实例"
instanceFollowers: "关注实例"
changePassword: "修改密码"
security: "安全性"
retypedNotMatch: "两次输入不一致!"
currentPassword: "现在的密码"
newPassword: "新密码"
newPasswordRetype: "重新输入密码:"
attachFile: "插入附件"
more: "更多!"
featured: "高亮"
lookup: "查询"
remove: "删除"
removed: "已删除"
removeAreYouSure: "要删掉「{x}」吗?"
saved: "已保存"
messaging: "聊天"
upload: "上传"
start: "开始"
images: "图片"
birthday: "生日"
yearsOld: "{age}岁"
registeredDate: "注册于"
location: "位置"
theme: "主题"
lightThemes: "亮色主题"
nsfw: "阅读注意"
accept: "允许"
reject: "拒绝"
instanceName: "实例名称"
instanceDescription: "实例介绍"
maintainerName: "管理员名称"
maintainerEmail: "管理员电子邮箱"
tosUrl: "服务条款URL"
thisYear: "今年"
thisMonth: "本月"
today: "今天"
dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "页面"
integration: "连携"
connectSerice: "已连接"
disconnectSerice: "断开连接"
enableLocalTimeline: "启用本地时间线功能"
enableGlobalTimeline: "启用全局时间线"
registration: "注册"
enableRegistration: "允许新用户注册"
invite: "邀请"
proxyRemoteFiles: "代理远程文件"
inMb: "以兆字节(Mbps)为单位"
iconUrl: "图标URL"
bannerUrl: "Banner URL"
basicInfo: "基本信息"
recaptcha: "reCAPTCHA"
enableRecaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可用. 如果启用, 可能导致无法正常使用登录或注册等功能)"
recaptchaSecretKey: "reCAPTCHA 密钥"
name: "名称"
userList: "列表"
lastUsed: "最后使用:"
unregister: "删除账户"
post: "投稿"
posted: "已投稿"
share: "分享"
help: "帮助"
invites: "邀请"
_permissions:
"read:blocks": "查看黑名单"
"write:blocks": "编辑黑名单"
"read:drive": "查看网盘"
"write:drive": "管理网盘文件"
"read:favorites": "查看收藏夹"
"write:favorites": "编辑收藏夹"
"read:following": "查看关注信息"
"write:following": "关注/取消关注"
"read:messaging": "查看对话"
"write:messaging": "对话操作"
"read:mutes": "查看屏蔽列表"
"write:mutes": "编辑屏蔽列表"
"read:notifications": "查看通知"
"write:notifications": "管理通知"
"read:reactions": "查看回应"
"write:reactions": "回应操作"
"write:votes": "投票"
"read:pages": "查看页面"
"write:pages": "操作页面"
"read:page-likes": "查看喜欢的页面"
"write:page-likes": "操作喜欢的页面"
"read:user-groups": "查看用户组"
"write:user-groups": "操作用户组"
_auth:
permissionAsk: "这个应用程序需要以下权限"
_widgets:
notifications: "通知"
timeline: "时间线"
_cw:
show: "查看更多"
_poll:
deadlineTime: "小时"
vote: "投票"
_visibility:
followers: "关注者"
_profile:
name: "名称"
username: "用户名"
_exportOrImport:
followingList: "关注中"
muteList: "屏蔽"
blockingList: "屏蔽"
userLists: "列表"
_pages:
viewPage: "查看页面"
blocks:
image: "图片"
script:
categories:
list: "列表"
blocks:
_join:
arg1: "列表"
_randomPick:
arg1: "列表"
_dailyRandomPick:
arg1: "列表"
_seedRandomPick:
arg2: "列表"
_pick:
arg1: "列表"
_listLen:
arg1: "列表"
types:
array: "列表"

View File

@ -1,7 +1,7 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>", "author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.2.0", "version": "12.3.0",
"codename": "indigo", "codename": "indigo",
"repository": { "repository": {
"type": "git", "type": "git",
@ -44,6 +44,7 @@
"@types/cbor": "5.0.0", "@types/cbor": "5.0.0",
"@types/dateformat": "3.0.1", "@types/dateformat": "3.0.1",
"@types/double-ended-queue": "2.1.1", "@types/double-ended-queue": "2.1.1",
"@types/glob": "7.1.1",
"@types/gulp": "4.0.6", "@types/gulp": "4.0.6",
"@types/gulp-mocha": "0.0.32", "@types/gulp-mocha": "0.0.32",
"@types/gulp-rename": "0.0.33", "@types/gulp-rename": "0.0.33",
@ -65,6 +66,7 @@
"@types/koa__multer": "2.0.1", "@types/koa__multer": "2.0.1",
"@types/koa__router": "8.0.2", "@types/koa__router": "8.0.2",
"@types/lolex": "5.1.0", "@types/lolex": "5.1.0",
"@types/markdown-it": "0.0.9",
"@types/mocha": "7.0.1", "@types/mocha": "7.0.1",
"@types/node": "13.7.0", "@types/node": "13.7.0",
"@types/nodemailer": "6.4.0", "@types/nodemailer": "6.4.0",
@ -127,6 +129,7 @@
"fibers": "4.0.2", "fibers": "4.0.2",
"file-type": "13.1.2", "file-type": "13.1.2",
"fluent-ffmpeg": "2.1.2", "fluent-ffmpeg": "2.1.2",
"glob": "7.1.6",
"gulp": "4.0.2", "gulp": "4.0.2",
"gulp-clean-css": "4.2.0", "gulp-clean-css": "4.2.0",
"gulp-dart-sass": "0.9.1", "gulp-dart-sass": "0.9.1",
@ -164,6 +167,7 @@
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
"lolex": "5.1.2", "lolex": "5.1.2",
"lookup-dns-cache": "2.1.0", "lookup-dns-cache": "2.1.0",
"markdown-it": "10.0.0",
"mocha": "7.0.1", "mocha": "7.0.1",
"moji": "0.5.1", "moji": "0.5.1",
"ms": "2.1.2", "ms": "2.1.2",

View File

@ -24,54 +24,67 @@
</div> </div>
</header> </header>
<nav class="nav" ref="nav"> <transition name="nav-back">
<div> <div class="nav-back"
<button class="item _button account" @click="openAccountMenu" v-if="$store.getters.isSignedIn"> v-if="showNav"
<mk-avatar :user="$store.state.i" class="avatar"/><mk-acct class="text" :user="$store.state.i"/> @click="showNav = false"
</button> @touchstart="showNav = false"
<router-link class="item" active-class="active" to="/" exact v-if="$store.getters.isSignedIn"> ></div>
<fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span> </transition>
</router-link>
<router-link class="item" active-class="active" to="/" exact v-else> <transition name="nav">
<fa :icon="faHome" fixed-width/><span class="text">{{ $t('home') }}</span> <nav class="nav" ref="nav" v-show="showNav">
</router-link> <div>
<router-link class="item" active-class="active" to="/featured"> <button class="item _button account" @click="openAccountMenu" v-if="$store.getters.isSignedIn">
<fa :icon="faFireAlt" fixed-width/><span class="text">{{ $t('featured') }}</span> <mk-avatar :user="$store.state.i" class="avatar"/><mk-acct class="text" :user="$store.state.i"/>
</router-link> </button>
<router-link class="item" active-class="active" to="/explore"> <div class="divider"></div>
<fa :icon="faHashtag" fixed-width/><span class="text">{{ $t('explore') }}</span> <router-link class="item index" active-class="active" to="/" exact v-if="$store.getters.isSignedIn">
</router-link> <fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span>
<button class="item _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.getters.isSignedIn"> </router-link>
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span> <router-link class="item index" active-class="active" to="/" exact v-else>
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i> <fa :icon="faHome" fixed-width/><span class="text">{{ $t('home') }}</span>
</button> </router-link>
<router-link class="item" active-class="active" to="/my/messaging" v-if="$store.getters.isSignedIn"> <button class="item _button notifications" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.getters.isSignedIn">
<fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span> <fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
<i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i> <i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
</router-link> </button>
<router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.getters.isSignedIn && $store.state.i.isLocked"> <router-link class="item" active-class="active" to="/my/messaging" v-if="$store.getters.isSignedIn">
<fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span> <fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span>
<i v-if="$store.state.i.pendingReceivedFollowRequestsCount"><fa :icon="faCircle"/></i> <i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i>
</router-link> </router-link>
<router-link class="item" active-class="active" to="/my/drive" v-if="$store.getters.isSignedIn"> <router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.getters.isSignedIn && $store.state.i.isLocked">
<fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span> <fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span>
</router-link> <i v-if="$store.state.i.pendingReceivedFollowRequestsCount"><fa :icon="faCircle"/></i>
<router-link class="item" active-class="active" to="/announcements"> </router-link>
<fa :icon="faBroadcastTower" fixed-width/><span class="text">{{ $t('announcements') }}</span> <router-link class="item" active-class="active" to="/my/drive" v-if="$store.getters.isSignedIn">
<i v-if="$store.getters.isSignedIn && $store.state.i.hasUnreadAnnouncement"><fa :icon="faCircle"/></i> <fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span>
</router-link> </router-link>
<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.getters.isSignedIn && ($store.state.i.isAdmin || $store.state.i.isModerator)" @click="oepnInstanceMenu"> <div class="divider"></div>
<fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span> <router-link class="item" active-class="active" to="/featured">
</button> <fa :icon="faFireAlt" fixed-width/><span class="text">{{ $t('featured') }}</span>
<button class="item _button" @click="search()"> </router-link>
<fa :icon="faSearch" fixed-width/><span class="text">{{ $t('search') }}</span> <router-link class="item" active-class="active" to="/explore">
</button> <fa :icon="faHashtag" fixed-width/><span class="text">{{ $t('explore') }}</span>
<button class="item _button" @click="more"> </router-link>
<fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span> <router-link class="item" active-class="active" to="/announcements">
<i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadMentions || $store.state.i.hasUnreadSpecifiedNotes)"><fa :icon="faCircle"/></i> <fa :icon="faBroadcastTower" fixed-width/><span class="text">{{ $t('announcements') }}</span>
</button> <i v-if="$store.getters.isSignedIn && $store.state.i.hasUnreadAnnouncement"><fa :icon="faCircle"/></i>
</div> </router-link>
</nav> <button class="item _button" @click="search()">
<fa :icon="faSearch" fixed-width/><span class="text">{{ $t('search') }}</span>
</button>
<div class="divider"></div>
<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.getters.isSignedIn && ($store.state.i.isAdmin || $store.state.i.isModerator)" @click="oepnInstanceMenu">
<fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span>
</button>
<button class="item _button" @click="more">
<fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span>
<i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadMentions || $store.state.i.hasUnreadSpecifiedNotes)"><fa :icon="faCircle"/></i>
</button>
</div>
</nav>
</transition>
<div class="contents"> <div class="contents">
<main ref="main"> <main ref="main">
@ -121,7 +134,7 @@
</div> </div>
<div class="buttons"> <div class="buttons">
<button v-if="$store.getters.isSignedIn" class="button nav _button" @click="showNav" ref="navButton"><fa :icon="faBars"/><i v-if="$store.state.i.hasUnreadSpecifiedNotes || $store.state.i.pendingReceivedFollowRequestsCount || $store.state.i.hasUnreadMessagingMessage || $store.state.i.hasUnreadAnnouncement"><fa :icon="faCircle"/></i></button> <button v-if="$store.getters.isSignedIn" class="button nav _button" @click="showNav = true" ref="navButton"><fa :icon="faBars"/><i v-if="$store.state.i.hasUnreadSpecifiedNotes || $store.state.i.pendingReceivedFollowRequestsCount || $store.state.i.hasUnreadMessagingMessage || $store.state.i.hasUnreadAnnouncement"><fa :icon="faCircle"/></i></button>
<button v-if="$store.getters.isSignedIn" class="button home _button" :disabled="$route.path === '/'" @click="$router.push('/')"><fa :icon="faHome"/></button> <button v-if="$store.getters.isSignedIn" class="button home _button" :disabled="$route.path === '/'" @click="$router.push('/')"><fa :icon="faHome"/></button>
<button v-if="$store.getters.isSignedIn" class="button notifications _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton2"><fa :icon="notificationsOpen ? faTimes : faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button> <button v-if="$store.getters.isSignedIn" class="button notifications _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton2"><fa :icon="notificationsOpen ? faTimes : faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button>
<button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button> <button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
@ -137,7 +150,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faGamepad, faServer, faFileAlt, faSatellite, faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import { faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faGamepad, faServer, faFileAlt, faSatellite, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons'; import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import i18n from './i18n'; import i18n from './i18n';
@ -159,6 +172,7 @@ export default Vue.extend({
return { return {
host: host, host: host,
pageKey: 0, pageKey: 0,
showNav: false,
searching: false, searching: false,
notificationsOpen: false, notificationsOpen: false,
accounts: [], accounts: [],
@ -179,6 +193,7 @@ export default Vue.extend({
return { return {
'p': this.post, 'p': this.post,
'n': this.post, 'n': this.post,
'h|/': this.help
}; };
}, },
@ -241,12 +256,15 @@ export default Vue.extend({
}); });
setInterval(() => { setInterval(() => {
if (this.showNav) return; // TODO: トランジション中も false になるので、これだけでは不十分
this.$refs.title.style.left = (this.$refs.main.getBoundingClientRect().left - this.$refs.nav.offsetWidth) + 'px'; this.$refs.title.style.left = (this.$refs.main.getBoundingClientRect().left - this.$refs.nav.offsetWidth) + 'px';
}, 1000); }, 1000);
// https://stackoverflow.com/questions/33891709/when-flexbox-items-wrap-in-column-mode-container-does-not-grow-its-width // https://stackoverflow.com/questions/33891709/when-flexbox-items-wrap-in-column-mode-container-does-not-grow-its-width
if (this.enableWidgets) { if (this.enableWidgets) {
setInterval(() => { setInterval(() => {
if (!this.$refs.widgetsEditButton) return;
const width = this.$refs.widgetsEditButton.offsetLeft + 300; const width = this.$refs.widgetsEditButton.offsetLeft + 300;
this.$refs.widgets.style.width = width + 'px'; this.$refs.widgets.style.width = width + 'px';
}, 1000); }, 1000);
@ -254,6 +272,10 @@ export default Vue.extend({
}, },
methods: { methods: {
help() {
this.$router.push('/docs/keyboard-shortcut');
},
back() { back() {
if (this.canBack) window.history.back(); if (this.canBack) window.history.back();
}, },
@ -292,67 +314,6 @@ export default Vue.extend({
} }
}, },
showNav(ev) {
this.$root.menu({
items: [{
text: this.$t('search'),
icon: faSearch,
action: this.search,
}, null, this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? {
text: this.$t('instance'),
icon: faServer,
action: () => this.oepnInstanceMenu(ev),
} : undefined, {
type: 'link',
text: this.$t('announcements'),
to: '/announcements',
icon: faBroadcastTower,
indicate: this.$store.state.i.hasUnreadAnnouncement,
}, {
type: 'link',
text: this.$t('featured'),
to: '/featured',
icon: faFireAlt,
}, {
type: 'link',
text: this.$t('explore'),
to: '/explore',
icon: faHashtag,
}, {
type: 'link',
text: this.$t('messaging'),
to: '/my/messaging',
icon: faComments,
indicate: this.$store.state.i.hasUnreadMessagingMessage,
}, this.$store.state.i.isLocked ? {
type: 'link',
text: this.$t('followRequests'),
to: '/my/follow-requests',
icon: faUserClock,
indicate: this.$store.state.i.pendingReceivedFollowRequestsCount > 0,
} : undefined, {
type: 'link',
text: this.$t('drive'),
to: '/my/drive',
icon: faCloud,
}, {
text: this.$t('more'),
icon: faEllipsisH,
action: () => this.more(ev),
indicate: this.$store.state.i.hasUnreadMentions || this.$store.state.i.hasUnreadSpecifiedNotes
}, null, {
type: 'user',
user: this.$store.state.i,
action: () => this.openAccountMenu(ev),
}],
direction: 'up',
align: 'left',
fixed: true,
width: 200,
source: ev.currentTarget || ev.target,
});
},
async openAccountMenu(ev) { async openAccountMenu(ev) {
const accounts = (await this.$root.api('users/show', { userIds: this.$store.state.device.accounts.map(x => x.id) })).filter(x => x.id !== this.$store.state.i.id); const accounts = (await this.$root.api('users/show', { userIds: this.$store.state.device.accounts.map(x => x.id) })).filter(x => x.id !== this.$store.state.i.id);
@ -448,6 +409,11 @@ export default Vue.extend({
text: this.$t('lists'), text: this.$t('lists'),
to: '/my/lists', to: '/my/lists',
icon: faListUl, icon: faListUl,
}, {
type: 'link',
text: this.$t('groups'),
to: '/my/groups',
icon: faUsers,
}, { }, {
type: 'link', type: 'link',
text: this.$t('antennas'), text: this.$t('antennas'),
@ -481,6 +447,11 @@ export default Vue.extend({
to: '/games', to: '/games',
icon: faGamepad, icon: faGamepad,
}, null] : []), { }, null] : []), {
type: 'link',
text: this.$t('help'),
to: '/docs',
icon: faQuestionCircle,
}, {
type: 'link', type: 'link',
text: this.$t('about'), text: this.$t('about'),
to: '/about', to: '/about',
@ -604,6 +575,28 @@ export default Vue.extend({
transform: translateY(32px); transform: translateY(32px);
} }
.nav-enter-active,
.nav-leave-active {
opacity: 1;
transform: translateX(0);
transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.nav-enter,
.nav-leave-active {
opacity: 0;
transform: translateX(-240px);
}
.nav-back-enter-active,
.nav-back-leave-active {
opacity: 1;
transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.nav-back-enter,
.nav-back-leave-active {
opacity: 0;
}
.mk-app { .mk-app {
$header-height: 60px; $header-height: 60px;
$nav-width: 250px; $nav-width: 250px;
@ -749,6 +742,16 @@ export default Vue.extend({
} }
} }
> .nav-back {
position: fixed;
top: 0;
left: 0;
z-index: 1001;
width: 100%;
height: 100%;
background: var(--modalBg);
}
> .nav { > .nav {
$avatar-size: 32px; $avatar-size: 32px;
$avatar-margin: ($header-height - $avatar-size) / 2; $avatar-margin: ($header-height - $avatar-size) / 2;
@ -763,7 +766,14 @@ export default Vue.extend({
} }
@media (max-width: $nav-hide-threshold) { @media (max-width: $nav-hide-threshold) {
display: none; position: fixed;
top: 0;
left: 0;
z-index: 1001;
}
@media (min-width: $nav-hide-threshold + 1px) {
display: block !important;
} }
> div { > div {
@ -773,13 +783,24 @@ export default Vue.extend({
z-index: 1001; z-index: 1001;
width: $nav-width; width: $nav-width;
height: 100vh; height: 100vh;
padding-top: 16px; padding: 16px 0;
box-sizing: border-box; box-sizing: border-box;
overflow: auto;
background: var(--navBg); background: var(--navBg);
border-right: solid 1px var(--divider); border-right: solid 1px var(--divider);
@media (max-width: $nav-icon-only-threshold) { > .divider {
margin: 16px 0;
border-top: solid 1px var(--divider);
}
@media (max-width: $nav-icon-only-threshold) and (min-width: $nav-hide-threshold + 1px) {
width: $nav-icon-only-width; width: $nav-icon-only-width;
padding: 8px 0;
> .divider {
margin: 8px 0;
}
} }
> .item { > .item {
@ -787,7 +808,6 @@ export default Vue.extend({
display: block; display: block;
padding-left: 32px; padding-left: 32px;
font-size: $ui-font-size; font-size: $ui-font-size;
font-weight: bold;
line-height: 3.2rem; line-height: 3.2rem;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
@ -797,22 +817,6 @@ export default Vue.extend({
box-sizing: border-box; box-sizing: border-box;
color: var(--navFg); color: var(--navFg);
&:not(.active) {
opacity: 0.85;
&:hover {
opacity: 1;
> [data-icon] {
opacity: 1;
}
}
> [data-icon] {
opacity: 0.85;
}
}
> [data-icon] { > [data-icon] {
width: ($header-height - ($avatar-margin * 2)); width: ($header-height - ($avatar-margin * 2));
} }
@ -845,7 +849,7 @@ export default Vue.extend({
color: var(--navActive); color: var(--navActive);
} }
@media (max-width: $nav-icon-only-threshold) { @media (max-width: $nav-icon-only-threshold) and (min-width: $nav-hide-threshold + 1px) {
padding-left: 0; padding-left: 0;
width: 100%; width: 100%;
text-align: center; text-align: center;
@ -866,6 +870,13 @@ export default Vue.extend({
} }
} }
} }
@media (max-width: $nav-hide-threshold) {
> .index,
> .notifications {
display: none;
}
}
} }
} }

View File

@ -1,6 +1,8 @@
<template> <template>
<div> <div>
<mk-avatar v-for="user in us" :user="user" :key="user.id" style="width:32px;height:32px;"/> <div v-for="user in us" :key="user.id" style="width:32px;height:32px;margin-right:8px;">
<mk-avatar :user="user" style="width:32px;height:32px;"/>
</div>
</div> </div>
</template> </template>

View File

@ -133,6 +133,7 @@ export default Vue.extend({
<style lang="scss" scoped> <style lang="scss" scoped>
.zdjebgpv { .zdjebgpv {
display: flex; display: flex;
position: relative;
> img, > img,
> .icon { > .icon {

View File

@ -19,7 +19,7 @@
{{ folder.name }} {{ folder.name }}
</p> </p>
<p class="upload" v-if="$store.state.settings.uploadFolder == folder.id"> <p class="upload" v-if="$store.state.settings.uploadFolder == folder.id">
{{ $t('upload-folder') }} {{ $t('uploadFolder') }}
</p> </p>
</div> </div>
</template> </template>

View File

@ -319,6 +319,49 @@ export default Vue.extend({
}); });
}, },
renameFolder(folder) {
this.$root.dialog({
title: this.$t('contextmenu.rename-folder'),
input: {
placeholder: this.$t('contextmenu.input-new-folder-name'),
default: folder.name
}
}).then(({ canceled, result: name }) => {
if (canceled) return;
this.$root.api('drive/folders/update', {
folderId: folder.id,
name: name
}).then(folder => {
// FIXME: 画面を更新するために自分自身に移動
this.move(folder);
});
});
},
deleteFolder(folder) {
this.$root.api('drive/folders/delete', {
folderId: folder.id
}).then(() => {
// 削除時に親フォルダに移動
this.move(folder.parentId);
}).catch(err => {
switch(err.id) {
case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
this.$root.dialog({
type: 'error',
title: this.$t('unable-to-delete'),
text: this.$t('has-child-files-or-folders')
});
break;
default:
this.$root.dialog({
type: 'error',
text: this.$t('unable-to-delete')
});
}
});
},
onChangeFileInput() { onChangeFileInput() {
for (const file of Array.from((this.$refs.fileInput as any).files)) { for (const file of Array.from((this.$refs.fileInput as any).files)) {
this.upload(file, this.folder); this.upload(file, this.folder);

View File

@ -1,5 +1,6 @@
<template> <template>
<div class="mjndxjcg _panel"> <div class="mjndxjcg _panel">
<img src="https://xn--931a.moe/assets/error.jpg" alt=""/>
<p><fa :icon="faExclamationTriangle"/> {{ $t('error') }}</p> <p><fa :icon="faExclamationTriangle"/> {{ $t('error') }}</p>
<mk-button @click="() => $emit('retry')" class="button">{{ $t('retry') }}</mk-button> <mk-button @click="() => $emit('retry')" class="button">{{ $t('retry') }}</mk-button>
</div> </div>
@ -38,5 +39,12 @@ export default Vue.extend({
> .button { > .button {
margin: 0 auto; margin: 0 auto;
} }
> img {
vertical-align: bottom;
height: 150px;
margin-bottom: 16px;
border-radius: 16px;
}
} }
</style> </style>

View File

@ -154,21 +154,17 @@ export default Vue.component('misskey-flavored-markdown', {
url: token.node.props.url, url: token.node.props.url,
rel: 'nofollow noopener', rel: 'nofollow noopener',
}, },
attrs: {
style: 'color:var(--link);'
}
})]; })];
} }
case 'link': { case 'link': {
return [createElement('a', { return [createElement('a', {
attrs: { attrs: {
class: 'link', class: 'link _link',
href: token.node.props.url, href: token.node.props.url,
rel: 'nofollow noopener', rel: 'nofollow noopener',
target: '_blank', target: '_blank',
title: token.node.props.url, title: token.node.props.url,
style: 'color:var(--link);'
} }
}, genEl(token.children))]; }, genEl(token.children))];
} }

View File

@ -83,7 +83,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faTrashAlt, faQuoteRight } from '@fortawesome/free-solid-svg-icons';
import { parse } from '../../mfm/parse'; import { parse } from '../../mfm/parse';
import { sum, unique } from '../../prelude/array'; import { sum, unique } from '../../prelude/array';
import i18n from '../i18n'; import i18n from '../i18n';
@ -97,7 +97,6 @@ import XPoll from './poll.vue';
import XUrlPreview from './url-preview.vue'; import XUrlPreview from './url-preview.vue';
import MkNoteMenu from './note-menu.vue'; import MkNoteMenu from './note-menu.vue';
import MkReactionPicker from './reaction-picker.vue'; import MkReactionPicker from './reaction-picker.vue';
import MkRenotePicker from './renote-picker.vue';
import pleaseLogin from '../scripts/please-login'; import pleaseLogin from '../scripts/please-login';
function focus(el, fn) { function focus(el, fn) {
@ -379,10 +378,26 @@ export default Vue.extend({
renote() { renote() {
pleaseLogin(this.$root); pleaseLogin(this.$root);
this.blur(); this.blur();
this.$root.new(MkRenotePicker, { this.$root.menu({
items: [{
text: this.$t('renote'),
icon: faRetweet,
action: () => {
(this as any).$root.api('notes/create', {
renoteId: this.appearNote.id
});
}
}, {
text: this.$t('quote'),
icon: faQuoteRight,
action: () => {
this.$root.post({
renote: this.appearNote,
});
}
}]
source: this.$refs.renoteButton, source: this.$refs.renoteButton,
note: this.appearNote, }).then(this.focus);
}).$once('closed', this.focus);
}, },
renoteDirectly() { renoteDirectly() {

View File

@ -1,6 +1,9 @@
<template> <template>
<div class="mk-notes" v-size="[{ max: 500 }]"> <div class="mk-notes" v-size="[{ max: 500 }]">
<div class="empty" v-if="empty">{{ $t('noNotes') }}</div> <div class="empty _panel" v-if="empty">
<img src="https://xn--931a.moe/assets/info.jpg" alt=""/>
<div>{{ $t('noNotes') }}</div>
</div>
<mk-error v-if="error" @retry="init()"/> <mk-error v-if="error" @retry="init()"/>
@ -24,8 +27,6 @@ import i18n from '../i18n';
import paging from '../scripts/paging'; import paging from '../scripts/paging';
import XNote from './note.vue'; import XNote from './note.vue';
import XList from './date-separated-list.vue'; import XList from './date-separated-list.vue';
import getUserName from '../../misc/get-user-name';
import getNoteSummary from '../../misc/get-note-summary';
export default Vue.extend({ export default Vue.extend({
i18n, i18n,
@ -85,14 +86,15 @@ export default Vue.extend({
<style lang="scss" scoped> <style lang="scss" scoped>
.mk-notes { .mk-notes {
> .empty { > .empty {
margin: 0 auto;
padding: 32px; padding: 32px;
text-align: center; text-align: center;
background: rgba(0, 0, 0, 0.3);
color: #fff; > img {
-webkit-backdrop-filter: blur(16px); vertical-align: bottom;
backdrop-filter: blur(16px); height: 128px;
border-radius: 6px; margin-bottom: 16px;
border-radius: 16px;
}
} }
> .notes { > .notes {

View File

@ -134,7 +134,7 @@ export default Vue.extend({
position: absolute; position: absolute;
z-index: 10001; z-index: 10001;
background: var(--panel); background: var(--panel);
border-radius: 4px; border-radius: 8px;
box-shadow: 0 3px 12px rgba(27, 31, 35, 0.15); box-shadow: 0 3px 12px rgba(27, 31, 35, 0.15);
overflow: hidden; overflow: hidden;
transform-origin: center top; transform-origin: center top;

View File

@ -15,7 +15,7 @@
<span v-if="visibility === 'followers'"><fa :icon="faUnlock"/></span> <span v-if="visibility === 'followers'"><fa :icon="faUnlock"/></span>
<span v-if="visibility === 'specified'"><fa :icon="faEnvelope"/></span> <span v-if="visibility === 'specified'"><fa :icon="faEnvelope"/></span>
</button> </button>
<button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}</button> <button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}<fa :icon="reply ? faReply : renote ? faQuoteRight : faPaperPlane"/></button>
</div> </div>
</header> </header>
<div class="form"> <div class="form">
@ -52,7 +52,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { faTimes, faUpload, faChartPie, faGlobe, faHome, faUnlock, faEnvelope, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard } from '@fortawesome/free-solid-svg-icons'; import { faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faChartPie, faGlobe, faHome, faUnlock, faEnvelope, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard } from '@fortawesome/free-solid-svg-icons';
import { faEyeSlash, faLaughSquint } from '@fortawesome/free-regular-svg-icons'; import { faEyeSlash, faLaughSquint } from '@fortawesome/free-regular-svg-icons';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
import { length } from 'stringz'; import { length } from 'stringz';
@ -130,7 +130,7 @@ export default Vue.extend({
draghover: false, draghover: false,
quoteId: null, quoteId: null,
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'), recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
faTimes, faUpload, faChartPie, faGlobe, faHome, faUnlock, faEnvelope, faEyeSlash, faLaughSquint, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faChartPie, faGlobe, faHome, faUnlock, faEnvelope, faEyeSlash, faLaughSquint, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard
}; };
}, },
@ -163,10 +163,10 @@ export default Vue.extend({
submitText(): string { submitText(): string {
return this.renote return this.renote
? this.$t('renote') ? this.$t('quote')
: this.reply : this.reply
? this.$t('reply') ? this.$t('reply')
: this.$t('post'); : this.$t('note');
}, },
canPost(): boolean { canPost(): boolean {
@ -622,8 +622,9 @@ export default Vue.extend({
> .submit { > .submit {
margin: 16px 16px 16px 0; margin: 16px 16px 16px 0;
padding: 0 16px; padding: 0 12px;
line-height: 34px; line-height: 34px;
font-weight: bold;
vertical-align: bottom; vertical-align: bottom;
border-radius: 4px; border-radius: 4px;
@ -634,6 +635,10 @@ export default Vue.extend({
&:disabled { &:disabled {
opacity: 0.7; opacity: 0.7;
} }
> [data-icon] {
margin-left: 6px;
}
} }
} }
} }

View File

@ -1,94 +0,0 @@
<template>
<x-popup :source="source" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap">
<div class="rdfaahpc">
<button class="_button" @click="quote()"><fa :icon="faQuoteRight"/></button>
<button class="_button" @click="renote()"><fa :icon="faRetweet"/></button>
</div>
</x-popup>
</template>
<script lang="ts">
import Vue from 'vue';
import { faQuoteRight, faRetweet } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import XPopup from './popup.vue';
export default Vue.extend({
i18n,
components: {
XPopup,
},
props: {
note: {
type: Object,
required: true
},
source: {
required: true
},
},
data() {
return {
faQuoteRight, faRetweet
};
},
computed: {
keymap(): any {
return {
'esc': this.close,
};
}
},
methods: {
renote() {
(this as any).$root.api('notes/create', {
renoteId: this.note.id
}).then(() => {
this.$emit('closed');
this.destroyDom();
});
},
quote() {
this.$emit('closed');
this.destroyDom();
this.$root.post({
renote: this.note,
});
}
}
});
</script>
<style lang="scss" scoped>
.rdfaahpc {
padding: 4px;
> button {
padding: 0;
width: 40px;
height: 40px;
font-size: 16px;
border-radius: 2px;
> * {
height: 1em;
}
&:hover {
background: rgba(0, 0, 0, 0.05);
}
&:active {
background: var(--accent);
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
}
}
}
</style>

View File

@ -8,9 +8,16 @@
<template v-else><fa :icon="faAngleDown"/></template> <template v-else><fa :icon="faAngleDown"/></template>
</button> </button>
</header> </header>
<div v-show="showBody"> <transition name="container-toggle"
<slot></slot> @enter="enter"
</div> @after-enter="afterEnter"
@leave="leave"
@after-leave="afterLeave"
>
<div v-show="showBody">
<slot></slot>
</div>
</transition>
</div> </div>
</template> </template>
@ -51,12 +58,42 @@ export default Vue.extend({
toggleContent(show: boolean) { toggleContent(show: boolean) {
if (!this.bodyTogglable) return; if (!this.bodyTogglable) return;
this.showBody = show; this.showBody = show;
} },
enter(el) {
const elementHeight = el.getBoundingClientRect().height;
el.style.height = 0;
el.offsetHeight; // reflow
el.style.height = elementHeight + 'px';
},
afterEnter(el) {
el.style.height = null;
},
leave(el) {
const elementHeight = el.getBoundingClientRect().height;
el.style.height = elementHeight + 'px';
el.offsetHeight; // reflow
el.style.height = 0;
},
afterLeave(el) {
el.style.height = null;
},
} }
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.container-toggle-enter-active, .container-toggle-leave-active {
overflow-y: hidden;
transition: opacity 0.5s, height 0.5s !important;
}
.container-toggle-enter {
opacity: 0;
}
.container-toggle-leave-to {
opacity: 0;
}
.ukygtjoj { .ukygtjoj {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
@ -72,6 +109,7 @@ export default Vue.extend({
> header { > header {
position: relative; position: relative;
box-shadow: 0 1px 0 0 var(--divider);
> .title { > .title {
margin: 0; margin: 0;

View File

@ -1,5 +1,5 @@
<template> <template>
<component :is="hasRoute ? 'router-link' : 'a'" class="mk-url" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target"> <component :is="hasRoute ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target">
<template v-if="!self"> <template v-if="!self">
<span class="schema">{{ schema }}//</span> <span class="schema">{{ schema }}//</span>
<span class="hostname">{{ hostname }}</span> <span class="hostname">{{ hostname }}</span>
@ -58,7 +58,7 @@ export default Vue.extend({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.mk-url { .ieqqeuvs {
word-break: break-all; word-break: break-all;
> .icon { > .icon {

View File

@ -84,7 +84,7 @@ export default Vue.extend({
methods: { methods: {
close() { close() {
this.show = false; this.show = false;
(this.$refs.content as any).style.pointerEvents = 'none'; if (this.$refs.content) (this.$refs.content as any).style.pointerEvents = 'none';
} }
} }
}); });

View File

@ -27,17 +27,17 @@
<div style="margin-bottom: 1em;">{{ $t('aboutMisskeyText') }}</div> <div style="margin-bottom: 1em;">{{ $t('aboutMisskeyText') }}</div>
<div>{{ $t('misskeyMembers') }}</div> <div>{{ $t('misskeyMembers') }}</div>
<span class="members"> <span class="members">
<a href="https://github.com/syuilo" target="_blank">@syuilo</a> <a href="https://github.com/syuilo" target="_blank" class="_link">@syuilo</a>
<a href="https://github.com/AyaMorisawa" target="_blank">@AyaMorisawa</a> <a href="https://github.com/AyaMorisawa" target="_blank" class="_link">@AyaMorisawa</a>
<a href="https://github.com/mei23" target="_blank">@mei23</a> <a href="https://github.com/mei23" target="_blank" class="_link">@mei23</a>
<a href="https://github.com/acid-chicken" target="_blank">@acid-chicken</a> <a href="https://github.com/acid-chicken" target="_blank" class="_link">@acid-chicken</a>
<a href="https://github.com/tamaina" target="_blank">@tamaina</a> <a href="https://github.com/tamaina" target="_blank" class="_link">@tamaina</a>
<a href="https://github.com/rinsuki" target="_blank">@rinsuki</a> <a href="https://github.com/rinsuki" target="_blank" class="_link">@rinsuki</a>
</span> </span>
<div style="margin-top: 1em;">{{ $t('misskeySource') }}</div> <div style="margin-top: 1em;">{{ $t('misskeySource') }}</div>
<a href="https://github.com/syuilo/misskey" target="_blank" style="color: var(--link);">https://github.com/syuilo/misskey</a> <mk-url url="https://github.com/syuilo/misskey"/>
<div style="margin-top: 1em;">{{ $t('misskeyDonate') }}</div> <div style="margin-top: 1em;">{{ $t('misskeyDonate') }}</div>
<a href="https://www.patreon.com/syuilo" target="_blank" style="color: var(--link);">https://www.patreon.com/syuilo</a> <mk-url url="https://www.patreon.com/syuilo"/>
</div> </div>
<div class="_content"> <div class="_content">
<span><mfm text="<motion>❤</motion>"/> {{ $t('patrons') }}</span> <span><mfm text="<motion>❤</motion>"/> {{ $t('patrons') }}</span>
@ -121,7 +121,6 @@ export default Vue.extend({
> ._content { > ._content {
> .members { > .members {
> a { > a {
color: var(--link);
margin-right: 0.5em; margin-right: 0.5em;
} }
} }

134
src/client/pages/doc.vue Normal file
View File

@ -0,0 +1,134 @@
<template>
<div>
<portal to="icon"><fa :icon="faFileAlt"/></portal>
<portal to="title">{{ title }}</portal>
<main class="_card">
<div class="_content">
<div v-html="body" class="qyqbqfal"></div>
</div>
</main>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faFileAlt } from '@fortawesome/free-solid-svg-icons'
import MarkdownIt from 'markdown-it';
import { url, lang } from '../config';
const markdown = MarkdownIt({
html: true
});
export default Vue.extend({
metaInfo() {
return {
title: this.title,
};
},
props: {
doc: {
type: String,
required: true
}
},
watch: {
doc: {
handler() {
this.fetchDoc();
},
immediate: true,
}
},
data() {
return {
faFileAlt,
title: '',
body: '',
markdown: '',
}
},
methods: {
fetchDoc() {
fetch(`${url}/assets/docs/${this.doc}.${lang}.md`).then(res => res.text()).then(md => {
this.parse(md);
});
},
parse(md: string) {
// markdown の全容をパースする
const parsed = markdown.parse(md, {});
if (parsed.length === 0) return;
const buf = [...parsed];
const headingTokens = [];
let headingStart = 0;
// もっとも上にある見出しを抽出する
while (buf[0].type !== 'heading_open') {
buf.shift();
headingStart++;
}
buf.shift();
while (buf[0].type as string !== 'heading_close') {
const token = buf.shift();
if (token) {
headingTokens.push(token);
}
}
// 抽出した見出しを除く部分をbodyとして抽出する
const bodyTokens = [...parsed];
bodyTokens.splice(headingStart, headingTokens.length + 2);
// 各々レンダーする
this.title = markdown.renderer.render(headingTokens, {}, {});
this.body = markdown.renderer.render(bodyTokens, {}, {});
}
}
});
</script>
<style lang="scss" scoped>
.qyqbqfal {
> *:first-child {
margin-top: 0;
}
> *:last-child {
margin-bottom: 0;
}
::v-deep h2 {
font-size: 1.25em;
padding: 0 0 0.5em 0;
border-bottom: solid 1px var(--divider);
}
::v-deep table {
width: 100%;
max-width: 100%;
overflow: auto;
}
::v-deep kbd.group {
display: inline-block;
padding: 2px;
border: 1px solid var(--divider);
border-radius: 4px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
::v-deep kbd.key {
display: inline-block;
padding: 6px 8px;
border: solid 1px var(--divider);
border-radius: 4px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
}
</style>

42
src/client/pages/docs.vue Normal file
View File

@ -0,0 +1,42 @@
<template>
<div>
<portal to="icon"><fa :icon="faQuestionCircle"/></portal>
<portal to="title">{{ $t('help') }}</portal>
<main class="_card">
<div class="_content">
<ul>
<li v-for="doc in docs" :key="doc.path">
<router-link :to="`/docs/${doc.path}`">{{ doc.title }}</router-link>
</li>
</ul>
</div>
</main>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'
import { url, lang } from '../config';
export default Vue.extend({
metaInfo() {
return {
title: this.$t('help') as string,
};
},
data() {
return {
docs: [],
faQuestionCircle
}
},
created() {
fetch(`${url}/docs.json?lang=${lang}`).then(res => res.json()).then(docs => {
this.docs = docs;
});
},
});
</script>

View File

@ -57,11 +57,11 @@ export default Vue.extend({
}, this.folder ? { }, this.folder ? {
text: this.$t('renameFolder'), text: this.$t('renameFolder'),
icon: faICursor, icon: faICursor,
action: () => { this.$refs.drive.renameFolder(); } action: () => { this.$refs.drive.renameFolder(this.folder); }
} : undefined, this.folder ? { } : undefined, this.folder ? {
text: this.$t('deleteFolder'), text: this.$t('deleteFolder'),
icon: faTrashAlt, icon: faTrashAlt,
action: () => { this.$refs.drive.deleteFolder(); } action: () => { this.$refs.drive.deleteFolder(this.folder); }
} : undefined, { } : undefined, {
text: this.$t('createFolder'), text: this.$t('createFolder'),
icon: faFolderPlus, icon: faFolderPlus,

View File

@ -8,7 +8,7 @@
ref="text" ref="text"
@keypress="onKeypress" @keypress="onKeypress"
@paste="onPaste" @paste="onPaste"
:placeholder="$t('input-message-here')" :placeholder="$t('inputMessageHere')"
v-autocomplete="{ model: 'text' }" v-autocomplete="{ model: 'text' }"
></textarea> ></textarea>
<div class="file" @click="file = null" v-if="file">{{ file.name }}</div> <div class="file" @click="file = null" v-if="file">{{ file.name }}</div>

View File

@ -3,7 +3,7 @@
<portal to="icon"><fa :icon="faSatellite"/></portal> <portal to="icon"><fa :icon="faSatellite"/></portal>
<portal to="title">{{ $t('manageAntennas') }}</portal> <portal to="title">{{ $t('manageAntennas') }}</portal>
<mk-button @click="create" primary class="add"><fa :icon="faPlus"/> {{ $t('createAntenna') }}</mk-button> <mk-button @click="create" primary class="add"><fa :icon="faPlus"/> {{ $t('add') }}</mk-button>
<x-antenna v-if="draft" :antenna="draft" @created="onAntennaCreated" style="margin-bottom: var(--margin);"/> <x-antenna v-if="draft" :antenna="draft" @created="onAntennaCreated" style="margin-bottom: var(--margin);"/>

View File

@ -0,0 +1,214 @@
<template>
<div class="mk-group-page">
<portal to="icon"><fa :icon="faUsers"/></portal>
<portal to="title">{{ group.name }}</portal>
<transition name="zoom" mode="out-in">
<div v-if="group" class="_card">
<div class="_content">
<mk-button inline @click="renameGroup()">{{ $t('rename') }}</mk-button>
<mk-button inline @click="transfer()">{{ $t('transfer') }}</mk-button>
<mk-button inline @click="deleteGroup()">{{ $t('delete') }}</mk-button>
</div>
</div>
</transition>
<transition name="zoom" mode="out-in">
<div v-if="group" class="_card members">
<div class="_title">{{ $t('members') }}</div>
<div class="_content">
<div class="users">
<div class="user" v-for="user in users" :key="user.id">
<mk-avatar :user="user" class="avatar"/>
<div class="body">
<mk-user-name :user="user" class="name"/>
<mk-acct :user="user" class="acct"/>
</div>
<div class="action">
<button class="_button" @click="removeUser(user)"><fa :icon="faTimes"/></button>
</div>
</div>
</div>
</div>
<div class="_footer">
<mk-button inline @click="invite()">{{ $t('invite') }}</mk-button>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faTimes, faUsers } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import Progress from '../../scripts/loading';
import MkButton from '../../components/ui/button.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.group ? `${this.group.name} | ${this.$t('manageGroups')}` : this.$t('manageGroups')
};
},
components: {
MkButton
},
data() {
return {
group: null,
users: [],
faTimes, faUsers
};
},
watch: {
$route: 'fetch'
},
created() {
this.fetch();
},
methods: {
fetch() {
Progress.start();
this.$root.api('users/groups/show', {
groupId: this.$route.params.group
}).then(group => {
this.group = group;
this.$root.api('users/show', {
userIds: this.group.userIds
}).then(users => {
this.users = users;
Progress.done();
});
});
},
invite() {
this.$root.new(MkUserSelect, {}).$once('selected', user => {
this.$root.api('users/groups/invite', {
groupId: this.group.id,
userId: user.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
});
});
},
removeUser(user) {
this.$root.api('users/groups/pull', {
groupId: this.group.id,
userId: user.id
}).then(() => {
this.users = this.users.filter(x => x.id !== user.id);
});
},
async renameGroup() {
const { canceled, result: name } = await this.$root.dialog({
title: this.$t('groupName'),
input: {
default: this.group.name
}
});
if (canceled) return;
await this.$root.api('users/groups/update', {
groupId: this.group.id,
name: name
});
this.group.name = name;
},
transfer() {
this.$root.new(MkUserSelect, {}).$once('selected', user => {
this.$root.api('users/groups/transfer', {
groupId: this.group.id,
userId: user.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
});
});
},
async deleteGroup() {
const { canceled } = await this.$root.dialog({
type: 'warning',
text: this.$t('removeAreYouSure', { x: this.group.name }),
showCancelButton: true
});
if (canceled) return;
await this.$root.api('users/groups/delete', {
groupId: this.group.id
});
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
this.$router.push('/my/groups');
}
}
});
</script>
<style lang="scss" scoped>
.mk-group-page {
> .members {
> ._content {
max-height: 400px;
overflow: auto;
> .users {
> .user {
display: flex;
align-items: center;
> .avatar {
width: 50px;
height: 50px;
}
> .body {
flex: 1;
padding: 8px;
> .name {
display: block;
font-weight: bold;
}
> .acct {
opacity: 0.5;
}
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,103 @@
<template>
<div class="">
<portal to="icon"><fa :icon="faUsers"/></portal>
<portal to="title">{{ $t('groups') }}</portal>
<mk-button @click="create" primary style="margin: 0 auto var(--margin) auto;"><fa :icon="faPlus"/> {{ $t('createGroup') }}</mk-button>
<mk-container :body-togglable="true">
<template #header><fa :icon="faUsers"/> {{ $t('ownedGroups') }}</template>
<mk-pagination :pagination="ownedPagination" #default="{items}" ref="owned">
<div class="_frame" v-for="group in items" :key="group.id">
<div class="_title"><router-link :to="`/my/groups/${ group.id }`" class="_link">{{ group.name }}</router-link></div>
<div class="_content"><mk-avatars :user-ids="group.userIds"/></div>
</div>
</mk-pagination>
</mk-container>
<mk-container :body-togglable="true">
<template #header><fa :icon="faEnvelopeOpenText"/> {{ $t('invites') }}</template>
<mk-pagination :pagination="invitePagination" #default="{items}">
<div class="_frame" v-for="invite in items" :key="invite.id">
<div class="_title">{{ invite.group.name }}</div>
<div class="_content"><mk-avatars :user-ids="invite.group.userIds"/></div>
<div class="_footer">
<mk-button @click="acceptInvite(invite)" primary inline><fa :icon="faCheck"/> {{ $t('accept') }}</mk-button>
<mk-button @click="rejectInvite(invite)" primary inline><fa :icon="faBan"/> {{ $t('reject') }}</mk-button>
</div>
</div>
</mk-pagination>
</mk-container>
<mk-container :body-togglable="true">
<template #header><fa :icon="faUsers"/> {{ $t('joinedGroups') }}</template>
<mk-pagination :pagination="joinedPagination" #default="{items}">
<div class="_frame" v-for="group in items" :key="group.id">
<div class="_title">{{ group.name }}</div>
<div class="_content"><mk-avatars :user-ids="group.userIds"/></div>
</div>
</mk-pagination>
</mk-container>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faUsers, faPlus, faEnvelopeOpenText } from '@fortawesome/free-solid-svg-icons';
import MkPagination from '../../components/ui/pagination.vue';
import MkButton from '../../components/ui/button.vue';
import MkContainer from '../../components/ui/container.vue';
import MkAvatars from '../../components/avatars.vue';
export default Vue.extend({
metaInfo() {
return {
title: this.$t('groups') as string,
};
},
components: {
MkPagination,
MkButton,
MkContainer,
MkAvatars,
},
data() {
return {
ownedPagination: {
endpoint: 'users/groups/owned',
limit: 10,
},
joinedPagination: {
endpoint: 'users/groups/joined',
limit: 10,
},
invitePagination: {
endpoint: 'i/user-group-invites',
limit: 10,
},
faUsers, faPlus, faEnvelopeOpenText
};
},
methods: {
async create() {
const { canceled, result: name } = await this.$root.dialog({
title: this.$t('groupName'),
input: true
});
if (canceled) return;
await this.$root.api('users/groups/create', { name: name });
this.$refs.owned.reload();
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
},
}
});
</script>
<style lang="scss" scoped>
</style>

View File

@ -7,7 +7,7 @@
<mk-pagination :pagination="pagination" #default="{items}" class="lists" ref="list"> <mk-pagination :pagination="pagination" #default="{items}" class="lists" ref="list">
<div class="list _panel" v-for="(list, i) in items" :key="list.id"> <div class="list _panel" v-for="(list, i) in items" :key="list.id">
<router-link :to="`/lists/${ list.id }`">{{ list.name }}</router-link> <router-link :to="`/my/lists/${ list.id }`">{{ list.name }}</router-link>
</div> </div>
</mk-pagination> </mk-pagination>
</div> </div>
@ -62,7 +62,7 @@ export default Vue.extend({
<style lang="scss" scoped> <style lang="scss" scoped>
.qkcjvfiv { .qkcjvfiv {
> .add { > .add {
margin: 0 auto 16px auto; margin: 0 auto var(--margin) auto;
} }
> .lists { > .lists {

View File

@ -1,11 +1,23 @@
<template> <template>
<div class="mk-list-page"> <div class="mk-list-page">
<portal to="icon"><fa :icon="faListUl"/></portal>
<portal to="title">{{ list.name }}</portal>
<transition name="zoom" mode="out-in"> <transition name="zoom" mode="out-in">
<div v-if="list" :key="list.id" class="_card list"> <div v-if="list" class="_card">
<div class="_title">{{ list.name }}</div> <div class="_content">
<mk-button inline @click="renameList()">{{ $t('rename') }}</mk-button>
<mk-button inline @click="deleteList()">{{ $t('delete') }}</mk-button>
</div>
</div>
</transition>
<transition name="zoom" mode="out-in">
<div v-if="list" class="_card members">
<div class="_title">{{ $t('members') }}</div>
<div class="_content"> <div class="_content">
<div class="users"> <div class="users">
<div class="user" v-for="(user, i) in users" :key="user.id"> <div class="user" v-for="user in users" :key="user.id">
<mk-avatar :user="user" class="avatar"/> <mk-avatar :user="user" class="avatar"/>
<div class="body"> <div class="body">
<mk-user-name :user="user" class="name"/> <mk-user-name :user="user" class="name"/>
@ -18,8 +30,7 @@
</div> </div>
</div> </div>
<div class="_footer"> <div class="_footer">
<mk-button inline @click="renameList()">{{ $t('renameList') }}</mk-button> <mk-button inline @click="addUser()">{{ $t('addUser') }}</mk-button>
<mk-button inline @click="deleteList()">{{ $t('deleteList') }}</mk-button>
</div> </div>
</div> </div>
</transition> </transition>
@ -28,10 +39,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { faTimes, faListUl } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n'; import i18n from '../../i18n';
import Progress from '../../scripts/loading'; import Progress from '../../scripts/loading';
import MkButton from '../../components/ui/button.vue'; import MkButton from '../../components/ui/button.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({ export default Vue.extend({
i18n, i18n,
@ -50,7 +62,7 @@ export default Vue.extend({
return { return {
list: null, list: null,
users: [], users: [],
faTimes faTimes, faListUl
}; };
}, },
@ -78,6 +90,26 @@ export default Vue.extend({
}); });
}, },
addUser() {
this.$root.new(MkUserSelect, {}).$once('selected', user => {
this.$root.api('users/lists/push', {
listId: this.list.id,
userId: user.id
}).then(() => {
this.users.push(user);
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
});
});
},
removeUser(user) { removeUser(user) {
this.$root.api('users/lists/pull', { this.$root.api('users/lists/pull', {
listId: this.list.id, listId: this.list.id,
@ -107,7 +139,7 @@ export default Vue.extend({
async deleteList() { async deleteList() {
const { canceled } = await this.$root.dialog({ const { canceled } = await this.$root.dialog({
type: 'warning', type: 'warning',
text: this.$t('deleteListConfirm', { list: this.list.name }), text: this.$t('removeAreYouSure', { x: this.list.name }),
showCancelButton: true showCancelButton: true
}); });
if (canceled) return; if (canceled) return;
@ -127,7 +159,7 @@ export default Vue.extend({
<style lang="scss" scoped> <style lang="scss" scoped>
.mk-list-page { .mk-list-page {
> .list { > .members {
> ._content { > ._content {
max-height: 400px; max-height: 400px;
overflow: auto; overflow: auto;

View File

@ -0,0 +1,52 @@
<template>
<div class="ipledcug">
<portal to="icon"><fa :icon="faExclamationTriangle"/></portal>
<portal to="title">{{ $t('notFound') }}</portal>
<section class="_card">
<div class="_content">
<img src="https://xn--931a.moe/assets/not-found.jpg" alt=""/>
<div>{{ $t('notFoundDescription') }}</div>
</div>
</section>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('notFound') as string
};
},
data() {
return {
faExclamationTriangle
}
},
});
</script>
<style lang="scss" scoped>
.ipledcug {
> ._card {
> ._content {
text-align: center;
> img {
vertical-align: bottom;
height: 150px;
margin-bottom: 16px;
border-radius: 16px;
}
}
}
}
</style>

View File

@ -4,8 +4,7 @@
<div class="_content"> <div class="_content">
<p v-if="!data && !$store.state.i.twoFactorEnabled"><mk-button @click="register">{{ $t('_2fa.registerDevice') }}</mk-button></p> <p v-if="!data && !$store.state.i.twoFactorEnabled"><mk-button @click="register">{{ $t('_2fa.registerDevice') }}</mk-button></p>
<template v-if="$store.state.i.twoFactorEnabled"> <template v-if="$store.state.i.twoFactorEnabled">
<h2 class="heading">{{ $t('totp-header') }}</h2> <p>{{ $t('_2fa.alreadyRegistered') }}</p>
<p>{{ $t('already-registered') }}</p>
<mk-button @click="unregister">{{ $t('unregister') }}</mk-button> <mk-button @click="unregister">{{ $t('unregister') }}</mk-button>
<template v-if="supportsCredentials"> <template v-if="supportsCredentials">
@ -24,7 +23,7 @@
<mk-switch v-model="usePasswordLessLogin" @change="updatePasswordLessLogin" v-if="$store.state.i.securityKeysList.length > 0">{{ $t('passwordLessLogin') }}</mk-switch> <mk-switch v-model="usePasswordLessLogin" @change="updatePasswordLessLogin" v-if="$store.state.i.securityKeysList.length > 0">{{ $t('passwordLessLogin') }}</mk-switch>
<mk-info warn v-if="registration && registration.error">{{ $t('something-went-wrong') }} {{ registration.error }}</mk-info> <mk-info warn v-if="registration && registration.error">{{ $t('something-went-wrong') }} {{ registration.error }}</mk-info>
<mk-button v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('register') }}</mk-button> <mk-button v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('_2fa.registerKey') }}</mk-button>
<ol v-if="registration && !registration.error"> <ol v-if="registration && !registration.error">
<li v-if="registration.stage >= 0"> <li v-if="registration.stage >= 0">
@ -47,8 +46,8 @@
<ol style="margin: 0; padding: 0 0 0 1em;"> <ol style="margin: 0; padding: 0 0 0 1em;">
<li> <li>
<i18n path="_2fa.step1" tag="span"> <i18n path="_2fa.step1" tag="span">
<a href="https://authy.com/" rel="noopener" target="_blank" place="a" style="color: var(--link);">Authy</a> <a href="https://authy.com/" rel="noopener" target="_blank" place="a" class="_link">Authy</a>
<a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" place="b" style="color: var(--link);">Google Authenticator</a> <a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" place="b" class="_link">Google Authenticator</a>
</i18n> </i18n>
</li> </li>
<li>{{ $t('_2fa.step2') }}<br><img :src="data.qr"></li> <li>{{ $t('_2fa.step2') }}<br><img :src="data.qr"></li>

View File

@ -18,9 +18,9 @@
</mk-switch> </mk-switch>
</div> </div>
<div class="_content"> <div class="_content">
<mk-button @click="readAllNotifications">{{ $t('mark-as-read-all-notifications') }}</mk-button> <mk-button @click="readAllNotifications">{{ $t('markAsReadAllNotifications') }}</mk-button>
<mk-button @click="readAllUnreadNotes">{{ $t('mark-as-read-all-unread-notes') }}</mk-button> <mk-button @click="readAllUnreadNotes">{{ $t('markAsReadAllUnreadNotes') }}</mk-button>
<mk-button @click="readAllMessagingMessages">{{ $t('mark-as-read-all-talk-messages') }}</mk-button> <mk-button @click="readAllMessagingMessages">{{ $t('markAsReadAllTalkMessages') }}</mk-button>
</div> </div>
<div class="_content"> <div class="_content">
<mk-switch v-model="reduceAnimation"> <mk-switch v-model="reduceAnimation">

View File

@ -0,0 +1,85 @@
<template>
<div class="">
<portal to="icon"><fa :icon="faShareAlt"/></portal>
<portal to="title">{{ $t('share') }}</portal>
<section class="_card">
<div class="_title" v-if="title">{{ title }}</div>
<div class="_content">
<div>{{ text }}</div>
<mk-button @click="post()" v-if="!posted">{{ $t('post') }}</mk-button>
<mk-button primary @click="close()" v-else>{{ $t('close') }}</mk-button>
</div>
<div class="_footer" v-if="url">{{ url }}</div>
</section>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faShareAlt } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import PostFormDialog from '../components/post-form-dialog.vue';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('share') as string
};
},
components: {
MkButton
},
data() {
return {
title: null,
text: null,
url: null,
posted: false,
faShareAlt
}
},
created() {
const urlParams = new URLSearchParams(window.location.search);
this.title = urlParams.get('title');
this.text = urlParams.get('text');
this.url = urlParams.get('url');
},
mounted() {
this.post();
},
methods: {
post() {
let text = '';
if (this.title) text += `${this.title}\n`;
if (this.text) text += `${this.text}\n`;
if (this.url) text += `${this.url}`;
this.$root.new(PostFormDialog, {
instant: true,
initialText: text.trim()
}).$once('posted', () => {
this.posted = true;
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
});
},
close() {
window.close()
}
}
});
</script>
<style lang="scss" scoped>
</style>

View File

@ -21,6 +21,8 @@ export const router = new VueRouter({
{ path: '/announcements', component: page('announcements') }, { path: '/announcements', component: page('announcements') },
{ path: '/about', component: page('about') }, { path: '/about', component: page('about') },
{ path: '/featured', component: page('featured') }, { path: '/featured', component: page('featured') },
{ path: '/docs', component: page('docs') },
{ path: '/docs/:doc', component: page('doc'), props: true },
{ path: '/explore', component: page('explore') }, { path: '/explore', component: page('explore') },
{ path: '/explore/tags/:tag', props: true, component: page('explore') }, { path: '/explore/tags/:tag', props: true, component: page('explore') },
{ path: '/search', component: page('search') }, { path: '/search', component: page('search') },
@ -38,6 +40,8 @@ export const router = new VueRouter({
{ path: '/my/follow-requests', component: page('follow-requests') }, { path: '/my/follow-requests', component: page('follow-requests') },
{ path: '/my/lists', component: page('my-lists/index') }, { path: '/my/lists', component: page('my-lists/index') },
{ path: '/my/lists/:list', component: page('my-lists/list') }, { path: '/my/lists/:list', component: page('my-lists/list') },
{ path: '/my/groups', component: page('my-groups/index') },
{ path: '/my/groups/:group', component: page('my-groups/group') },
{ path: '/my/antennas', component: page('my-antennas/index') }, { path: '/my/antennas', component: page('my-antennas/index') },
{ path: '/instance', component: page('instance/index') }, { path: '/instance', component: page('instance/index') },
{ path: '/instance/emojis', component: page('instance/emojis') }, { path: '/instance/emojis', component: page('instance/emojis') },
@ -52,7 +56,8 @@ export const router = new VueRouter({
{ path: '/tags/:tag', component: page('tag') }, { path: '/tags/:tag', component: page('tag') },
{ path: '/auth/:token', component: page('auth') }, { path: '/auth/:token', component: page('auth') },
{ path: '/authorize-follow', component: page('follow') }, { path: '/authorize-follow', component: page('follow') },
/*{ path: '*', component: MkNotFound }*/ { path: '/share', component: page('share') },
{ path: '*', component: page('not-found') }
], ],
// なんかHacky // なんかHacky
// 通常の使い方をすると scroll メソッドの behavior を設定できないため、自前で window.scroll するようにする // 通常の使い方をすると scroll メソッドの behavior を設定できないため、自前で window.scroll するようにする

View File

@ -301,6 +301,37 @@ a {
} }
} }
._frame {
position: relative;
border: solid 1px var(--divider);
border-radius: var(--radius);
margin: var(--margin);
> ._title {
margin: 0;
padding: 16px;
border-bottom: solid 1px var(--divider);
font-weight: bold;
}
> ._content {
padding: 16px;
& + ._content {
border-top: solid 1px var(--divider);
}
}
> ._footer {
border-top: solid 1px var(--divider);
padding: 16px;
}
}
._link {
color: var(--link);
}
.zoom-enter-active, .zoom-leave-active { .zoom-enter-active, .zoom-leave-active {
transition: opacity 0.5s, transform 0.5s !important; transition: opacity 0.5s, transform 0.5s !important;
} }

View File

@ -1,3 +0,0 @@
# About Misskey
Misskey is a mini blog SNS.

View File

@ -1,3 +0,0 @@
# Misskeyについて
MisskeyはミニブログSNSです。

View File

@ -1,9 +0,0 @@
extends ./base
block main
!= html
block footer
p
= i18n('docs.edit-this-page-on-github')
a(href=src rel="noopener" target="_blank")= i18n('docs.edit-this-page-on-github-link')

View File

@ -1,50 +0,0 @@
doctype html
html(lang= lang)
head
meta(charset="UTF-8")
meta(name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no")
title
| #{title} | Misskey Docs
link(rel="stylesheet" href="/docs/assets/style.css")
link(rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css")
script(src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js")
link(rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous")
block meta
body
nav
ul
each doc in docs
li: a(href=`/docs/${lang}/${doc.name}`)= doc.title[lang] || doc.title['ja-JP']
main
article
block main
if content
| !{content}
aside.
<div id="disqus_thread"></div>
<script>
/**
* RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
* LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
/*
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = "#{ id }"; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
*/
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://misskey.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
footer
block footer
small= copyright

View File

@ -10,9 +10,6 @@
<tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>新規投稿</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr> <tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>新規投稿</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr>
<tr><td><kbd class="key">T</kbd></td><td>タイムラインの最も新しい投稿にフォーカス</td><td><b>T</b>imeline, <b>T</b>op</td></tr> <tr><td><kbd class="key">T</kbd></td><td>タイムラインの最も新しい投稿にフォーカス</td><td><b>T</b>imeline, <b>T</b>op</td></tr>
<tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>通知を表示/隠す</td><td><b>N</b>otifications</td></tr> <tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>通知を表示/隠す</td><td><b>N</b>otifications</td></tr>
<tr><td><kbd class="key">A</kbd>, <kbd class="key">M</kbd></td><td>アカウントメニューを表示/隠す</td><td><b>A</b>ccount, <b>M</b>y, <b>M</b>e, <b>M</b>enu</td></tr>
<tr><td><kbd class="key">D</kbd></td><td>ダークモード切り替え</td><td><b>D</b>ark</td></tr>
<tr><td><kbd class="key">Z</kbd></td><td>上部のバーを隠す</td><td><b>Z</b>en</td></tr>
<tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>ヘルプを表示</td><td><b>H</b>elp</td></tr> <tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>ヘルプを表示</td><td><b>H</b>elp</td></tr>
</tbody> </tbody>
</table> </table>
@ -62,51 +59,7 @@
<tr><td><kbd class="key"></kbd>, <kbd class="key">H</kbd>, <kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">Tab</kbd></kbd></td><td>左のリアクションにフォーカスを移動</td><td>-</td></tr> <tr><td><kbd class="key"></kbd>, <kbd class="key">H</kbd>, <kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">Tab</kbd></kbd></td><td>左のリアクションにフォーカスを移動</td><td>-</td></tr>
<tr><td><kbd class="key"></kbd>, <kbd class="key">L</kbd>, <kbd class="key">Tab</kbd></td><td>右のリアクションにフォーカスを移動</td><td>-</td></tr> <tr><td><kbd class="key"></kbd>, <kbd class="key">L</kbd>, <kbd class="key">Tab</kbd></td><td>右のリアクションにフォーカスを移動</td><td>-</td></tr>
<tr><td><kbd class="key">Enter</kbd>, <kbd class="key">Space</kbd>, <kbd class="key">+</kbd></td><td>リアクション確定</td><td>-</td></tr> <tr><td><kbd class="key">Enter</kbd>, <kbd class="key">Space</kbd>, <kbd class="key">+</kbd></td><td>リアクション確定</td><td>-</td></tr>
<tr><td><kbd class="key">0</kbd>~<kbd class="key">9</kbd></td><td>数字に対応したリアクションで確定(対応については後述)</td><td>-</td></tr> <tr><td><kbd class="key">0</kbd>~<kbd class="key">9</kbd></td><td>数字に対応したリアクションで確定</td><td>-</td></tr>
<tr><td><kbd class="key">Esc</kbd></td><td>リアクションするのをやめる</td><td>-</td></tr> <tr><td><kbd class="key">Esc</kbd></td><td>リアクションするのをやめる</td><td>-</td></tr>
</tbody> </tbody>
</table> </table>
## リアクションと数字キーの対応
<table>
<thead>
<tr><th>数字キー</th><th>リアクション</th></tr>
</thead>
<tbody>
<tr><td><kbd class="key">1</kbd></td><td>👍</td></tr>
<tr><td><kbd class="key">2</kbd></td><td>❤️</td></tr>
<tr><td><kbd class="key">3</kbd></td><td>😆</td></tr>
<tr><td><kbd class="key">4</kbd></td><td>🤔</td></tr>
<tr><td><kbd class="key">5</kbd></td><td>😮</td></tr>
<tr><td><kbd class="key">6</kbd></td><td>🎉</td></tr>
<tr><td><kbd class="key">7</kbd></td><td>💢</td></tr>
<tr><td><kbd class="key">8</kbd></td><td>😥</td></tr>
<tr><td><kbd class="key">9</kbd></td><td>😇</td></tr>
<tr><td><kbd class="key">0</kbd></td><td>🍮 or 🍣</td></tr>
</tbody>
</table>
## デッキ
<table>
<thead>
<tr><th>ショートカット</th><th>効果</th><th>由来</th></tr>
</thead>
<tbody>
<tr><td>投稿にフォーカスした状態で<kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key"></kbd></kbd></td><td>上のカラムにフォーカス</td><td>-</td></tr>
<tr><td>投稿にフォーカスした状態で<kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key"></kbd></kbd></td><td>下のカラムにフォーカス</td><td>-</td></tr>
<tr><td>投稿にフォーカスした状態で<kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key"></kbd></kbd></td><td>右のカラムにフォーカス</td><td>-</td></tr>
<tr><td>投稿にフォーカスした状態で<kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key"></kbd></kbd></td><td>左のカラムにフォーカス</td><td>-</td></tr>
</tbody>
</table>
# 例
<table>
<thead>
<tr><th>ショートカット</th><th>動作</th></tr>
</thead>
<tbody>
<tr><td><kbd class="key">t</kbd><kbd class="key">+</kbd><kbd class="key">+</kbd></td><td>タイムラインの最新の投稿に👍する</td></tr>
<tr><td><kbd class="key">t</kbd><kbd class="key">1</kbd></td><td>タイムラインの最新の投稿に👍する</td></tr>
<tr><td><kbd class="key">t</kbd><kbd class="key">0</kbd></td><td>タイムラインの最新の投稿に🍮する</td></tr>
</tbody>
</table>

View File

@ -1,156 +0,0 @@
@import "../client/style"
@import "./ui"
html
--accent #fb4e4e
--link #fb4e4e
--linkTapHighlight #fb4e4eb3
body
margin 0
color #34495e
word-break break-word
main
margin 0 0 0 330px
padding 64px
width 850px
max-width calc(100% - 330px)
h1
margin 0 0 24px 0
padding 16px 0
font-size 1.5em
border-bottom solid 2px #eee
h2
margin 1em 0 24px 0
padding 0 0 16px 0
font-size 1.4em
border-bottom solid 1px #eee
h3
margin 1em 0 0 0
padding 0
font-size 1.25em
h4
margin 1em 0 0 0
p
margin 1em 0
line-height 1.6em
hr
border none
border-bottom solid 2px #eee
> aside
margin-top 32px
padding-top 32px
border-top solid 2px #eee
> footer
margin 32px 0 0 0
border-top solid 2px #eee
> small
display block
margin 16px 0 0 0
color #aaa
nav
display block
position fixed
z-index 10000
top 0
left 0
width 330px
height 100%
overflow auto
padding 32px
background #fff
border-right solid 2px #eee
ul
padding 0
margin 0
@media (max-width 1025px)
main
margin 0
max-width 100%
nav
position relative
width 100%
max-height 128px
background #f9f9f9
border-right none
@media (max-width 768px)
main
padding 32px
@media (max-width 512px)
main
padding 16px
table
width 100%
max-width 100%
overflow auto
border-spacing 0
border-collapse collapse
thead
font-weight bold
border-bottom solid 2px #eee
tr
th
text-align left
tbody
tr
&:nth-child(odd)
background #fbfbfb
th, td
padding 8px 16px
min-width 128px
code
padding 4px 8px
font-family Consolas, 'Courier New', Courier, Monaco, monospace
//color #295c92
background #f2f2f2
border-radius 4px
pre
overflow auto
> code
display block
padding 16px
kbd.group
display inline-block
padding 4px
background #fbfbfb
border 1px solid #d6d6d6
border-radius 4px
box-shadow 0 1px 1px rgba(0, 0, 0, 0.1)
kbd.key
display inline-block
padding 6px 8px
background #fff
border solid 1px #cecece
border-radius 4px
box-shadow 0 1px 1px rgba(0, 0, 0, 0.1)
td
> kbd.group,
> kbd.key
margin 4px

View File

@ -1,19 +0,0 @@
.ui.info
display block
margin 1em 0
padding 0 1em
font-size 90%
color rgba(#000, 0.87)
background #f8f8f9
border-radius 4px
overflow hidden
> p
opacity 0.8
> [data-icon]:first-child
margin-right 0.25em
&.warn
color #573a08
background #FFFAF3

View File

@ -34,6 +34,7 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
break; break;
case 'Like': case 'Like':
case 'EmojiReaction': case 'EmojiReaction':
case 'EmojiReact':
undoLike(actor, object as ILike); undoLike(actor, object as ILike);
break; break;
case 'Announce': case 'Announce':

View File

@ -171,7 +171,7 @@ export interface IRemove extends IActivity {
} }
export interface ILike extends IActivity { export interface ILike extends IActivity {
type: 'Like' | 'EmojiReaction'; type: 'Like' | 'EmojiReaction' | 'EmojiReact';
_misskey_reaction?: string; _misskey_reaction?: string;
} }
@ -193,6 +193,6 @@ export const isAccept = (object: IObject): object is IAccept => object.type ===
export const isReject = (object: IObject): object is IReject => object.type === 'Reject'; export const isReject = (object: IObject): object is IReject => object.type === 'Reject';
export const isAdd = (object: IObject): object is IAdd => object.type === 'Add'; export const isAdd = (object: IObject): object is IAdd => object.type === 'Add';
export const isRemove = (object: IObject): object is IRemove => object.type === 'Remove'; export const isRemove = (object: IObject): object is IRemove => object.type === 'Remove';
export const isLike = (object: IObject): object is ILike => object.type === 'Like' || object.type === 'EmojiReaction'; export const isLike = (object: IObject): object is ILike => object.type === 'Like' || object.type === 'EmojiReaction' || object.type === 'EmojiReact';
export const isAnnounce = (object: IObject): object is IAnnounce => object.type === 'Announce'; export const isAnnounce = (object: IObject): object is IAnnounce => object.type === 'Announce';
export const isBlock = (object: IObject): object is IBlock => object.type === 'Block'; export const isBlock = (object: IObject): object is IBlock => object.type === 'Block';

View File

@ -1,106 +0,0 @@
/**
* Docs
*/
import * as fs from 'fs';
import * as path from 'path';
import * as showdown from 'showdown';
import 'showdown-highlightjs-extension';
import ms = require('ms');
import * as Router from '@koa/router';
import * as send from 'koa-send';
import * as glob from 'glob';
import config from '../../config';
import { licenseHtml } from '../../misc/license';
import * as locales from '../../../locales';
import * as nestedProperty from 'nested-property';
function getLang(lang: string): string {
if (['en-US', 'ja-JP'].includes(lang)) {
return lang;
} else {
return 'en-US';
}
}
async function genVars(lang: string): Promise<{ [key: string]: any }> {
const vars = {} as { [key: string]: any };
vars['lang'] = lang;
const cwd = path.resolve(__dirname + '/../../../') + '/';
const docs = glob.sync(`src/docs/**/*.${lang}.md`, { cwd });
vars['docs'] = {};
for (const x of docs) {
const [, name] = x.match(/docs\/(.+?)\.(.+?)\.md$/)!;
if (vars['docs'][name] == null) {
vars['docs'][name] = {
name,
title: {}
};
}
vars['docs'][name]['title'][lang] = fs.readFileSync(cwd + x, 'utf-8').match(/^# (.+?)\r?\n/)![1];
}
vars['kebab'] = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
vars['config'] = config;
vars['copyright'] = '(c) Misskey';
vars['license'] = licenseHtml;
vars['i18n'] = (key: string) => nestedProperty.get(locales[lang], key);
return vars;
}
const router = new Router();
router.get('/assets/*', async ctx => {
await send(ctx as any, ctx.params[0], {
root: `${__dirname}/../../docs/assets/`,
maxage: ms('1 days')
});
});
router.get('/*/*', async ctx => {
const lang = getLang(ctx.params[0]);
const doc = ctx.params[1];
showdown.extension('urlExtension', () => ({
type: 'output',
regex: /%URL%/g,
replace: config.url
}));
showdown.extension('wsUrlExtension', () => ({
type: 'output',
regex: /%WS_URL%/g,
replace: config.wsUrl
}));
showdown.extension('apiUrlExtension', () => ({
type: 'output',
regex: /%API_URL%/g,
replace: config.apiUrl
}));
const conv = new showdown.Converter({
tables: true,
extensions: ['urlExtension', 'apiUrlExtension', 'highlightjs']
});
const md = fs.readFileSync(`${__dirname}/../../../src/docs/${doc}.${lang}.md`, 'utf8');
await ctx.render('../../../../src/docs/article', Object.assign({
id: doc,
html: conv.makeHtml(md),
title: md.match(/^# (.+?)\r?\n/)![1],
src: `https://github.com/syuilo/misskey/tree/master/src/docs/${doc}.${lang}.md`
}, await genVars(lang)));
ctx.set('Cache-Control', 'public, max-age=300');
});
export default router;

View File

@ -3,14 +3,16 @@
*/ */
import * as os from 'os'; import * as os from 'os';
import * as fs from 'fs';
import ms = require('ms'); import ms = require('ms');
import * as Koa from 'koa'; import * as Koa from 'koa';
import * as Router from '@koa/router'; import * as Router from '@koa/router';
import * as send from 'koa-send'; import * as send from 'koa-send';
import * as favicon from 'koa-favicon'; import * as favicon from 'koa-favicon';
import * as views from 'koa-views'; import * as views from 'koa-views';
import * as glob from 'glob';
import * as MarkdownIt from 'markdown-it';
import docs from './docs';
import packFeed from './feed'; import packFeed from './feed';
import { fetchMeta } from '../../misc/fetch-meta'; import { fetchMeta } from '../../misc/fetch-meta';
import { genOpenapiSpec } from '../api/openapi/gen-spec'; import { genOpenapiSpec } from '../api/openapi/gen-spec';
@ -21,6 +23,11 @@ import getNoteSummary from '../../misc/get-note-summary';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { getConnection } from 'typeorm'; import { getConnection } from 'typeorm';
import redis from '../../db/redis'; import redis from '../../db/redis';
import locales = require('../../../locales');
const markdown = MarkdownIt({
html: true
});
const client = `${__dirname}/../../client/`; const client = `${__dirname}/../../client/`;
@ -84,7 +91,6 @@ router.get('/robots.txt', async ctx => {
//#endregion //#endregion
// Docs // Docs
router.use('/docs', docs.routes());
router.get('/api-doc', async ctx => { router.get('/api-doc', async ctx => {
await send(ctx as any, '/assets/redoc.html', { await send(ctx as any, '/assets/redoc.html', {
root: client root: client
@ -98,6 +104,43 @@ router.get('/api.json', async ctx => {
ctx.body = genOpenapiSpec(); ctx.body = genOpenapiSpec();
}); });
router.get('/docs.json', async ctx => {
const lang = ctx.query.lang;
if (!Object.keys(locales).includes(lang)) {
ctx.body = [];
return;
}
const paths = glob.sync(__dirname + `/../../../src/docs/*.${lang}.md`);
const docs: { path: string; title: string; }[] = [];
for (const path of paths) {
const md = fs.readFileSync(path, { encoding: 'utf8' });
const parsed = markdown.parse(md, {});
if (parsed.length === 0) return;
const buf = [...parsed];
const headingTokens = [];
// もっとも上にある見出しを抽出する
while (buf[0].type !== 'heading_open') {
buf.shift();
}
buf.shift();
while (buf[0].type as string !== 'heading_close') {
const token = buf.shift();
if (token) {
headingTokens.push(token);
}
}
docs.push({
path: path.split('/').pop()!.split('.')[0],
title: markdown.renderer.render(headingTokens, {}, {})
});
}
ctx.body = docs;
});
const getFeed = async (acct: string) => { const getFeed = async (acct: string) => {
const { username, host } = parseAcct(acct); const { username, host } = parseAcct(acct);
const user = await Users.findOne({ const user = await Users.findOne({

148
yarn.lock
View File

@ -285,7 +285,7 @@
"@types/glob" "*" "@types/glob" "*"
"@types/node" "*" "@types/node" "*"
"@types/glob@*": "@types/glob@*", "@types/glob@7.1.1":
version "7.1.1" version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
@ -468,11 +468,23 @@
dependencies: dependencies:
"@types/koa" "*" "@types/koa" "*"
"@types/linkify-it@*":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw==
"@types/lolex@5.1.0": "@types/lolex@5.1.0":
version "5.1.0" version "5.1.0"
resolved "https://registry.yarnpkg.com/@types/lolex/-/lolex-5.1.0.tgz#11b4c4756c007306d0feeaf2f08f88350c635d2b" resolved "https://registry.yarnpkg.com/@types/lolex/-/lolex-5.1.0.tgz#11b4c4756c007306d0feeaf2f08f88350c635d2b"
integrity sha512-hCQ2dOEQUw1LwofdIpMMGGqENd5p5ANzvcTe1nXTjcQL84r7tcLXFJlBgi0Ggz0f7BLmE2epf0C5Q07iq2gV0g== integrity sha512-hCQ2dOEQUw1LwofdIpMMGGqENd5p5ANzvcTe1nXTjcQL84r7tcLXFJlBgi0Ggz0f7BLmE2epf0C5Q07iq2gV0g==
"@types/markdown-it@0.0.9":
version "0.0.9"
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.9.tgz#a5d552f95216c478e0a27a5acc1b28dcffd989ce"
integrity sha512-IFSepyZXbF4dgSvsk8EsgaQ/8Msv1I5eTL0BZ0X3iGO9jw6tCVtPG8HchIPm3wrkmGdqZOD42kE0zplVi1gYDA==
dependencies:
"@types/linkify-it" "*"
"@types/mime@*": "@types/mime@*":
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
@ -2966,7 +2978,7 @@ debug@3.1.0, debug@~3.1.0:
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@3.2.6, debug@3.X, debug@^3.1.0, debug@^3.2.6: debug@3.2.6, debug@3.X, debug@^3.1.0:
version "3.2.6" version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
@ -3128,7 +3140,7 @@ detect-indent@^5.0.0:
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50=
detect-libc@^1.0.2, detect-libc@^1.0.3: detect-libc@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
@ -3389,7 +3401,7 @@ entities@^1.1.1, entities@~1.1.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
entities@^2.0.0: entities@^2.0.0, entities@~2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
@ -4054,13 +4066,6 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs-minipass@^1.2.5:
version "1.2.7"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
dependencies:
minipass "^2.6.0"
fs-minipass@^2.0.0: fs-minipass@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@ -4241,7 +4246,7 @@ glob@7.1.3:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: glob@7.1.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.6" version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@ -4767,7 +4772,7 @@ humanize-number@0.0.2:
resolved "https://registry.yarnpkg.com/humanize-number/-/humanize-number-0.0.2.tgz#11c0af6a471643633588588048f1799541489c18" resolved "https://registry.yarnpkg.com/humanize-number/-/humanize-number-0.0.2.tgz#11c0af6a471643633588588048f1799541489c18"
integrity sha1-EcCvakcWQ2M1iFiASPF5lUFInBg= integrity sha1-EcCvakcWQ2M1iFiASPF5lUFInBg=
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24" version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@ -4791,13 +4796,6 @@ iferr@^0.1.5:
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
ignore-walk@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
dependencies:
minimatch "^3.0.4"
ignore@^4.0.6: ignore@^4.0.6:
version "4.0.6" version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
@ -5800,6 +5798,13 @@ liftoff@^3.1.0:
rechoir "^0.6.2" rechoir "^0.6.2"
resolve "^1.1.7" resolve "^1.1.7"
linkify-it@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf"
integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==
dependencies:
uc.micro "^1.0.1"
load-json-file@^1.0.0: load-json-file@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@ -6058,6 +6063,17 @@ map-visit@^1.0.0:
dependencies: dependencies:
object-visit "^1.0.0" object-visit "^1.0.0"
markdown-it@10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==
dependencies:
argparse "^1.0.7"
entities "~2.0.0"
linkify-it "^2.0.0"
mdurl "^1.0.1"
uc.micro "^1.0.5"
matchdep@^2.0.0: matchdep@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e"
@ -6087,6 +6103,11 @@ mdn-data@2.0.4:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
media-typer@0.3.0: media-typer@0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -6258,14 +6279,6 @@ minipass-pipeline@^1.2.2:
dependencies: dependencies:
minipass "^3.0.0" minipass "^3.0.0"
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
dependencies:
safe-buffer "^5.1.2"
yallist "^3.0.0"
minipass@^3.0.0, minipass@^3.1.1: minipass@^3.0.0, minipass@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5"
@ -6273,13 +6286,6 @@ minipass@^3.0.0, minipass@^3.1.1:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
minizlib@^1.2.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
dependencies:
minipass "^2.9.0"
minizlib@^2.1.0: minizlib@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3"
@ -6489,15 +6495,6 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
needle@^2.2.1:
version "2.4.0"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c"
integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==
dependencies:
debug "^3.2.6"
iconv-lite "^0.4.4"
sax "^1.2.4"
negotiator@0.6.2: negotiator@0.6.2:
version "0.6.2" version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
@ -6597,22 +6594,6 @@ node-object-hash@^1.2.0:
resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-1.4.2.tgz#385833d85b229902b75826224f6077be969a9e94" resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-1.4.2.tgz#385833d85b229902b75826224f6077be969a9e94"
integrity sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ== integrity sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ==
node-pre-gyp@*:
version "0.14.0"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83"
integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
needle "^2.2.1"
nopt "^4.0.1"
npm-packlist "^1.1.6"
npmlog "^4.0.2"
rc "^1.2.7"
rimraf "^2.6.1"
semver "^5.3.0"
tar "^4.4.2"
node-releases@^1.1.47: node-releases@^1.1.47:
version "1.1.47" version "1.1.47"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.47.tgz#c59ef739a1fd7ecbd9f0b7cf5b7871e8a8b591e4" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.47.tgz#c59ef739a1fd7ecbd9f0b7cf5b7871e8a8b591e4"
@ -6635,7 +6616,7 @@ noop-logger@^0.1.1:
resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=
nopt@^4.0.1, nopt@~4.0.1: nopt@~4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
@ -6677,27 +6658,6 @@ now-and-later@^2.0.0:
dependencies: dependencies:
once "^1.3.2" once "^1.3.2"
npm-bundled@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b"
integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==
dependencies:
npm-normalize-package-bin "^1.0.1"
npm-normalize-package-bin@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2"
integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==
npm-packlist@^1.1.6:
version "1.4.8"
resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==
dependencies:
ignore-walk "^3.0.1"
npm-bundled "^1.0.1"
npm-normalize-package-bin "^1.0.1"
npm-run-path@^2.0.0: npm-run-path@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@ -6712,7 +6672,7 @@ npm-run-path@^3.0.0:
dependencies: dependencies:
path-key "^3.0.0" path-key "^3.0.0"
npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2: npmlog@^4.0.1, npmlog@^4.1.2:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@ -8566,7 +8526,7 @@ rimraf@3.0.1:
dependencies: dependencies:
glob "^7.1.3" glob "^7.1.3"
rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1:
version "2.7.1" version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@ -9498,19 +9458,6 @@ tar-stream@^2.0.0:
inherits "^2.0.3" inherits "^2.0.3"
readable-stream "^3.1.1" readable-stream "^3.1.1"
tar@^4.4.2:
version "4.4.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
minipass "^2.8.6"
minizlib "^1.2.1"
mkdirp "^0.5.0"
safe-buffer "^5.1.2"
yallist "^3.0.3"
tar@^5.0.5: tar@^5.0.5:
version "5.0.5" version "5.0.5"
resolved "https://registry.yarnpkg.com/tar/-/tar-5.0.5.tgz#03fcdb7105bc8ea3ce6c86642b9c942495b04f93" resolved "https://registry.yarnpkg.com/tar/-/tar-5.0.5.tgz#03fcdb7105bc8ea3ce6c86642b9c942495b04f93"
@ -9951,6 +9898,11 @@ typescript@3.7.5:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
uglify-js@^2.6.1: uglify-js@^2.6.1:
version "2.8.29" version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
@ -10778,7 +10730,7 @@ yallist@^2.1.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: yallist@^3.0.2:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==