Compare commits
73 Commits
Author | SHA1 | Date | |
---|---|---|---|
d54f71774e | |||
a47358f590 | |||
8f2cf066b6 | |||
e3e5a4272b | |||
2dcb3af70f | |||
3a19e9f80e | |||
c6fe798092 | |||
e74a47916d | |||
761071e1ce | |||
9e3610d513 | |||
393ac6c203 | |||
b7a79d25b0 | |||
486f045751 | |||
d6e3ec3218 | |||
078b043cae | |||
296cbc2e5a | |||
a74beaac36 | |||
79f8eb910f | |||
c91eef0030 | |||
7bf9d726d0 | |||
1946ff8ed4 | |||
b4d85d4f69 | |||
a03702d2bd | |||
71d7de4989 | |||
35d9e13dbb | |||
b427842679 | |||
4ae172be57 | |||
e6705b1a65 | |||
bc22cabdb5 | |||
7128b9f16a | |||
8c0490fef1 | |||
882a81636d | |||
24b9be76ba | |||
e763c6e661 | |||
899e2c73d7 | |||
10cb15b000 | |||
873d4bd707 | |||
97dea72c94 | |||
7d49f260b8 | |||
3055e6d8c7 | |||
2c93246860 | |||
360c820b9d | |||
87847c6ed5 | |||
4874f54d4d | |||
ff73efcc08 | |||
4ee64cbd9e | |||
0c40a86fca | |||
f92eed0549 | |||
69ed8cc409 | |||
4fff1279db | |||
d80699e454 | |||
deee1dbf53 | |||
067588845b | |||
b177b0cafc | |||
5679777010 | |||
a70c24cd1e | |||
357f3dd258 | |||
d815838762 | |||
4ceef78599 | |||
d5811633ea | |||
32265da72c | |||
ef94236e7f | |||
2924f0e434 | |||
1e419a9e1f | |||
097ece9dc9 | |||
6b6483c9fc | |||
22049b10ff | |||
de86644cb6 | |||
657aac5bc5 | |||
7ae3640d1a | |||
6fb42857c6 | |||
abf21349cd | |||
ed17af8339 |
@ -4,7 +4,7 @@ executors:
|
||||
docker:
|
||||
working_directory: /tmp/workspace
|
||||
docker:
|
||||
- image: docker:12.10.1
|
||||
- image: docker:latest
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
|
@ -45,6 +45,7 @@ copyUsername: "انسخ اسم المستخدم"
|
||||
searchUser: "ابحث عن مستخدمين"
|
||||
reply: "رد"
|
||||
loadMore: "عرض المزيد"
|
||||
showMore: "عرض المزيد"
|
||||
youGotNewFollower: "يتابعك"
|
||||
receiveFollowRequest: "تلقيت طلب متابعة"
|
||||
followRequestAccepted: "قُبل طلب المتابعة"
|
||||
|
@ -1,5 +1,6 @@
|
||||
---
|
||||
_lang_: "Deutsch"
|
||||
headlineMisskey: "Durch Notizen verbundenes Netzwerk"
|
||||
introMisskey: "Willkommen! Misskey ist eine dezentralisierte Open-Source Microblogging-Platform.\nVerfasse \"Notizen\" um mitzuteilen, was gerade passiert oder um Ereignisse mit Anderen zu teilen. 📡\nMit \"Reaktionen\" kannst du außerdem schnell deine Gefühle über Notizen anderer Benutzer zum Ausdruck bringen. 👍\nLass uns eine neue Welt erforschen! 🚀"
|
||||
monthAndDay: "{day}/{month}"
|
||||
search: "Suchen"
|
||||
@ -49,6 +50,7 @@ copyUsername: "Benutzernamen kopieren"
|
||||
searchUser: "Benutzersuche"
|
||||
reply: "Antworten"
|
||||
loadMore: "Mehr anzeigen"
|
||||
showMore: "Mehr anzeigen"
|
||||
youGotNewFollower: "Du hast einen neuen Follower"
|
||||
receiveFollowRequest: "Follow-Anfrage erhalten"
|
||||
followRequestAccepted: "Follow-Anfrage akzeptiert"
|
||||
@ -182,7 +184,7 @@ clearCachedFiles: "Cache leeren"
|
||||
clearCachedFilesConfirm: "Sollen alle im Cache gespeicherten Dateien von anderen Instanzen wirklich gelöscht werden?"
|
||||
blockedInstances: "Blockierte Instanzen"
|
||||
blockedInstancesDescription: "Gib den Hostnamen der Instanz an, die blockiert werden soll. Blockierte Instanzen können nicht mehr mit dieser kommunizieren."
|
||||
muteAndBlock: "Stummgeschaltet / Blockiert"
|
||||
muteAndBlock: "Stummschaltungen / Blockierungen"
|
||||
mutedUsers: "Stummgeschaltete Benutzer"
|
||||
blockedUsers: "Blockierte Benutzer"
|
||||
noUsers: "Keine Benutzer"
|
||||
@ -243,7 +245,7 @@ nUsersRead: "Von {n} gelesen"
|
||||
agreeTo: "Ich stimme {0} zu"
|
||||
tos: "Nutzungsbedingungen"
|
||||
start: "Anfangen"
|
||||
home: "Home"
|
||||
home: "Startseite"
|
||||
remoteUserCaution: "Diese Informationen sind möglicherweise veraltet, da der Benutzer von einer anderen Instanz stammt."
|
||||
activity: "Aktivität"
|
||||
images: "Bilder"
|
||||
@ -316,7 +318,7 @@ enableRegistration: "Registration neuer Benutzer erlauben"
|
||||
invite: "Einladen"
|
||||
proxyRemoteFiles: "Dateien anderer Instanzen durch Proxy leiten"
|
||||
proxyRemoteFilesDescription: "Wenn diese Einstellung aktiviert ist, dann werden Dateien von anderen Instanzen, welche entweder nicht lokal gespeichert sind oder durch Überschreiten des Speicherlimits gelöscht wurden, durch einen Proxy geleitet. Hierbei wird auch ein Vorschaubild generiert. \n Dies hat keinen Effekt auf den Speicherplatz des Servers."
|
||||
driveCapacityPerLocalAccount: "Drivekapazität pro lokales Benutzerkonto"
|
||||
driveCapacityPerLocalAccount: "Drive-Kapazität pro lokales Benutzerkonto"
|
||||
driveCapacityPerRemoteAccount: "Drive-Kapazität pro Benutzer anderer Instanzen"
|
||||
inMb: "In Megabytes"
|
||||
iconUrl: "Icon-URL"
|
||||
@ -491,7 +493,7 @@ showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik an
|
||||
newNoteRecived: "Es gibt neue Notizen"
|
||||
sounds: "Töne"
|
||||
listen: "Anhören"
|
||||
none: "Keine"
|
||||
none: "Nichts"
|
||||
showInPage: "In Seite anzeigen"
|
||||
popout: "Pop-Up"
|
||||
volume: "Lautstärke"
|
||||
@ -536,7 +538,7 @@ invisibleNote: "Private Notiz"
|
||||
enableInfiniteScroll: "Automatisch mehr Notizen laden"
|
||||
visibility: "Sichtbarkeit"
|
||||
poll: "Umfrage"
|
||||
useCw: "Inhalt verstecken"
|
||||
useCw: "Inhalt verdecken"
|
||||
enablePlayer: "Video-Player öffnen"
|
||||
disablePlayer: "Video-Player schließen"
|
||||
expandTweet: "Tweet ausklappen"
|
||||
@ -562,7 +564,7 @@ enableAll: "Alle aktivieren"
|
||||
disableAll: "Alle deaktivieren"
|
||||
tokenRequested: "Benutzerkontozugriff gewähren"
|
||||
pluginTokenRequestedDescription: "Dieses Plugin wird die hier konfigurierten Berechtigungen verwenden können."
|
||||
notificationType: "Benachrichtigungstyp"
|
||||
notificationType: "Benachrichtigungsart"
|
||||
edit: "Bearbeiten"
|
||||
useStarForReactionFallback: "Verwende ★ falls das Reaktions-Emoji unbekannt ist"
|
||||
emailConfig: "Email-Server Konfiguration"
|
||||
@ -625,7 +627,7 @@ createNew: "Neu erstellen"
|
||||
optional: "Optional"
|
||||
createNewClip: "Neuen Clip erstellen"
|
||||
public: "Öffentlich"
|
||||
i18nInfo: "Misskey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Unter {link} kannst du mithelfen."
|
||||
i18nInfo: "Misskey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Auf {link} kannst du mithelfen."
|
||||
manageAccessTokens: "Zugriffstoken verwalten"
|
||||
accountInfo: "Benutzerkonto-Informationen"
|
||||
notesCount: "Anzahl von Notizen"
|
||||
@ -669,7 +671,12 @@ left: "Links"
|
||||
center: "Mitte"
|
||||
wide: "Breit"
|
||||
narrow: "Schmal"
|
||||
reloadToApplySetting: "Einstellungen treten nach einer Aktualisierung der Seite in Kraft. Jetzt aktualisieren?"
|
||||
reloadToApplySetting: "Diese Einstellung tritt nach einer Aktualisierung der Seite in Kraft. Jetzt aktualisieren?"
|
||||
showTitlebar: "Titelleiste anzeigen"
|
||||
clearCache: "Cache leeren"
|
||||
onlineUsersCount: "{n} Benutzer sind online"
|
||||
nUsers: "{n} Benutzer"
|
||||
nNotes: "{n} Notizen"
|
||||
_aboutMisskey:
|
||||
about: "Misskey ist Open-Source-Software die von syuilo seit 2014 entwickelt wird."
|
||||
contributors: "Hauptmitwirkende"
|
||||
@ -692,7 +699,7 @@ _mfm:
|
||||
hashtag: "Hashtag"
|
||||
hashtagDescription: "Mit einer Raute und Text kann ein Hashtag angegeben werden."
|
||||
url: "URL"
|
||||
urlDescription: "URLs können angezeigt werden."
|
||||
urlDescription: "Zeigt URLs an."
|
||||
link: "Link"
|
||||
linkDescription: "Ein spezifizierter Textabschnitt kann als URL angezeigt werden."
|
||||
bold: "Fett"
|
||||
@ -731,6 +738,16 @@ _mfm:
|
||||
twitchDescription: "Verleiht eine sehr stark zuckende Animation."
|
||||
spin: "Animation (Rotieren)"
|
||||
spinDescription: "Verleiht eine rotierende Animation."
|
||||
x2: "Groß"
|
||||
x2Description: "Lässt Inhalte größer angezeigt werden."
|
||||
x3: "Sehr groß"
|
||||
x3Description: "Lässt Inhalte noch größer angezeigt werden."
|
||||
x4: "Am größten"
|
||||
x4Description: "Lässt Inhalte noch größer als größer als groß angezeigt werden."
|
||||
blur: "Weichzeichner"
|
||||
blurDescription: "Inhalte durch Weihzeichnung verschwimmen lassen. Durch das Bewegen des Mauszeigers auf den Inhalt wird er klar angezeigt."
|
||||
font: "Schriftart"
|
||||
fontDescription: "Setzt die Schriftart des Inhaltes fest."
|
||||
_reversi:
|
||||
reversi: "Reversi"
|
||||
gameSettings: "Spieleinstellungen"
|
||||
@ -815,7 +832,7 @@ _theme:
|
||||
refConst: "Konstante referenzieren"
|
||||
key: "Schlüssel"
|
||||
func: "Funktionen"
|
||||
funcKind: "Funktionstyp"
|
||||
funcKind: "Funktionsart"
|
||||
argument: "Parameter"
|
||||
basedProp: "Referenzierte Eigenschaft"
|
||||
alpha: "Transparenz"
|
||||
@ -852,9 +869,9 @@ _theme:
|
||||
infoFg: "Text von Informationen"
|
||||
infoWarnBg: "Hintergrund von Warnungen"
|
||||
infoWarnFg: "Text von Informationen"
|
||||
cwBg: "Hintergrund von versteckten Inhalten"
|
||||
cwFg: "Text von versteckten Inhalten"
|
||||
cwHoverBg: "Hintergrund von versteckten Inhalten (Mouseover)"
|
||||
cwBg: "Hintergrund von verdeckten Inhalten"
|
||||
cwFg: "Text von verdeckten Inhalten"
|
||||
cwHoverBg: "Hintergrund von verdeckten Inhalten (Mouseover)"
|
||||
toastBg: "Hintergrund von Benachrichtigungen"
|
||||
toastFg: "Text von Benachrichtigungen"
|
||||
buttonBg: "Hintergrund von Schaltflächen"
|
||||
@ -991,6 +1008,8 @@ _widgets:
|
||||
postForm: "Neue Notiz anfertigen"
|
||||
slideshow: "Diashow"
|
||||
button: "Knopf"
|
||||
onlineUsers: "Benutzer die Online sind"
|
||||
serverMetric: "Servermetriken"
|
||||
_cw:
|
||||
hide: "Ausblenden"
|
||||
show: "Mehr anzeigen"
|
||||
@ -1100,7 +1119,7 @@ _rooms:
|
||||
clearConfirm: "Möchtest du wirklich alle Möbel entfernen?"
|
||||
leaveConfirm: "Es gibt ungespeicherte Änderungen. Möchtest du wirklich gehen?"
|
||||
chooseImage: "Bild auswählen"
|
||||
roomType: "Raumtyp"
|
||||
roomType: "Raumart"
|
||||
carpetColor: "Teppichfarbe"
|
||||
_roomType:
|
||||
default: "Standard"
|
||||
@ -1480,7 +1499,7 @@ _deck:
|
||||
swapRight: "Nach rechts verschieben"
|
||||
swapUp: "Nach oben verschieben"
|
||||
swapDown: "Nach unten verschieben"
|
||||
stackLeft: "Nach links stapeln"
|
||||
stackLeft: "Auf linke Spalte stapeln"
|
||||
popRight: "Nach rechts vom Stapel nehmen"
|
||||
_columns:
|
||||
main: "Hauptspalte"
|
||||
|
@ -1,5 +1,6 @@
|
||||
---
|
||||
_lang_: "English"
|
||||
headlineMisskey: "Network connected by notes"
|
||||
introMisskey: "Welcome! Misskey is an open source, and also a decentralized microblogging service.\nCreate \"notes\" to share what is happening now, or to share it with everyone around you 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes 👍\nLet's explore a new world 🚀"
|
||||
monthAndDay: "{month}/{day}"
|
||||
search: "Search"
|
||||
@ -49,6 +50,7 @@ copyUsername: "Copy username"
|
||||
searchUser: "User search"
|
||||
reply: "Reply"
|
||||
loadMore: "Load more"
|
||||
showMore: "Load more"
|
||||
youGotNewFollower: "Followed you"
|
||||
receiveFollowRequest: "Follow request received"
|
||||
followRequestAccepted: "Follow request accepted"
|
||||
@ -66,7 +68,7 @@ exportRequested: "You have requested an export. This may take a while. After the
|
||||
importRequested: "You requested an import. This may take a while."
|
||||
lists: "Lists"
|
||||
noLists: "You don't have any lists"
|
||||
note: "Notes"
|
||||
note: "Note"
|
||||
notes: "Notes"
|
||||
following: "Following"
|
||||
followers: "Followers"
|
||||
@ -77,7 +79,7 @@ error: "Error"
|
||||
somethingHappened: "An error occurred"
|
||||
retry: "Retry"
|
||||
pageLoadError: "Failed to load page"
|
||||
pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearung the cache and then try again after waiting a little while."
|
||||
pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearing the cache and then try again after waiting a little while."
|
||||
enterListName: "List name"
|
||||
privacy: "Privacy"
|
||||
makeFollowManuallyApprove: "Follow requests require approval"
|
||||
@ -251,7 +253,7 @@ birthday: "Birthday"
|
||||
yearsOld: "{age} years old"
|
||||
registeredDate: "Joined on"
|
||||
location: "Location"
|
||||
theme: "Theme"
|
||||
theme: "Themes"
|
||||
themeForLightMode: "Theme to use in Light Mode"
|
||||
themeForDarkMode: "Theme to use in Dark Mode"
|
||||
light: "Light"
|
||||
@ -669,7 +671,12 @@ left: "Left"
|
||||
center: "Center"
|
||||
wide: "Wide"
|
||||
narrow: "Narrow"
|
||||
reloadToApplySetting: "Settings will be applied upon page reload. Reload now?"
|
||||
reloadToApplySetting: "This setting will be applied upon page reload. Reload now?"
|
||||
showTitlebar: "Show title bar"
|
||||
clearCache: "Clear cache"
|
||||
onlineUsersCount: "{n} people are online"
|
||||
nUsers: "{n} Users"
|
||||
nNotes: "{n} Notes"
|
||||
_aboutMisskey:
|
||||
about: "Misskey is open-source software being developed by syuilo since 2014."
|
||||
contributors: "Main contributors"
|
||||
@ -731,6 +738,16 @@ _mfm:
|
||||
twitchDescription: "Infuses a strongly twitching animation."
|
||||
spin: "Animation (Spin)"
|
||||
spinDescription: "Infuses a spinning animation."
|
||||
x2: "Big"
|
||||
x2Description: "Displays content bigger."
|
||||
x3: "Very big"
|
||||
x3Description: "Displays content even bigger."
|
||||
x4: "Extremely big"
|
||||
x4Description: "Displays content even bigger than bigger than big."
|
||||
blur: "Blur"
|
||||
blurDescription: "Content can be blurred via this effect. It will be clearly displayed by hovering your cursor above it."
|
||||
font: "Font"
|
||||
fontDescription: "Sets the font to display contents in."
|
||||
_reversi:
|
||||
reversi: "Reversi"
|
||||
gameSettings: "Game settings"
|
||||
@ -991,6 +1008,8 @@ _widgets:
|
||||
postForm: "Compose a note"
|
||||
slideshow: "Slideshow"
|
||||
button: "Button"
|
||||
onlineUsers: "Online users"
|
||||
serverMetric: "Server metrics"
|
||||
_cw:
|
||||
hide: "Hide"
|
||||
show: "Load more"
|
||||
@ -1480,7 +1499,7 @@ _deck:
|
||||
swapRight: "Swap to right"
|
||||
swapUp: "Swap with above"
|
||||
swapDown: "Swap with below"
|
||||
stackLeft: "Stack on the left"
|
||||
stackLeft: "Stack on left column"
|
||||
popRight: "Pop to the right"
|
||||
_columns:
|
||||
main: "Main"
|
||||
|
@ -49,6 +49,7 @@ copyUsername: "Copiar nombre de usuario"
|
||||
searchUser: "Búsqueda de usuarios"
|
||||
reply: "Responder"
|
||||
loadMore: "Ver más"
|
||||
showMore: "Ver más"
|
||||
youGotNewFollower: "te ha seguido"
|
||||
receiveFollowRequest: "Recibiste una solicitud de seguimiento"
|
||||
followRequestAccepted: "La solicitud de seguimiento fue aceptada"
|
||||
@ -646,6 +647,7 @@ driveUsage: "Uso del drive"
|
||||
noCrawle: "Rechazar indexación del crawler"
|
||||
noCrawleDescription: "Pedir a los motores de búsqueda que no indexen tu perfil, notas, páginas, etc."
|
||||
clips: "Clip"
|
||||
clearCache: "Limpiar caché"
|
||||
_mfm:
|
||||
cheatSheet: "Hoja de referencia de MFM"
|
||||
intro: "MFM es un lenguaje de marcado dedicado que se puede usar en varios lugares dentro de Misskey. Aquí puede ver una lista de sintaxis disponibles en MFM."
|
||||
@ -663,6 +665,7 @@ _mfm:
|
||||
search: "Buscar"
|
||||
flip: "Echar de un capirotazo"
|
||||
flipDescription: "Voltea el contenido hacia arriba / abajo o hacia la izquierda / derecha."
|
||||
font: "Fuente"
|
||||
_reversi:
|
||||
reversi: "Reversi"
|
||||
gameSettings: "Configuración del juego"
|
||||
|
@ -46,6 +46,7 @@ copyUsername: "Copier le nom d’utilisateur·rice"
|
||||
searchUser: "Chercher un·e utilisateur·rice"
|
||||
reply: "Répondre"
|
||||
loadMore: "Afficher plus …"
|
||||
showMore: "Afficher plus …"
|
||||
youGotNewFollower: "Vous suit"
|
||||
receiveFollowRequest: "Demande d’abonnement reçue"
|
||||
followRequestAccepted: "La demande d’abonnement a été acceptée"
|
||||
@ -577,6 +578,7 @@ fileIdOrUrl: "ID du fichier ou URL"
|
||||
chatOpenBehavior: "Comportement de la fenêtre de discussion lors de son ouverture"
|
||||
random: "Aléatoire"
|
||||
public: "Public"
|
||||
clearCache: "Vider le cache"
|
||||
_mfm:
|
||||
mention: "Mentionner"
|
||||
hashtag: "Hashtags"
|
||||
@ -585,6 +587,7 @@ _mfm:
|
||||
quote: "Citer"
|
||||
emoji: "Émojis personnalisés"
|
||||
search: "Rechercher"
|
||||
font: "Police de caractères"
|
||||
_reversi:
|
||||
total: "Total"
|
||||
_serverDisconnectedBehavior:
|
||||
|
1
locales/ht-HT.yml
Normal file
1
locales/ht-HT.yml
Normal file
@ -0,0 +1 @@
|
||||
---
|
207
locales/id-ID.yml
Normal file
207
locales/id-ID.yml
Normal file
@ -0,0 +1,207 @@
|
||||
---
|
||||
_lang_: "Bahasa Jepang"
|
||||
monthAndDay: "{day} {month}"
|
||||
search: "Pencarian"
|
||||
notifications: "Notifikasi"
|
||||
username: "Nama Pengguna"
|
||||
password: "Kata sandi"
|
||||
ok: "OK"
|
||||
gotIt: "Saya mengerti"
|
||||
cancel: "Batalkan"
|
||||
enterUsername: "Masukkan nama pengguna"
|
||||
renotedBy: "direnote oleh {user}"
|
||||
noNotes: "Tidak ada notes"
|
||||
noNotifications: "Tidak ada notifikasi"
|
||||
settings: "Pengaturan"
|
||||
basicSettings: "Pengaturan umum"
|
||||
otherSettings: "Pengaturan lainnya"
|
||||
openInWindow: "Buka di jendela"
|
||||
profile: "Profil"
|
||||
timeline: "Linimasa"
|
||||
noAccountDescription: "Pengguna ini belum menulis bio"
|
||||
login: "Masuk"
|
||||
loggingIn: "Sedang masuk"
|
||||
logout: "Keluar"
|
||||
signup: "Daftar"
|
||||
uploading: "Sedang mengunggah"
|
||||
save: "Simpan"
|
||||
users: "Pengguna"
|
||||
addUser: "Tambah pengguna"
|
||||
favorite: "Favorit"
|
||||
favorites: "Favorit"
|
||||
unfavorite: "Hapus favorit"
|
||||
favorited: "Ditambahkan ke favorit"
|
||||
alreadyFavorited: "Telah ditambahkan ke favorit"
|
||||
cantFavorite: "Tidak dapat menambahkan ke favorit"
|
||||
pin: "Sematkan ke profil"
|
||||
unpin: "Lepas sematan dari profil"
|
||||
copyContent: "Salin konten"
|
||||
copyLink: "Salin tautan"
|
||||
delete: "Hapus"
|
||||
deleteAndEdit: "Hapus dan sunting"
|
||||
addToList: "Tambahkan ke daftar"
|
||||
sendMessage: "Kirim pesan"
|
||||
copyUsername: "Salin nama pengguna"
|
||||
searchUser: "Cari pengguna"
|
||||
reply: "Balas"
|
||||
cantReRenote: "Renote tidak dapat direnote"
|
||||
quote: "Kutip"
|
||||
pinnedNote: "Note yang disematkan"
|
||||
you: "Anda"
|
||||
clickToShow: "Klik untuk melihat"
|
||||
sensitive: "Konten sensitif"
|
||||
add: "Tambahkan"
|
||||
reaction: "Reaksi"
|
||||
reactionSettingDescription: "Masukkan reaksi favorit yang ingin anda sematkan pada bilah reaksi"
|
||||
reactionSettingDescription2: "Geser untuk memindah urutkan, klik untuk menghapus, tekan \"+\" untuk menambahkan"
|
||||
rememberNoteVisibility: "Ingat pengaturan visibilitas note"
|
||||
attachCancel: "Hapus lampiran"
|
||||
markAsSensitive: "Tandai sebagai konten sensitif"
|
||||
unmarkAsSensitive: "Hapus tanda konten sensitif"
|
||||
enterFileName: "Masukkan nama berkas"
|
||||
mute: "Bisukan"
|
||||
unmute: "Hapus bisukan"
|
||||
block: "Blokir"
|
||||
unblock: "Buka blokir"
|
||||
suspend: "Bekukan"
|
||||
unsuspend: "Buka pembekuan"
|
||||
blockConfirm: "Apakah anda yakin ingin memblokir akun ini?"
|
||||
unblockConfirm: "Apakah anda yakin ingin membuka blokir akun ini?"
|
||||
suspendConfirm: "Apakah anda yakin ingin membekukan akun ini?"
|
||||
unsuspendConfirm: "Apakah anda yakin ingin membuka pembekuan akun ini?"
|
||||
selectList: "Pilih daftar"
|
||||
selectAntenna: "Pilih Antena"
|
||||
selectWidget: "Pilih gawit"
|
||||
editWidgets: "Sunting gawit"
|
||||
editWidgetsExit: "Selesai"
|
||||
customEmojis: "Emoji kustom"
|
||||
emoji: "Emoji"
|
||||
emojiName: "Nama emoji"
|
||||
emojiUrl: "URL Emoji"
|
||||
addEmoji: "Tambahkan emoji"
|
||||
settingGuide: "Pengaturan rekomendasi"
|
||||
cacheRemoteFiles: "Tembolokkan berkas remote"
|
||||
flagAsBot: "Atur akun ini sebagai Bot"
|
||||
flagAsCat: "Atur akun ini sebagai kucing"
|
||||
autoAcceptFollowed: "Setujui otomatis permintaan mengikuti dari pengguna yang anda ikuti"
|
||||
addAcount: "Tambahkan akun"
|
||||
loginFailed: "Gagal untuk masuk"
|
||||
showOnRemote: "Lihat profil asli"
|
||||
general: "Umum"
|
||||
wallpaper: "Wallpaper"
|
||||
setWallpaper: "Atur wallpaper"
|
||||
removeWallpaper: "Hapus wallpaper"
|
||||
intro: "Instalasi Misskey telah selesai! Mohon untuk membuat pengguna admin."
|
||||
done: "Selesai"
|
||||
processing: "Memproses"
|
||||
preview: "Pratinjau"
|
||||
default: "Bawaan"
|
||||
noCustomEmojis: "Tidak ada emoji kustom"
|
||||
federating: "memfederasi"
|
||||
blocked: "Diblokir"
|
||||
all: "Semua"
|
||||
subscribing: "Berlangganan"
|
||||
publishing: "Sedang menyiarkan langsung"
|
||||
notResponding: "Tidak ada respon"
|
||||
instanceFollowing: "Mengikuti instance"
|
||||
instanceFollowers: "Pengikut instance"
|
||||
instanceUsers: "Pengguna pada instance ini"
|
||||
changePassword: "Ubah kata sandi"
|
||||
security: "Keamanan"
|
||||
retypedNotMatch: "Input tidak sama"
|
||||
currentPassword: "Kata sandi saat ini"
|
||||
newPassword: "Kata sandi baru"
|
||||
newPasswordRetype: "Ulangi kata sandi baru"
|
||||
attachFile: "Lampirkan berkas"
|
||||
more: "Lagi !"
|
||||
featured: "Sorotan"
|
||||
usernameOrUserId: "Nama pengguna atau User ID"
|
||||
noSuchUser: "Pengguna tidak ditemukan"
|
||||
lookup: "Mencari"
|
||||
announcements: "Pengumuman"
|
||||
imageUrl: "URL Gambar"
|
||||
remove: "Hapus"
|
||||
removed: "Telah dihapus"
|
||||
removeAreYouSure: "Apakah anda yakin ingin menghapus \"{x}\"?"
|
||||
deleteAreYouSure: "Apakah anda yakin ingin menghapus \"{x}\"?"
|
||||
saved: "Telah disimpan"
|
||||
messaging: "Pesan"
|
||||
upload: "Unggah"
|
||||
fromDrive: "Dari Drive"
|
||||
fromUrl: "Dari URL"
|
||||
uploadFromUrl: "Unggah dari URL"
|
||||
uploadFromUrlDescription: "URL berkas yang ingin anda unggah"
|
||||
uploadFromUrlRequested: "Pengunggahan telah diminta"
|
||||
uploadFromUrlMayTakeTime: "Membutuhkan beberapa waktu hingga pengunggahan selesai"
|
||||
explore: "Jelajahi"
|
||||
games: "Permainan Misskey"
|
||||
messageRead: "Telah dibaca"
|
||||
noMoreHistory: "Tidak ada sejarah lagi"
|
||||
startMessaging: "Mulai mengirim pesan"
|
||||
nUsersRead: "Dibaca oleh {n}"
|
||||
nsfw: "Konten sensitif"
|
||||
watch: "Tonton"
|
||||
unwatch: "Batal tonton"
|
||||
accept: "Terima"
|
||||
reject: "Tolak"
|
||||
normal: "Normal"
|
||||
instanceName: "Nama instance"
|
||||
instanceDescription: "Tentang instance"
|
||||
maintainerName: "Pengelola"
|
||||
maintainerEmail: "Surel pengelola"
|
||||
tosUrl: "URL Syarat dan Ketentuan"
|
||||
thisYear: "Tahun ini"
|
||||
thisMonth: "Bulan ini"
|
||||
today: "Hari ini"
|
||||
dayX: "{day}"
|
||||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Halaman"
|
||||
integration: "Integrasi"
|
||||
connectSerice: "Sambungkan"
|
||||
disconnectSerice: "Putuskan"
|
||||
enableLocalTimeline: "Nyalakan linimasa lokal"
|
||||
enableGlobalTimeline: "Nyalakan linimasa global"
|
||||
registration: "Pendaftaran"
|
||||
enableRegistration: "Nyalakan pendaftaran pengguna baru"
|
||||
invite: "Undang"
|
||||
proxyRemoteFiles: "Proksi berkas remote"
|
||||
driveCapacityPerLocalAccount: "Kapasitas drive per pengguna lokal"
|
||||
driveCapacityPerRemoteAccount: "Kapasitas drive per pengguna remote"
|
||||
inMb: "dalam Megabytes"
|
||||
iconUrl: "URL Gambar ikon"
|
||||
bannerUrl: "URL Banner"
|
||||
basicInfo: "Informasi Umum"
|
||||
pinnedUsers: "Pengguna yang disematkan"
|
||||
pinnedPages: "Halaman yang disematkan"
|
||||
pinnedNotes: "Note yang disematkan"
|
||||
invites: "Undang"
|
||||
invitations: "Undang"
|
||||
smtpUser: "Nama Pengguna"
|
||||
smtpPass: "Kata sandi"
|
||||
_mfm:
|
||||
quote: "Kutip"
|
||||
emoji: "Emoji kustom"
|
||||
search: "Pencarian"
|
||||
_sfx:
|
||||
notification: "Notifikasi"
|
||||
chat: "Pesan"
|
||||
_widgets:
|
||||
notifications: "Notifikasi"
|
||||
timeline: "Linimasa"
|
||||
_profile:
|
||||
username: "Nama Pengguna"
|
||||
_exportOrImport:
|
||||
muteList: "Bisukan"
|
||||
blockingList: "Blokir"
|
||||
_rooms:
|
||||
_roomType:
|
||||
default: "Bawaan"
|
||||
_notification:
|
||||
_types:
|
||||
quote: "Kutip"
|
||||
reaction: "Reaksi"
|
||||
_deck:
|
||||
_columns:
|
||||
notifications: "Notifikasi"
|
||||
tl: "Linimasa"
|
1
locales/it-IT.yml
Normal file
1
locales/it-IT.yml
Normal file
@ -0,0 +1 @@
|
||||
---
|
@ -1,5 +1,6 @@
|
||||
_lang_: "日本語"
|
||||
|
||||
headlineMisskey: "ノートでつながるネットワーク"
|
||||
introMisskey: "ようこそ!Misskeyは、オープンソースの分散型マイクロブログサービスです。\n「ノート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "検索"
|
||||
@ -49,6 +50,7 @@ copyUsername: "ユーザー名をコピー"
|
||||
searchUser: "ユーザーを検索"
|
||||
reply: "返信"
|
||||
loadMore: "もっと見る"
|
||||
showMore: "もっと見る"
|
||||
youGotNewFollower: "フォローされました"
|
||||
receiveFollowRequest: "フォローリクエストされました"
|
||||
followRequestAccepted: "フォローが承認されました"
|
||||
@ -670,6 +672,11 @@ center: "中央"
|
||||
wide: "広い"
|
||||
narrow: "狭い"
|
||||
reloadToApplySetting: "設定はページリロード後に反映されます。今すぐリロードしますか?"
|
||||
showTitlebar: "タイトルバーを表示する"
|
||||
clearCache: "キャッシュをクリア"
|
||||
onlineUsersCount: "{n}人がオンライン"
|
||||
nUsers: "{n}ユーザー"
|
||||
nNotes: "{n}ノート"
|
||||
|
||||
_aboutMisskey:
|
||||
about: "Misskeyはsyuiloによって2014年から開発されている、オープンソースのソフトウェアです。"
|
||||
@ -729,11 +736,21 @@ _mfm:
|
||||
bounce: "アニメーション(バウンド)"
|
||||
bounceDescription: "ぽよんぽよん弾むようなアニメーションを与えます。"
|
||||
shake: "アニメーション(ぶるぶる)"
|
||||
shakeDescription: "ぶるぶるするアニメーションを与えます。"
|
||||
shakeDescription: "ぶるぶる震えるアニメーションを与えます。"
|
||||
twitch: "アニメーション(ブレ)"
|
||||
twitchDescription: "激しくブレるアニメーションを与えます。"
|
||||
spin: "アニメーション(回転)"
|
||||
spinDescription: "回転するアニメーションを与えます。"
|
||||
x2: "大きく"
|
||||
x2Description: "内容を大きく表示します。"
|
||||
x3: "とても大きく"
|
||||
x3Description: "内容をとても大きく表示します。"
|
||||
x4: "究極に大きく"
|
||||
x4Description: "内容を究極に大きく表示します。"
|
||||
blur: "ぼかし"
|
||||
blurDescription: "内容をぼかすことができます。ポインターを上に乗せるとはっきり見えるようになります。"
|
||||
font: "フォント"
|
||||
fontDescription: "内容のフォントを指定することができます。"
|
||||
|
||||
_reversi:
|
||||
reversi: "リバーシ"
|
||||
@ -1012,6 +1029,9 @@ _widgets:
|
||||
postForm: "投稿フォーム"
|
||||
slideshow: "スライドショー"
|
||||
button: "ボタン"
|
||||
onlineUsers: "オンラインユーザー"
|
||||
jobQueue: "ジョブキュー"
|
||||
serverMetric: "サーバーメトリクス"
|
||||
|
||||
_cw:
|
||||
hide: "隠す"
|
||||
|
@ -49,6 +49,7 @@ copyUsername: "ユーザー名をコピー"
|
||||
searchUser: "ユーザーを検索"
|
||||
reply: "返す"
|
||||
loadMore: "もっとあるやろ!"
|
||||
showMore: "もっとあるやろ!"
|
||||
youGotNewFollower: "フォローされたで"
|
||||
receiveFollowRequest: "フォローリクエストされたで"
|
||||
followRequestAccepted: "フォローが承認されたで"
|
||||
@ -454,6 +455,7 @@ remote: "リモート"
|
||||
smtpHost: "ホスト"
|
||||
smtpUser: "ユーザー名"
|
||||
smtpPass: "パスワード"
|
||||
clearCache: "キャッシュにさいなら"
|
||||
_mfm:
|
||||
mention: "メンション"
|
||||
quote: "引用"
|
||||
|
@ -13,6 +13,7 @@ delete: "Kkes"
|
||||
addToList: "Rnu ɣer tebdart"
|
||||
reply: "Err"
|
||||
loadMore: "Wali ugar"
|
||||
showMore: "Wali ugar"
|
||||
youGotNewFollower: "Yeṭṭafaṛ-ik·em-id"
|
||||
mention: "Bder"
|
||||
import: "Kter"
|
||||
@ -38,6 +39,7 @@ smtpPass: "Awal uffir"
|
||||
_mfm:
|
||||
mention: "Bder"
|
||||
search: "Nadi"
|
||||
font: "Tasefsit"
|
||||
_theme:
|
||||
keys:
|
||||
mention: "Bder"
|
||||
|
@ -40,6 +40,7 @@ sendMessage: "ಸಂದೇಶ ಕಳುಹಿಸು"
|
||||
copyUsername: "ಬಳಕೆಹೆಸರು ನಕಲಿಸು"
|
||||
reply: "ಉತ್ತರಿಸು"
|
||||
loadMore: "ಇನ್ನಷ್ಟು ನೋಡು"
|
||||
showMore: "ಇನ್ನಷ್ಟು ನೋಡು"
|
||||
youGotNewFollower: "ಹಿಂಬಾಲಿಸಿದರು"
|
||||
receiveFollowRequest: "ಹಿಂಬಾಲನೆ ವಿನಂತಿ ಬಂದಿದೆ"
|
||||
followRequestAccepted: "ಹಿಂಬಾಲನೆ ವಿನಂತಿ ಸ್ವೀಕರಿಸಲಾಯಿತು"
|
||||
|
@ -46,6 +46,7 @@ copyUsername: "유저명 복사"
|
||||
searchUser: "사용자 검색"
|
||||
reply: "답글"
|
||||
loadMore: "더 보기"
|
||||
showMore: "더 보기"
|
||||
youGotNewFollower: "새로운 팔로워가 있습니다"
|
||||
receiveFollowRequest: "새로운 팔로우 요청이 있습니다"
|
||||
followRequestAccepted: "팔로우가 수락되었습니다"
|
||||
@ -612,6 +613,7 @@ lockedAccountInfo: "팔로우를 승인으로 승인받더라도 노트의 공
|
||||
alwaysMarkSensitive: "미디어를 항상 열람 주의로 설정"
|
||||
emailVerified: "메일 주소가 확인되었습니다."
|
||||
clips: "클립"
|
||||
clearCache: "캐시 비우기"
|
||||
_nsfw:
|
||||
ignore: "열람 주의 미디어 항상 표시"
|
||||
_mfm:
|
||||
@ -642,6 +644,7 @@ _mfm:
|
||||
flip: "플립"
|
||||
flipDescription: "내용을 상하 또는 좌우로 반전시킵니다."
|
||||
jump: "애니메이션(점프)"
|
||||
font: "폰트"
|
||||
_reversi:
|
||||
reversi: "리버시"
|
||||
gameSettings: "대국 설정"
|
||||
|
@ -1,23 +1,608 @@
|
||||
---
|
||||
_lang_: "język polski"
|
||||
headlineMisskey: "Sieć połączona wpisami"
|
||||
monthAndDay: "{month}-{day}"
|
||||
search: "Szukaj"
|
||||
notifications: "Powiadomienia"
|
||||
username: "Nazwa użytkownika"
|
||||
password: "Hasło"
|
||||
fetchingAsApObject: "Pobieranie z Fediwersum…"
|
||||
ok: "OK"
|
||||
gotIt: "Rozumiem!"
|
||||
cancel: "Anuluj"
|
||||
enterUsername: "Wprowadź nazwę użytkownika"
|
||||
renotedBy: "Udostępniono przez {user}"
|
||||
noNotes: "Brak wpisów"
|
||||
noNotifications: "Brak powiadomień"
|
||||
instance: "Instancja"
|
||||
settings: "Ustawienia"
|
||||
basicSettings: "Podstawowe ustawienia"
|
||||
otherSettings: "Pozostałe ustawienia"
|
||||
openInWindow: "Otwórz w oknie"
|
||||
profile: "Profil"
|
||||
timeline: "Oś czasu"
|
||||
noAccountDescription: "Ten użytkownik nie napisał jeszcze swojej biografii."
|
||||
login: "Zaloguj się"
|
||||
loggingIn: "Logowanie"
|
||||
logout: "Wyloguj się"
|
||||
signup: "Zarejestruj się"
|
||||
uploading: "Wysyłanie"
|
||||
save: "Zapisz"
|
||||
users: "Użytkownicy"
|
||||
addUser: "Dodaj użytkownika"
|
||||
favorite: "Dodaj do ulubionych"
|
||||
favorites: "Ulubione"
|
||||
unfavorite: "Usuń z ulubionych"
|
||||
favorited: "Dodano do ulubionych."
|
||||
alreadyFavorited: "Już jest w ulubionych."
|
||||
cantFavorite: "Nie można dodać do ulubionych."
|
||||
pin: "Przypnij do profilu"
|
||||
unpin: "Odepnij z profilu"
|
||||
copyContent: "Skopiuj zawartość"
|
||||
copyLink: "Skopiuj odnośnik"
|
||||
delete: "Usuń"
|
||||
deleteAndEdit: "Usuń i edytuj"
|
||||
deleteAndEditConfirm: "Czy na pewno chcesz usunąć ten wpis i zedytować go? Utracisz wszystkie reakcje, udostępnienia i odpowiedzi do tego wpisu."
|
||||
addToList: "Dodaj do listy"
|
||||
sendMessage: "Wyślij wiadomość"
|
||||
copyUsername: "Kopiuj nazwę użytkownika"
|
||||
searchUser: "Wyszukiwanie użytkowników"
|
||||
reply: "Odpowiedz"
|
||||
loadMore: "Załaduj więcej"
|
||||
showMore: "Załaduj więcej"
|
||||
youGotNewFollower: "Zaobserwował(a) Cię"
|
||||
receiveFollowRequest: "Otrzymano prośbę o możliwość obserwacji"
|
||||
followRequestAccepted: "Zaakceptowano prośbę o możliwość obserwacji"
|
||||
mention: "Wspomnij"
|
||||
mentions: "Wspomnienia"
|
||||
directNotes: "Bezpośrednie wpisy"
|
||||
importAndExport: "Import i eksport"
|
||||
import: "Importuj"
|
||||
export: "Eksportuj"
|
||||
files: "Pliki"
|
||||
download: "Pobierz"
|
||||
lists: "Listy"
|
||||
noLists: "Nie masz żadnych list"
|
||||
note: "Wpis"
|
||||
notes: "Wpisy"
|
||||
following: "Obserwowani"
|
||||
followers: "Obserwujący"
|
||||
followsYou: "Obserwuje Cię"
|
||||
createList: "Utwórz listę"
|
||||
manageLists: "Zarządzaj listami"
|
||||
error: "Błąd"
|
||||
somethingHappened: "Coś poszło nie tak"
|
||||
retry: "Spróbuj ponownie"
|
||||
pageLoadError: "Nie udało się załadować strony"
|
||||
pageLoadErrorDescription: "Zwykle jest to spowodowane problemem z siecią lub cache przeglądarki. Spróbuj wyczyścić cache i sprawdź jeszcze raz za chwilę."
|
||||
enterListName: "Nazwa listy"
|
||||
privacy: "Prywatność"
|
||||
makeFollowManuallyApprove: "Prośby o możliwość obserwacji wymagają zatwierdzenia"
|
||||
defaultNoteVisibility: "Domyślna widoczność"
|
||||
follow: "Obserwowani"
|
||||
followRequest: "Poproś o możliwość obserwacji"
|
||||
followRequests: "Prośby o możliwość obserwacji"
|
||||
unfollow: "Przestań obserwować"
|
||||
followRequestPending: "Oczekująca prośba o możliwość obserwacji"
|
||||
enterEmoji: "Wprowadź emoji"
|
||||
renote: "Udostępnij"
|
||||
unrenote: "Cofnij udostępnienie"
|
||||
renoted: "Udostępniono."
|
||||
cantRenote: "Ten wpis nie może zostać udostępniony."
|
||||
cantReRenote: "Udostępnienie nie może zostać udostępnione."
|
||||
quote: "Cytuj"
|
||||
pinnedNote: "Przypięty wpis"
|
||||
you: "Ty"
|
||||
clickToShow: "Kliknij, aby wyświetlić"
|
||||
sensitive: "NSFW"
|
||||
add: "Dodaj"
|
||||
reaction: "Reakcja"
|
||||
reactionSettingDescription: "Przypisz swoje ulubione reakcje, które chcesz przypiąć w wyborze reakcji."
|
||||
reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, naciśnij „+” aby dodać"
|
||||
rememberNoteVisibility: "Zapamiętuj ustawienia widoczności wpisu"
|
||||
attachCancel: "Usuń załącznik"
|
||||
markAsSensitive: "Oznacz jako NSFW"
|
||||
unmarkAsSensitive: "Cofnij NSFW"
|
||||
enterFileName: "Wprowadź nazwę pliku"
|
||||
mute: "Wycisz"
|
||||
unmute: "Cofnij wyciszenie"
|
||||
block: "Zablokuj"
|
||||
unblock: "Odblokuj"
|
||||
suspend: "Zawieś"
|
||||
unsuspend: "Cofnij zawieszenie"
|
||||
blockConfirm: "Czy na pewno chcesz zablokować to konto?"
|
||||
unblockConfirm: "Czy na pewno chcesz odblokować to konto?"
|
||||
suspendConfirm: "Czy na pewno chcesz zawiesić to konto?"
|
||||
unsuspendConfirm: "Czy na pewno chcesz cofnąć zawieszenie tego konta?"
|
||||
selectList: "Wybierz listę"
|
||||
selectAntenna: "Wybierz Antennę"
|
||||
selectWidget: "Wybierz widżet"
|
||||
editWidgets: "Edytuj widżet"
|
||||
editWidgetsExit: "Gotowe"
|
||||
customEmojis: "Niestandardowe emoji"
|
||||
emoji: "Emoji"
|
||||
emojiName: "Nazwa emoji"
|
||||
emojiUrl: "Adres URL emoji"
|
||||
addEmoji: "Dodaj emoji"
|
||||
settingGuide: "Proponowana konfiguracja"
|
||||
cacheRemoteFiles: "Przechowuj zdalne pliki w pamięci podręcznej"
|
||||
flagAsBot: "To konto jest botem"
|
||||
flagAsCat: "To konto jest kotem"
|
||||
flagAsCatDescription: "Przełącz tę opcję, aby konto było oznaczone jako kot."
|
||||
autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, których obserwujesz"
|
||||
addAcount: "Dodaj konto"
|
||||
loginFailed: "Nie udało się zalogować"
|
||||
showOnRemote: "Zobacz na zdalnej instancji"
|
||||
general: "Ogólne"
|
||||
wallpaper: "Tapeta"
|
||||
setWallpaper: "Ustaw tapetę"
|
||||
removeWallpaper: "Usuń tapetę"
|
||||
searchWith: "Szukaj: {q}"
|
||||
youHaveNoLists: "Nie masz żadnej listy"
|
||||
followConfirm: "Czy na pewno chcesz zaobserwować {name}?"
|
||||
proxyAccount: "Konto proxy"
|
||||
host: "Host"
|
||||
selectUser: "Wybierz użytkownika"
|
||||
recipient: "Odbiorca"
|
||||
annotation: "Komentarze"
|
||||
federation: "Federacja"
|
||||
instances: "Instancja"
|
||||
registeredAt: "Zarejestrowano"
|
||||
latestRequestSentAt: "Ostatnie żądanie wysłano o"
|
||||
latestRequestReceivedAt: "Ostatnie żądanie otrzymano o"
|
||||
latestStatus: "Najnowszy status"
|
||||
storageUsage: "Użycie pamięci"
|
||||
charts: "Wykresy"
|
||||
perHour: "co godzinę"
|
||||
perDay: "co dzień"
|
||||
stopActivityDelivery: "Przestań przesyłać aktywności"
|
||||
blockThisInstance: "Zablokuj tę instancję"
|
||||
operations: "Działania"
|
||||
software: "Oprogramowanie"
|
||||
version: "Wersja"
|
||||
metadata: "Metadane"
|
||||
withNFiles: "{n} plik(i)"
|
||||
monitor: "Monitor"
|
||||
jobQueue: "Kolejka zadań"
|
||||
cpuAndMemory: "CPU i pamięć"
|
||||
network: "Sieć"
|
||||
disk: "Dysk"
|
||||
instanceInfo: "Informacje o instancji"
|
||||
statistics: "Statystyki"
|
||||
clearQueue: "Wyczyść kolejkę"
|
||||
clearQueueConfirmTitle: "Czy na pewno chcesz wyczyścić kolejkę?"
|
||||
done: "Gotowe"
|
||||
processing: "Przetwarzanie"
|
||||
federating: "Federowanie"
|
||||
suspended: "Zawieszono"
|
||||
all: "Wszystkie"
|
||||
subscribing: "Subskrybowanie"
|
||||
publishing: "Publikowanie"
|
||||
notResponding: "Nie odpowiada"
|
||||
instanceFollowing: "Obserwowani na instancji"
|
||||
instanceFollowers: "Obserwujący na instancji"
|
||||
instanceUsers: "Użytkownicy tej instancji"
|
||||
more: "Więcej!"
|
||||
usernameOrUserId: "Nazwa lub id użytkownika"
|
||||
noSuchUser: "Nie znaleziono użytkownika"
|
||||
announcements: "Ogłoszenia"
|
||||
imageUrl: "Adres URL obrazka"
|
||||
remove: "Usuń"
|
||||
removed: "Pomyślnie usunięto"
|
||||
removeAreYouSure: "Czy na pewno chcesz usunąć „{x}”?"
|
||||
deleteAreYouSure: "Czy na pewno chcesz usunąć „{x}”?"
|
||||
resetAreYouSure: "Czy na pewno chcesz zresetować?"
|
||||
saved: "Zapisano"
|
||||
messaging: "Wiadomości"
|
||||
upload: "Wyślij"
|
||||
fromDrive: "Z dysku"
|
||||
fromUrl: "Z adresu URL"
|
||||
uploadFromUrl: "Wyślij z adresu URL"
|
||||
uploadFromUrlDescription: "Adres URL pliku, który chcesz wysłać"
|
||||
uploadFromUrlRequested: "Zażądano wysłania"
|
||||
explore: "Eksploruj"
|
||||
games: "Gry Misskey"
|
||||
messageRead: "Przeczytano"
|
||||
noMoreHistory: "Nie ma dalszej historii"
|
||||
startMessaging: "Rozpocznij czat"
|
||||
nUsersRead: "przeczytano przez {n}"
|
||||
agreeTo: "Wyrażam zgodę na {0}"
|
||||
tos: "Regulamin"
|
||||
start: "Rozpocznij"
|
||||
home: "Strona główna"
|
||||
activity: "Aktywność"
|
||||
images: "Zdjęcia"
|
||||
birthday: "Data urodzenia"
|
||||
yearsOld: "{age} lat"
|
||||
registeredDate: "Zarejestrowano"
|
||||
location: "Lokalizacja"
|
||||
theme: "Motywy"
|
||||
themeForLightMode: "Motyw używany w trybie jasnym"
|
||||
themeForDarkMode: "Motyw używany w trybie ciemnym"
|
||||
light: "Jasny"
|
||||
dark: "Ciemny"
|
||||
lightThemes: "Jasny motyw"
|
||||
darkThemes: "Ciemny motyw"
|
||||
syncDeviceDarkMode: "Synchronizuj ciemny motyw z ustawieniami urządzenia"
|
||||
drive: "Dysk"
|
||||
fileName: "Nazwa pliku"
|
||||
selectFile: "Wybierz plik"
|
||||
selectFiles: "Wybierz pliki"
|
||||
selectFolder: "Wybierz folder"
|
||||
selectFolders: "Wybierz foldery"
|
||||
renameFile: "Zmień nazwę pliku"
|
||||
avatar: "Awatar"
|
||||
banner: "Baner"
|
||||
nsfw: "NSFW"
|
||||
accept: "Akceptuj"
|
||||
reject: "Odrzuć"
|
||||
normal: "Normalny"
|
||||
thisYear: "Rok"
|
||||
thisMonth: "Miesiąc"
|
||||
today: "Dziś"
|
||||
pages: "Strony"
|
||||
integration: "Integracja"
|
||||
connectSerice: "Połącz"
|
||||
disconnectSerice: "Rozłącz"
|
||||
registration: "Zarejestruj się"
|
||||
invite: "Zaproś"
|
||||
inMb: "W megabajtach"
|
||||
iconUrl: "Adres URL ikony"
|
||||
bannerUrl: "Adres URL banera"
|
||||
basicInfo: "Podstawowe informacje"
|
||||
pinnedUsers: "Przypięty użytkownik"
|
||||
pinnedPages: "Przypięte strony"
|
||||
pinnedNotes: "Przypięty wpis"
|
||||
hcaptcha: "hCaptcha"
|
||||
enableHcaptcha: "Włącz hCaptcha"
|
||||
hcaptchaSiteKey: "Klucz strony"
|
||||
hcaptchaSecretKey: "Tajny klucz"
|
||||
recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "Włącz reCAPTCHA"
|
||||
recaptchaSiteKey: "Klucz strony"
|
||||
recaptchaSecretKey: "Tajny klucz"
|
||||
antennas: "Anteny"
|
||||
manageAntennas: "Zarządzaj Antenami"
|
||||
name: "Nazwa"
|
||||
antennaSource: "Źródło Anteny"
|
||||
userList: "Listy"
|
||||
about: "Informacje"
|
||||
administrator: "Admin"
|
||||
token: "Token"
|
||||
share: "Udostępnij"
|
||||
help: "Pomoc"
|
||||
close: "Zamknij"
|
||||
invites: "Zaproś"
|
||||
transfer: "Transfer"
|
||||
title: "Tytuł"
|
||||
text: "Tekst"
|
||||
retype: "Wprowadź ponownie"
|
||||
invitations: "Zaproś"
|
||||
tags: "Tagi"
|
||||
regenerate: "Wygeneruj ponownie"
|
||||
local: "Lokalne"
|
||||
total: "Łącznie"
|
||||
weekOverWeekChanges: "Cotygodniowo"
|
||||
dayOverDayChanges: "Codziennie"
|
||||
numberOfDays: "Liczba dni"
|
||||
listen: "Słuchaj"
|
||||
none: "Brak"
|
||||
volume: "Głośność"
|
||||
details: "Szczegóły"
|
||||
state: "Stan"
|
||||
sort: "Sortuj"
|
||||
ascendingOrder: "Rosnąco"
|
||||
descendingOrder: "Malejąco"
|
||||
output: "Wyjście"
|
||||
divider: "Rozdzielacz"
|
||||
rooms: "Pokój"
|
||||
relays: "Przekaźniki"
|
||||
description: "Opis"
|
||||
author: "Autor"
|
||||
manage: "Zarządzanie"
|
||||
deck: "Tablica"
|
||||
height: "Wysokość"
|
||||
permission: "Uprawnienia"
|
||||
edit: "Edytuj"
|
||||
email: "Adres e-mail"
|
||||
smtpHost: "Host"
|
||||
smtpPort: "Port"
|
||||
smtpUser: "Nazwa użytkownika"
|
||||
smtpPass: "Hasło"
|
||||
display: "Wyświetlanie"
|
||||
copy: "Kopiuj"
|
||||
overview: "Przegląd"
|
||||
logs: "Dzienniki"
|
||||
delayed: "Opóźnione"
|
||||
create: "Utwórz"
|
||||
other: "Inne"
|
||||
behavior: "Zachowanie"
|
||||
abuseReports: "Zgłoszenia"
|
||||
reportAbuse: "Zgłoś"
|
||||
send: "Wyślij"
|
||||
optional: "Nieobowiązkowe"
|
||||
yes: "Tak"
|
||||
no: "Nie"
|
||||
notSet: "Nie ustawiono"
|
||||
contact: "Kontakt"
|
||||
duplicate: "Duplikuj"
|
||||
center: "Wyśsrodkuj"
|
||||
wide: "Szerokie"
|
||||
narrow: "Wąskie"
|
||||
_aboutMisskey:
|
||||
patrons: "Wspierający"
|
||||
_mfm:
|
||||
mention: "Wspomnij"
|
||||
url: "Adres URL"
|
||||
link: "Odnośnik"
|
||||
bold: "Pogrubienie"
|
||||
quote: "Cytuj"
|
||||
emoji: "Niestandardowe emoji"
|
||||
search: "Szukaj"
|
||||
flip: "Odwróć"
|
||||
font: "Czcionka"
|
||||
_reversi:
|
||||
rules: "Zasady"
|
||||
surrender: "Poddaj się"
|
||||
total: "Łącznie"
|
||||
ended: "Zakończono"
|
||||
playing: "W trakcie gry"
|
||||
_channel:
|
||||
featured: "Na czasie"
|
||||
owned: "Własny"
|
||||
_sidebar:
|
||||
full: "Pełne"
|
||||
icon: "Awatar"
|
||||
hide: "Ukryj"
|
||||
_wordMute:
|
||||
soft: "Łagodny"
|
||||
hard: "Twardy"
|
||||
_theme:
|
||||
base: "Podstawowy"
|
||||
constant: "Stała"
|
||||
key: "Klucz"
|
||||
func: "Funkcje"
|
||||
argument: "Argument"
|
||||
darken: "Ściemnij"
|
||||
lighten: "Rozjaśnij"
|
||||
keys:
|
||||
bg: "Tło"
|
||||
fg: "Tekst"
|
||||
panel: "Panel"
|
||||
link: "Odnośnik"
|
||||
mention: "Wspomnij"
|
||||
renote: "Udostępnij"
|
||||
divider: "Rozdzielacz"
|
||||
badge: "Odznaka"
|
||||
_sfx:
|
||||
note: "Wpisy"
|
||||
notification: "Powiadomienia"
|
||||
chat: "Wiadomości"
|
||||
_ago:
|
||||
unknown: "Nieznane"
|
||||
future: "W przyszłości"
|
||||
justNow: "Przed chwilą"
|
||||
yearsAgo: "{n} lat temu"
|
||||
_permissions:
|
||||
"write:votes": "Głosowanie w ankiecie"
|
||||
_weekday:
|
||||
sunday: "Niedziela"
|
||||
monday: "Poniedziałek"
|
||||
tuesday: "Wtorek"
|
||||
wednesday: "Środa"
|
||||
thursday: "Czwartek"
|
||||
friday: "Piątek"
|
||||
saturday: "Sobota"
|
||||
_widgets:
|
||||
memo: "Przypięte notatki"
|
||||
notifications: "Powiadomienia"
|
||||
timeline: "Oś czasu"
|
||||
trends: "Na czasie"
|
||||
clock: "Zegar"
|
||||
activity: "Aktywność"
|
||||
photos: "Zdjęcia"
|
||||
federation: "Federacja"
|
||||
button: "Przycisk"
|
||||
_cw:
|
||||
hide: "Ukryj"
|
||||
show: "Załaduj więcej"
|
||||
_poll:
|
||||
expiration: "Ankieta kończy się"
|
||||
infinite: "Nigdy"
|
||||
deadlineDate: "Data zakończenia"
|
||||
duration: "Czas trwania"
|
||||
votesCount: "{} głosów"
|
||||
vote: "Głosowanie w ankiecie"
|
||||
voted: "Zagłosowano"
|
||||
closed: "Zakończono"
|
||||
_visibility:
|
||||
home: "Strona główna"
|
||||
followers: "Obserwujący"
|
||||
_profile:
|
||||
name: "Nazwa"
|
||||
username: "Nazwa użytkownika"
|
||||
description: "Biografia"
|
||||
metadataLabel: "Etykieta"
|
||||
metadataContent: "Treść"
|
||||
_exportOrImport:
|
||||
followingList: "Obserwowani"
|
||||
muteList: "Wycisz"
|
||||
blockingList: "Zablokuj"
|
||||
userLists: "Listy"
|
||||
_timelines:
|
||||
home: "Strona główna"
|
||||
local: "Lokalne"
|
||||
_rooms:
|
||||
translate: "Przenieś"
|
||||
rotate: "Obróć"
|
||||
exit: "Wróć"
|
||||
remove: "Usuń"
|
||||
clear: "Usuń wszystkie"
|
||||
carpetColor: "Kolor dywanu"
|
||||
_roomType:
|
||||
washitsu: "W japońskim stylu"
|
||||
_furnitures:
|
||||
bed: "Łóżko"
|
||||
desk: "Biurko"
|
||||
chair: "Krzesło"
|
||||
fan: "Chłodzenie"
|
||||
pencil: "Ołówek"
|
||||
pudding: "Budyń"
|
||||
book2: "Książka 2"
|
||||
piano: "Fortepian"
|
||||
monitor: "Monitor"
|
||||
photoframe: "Ramka do zdjęć"
|
||||
tv: "Telewizor"
|
||||
sofa: "Kanapa"
|
||||
bin: "Kosz"
|
||||
banknote: "Banknot"
|
||||
_pages:
|
||||
like: "Lubię"
|
||||
featured: "Wyróżnione"
|
||||
variables: "Zmienne"
|
||||
title: "Tytuł"
|
||||
font: "Czcionka"
|
||||
fontSerif: "Szeryfowa"
|
||||
inputBlocks: "Wejście"
|
||||
specialBlocks: "Specjalne"
|
||||
blocks:
|
||||
text: "Tekst"
|
||||
image: "Zdjęcia"
|
||||
button: "Przycisk"
|
||||
if: "Jeżeli"
|
||||
_if:
|
||||
variable: "Zmienna"
|
||||
_post:
|
||||
text: "Treść"
|
||||
_textInput:
|
||||
name: "Nazwa zmiennej"
|
||||
text: "Tytuł"
|
||||
_textareaInput:
|
||||
name: "Nazwa zmiennej"
|
||||
text: "Tytuł"
|
||||
_numberInput:
|
||||
name: "Nazwa zmiennej"
|
||||
text: "Tytuł"
|
||||
_canvas:
|
||||
height: "Wysokość"
|
||||
_switch:
|
||||
name: "Nazwa zmiennej"
|
||||
text: "Tytuł"
|
||||
_counter:
|
||||
name: "Nazwa zmiennej"
|
||||
text: "Tytuł"
|
||||
inc: "Zwiększ o"
|
||||
_button:
|
||||
text: "Tytuł"
|
||||
colored: "Kolorowe"
|
||||
_action:
|
||||
_dialog:
|
||||
content: "Treść"
|
||||
_pushEvent:
|
||||
no-variable: "Brak"
|
||||
_callAiScript:
|
||||
functionName: "Nazwa funkcji"
|
||||
radioButton: "Wybór"
|
||||
_radioButton:
|
||||
name: "Nazwa zmiennej"
|
||||
title: "Tytuł"
|
||||
script:
|
||||
categories:
|
||||
flow: "Kontrola przepływu"
|
||||
operation: "Obliczanie"
|
||||
comparison: "Porównanie"
|
||||
fn: "Funkcje"
|
||||
convert: "Transformacja"
|
||||
list: "Listy"
|
||||
blocks:
|
||||
text: "Tekst"
|
||||
_strLen:
|
||||
arg1: "Tekst"
|
||||
_strPick:
|
||||
arg1: "Tekst"
|
||||
_strReplace:
|
||||
arg1: "Tekst"
|
||||
_strReverse:
|
||||
arg1: "Tekst"
|
||||
_join:
|
||||
arg1: "Listy"
|
||||
arg2: "Odstęp"
|
||||
add: "Dodaj"
|
||||
subtract: "Odejmij"
|
||||
multiply: "Pomnóż"
|
||||
divide: "Podziel"
|
||||
_round:
|
||||
arg1: "Liczba"
|
||||
if: "Warunek"
|
||||
_if:
|
||||
arg1: "Jeżeli"
|
||||
arg2: "Jeżeli prawda"
|
||||
not: "NIE"
|
||||
_not:
|
||||
arg1: "NIE"
|
||||
_random:
|
||||
arg1: "Prawdopodobieństwo"
|
||||
rannum: "Losowa liczba"
|
||||
_rannum:
|
||||
arg1: "Minimalna wartość"
|
||||
arg2: "Maksymalna wartość"
|
||||
_randomPick:
|
||||
arg1: "Listy"
|
||||
_dailyRandom:
|
||||
arg1: "Prawdopodobieństwo"
|
||||
_dailyRannum:
|
||||
arg1: "Minimalna wartość"
|
||||
arg2: "Maksymalna wartość"
|
||||
_dailyRandomPick:
|
||||
arg1: "Listy"
|
||||
_seedRandom:
|
||||
arg1: "Ziarno"
|
||||
arg2: "Prawdopodobieństwo"
|
||||
_seedRannum:
|
||||
arg1: "Ziarno"
|
||||
arg2: "Minimalna wartość"
|
||||
arg3: "Maksymalna wartość"
|
||||
_seedRandomPick:
|
||||
arg1: "Ziarno"
|
||||
arg2: "Listy"
|
||||
_pick:
|
||||
arg1: "Listy"
|
||||
arg2: "Położenie"
|
||||
_listLen:
|
||||
arg1: "Listy"
|
||||
number: "Liczba"
|
||||
_stringToNumber:
|
||||
arg1: "Tekst"
|
||||
_numberToString:
|
||||
arg1: "Liczba"
|
||||
_splitStrByLine:
|
||||
arg1: "Tekst"
|
||||
ref: "Zmienne"
|
||||
fn: "Funkcje"
|
||||
_fn:
|
||||
arg1: "Wyjście"
|
||||
_for:
|
||||
arg1: "Liczba powtórzeń"
|
||||
arg2: "Działanie"
|
||||
types:
|
||||
string: "Tekst"
|
||||
number: "Liczba"
|
||||
boolean: "Flaguj"
|
||||
array: "Listy"
|
||||
_notification:
|
||||
youWereFollowed: "Zaobserwował(a) Cię"
|
||||
_types:
|
||||
all: "Wszystkie"
|
||||
follow: "Obserwowani"
|
||||
mention: "Wspomnij"
|
||||
renote: "Udostępnij"
|
||||
quote: "Cytuj"
|
||||
reaction: "Reakcja"
|
||||
_deck:
|
||||
_columns:
|
||||
main: "Główna"
|
||||
notifications: "Powiadomienia"
|
||||
tl: "Oś czasu"
|
||||
antenna: "Anteny"
|
||||
list: "Listy"
|
||||
mentions: "Wspomnienia"
|
||||
|
@ -49,6 +49,7 @@ copyUsername: "Скопировать имя пользователя"
|
||||
searchUser: "Поиск людей"
|
||||
reply: "Ответить"
|
||||
loadMore: "Показать еще"
|
||||
showMore: "Показать еще"
|
||||
youGotNewFollower: "Новый подписчик"
|
||||
receiveFollowRequest: "Получен запрос на подписку"
|
||||
followRequestAccepted: "Запрос на подписку принят"
|
||||
@ -667,6 +668,12 @@ showGapBetweenNotesInTimeline: "Показывать разделитель ме
|
||||
duplicate: "Дубликат"
|
||||
left: "Влево"
|
||||
center: "По центру"
|
||||
reloadToApplySetting: "Это настройка вступает в силу при загрузке страницы. Перезагрузить сейчас?"
|
||||
showTitlebar: "Показать заголовок"
|
||||
clearCache: "Очистить кэш"
|
||||
onlineUsersCount: "Пользователей сейчас в сети: {n}"
|
||||
nUsers: "Пользователей: {n}"
|
||||
nNotes: "Заметок: {n}"
|
||||
_aboutMisskey:
|
||||
about: "Misskey — программа с открытым исходным кодом, которую разрабатывает syuilo с 2014 года."
|
||||
contributors: "Основные соавторы"
|
||||
@ -728,6 +735,7 @@ _mfm:
|
||||
twitchDescription: "Заставляет трястись как одержимого"
|
||||
spin: "Вращение"
|
||||
spinDescription: "Так можно крутить содержимое в разных направлениях."
|
||||
font: "Шрифт"
|
||||
_reversi:
|
||||
reversi: "Реверси"
|
||||
gameSettings: "Настройки игры"
|
||||
@ -988,6 +996,7 @@ _widgets:
|
||||
postForm: "Форма отправки"
|
||||
slideshow: "Показ слайдов"
|
||||
button: "Кнопка"
|
||||
onlineUsers: "Пользователи сейчас с сети"
|
||||
_cw:
|
||||
hide: "Спрятать"
|
||||
show: "Показать еще"
|
||||
|
1
locales/th-TH.yml
Normal file
1
locales/th-TH.yml
Normal file
@ -0,0 +1 @@
|
||||
---
|
@ -46,6 +46,7 @@ copyUsername: "Скопіювати ім’я користувача"
|
||||
searchUser: "Пошук користувачів"
|
||||
reply: "Відповісти"
|
||||
loadMore: "Показати більше"
|
||||
showMore: "Показати більше"
|
||||
youGotNewFollower: "Новий підписник"
|
||||
receiveFollowRequest: "Отримано запит на підписку"
|
||||
followRequestAccepted: "Підписка прийнята"
|
||||
@ -611,6 +612,7 @@ useSystemFont: "Використовувати стандартний шрифт
|
||||
clips: "Добірка"
|
||||
experimentalFeatures: "Експериментальні функції"
|
||||
developer: "Розробник"
|
||||
clearCache: "Очистити кеш"
|
||||
_aboutMisskey:
|
||||
about: "Misskey - це програмне забезпечення з відкритим кодом, яке розробляє syuilo з 2014 року."
|
||||
contributors: "Головні помічники"
|
||||
@ -664,6 +666,7 @@ _mfm:
|
||||
shake: "Анімація (Shake)"
|
||||
twitch: "Анімація (Twitch)"
|
||||
spin: "Анімація (Spin)"
|
||||
font: "Шрифт"
|
||||
_reversi:
|
||||
reversi: "Реверсі"
|
||||
gameSettings: "Налаштування гри"
|
||||
|
@ -1,5 +1,6 @@
|
||||
---
|
||||
_lang_: "中文(简体)"
|
||||
headlineMisskey: "通过帖子连接在一起的网络"
|
||||
introMisskey: "欢迎!Misskey是一个开源的、去中心化的“微博客”服务。\n通过编写「帖文」来和大家分享你的以及你周围的事情吧!📡\n通过「回应」功能,可以让你快速地对大家的帖文表达反馈👍\n来探索新的世界吧!🚀"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "搜索"
|
||||
@ -49,6 +50,7 @@ copyUsername: "复制用户名"
|
||||
searchUser: "搜索用户"
|
||||
reply: "回复"
|
||||
loadMore: "查看更多"
|
||||
showMore: "查看更多"
|
||||
youGotNewFollower: "你有新的关注者"
|
||||
receiveFollowRequest: "您收到了关注请求"
|
||||
followRequestAccepted: "您的关注请求被通过了"
|
||||
@ -109,12 +111,12 @@ unmarkAsSensitive: "取消标记为敏感内容"
|
||||
enterFileName: "请输入文件名"
|
||||
mute: "屏蔽"
|
||||
unmute: "解除屏蔽"
|
||||
block: "屏蔽"
|
||||
unblock: "取消屏蔽"
|
||||
block: "拉黑"
|
||||
unblock: "取消拉黑"
|
||||
suspend: "冻结"
|
||||
unsuspend: "解除冻结"
|
||||
blockConfirm: "确定要屏蔽吗?"
|
||||
unblockConfirm: "确定要解除屏蔽吗?"
|
||||
blockConfirm: "确定要拉黑吗?"
|
||||
unblockConfirm: "确定要解除拉黑吗?"
|
||||
suspendConfirm: "要冻结吗?"
|
||||
unsuspendConfirm: "要解除冻结吗?"
|
||||
selectList: "选择列表"
|
||||
@ -183,8 +185,8 @@ clearCachedFilesConfirm: "确定要清除缓存文件?"
|
||||
blockedInstances: "被阻拦的实例"
|
||||
blockedInstancesDescription: "设定要阻拦的实例,以换行来进行分割。被阻拦的实例将无法与本实例进行交换通讯。"
|
||||
muteAndBlock: "屏蔽/拉黑"
|
||||
mutedUsers: "禁言用户"
|
||||
blockedUsers: "已屏蔽用户"
|
||||
mutedUsers: "已屏蔽用户"
|
||||
blockedUsers: "被拉黑的用户"
|
||||
noUsers: "无用户"
|
||||
editProfile: "编辑资料"
|
||||
noteDeleteConfirm: "要删除该帖子吗?"
|
||||
@ -197,7 +199,7 @@ default: "默认"
|
||||
noCustomEmojis: "没有自定义表情符号"
|
||||
noJobs: "没有任务"
|
||||
federating: "联合中"
|
||||
blocked: "已拦截"
|
||||
blocked: "已拉黑"
|
||||
suspended: "停止推流"
|
||||
all: "全部"
|
||||
subscribing: "已订阅"
|
||||
@ -670,6 +672,11 @@ center: "中央"
|
||||
wide: "宽"
|
||||
narrow: "窄"
|
||||
reloadToApplySetting: "页面刷新后设置才会生效。是否现在刷新页面?"
|
||||
showTitlebar: "显示标题栏"
|
||||
clearCache: "清除缓存"
|
||||
onlineUsersCount: "{n}人在线"
|
||||
nUsers: "{n}用户"
|
||||
nNotes: "{n}帖子"
|
||||
_aboutMisskey:
|
||||
about: "Misskey是由syuilo于2014年开发的开源软件。"
|
||||
contributors: "主要贡献者"
|
||||
@ -731,6 +738,16 @@ _mfm:
|
||||
twitchDescription: "显示强烈颤抖的动画效果。"
|
||||
spin: "动画(回转)"
|
||||
spinDescription: "显示回转的动画效果。"
|
||||
x2: "大"
|
||||
x2Description: "以大尺寸显示内容。"
|
||||
x3: "非常大"
|
||||
x3Description: "以更大尺寸显示内容。"
|
||||
x4: "最大"
|
||||
x4Description: "以最大尺寸显示内容。"
|
||||
blur: "模糊"
|
||||
blurDescription: "产生模糊效果。将鼠标指针放在上面即可将内容显示出来。"
|
||||
font: "字体"
|
||||
fontDescription: "可以设置内容所使用的字体。"
|
||||
_reversi:
|
||||
reversi: "黑白棋"
|
||||
gameSettings: "对局设置"
|
||||
@ -991,6 +1008,8 @@ _widgets:
|
||||
postForm: "投稿窗口"
|
||||
slideshow: "幻灯片展示"
|
||||
button: "按钮"
|
||||
onlineUsers: "在线用户"
|
||||
serverMetric: "服务器指标"
|
||||
_cw:
|
||||
hide: "隐藏"
|
||||
show: "查看更多"
|
||||
@ -1056,7 +1075,7 @@ _exportOrImport:
|
||||
allNotes: "所有帖子"
|
||||
followingList: "关注中"
|
||||
muteList: "屏蔽"
|
||||
blockingList: "屏蔽"
|
||||
blockingList: "拉黑"
|
||||
userLists: "列表"
|
||||
_charts:
|
||||
federationInstancesIncDec: "联合:增加/减少"
|
||||
|
@ -1,12 +1,12 @@
|
||||
---
|
||||
_lang_: "繁體中文"
|
||||
introMisskey: "歡迎! Misskey是一個開源的去中心化的社群網站。\n通過「貼文」來分享現在發生的事情吧! 📡\n「反應」功能,可以讓你快速的對大家的「帖子」來表達感情👍\n一起來探索新的世界吧! 🚀"
|
||||
introMisskey: "歡迎! Misskey是一個開源且去中心化的社群網絡。\n通過「箋文」分享周邊新鮮事,並告訴其他人您的想法!📡\n透過「情感」功能,對大家的箋文表達情感!👍\n一起來探索這個新的世界吧!🚀"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "搜尋"
|
||||
notifications: "通知"
|
||||
username: "使用者名稱"
|
||||
password: "密碼"
|
||||
fetchingAsApObject: "從 Fediverse 查詢中..."
|
||||
fetchingAsApObject: "從聯邦宇宙取得中..."
|
||||
ok: "OK"
|
||||
gotIt: "知道了"
|
||||
cancel: "取消"
|
||||
@ -31,38 +31,42 @@ save: "儲存"
|
||||
users: "使用者"
|
||||
addUser: "新增使用者"
|
||||
favorite: "收藏"
|
||||
favorites: "已加星號"
|
||||
favorites: "已收藏"
|
||||
unfavorite: "取消收藏"
|
||||
favorited: "已添加至收藏夾"
|
||||
alreadyFavorited: "已經有添加入收藏夾過了"
|
||||
cantFavorite: "無法添加至收藏夾"
|
||||
pin: "置頂"
|
||||
unpin: "取消置頂"
|
||||
copyContent: "複製內容"
|
||||
copyLink: "複製連結"
|
||||
delete: "刪除"
|
||||
deleteAndEdit: "刪除並編輯"
|
||||
deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有反應,轉發和回覆也將會消失。"
|
||||
addToList: "添加至清單"
|
||||
deleteAndEditConfirm: "要刪除並再次編輯嗎?此箋文的所有情感、轉發和回覆也將會消失。"
|
||||
addToList: "新增至清單"
|
||||
sendMessage: "發送訊息"
|
||||
copyUsername: "複製用戶名"
|
||||
searchUser: "搜尋用戶"
|
||||
reply: "回覆"
|
||||
loadMore: "瀏覽更多"
|
||||
loadMore: "載入更多"
|
||||
showMore: "載入更多"
|
||||
youGotNewFollower: "您有新的追隨者"
|
||||
receiveFollowRequest: "收到追隨請求"
|
||||
receiveFollowRequest: "您有新的追隨請求"
|
||||
followRequestAccepted: "追隨請求已接受"
|
||||
mention: "提及"
|
||||
mentions: "提及"
|
||||
directNotes: "私信"
|
||||
importAndExport: "匯入 / 匯出"
|
||||
directNotes: "指定使用者發佈"
|
||||
importAndExport: "匯入與匯出"
|
||||
import: "匯入"
|
||||
export: "匯出"
|
||||
files: "檔案"
|
||||
download: "下載"
|
||||
driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的貼文也會跟著消失。"
|
||||
unfollowConfirm: "確定要取消對{name}的追隨嗎?"
|
||||
driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的箋文也會跟著消失。\n"
|
||||
unfollowConfirm: "確定要取消追隨{name}嗎?"
|
||||
exportRequested: "已請求匯出。這可能會花一點時間。結束後檔案將會被放到雲端裡。"
|
||||
importRequested: "已請求匯入。這可能會花一點時間"
|
||||
lists: "清單"
|
||||
noLists: "沒有清單"
|
||||
noLists: "你沒有任何清單"
|
||||
note: "箋文"
|
||||
notes: "箋文"
|
||||
following: "追隨中"
|
||||
@ -74,28 +78,32 @@ error: "錯誤"
|
||||
somethingHappened: "發生錯誤"
|
||||
retry: "重試"
|
||||
pageLoadError: "載入頁面失敗"
|
||||
pageLoadErrorDescription: "這通常是因為網路錯誤或是瀏覽器快取殘留的原因。請先清除瀏覽器快取,稍後再重試"
|
||||
enterListName: "輸入清單名稱"
|
||||
privacy: "隱私"
|
||||
makeFollowManuallyApprove: "手動審核追隨請求"
|
||||
defaultNoteVisibility: "預設的筆記隱私權"
|
||||
defaultNoteVisibility: "預設可見性"
|
||||
follow: "追隨"
|
||||
followRequest: "追隨請求"
|
||||
followRequests: "追隨請求"
|
||||
unfollow: "取消追隨"
|
||||
followRequestPending: "追隨許可批准中"
|
||||
enterEmoji: "輸入表情符號"
|
||||
renote: "轉發貼文"
|
||||
unrenote: "取消轉發貼文"
|
||||
renote: "轉箋"
|
||||
unrenote: "取消轉箋"
|
||||
renoted: "轉發成功"
|
||||
cantRenote: "無法轉發此箋文。"
|
||||
cantReRenote: "無法轉發之前已經轉發過的內容"
|
||||
quote: "引用"
|
||||
pinnedNote: "已置頂的貼文"
|
||||
pinnedNote: "已置頂的箋文"
|
||||
you: "您"
|
||||
clickToShow: "按一下以顯示"
|
||||
sensitive: "敏感內容"
|
||||
add: "新增"
|
||||
reaction: "反應"
|
||||
reaction: "情感"
|
||||
reactionSettingDescription: "置頂「反應」表情符號\n"
|
||||
rememberNoteVisibility: "記住筆記隱私設定"
|
||||
reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。"
|
||||
rememberNoteVisibility: "記住箋文可見性"
|
||||
attachCancel: "移除附件"
|
||||
markAsSensitive: "標記為敏感內容"
|
||||
unmarkAsSensitive: "取消標記為敏感內容"
|
||||
@ -105,7 +113,7 @@ unmute: "解除靜音"
|
||||
block: "封鎖"
|
||||
unblock: "解除封鎖"
|
||||
suspend: "凍結"
|
||||
unsuspend: "解凍"
|
||||
unsuspend: "解除凍結"
|
||||
blockConfirm: "確定要封鎖此用戶?"
|
||||
unblockConfirm: "確定解除封鎖此用戶?"
|
||||
suspendConfirm: "確定凍結此帳號?"
|
||||
@ -114,7 +122,7 @@ selectList: "選擇清單"
|
||||
selectAntenna: "選擇天線"
|
||||
selectWidget: "選擇小工具"
|
||||
editWidgets: "編輯小工具"
|
||||
editWidgetsExit: "停止編輯"
|
||||
editWidgetsExit: "完成"
|
||||
customEmojis: "自訂表情符號"
|
||||
emoji: "表情符號"
|
||||
emojiName: "表情符號名稱"
|
||||
@ -122,11 +130,13 @@ emojiUrl: "表情符號URL"
|
||||
addEmoji: "新增表情符號"
|
||||
settingGuide: "推薦設定"
|
||||
cacheRemoteFiles: "緩存非遠程檔案"
|
||||
cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間。但資料會因直接連線從而產生額外連接數據。"
|
||||
cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間,但資料會因直接連線從而產生額外連接數據。"
|
||||
flagAsBot: "此使用者是機器人"
|
||||
flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整Misskey內部系統將本帳戶識別為機器人"
|
||||
flagAsCat: "此使用者是貓"
|
||||
autoAcceptFollowed: "自動許可追隨"
|
||||
addAcount: "新增帳號"
|
||||
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
|
||||
autoAcceptFollowed: "自動追隨中使用者的追隨請求"
|
||||
addAcount: "新增帳戶"
|
||||
loginFailed: "登入失敗"
|
||||
showOnRemote: "轉到所在實例顯示"
|
||||
general: "一般"
|
||||
@ -134,15 +144,15 @@ wallpaper: "桌布"
|
||||
setWallpaper: "設定桌布"
|
||||
removeWallpaper: "移除桌布"
|
||||
searchWith: "搜尋: {q}"
|
||||
youHaveNoLists: "沒有任何清單"
|
||||
followConfirm: "你真的要關注{name}嗎?"
|
||||
youHaveNoLists: "你沒有任何清單"
|
||||
followConfirm: "你真的要追隨{name}嗎?"
|
||||
proxyAccount: "代理帳號"
|
||||
proxyAccountDescription: "代理帳號是在某些情況下充當其他服務器用戶的帳號。例如,當用戶將一個來自其他服務器的帳號放在列表中時,由於沒有其他用戶關注該帳號,該指令不會傳送到該服務器上,因此會由代理帳戶關注。"
|
||||
host: "主機"
|
||||
selectUser: "選取使用者"
|
||||
recipient: "發送至"
|
||||
recipient: "收件人"
|
||||
annotation: "註解"
|
||||
federation: "聯邦宇宙"
|
||||
federation: "站台聯邦"
|
||||
instances: "實例"
|
||||
registeredAt: "初次觀測"
|
||||
latestRequestSentAt: "上次發送的請求"
|
||||
@ -156,7 +166,7 @@ blockThisInstance: "封鎖此實例"
|
||||
operations: "操作"
|
||||
software: "軟體"
|
||||
version: "版本"
|
||||
metadata: "元資料(Metadata)"
|
||||
metadata: "元資料"
|
||||
withNFiles: "{n}個檔案"
|
||||
monitor: "監視器"
|
||||
jobQueue: "佇列"
|
||||
@ -172,13 +182,13 @@ clearCachedFiles: "清除快取資料"
|
||||
clearCachedFilesConfirm: "確定要清除緩存資料嗎?"
|
||||
blockedInstances: "已封鎖的實例"
|
||||
blockedInstancesDescription: "請逐行輸入需要封鎖的實例。已封鎖的實例將無法與本實例進行通訊。"
|
||||
muteAndBlock: "靜音/封鎖"
|
||||
muteAndBlock: "靜音和封鎖"
|
||||
mutedUsers: "已靜音用戶"
|
||||
blockedUsers: "已封鎖用戶"
|
||||
noUsers: "無用戶"
|
||||
noUsers: "沒有任何使用者"
|
||||
editProfile: "編輯個人檔案"
|
||||
noteDeleteConfirm: "確定刪除此貼文嗎?"
|
||||
pinLimitExceeded: "不能再置頂更多的貼文了"
|
||||
noteDeleteConfirm: "確定刪除此箋文嗎?"
|
||||
pinLimitExceeded: "不能置頂更多箋文了"
|
||||
intro: "Misskey 部署完成!請開設管理員帳號!"
|
||||
done: "完成"
|
||||
processing: "處理中"
|
||||
@ -191,30 +201,31 @@ blocked: "已封鎖"
|
||||
suspended: "已凍結"
|
||||
all: "全部"
|
||||
subscribing: "訂閱中"
|
||||
publishing: "現正直播"
|
||||
publishing: "直播中"
|
||||
notResponding: "沒有回應"
|
||||
instanceFollowing: "追蹤實例"
|
||||
instanceFollowers: "追蹤實例"
|
||||
instanceUsers: "用戶"
|
||||
changePassword: "修改密碼"
|
||||
security: "安全性"
|
||||
retypedNotMatch: "不相符的輸入內容"
|
||||
currentPassword: "現在的密碼"
|
||||
newPassword: "新的密碼"
|
||||
newPasswordRetype: "新的密碼(再輸入一次)"
|
||||
retypedNotMatch: "兩次輸入不一致。"
|
||||
currentPassword: "目前密碼"
|
||||
newPassword: "新密碼"
|
||||
newPasswordRetype: "確認密碼"
|
||||
attachFile: "添加附件"
|
||||
more: "更多!"
|
||||
featured: "精選"
|
||||
usernameOrUserId: "使用者名稱或使用者 ID"
|
||||
usernameOrUserId: "使用者名稱或用戶ID"
|
||||
noSuchUser: "使用者不存在"
|
||||
lookup: "查詢"
|
||||
announcements: "公告"
|
||||
imageUrl: "圖片URL"
|
||||
remove: "刪除"
|
||||
removed: "成功移除"
|
||||
removed: "已刪除"
|
||||
removeAreYouSure: "確定要刪掉「{x}」嗎?"
|
||||
deleteAreYouSure: "確定要刪掉「{x}」嗎?"
|
||||
saved: "已保存"
|
||||
resetAreYouSure: "確定要重設嗎?"
|
||||
saved: "已儲存"
|
||||
messaging: "傳送訊息"
|
||||
upload: "上傳"
|
||||
fromDrive: "從雲端"
|
||||
@ -259,9 +270,9 @@ folderName: "資料夾名稱"
|
||||
createFolder: "新增資料夾"
|
||||
renameFolder: "重新命名資料夾"
|
||||
deleteFolder: "刪除資料夾"
|
||||
addFile: "添加檔案"
|
||||
addFile: "添加附件"
|
||||
emptyDrive: "雲端硬碟為空"
|
||||
emptyFolder: "空的資料夾"
|
||||
emptyFolder: "資料夾為空"
|
||||
unableToDelete: "無法刪除"
|
||||
inputNewFileName: "輸入檔案名稱"
|
||||
inputNewFolderName: "輸入新資料夾的名稱"
|
||||
@ -269,7 +280,7 @@ circularReferenceFolder: "目標文件夾是您要移動的文件夾的子文件
|
||||
hasChildFilesOrFolders: "此文件夾不是空的,無法刪除。"
|
||||
copyUrl: "複製URL"
|
||||
rename: "重新命名"
|
||||
avatar: "頭像"
|
||||
avatar: "大頭貼"
|
||||
banner: "橫幅"
|
||||
nsfw: "敏感內容"
|
||||
whenServerDisconnected: "與服務器的連接中斷時"
|
||||
@ -283,11 +294,11 @@ accept: "接受"
|
||||
reject: "拒絕"
|
||||
normal: "正常"
|
||||
instanceName: "實例名稱"
|
||||
instanceDescription: "實例描述"
|
||||
instanceDescription: "實例說明"
|
||||
maintainerName: "管理員名稱"
|
||||
maintainerEmail: "管理員信箱"
|
||||
maintainerEmail: "管理員郵箱"
|
||||
tosUrl: "服務條款URL"
|
||||
thisYear: "今年"
|
||||
thisYear: "本年"
|
||||
thisMonth: "本月"
|
||||
today: "本日"
|
||||
dayX: "{day}日"
|
||||
@ -313,7 +324,9 @@ bannerUrl: "橫幅圖片URL"
|
||||
basicInfo: "基本資訊"
|
||||
pinnedUsers: "置頂用戶"
|
||||
pinnedUsersDescription: "在「發現」頁面中使用換行標記想要置頂的用戶。"
|
||||
pinnedNotes: "已置頂的貼文"
|
||||
pinnedPages: "釘選頁面"
|
||||
pinnedPagesDescription: "輸入要固定至實例首頁的頁面路徑,以換行符分隔。"
|
||||
pinnedNotes: "已置頂的箋文"
|
||||
hcaptcha: "hCaptcha"
|
||||
enableHcaptcha: "啟用 hCaptcha"
|
||||
hcaptchaSiteKey: "網站金鑰"
|
||||
@ -330,7 +343,7 @@ antennaSource: "接收來源"
|
||||
antennaKeywords: "包含關鍵字"
|
||||
antennaExcludeKeywords: "排除關鍵字"
|
||||
antennaKeywordsDescription: "用空格分隔指定AND、用換行符分隔指定OR"
|
||||
notifyAntenna: "通知我有新的貼文"
|
||||
notifyAntenna: "通知有新箋文"
|
||||
withFileAntenna: "僅帶有附件的箋文"
|
||||
serviceworker: "ServiceWorker"
|
||||
enableServiceworker: "開啟 ServiceWorker"
|
||||
@ -338,7 +351,7 @@ antennaUsersDescription: "指定用換行符分隔的用戶名"
|
||||
caseSensitive: "區分大小寫"
|
||||
withReplies: "包含回覆"
|
||||
connectedTo: "您的帳號已連接到以下社交帳號"
|
||||
notesAndReplies: "貼文與回覆"
|
||||
notesAndReplies: "箋文與回覆"
|
||||
withFiles: "附件"
|
||||
silence: "禁言"
|
||||
silenceConfirm: "確定要禁言此用戶嗎?"
|
||||
@ -355,7 +368,7 @@ userList: "清單"
|
||||
about: "資訊"
|
||||
aboutMisskey: "關於 Misskey"
|
||||
administrator: "管理員"
|
||||
token: "令牌"
|
||||
token: "權杖"
|
||||
twoStepAuthentication: "雙重身份驗證"
|
||||
moderator: "板主"
|
||||
nUsersMentioned: "提到了{n}"
|
||||
@ -374,7 +387,7 @@ notFoundDescription: "找不到與指定URL回應的頁面"
|
||||
uploadFolder: "預設上傳資料夾"
|
||||
cacheClear: "清除快取"
|
||||
markAsReadAllNotifications: "標記所有通知為已讀"
|
||||
markAsReadAllUnreadNotes: "標記所有貼文為已讀"
|
||||
markAsReadAllUnreadNotes: "標記所有箋文為已讀"
|
||||
markAsReadAllTalkMessages: "標記所有訊息為已讀"
|
||||
help: "幫助"
|
||||
inputMessageHere: "在此輸入訊息"
|
||||
@ -397,7 +410,7 @@ next: "下一步"
|
||||
retype: "重新輸入"
|
||||
noteOf: "{user}的箋文"
|
||||
inviteToGroup: "邀請至群組"
|
||||
maxNoteTextLength: "貼文的字數限制"
|
||||
maxNoteTextLength: "箋文的字數限制"
|
||||
quoteAttached: "引用"
|
||||
quoteQuestion: "是否要引用?"
|
||||
noMessagesYet: "沒有訊息"
|
||||
@ -428,6 +441,7 @@ useOsNativeEmojis: "使用OS原生表情符號"
|
||||
youHaveNoGroups: "找不到群組"
|
||||
joinOrCreateGroup: "請加入現有群組,或創建新群組。"
|
||||
noHistory: "沒有歷史紀錄"
|
||||
signinHistory: "登錄歷史"
|
||||
disableAnimatedMfm: "禁用MFM動畫"
|
||||
doing: "正在進行"
|
||||
category: "類別"
|
||||
@ -437,7 +451,9 @@ createAccount: "建立帳戶"
|
||||
existingAcount: "現有帳戶"
|
||||
regenerate: "再生"
|
||||
fontSize: "字體大小"
|
||||
noFollowRequests: "沒有要求跟隨您的申請"
|
||||
openImageInNewTab: "於新分頁中開啟圖片"
|
||||
dashboard: "儀表板"
|
||||
local: "本地"
|
||||
remote: "遠端"
|
||||
total: "合計"
|
||||
@ -446,10 +462,10 @@ dayOverDayChanges: "與前一日相比"
|
||||
appearance: "外觀"
|
||||
clientSettings: "用戶端設定"
|
||||
accountSettings: "帳號設定"
|
||||
promotion: "推廣貼文"
|
||||
promotion: "推廣"
|
||||
promote: "推廣"
|
||||
numberOfDays: "有效天數"
|
||||
hideThisNote: "隱藏此貼文"
|
||||
hideThisNote: "隱藏此箋文"
|
||||
showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
|
||||
objectStorageBaseUrl: "Base URL"
|
||||
objectStorageBucket: "儲存空間(Bucket)"
|
||||
@ -459,14 +475,18 @@ objectStorageEndpointDesc: "如要使用AWS S3,請留空。否則請根據伺
|
||||
objectStorageRegion: "地域(Region)"
|
||||
objectStorageUseSSL: "使用SSL"
|
||||
objectStorageUseProxy: "使用網路代理"
|
||||
objectStorageSetPublicRead: "上載時設定為\"public-read\""
|
||||
serverLogs: "伺服器日誌"
|
||||
deleteAll: "刪除所有記錄"
|
||||
newNoteRecived: "有新的箋文"
|
||||
showFixedPostForm: "於時間軸頁頂顯示「發送箋文」方框"
|
||||
newNoteRecived: "發現新的箋文"
|
||||
sounds: "音效"
|
||||
listen: "聆聽"
|
||||
none: "無"
|
||||
showInPage: "在頁面中顯示"
|
||||
popout: "彈出型窗口"
|
||||
volume: "音量"
|
||||
masterVolume: "主音量"
|
||||
details: "詳細資訊"
|
||||
chooseEmoji: "選擇您的表情符號\n"
|
||||
unableToProcess: "操作無法完成"
|
||||
@ -484,10 +504,12 @@ descendingOrder: "降冪"
|
||||
scratchpad: "暫存記憶體"
|
||||
output: "輸出"
|
||||
script: "腳本"
|
||||
disablePagesScript: "停用頁面的AiScript腳本"
|
||||
updateRemoteUser: "更新非本地用戶資料"
|
||||
deleteAllFiles: "刪除所有檔案"
|
||||
deleteAllFilesConfirm: "要删除所有檔案吗?"
|
||||
removeAllFollowing: "解除所有追隨"
|
||||
removeAllFollowingDescription: "解除{host}所有的跟隨。在實例不再存在時執行。"
|
||||
userSuspended: "該用戶已被凍結"
|
||||
userSilenced: "該用戶已被禁言。"
|
||||
sidebar: "側邊列"
|
||||
@ -499,10 +521,10 @@ addRelay: "添加中繼"
|
||||
inboxUrl: "私信URL"
|
||||
addedRelays: "已添加的中繼"
|
||||
serviceworkerInfo: "您需要啟用推送通知"
|
||||
deletedNote: "已删除的貼文"
|
||||
deletedNote: "已删除的箋文"
|
||||
invisibleNote: "隱藏的帖子"
|
||||
enableInfiniteScroll: "啟用自動滾動頁面模式"
|
||||
visibility: "公開範圍"
|
||||
visibility: "可見性"
|
||||
poll: "投票"
|
||||
useCw: "隱藏內容"
|
||||
enablePlayer: "打開播放器"
|
||||
@ -517,6 +539,8 @@ plugins: "插件"
|
||||
pluginInstallWarn: "請不要安裝來源不明的插件。"
|
||||
deck: "多欄模式"
|
||||
undeck: "取消多欄模式"
|
||||
useBlurEffectForModal: "在模態框使用模糊效果"
|
||||
useFullReactionPicker: "使用大尺寸的情感選擇欄"
|
||||
width: "寬度"
|
||||
height: "高度"
|
||||
large: "大"
|
||||
@ -542,6 +566,8 @@ smtpPass: "密碼"
|
||||
emptyToDisableSmtpAuth: "留空使用者名稱和密碼以禁用SMTP驗證。"
|
||||
testEmail: "郵件測試發送"
|
||||
wordMute: "靜音文字"
|
||||
userSaysSomething: "{name}說了什麼"
|
||||
makeActive: "啟用"
|
||||
display: "檢視"
|
||||
copy: "複製"
|
||||
metrics: "指標"
|
||||
@ -552,42 +578,91 @@ database: "資料庫"
|
||||
channel: "頻道"
|
||||
create: "新增"
|
||||
notificationSetting: "通知設定"
|
||||
notificationSettingDesc: "選擇顯示通知的類型"
|
||||
useGlobalSetting: "使用全域設定"
|
||||
other: "其他"
|
||||
regenerateLoginTokenDescription: "再生用於登入的內部權杖。一般情況下是不需要這樣做的。一旦再生,所有裝置將會被登出。"
|
||||
fileIdOrUrl: "文檔ID或者URL"
|
||||
chatOpenBehavior: "開啟聊天窗口時的行為"
|
||||
behavior: "行為"
|
||||
sample: "範例 "
|
||||
abuseReports: "檢舉"
|
||||
reportAbuse: "檢舉"
|
||||
reportAbuseOf: "檢舉{name}"
|
||||
fillAbuseReportDescription: "請填寫檢舉的詳細理由。可以的話,請附上針對的URL網址。"
|
||||
abuseReported: "內容已經發送。感謝您的報告。"
|
||||
send: "發送"
|
||||
abuseMarkAsResolved: "處理完畢"
|
||||
openInNewTab: "在新分頁中開啟"
|
||||
instanceTicker: "箋文的實例資訊"
|
||||
openInSideView: "在側欄中開啟"
|
||||
editTheseSettingsMayBreakAccount: "修改這些設定可能會毀壞您的帳戶"
|
||||
instanceTicker: "箋文的實例來源"
|
||||
waitingFor: "等待{x}"
|
||||
random: "隨機"
|
||||
system: "系統"
|
||||
switchUi: "切換界面"
|
||||
desktop: "桌面"
|
||||
clip: "片段"
|
||||
createNew: "新建"
|
||||
optional: "可選"
|
||||
public: "公開"
|
||||
i18nInfo: "Misskey已經被志願者們翻譯成各種語言版本,如果想要幫忙的話,可以進入{link}幫助翻譯。"
|
||||
accountInfo: "帳戶資訊"
|
||||
notesCount: "箋文數量"
|
||||
repliesCount: "回覆數量\n"
|
||||
renotesCount: "轉發數量"
|
||||
repliedCount: "回覆數量"
|
||||
renotedCount: "轉發次數"
|
||||
followingCount: "正在跟隨的用戶數量"
|
||||
followersCount: "跟隨者數量"
|
||||
sentReactionsCount: "情感發送次數"
|
||||
receivedReactionsCount: "情感收到次數"
|
||||
yes: "確定"
|
||||
no: "取消"
|
||||
driveFilesCount: "雲端硬碟檔案數量"
|
||||
driveUsage: "雲端硬碟使用量"
|
||||
noCrawleDescription: "請求網路搜尋引擎不要索引你的個人資料頁、箋文及頁面等。"
|
||||
lockedAccountInfo: "即使你通過了追隨者請求,除非你將筆記的公開範圍設定為 「追隨者」,否則任何人都能看見你的箋文。"
|
||||
lockedAccountInfo: "即使你通過了追隨者請求,除非你將箋文的可見性設定為 「追隨者」,否則任何人都能看見你的箋文。"
|
||||
loadRawImages: "以原始圖像質量顯示附件圖像的縮略圖"
|
||||
disableShowingAnimatedImages: "不播放動態圖像"
|
||||
notSet: "未設定"
|
||||
emailVerified: "已成功驗證您的電郵"
|
||||
noteFavoritesCount: "收藏箋文的數目"
|
||||
pageLikesCount: "頁面被喜歡次數"
|
||||
pageLikedCount: "頁面被喜歡次數"
|
||||
contact: "聯絡人"
|
||||
useSystemFont: "使用系統默認的字型"
|
||||
clips: "標籤"
|
||||
experimentalFeatures: "測試中的功能"
|
||||
developer: "開發者"
|
||||
makeExplorable: "讓自己的帳戶能夠在“探索”版面顯示"
|
||||
makeExplorableDescription: "如果關閉,帳戶將不會被顯示在\"探索\"版面中。"
|
||||
showGapBetweenNotesInTimeline: "分開顯示時間線上的箋文。"
|
||||
duplicate: "複製"
|
||||
left: "左"
|
||||
center: "向中央"
|
||||
wide: "寬"
|
||||
narrow: "窄"
|
||||
reloadToApplySetting: "設定將會在頁面重新載入之後生效。要現在就重載頁面嗎?"
|
||||
showTitlebar: "顯示標題列"
|
||||
clearCache: "清除快取資料"
|
||||
onlineUsersCount: "{n}人正在線上"
|
||||
nUsers: "{n}用戶"
|
||||
_aboutMisskey:
|
||||
about: "Misskey是由syuilo於2014年開發的開源軟件。"
|
||||
contributors: "主要貢獻者"
|
||||
allContributors: "全體貢獻人員"
|
||||
source: "原始碼"
|
||||
translation: "翻譯Misskey"
|
||||
donate: "捐贈給Misskey"
|
||||
morePatrons: "感謝你們的支持、 幫助。 🥰"
|
||||
patrons: "贊助者"
|
||||
_nsfw:
|
||||
respect: "隱藏敏感內容"
|
||||
ignore: "不隱藏敏感內容"
|
||||
force: "隱藏所有內容"
|
||||
_mfm:
|
||||
cheatSheet: "MFM代碼小抄"
|
||||
mention: "提及"
|
||||
hashtag: "#tag"
|
||||
url: "URL"
|
||||
@ -597,39 +672,67 @@ _mfm:
|
||||
quote: "引用"
|
||||
emoji: "自訂表情符號"
|
||||
search: "搜尋"
|
||||
flip: "翻轉"
|
||||
flipDescription: "將內容上下或左右翻轉。"
|
||||
jelly: "動畫(果凍)"
|
||||
jellyDescription: "顯示果凍一樣的動畫效果。"
|
||||
bounce: "動畫(反彈)"
|
||||
shake: "動畫(搖晃)"
|
||||
twitch: "動畫(顫抖)"
|
||||
spin: "動畫(旋轉)"
|
||||
spinDescription: "顯示旋轉的動畫效果。"
|
||||
blur: "模糊"
|
||||
font: "字型"
|
||||
fontDescription: "可設置顯示內容所使用的字型"
|
||||
_reversi:
|
||||
reversi: "黑白棋"
|
||||
gameSettings: "對弈設定"
|
||||
chooseBoard: "選擇棋盤"
|
||||
blackOrWhite: "黑棋/白棋"
|
||||
blackIs: "{name}在玩黑棋"
|
||||
rules: "規則"
|
||||
botSettings: "機器人設定"
|
||||
thisGameIsStartedSoon: "遊戲即將開始"
|
||||
waitingForOther: "等待對手準備"
|
||||
waitingForMe: "等待您的準備"
|
||||
waitingBoth: "請準備"
|
||||
ready: "已就緒"
|
||||
cancelReady: "重新準備"
|
||||
opponentTurn: "對手回合"
|
||||
myTurn: "你的回合"
|
||||
turnOf: "{name}的回合"
|
||||
pastTurnOf: "{name}的回合"
|
||||
surrender: "認輸"
|
||||
drawn: "平局"
|
||||
won: "{name}獲勝"
|
||||
black: "黑"
|
||||
white: "白"
|
||||
total: "合計"
|
||||
turnCount: "{count}回合"
|
||||
myGames: "我的對弈"
|
||||
allGames: "所有對弈"
|
||||
ended: "已結束"
|
||||
playing: "正在對弈"
|
||||
_instanceTicker:
|
||||
none: "隱藏"
|
||||
remote: "向遠端使用者顯示"
|
||||
always: "總是顯示"
|
||||
_serverDisconnectedBehavior:
|
||||
reload: "自動重載"
|
||||
dialog: "以對話框警告"
|
||||
quiet: "適當地警告"
|
||||
dialog: "彈出式警告"
|
||||
quiet: "非侵入式警告"
|
||||
_channel:
|
||||
create: "建立頻道"
|
||||
edit: "編輯頻道"
|
||||
setBanner: "設定橫幅"
|
||||
removeBanner: "移除封面圖"
|
||||
featured: "流行"
|
||||
featured: "發燒內容"
|
||||
owned: "管理中"
|
||||
following: "關注中"
|
||||
usersCount: "有{n}人參與"
|
||||
notesCount: "有{n}個帖子"
|
||||
_sidebar:
|
||||
full: "全部"
|
||||
icon: "頭像"
|
||||
hide: "隱藏"
|
||||
_wordMute:
|
||||
@ -637,10 +740,22 @@ _wordMute:
|
||||
softDescription: "隱藏時間軸中指定條件的箋文。"
|
||||
mutedNotes: "已靜音的箋文"
|
||||
_theme:
|
||||
explore: "取得佈景主題"
|
||||
install: "安裝佈景主題"
|
||||
manage: "佈景主題管理員"
|
||||
code: "主題代碼"
|
||||
installed: "{name}已安裝"
|
||||
installedThemes: "已經安裝的主題"
|
||||
builtinThemes: "標準主題"
|
||||
alreadyInstalled: "此主題已經安裝"
|
||||
invalid: "主題格式錯誤"
|
||||
make: "製作主題"
|
||||
base: "基於"
|
||||
constant: "常數"
|
||||
defaultValue: "預設值"
|
||||
color: "顏色"
|
||||
func: "函数"
|
||||
funcKind: "功能類型"
|
||||
argument: "引數"
|
||||
alpha: "透明度"
|
||||
darken: "暗度"
|
||||
@ -649,21 +764,38 @@ _theme:
|
||||
bg: "背景"
|
||||
fg: "文本"
|
||||
shadow: "陰影"
|
||||
navIndicator: "側邊欄指示符"
|
||||
link: "鏈接"
|
||||
hashtag: "#tag"
|
||||
mention: "提及"
|
||||
mentionMe: "提及我"
|
||||
renote: "轉發貼文"
|
||||
renote: "轉發箋文"
|
||||
divider: "分割線"
|
||||
scrollbarHandle: "滾動條"
|
||||
scrollbarHandleHover: "滾動條 (漂浮)"
|
||||
dateLabelFg: "日期標籤文字"
|
||||
infoBg: "資訊背景"
|
||||
infoFg: "資訊內容"
|
||||
infoWarnBg: "警告背景"
|
||||
infoWarnFg: "警告字元"
|
||||
cwBg: "CW 按鈕背景"
|
||||
cwFg: "CW 按鈕文本"
|
||||
cwHoverBg: "CW 按鈕背景 (漂浮)"
|
||||
buttonBg: "按鈕背景"
|
||||
buttonHoverBg: "按鈕背景 (漂浮)"
|
||||
inputBorder: "輸入框邊框"
|
||||
listItemHoverBg: "列表物品背景 (漂浮)"
|
||||
driveFolderBg: "雲端硬碟文件夾背景"
|
||||
messageBg: "私信背景"
|
||||
accentDarken: "強調色(偏暗)"
|
||||
accentLighten: "強調色(明亮)"
|
||||
fgHighlighted: "高亮顯示文本"
|
||||
_sfx:
|
||||
note: "箋文"
|
||||
noteMy: "我的箋文"
|
||||
notification: "通知"
|
||||
chat: "傳送訊息"
|
||||
antenna: "天線接收"
|
||||
channel: "頻道通知"
|
||||
_ago:
|
||||
unknown: "未知"
|
||||
@ -684,28 +816,33 @@ _time:
|
||||
_tutorial:
|
||||
title: "Misskey使用方法"
|
||||
step1_1: "歡迎!"
|
||||
step1_2: "此為「時間軸」頁面,它會按照時間順序顯示你「追隨」的人的「貼文」"
|
||||
step1_3: "由於你沒有發布任何筆記,也沒有追隨任何人,所以你的時間軸目前是空的。"
|
||||
step1_2: "此為「時間軸」頁面,它會按照時間順序顯示你「追隨」的人發出的「箋文」"
|
||||
step1_3: "由於你沒有發佈任何箋文,也沒有追隨任何人,所以你的時間軸目前是空的。"
|
||||
step2_1: "在發文或追隨其他人之前先讓我們設定一下個人資料吧。"
|
||||
step2_2: "提供一些關於自己的資訊來讓其他人更有追隨你的意願。"
|
||||
step3_1: "個人資料都打理好了嗎?"
|
||||
step3_2: "下一步讓我們來試試看發個文,按一下畫面上的鉛筆圖示來開始"
|
||||
step3_3: "輸入完內容後,按視窗右上角的按鈕來發文"
|
||||
step3_4: "不知道該寫什麼內容嗎?試試看「開始使用Misskey了」如何。"
|
||||
step4_1: "筆記發出去了嗎?"
|
||||
step4_1: "箋文發出去了嗎?"
|
||||
step4_2: "如果你的箋文出現在時間軸上,就代表發文成功。"
|
||||
step5_1: "現在試試看追隨其他人來讓你的時間軸變得更生動吧。"
|
||||
step5_2: "你可以在{featured}上看到受歡迎的貼文,你也可以選擇從列表中追隨你喜歡的人,或者在{explore}上找到熱門使用者。"
|
||||
step5_2: "你會在{featured}上看到受歡迎的箋文,你也可以從列表中追隨你喜歡的人,或者在{explore}上找到熱門使用者。"
|
||||
step5_3: "想要追隨其他人,只要點擊他們的頭像並按「追隨」即可。"
|
||||
step5_4: "如果使用者的名字旁有鎖頭的圖示,代表他們需要手動核准你的追隨請求。"
|
||||
step6_1: "現在你可以在時間軸上看到其他用戶的貼文"
|
||||
step6_2: "你也可以在其他人的貼文上進行「反應」來表達簡單的回覆。"
|
||||
step6_3: "在他人的貼文按下「+」的圖示即可選擇想要的表情符號來進行「反應」。"
|
||||
step6_1: "現在你可以在時間軸上看到其他用戶的箋文。"
|
||||
step6_2: "你也可以對別人的箋文作出「情感」,作出簡單的回覆。"
|
||||
step7_1: "以上為Misskey的基本操作說明,教學在此告一段落。辛苦了。"
|
||||
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
|
||||
step7_3: "那麼,祝您在Misskey玩的開心~ 🚀"
|
||||
_2fa:
|
||||
alreadyRegistered: "此設備已經被註冊過了"
|
||||
registerDevice: "註冊裝置"
|
||||
step1: "首先,在您的設備上安裝二步驗證程式,例如{a}或{b}。"
|
||||
step2: "然後,掃描熒幕上的QR code。"
|
||||
_permissions:
|
||||
"read:account": "查看帳戶信息"
|
||||
"write:account": "更改帳戶信息"
|
||||
"read:blocks": "已封鎖用戶名單"
|
||||
"write:blocks": "編輯已封鎖用戶名單"
|
||||
"read:drive": "存取雲端硬碟\n"
|
||||
@ -720,8 +857,8 @@ _permissions:
|
||||
"write:notes": "撰寫或刪除箋文"
|
||||
"read:notifications": "查看通知"
|
||||
"write:notifications": "編輯通知"
|
||||
"read:reactions": "查看反應"
|
||||
"write:reactions": "編輯反應"
|
||||
"read:reactions": "查看情感"
|
||||
"write:reactions": "編輯情感"
|
||||
"write:votes": "投票"
|
||||
"read:pages": "顯示頁面"
|
||||
"write:pages": "編輯頁面"
|
||||
@ -733,6 +870,11 @@ _permissions:
|
||||
"write:channels": "編輯頻道"
|
||||
_auth:
|
||||
shareAccess: "要授權「“{name}”」存取您的帳戶嗎?"
|
||||
shareAccessAsk: "您確定要授權這個應用程式使用您的帳戶嗎?"
|
||||
permissionAsk: "此應用程式需要以下權限"
|
||||
pleaseGoBack: "請返回至應用程式"
|
||||
callback: "回到應用程式"
|
||||
denied: "拒絕訪問"
|
||||
_antennaSources:
|
||||
all: "全部箋文"
|
||||
homeTimeline: "來自已追隨使用者的箋文"
|
||||
@ -752,14 +894,17 @@ _widgets:
|
||||
notifications: "通知"
|
||||
timeline: "時間軸"
|
||||
calendar: "行事曆"
|
||||
trends: "發燒貼文"
|
||||
trends: "發燒箋文"
|
||||
clock: "時鐘"
|
||||
rss: "RSS閱讀器"
|
||||
activity: "動態"
|
||||
photos: "照片"
|
||||
digitalClock: "電子時鐘"
|
||||
federation: "聯邦宇宙"
|
||||
postForm: "篆文窗口"
|
||||
slideshow: "幻燈片"
|
||||
button: "按鈕"
|
||||
onlineUsers: "在線上的用戶"
|
||||
_cw:
|
||||
hide: "隱藏"
|
||||
show: "瀏覽更多"
|
||||
@ -767,6 +912,8 @@ _cw:
|
||||
files: "{count} 個檔案"
|
||||
_poll:
|
||||
noOnlyOneChoice: "至少需要兩個選項。"
|
||||
choiceN: "選擇{n}"
|
||||
noMore: "沒辦法再添加選項了"
|
||||
expiration: "期限"
|
||||
infinite: "無期限"
|
||||
at: "結束時間"
|
||||
@ -780,6 +927,7 @@ _poll:
|
||||
voted: "已投票"
|
||||
closed: "已結束"
|
||||
remainingDays: "{d}天{h}小時後結束"
|
||||
remainingHours: "{h}小時{m}分後結束"
|
||||
_visibility:
|
||||
public: "公開"
|
||||
home: "首頁"
|
||||
@ -790,7 +938,7 @@ _visibility:
|
||||
localOnlyDescription: "對遠端使用者隱藏"
|
||||
_postForm:
|
||||
replyPlaceholder: "回覆此箋文..."
|
||||
quotePlaceholder: "引用此貼文..."
|
||||
quotePlaceholder: "引用此箋文..."
|
||||
channelPlaceholder: "發佈到頻道"
|
||||
_placeholders:
|
||||
a: "今天過得如何?"
|
||||
@ -807,7 +955,7 @@ _profile:
|
||||
metadataLabel: "標籤"
|
||||
metadataContent: "内容"
|
||||
_exportOrImport:
|
||||
allNotes: "全部箋文"
|
||||
allNotes: "所有箋文"
|
||||
followingList: "追隨中"
|
||||
muteList: "靜音"
|
||||
blockingList: "封鎖"
|
||||
@ -883,6 +1031,11 @@ _rooms:
|
||||
monitor: "監視器"
|
||||
keyboard: "鍵盤"
|
||||
carpet-stripe: "條紋地毯"
|
||||
wall-clock: "壁鐘"
|
||||
photoframe: "相框"
|
||||
cube: "立方體"
|
||||
tv: "電視"
|
||||
pinguin: "企鵝蠟像"
|
||||
bin: "垃圾箱"
|
||||
cup-noodle: "杯面"
|
||||
holo-display: "投影機"
|
||||
@ -892,9 +1045,14 @@ _rooms:
|
||||
_pages:
|
||||
newPage: "建立頁面"
|
||||
editPage: "編輯頁面"
|
||||
readPage: "正檢視原始碼"
|
||||
created: "頁面已建立"
|
||||
updated: "頁面已更新"
|
||||
deleted: "頁面已被刪除"
|
||||
pageSetting: "頁面設定"
|
||||
nameAlreadyExists: "指定的頁面URL已經存在"
|
||||
invalidNameTitle: "指定的頁面URL無效"
|
||||
invalidNameText: "請確定是否為非空白"
|
||||
editThisPage: "編輯此頁面"
|
||||
viewSource: "檢視原始碼"
|
||||
viewPage: "顯示頁面"
|
||||
@ -902,14 +1060,28 @@ _pages:
|
||||
unlike: "收回喜歡"
|
||||
my: "我的頁面"
|
||||
liked: "已喜歡的頁面"
|
||||
featured: "人氣"
|
||||
inspector: "面板檢查"
|
||||
contents: "內容"
|
||||
content: "頁面方塊"
|
||||
variables: "變數"
|
||||
title: "標題"
|
||||
url: "頁面網址"
|
||||
summary: "頁面摘要"
|
||||
alignCenter: "置中"
|
||||
hideTitleWhenPinned: "被置頂於個人資料時隱藏頁面標題"
|
||||
font: "字型"
|
||||
fontSerif: "襯線體"
|
||||
fontSansSerif: "無襯線體"
|
||||
eyeCatchingImageSet: "設定封面影像"
|
||||
eyeCatchingImageRemove: "刪除封面影像"
|
||||
chooseBlock: "新增方塊"
|
||||
selectType: "選擇類型"
|
||||
enterVariableName: "請輸入變數名稱"
|
||||
variableNameIsAlreadyUsed: "變數名稱已被佔用"
|
||||
contentBlocks: "內容"
|
||||
inputBlocks: "輸入"
|
||||
specialBlocks: "特殊"
|
||||
blocks:
|
||||
text: "文本"
|
||||
textarea: "文字區域"
|
||||
@ -1002,6 +1174,7 @@ _pages:
|
||||
arg1: "本文"
|
||||
_join:
|
||||
arg1: "清單"
|
||||
arg2: "分隔字元"
|
||||
add: "加"
|
||||
_add:
|
||||
arg1: "A"
|
||||
@ -1018,6 +1191,7 @@ _pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
mod: "餘數"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
@ -1129,9 +1303,14 @@ _relayStatus:
|
||||
accepted: "已通過核准"
|
||||
rejected: "已拒絕"
|
||||
_notification:
|
||||
youRenoted: "{name} 轉發了你的貼文"
|
||||
youGotMention: "{name}提及到您"
|
||||
youGotReply: "{name}回覆了您"
|
||||
youGotQuote: "{name}引用了您"
|
||||
youRenoted: "{name} 轉發了你的箋文"
|
||||
youGotPoll: "{name}已投票"
|
||||
youGotMessagingMessageFromUser: "{name}發送給您的訊息"
|
||||
youWereFollowed: "您有新的追隨者"
|
||||
youReceivedFollowRequest: "您有新的追隨請求"
|
||||
yourFollowRequestAccepted: "您的追隨請求已通過"
|
||||
youWereInvitedToGroup: "您有新的群組邀請"
|
||||
_types:
|
||||
@ -1139,15 +1318,17 @@ _notification:
|
||||
follow: "追隨中"
|
||||
mention: "提及"
|
||||
reply: "回覆"
|
||||
renote: "轉發貼文"
|
||||
renote: "轉發箋文"
|
||||
quote: "引用"
|
||||
reaction: "反應"
|
||||
reaction: "情感"
|
||||
receiveFollowRequest: "已收到追隨請求"
|
||||
followRequestAccepted: "追隨請求已接受"
|
||||
groupInvited: "加入社群邀請"
|
||||
app: "應用程式通知"
|
||||
_deck:
|
||||
alwaysShowMainColumn: "總是顯示主欄"
|
||||
columnAlign: "對齊欄位"
|
||||
columnMargin: "列之間的邊距"
|
||||
addColumn: "新增欄位"
|
||||
swapLeft: "向左移動"
|
||||
swapRight: "向右移動"
|
||||
@ -1156,6 +1337,7 @@ _deck:
|
||||
stackLeft: "向左折疊"
|
||||
popRight: "向右彈出"
|
||||
_columns:
|
||||
main: "主列"
|
||||
widgets: "小工具"
|
||||
notifications: "通知"
|
||||
tl: "時間軸"
|
||||
|
16
package.json
16
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||
"version": "12.64.2",
|
||||
"version": "12.66.0",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -87,7 +87,7 @@
|
||||
"@types/pug": "2.0.4",
|
||||
"@types/qrcode": "1.3.5",
|
||||
"@types/random-seed": "0.3.3",
|
||||
"@types/ratelimiter": "3.4.0",
|
||||
"@types/ratelimiter": "3.4.1",
|
||||
"@types/redis": "2.8.28",
|
||||
"@types/rename": "1.0.2",
|
||||
"@types/request-stats": "3.0.0",
|
||||
@ -105,7 +105,7 @@
|
||||
"@types/websocket": "1.0.1",
|
||||
"@types/ws": "7.4.0",
|
||||
"@typescript-eslint/parser": "4.10.0",
|
||||
"@vue/compiler-sfc": "3.0.3",
|
||||
"@vue/compiler-sfc": "3.0.5",
|
||||
"abort-controller": "3.0.0",
|
||||
"apexcharts": "3.22.3",
|
||||
"autobind-decorator": "2.4.0",
|
||||
@ -204,7 +204,7 @@
|
||||
"qrcode": "1.4.4",
|
||||
"random-seed": "0.3.0",
|
||||
"ratelimiter": "3.4.1",
|
||||
"re2": "1.15.8",
|
||||
"re2": "1.15.9",
|
||||
"recaptcha-promise": "1.0.0",
|
||||
"reconnecting-websocket": "4.4.0",
|
||||
"redis": "3.0.2",
|
||||
@ -237,14 +237,14 @@
|
||||
"tslint": "6.1.3",
|
||||
"tslint-sonarts": "1.9.0",
|
||||
"typeorm": "0.2.29",
|
||||
"typescript": "4.1.2",
|
||||
"typescript": "4.1.3",
|
||||
"ulid": "2.3.0",
|
||||
"url-loader": "4.1.1",
|
||||
"uuid": "8.3.2",
|
||||
"v-debounce": "0.1.2",
|
||||
"vanilla-tilt": "1.7.0",
|
||||
"vue": "3.0.3",
|
||||
"vue-color": "2.7.1",
|
||||
"vue": "3.0.5",
|
||||
"vue-color": "2.8.1",
|
||||
"vue-json-pretty": "1.7.1",
|
||||
"vue-loader": "16.0.0",
|
||||
"vue-prism-editor": "2.0.0-alpha.2",
|
||||
@ -253,7 +253,7 @@
|
||||
"vuedraggable": "4.0.1",
|
||||
"web-push": "3.4.4",
|
||||
"webpack": "5.10.1",
|
||||
"webpack-cli": "4.2.0",
|
||||
"webpack-cli": "4.3.0",
|
||||
"websocket": "1.0.33",
|
||||
"ws": "7.4.1",
|
||||
"xev": "2.0.1"
|
||||
|
7
src/client/assets/misskey.svg
Normal file
7
src/client/assets/misskey.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.2 KiB |
@ -49,7 +49,7 @@ export default defineComponent({
|
||||
const el = this.$slots.default({
|
||||
item: item
|
||||
})[0];
|
||||
el.key = item.id;
|
||||
if (el.key == null && item.id) el.key = item.id;
|
||||
|
||||
if (
|
||||
i != this.items.length - 1 &&
|
||||
|
@ -36,7 +36,7 @@ import { faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faDownload, faLink, faICursor, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import MkDriveFileThumbnail from './drive-file-thumbnail.vue';
|
||||
import bytes from '../filters/bytes';
|
||||
import bytes from '@/filters/bytes';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -2,7 +2,7 @@
|
||||
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
|
||||
<div class="omfetrab _popup" :class="['w' + width, 'h' + height, { big }]">
|
||||
<input ref="search" class="search" :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$ts.search" @paste.stop="paste" @keyup.enter="done()">
|
||||
<div class="emojis">
|
||||
<div class="emojis" ref="emojis">
|
||||
<section class="result">
|
||||
<div v-if="searchResultCustom.length > 0">
|
||||
<button v-for="emoji in searchResultCustom"
|
||||
@ -180,6 +180,8 @@ export default defineComponent({
|
||||
|
||||
watch: {
|
||||
q() {
|
||||
this.$refs.emojis.scrollTop = 0;
|
||||
|
||||
if (this.q == null || this.q === '') {
|
||||
this.searchResultCustom = [];
|
||||
this.searchResultUnicode = [];
|
||||
|
34
src/client/components/featured-photos.vue
Normal file
34
src/client/components/featured-photos.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="xfbouadm" v-if="meta" :style="{ backgroundImage: `url(${ meta.backgroundImageUrl })` }">
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
meta: null,
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
os.api('meta', { detail: true }).then(meta => {
|
||||
this.meta = meta;
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.xfbouadm {
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
@ -11,6 +11,11 @@ export default defineComponent({
|
||||
required: false,
|
||||
default: 'span',
|
||||
},
|
||||
textTag: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
render() {
|
||||
let str = this.src;
|
||||
@ -32,6 +37,6 @@ export default defineComponent({
|
||||
str = str.substr(nextBracketClose + 1);
|
||||
}
|
||||
|
||||
return h(this.tag, parsed.map(x => typeof x === 'string' ? x : this.$slots[x.arg]()));
|
||||
return h(this.tag, parsed.map(x => typeof x === 'string' ? (this.textTag ? h(this.textTag, x) : x) : this.$slots[x.arg]()));
|
||||
}
|
||||
});
|
||||
|
@ -14,6 +14,15 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
._mfm_blur_ {
|
||||
filter: blur(6px);
|
||||
transition: filter 0.3s;
|
||||
|
||||
&:hover {
|
||||
filter: blur(0px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mfm-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
|
@ -48,6 +48,11 @@ export default defineComponent({
|
||||
|
||||
const ast = (this.plain ? parsePlain : parse)(this.text);
|
||||
|
||||
const validTime = (t: string | null | undefined) => {
|
||||
if (t == null) return null;
|
||||
return t.match(/^[0-9.]+s$/) ? t : null;
|
||||
};
|
||||
|
||||
const genEl = (ast: MfmForest) => concat(ast.map((token): VNode[] => {
|
||||
switch (token.node.type) {
|
||||
case 'text': {
|
||||
@ -86,17 +91,17 @@ export default defineComponent({
|
||||
break;
|
||||
}
|
||||
case 'jelly': {
|
||||
const speed = token.node.props.args.speed || '1s';
|
||||
const speed = validTime(token.node.props.args.speed) || '1s';
|
||||
style = (this.$store.state.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
|
||||
break;
|
||||
}
|
||||
case 'twitch': {
|
||||
const speed = token.node.props.args.speed || '0.5s';
|
||||
const speed = validTime(token.node.props.args.speed) || '0.5s';
|
||||
style = this.$store.state.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : '';
|
||||
break;
|
||||
}
|
||||
case 'shake': {
|
||||
const speed = token.node.props.args.speed || '0.5s';
|
||||
const speed = validTime(token.node.props.args.speed) || '0.5s';
|
||||
style = this.$store.state.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : '';
|
||||
break;
|
||||
}
|
||||
@ -109,7 +114,7 @@ export default defineComponent({
|
||||
token.node.props.args.x ? 'mfm-spinX' :
|
||||
token.node.props.args.y ? 'mfm-spinY' :
|
||||
'mfm-spin';
|
||||
const speed = token.node.props.args.speed || '1.5s';
|
||||
const speed = validTime(token.node.props.args.speed) || '1.5s';
|
||||
style = this.$store.state.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
|
||||
break;
|
||||
}
|
||||
@ -129,6 +134,35 @@ export default defineComponent({
|
||||
style = `transform: ${transform};`;
|
||||
break;
|
||||
}
|
||||
case 'x2': {
|
||||
style = `font-size: 200%;`;
|
||||
break;
|
||||
}
|
||||
case 'x3': {
|
||||
style = `font-size: 400%;`;
|
||||
break;
|
||||
}
|
||||
case 'x4': {
|
||||
style = `font-size: 600%;`;
|
||||
break;
|
||||
}
|
||||
case 'font': {
|
||||
const family =
|
||||
token.node.props.args.serif ? 'serif' :
|
||||
token.node.props.args.monospace ? 'monospace' :
|
||||
token.node.props.args.cursive ? 'cursive' :
|
||||
token.node.props.args.fantasy ? 'fantasy' :
|
||||
token.node.props.args.emoji ? 'emoji' :
|
||||
token.node.props.args.math ? 'math' :
|
||||
null;
|
||||
if (family) style = `font-family: ${family};`;
|
||||
break;
|
||||
}
|
||||
case 'blur': {
|
||||
return h('span', {
|
||||
class: '_mfm_blur_',
|
||||
}, genEl(token.children));
|
||||
}
|
||||
}
|
||||
if (style == null) {
|
||||
return h('span', {}, ['[', token.node.props.name, ...genEl(token.children), ']']);
|
||||
|
1190
src/client/components/note-detailed.vue
Normal file
1190
src/client/components/note-detailed.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,6 @@
|
||||
v-hotkey="keymap"
|
||||
v-size="{ max: [500, 450, 350, 300] }"
|
||||
>
|
||||
<XSub v-for="note in conversation" class="reply-to-more" :key="note.id" :note="note"/>
|
||||
<XSub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/>
|
||||
<div class="info" v-if="pinned"><Fa :icon="faThumbtack"/> {{ $ts.pinnedNote }}</div>
|
||||
<div class="info" v-if="appearNote._prId_"><Fa :icon="faBullhorn"/> {{ $ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ $ts.hideThisNote }} <Fa :icon="faTimes"/></button></div>
|
||||
@ -36,7 +35,7 @@
|
||||
<span class="localOnly" v-if="note.localOnly"><Fa :icon="faBiohazard"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<article class="article" @contextmenu="onContextmenu">
|
||||
<article class="article" @contextmenu.stop="onContextmenu">
|
||||
<MkAvatar class="avatar" :user="appearNote.user"/>
|
||||
<div class="main">
|
||||
<XNoteHeader class="header" :note="appearNote" :mini="true"/>
|
||||
@ -46,7 +45,7 @@
|
||||
<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
|
||||
<XCwButton v-model:value="showContent" :note="appearNote"/>
|
||||
</p>
|
||||
<div class="content" v-show="appearNote.cw == null || showContent">
|
||||
<div class="content" :class="{ collapsed }" v-show="appearNote.cw == null || showContent">
|
||||
<div class="text">
|
||||
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $ts.private }})</span>
|
||||
<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></MkA>
|
||||
@ -57,8 +56,11 @@
|
||||
<XMediaList :media-list="appearNote.files"/>
|
||||
</div>
|
||||
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
|
||||
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="detail" class="url-preview"/>
|
||||
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/>
|
||||
<div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div>
|
||||
<button v-if="collapsed" class="fade _button" @click="collapsed = false">
|
||||
<span>{{ $ts.showMore }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><Fa :icon="faSatelliteDish"/> {{ appearNote.channel.name }}</MkA>
|
||||
</div>
|
||||
@ -87,7 +89,6 @@
|
||||
</footer>
|
||||
</div>
|
||||
</article>
|
||||
<XSub v-for="note in replies" :key="note.id" :note="note" class="reply" :detail="true"/>
|
||||
</div>
|
||||
<div v-else class="_panel muted" @click="muted = false">
|
||||
<I18n :src="$ts.userSaysSomething" tag="small">
|
||||
@ -154,11 +155,6 @@ export default defineComponent({
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
detail: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
pinned: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
@ -171,9 +167,9 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
conversation: [],
|
||||
replies: [],
|
||||
showContent: false,
|
||||
collapsed: false,
|
||||
isDeleted: false,
|
||||
muted: false,
|
||||
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
|
||||
@ -273,6 +269,12 @@ export default defineComponent({
|
||||
this.connection = os.stream;
|
||||
}
|
||||
|
||||
this.collapsed = this.appearNote.cw == null && this.appearNote.text && (
|
||||
(this.appearNote.text.split('\n').length > 9) ||
|
||||
(this.appearNote.text.length > 500)
|
||||
);
|
||||
this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords);
|
||||
|
||||
// plugin
|
||||
if (noteViewInterruptors.length > 0) {
|
||||
let result = this.note;
|
||||
@ -281,25 +283,6 @@ export default defineComponent({
|
||||
}
|
||||
this.$emit('update:note', Object.freeze(result));
|
||||
}
|
||||
|
||||
this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords);
|
||||
|
||||
if (this.detail) {
|
||||
os.api('notes/children', {
|
||||
noteId: this.appearNote.id,
|
||||
limit: 30
|
||||
}).then(replies => {
|
||||
this.replies = replies;
|
||||
});
|
||||
|
||||
if (this.appearNote.replyId) {
|
||||
os.api('notes/conversation', {
|
||||
noteId: this.appearNote.replyId
|
||||
}).then(conversation => {
|
||||
this.conversation = conversation.reverse();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
@ -943,10 +926,6 @@ export default defineComponent({
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
> .reply-to-more {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> .renote {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -1038,6 +1017,37 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> .content {
|
||||
&.collapsed {
|
||||
position: relative;
|
||||
max-height: 9em;
|
||||
overflow: hidden;
|
||||
|
||||
> .fade {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
background: linear-gradient(0deg, var(--panel), var(--X15));
|
||||
|
||||
> span {
|
||||
display: inline-block;
|
||||
background: var(--panel);
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8em;
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> span {
|
||||
background: var(--panelHighlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .text {
|
||||
overflow-wrap: break-word;
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed">
|
||||
<XNote :note="note" @update:note="updated(note, $event)" :detail="detail" :key="note._featuredId_ || note._prId_ || note.id"/>
|
||||
<XNote :note="note" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
|
||||
</XList>
|
||||
|
||||
<div v-show="more && !reversed" style="margin-top: var(--margin);">
|
||||
@ -55,12 +55,6 @@ export default defineComponent({
|
||||
required: true
|
||||
},
|
||||
|
||||
detail: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
|
||||
prop: {
|
||||
type: String,
|
||||
required: false
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<XWindow ref="window"
|
||||
:initial-width="700"
|
||||
:initial-width="500"
|
||||
:initial-height="500"
|
||||
:can-resize="true"
|
||||
:close-right="true"
|
||||
|
@ -59,7 +59,6 @@ export default defineComponent({
|
||||
return {
|
||||
host: host,
|
||||
showing: false,
|
||||
searching: false,
|
||||
accounts: [],
|
||||
connection: null,
|
||||
menuDef: sidebarDef,
|
||||
@ -125,19 +124,7 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
search() {
|
||||
if (this.searching) return;
|
||||
|
||||
os.dialog({
|
||||
title: this.$ts.search,
|
||||
input: true
|
||||
}).then(async ({ canceled, result: query }) => {
|
||||
if (canceled || query == null || query === '') return;
|
||||
|
||||
this.searching = true;
|
||||
search(this, query).finally(() => {
|
||||
this.searching = false;
|
||||
});
|
||||
});
|
||||
search();
|
||||
},
|
||||
|
||||
async openAccountMenu(ev) {
|
||||
@ -357,12 +344,6 @@ export default defineComponent({
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
|
||||
> div {
|
||||
> .notifications {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.hidden) {
|
||||
|
@ -115,6 +115,22 @@ export default defineComponent({
|
||||
endpoint = 'notes/global-timeline';
|
||||
this.connection = os.stream.useSharedConnection('globalTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'mentions') {
|
||||
endpoint = 'notes/mentions';
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', prepend);
|
||||
} else if (this.src == 'directs') {
|
||||
endpoint = 'notes/mentions';
|
||||
this.query = {
|
||||
visibility: 'specified'
|
||||
};
|
||||
const onNote = note => {
|
||||
if (note.visibility == 'specified') {
|
||||
prepend(note);
|
||||
}
|
||||
};
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', onNote);
|
||||
} else if (this.src == 'list') {
|
||||
endpoint = 'notes/user-list-timeline';
|
||||
this.query = {
|
||||
|
@ -123,7 +123,7 @@ export default defineComponent({
|
||||
box-shadow: none;
|
||||
text-decoration: none;
|
||||
background: var(--buttonBg);
|
||||
border-radius: 8px;
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
|
||||
&:not(:disabled):hover {
|
||||
|
@ -134,6 +134,8 @@ export default defineComponent({
|
||||
|
||||
> header {
|
||||
position: relative;
|
||||
color: var(--panelHeaderFg);
|
||||
background: var(--panelHeaderBg);
|
||||
box-shadow: 0 1px 0 0 var(--panelHeaderDivider);
|
||||
z-index: 2;
|
||||
line-height: 1.4em;
|
||||
@ -179,6 +181,7 @@ export default defineComponent({
|
||||
> header {
|
||||
> .title {
|
||||
padding: 8px 10px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,7 +243,7 @@ export default defineComponent({
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 0 0 1px var(--divider);
|
||||
border-radius: 6px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
|
@ -91,10 +91,6 @@ if (_DEV_) {
|
||||
// タッチデバイスでCSSの:hoverを機能させる
|
||||
document.addEventListener('touchend', () => {}, { passive: true });
|
||||
|
||||
if (localStorage.theme == null) {
|
||||
applyTheme(require('@/themes/l-light.json5'));
|
||||
}
|
||||
|
||||
//#region SEE: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
|
||||
// TODO: いつの日にか消したい
|
||||
const vh = window.innerHeight * 0.01;
|
||||
@ -204,7 +200,7 @@ watch(defaultStore.reactiveState.darkMode, (darkMode) => {
|
||||
const themes = builtinThemes.concat(ColdDeviceStorage.get('themes'));
|
||||
applyTheme(themes.find(x => x.id === (darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme'))));
|
||||
});
|
||||
});
|
||||
}, { immediate: localStorage.theme == null });
|
||||
|
||||
//#region Sync dark mode
|
||||
if (ColdDeviceStorage.get('syncDeviceDarkMode')) {
|
||||
|
@ -97,8 +97,8 @@ export function apiWithDialog(
|
||||
|
||||
export function promiseDialog<T extends Promise<any>>(
|
||||
promise: T,
|
||||
onSuccess?: (res: any) => void,
|
||||
onFailure?: (e: Error) => void,
|
||||
onSuccess?: ((res: any) => void) | null,
|
||||
onFailure?: ((e: Error) => void) | null,
|
||||
text?: string,
|
||||
): T {
|
||||
const showing = ref(true);
|
||||
|
@ -23,6 +23,18 @@
|
||||
</FormGroup>
|
||||
|
||||
<FormLink v-if="meta.tosUrl" :to="meta.tosUrl" external>{{ $ts.tos }}</FormLink>
|
||||
|
||||
<FormGroup v-if="stats">
|
||||
<template #label>{{ $ts.statistics }}</template>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.users }}</template>
|
||||
<template #value>{{ number(stats.originalUsersCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
<FormKeyValueView>
|
||||
<template #key>{{ $ts.notes }}</template>
|
||||
<template #value>{{ number(stats.originalNotesCount) }}</template>
|
||||
</FormKeyValueView>
|
||||
</FormGroup>
|
||||
</FormBase>
|
||||
</template>
|
||||
|
||||
@ -35,6 +47,7 @@ import FormBase from '@/components/form/base.vue';
|
||||
import FormGroup from '@/components/form/group.vue';
|
||||
import FormKeyValueView from '@/components/form/key-value-view.vue';
|
||||
import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -52,7 +65,7 @@ export default defineComponent({
|
||||
},
|
||||
version,
|
||||
instanceName,
|
||||
serverInfo: null,
|
||||
stats: null,
|
||||
faInfoCircle
|
||||
}
|
||||
},
|
||||
@ -62,6 +75,16 @@ export default defineComponent({
|
||||
return this.$instance;
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
os.api('stats').then(stats => {
|
||||
this.stats = stats;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
<XPostForm :channel="channel" class="post-form _content _panel _vMargin" fixed v-if="$i"/>
|
||||
|
||||
<XTimeline class="_content _vMargin" src="channel" :channel="channelId" @before="before" @after="after"/>
|
||||
<XTimeline class="_content _vMargin _noGap_" src="channel" :key="channelId" :channel="channelId" @before="before" @after="after"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="title">{{ title }}</div>
|
||||
<div class="body" v-html="body"></div>
|
||||
<div class="footer">
|
||||
<MkLink :url="`https://github.com/syuilo/misskey/blob/master/src/docs/${doc}.ja-JP.md`" class="at">{{ $ts.docSource }}</MkLink>
|
||||
<MkLink :url="`https://github.com/syuilo/misskey/blob/master/src/docs/${lang}/${doc}.md`" class="at">{{ $ts.docSource }}</MkLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -45,6 +45,7 @@ export default defineComponent({
|
||||
title: null,
|
||||
body: null,
|
||||
markdown: null,
|
||||
lang,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -145,6 +145,56 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $ts._mfm.font }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $ts._mfm.fontDescription }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_font"/>
|
||||
<MkTextarea v-model:value="preview_font"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $ts._mfm.x2 }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $ts._mfm.x2Description }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_x2"/>
|
||||
<MkTextarea v-model:value="preview_x2"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $ts._mfm.x3 }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $ts._mfm.x3Description }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_x3"/>
|
||||
<MkTextarea v-model:value="preview_x3"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $ts._mfm.x4 }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $ts._mfm.x4Description }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_x4"/>
|
||||
<MkTextarea v-model:value="preview_x4"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $ts._mfm.blur }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $ts._mfm.blurDescription }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_blur"/>
|
||||
<MkTextarea v-model:value="preview_blur"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $ts._mfm.jelly }}</div>
|
||||
<div class="_content">
|
||||
@ -255,6 +305,11 @@ export default defineComponent({
|
||||
preview_twitch: `[twitch 🍮]`,
|
||||
preview_spin: `[spin 🍮] [spin.left 🍮] [spin.alternate 🍮]\n[spin.x 🍮] [spin.x,left 🍮] [spin.x,alternate 🍮]\n[spin.y 🍮] [spin.y,left 🍮] [spin.y,alternate 🍮]`,
|
||||
preview_flip: `[flip ${this.$ts._mfm.dummy}]\n[flip.v ${this.$ts._mfm.dummy}]\n[flip.h,v ${this.$ts._mfm.dummy}]`,
|
||||
preview_font: `[font.serif ${this.$ts._mfm.dummy}]\n[font.monospace ${this.$ts._mfm.dummy}]\n[font.cursive ${this.$ts._mfm.dummy}]\n[font.fantasy ${this.$ts._mfm.dummy}]`,
|
||||
preview_x2: `[x2 🍮]`,
|
||||
preview_x3: `[x3 🍮]`,
|
||||
preview_x4: `[x4 🍮]`,
|
||||
preview_blur: `[blur ${this.$ts._mfm.dummy}]`,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -2,14 +2,14 @@
|
||||
<div class="fcuexfpr">
|
||||
<div v-if="note" class="note">
|
||||
<div class="_section" v-if="showNext">
|
||||
<XNotes class="_content" :pagination="next"/>
|
||||
<XNotes class="_content _noGap_" :pagination="next"/>
|
||||
</div>
|
||||
|
||||
<div class="_section main">
|
||||
<MkButton v-if="!showNext && hasNext" class="load next _content" @click="showNext = true"><Fa :icon="faChevronUp"/></MkButton>
|
||||
<div class="_content _vMargin">
|
||||
<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_vMargin"/>
|
||||
<XNote v-model:note="note" :key="note.id" :detail="true" class="_vMargin"/>
|
||||
<XNoteDetailed v-model:note="note" :key="note.id" class="_vMargin"/>
|
||||
</div>
|
||||
<div class="_content clips _vMargin" v-if="clips && clips.length > 0">
|
||||
<div class="title">{{ $ts.clip }}</div>
|
||||
@ -25,7 +25,7 @@
|
||||
</div>
|
||||
|
||||
<div class="_section" v-if="showPrev">
|
||||
<XNotes class="_content" :pagination="prev"/>
|
||||
<XNotes class="_content _noGap_" :pagination="prev"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';
|
||||
import XNote from '@/components/note.vue';
|
||||
import XNoteDetailed from '@/components/note-detailed.vue';
|
||||
import XNotes from '@/components/notes.vue';
|
||||
import MkRemoteCaution from '@/components/remote-caution.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
@ -47,6 +48,7 @@ import * as os from '@/os';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XNote,
|
||||
XNoteDetailed,
|
||||
XNotes,
|
||||
MkRemoteCaution,
|
||||
MkButton,
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormSwitch v-model:value="titlebar">{{ $ts.showTitlebar }}</FormSwitch>
|
||||
<FormSwitch v-model:value="showFixedPostForm">{{ $ts.showFixedPostForm }}</FormSwitch>
|
||||
|
||||
<FormSelect v-model:value="lang">
|
||||
@ -133,6 +134,7 @@ export default defineComponent({
|
||||
useOsNativeEmojis: defaultStore.makeGetterSetter('useOsNativeEmojis'),
|
||||
disableShowingAnimatedImages: defaultStore.makeGetterSetter('disableShowingAnimatedImages'),
|
||||
loadRawImages: defaultStore.makeGetterSetter('loadRawImages'),
|
||||
titlebar: defaultStore.makeGetterSetter('titlebar'),
|
||||
imageNewTab: defaultStore.makeGetterSetter('imageNewTab'),
|
||||
nsfw: defaultStore.makeGetterSetter('nsfw'),
|
||||
disablePagesScript: defaultStore.makeGetterSetter('disablePagesScript'),
|
||||
@ -175,6 +177,14 @@ export default defineComponent({
|
||||
showGapBetweenNotesInTimeline() {
|
||||
this.reloadAsk();
|
||||
},
|
||||
|
||||
titlebar() {
|
||||
this.reloadAsk();
|
||||
},
|
||||
|
||||
instanceTicker() {
|
||||
this.reloadAsk();
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
@ -1,49 +1,71 @@
|
||||
<template>
|
||||
<section class="_section">
|
||||
<div class="_title"><Fa :icon="faBoxes"/> {{ $ts.importAndExport }}</div>
|
||||
<div class="_content">
|
||||
<MkSelect v-model:value="exportTarget">
|
||||
<option value="notes">{{ $ts._exportOrImport.allNotes }}</option>
|
||||
<option value="following">{{ $ts._exportOrImport.followingList }}</option>
|
||||
<option value="user-lists">{{ $ts._exportOrImport.userLists }}</option>
|
||||
<option value="mute">{{ $ts._exportOrImport.muteList }}</option>
|
||||
<option value="blocking">{{ $ts._exportOrImport.blockingList }}</option>
|
||||
</MkSelect>
|
||||
<MkButton inline primary @click="doExport"><Fa :icon="faDownload"/> {{ $ts.export }}</MkButton>
|
||||
<MkButton inline primary @click="doImport" :disabled="!['following', 'user-lists'].includes(exportTarget)"><Fa :icon="faUpload"/> {{ $ts.import }}</MkButton>
|
||||
</div>
|
||||
</section>
|
||||
<FormBase>
|
||||
<FormGroup>
|
||||
<template #label>{{ $ts._exportOrImport.allNotes }}</template>
|
||||
<FormButton @click="doExport('notes')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<template #label>{{ $ts._exportOrImport.followingList }}</template>
|
||||
<FormButton @click="doExport('following')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
|
||||
<FormButton @click="doImport('following', $event)"><Fa :icon="faUpload"/> {{ $ts.import }}</FormButton>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<template #label>{{ $ts._exportOrImport.userLists }}</template>
|
||||
<FormButton @click="doExport('user-lists')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
|
||||
<FormButton @click="doImport('user-lists', $event)"><Fa :icon="faUpload"/> {{ $ts.import }}</FormButton>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<template #label>{{ $ts._exportOrImport.muteList }}</template>
|
||||
<FormButton @click="doExport('mute')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<template #label>{{ $ts._exportOrImport.blockingList }}</template>
|
||||
<FormButton @click="doExport('blocking')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
|
||||
</FormGroup>
|
||||
</FormBase>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faDownload, faUpload, faBoxes } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkSelect from '@/components/ui/select.vue';
|
||||
import FormSelect from '@/components/form/select.vue';
|
||||
import FormButton from '@/components/form/button.vue';
|
||||
import FormBase from '@/components/form/base.vue';
|
||||
import FormGroup from '@/components/form/group.vue';
|
||||
import * as os from '@/os';
|
||||
import { selectFile } from '@/scripts/select-file';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
MkSelect,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormButton,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
exportTarget: 'notes',
|
||||
INFO: {
|
||||
title: this.$ts.importAndExport,
|
||||
icon: faBoxes
|
||||
},
|
||||
faDownload, faUpload, faBoxes
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit('info', this.INFO);
|
||||
},
|
||||
|
||||
methods: {
|
||||
doExport() {
|
||||
doExport(target) {
|
||||
os.api(
|
||||
this.exportTarget == 'notes' ? 'i/export-notes' :
|
||||
this.exportTarget == 'following' ? 'i/export-following' :
|
||||
this.exportTarget == 'blocking' ? 'i/export-blocking' :
|
||||
this.exportTarget == 'user-lists' ? 'i/export-user-lists' :
|
||||
this.exportTarget == 'mute' ? 'i/export-mute' :
|
||||
target == 'notes' ? 'i/export-notes' :
|
||||
target == 'following' ? 'i/export-following' :
|
||||
target == 'blocking' ? 'i/export-blocking' :
|
||||
target == 'user-lists' ? 'i/export-user-lists' :
|
||||
target == 'mute' ? 'i/export-mute' :
|
||||
null, {})
|
||||
.then(() => {
|
||||
os.dialog({
|
||||
@ -58,12 +80,12 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
async doImport(e) {
|
||||
async doImport(target, e) {
|
||||
const file = await selectFile(e.currentTarget || e.target);
|
||||
|
||||
os.api(
|
||||
this.exportTarget == 'following' ? 'i/import-following' :
|
||||
this.exportTarget == 'user-lists' ? 'i/import-user-lists' :
|
||||
target == 'following' ? 'i/import-following' :
|
||||
target == 'user-lists' ? 'i/import-user-lists' :
|
||||
null, {
|
||||
fileId: file.id
|
||||
}).then(() => {
|
||||
|
@ -27,6 +27,9 @@
|
||||
<FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><Fa :icon="faKey"/></template>API</FormLink>
|
||||
<FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><Fa :icon="faEllipsisH"/></template>{{ $ts.other }}</FormLink>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormButton @click="clear">{{ $ts.clearCache }}</FormButton>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormButton @click="logout" danger>{{ $ts.logout }}</FormButton>
|
||||
</FormGroup>
|
||||
@ -126,6 +129,11 @@ export default defineComponent({
|
||||
logout: () => {
|
||||
signout();
|
||||
},
|
||||
clear: () => {
|
||||
localStorage.removeItem('locale');
|
||||
localStorage.removeItem('theme');
|
||||
location.reload();
|
||||
},
|
||||
faPalette, faPlug, faUser, faListUl, faLock, faLaugh, faCommentSlash, faMusic, faBell, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faEnvelope,
|
||||
};
|
||||
},
|
||||
|
@ -1,34 +1,34 @@
|
||||
<template>
|
||||
<section class="rrfwjxfl _section">
|
||||
<FormBase>
|
||||
<MkTab v-model:value="tab" style="margin-bottom: var(--margin);">
|
||||
<option value="mute">{{ $ts.mutedUsers }}</option>
|
||||
<option value="block">{{ $ts.blockedUsers }}</option>
|
||||
</MkTab>
|
||||
<div class="_content" v-if="tab === 'mute'">
|
||||
<div v-if="tab === 'mute'">
|
||||
<MkPagination :pagination="mutingPagination" class="muting">
|
||||
<template #empty><MkInfo>{{ $ts.noUsers }}</MkInfo></template>
|
||||
<template #default="{items}">
|
||||
<div class="user" v-for="mute in items" :key="mute.id">
|
||||
<MkA class="name" :to="userPage(mute.mutee)">
|
||||
<FormGroup>
|
||||
<FormLink v-for="mute in items" :key="mute.id" :to="userPage(mute.mutee)">
|
||||
<MkAcct :user="mute.mutee"/>
|
||||
</MkA>
|
||||
</div>
|
||||
</FormLink>
|
||||
</FormGroup>
|
||||
</template>
|
||||
</MkPagination>
|
||||
</div>
|
||||
<div class="_content" v-if="tab === 'block'">
|
||||
<div v-if="tab === 'block'">
|
||||
<MkPagination :pagination="blockingPagination" class="blocking">
|
||||
<template #empty><MkInfo>{{ $ts.noUsers }}</MkInfo></template>
|
||||
<template #default="{items}">
|
||||
<div class="user" v-for="block in items" :key="block.id">
|
||||
<MkA class="name" :to="userPage(block.blockee)">
|
||||
<FormGroup>
|
||||
<FormLink v-for="block in items" :key="block.id" :to="userPage(block.blockee)">
|
||||
<MkAcct :user="block.blockee"/>
|
||||
</MkA>
|
||||
</div>
|
||||
</FormLink>
|
||||
</FormGroup>
|
||||
</template>
|
||||
</MkPagination>
|
||||
</div>
|
||||
</section>
|
||||
</FormBase>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -37,6 +37,9 @@ import { faBan } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import MkTab from '@/components/tab.vue';
|
||||
import MkInfo from '@/components/ui/info.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
import FormBase from '@/components/form/base.vue';
|
||||
import FormGroup from '@/components/form/group.vue';
|
||||
import { userPage } from '@/filters/user';
|
||||
import * as os from '@/os';
|
||||
|
||||
@ -45,6 +48,9 @@ export default defineComponent({
|
||||
MkPagination,
|
||||
MkTab,
|
||||
MkInfo,
|
||||
FormBase,
|
||||
FormGroup,
|
||||
FormLink,
|
||||
},
|
||||
|
||||
emits: ['info'],
|
||||
@ -76,19 +82,3 @@ export default defineComponent({
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rrfwjxfl {
|
||||
> ._content {
|
||||
max-height: 350px;
|
||||
overflow: auto;
|
||||
|
||||
> .muting,
|
||||
> .blocking {
|
||||
> .empty {
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -90,12 +90,25 @@ export default defineComponent({
|
||||
|
||||
save() {
|
||||
this.$store.set('menu', this.splited);
|
||||
this.reloadAsk();
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.$store.reset('menu');
|
||||
this.items = this.$store.state.menu.join('\n');
|
||||
this.reloadAsk();
|
||||
},
|
||||
|
||||
async reloadAsk() {
|
||||
const { canceled } = await os.dialog({
|
||||
type: 'info',
|
||||
text: this.$ts.reloadToApplySetting,
|
||||
showCancelButton: true
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<FormBase>
|
||||
<FormSelect v-model:value="selectedThemeId">
|
||||
<template #label>{{ $ts.installedThemes }}</template>
|
||||
<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||
<optgroup :label="$ts.builtinThemes">
|
||||
<template #label>{{ $ts.theme }}</template>
|
||||
<optgroup :label="$ts._theme.installedThemes">
|
||||
<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||
</optgroup>
|
||||
<optgroup :label="$ts._theme.builtinThemes">
|
||||
<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||
</optgroup>
|
||||
</FormSelect>
|
||||
|
@ -1,10 +1,26 @@
|
||||
<template>
|
||||
<div class="mk-home" v-hotkey.global="keymap">
|
||||
<div class="cmuxhskf" v-hotkey.global="keymap">
|
||||
<div class="new" v-if="queue > 0" :style="{ width: width + 'px' }"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
|
||||
|
||||
<div class="_section">
|
||||
<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _content _vMargin"/>
|
||||
<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _panel _content _vMargin" fixed/>
|
||||
<div class="tabs _panel _vMargin">
|
||||
<div class="left">
|
||||
<button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><Fa :icon="faHome"/></button>
|
||||
<button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local" v-if="isLocalTimelineAvailable"><Fa :icon="faComments"/></button>
|
||||
<button class="_button tab" @click="() => { src = 'social'; saveSrc(); }" :class="{ active: src === 'social' }" v-tooltip="$ts._timelines.social" v-if="isLocalTimelineAvailable"><Fa :icon="faShareAlt"/></button>
|
||||
<button class="_button tab" @click="() => { src = 'global'; saveSrc(); }" :class="{ active: src === 'global' }" v-tooltip="$ts._timelines.global" v-if="isGlobalTimelineAvailable"><Fa :icon="faGlobe"/></button>
|
||||
<span class="divider"></span>
|
||||
<button class="_button tab" @click="() => { src = 'mentions'; saveSrc(); }" :class="{ active: src === 'mentions' }" v-tooltip="$ts.mentions"><Fa :icon="faAt"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadMentions"/></button>
|
||||
<button class="_button tab" @click="() => { src = 'directs'; saveSrc(); }" :class="{ active: src === 'directs' }" v-tooltip="$ts.directNotes"><Fa :icon="faEnvelope"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadSpecifiedNotes"/></button>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button class="_button tab" @click="chooseChannel" :class="{ active: src === 'channel' }" v-tooltip="$ts.channel"><Fa :icon="faSatelliteDish"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadChannel"/></button>
|
||||
<button class="_button tab" @click="chooseAntenna" :class="{ active: src === 'antenna' }" v-tooltip="$ts.antennas"><Fa :icon="faSatellite"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadAntenna"/></button>
|
||||
<button class="_button tab" @click="chooseList" :class="{ active: src === 'list' }" v-tooltip="$ts.lists"><Fa :icon="faListUl"/></button>
|
||||
</div>
|
||||
</div>
|
||||
<XTimeline ref="tl"
|
||||
class="_content _vMargin"
|
||||
:key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src"
|
||||
@ -23,8 +39,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, defineAsyncComponent, computed } from 'vue';
|
||||
import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faComments } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faPencilAlt, faAt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faComments, faEnvelope } from '@fortawesome/free-regular-svg-icons';
|
||||
import Progress from '@/scripts/loading';
|
||||
import XTimeline from '@/components/timeline.vue';
|
||||
import XPostForm from '@/components/post-form.vue';
|
||||
@ -49,64 +65,15 @@ export default defineComponent({
|
||||
menuOpened: false,
|
||||
queue: 0,
|
||||
width: 0,
|
||||
INFO: computed(() => {
|
||||
const tabs = [{
|
||||
id: 'home',
|
||||
title: null,
|
||||
tooltip: this.$ts._timelines.home,
|
||||
icon: faHome,
|
||||
onClick: () => { this.src = 'home'; this.saveSrc(); },
|
||||
selected: computed(() => this.src === 'home')
|
||||
}];
|
||||
|
||||
if (!this.$instance.disableLocalTimeline || this.$i.isModerator || this.$i.isAdmin) {
|
||||
tabs.push({
|
||||
id: 'local',
|
||||
title: null,
|
||||
tooltip: this.$ts._timelines.local,
|
||||
icon: faComments,
|
||||
onClick: () => { this.src = 'local'; this.saveSrc(); },
|
||||
selected: computed(() => this.src === 'local')
|
||||
});
|
||||
|
||||
tabs.push({
|
||||
id: 'social',
|
||||
title: null,
|
||||
tooltip: this.$ts._timelines.social,
|
||||
icon: faShareAlt,
|
||||
onClick: () => { this.src = 'social'; this.saveSrc(); },
|
||||
selected: computed(() => this.src === 'social')
|
||||
});
|
||||
INFO: computed(() => ({
|
||||
title: this.$ts.timeline,
|
||||
icon: this.src === 'local' ? faComments : this.src === 'social' ? faShareAlt : this.src === 'global' ? faGlobe : faHome,
|
||||
action: {
|
||||
icon: faPencilAlt,
|
||||
handler: () => os.post()
|
||||
}
|
||||
|
||||
if (!this.$instance.disableGlobalTimeline || this.$i.isModerator || this.$i.isAdmin) {
|
||||
tabs.push({
|
||||
id: 'global',
|
||||
title: null,
|
||||
tooltip: this.$ts._timelines.global,
|
||||
icon: faGlobe,
|
||||
onClick: () => { this.src = 'global'; this.saveSrc(); },
|
||||
selected: computed(() => this.src === 'global')
|
||||
});
|
||||
}
|
||||
|
||||
tabs.push({
|
||||
id: 'other',
|
||||
title: null,
|
||||
icon: faEllipsisH,
|
||||
onClick: this.choose,
|
||||
indicate: computed(() => this.$i.hasUnreadAntenna || this.$i.hasUnreadChannel)
|
||||
});
|
||||
|
||||
return {
|
||||
tabs,
|
||||
action: {
|
||||
icon: faPencilAlt,
|
||||
handler: () => os.post()
|
||||
}
|
||||
};
|
||||
}),
|
||||
faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faSatelliteDish, faCircle
|
||||
})),
|
||||
faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faAt, faEnvelope,
|
||||
};
|
||||
},
|
||||
|
||||
@ -117,8 +84,12 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
|
||||
meta() {
|
||||
return this.$instance;
|
||||
isLocalTimelineAvailable(): boolean {
|
||||
return !this.$instance.disableLocalTimeline || this.$i.isModerator || this.$i.isAdmin;
|
||||
},
|
||||
|
||||
isGlobalTimelineAvailable(): boolean {
|
||||
return !this.$instance.disableGlobalTimeline || this.$i.isModerator || this.$i.isAdmin;
|
||||
},
|
||||
},
|
||||
|
||||
@ -176,16 +147,23 @@ export default defineComponent({
|
||||
scroll(this.$el, 0);
|
||||
},
|
||||
|
||||
async choose(ev) {
|
||||
if (this.meta == null) return;
|
||||
const [antennas, lists, channels] = await Promise.all([
|
||||
os.api('antennas/list'),
|
||||
os.api('users/lists/list'),
|
||||
os.api('channels/followed'),
|
||||
]);
|
||||
const antennaItems = antennas.map(antenna => ({
|
||||
async chooseList(ev) {
|
||||
const lists = await os.api('users/lists/list');
|
||||
const items = lists.map(list => ({
|
||||
text: list.name,
|
||||
action: () => {
|
||||
this.list = list;
|
||||
this.src = 'list';
|
||||
this.saveSrc();
|
||||
}
|
||||
}));
|
||||
os.modalMenu(items, ev.currentTarget || ev.target);
|
||||
},
|
||||
|
||||
async chooseAntenna(ev) {
|
||||
const antennas = await os.api('antennas/list');
|
||||
const items = antennas.map(antenna => ({
|
||||
text: antenna.name,
|
||||
icon: faSatellite,
|
||||
indicate: antenna.hasUnreadNote,
|
||||
action: () => {
|
||||
this.antenna = antenna;
|
||||
@ -193,18 +171,13 @@ export default defineComponent({
|
||||
this.saveSrc();
|
||||
}
|
||||
}));
|
||||
const listItems = lists.map(list => ({
|
||||
text: list.name,
|
||||
icon: faListUl,
|
||||
action: () => {
|
||||
this.list = list;
|
||||
this.src = 'list';
|
||||
this.saveSrc();
|
||||
}
|
||||
}));
|
||||
const channelItems = channels.map(channel => ({
|
||||
os.modalMenu(items, ev.currentTarget || ev.target);
|
||||
},
|
||||
|
||||
async chooseChannel(ev) {
|
||||
const channels = await os.api('channels/followed');
|
||||
const items = channels.map(channel => ({
|
||||
text: channel.name,
|
||||
icon: faSatelliteDish,
|
||||
indicate: channel.hasUnreadNote,
|
||||
action: () => {
|
||||
// NOTE: チャンネルタイムラインをこのコンポーネントで表示するようにすると投稿フォームはどうするかなどの問題が生じるのでとりあえずページ遷移で
|
||||
@ -214,7 +187,7 @@ export default defineComponent({
|
||||
this.$router.push(`/channels/${channel.id}`);
|
||||
}
|
||||
}));
|
||||
os.modalMenu([...antennaItems, listItems.length > 0 ? null : undefined, ...listItems, channelItems.length > 0 ? null : undefined, ...channelItems], ev.currentTarget || ev.target);
|
||||
os.modalMenu(items, ev.currentTarget || ev.target);
|
||||
},
|
||||
|
||||
saveSrc() {
|
||||
@ -235,7 +208,7 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mk-home {
|
||||
.cmuxhskf {
|
||||
> .new {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
@ -249,7 +222,71 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> ._section {
|
||||
> .tabs {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
padding: 0 8px;
|
||||
max-width: var(--baseContentWidth);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
|
||||
// 影の都合上
|
||||
position: relative;
|
||||
|
||||
> .right {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
> .left, > .right {
|
||||
> .tab {
|
||||
position: relative;
|
||||
height: 50px;
|
||||
padding: 0 12px;
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--fgHighlighted);
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
width: calc(100% - 16px);
|
||||
height: 4px;
|
||||
background: var(--accent);
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .i {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 8px;
|
||||
color: var(--indicator);
|
||||
font-size: 8px;
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
> .divider {
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
height: 28px;
|
||||
vertical-align: middle;
|
||||
margin: 0 8px;
|
||||
background: var(--divider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -80,7 +80,7 @@
|
||||
</div>
|
||||
<template v-if="page === 'index'">
|
||||
<div v-if="user.pinnedNotes.length > 0" class="_vMargin">
|
||||
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :detail="true" :pinned="true"/>
|
||||
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
|
||||
</div>
|
||||
<div class="_vMargin">
|
||||
<XUserTimeline :user="user"/>
|
||||
@ -195,7 +195,7 @@
|
||||
<template v-if="page === 'index'">
|
||||
<div class="_content _vMargin">
|
||||
<div v-if="user.pinnedNotes.length > 0" class="_vMargin">
|
||||
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :detail="true" :pinned="true"/>
|
||||
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
|
||||
</div>
|
||||
<XPhotos :user="user" :key="user.id" class="_vMargin"/>
|
||||
<XActivity :user="user" :key="user.id" class="_vMargin"/>
|
||||
|
308
src/client/pages/welcome.entrance.a.vue
Normal file
308
src/client/pages/welcome.entrance.a.vue
Normal file
@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<div class="rsqzvsbo" v-if="meta">
|
||||
<div class="top">
|
||||
<MkFeaturedPhotos class="bg"/>
|
||||
<XTimeline class="tl"/>
|
||||
<div class="shape"></div>
|
||||
<img src="/assets/misskey.svg" class="misskey"/>
|
||||
<div class="emojis">
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="👍"/>
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="❤"/>
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="😆"/>
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="🍮"/>
|
||||
</div>
|
||||
<div class="main _panel">
|
||||
<div class="bg">
|
||||
<div class="fade"></div>
|
||||
</div>
|
||||
<div class="fg">
|
||||
<h1>
|
||||
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
|
||||
</h1>
|
||||
<div class="about">
|
||||
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
|
||||
</div>
|
||||
<div class="action">
|
||||
<MkButton @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
|
||||
<MkButton @click="signin()" inline>{{ $ts.login }}</MkButton>
|
||||
</div>
|
||||
<div class="status" v-if="onlineUsersCount && stats">
|
||||
<div>
|
||||
<I18n :src="$ts.nUsers" text-tag="span" class="users">
|
||||
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
|
||||
</I18n>
|
||||
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
|
||||
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
|
||||
</I18n>
|
||||
</div>
|
||||
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
|
||||
<template #n><b>{{ onlineUsersCount }}</b></template>
|
||||
</I18n>
|
||||
</div>
|
||||
<button class="_button _acrylic menu" @click="showMenu"><Fa :icon="faEllipsisH"/></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { toUnicode } from 'punycode';
|
||||
import XSigninDialog from '@/components/signin-dialog.vue';
|
||||
import XSignupDialog from '@/components/signup-dialog.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import XNote from '@/components/note.vue';
|
||||
import MkFeaturedPhotos from '@/components/featured-photos.vue';
|
||||
import XTimeline from './welcome.timeline.vue';
|
||||
import { host, instanceName } from '@/config';
|
||||
import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
XNote,
|
||||
MkFeaturedPhotos,
|
||||
XTimeline,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
host: toUnicode(host),
|
||||
instanceName,
|
||||
meta: null,
|
||||
stats: null,
|
||||
tags: [],
|
||||
onlineUsersCount: null,
|
||||
faEllipsisH
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
os.api('meta', { detail: true }).then(meta => {
|
||||
this.meta = meta;
|
||||
});
|
||||
|
||||
os.api('stats').then(stats => {
|
||||
this.stats = stats;
|
||||
});
|
||||
|
||||
os.api('get-online-users-count').then(res => {
|
||||
this.onlineUsersCount = res.count;
|
||||
});
|
||||
|
||||
os.api('hashtags/list', {
|
||||
sort: '+mentionedLocalUsers',
|
||||
limit: 8
|
||||
}).then(tags => {
|
||||
this.tags = tags;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
signin() {
|
||||
os.popup(XSigninDialog, {
|
||||
autoSet: true
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
||||
signup() {
|
||||
os.popup(XSignupDialog, {
|
||||
autoSet: true
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
||||
showMenu(ev) {
|
||||
os.modalMenu([{
|
||||
text: this.$t('aboutX', { x: instanceName }),
|
||||
icon: faInfoCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/about');
|
||||
}
|
||||
}, {
|
||||
text: this.$ts.aboutMisskey,
|
||||
icon: faInfoCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/about-misskey');
|
||||
}
|
||||
}, null, {
|
||||
text: this.$ts.help,
|
||||
icon: faQuestionCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/docs');
|
||||
}
|
||||
}], ev.currentTarget || ev.target);
|
||||
},
|
||||
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rsqzvsbo {
|
||||
> .top {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
padding: 16px;
|
||||
|
||||
> .bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
> .tl {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 64px;
|
||||
margin: auto;
|
||||
width: 500px;
|
||||
height: calc(100% - 128px);
|
||||
overflow: hidden;
|
||||
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
|
||||
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
> .shape {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--accent);
|
||||
clip-path: polygon(0% 0%, 50% 0%, 15% 100%, 0% 100%);
|
||||
}
|
||||
|
||||
> .misskey {
|
||||
position: absolute;
|
||||
top: 42px;
|
||||
left: 42px;
|
||||
width: 160px;
|
||||
|
||||
@media (max-width: 450px) {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
> .emojis {
|
||||
position: absolute;
|
||||
bottom: 32px;
|
||||
left: 35px;
|
||||
|
||||
> * {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
> .main {
|
||||
position: relative;
|
||||
width: min(480px, 100%);
|
||||
margin: auto auto auto 128px;
|
||||
box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
> .bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 128px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
opacity: 0.75;
|
||||
|
||||
> .fade {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 128px;
|
||||
background: linear-gradient(0deg, var(--panel), var(--X15));
|
||||
}
|
||||
}
|
||||
|
||||
> .fg {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
> h1 {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 32px 32px 24px 32px;
|
||||
|
||||
> .logo {
|
||||
vertical-align: bottom;
|
||||
max-height: 120px;
|
||||
max-width: min(100%, 300px);
|
||||
}
|
||||
}
|
||||
|
||||
> .about {
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
> .action {
|
||||
padding: 32px;
|
||||
|
||||
> * {
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
> .status {
|
||||
border-top: solid 1px var(--divider);
|
||||
padding: 32px;
|
||||
font-size: 90%;
|
||||
|
||||
> div {
|
||||
> span:not(:last-child) {
|
||||
padding-right: 1em;
|
||||
margin-right: 1em;
|
||||
border-right: solid 1px var(--divider);
|
||||
}
|
||||
}
|
||||
|
||||
> .online {
|
||||
::v-deep(b) {
|
||||
color: #41b781;
|
||||
}
|
||||
|
||||
::v-deep(span) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .menu {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
238
src/client/pages/welcome.entrance.b.vue
Normal file
238
src/client/pages/welcome.entrance.b.vue
Normal file
@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="rsqzvsbo" v-if="meta">
|
||||
<div class="top">
|
||||
<MkFeaturedPhotos class="bg"/>
|
||||
<XTimeline class="tl"/>
|
||||
<div class="shape"></div>
|
||||
<div class="main">
|
||||
<h1>
|
||||
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
|
||||
</h1>
|
||||
<div class="about">
|
||||
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
|
||||
</div>
|
||||
<div class="action">
|
||||
<MkButton class="signup" @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
|
||||
<MkButton class="signin" @click="signin()" inline>{{ $ts.login }}</MkButton>
|
||||
</div>
|
||||
<div class="status" v-if="onlineUsersCount && stats">
|
||||
<div>
|
||||
<I18n :src="$ts.nUsers" text-tag="span" class="users">
|
||||
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
|
||||
</I18n>
|
||||
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
|
||||
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
|
||||
</I18n>
|
||||
</div>
|
||||
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
|
||||
<template #n><b>{{ onlineUsersCount }}</b></template>
|
||||
</I18n>
|
||||
</div>
|
||||
</div>
|
||||
<img src="/assets/misskey.svg" class="misskey"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { toUnicode } from 'punycode';
|
||||
import XSigninDialog from '@/components/signin-dialog.vue';
|
||||
import XSignupDialog from '@/components/signup-dialog.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import XNote from '@/components/note.vue';
|
||||
import MkFeaturedPhotos from '@/components/featured-photos.vue';
|
||||
import XTimeline from './welcome.timeline.vue';
|
||||
import { host, instanceName } from '@/config';
|
||||
import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
XNote,
|
||||
XTimeline,
|
||||
MkFeaturedPhotos,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
host: toUnicode(host),
|
||||
instanceName,
|
||||
meta: null,
|
||||
stats: null,
|
||||
tags: [],
|
||||
onlineUsersCount: null,
|
||||
faEllipsisH
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
os.api('meta', { detail: true }).then(meta => {
|
||||
this.meta = meta;
|
||||
});
|
||||
|
||||
os.api('stats').then(stats => {
|
||||
this.stats = stats;
|
||||
});
|
||||
|
||||
os.api('get-online-users-count').then(res => {
|
||||
this.onlineUsersCount = res.count;
|
||||
});
|
||||
|
||||
os.api('hashtags/list', {
|
||||
sort: '+mentionedLocalUsers',
|
||||
limit: 8
|
||||
}).then(tags => {
|
||||
this.tags = tags;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
signin() {
|
||||
os.popup(XSigninDialog, {
|
||||
autoSet: true
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
||||
signup() {
|
||||
os.popup(XSignupDialog, {
|
||||
autoSet: true
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
||||
showMenu(ev) {
|
||||
os.modalMenu([{
|
||||
text: this.$t('aboutX', { x: instanceName }),
|
||||
icon: faInfoCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/about');
|
||||
}
|
||||
}, {
|
||||
text: this.$ts.aboutMisskey,
|
||||
icon: faInfoCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/about-misskey');
|
||||
}
|
||||
}, null, {
|
||||
text: this.$ts.help,
|
||||
icon: faQuestionCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/docs');
|
||||
}
|
||||
}], ev.currentTarget || ev.target);
|
||||
},
|
||||
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rsqzvsbo {
|
||||
> .top {
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
|
||||
> .bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
> .tl {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 64px;
|
||||
margin: auto;
|
||||
width: 500px;
|
||||
height: calc(100% - 128px);
|
||||
overflow: hidden;
|
||||
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
|
||||
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
|
||||
}
|
||||
|
||||
> .shape {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--accent);
|
||||
clip-path: polygon(0% 0%, 40% 0%, 22% 100%, 0% 100%);
|
||||
}
|
||||
|
||||
> .misskey {
|
||||
position: absolute;
|
||||
bottom: 64px;
|
||||
left: 64px;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
> .main {
|
||||
position: relative;
|
||||
width: min(450px, 100%);
|
||||
padding: 64px;
|
||||
color: #fff;
|
||||
font-size: 1.1em;
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
> h1 {
|
||||
display: block;
|
||||
margin: 0 0 32px 0;
|
||||
padding: 0;
|
||||
|
||||
> .logo {
|
||||
vertical-align: bottom;
|
||||
max-height: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
> .about {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
> .action {
|
||||
margin: 32px 0;
|
||||
|
||||
> * {
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
> .signup {
|
||||
background: var(--panel);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
> .signin {
|
||||
background: var(--accent);
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
> .status {
|
||||
margin: 32px 0;
|
||||
border-top: solid 1px rgba(255, 255, 255, 0.5);
|
||||
font-size: 90%;
|
||||
|
||||
> div {
|
||||
padding: 16px 0;
|
||||
|
||||
> span:not(:last-child) {
|
||||
padding-right: 1em;
|
||||
margin-right: 1em;
|
||||
border-right: solid 1px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
307
src/client/pages/welcome.entrance.c.vue
Normal file
307
src/client/pages/welcome.entrance.c.vue
Normal file
@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div class="rsqzvsbo" v-if="meta">
|
||||
<div class="top">
|
||||
<MkFeaturedPhotos class="bg"/>
|
||||
<div class="fade"></div>
|
||||
<div class="emojis">
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="👍"/>
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="❤"/>
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="😆"/>
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
|
||||
<MkEmoji :normal="true" :no-style="true" emoji="🍮"/>
|
||||
</div>
|
||||
<div class="main">
|
||||
<img src="/assets/misskey.svg" class="misskey"/>
|
||||
<div class="form _panel">
|
||||
<div class="bg">
|
||||
<div class="fade"></div>
|
||||
</div>
|
||||
<div class="fg">
|
||||
<h1>
|
||||
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
|
||||
</h1>
|
||||
<div class="about">
|
||||
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
|
||||
</div>
|
||||
<div class="action">
|
||||
<MkButton @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
|
||||
<MkButton @click="signin()" inline>{{ $ts.login }}</MkButton>
|
||||
</div>
|
||||
<div class="status" v-if="onlineUsersCount && stats">
|
||||
<div>
|
||||
<I18n :src="$ts.nUsers" text-tag="span" class="users">
|
||||
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
|
||||
</I18n>
|
||||
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
|
||||
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
|
||||
</I18n>
|
||||
</div>
|
||||
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
|
||||
<template #n><b>{{ onlineUsersCount }}</b></template>
|
||||
</I18n>
|
||||
</div>
|
||||
<button class="_button _acrylic menu" @click="showMenu"><Fa :icon="faEllipsisH"/></button>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<MkA to="/announcements">{{ $ts.announcements }}</MkA>
|
||||
<MkA to="/explore">{{ $ts.explore }}</MkA>
|
||||
<MkA to="/channels">{{ $ts.channel }}</MkA>
|
||||
<MkA to="/featured">{{ $ts.featured }}</MkA>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { toUnicode } from 'punycode';
|
||||
import XSigninDialog from '@/components/signin-dialog.vue';
|
||||
import XSignupDialog from '@/components/signup-dialog.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import XNote from '@/components/note.vue';
|
||||
import MkFeaturedPhotos from '@/components/featured-photos.vue';
|
||||
import XTimeline from './welcome.timeline.vue';
|
||||
import { host, instanceName } from '@/config';
|
||||
import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
XNote,
|
||||
MkFeaturedPhotos,
|
||||
XTimeline,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
host: toUnicode(host),
|
||||
instanceName,
|
||||
meta: null,
|
||||
stats: null,
|
||||
tags: [],
|
||||
onlineUsersCount: null,
|
||||
faEllipsisH
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
os.api('meta', { detail: true }).then(meta => {
|
||||
this.meta = meta;
|
||||
});
|
||||
|
||||
os.api('stats').then(stats => {
|
||||
this.stats = stats;
|
||||
});
|
||||
|
||||
os.api('get-online-users-count').then(res => {
|
||||
this.onlineUsersCount = res.count;
|
||||
});
|
||||
|
||||
os.api('hashtags/list', {
|
||||
sort: '+mentionedLocalUsers',
|
||||
limit: 8
|
||||
}).then(tags => {
|
||||
this.tags = tags;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
signin() {
|
||||
os.popup(XSigninDialog, {
|
||||
autoSet: true
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
||||
signup() {
|
||||
os.popup(XSignupDialog, {
|
||||
autoSet: true
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
||||
showMenu(ev) {
|
||||
os.modalMenu([{
|
||||
text: this.$t('aboutX', { x: instanceName }),
|
||||
icon: faInfoCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/about');
|
||||
}
|
||||
}, {
|
||||
text: this.$ts.aboutMisskey,
|
||||
icon: faInfoCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/about-misskey');
|
||||
}
|
||||
}, null, {
|
||||
text: this.$ts.help,
|
||||
icon: faQuestionCircle,
|
||||
action: () => {
|
||||
os.pageWindow('/docs');
|
||||
}
|
||||
}], ev.currentTarget || ev.target);
|
||||
},
|
||||
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rsqzvsbo {
|
||||
> .top {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
padding: 16px;
|
||||
|
||||
> .bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
> .fade {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
> .emojis {
|
||||
position: absolute;
|
||||
bottom: 32px;
|
||||
left: 35px;
|
||||
|
||||
> * {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
> .main {
|
||||
position: relative;
|
||||
width: min(460px, 100%);
|
||||
margin: auto;
|
||||
|
||||
> .misskey {
|
||||
width: 150px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
@media (max-width: 450px) {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
> .form {
|
||||
position: relative;
|
||||
box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
|
||||
|
||||
> .bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 128px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
opacity: 0.75;
|
||||
|
||||
> .fade {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 128px;
|
||||
background: linear-gradient(0deg, var(--panel), var(--X15));
|
||||
}
|
||||
}
|
||||
|
||||
> .fg {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
> h1 {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 32px 32px 24px 32px;
|
||||
|
||||
> .logo {
|
||||
vertical-align: bottom;
|
||||
max-height: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
> .about {
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
> .action {
|
||||
padding: 32px;
|
||||
|
||||
> * {
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
> .status {
|
||||
border-top: solid 1px var(--divider);
|
||||
padding: 32px;
|
||||
font-size: 90%;
|
||||
|
||||
> div {
|
||||
> span:not(:last-child) {
|
||||
padding-right: 1em;
|
||||
margin-right: 1em;
|
||||
border-right: solid 1px var(--divider);
|
||||
}
|
||||
}
|
||||
|
||||
> .online {
|
||||
::v-deep(b) {
|
||||
color: #41b781;
|
||||
}
|
||||
|
||||
::v-deep(span) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .menu {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .nav {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-top: 20px;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 8px black;
|
||||
font-size: 0.9em;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,181 +0,0 @@
|
||||
<template>
|
||||
<div class="rsqzvsbo _section" v-if="meta">
|
||||
<div class="overview _monospace" v-if="stats">
|
||||
<div class="stats">
|
||||
<div><span>Users</span><span>{{ number(stats.originalUsersCount) }}</span></div>
|
||||
<div><span>Notes</span><span>{{ number(stats.originalNotesCount) }}</span></div>
|
||||
<div><span>Reactions</span><span>{{ number(stats.reactionsCount) }}</span></div>
|
||||
</div>
|
||||
<div class="tags">
|
||||
<MkA class="tag" v-for="tag in tags" :to="`/tags/${encodeURIComponent(tag.tag)}`">#{{ tag.tag }}</MkA>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="meta.pinnedClipId">
|
||||
<h2># {{ $ts.pinnedNotes }}</h2>
|
||||
<MkPagination :pagination="clipPagination" #default="{items}">
|
||||
<XNote class="kmkqjgkl" v-for="note in items" :note="note" :key="note.id"/>
|
||||
</MkPagination>
|
||||
</template>
|
||||
<template v-else>
|
||||
<h2># {{ $ts.featured }}</h2>
|
||||
<MkPagination :pagination="featuredPagination" #default="{items}">
|
||||
<XNote class="kmkqjgkl" v-for="note in items" :note="note" :key="note.id"/>
|
||||
</MkPagination>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { toUnicode } from 'punycode';
|
||||
import XSigninDialog from '@/components/signin-dialog.vue';
|
||||
import XSignupDialog from '@/components/signup-dialog.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import XNote from '@/components/note.vue';
|
||||
import MkPagination from '@/components/ui/pagination.vue';
|
||||
import { host, instanceName } from '@/config';
|
||||
import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkButton,
|
||||
XNote,
|
||||
MkPagination,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
host: toUnicode(host),
|
||||
instanceName,
|
||||
meta: null,
|
||||
stats: null,
|
||||
tags: [],
|
||||
clipPagination: {
|
||||
endpoint: 'clips/notes',
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
clipId: this.meta.pinnedClipId,
|
||||
})
|
||||
},
|
||||
featuredPagination: {
|
||||
endpoint: 'notes/featured',
|
||||
limit: 10,
|
||||
offsetMode: true
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
os.api('meta', { detail: true }).then(meta => {
|
||||
this.meta = meta;
|
||||
});
|
||||
|
||||
os.api('stats').then(stats => {
|
||||
this.stats = stats;
|
||||
});
|
||||
|
||||
os.api('hashtags/list', {
|
||||
sort: '+mentionedLocalUsers',
|
||||
limit: 8
|
||||
}).then(tags => {
|
||||
this.tags = tags;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
signin() {
|
||||
os.popup(XSigninDialog, {
|
||||
autoSet: true
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
||||
signup() {
|
||||
os.popup(XSignupDialog, {
|
||||
autoSet: true
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rsqzvsbo {
|
||||
text-align: center;
|
||||
|
||||
> h2 {
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
margin: 16px;
|
||||
padding: 8px 12px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
> .overview {
|
||||
> .stats, > .tags {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 530px;
|
||||
padding: 32px;
|
||||
margin: 16px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .stats {
|
||||
background: var(--accent);
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
font-size: 1.5em;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
|
||||
> span:first-child {
|
||||
opacity: 0.7;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
> span:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .tags {
|
||||
background: var(--panel);
|
||||
border-radius: 12px;
|
||||
color: var(--fg);
|
||||
font-size: 1.5em;
|
||||
|
||||
> .tag {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.kmkqjgkl {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 530px;
|
||||
margin: 16px;
|
||||
box-sizing: border-box;
|
||||
text-align: left;
|
||||
box-shadow: 0 6px 46px rgb(0 0 0 / 25%);
|
||||
border-radius: 12px;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
51
src/client/pages/welcome.timeline.vue
Normal file
51
src/client/pages/welcome.timeline.vue
Normal file
@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="civpbkhh">
|
||||
<div v-for="note in notes" class="note">
|
||||
<div class="content _panel">
|
||||
{{ note.text }}
|
||||
</div>
|
||||
<XReactionsViewer :note="note" ref="reactionsViewer"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XReactionsViewer from '@/components/reactions-viewer.vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XReactionsViewer
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
notes: [],
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
os.api('notes/featured').then(notes => {
|
||||
this.notes = notes;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.civpbkhh {
|
||||
text-align: right;
|
||||
|
||||
> .note {
|
||||
margin: 16px 0 16px auto;
|
||||
|
||||
> .content {
|
||||
padding: 16px;
|
||||
margin: 0 0 0 auto;
|
||||
max-width: max-content;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -8,7 +8,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XSetup from './welcome.setup.vue';
|
||||
import XEntrance from './welcome.entrance.vue';
|
||||
import XEntrance from './welcome.entrance.a.vue';
|
||||
import { instanceName } from '@/config';
|
||||
import * as os from '@/os';
|
||||
|
||||
|
@ -1,19 +1,13 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import * as seedrandom from 'seedrandom';
|
||||
import { Variable, PageVar, envVarsDef, funcDefs, Block, isFnBlock } from '.';
|
||||
import { Variable, PageVar, envVarsDef, Block, isFnBlock, Fn, HpmlScope, HpmlError } from '.';
|
||||
import { version } from '@/config';
|
||||
import { AiScript, utils, values } from '@syuilo/aiscript';
|
||||
import { createAiScriptEnv } from '../aiscript/api';
|
||||
import { collectPageVars } from '../collect-page-vars';
|
||||
import { initLib } from './lib';
|
||||
import { initHpmlLib, initAiLib } from './lib';
|
||||
import * as os from '@/os';
|
||||
import { markRaw, ref, Ref } from 'vue';
|
||||
|
||||
type Fn = {
|
||||
slots: string[];
|
||||
exec: (args: Record<string, any>) => ReturnType<Hpml['evaluate']>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hpml evaluator
|
||||
*/
|
||||
@ -41,7 +35,7 @@ export class Hpml {
|
||||
if (this.opts.enableAiScript) {
|
||||
this.aiscript = markRaw(new AiScript({ ...createAiScriptEnv({
|
||||
storageKey: 'pages:' + this.page.id
|
||||
}), ...initLib(this)}, {
|
||||
}), ...initAiLib(this)}, {
|
||||
in: (q) => {
|
||||
return new Promise(ok => {
|
||||
os.dialog({
|
||||
@ -137,7 +131,7 @@ export class Hpml {
|
||||
}
|
||||
|
||||
@autobind
|
||||
private _interpolate(str: string, scope: Scope) {
|
||||
private _interpolateScope(str: string, scope: HpmlScope) {
|
||||
return str.replace(/{(.+?)}/g, match => {
|
||||
const v = scope.getState(match.slice(1, -1).trim());
|
||||
return v == null ? 'NULL' : v.toString();
|
||||
@ -157,14 +151,14 @@ export class Hpml {
|
||||
}
|
||||
|
||||
for (const v of this.variables) {
|
||||
values[v.name] = this.evaluate(v, new Scope([values]));
|
||||
values[v.name] = this.evaluate(v, new HpmlScope([values]));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@autobind
|
||||
private evaluate(block: Block, scope: Scope): any {
|
||||
private evaluate(block: Block, scope: HpmlScope): any {
|
||||
if (block.type === null) {
|
||||
return null;
|
||||
}
|
||||
@ -174,11 +168,11 @@ export class Hpml {
|
||||
}
|
||||
|
||||
if (block.type === 'text' || block.type === 'multiLineText') {
|
||||
return this._interpolate(block.value || '', scope);
|
||||
return this._interpolateScope(block.value || '', scope);
|
||||
}
|
||||
|
||||
if (block.type === 'textList') {
|
||||
return this._interpolate(block.value || '', scope).trim().split('\n');
|
||||
return this._interpolateScope(block.value || '', scope).trim().split('\n');
|
||||
}
|
||||
|
||||
if (block.type === 'ref') {
|
||||
@ -197,7 +191,8 @@ export class Hpml {
|
||||
}
|
||||
}
|
||||
|
||||
if (isFnBlock(block)) { // ユーザー関数定義
|
||||
// Define user function
|
||||
if (isFnBlock(block)) {
|
||||
return {
|
||||
slots: block.value.slots.map(x => x.name),
|
||||
exec: (slotArg: Record<string, any>) => {
|
||||
@ -206,7 +201,8 @@ export class Hpml {
|
||||
} as Fn;
|
||||
}
|
||||
|
||||
if (block.type.startsWith('fn:')) { // ユーザー関数呼び出し
|
||||
// Call user function
|
||||
if (block.type.startsWith('fn:')) {
|
||||
const fnName = block.type.split(':')[1];
|
||||
const fn = scope.getState(fnName);
|
||||
const args = {} as Record<string, any>;
|
||||
@ -219,77 +215,9 @@ export class Hpml {
|
||||
|
||||
if (block.args === undefined) return null;
|
||||
|
||||
const date = new Date();
|
||||
const day = `${this.opts.visitor ? this.opts.visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
|
||||
|
||||
const funcs: { [p in keyof typeof funcDefs]: Function } = {
|
||||
not: (a: boolean) => !a,
|
||||
or: (a: boolean, b: boolean) => a || b,
|
||||
and: (a: boolean, b: boolean) => a && b,
|
||||
eq: (a: any, b: any) => a === b,
|
||||
notEq: (a: any, b: any) => a !== b,
|
||||
gt: (a: number, b: number) => a > b,
|
||||
lt: (a: number, b: number) => a < b,
|
||||
gtEq: (a: number, b: number) => a >= b,
|
||||
ltEq: (a: number, b: number) => a <= b,
|
||||
if: (bool: boolean, a: any, b: any) => bool ? a : b,
|
||||
for: (times: number, fn: Fn) => {
|
||||
const result = [];
|
||||
for (let i = 0; i < times; i++) {
|
||||
result.push(fn.exec({
|
||||
[fn.slots[0]]: i + 1
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
add: (a: number, b: number) => a + b,
|
||||
subtract: (a: number, b: number) => a - b,
|
||||
multiply: (a: number, b: number) => a * b,
|
||||
divide: (a: number, b: number) => a / b,
|
||||
mod: (a: number, b: number) => a % b,
|
||||
round: (a: number) => Math.round(a),
|
||||
strLen: (a: string) => a.length,
|
||||
strPick: (a: string, b: number) => a[b - 1],
|
||||
strReplace: (a: string, b: string, c: string) => a.split(b).join(c),
|
||||
strReverse: (a: string) => a.split('').reverse().join(''),
|
||||
join: (texts: string[], separator: string) => texts.join(separator || ''),
|
||||
stringToNumber: (a: string) => parseInt(a),
|
||||
numberToString: (a: number) => a.toString(),
|
||||
splitStrByLine: (a: string) => a.split('\n'),
|
||||
pick: (list: any[], i: number) => list[i - 1],
|
||||
listLen: (list: any[]) => list.length,
|
||||
random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
|
||||
rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
|
||||
randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
|
||||
dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability,
|
||||
dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)),
|
||||
dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)],
|
||||
seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
|
||||
seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
|
||||
seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
|
||||
DRPWPM: (list: string[]) => {
|
||||
const xs = [];
|
||||
let totalFactor = 0;
|
||||
for (const x of list) {
|
||||
const parts = x.split(' ');
|
||||
const factor = parseInt(parts.pop()!, 10);
|
||||
const text = parts.join(' ');
|
||||
totalFactor += factor;
|
||||
xs.push({ factor, text });
|
||||
}
|
||||
const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
|
||||
let stackedFactor = 0;
|
||||
for (const x of xs) {
|
||||
if (r >= stackedFactor && r <= stackedFactor + x.factor) {
|
||||
return x.text;
|
||||
} else {
|
||||
stackedFactor += x.factor;
|
||||
}
|
||||
}
|
||||
return xs[0].text;
|
||||
},
|
||||
};
|
||||
const funcs = initHpmlLib(block, scope, this.opts.randomSeed, this.opts.visitor);
|
||||
|
||||
// Call function
|
||||
const fnName = block.type;
|
||||
const fn = (funcs as any)[fnName];
|
||||
if (fn == null) {
|
||||
@ -299,53 +227,3 @@ export class Hpml {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HpmlError extends Error {
|
||||
public info?: any;
|
||||
|
||||
constructor(message: string, info?: any) {
|
||||
super(message);
|
||||
|
||||
this.info = info;
|
||||
|
||||
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, HpmlError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Scope {
|
||||
private layerdStates: Record<string, any>[];
|
||||
public name: string;
|
||||
|
||||
constructor(layerdStates: Scope['layerdStates'], name?: Scope['name']) {
|
||||
this.layerdStates = layerdStates;
|
||||
this.name = name || 'anonymous';
|
||||
}
|
||||
|
||||
@autobind
|
||||
public createChildScope(states: Record<string, any>, name?: Scope['name']): Scope {
|
||||
const layer = [states, ...this.layerdStates];
|
||||
return new Scope(layer, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定した名前の変数の値を取得します
|
||||
* @param name 変数名
|
||||
*/
|
||||
@autobind
|
||||
public getState(name: string): any {
|
||||
for (const later of this.layerdStates) {
|
||||
const state = later[name];
|
||||
if (state !== undefined) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
throw new HpmlError(
|
||||
`No such variable '${name}' in scope '${this.name}'`, {
|
||||
scope: this.layerdStates
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
* Hpml
|
||||
*/
|
||||
|
||||
import autobind from 'autobind-decorator';
|
||||
|
||||
import {
|
||||
faMagic,
|
||||
faSquareRootAlt,
|
||||
@ -27,6 +29,7 @@ import {
|
||||
faCalculator,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { faFlag } from '@fortawesome/free-regular-svg-icons';
|
||||
import { Hpml } from './evaluator';
|
||||
|
||||
export type Block<V = any> = {
|
||||
id: string;
|
||||
@ -47,6 +50,11 @@ export type Variable = Block & {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type Fn = {
|
||||
slots: string[];
|
||||
exec: (args: Record<string, any>) => ReturnType<Hpml['evaluate']>;
|
||||
};
|
||||
|
||||
export type Type = 'string' | 'number' | 'boolean' | 'stringArray' | null;
|
||||
|
||||
export const funcDefs: Record<string, { in: any[]; out: any; category: string; icon: any; }> = {
|
||||
@ -137,3 +145,53 @@ export function isLiteralBlock(v: Block) {
|
||||
if (literalDefs[v.type]) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export class HpmlScope {
|
||||
private layerdStates: Record<string, any>[];
|
||||
public name: string;
|
||||
|
||||
constructor(layerdStates: HpmlScope['layerdStates'], name?: HpmlScope['name']) {
|
||||
this.layerdStates = layerdStates;
|
||||
this.name = name || 'anonymous';
|
||||
}
|
||||
|
||||
@autobind
|
||||
public createChildScope(states: Record<string, any>, name?: HpmlScope['name']): HpmlScope {
|
||||
const layer = [states, ...this.layerdStates];
|
||||
return new HpmlScope(layer, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定した名前の変数の値を取得します
|
||||
* @param name 変数名
|
||||
*/
|
||||
@autobind
|
||||
public getState(name: string): any {
|
||||
for (const later of this.layerdStates) {
|
||||
const state = later[name];
|
||||
if (state !== undefined) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
throw new HpmlError(
|
||||
`No such variable '${name}' in scope '${this.name}'`, {
|
||||
scope: this.layerdStates
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class HpmlError extends Error {
|
||||
public info?: any;
|
||||
|
||||
constructor(message: string, info?: any) {
|
||||
super(message);
|
||||
|
||||
this.info = info;
|
||||
|
||||
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, HpmlError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ import * as tinycolor from 'tinycolor2';
|
||||
import Chart from 'chart.js';
|
||||
import { Hpml } from './evaluator';
|
||||
import { values, utils } from '@syuilo/aiscript';
|
||||
import { Block, Fn, HpmlScope } from '.';
|
||||
import * as seedrandom from 'seedrandom';
|
||||
|
||||
// https://stackoverflow.com/questions/38493564/chart-area-background-color-chartjs
|
||||
Chart.pluginService.register({
|
||||
@ -16,7 +18,7 @@ Chart.pluginService.register({
|
||||
}
|
||||
});
|
||||
|
||||
export function initLib(hpml: Hpml) {
|
||||
export function initAiLib(hpml: Hpml) {
|
||||
return {
|
||||
'MkPages:updated': values.FN_NATIVE(([callback]) => {
|
||||
hpml.pageVarUpdatedCallback = (callback as values.VFn);
|
||||
@ -122,3 +124,79 @@ export function initLib(hpml: Hpml) {
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
export function initHpmlLib(block: Block, scope: HpmlScope, randomSeed: string, visitor?: any) {
|
||||
|
||||
const date = new Date();
|
||||
const day = `${visitor ? visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
|
||||
|
||||
const funcs: Record<string, Function> = {
|
||||
not: (a: boolean) => !a,
|
||||
or: (a: boolean, b: boolean) => a || b,
|
||||
and: (a: boolean, b: boolean) => a && b,
|
||||
eq: (a: any, b: any) => a === b,
|
||||
notEq: (a: any, b: any) => a !== b,
|
||||
gt: (a: number, b: number) => a > b,
|
||||
lt: (a: number, b: number) => a < b,
|
||||
gtEq: (a: number, b: number) => a >= b,
|
||||
ltEq: (a: number, b: number) => a <= b,
|
||||
if: (bool: boolean, a: any, b: any) => bool ? a : b,
|
||||
for: (times: number, fn: Fn) => {
|
||||
const result: any[] = [];
|
||||
for (let i = 0; i < times; i++) {
|
||||
result.push(fn.exec({
|
||||
[fn.slots[0]]: i + 1
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
add: (a: number, b: number) => a + b,
|
||||
subtract: (a: number, b: number) => a - b,
|
||||
multiply: (a: number, b: number) => a * b,
|
||||
divide: (a: number, b: number) => a / b,
|
||||
mod: (a: number, b: number) => a % b,
|
||||
round: (a: number) => Math.round(a),
|
||||
strLen: (a: string) => a.length,
|
||||
strPick: (a: string, b: number) => a[b - 1],
|
||||
strReplace: (a: string, b: string, c: string) => a.split(b).join(c),
|
||||
strReverse: (a: string) => a.split('').reverse().join(''),
|
||||
join: (texts: string[], separator: string) => texts.join(separator || ''),
|
||||
stringToNumber: (a: string) => parseInt(a),
|
||||
numberToString: (a: number) => a.toString(),
|
||||
splitStrByLine: (a: string) => a.split('\n'),
|
||||
pick: (list: any[], i: number) => list[i - 1],
|
||||
listLen: (list: any[]) => list.length,
|
||||
random: (probability: number) => Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * 100) < probability,
|
||||
rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * (max - min + 1)),
|
||||
randomPick: (list: any[]) => list[Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * list.length)],
|
||||
dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability,
|
||||
dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)),
|
||||
dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)],
|
||||
seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
|
||||
seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
|
||||
seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
|
||||
DRPWPM: (list: string[]) => {
|
||||
const xs: any[] = [];
|
||||
let totalFactor = 0;
|
||||
for (const x of list) {
|
||||
const parts = x.split(' ');
|
||||
const factor = parseInt(parts.pop()!, 10);
|
||||
const text = parts.join(' ');
|
||||
totalFactor += factor;
|
||||
xs.push({ factor, text });
|
||||
}
|
||||
const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
|
||||
let stackedFactor = 0;
|
||||
for (const x of xs) {
|
||||
if (r >= stackedFactor && r <= stackedFactor + x.factor) {
|
||||
return x.text;
|
||||
} else {
|
||||
stackedFactor += x.factor;
|
||||
}
|
||||
}
|
||||
return xs[0].text;
|
||||
},
|
||||
};
|
||||
|
||||
return funcs;
|
||||
}
|
||||
|
@ -3,19 +3,14 @@ import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { router } from '@/router';
|
||||
|
||||
export async function search(q?: string | null | undefined) {
|
||||
if (q == null) {
|
||||
const { canceled, result: query } = await os.dialog({
|
||||
title: i18n.locale.search,
|
||||
input: true
|
||||
});
|
||||
export async function search() {
|
||||
const { canceled, result: query } = await os.dialog({
|
||||
title: i18n.locale.search,
|
||||
input: true
|
||||
});
|
||||
if (canceled || query == null || query === '') return;
|
||||
|
||||
if (canceled || query == null || query === '') return;
|
||||
|
||||
q = query;
|
||||
}
|
||||
|
||||
q = q.trim();
|
||||
const q = query.trim();
|
||||
|
||||
if (q.startsWith('@') && !q.includes(' ')) {
|
||||
router.push(`/${q}`);
|
||||
@ -39,7 +34,8 @@ export async function search(q?: string | null | undefined) {
|
||||
date.setHours(23, 59, 59, 999);
|
||||
}
|
||||
|
||||
v.$root.$emit('warp', date);
|
||||
// TODO
|
||||
//v.$root.$emit('warp', date);
|
||||
os.dialog({
|
||||
icon: faHistory,
|
||||
iconOnly: true, autoClose: true
|
||||
|
@ -180,6 +180,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||
where: 'device',
|
||||
default: 'full' as 'full' | 'icon'
|
||||
},
|
||||
titlebar: {
|
||||
where: 'device',
|
||||
default: true
|
||||
},
|
||||
}));
|
||||
|
||||
// TODO: 他のタブと永続化されたstateを同期
|
||||
|
@ -62,7 +62,6 @@
|
||||
error: '#ec4137',
|
||||
warn: '#ecb637',
|
||||
htmlThemeColor: '@bg',
|
||||
X1: ':alpha<0<@bg',
|
||||
X2: ':darken<2<@panel',
|
||||
X3: 'rgba(255, 255, 255, 0.05)',
|
||||
X4: 'rgba(255, 255, 255, 0.1)',
|
||||
|
@ -62,7 +62,6 @@
|
||||
error: '#ec4137',
|
||||
warn: '#ecb637',
|
||||
htmlThemeColor: '@bg',
|
||||
X1: ':alpha<0<@bg',
|
||||
X2: ':darken<2<@panel',
|
||||
X3: 'rgba(0, 0, 0, 0.05)',
|
||||
X4: 'rgba(0, 0, 0, 0.1)',
|
||||
|
@ -14,8 +14,6 @@
|
||||
divider: 'rgba(255, 255, 255, 0.14)',
|
||||
panel: 'rgb(41, 43, 41)',
|
||||
panelShadow: '" 0 8px 24px rgb(0 0 0 / 25%)',
|
||||
panelHeaderBg: '@panel',
|
||||
panelHeaderDivider: '@divider',
|
||||
infoFg: '@fg',
|
||||
infoBg: '#333c3b',
|
||||
header: ':alpha<0.7<@bg',
|
||||
|
@ -5,21 +5,12 @@
|
||||
</transition>
|
||||
<template v-if="info">
|
||||
<div class="titleContainer">
|
||||
<template v-if="info.tabs">
|
||||
<div class="title" v-for="tab in info.tabs" :key="tab.id" :class="{ _button: tab.onClick, selected: tab.selected }" @click.stop="tab.onClick" v-tooltip="tab.tooltip">
|
||||
<Fa v-if="tab.icon" :icon="tab.icon" :key="tab.icon" class="icon"/>
|
||||
<span v-if="tab.title" class="text">{{ tab.title }}</span>
|
||||
<Fa class="indicator" v-if="tab.indicate" :icon="faCircle"/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="title">
|
||||
<Fa v-if="info.icon" :icon="info.icon" :key="info.icon" class="icon"/>
|
||||
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true"/>
|
||||
<span v-if="info.title" class="text">{{ info.title }}</span>
|
||||
<MkUserName v-else-if="info.userName" :user="info.userName" :nowrap="false" class="text"/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="title">
|
||||
<Fa v-if="info.icon" :icon="info.icon" :key="info.icon" class="icon"/>
|
||||
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true"/>
|
||||
<span v-if="info.title" class="text">{{ info.title }}</span>
|
||||
<MkUserName v-else-if="info.userName" :user="info.userName" :nowrap="false" class="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<button class="_button action" v-if="info.action" @click.stop="info.action.handler"><Fa :icon="info.action.icon" :key="info.action.icon"/></button>
|
||||
</template>
|
||||
@ -155,17 +146,6 @@ export default defineComponent({
|
||||
height: $size;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
&._button {
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
box-shadow: 0 -2px 0 0 var(--accent) inset;
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
<!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
|
||||
<section v-if="ids.length > 1"
|
||||
class="folder column"
|
||||
:style="{ width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
|
||||
:style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
|
||||
>
|
||||
<DeckColumnCore v-for="id in ids" :ref="id" :key="id" :column="columns.find(c => c.id === id)" :is-stacked="true" @parent-focus="moveFocus(id, $event)"/>
|
||||
</section>
|
||||
|
@ -191,6 +191,7 @@ export default defineComponent({
|
||||
} : undefined, null, {
|
||||
icon: faTrashAlt,
|
||||
text: this.$ts.remove,
|
||||
danger: true,
|
||||
action: () => {
|
||||
removeColumn(this.column.id);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition>
|
||||
<keep-alive :include="['timeline']">
|
||||
<component :is="Component" :ref="changePage"/>
|
||||
<component :is="Component" :ref="changePage" @contextmenu.stop="onContextmenu"/>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
@ -16,10 +16,12 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
|
||||
import XColumn from './column.vue';
|
||||
import XNotes from '@/components/notes.vue';
|
||||
import XHeader from '@/ui/_common_/header.vue';
|
||||
import { deckStore } from '@/ui/deck/deck-store';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -43,7 +45,6 @@ export default defineComponent({
|
||||
return {
|
||||
deckStore,
|
||||
pageInfo: null,
|
||||
pageKey: 0,
|
||||
}
|
||||
},
|
||||
|
||||
@ -54,6 +55,21 @@ export default defineComponent({
|
||||
this.pageInfo = page.INFO;
|
||||
}
|
||||
},
|
||||
|
||||
onContextmenu(e) {
|
||||
if (['INPUT', 'TEXTAREA'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return;
|
||||
const path = this.$route.path;
|
||||
os.contextMenu([{
|
||||
type: 'label',
|
||||
text: path,
|
||||
}, {
|
||||
icon: faWindowMaximize,
|
||||
text: this.$ts.openInWindow,
|
||||
action: () => {
|
||||
os.pageWindow(path);
|
||||
}
|
||||
}], e);
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -114,7 +114,9 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wtdtxvec {
|
||||
padding-top: 1px; // ウィジェットのbox-shadowを利用した1px borderを隠さないようにするため
|
||||
._panel {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
> header {
|
||||
padding: 16px;
|
||||
|
@ -2,8 +2,8 @@
|
||||
<div class="mk-app" :class="{ wallpaper }">
|
||||
<XSidebar ref="nav" class="sidebar"/>
|
||||
|
||||
<div class="contents" ref="contents">
|
||||
<header class="header" ref="header" @contextmenu.prevent.stop="onContextmenu" @click="onHeaderClick">
|
||||
<div class="contents" ref="contents" :class="{ withHeader: $store.state.titlebar }" @contextmenu.stop="onContextmenu">
|
||||
<header v-if="$store.state.titlebar" class="header" ref="header" @click="onHeaderClick">
|
||||
<XHeader :info="pageInfo"/>
|
||||
</header>
|
||||
<main ref="main">
|
||||
@ -29,6 +29,7 @@
|
||||
|
||||
<div class="buttons" :class="{ navHidden }">
|
||||
<button class="button nav _button" @click="showNav" ref="navButton"><Fa :icon="faBars"/><i v-if="navIndicated"><Fa :icon="faCircle"/></i></button>
|
||||
<button class="button home _button" @click="$route.name === 'index' ? top() : $router.push('/')"><Fa :icon="faHome"/></button>
|
||||
<button class="button notifications _button" @click="$router.push('/my/notifications')"><Fa :icon="faBell"/><i v-if="$i.hasUnreadNotification"><Fa :icon="faCircle"/></i></button>
|
||||
<button class="button widget _button" @click="widgetsShowing = true"><Fa :icon="faLayerGroup"/></button>
|
||||
<button class="button post _button" @click="post"><Fa :icon="faPencilAlt"/></button>
|
||||
@ -53,10 +54,10 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, defineAsyncComponent, markRaw } from 'vue';
|
||||
import { defineComponent, defineAsyncComponent } from 'vue';
|
||||
import { faLayerGroup, faBars, faHome, faCircle, faWindowMaximize, faColumns, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faBell } from '@fortawesome/free-regular-svg-icons';
|
||||
import { host } from '@/config';
|
||||
import { instanceName } from '@/config';
|
||||
import { StickySidebar } from '@/scripts/sticky-sidebar';
|
||||
import XSidebar from '@/components/sidebar.vue';
|
||||
import XCommon from './_common_/common.vue';
|
||||
@ -86,8 +87,6 @@ export default defineComponent({
|
||||
|
||||
data() {
|
||||
return {
|
||||
host: host,
|
||||
pageKey: 0,
|
||||
pageInfo: null,
|
||||
isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
|
||||
menuDef: sidebarDef,
|
||||
@ -108,12 +107,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
this.pageKey++;
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
document.documentElement.style.overflowY = 'scroll';
|
||||
|
||||
@ -154,6 +147,7 @@ export default defineComponent({
|
||||
if (page == null) return;
|
||||
if (page.INFO) {
|
||||
this.pageInfo = page.INFO;
|
||||
document.title = `${this.pageInfo.title} | ${instanceName}`;
|
||||
}
|
||||
},
|
||||
|
||||
@ -162,7 +156,7 @@ export default defineComponent({
|
||||
this.navHidden = navWidth === 0;
|
||||
if (this.$refs.contents == null) return;
|
||||
const width = this.$refs.contents.offsetWidth;
|
||||
this.$refs.header.style.width = `${width}px`;
|
||||
if (this.$refs.header) this.$refs.header.style.width = `${width}px`;
|
||||
},
|
||||
|
||||
showNav() {
|
||||
@ -193,6 +187,7 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
onContextmenu(e) {
|
||||
if (['INPUT', 'TEXTAREA'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return;
|
||||
const path = this.$route.path;
|
||||
os.contextMenu([{
|
||||
type: 'label',
|
||||
@ -256,7 +251,10 @@ export default defineComponent({
|
||||
> .contents {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding-top: $header-height;
|
||||
|
||||
&.withHeader {
|
||||
padding-top: $header-height;
|
||||
}
|
||||
|
||||
> .header {
|
||||
position: fixed;
|
||||
@ -336,15 +334,13 @@ export default defineComponent({
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
bottom: 0;
|
||||
padding: 0 32px 32px 32px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(0deg, var(--bg), var(--X1));
|
||||
|
||||
@media (max-width: 500px) {
|
||||
padding: 0 16px 16px 16px;
|
||||
}
|
||||
-webkit-backdrop-filter: blur(32px);
|
||||
backdrop-filter: blur(32px);
|
||||
background-color: var(--header);
|
||||
|
||||
&:not(.navHidden) {
|
||||
display: none;
|
||||
@ -352,15 +348,26 @@ export default defineComponent({
|
||||
|
||||
> .button {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 100%;
|
||||
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12);
|
||||
border-radius: 8px;
|
||||
background: var(--panel);
|
||||
color: var(--fg);
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
height: 60px;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--X2);
|
||||
}
|
||||
|
@ -63,7 +63,6 @@ export default defineComponent({
|
||||
return {
|
||||
host,
|
||||
instanceName,
|
||||
pageKey: 0,
|
||||
pageInfo: null,
|
||||
meta: null,
|
||||
narrow: window.innerWidth < 1280,
|
||||
@ -88,12 +87,6 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
this.pageKey++;
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
document.documentElement.style.overflowY = 'scroll';
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="mk-app" :style="{ backgroundImage: root ? `url(${ $instance.backgroundImageUrl })` : 'none' }">
|
||||
<div class="mk-app">
|
||||
<a v-if="root" href="https://github.com/syuilo/misskey" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--panel); color:var(--fg); position: fixed; z-index: 10; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
|
||||
|
||||
<div class="side" v-if="!narrow">
|
||||
<XKanban class="kanban" full :transparent="root" :powered-by="root"/>
|
||||
<div class="side" v-if="!narrow && !root">
|
||||
<XKanban class="kanban" full/>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<XKanban class="banner" :full="root" :transparent="root" :powered-by="root" v-if="narrow"/>
|
||||
<XKanban class="banner" :powered-by="root" v-if="narrow && !root"/>
|
||||
|
||||
<div class="contents">
|
||||
<XHeader class="header" :info="pageInfo" v-if="!root"/>
|
||||
@ -76,7 +76,6 @@ export default defineComponent({
|
||||
return {
|
||||
host,
|
||||
instanceName,
|
||||
pageKey: 0,
|
||||
pageInfo: null,
|
||||
meta: null,
|
||||
showMenu: false,
|
||||
@ -106,14 +105,8 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
this.pageKey++;
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
document.documentElement.style.overflowY = 'scroll';
|
||||
//document.documentElement.style.overflowY = 'scroll';
|
||||
|
||||
os.api('meta', { detail: true }).then(meta => {
|
||||
this.meta = meta;
|
||||
|
@ -38,18 +38,11 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
host: host,
|
||||
pageKey: 0,
|
||||
pageInfo: null,
|
||||
faLayerGroup, faBars, faBell, faHome, faCircle,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
this.pageKey++;
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
document.documentElement.style.overflowY = 'scroll';
|
||||
},
|
||||
|
@ -14,6 +14,9 @@ export default function(app: App) {
|
||||
app.component('MkwFederation', defineAsyncComponent(() => import('./federation.vue')));
|
||||
app.component('MkwPostForm', defineAsyncComponent(() => import('./post-form.vue')));
|
||||
app.component('MkwSlideshow', defineAsyncComponent(() => import('./slideshow.vue')));
|
||||
app.component('MkwServerMetric', defineAsyncComponent(() => import('./server-metric/index.vue')));
|
||||
app.component('MkwOnlineUsers', defineAsyncComponent(() => import('./online-users.vue')));
|
||||
app.component('MkwJobQueue', defineAsyncComponent(() => import('./job-queue.vue')));
|
||||
app.component('MkwButton', defineAsyncComponent(() => import('./button.vue')));
|
||||
}
|
||||
|
||||
@ -31,5 +34,8 @@ export const widgets = [
|
||||
'federation',
|
||||
'postForm',
|
||||
'slideshow',
|
||||
'serverMetric',
|
||||
'onlineUsers',
|
||||
'jobQueue',
|
||||
'button',
|
||||
];
|
||||
|
158
src/client/widgets/job-queue.vue
Normal file
158
src/client/widgets/job-queue.vue
Normal file
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div class="mkw-jobQueue _monospace" :class="{ _panel: !props.transparent }">
|
||||
<div class="inbox">
|
||||
<div class="label">Inbox queue<Fa :icon="faExclamationTriangle" v-if="inbox.waiting > 0" class="icon"/></div>
|
||||
<div class="values">
|
||||
<div>
|
||||
<div>Process</div>
|
||||
<div>{{ number(inbox.activeSincePrevTick) }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>Active</div>
|
||||
<div>{{ number(inbox.active) }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>Delayed</div>
|
||||
<div>{{ number(inbox.delayed) }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>Waiting</div>
|
||||
<div>{{ number(inbox.waiting) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="deliver">
|
||||
<div class="label">Deliver queue<Fa :icon="faExclamationTriangle" v-if="inbox.waiting > 0" class="icon"/></div>
|
||||
<div class="values">
|
||||
<div>
|
||||
<div>Process</div>
|
||||
<div>{{ number(deliver.activeSincePrevTick) }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>Active</div>
|
||||
<div>{{ number(deliver.active) }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>Delayed</div>
|
||||
<div>{{ number(deliver.delayed) }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>Waiting</div>
|
||||
<div>{{ number(deliver.waiting) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||
import define from './define';
|
||||
import * as os from '@/os';
|
||||
import number from '@/filters/number';
|
||||
|
||||
const widget = define({
|
||||
name: 'jobQueue',
|
||||
props: () => ({
|
||||
transparent: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
export default defineComponent({
|
||||
extends: widget,
|
||||
data() {
|
||||
return {
|
||||
connection: os.stream.useSharedConnection('queueStats'),
|
||||
inbox: {
|
||||
activeSincePrevTick: 0,
|
||||
active: 0,
|
||||
waiting: 0,
|
||||
delayed: 0,
|
||||
},
|
||||
deliver: {
|
||||
activeSincePrevTick: 0,
|
||||
active: 0,
|
||||
waiting: 0,
|
||||
delayed: 0,
|
||||
},
|
||||
faExclamationTriangle,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.connection.on('stats', this.onStats);
|
||||
this.connection.on('statsLog', this.onStatsLog);
|
||||
|
||||
this.connection.send('requestLog', {
|
||||
id: Math.random().toString().substr(2, 8),
|
||||
length: 1
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.connection.off('stats', this.onStats);
|
||||
this.connection.off('statsLog', this.onStatsLog);
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
onStats(stats) {
|
||||
for (const domain of ['inbox', 'deliver']) {
|
||||
this[domain].activeSincePrevTick = stats[domain].activeSincePrevTick;
|
||||
this[domain].active = stats[domain].active;
|
||||
this[domain].waiting = stats[domain].waiting;
|
||||
this[domain].delayed = stats[domain].delayed;
|
||||
}
|
||||
},
|
||||
|
||||
onStatsLog(statsLog) {
|
||||
for (const stats of [...statsLog].reverse()) {
|
||||
this.onStats(stats);
|
||||
}
|
||||
},
|
||||
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes warnBlink {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
|
||||
.mkw-jobQueue {
|
||||
> div {
|
||||
padding: 16px;
|
||||
|
||||
&:not(:first-child) {
|
||||
border-top: solid 1px var(--divider);
|
||||
}
|
||||
|
||||
> .label {
|
||||
display: flex;
|
||||
|
||||
> .icon {
|
||||
color: var(--warn);
|
||||
margin-left: auto;
|
||||
animation: warnBlink 1s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
> .values {
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
|
||||
> div:first-child {
|
||||
opacity: 0.7;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
67
src/client/widgets/online-users.vue
Normal file
67
src/client/widgets/online-users.vue
Normal file
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="mkw-onlineUsers" :class="{ _panel: !props.transparent, pad: !props.transparent }">
|
||||
<I18n v-if="onlineUsersCount" :src="$ts.onlineUsersCount" text-tag="span" class="text">
|
||||
<template #n><b>{{ onlineUsersCount }}</b></template>
|
||||
</I18n>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import define from './define';
|
||||
import * as os from '@/os';
|
||||
|
||||
const widget = define({
|
||||
name: 'onlineUsers',
|
||||
props: () => ({
|
||||
transparent: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
export default defineComponent({
|
||||
extends: widget,
|
||||
data() {
|
||||
return {
|
||||
onlineUsersCount: null,
|
||||
clock: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.tick();
|
||||
this.clock = setInterval(this.tick, 1000 * 15);
|
||||
},
|
||||
beforeUnmount() {
|
||||
clearInterval(this.clock);
|
||||
},
|
||||
methods: {
|
||||
tick() {
|
||||
os.api('get-online-users-count').then(res => {
|
||||
this.onlineUsersCount = res.count;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mkw-onlineUsers {
|
||||
text-align: center;
|
||||
|
||||
&.pad {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
> .text {
|
||||
::v-deep(b) {
|
||||
color: #41b781;
|
||||
}
|
||||
|
||||
::v-deep(span) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
174
src/client/widgets/server-metric/cpu-mem.vue
Normal file
174
src/client/widgets/server-metric/cpu-mem.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div class="lcfyofjk">
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
|
||||
<defs>
|
||||
<linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0">
|
||||
<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
|
||||
<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
|
||||
</linearGradient>
|
||||
<mask :id="cpuMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
|
||||
<polygon
|
||||
:points="cpuPolygonPoints"
|
||||
fill="#fff"
|
||||
fill-opacity="0.5"
|
||||
/>
|
||||
<polyline
|
||||
:points="cpuPolylinePoints"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<circle
|
||||
:cx="cpuHeadX"
|
||||
:cy="cpuHeadY"
|
||||
r="1.5"
|
||||
fill="#fff"
|
||||
/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect
|
||||
x="-2" y="-2"
|
||||
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
||||
:style="`stroke: none; fill: url(#${ cpuGradientId }); mask: url(#${ cpuMaskId })`"
|
||||
/>
|
||||
<text x="1" y="5">CPU <tspan>{{ cpuP }}%</tspan></text>
|
||||
</svg>
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
|
||||
<defs>
|
||||
<linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0">
|
||||
<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
|
||||
<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
|
||||
</linearGradient>
|
||||
<mask :id="memMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
|
||||
<polygon
|
||||
:points="memPolygonPoints"
|
||||
fill="#fff"
|
||||
fill-opacity="0.5"
|
||||
/>
|
||||
<polyline
|
||||
:points="memPolylinePoints"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<circle
|
||||
:cx="memHeadX"
|
||||
:cy="memHeadY"
|
||||
r="1.5"
|
||||
fill="#fff"
|
||||
/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect
|
||||
x="-2" y="-2"
|
||||
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
||||
:style="`stroke: none; fill: url(#${ memGradientId }); mask: url(#${ memMaskId })`"
|
||||
/>
|
||||
<text x="1" y="5">MEM <tspan>{{ memP }}%</tspan></text>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
connection: {
|
||||
required: true,
|
||||
},
|
||||
meta: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
viewBoxX: 50,
|
||||
viewBoxY: 30,
|
||||
stats: [],
|
||||
cpuGradientId: uuid(),
|
||||
cpuMaskId: uuid(),
|
||||
memGradientId: uuid(),
|
||||
memMaskId: uuid(),
|
||||
cpuPolylinePoints: '',
|
||||
memPolylinePoints: '',
|
||||
cpuPolygonPoints: '',
|
||||
memPolygonPoints: '',
|
||||
cpuHeadX: null,
|
||||
cpuHeadY: null,
|
||||
memHeadX: null,
|
||||
memHeadY: null,
|
||||
cpuP: '',
|
||||
memP: ''
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection.on('stats', this.onStats);
|
||||
this.connection.on('statsLog', this.onStatsLog);
|
||||
this.connection.send('requestLog', {
|
||||
id: Math.random().toString().substr(2, 8)
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.connection.off('stats', this.onStats);
|
||||
this.connection.off('statsLog', this.onStatsLog);
|
||||
},
|
||||
methods: {
|
||||
onStats(stats) {
|
||||
this.stats.push(stats);
|
||||
if (this.stats.length > 50) this.stats.shift();
|
||||
|
||||
const cpuPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - s.cpu) * this.viewBoxY]);
|
||||
const memPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.mem.used / this.meta.mem.total)) * this.viewBoxY]);
|
||||
this.cpuPolylinePoints = cpuPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
this.memPolylinePoints = memPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
|
||||
this.cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.cpuPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
|
||||
this.memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.memPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
|
||||
|
||||
this.cpuHeadX = cpuPolylinePoints[cpuPolylinePoints.length - 1][0];
|
||||
this.cpuHeadY = cpuPolylinePoints[cpuPolylinePoints.length - 1][1];
|
||||
this.memHeadX = memPolylinePoints[memPolylinePoints.length - 1][0];
|
||||
this.memHeadY = memPolylinePoints[memPolylinePoints.length - 1][1];
|
||||
|
||||
this.cpuP = (stats.cpu * 100).toFixed(0);
|
||||
this.memP = (stats.mem.used / this.meta.mem.total * 100).toFixed(0);
|
||||
},
|
||||
onStatsLog(statsLog) {
|
||||
for (const stats of [...statsLog].reverse()) {
|
||||
this.onStats(stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.lcfyofjk {
|
||||
display: flex;
|
||||
|
||||
> svg {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
width: 50%;
|
||||
|
||||
&:first-child {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
> text {
|
||||
font-size: 5px;
|
||||
fill: currentColor;
|
||||
|
||||
> tspan {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
78
src/client/widgets/server-metric/cpu.vue
Normal file
78
src/client/widgets/server-metric/cpu.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div class="vrvdvrys">
|
||||
<XPie class="pie" :value="usage"/>
|
||||
<div>
|
||||
<p><fa :icon="faMicrochip"/>CPU</p>
|
||||
<p>{{ meta.cpu.cores }} Logical cores</p>
|
||||
<p>{{ meta.cpu.model }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faMicrochip } from '@fortawesome/free-solid-svg-icons';
|
||||
import XPie from './pie.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XPie
|
||||
},
|
||||
props: {
|
||||
connection: {
|
||||
required: true,
|
||||
},
|
||||
meta: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
usage: 0,
|
||||
faMicrochip,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection.on('stats', this.onStats);
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.connection.off('stats', this.onStats);
|
||||
},
|
||||
methods: {
|
||||
onStats(stats) {
|
||||
this.usage = stats.cpu;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vrvdvrys {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
|
||||
> .pie {
|
||||
height: 82px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
|
||||
> p {
|
||||
margin: 0;
|
||||
font-size: 0.8em;
|
||||
|
||||
&:first-child {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
|
||||
> [data-icon] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
72
src/client/widgets/server-metric/disk.vue
Normal file
72
src/client/widgets/server-metric/disk.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="zbwaqsat">
|
||||
<XPie class="pie" :value="usage"/>
|
||||
<div>
|
||||
<p><fa :icon="faHdd"/>Disk</p>
|
||||
<p>Total: {{ bytes(total, 1) }}</p>
|
||||
<p>Free: {{ bytes(available, 1) }}</p>
|
||||
<p>Used: {{ bytes(used, 1) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faHdd } from '@fortawesome/free-solid-svg-icons';
|
||||
import XPie from './pie.vue';
|
||||
import bytes from '@/filters/bytes';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XPie
|
||||
},
|
||||
props: {
|
||||
meta: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
usage: this.meta.fs.used / this.meta.fs.total,
|
||||
total: this.meta.fs.total,
|
||||
used: this.meta.fs.used,
|
||||
available: this.meta.fs.total - this.meta.fs.used,
|
||||
faHdd,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
bytes
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.zbwaqsat {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
|
||||
> .pie {
|
||||
height: 82px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
|
||||
> p {
|
||||
margin: 0;
|
||||
font-size: 0.8em;
|
||||
|
||||
&:first-child {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
|
||||
> [data-icon] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
84
src/client/widgets/server-metric/index.vue
Normal file
84
src/client/widgets/server-metric/index.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<MkContainer :show-header="props.showHeader" :naked="props.transparent">
|
||||
<template #header><Fa :icon="faServer"/>{{ $ts._widgets.serverMetric }}</template>
|
||||
<template #func><button @click="toggleView()" class="_button"><Fa :icon="faSort"/></button></template>
|
||||
|
||||
<div class="mkw-serverMetric" v-if="meta">
|
||||
<XCpuMemory v-if="props.view === 0" :connection="connection" :meta="meta"/>
|
||||
<XNet v-if="props.view === 1" :connection="connection" :meta="meta"/>
|
||||
<XCpu v-if="props.view === 2" :connection="connection" :meta="meta"/>
|
||||
<XMemory v-if="props.view === 3" :connection="connection" :meta="meta"/>
|
||||
<XDisk v-if="props.view === 4" :connection="connection" :meta="meta"/>
|
||||
</div>
|
||||
</MkContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faServer, faSort } from '@fortawesome/free-solid-svg-icons';
|
||||
import define from '../define';
|
||||
import MkContainer from '@/components/ui/container.vue';
|
||||
import XCpuMemory from './cpu-mem.vue';
|
||||
import XNet from './net.vue';
|
||||
import XCpu from './cpu.vue';
|
||||
import XMemory from './mem.vue';
|
||||
import XDisk from './disk.vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
const widget = define({
|
||||
name: 'serverMetric',
|
||||
props: () => ({
|
||||
showHeader: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
transparent: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
view: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
hidden: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
export default defineComponent({
|
||||
extends: widget,
|
||||
components: {
|
||||
MkContainer,
|
||||
XCpuMemory,
|
||||
XNet,
|
||||
XCpu,
|
||||
XMemory,
|
||||
XDisk,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
meta: null,
|
||||
connection: null,
|
||||
faServer, faSort,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
os.api('server-info', {}).then(res => {
|
||||
this.meta = res;
|
||||
});
|
||||
this.connection = os.stream.useSharedConnection('serverStats');
|
||||
},
|
||||
unmounted() {
|
||||
this.connection.dispose();
|
||||
},
|
||||
methods: {
|
||||
toggleView() {
|
||||
if (this.props.view == 4) {
|
||||
this.props.view = 0;
|
||||
} else {
|
||||
this.props.view++;
|
||||
}
|
||||
this.save();
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
87
src/client/widgets/server-metric/mem.vue
Normal file
87
src/client/widgets/server-metric/mem.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="zlxnikvl">
|
||||
<XPie class="pie" :value="usage"/>
|
||||
<div>
|
||||
<p><fa :icon="faMemory"/>RAM</p>
|
||||
<p>Total: {{ bytes(total, 1) }}</p>
|
||||
<p>Used: {{ bytes(used, 1) }}</p>
|
||||
<p>Free: {{ bytes(free, 1) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faMemory } from '@fortawesome/free-solid-svg-icons';
|
||||
import XPie from './pie.vue';
|
||||
import bytes from '@/filters/bytes';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XPie
|
||||
},
|
||||
props: {
|
||||
connection: {
|
||||
required: true,
|
||||
},
|
||||
meta: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
usage: 0,
|
||||
total: 0,
|
||||
used: 0,
|
||||
free: 0,
|
||||
faMemory,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection.on('stats', this.onStats);
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.connection.off('stats', this.onStats);
|
||||
},
|
||||
methods: {
|
||||
onStats(stats) {
|
||||
this.usage = stats.mem.used / this.meta.mem.total;
|
||||
this.total = this.meta.mem.total;
|
||||
this.used = stats.mem.used;
|
||||
this.free = this.meta.mem.total - stats.mem.used;
|
||||
},
|
||||
bytes
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.zlxnikvl {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
|
||||
> .pie {
|
||||
height: 82px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
|
||||
> p {
|
||||
margin: 0;
|
||||
font-size: 0.8em;
|
||||
|
||||
&:first-child {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
|
||||
> [data-icon] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
148
src/client/widgets/server-metric/net.vue
Normal file
148
src/client/widgets/server-metric/net.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div class="oxxrhrto">
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
|
||||
<polygon
|
||||
:points="inPolygonPoints"
|
||||
fill="#94a029"
|
||||
fill-opacity="0.5"
|
||||
/>
|
||||
<polyline
|
||||
:points="inPolylinePoints"
|
||||
fill="none"
|
||||
stroke="#94a029"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<circle
|
||||
:cx="inHeadX"
|
||||
:cy="inHeadY"
|
||||
r="1.5"
|
||||
fill="#94a029"
|
||||
/>
|
||||
<text x="1" y="5">NET rx <tspan>{{ bytes(inRecent) }}</tspan></text>
|
||||
</svg>
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
|
||||
<polygon
|
||||
:points="outPolygonPoints"
|
||||
fill="#ff9156"
|
||||
fill-opacity="0.5"
|
||||
/>
|
||||
<polyline
|
||||
:points="outPolylinePoints"
|
||||
fill="none"
|
||||
stroke="#ff9156"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<circle
|
||||
:cx="outHeadX"
|
||||
:cy="outHeadY"
|
||||
r="1.5"
|
||||
fill="#ff9156"
|
||||
/>
|
||||
<text x="1" y="5">NET tx <tspan>{{ bytes(outRecent) }}</tspan></text>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import bytes from '@/filters/bytes';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
connection: {
|
||||
required: true,
|
||||
},
|
||||
meta: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
viewBoxX: 50,
|
||||
viewBoxY: 30,
|
||||
stats: [],
|
||||
inPolylinePoints: '',
|
||||
outPolylinePoints: '',
|
||||
inPolygonPoints: '',
|
||||
outPolygonPoints: '',
|
||||
inHeadX: null,
|
||||
inHeadY: null,
|
||||
outHeadX: null,
|
||||
outHeadY: null,
|
||||
inRecent: 0,
|
||||
outRecent: 0
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection.on('stats', this.onStats);
|
||||
this.connection.on('statsLog', this.onStatsLog);
|
||||
this.connection.send('requestLog', {
|
||||
id: Math.random().toString().substr(2, 8)
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.connection.off('stats', this.onStats);
|
||||
this.connection.off('statsLog', this.onStatsLog);
|
||||
},
|
||||
methods: {
|
||||
onStats(stats) {
|
||||
this.stats.push(stats);
|
||||
if (this.stats.length > 50) this.stats.shift();
|
||||
|
||||
const inPeak = Math.max(1024 * 64, Math.max(...this.stats.map(s => s.net.rx)));
|
||||
const outPeak = Math.max(1024 * 64, Math.max(...this.stats.map(s => s.net.tx)));
|
||||
|
||||
const inPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.net.rx / inPeak)) * this.viewBoxY]);
|
||||
const outPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.net.tx / outPeak)) * this.viewBoxY]);
|
||||
this.inPolylinePoints = inPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
this.outPolylinePoints = outPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
|
||||
this.inPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.inPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
|
||||
this.outPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.outPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
|
||||
|
||||
this.inHeadX = inPolylinePoints[inPolylinePoints.length - 1][0];
|
||||
this.inHeadY = inPolylinePoints[inPolylinePoints.length - 1][1];
|
||||
this.outHeadX = outPolylinePoints[outPolylinePoints.length - 1][0];
|
||||
this.outHeadY = outPolylinePoints[outPolylinePoints.length - 1][1];
|
||||
|
||||
this.inRecent = stats.net.rx;
|
||||
this.outRecent = stats.net.tx;
|
||||
},
|
||||
onStatsLog(statsLog) {
|
||||
for (const stats of [...statsLog].reverse()) {
|
||||
this.onStats(stats);
|
||||
}
|
||||
},
|
||||
bytes
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.oxxrhrto {
|
||||
display: flex;
|
||||
|
||||
> svg {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
width: 50%;
|
||||
|
||||
&:first-child {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
> text {
|
||||
font-size: 5px;
|
||||
fill: currentColor;
|
||||
|
||||
> tspan {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
65
src/client/widgets/server-metric/pie.vue
Normal file
65
src/client/widgets/server-metric/pie.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<svg class="hsalcinq" viewBox="0 0 1 1" preserveAspectRatio="none">
|
||||
<circle
|
||||
:r="r"
|
||||
cx="50%" cy="50%"
|
||||
fill="none"
|
||||
stroke-width="0.1"
|
||||
stroke="rgba(0, 0, 0, 0.05)"
|
||||
/>
|
||||
<circle
|
||||
:r="r"
|
||||
cx="50%" cy="50%"
|
||||
:stroke-dasharray="Math.PI * (r * 2)"
|
||||
:stroke-dashoffset="strokeDashoffset"
|
||||
fill="none"
|
||||
stroke-width="0.1"
|
||||
:stroke="color"
|
||||
/>
|
||||
<text x="50%" y="50%" dy="0.05" text-anchor="middle">{{ (value * 100).toFixed(0) }}%</text>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
r: 0.45
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
color(): string {
|
||||
return `hsl(${180 - (this.value * 180)}, 80%, 70%)`;
|
||||
},
|
||||
strokeDashoffset(): number {
|
||||
return (1 - this.value) * (Math.PI * (this.r * 2));
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hsalcinq {
|
||||
display: block;
|
||||
height: 100%;
|
||||
|
||||
> circle {
|
||||
transform-origin: center;
|
||||
transform: rotate(-90deg);
|
||||
transition: stroke-dashoffset 0.5s ease;
|
||||
}
|
||||
|
||||
> text {
|
||||
font-size: 0.15px;
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -6,6 +6,9 @@ const ev = new Xev();
|
||||
|
||||
const interval = 2000;
|
||||
|
||||
const roundCpu = (num: number) => Math.round(num * 1000) / 1000;
|
||||
const round = (num: number) => Math.round(num * 10) / 10;
|
||||
|
||||
/**
|
||||
* Report server stats regularly
|
||||
*/
|
||||
@ -23,18 +26,18 @@ export default function() {
|
||||
const fsStats = await fs();
|
||||
|
||||
const stats = {
|
||||
cpu: cpu,
|
||||
cpu: roundCpu(cpu),
|
||||
mem: {
|
||||
used: memStats.used,
|
||||
active: memStats.active,
|
||||
used: round(memStats.used),
|
||||
active: round(memStats.active),
|
||||
},
|
||||
net: {
|
||||
rx: Math.max(0, netStats.rx_sec),
|
||||
tx: Math.max(0, netStats.tx_sec),
|
||||
rx: round(Math.max(0, netStats.rx_sec)),
|
||||
tx: round(Math.max(0, netStats.tx_sec)),
|
||||
},
|
||||
fs: {
|
||||
r: Math.max(0, fsStats.rIO_sec),
|
||||
w: Math.max(0, fsStats.wIO_sec),
|
||||
r: round(Math.max(0, fsStats.rIO_sec)),
|
||||
w: round(Math.max(0, fsStats.wIO_sec)),
|
||||
}
|
||||
};
|
||||
ev.emit('serverStats', stats);
|
||||
|
@ -1,4 +1,4 @@
|
||||
# AiScript
|
||||
|
||||
## Funktionen
|
||||
デフォルトで値渡しです。
|
||||
Verwenden für Parameter standardmäßig Pass-by-Value (Wertparameter).
|
||||
|
@ -1,58 +1,58 @@
|
||||
# Misskey API
|
||||
|
||||
MisskeyAPIを使ってMisskeyクライアント、Misskey連携Webサービス、Bot等(以下「アプリケーション」と呼びます)を開発できます。 ストリーミングAPIもあるので、リアルタイム性のあるアプリケーションを作ることも可能です。
|
||||
Durch die Verwendung des Misskey APIs können Misskey Clients, mit Misskey kompatible Web-Services, Bots (ab sofort "Anwendungen" genannt) und mehr entwickelt werden. Ebenso existiert ein Streaming-API, um Echtzeit-Anwendungen zu erstellen.
|
||||
|
||||
APIを使い始めるには、まずアクセストークンを取得する必要があります。 このドキュメントでは、アクセストークンを取得する手順を説明した後、基本的なAPIの使い方を説明します。
|
||||
Um mit der Verwendung des APIs zu beginnen, wird zuerst ein Zugriffstoken benötigt. Wie ein solcher Token erhalten werden kann, und wie die API dann hiermit verwendet werden kann, wird auf dieser Seite erklärt.
|
||||
|
||||
## アクセストークンの取得
|
||||
基本的に、APIはリクエストにはアクセストークンが必要となります。 APIにリクエストするのが自分自身なのか、不特定の利用者に使ってもらうアプリケーションなのかによって取得手順は異なります。
|
||||
## Einen Zugriffstoken erhalten
|
||||
Generell benötigen alle API-Anfragen einen Zugriffstoken. Die Methode, wie ein solcher Zugriffstoken erlangt werden kann, unterscheidet sich je nach dem, ob Anfragen vom eigenen Benutzerkonto aus gesendet werden oder ob die Anfragen von einem anderen Benutzer aus durch eine Anwendung gesendet werden.
|
||||
|
||||
* 前者の場合: [「自分自身のアクセストークンを手動発行する」](#自分自身のアクセストークンを手動発行する)に進む
|
||||
* 後者の場合: [「アプリケーション利用者にアクセストークンの発行をリクエストする」](#アプリケーション利用者にアクセストークンの発行をリクエストする)に進む
|
||||
* Im ersten Fall: Fahre mit "Einen Zugriffstoken für das eigene Benutzerkonto generieren" fort.
|
||||
* Im zweiten Fall: Fahre mit "Einen Benutzer zur Generierung eines Zugangstokens für eine Anwendung auffordern" fort.
|
||||
|
||||
### 自分自身のアクセストークンを手動発行する
|
||||
「設定 > API」で、自分のアクセストークンを発行できます。
|
||||
### Einen Zugriffstoken für das eigene Benutzerkonto generieren
|
||||
In Einstellungen > API kann ein Zugriffstoken für das eigene Benutzerkonto generiert werden.
|
||||
|
||||
[「APIの使い方」へ進む](#APIの使い方)
|
||||
[Fahre mit "Verwendung der API" fort.](#APIの使い方)
|
||||
|
||||
### アプリケーション利用者にアクセストークンの発行をリクエストする
|
||||
アプリケーション利用者のアクセストークンを取得するには、以下の手順で発行をリクエストします。
|
||||
### Einen Benutzer zur Generierung eines Zugangstokens für eine Anwendung auffordern
|
||||
Um einen in einer Anwendung zu verwendenden Zugriffstoken für ein Benutzerkonto zu erhalten, fordere die Generierung eines solchen durch den unten beschrieben Prozess an.
|
||||
|
||||
#### Step 1
|
||||
#### Schritt 1
|
||||
|
||||
UUIDを生成する。以後これをセッションIDと呼びます。
|
||||
Generiere eine UUID.Diese werden wir ab jetzt die Sitzungs-ID nennen.
|
||||
|
||||
> このセッションIDは毎回生成し、使いまわさないようにしてください。
|
||||
> Die selbe Sitzungs-ID sollte nie mehrfach wieder verwendet werden, generiere anstattdessen für jeden Zugriffstoken eine neue Sitzungs-ID.
|
||||
|
||||
#### Step 2
|
||||
#### Schritt 2
|
||||
|
||||
`{_URL_}/miauth/{session}`をユーザーのブラウザで表示させる。`{session}`の部分は、セッションIDに置き換えてください。
|
||||
> 例: `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f`
|
||||
Öffne `{_URL_}/miauth/{session}` im Browser des Benutzers.`{session}` soll hierbei durch die vorher generierte Sitzungs-ID ersetzt werden.
|
||||
> z.B.: `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f`
|
||||
|
||||
表示する際、URLにクエリパラメータとしていくつかのオプションを設定できます:
|
||||
* `name` ... アプリケーション名
|
||||
* > 例: `MissDeck`
|
||||
* `icon` ... アプリケーションのアイコン画像URL
|
||||
* > 例: `https://missdeck.example.com/icon.png`
|
||||
* `callback` ... 認証が終わった後にリダイレクトするURL
|
||||
* > 例: `https://missdeck.example.com/callback`
|
||||
* リダイレクト時には、`session`というクエリパラメータでセッションIDが付きます
|
||||
* `permission` ... アプリケーションが要求する権限
|
||||
* > 例: `write:notes,write:following,read:drive`
|
||||
* 要求する権限を`,`で区切って列挙します
|
||||
* どのような権限があるかは[APIリファレンス](/api-doc)で確認できます
|
||||
Bei Aufruf dieser URL können verschiedene Einstellungen via Query-Parameter gesetzt werden:
|
||||
* `name` ... Anwendungsname
|
||||
* > z.B.: `MissDeck`
|
||||
* `icon` ... URL zum Anwendungs-Icon
|
||||
* > z.B.: `https://missdeck.example.com/icon.png`
|
||||
* `callback` ... URL, zu der nach Ende der Authentifizierung weitergeleitet wird
|
||||
* > z.B.: `https://missdeck.example.com/callback`
|
||||
* In dieser Weiterleitung wird die Sessions-ID als `session` Query-Parameter an die URL angefügt
|
||||
* `permission` ... Von der Anwendung geforderte Berechtigungen
|
||||
* > z.B.: `write:notes,write:following,read:drive`
|
||||
* Angeforderte Berechtigungen sind durch `,` von einander getrennt
|
||||
* Welche Berechtigungen existieren kann in der [API-Referenz](/api-doc) nachgelesen werden
|
||||
|
||||
#### Step 3
|
||||
ユーザーが発行を許可した後、`{_URL_}/api/miauth/{session}/check`にPOSTリクエストすると、レスポンスとしてアクセストークンを含むJSONが返ります。
|
||||
#### Schritt 3
|
||||
Sobald der Benutzer der Erstellung des Zugriffstokens zugestimmt hat, kann durch eine POST-Anfrage an `{_URL_}/api/miauth/{session}/check` der Zugriffstoken aus dem JSON-Objekt der Antwort ausgelesen werden.
|
||||
|
||||
レスポンスに含まれるプロパティ:
|
||||
* `token` ... ユーザーのアクセストークン
|
||||
* `user` ... ユーザーの情報
|
||||
In der Antwort enthaltene Attribute:
|
||||
* `token` ... Zugriffstoken des Nutzers
|
||||
* `user` ... Benutzerdaten
|
||||
|
||||
[「APIの使い方」へ進む](#APIの使い方)
|
||||
[Fahre mit "Verwendung der API" fort.](#APIの使い方)
|
||||
|
||||
## APIの使い方
|
||||
**APIはすべてPOSTで、リクエスト/レスポンスともにJSON形式です。RESTではありません。** アクセストークンは、`i`というパラメータ名でリクエストに含めます。
|
||||
## Verwendung der API
|
||||
**Alle API-Anfragen sind POST-Anfragen, und alle Anfragen bzw. Antworten sind JSON-Objekte.REST wird nicht unterstützt.** Der Zugriffstoken muss unter dem `i`-Parameter beinhaltet werden.
|
||||
|
||||
* [APIリファレンス](/api-doc)
|
||||
* [ストリーミングAPI](./stream)
|
||||
* [API-Referenz](/api-doc)
|
||||
* [Streaming-API](./stream)
|
||||
|
@ -1,74 +1,74 @@
|
||||
# プラグインの作成
|
||||
Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。
|
||||
# Erstellen von Plugins
|
||||
Durch die Verwendung der Plugin-Funktionalität des Misskey Web-Clients kann dieser mit verschiedenen Funktionen erweitert werden. Diese Seite beinhaltet Definitionen von Metadaten für die Erstellung von Plugins sowie eine AiScript API-Referenz für Plugins.
|
||||
|
||||
## Metadaten
|
||||
プラグインは、AiScriptのメタデータ埋め込み機能を使って、デフォルトとしてプラグインのメタデータを定義する必要があります。 メタデータは次のプロパティを含むオブジェクトです。
|
||||
Plugins müssen benötigte Metadaten im AiScript Metadata-Format angeben. Bei diesen Metadaten handelt es sich um ein Objekt mit folgenden Attributen:
|
||||
|
||||
### name
|
||||
プラグイン名
|
||||
Name des Plugins
|
||||
|
||||
### author
|
||||
プラグイン作者
|
||||
Name des Plugin-Erstellers
|
||||
|
||||
### version
|
||||
プラグインバージョン。数値を指定してください。
|
||||
Version des Plugins.Muss eine Zahl sein.
|
||||
|
||||
### description
|
||||
プラグインの説明
|
||||
Beschreibung des Plugins
|
||||
|
||||
### permissions
|
||||
プラグインが要求する権限。MisskeyAPIにリクエストする際に用いられます。
|
||||
Die vom Plugin geforderten Berechtigungen.Werden bei Anfragen der Misskey API verwendet.
|
||||
|
||||
### config
|
||||
プラグインの設定情報を表すオブジェクト。 キーに設定名、値に以下のプロパティを含めます。
|
||||
Ein Objekt, dass die Einstellungen des Plugins enthält. Schlüssel representieren Namen von Einstellungen, und Werte sind einer der unten genannten Attribute.
|
||||
|
||||
#### type
|
||||
設定値の種類を表す文字列。以下から選択します。 string number boolean
|
||||
Der Typ eines Einstellungswertes.Muss aus einem dieser Typen gewählt sein: string number boolean
|
||||
|
||||
#### label
|
||||
ユーザーに表示する設定名
|
||||
Dem Benutzer angezeigter Einstellungsname
|
||||
|
||||
#### description
|
||||
設定の説明
|
||||
Beschreibung der Einstellung
|
||||
|
||||
#### default
|
||||
設定のデフォルト値
|
||||
Standardwert der Einstellung
|
||||
|
||||
## APIリファレンス
|
||||
AiScript標準で組み込まれているAPIは掲載しません。
|
||||
## API-Referenz
|
||||
Direkt in den AiScript-Standard eingebaute API wird nicht aufgelistet.
|
||||
|
||||
### Mk:dialog(title text type)
|
||||
ダイアログを表示します。typeには以下の値が設定できます。 info success warn error question 省略すると info になります。
|
||||
Zeigt ein Dialogfenster an.type muss aus einem der folgenden Werte gewählt werden. info success warn error question Falls kein Typ angegeben wird, wird dieser zu info gesetzt.
|
||||
|
||||
### Mk:confirm(title text type)
|
||||
確認ダイアログを表示します。typeには以下の値が設定できます。 info success warn error question 省略すると question になります。 ユーザーが"OK"を選択した場合は true を、"キャンセル"を選択した場合は false が返ります。
|
||||
Zeigt ein Bestätigungsfenster an.type muss aus einem der folgenden Werte gewählt werden. info success warn error question Falls kein Typ angegeben wird, wird dieser zu question gesetzt. Drückt der Benutzer "OK" wird true zurückgegeben, drückt er "Cancel" wird false zurückgegeben.
|
||||
|
||||
### Mk:api(endpoint params)
|
||||
Misskey APIにリクエストします。第一引数にエンドポイント名、第二引数にパラメータオブジェクトを渡します。
|
||||
Sendet eine Misskey API-Anfrage.Der erste Parameter gibt den API-Endpunkt an, der zweite die Anfrageparameter als Objekt.
|
||||
|
||||
### Mk:save(key value)
|
||||
任意の値に任意の名前を付けて永続化します。永続化した値は、AiScriptコンテキストが終了しても残り、Mk:loadで読み取ることができます。
|
||||
Speichert einen beliebigen Wert dauerhaft unter einem beliebigen Namen.Der gespeicherte Wert bleibt auch nach Verlassen des AiScript-Kontexts erhalten und kann mit Mk:load ausgelesen werden.
|
||||
|
||||
### Mk:load(key)
|
||||
Mk:saveで永続化した指定の名前の値を読み取ります。
|
||||
Läd den Wert des gegebenen Schlüssels, der zuvor mit Mk:save gespeichert wurde
|
||||
|
||||
### Plugin:register_post_form_action(title fn)
|
||||
投稿フォームにアクションを追加します。第一引数にアクション名、第二引数にアクションが選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に投稿フォームオブジェクトが渡されます。
|
||||
Fügt dem Beitragsfenster eine Aktion hinzu.Der erste Parameter gibt den Aktionsnamen an, der zweite Parameter eine Callback-Funktion, die bei Auswahl dieser Aktion ausgeführt wird, an. Die Callback-Funktion erhält als ersten Parameter ein Beitragsfenster-Objekt.
|
||||
|
||||
### Plugin:register_note_action(title fn)
|
||||
ノートメニューに項目を追加します。第一引数に項目名、第二引数に項目が選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。
|
||||
Fügt dem Notiz-Menü ein Listenelement hinzu.Der erste Parameter gibt den Aktionsnamen an, der zweite Parameter eine Callback-Funktion, die bei Auswahl dieses Elements ausgeführt wird, an. Die Callback-Funktion erhält als ersten Parameter ein Notiz-Objekt.
|
||||
|
||||
### Plugin:register_user_action(title fn)
|
||||
ユーザーメニューに項目を追加します。第一引数に項目名、第二引数に項目が選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に対象のユーザーオブジェクトが渡されます。
|
||||
Fügt dem Benutzer-Menü ein Listenelement hinzu.Der erste Parameter gibt den Aktionsnamen an, der zweite Parameter eine Callback-Funktion, die bei Auswahl dieses Elements ausgeführt wird, an. Die Callback-Funktion erhält als ersten Parameter ein Benutzer-Objekt.
|
||||
|
||||
### Plugin:register_note_view_interruptor(fn)
|
||||
UIに表示されるノート情報を書き換えます。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。 コールバック関数の返り値でノートが書き換えられます。
|
||||
Verändert die Daten einer im UI angezeigten Notiz. Die Callback-Funktion erhält als ersten Parameter ein Notiz-Objekt. Die angezeigte Notiz wird mit dem Rückgabewert der Callback-Funktion überschrieben.
|
||||
|
||||
### Plugin:register_note_post_interruptor(fn)
|
||||
ノート投稿時にノート情報を書き換えます。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。 コールバック関数の返り値でノートが書き換えられます。
|
||||
Verändert die Daten einer zu erstellenden Notiz. Die Callback-Funktion erhält als ersten Parameter ein Notiz-Objekt. Die zu erstellende Notiz wird mit dem Rückgabewert der Callback-Funktion überschrieben.
|
||||
|
||||
### Plugin:open_url(url)
|
||||
第一引数に渡されたURLをブラウザの新しいタブで開きます。
|
||||
Öffnet die als ersten Parameter gegebene URL in einem neuen Browser-Tab.
|
||||
|
||||
### Plugin:config
|
||||
プラグインの設定が格納されるオブジェクト。プラグイン定義のconfigで設定したキーで値が入ります。
|
||||
Ein Objekt, dass die Plugin-Einstellungen enthält.Die in den Plugin-Einstellung eingetragenen Werte sind hier unter den Einstellungsnamen gespeichert.
|
||||
|
@ -1,2 +1,2 @@
|
||||
# Benutzerdefinierte Emojis
|
||||
カスタム絵文字は、インスタンスで用意された画像を絵文字のように使える機能です。 ノート、リアクション、チャット、自己紹介、名前などの場所で使うことができます。 カスタム絵文字をそれらの場所で使うには、絵文字ピッカーボタン(ある場合)を押すか、`:`を入力して絵文字サジェストを表示します。 テキスト内に`:foo:`のような形式の文字列が見つかると、`foo`の部分がカスタム絵文字名と解釈され、表示時には対応したカスタム絵文字に置き換わります。
|
||||
Benutzerdefinierte Emojis sind instanzenspezifische Bilder, die dort wie Emojis verwendet werden können. Sie können in Notizen, Reaktionen, Chats, Profilbeschreibungen, Benutzernamen und an anderen Orten verwendet werden. Um ein benutzerdefiniertes Emoji an einem solchen Ort zu verwenden, drücke den Knopf, um die Emoji-Auswahl zu öffnen (falls vorhanden), oder gib ein `:` ein, um Emoji-Vorschläge anzeigen zu lassen. Wird eine Zeichenkette des Formats `:foo:` innerhalb eines Textes gefunden, so wird `foo` als Name eines benutzerdefinierten Emojis interpretiert und mit dem dazugehörigen Bild ersetzt.
|
||||
|
@ -1,18 +1,18 @@
|
||||
# Deck
|
||||
|
||||
デッキは利用可能なUIのひとつです。「カラム」と呼ばれるビューを複数並べて表示させることで、カスタマイズ性が高く、情報量の多いUIが構築できることが特徴です。
|
||||
Deck ist eines der in Misskey verwendbaren Benutzeroberflächen.Durch die Aneinanderreihung sogenannter "Spalten" wird ein hoher Anpassungsgrad sowie das Anzeigen vieler Informationen auf einmal ermöglicht.
|
||||
|
||||
## カラムの追加
|
||||
デッキの背景を右クリックし、「カラムを追加」して任意のカラムを追加できます。
|
||||
## Spalten hinzufügen
|
||||
Um eine Spalte hinzuzufügen, klicke auf den Hintergrund des Decks und drücke "Spalte hinzufügen".
|
||||
|
||||
## カラムの移動
|
||||
カラムは、ドラッグアンドドロップで他のカラムと位置を入れ替えることが出来るほか、カラムメニュー(カラムのヘッダー右クリック)から位置を移動させることもできます。
|
||||
## Spalten verschieben
|
||||
Spalten können entweder durch Drag-and-Drop oder durch Anpassung der Spaltenoptionen (aufgerufen durch Rechtsklick auf den Spaltenkopf) individueller Spalten verschoben werden.
|
||||
|
||||
## カラムの水平分割
|
||||
カラムは左右だけでなく、上下に並べることもできます。 カラムメニューを開き、「左に重ねる」を選択すると、左のカラムの下に現在のカラムが移動します。 上下分割を解除するには、カラムメニューの「右に出す」を選択します。
|
||||
## Horizontale Ausrichtung von Spalten
|
||||
Spalten können nicht nur horizontal, sondern auch vertikal angeordnet werden. Durch Auswahl von "Auf linke Seite stapeln" im Spaltenmenü einer Spalte wird diese unter die Spalte links von sich selbst verschoben. Um dies aufzuheben, drücke "Nach rechts vom Stapel nehmen" im Spaltenmenü einer vertikal angeordneten Spalte.
|
||||
|
||||
## カラムの設定
|
||||
カラムメニューの「編集」を選択するとカラムの設定を編集できます。カラムの名前を変えたり、幅を変えたりできます。
|
||||
## Spalteneinstellungen
|
||||
Um eine Spalte zu bearbeiten, drücke "Bearbeiten" in dessen Spaltenmenü.Der Name einer Spalte sowie dessen Breite können verändert werden.
|
||||
|
||||
## デッキの設定
|
||||
デッキに関する設定は、[settings/deck](/settings/deck)で行えます。
|
||||
## Deck-Einstellungen
|
||||
Deck-spezifische Einstellungen können in [settings/deck](/settings/deck) gefunden werden.
|
||||
|
@ -1,2 +1,2 @@
|
||||
# Folgt
|
||||
ユーザーをフォローすると、タイムラインにそのユーザーの投稿が表示されるようになります。ただし、他のユーザーに対する返信は含まれません。 ユーザーをフォローするには、ユーザーページの「フォロー」ボタンをクリックします。フォローを解除するには、もう一度クリックします。
|
||||
# Folgen
|
||||
Folgst du einem Benutzer, so werden dessen Beiträge auf deiner Chronik angezeigt.Antworten dieses Benutzers auf Beiträge Anderer werden jedoch nicht angezeigt. Um einem Benutzer zu folgen, drücke den "Folgen"-Knopf auf dessen Profil.Um einem Benutzer nicht mehr zu folgen, drücke den selben Knopf erneut.
|
||||
|
@ -1,68 +1,68 @@
|
||||
# キーボードショートカット
|
||||
# Tastenkürzel
|
||||
|
||||
## Global
|
||||
これらのショートカットは基本的にどこでも使えます。
|
||||
Hier gelistete Tastenkürzel können generell überall verwendet werden.
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>ショートカット</th><th>効果</th><th>由来</th></tr>
|
||||
<tr><th>Kürzel</th><th>Effekt</th><th>Ursprung</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<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="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">P</kbd>, <kbd class="key">N</kbd></td><td>Neuer Beitrag</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>Neusten Beitrag der Chronik fokussieren</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>Benachrichtigungen anzeigen / ausblenden</td><td><b>N</b>otifications</td></tr>
|
||||
<tr><td><kbd class="key">S</kbd></td><td>Suchen</td><td><b>S</b>earch</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>Hilfe anzeigen</td><td><b>H</b>elp</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## 投稿にフォーカスされた状態
|
||||
## Auf Beiträge bezogene Kürzel
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>ショートカット</th><th>効果</th><th>由来</th></tr>
|
||||
<tr><th>Kürzel</th><th>Effekt</th><th>Ursprung</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</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">J</kbd>, <kbd class="key">Tab</kbd></td><td>下の投稿にフォーカスを移動</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">R</kbd></td><td>返信フォームを開く</td><td><b>R</b>eply</td></tr>
|
||||
<tr><td><kbd class="key">Q</kbd></td><td>Renoteフォームを開く</td><td><b>Q</b>uote</td></tr>
|
||||
<tr><td><kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">Q</kbd></kbd></td><td>即刻Renoteする(フォームを開かずに)</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">E</kbd>, <kbd class="key">A</kbd>, <kbd class="key">+</kbd></td><td>リアクションフォームを開く</td><td><b>E</b>mote, re<b>A</b>ction</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">F</kbd>, <kbd class="key">B</kbd></td><td>お気に入りに登録</td><td><b>F</b>avorite, <b>B</b>ookmark</td></tr>
|
||||
<tr><td><kbd class="key">Del</kbd>, <kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">D</kbd></kbd></td><td>投稿を削除</td><td><b>D</b>elete</tr>
|
||||
<tr><td><kbd class="key">M</kbd>, <kbd class="key">O</kbd></td><td>投稿に対するメニューを開く</td><td><b>M</b>ore, <b>O</b>ther</td></tr>
|
||||
<tr><td><kbd class="key">S</kbd></td><td>CWで隠された部分を表示 or 隠す</td><td><b>S</b>how, <b>S</b>ee</td></tr>
|
||||
<tr><td><kbd class="key">Esc</kbd></td><td>フォーカスを外す</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd>, <kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">Tab</kbd></kbd></td><td>Fokus zum oberen Beitrag wechseln</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">↓</kbd>, <kbd class="key">J</kbd>, <kbd class="key">Tab</kbd></td><td>Fokus zum unteren Beitrag wechseln</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">R</kbd></td><td>Antwort-Fenster öffnen</td><td><b>R</b>eply</td></tr>
|
||||
<tr><td><kbd class="key">Q</kbd></td><td>Renote-Fenster öffnen</td><td><b>Q</b>uote</td></tr>
|
||||
<tr><td><kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">Q</kbd></kbd></td><td>Sofort Renote tätigen (ohne Fenster)</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">E</kbd>, <kbd class="key">A</kbd>, <kbd class="key">+</kbd></td><td>Reaktionsfenster öffnen</td><td><b>E</b>mote, re<b>A</b>ction</td></tr>
|
||||
<tr><td><kbd class="key">0</kbd>~<kbd class="key">9</kbd></td><td>Mit dem Emoji, dessen Position in der Emoji-Auswahl der gedrückten Zahl entspricht, reagieren (Details folgen)</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">F</kbd>, <kbd class="key">B</kbd></td><td>Favoriten hinzufügen</td><td><b>F</b>avorite, <b>B</b>ookmark</td></tr>
|
||||
<tr><td><kbd class="key">Del</kbd>, <kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">D</kbd></kbd></td><td>Beitrag löschen</td><td><b>D</b>elete</tr>
|
||||
<tr><td><kbd class="key">M</kbd>, <kbd class="key">O</kbd></td><td>Beitrags-Menü öffnen</td><td><b>M</b>ore, <b>O</b>ther</td></tr>
|
||||
<tr><td><kbd class="key">S</kbd></td><td>Verdeckten Inhalt anzeigen / ausblenden</td><td><b>S</b>how, <b>S</b>ee</td></tr>
|
||||
<tr><td><kbd class="key">Esc</kbd></td><td>Fokus lösen</td><td>-</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Renoteフォーム
|
||||
## Auf das Renote-Fenster bezogene Kürzel
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>ショートカット</th><th>効果</th><th>由来</th></tr>
|
||||
<tr><th>Kürzel</th><th>Effekt</th><th>Ursprung</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td><kbd class="key">Enter</kbd></td><td>Renoteする</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">Q</kbd></td><td>フォームを展開する</td><td><b>Q</b>uote</td></tr>
|
||||
<tr><td><kbd class="key">Esc</kbd></td><td>フォームを閉じる</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">Enter</kbd></td><td>Sendet Renote</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">Q</kbd></td><td>Fenster expandieren</td><td><b>Q</b>uote</td></tr>
|
||||
<tr><td><kbd class="key">Esc</kbd></td><td>Fenster schließen</td><td>-</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## リアクションフォーム
|
||||
デフォルトで「👍」にフォーカスが当たっている状態です。
|
||||
## Auf die Reaktionsauswahl bezogene Kürzel
|
||||
Standardmäßig liegt der Fokus auf der "👍"-Reaktion.
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>ショートカット</th><th>効果</th><th>由来</th></tr>
|
||||
<tr><th>Kürzel</th><th>Effekt</th><th>Ursprung</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd></td><td>上のリアクションにフォーカスを移動</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">↓</kbd>, <kbd class="key">J</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">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">Esc</kbd></td><td>リアクションするのをやめる</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd></td><td>Fokus zu oberer Reaktion wechseln</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">↓</kbd>, <kbd class="key">J</kbd></td><td>Fokus zur unteren Reaktion wechseln</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>Fokus zur linken Reaktion wechseln</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">→</kbd>, <kbd class="key">L</kbd>, <kbd class="key">Tab</kbd></td><td>Fokus zur rechten Reaktion wechseln</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">Enter</kbd>, <kbd class="key">Space</kbd>, <kbd class="key">+</kbd></td><td>Reaktion senden</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">0</kbd>~<kbd class="key">9</kbd></td><td>Mit dem Emoji, dessen Position der gedrückten Zahl entspricht, reagieren</td><td>-</td></tr>
|
||||
<tr><td><kbd class="key">Esc</kbd></td><td>Reagieren abbrechen</td><td>-</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -1,2 +1,2 @@
|
||||
# MFM
|
||||
MFMは、Misskey Flavored Markdownの略で、Misskeyの様々な場所で使用できる専用のマークアップ言語です。 MFMで使用可能な構文は[MFMチートシート](/mfm-cheat-sheet)で確認できます。
|
||||
MFM, abgekürzt für Misskey Flavored Markdown, beschreibt eine Misskey-exklusive Markup-Sprache. Eine Liste verwendbarer MFM-Syntax kann auf dem [MFM Spickzettel](/mfm-cheat-sheet) eingesehen werden.
|
||||
|
@ -1,13 +1,13 @@
|
||||
# Stummschalten
|
||||
# Stummschaltung
|
||||
|
||||
ユーザーをミュートすると、そのユーザーに関する次のコンテンツがMisskeyに表示されなくなります:
|
||||
Sobald du einen Benutzer stummschaltest, werden die folgenden Arten von mit diesem Benutzer verbundenen Beiträge von Misskey nicht mehr angezeigt:
|
||||
|
||||
* タイムラインや投稿の検索結果内の、そのユーザーの投稿(およびそれらの投稿に対する返信やRenote)
|
||||
* そのユーザーからの通知
|
||||
* メッセージ履歴一覧内の、そのユーザーとのメッセージ履歴
|
||||
* Beiträge dieses Nutzers in Chroniken sowie Suchergebnissen (inklusive Renotes und Antworten auf diese)
|
||||
* Benachrichtigungen von diesem Benutzer
|
||||
* Chatverläufe mit diesem Benutzer in der Liste aller Chatverläufe
|
||||
|
||||
ユーザーをミュートするには、対象のユーザーのユーザーページに表示されている「ミュート」ボタンを押します。
|
||||
Um einen Benutzer stummzuschalten, navigiere zu dessen Profil und drücke den dort angezeigten "Stummschalten"-Knopf.
|
||||
|
||||
ミュートを行ったことは相手に通知されず、ミュートされていることを知ることもできません。
|
||||
Der stummgeschaltete Benutzer erhält hierüber keine Benachrichtung, und erhält darüber, ob er von einem anderen Benutzer Stummgeschaltet ist, auch keine Informationen.
|
||||
|
||||
設定>ミュート から、自分がミュートしているユーザー一覧を確認することができます。
|
||||
Unter Einstellungen > Stummschaltungen / Blockierungen kann eine Liste aller stummgeschalteter Benutzer eingesehen werden.
|
||||
|
@ -1,10 +1,10 @@
|
||||
# Pages
|
||||
# Seiten
|
||||
|
||||
## Variablen
|
||||
変数を使うことで動的なページを作成できます。テキスト内で <b>{ 変数名 }</b> と書くとそこに変数の値を埋め込めます。例えば <b>Hello { thing } world!</b> というテキストで、変数(thing)の値が <b>ai</b> だった場合、テキストは <b>Hello ai world!</b> になります。
|
||||
Durch die Verwendung von Variablen ist es möglich, dynamische Seiten zu erstellen.Wird in einem Text <b>{ Variablenname }</b> beinhaltet, wird dies mit dem Wert dieser Variable eingesetzt.Ist Beispielsweise der Wert der Variable thing in diesem Fall <b>ai</b>, dann wird der Text <b>Hallo { thing } Welt!</b> zu <b>Hallo ai Welt!</b> ausgewertet.
|
||||
|
||||
変数の評価(値を算出すること)は上から下に行われるので、ある変数の中で自分より下の変数を参照することはできません。例えば上から <b>A、B、C</b> と3つの変数を定義したとき、<b>C</b>の中で<b>A</b>や<b>B</b>を参照することはできますが、<b>A</b>の中で<b>B</b>や<b>C</b>を参照することはできません。
|
||||
Variablen werden von oben nach unten ausgewertet, d.h. Referenzen zu Variablen, die noch nicht definiert sind, sind nicht möglich.Werden Beispielsweise die Variablen <b>A, B, C</b> in der gegebenen Reihenfolge definiert, so kann sich innerhalb von <b>C</b> auf <b>A</b> oder <b>B</b> bezogen werden, aber innerhalb von <b>A</b> nicht auf <b>B</b> oder <b>C</b>.
|
||||
|
||||
ユーザーからの入力を受け取るには、ページに「ユーザー入力」ブロックを設置し、「変数名」に入力を格納したい変数名を設定します(変数は自動で作成されます)。その変数を使ってユーザー入力に応じた動作を行えます。
|
||||
Um (Text-)Eingabe durch Benutzer empfangen zu können, kann der Seite ein "Benutzereingabe"-Feld hinzugefügt werden, welches dann den Wert, den der Benutzer eingibt, in einer Variable mit gewünschtem Namen speichert.Durch die Verwendung dieser Benutzereingabe können dann die weiteren Aktionen der Seite gesteuert werden.
|
||||
|
||||
関数を使うと、値の算出処理を再利用可能な形にまとめることができます。関数を作るには、「関数」タイプの変数を作成します。関数にはスロット(引数)を設定することができ、スロットの値は関数内で変数として利用可能です。また、関数を引数に取る関数(高階関数と呼ばれます)も存在します。関数は予め定義しておくほかに、このような高階関数のスロットに即席でセットすることもできます。
|
||||
Die Verwendung von Funktionen erlaubt die Definition von wiederverwendbaren Werteberechnungen.Um eine Funktion zu erstellen, wähle "Funktion" als Variablentyp.Funktionen können ebenso Slots (Parameter) verwenden, auf dessen Werte dann innerhalb der Funktion zugegriffen werden kann.Zudem ist es möglich, eine Funktion zu erstellen, dessen Parameter eine weitere Funktion ist ("Funktion höherer Ordnung").Neben der vorherigen Definition von Funktionen können in Funktionen höherer Ordnung Funktionen ebenso direkt in der Parametereingabe definiert werden.
|
||||
|
@ -1,11 +1,11 @@
|
||||
# Reaktionen
|
||||
他の人のノートに、絵文字を付けて簡単にあなたの反応を伝えられる機能です。 リアクションするには、ノートの + アイコンをクリックしてピッカーを表示し、絵文字を選択します。 リアクションには[カスタム絵文字](./custom-emoji)も使用できます。
|
||||
Verleih deinen Gedanken über die Beiträge anderer durch Reaktionen einfach Ausdruck. Um auf einen Beitrag zu reagieren, drücke das "+"-Symbol, das mit dem Beitrag angezeigt wird, und wähle ein Emoji. Es ist ebenso möglich, mit [Benutzerdefinierten Emojis](./custom-emoji) zu reagieren.
|
||||
|
||||
## リアクションピッカーのカスタマイズ
|
||||
ピッカーに表示される絵文字を自分好みにカスタマイズすることができます。 設定の「リアクション」で設定します。
|
||||
## Anpassung der Reaktionsauswahl
|
||||
Die in der Reaktionsauswahl angezeigten Emojis können angepasst werden. Dies ist in den Einstellungen im Unterpunkt "Reaktionen" möglich.
|
||||
|
||||
## リモート投稿へのリアクションについて
|
||||
リアクションはMisskeyオリジナルの機能であるため、リモートインスタンスがMisskeyでない限りは、ほとんどの場合「Like」としてアクティビティが送信されます。一般的にはLikeは「お気に入り」として実装されているようです。 また、相手がMisskeyであったとしても、カスタム絵文字リアクションは伝わらず、自動的に「👍」等にフォールバックされます。
|
||||
## Reaktionen auf Beiträge von anderen Servern
|
||||
Da Reaktionen eine Misskey-spezifische Funktionalität sind, werden diese von nicht-Misskey Instanzen meist als "Like"-Aktivität interpretiert.In den meisten Fällen ist die "Like"-Funktionalität als "Favoriten"-Funktion implementiert. Zudem werden Reaktionen mit benutzerdefinierten Emojis nicht übertragen und anstattdessen automatisch auf eine "👍"-Reaktion zurückfallen, auch wenn der Interaktionspartner ein anderer Misskey-Server ist.
|
||||
|
||||
## リモートからのリアクションについて
|
||||
リモートから「Like」アクティビティを受信したとき、Misskeyでは「👍」のリアクションとして解釈されます。
|
||||
## Reaktionen von Benutzern anderer Server
|
||||
Werden von entfernten Servern "Like"-Aktivitäten gesendet, so werden diese von Misskey als "👍"-Reaktion interpretiert.
|
||||
|
@ -1,33 +1,33 @@
|
||||
# MisskeyリバーシBotの開発
|
||||
Misskeyのリバーシ機能に対応したBotの開発方法をここに記します。
|
||||
# Entwicklung eines Misskey Reversi-Bots
|
||||
Auf dieser Seite wird die Entwicklung eines Bots für Misskey Reversi erläutert.
|
||||
|
||||
1. `games/reversi`ストリームに以下のパラメータを付けて接続する:
|
||||
* `i`: botアカウントのAPIキー
|
||||
1. Verbinde dich unter Verwendung folgender Parameter mit dem `games/reversi`-Stream:
|
||||
* `i`: API-Schlüssel des Bot-Kontos
|
||||
|
||||
2. 対局への招待が来たら、ストリームから`invited`イベントが流れてくる
|
||||
* イベントの中身に、`parent`という名前で対局へ誘ってきたユーザーの情報が含まれている
|
||||
2. Sobald den Bot eine Spieleinladung erreicht, wird das `invited`-Event vom Stream gesendet
|
||||
* Der Inhalt dieses Events ist ein `parent`-Attribut, was Daten über den Benutzer, der die Einladung verschickt hat, beinhaltet
|
||||
|
||||
3. `games/reversi/match`へ、`user_id`として`parent`の`id`が含まれたリクエストを送信する
|
||||
3. Sende eine Anfrage an `games/reversi/match`, wobei der Wert des `user_id`-Parameters das `id`-Attribut des vorher erhaltenen `parent`-Objekts ist
|
||||
|
||||
4. 上手くいくとゲーム情報が返ってくるので、`games/reversi-game`ストリームへ、以下のパラメータを付けて接続する:
|
||||
* `i`: botアカウントのAPIキー
|
||||
* `game`: `game`の`id`
|
||||
4. Gelingt die Anfrage, werden die Spieldaten als Rückgabewert geliefert. Nutze diese dann, um die unten gelisteten Parameter an den `games/reversi-game`-Stream zu senden:
|
||||
* `i`: API-Schlüssel des Bot-Kontos
|
||||
* `game`: `id`-Attribut des `game`-Objekts
|
||||
|
||||
5. この間、相手がゲームの設定を変更するとその都度`update-settings`イベントが流れてくるので、必要であれば何かしらの処理を行う
|
||||
5. Währenddessen kann der Spielgegner die Spieleinstellungen verändern. Jedes mal, wenn eine Einstellung verändert wird, sendet der Stream ein `update-settings`-Event, weswegen möglicherweise Logik, um solche Events verarbeiten zu können, notwendig ist.
|
||||
|
||||
6. 設定に満足したら、`{ type: 'accept' }`メッセージをストリームに送信する
|
||||
6. Sobald du mit den Spieleinstellungen zufrieden bist, sende die Nachricht `{ type: 'accept' }` an den Stream
|
||||
|
||||
7. ゲームが開始すると、`started`イベントが流れてくる
|
||||
* イベントの中身にはゲーム情報が含まれている
|
||||
7. Sobald das Spiel startet, wird das `started`-Event gesendet
|
||||
* Der Inhalt dieses Events sind die Spieldaten
|
||||
|
||||
8. 石を打つには、ストリームに`{ type: 'set', pos: <位置> }`を送信する(位置の計算方法は後述)
|
||||
8. Um einen Stein zu setzen, sende die folgende Nachricht an den Stream: `{ type: 'set', pos: <Position> }` (Positionsberechnungen werden später erläutert)
|
||||
|
||||
9. 相手または自分が石を打つと、ストリームから`set`イベントが流れてくる
|
||||
* `color`として石の色が含まれている
|
||||
* `pos`として位置情報が含まれている
|
||||
9. Setzt der Gegner oder du einen Stein, wird das `set`-Event vom Stream gesendet
|
||||
* Die Farbe der Spielfigur ist als `color` enthalten
|
||||
* Die Position der Spielfigur ist als `pos` enthalten
|
||||
|
||||
## 位置の計算法
|
||||
8x8のマップを考える場合、各マスの位置(インデックスと呼びます)は次のようになっています:
|
||||
## Positionsberechnungen
|
||||
Im Falle eines 8x8 Spielbrettes sind die Felder wie folgt aufgestellt (jeweils mit ihrem Index versehen):
|
||||
```
|
||||
+--+--+--+--+--+--+--+--+
|
||||
| 0| 1| 2| 3| 4| 5| 6| 7|
|
||||
@ -38,29 +38,29 @@ Misskeyのリバーシ機能に対応したBotの開発方法をここに記し
|
||||
...
|
||||
```
|
||||
|
||||
### X,Y座標 から インデックス に変換する
|
||||
### Berechnung von Indizes durch X und Y Koordinaten
|
||||
```
|
||||
pos = x + (y * mapWidth)
|
||||
```
|
||||
`mapWidth`は、ゲーム情報の`map`から、次のようにして計算できます:
|
||||
Bei `mapWidth` handelt es sich um wie folgt aus `map` entnommene Spielbrettdaten:
|
||||
```
|
||||
mapWidth = map[0].length
|
||||
```
|
||||
|
||||
### インデックス から X,Y座標 に変換する
|
||||
### Berechnung der X und Y Koordinaten durch Indizes
|
||||
```
|
||||
x = pos % mapWidth
|
||||
y = Math.floor(pos / mapWidth)
|
||||
```
|
||||
|
||||
## マップ情報
|
||||
マップ情報は、ゲーム情報の`map`に入っています。 文字列の配列になっており、ひとつひとつの文字がマス情報を表しています。 それをもとにマップのデザインを知る事が出来ます:
|
||||
* `(スペース)` ... マス無し
|
||||
* `-` ... マス
|
||||
* `b` ... 初期配置される黒石
|
||||
* `w` ... 初期配置される白石
|
||||
## Spielbrettdaten
|
||||
Die Spielbrettdaten sind innerhalb vom in den Spieldaten enthaltenen `map`-Attribut gespeichert. Da das Spielbrett als Array von Zeichenketten representiert wird, steht jedes Symbol für ein Spielfeld. Basierend auf diesen Informationen lässt sich der Spielbrettzustand rekonstruieren.
|
||||
* `(Leer)` ... Kein Spielfeld
|
||||
* `-` ... Spielfeld
|
||||
* `b` ... Spielfeld auf dem zuerst platzierter Stein schwarz war
|
||||
* `w` ... Spielfeld auf dem zuerst platzierter Stein weiß war
|
||||
|
||||
例えば、4*4の次のような単純なマップがあるとします:
|
||||
Sei folgendes simple 4*4 Spielbrett als Beispiel gegeben:
|
||||
```text
|
||||
+---+---+---+---+
|
||||
| | | | |
|
||||
@ -73,23 +73,23 @@ y = Math.floor(pos / mapWidth)
|
||||
+---+---+---+---+
|
||||
```
|
||||
|
||||
この場合、マップデータはこのようになります:
|
||||
In diesem Fall sehen die Spielbrettdaten wie folgt aus:
|
||||
```javascript
|
||||
['----', '-wb-', '-bw-', '----']
|
||||
```
|
||||
|
||||
## ユーザーにフォームを提示して対話可能Botを作成する
|
||||
ユーザーとのコミュニケーションを行うため、ゲームの設定画面でユーザーにフォームを提示することができます。 例えば、Botの強さをユーザーが設定できるようにする、といったシナリオが考えられます。
|
||||
## Erstellen eines Bots, der mit dem Benutzer durch das Zeigen von Fenstern kommunizieren kann
|
||||
Das Kommunizieren mit dem Spieler kann durch das Anzeigen von Fenstern während der Vorbereitungsphase des Spiels umgesetz werden. Beispielsweise kann so die Schwierigkeit des Bots durch den Benutzer konfiguriert werden.
|
||||
|
||||
フォームを提示するには、`reversi-game`ストリームに次のメッセージを送信します:
|
||||
Um ein Fenster anzuzeigen, sende folgende Nachricht an den `reversi-game`-Stream:
|
||||
```javascript
|
||||
{
|
||||
type: 'init-form',
|
||||
body: [フォームコントロールの配列]
|
||||
body: [Array an Fenster-Elementen]
|
||||
}
|
||||
```
|
||||
|
||||
フォームコントロールの配列については今から説明します。 フォームコントロールは、次のようなオブジェクトです:
|
||||
Details bezüglich des Arrays an Fenster-Elementen werden nun erklärt. Ein Element eines Fensters ist wie das folgende Objekt aufgebaut:
|
||||
```javascript
|
||||
{
|
||||
id: 'switch1',
|
||||
@ -98,10 +98,10 @@ y = Math.floor(pos / mapWidth)
|
||||
value: false
|
||||
}
|
||||
```
|
||||
`id` ... コントロールのID。 `type` ... コントロールの種類。後述します。 `label` ... コントロールと一緒に表記するテキスト。 `value` ... コントロールのデフォルト値。
|
||||
`id` ... Die ID des Elements. `type` ... Der Typ des Elements.Diese werden später erläutert. `label` ... Text der zusammen mit dem Element angezeigt wird. `value` ... Standardwert des Elements.
|
||||
|
||||
### フォームの操作を受け取る
|
||||
ユーザーがフォームを操作すると、ストリームから`update-form`イベントが流れてきます。 イベントの中身には、コントロールのIDと、ユーザーが設定した値が含まれています。 例えば、上で示したスイッチをユーザーがオンにしたとすると、次のイベントが流れてきます:
|
||||
### Verarbeitung von Interaktionen mit Elementen
|
||||
Interagiert der Benutzer mit einem der Elemente eines Fensters, so wird ein `update-form`-Element vom Stream gesendet. Die Inhalte dieses Events sind die ID des Elements sowie der Wert des Elements, der vom Benutzer eingestellt wurde. Wird beispielsweise der obige Beispielschalter eingeschaltet, wird das folgende Event gesendet:
|
||||
```javascript
|
||||
{
|
||||
id: 'switch1',
|
||||
@ -109,52 +109,52 @@ y = Math.floor(pos / mapWidth)
|
||||
}
|
||||
```
|
||||
|
||||
### フォームコントロールの種類
|
||||
#### Fallunterscheidung
|
||||
type: `switch` スイッチを表示します。何かの機能をオン/オフさせたい場合に有用です。
|
||||
### Arten von Form-Elementen
|
||||
#### Schalter
|
||||
type: `switch` Zeigt einen Schalter an.Eignet sich für Fälle, in denen etwas entweder ein- oder ausgeschaltet werden kann.
|
||||
|
||||
##### プロパティ
|
||||
`label` ... スイッチに表記するテキスト。
|
||||
##### Attribute
|
||||
`label` ... Auf dem Schalter anzuzeigender Text.
|
||||
|
||||
#### ラジオボタン
|
||||
type: `radio` ラジオボタンを表示します。選択肢を提示するのに有用です。例えば、Botの強さを設定させるなどです。
|
||||
#### Optionsfeld
|
||||
type: `radio` Zeigt ein Optionsfeld an.Eignet sich für Fälle, in denen verschiedene Optionen angezeigt werden.z.B. zur Einstellung der Stärke des Bots.
|
||||
|
||||
##### プロパティ
|
||||
`items` ... ラジオボタンの選択肢。例:
|
||||
##### Attribute
|
||||
`items` ... Die verfügbaren Optionen.z.B.:
|
||||
```javascript
|
||||
items: [{
|
||||
label: '弱',
|
||||
label: 'Schwach',
|
||||
value: 1
|
||||
}, {
|
||||
label: '中',
|
||||
label: 'Mittelmäßíg',
|
||||
value: 2
|
||||
}, {
|
||||
label: '強',
|
||||
label: 'Stark',
|
||||
value: 3
|
||||
}]
|
||||
```
|
||||
|
||||
#### スライダー
|
||||
type: `slider` スライダーを表示します。
|
||||
#### Schieberegler
|
||||
type: `slider` Zeigt einen Schieberegler an.
|
||||
|
||||
##### プロパティ
|
||||
`min` ... スライダーの下限。 `max` ... スライダーの上限。 `step` ... 入力欄で刻むステップ値。
|
||||
##### Attribute
|
||||
`min` ... Der minimale Reglerwert. `max` ... Der maximale Reglerwert. `step` ... Der Abstand zwischen zwei Stufen des Reglers.
|
||||
|
||||
#### テキストボックス
|
||||
type: `textbox` テキストボックスを表示します。ユーザーになにか入力させる一般的な用途に利用できます。
|
||||
#### Textbox
|
||||
type: `textbox` Zeigt eine Textbox an.Für verschiedene Fälle, in denen Texteingabe des Benutzers gefragt sind, verwendbar.
|
||||
|
||||
## ユーザーにメッセージを表示する
|
||||
設定画面でユーザーと対話する、フォーム以外のもうひとつの方法がこれです。ユーザーになにかメッセージを表示することができます。 例えば、ユーザーがBotの対応していないモードやマップを選択したとき、警告を表示するなどです。 メッセージを表示するには、次のメッセージをストリームに送信します:
|
||||
## Dem Benutzer Nachrichten zeigen
|
||||
Dies ist eine alternative Methode, um mit dem Benutzer zu kommunieren, abgesehen vom Anzeigen eines Fensters während der Vorbereitungsphase des Spiels.Hierdurch kann dem Benutzer eine Nachricht angezeigt werden. Beispielsweise kann eine Warnung angezeigt werden, falls ein Spielmodus oder eine Spielkarte ausgewählt wird, mit der der Bot nicht kompatibel ist. Um eine Nachricht anzuzeigen, muss folgende Nachricht an den Stream gesendet werden:
|
||||
```javascript
|
||||
{
|
||||
type: 'message',
|
||||
body: {
|
||||
text: 'メッセージ内容',
|
||||
type: 'メッセージの種類'
|
||||
text: 'Nachrichteninhalt',
|
||||
type: 'Nachrichtenart'
|
||||
}
|
||||
}
|
||||
```
|
||||
メッセージの種類: `success`, `info`, `warning`, `error`。
|
||||
Nachrichtenarten: `success`, `info`, `warning`, `error`.
|
||||
|
||||
## 投了する
|
||||
投了をするには、<a href="./api/endpoints/games/reversi/games/surrender">このエンドポイント</a>にリクエストします。
|
||||
## Aufgeben
|
||||
Um aufzugeben, sende eine Anfrage an <a href="./api/endpoints/games/reversi/games/surrender">diesen Endpunkt</a>.
|
||||
|
@ -1,25 +1,25 @@
|
||||
# ストリーミングAPI
|
||||
# Streaming API
|
||||
|
||||
ストリーミングAPIを使うと、リアルタイムで様々な情報(例えばタイムラインに新しい投稿が流れてきた、メッセージが届いた、フォローされた、など)を受け取ったり、様々な操作を行ったりすることができます。
|
||||
Durch die Verwendung des Streaming-APIs können verschiedene Daten (z.B. darüber, dass neue Beiträge geschrieben wurden, neue Direktnachrichten eingetroffen sind, Benachrichtigungen über einen neuen Follower, usw) in Echtzeit empfangen werden und dann basierend auf diesen verschiedene Aktionen getätigt werden.
|
||||
|
||||
## ストリームに接続する
|
||||
## Eine Verbindung zum Stream aufbauen
|
||||
|
||||
ストリーミングAPIを利用するには、まずMisskeyサーバーに**websocket**接続する必要があります。
|
||||
Um das Streaming-API zu benutzen, muss zuerst eine Verbindung zu Misskey's **websocket** Server aufgebaut werden.
|
||||
|
||||
以下のURLに、`i`というパラメータ名で認証情報を含めて、websocket接続してください。例:
|
||||
Baue bitte mit Hilfe der unten stehenden URL eine websocket-Verbindung auf, wobei die Anmeldedaten als `i`-Parameter enthalten sind.z.B.:
|
||||
```
|
||||
%WS_URL%/streaming?i=xxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
認証情報は、自分のAPIキーや、アプリケーションからストリームに接続する際はユーザーのアクセストークンのことを指します。
|
||||
Anmeldedaten steht hierfür entweder für den eigenen API-Schlüssel oder bei Verbindungen zum Stream für den durch eine Anwendung generierten Zugangstoken eines Benutzers.
|
||||
|
||||
<div class="ui info">
|
||||
<p><i class="fas fa-info-circle"></i> 認証情報の取得については、<a href="./api">こちらのドキュメント</a>をご確認ください。</p>
|
||||
<p><i class="fas fa-info-circle"></i> Siehe <a href="./api">dieses Dokument</a> für Informationen, wie solche Anmeldedaten erhalten werden können.</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
認証情報は省略することもできますが、その場合非ログインでの利用ということになり、受信できる情報や可能な操作は限られます。例:
|
||||
Ein Verbindungsaufbau ohne Anmeldedaten ist ebenso möglich, jedoch wird in diesem Fall der Zugriff auf manche Daten sowie die Verwendung mancher Funktionen eingeschränkt.z.B.:
|
||||
|
||||
```
|
||||
%WS_URL%/streaming
|
||||
@ -27,15 +27,15 @@
|
||||
|
||||
---
|
||||
|
||||
ストリームに接続すると、後述するAPI操作や、投稿の購読を行ったりすることができます。 しかしまだこの段階では、例えばタイムラインへの新しい投稿を受信したりすることはできません。 それを行うには、ストリーム上で、後述する**チャンネル**に接続する必要があります。
|
||||
Eine Verbindung zum Stream kann durch die später erläuterte API oder durch das Abbonieren individueller Beiträge getätigt werden. Jedoch können zu diesem Zeitpunkt noch keine Informationen über Chroniken wie das Eintreffen neuer Beiträge empfangen werden. Um dies zu ermöglichen, müssen Verbindungen zu später erläuterten **Kanälen** aufgebaut werden.
|
||||
|
||||
**ストリームでのやり取りはすべてJSONです。**
|
||||
**Alle Nachrichten an den sowie vom Stream sind in JSON-Format.**
|
||||
|
||||
## Kanäle
|
||||
MisskeyのストリーミングAPIにはチャンネルという概念があります。これは、送受信する情報を分離するための仕組みです。 Misskeyのストリームに接続しただけでは、まだリアルタイムでタイムラインの投稿を受信したりはできません。 ストリーム上でチャンネルに接続することで、様々な情報を受け取ったり情報を送信したりすることができるようになります。
|
||||
Innerhalb des Misskey Streaming-APIs existiert das Konzept von Kanälen.Diese werden zur Abspaltung der Informationen, die erhalten werden sollen, verwendet. Wird eine Verbindung zum Misskey Stream aufgebaut, so ist es noch nicht möglich, sofort Echtzeit-Aktualisierungen zu empfangen. Durch den Verbindungsaufbau zu Kanälen des Streams wird beidseitige Kommunikation bezüglich Informationen dieses Kanals ermöglicht.
|
||||
|
||||
### チャンネルに接続する
|
||||
チャンネルに接続するには、次のようなデータをJSONでストリームに送信します:
|
||||
### Verbindungen zu Kanälen aufbauen
|
||||
Um eine Verbindung zu einem Kanal aufzubauen, sende die folgende Nachricht:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -50,19 +50,19 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `channel`には接続したいチャンネル名を設定します。チャンネルの種類については後述します。
|
||||
* `id`にはそのチャンネルとやり取りするための任意のIDを設定します。ストリームでは様々なメッセージが流れるので、そのメッセージがどのチャンネルからのものなのか識別する必要があるからです。このIDは、UUIDや、乱数のようなもので構いません。
|
||||
* `params`はチャンネルに接続する際のパラメータです。チャンネルによって接続時に必要とされるパラメータは異なります。パラメータ不要のチャンネルに接続する際は、このプロパティは省略可能です。
|
||||
Hier,
|
||||
* steht `channel` für den Namen des Kanals, zu dem eine Verbindung aufgebaut werden soll.Eine Liste der verfügbaren Kanäle wird später angegeben.
|
||||
* steht `id` für eine einzigartige ID zur Kommunikation mit diesem Kanal.Da durch den Stream viele verschiedene Nachrichten erhalten werden können, ist eine Zuordnung, zu welchen Kanal eine Nachricht gehört, notwendig.Diese ID kann eine UUID oder etwas wie der Wert eines Zufallszahlengenerators sein.
|
||||
* steht `params` für die Parameter zum Verbindunsgaufbau.Je nach Kanal können die verfügbaren Parameter abweichen.Bei Kanälen, die keine Parameter akzeptieren, kann dieses Attribut ausgelassen werden.
|
||||
|
||||
<div class="ui info">
|
||||
<p><i class="fas fa-info-circle"></i> IDはチャンネルごとではなく「チャンネルの接続ごと」です。なぜなら、同じチャンネルに異なるパラメータで複数接続するケースもあるからです。</p>
|
||||
<p><i class="fas fa-info-circle"></i> Diese IDs sollen für jede Verbindung zu einem Kanal einzigartig sein, nicht nur für jeden Kanal.Der Grund dafür ist, dass mehrere Verbindungen zum selben Kanal mit unterschiedlichen Parametern zur selben Zeit bestehen können.</p>
|
||||
</div>
|
||||
|
||||
### チャンネルからのメッセージを受け取る
|
||||
例えばタイムラインのチャンネルなら、新しい投稿があった時にメッセージを発します。そのメッセージを受け取ることで、タイムラインに新しい投稿がされたことをリアルタイムで知ることができます。
|
||||
### Verarbeitung von eintreffenden Nachrichten der Kanäle
|
||||
Beispielsweise wird bei Erstellung eines neuen Beitrags auf einer Chronik von einem Kanal eine Nachricht ausgelöst.Durch die Verarbeitung solcher Nachrichten ist es möglich, in Echtzeit über die Erstellung eines neuen Beitrags zu erfahren.
|
||||
|
||||
チャンネルがメッセージを発すると、次のようなデータがJSONでストリームに流れてきます:
|
||||
Sendet ein Kanal eine Nachricht, so wird vom Stream folgendes JSON-Objekt empfangen:
|
||||
```json
|
||||
{
|
||||
type: 'channel',
|
||||
@ -76,15 +76,15 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `id`には前述したそのチャンネルに接続する際に設定したIDが設定されています。これで、このメッセージがどのチャンネルからのものなのか知ることができます。
|
||||
* `type`にはメッセージの種類が設定されます。チャンネルによって、どのような種類のメッセージが流れてくるかは異なります。
|
||||
* `body`にはメッセージの内容が設定されます。チャンネルによって、どのような内容のメッセージが流れてくるかは異なります。
|
||||
Hier,
|
||||
* steht `id` für die zum Verbindungsaufbau gewählte ID, die zuvor erläutert wurde.Hierdurch wird eine Zuordnung, welche Nachricht zu welchen Kanal gehört, ermöglicht.
|
||||
* steht `type` für die Art der Nachricht.Je nach Kanal können die Arten der Nachrichten, die von einem Kanal empfangen werden, abweichen.
|
||||
* steht `body` für den Inhalt der Nachricht.Je nach Kanal kann der Inhalt der Nachrichten, die von einem Kanal empfangen werden, abweichen.
|
||||
|
||||
### チャンネルに向けてメッセージを送信する
|
||||
チャンネルによっては、メッセージを受け取るだけでなく、こちらから何かメッセージを送信し、何らかの操作を行える場合があります。
|
||||
### Nachrichten an Kanäle senden
|
||||
Je nach Kanal kann es möglich sein, nicht nur Nachrichten vom Kanal zu empfangen, sondern auch Nachrichten an diesen zu senden, die dann unterschiedliche Aktionen auslösen können.
|
||||
|
||||
チャンネルにメッセージを送信するには、次のようなデータをJSONでストリームに送信します:
|
||||
Um eine Nachricht an einen Kanal zu senden, sende folgendes JSON-Objekt an den Stream:
|
||||
```json
|
||||
{
|
||||
type: 'channel',
|
||||
@ -98,13 +98,13 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。これで、このメッセージがどのチャンネルに向けたものなのか識別させることができます。
|
||||
* `type`にはメッセージの種類を設定します。チャンネルによって、どのような種類のメッセージを受け付けるかは異なります。
|
||||
* `body`にはメッセージの内容を設定します。チャンネルによって、どのような内容のメッセージを受け付けるかは異なります。
|
||||
Hier,
|
||||
* steht `id` für die zum Verbindungsaufbau gewählte ID, die zuvor erläutert wurde.Hierdurch wird festgelegt, an welchen Kanal diese Nachricht gesendet werden soll.
|
||||
* steht `type` für die Art der Nachricht.Je nach Kanal können die Arten der Nachrichten, die an einen Kanal gesendet werden können, abweichen.
|
||||
* steht `body` für den Inhalt der Nachricht.Je nach Kanal kann der Inhalt der Nachrichten, der an einen Kanal gesendet werden kann, abweichen.
|
||||
|
||||
### チャンネルから切断する
|
||||
チャンネルから切断するには、次のようなデータをJSONでストリームに送信します:
|
||||
### Verbindungen zu Kanälen trennen
|
||||
Um die Verbindung zu einem Kanal zu trennen, sende die folgende Nachricht:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -115,14 +115,14 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。
|
||||
Hier,
|
||||
* steht `id` für die zum Verbindungsaufbau gewählte ID.
|
||||
|
||||
## ストリームを経由してAPIリクエストする
|
||||
## API-Anfragen durch den Stream senden
|
||||
|
||||
ストリームを経由してAPIリクエストすると、HTTPリクエストを発生させずにAPIを利用できます。そのため、コードを簡潔にできたり、パフォーマンスの向上を見込めるかもしれません。
|
||||
Durch den Stream ist es auch möglich, API-Anfragen ohne die Verwendung einer HTTP-Anfrage zu senden.So kann möglicherweise Code übersichtlicher und effizienter gehalten werden.
|
||||
|
||||
ストリームを経由してAPIリクエストするには、次のようなデータをJSONでストリームに送信します:
|
||||
Um via den Stream eine API-Anfrage zu senden, sende folgendes JSON-Objekt an den Stream:
|
||||
```json
|
||||
{
|
||||
type: 'api',
|
||||
@ -136,18 +136,18 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `id`には、APIのレスポンスを識別するための、APIリクエストごとの一意なIDを設定する必要があります。UUIDや、簡単な乱数のようなもので構いません。
|
||||
* `endpoint`には、あなたがリクエストしたいAPIのエンドポイントを指定します。
|
||||
* `data`には、エンドポイントのパラメータを含めます。
|
||||
Hier,
|
||||
* existiert `id` zur Zuordnung von Anfrage und Antwort und muss auf eine einzigartige ID gesetzt werden.Die Verwendung von UUIDs oder auch dem Wert eines simplen Zufallszahlengenerators ist empfohlen.
|
||||
* gibt `endpoint` den Endpoint an, an den die Anfrage gesendet werden soll.
|
||||
* enthält `data` die Parameter der Anfrage des Endpoints.
|
||||
|
||||
<div class="ui info">
|
||||
<p><i class="fas fa-info-circle"></i> APIのエンドポイントやパラメータについてはAPIリファレンスをご確認ください。</p>
|
||||
<p><i class="fas fa-info-circle"></i> Siehe die API-Referenz bezüglich einer Auflistung verfügbarer API-Endpoints sowie dessen Parameter.</p>
|
||||
</div>
|
||||
|
||||
### レスポンスの受信
|
||||
### Verarbeitung von Antworten auf Anfragen
|
||||
|
||||
APIへリクエストすると、レスポンスがストリームから次のような形式で流れてきます。
|
||||
Sobald eine Anfrage an die API gesendet wurde, wird eine Antwort wie die folgende empfangen:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -158,23 +158,23 @@ APIへリクエストすると、レスポンスがストリームから次の
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `xxxxxxxxxxxxxxxx`の部分には、リクエストの際に設定された`id`が含まれています。これにより、どのリクエストに対するレスポンスなのか判別することができます。
|
||||
* `body`には、レスポンスが含まれています。
|
||||
Hier,
|
||||
* steht an Stelle der `xxxxxxxxxxxxxxxx` die vorher angegebene `id`.Dadurch ist eine Zuordnung von Anfrage zu Antwort möglich.
|
||||
* ist der Antwortwert der Anfrage in `body` enthalten.
|
||||
|
||||
## 投稿のキャプチャ
|
||||
## Beitragserfassung
|
||||
|
||||
Misskeyは投稿のキャプチャと呼ばれる仕組みを提供しています。これは、指定した投稿のイベントをストリームで受け取る機能です。
|
||||
Misskey stellt eine sogenannte Beitragserfassung zur Verfügung.Hierdurch können dem angegebenen Beitrag zugehörige Events über den Stream empfangen werden.
|
||||
|
||||
例えばタイムラインを取得してユーザーに表示したとします。ここで誰かがそのタイムラインに含まれるどれかの投稿に対してリアクションしたとします。
|
||||
Sei beispielsweise eine Situation, in der einem Benutzer eine Chronik angezeigt wird.Nun wird von jemanden auf einen der Beiträge dieser Chronik reagiert.
|
||||
|
||||
しかし、クライアントからするとある投稿にリアクションが付いたことなどは知る由がないため、リアルタイムでリアクションをタイムライン上の投稿に反映して表示するといったことができません。
|
||||
Da der Client jedoch nicht wissen kann, dass ein spezieller Beitrag eine Reaktion erhalten hat, ist es nicht möglich, diese Reaktion in Echtzeit darzustellen.
|
||||
|
||||
この問題を解決するために、Misskeyは投稿のキャプチャ機構を用意しています。投稿をキャプチャすると、その投稿に関するイベントを受け取ることができるため、リアルタイムでリアクションを反映させたりすることが可能になります。
|
||||
Um dieses Problem zu lösen, wurde die Funktionalität der Beitragserfassung implementiert.Wird ein Beitrag erfasst, so werden verschiedene diesem Beitrag zugeordnete Events in Echtzeit übermittelt, um bei einer Veränderung diese sofort auf der Chronik anzeigen zu können.
|
||||
|
||||
### 投稿をキャプチャする
|
||||
### Einen Beitrag erfassen
|
||||
|
||||
投稿をキャプチャするには、ストリームに次のようなメッセージを送信します:
|
||||
Um einen Beitrag zu erfassen, sende folgende Nachricht an den Stream:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -185,12 +185,12 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `id`にキャプチャしたい投稿の`id`を設定します。
|
||||
Hier,
|
||||
* `id` enthält die `id` des Beitrags der erfasst werden soll.
|
||||
|
||||
このメッセージを送信すると、Misskeyにキャプチャを要請したことになり、以後、その投稿に関するイベントが流れてくるようになります。
|
||||
Sobald diese Nachricht gesendet wurde wird dieser Beitrag von Misskey erfasst und es können von nun an diesen Beitrag betreffende Events empfangen werden.
|
||||
|
||||
例えば投稿にリアクションが付いたとすると、次のようなメッセージが流れてきます:
|
||||
Beispielsweise wird das folgende Event empfangen, sobald einem erfassten Beitrag eine Reaktion hinzugefügt wurde:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -206,20 +206,20 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `body`内の`id`に、イベントを発生させた投稿のIDが設定されます。
|
||||
* `body`内の`type`に、イベントの種類が設定されます。
|
||||
* `body`内の`body`に、イベントの詳細が設定されます。
|
||||
Hier,
|
||||
* das `id`-Attribut in `body` enthält die ID des Beitrags, der das Event ausgelöst hat.
|
||||
* das `type`-Attribut in `body` die Art des Events.
|
||||
* das `body`-Attribut von `body` enthält weitere Informationen über das Event.
|
||||
|
||||
#### イベントの種類
|
||||
#### Arten von Events
|
||||
|
||||
##### `reacted`
|
||||
その投稿にリアクションがされた時に発生します。
|
||||
Wird bei Reaktion auf den Beitrag ausgelöst.
|
||||
|
||||
* `reaction`に、リアクションの種類が設定されます。
|
||||
* `userId`に、リアクションを行ったユーザーのIDが設定されます。
|
||||
* `reaction` enthält die Art der Reaktion.
|
||||
* `userId` enthält die ID des Benutzers, der die Reaktion hinzufügte
|
||||
|
||||
例:
|
||||
z.B.:
|
||||
```json
|
||||
{
|
||||
type: 'noteUpdated',
|
||||
@ -235,11 +235,11 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
||||
```
|
||||
|
||||
##### `deleted`
|
||||
その投稿が削除された時に発生します。
|
||||
Wird bei Löschung des Beitrags ausgelöst.
|
||||
|
||||
* `deletedAt`に、削除日時が設定されます。
|
||||
* `deletedAt` enthält Löschdatum und Zeitpunkt.
|
||||
|
||||
例:
|
||||
z.B.:
|
||||
```json
|
||||
{
|
||||
type: 'noteUpdated',
|
||||
@ -254,12 +254,12 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
||||
```
|
||||
|
||||
##### `pollVoted`
|
||||
その投稿に添付されたアンケートに投票された時に発生します。
|
||||
Wird bei Abstimmung in einer dem Beitrag angehörigen Umfrage ausgelöst.
|
||||
|
||||
* `choice`に、選択肢IDが設定されます。
|
||||
* `userId`に、投票を行ったユーザーのIDが設定されます。
|
||||
* `choice` enthält die ID der gewählten Auswahlmöglichkeit.
|
||||
* `userId` enthält die ID des Benutzers, der auf die Umfrage antwortete
|
||||
|
||||
例:
|
||||
z.B.:
|
||||
```json
|
||||
{
|
||||
type: 'noteUpdated',
|
||||
@ -274,11 +274,11 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
||||
}
|
||||
```
|
||||
|
||||
### 投稿のキャプチャを解除する
|
||||
### Beitragserfassung aufheben
|
||||
|
||||
その投稿がもう画面に表示されなくなったりして、その投稿に関するイベントをもう受け取る必要がなくなったときは、キャプチャの解除を申請してください。
|
||||
Sobald ein Beitrag nicht mehr auf der Chronik angezeigt wird und somit diesen Beitrag betreffende Events nicht mehr benötigt werden, bitten wir um die Aufhebung der Erfassung dieses Beitrags.
|
||||
|
||||
次のメッセージを送信します:
|
||||
Sende die folgende Nachricht:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -289,66 +289,66 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
||||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
* `id`にキャプチャを解除したい投稿の`id`を設定します。
|
||||
Hier,
|
||||
* `id` enthält die `id` des Beitrags, für den Erfassung aufgehoben werden soll.
|
||||
|
||||
このメッセージを送信すると、以後、その投稿に関するイベントは流れてこないようになります。
|
||||
Sobald diese Nachricht versendet wurde, werden mit diesem Beitrag verbundene Events nicht mehr empfangen.
|
||||
|
||||
# チャンネル一覧
|
||||
# List aller Kanäle
|
||||
## `main`
|
||||
アカウントに関する基本的な情報が流れてきます。このチャンネルにパラメータはありません。
|
||||
Allgemeine den Benutzer betreffende Informationen werden über diesen Kanal empfangen.Dieser Kanal hat keine Parameter.
|
||||
|
||||
### 流れてくるイベント一覧
|
||||
### Liste der Events, die augelöst werden können
|
||||
|
||||
#### `renote`
|
||||
自分の投稿がRenoteされた時に発生するイベントです。自分自身の投稿をRenoteしたときは発生しません。
|
||||
Wird ausgelöst, sobald ein eigener Beitrag ein Renote erhält.Renotes von eigenen Beiträgen lösen dieses Event nicht aus.
|
||||
|
||||
#### `mention`
|
||||
誰かからメンションされたときに発生するイベントです。
|
||||
Wird ausgelöst, sobald der Benutzer von einem anderen Benutzer erwähnt wird.
|
||||
|
||||
#### `readAllNotifications`
|
||||
自分宛ての通知がすべて既読になったことを表すイベントです。このイベントを利用して、「通知があることを示すアイコン」のようなものをオフにしたりする等のケースが想定されます。
|
||||
Dieses Event gibt an, dass alle Benachrichtungen auf gelesen gesetzt wurden.Es wird erwartet, dass dieses Event für bsp. Fälle eingesetzt wird, in denen der Indikator für ungelesene Benachrichtigungen deaktiviert werden soll.
|
||||
|
||||
#### `meUpdated`
|
||||
自分の情報が更新されたことを表すイベントです。
|
||||
Wird bei Aktualisierung der eigenen Benutzerdaten augelöst.
|
||||
|
||||
#### `follow`
|
||||
自分が誰かをフォローしたときに発生するイベントです。
|
||||
Wird augelöst, sobald einem neuen Benutzer gefolgt wird.
|
||||
|
||||
#### `unfollow`
|
||||
自分が誰かのフォローを解除したときに発生するイベントです。
|
||||
Wird augelöst, sobald einem Benutzer nicht mehr gefolgt wird.
|
||||
|
||||
#### `followed`
|
||||
自分が誰かにフォローされたときに発生するイベントです。
|
||||
Wird augelöst, sobald der Benutzer einen neuen Follower erhält.
|
||||
|
||||
## `homeTimeline`
|
||||
ホームタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
|
||||
Informationen über Beiträge der Startseiten-Chronik werden über diesen Kanal empfangen.Dieser Kanal hat keine Parameter.
|
||||
|
||||
### 流れてくるイベント一覧
|
||||
### Liste der Events, die augelöst werden können
|
||||
|
||||
#### `note`
|
||||
タイムラインに新しい投稿が流れてきたときに発生するイベントです。
|
||||
Wird augelöst, sobald auf der Chronik ein neuer Beitrag erscheint.
|
||||
|
||||
## `localTimeline`
|
||||
ローカルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
|
||||
Informationen über Beiträge der lokalen Chronik werden über diesen Kanal empfangen.Dieser Kanal hat keine Parameter.
|
||||
|
||||
### 流れてくるイベント一覧
|
||||
### Liste der Events, die augelöst werden können
|
||||
|
||||
#### `note`
|
||||
ローカルタイムラインに新しい投稿が流れてきたときに発生するイベントです。
|
||||
Wird augelöst, sobald auf der lokalen Chronik ein neuer Beitrag erscheint.
|
||||
|
||||
## `hybridTimeline`
|
||||
ソーシャルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
|
||||
Informationen über Beiträge der Sozial-Chronik werden über diesen Kanal empfangen.Dieser Kanal hat keine Parameter.
|
||||
|
||||
### 流れてくるイベント一覧
|
||||
### Liste der Events, die augelöst werden können
|
||||
|
||||
#### `note`
|
||||
ソーシャルタイムラインに新しい投稿が流れてきたときに発生するイベントです。
|
||||
Wird augelöst, sobald auf der Sozial-Chronik ein neuer Beitrag erscheint.
|
||||
|
||||
## `globalTimeline`
|
||||
グローバルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
|
||||
Informationen über Beiträge der globalen Chronik werden über diesen Kanal empfangen.Dieser Kanal hat keine Parameter.
|
||||
|
||||
### 流れてくるイベント一覧
|
||||
### Liste der Events, die augelöst werden können
|
||||
|
||||
#### `note`
|
||||
グローバルタイムラインに新しい投稿が流れてきたときに発生するイベントです。
|
||||
Wird augelöst, sobald auf der globalen Chronik ein neuer Beitrag erscheint.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user