Compare commits
100 Commits
Author | SHA1 | Date | |
---|---|---|---|
d10ad1b413 | |||
34063a0b84 | |||
8c9d975d69 | |||
03b072b894 | |||
d1bed49808 | |||
6c3417d9b5 | |||
ba65226460 | |||
9c9cd168ee | |||
abb3d2a8d9 | |||
637fe8a04b | |||
be321e95e5 | |||
ed46c1486c | |||
c9fcfc6862 | |||
8495e37566 | |||
247bd43ae2 | |||
a6685b1559 | |||
66c4e8064b | |||
9d1fa3f202 | |||
a6985d7dc7 | |||
027c021ac9 | |||
604205ec09 | |||
77db016866 | |||
c6a009dbae | |||
4299e3f90c | |||
19f4812c03 | |||
d01c465a8d | |||
4f1409601e | |||
52cffe0864 | |||
0866d5c055 | |||
78c08f6503 | |||
27d0ac3d75 | |||
a8776002f3 | |||
31aa008566 | |||
9d405b4581 | |||
80c490a18b | |||
30c9c3739f | |||
ee0e7a09e0 | |||
bfd9577f0d | |||
0cada4ca76 | |||
a718ccc0b6 | |||
1fcfd8e645 | |||
c6dd932a0b | |||
b79eed01e0 | |||
3a7dbe9764 | |||
bef2534fa8 | |||
888dcd2559 | |||
2b69fca6bd | |||
7d088d42b4 | |||
f8ad303b13 | |||
3c59c6fc9b | |||
7353d729d7 | |||
62591e0e7a | |||
012f15d84b | |||
e57c6f94d2 | |||
40b27e8ad8 | |||
055e9f21b7 | |||
d7085b17fe | |||
0d4d7c9c0c | |||
99209d36e1 | |||
e2a9a0ff3d | |||
ab166959a4 | |||
ab692cfa3d | |||
c3ae6f3a4a | |||
5ef4a52bbd | |||
582768a5e4 | |||
1852d1cc6f | |||
7a5a541a4e | |||
72b03e009c | |||
1b113c1045 | |||
54959557ea | |||
d44cb7f256 | |||
3d063c95d1 | |||
09cab605fc | |||
666c8c0498 | |||
d3e764d7f9 | |||
7060625adf | |||
21b6e23e98 | |||
a0f794e372 | |||
9195504329 | |||
8c5d9dd549 | |||
580f6a5b6c | |||
74e76b460b | |||
c4570b37b7 | |||
cd0b0012d9 | |||
c055b4d32d | |||
75a9ff832a | |||
b64d3af1f3 | |||
fb6605bb40 | |||
3bfae80fa7 | |||
cb16cb0610 | |||
0baed1a275 | |||
42162c8015 | |||
0fab0c416d | |||
e2e262c8ce | |||
cf6596203b | |||
471911a54f | |||
9394f4f540 | |||
4e968216ad | |||
84a7a9555f | |||
8d12fd152b |
@ -1 +1 @@
|
||||
v14.4.0
|
||||
v15.0.1
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:14.4.0-alpine AS base
|
||||
FROM node:15.0.1-alpine AS base
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
|
@ -285,7 +285,6 @@ unregister: "إلغاء التسجيل"
|
||||
passwordLessLogin: "لِج مِن دون كلمة سرية"
|
||||
resetPassword: "أعد تعيين كلمتك السرية"
|
||||
newPasswordIs: "كلمتك السرية الجديدة هي {password}"
|
||||
autoNoteWatch: "راقب الملاحظات تلقائيا"
|
||||
share: "شارِك"
|
||||
notFound: "غير موجود"
|
||||
help: "المساعدة"
|
||||
@ -380,6 +379,11 @@ smtpHost: "المضيف"
|
||||
smtpUser: "اسم المستخدم"
|
||||
smtpPass: "الكلمة السرية"
|
||||
display: "المظهر"
|
||||
_mfm:
|
||||
mention: "أشر الى"
|
||||
quote: "اقتبس"
|
||||
emoji: "إيموجي مخصص"
|
||||
search: "البحث"
|
||||
_reversi:
|
||||
total: "المجموع"
|
||||
_channel:
|
||||
|
@ -373,8 +373,6 @@ unregister: "Deaktivieren"
|
||||
passwordLessLogin: "Passwortloses Anmelden einrichten"
|
||||
resetPassword: "Passwort zurücksetzen"
|
||||
newPasswordIs: "Das neue Passwort ist \"{password}\""
|
||||
autoNoteWatch: "Notizen automatisch beobachten"
|
||||
autoNoteWatchDescription: "Werde über Notizen, auf die du reagiert oder geantwortet hast, informiert"
|
||||
reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren"
|
||||
share: "Teilen"
|
||||
notFound: "Nicht gefunden"
|
||||
@ -412,7 +410,7 @@ noMessagesYet: "Noch keine Nachrichten"
|
||||
newMessageExists: "Du hast eine neue Nachricht"
|
||||
onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden"
|
||||
signinRequired: "Anmeldung erforderlich"
|
||||
invitations: "Einladen"
|
||||
invitations: "Einladungen"
|
||||
invitationCode: "Einladungscode"
|
||||
checking: "Wird überprüft..."
|
||||
available: "Verfügbar"
|
||||
@ -542,6 +540,7 @@ pluginInstallWarn: "Installiere nur vertrauenswürdige Plugins."
|
||||
deck: "Deck"
|
||||
undeck: "Deck verlassen"
|
||||
useBlurEffectForModal: "Weichzeichnungseffekt für Modals verwenden"
|
||||
useFullReactionPicker: "Vollständige Reaktionsauswahl nutzen"
|
||||
generateAccessToken: "Zugriffstoken generieren"
|
||||
permission: "Berechtigungen"
|
||||
enableAll: "Alle aktivieren"
|
||||
@ -598,10 +597,93 @@ openInNewTab: "In neuem Tab öffnen"
|
||||
openInSideView: "In Seitenansicht öffnen"
|
||||
defaultNavigationBehaviour: "Standardnavigationsverhalten"
|
||||
editTheseSettingsMayBreakAccount: "Bei Bearbeitung dieser Einstellungen besteht die Gefahr, dein Benutzerkonto zu beschädigen."
|
||||
instanceTicker: "Instanz-Informationen der Notiz"
|
||||
instanceTicker: "Instanz-Informationen von Notizen"
|
||||
waitingFor: "Warte auf {x}"
|
||||
random: "Zufällig"
|
||||
system: "System"
|
||||
switchUi: "UI wechseln"
|
||||
desktop: "Desktop"
|
||||
_mfm:
|
||||
cheatSheet: "MFM Spickzettel"
|
||||
intro: "MFM ist eine an vielen Stellen verwendbare und Misskey-exklusive Markup-Sprache. Hier kannst du eine Liste von verfügbarer MFM-Syntax anschauen."
|
||||
dummy: "Misskey erweitert die Welt des Fediverse"
|
||||
mention: "Erwähnung"
|
||||
mentionDescription: "Mit At-Zeichen und Nutzername kann ein individueller Nutzer angegeben werden."
|
||||
hashtag: "Hashtag"
|
||||
hashtagDescription: "Mit einer Raute und Text kann ein Hashtag angegeben werden."
|
||||
url: "URL"
|
||||
urlDescription: "URLs können angezeigt werden."
|
||||
link: "Link"
|
||||
linkDescription: "Ein spezifizierter Textabschnitt kann als URL angezeigt werden."
|
||||
bold: "Fett"
|
||||
boldDescription: "Zeichen zur Betonung dicker erscheinen lassen."
|
||||
small: "Klein"
|
||||
smallDescription: "Inhalt klein und dünn erscheinen lassen."
|
||||
center: "Zentrieren"
|
||||
centerDescription: "Inhalt zentriert anzeigen lassen."
|
||||
inlineCode: "Code (Eingebettet)"
|
||||
inlineCodeDescription: "Syntax-Hervorhebung für (Programm-)Code eingebettet anzeigen lassen."
|
||||
blockCode: "Code (Block)"
|
||||
blockCodeDescription: "Syntax-Hervorhebung für mehrzeiligen (Programm-)Code als Block anzeigen lassen."
|
||||
inlineMath: "Mathe (Eingebettet)"
|
||||
inlineMathDescription: "Mathematische Formeln (KaTeX) eingebettet anzeigen."
|
||||
blockMath: "Mathe (Block)"
|
||||
blockMathDescription: "Mehrzeilige mathematische Formeln (KaTeX) als Block einbetten."
|
||||
quote: "Zitationen"
|
||||
quoteDescription: "Inhalt als Zitat anzeigen lassen."
|
||||
emoji: "Benutzerdefinierte Emojis"
|
||||
emojiDescription: "Emoji-Namen mit Doppelpunkten umschließen, um benutzerdefinierte Emojis anzeigen zu lassen."
|
||||
search: "Suche"
|
||||
searchDescription: "Eine vorgefertige Suchanfragebox anzeigen lassen."
|
||||
flip: "Spiegelung"
|
||||
flipDescription: "Inhalt horizontal oder vertikal gespiegelt anzeigen lassen."
|
||||
jelly: "Animation (Dehnen)"
|
||||
jellyDescription: "Verleiht eine sich dehnende Animation."
|
||||
tada: "Animation (Tada)"
|
||||
tadaDescription: "Verleiht eine Animation mit \"Tada!\"-Gefühl"
|
||||
jump: "Animation (Sprung)"
|
||||
jumpDescription: "Verleiht eine springende Animation."
|
||||
bounce: "Animation (Federn)"
|
||||
bounceDescription: "Erzeugt eine federnde Animation."
|
||||
shake: "Animation (Zittern)"
|
||||
shakeDescription: "Verleiht eine zitternde Animation."
|
||||
twitch: "Animation (Zucken)"
|
||||
twitchDescription: "Verleiht eine sehr stark zuckende Animation."
|
||||
spin: "Animation (Rotieren)"
|
||||
spinDescription: "Verleiht eine rotierende Animation."
|
||||
_reversi:
|
||||
reversi: "Reversi"
|
||||
gameSettings: "Spieleinstellungen"
|
||||
chooseBoard: "Spielbrett auswählen"
|
||||
blackOrWhite: "Schwarz/Weiß"
|
||||
blackIs: "{name} spielt Schwarz"
|
||||
rules: "Regeln"
|
||||
botSettings: "Optionen des Computergegners"
|
||||
thisGameIsStartedSoon: "Dieses Spiel beginnt in wenigen Sekunden"
|
||||
waitingForOther: "Warte auf den Zug des Gegenspielers"
|
||||
waitingForMe: "Warte auf deinen Zug"
|
||||
waitingBoth: "Mach dich bereit"
|
||||
ready: "Bereit"
|
||||
cancelReady: "Nicht bereit"
|
||||
opponentTurn: "Zug deines Gegners"
|
||||
myTurn: "Dein Zug"
|
||||
turnOf: "Zug von {name}"
|
||||
pastTurnOf: "Zug von {name}"
|
||||
surrender: "Aufgeben"
|
||||
surrendered: "durch Aufgabe"
|
||||
drawn: "Unentschieden"
|
||||
won: "{name} hat gesiegt"
|
||||
black: "Schwarz"
|
||||
white: "Weiß"
|
||||
total: "Gesamt"
|
||||
turnCount: " Zug {count}"
|
||||
myGames: "Meine Runden"
|
||||
allGames: "Alle Runden"
|
||||
ended: "Beendet"
|
||||
playing: "Laufend"
|
||||
isLlotheo: "Der mit weniger Steinen gewinnt (Llotheo)"
|
||||
loopedMap: "Wiederholendes Spielbrett"
|
||||
canPutEverywhere: "Steine können überall platziert werden"
|
||||
_instanceTicker:
|
||||
none: "Nie anzeigen"
|
||||
remote: "Für Benutzer fremder Instanzen anzeigen"
|
||||
|
@ -373,8 +373,6 @@ unregister: "Unregister"
|
||||
passwordLessLogin: "Set up password-less login"
|
||||
resetPassword: "Reset password"
|
||||
newPasswordIs: "The new password is \"{password}\""
|
||||
autoNoteWatch: "Watch note automatically"
|
||||
autoNoteWatchDescription: "Get notified about the notes which you reactioned or replied."
|
||||
reduceUiAnimation: "Reduce UI animation"
|
||||
share: "Share"
|
||||
notFound: "Not found"
|
||||
@ -405,14 +403,14 @@ next: "Next"
|
||||
retype: "Enter again"
|
||||
noteOf: "{user}'s notes"
|
||||
inviteToGroup: "Invite to group"
|
||||
maxNoteTextLength: "Character limit of the note"
|
||||
maxNoteTextLength: "Character limit of notes"
|
||||
quoteAttached: "Quoted"
|
||||
quoteQuestion: "Do you want to append a quote?"
|
||||
noMessagesYet: "No messages yet"
|
||||
newMessageExists: "You've got a new message"
|
||||
onlyOneFileCanBeAttached: "You can only attach one file to a message"
|
||||
signinRequired: "Please sign in"
|
||||
invitations: "Invite"
|
||||
invitations: "Invitations"
|
||||
invitationCode: "Invitation code"
|
||||
checking: "Checking"
|
||||
available: "Available"
|
||||
@ -542,6 +540,7 @@ pluginInstallWarn: "Please do not install untrustworthy plugins."
|
||||
deck: "Deck"
|
||||
undeck: "Leave Deck"
|
||||
useBlurEffectForModal: "Use blur effect for modals"
|
||||
useFullReactionPicker: "Use full-size reaction picker"
|
||||
generateAccessToken: "Generate access token"
|
||||
permission: "Permissions"
|
||||
enableAll: "Enable all"
|
||||
@ -598,10 +597,93 @@ openInNewTab: "Open in new tab"
|
||||
openInSideView: "Open in side view"
|
||||
defaultNavigationBehaviour: "Default navigation behavior"
|
||||
editTheseSettingsMayBreakAccount: "Editing these settings may damage your account."
|
||||
instanceTicker: "Instance information of the Note"
|
||||
instanceTicker: "Instance information of notes"
|
||||
waitingFor: "Waiting for {x}"
|
||||
random: "Random"
|
||||
system: "System"
|
||||
switchUi: "Switch UI"
|
||||
desktop: "Desktop"
|
||||
_mfm:
|
||||
cheatSheet: "MFM Cheatsheet"
|
||||
intro: "MFM is a Misskey-exclusive markup language that can be used in many places. Here you can view a list of all available MFM syntax."
|
||||
dummy: "Misskey expands the world of the Fediverse"
|
||||
mention: "Mention"
|
||||
mentionDescription: "Using an At-Symbol and a username, you can specify a specific user."
|
||||
hashtag: "Hashtag"
|
||||
hashtagDescription: "Using a number sign and text, you can specify a hashtag."
|
||||
url: "URL"
|
||||
urlDescription: "URLs can be displayed."
|
||||
link: "Link"
|
||||
linkDescription: "Specific parts of text can be displayed as URL."
|
||||
bold: "Bold"
|
||||
boldDescription: "Highlights letters by making them thicker."
|
||||
small: "Small"
|
||||
smallDescription: "Displays contents small and thinn."
|
||||
center: "Center"
|
||||
centerDescription: "Displays content centered."
|
||||
inlineCode: "Code (Inline)"
|
||||
inlineCodeDescription: "Displays inline syntax highlighting for (program-)code."
|
||||
blockCode: "Code (Block)"
|
||||
blockCodeDescription: "Displays syntax highlighting for multi-line (program-)code in a block."
|
||||
inlineMath: "Math (In-line)"
|
||||
inlineMathDescription: "Display math formulas (KaTeX) in-line"
|
||||
blockMath: "Math (Block)"
|
||||
blockMathDescription: "Display multi-line Math formulas (KaTeX) in a block"
|
||||
quote: "Quote"
|
||||
quoteDescription: "Displays content as quote."
|
||||
emoji: "Custom Emoji"
|
||||
emojiDescription: "By surrounding a custom emoji name with colons, custom emoji can be displayed."
|
||||
search: "Search"
|
||||
searchDescription: "Displays a search box with pre-entered text."
|
||||
flip: "Flip"
|
||||
flipDescription: "Flips content horizontally or vertically."
|
||||
jelly: "Animation (Jelly)"
|
||||
jellyDescription: "Infuses a jelly-like animation."
|
||||
tada: "Animation (Tada)"
|
||||
tadaDescription: "Infuses a \"Tada!\"-like animation."
|
||||
jump: "Animation (Jump)"
|
||||
jumpDescription: "Infuses a jumping animation."
|
||||
bounce: "Animation (Bounce)"
|
||||
bounceDescription: "Causes a bouncy animation."
|
||||
shake: "Animation (Shake)"
|
||||
shakeDescription: "Infuses a shaking animation."
|
||||
twitch: "Animation (Twitch)"
|
||||
twitchDescription: "Infuses a strongly twitching animation."
|
||||
spin: "Animation (Spin)"
|
||||
spinDescription: "Infuses a spinning animation."
|
||||
_reversi:
|
||||
reversi: "Reversi"
|
||||
gameSettings: "Game settings"
|
||||
chooseBoard: "Choose a board"
|
||||
blackOrWhite: "Black/White"
|
||||
blackIs: "{name} is playing Black"
|
||||
rules: "Rules"
|
||||
botSettings: "Bot options"
|
||||
thisGameIsStartedSoon: "The game will start in a few seconds"
|
||||
waitingForOther: "Waiting for the opponent's turn"
|
||||
waitingForMe: "Waiting for your turn"
|
||||
waitingBoth: "Get ready"
|
||||
ready: "Ready"
|
||||
cancelReady: "Cancel ready"
|
||||
opponentTurn: "Opponent's turn"
|
||||
myTurn: "Your turn"
|
||||
turnOf: "{name}'s turn"
|
||||
pastTurnOf: "{name}'s turn"
|
||||
surrender: "Surrender"
|
||||
surrendered: "By surrender"
|
||||
drawn: "Draw"
|
||||
won: "{name}'s win"
|
||||
black: "Black"
|
||||
white: "White"
|
||||
total: "Total"
|
||||
turnCount: "Turn {count}"
|
||||
myGames: "My rounds"
|
||||
allGames: "All rounds"
|
||||
ended: "Ended"
|
||||
playing: "Currently playing"
|
||||
isLlotheo: "The one with fewer stones wins (Llotheo)"
|
||||
loopedMap: "Looped map"
|
||||
canPutEverywhere: "Tiles are placeable everywhere"
|
||||
_instanceTicker:
|
||||
none: "Never show"
|
||||
remote: "Show for remote users"
|
||||
|
@ -373,8 +373,6 @@ unregister: "Cancelar registro"
|
||||
passwordLessLogin: "Iniciar sesión sin contraseña"
|
||||
resetPassword: "Resetear contraseña"
|
||||
newPasswordIs: "La nueva contraseña es \"{password}\""
|
||||
autoNoteWatch: "Ver nota automáticamente"
|
||||
autoNoteWatchDescription: "Recibe notificaciones sobre las notas de otros usuarios que a los que respondiste y reaccionaste"
|
||||
reduceUiAnimation: "Reducir la animación de la UI"
|
||||
share: "Compartir"
|
||||
notFound: "No se encuentra"
|
||||
@ -542,6 +540,7 @@ pluginInstallWarn: "Por favor no instale plugins que no son de confianza"
|
||||
deck: "Deck"
|
||||
undeck: "Quitar deck"
|
||||
useBlurEffectForModal: "Usar efecto borroso en modales"
|
||||
useFullReactionPicker: "Reacción"
|
||||
generateAccessToken: "Generar token de acceso"
|
||||
permission: "Permisos"
|
||||
enableAll: "Activar todo"
|
||||
@ -598,9 +597,66 @@ openInNewTab: "Abrir en una Nueva Pestaña"
|
||||
openInSideView: "Abrir en una vista al costado"
|
||||
defaultNavigationBehaviour: "Navegación por defecto"
|
||||
editTheseSettingsMayBreakAccount: "Editar estas configuraciones puede dañar su cuenta."
|
||||
instanceTicker: "Información de notas de la instancia"
|
||||
waitingFor: "Esperando a {x}"
|
||||
random: "Aleatorio"
|
||||
system: "Sistema"
|
||||
switchUi: "Cambiar interfaz de usuario"
|
||||
desktop: "Escritorio"
|
||||
_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."
|
||||
mention: "Menciones"
|
||||
mentionDescription: "El signo @ seguido de un nombre de usuario se puede utilizar para notificar a un usuario en particular."
|
||||
hashtag: "Hashtag"
|
||||
url: "URL"
|
||||
link: "Vínculo"
|
||||
bold: "Negrita"
|
||||
center: "Centrar"
|
||||
blockCode: "Código (bloque)"
|
||||
blockCodeDescription: "Código de resaltado de sintaxis, como programas de varias líneas con bloques."
|
||||
quote: "Citar"
|
||||
emoji: "Emojis personalizados"
|
||||
search: "Buscar"
|
||||
flip: "Echar de un capirotazo"
|
||||
flipDescription: "Voltea el contenido hacia arriba / abajo o hacia la izquierda / derecha."
|
||||
_reversi:
|
||||
reversi: "Reversi"
|
||||
gameSettings: "Configuración del juego"
|
||||
chooseBoard: "Elegir tablero"
|
||||
blackOrWhite: "Blancas/Negras"
|
||||
blackIs: "{name} juega con fichas negras"
|
||||
rules: "Reglas"
|
||||
botSettings: "Opciones del bot"
|
||||
thisGameIsStartedSoon: "El juego empezará en segundos"
|
||||
waitingForOther: "Esperando el turno del adversario"
|
||||
waitingForMe: "Esperando mi turno"
|
||||
waitingBoth: "Prepárate"
|
||||
ready: "Listo"
|
||||
cancelReady: "No estoy listo"
|
||||
opponentTurn: "Turno del adversario"
|
||||
myTurn: "Mi turno"
|
||||
turnOf: "Turno de {name}"
|
||||
pastTurnOf: "Turno de {name}"
|
||||
surrender: "Rendirse"
|
||||
surrendered: "Por rendirse"
|
||||
drawn: "Empate"
|
||||
won: "{name} ha ganado"
|
||||
black: "Negro"
|
||||
white: "Blanco"
|
||||
total: "Total"
|
||||
turnCount: "Turno {count}"
|
||||
myGames: "Mis juegos"
|
||||
allGames: "Todos los juegos"
|
||||
ended: "Finalizado"
|
||||
playing: "Jugando"
|
||||
isLlotheo: "El que tenga menos fichas gana (LLoTheO)"
|
||||
loopedMap: "Mapa en bucle"
|
||||
canPutEverywhere: "Puedes colocar donde quieras"
|
||||
_instanceTicker:
|
||||
none: "No mostrar"
|
||||
remote: "Mostrar a usuarios remotos"
|
||||
always: "Mostrar siempre"
|
||||
_serverDisconnectedBehavior:
|
||||
reload: "Recargar automáticamente"
|
||||
dialog: "Mostrar diálogo de advertencia"
|
||||
|
@ -372,8 +372,6 @@ unregister: "Se désinscrire"
|
||||
passwordLessLogin: "Connectez-vous sans mot de passe"
|
||||
resetPassword: "Réinitialiser mot de passe"
|
||||
newPasswordIs: "Votre nouveau mot de passe est \"{password}\""
|
||||
autoNoteWatch: "Surveiller les notes automatiquement"
|
||||
autoNoteWatchDescription: "Soyez informé des notes auxquelles vous avez réagi ou répondu."
|
||||
reduceUiAnimation: "Réduire les animations dans l’interface"
|
||||
share: "Partager"
|
||||
notFound: "Non trouvé"
|
||||
@ -582,6 +580,14 @@ setMultipleBySeparatingWithSpace: "Vous pouvez définir plus d’un, séparés p
|
||||
fileIdOrUrl: "ID du fichier ou URL"
|
||||
chatOpenBehavior: "Comportement de la fenêtre de discussion lors de son ouverture"
|
||||
random: "Aléatoire"
|
||||
_mfm:
|
||||
mention: "Mentionner"
|
||||
hashtag: "Hashtags"
|
||||
link: "Lien"
|
||||
center: "Centrée"
|
||||
quote: "Citer"
|
||||
emoji: "Émojis personnalisés"
|
||||
search: "Rechercher"
|
||||
_reversi:
|
||||
total: "Total"
|
||||
_serverDisconnectedBehavior:
|
||||
|
@ -15,17 +15,24 @@ const merge = (...args) => args.reduce((a, c) => ({
|
||||
|
||||
const languages = [
|
||||
'ar-SA',
|
||||
//'cs-CZ',
|
||||
//'da-DK',
|
||||
'cs-CZ',
|
||||
'da-DK',
|
||||
'de-DE',
|
||||
'en-US',
|
||||
'es-ES',
|
||||
'fr-FR',
|
||||
'ja-JP',
|
||||
'ja-KS',
|
||||
'kab-KAB',
|
||||
'kn-IN',
|
||||
'ko-KR',
|
||||
//'nl-NL',
|
||||
//'pl-PL',
|
||||
'nl-NL',
|
||||
'no-NO',
|
||||
'pl-PL',
|
||||
'pt-PT',
|
||||
'ru-RU',
|
||||
'ug-CN',
|
||||
'uk-UA',
|
||||
'zh-CN',
|
||||
'zh-TW',
|
||||
];
|
||||
|
@ -214,6 +214,7 @@ imageUrl: "画像URL"
|
||||
remove: "削除"
|
||||
removed: "削除しました"
|
||||
removeAreYouSure: "「{x}」を削除しますか?"
|
||||
resetAreYouSure: "リセットしますか?"
|
||||
saved: "保存しました"
|
||||
messaging: "チャット"
|
||||
upload: "アップロード"
|
||||
@ -373,8 +374,6 @@ unregister: "登録を解除"
|
||||
passwordLessLogin: "パスワード無しログイン"
|
||||
resetPassword: "パスワードをリセット"
|
||||
newPasswordIs: "新しいパスワードは「{password}」です"
|
||||
autoNoteWatch: "ノートの自動ウォッチ"
|
||||
autoNoteWatchDescription: "あなたがリアクションしたり返信したりした他のユーザーのノートに関する通知を受け取るようにします。"
|
||||
reduceUiAnimation: "UIのアニメーションを減らす"
|
||||
share: "共有"
|
||||
notFound: "見つかりません"
|
||||
@ -542,6 +541,7 @@ pluginInstallWarn: "信頼できないプラグインはインストールしな
|
||||
deck: "デッキ"
|
||||
undeck: "デッキ解除"
|
||||
useBlurEffectForModal: "モーダルにぼかし効果を使用"
|
||||
useFullReactionPicker: "フル機能リアクションピッカーを使用"
|
||||
generateAccessToken: "アクセストークンの発行"
|
||||
permission: "権限"
|
||||
enableAll: "全て有効にする"
|
||||
@ -601,6 +601,58 @@ editTheseSettingsMayBreakAccount: "これらの設定を編集するとアカウ
|
||||
instanceTicker: "ノートのインスタンス情報"
|
||||
waitingFor: "{x}を待っています"
|
||||
random: "ランダム"
|
||||
system: "システム"
|
||||
switchUi: "UI切り替え"
|
||||
desktop: "デスクトップ"
|
||||
|
||||
_mfm:
|
||||
cheatSheet: "MFMチートシート"
|
||||
intro: "MFMは、Misskey内の様々な場所で使用できる専用のマークアップ言語です。ここでは、MFMで使用可能な構文一覧が確認できます。"
|
||||
dummy: "MisskeyでFediverseの世界が広がります"
|
||||
mention: "メンション"
|
||||
mentionDescription: "アットマーク + ユーザー名で、特定のユーザーを示すことができます。"
|
||||
hashtag: "ハッシュタグ"
|
||||
hashtagDescription: "ナンバーサイン + タグで、ハッシュタグを示すことができます。"
|
||||
url: "URL"
|
||||
urlDescription: "URLを示すことができます。"
|
||||
link: "リンク"
|
||||
linkDescription: "文章の特定の範囲を、URLに紐づけることができます。"
|
||||
bold: "太字"
|
||||
boldDescription: "文字を太く表示して強調することができます。"
|
||||
small: "目立たなく"
|
||||
smallDescription: "内容を小さく・薄く表示させることができます。"
|
||||
center: "中央寄せ"
|
||||
centerDescription: "内容を中央寄せで表示させることができます。"
|
||||
inlineCode: "コード(インライン)"
|
||||
inlineCodeDescription: "プログラムなどのコードをインラインでシンタックスハイライトします。"
|
||||
blockCode: "コード(ブロック)"
|
||||
blockCodeDescription: "複数行のプログラムなどのコードをブロックでシンタックスハイライトします。"
|
||||
inlineMath: "数式(インライン)"
|
||||
inlineMathDescription: "数式(KaTeX)をインラインで表示します。"
|
||||
blockMath: "数式(ブロック)"
|
||||
blockMathDescription: "複数行の数式(KaTeX)をブロックで表示します。"
|
||||
quote: "引用"
|
||||
quoteDescription: "内容が引用であることを示すことができます。"
|
||||
emoji: "カスタム絵文字"
|
||||
emojiDescription: "コロンでカスタム絵文字名を囲むと、カスタム絵文字を表示させることができます。"
|
||||
search: "検索"
|
||||
searchDescription: "入力済み検索ボックスを表示させることができます。"
|
||||
flip: "反転"
|
||||
flipDescription: "内容を上下または左右に反転させます。"
|
||||
jelly: "アニメーション(びよんびよん)"
|
||||
jellyDescription: "びよんびよんするアニメーションを与えます。"
|
||||
tada: "アニメーション(じゃーん)"
|
||||
tadaDescription: "ジャーン!という感じのアニメーションを与えます。"
|
||||
jump: "アニメーション(ジャンプ)"
|
||||
jumpDescription: "飛び跳ねるようなアニメーションを与えます。"
|
||||
bounce: "アニメーション(バウンド)"
|
||||
bounceDescription: "ぽよんぽよん弾むようなアニメーションを与えます。"
|
||||
shake: "アニメーション(ぶるぶる)"
|
||||
shakeDescription: "ぶるぶるするアニメーションを与えます。"
|
||||
twitch: "アニメーション(ブレ)"
|
||||
twitchDescription: "激しくブレるアニメーションを与えます。"
|
||||
spin: "アニメーション(回転)"
|
||||
spinDescription: "回転するアニメーションを与えます。"
|
||||
|
||||
_reversi:
|
||||
reversi: "リバーシ"
|
||||
|
@ -1,12 +1,12 @@
|
||||
---
|
||||
_lang_: "日本語 (関西弁)"
|
||||
introMisskey: "ようこそ!Misskeyは、オープンソースの分散型マイクロブログサービスやねん。\n「ノート」を作成しぃ、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素はよ反応を追加することもできます✌\n新しい世界を探検しよう🚀"
|
||||
introMisskey: "ようこそ!Misskeyってのは、オープンソースの分散型マイクロブログサービスやねん。\n「ノート」を作成し、いま起こっとることを共有したり、あんたんこととか皆に伝えていこう📡\n「リアクション」機能で、皆のノートに素はよ反応を追加することもできるんやで✌\n新しい世界を探検してみらん?🚀"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "探す"
|
||||
notifications: "通知"
|
||||
username: "ユーザー名"
|
||||
password: "パスワード"
|
||||
fetchingAsApObject: "連合に照会中"
|
||||
fetchingAsApObject: "今ちと連合に照会しとるで"
|
||||
ok: "おっけー"
|
||||
gotIt: "ほい"
|
||||
cancel: "やめとくわ"
|
||||
@ -16,35 +16,40 @@ noNotes: "ノートはあらへん"
|
||||
noNotifications: "通知はあらへん"
|
||||
instance: "インスタンス"
|
||||
settings: "設定"
|
||||
basicSettings: "基本設定"
|
||||
otherSettings: "その他の設定"
|
||||
openInWindow: "ウィンドウで開いてや"
|
||||
profile: "プロフィール"
|
||||
timeline: "タイムライン"
|
||||
noAccountDescription: "自己紹介はあらへん"
|
||||
login: "ログイン"
|
||||
loggingIn: "ログインしとります"
|
||||
loggingIn: "ログインしよるで"
|
||||
logout: "ログアウト"
|
||||
signup: "新規登録"
|
||||
uploading: "アップロードしとります"
|
||||
save: "保存"
|
||||
uploading: "アップロードしよるで"
|
||||
save: "とっとく"
|
||||
users: "ユーザー"
|
||||
addUser: "ユーザー増やす"
|
||||
addUser: "ユーザーを追加や"
|
||||
favorite: "お気に入り"
|
||||
favorites: "お気に入り"
|
||||
unfavorite: "お気に入りやめる"
|
||||
pin: "ピン留め"
|
||||
unpin: "ピン留めやめる"
|
||||
unfavorite: "やっぱ気に入らん"
|
||||
pin: "ピン留めしとく"
|
||||
unpin: "やっぱピン留めせん"
|
||||
copyContent: "内容をコピー"
|
||||
copyLink: "リンクをコピー"
|
||||
delete: "ほかす"
|
||||
deleteAndEdit: "ほかして直す"
|
||||
deleteAndEditConfirm: "このノートをほかしてもっかい直す?このノートへのリアクション、Remote、返信も全部消えんで"
|
||||
deleteAndEditConfirm: "このノートをほかしてもっかい直す?このノートへのリアクション、Renote、返信も全部消えるんやけどそれでもええん?"
|
||||
addToList: "リストに入れたる"
|
||||
sendMessage: "メッセージを送る"
|
||||
copyUsername: "ユーザー名をコピー"
|
||||
searchUser: "ユーザーを検索"
|
||||
reply: "返す"
|
||||
loadMore: "もっとあるやろ!"
|
||||
youGotNewFollower: "フォローされたで"
|
||||
receiveFollowRequest: "フォローリクエストされたで"
|
||||
followRequestAccepted: "フォローが承認されたで"
|
||||
mention: "メンション"
|
||||
mentions: "あんた宛て"
|
||||
directNotes: "ダイレクト投稿"
|
||||
importAndExport: "インポートとエクスポート"
|
||||
@ -57,7 +62,7 @@ unfollowConfirm: "{name}のフォローを解除してもええんか?"
|
||||
exportRequested: "エクスポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。エクスポート終わったら「ドライブ」に突っ込んどくで。"
|
||||
importRequested: "インポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。"
|
||||
lists: "リスト"
|
||||
noLists: "リストはあらへん"
|
||||
noLists: "リストなんてあらへんで"
|
||||
note: "ノート"
|
||||
notes: "ノート"
|
||||
following: "フォロー"
|
||||
@ -65,31 +70,35 @@ followers: "フォロワー"
|
||||
followsYou: "フォローされとるで"
|
||||
createList: "リスト作る"
|
||||
manageLists: "リストの管理"
|
||||
retry: "もっぺんやってみる"
|
||||
error: "エラー"
|
||||
somethingHappened: "なんかアカンことが起こったで"
|
||||
retry: "もっぺんやる?"
|
||||
pageLoadError: "ページの読み込みに失敗してしもうたで…"
|
||||
pageLoadErrorDescription: "これは普通、ネットワークかブラウザキャッシュが原因やからね。キャッシュをクリアするか、もうちっとだけ待ってくれへんか?"
|
||||
enterListName: "リスト名を入れてや"
|
||||
privacy: "プライバシーってなんや?オカンの年齢か?"
|
||||
makeFollowManuallyApprove: "他人のフォローは許可してからや!"
|
||||
privacy: "プライバシーってなんぞや?"
|
||||
makeFollowManuallyApprove: "他人からのフォローは自分が決める"
|
||||
defaultNoteVisibility: "もとからの公開範囲"
|
||||
follow: "フォロー"
|
||||
followRequest: "フォロー許してくれや!言うてみる"
|
||||
followRequests: "フォロー許してくれや!"
|
||||
followRequest: "フォローを頼む"
|
||||
followRequests: "フォローを頼む"
|
||||
unfollow: "フォローやめる"
|
||||
followRequestPending: "フォロー許してくれるん待っとる"
|
||||
enterEmoji: "絵文字を入れてや"
|
||||
renote: "Renote"
|
||||
unrenote: "Renoteやめる"
|
||||
quote: "引用"
|
||||
pinnedNote: "ピン留めされたノート"
|
||||
pinnedNote: "ピン留めされとるノート"
|
||||
you: "あんた"
|
||||
clickToShow: "押してみ、見せたるわ"
|
||||
sensitive: "見たらあかんで"
|
||||
clickToShow: "押したら見えるようになるで"
|
||||
sensitive: "ちょっとアカンやつやで"
|
||||
add: "増やす"
|
||||
reaction: "リアクション"
|
||||
reactionSettingDescription: "リアクションピッカーに出しとくリアクションを選んでや。"
|
||||
rememberNoteVisibility: "公開範囲覚えといて"
|
||||
attachCancel: "くっつけるのやめよか"
|
||||
markAsSensitive: "ちょっと見せられへんわ"
|
||||
unmarkAsSensitive: "別にええんじゃね?"
|
||||
attachCancel: "やっぱ添付やめてくれん?"
|
||||
markAsSensitive: "ちょっとこれはアカン"
|
||||
unmarkAsSensitive: "そこまでアカンことないやろ"
|
||||
enterFileName: "ファイル名を入れてや"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュートやめたる"
|
||||
@ -97,30 +106,35 @@ block: "ブロック"
|
||||
unblock: "ブロックやめたる"
|
||||
suspend: "凍結"
|
||||
unsuspend: "溶かす"
|
||||
blockConfirm: "ブロックしてしもうてええか?"
|
||||
unblockConfirm: "ブロックすんのやめるけどええか?"
|
||||
blockConfirm: "ブロックしてもええんか?"
|
||||
unblockConfirm: "ブロックやめたるってほんまか?"
|
||||
suspendConfirm: "凍結してしもうてええか?"
|
||||
unsuspendConfirm: "解凍するけどええか?"
|
||||
selectList: "リストを選ぶ"
|
||||
selectAntenna: "アンテナを選ぶ"
|
||||
selectWidget: "ウィジェットを選ぶ"
|
||||
editWidgets: "ウィジェットをいじる"
|
||||
editWidgetsExit: "編集終ったで"
|
||||
customEmojis: "カスタム絵文字"
|
||||
emoji: "絵文字"
|
||||
emojiName: "絵文字名"
|
||||
emojiUrl: "絵文字画像URL"
|
||||
addEmoji: "絵文字を追加"
|
||||
settingGuide: "ええ感じの設定"
|
||||
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
||||
cacheRemoteFilesDescription: "この設定をチャラにすると、リモートファイルをキャッシュせず直リンクするようになります。サーバーのストレージを節約できますが、サムネイルが生成されへんので通信量が増加します。"
|
||||
flagAsBot: "Botやでと言っとく"
|
||||
flagAsCat: "Catやでと言っとく"
|
||||
autoAcceptFollowed: "フォローしとるユーザーからのフォロリクは全部勝手にええでって言うで"
|
||||
cacheRemoteFilesDescription: "この設定を切っとくと、リモートファイルをキャッシュせず直リンクするようになってしまうんやで? サーバーのストレージは節約できるんやけど、かわりにサムネイルが作られんくなるから通信量が増えるで?"
|
||||
flagAsBot: "Botやで"
|
||||
flagAsCat: "Catやで"
|
||||
autoAcceptFollowed: "フォローしとるユーザーからのフォローリクエストには勝手に許可しとくで。"
|
||||
addAcount: "アカウント追加"
|
||||
loginFailed: "ログインに失敗してん"
|
||||
loginFailed: "ログインに失敗してしもうた…"
|
||||
showOnRemote: "リモートで見る"
|
||||
general: "全般"
|
||||
wallpaper: "壁紙"
|
||||
setWallpaper: "壁紙を設定"
|
||||
removeWallpaper: "壁紙ほかす"
|
||||
removeWallpaper: "壁紙を削除"
|
||||
searchWith: "検索: {q}"
|
||||
youHaveNoLists: "リストはあらへん"
|
||||
youHaveNoLists: "リストがあらへんで?"
|
||||
followConfirm: "{name}をフォローしてええか?"
|
||||
proxyAccount: "プロキシアカウント"
|
||||
proxyAccountDescription: "プロキシアカウントは、代わりにフォローしてくれるアカウントや。例えば、551に豚まんが無いときやったり、ユーザーがリモートユーザーをアカウントに入れたとき、リストに入れられたユーザーが誰からもフォローされてないと寂しいやん。寂しいし、アクティビティも配達されへんから、プロキシアカウントがフォローしてくれるで。ええやつやん…"
|
||||
@ -130,7 +144,7 @@ recipient: "宛先"
|
||||
annotation: "注釈"
|
||||
federation: "連合"
|
||||
instances: "インスタンス"
|
||||
registeredAt: "一見さんになった日"
|
||||
registeredAt: "初観測"
|
||||
latestRequestSentAt: "ちょっと前のリクエスト送信"
|
||||
latestRequestReceivedAt: "ちょっと前のリクエスト受信"
|
||||
latestStatus: "ちょっと前のステータス"
|
||||
@ -257,7 +271,8 @@ copyUrl: "URLをコピー"
|
||||
rename: "名前を変えるで"
|
||||
avatar: "アイコン"
|
||||
banner: "バナー"
|
||||
nsfw: "見たらあかんで"
|
||||
nsfw: "ちょっとアカンやつやで"
|
||||
whenServerDisconnected: "サーバーとの接続が失くなってしもうたとき"
|
||||
disconnectedFromServer: "サーバーが機嫌悪いねん"
|
||||
reload: "リロード"
|
||||
doNothing: "何もせんとく"
|
||||
@ -293,7 +308,19 @@ proxyRemoteFilesDescription: "この設定を入れると、保存しとらん
|
||||
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
|
||||
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
|
||||
inMb: "メガバイト単位"
|
||||
iconUrl: "アイコン画像のURL"
|
||||
bannerUrl: "バナー画像のURL"
|
||||
basicInfo: "基本情報"
|
||||
pinnedUsers: "ピン留めしたユーザー"
|
||||
pinnedUsersDescription: "「みつける」ページとかにピン留めしたいユーザーをここに書けばええんやで。他ん人との名前は改行で区切ればええんやで。"
|
||||
hcaptcha: "hCaptcha(キャプチャ)"
|
||||
enableHcaptcha: "hCaptcha(キャプチャ)をつけとく"
|
||||
hcaptchaSiteKey: "サイトキー"
|
||||
hcaptchaSecretKey: "シークレットキー"
|
||||
recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "reCAPTCHA(リキャプチャ)を有効にする"
|
||||
recaptchaSiteKey: "サイトキー"
|
||||
recaptchaSecretKey: "シークレットキー"
|
||||
avoidMultiCaptchaConfirm: "ぎょうさんのCaptchaをつこてしまうと、仲良うせんことがあるんや。他のCaptchaをなおしとこか?別にキャンセルしてもろうたらCaptchaは消されへんで済むけど知らんで。"
|
||||
antennas: "アンテナ"
|
||||
manageAntennas: "アンテナいじる"
|
||||
@ -346,18 +373,58 @@ unregister: "登録やめる"
|
||||
passwordLessLogin: "パスワード無くてもログインできるようにする"
|
||||
resetPassword: "パスワードをリセット"
|
||||
newPasswordIs: "今度のパスワードは「{password}」や"
|
||||
reduceUiAnimation: "UIの動きやアニメーションを減らしてくれや。"
|
||||
share: "わけわけ"
|
||||
notFound: "見つからへんね"
|
||||
notFoundDescription: "指定されたURLに該当するページはあらへんやった。"
|
||||
uploadFolder: "とりあえずここへアップロード"
|
||||
cacheClear: "キャッシュをほかす"
|
||||
markAsReadAllNotifications: "通知はもう全て読んだわっ"
|
||||
markAsReadAllUnreadNotes: "投稿は全て読んだわっ"
|
||||
markAsReadAllTalkMessages: "チャットはもうぜんぶ読んだわっ"
|
||||
help: "ヘルプ"
|
||||
inputMessageHere: "ここにメッセージ書いてや"
|
||||
close: "さいなら"
|
||||
group: "グループ"
|
||||
groups: "グループ"
|
||||
createGroup: "グループを作るで"
|
||||
ownedGroups: "所有しとるグループ"
|
||||
joinedGroups: "参加しとるグループ"
|
||||
invites: "来てや"
|
||||
groupName: "グループ名"
|
||||
members: "メンバー"
|
||||
transfer: "譲渡"
|
||||
messagingWithUser: "ユーザーとチャット"
|
||||
messagingWithGroup: "グループでチャット"
|
||||
title: "タイトル"
|
||||
text: "テキスト"
|
||||
enable: "有効にするで"
|
||||
next: "次"
|
||||
retype: "もっかい入力"
|
||||
noteOf: "{user}のノート"
|
||||
inviteToGroup: "グループに招く"
|
||||
maxNoteTextLength: "ノートの文字数制限"
|
||||
quoteAttached: "引用付いとるで"
|
||||
quoteQuestion: "引用として添付してもええか?"
|
||||
noMessagesYet: "まだチャットはあらへんで"
|
||||
newMessageExists: "新しいメッセージがきたで"
|
||||
onlyOneFileCanBeAttached: "すまん、メッセージに添付できるファイルはひとつだけなんや。"
|
||||
invitations: "来てや"
|
||||
invitationCode: "招待コード"
|
||||
checking: "確認しとるで"
|
||||
smtpHost: "ホスト"
|
||||
smtpUser: "ユーザー名"
|
||||
smtpPass: "パスワード"
|
||||
_mfm:
|
||||
mention: "メンション"
|
||||
quote: "引用"
|
||||
emoji: "カスタム絵文字"
|
||||
search: "探す"
|
||||
_sidebar:
|
||||
icon: "アイコン"
|
||||
_theme:
|
||||
keys:
|
||||
mention: "メンション"
|
||||
renote: "Renote"
|
||||
_sfx:
|
||||
note: "ノート"
|
||||
@ -441,6 +508,7 @@ _notification:
|
||||
youWereFollowed: "フォローされたで"
|
||||
_types:
|
||||
follow: "フォロー"
|
||||
mention: "メンション"
|
||||
renote: "Renote"
|
||||
quote: "引用"
|
||||
reaction: "リアクション"
|
||||
|
@ -35,6 +35,9 @@ userList: "Tibdarin"
|
||||
uiLanguage: "Tutlayt n wegrudem"
|
||||
smtpUser: "Isem n umseqdac"
|
||||
smtpPass: "Awal uffir"
|
||||
_mfm:
|
||||
mention: "Bder"
|
||||
search: "Nadi"
|
||||
_theme:
|
||||
keys:
|
||||
mention: "Bder"
|
||||
|
@ -56,6 +56,8 @@ instances: "ನಿದರ್ಶನ"
|
||||
remove: "ಅಳಿಸು"
|
||||
smtpUser: "ಬಳಕೆಹೆಸರು"
|
||||
smtpPass: "ಗುಪ್ತಪದ"
|
||||
_mfm:
|
||||
search: "ಹುಡುಕು"
|
||||
_sfx:
|
||||
notification: "ಅಧಿಸೂಚನೆಗಳು"
|
||||
_widgets:
|
||||
|
@ -362,8 +362,6 @@ unregister: "등록 해제"
|
||||
passwordLessLogin: "비밀번호 없이 로그인"
|
||||
resetPassword: "비밀번호 재설정"
|
||||
newPasswordIs: "새로운 비밀번호는 \"{password}\" 입니다"
|
||||
autoNoteWatch: "노트를 자동으로 지켜보기"
|
||||
autoNoteWatchDescription: "리액션하거나 답글을 남긴 다른 유저의 노트에 대한 알림을 받습니다."
|
||||
reduceUiAnimation: "UI의 애니메이션을 줄이기"
|
||||
share: "공유"
|
||||
notFound: "찾을 수 없습니다"
|
||||
@ -550,6 +548,14 @@ logs: "로그"
|
||||
database: "데이터베이스"
|
||||
channel: "채널"
|
||||
random: "랜덤"
|
||||
_mfm:
|
||||
mention: "멘션"
|
||||
hashtag: "해시태그"
|
||||
link: "링크"
|
||||
center: "가운데 정렬"
|
||||
quote: "인용"
|
||||
emoji: "커스텀 이모지"
|
||||
search: "검색"
|
||||
_reversi:
|
||||
total: "합계"
|
||||
_channel:
|
||||
|
@ -10,6 +10,8 @@ cancel: "Anuluj"
|
||||
enterUsername: "Wprowadź nazwę użytkownika"
|
||||
smtpUser: "Nazwa użytkownika"
|
||||
smtpPass: "Hasło"
|
||||
_mfm:
|
||||
search: "Szukaj"
|
||||
_sfx:
|
||||
notification: "Powiadomienia"
|
||||
_widgets:
|
||||
|
@ -6,12 +6,12 @@ search: "Поиск"
|
||||
notifications: "Уведомления"
|
||||
username: "Имя пользователя"
|
||||
password: "Пароль"
|
||||
fetchingAsApObject: "Приём с других сайтов…"
|
||||
ok: "Согласен"
|
||||
gotIt: "Понятно!"
|
||||
fetchingAsApObject: "Приём с других сайтов"
|
||||
ok: "Окей"
|
||||
gotIt: "Ясно!"
|
||||
cancel: "Отмена"
|
||||
enterUsername: "Введите имя пользователя"
|
||||
renotedBy: "{user} передаёт…"
|
||||
renotedBy: "{user} делится"
|
||||
noNotes: "Нет ни одной заметки"
|
||||
noNotifications: "Нет ни одного уведомления"
|
||||
instance: "Инстанс"
|
||||
@ -30,16 +30,16 @@ uploading: "Загрузка..."
|
||||
save: "Сохранить"
|
||||
users: "Пользователи"
|
||||
addUser: "Добавить пользователя"
|
||||
favorite: "Избранное"
|
||||
favorite: "В избранное"
|
||||
favorites: "Избранное"
|
||||
unfavorite: "Убрать из избранных"
|
||||
unfavorite: "Убрать из избранного"
|
||||
pin: "Закрепить в профиле"
|
||||
unpin: "Открепить от профиля"
|
||||
copyContent: "Скопировать содержимое"
|
||||
copyLink: "Скопировать ссылку"
|
||||
delete: "Удалить"
|
||||
deleteAndEdit: "Удалить и отредактировать"
|
||||
deleteAndEditConfirm: "Удалить этот пост и создать отредактированный? Все реакции, ссылки и ответы на существующий будут будут потеряны."
|
||||
deleteAndEditConfirm: "Удалить эту заметку и создать отредактированную? Все реакции, ссылки и ответы на существующую будут будут потеряны."
|
||||
addToList: "Добавить в список"
|
||||
sendMessage: "Отправить сообщение"
|
||||
copyUsername: "Скопировать имя пользователя"
|
||||
@ -51,19 +51,19 @@ receiveFollowRequest: "Получен запрос на подписку"
|
||||
followRequestAccepted: "Запрос на подписку принят"
|
||||
mention: "Упоминание"
|
||||
mentions: "Упоминания"
|
||||
directNotes: "Прямые сообщения"
|
||||
importAndExport: "Импорт / Экспорт"
|
||||
directNotes: "Личные сообщения"
|
||||
importAndExport: "Импорт и экспорт"
|
||||
import: "Импорт"
|
||||
export: "Экспорт"
|
||||
files: "Файлы"
|
||||
download: "Скачать"
|
||||
driveFileDeleteConfirm: "Удалить файл {name} ? Посты с ним также будут удалены"
|
||||
unfollowConfirm: "Удалить из подписок {name}?"
|
||||
driveFileDeleteConfirm: "Удалить файл «{name}»? Заметки с ним также будут удалены."
|
||||
unfollowConfirm: "Удалить из подписок пользователя {name}?"
|
||||
exportRequested: "Вы запросили экспорт. Это может занять некоторое время. Результат будет добавлен на «Диск»."
|
||||
importRequested: "Вы запросили импорт. Это может занять некоторое время."
|
||||
lists: "Списки"
|
||||
noLists: "Нет ни одного списка"
|
||||
note: "Пост"
|
||||
note: "Заметка"
|
||||
notes: "Заметки"
|
||||
following: "Подписки"
|
||||
followers: "Подписчики"
|
||||
@ -75,10 +75,10 @@ somethingHappened: "Что-то пошло не так"
|
||||
retry: "Повторить попытку"
|
||||
pageLoadError: "Не удалось загрузить страницу"
|
||||
pageLoadErrorDescription: "Обычно это случается из-за сбоев в сети или кэша браузера. Попробуйте очистить кэш, или подождать пару минут, а потом попытаться загрузить страницу снова."
|
||||
enterListName: "Введите имя списка"
|
||||
enterListName: "Название списка"
|
||||
privacy: "Конфиденциальность"
|
||||
makeFollowManuallyApprove: "Принимать подписчиков вручную"
|
||||
defaultNoteVisibility: "Видимость постов по умолчанию"
|
||||
defaultNoteVisibility: "Видимость заметок по умолчанию"
|
||||
follow: "Подписка"
|
||||
followRequest: "Запрос на подписку"
|
||||
followRequests: "Запросы на подписку"
|
||||
@ -94,8 +94,8 @@ clickToShow: "Нажмите для просмотра"
|
||||
sensitive: "Содержимое не для всех"
|
||||
add: "Добавить"
|
||||
reaction: "Реакции"
|
||||
reactionSettingDescription: "Выберите, что показывать в палитре реакций"
|
||||
rememberNoteVisibility: "Запоминать видимость поста"
|
||||
reactionSettingDescription: "Подберите, что будет у вас в палитре реакций"
|
||||
rememberNoteVisibility: "Запоминать видимость заметок"
|
||||
attachCancel: "Удалить вложение"
|
||||
markAsSensitive: "Отметить как «не для всех»"
|
||||
unmarkAsSensitive: "Снять отметку «не для всех»"
|
||||
@ -113,7 +113,7 @@ unsuspendConfirm: "Разморозить этот аккаунт?"
|
||||
selectList: "Выберите список"
|
||||
selectAntenna: "Выберите антенну"
|
||||
selectWidget: "Выберите виджет"
|
||||
editWidgets: "Редактировать виджет"
|
||||
editWidgets: "Редактировать виджеты"
|
||||
editWidgetsExit: "Готово"
|
||||
customEmojis: "Эмодзи пользователя"
|
||||
emoji: "Эмодзи"
|
||||
@ -128,13 +128,13 @@ flagAsCat: "Аккаунт кота"
|
||||
autoAcceptFollowed: "Принимать подписчиков автоматически"
|
||||
addAcount: "Добавить аккаунт"
|
||||
loginFailed: "Неудачная попытка входа"
|
||||
showOnRemote: "Перейти к оригиналу на его сайт"
|
||||
showOnRemote: "Перейти к оригиналу на сайт"
|
||||
general: "Общее"
|
||||
wallpaper: "Обои"
|
||||
setWallpaper: "Установить обои"
|
||||
removeWallpaper: "Удалить обои"
|
||||
searchWith: "Искать в {q}"
|
||||
youHaveNoLists: "У вас нет списков"
|
||||
searchWith: "Найденное «{q}»"
|
||||
youHaveNoLists: "У вас нет ни одного списка"
|
||||
followConfirm: "Подписаться на {name}?"
|
||||
proxyAccount: "Учётная запись прокси"
|
||||
proxyAccountDescription: "Учетная запись прокси предназначена служить подписчиком на пользователей с других сайтов. Например, если пользователь добавит кого-то с другого сайта а список, деятельность того не отобразится, пока никто с этого же сайта не подписан на него. Чтобы это стало возможным, на него подписывается прокси."
|
||||
@ -150,15 +150,15 @@ latestRequestReceivedAt: "Последний полученный запрос"
|
||||
latestStatus: "Последний статус"
|
||||
storageUsage: "Использовано"
|
||||
charts: "Диаграммы"
|
||||
perHour: "Каждый час"
|
||||
perDay: "Каждый день"
|
||||
perHour: "По часам"
|
||||
perDay: "По дням"
|
||||
stopActivityDelivery: "Остановить отправку обновлений активности"
|
||||
blockThisInstance: "Блокировать этот инстанс"
|
||||
operations: "Операции"
|
||||
software: "Программы"
|
||||
version: "Версия"
|
||||
metadata: "Метаданные"
|
||||
withNFiles: "файлов: {n}"
|
||||
withNFiles: "Файлы, {n} шт."
|
||||
monitor: "Монитор"
|
||||
jobQueue: "Очередь заданий"
|
||||
cpuAndMemory: "Процессор и память"
|
||||
@ -189,7 +189,7 @@ noCustomEmojis: "Эмодзи пользователя отсутствуют"
|
||||
noJobs: "Нет заданий"
|
||||
federating: "Федерируется"
|
||||
blocked: "Заблокировано"
|
||||
suspended: "Приостановленный"
|
||||
suspended: "Заморожено"
|
||||
all: "Всё"
|
||||
subscribing: "Подписка"
|
||||
publishing: "Публикация"
|
||||
@ -202,7 +202,7 @@ security: "Безопасность"
|
||||
retypedNotMatch: "Не совпадают"
|
||||
currentPassword: "Текущий пароль"
|
||||
newPassword: "Новый пароль"
|
||||
newPasswordRetype: "Новый пароль (повторно)"
|
||||
newPasswordRetype: "Новый пароль (ещё раз)"
|
||||
attachFile: "Прикрепить файлы"
|
||||
more: "Ещё!"
|
||||
featured: "Подборка"
|
||||
@ -225,7 +225,7 @@ uploadFromUrlRequested: "Загрузка выбранного"
|
||||
uploadFromUrlMayTakeTime: "Загрузка может занять некоторое время."
|
||||
explore: "Обзор"
|
||||
games: "Игры Misskey"
|
||||
messageRead: "Прочитанных"
|
||||
messageRead: "Прочитали"
|
||||
noMoreHistory: "История закончилась"
|
||||
startMessaging: "Отправить сообщение"
|
||||
nUsersRead: "Прочитали {n}"
|
||||
@ -237,7 +237,7 @@ remoteUserCaution: "Это пользователь с другого сайта
|
||||
activity: "Активность"
|
||||
images: "Изображения"
|
||||
birthday: "День рождения"
|
||||
yearsOld: "{age} лет"
|
||||
yearsOld: "Возраст: {age}"
|
||||
registeredDate: "Дата регистрации"
|
||||
location: "Местоположение"
|
||||
theme: "Тема"
|
||||
@ -250,10 +250,10 @@ darkThemes: "Тёмные темы"
|
||||
syncDeviceDarkMode: "Синхронизировать с темным режимом устройства"
|
||||
drive: "Диск"
|
||||
fileName: "Имя файла"
|
||||
selectFile: "Выберите Файл"
|
||||
selectFiles: "Выберите Файл"
|
||||
selectFile: "Выберите файл"
|
||||
selectFiles: "Выберите файлы"
|
||||
selectFolder: "Выберите папку"
|
||||
selectFolders: "Выберите папку"
|
||||
selectFolders: "Выберите папки"
|
||||
renameFile: "Переименовать файл"
|
||||
folderName: "Имя папки"
|
||||
createFolder: "Создать папку"
|
||||
@ -265,51 +265,51 @@ emptyFolder: "Папка пуста"
|
||||
unableToDelete: "Удаление невозможно"
|
||||
inputNewFileName: "Введите имя нового файла"
|
||||
inputNewFolderName: "Пожалуйста, введите новое имя папки!"
|
||||
circularReferenceFolder: "Конечная папка - это вложенная папка, которую вы хотите переместить."
|
||||
circularReferenceFolder: "Вы пытаетесь переместить папку внутрь себя."
|
||||
hasChildFilesOrFolders: "Эта папка не пуста и не может быть удалена."
|
||||
copyUrl: "Копировать URL"
|
||||
copyUrl: "Копировать ссылку"
|
||||
rename: "Переименовать"
|
||||
avatar: "Иконка"
|
||||
banner: "Баннер"
|
||||
avatar: "Аватар"
|
||||
banner: "Шапка"
|
||||
nsfw: "Содержимое не для всех"
|
||||
whenServerDisconnected: "Когда соединение с сервером потеряно"
|
||||
disconnectedFromServer: "Разорвано соединение с сервером"
|
||||
reload: "Перезагрузить"
|
||||
doNothing: "Ничего не делать"
|
||||
reloadConfirm: "Подтвердить перезагрузку?"
|
||||
reloadConfirm: "Перезагрузить ленту?"
|
||||
watch: "Следить"
|
||||
unwatch: "Отписаться"
|
||||
accept: "Принять"
|
||||
reject: "Отклонить"
|
||||
normal: "Стабильно"
|
||||
instanceName: "Имя экземпляра"
|
||||
instanceName: "Название инстанса"
|
||||
instanceDescription: "Описание инстанса"
|
||||
maintainerName: "Имя администратора"
|
||||
maintainerEmail: "email администратора"
|
||||
tosUrl: "Пользовательское соглашение URL"
|
||||
thisYear: "Текущий год"
|
||||
thisMonth: "Текущий месяц"
|
||||
today: "Сегодня"
|
||||
dayX: "{day}дней"
|
||||
monthX: "{month}месяц"
|
||||
yearX: "{year}год"
|
||||
pages: "Страница"
|
||||
integration: "подключение"
|
||||
maintainerEmail: "Электронная почта администратора"
|
||||
tosUrl: "Ссылка на пользовательское соглашение"
|
||||
thisYear: "Этот год"
|
||||
thisMonth: "Этот месяц"
|
||||
today: "Этот день"
|
||||
dayX: "{day} день"
|
||||
monthX: "{month} месяц"
|
||||
yearX: "{year} год"
|
||||
pages: "Страницы"
|
||||
integration: "Интеграция"
|
||||
connectSerice: "Соединение"
|
||||
disconnectSerice: "Отключение"
|
||||
enableLocalTimeline: "Включить локальную ленту"
|
||||
enableGlobalTimeline: "Включить глобальную ленту"
|
||||
disablingTimelinesInfo: "Администраторы и Модераторы всегда будут иметь доступ ко всем временным параметрам, даже если они не включены."
|
||||
disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам, даже если они отключены."
|
||||
registration: "Регистрация"
|
||||
enableRegistration: "Разрешить регистрацию"
|
||||
invite: "Пригласить"
|
||||
proxyRemoteFiles: "Удаленные файлы прокси"
|
||||
proxyRemoteFilesDescription: "Если эта функция включена, удаленные файлы, которые (1) не хранятся локально или (2) были удалены с превышением лимита хранения, будут проксированы локально (с эскизами). Это не влияет на хранение на сервере."
|
||||
driveCapacityPerLocalAccount: "Емкость диска для локального пользователя"
|
||||
driveCapacityPerRemoteAccount: "Емкость диска для удаленного пользователя"
|
||||
proxyRemoteFiles: "Файлы с других сайтов пускать через прокси"
|
||||
proxyRemoteFilesDescription: "Когда эта настройка включена, файлы с других серверов, которые не сохранены или удалены для освобождения места, будут проксироваться локально, а так же для них будут создаваться миниатюры. Эта настройка не затрагивает хранение на сервере."
|
||||
driveCapacityPerLocalAccount: "Объём диска на одного локального пользователя"
|
||||
driveCapacityPerRemoteAccount: "Объём диска на одного пользователя с другого сайта"
|
||||
inMb: "В мегабайтах"
|
||||
iconUrl: "URL-адрес иконки"
|
||||
bannerUrl: "URL-адрес изображения баннера"
|
||||
iconUrl: "Ссылка на аватар"
|
||||
bannerUrl: "Ссылка на изображение в шапке"
|
||||
basicInfo: "Общая информация"
|
||||
pinnedUsers: "Прикреплённый пользователь"
|
||||
pinnedUsersDescription: "Перечислите по одному имени пользователя в строке. Пользователи, перечисленные здесь, будут привязаны к закладке \"Изучение\"."
|
||||
@ -321,24 +321,24 @@ recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "Включить reCAPTCHA"
|
||||
recaptchaSiteKey: "Ключ сайта"
|
||||
recaptchaSecretKey: "Секретный ключ"
|
||||
avoidMultiCaptchaConfirm: "Использование нескольких Captchas может вызвать помехи. Хотите отключить другую Captcha? Вы можете оставить несколько Captchas включенными, нажав \"Отмена\"."
|
||||
avoidMultiCaptchaConfirm: "Несколько способов проверки могут мешать друг другу. Подтвердите, если хотите отключить другие способы. Или нажмите «Отмена», чтобы оставить их включёнными."
|
||||
antennas: "Антенны"
|
||||
manageAntennas: "Настройки антенн"
|
||||
name: "Имя"
|
||||
name: "Название"
|
||||
antennaSource: "Источник антенны"
|
||||
antennaKeywords: "Ключевые слова"
|
||||
antennaExcludeKeywords: "Исключения"
|
||||
antennaKeywordsDescription: "Разделяйте пробелами для условия \"И\". Разделяйте переводом строки для \"ИЛИ\"."
|
||||
notifyAntenna: "Уведомлять о новых записях"
|
||||
withFileAntenna: "Включать только заметки с вложениями"
|
||||
antennaKeywordsDescription: "Пишите слова через пробел в одной строке, чтобы ловить их появление вместе; на отдельных строках располагайте слова, или группы слов, чтобы ловить любые из них."
|
||||
notifyAntenna: "Уведомлять о новых заметках"
|
||||
withFileAntenna: "Только заметки с вложениями"
|
||||
serviceworker: "ServiceWorker"
|
||||
enableServiceworker: "Включить ServiceWorker"
|
||||
antennaUsersDescription: "Располагать каждое имя с новой строки"
|
||||
antennaUsersDescription: "Пишите каждое название аккаута на отдельной строке"
|
||||
caseSensitive: "С учётом регистра"
|
||||
withReplies: "Включить ответы"
|
||||
withReplies: "Включая ответы"
|
||||
connectedTo: "Вы подключены к следующим аккаунтам"
|
||||
notesAndReplies: "Посты и ответы"
|
||||
withFiles: "с файлами"
|
||||
notesAndReplies: "Заметки и ответы"
|
||||
withFiles: "Заметки с файлами"
|
||||
silence: "Заглушить"
|
||||
silenceConfirm: " Заглушить этого пользователя? Уверены?"
|
||||
unsilence: "Снять глушение"
|
||||
@ -364,7 +364,7 @@ administrator: "Администратор"
|
||||
token: "Токен"
|
||||
twoStepAuthentication: "Двухфакторная аутентификация"
|
||||
moderator: "Модератор"
|
||||
nUsersMentioned: "{n}указанные пользователи"
|
||||
nUsersMentioned: "Упомянуло пользователей: {n}"
|
||||
securityKey: "Ключ безопасности"
|
||||
securityKeyName: "Имя ключа"
|
||||
registerSecurityKey: "Зарегистрировать защитный ключ"
|
||||
@ -372,18 +372,16 @@ lastUsed: "Последнее использование"
|
||||
unregister: "Отписаться"
|
||||
passwordLessLogin: "Настроить вход без пароля"
|
||||
resetPassword: "Сброс пароля:"
|
||||
newPasswordIs: "Новый пароль - \"{пароль}\"."
|
||||
autoNoteWatch: "Автоматически просматривать записи"
|
||||
autoNoteWatchDescription: "Получать уведомления о заметках других пользователей, на которые вы отреагировали или на которые вы ответили."
|
||||
reduceUiAnimation: "Уменьшить анимацию в пользовательском интерфейсе."
|
||||
newPasswordIs: "Новый пароль — «{password}»."
|
||||
reduceUiAnimation: "Уменьшить анимацию в пользовательском интерфейсе"
|
||||
share: "Поделиться"
|
||||
notFound: "Не найдено"
|
||||
notFoundDescription: "Страница, соответствующая указанному URL, не найдена."
|
||||
notFoundDescription: "Страница по указанной ссылке не найдена"
|
||||
uploadFolder: "Место загрузки по умолчанию"
|
||||
cacheClear: "Очистка кэша"
|
||||
markAsReadAllNotifications: "Отметить все уведомления как прочитанные"
|
||||
markAsReadAllUnreadNotes: "Отметьте все сообщения как прочитанные."
|
||||
markAsReadAllTalkMessages: "Отметьте все сообщения как прочитанные"
|
||||
markAsReadAllUnreadNotes: "Отметить все заметки как прочитанные"
|
||||
markAsReadAllTalkMessages: "Отметить все реплики как прочитанные"
|
||||
help: "Помощь"
|
||||
inputMessageHere: "Введите сообщение здесь"
|
||||
close: "Закрыть"
|
||||
@ -392,71 +390,71 @@ groups: "Группы"
|
||||
createGroup: "Создать группу"
|
||||
ownedGroups: "Собственные группы"
|
||||
joinedGroups: "Участие в группах"
|
||||
invites: "Пригласить"
|
||||
invites: "Приглашения"
|
||||
groupName: "Название группы"
|
||||
members: "Участники"
|
||||
transfer: "Перенос"
|
||||
transfer: "Отдать"
|
||||
messagingWithUser: "Сообщения пользователей"
|
||||
messagingWithGroup: "Чат в группе"
|
||||
title: "Заголовок."
|
||||
title: "Заголовок"
|
||||
text: "Текст"
|
||||
enable: "Включить."
|
||||
enable: "Включить"
|
||||
next: "Дальше"
|
||||
retype: "Введите повторно"
|
||||
noteOf: "Посты {user}"
|
||||
retype: "Введите ещё раз"
|
||||
noteOf: "Что пишет {user}"
|
||||
inviteToGroup: "Пригласить в группу"
|
||||
maxNoteTextLength: "Максимальная длина текста"
|
||||
quoteAttached: "Цитата"
|
||||
quoteQuestion: "Хочешь добавить цитату?"
|
||||
noMessagesYet: "Сообщений нет"
|
||||
quoteQuestion: "Хотите добавить цитату?"
|
||||
noMessagesYet: "Пока ни одного сообщения"
|
||||
newMessageExists: "Новое сообщение"
|
||||
onlyOneFileCanBeAttached: "К сообщению можно прикрепить только один файл."
|
||||
signinRequired: "Пожалуйста, войдите."
|
||||
invitations: "Пригласить"
|
||||
onlyOneFileCanBeAttached: "К сообщению можно прикрепить только один файл"
|
||||
signinRequired: "Пожалуйста, войдите"
|
||||
invitations: "Приглашения"
|
||||
invitationCode: "Код приглашения"
|
||||
checking: "Проверка"
|
||||
available: "Доступен"
|
||||
available: "Доступно"
|
||||
unavailable: "Не доступно"
|
||||
usernameInvalidFormat: "Вы можете использовать a-z, A-Z, 0-9 и _"
|
||||
usernameInvalidFormat: "Можно использовать только латинские буквы (A—Z, a—z), цифры (0—9) и знак подчёркивания (_)"
|
||||
tooShort: "Слишком короткий"
|
||||
tooLong: "Слишком длинный"
|
||||
weakPassword: "Слабый пароль"
|
||||
normalPassword: "Обычный пароль"
|
||||
normalPassword: "Годный пароль"
|
||||
strongPassword: "Надёжный пароль"
|
||||
passwordMatched: "Подходящий пароль"
|
||||
passwordNotMatched: "Пароль не совпадает"
|
||||
signinWith: "Войти в систему с помощью {x}"
|
||||
passwordMatched: "Совпали"
|
||||
passwordNotMatched: "Не совпадают"
|
||||
signinWith: "Использовать {x} для входа"
|
||||
signinFailed: "Невозможно войти в систему. Введенное вами имя пользователя или пароль неверны."
|
||||
tapSecurityKey: "Нажмите на свой электронный ключ"
|
||||
or: "или"
|
||||
uiLanguage: "Язык интерфейса"
|
||||
groupInvited: "Приглашение в группу"
|
||||
aboutX: "Описание {x}"
|
||||
useOsNativeEmojis: "Используйте родной для ОС Emojis"
|
||||
youHaveNoGroups: "Группы не найдены"
|
||||
joinOrCreateGroup: "Получите приглашение присоединиться к группам или вы можете создать свою собственную группу."
|
||||
noHistory: "Ничего не найдено"
|
||||
useOsNativeEmojis: "Использовать эмодзи операционной системы"
|
||||
youHaveNoGroups: "У вас нет ни одной группы"
|
||||
joinOrCreateGroup: "Получайте приглашения в группы или создавайте свои собственные"
|
||||
noHistory: "История пока пуста"
|
||||
disableAnimatedMfm: "Отключение анимированной разметки MFM"
|
||||
doing: "В пути"
|
||||
doing: "В процессе"
|
||||
category: "Категория"
|
||||
tags: "Теги"
|
||||
tags: "Метки"
|
||||
docSource: "Источник документа"
|
||||
createAccount: "Зарегистрироваться"
|
||||
existingAcount: "У вас уже есть учетная запись?"
|
||||
regenerate: "Восстановить"
|
||||
createAccount: "Новый аккаунт"
|
||||
existingAcount: "Уже существующий"
|
||||
regenerate: "Создать повторно"
|
||||
fontSize: "Размер шрифта"
|
||||
noFollowRequests: "У вас нет никаких ожидающих ответа запросов"
|
||||
noFollowRequests: "Нерассмотренные запросы на подписку отсутствуют"
|
||||
openImageInNewTab: "Открыть изображение в новой вкладке"
|
||||
dashboard: "Панель управления"
|
||||
local: "Локальный"
|
||||
remote: "Удаленный"
|
||||
local: "С этого сайта"
|
||||
remote: "С других сайтов"
|
||||
total: "Всего"
|
||||
weekOverWeekChanges: "Еженедельно"
|
||||
dayOverDayChanges: "Ежедневно"
|
||||
weekOverWeekChanges: "За неделю"
|
||||
dayOverDayChanges: "За день"
|
||||
appearance: "Внешний вид"
|
||||
clientSettings: "Настройки клиента"
|
||||
accountSettings: "Настройки учетной записи"
|
||||
promotion: "Опубликовано"
|
||||
promotion: "Продвинуто"
|
||||
promote: "Продвинуть"
|
||||
numberOfDays: "Количество дней"
|
||||
hideThisNote: "Спрятать эту запись"
|
||||
@ -480,8 +478,8 @@ objectStorageUseProxyDesc: "Отключите, если не будете ис
|
||||
objectStorageSetPublicRead: "Устанавливать public-read при загрузке на сервер"
|
||||
serverLogs: "Журнал сервера"
|
||||
deleteAll: "Удалить всё"
|
||||
showFixedPostForm: "Показывать поле для ввода новой заметки наверху ленты."
|
||||
newNoteRecived: "Есть новые посты"
|
||||
showFixedPostForm: "Показывать поле для ввода новой заметки наверху ленты"
|
||||
newNoteRecived: "Появилась новая заметка"
|
||||
sounds: "Звуки"
|
||||
listen: "Слушать"
|
||||
none: "Ничего"
|
||||
@ -490,23 +488,23 @@ popout: "Развернуть"
|
||||
volume: "Громкость"
|
||||
details: "Подробнее"
|
||||
chooseEmoji: "Выберите эмодзи"
|
||||
unableToProcess: "Я не могу завершить операцию."
|
||||
unableToProcess: "Не удаётся завершить операцию"
|
||||
recentUsed: "Последние использованные"
|
||||
install: "Установить"
|
||||
uninstall: "Удалить"
|
||||
installedApps: "Установленные приложения"
|
||||
nothing: "Ничего не найдено"
|
||||
nothing: "Ничего нет"
|
||||
installedDate: "Дата установки"
|
||||
lastUsedDate: "Дата использования"
|
||||
state: "Состояние"
|
||||
sort: "Сортировать"
|
||||
ascendingOrder: "по возрастанию"
|
||||
descendingOrder: "По убыванию"
|
||||
scratchpad: "Редактор "
|
||||
scratchpadDescription: "Scratchpad предоставляет экспериментальную среду для AiScript, позволяющую писать, запускать и проверять результаты кода, взаимодействующего с Misskey."
|
||||
scratchpad: "Когтеточка"
|
||||
scratchpadDescription: "«Когтеточка» — это место для опытов с AiScript. Здесь можно писать программы, взаимодействующие с Misskey, запускать и смотреть что из этого получается."
|
||||
output: "Выходы"
|
||||
script: "Скрипт"
|
||||
disablePagesScript: "Отключение скриптов в Pages"
|
||||
disablePagesScript: "Отключить скрипты на «Страницах»"
|
||||
updateRemoteUser: "Обновить данные пользователя с его сервера"
|
||||
deleteAllFiles: "Удалить все файлы"
|
||||
deleteAllFilesConfirm: "Вы хотите удалить все файлы?"
|
||||
@ -515,9 +513,9 @@ removeAllFollowingDescription: "Отменить все подписки с до
|
||||
userSuspended: "Этот пользователь был заморожен"
|
||||
userSilenced: "Этот пользователь был заглушен"
|
||||
sidebar: "Боковая панель"
|
||||
divider: "Разделительная полоса"
|
||||
divider: "Линия-разделитель"
|
||||
addItem: "Добавить элемент"
|
||||
rooms: "Комнаты"
|
||||
rooms: "Комната"
|
||||
relays: "Ретрансляторы"
|
||||
addRelay: "Добавить ретранслятор"
|
||||
inboxUrl: "URL ящика входящих сообщений"
|
||||
@ -529,19 +527,20 @@ enableInfiniteScroll: "Включить бесконечную прокрутк
|
||||
visibility: "Видимость"
|
||||
poll: "Опрос"
|
||||
useCw: "Скрывать содержимое под предупреждением"
|
||||
enablePlayer: "Включить плеер"
|
||||
disablePlayer: "Выключить плеер"
|
||||
expandTweet: "Разавернуть твит"
|
||||
themeEditor: "Редактор темы"
|
||||
enablePlayer: "Включить проигрыватель"
|
||||
disablePlayer: "Выключить проигрыватель"
|
||||
expandTweet: "Развернуть твит"
|
||||
themeEditor: "Редактор темы оформления"
|
||||
description: "Описание"
|
||||
author: "Автор"
|
||||
leaveConfirm: "Вы не сохранили изменения. Хотите выйти и потерять их?"
|
||||
manage: "Управление"
|
||||
plugins: "Плагины"
|
||||
pluginInstallWarn: "Пожалуста, не устанавливайте плагины, которым не доверяете."
|
||||
plugins: "Расширения"
|
||||
pluginInstallWarn: "Пожалуста, не устанавливайте расширения, которым не доверяете"
|
||||
deck: "Пульт"
|
||||
undeck: "Покинуть пульт"
|
||||
useBlurEffectForModal: "Размывка под формой поверх всего"
|
||||
useFullReactionPicker: "Полнофункциональный выбор реакций"
|
||||
generateAccessToken: "Создать токен доступа"
|
||||
permission: "Разрешения"
|
||||
enableAll: "Включить все"
|
||||
@ -552,55 +551,143 @@ notificationType: "Тип уведомления"
|
||||
edit: "Изменить"
|
||||
useStarForReactionFallback: "Ставить ★ в качестве реакции вместо неизвестного эмодзи"
|
||||
emailConfig: "Настройки почтового сервера"
|
||||
enableEmail: "Активировать функцию доставки электронной почты"
|
||||
emailConfigInfo: "Он используется для подтверждения адреса электронной почты и сброса пароля."
|
||||
email: "email"
|
||||
enableEmail: "Включить обмен электронной почтой"
|
||||
emailConfigInfo: "Используется для подтверждения адреса электронной почты и сброса пароля."
|
||||
email: "Адрес электронной почты"
|
||||
smtpConfig: "Конфигурация SMTP-сервера"
|
||||
smtpHost: "Хост"
|
||||
smtpPort: "smtp порт"
|
||||
smtpPort: "Порт"
|
||||
smtpUser: "Имя пользователя"
|
||||
smtpPass: "Пароль"
|
||||
emptyToDisableSmtpAuth: "Вы можете отключить SMTP аутентификацию, оставив ваше имя пользователя и пароль пустыми"
|
||||
emptyToDisableSmtpAuth: "Не заполняйте имя пользователя и пароль, чтобы отключить аутентификацию в SMTP."
|
||||
smtpSecure: "Использовать SSL/TLS для SMTP-соединений"
|
||||
smtpSecureInfo: "Выключите его при использовании STARTTLS."
|
||||
testEmail: "Проверка Email"
|
||||
smtpSecureInfo: "Выключите при использовании STARTTLS."
|
||||
testEmail: "Проверка доставки электронной почты"
|
||||
wordMute: "Скрытие слов"
|
||||
userSaysSomething: "{name} что-то сообщает"
|
||||
makeActive: "Активировать"
|
||||
display: "Показать"
|
||||
display: "Отображение"
|
||||
copy: "Копировать"
|
||||
metrics: "Метрика"
|
||||
metrics: "Метрики"
|
||||
overview: "Обзор"
|
||||
logs: "Лог-файлы"
|
||||
logs: "Журналы"
|
||||
delayed: "Задержка"
|
||||
database: "База данных"
|
||||
channel: "каналы"
|
||||
channel: "Каналы"
|
||||
create: "Создать"
|
||||
notificationSetting: "Настройки уведомлений"
|
||||
notificationSettingDesc: "Выберите тип уведомлений для отображения"
|
||||
useGlobalSetting: "Использовать глобальные настройки"
|
||||
useGlobalSettingDesc: "Использовать глобальные настройки"
|
||||
useGlobalSettingDesc: "Если включено, будут использоваться настройки учётной записи. Если включить, этот виджет можно будет настроить индивидуально."
|
||||
other: "Другие"
|
||||
regenerateLoginToken: "Создать новый токен для входа"
|
||||
regenerateLoginTokenDescription: "Создаёт новый токен, используемый внутри программы во время входа. Обычно в этом нет необходимости. При создании все устройства будут отключены."
|
||||
setMultipleBySeparatingWithSpace: "Вы можете установить несколько, разделив их пробелами."
|
||||
fileIdOrUrl: "ID файла или URL-адрес"
|
||||
setMultipleBySeparatingWithSpace: "Можно написать несколько через пробел"
|
||||
fileIdOrUrl: "Идентификатор файла или ссылка"
|
||||
chatOpenBehavior: "Поведение окна чата при открытии"
|
||||
sample: "Пример"
|
||||
abuseReports: "Отчеты"
|
||||
reportAbuse: "Отчеты"
|
||||
abuseReports: "Жалобы"
|
||||
reportAbuse: "Жалоба"
|
||||
reportAbuseOf: "Пожаловаться на пользователя {name}"
|
||||
fillAbuseReportDescription: "Пожалуйста, заполните данные отчета. Если речь идет о конкретном сообщении, пожалуйста, укажите его URL."
|
||||
abuseReported: "Содержимое отправлено. Спасибо за сообщение"
|
||||
fillAbuseReportDescription: "Опишите, пожалуйста, причину жалобы подробнее. Если речь о конкретной заметке, будьте добры приложить ссылку на неё."
|
||||
abuseReported: "Жалоба отправлена. Большое спасибо за информацию."
|
||||
send: "Отправить"
|
||||
abuseMarkAsResolved: "Отметить отчет как решенный"
|
||||
abuseMarkAsResolved: "Отметить жалобу как решённую"
|
||||
openInNewTab: "Открыть в новой вкладке"
|
||||
openInSideView: "Открывать в боковой колонке"
|
||||
defaultNavigationBehaviour: "Поведение навигации по умолчанию"
|
||||
editTheseSettingsMayBreakAccount: "От изменений в этих настройках ваша учётная запись может поломаться."
|
||||
instanceTicker: "Строка с названием инстанса в заметках"
|
||||
waitingFor: "Ждём, когда {x} ответит"
|
||||
random: "Случайные"
|
||||
system: "Система"
|
||||
switchUi: "Выбор вида"
|
||||
desktop: "Стол"
|
||||
_mfm:
|
||||
cheatSheet: "Подсказка по разметке MFM"
|
||||
intro: "MFM — язык оформления текста, придуманный специально для Misskey, который здесь можно много где использовать. На этой странице собраны и кратко изложены способы его применения."
|
||||
dummy: "Misskey расширяет границы Федиверса."
|
||||
mention: "Упоминание"
|
||||
mentionDescription: "При помощи знака «собака» перед именем можно упомянуть какого-нибудь пользователя."
|
||||
hashtag: "Хэштег"
|
||||
hashtagDescription: "При помощи знака «решётка» перед словом задаётся хэштег."
|
||||
url: "Простая ссылка (URL)"
|
||||
urlDescription: "Ссылки могут отображаться непосредственно."
|
||||
link: "Ссылка с пояснением"
|
||||
linkDescription: "Можно ссылку оформить в виде произвольного текста."
|
||||
bold: "Жирный шрифт"
|
||||
boldDescription: "Выделяет текст, делая буквы жирнее."
|
||||
small: "Мелкий шрифт"
|
||||
smallDescription: "Делает текст маленьким и незаметным."
|
||||
center: "Выровнять элементы по центру"
|
||||
centerDescription: "Так можно выровнять что-то по центру."
|
||||
inlineCode: "Программа (в тексте)"
|
||||
inlineCodeDescription: "Подсвечивает фрагмент программы внутри сплошного текста."
|
||||
blockCode: "Программа (блок)"
|
||||
blockCodeDescription: "Оформляет текст программы в виде отдельного блокоа. Он может состоять из множества строк."
|
||||
inlineMath: "Математическое выражение (в тексте)"
|
||||
inlineMathDescription: "Позволяет вставлять математические выражения внутрь текста при помощи языка KaTeX."
|
||||
blockMath: "Математическое выражение (блок)"
|
||||
blockMathDescription: "Оформляет математическое выражение (KaTeX) на отдельной строке."
|
||||
quote: "Цитата"
|
||||
quoteDescription: "Так можно процитировать чей-то текст."
|
||||
emoji: "Эмодзи пользователя"
|
||||
emojiDescription: "Можно вставить эмодзи в текст, окружив название двоеточиями."
|
||||
search: "Поиск"
|
||||
searchDescription: "Можно добавить форму для поиска, сразу задав, что искать."
|
||||
flip: "Переворот"
|
||||
flipDescription: "Позволяет отразить текст зеркально по вертикали или горизонтали."
|
||||
jelly: "Анимация желе (шлёп-плёп)"
|
||||
jellyDescription: "Напоминает горку джема, дёргающуюся от шлепков."
|
||||
tada: "Анимация (та-дам!)"
|
||||
tadaDescription: "Получается нечто выпрыгивающее, как бы крича: «а вот и я!»"
|
||||
jump: "Анимация прыжков (прыг-скок)"
|
||||
jumpDescription: "Побуждает радостно подпрыгивать."
|
||||
bounce: "Анимация отскоков (бум-бум)"
|
||||
bounceDescription: "Это будет скакать как мяч."
|
||||
shake: "Анимация дрожи (б-р-р-р)"
|
||||
shakeDescription: "Такое дрожит, словно от холода. Или от страха."
|
||||
twitch: "Анимация тряски"
|
||||
twitchDescription: "Заставляет трястись как одержимого"
|
||||
spin: "Вращение"
|
||||
spinDescription: "Так можно крутить содержимое в разных направлениях."
|
||||
_reversi:
|
||||
reversi: "Реверси"
|
||||
gameSettings: "Настройки игры"
|
||||
chooseBoard: "Выберите доску"
|
||||
blackOrWhite: "Черные/Белые"
|
||||
blackIs: "{name} за чёрных"
|
||||
rules: "Правила"
|
||||
botSettings: "Настройки бота"
|
||||
thisGameIsStartedSoon: "Игра скоро начнётся."
|
||||
waitingForOther: "Ожидание соперника..."
|
||||
waitingForMe: "В ожидании, когда будете готовы."
|
||||
waitingBoth: "Приготовьтесь."
|
||||
ready: "Готово"
|
||||
cancelReady: "Возврат к подготовке"
|
||||
opponentTurn: "Ход соперника"
|
||||
myTurn: "Ваш ход"
|
||||
turnOf: "Ходит {name}."
|
||||
pastTurnOf: "Ходит {name}."
|
||||
surrender: "Сдаться"
|
||||
surrendered: "Противник сдался"
|
||||
drawn: "Ничья"
|
||||
won: "Победитель — {name}"
|
||||
black: "Чёрные"
|
||||
white: "Белые"
|
||||
total: "Всего"
|
||||
turnCount: "Ход {count}"
|
||||
myGames: "Сыгранное вами"
|
||||
allGames: "Все игры"
|
||||
ended: "Завершена"
|
||||
playing: "Идёт игра"
|
||||
isLlotheo: "Выигрывает меньшее число камней (LLoTheO)"
|
||||
loopedMap: "Замкнутая в кольцо доска"
|
||||
canPutEverywhere: "Камни можно ставить везде"
|
||||
_instanceTicker:
|
||||
none: "Не показывать"
|
||||
remote: "Только для других сайтов"
|
||||
always: "Показывать всегда"
|
||||
_serverDisconnectedBehavior:
|
||||
reload: "Автоматическая перезагрузка"
|
||||
dialog: "Предупреждение"
|
||||
@ -610,37 +697,37 @@ _channel:
|
||||
edit: "Редактировать канал"
|
||||
setBanner: "Установить баннер"
|
||||
removeBanner: "Удалить баннер"
|
||||
featured: "В тренде"
|
||||
featured: "Из подборки"
|
||||
owned: "Владелец"
|
||||
following: "Читаю"
|
||||
usersCount: "{n} Участники"
|
||||
notesCount: "{n} Записи"
|
||||
usersCount: "Участников: {n}"
|
||||
notesCount: "Заметок: {n}"
|
||||
_sidebar:
|
||||
full: "Полный"
|
||||
icon: "Иконка"
|
||||
full: "Полностью"
|
||||
icon: "Только значки"
|
||||
hide: "Спрятать"
|
||||
_wordMute:
|
||||
muteWords: "Скрыть слово"
|
||||
muteWordsDescription: "Разделяйте пробелами для условия \"И\". Разделяйте переводом строки для \"ИЛИ\"."
|
||||
muteWordsDescription2: "Округляйте ключевые слова слэшами для использования регулярных выражений."
|
||||
muteWordsDescription: "Пишите слова через пробел в одной строке, чтобы фильтровать их появление вместе; а если хотите фильтровать любое из них, пишите в отдельных строках."
|
||||
muteWordsDescription2: "Здесь можно использовать регулярные выражения — просто заключите их между двумя дробными чертами (/)."
|
||||
softDescription: "Соответствующие условиям заметки будут спрятаны из вашей ленты."
|
||||
hardDescription: "Соответстующие условиям заметки вообще не будут попадать в вашу ленту. Даже если вы поменяете условия, отсеенные таким образом заметки уже не появятся."
|
||||
soft: "Лёгкий "
|
||||
hard: "Сложный"
|
||||
mutedNotes: "Скрытые посты"
|
||||
soft: "Мягкий"
|
||||
hard: "Жёсткий"
|
||||
mutedNotes: "Скрытые заметки"
|
||||
_theme:
|
||||
explore: "Обзор"
|
||||
install: "Установить тему"
|
||||
manage: "Менеджер тем"
|
||||
code: "Код темы"
|
||||
installed: "{name} установлено "
|
||||
alreadyInstalled: "Тема уже установлена"
|
||||
invalid: "Формат темы некорректный "
|
||||
installed: "Тема «{name}» установлена."
|
||||
alreadyInstalled: "Тема уже установлена."
|
||||
invalid: "Формат темы некорректный."
|
||||
make: "Создать тему"
|
||||
base: "Основа"
|
||||
addConstant: "Добавить константу"
|
||||
constant: "Константа"
|
||||
defaultValue: "Исходное содержимое"
|
||||
defaultValue: "По умолчанию"
|
||||
color: "Цвет"
|
||||
refProp: "Ссылка на свойство"
|
||||
refConst: "Ссылка на константу"
|
||||
@ -648,11 +735,11 @@ _theme:
|
||||
func: "Функции"
|
||||
funcKind: "Тип функции"
|
||||
argument: "Аргумент"
|
||||
basedProp: "Указанное свойство"
|
||||
basedProp: "Исходное свойство"
|
||||
alpha: "Непрозрачность"
|
||||
darken: "Затемнение"
|
||||
lighten: "Осветление"
|
||||
inputConstantName: "Введите имя для константы"
|
||||
inputConstantName: "Введите имя для константы."
|
||||
importInfo: "Если вы введете код темы здесь, вы можете импортировать его в редактор тем."
|
||||
deleteConstantConfirm: "Вы действительно хотите удалить константу {const}?"
|
||||
keys:
|
||||
@ -713,7 +800,7 @@ _ago:
|
||||
justNow: "Только что"
|
||||
secondsAgo: "{n} с назад"
|
||||
minutesAgo: "{n} мин назад"
|
||||
hoursAgo: "{} часов назад"
|
||||
hoursAgo: "{n} ч назад"
|
||||
daysAgo: "{n} сут назад"
|
||||
weeksAgo: "{n} нед. назад"
|
||||
monthsAgo: "{n} мес. назад"
|
||||
@ -733,7 +820,7 @@ _tutorial:
|
||||
step3_1: "Успешно заполнили профиль?"
|
||||
step3_2: "Что ж, теперь самое время опубликуовать заметку. Если нажать вверху страницы на изображение карандаша, появится форма для текста."
|
||||
step3_3: "Напишите в неё, что хотите, и нажмите на кнопку в правом верхнем углу."
|
||||
step3_4: "Ничего не приходит в голову? Как насчёт: «я новенький, пока осваиваюсь в Misskey»?"
|
||||
step3_4: "Ничего не приходит в голову? Как насчёт: «Я новенький, пока осваиваюсь в Misskey»?"
|
||||
step4_1: "С написанием первой заметки покончено?"
|
||||
step4_2: "Отлично, теперь она должна появиться в вашей ленте."
|
||||
step5_1: "А теперь самое время немного оживить ленту, подписавшись на других."
|
||||
@ -810,7 +897,7 @@ _widgets:
|
||||
notifications: "Уведомления"
|
||||
timeline: "Лента"
|
||||
calendar: "Календарь"
|
||||
trends: "Популярное"
|
||||
trends: "Актуальное"
|
||||
clock: "Часы"
|
||||
rss: "Просмотр RSS"
|
||||
activity: "Активность"
|
||||
@ -821,7 +908,7 @@ _widgets:
|
||||
_cw:
|
||||
hide: "Спрятать"
|
||||
show: "Показать еще"
|
||||
chars: "символов: {count}"
|
||||
chars: "знаков: {count}"
|
||||
files: "файлов: {count}"
|
||||
_poll:
|
||||
noOnlyOneChoice: "Нужно хотя бы два варианта."
|
||||
@ -839,7 +926,7 @@ _poll:
|
||||
totalVotes: "Голосов всего: {n}"
|
||||
vote: "Проголосовать"
|
||||
showResult: "Смотреть результаты"
|
||||
voted: "Проголосовали"
|
||||
voted: "Голос отдан"
|
||||
closed: "Завершено"
|
||||
remainingDays: "Осталось {d} сут {h} ч"
|
||||
remainingHours: "Осталось {h} ч {m} мин"
|
||||
@ -908,10 +995,10 @@ _instanceCharts:
|
||||
files: "Изменения числа файлов"
|
||||
filesTotal: "Суммарное количество файлов"
|
||||
_timelines:
|
||||
home: "Персональное"
|
||||
local: "Местное"
|
||||
social: "Социальное"
|
||||
global: "Глобальное"
|
||||
home: "Персональная"
|
||||
local: "Местная"
|
||||
social: "Социальная"
|
||||
global: "Всеобщая"
|
||||
_rooms:
|
||||
roomOf: "Комната {user}"
|
||||
addFurniture: "Добавить обстановку"
|
||||
@ -921,7 +1008,7 @@ _rooms:
|
||||
remove: "Выбросить"
|
||||
clear: "Очистить"
|
||||
clearConfirm: "Уверены что стоит убрать всю обстановку из вашей комнаты?"
|
||||
leaveConfirm: "Измнения не сохранены, правда покинуть комнату?"
|
||||
leaveConfirm: "Изменения не сохранены, правда хотите покинуть комнату?"
|
||||
chooseImage: "Выберите изображение"
|
||||
roomType: "Стиль комнаты"
|
||||
carpetColor: "Цвет ковра"
|
||||
@ -978,11 +1065,11 @@ _pages:
|
||||
newPage: "Создать страницу"
|
||||
editPage: "Править страницу"
|
||||
readPage: "Читать страницу"
|
||||
created: "Страница успешно создана"
|
||||
updated: "Страница успешно обновлена"
|
||||
deleted: "Страница успешно удалена"
|
||||
nameAlreadyExists: "Указанный адрес страницы уже существует"
|
||||
invalidNameTitle: "Указанный адрес страницы недопустим"
|
||||
created: "Страница успешно создана."
|
||||
updated: "Страница успешно обновлена."
|
||||
deleted: "Страница успешно удалена."
|
||||
nameAlreadyExists: "Указанный адрес страницы уже существует."
|
||||
invalidNameTitle: "Указанный адрес страницы недопустим."
|
||||
invalidNameText: "Проверьте, что не оставили поле пустым."
|
||||
editThisPage: "Правка этой страницы"
|
||||
viewSource: "Просмотр исходника"
|
||||
@ -995,7 +1082,7 @@ _pages:
|
||||
content: "Содержимое"
|
||||
variables: "Переменные"
|
||||
title: "Заголовок"
|
||||
url: "URL страницы"
|
||||
url: "Адрес страницы"
|
||||
summary: "Краткое содержание"
|
||||
alignCenter: "Выровнять элементы по центру"
|
||||
hideTitleWhenPinned: "Скрыть заголовок страницы при привязке к профилю"
|
||||
@ -1057,7 +1144,7 @@ _pages:
|
||||
inc: "Увеличивать на"
|
||||
_button:
|
||||
text: "Надпись"
|
||||
colored: "Цветная"
|
||||
colored: "Выделена цветом"
|
||||
action: "Действие по нажатию"
|
||||
_action:
|
||||
dialog: "Показать всплывающий текст"
|
||||
@ -1257,21 +1344,21 @@ _pages:
|
||||
argVariables: "Аргументы"
|
||||
_relayStatus:
|
||||
requesting: "В ожидании одобрения"
|
||||
accepted: "Одобрено"
|
||||
rejected: "Отказано"
|
||||
accepted: "Одобрено."
|
||||
rejected: "Отказано."
|
||||
_notification:
|
||||
fileUploaded: "Файл успешно загружен"
|
||||
youGotMention: "{name} упоминает вас"
|
||||
youGotReply: "{name} отвечает вам"
|
||||
youGotQuote: "{name} цитирует вас"
|
||||
youRenoted: "{name} передаёт вашу заметку"
|
||||
youGotPoll: "{name} — участник вашего опроса"
|
||||
youGotMessagingMessageFromUser: "{name} пишет вам"
|
||||
youGotMessagingMessageFromGroup: "Отправлено сообщение в группу «{name}»"
|
||||
youWereFollowed: "У вас новый подписчик"
|
||||
youReceivedFollowRequest: "У вас новый запрос на подписку"
|
||||
yourFollowRequestAccepted: "Ваш запрос на подписку одобрен"
|
||||
youWereInvitedToGroup: "Приглашение в группу"
|
||||
fileUploaded: "Файл успешно загружен."
|
||||
youGotMention: "{name} упоминает вас."
|
||||
youGotReply: "{name} отвечает вам."
|
||||
youGotQuote: "{name} цитирует вас."
|
||||
youRenoted: "{name} передаёт вашу заметку."
|
||||
youGotPoll: "{name} участвует в вашем опросе."
|
||||
youGotMessagingMessageFromUser: "{name} пишет вам."
|
||||
youGotMessagingMessageFromGroup: "Новое сообщение в группе «{name}»."
|
||||
youWereFollowed: "У вас новый подписчик."
|
||||
youReceivedFollowRequest: "У вас новый запрос на подписку."
|
||||
yourFollowRequestAccepted: "Ваш запрос на подписку одобрен."
|
||||
youWereInvitedToGroup: "Вы приглашены в группу."
|
||||
_types:
|
||||
all: "Все"
|
||||
follow: "Подписки"
|
||||
@ -1283,7 +1370,7 @@ _notification:
|
||||
pollVote: "Голосования"
|
||||
receiveFollowRequest: "Получен запрос на подписку"
|
||||
followRequestAccepted: "Запрос на подписку одобрен"
|
||||
groupInvited: "Пришлашение в группы"
|
||||
groupInvited: "Приглашение в группы"
|
||||
app: "Уведомления из приложений"
|
||||
_deck:
|
||||
alwaysShowMainColumn: "Всегда показывать главную колонку"
|
||||
|
@ -1,3 +1,5 @@
|
||||
---
|
||||
_lang_: "ياپونچە"
|
||||
search: "ئىزدەش"
|
||||
_mfm:
|
||||
search: "ئىزدەش"
|
||||
|
482
locales/uk-UA.yml
Normal file
482
locales/uk-UA.yml
Normal file
@ -0,0 +1,482 @@
|
||||
---
|
||||
_lang_: "Українська"
|
||||
monthAndDay: "{month}/{day}"
|
||||
search: "Пошук"
|
||||
notifications: "Сповіщення"
|
||||
username: "Ім'я користувача"
|
||||
password: "Пароль"
|
||||
fetchingAsApObject: "Отримуємо з федіверсу..."
|
||||
ok: "OK"
|
||||
gotIt: "Зрозуміло!"
|
||||
cancel: "Скасувати"
|
||||
enterUsername: "Введіть ім'я користувача"
|
||||
renotedBy: "Поширено {user}"
|
||||
noNotes: "Немає дописів"
|
||||
noNotifications: "Немає сповіщень"
|
||||
instance: "Інстанс"
|
||||
settings: "Налаштування"
|
||||
basicSettings: "Основні налаштування"
|
||||
otherSettings: "Інші налаштування"
|
||||
openInWindow: "Відкрити у вікні"
|
||||
profile: "Профіль"
|
||||
timeline: "Стрічка"
|
||||
noAccountDescription: "Цей користувач ще нічого не написав про себе"
|
||||
login: "Увійти"
|
||||
loggingIn: "Здійснюємо вхід..."
|
||||
logout: "Вийти"
|
||||
signup: "Реєстрація"
|
||||
uploading: "Завантаження..."
|
||||
save: "Зберегти"
|
||||
users: "Користувачі"
|
||||
addUser: "Додати користувача"
|
||||
favorite: "Обране"
|
||||
favorites: "Обране"
|
||||
unfavorite: "Видалити з обраного"
|
||||
pin: "Закріпити"
|
||||
unpin: "Відкріпити"
|
||||
copyContent: "Скопіювати контент"
|
||||
copyLink: "Скопіювати посилання"
|
||||
delete: "Видалити"
|
||||
deleteAndEdit: "Видалити й редагувати"
|
||||
addToList: "Додати до списку"
|
||||
sendMessage: "Надіслати повідомлення"
|
||||
copyUsername: "Скопіювати ім’я користувача"
|
||||
searchUser: "Пошук користувачів"
|
||||
reply: "Відповісти"
|
||||
loadMore: "Показати більше"
|
||||
youGotNewFollower: "У вас новий підписник"
|
||||
receiveFollowRequest: "Отримано запит на підписку"
|
||||
followRequestAccepted: "Запит на підписку прийнято"
|
||||
mention: "Згадка"
|
||||
mentions: "Згадки"
|
||||
directNotes: "Прямі повідомлення"
|
||||
importAndExport: "Імпорт та експорт"
|
||||
import: "Імпорт"
|
||||
export: "Експорт"
|
||||
files: "Файли"
|
||||
download: "Завантажити"
|
||||
unfollowConfirm: "Ви впевнені, що хочете відписатися від {name}?"
|
||||
lists: "Списки"
|
||||
noLists: "Немає списків"
|
||||
note: "Дописи"
|
||||
notes: "Дописи"
|
||||
following: "Підписки"
|
||||
followers: "Підписники"
|
||||
followsYou: "Підписаний(-а) на вас"
|
||||
createList: "Створити список"
|
||||
manageLists: "Управління списками"
|
||||
error: "Помилка"
|
||||
somethingHappened: "Щось пішло не так"
|
||||
retry: "Спробувати знову"
|
||||
pageLoadError: "Помилка при завантаженні сторінки"
|
||||
enterListName: "Введіть назву списку"
|
||||
privacy: "Приватність"
|
||||
makeFollowManuallyApprove: "Підтверджувати підписників уручну"
|
||||
defaultNoteVisibility: "Видимість допису за замовчуванням"
|
||||
follow: "Підписка"
|
||||
followRequest: "Запит на підписку"
|
||||
followRequests: "Запити на підписку"
|
||||
unfollow: "Відписатися"
|
||||
followRequestPending: "Очікуючі запити на підписку"
|
||||
enterEmoji: "Введіть емодзі"
|
||||
renote: "Поширити"
|
||||
unrenote: "Відміна поширення"
|
||||
quote: "Цитата"
|
||||
pinnedNote: "Закріплений допис"
|
||||
you: "Ви"
|
||||
clickToShow: "Натисніть для перегляду"
|
||||
sensitive: "NSFW"
|
||||
add: "Додати"
|
||||
reaction: "Реакції"
|
||||
rememberNoteVisibility: "Пам’ятати видимість дописів"
|
||||
attachCancel: "Видалити вкладення"
|
||||
markAsSensitive: "Позначити як NSFW"
|
||||
unmarkAsSensitive: "Зняти позначку NSFW"
|
||||
enterFileName: "Введіть ім'я файлу"
|
||||
mute: "Ігнорувати"
|
||||
unmute: "Показувати"
|
||||
block: "Заблокувати"
|
||||
unblock: "Розблокувати"
|
||||
suspend: "Призупинити"
|
||||
unsuspend: "Відновити"
|
||||
blockConfirm: "Ви впевнені, що хочете заблокувати цей акаунт?"
|
||||
unblockConfirm: "Ви впевнені, що хочете розблокувати цей акаунт?"
|
||||
suspendConfirm: "Ви впевнені, що хочете призупинити цей акаунт?"
|
||||
unsuspendConfirm: "Ви впевнені, що хочете відновити цей акаунт?"
|
||||
selectList: "Виберіть список"
|
||||
selectAntenna: "Виберіть антену"
|
||||
selectWidget: "Виберіть віджет"
|
||||
editWidgets: "Редагувати віджети"
|
||||
editWidgetsExit: "Готово"
|
||||
customEmojis: "Кастомні емоджі"
|
||||
emoji: "Емоджі"
|
||||
emojiName: "Назва емоджі"
|
||||
emojiUrl: "URL емодзі"
|
||||
addEmoji: "Додати емодзі"
|
||||
settingGuide: "Рекомендована конфігурація"
|
||||
cacheRemoteFiles: "Кешувати дані з інших інстансів"
|
||||
flagAsBot: "Акаунт бота"
|
||||
flagAsCat: "Акаунт кота"
|
||||
autoAcceptFollowed: "Автоматично приймати запити на підписку від користувачів, на яких ви підписані"
|
||||
addAcount: "Додати акаунт"
|
||||
loginFailed: "Не вдалося увійти"
|
||||
showOnRemote: "Переглянути в оригіналі"
|
||||
general: "Загальне"
|
||||
wallpaper: "Шпалери"
|
||||
setWallpaper: "Встановити шпалери"
|
||||
removeWallpaper: "Прибрати шпалери"
|
||||
searchWith: "Шукати з {q}"
|
||||
youHaveNoLists: "У вас немає списків"
|
||||
followConfirm: "Підписатися на {name}?"
|
||||
proxyAccount: "Проксі-акаунт"
|
||||
host: "Хост"
|
||||
selectUser: "Виберіть користувача"
|
||||
recipient: "Кому"
|
||||
annotation: "Коментар"
|
||||
federation: "Федіверс"
|
||||
instances: "Інстанс"
|
||||
registeredAt: "Приєднався(-лась)"
|
||||
latestRequestSentAt: "Останній запит надіслано"
|
||||
latestRequestReceivedAt: "Останній запит прийнято"
|
||||
latestStatus: "Останній статус"
|
||||
storageUsage: "Використання простіру"
|
||||
charts: "Графіки"
|
||||
perHour: "Щогодини"
|
||||
perDay: "Щоденно"
|
||||
stopActivityDelivery: "Припинити розсилання активності"
|
||||
blockThisInstance: "Заблокувати цей інстанс"
|
||||
operations: "Операції"
|
||||
software: "Програмне забезпечення"
|
||||
version: "Версія"
|
||||
metadata: "Метадані"
|
||||
withNFiles: "файли: {n}"
|
||||
monitor: "Монітор"
|
||||
jobQueue: "Черга завдань"
|
||||
cpuAndMemory: "ЦП та пам'ять"
|
||||
network: "Мережа"
|
||||
disk: "Диск"
|
||||
instanceInfo: "Про цей інстанс"
|
||||
statistics: "Статистика"
|
||||
clearQueue: "Очистити чергу"
|
||||
clearQueueConfirmTitle: "Ви впевнені, що хочете очистити чергу?"
|
||||
clearCachedFiles: "Очистити кеш"
|
||||
clearCachedFilesConfirm: "Ви впевнені, що хочете видалити всі кешовані файли?"
|
||||
blockedInstances: "Заблоковані інстанси"
|
||||
muteAndBlock: "Ігнор і блокування"
|
||||
mutedUsers: "Ігноровані користувачі"
|
||||
blockedUsers: "Заблоковані користувачі"
|
||||
noUsers: "Немає користувачів"
|
||||
editProfile: "Редагувати профіль"
|
||||
noteDeleteConfirm: "Ви дійсно хочете видалити цей допис?"
|
||||
pinLimitExceeded: "Більше дописів не можна закріпити"
|
||||
done: "Готово"
|
||||
processing: "Обробка"
|
||||
preview: "Передогляд"
|
||||
default: "За умовчанням"
|
||||
noCustomEmojis: "Немає кастомних емоджі"
|
||||
noJobs: "Немає завдань"
|
||||
federating: "Федерується"
|
||||
blocked: "Заблоковано"
|
||||
suspended: "Призупинено"
|
||||
notResponding: "Не відповідає"
|
||||
changePassword: "Змінити пароль"
|
||||
security: "Безпека"
|
||||
currentPassword: "Поточний пароль"
|
||||
newPassword: "Новий пароль"
|
||||
newPasswordRetype: "Новий пароль (повторно)"
|
||||
attachFile: "Вкласти файл"
|
||||
more: "Бiльше!"
|
||||
featured: "Виділено"
|
||||
noSuchUser: "Користувача не знайдено"
|
||||
lookup: "Пошук"
|
||||
announcements: "Оголошення"
|
||||
imageUrl: "URL зображення"
|
||||
remove: "Видалити"
|
||||
removed: "Видалено"
|
||||
saved: "Збережено"
|
||||
messaging: "Чати"
|
||||
upload: "Завантажити"
|
||||
fromDrive: "З диска"
|
||||
fromUrl: "З URL"
|
||||
uploadFromUrl: "Завантажити з URL"
|
||||
explore: "Огляд"
|
||||
games: "Ігри Misskey"
|
||||
noMoreHistory: "Подальшої історії немає"
|
||||
start: "Розпочати"
|
||||
home: "Домівка"
|
||||
activity: "Активність"
|
||||
images: "Зображення"
|
||||
birthday: "День народження"
|
||||
yearsOld: "{age} років"
|
||||
registeredDate: "Приєднався(-лась)"
|
||||
location: "Локація"
|
||||
theme: "Тема"
|
||||
themeForLightMode: "Світла тема"
|
||||
themeForDarkMode: "Темна тема"
|
||||
light: "Світла"
|
||||
dark: "Темна"
|
||||
lightThemes: "Світлі теми"
|
||||
darkThemes: "Темні теми"
|
||||
drive: "Диск"
|
||||
fileName: "Ім'я файлу"
|
||||
selectFile: "Вибрати файл"
|
||||
selectFiles: "Вибрати файли"
|
||||
selectFolder: "Вибрати теку"
|
||||
selectFolders: "Вибрати теки"
|
||||
renameFile: "Перейменувати файл"
|
||||
folderName: "Ім'я теки"
|
||||
createFolder: "Створити теку"
|
||||
renameFolder: "Перейменувати теку"
|
||||
deleteFolder: "Видалити теку"
|
||||
addFile: "Додати файл"
|
||||
emptyDrive: "Диск порожній"
|
||||
emptyFolder: "Тека порожня"
|
||||
unableToDelete: "Видалення неможливе"
|
||||
inputNewFileName: "Введіть ім'я нового файлу"
|
||||
inputNewFolderName: "Введіть ім'я нової теки"
|
||||
hasChildFilesOrFolders: "Ця тека не порожня і не може бути видалена"
|
||||
copyUrl: "Копіювати URL"
|
||||
rename: "Перейменувати"
|
||||
avatar: "Аватар"
|
||||
banner: "Банер"
|
||||
nsfw: "NSFW"
|
||||
disconnectedFromServer: "Підключення до сервера було перервано"
|
||||
reload: "Оновити"
|
||||
doNothing: "Нічого не робити"
|
||||
reloadConfirm: "Перезавантажити стрічку?"
|
||||
watch: "Стежити"
|
||||
unwatch: "Не стежити"
|
||||
accept: "Прийняти"
|
||||
reject: "Відхилити"
|
||||
instanceName: "Назва інстансу"
|
||||
instanceDescription: "Описання інстансу"
|
||||
maintainerName: "Ім'я адміністратора"
|
||||
maintainerEmail: "Email адміністратора"
|
||||
tosUrl: "URL умов використання"
|
||||
thisYear: "Рік"
|
||||
thisMonth: "Місяць"
|
||||
today: "День"
|
||||
dayX: "{day}"
|
||||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Сторінки"
|
||||
integration: "Інтеграція"
|
||||
connectSerice: "Під’єднатися"
|
||||
disconnectSerice: "Відключитися"
|
||||
enableLocalTimeline: "Увімкнути локальну стрічку"
|
||||
enableGlobalTimeline: "Увімкнути глобальну стрічку"
|
||||
registration: "Реєстрація"
|
||||
enableRegistration: "Дозволити реєстрацію"
|
||||
invite: "Запрошення"
|
||||
proxyRemoteFiles: "Проксувати файли з інших інстансів"
|
||||
iconUrl: "URL аватара"
|
||||
bannerUrl: "URL банера"
|
||||
basicInfo: "Основна інформація"
|
||||
pinnedUsers: "Закріплені користувачі"
|
||||
hcaptcha: "hCaptcha"
|
||||
enableHcaptcha: "Увімкнути hCaptcha"
|
||||
hcaptchaSiteKey: "Ключ сайту"
|
||||
hcaptchaSecretKey: "Секретний ключ"
|
||||
recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "Увімкнути reCAPTCHA"
|
||||
recaptchaSiteKey: "Ключ сайту"
|
||||
recaptchaSecretKey: "Секретний ключ"
|
||||
antennas: "Антени"
|
||||
manageAntennas: "Налаштування антен"
|
||||
name: "Ім'я"
|
||||
antennaSource: "Джерело антени"
|
||||
antennaKeywords: "Ключові слова антени"
|
||||
antennaExcludeKeywords: "Винятки"
|
||||
serviceworker: "ServiceWorker"
|
||||
enableServiceworker: "Ввімкнути ServiceWorker"
|
||||
caseSensitive: "З урахуванням регістру"
|
||||
notesAndReplies: "Дописи та відповіді"
|
||||
popularUsers: "Популярні користувачі"
|
||||
recentlyUpdatedUsers: "Нещодавно активні користувачі"
|
||||
recentlyRegisteredUsers: "Нещодавно зареєстровані користувачі"
|
||||
recentlyDiscoveredUsers: "Нещодавно знайдені користувачі"
|
||||
exploreUsersCount: "{count} користувачів"
|
||||
exploreFediverse: "Огляд федіверсу"
|
||||
popularTags: "Популярні теги"
|
||||
userList: "Списки"
|
||||
about: "Інформація"
|
||||
aboutMisskey: "Про Misskey"
|
||||
administrator: "Адмін"
|
||||
token: "Токен"
|
||||
twoStepAuthentication: "Двохфакторна аутентифікація"
|
||||
moderator: "Модератор"
|
||||
securityKey: "Ключ захисту"
|
||||
securityKeyName: "Назва ключа"
|
||||
registerSecurityKey: "Зареєструвати ключ захисту"
|
||||
lastUsed: "Востаннє використано"
|
||||
unregister: "Скасувати реєстрацію"
|
||||
passwordLessLogin: "Налаштувати вхід без пароля"
|
||||
resetPassword: "Скинути пароль"
|
||||
newPasswordIs: "Новий пароль: {password}"
|
||||
share: "Поділитись"
|
||||
notFound: "Не знайдено"
|
||||
cacheClear: "Очистити кеш"
|
||||
help: "Допомога"
|
||||
inputMessageHere: "Введіть повідомлення тут"
|
||||
close: "Закрити"
|
||||
group: "Група"
|
||||
groups: "Групи"
|
||||
createGroup: "Створити групу"
|
||||
ownedGroups: "Власні групи"
|
||||
invites: "Запрошення"
|
||||
groupName: "Назва групи"
|
||||
transfer: "Передача"
|
||||
messagingWithUser: "Чат з користувачами"
|
||||
messagingWithGroup: "Чат з групою"
|
||||
title: "Тема"
|
||||
text: "Текст"
|
||||
next: "Далі"
|
||||
retype: "Введіть ще раз"
|
||||
noteOf: "Допис {user}"
|
||||
inviteToGroup: "Запрошення до групи"
|
||||
maxNoteTextLength: "Максимальна довжина допису"
|
||||
quoteAttached: "Цитата"
|
||||
quoteQuestion: "Ви хочете додати цитату?"
|
||||
noMessagesYet: "Ще немає повідомлень"
|
||||
newMessageExists: "Є нові повідомлення"
|
||||
onlyOneFileCanBeAttached: "До повідомлення можна вкласти лише один файл"
|
||||
signinRequired: "Будь ласка, авторизуйтесь"
|
||||
invitations: "Запрошення"
|
||||
invitationCode: "Код запрошення"
|
||||
checking: "Перевірка…"
|
||||
available: "Доступно"
|
||||
unavailable: "Недоступно"
|
||||
usernameInvalidFormat: "літери, цифри та _ є прийнятними"
|
||||
tooShort: "Занадто короткий"
|
||||
tooLong: "Занадто довгий"
|
||||
weakPassword: "Слабкий пароль"
|
||||
strongPassword: "Міцний пароль"
|
||||
passwordMatched: "Все вірно"
|
||||
passwordNotMatched: "Паролі не співпадають"
|
||||
signinWith: "Увійти за допомогою {x}"
|
||||
uiLanguage: "Мова інтерфейсу"
|
||||
aboutX: "Про {x}"
|
||||
useOsNativeEmojis: "Використовувати емодзі ОС"
|
||||
youHaveNoGroups: "Немає груп"
|
||||
noHistory: "Історія порожня"
|
||||
disableAnimatedMfm: "Відключити анімації MFM"
|
||||
doing: "Виконується"
|
||||
category: "Категорія"
|
||||
tags: "Теги"
|
||||
docSource: "Джерело цього документа"
|
||||
createAccount: "Створити акаунт"
|
||||
existingAcount: "Існуючий акаунт"
|
||||
regenerate: "Оновити"
|
||||
fontSize: "Розмір шрифту"
|
||||
noFollowRequests: "Немає запитів на підписку"
|
||||
dashboard: "Панель приладів"
|
||||
local: "Локальні"
|
||||
remote: "Віддалений"
|
||||
total: "Всього"
|
||||
appearance: "Вигляд"
|
||||
clientSettings: "Налаштування клієнта"
|
||||
accountSettings: "Налаштування акаунта"
|
||||
promotion: "Просування"
|
||||
promote: "Просунути"
|
||||
numberOfDays: "Кількість днів"
|
||||
hideThisNote: "Сховати цей допис"
|
||||
showInPage: "Показати на сторінці"
|
||||
sort: "Сортування"
|
||||
ascendingOrder: "За зростанням"
|
||||
descendingOrder: "За спаданням"
|
||||
script: "Скрипт"
|
||||
deleteAllFiles: "Видалити всі файли"
|
||||
deleteAllFilesConfirm: "Ви дійсно хочете видалити всі файли?"
|
||||
sidebar: "Бокова панель"
|
||||
rooms: "Кімнати"
|
||||
relays: "Ретранслятори"
|
||||
addRelay: "Додати ретранслятор"
|
||||
smtpHost: "Хост"
|
||||
smtpUser: "Ім'я користувача"
|
||||
smtpPass: "Пароль"
|
||||
regenerateLoginToken: "Оновити Login Token"
|
||||
_mfm:
|
||||
cheatSheet: " Довідка MFM"
|
||||
mention: "Згадка"
|
||||
quote: "Цитата"
|
||||
emoji: "Кастомні емоджі"
|
||||
search: "Пошук"
|
||||
_reversi:
|
||||
total: "Всього"
|
||||
_sidebar:
|
||||
icon: "Аватар"
|
||||
_theme:
|
||||
keys:
|
||||
mention: "Згадка"
|
||||
renote: "Поширити"
|
||||
_sfx:
|
||||
note: "Дописи"
|
||||
notification: "Сповіщення"
|
||||
chat: "Чати"
|
||||
_antennaSources:
|
||||
homeTimeline: "Дописи тих, на кого ви підписані"
|
||||
_widgets:
|
||||
notifications: "Сповіщення"
|
||||
timeline: "Стрічка"
|
||||
activity: "Активність"
|
||||
federation: "Федіверс"
|
||||
_cw:
|
||||
show: "Показати більше"
|
||||
_visibility:
|
||||
home: "Домівка"
|
||||
followers: "Підписники"
|
||||
localOnly: "Лише локально"
|
||||
_postForm:
|
||||
replyPlaceholder: "Відповідь на допис..."
|
||||
_profile:
|
||||
name: "Ім'я"
|
||||
username: "Ім'я користувача"
|
||||
_exportOrImport:
|
||||
followingList: "Підписки"
|
||||
muteList: "Ігнорувати"
|
||||
blockingList: "Заблокувати"
|
||||
userLists: "Списки"
|
||||
_timelines:
|
||||
home: "Домівка"
|
||||
_rooms:
|
||||
_roomType:
|
||||
default: "За умовчанням"
|
||||
_furnitures:
|
||||
monitor: "Монітор"
|
||||
_pages:
|
||||
blocks:
|
||||
image: "Зображення"
|
||||
script:
|
||||
categories:
|
||||
list: "Списки"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "Списки"
|
||||
_randomPick:
|
||||
arg1: "Списки"
|
||||
_dailyRandomPick:
|
||||
arg1: "Списки"
|
||||
_seedRandomPick:
|
||||
arg2: "Списки"
|
||||
_pick:
|
||||
arg1: "Списки"
|
||||
_listLen:
|
||||
arg1: "Списки"
|
||||
types:
|
||||
array: "Списки"
|
||||
_notification:
|
||||
youRenoted: "{name} поширив(ла) ваш допис"
|
||||
youWereFollowed: "У вас новий підписник"
|
||||
_types:
|
||||
follow: "Підписки"
|
||||
mention: "Згадка"
|
||||
renote: "Поширити"
|
||||
quote: "Цитата"
|
||||
reaction: "Реакції"
|
||||
_deck:
|
||||
_columns:
|
||||
notifications: "Сповіщення"
|
||||
tl: "Стрічка"
|
||||
antenna: "Антени"
|
||||
list: "Списки"
|
||||
mentions: "Згадки"
|
@ -84,7 +84,7 @@ followRequest: "关注申请"
|
||||
followRequests: "关注申请"
|
||||
unfollow: "取消关注"
|
||||
followRequestPending: "发送关注申请"
|
||||
enterEmoji: "输入Emoji"
|
||||
enterEmoji: "输入表情符号"
|
||||
renote: "转发"
|
||||
unrenote: "取消转发"
|
||||
quote: "引用"
|
||||
@ -117,9 +117,9 @@ editWidgets: "编辑小工具"
|
||||
editWidgetsExit: "完成编辑"
|
||||
customEmojis: "自定义Emoji"
|
||||
emoji: "表情符号"
|
||||
emojiName: "Emoji 名称"
|
||||
emojiUrl: "emoji 地址"
|
||||
addEmoji: "添加Emoji"
|
||||
emojiName: "表情符号名称"
|
||||
emojiUrl: "表情符号地址"
|
||||
addEmoji: "添加表情符号"
|
||||
settingGuide: "推荐配置"
|
||||
cacheRemoteFiles: "远程文件缓存"
|
||||
cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程实例载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。"
|
||||
@ -164,7 +164,7 @@ jobQueue: "作业队列"
|
||||
cpuAndMemory: "CPU使用量"
|
||||
network: "网络"
|
||||
disk: "存储"
|
||||
instanceInfo: "实例情报"
|
||||
instanceInfo: "实例信息"
|
||||
statistics: "统计"
|
||||
clearQueue: "清除队列"
|
||||
clearQueueConfirmTitle: "确定清除队列?"
|
||||
@ -185,7 +185,7 @@ done: "完成"
|
||||
processing: "处理中"
|
||||
preview: "预览"
|
||||
default: "默认"
|
||||
noCustomEmojis: "无自定义Emoji"
|
||||
noCustomEmojis: "没有自定义表情符号"
|
||||
noJobs: "没有任务"
|
||||
federating: "联合中"
|
||||
blocked: "已拦截"
|
||||
@ -353,7 +353,7 @@ popularTags: "热门标签"
|
||||
userList: "列表"
|
||||
about: "关于"
|
||||
aboutMisskey: "关于 Misskey"
|
||||
aboutMisskeyText: "Misskey是由syuilo于2014年开发的开放源代码软件。"
|
||||
aboutMisskeyText: "Misskey是由syuilo于2014年开发的开源软件。"
|
||||
misskeyMembers: "现在由以下成员进行开发和维护:"
|
||||
misskeySource: "源代码在这里公开:"
|
||||
misskeyTranslation: "与我们一同进行Misskey的翻译工作:"
|
||||
@ -373,8 +373,6 @@ unregister: "删除账户"
|
||||
passwordLessLogin: "无密码登录"
|
||||
resetPassword: "重置密码"
|
||||
newPasswordIs: "新的密码是「{password}」"
|
||||
autoNoteWatch: "自动关注帖子"
|
||||
autoNoteWatchDescription: "让您能够收到关于「回应」和回复其他用户的帖子的通知。"
|
||||
reduceUiAnimation: "减少UI动画"
|
||||
share: "分享"
|
||||
notFound: "未找到"
|
||||
@ -432,7 +430,7 @@ or: "或者"
|
||||
uiLanguage: "显示语言"
|
||||
groupInvited: "群组招待"
|
||||
aboutX: "关于 {x}"
|
||||
useOsNativeEmojis: "使用OS原生Emoji"
|
||||
useOsNativeEmojis: "使用OS原生表情符号"
|
||||
youHaveNoGroups: "没有群组"
|
||||
joinOrCreateGroup: "请加入一个现有的群组,或者创建新群组。"
|
||||
noHistory: "没有历史记录"
|
||||
@ -542,6 +540,7 @@ pluginInstallWarn: "请不要安装不明来源的插件"
|
||||
deck: "Deck"
|
||||
undeck: "取消Deck"
|
||||
useBlurEffectForModal: "模态框使用模糊效果"
|
||||
useFullReactionPicker: "使用全功能的回应工具栏"
|
||||
generateAccessToken: "生成访问令牌"
|
||||
permission: "权限"
|
||||
enableAll: "启用全部"
|
||||
@ -550,7 +549,7 @@ tokenRequested: "允许访问账户"
|
||||
pluginTokenRequestedDescription: "此插件将能够拥有此处设置的权限"
|
||||
notificationType: "通知类型"
|
||||
edit: "编辑"
|
||||
useStarForReactionFallback: "如果回应的颜文字未知,则使用★作为代替"
|
||||
useStarForReactionFallback: "如果回应的是未知表情符号,则使用★作为代替"
|
||||
emailConfig: "邮件服务器设置"
|
||||
enableEmail: "启用发送邮件功能"
|
||||
emailConfigInfo: "用于确认电子邮件和密码重置"
|
||||
@ -598,9 +597,97 @@ openInNewTab: "在新标签页中打开"
|
||||
openInSideView: "在侧边栏中打开"
|
||||
defaultNavigationBehaviour: "默认导航"
|
||||
editTheseSettingsMayBreakAccount: "编辑这些设置可以会损坏您的账号"
|
||||
instanceTicker: "帖子的实例信息"
|
||||
waitingFor: "等待{x}"
|
||||
random: "随机"
|
||||
system: "系统"
|
||||
switchUi: "切换界面"
|
||||
desktop: "桌面"
|
||||
_mfm:
|
||||
cheatSheet: "MFM代码速查表"
|
||||
intro: "MFM是一种在Misskey中的各个位置使用的专用标记语言。在这里您可以看到MFM中可用的语法列表。"
|
||||
dummy: "通过Misskey扩展Fediverse的世界"
|
||||
mention: "提及"
|
||||
mentionDescription: "可以使用 @+用户名 来指示特定用户"
|
||||
hashtag: "话题标签"
|
||||
hashtagDescription: "可以使用井号+文字来表示话题标签。"
|
||||
url: "URL"
|
||||
urlDescription: "可以表示URL地址。"
|
||||
link: "链接"
|
||||
linkDescription: "可以将部分文字和URL关联起来。"
|
||||
bold: "粗体"
|
||||
boldDescription: "可以将文字显示为粗体来表示强调。"
|
||||
small: "缩小"
|
||||
smallDescription: "可以使内容文字变小、变淡。"
|
||||
center: "居中"
|
||||
centerDescription: "可以将内容居中显示。"
|
||||
inlineCode: "代码(内嵌)"
|
||||
inlineCodeDescription: "将文字中的程序代码语法高亮显示。"
|
||||
blockCode: "代码(块)"
|
||||
blockCodeDescription: "语法高亮显示整块程序代码。"
|
||||
inlineMath: "数学公式(内嵌)"
|
||||
inlineMathDescription: "显示内嵌的KaTex公式。"
|
||||
blockMath: "数学公式(块)"
|
||||
blockMathDescription: "显示整块的多行KaTex数学公式。"
|
||||
quote: "引用"
|
||||
quoteDescription: "可以用来表示引用的内容。"
|
||||
emoji: "自定义表情符号"
|
||||
emojiDescription: "可以将自定义表情符号使用冒号括起来,就可以显示自定义表情符号了。"
|
||||
search: "搜索"
|
||||
searchDescription: "显示含有搜索内容示例的搜索框。"
|
||||
flip: "翻转"
|
||||
flipDescription: "将内容上下或左右翻转。"
|
||||
jelly: "动画(果冻)"
|
||||
jellyDescription: "显示果冻一样的动画效果。"
|
||||
tada: "动画(锵锵)"
|
||||
tadaDescription: "显示\"锵锵!\"的动画效果。"
|
||||
jump: "动画(跳动)"
|
||||
jumpDescription: "显示跳动的动画效果。"
|
||||
bounce: "动画(弹性)"
|
||||
bounceDescription: "显示弹性一样的动画效果。"
|
||||
shake: "动画(摇晃)"
|
||||
shakeDescription: "显示摇晃的动画效果。"
|
||||
twitch: "动画(颤抖)"
|
||||
twitchDescription: "显示强烈颤抖的动画效果。"
|
||||
spin: "动画(回转)"
|
||||
spinDescription: "显示回转的动画效果。"
|
||||
_reversi:
|
||||
reversi: "黑白棋"
|
||||
gameSettings: "对局设置"
|
||||
chooseBoard: "棋盘选择"
|
||||
blackOrWhite: "先手/后手"
|
||||
blackIs: "{name}执黑(先走)"
|
||||
rules: "规则"
|
||||
botSettings: "机器人设置"
|
||||
thisGameIsStartedSoon: "对局在几秒后开始"
|
||||
waitingForOther: "等待对手准备"
|
||||
waitingForMe: "等待您的准备"
|
||||
waitingBoth: "请准备"
|
||||
ready: "准备就绪"
|
||||
cancelReady: "重新准备"
|
||||
opponentTurn: "对手的会合"
|
||||
myTurn: "您的回合"
|
||||
turnOf: "{name}的回合"
|
||||
pastTurnOf: "{name}的回合"
|
||||
surrender: "认输 "
|
||||
surrendered: "对手认输"
|
||||
drawn: "平局"
|
||||
won: "{name}获胜"
|
||||
black: "黑"
|
||||
white: "白"
|
||||
total: "总计"
|
||||
turnCount: "{count}回合"
|
||||
myGames: "我的对局"
|
||||
allGames: "所有对局"
|
||||
ended: "结束"
|
||||
playing: "对局中"
|
||||
isLlotheo: "棋子较少一方获胜(LLoTheO规则)"
|
||||
loopedMap: "循环棋盘"
|
||||
canPutEverywhere: "可以下在任意位置"
|
||||
_instanceTicker:
|
||||
none: "不显示"
|
||||
remote: "显示给远程用户"
|
||||
always: "始终显示"
|
||||
_serverDisconnectedBehavior:
|
||||
reload: "自动重载"
|
||||
dialog: "对话框警告"
|
||||
|
@ -99,8 +99,8 @@ attachCancel: "移除附件"
|
||||
markAsSensitive: "標記為敏感內容"
|
||||
unmarkAsSensitive: "取消標記為敏感內容"
|
||||
enterFileName: "請輸入檔案名稱"
|
||||
mute: "消音"
|
||||
unmute: "解除消音"
|
||||
mute: "靜音"
|
||||
unmute: "解除靜音"
|
||||
block: "封鎖"
|
||||
unblock: "解除封鎖"
|
||||
suspend: "凍結"
|
||||
@ -171,8 +171,8 @@ clearCachedFiles: "清除快取資料"
|
||||
clearCachedFilesConfirm: "確定要清除緩存資料嗎?"
|
||||
blockedInstances: "已封鎖的實例"
|
||||
blockedInstancesDescription: "請逐行輸入需要封鎖的實例。已封鎖的實例將無法與本實例進行通訊。"
|
||||
muteAndBlock: "禁言 / 封鎖"
|
||||
mutedUsers: "已禁言用戶"
|
||||
muteAndBlock: "靜音/封鎖"
|
||||
mutedUsers: "已靜音用戶"
|
||||
blockedUsers: "已封鎖用戶"
|
||||
noUsers: "無用戶"
|
||||
editProfile: "編輯個人檔案"
|
||||
@ -370,8 +370,6 @@ unregister: "刪除賬戶"
|
||||
passwordLessLogin: "設置無密碼登入"
|
||||
resetPassword: "重置密碼"
|
||||
newPasswordIs: "新密碼為「{password}」"
|
||||
autoNoteWatch: "自動追隨貼文"
|
||||
autoNoteWatchDescription: "收到反應或回覆過的貼文的通知"
|
||||
reduceUiAnimation: "減少介面的動態視覺"
|
||||
share: "分享"
|
||||
notFound: "找不到"
|
||||
@ -452,8 +450,10 @@ appearance: "外觀"
|
||||
clientSettings: "用戶端設定"
|
||||
accountSettings: "帳號設定"
|
||||
promotion: "推廣貼文"
|
||||
promote: "推廣"
|
||||
numberOfDays: "有效天數"
|
||||
hideThisNote: "隱藏此貼文"
|
||||
showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
|
||||
objectStorageBaseUrl: "Base URL"
|
||||
objectStorageBucket: "儲存空間(Bucket)"
|
||||
objectStoragePrefix: "前綴"
|
||||
@ -536,6 +536,7 @@ smtpUser: "使用者名稱"
|
||||
smtpPass: "密碼"
|
||||
emptyToDisableSmtpAuth: "留空使用者名稱和密碼以禁用SMTP驗證。"
|
||||
testEmail: "郵件測試發送"
|
||||
wordMute: "靜音文字"
|
||||
display: "檢視"
|
||||
copy: "複製"
|
||||
metrics: "指標"
|
||||
@ -547,6 +548,7 @@ channel: "頻道"
|
||||
create: "新增"
|
||||
notificationSetting: "通知設定"
|
||||
other: "其他"
|
||||
regenerateLoginTokenDescription: "再生用於登入的內部權杖。一般情況下是不需要這樣做的。一旦再生,所有裝置將會被登出。"
|
||||
sample: "範例 "
|
||||
abuseReports: "檢舉"
|
||||
reportAbuse: "檢舉"
|
||||
@ -554,8 +556,32 @@ reportAbuseOf: "檢舉{name}"
|
||||
send: "發送"
|
||||
openInNewTab: "在新分頁中開啟"
|
||||
random: "隨機"
|
||||
system: "系統"
|
||||
_mfm:
|
||||
mention: "提及"
|
||||
hashtag: "#tag"
|
||||
link: "鏈接"
|
||||
quote: "引用"
|
||||
emoji: "自訂表情符號"
|
||||
search: "搜尋"
|
||||
_reversi:
|
||||
reversi: "黑白棋"
|
||||
gameSettings: "對弈設定"
|
||||
chooseBoard: "選擇棋盤"
|
||||
rules: "規則"
|
||||
botSettings: "機器人設定"
|
||||
opponentTurn: "對手回合"
|
||||
myTurn: "你的回合"
|
||||
turnOf: "{name}的回合"
|
||||
pastTurnOf: "{name}的回合"
|
||||
surrender: "認輸"
|
||||
black: "黑"
|
||||
white: "白"
|
||||
total: "合計"
|
||||
ended: "已結束"
|
||||
playing: "正在對弈"
|
||||
_instanceTicker:
|
||||
always: "總是顯示"
|
||||
_serverDisconnectedBehavior:
|
||||
reload: "自動重載"
|
||||
dialog: "以對話框警告"
|
||||
@ -575,14 +601,24 @@ _sidebar:
|
||||
hide: "隱藏"
|
||||
_wordMute:
|
||||
muteWords: "加入靜音文字"
|
||||
mutedNotes: "已靜音的貼文"
|
||||
_theme:
|
||||
constant: "常數"
|
||||
defaultValue: "預設值"
|
||||
color: "顏色"
|
||||
func: "函数"
|
||||
argument: "引數"
|
||||
alpha: "透明度"
|
||||
darken: "暗度"
|
||||
lighten: "亮度"
|
||||
keys:
|
||||
bg: "背景"
|
||||
fg: "文本"
|
||||
shadow: "陰影"
|
||||
link: "鏈接"
|
||||
hashtag: "#tag"
|
||||
mention: "提及"
|
||||
mentionMe: "提及我"
|
||||
renote: "轉發貼文"
|
||||
divider: "分割線"
|
||||
infoBg: "資訊背景"
|
||||
@ -633,6 +669,8 @@ _tutorial:
|
||||
step6_3: "在他人的貼文按下「+」的圖示即可選擇想要的表情符號來進行「反應」。"
|
||||
step7_1: "以上為Misskey的基本操作說明,教學在此告一段落。辛苦了。"
|
||||
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
|
||||
_2fa:
|
||||
registerDevice: "註冊裝置"
|
||||
_permissions:
|
||||
"read:blocks": "已封鎖用戶名單"
|
||||
"write:blocks": "編輯已封鎖用戶名單"
|
||||
@ -641,15 +679,26 @@ _permissions:
|
||||
"read:favorites": "瀏覽已收藏"
|
||||
"write:favorites": "編輯收藏清單"
|
||||
"write:following": "追隨/解除追隨"
|
||||
"read:messaging": "顯示訊息"
|
||||
"write:messaging": "撰寫或刪除私人訊息"
|
||||
"read:mutes": "顯示已靜音列表"
|
||||
"write:mutes": "編輯已靜音列表"
|
||||
"write:notes": "撰寫或刪除貼文"
|
||||
"read:notifications": "查看通知"
|
||||
"write:notifications": "編輯通知"
|
||||
"read:reactions": "查看反應"
|
||||
"write:reactions": "編輯反應"
|
||||
"write:votes": "投票"
|
||||
"read:pages": "顯示頁面"
|
||||
"write:pages": "編輯頁面"
|
||||
"read:page-likes": "顯示頁面的已喜歡"
|
||||
"write:page-likes": "編輯頁面上喜歡"
|
||||
"read:user-groups": "顯示使用者群組"
|
||||
"write:user-groups": "編輯使用者群組"
|
||||
"read:channels": "已查看的頻道"
|
||||
"write:channels": "操作頻道"
|
||||
"write:channels": "編輯頻道"
|
||||
_auth:
|
||||
shareAccess: "要授權「“{name}”」存取您的帳戶嗎?"
|
||||
_antennaSources:
|
||||
all: "全部貼文"
|
||||
homeTimeline: "來自已追隨使用者的貼文"
|
||||
@ -685,14 +734,17 @@ _poll:
|
||||
noOnlyOneChoice: "至少需要兩個選項。"
|
||||
expiration: "期限"
|
||||
infinite: "無期限"
|
||||
at: "結束時間"
|
||||
deadlineDate: "截止日期"
|
||||
deadlineTime: "小時"
|
||||
duration: "時長"
|
||||
votesCount: "{n}票"
|
||||
totalVotes: "一共{n}票"
|
||||
vote: "投票"
|
||||
showResult: "顯示結果"
|
||||
voted: "已投票"
|
||||
closed: "已結束"
|
||||
remainingDays: "{d}天{h}小時後結束"
|
||||
_visibility:
|
||||
public: "公開"
|
||||
home: "首頁"
|
||||
@ -723,7 +775,7 @@ _profile:
|
||||
_exportOrImport:
|
||||
allNotes: "全部貼文"
|
||||
followingList: "追隨中"
|
||||
muteList: "消音"
|
||||
muteList: "靜音"
|
||||
blockingList: "封鎖"
|
||||
userLists: "清單"
|
||||
_charts:
|
||||
@ -820,10 +872,13 @@ _pages:
|
||||
variables: "變數"
|
||||
title: "標題"
|
||||
url: "頁面網址"
|
||||
font: "字型"
|
||||
fontSerif: "襯線體"
|
||||
fontSansSerif: "無襯線體"
|
||||
inputBlocks: "輸入"
|
||||
blocks:
|
||||
text: "文本"
|
||||
textarea: "文字區域"
|
||||
section: "區段"
|
||||
image: "圖片"
|
||||
button: "按鈕"
|
||||
@ -832,17 +887,25 @@ _pages:
|
||||
variable: "變數"
|
||||
_post:
|
||||
text: "内容"
|
||||
canvasId: "畫布ID"
|
||||
textInput: "插入文字"
|
||||
_textInput:
|
||||
name: "變數名稱"
|
||||
text: "標題"
|
||||
default: "預設值"
|
||||
textareaInput: "多行文字输入"
|
||||
_textareaInput:
|
||||
name: "變數名稱"
|
||||
text: "標題"
|
||||
default: "預設值"
|
||||
numberInput: "輸入數值"
|
||||
_numberInput:
|
||||
name: "變數名稱"
|
||||
text: "標題"
|
||||
default: "預設值"
|
||||
canvas: "畫布"
|
||||
_canvas:
|
||||
id: "畫布ID"
|
||||
width: "寬度"
|
||||
height: "高度"
|
||||
switch: "開關"
|
||||
@ -858,6 +921,7 @@ _pages:
|
||||
_button:
|
||||
text: "標題"
|
||||
colored: "彩色"
|
||||
action: "按下按鈕後發生的行為"
|
||||
_action:
|
||||
_dialog:
|
||||
content: "内容"
|
||||
@ -873,6 +937,7 @@ _pages:
|
||||
_radioButton:
|
||||
name: "變數名稱"
|
||||
title: "標題"
|
||||
default: "預設值"
|
||||
script:
|
||||
categories:
|
||||
logical: "邏輯運算"
|
||||
@ -888,6 +953,8 @@ _pages:
|
||||
text: "文本"
|
||||
multiLineText: "文本 (多行)"
|
||||
textList: "文本列表"
|
||||
_strLen:
|
||||
arg1: "文本"
|
||||
_strPick:
|
||||
arg1: "文本"
|
||||
arg2: "字元位置"
|
||||
@ -1001,6 +1068,8 @@ _pages:
|
||||
arg1: "文字"
|
||||
_numberToString:
|
||||
arg1: "數值"
|
||||
_splitStrByLine:
|
||||
arg1: "文本"
|
||||
ref: "變數"
|
||||
aiScriptVar: "AiScript的變數"
|
||||
fn: "函数"
|
||||
@ -1016,6 +1085,7 @@ _pages:
|
||||
array: "清單"
|
||||
stringArray: "文本列表"
|
||||
enviromentVariables: "環境變數"
|
||||
pageVariables: "頁面元素"
|
||||
_relayStatus:
|
||||
requesting: "等待核准"
|
||||
accepted: "已通過核准"
|
||||
@ -1034,7 +1104,13 @@ _notification:
|
||||
renote: "轉發貼文"
|
||||
quote: "引用"
|
||||
reaction: "反應"
|
||||
receiveFollowRequest: "已收到追隨請求"
|
||||
followRequestAccepted: "追隨請求已接受"
|
||||
app: "應用程式通知"
|
||||
_deck:
|
||||
alwaysShowMainColumn: "總是顯示主欄"
|
||||
columnAlign: "對齊欄位"
|
||||
addColumn: "新增欄位"
|
||||
swapLeft: "向左移動"
|
||||
swapRight: "向右移動"
|
||||
swapUp: "往上移動"
|
||||
|
14
migration/1604821689616-delete-auto-watch.ts
Normal file
14
migration/1604821689616-delete-auto-watch.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class deleteAutoWatch1604821689616 implements MigrationInterface {
|
||||
name = 'deleteAutoWatch1604821689616'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "autoWatch"`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD "autoWatch" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
}
|
57
package.json
57
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||
"version": "12.51.0",
|
||||
"version": "12.56.0",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -99,51 +99,50 @@
|
||||
"@types/tmp": "0.2.0",
|
||||
"@types/uuid": "8.3.0",
|
||||
"@types/web-push": "3.3.0",
|
||||
"@types/webpack": "4.41.22",
|
||||
"@types/webpack": "4.41.24",
|
||||
"@types/webpack-stream": "3.2.11",
|
||||
"@types/websocket": "1.0.1",
|
||||
"@types/ws": "7.2.7",
|
||||
"@typescript-eslint/parser": "4.4.0",
|
||||
"@typescript-eslint/parser": "4.6.1",
|
||||
"@vue/compiler-sfc": "3.0.2",
|
||||
"abort-controller": "3.0.0",
|
||||
"apexcharts": "3.22.0",
|
||||
"apexcharts": "3.22.1",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.770.0",
|
||||
"aws-sdk": "2.787.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "1.1.3",
|
||||
"bull": "3.18.0",
|
||||
"bull": "3.18.1",
|
||||
"cafy": "15.2.1",
|
||||
"cbor": "5.1.0",
|
||||
"chalk": "4.1.0",
|
||||
"chart.js": "2.9.3",
|
||||
"chart.js": "2.9.4",
|
||||
"cli-highlight": "2.1.4",
|
||||
"commander": "4.1.1",
|
||||
"content-disposition": "0.5.3",
|
||||
"core-js": "3.6.5",
|
||||
"core-js": "3.7.0",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "5.0.0",
|
||||
"css-loader": "5.0.1",
|
||||
"cssnano": "4.1.10",
|
||||
"dateformat": "3.0.3",
|
||||
"deep-entries": "3.1.0",
|
||||
"diskusage": "1.1.3",
|
||||
"double-ended-queue": "2.1.0-0",
|
||||
"escape-regexp": "0.0.1",
|
||||
"eslint": "7.11.0",
|
||||
"eslint": "7.12.1",
|
||||
"eslint-plugin-vue": "7.1.0",
|
||||
"eventemitter3": "4.0.7",
|
||||
"feed": "4.2.1",
|
||||
"fibers": "5.0.0",
|
||||
"file-type": "16.0.0",
|
||||
"file-type": "16.0.1",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"glob": "7.1.6",
|
||||
"got": "11.7.0",
|
||||
"got": "11.8.0",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-rename": "2.0.0",
|
||||
"gulp-replace": "1.0.0",
|
||||
"gulp-sourcemaps": "2.6.5",
|
||||
"gulp-terser": "1.4.0",
|
||||
"gulp-tslint": "8.1.4",
|
||||
"gulp-typescript": "6.0.0-alpha.1",
|
||||
"hard-source-webpack-plugin": "0.13.1",
|
||||
@ -176,24 +175,24 @@
|
||||
"lookup-dns-cache": "2.1.0",
|
||||
"markdown-it": "11.0.1",
|
||||
"markdown-it-anchor": "6.0.0",
|
||||
"mocha": "8.1.3",
|
||||
"mocha": "8.2.1",
|
||||
"moji": "0.5.1",
|
||||
"ms": "2.1.2",
|
||||
"multer": "1.4.2",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "2.6.1",
|
||||
"nodemailer": "6.4.13",
|
||||
"nodemailer": "6.4.15",
|
||||
"object-assign-deep": "0.4.0",
|
||||
"os-utils": "0.0.14",
|
||||
"p-cancelable": "2.0.0",
|
||||
"parse5": "6.0.1",
|
||||
"parsimmon": "1.16.0",
|
||||
"pg": "8.4.1",
|
||||
"pg": "8.4.2",
|
||||
"portscanner": "2.2.0",
|
||||
"postcss": "8.1.3",
|
||||
"postcss": "8.1.6",
|
||||
"postcss-loader": "4.0.4",
|
||||
"prismjs": "1.22.0",
|
||||
"probe-image-size": "5.0.0",
|
||||
"probe-image-size": "6.0.0",
|
||||
"promise-limit": "2.7.0",
|
||||
"promise-sequential": "1.1.1",
|
||||
"pug": "2.0.4",
|
||||
@ -215,8 +214,8 @@
|
||||
"rimraf": "3.0.2",
|
||||
"rndstr": "1.0.0",
|
||||
"s-age": "1.1.2",
|
||||
"sass": "1.27.0",
|
||||
"sass-loader": "10.0.4",
|
||||
"sass": "1.29.0",
|
||||
"sass-loader": "10.0.5",
|
||||
"seedrandom": "3.0.5",
|
||||
"sharp": "0.26.2",
|
||||
"speakeasy": "2.0.0",
|
||||
@ -224,18 +223,18 @@
|
||||
"style-loader": "2.0.0",
|
||||
"summaly": "2.4.0",
|
||||
"syslog-pro": "1.0.0",
|
||||
"systeminformation": "4.27.10",
|
||||
"systeminformation": "4.28.1",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.117.1",
|
||||
"tinycolor2": "1.4.2",
|
||||
"tmp": "0.2.1",
|
||||
"ts-loader": "8.0.6",
|
||||
"ts-loader": "8.0.9",
|
||||
"ts-node": "9.0.0",
|
||||
"tslint": "6.1.3",
|
||||
"tslint-sonarts": "1.9.0",
|
||||
"typeorm": "0.2.28",
|
||||
"typescript": "4.0.3",
|
||||
"typeorm": "0.2.29",
|
||||
"typescript": "4.0.5",
|
||||
"ulid": "2.3.0",
|
||||
"url-loader": "4.1.1",
|
||||
"uuid": "8.3.1",
|
||||
@ -244,17 +243,17 @@
|
||||
"vue-color": "2.7.1",
|
||||
"vue-draggable-next": "1.0.8",
|
||||
"vue-i18n": "9.0.0-beta.6",
|
||||
"vue-json-pretty": "1.7.0",
|
||||
"vue-json-pretty": "1.7.1",
|
||||
"vue-loader": "16.0.0-beta.8",
|
||||
"vue-prism-editor": "1.2.2",
|
||||
"vue-router": "4.0.0-beta.13",
|
||||
"vue-router": "4.0.0-rc.2",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-template-compiler": "2.6.12",
|
||||
"vuex": "4.0.0-beta.4",
|
||||
"vuex": "4.0.0-rc.1",
|
||||
"vuex-persistedstate": "3.1.0",
|
||||
"web-push": "3.4.4",
|
||||
"webpack": "5.2.0",
|
||||
"webpack-cli": "4.1.0",
|
||||
"webpack": "5.4.0",
|
||||
"webpack-cli": "4.2.0",
|
||||
"websocket": "1.0.32",
|
||||
"ws": "7.3.1",
|
||||
"xev": "2.0.1"
|
||||
|
@ -28,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { emojilist } from '../../misc/emojilist';
|
||||
import contains from '@/scripts/contains';
|
||||
import { twemojiSvgBase } from '../../misc/twemoji-base';
|
||||
@ -122,17 +122,13 @@ export default defineComponent({
|
||||
users: [],
|
||||
hashtags: [],
|
||||
emojis: [],
|
||||
items: [],
|
||||
select: -1,
|
||||
emojilist,
|
||||
emojiDb: [] as EmojiDef[]
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
items(): HTMLCollection {
|
||||
return (this.$refs.suggests as Element).children;
|
||||
},
|
||||
|
||||
useOsNativeEmojis(): boolean {
|
||||
return this.$store.state.device.useOsNativeEmojis;
|
||||
}
|
||||
@ -148,6 +144,7 @@ export default defineComponent({
|
||||
|
||||
updated() {
|
||||
this.setPosition();
|
||||
this.items = (this.$refs.suggests as Element | undefined)?.children || [];
|
||||
},
|
||||
|
||||
mounted() {
|
||||
@ -180,7 +177,7 @@ export default defineComponent({
|
||||
|
||||
emojiDefinitions.sort((a, b) => a.name.length - b.name.length);
|
||||
|
||||
this.emojiDb = emojiDefinitions.concat(emjdb);
|
||||
this.emojiDb = markRaw(emojiDefinitions.concat(emjdb));
|
||||
//#endregion
|
||||
|
||||
this.textarea.addEventListener('keydown', this.onKeydown);
|
||||
@ -371,6 +368,7 @@ export default defineComponent({
|
||||
|
||||
selectNext() {
|
||||
if (++this.select >= this.items.length) this.select = 0;
|
||||
if (this.items.length === 0) this.select = -1;
|
||||
this.applySelect();
|
||||
},
|
||||
|
||||
@ -384,8 +382,10 @@ export default defineComponent({
|
||||
el.removeAttribute('data-selected');
|
||||
}
|
||||
|
||||
this.items[this.select].setAttribute('data-selected', 'true');
|
||||
(this.items[this.select] as any).focus();
|
||||
if (this.select !== -1) {
|
||||
this.items[this.select].setAttribute('data-selected', 'true');
|
||||
(this.items[this.select] as any).focus();
|
||||
}
|
||||
},
|
||||
|
||||
chooseUser() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
|
||||
<div class="banner" v-if="channel.bannerUrl" :style="`background-image: url('${channel.bannerUrl}')`">
|
||||
<div class="banner" :style="bannerStyle">
|
||||
<div class="fade"></div>
|
||||
<div class="name"><Fa :icon="faSatelliteDish"/> {{ channel.name }}</div>
|
||||
<div class="status">
|
||||
@ -45,6 +45,16 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
bannerStyle() {
|
||||
if (this.channel.bannerUrl) {
|
||||
return { backgroundImage: `url(${this.channel.bannerUrl})` };
|
||||
} else {
|
||||
return { backgroundColor: '#4c5e6d' };
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faSatelliteDish, faUsers, faPencilAlt,
|
||||
|
@ -169,15 +169,15 @@ export default defineComponent({
|
||||
font-size: 32px;
|
||||
|
||||
&.success {
|
||||
color: var(--accent);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
&.error {
|
||||
color: #ec4137;
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
&.warning {
|
||||
color: #ecb637;
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
> * {
|
||||
|
70
src/client/components/drive-select-dialog.vue
Normal file
70
src/client/components/drive-select-dialog.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<XModalWindow ref="dialog"
|
||||
:width="800"
|
||||
:height="500"
|
||||
:with-ok-button="true"
|
||||
:ok-button-disabled="(type === 'file') && (selected.length === 0)"
|
||||
@click="cancel()"
|
||||
@close="cancel()"
|
||||
@ok="ok()"
|
||||
@closed="$emit('closed')"
|
||||
>
|
||||
<template #header>
|
||||
{{ multiple ? ((type === 'file') ? $t('selectFiles') : $t('selectFolders')) : ((type === 'file') ? $t('selectFile') : $t('selectFolder')) }}
|
||||
<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span>
|
||||
</template>
|
||||
<XDrive :multiple="multiple" @changeSelection="onChangeSelection" @selected="ok()" :select="type"/>
|
||||
</XModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XDrive from './drive.vue';
|
||||
import XModalWindow from '@/components/ui/modal-window.vue';
|
||||
import number from '@/filters/number';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XDrive,
|
||||
XModalWindow,
|
||||
},
|
||||
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'file'
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['done', 'closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
selected: []
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
ok() {
|
||||
this.$emit('done', this.selected);
|
||||
this.$refs.dialog.close();
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.$emit('done');
|
||||
this.$refs.dialog.close();
|
||||
},
|
||||
|
||||
onChangeSelection(xs) {
|
||||
this.selected = xs;
|
||||
},
|
||||
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,72 +1,44 @@
|
||||
<template>
|
||||
<XModalWindow ref="dialog"
|
||||
:width="800"
|
||||
:height="500"
|
||||
:with-ok-button="true"
|
||||
:ok-button-disabled="(type === 'file') && (selected.length === 0)"
|
||||
@click="cancel()"
|
||||
@close="cancel()"
|
||||
@ok="ok()"
|
||||
<XWindow ref="window"
|
||||
:initial-width="800"
|
||||
:initial-height="500"
|
||||
:can-resize="true"
|
||||
@closed="$emit('closed')"
|
||||
>
|
||||
<template #header>
|
||||
{{ multiple ? ((type === 'file') ? $t('selectFiles') : $t('selectFolders')) : ((type === 'file') ? $t('selectFile') : $t('selectFolder')) }}
|
||||
<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span>
|
||||
{{ $t('drive') }}
|
||||
</template>
|
||||
<div>
|
||||
<XDrive :multiple="multiple" @changeSelection="onChangeSelection" @selected="ok()" :select="type"/>
|
||||
</div>
|
||||
</XModalWindow>
|
||||
<XDrive :initial-folder="initialFolder"/>
|
||||
</XWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XDrive from './drive.vue';
|
||||
import XModalWindow from '@/components/ui/modal-window.vue';
|
||||
import number from '@/filters/number';
|
||||
import XWindow from '@/components/ui/window.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XDrive,
|
||||
XModalWindow,
|
||||
XWindow,
|
||||
},
|
||||
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'file'
|
||||
initialFolder: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['done', 'closed'],
|
||||
emits: ['closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
selected: []
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
ok() {
|
||||
this.$emit('done', this.selected);
|
||||
this.$refs.dialog.close();
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.$emit('done');
|
||||
this.$refs.dialog.close();
|
||||
},
|
||||
|
||||
onChangeSelection(xs) {
|
||||
this.selected = xs;
|
||||
},
|
||||
|
||||
number
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<div class="rghtznwe"
|
||||
:class="{ draghover }"
|
||||
@click="onClick"
|
||||
@contextmenu.stop="onContextmenu"
|
||||
@mouseover="onMouseover"
|
||||
@mouseout="onMouseout"
|
||||
@dragover.prevent.stop="onDragover"
|
||||
@ -27,8 +28,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faFolder, faFolderOpen } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faFolder, faFolderOpen, faTrashAlt, faWindowRestore } from '@fortawesome/free-regular-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import { faICursor } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -241,6 +243,28 @@ export default defineComponent({
|
||||
value: this.folder.id
|
||||
});
|
||||
},
|
||||
|
||||
onContextmenu(e) {
|
||||
os.contextMenu([{
|
||||
text: this.$t('openInWindow'),
|
||||
icon: faWindowRestore,
|
||||
action: () => {
|
||||
os.popup(import('./drive-window.vue'), {
|
||||
initialFolder: this.folder
|
||||
}, {
|
||||
}, 'closed');
|
||||
}
|
||||
}, null, {
|
||||
text: this.$t('rename'),
|
||||
icon: faICursor,
|
||||
action: this.rename
|
||||
}, null, {
|
||||
text: this.$t('delete'),
|
||||
icon: faTrashAlt,
|
||||
danger: true,
|
||||
action: this.deleteFolder
|
||||
}], e);
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -64,7 +64,7 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
props: {
|
||||
initFolder: {
|
||||
initialFolder: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
@ -151,8 +151,8 @@ export default defineComponent({
|
||||
this.connection.on('folderUpdated', this.onStreamDriveFolderUpdated);
|
||||
this.connection.on('folderDeleted', this.onStreamDriveFolderDeleted);
|
||||
|
||||
if (this.initFolder) {
|
||||
this.move(this.initFolder);
|
||||
if (this.initialFolder) {
|
||||
this.move(this.initialFolder);
|
||||
} else {
|
||||
this.fetch();
|
||||
}
|
||||
@ -639,6 +639,10 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.yfudmmck {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
> nav {
|
||||
display: block;
|
||||
z-index: 2;
|
||||
@ -698,6 +702,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> .main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
|
||||
&, * {
|
||||
|
@ -1,75 +1,104 @@
|
||||
<template>
|
||||
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
|
||||
<div class="omfetrab _popup">
|
||||
<header>
|
||||
<button v-for="(category, i) in categories"
|
||||
class="_button"
|
||||
@click="go(category)"
|
||||
:class="{ active: category.isActive }"
|
||||
:key="i"
|
||||
>
|
||||
<Fa :icon="category.icon" fixed-width/>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="omfetrab _popup" :class="{ compact }">
|
||||
<input ref="search" class="search" :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$t('search')" @paste.stop="paste" @keyup.enter="done()">
|
||||
<div class="emojis">
|
||||
<template v-if="categories[0].isActive">
|
||||
<header class="category"><Fa :icon="faHistory" fixed-width/> {{ $t('recentUsed') }}</header>
|
||||
<div class="list">
|
||||
<button v-for="emoji in ($store.state.device.recentEmojis || [])"
|
||||
<section class="result">
|
||||
<div v-if="searchResultCustom.length > 0">
|
||||
<button v-for="emoji in searchResultCustom"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji)"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>
|
||||
<img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<header class="category"><Fa :icon="faAsterisk" fixed-width/> {{ $t('customEmojis') }}</header>
|
||||
</template>
|
||||
|
||||
<template v-if="categories.find(x => x.isActive).name">
|
||||
<div class="list">
|
||||
<button v-for="emoji in emojilist.filter(e => e.category === categories.find(x => x.isActive).name)"
|
||||
<div v-if="searchResultUnicode.length > 0">
|
||||
<button v-for="emoji in searchResultUnicode"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji)"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji.name"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji :emoji="emoji.char"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="index">
|
||||
<section v-if="showPinned">
|
||||
<div>
|
||||
<button v-for="emoji in pinned"
|
||||
class="_button"
|
||||
@click="chosen(emoji, $event)"
|
||||
tabindex="0"
|
||||
>
|
||||
<MkEmoji :emoji="emoji" :normal="true"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $t('recentUsed') }}</header>
|
||||
<div>
|
||||
<button v-for="emoji in $store.state.device.recentlyUsedEmojis"
|
||||
class="_button"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji"
|
||||
>
|
||||
<MkEmoji :emoji="emoji" :normal="true"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="arrow"><Fa :icon="faChevronDown"/></div>
|
||||
</div>
|
||||
|
||||
<section v-for="category in customEmojiCategories" :key="'custom:' + category" class="custom">
|
||||
<header class="_acrylic" v-appear="() => visibleCategories[category] = true">{{ category || $t('other') }}</header>
|
||||
<div v-if="visibleCategories[category]">
|
||||
<button v-for="emoji in customEmojis.filter(e => e.category === category)"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji.name"
|
||||
>
|
||||
<img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-for="category in categories" :key="category.name" class="unicode">
|
||||
<header class="_acrylic" v-appear="() => category.isActive = true"><Fa :icon="category.icon" fixed-width/> {{ category.name }}</header>
|
||||
<div v-if="category.isActive">
|
||||
<button v-for="emoji in emojilist.filter(e => e.category === category.name)"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji, $event)"
|
||||
:key="emoji.name"
|
||||
>
|
||||
<MkEmoji :emoji="emoji.char"/>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-for="(key, i) in Object.keys(customEmojis)" :key="i">
|
||||
<header class="sub" v-if="key">{{ key }}</header>
|
||||
<div class="list">
|
||||
<button v-for="emoji in customEmojis[key]"
|
||||
class="_button"
|
||||
:title="emoji.name"
|
||||
@click="chosen(emoji)"
|
||||
:key="emoji.name"
|
||||
>
|
||||
<img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { emojilist } from '../../misc/emojilist';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faHistory, faUser } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faClock, faUser, faChevronDown } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons';
|
||||
import { groupByX } from '../../prelude/array';
|
||||
import MkModal from '@/components/ui/modal.vue';
|
||||
import Particle from '@/components/particle.vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -80,20 +109,30 @@ export default defineComponent({
|
||||
src: {
|
||||
required: false
|
||||
},
|
||||
showPinned: {
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
compact: {
|
||||
required: false
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['done', 'closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
emojilist,
|
||||
emojilist: markRaw(emojilist),
|
||||
getStaticImageUrl,
|
||||
customEmojis: {},
|
||||
faGlobe, faHistory,
|
||||
pinned: this.$store.state.settings.reactions,
|
||||
customEmojiCategories: this.$store.getters['instance/emojiCategories'],
|
||||
customEmojis: this.$store.state.instance.meta.emojis,
|
||||
visibleCategories: {},
|
||||
q: null,
|
||||
searchResultCustom: [],
|
||||
searchResultUnicode: [],
|
||||
faGlobe, faClock, faChevronDown,
|
||||
categories: [{
|
||||
icon: faAsterisk,
|
||||
isActive: true
|
||||
}, {
|
||||
name: 'face',
|
||||
icon: faLaugh,
|
||||
isActive: false
|
||||
@ -134,38 +173,212 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
let local = this.$store.state.instance.meta.emojis;
|
||||
local = groupByX(local, (x: any) => x.category || '');
|
||||
this.customEmojis = local;
|
||||
watch: {
|
||||
q() {
|
||||
if (this.q == null || this.q === '') {
|
||||
this.searchResultCustom = [];
|
||||
this.searchResultUnicode = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const q = this.q.replace(/:/g, '');
|
||||
|
||||
const searchCustom = () => {
|
||||
const max = 8;
|
||||
const emojis = this.customEmojis;
|
||||
const matches = new Set();
|
||||
|
||||
const exactMatch = emojis.find(e => e.name === q);
|
||||
if (exactMatch) matches.add(exactMatch);
|
||||
|
||||
if (q.includes(' ')) { // AND検索
|
||||
const keywords = q.split(' ');
|
||||
|
||||
// 名前にキーワードが含まれている
|
||||
for (const emoji of emojis) {
|
||||
if (keywords.every(keyword => emoji.name.includes(keyword))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
// 名前またはエイリアスにキーワードが含まれている
|
||||
for (const emoji of emojis) {
|
||||
if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.name.startsWith(q)) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.aliases.some(alias => alias.startsWith(q))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.name.includes(q)) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.aliases.some(alias => alias.includes(q))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
const searchUnicode = () => {
|
||||
const max = 8;
|
||||
const emojis = this.emojilist;
|
||||
const matches = new Set();
|
||||
|
||||
const exactMatch = emojis.find(e => e.name === q);
|
||||
if (exactMatch) matches.add(exactMatch);
|
||||
|
||||
if (q.includes(' ')) { // AND検索
|
||||
const keywords = q.split(' ');
|
||||
|
||||
// 名前にキーワードが含まれている
|
||||
for (const emoji of emojis) {
|
||||
if (keywords.every(keyword => emoji.name.includes(keyword))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
// 名前またはエイリアスにキーワードが含まれている
|
||||
for (const emoji of emojis) {
|
||||
if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.keywords.some(alias => alias.includes(keyword)))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.name.startsWith(q)) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.keywords.some(keyword => keyword.startsWith(q))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.name.includes(q)) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.keywords.some(keyword => keyword.includes(q))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
this.searchResultCustom = Array.from(searchCustom());
|
||||
this.searchResultUnicode = Array.from(searchUnicode());
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const isIos = navigator.userAgent.includes('WebKit') && !navigator.userAgent.includes('Chrome');
|
||||
if (!isIos) {
|
||||
this.$refs.search.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
go(category: any) {
|
||||
this.goCategory(category.name);
|
||||
getKey(emoji: any) {
|
||||
return typeof emoji === 'string' ? emoji : (emoji.char || `:${emoji.name}:`);
|
||||
},
|
||||
|
||||
goCategory(name: string) {
|
||||
let matched = false;
|
||||
for (const c of this.categories) {
|
||||
c.isActive = c.name === name;
|
||||
if (c.isActive) {
|
||||
matched = true;
|
||||
}
|
||||
chosen(emoji: any, ev) {
|
||||
if (ev) {
|
||||
const el = ev.currentTarget || ev.target;
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = rect.left + (el.clientWidth / 2);
|
||||
const y = rect.top + (el.clientHeight / 2);
|
||||
os.popup(Particle, { x, y }, {}, 'end');
|
||||
}
|
||||
if (!matched) {
|
||||
this.categories[0].isActive = true;
|
||||
}
|
||||
},
|
||||
|
||||
chosen(emoji: any) {
|
||||
const getKey = (emoji: any) => emoji.char || `:${emoji.name}:`;
|
||||
let recents = this.$store.state.device.recentEmojis || [];
|
||||
recents = recents.filter((e: any) => getKey(e) !== getKey(emoji));
|
||||
recents.unshift(emoji)
|
||||
this.$store.commit('device/set', { key: 'recentEmojis', value: recents.splice(0, 16) });
|
||||
this.$emit('done', getKey(emoji));
|
||||
const key = this.getKey(emoji);
|
||||
this.$emit('done', key);
|
||||
this.$refs.modal.close();
|
||||
|
||||
// 最近使った絵文字更新
|
||||
if (!this.pinned.includes(key)) {
|
||||
let recents = this.$store.state.device.recentlyUsedEmojis;
|
||||
recents = recents.filter((e: any) => e !== key);
|
||||
recents.unshift(key);
|
||||
this.$store.commit('device/set', { key: 'recentlyUsedEmojis', value: recents.splice(0, 16) });
|
||||
}
|
||||
},
|
||||
|
||||
paste(event) {
|
||||
const paste = (event.clipboardData || window.clipboardData).getData('text');
|
||||
if (this.done(paste)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
done(query) {
|
||||
if (query == null) query = this.q;
|
||||
if (query == null) return;
|
||||
const q = query.replace(/:/g, '');
|
||||
const exactMatchCustom = this.customEmojis.find(e => e.name === q);
|
||||
if (exactMatchCustom) {
|
||||
this.chosen(exactMatchCustom);
|
||||
return true;
|
||||
}
|
||||
const exactMatchUnicode = this.emojilist.find(e => e.char === q || e.name === q);
|
||||
if (exactMatchUnicode) {
|
||||
this.chosen(exactMatchUnicode);
|
||||
return true;
|
||||
}
|
||||
if (this.searchResultCustom.length > 0) {
|
||||
this.chosen(this.searchResultCustom[0]);
|
||||
return true;
|
||||
}
|
||||
if (this.searchResultUnicode.length > 0) {
|
||||
this.chosen(this.searchResultUnicode[0]);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
@ -173,87 +386,123 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.omfetrab {
|
||||
width: 350px;
|
||||
$eachSize: 40px;
|
||||
$pad: 8px;
|
||||
|
||||
> header {
|
||||
display: flex;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: ($eachSize * 7) + ($pad * 2);
|
||||
contain: content;
|
||||
--height: 300px;
|
||||
|
||||
> button {
|
||||
flex: 1;
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
transition: color 0.2s ease;
|
||||
&.compact {
|
||||
width: ($eachSize * 5) + ($pad * 2);
|
||||
--height: 210px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--fgHighlighted);
|
||||
transition: color 0s;
|
||||
}
|
||||
> .search {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
|
||||
&.active {
|
||||
color: var(--accent);
|
||||
transition: color 0s;
|
||||
}
|
||||
&:not(.filled) {
|
||||
order: 1;
|
||||
z-index: 2;
|
||||
box-shadow: 0px -1px 0 0px var(--divider);
|
||||
}
|
||||
}
|
||||
|
||||
> .emojis {
|
||||
height: 300px;
|
||||
height: var(--height);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
> header.category {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
padding: 8px;
|
||||
background: var(--panel);
|
||||
font-size: 12px;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header.sub {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
div.list {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
|
||||
> button {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
> .index {
|
||||
min-height: var(--height);
|
||||
position: relative;
|
||||
border-bottom: solid 1px var(--divider);
|
||||
|
||||
> .arrow {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 16px 0;
|
||||
text-align: center;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
section {
|
||||
> header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding: $pad;
|
||||
|
||||
> button {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
width: $eachSize;
|
||||
height: $eachSize;
|
||||
border-radius: 4px;
|
||||
|
||||
&:focus {
|
||||
outline: solid 2px var(--focus);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--accent);
|
||||
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> * {
|
||||
transform: scale(1.2);
|
||||
transition: transform 0s;
|
||||
font-size: 24px;
|
||||
height: 1.25em;
|
||||
vertical-align: -.25em;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> * {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
font-size: 28px;
|
||||
transition: transform 0.2s ease;
|
||||
pointer-events: none;
|
||||
&.result {
|
||||
border-bottom: solid 1px var(--divider);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.unicode {
|
||||
min-height: 384px;
|
||||
}
|
||||
|
||||
&.custom {
|
||||
min-height: 64px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt"/>
|
||||
<img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" :title="alt"/>
|
||||
<span v-else-if="char && useOsNativeEmojis">{{ char }}</span>
|
||||
<span v-else>:{{ name }}:</span>
|
||||
<span v-else>{{ emoji }}</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -12,13 +12,9 @@ import { twemojiSvgBase } from '../../misc/twemoji-base';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
emoji: {
|
||||
type: String,
|
||||
required: false
|
||||
required: true
|
||||
},
|
||||
normal: {
|
||||
type: Boolean,
|
||||
@ -49,6 +45,10 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
computed: {
|
||||
isCustom(): boolean {
|
||||
return this.emoji.startsWith(':');
|
||||
},
|
||||
|
||||
alt(): string {
|
||||
return this.customEmoji ? `:${this.customEmoji.name}:` : this.char;
|
||||
},
|
||||
@ -68,8 +68,8 @@ export default defineComponent({
|
||||
watch: {
|
||||
ce: {
|
||||
handler() {
|
||||
if (this.name) {
|
||||
const customEmoji = this.ce.find(x => x.name == this.name);
|
||||
if (this.isCustom) {
|
||||
const customEmoji = this.ce.find(x => x.name === this.emoji.substr(1, this.emoji.length - 2));
|
||||
if (customEmoji) {
|
||||
this.customEmoji = customEmoji;
|
||||
this.url = this.$store.state.device.disableShowingAnimatedImages
|
||||
@ -83,7 +83,7 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
created() {
|
||||
if (!this.name) {
|
||||
if (!this.isCustom) {
|
||||
this.char = this.emoji;
|
||||
}
|
||||
|
||||
|
@ -41,10 +41,13 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.xubzgfga {
|
||||
max-width: 1024px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
> header,
|
||||
> footer {
|
||||
align-self: center;
|
||||
display: inline-block;
|
||||
padding: 6px 9px;
|
||||
font-size: 90%;
|
||||
@ -60,7 +63,10 @@ export default defineComponent({
|
||||
|
||||
> img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
object-fit: contain;
|
||||
width: 100%;
|
||||
cursor: zoom-out;
|
||||
image-orientation: from-image;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ export default defineComponent({
|
||||
if (!document.body.contains(this.$el)) return;
|
||||
if (this.close) return;
|
||||
|
||||
const { dispose } = os.popup(await import('@/components/url-preview-popup.vue'), {
|
||||
const { dispose } = await os.popup(import('@/components/url-preview-popup.vue'), {
|
||||
url: this.url,
|
||||
source: this.$el
|
||||
});
|
||||
|
@ -32,8 +32,6 @@ export default defineComponent({
|
||||
raw: {
|
||||
default: false
|
||||
},
|
||||
// specify the parent element
|
||||
parentElement: {}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -66,7 +64,7 @@ export default defineComponent({
|
||||
|
||||
if (this.$refs.gridOuter) {
|
||||
let height = 287;
|
||||
const parent = this.parentElement || this.$parent.$el;
|
||||
const parent = this.$parent.$el;
|
||||
|
||||
if (this.$refs.gridOuter.clientHeight) {
|
||||
height = this.$refs.gridOuter.clientHeight;
|
||||
@ -81,11 +79,6 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
parentElement() {
|
||||
this.size();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -77,10 +77,66 @@ export default defineComponent({
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
||||
case 'big': {
|
||||
return h('strong', {
|
||||
style: `display: inline-block; font-size: 150%;` + (this.$store.state.device.animatedMfm ? 'animation: anime-tada 1s linear infinite both;' : ''),
|
||||
}, genEl(token.children));
|
||||
case 'fn': {
|
||||
// TODO: CSSを文字列で組み立てていくと token.node.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
|
||||
let style;
|
||||
switch (token.node.props.name) {
|
||||
case 'tada': {
|
||||
style = `font-size: 150%;` + (this.$store.state.device.animatedMfm ? 'animation: tada 1s linear infinite both;' : '');
|
||||
break;
|
||||
}
|
||||
case 'jelly': {
|
||||
const speed = token.node.props.args.speed || '1s';
|
||||
style = (this.$store.state.device.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
|
||||
break;
|
||||
}
|
||||
case 'twitch': {
|
||||
const speed = token.node.props.args.speed || '0.5s';
|
||||
style = this.$store.state.device.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : '';
|
||||
break;
|
||||
}
|
||||
case 'shake': {
|
||||
const speed = token.node.props.args.speed || '0.5s';
|
||||
style = this.$store.state.device.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : '';
|
||||
break;
|
||||
}
|
||||
case 'spin': {
|
||||
const direction =
|
||||
token.node.props.args.left ? 'reverse' :
|
||||
token.node.props.args.alternate ? 'alternate' :
|
||||
'normal';
|
||||
const anime =
|
||||
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';
|
||||
style = this.$store.state.device.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
|
||||
break;
|
||||
}
|
||||
case 'jump': {
|
||||
style = this.$store.state.device.animatedMfm ? 'animation: mfm-jump 0.75s linear infinite;' : '';
|
||||
break;
|
||||
}
|
||||
case 'bounce': {
|
||||
style = this.$store.state.device.animatedMfm ? 'animation: mfm-bounce 0.75s linear infinite; transform-origin: center bottom;' : '';
|
||||
break;
|
||||
}
|
||||
case 'flip': {
|
||||
const transform =
|
||||
(token.node.props.args.h && token.node.props.args.v) ? 'scale(-1, -1)' :
|
||||
token.node.props.args.v ? 'scaleY(-1)' :
|
||||
'scaleX(-1)';
|
||||
style = `transform: ${transform};`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (style == null) {
|
||||
return h('span', {}, ['[', token.node.props.name, ...genEl(token.children), ']']);
|
||||
} else {
|
||||
return h('span', {
|
||||
style: 'display: inline-block;' + style,
|
||||
}, genEl(token.children));
|
||||
}
|
||||
}
|
||||
|
||||
case 'small': {
|
||||
@ -95,36 +151,6 @@ export default defineComponent({
|
||||
}, genEl(token.children))];
|
||||
}
|
||||
|
||||
case 'motion': {
|
||||
return h('span', {
|
||||
style: 'display: inline-block;' + (this.$store.state.device.animatedMfm ? 'animation: anime-rubberBand 1s linear infinite both;' : ''),
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
||||
case 'spin': {
|
||||
const direction =
|
||||
token.node.props.attr == 'left' ? 'reverse' :
|
||||
token.node.props.attr == 'alternate' ? 'alternate' :
|
||||
'normal';
|
||||
const style = this.$store.state.device.animatedMfm
|
||||
? `animation: anime-spin 1.5s linear infinite; animation-direction: ${direction};` : '';
|
||||
return h('span', {
|
||||
style: 'display: inline-block;' + style
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
||||
case 'jump': {
|
||||
return h('span', {
|
||||
style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: anime-jump 0.75s linear infinite;' : 'display: inline-block;'
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
||||
case 'flip': {
|
||||
return h('span', {
|
||||
style: 'display: inline-block; transform: scaleX(-1);'
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
||||
case 'url': {
|
||||
return [h(MkUrl, {
|
||||
key: Math.random(),
|
||||
@ -186,17 +212,10 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
case 'title': {
|
||||
return [h('div', {
|
||||
class: 'title'
|
||||
}, genEl(token.children))];
|
||||
}
|
||||
|
||||
case 'emoji': {
|
||||
return [h(MkEmoji, {
|
||||
key: Math.random(),
|
||||
emoji: token.node.props.emoji,
|
||||
name: token.node.props.name,
|
||||
emoji: token.node.props.name ? `:${token.node.props.name}:` : token.node.props.emoji,
|
||||
customEmojis: this.customEmojis,
|
||||
normal: this.plain
|
||||
})];
|
||||
|
@ -13,6 +13,103 @@ export default defineComponent({
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@keyframes mfm-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes mfm-spinX {
|
||||
0% { transform: perspective(128px) rotateX(0deg); }
|
||||
100% { transform: perspective(128px) rotateX(360deg); }
|
||||
}
|
||||
|
||||
@keyframes mfm-spinY {
|
||||
0% { transform: perspective(128px) rotateY(0deg); }
|
||||
100% { transform: perspective(128px) rotateY(360deg); }
|
||||
}
|
||||
|
||||
@keyframes mfm-jump {
|
||||
0% { transform: translateY(0); }
|
||||
25% { transform: translateY(-16px); }
|
||||
50% { transform: translateY(0); }
|
||||
75% { transform: translateY(-8px); }
|
||||
100% { transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes mfm-bounce {
|
||||
0% { transform: translateY(0) scale(1, 1); }
|
||||
25% { transform: translateY(-16px) scale(1, 1); }
|
||||
50% { transform: translateY(0) scale(1, 1); }
|
||||
75% { transform: translateY(0) scale(1.5, 0.75); }
|
||||
100% { transform: translateY(0) scale(1, 1); }
|
||||
}
|
||||
|
||||
// const val = () => `translate(${Math.floor(Math.random() * 20) - 10}px, ${Math.floor(Math.random() * 20) - 10}px)`;
|
||||
// let css = '';
|
||||
// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
|
||||
@keyframes mfm-twitch {
|
||||
0% { transform: translate(7px, -2px) }
|
||||
5% { transform: translate(-3px, 1px) }
|
||||
10% { transform: translate(-7px, -1px) }
|
||||
15% { transform: translate(0px, -1px) }
|
||||
20% { transform: translate(-8px, 6px) }
|
||||
25% { transform: translate(-4px, -3px) }
|
||||
30% { transform: translate(-4px, -6px) }
|
||||
35% { transform: translate(-8px, -8px) }
|
||||
40% { transform: translate(4px, 6px) }
|
||||
45% { transform: translate(-3px, 1px) }
|
||||
50% { transform: translate(2px, -10px) }
|
||||
55% { transform: translate(-7px, 0px) }
|
||||
60% { transform: translate(-2px, 4px) }
|
||||
65% { transform: translate(3px, -8px) }
|
||||
70% { transform: translate(6px, 7px) }
|
||||
75% { transform: translate(-7px, -2px) }
|
||||
80% { transform: translate(-7px, -8px) }
|
||||
85% { transform: translate(9px, 3px) }
|
||||
90% { transform: translate(-3px, -2px) }
|
||||
95% { transform: translate(-10px, 2px) }
|
||||
100% { transform: translate(-2px, -6px) }
|
||||
}
|
||||
|
||||
// const val = () => `translate(${Math.floor(Math.random() * 6) - 3}px, ${Math.floor(Math.random() * 6) - 3}px) rotate(${Math.floor(Math.random() * 24) - 12}deg)`;
|
||||
// let css = '';
|
||||
// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
|
||||
@keyframes mfm-shake {
|
||||
0% { transform: translate(-3px, -1px) rotate(-8deg) }
|
||||
5% { transform: translate(0px, -1px) rotate(-10deg) }
|
||||
10% { transform: translate(1px, -3px) rotate(0deg) }
|
||||
15% { transform: translate(1px, 1px) rotate(11deg) }
|
||||
20% { transform: translate(-2px, 1px) rotate(1deg) }
|
||||
25% { transform: translate(-1px, -2px) rotate(-2deg) }
|
||||
30% { transform: translate(-1px, 2px) rotate(-3deg) }
|
||||
35% { transform: translate(2px, 1px) rotate(6deg) }
|
||||
40% { transform: translate(-2px, -3px) rotate(-9deg) }
|
||||
45% { transform: translate(0px, -1px) rotate(-12deg) }
|
||||
50% { transform: translate(1px, 2px) rotate(10deg) }
|
||||
55% { transform: translate(0px, -3px) rotate(8deg) }
|
||||
60% { transform: translate(1px, -1px) rotate(8deg) }
|
||||
65% { transform: translate(0px, -1px) rotate(-7deg) }
|
||||
70% { transform: translate(-1px, -3px) rotate(6deg) }
|
||||
75% { transform: translate(0px, -2px) rotate(4deg) }
|
||||
80% { transform: translate(-2px, -1px) rotate(3deg) }
|
||||
85% { transform: translate(1px, -3px) rotate(-10deg) }
|
||||
90% { transform: translate(1px, 0px) rotate(3deg) }
|
||||
95% { transform: translate(-2px, 0px) rotate(-3deg) }
|
||||
100% { transform: translate(2px, 1px) rotate(2deg) }
|
||||
}
|
||||
|
||||
@keyframes mfm-rubberBand {
|
||||
from { transform: scale3d(1, 1, 1); }
|
||||
30% { transform: scale3d(1.25, 0.75, 1); }
|
||||
40% { transform: scale3d(0.75, 1.25, 1); }
|
||||
50% { transform: scale3d(1.15, 0.85, 1); }
|
||||
65% { transform: scale3d(0.95, 1.05, 1); }
|
||||
75% { transform: scale3d(1.05, 0.95, 1); }
|
||||
to { transform: scale3d(1, 1, 1); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.havbbuyv {
|
||||
white-space: pre-wrap;
|
||||
@ -42,10 +139,5 @@ export default defineComponent({
|
||||
word-break: break-all;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
::v-deep(.title) {
|
||||
text-align: center;
|
||||
border-bottom: solid 1px var(--divider);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -41,7 +41,7 @@
|
||||
<div class="main">
|
||||
<XNoteHeader class="header" :note="appearNote" :mini="true"/>
|
||||
<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/>
|
||||
<div class="body" ref="noteBody">
|
||||
<div class="body">
|
||||
<p v-if="appearNote.cw != null" class="cw">
|
||||
<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis"/>
|
||||
<XCwButton v-model:value="showContent" :note="appearNote"/>
|
||||
@ -54,7 +54,7 @@
|
||||
<a class="rp" v-if="appearNote.renote != null">RN:</a>
|
||||
</div>
|
||||
<div class="files" v-if="appearNote.files.length > 0">
|
||||
<XMediaList :media-list="appearNote.files" :parent-element="noteBody"/>
|
||||
<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"/>
|
||||
@ -176,7 +176,6 @@ export default defineComponent({
|
||||
showContent: false,
|
||||
isDeleted: false,
|
||||
muted: false,
|
||||
noteBody: this.$refs.noteBody,
|
||||
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
|
||||
};
|
||||
},
|
||||
@ -309,8 +308,6 @@ export default defineComponent({
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection.on('_connected_', this.onStreamConnected);
|
||||
}
|
||||
|
||||
this.noteBody = this.$refs.noteBody;
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
@ -501,9 +498,9 @@ export default defineComponent({
|
||||
react(viaKeyboard = false) {
|
||||
pleaseLogin();
|
||||
this.blur();
|
||||
os.popup(defineAsyncComponent(() => import('@/components/reaction-picker.vue')), {
|
||||
showFocus: viaKeyboard,
|
||||
os.popup(import('@/components/emoji-picker.vue'), {
|
||||
src: this.$refs.reactButton,
|
||||
compact: !this.$store.state.device.useFullReactionPicker
|
||||
}, {
|
||||
done: reaction => {
|
||||
if (reaction) {
|
||||
@ -647,7 +644,7 @@ export default defineComponent({
|
||||
text: this.$t('reportAbuse'),
|
||||
action: () => {
|
||||
const u = `${url}/notes/${this.appearNote.id}`;
|
||||
os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
|
||||
os.popup(import('@/components/abuse-report-window.vue'), {
|
||||
user: this.appearNote.user,
|
||||
initialComment: `Note: ${u}\n-----\n`
|
||||
}, {}, 'closed');
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<XWindow ref="window"
|
||||
:initial-width="400"
|
||||
:initial-width="700"
|
||||
:initial-height="500"
|
||||
:can-resize="true"
|
||||
:close-right="true"
|
||||
@ -22,12 +22,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft, faColumns } from '@fortawesome/free-solid-svg-icons';
|
||||
import XWindow from '@/components/ui/window.vue';
|
||||
import XHeader from '@/ui/_common_/header.vue';
|
||||
import { popout } from '@/scripts/popout';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@/router';
|
||||
import { url } from '@/config';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -35,16 +36,22 @@ export default defineComponent({
|
||||
XHeader,
|
||||
},
|
||||
|
||||
inject: {
|
||||
sideViewHook: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
navHook: (url) => {
|
||||
this.navigate(url);
|
||||
navHook: (path) => {
|
||||
this.navigate(path);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
initialUrl: {
|
||||
initialPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
@ -64,7 +71,7 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
pageInfo: null,
|
||||
url: this.initialUrl,
|
||||
path: this.initialPath,
|
||||
component: this.initialComponent,
|
||||
props: this.initialProps,
|
||||
history: [],
|
||||
@ -73,15 +80,26 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
computed: {
|
||||
url(): string {
|
||||
return url + this.path;
|
||||
},
|
||||
|
||||
contextmenu() {
|
||||
return [{
|
||||
type: 'label',
|
||||
text: this.url,
|
||||
text: this.path,
|
||||
}, {
|
||||
icon: faExpandAlt,
|
||||
text: this.$t('showInPage'),
|
||||
action: this.expand
|
||||
}, {
|
||||
}, this.sideViewHook ? {
|
||||
icon: faColumns,
|
||||
text: this.$t('openInSideView'),
|
||||
action: () => {
|
||||
this.sideViewHook(this.path);
|
||||
this.$refs.window.close();
|
||||
}
|
||||
} : undefined, {
|
||||
icon: faExternalLinkAlt,
|
||||
text: this.$t('popout'),
|
||||
action: this.popout
|
||||
@ -110,10 +128,10 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
navigate(url, record = true) {
|
||||
if (record) this.history.push(this.url);
|
||||
this.url = url;
|
||||
const { component, props } = resolve(url);
|
||||
navigate(path, record = true) {
|
||||
if (record) this.history.push(this.path);
|
||||
this.path = path;
|
||||
const { component, props } = resolve(path);
|
||||
this.component = component;
|
||||
this.props = props;
|
||||
},
|
||||
@ -123,12 +141,12 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
expand() {
|
||||
this.$router.push(this.url);
|
||||
this.$router.push(this.path);
|
||||
this.$refs.window.close();
|
||||
},
|
||||
|
||||
popout() {
|
||||
popout(this.url, this.$el);
|
||||
popout(this.path, this.$el);
|
||||
this.$refs.window.close();
|
||||
},
|
||||
},
|
||||
|
@ -378,13 +378,13 @@ export default defineComponent({
|
||||
this.saveDraft();
|
||||
},
|
||||
|
||||
async setVisibility() {
|
||||
setVisibility() {
|
||||
if (this.channel) {
|
||||
// TODO: information dialog
|
||||
return;
|
||||
}
|
||||
|
||||
os.popup(await import('./visibility-picker.vue'), {
|
||||
os.popup(import('./visibility-picker.vue'), {
|
||||
currentVisibility: this.visibility,
|
||||
currentLocalOnly: this.localOnly,
|
||||
src: this.$refs.visibilityButton
|
||||
@ -564,7 +564,7 @@ export default defineComponent({
|
||||
this.posting = false;
|
||||
os.dialog({
|
||||
type: 'error',
|
||||
text: err.message + '<br>' + (err as any).id,
|
||||
text: err.message + '\n' + (err as any).id,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<MkEmoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :customEmojis="customEmojis" :is-reaction="true" :normal="true" :no-style="noStyle"/>
|
||||
<MkEmoji :emoji="reaction" :custom-emojis="customEmojis" :is-reaction="true" :normal="true" :no-style="noStyle"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';import * as os from '@/os';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -1,214 +0,0 @@
|
||||
<template>
|
||||
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
|
||||
<div class="rdfaahpb _popup" v-hotkey="keymap">
|
||||
<div class="buttons" ref="buttons" :class="{ showFocus }">
|
||||
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction" v-particle><XReactionIcon :reaction="reaction"/></button>
|
||||
</div>
|
||||
<input class="text" ref="text" v-model.trim="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText">
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { emojiRegex } from '../../misc/emoji-regex';
|
||||
import XReactionIcon from '@/components/reaction-icon.vue';
|
||||
import MkModal from '@/components/ui/modal.vue';
|
||||
import { Autocomplete } from '@/scripts/autocomplete';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XReactionIcon,
|
||||
MkModal,
|
||||
},
|
||||
|
||||
props: {
|
||||
reactions: {
|
||||
required: false
|
||||
},
|
||||
|
||||
showFocus: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
|
||||
src: {
|
||||
required: false
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['done', 'closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
rs: this.reactions || this.$store.state.settings.reactions,
|
||||
text: null,
|
||||
focus: null
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
keymap(): any {
|
||||
return {
|
||||
'esc': this.close,
|
||||
'enter|space|plus': this.choose,
|
||||
'up|k': this.focusUp,
|
||||
'left|h|shift+tab': this.focusLeft,
|
||||
'right|l|tab': this.focusRight,
|
||||
'down|j': this.focusDown,
|
||||
'1': () => this.react(this.rs[0]),
|
||||
'2': () => this.react(this.rs[1]),
|
||||
'3': () => this.react(this.rs[2]),
|
||||
'4': () => this.react(this.rs[3]),
|
||||
'5': () => this.react(this.rs[4]),
|
||||
'6': () => this.react(this.rs[5]),
|
||||
'7': () => this.react(this.rs[6]),
|
||||
'8': () => this.react(this.rs[7]),
|
||||
'9': () => this.react(this.rs[8]),
|
||||
'0': () => this.react(this.rs[9]),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
focus(i) {
|
||||
this.$refs.buttons.children[i].focus({
|
||||
preventScroll: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.focus = 0;
|
||||
});
|
||||
|
||||
// TODO: detach when unmount
|
||||
new Autocomplete(this.$refs.text, this, { model: 'text' });
|
||||
},
|
||||
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit('done');
|
||||
this.$refs.modal.close();
|
||||
},
|
||||
|
||||
react(reaction) {
|
||||
this.$emit('done', reaction);
|
||||
this.$refs.modal.close();
|
||||
},
|
||||
|
||||
reactText() {
|
||||
if (!this.text) return;
|
||||
this.react(this.text);
|
||||
},
|
||||
|
||||
tryReactText() {
|
||||
if (!this.text) return;
|
||||
if (!this.text.match(emojiRegex)) return;
|
||||
this.reactText();
|
||||
},
|
||||
|
||||
focusUp() {
|
||||
this.focus = this.focus == 0 ? 9 : this.focus < 5 ? (this.focus + 4) : (this.focus - 5);
|
||||
},
|
||||
|
||||
focusDown() {
|
||||
this.focus = this.focus == 9 ? 0 : this.focus >= 5 ? (this.focus - 4) : (this.focus + 5);
|
||||
},
|
||||
|
||||
focusRight() {
|
||||
this.focus = this.focus == 9 ? 0 : (this.focus + 1);
|
||||
},
|
||||
|
||||
focusLeft() {
|
||||
this.focus = this.focus == 0 ? 9 : (this.focus - 1);
|
||||
},
|
||||
|
||||
choose() {
|
||||
this.$refs.buttons.children[this.focus].click();
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rdfaahpb {
|
||||
> .buttons {
|
||||
padding: 6px 6px 0 6px;
|
||||
width: 212px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 1025px) {
|
||||
padding: 8px 8px 0 8px;
|
||||
width: 256px;
|
||||
}
|
||||
|
||||
&.showFocus {
|
||||
> button:focus {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border: 2px solid var(--focus);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> button {
|
||||
padding: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 24px;
|
||||
border-radius: 2px;
|
||||
|
||||
@media (max-width: 1025px) {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
> * {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--accent);
|
||||
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .text {
|
||||
width: 208px;
|
||||
padding: 8px;
|
||||
margin: 0 0 6px 0;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
|
||||
@media (max-width: 1025px) {
|
||||
width: 256px;
|
||||
margin: 4px 0 8px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -11,14 +11,11 @@
|
||||
<transition name="nav">
|
||||
<nav class="nav" :class="{ iconOnly, hidden }" v-show="showing">
|
||||
<div>
|
||||
<button class="item _button account" @click="openAccountMenu" v-if="$store.getters.isSignedIn">
|
||||
<button class="item _button account" @click="openAccountMenu">
|
||||
<MkAvatar :user="$store.state.i" class="avatar"/><MkAcct class="text" :user="$store.state.i"/>
|
||||
</button>
|
||||
<button class="item _button index active" @click="top()" v-if="$route.name === 'index'">
|
||||
<Fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
|
||||
</button>
|
||||
<MkA class="item index" active-class="active" to="/" exact v-else>
|
||||
<Fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
|
||||
<MkA class="item index" active-class="active" to="/" exact>
|
||||
<Fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span>
|
||||
</MkA>
|
||||
<template v-for="item in menu">
|
||||
<div v-if="item === '-'" class="divider"></div>
|
||||
@ -28,7 +25,7 @@
|
||||
</component>
|
||||
</template>
|
||||
<div class="divider"></div>
|
||||
<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.getters.isSignedIn && ($store.state.i.isAdmin || $store.state.i.isModerator)" @click="oepnInstanceMenu">
|
||||
<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.state.i.isAdmin || $store.state.i.isModerator" @click="oepnInstanceMenu">
|
||||
<Fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span>
|
||||
</button>
|
||||
<button class="item _button" @click="more">
|
||||
@ -74,7 +71,6 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
otherNavItemIndicated(): boolean {
|
||||
if (!this.$store.getters.isSignedIn) return false;
|
||||
for (const def in this.menuDef) {
|
||||
if (this.menu.includes(def)) continue;
|
||||
if (this.menuDef[def].indicated) return true;
|
||||
@ -120,10 +116,6 @@ export default defineComponent({
|
||||
this.showing = true;
|
||||
},
|
||||
|
||||
top() {
|
||||
window.scroll({ top: 0, behavior: 'smooth' });
|
||||
},
|
||||
|
||||
search() {
|
||||
if (this.searching) return;
|
||||
|
||||
@ -257,8 +249,8 @@ export default defineComponent({
|
||||
}], ev.currentTarget || ev.target);
|
||||
},
|
||||
|
||||
async addAcount() {
|
||||
os.popup(await import('./signin-dialog.vue'), {}, {
|
||||
addAcount() {
|
||||
os.popup(import('./signin-dialog.vue'), {}, {
|
||||
done: res => {
|
||||
this.$store.dispatch('addAcount', res);
|
||||
os.success();
|
||||
@ -266,8 +258,8 @@ export default defineComponent({
|
||||
}, 'closed');
|
||||
},
|
||||
|
||||
async createAccount() {
|
||||
os.popup(await import('./signup-dialog.vue'), {}, {
|
||||
createAccount() {
|
||||
os.popup(import('./signup-dialog.vue'), {}, {
|
||||
done: res => {
|
||||
this.$store.dispatch('addAcount', res);
|
||||
this.switchAccountWithToken(res.i);
|
||||
@ -275,7 +267,7 @@ export default defineComponent({
|
||||
}, 'closed');
|
||||
},
|
||||
|
||||
async switchAccount(account: any) {
|
||||
switchAccount(account: any) {
|
||||
const token = this.$store.state.device.accounts.find((x: any) => x.id === account.id).token;
|
||||
this.switchAccountWithToken(token);
|
||||
},
|
||||
@ -422,9 +414,9 @@ export default defineComponent({
|
||||
> .item {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding-left: 32px;
|
||||
padding-left: 24px;
|
||||
font-size: $ui-font-size;
|
||||
line-height: 3.2rem;
|
||||
line-height: 3rem;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
@ -7,9 +7,7 @@
|
||||
>
|
||||
<template #header>{{ $t('login') }}</template>
|
||||
|
||||
<div class="_section">
|
||||
<MkSignin :auto-set="autoSet" @login="onLogin"/>
|
||||
</div>
|
||||
<MkSignin :auto-set="autoSet" @login="onLogin"/>
|
||||
</XModalWindow>
|
||||
</template>
|
||||
|
||||
|
@ -1,43 +1,47 @@
|
||||
<template>
|
||||
<form class="eppvobhk" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
|
||||
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
|
||||
<div class="normal-signin" v-if="!totpLogin">
|
||||
<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:value="onUsernameChange">
|
||||
<span>{{ $t('username') }}</span>
|
||||
<template #prefix>@</template>
|
||||
<template #suffix>@{{ host }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
|
||||
<span>{{ $t('password') }}</span>
|
||||
<template #prefix><Fa :icon="faLock"/></template>
|
||||
</MkInput>
|
||||
<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
|
||||
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><Fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
|
||||
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><Fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
|
||||
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><Fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
|
||||
</div>
|
||||
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
|
||||
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
|
||||
<p>{{ $t('tapSecurityKey') }}</p>
|
||||
<MkButton @click="queryKey" v-if="!queryingKey">
|
||||
{{ $t('retry') }}
|
||||
</MkButton>
|
||||
</div>
|
||||
<div class="or-hr" v-if="user && user.securityKeys">
|
||||
<p class="or-msg">{{ $t('or') }}</p>
|
||||
</div>
|
||||
<div class="twofa-group totp-group">
|
||||
<p style="margin-bottom:0;">{{ $t('twoStepAuthentication') }}</p>
|
||||
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="user && user.usePasswordLessLogin" required>
|
||||
<div class="auth _section">
|
||||
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
|
||||
<div class="normal-signin" v-if="!totpLogin">
|
||||
<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:value="onUsernameChange">
|
||||
<span>{{ $t('username') }}</span>
|
||||
<template #prefix>@</template>
|
||||
<template #suffix>@{{ host }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
|
||||
<span>{{ $t('password') }}</span>
|
||||
<template #prefix><Fa :icon="faLock"/></template>
|
||||
</MkInput>
|
||||
<MkInput v-model:value="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
|
||||
<span>{{ $t('token') }}</span>
|
||||
<template #prefix><Fa :icon="faGavel"/></template>
|
||||
</MkInput>
|
||||
<MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
|
||||
<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
|
||||
</div>
|
||||
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
|
||||
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
|
||||
<p>{{ $t('tapSecurityKey') }}</p>
|
||||
<MkButton @click="queryKey" v-if="!queryingKey">
|
||||
{{ $t('retry') }}
|
||||
</MkButton>
|
||||
</div>
|
||||
<div class="or-hr" v-if="user && user.securityKeys">
|
||||
<p class="or-msg">{{ $t('or') }}</p>
|
||||
</div>
|
||||
<div class="twofa-group totp-group">
|
||||
<p style="margin-bottom:0;">{{ $t('twoStepAuthentication') }}</p>
|
||||
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="user && user.usePasswordLessLogin" required>
|
||||
<span>{{ $t('password') }}</span>
|
||||
<template #prefix><Fa :icon="faLock"/></template>
|
||||
</MkInput>
|
||||
<MkInput v-model:value="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
|
||||
<span>{{ $t('token') }}</span>
|
||||
<template #prefix><Fa :icon="faGavel"/></template>
|
||||
</MkInput>
|
||||
<MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="social _section">
|
||||
<a class="_borderButton _vMargin" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><Fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
|
||||
<a class="_borderButton _vMargin" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><Fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
|
||||
<a class="_borderButton _vMargin" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><Fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
@ -203,14 +207,16 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.eppvobhk {
|
||||
> .avatar {
|
||||
margin: 0 auto 0 auto;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: #ddd;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
border-radius: 100%;
|
||||
> .auth {
|
||||
> .avatar {
|
||||
margin: 0 auto 0 auto;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: #ddd;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="pxhvhrfw" v-size="{ max: [500] }">
|
||||
<button v-for="item in items" class="_button" @click="$emit('update:value', item.value)" :class="{ active: value === item.value }" :key="item.value"><Fa v-if="item.icon" :icon="item.icon" class="icon"/>{{ item.label }}</button>
|
||||
<button v-for="item in items" class="_button" @click="$emit('update:value', item.value)" :class="{ active: value === item.value }" :disabled="value === item.value" :key="item.value"><Fa v-if="item.icon" :icon="item.icon" class="icon"/>{{ item.label }}</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -23,19 +23,26 @@ export default defineComponent({
|
||||
<style lang="scss" scoped>
|
||||
.pxhvhrfw {
|
||||
display: flex;
|
||||
max-width: var(--baseContentWidth);
|
||||
margin: 0 auto;
|
||||
|
||||
> button {
|
||||
flex: 1;
|
||||
padding: 15px 12px 12px 12px;
|
||||
border-bottom: solid 3px transparent;
|
||||
|
||||
&:disabled {
|
||||
opacity: 1 !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--accent);
|
||||
border-bottom-color: var(--accent);
|
||||
}
|
||||
|
||||
&:not(.active):hover {
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
|
||||
> .icon {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
70
src/client/components/taskmanager.api-window.vue
Normal file
70
src/client/components/taskmanager.api-window.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<XWindow ref="window"
|
||||
:initial-width="370"
|
||||
:initial-height="450"
|
||||
:can-resize="true"
|
||||
@close="$refs.window.close()"
|
||||
@closed="$emit('closed')"
|
||||
>
|
||||
<template #header>Req Viewer</template>
|
||||
|
||||
<div class="rlkneywz">
|
||||
<MkTab v-model:value="tab" :items="[{ label: 'Request', value: 'req', }, { label: 'Response', value: 'res', }]" style="border-bottom: solid 1px var(--divider);"/>
|
||||
|
||||
<code v-if="tab === 'req'">{{ reqStr }}</code>
|
||||
<code v-if="tab === 'res'">{{ resStr }}</code>
|
||||
</div>
|
||||
</XWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as JSON5 from 'json5';
|
||||
import XWindow from '@/components/ui/window.vue';
|
||||
import MkTab from '@/components/tab.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XWindow,
|
||||
MkTab,
|
||||
},
|
||||
|
||||
props: {
|
||||
req: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
tab: 'req',
|
||||
reqStr: JSON5.stringify(this.req.req, null, '\t'),
|
||||
resStr: JSON5.stringify(this.req.res, null, '\t'),
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rlkneywz {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
> code {
|
||||
display: block;
|
||||
flex: 1;
|
||||
padding: 8px;
|
||||
overflow: auto;
|
||||
font-size: 0.9em;
|
||||
tab-size: 2;
|
||||
white-space: pre;
|
||||
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||
}
|
||||
}
|
||||
</style>
|
231
src/client/components/taskmanager.vue
Normal file
231
src/client/components/taskmanager.vue
Normal file
@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<XWindow ref="window" :initial-width="650" :initial-height="420" :can-resize="true" @closed="$emit('closed')">
|
||||
<template #header>
|
||||
<Fa :icon="faTerminal" style="margin-right: 0.5em;"/>Task Manager
|
||||
</template>
|
||||
<div class="qljqmnzj">
|
||||
<MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/>
|
||||
|
||||
<div class="content">
|
||||
<div v-if="tab === 'windows'" class="windows" v-follow>
|
||||
<div class="header">
|
||||
<div>#ID</div>
|
||||
<div>Component</div>
|
||||
<div>Action</div>
|
||||
</div>
|
||||
<div v-for="p in popups">
|
||||
<div>#{{ p.id }}</div>
|
||||
<div>{{ p.component.name ? p.component.name : '<anonymous>' }}</div>
|
||||
<div><button class="_textButton" @click="killPopup(p)">Kill</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tab === 'stream'" class="stream" v-follow>
|
||||
<div class="header">
|
||||
<div>#ID</div>
|
||||
<div>Ch</div>
|
||||
<div>Handle</div>
|
||||
<div>In</div>
|
||||
<div>Out</div>
|
||||
</div>
|
||||
<div v-for="c in connections">
|
||||
<div>#{{ c.id }}</div>
|
||||
<div>{{ c.channel }}</div>
|
||||
<div v-if="c.users !== null">(shared)<span v-if="c.name">{{ ' ' + c.name }}</span></div>
|
||||
<div v-else>{{ c.name ? c.name : '<anonymous>' }}</div>
|
||||
<div>{{ c.in }}</div>
|
||||
<div>{{ c.out }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tab === 'streamPool'" class="streamPool" v-follow>
|
||||
<div class="header">
|
||||
<div>#ID</div>
|
||||
<div>Ch</div>
|
||||
<div>Users</div>
|
||||
</div>
|
||||
<div v-for="p in pools">
|
||||
<div>#{{ p.id }}</div>
|
||||
<div>{{ p.channel }}</div>
|
||||
<div>{{ p.users }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tab === 'api'" class="api" v-follow>
|
||||
<div class="header">
|
||||
<div>#ID</div>
|
||||
<div>Endpoint</div>
|
||||
<div>State</div>
|
||||
</div>
|
||||
<div v-for="req in apiRequests" @click="showReq(req)">
|
||||
<div>#{{ req.id }}</div>
|
||||
<div>{{ req.endpoint }}</div>
|
||||
<div class="state" :class="req.state">{{ req.state }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<div><span class="label">Windows</span>{{ popups.length }}</div>
|
||||
<div><span class="label">Stream</span>{{ connections.length }}</div>
|
||||
<div><span class="label">Stream (Pool)</span>{{ pools.length }}</div>
|
||||
</footer>
|
||||
</div>
|
||||
</XWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw, onBeforeUnmount, ref, shallowRef } from 'vue';
|
||||
import { faTerminal } from '@fortawesome/free-solid-svg-icons';
|
||||
import XWindow from '@/components/ui/window.vue';
|
||||
import MkTab from '@/components/tab.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import follow from '@/directives/follow-append';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XWindow,
|
||||
MkTab,
|
||||
MkButton,
|
||||
},
|
||||
|
||||
directives: {
|
||||
follow
|
||||
},
|
||||
|
||||
props: {
|
||||
},
|
||||
|
||||
emits: ['closed'],
|
||||
|
||||
setup() {
|
||||
const connections = shallowRef([]);
|
||||
const pools = shallowRef([]);
|
||||
const refreshStreamInfo = () => {
|
||||
console.log(os.stream.sharedConnectionPools, os.stream.sharedConnections, os.stream.nonSharedConnections);
|
||||
const conn = os.stream.sharedConnections.map(c => ({
|
||||
id: c.id, name: c.name, channel: c.channel, users: c.pool.users, in: c.inCount, out: c.outCount,
|
||||
})).concat(os.stream.nonSharedConnections.map(c => ({
|
||||
id: c.id, name: c.name, channel: c.channel, users: null, in: c.inCount, out: c.outCount,
|
||||
})));
|
||||
conn.sort((a, b) => (a.id > b.id) ? 1 : -1);
|
||||
connections.value = conn;
|
||||
pools.value = os.stream.sharedConnectionPools;
|
||||
};
|
||||
const interval = setInterval(refreshStreamInfo, 1000);
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(interval);
|
||||
});
|
||||
|
||||
const killPopup = p => {
|
||||
os.popups.value = os.popups.value.filter(x => x !== p);
|
||||
};
|
||||
|
||||
const showReq = req => {
|
||||
os.popup(import('./taskmanager.api-window.vue'), {
|
||||
req: req
|
||||
}, {
|
||||
}, 'closed');
|
||||
};
|
||||
|
||||
return {
|
||||
tab: ref('stream'),
|
||||
popups: os.popups,
|
||||
apiRequests: os.apiRequests,
|
||||
connections,
|
||||
pools,
|
||||
killPopup,
|
||||
showReq,
|
||||
faTerminal,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.qljqmnzj {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||
|
||||
> .content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
|
||||
> div {
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
|
||||
> div {
|
||||
display: table-row;
|
||||
|
||||
&:nth-child(even) {
|
||||
//background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&.header {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: table-cell;
|
||||
white-space: nowrap;
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.api {
|
||||
> div {
|
||||
&:not(.header) {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
|
||||
> .state {
|
||||
&.pending {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
&.success {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
&.failed {
|
||||
color: var(--error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> footer {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 8px 16px;
|
||||
box-sizing: border-box;
|
||||
border-top: solid 1px var(--divider);
|
||||
font-size: 0.9em;
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
|
||||
> .label {
|
||||
opacity: 0.7;
|
||||
margin-right: 0.5em;
|
||||
|
||||
&:after {
|
||||
content: ":";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -10,7 +10,8 @@ import { faExpandAlt, faColumns, faExternalLinkAlt, faLink, faWindowMaximize } f
|
||||
import * as os from '@/os';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { router } from '@/router';
|
||||
import { deckmode } from '@/config';
|
||||
import { ui, url } from '@/config';
|
||||
import { popout } from '@/scripts/popout';
|
||||
|
||||
export default defineComponent({
|
||||
inject: {
|
||||
@ -60,7 +61,7 @@ export default defineComponent({
|
||||
action: () => {
|
||||
os.pageWindow(this.to);
|
||||
}
|
||||
}, !this.navHook && this.sideViewHook ? {
|
||||
}, this.sideViewHook ? {
|
||||
icon: faColumns,
|
||||
text: this.$t('openInSideView'),
|
||||
action: () => {
|
||||
@ -82,16 +83,28 @@ export default defineComponent({
|
||||
icon: faLink,
|
||||
text: this.$t('copyLink'),
|
||||
action: () => {
|
||||
copyToClipboard(this.to);
|
||||
copyToClipboard(`${url}${this.to}`);
|
||||
}
|
||||
}], e);
|
||||
},
|
||||
|
||||
window() {
|
||||
os.pageWindow(this.to);
|
||||
},
|
||||
|
||||
popout() {
|
||||
popout(this.to);
|
||||
},
|
||||
|
||||
nav() {
|
||||
if (this.to.startsWith('/my/messaging')) {
|
||||
if (this.$store.state.device.chatOpenBehavior === 'window') return this.window();
|
||||
if (this.$store.state.device.chatOpenBehavior === 'popout') return this.popout();
|
||||
}
|
||||
|
||||
if (this.behavior) {
|
||||
if (this.behavior === 'window') {
|
||||
os.pageWindow(this.to);
|
||||
return;
|
||||
return this.window();
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,15 +112,20 @@ export default defineComponent({
|
||||
this.navHook(this.to);
|
||||
} else {
|
||||
if (this.$store.state.device.defaultSideView && this.sideViewHook && this.to !== '/') {
|
||||
this.sideViewHook(this.to);
|
||||
return;
|
||||
return this.sideViewHook(this.to);
|
||||
}
|
||||
if (this.$store.state.device.deckNavWindow && deckmode && this.to !== '/') {
|
||||
os.pageWindow(this.to);
|
||||
return;
|
||||
if (this.$store.state.device.deckNavWindow && (ui === 'deck') && this.to !== '/') {
|
||||
return this.window();
|
||||
}
|
||||
if (ui === 'desktop') {
|
||||
return this.window();
|
||||
}
|
||||
|
||||
this.$router.push(this.to);
|
||||
if (this.$router.currentRoute.value.path === this.to) {
|
||||
window.scroll({ top: 0, behavior: 'smooth' });
|
||||
} else {
|
||||
this.$router.push(this.to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,11 @@
|
||||
<span>{{ item.text }}</span>
|
||||
<i v-if="item.indicate"><Fa :icon="faCircle"/></i>
|
||||
</a>
|
||||
<button v-else-if="item.type === 'user'" @click="clicked(item.action)" :tabindex="i" class="_button item">
|
||||
<button v-else-if="item.type === 'user'" @click="clicked(item.action, $event)" :tabindex="i" class="_button item">
|
||||
<MkAvatar :user="item.user" class="avatar"/><MkUserName :user="item.user"/>
|
||||
<i v-if="item.indicate"><Fa :icon="faCircle"/></i>
|
||||
</button>
|
||||
<button v-else @click="clicked(item.action)" :tabindex="i" class="_button item" :class="{ danger: item.danger }">
|
||||
<button v-else @click="clicked(item.action, $event)" :tabindex="i" class="_button item" :class="{ danger: item.danger }">
|
||||
<Fa v-if="item.icon" :icon="item.icon" fixed-width/>
|
||||
<MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
|
||||
<span>{{ item.text }}</span>
|
||||
@ -115,8 +115,8 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clicked(fn) {
|
||||
fn();
|
||||
clicked(fn, ev) {
|
||||
fn(ev);
|
||||
this.close();
|
||||
},
|
||||
close() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="cxiknjgy" :class="{ autoMargin }">
|
||||
<div class="cxiknjgy">
|
||||
<slot :items="items"></slot>
|
||||
<div class="empty" v-if="empty" key="_empty_">
|
||||
<slot name="empty"></slot>
|
||||
@ -31,24 +31,12 @@ export default defineComponent({
|
||||
pagination: {
|
||||
required: true
|
||||
},
|
||||
autoMargin: {
|
||||
required: false,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cxiknjgy {
|
||||
&.autoMargin > *:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
> .more > .button {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -51,7 +50,7 @@ export default defineComponent({
|
||||
.novjtctn {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin: 16px 32px 0 0;
|
||||
margin: 8px 20px 0 0;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
58
src/client/components/ui/radios.vue
Normal file
58
src/client/components/ui/radios.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, h } from 'vue';
|
||||
import MkRadio from '@/components/ui/radio.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkRadio
|
||||
},
|
||||
props: {
|
||||
defs: {
|
||||
required: true
|
||||
},
|
||||
modelValue: {
|
||||
required: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: this.modelValue,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.$emit('update:modelValue', this.value);
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const label = this.$slots.desc();
|
||||
const options = this.$slots.default();
|
||||
|
||||
return h('div', {
|
||||
class: 'novjtcto'
|
||||
}, [
|
||||
h('div', label),
|
||||
...options.map(option => h(MkRadio, {
|
||||
key: option.props.value,
|
||||
value: option.props.value,
|
||||
modelValue: this.value,
|
||||
'onUpdate:modelValue': value => this.value = value,
|
||||
}, option.children))
|
||||
]);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.novjtcto {
|
||||
margin: 32px 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -119,6 +119,9 @@ export default defineComponent({
|
||||
z: Number(document.defaultView.getComputedStyle(this.$el, null).zIndex)
|
||||
});
|
||||
|
||||
// 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
|
||||
this.top();
|
||||
|
||||
window.addEventListener('resize', this.onBrowserResize);
|
||||
},
|
||||
|
||||
|
@ -71,7 +71,7 @@ export default defineComponent({
|
||||
if (!document.body.contains(this.$el)) return;
|
||||
if (this.close) return;
|
||||
|
||||
const { dispose } = os.popup(await import('@/components/url-preview-popup.vue'), {
|
||||
const { dispose } = await os.popup(import('@/components/url-preview-popup.vue'), {
|
||||
url: this.url,
|
||||
source: this.$el
|
||||
});
|
||||
|
@ -14,10 +14,10 @@
|
||||
<MkInput v-model:value="host" class="input" @update:value="search"><span>{{ $t('host') }}</span><template #prefix>@</template></MkInput>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tbhwbxda _section" :style="users.length > 0 ? 'padding: 0;' : ''">
|
||||
<div class="tbhwbxda _section result" v-if="username != '' || host != ''" :class="{ hit: users.length > 0 }">
|
||||
<div class="users" v-if="users.length > 0">
|
||||
<div class="user" v-for="user in users" :key="user.id" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
|
||||
<MkAvatar :user="user" class="avatar" :disable-link="true"/>
|
||||
<MkAvatar :user="user" class="avatar"/>
|
||||
<div class="body">
|
||||
<MkUserName :user="user" class="name"/>
|
||||
<MkAcct :user="user" class="acct"/>
|
||||
@ -28,6 +28,17 @@
|
||||
<span>{{ $t('noUsers') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tbhwbxda _section recent" v-if="username == '' && host == ''">
|
||||
<div class="users">
|
||||
<div class="user" v-for="user in recentUsers" :key="user.id" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
|
||||
<MkAvatar :user="user" class="avatar"/>
|
||||
<div class="body">
|
||||
<MkUserName :user="user" class="name"/>
|
||||
<MkAcct :user="user" class="acct"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</XModalWindow>
|
||||
</template>
|
||||
|
||||
@ -53,18 +64,23 @@ export default defineComponent({
|
||||
return {
|
||||
username: '',
|
||||
host: '',
|
||||
recentUsers: [],
|
||||
users: [],
|
||||
selected: null,
|
||||
faTimes, faCheck
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
async mounted() {
|
||||
this.focus();
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.focus();
|
||||
});
|
||||
|
||||
this.recentUsers = await os.api('users/show', {
|
||||
userIds: this.$store.state.device.recentlyUsedUsers
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
@ -90,6 +106,12 @@ export default defineComponent({
|
||||
ok() {
|
||||
this.$emit('ok', this.selected);
|
||||
this.$refs.dialog.close();
|
||||
|
||||
// 最近使ったユーザー更新
|
||||
let recents = this.$store.state.device.recentlyUsedUsers;
|
||||
recents = recents.filter(x => x !== this.selected.id);
|
||||
recents.unshift(this.selected.id);
|
||||
this.$store.commit('device/set', { key: 'recentlyUsedUsers', value: recents.splice(0, 16) });
|
||||
},
|
||||
|
||||
cancel() {
|
||||
@ -107,6 +129,14 @@ export default defineComponent({
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
|
||||
&.result.hit {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.recent {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
> .inputs {
|
||||
> .input {
|
||||
display: inline-block;
|
||||
|
@ -13,4 +13,5 @@ export const langs = _LANGS_;
|
||||
export const getLocale = async () => Object.fromEntries((await entries(clientDb.i18n)) as [string, string][]);
|
||||
export const version = _VERSION_;
|
||||
export const instanceName = siteName === 'Misskey' ? host : siteName;
|
||||
export const deckmode = localStorage.getItem('deckmode') === 'true';
|
||||
export const ui = localStorage.getItem('ui');
|
||||
export const debug = localStorage.getItem('debug') === 'true';
|
||||
|
25
src/client/directives/follow-append.ts
Normal file
25
src/client/directives/follow-append.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Directive } from 'vue';
|
||||
import { getScrollContainer, getScrollPosition } from '@/scripts/scroll';
|
||||
|
||||
export default {
|
||||
mounted(src, binding, vn) {
|
||||
const ro = new ResizeObserver((entries, observer) => {
|
||||
const pos = getScrollPosition(src);
|
||||
const container = getScrollContainer(src);
|
||||
const viewHeight = container.clientHeight;
|
||||
const height = container.scrollHeight;
|
||||
if (pos + viewHeight > height - 32) {
|
||||
container.scrollTop = height;
|
||||
}
|
||||
});
|
||||
|
||||
ro.observe(src);
|
||||
|
||||
// TODO: 新たにプロパティを作るのをやめMapを使う
|
||||
src._ro_ = ro;
|
||||
},
|
||||
|
||||
unmounted(src, binding, vn) {
|
||||
src._ro_.unobserve(src);
|
||||
}
|
||||
} as Directive;
|
@ -23,13 +23,13 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
const show = async e => {
|
||||
const show = e => {
|
||||
if (!document.body.contains(el)) return;
|
||||
if (self._close) return;
|
||||
if (self.text == null) return;
|
||||
|
||||
const showing = ref(true);
|
||||
popup(await import('@/components/ui/tooltip.vue'), {
|
||||
popup(import('@/components/ui/tooltip.vue'), {
|
||||
showing,
|
||||
text: self.text,
|
||||
source: el
|
||||
|
@ -18,13 +18,13 @@ export class UserPreview {
|
||||
}
|
||||
|
||||
@autobind
|
||||
private async show() {
|
||||
private show() {
|
||||
if (!document.body.contains(this.el)) return;
|
||||
if (this.promise) return;
|
||||
|
||||
const showing = ref(true);
|
||||
|
||||
popup(await import('@/components/user-preview.vue'), {
|
||||
popup(import('@/components/user-preview.vue'), {
|
||||
showing,
|
||||
q: this.user,
|
||||
source: this.el
|
||||
|
@ -4,13 +4,13 @@
|
||||
|
||||
import '@/style.scss';
|
||||
|
||||
import { createApp, defineAsyncComponent } from 'vue';
|
||||
import { createApp } from 'vue';
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||
|
||||
import widgets from './widgets';
|
||||
import directives from './directives';
|
||||
import components from '@/components';
|
||||
import { version, apiUrl, deckmode } from '@/config';
|
||||
import { version, apiUrl, ui } from '@/config';
|
||||
import { store } from './store';
|
||||
import { router } from './router';
|
||||
import { applyTheme } from '@/scripts/theme';
|
||||
@ -154,7 +154,8 @@ stream.init(store.state.i);
|
||||
const app = createApp(await (
|
||||
window.location.search === '?zen' ? import('@/ui/zen.vue') :
|
||||
!store.getters.isSignedIn ? import('@/ui/visitor.vue') :
|
||||
deckmode ? import('@/ui/deck.vue') :
|
||||
ui === 'deck' ? import('@/ui/deck.vue') :
|
||||
ui === 'desktop' ? import('@/ui/desktop.vue') :
|
||||
import('@/ui/default.vue')
|
||||
).then(x => x.default));
|
||||
|
||||
@ -252,7 +253,7 @@ if (store.getters.isSignedIn) {
|
||||
}
|
||||
}
|
||||
|
||||
const main = stream.useSharedConnection('main');
|
||||
const main = stream.useSharedConnection('main', 'System');
|
||||
|
||||
// 自分の情報が更新されたとき
|
||||
main.on('meUpdated', i => {
|
||||
|
@ -2,7 +2,7 @@ import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vu
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import Stream from '@/scripts/stream';
|
||||
import { store } from '@/store';
|
||||
import { apiUrl } from '@/config';
|
||||
import { apiUrl, debug } from '@/config';
|
||||
import MkPostFormDialog from '@/components/post-form-dialog.vue';
|
||||
import MkWaitingDialog from '@/components/waiting-dialog.vue';
|
||||
import { resolve } from '@/router';
|
||||
@ -13,28 +13,30 @@ export const isMobile = /mobile|iphone|ipad|android/.test(ua);
|
||||
export const stream = markRaw(new Stream());
|
||||
|
||||
export const pendingApiRequestsCount = ref(0);
|
||||
let apiRequestsCount = 0; // for debug
|
||||
export const apiRequests = ref([]); // for debug
|
||||
|
||||
export const windows = new Map();
|
||||
|
||||
export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) {
|
||||
pendingApiRequestsCount.value++;
|
||||
|
||||
if (_DEV_) {
|
||||
performance.mark(_PERF_PREFIX_ + 'api:begin');
|
||||
}
|
||||
|
||||
const onFinally = () => {
|
||||
pendingApiRequestsCount.value--;
|
||||
|
||||
if (_DEV_) {
|
||||
performance.mark(_PERF_PREFIX_ + 'api:end');
|
||||
|
||||
performance.measure(_PERF_PREFIX_ + 'api',
|
||||
_PERF_PREFIX_ + 'api:begin',
|
||||
_PERF_PREFIX_ + 'api:end');
|
||||
}
|
||||
};
|
||||
|
||||
const log = debug ? reactive({
|
||||
id: ++apiRequestsCount,
|
||||
endpoint,
|
||||
req: markRaw(data),
|
||||
res: null,
|
||||
state: 'pending',
|
||||
}) : null;
|
||||
if (debug) {
|
||||
apiRequests.value.push(log);
|
||||
if (apiRequests.value.length > 128) apiRequests.value.shift();
|
||||
}
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
// Append a credential
|
||||
if (store.getters.isSignedIn) (data as any).i = store.state.i.token;
|
||||
@ -51,10 +53,21 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
|
||||
|
||||
if (res.status === 200) {
|
||||
resolve(body);
|
||||
if (debug) {
|
||||
log.res = markRaw(body);
|
||||
log.state = 'success';
|
||||
}
|
||||
} else if (res.status === 204) {
|
||||
resolve();
|
||||
if (debug) {
|
||||
log.state = 'success';
|
||||
}
|
||||
} else {
|
||||
reject(body.error);
|
||||
if (debug) {
|
||||
log.res = markRaw(body.error);
|
||||
log.state = 'failed';
|
||||
}
|
||||
}
|
||||
}).catch(reject);
|
||||
});
|
||||
@ -75,7 +88,7 @@ export function apiWithDialog(
|
||||
promiseDialog(promise, onSuccess, onFailure ? onFailure : (e) => {
|
||||
dialog({
|
||||
type: 'error',
|
||||
text: e.message + '<br>' + (e as any).id,
|
||||
text: e.message + '\n' + (e as any).id,
|
||||
});
|
||||
});
|
||||
|
||||
@ -127,17 +140,20 @@ function isModule(x: any): x is typeof import('*.vue') {
|
||||
return x.default != null;
|
||||
}
|
||||
|
||||
let popupIdCount = 0;
|
||||
export const popups = ref([]) as Ref<{
|
||||
id: any;
|
||||
component: any;
|
||||
props: Record<string, any>;
|
||||
}[]>;
|
||||
|
||||
export function popup(component: Component | typeof import('*.vue'), props: Record<string, any>, events = {}, disposeEvent?: string) {
|
||||
export async function popup(component: Component | typeof import('*.vue') | Promise<Component | typeof import('*.vue')>, props: Record<string, any>, events = {}, disposeEvent?: string) {
|
||||
if (component.then) component = await component;
|
||||
|
||||
if (isModule(component)) component = component.default;
|
||||
markRaw(component);
|
||||
|
||||
const id = Math.random().toString(); // TODO: uuidとか使う
|
||||
const id = ++popupIdCount;
|
||||
const dispose = () => {
|
||||
if (_DEV_) console.log('os:popup close', id, component, props, events);
|
||||
// このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ?
|
||||
@ -163,10 +179,10 @@ export function popup(component: Component | typeof import('*.vue'), props: Reco
|
||||
};
|
||||
}
|
||||
|
||||
export function pageWindow(url: string) {
|
||||
const { component, props } = resolve(url);
|
||||
popup(defineAsyncComponent(() => import('@/components/page-window.vue')), {
|
||||
initialUrl: url,
|
||||
export function pageWindow(path: string) {
|
||||
const { component, props } = resolve(path);
|
||||
popup(import('@/components/page-window.vue'), {
|
||||
initialPath: path,
|
||||
initialComponent: markRaw(component),
|
||||
initialProps: props,
|
||||
}, {}, 'closed');
|
||||
@ -174,7 +190,7 @@ export function pageWindow(url: string) {
|
||||
|
||||
export function dialog(props: Record<string, any>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(defineAsyncComponent(() => import('@/components/dialog.vue')), props, {
|
||||
popup(import('@/components/dialog.vue'), props, {
|
||||
done: result => {
|
||||
resolve(result ? result : { canceled: true });
|
||||
},
|
||||
@ -188,7 +204,7 @@ export function success() {
|
||||
setTimeout(() => {
|
||||
showing.value = false;
|
||||
}, 1000);
|
||||
popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), {
|
||||
popup(import('@/components/waiting-dialog.vue'), {
|
||||
success: true,
|
||||
showing: showing
|
||||
}, {
|
||||
@ -200,7 +216,7 @@ export function success() {
|
||||
export function waiting() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const showing = ref(true);
|
||||
popup(defineAsyncComponent(() => import('@/components/waiting-dialog.vue')), {
|
||||
popup(import('@/components/waiting-dialog.vue'), {
|
||||
success: false,
|
||||
showing: showing
|
||||
}, {
|
||||
@ -211,7 +227,7 @@ export function waiting() {
|
||||
|
||||
export function form(title, form) {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(defineAsyncComponent(() => import('@/components/form-dialog.vue')), { title, form }, {
|
||||
popup(import('@/components/form-dialog.vue'), { title, form }, {
|
||||
done: result => {
|
||||
resolve(result);
|
||||
},
|
||||
@ -221,7 +237,7 @@ export function form(title, form) {
|
||||
|
||||
export async function selectUser() {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(defineAsyncComponent(() => import('@/components/user-select-dialog.vue')), {}, {
|
||||
popup(import('@/components/user-select-dialog.vue'), {}, {
|
||||
ok: user => {
|
||||
resolve(user);
|
||||
},
|
||||
@ -231,7 +247,7 @@ export async function selectUser() {
|
||||
|
||||
export async function selectDriveFile(multiple: boolean) {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(defineAsyncComponent(() => import('@/components/drive-window.vue')), {
|
||||
popup(import('@/components/drive-select-dialog.vue'), {
|
||||
type: 'file',
|
||||
multiple
|
||||
}, {
|
||||
@ -246,7 +262,7 @@ export async function selectDriveFile(multiple: boolean) {
|
||||
|
||||
export async function selectDriveFolder(multiple: boolean) {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(defineAsyncComponent(() => import('@/components/drive-window.vue')), {
|
||||
popup(import('@/components/drive-select-dialog.vue'), {
|
||||
type: 'folder',
|
||||
multiple
|
||||
}, {
|
||||
@ -259,10 +275,11 @@ export async function selectDriveFolder(multiple: boolean) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function pickEmoji(src?: HTMLElement) {
|
||||
export async function pickEmoji(src?: HTMLElement, opts) {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(defineAsyncComponent(() => import('@/components/emoji-picker.vue')), {
|
||||
src
|
||||
popup(import('@/components/emoji-picker.vue'), {
|
||||
src,
|
||||
...opts
|
||||
}, {
|
||||
done: emoji => {
|
||||
resolve(emoji);
|
||||
@ -273,7 +290,8 @@ export async function pickEmoji(src?: HTMLElement) {
|
||||
|
||||
export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/ui/modal-menu.vue')), {
|
||||
let dispose;
|
||||
popup(import('@/components/ui/modal-menu.vue'), {
|
||||
items,
|
||||
src,
|
||||
align: options?.align,
|
||||
@ -283,6 +301,8 @@ export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: s
|
||||
resolve();
|
||||
dispose();
|
||||
},
|
||||
}).then(res => {
|
||||
dispose = res.dispose;
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -290,7 +310,8 @@ export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: s
|
||||
export function contextMenu(items: any[], ev: MouseEvent) {
|
||||
ev.preventDefault();
|
||||
return new Promise((resolve, reject) => {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/ui/context-menu.vue')), {
|
||||
let dispose;
|
||||
popup(import('@/components/ui/context-menu.vue'), {
|
||||
items,
|
||||
ev,
|
||||
}, {
|
||||
@ -298,6 +319,8 @@ export function contextMenu(items: any[], ev: MouseEvent) {
|
||||
resolve();
|
||||
dispose();
|
||||
},
|
||||
}).then(res => {
|
||||
dispose = res.dispose;
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -309,11 +332,14 @@ export function post(props: Record<string, any>) {
|
||||
// Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、
|
||||
// 複数のpost formを開いたときに場合によってはエラーになる
|
||||
// もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが
|
||||
const { dispose } = popup(MkPostFormDialog, props, {
|
||||
let dispose;
|
||||
popup(MkPostFormDialog, props, {
|
||||
closed: () => {
|
||||
resolve();
|
||||
dispose();
|
||||
},
|
||||
}).then(res => {
|
||||
dispose = res.dispose;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -22,10 +22,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('error'),
|
||||
icon: faExclamationTriangle
|
||||
}]
|
||||
title: this.$t('error'),
|
||||
icon: faExclamationTriangle
|
||||
},
|
||||
faExclamationTriangle
|
||||
};
|
||||
|
@ -87,10 +87,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('aboutMisskey'),
|
||||
icon: null
|
||||
}]
|
||||
title: this.$t('aboutMisskey'),
|
||||
icon: null
|
||||
},
|
||||
version,
|
||||
faInfoCircle
|
||||
|
@ -37,10 +37,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('about'),
|
||||
icon: faInfoCircle
|
||||
}]
|
||||
title: this.$t('about'),
|
||||
icon: faInfoCircle
|
||||
},
|
||||
version,
|
||||
serverInfo: null,
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="_section">
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content" ref="list">
|
||||
<section class="_card announcement" v-for="(announcement, i) in items" :key="announcement.id">
|
||||
<section class="_card announcement _vMargin" v-for="(announcement, i) in items" :key="announcement.id">
|
||||
<div class="_title"><span v-if="$store.getters.isSignedIn && !announcement.isRead">🆕 </span>{{ announcement.title }}</div>
|
||||
<div class="_content">
|
||||
<Mfm :text="announcement.text"/>
|
||||
@ -31,10 +31,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('announcements'),
|
||||
icon: faBroadcastTower
|
||||
}]
|
||||
title: this.$t('announcements'),
|
||||
icon: faBroadcastTower
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'announcements',
|
||||
|
@ -41,10 +41,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: 'API console',
|
||||
icon: faTerminal
|
||||
}]
|
||||
title: 'API console',
|
||||
icon: faTerminal
|
||||
},
|
||||
|
||||
endpoint: '',
|
||||
|
@ -51,10 +51,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('installedApps'),
|
||||
icon: faPlug,
|
||||
}],
|
||||
title: this.$t('installedApps'),
|
||||
icon: faPlug,
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'i/apps',
|
||||
|
@ -46,15 +46,11 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: computed(() => this.channelId ? {
|
||||
header: [{
|
||||
title: this.$t('_channel.edit'),
|
||||
icon: faSatelliteDish,
|
||||
}],
|
||||
title: this.$t('_channel.edit'),
|
||||
icon: faSatelliteDish,
|
||||
} : {
|
||||
header: [{
|
||||
title: this.$t('_channel.create'),
|
||||
icon: faSatelliteDish,
|
||||
}],
|
||||
title: this.$t('_channel.create'),
|
||||
icon: faSatelliteDish,
|
||||
}),
|
||||
channel: null,
|
||||
name: null,
|
||||
|
@ -54,10 +54,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: computed(() => this.channel ? {
|
||||
header: [{
|
||||
title: this.channel.name,
|
||||
icon: faSatelliteDish,
|
||||
}],
|
||||
title: this.channel.name,
|
||||
icon: faSatelliteDish,
|
||||
} : null),
|
||||
channel: null,
|
||||
showBanner: true,
|
||||
|
@ -43,10 +43,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('channel'),
|
||||
icon: faSatelliteDish
|
||||
}]
|
||||
title: this.$t('channel'),
|
||||
icon: faSatelliteDish
|
||||
},
|
||||
tab: 'featured',
|
||||
featuredPagination: {
|
||||
|
@ -43,10 +43,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.title,
|
||||
icon: faFileAlt
|
||||
}],
|
||||
title: this.title,
|
||||
icon: faFileAlt
|
||||
},
|
||||
faFileAlt,
|
||||
title: '',
|
||||
|
@ -21,10 +21,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('help'),
|
||||
icon: faQuestionCircle
|
||||
}],
|
||||
title: this.$t('help'),
|
||||
icon: faQuestionCircle
|
||||
},
|
||||
docs: [],
|
||||
faQuestionCircle
|
||||
|
@ -18,10 +18,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: computed(() => this.folder ? this.folder.name : this.$t('drive')),
|
||||
icon: faCloud,
|
||||
}],
|
||||
title: computed(() => this.folder ? this.folder.name : this.$t('drive')),
|
||||
icon: faCloud,
|
||||
action: {
|
||||
icon: faEllipsisH,
|
||||
handler: this.menu
|
||||
|
@ -93,10 +93,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('explore'),
|
||||
icon: faHashtag
|
||||
}],
|
||||
title: this.$t('explore'),
|
||||
icon: faHashtag
|
||||
},
|
||||
pinnedUsers: { endpoint: 'pinned-users' },
|
||||
popularUsers: { endpoint: 'users', limit: 10, noPaging: true, params: {
|
||||
|
@ -19,10 +19,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('favorites'),
|
||||
icon: faStar
|
||||
}]
|
||||
title: this.$t('favorites'),
|
||||
icon: faStar
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'i/favorites',
|
||||
|
@ -18,10 +18,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('featured'),
|
||||
icon: faFireAlt
|
||||
}],
|
||||
title: this.$t('featured'),
|
||||
icon: faFireAlt
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/featured',
|
||||
|
@ -44,10 +44,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('followRequests'),
|
||||
icon: faUserClock,
|
||||
}],
|
||||
title: this.$t('followRequests'),
|
||||
icon: faUserClock,
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'following/requests/list',
|
||||
|
@ -33,7 +33,7 @@
|
||||
</div>
|
||||
-->
|
||||
|
||||
<MkPagination :pagination="pagination" #default="{items}" ref="reports" :auto-margin="false" style="margin-top: var(--margin);">
|
||||
<MkPagination :pagination="pagination" #default="{items}" ref="reports" style="margin-top: var(--margin);">
|
||||
<div class="bcekxzvu _card _vMargin" v-for="report in items" :key="report.id">
|
||||
<div class="_content target">
|
||||
<MkAvatar class="avatar" :user="report.targetUser"/>
|
||||
@ -84,10 +84,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('abuseReports'),
|
||||
icon: faExclamationCircle
|
||||
}],
|
||||
title: this.$t('abuseReports'),
|
||||
icon: faExclamationCircle
|
||||
},
|
||||
searchUsername: '',
|
||||
searchHost: '',
|
||||
|
@ -45,10 +45,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('announcements'),
|
||||
icon: faBroadcastTower
|
||||
}]
|
||||
title: this.$t('announcements'),
|
||||
icon: faBroadcastTower
|
||||
},
|
||||
announcements: [],
|
||||
faBroadcastTower, faSave, faTrashAlt, faPlus
|
||||
|
@ -5,7 +5,7 @@
|
||||
</div>
|
||||
|
||||
<div class="_section">
|
||||
<div class="_content local" v-if="tab === 'local'">
|
||||
<div class="local" v-if="tab === 'local'">
|
||||
<MkButton primary @click="add" style="margin: 0 auto var(--margin) auto;"><Fa :icon="faPlus"/> {{ $t('addEmoji') }}</MkButton>
|
||||
<MkInput v-model:value="query" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $t('search') }}</span></MkInput>
|
||||
<MkPagination :pagination="pagination" ref="emojis">
|
||||
@ -24,7 +24,7 @@
|
||||
</MkPagination>
|
||||
</div>
|
||||
|
||||
<div class="_content remote" v-else-if="tab === 'remote'">
|
||||
<div class="remote" v-else-if="tab === 'remote'">
|
||||
<MkInput v-model:value="queryRemote" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $t('search') }}</span></MkInput>
|
||||
<MkInput v-model:value="host" :debounce="true"><span>{{ $t('host') }}</span></MkInput>
|
||||
<MkPagination :pagination="remotePagination" ref="remoteEmojis">
|
||||
@ -68,10 +68,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('customEmojis'),
|
||||
icon: faLaugh
|
||||
}],
|
||||
title: this.$t('customEmojis'),
|
||||
icon: faLaugh,
|
||||
action: {
|
||||
icon: faPlus,
|
||||
handler: this.add
|
||||
@ -83,14 +81,14 @@ export default defineComponent({
|
||||
host: '',
|
||||
pagination: {
|
||||
endpoint: 'admin/emoji/list',
|
||||
limit: 15,
|
||||
limit: 30,
|
||||
params: computed(() => ({
|
||||
query: (this.query && this.query !== '') ? this.query : null
|
||||
}))
|
||||
},
|
||||
remotePagination: {
|
||||
endpoint: 'admin/emoji/list-remote',
|
||||
limit: 15,
|
||||
limit: 30,
|
||||
params: computed(() => ({
|
||||
query: (this.queryRemote && this.queryRemote !== '') ? this.queryRemote : null,
|
||||
host: (this.host && this.host !== '') ? this.host : null
|
||||
@ -113,8 +111,8 @@ export default defineComponent({
|
||||
os.promiseDialog(promise);
|
||||
},
|
||||
|
||||
async edit(emoji) {
|
||||
os.popup(await import('./emoji-edit-dialog.vue'), {
|
||||
edit(emoji) {
|
||||
os.popup(import('./emoji-edit-dialog.vue'), {
|
||||
emoji: emoji
|
||||
}, {
|
||||
done: result => {
|
||||
|
@ -79,10 +79,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('federation'),
|
||||
icon: faGlobe
|
||||
}],
|
||||
title: this.$t('federation'),
|
||||
icon: faGlobe
|
||||
},
|
||||
host: '',
|
||||
state: 'federating',
|
||||
|
@ -84,8 +84,8 @@ export default defineComponent({
|
||||
Progress.done();
|
||||
},
|
||||
|
||||
async showUser() {
|
||||
os.popup(await import('./user-dialog.vue'), {
|
||||
showUser() {
|
||||
os.popup(import('./user-dialog.vue'), {
|
||||
userId: this.file.userId
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
@ -34,7 +34,7 @@
|
||||
<span>{{ $t('type') }}</span>
|
||||
</MkInput>
|
||||
</div>
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="urempief" ref="files" :auto-margin="false">
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="urempief" ref="files">
|
||||
<button class="file _panel _button _vMargin" v-for="file in items" :key="file.id" @click="show(file, $event)">
|
||||
<MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/>
|
||||
<div class="body">
|
||||
@ -42,7 +42,8 @@
|
||||
<small style="opacity: 0.7;">{{ file.name }}</small>
|
||||
</div>
|
||||
<div>
|
||||
<MkAcct :user="file.user"/>
|
||||
<MkAcct v-if="file.user" :user="file.user"/>
|
||||
<div v-else>{{ $t('system') }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span style="margin-right: 1em;">{{ file.type }}</span>
|
||||
@ -83,10 +84,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('files'),
|
||||
icon: faCloud
|
||||
}],
|
||||
title: this.$t('files'),
|
||||
icon: faCloud
|
||||
},
|
||||
q: null,
|
||||
origin: 'local',
|
||||
@ -130,8 +129,8 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
async show(file, ev) {
|
||||
os.popup(await import('./file-dialog.vue'), {
|
||||
show(file, ev) {
|
||||
os.popup(import('./file-dialog.vue'), {
|
||||
fileId: file.id
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
@ -86,7 +86,7 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
tabs: [{
|
||||
id: 'index',
|
||||
title: null,
|
||||
tooltip: this.$t('instance'),
|
||||
|
@ -49,10 +49,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('serverLogs'),
|
||||
icon: faStream
|
||||
}]
|
||||
title: this.$t('serverLogs'),
|
||||
icon: faStream
|
||||
},
|
||||
logs: [],
|
||||
logLevel: 'all',
|
||||
|
@ -31,10 +31,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('jobQueue'),
|
||||
icon: faExchangeAlt,
|
||||
}],
|
||||
title: this.$t('jobQueue'),
|
||||
icon: faExchangeAlt,
|
||||
},
|
||||
connection: os.stream.useSharedConnection('queueStats'),
|
||||
faExchangeAlt, faTrashAlt
|
||||
|
@ -38,10 +38,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('relays'),
|
||||
icon: faProjectDiagram,
|
||||
}],
|
||||
title: this.$t('relays'),
|
||||
icon: faProjectDiagram,
|
||||
},
|
||||
relays: [],
|
||||
inbox: '',
|
||||
|
@ -257,10 +257,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('instance'),
|
||||
icon: faCog,
|
||||
}],
|
||||
title: this.$t('instance'),
|
||||
icon: faCog,
|
||||
},
|
||||
url,
|
||||
proxyAccount: null,
|
||||
|
@ -52,7 +52,7 @@
|
||||
</MkInput>
|
||||
</div>
|
||||
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="users" ref="users" :auto-margin="false">
|
||||
<MkPagination :pagination="pagination" #default="{items}" class="users" ref="users">
|
||||
<button class="user _panel _button _vMargin" v-for="user in items" :key="user.id" @click="show(user)">
|
||||
<MkAvatar class="avatar" :user="user" :disable-link="true"/>
|
||||
<div class="body">
|
||||
@ -101,10 +101,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('users'),
|
||||
icon: faUsers
|
||||
}],
|
||||
title: this.$t('users'),
|
||||
icon: faUsers,
|
||||
action: {
|
||||
icon: faSearch,
|
||||
handler: this.searchUser
|
||||
@ -206,8 +204,8 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
async show(user) {
|
||||
os.popup(await import('./user-dialog.vue'), {
|
||||
show(user) {
|
||||
os.popup(import('./user-dialog.vue'), {
|
||||
userId: user.id
|
||||
}, {}, 'closed');
|
||||
},
|
||||
|
@ -18,10 +18,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('mentions'),
|
||||
icon: faAt
|
||||
}],
|
||||
title: this.$t('mentions'),
|
||||
icon: faAt
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/mentions',
|
||||
|
@ -18,10 +18,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('directNotes'),
|
||||
icon: faEnvelope
|
||||
}],
|
||||
title: this.$t('directNotes'),
|
||||
icon: faEnvelope
|
||||
},
|
||||
pagination: {
|
||||
endpoint: 'notes/mentions',
|
||||
|
@ -53,10 +53,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('messaging'),
|
||||
icon: faComments
|
||||
}]
|
||||
title: this.$t('messaging'),
|
||||
icon: faComments
|
||||
},
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
|
@ -62,19 +62,15 @@ const Component = defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: computed(() => !this.fetching ? this.user ? {
|
||||
header: [{
|
||||
userName: this.user,
|
||||
avatar: this.user,
|
||||
}],
|
||||
userName: this.user,
|
||||
avatar: this.user,
|
||||
action: {
|
||||
icon: faEllipsisH,
|
||||
handler: this.menu,
|
||||
},
|
||||
} : {
|
||||
header: [{
|
||||
title: this.group.name,
|
||||
icon: faUsers
|
||||
}],
|
||||
title: this.group.name,
|
||||
icon: faUsers,
|
||||
action: {
|
||||
icon: faEllipsisH,
|
||||
handler: this.menu,
|
||||
@ -311,20 +307,20 @@ const Component = defineComponent({
|
||||
},
|
||||
|
||||
menu(ev) {
|
||||
const url = this.groupId ? `/my/messaging/group/${this.groupId}` : `/my/messaging/${this.userAcct}`;
|
||||
const path = this.groupId ? `/my/messaging/group/${this.groupId}` : `/my/messaging/${this.userAcct}`;
|
||||
|
||||
os.modalMenu([this.inWindow ? undefined : {
|
||||
text: this.$t('openInWindow'),
|
||||
icon: faWindowMaximize,
|
||||
action: () => {
|
||||
os.pageWindow(url);
|
||||
os.pageWindow(path);
|
||||
this.$router.back();
|
||||
},
|
||||
}, this.inWindow ? undefined : {
|
||||
text: this.$t('popout'),
|
||||
icon: faExternalLinkAlt,
|
||||
action: () => {
|
||||
popout(url);
|
||||
popout(path);
|
||||
this.$router.back();
|
||||
},
|
||||
}], ev.currentTarget || ev.target);
|
||||
|
269
src/client/pages/mfm-cheat-sheet.vue
Normal file
269
src/client/pages/mfm-cheat-sheet.vue
Normal file
@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<div class="mwysmxbg">
|
||||
<div class="_section">
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.intro') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.mention') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.mentionDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_mention"/>
|
||||
<MkTextarea v-model:value="preview_mention"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.hashtag') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.hashtagDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_hashtag"/>
|
||||
<MkTextarea v-model:value="preview_hashtag"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.url') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.urlDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_url"/>
|
||||
<MkTextarea v-model:value="preview_url"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.link') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.linkDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_link"/>
|
||||
<MkTextarea v-model:value="preview_link"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.emoji') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.emojiDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_emoji"/>
|
||||
<MkTextarea v-model:value="preview_emoji"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.bold') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.boldDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_bold"/>
|
||||
<MkTextarea v-model:value="preview_bold"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.small') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.smallDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_small"/>
|
||||
<MkTextarea v-model:value="preview_small"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.quote') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.quoteDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_quote"/>
|
||||
<MkTextarea v-model:value="preview_quote"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.center') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.centerDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_center"/>
|
||||
<MkTextarea v-model:value="preview_center"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.inlineCode') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.inlineCodeDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_inlineCode"/>
|
||||
<MkTextarea v-model:value="preview_inlineCode"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.blockCode') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.blockCodeDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_blockCode"/>
|
||||
<MkTextarea v-model:value="preview_blockCode"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.inlineMath') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.inlineMathDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_inlineMath"/>
|
||||
<MkTextarea v-model:value="preview_inlineMath"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.search') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.searchDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_search"/>
|
||||
<MkTextarea v-model:value="preview_search"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.flip') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.flipDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_flip"/>
|
||||
<MkTextarea v-model:value="preview_flip"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.jelly') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.jellyDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_jelly"/>
|
||||
<MkTextarea v-model:value="preview_jelly"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.tada') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.tadaDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_tada"/>
|
||||
<MkTextarea v-model:value="preview_tada"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.jump') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.jumpDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_jump"/>
|
||||
<MkTextarea v-model:value="preview_jump"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.bounce') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.bounceDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_bounce"/>
|
||||
<MkTextarea v-model:value="preview_bounce"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.spin') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.spinDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_spin"/>
|
||||
<MkTextarea v-model:value="preview_spin"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.shake') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.shakeDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_shake"/>
|
||||
<MkTextarea v-model:value="preview_shake"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_title">{{ $t('_mfm.twitch') }}</div>
|
||||
<div class="_content">
|
||||
<p>{{ $t('_mfm.twitchDescription') }}</p>
|
||||
<div class="preview _panel">
|
||||
<Mfm :text="preview_twitch"/>
|
||||
<MkTextarea v-model:value="preview_twitch"><span>MFM</span></MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faQuestionCircle } from '@fortawesome/free-regular-svg-icons';
|
||||
import MkTextarea from '@/components/ui/textarea.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkTextarea
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
title: this.$t('_mfm.cheatSheet'),
|
||||
icon: faQuestionCircle,
|
||||
},
|
||||
preview_mention: '@example',
|
||||
preview_hashtag: '#test',
|
||||
preview_url: `https://example.com`,
|
||||
preview_link: `[${this.$t('_mfm.dummy')}](https://example.com)`,
|
||||
preview_emoji: `:${this.$store.state.instance.meta.emojis[0].name}:`,
|
||||
preview_bold: `**${this.$t('_mfm.dummy')}**`,
|
||||
preview_small: `<small>${this.$t('_mfm.dummy')}</small>`,
|
||||
preview_center: `<center>${this.$t('_mfm.dummy')}</center>`,
|
||||
preview_inlineCode: '`<: "Hello, world!"`',
|
||||
preview_blockCode: '```\n~ (#i, 100) {\n\t<: ? ((i % 15) = 0) "FizzBuzz"\n\t\t.? ((i % 3) = 0) "Fizz"\n\t\t.? ((i % 5) = 0) "Buzz"\n\t\t. i\n}\n```',
|
||||
preview_inlineMath: '\\(x= \\frac{-b\' \\pm \\sqrt{(b\')^2-ac}}{a}\\)',
|
||||
preview_quote: `> ${this.$t('_mfm.dummy')}`,
|
||||
preview_search: `${this.$t('_mfm.dummy')} 検索`,
|
||||
preview_jelly: `[jelly 🍮]`,
|
||||
preview_tada: `[tada 🍮]`,
|
||||
preview_jump: `[jump 🍮]`,
|
||||
preview_bounce: `[bounce 🍮]`,
|
||||
preview_shake: `[shake 🍮]`,
|
||||
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.$t('_mfm.dummy')}]\n[flip.v ${this.$t('_mfm.dummy')}]\n[flip.h,v ${this.$t('_mfm.dummy')}]`,
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mwysmxbg {
|
||||
.preview {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -29,10 +29,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('manageAntennas'),
|
||||
icon: faSatellite
|
||||
}],
|
||||
title: this.$t('manageAntennas'),
|
||||
icon: faSatellite,
|
||||
action: {
|
||||
icon: faPlus,
|
||||
handler: this.create
|
||||
|
@ -48,10 +48,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: computed(() => this.group ? {
|
||||
header: [{
|
||||
title: this.group.name,
|
||||
icon: faUsers,
|
||||
}],
|
||||
title: this.group.name,
|
||||
icon: faUsers,
|
||||
} : null),
|
||||
group: null,
|
||||
users: [],
|
||||
|
@ -63,10 +63,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('groups'),
|
||||
icon: faUsers
|
||||
}],
|
||||
title: this.$t('groups'),
|
||||
icon: faUsers
|
||||
},
|
||||
tab: 'owned',
|
||||
ownedPagination: {
|
||||
|
@ -26,10 +26,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('manageLists'),
|
||||
icon: faListUl
|
||||
}],
|
||||
title: this.$t('manageLists'),
|
||||
icon: faListUl,
|
||||
action: {
|
||||
icon: faPlus,
|
||||
handler: this.create
|
||||
|
@ -47,10 +47,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: computed(() => this.list ? {
|
||||
header: [{
|
||||
title: this.list.name,
|
||||
icon: faListUl,
|
||||
}],
|
||||
title: this.list.name,
|
||||
icon: faListUl,
|
||||
} : null),
|
||||
list: null,
|
||||
users: [],
|
||||
|
@ -16,10 +16,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: {
|
||||
header: [{
|
||||
title: this.$t('notFound'),
|
||||
icon: faExclamationTriangle
|
||||
}]
|
||||
title: this.$t('notFound'),
|
||||
icon: faExclamationTriangle
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -51,10 +51,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
INFO: computed(() => this.note ? {
|
||||
header: [{
|
||||
title: this.$t('note'),
|
||||
avatar: this.note.user,
|
||||
}],
|
||||
title: this.$t('note'),
|
||||
avatar: this.note.user,
|
||||
} : null),
|
||||
note: null,
|
||||
hasPrev: false,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user