Merge branch 'develop' into beta
|
@ -113,14 +113,9 @@ id: 'aid'
|
||||||
reservedUsernames:
|
reservedUsernames:
|
||||||
- root
|
- root
|
||||||
- admin
|
- admin
|
||||||
|
- administrator
|
||||||
|
- me
|
||||||
- system
|
- system
|
||||||
- test
|
|
||||||
- proxy
|
|
||||||
- relay
|
|
||||||
- mod
|
|
||||||
- moderator
|
|
||||||
- info
|
|
||||||
- information
|
|
||||||
|
|
||||||
# Whether disable HSTS
|
# Whether disable HSTS
|
||||||
#disableHsts: true
|
#disableHsts: true
|
||||||
|
@ -152,6 +147,7 @@ reservedUsernames:
|
||||||
#proxy: http://127.0.0.1:3128
|
#proxy: http://127.0.0.1:3128
|
||||||
|
|
||||||
#proxyBypassHosts: [
|
#proxyBypassHosts: [
|
||||||
|
# 'web.kaiteki.app',
|
||||||
# 'example.com',
|
# 'example.com',
|
||||||
# '192.0.2.8'
|
# '192.0.2.8'
|
||||||
#]
|
#]
|
||||||
|
|
|
@ -27,9 +27,9 @@
|
||||||
- Notable differences:
|
- Notable differences:
|
||||||
- Improved UI/UX (especially on mobile)
|
- Improved UI/UX (especially on mobile)
|
||||||
- Improved notifications
|
- Improved notifications
|
||||||
- Fediverse account migration
|
|
||||||
- Improved instance security
|
- Improved instance security
|
||||||
- Improved accessibility
|
- Improved accessibility
|
||||||
|
- Improved threads
|
||||||
- Recommended Instances timeline
|
- Recommended Instances timeline
|
||||||
- OCR image captioning
|
- OCR image captioning
|
||||||
- New and improved Groups
|
- New and improved Groups
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
_lang_: "Català"
|
_lang_: "Català"
|
||||||
headlineMisskey: "Una xarxa social de codi obert, descentralitzada i gratuita per\
|
headlineMisskey: "Una xarxa social de codi obert, descentralitzada i gratuita per\
|
||||||
\ sempre \U0001F680"
|
\ sempre \U0001F680"
|
||||||
introMisskey: "Benvinguts! Calckey es una plataforma social de codi obert, descentralitzada\
|
introMisskey: "Benvinguts! Calckey es una plataforma social de codi obert, descentralitzada\
|
||||||
\ i gratuita per sempre! \U0001F680"
|
\ i gratuita per sempre! \U0001F680"
|
||||||
monthAndDay: "{day}/{month}"
|
monthAndDay: "{day}/{month}"
|
||||||
search: "Cercar"
|
search: "Cercar"
|
||||||
|
@ -15,43 +15,43 @@ gotIt: "Ho he entès!"
|
||||||
cancel: "Cancel·lar"
|
cancel: "Cancel·lar"
|
||||||
enterUsername: "Introdueix el teu nom d'usuari"
|
enterUsername: "Introdueix el teu nom d'usuari"
|
||||||
renotedBy: "Resignat per {user}"
|
renotedBy: "Resignat per {user}"
|
||||||
noNotes: "Cap nota"
|
noNotes: "Cap publicació"
|
||||||
noNotifications: "Cap notificació"
|
noNotifications: "Cap notificació"
|
||||||
instance: "Instàncies"
|
instance: "Instància"
|
||||||
settings: "Preferències"
|
settings: "Preferències"
|
||||||
basicSettings: "Configuració bàsica"
|
basicSettings: "Configuració bàsica"
|
||||||
otherSettings: "Configuració avançada"
|
otherSettings: "Altres opcions"
|
||||||
openInWindow: "Obrir en una nova finestra"
|
openInWindow: "Obrir en una finestra nova"
|
||||||
profile: "Perfil"
|
profile: "Perfil"
|
||||||
timeline: "Línia de temps"
|
timeline: "Línia de temps"
|
||||||
noAccountDescription: "Aquest usuari encara no ha escrit la seva biografia."
|
noAccountDescription: "Aquest usuari encara no ha escrit la seva biografia."
|
||||||
login: "Iniciar sessió"
|
login: "Iniciar sessió"
|
||||||
loggingIn: "Identificant-se"
|
loggingIn: "Iniciant sessió"
|
||||||
logout: "Tancar la sessió"
|
logout: "Tancar sessió"
|
||||||
signup: "Registrar-se"
|
signup: "Registrar-se"
|
||||||
uploading: "Pujant..."
|
uploading: "Pujant..."
|
||||||
save: "Desar"
|
save: "Desar"
|
||||||
users: "Usuaris"
|
users: "Usuaris"
|
||||||
addUser: "Afegir un usuari"
|
addUser: "Afegir un usuari"
|
||||||
favorite: "Afegir a preferits"
|
favorite: "Afegir a favorits"
|
||||||
favorites: "Favorits"
|
favorites: "Favorits"
|
||||||
unfavorite: "Eliminar dels preferits"
|
unfavorite: "Eliminar de favorits"
|
||||||
favorited: "Afegit als preferits."
|
favorited: "Afegit a favorits."
|
||||||
alreadyFavorited: "Ja s'ha afegit als preferits."
|
alreadyFavorited: "Ja s'ha afegit a favorits."
|
||||||
cantFavorite: "No s'ha pogut afegir als preferits."
|
cantFavorite: "No s'ha pogut afegir a favorits."
|
||||||
pin: "Fixar al perfil"
|
pin: "Fixar al perfil"
|
||||||
unpin: "Para de fixar del perfil"
|
unpin: "Deixar de fixar al perfil"
|
||||||
copyContent: "Copiar el contingut"
|
copyContent: "Còpia el contingut"
|
||||||
copyLink: "Copiar l'enllaç"
|
copyLink: "Còpia l'enllaç"
|
||||||
delete: "Eliminar"
|
delete: "Esborra"
|
||||||
deleteAndEdit: "Esborrar i editar"
|
deleteAndEdit: "Esborrar i edita"
|
||||||
deleteAndEditConfirm: "Estàs segur que vols suprimir aquesta nota i editar-la? Perdràs\
|
deleteAndEditConfirm: "Estàs segur que vols esborrar aquesta nota i editar-la? Perdràs\
|
||||||
\ totes les reaccions, notes i respostes."
|
\ totes les reaccions, resignats i respostes."
|
||||||
addToList: "Afegir a una llista"
|
addToList: "Afegir a la llista"
|
||||||
sendMessage: "Enviar un missatge"
|
sendMessage: "Enviar un missatge"
|
||||||
copyUsername: "Copiar nom d'usuari"
|
copyUsername: "Còpia nom d'usuari"
|
||||||
searchUser: "Cercar usuaris"
|
searchUser: "Cercar un usuari"
|
||||||
reply: "Respondre"
|
reply: "Respon"
|
||||||
loadMore: "Carregar més"
|
loadMore: "Carregar més"
|
||||||
showMore: "Veure més"
|
showMore: "Veure més"
|
||||||
youGotNewFollower: "t'ha seguit"
|
youGotNewFollower: "t'ha seguit"
|
||||||
|
@ -60,21 +60,21 @@ followRequestAccepted: "Sol·licitud de seguiment acceptada"
|
||||||
mention: "Menció"
|
mention: "Menció"
|
||||||
mentions: "Mencions"
|
mentions: "Mencions"
|
||||||
directNotes: "Missatges directes"
|
directNotes: "Missatges directes"
|
||||||
importAndExport: "Importar / Exportar"
|
importAndExport: "Importar / Exportar Dades"
|
||||||
import: "Importar"
|
import: "Importar"
|
||||||
export: "Exportar"
|
export: "Exportar"
|
||||||
files: "Fitxers"
|
files: "Fitxers"
|
||||||
download: "Baixar"
|
download: "Descarregar"
|
||||||
driveFileDeleteConfirm: "Estàs segur que vols suprimir el fitxer \"{name}\"? Les notes\
|
driveFileDeleteConfirm: "Estàs segur que vols suprimir el fitxer \"{name}\"? Les publicacions\
|
||||||
\ associades a aquest fitxer adjunt també se suprimiran."
|
\ associades a aquest fitxer adjunt també es suprimiran."
|
||||||
unfollowConfirm: "Estàs segur que vols deixar de seguir {name}?"
|
unfollowConfirm: "Estàs segur que vols deixar de seguir {name}?"
|
||||||
exportRequested: "Has sol·licitat una exportació. Això pot trigar una estona. S'afegirà\
|
exportRequested: "Has sol·licitat una exportació. Això pot trigar una estona. S'afegirà\
|
||||||
\ a la teva unitat un cop completat."
|
\ al teu Disc un cop completada."
|
||||||
importRequested: "Has sol·licitat una importació. Això pot trigar una estona."
|
importRequested: "Has sol·licitat una importació. Això pot trigar una estona."
|
||||||
lists: "Llistes"
|
lists: "Llistes"
|
||||||
noLists: "No tens cap llista"
|
noLists: "No tens cap llista"
|
||||||
note: "Post"
|
note: "Publicació"
|
||||||
notes: "Posts"
|
notes: "Publicacions"
|
||||||
following: "Seguint"
|
following: "Seguint"
|
||||||
followers: "Seguidors"
|
followers: "Seguidors"
|
||||||
followsYou: "Et segueix"
|
followsYou: "Et segueix"
|
||||||
|
@ -83,7 +83,7 @@ manageLists: "Gestionar les llistes"
|
||||||
error: "Error"
|
error: "Error"
|
||||||
somethingHappened: "S'ha produït un error"
|
somethingHappened: "S'ha produït un error"
|
||||||
retry: "Torna-ho a intentar"
|
retry: "Torna-ho a intentar"
|
||||||
pageLoadError: "S'ha produït un error en carregar la pàgina"
|
pageLoadError: "Alguna cosa a sortit malament al carregar la pàgina."
|
||||||
pageLoadErrorDescription: "Això normalment es deu a errors de xarxa o a la memòria\
|
pageLoadErrorDescription: "Això normalment es deu a errors de xarxa o a la memòria\
|
||||||
\ cau del navegador. Prova d'esborrar la memòria cau i torna-ho a provar després\
|
\ cau del navegador. Prova d'esborrar la memòria cau i torna-ho a provar després\
|
||||||
\ d'esperar una estona."
|
\ d'esperar una estona."
|
||||||
|
@ -100,13 +100,13 @@ followRequests: "Sol·licituds de seguiment"
|
||||||
unfollow: "Deixar de seguir"
|
unfollow: "Deixar de seguir"
|
||||||
followRequestPending: "Sol·licituds de seguiment pendents"
|
followRequestPending: "Sol·licituds de seguiment pendents"
|
||||||
enterEmoji: "Introduir un emoji"
|
enterEmoji: "Introduir un emoji"
|
||||||
renote: "Renotar"
|
renote: "Impulsà"
|
||||||
unrenote: "Anul·lar renota"
|
unrenote: "Anul·lar impuls"
|
||||||
renoted: "Renotat."
|
renoted: "Impulsat."
|
||||||
cantRenote: "Aquesta publicació no pot ser renotada."
|
cantRenote: "Aquesta publicació no pot ser impulsada."
|
||||||
cantReRenote: "Impossible renotar una renota."
|
cantReRenote: "No es pot impulsar un impuls."
|
||||||
quote: "Citar"
|
quote: "Citar"
|
||||||
pinnedNote: "Nota fixada"
|
pinnedNote: "Publicació fixada"
|
||||||
pinned: "Fixar al perfil"
|
pinned: "Fixar al perfil"
|
||||||
you: "Tu"
|
you: "Tu"
|
||||||
clickToShow: "Fes clic per mostrar"
|
clickToShow: "Fes clic per mostrar"
|
||||||
|
@ -116,7 +116,7 @@ reaction: "Reaccions"
|
||||||
reactionSetting: "Reaccions a mostrar al selector de reaccions"
|
reactionSetting: "Reaccions a mostrar al selector de reaccions"
|
||||||
reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem\
|
reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem\
|
||||||
\ \"+\" per afegir."
|
\ \"+\" per afegir."
|
||||||
rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes"
|
rememberNoteVisibility: "Recorda la configuració de visibilitat de les publicacions"
|
||||||
attachCancel: "Eliminar el fitxer adjunt"
|
attachCancel: "Eliminar el fitxer adjunt"
|
||||||
markAsSensitive: "Marcar com a NSFW"
|
markAsSensitive: "Marcar com a NSFW"
|
||||||
unmarkAsSensitive: "Deixar de marcar com a sensible"
|
unmarkAsSensitive: "Deixar de marcar com a sensible"
|
||||||
|
@ -130,7 +130,7 @@ unsuspend: "Deixa de suspendre"
|
||||||
instances: "Instàncies"
|
instances: "Instàncies"
|
||||||
remove: "Eliminar"
|
remove: "Eliminar"
|
||||||
nsfw: "NSFW"
|
nsfw: "NSFW"
|
||||||
pinnedNotes: "Nota fixada"
|
pinnedNotes: "Publicació fixada"
|
||||||
userList: "Llistes"
|
userList: "Llistes"
|
||||||
smtpUser: "Nom d'usuari"
|
smtpUser: "Nom d'usuari"
|
||||||
smtpPass: "Contrasenya"
|
smtpPass: "Contrasenya"
|
||||||
|
@ -147,7 +147,7 @@ _mfm:
|
||||||
_theme:
|
_theme:
|
||||||
keys:
|
keys:
|
||||||
mention: "Menció"
|
mention: "Menció"
|
||||||
renote: "Renotar"
|
renote: "Impulsar"
|
||||||
_sfx:
|
_sfx:
|
||||||
note: "Posts"
|
note: "Posts"
|
||||||
notification: "Notificacions"
|
notification: "Notificacions"
|
||||||
|
@ -191,12 +191,12 @@ _notification:
|
||||||
_types:
|
_types:
|
||||||
follow: "Seguint"
|
follow: "Seguint"
|
||||||
mention: "Menció"
|
mention: "Menció"
|
||||||
renote: "Renotar"
|
renote: "Impulsos"
|
||||||
quote: "Citar"
|
quote: "Citar"
|
||||||
reaction: "Reaccions"
|
reaction: "Reaccions"
|
||||||
_actions:
|
_actions:
|
||||||
reply: "Respondre"
|
reply: "Respondre"
|
||||||
renote: "Renotar"
|
renote: "Impulsos"
|
||||||
_deck:
|
_deck:
|
||||||
_columns:
|
_columns:
|
||||||
notifications: "Notificacions"
|
notifications: "Notificacions"
|
||||||
|
@ -469,3 +469,209 @@ enableLocalTimeline: Activa la línea de temps local
|
||||||
enableRecommendedTimeline: Activa la línea de temps de recomanats
|
enableRecommendedTimeline: Activa la línea de temps de recomanats
|
||||||
pinnedClipId: ID del clip que vols fixar
|
pinnedClipId: ID del clip que vols fixar
|
||||||
hcaptcha: hCaptcha
|
hcaptcha: hCaptcha
|
||||||
|
manageAntennas: Gestiona les Antenes
|
||||||
|
name: Nom
|
||||||
|
notesAndReplies: Articles i respostes
|
||||||
|
silence: Posa en silenci
|
||||||
|
withFiles: Amb fitxers
|
||||||
|
popularUsers: Usuaris populars
|
||||||
|
exploreUsersCount: Hi han {count} usuaris
|
||||||
|
exploreFediverse: Explora el Fesiverse
|
||||||
|
popularTags: Etiquetes populars
|
||||||
|
about: Sobre
|
||||||
|
recentlyUpdatedUsers: Usuaris actius fa poc
|
||||||
|
recentlyRegisteredUsers: Usuaris registrats fa poc
|
||||||
|
recentlyDiscoveredUsers: Nous suaris descoberts
|
||||||
|
administrator: Administrador
|
||||||
|
token: Token
|
||||||
|
registerSecurityKey: Registra una clau de seguretat
|
||||||
|
securityKeyName: Nom clau
|
||||||
|
lastUsed: Feta servir per última vegada
|
||||||
|
unregister: Anul·lar el registre
|
||||||
|
passwordLessLogin: Identificació sense contrasenya
|
||||||
|
share: Comparteix
|
||||||
|
notFound: No s'ha trobat
|
||||||
|
newPasswordIs: La nova contrasenya és "{password}"
|
||||||
|
notFoundDescription: No es pot trobar cap pàgina que correspongui a aquesta adreça
|
||||||
|
URL.
|
||||||
|
uploadFolder: Carpeta per defecte per pujar arxius
|
||||||
|
cacheClear: Netejar la memòria cau
|
||||||
|
markAsReadAllNotifications: Marca totes les notificacions com llegides
|
||||||
|
markAsReadAllUnreadNotes: Marca totes les publicacions com a llegides
|
||||||
|
markAsReadAllTalkMessages: Marca tots els missatges com llegits
|
||||||
|
help: Ajuda
|
||||||
|
inputMessageHere: Escriu aquí el missatge
|
||||||
|
close: Tancar
|
||||||
|
group: Grup
|
||||||
|
groups: Grups
|
||||||
|
createGroup: Crea un grup
|
||||||
|
ownedGroups: Grups que et pertanyen
|
||||||
|
joinedGroups: Grups als que t'has unit
|
||||||
|
groupName: Nom del grup
|
||||||
|
members: Membres
|
||||||
|
transfer: Transferir
|
||||||
|
messagingWithUser: Conversa privada
|
||||||
|
title: Títol
|
||||||
|
text: Text
|
||||||
|
enable: Activar
|
||||||
|
next: Següent
|
||||||
|
retype: Torna a entrar
|
||||||
|
noteOf: Publicat per {user}
|
||||||
|
inviteToGroup: Invitar a un grup
|
||||||
|
quoteAttached: Cita
|
||||||
|
quoteQuestion: Adjuntar com a cita?
|
||||||
|
noMessagesYet: Encara no hi han missatges
|
||||||
|
signinRequired: Si us plau registrat o inicia sessió per continuar
|
||||||
|
invitations: Invitacions
|
||||||
|
invitationCode: Codi d'invitació
|
||||||
|
checking: Comprovant...
|
||||||
|
usernameInvalidFormat: Pots fer servir lletres en majúscules o minúscules, nombres
|
||||||
|
i guions baixos.
|
||||||
|
tooShort: Massa curt
|
||||||
|
tooLong: Massa llarg
|
||||||
|
weakPassword: Contrasenya amb seguretat feble
|
||||||
|
strongPassword: Contrasenya amb seguretat forta
|
||||||
|
passwordMatched: Coincidències
|
||||||
|
signinWith: Inicieu sessió com {x}
|
||||||
|
signinFailed: No es pot iniciar sessió. El nom d'usuari o la contrasenya són incorrectes.
|
||||||
|
or: O
|
||||||
|
language: Idioma
|
||||||
|
uiLanguage: Idioma de la interfície d'usuari
|
||||||
|
groupInvited: T'han invitat a un grup
|
||||||
|
aboutX: Sobre {x}
|
||||||
|
youHaveNoGroups: No tens grups
|
||||||
|
disableDrawer: No facis servir els menús amb estil de calaix
|
||||||
|
noHistory: No ha historial disponible
|
||||||
|
signinHistory: Historial d'inicis de sessió
|
||||||
|
disableAnimatedMfm: Desactiva les animacions amb MFM
|
||||||
|
doing: Processant...
|
||||||
|
category: Categoría
|
||||||
|
existingAccount: El compte ja existeix
|
||||||
|
regenerate: Regenerar
|
||||||
|
docSource: Font d'aquest document
|
||||||
|
createAccount: Crear compte
|
||||||
|
fontSize: Mida del text
|
||||||
|
noFollowRequests: No tens cap sol·licitud de seguiment per aprovar
|
||||||
|
openImageInNewTab: Obre les imatges en una pestanya nova
|
||||||
|
dashboard: Panell
|
||||||
|
local: Local
|
||||||
|
remote: Remot
|
||||||
|
total: Total
|
||||||
|
weekOverWeekChanges: Canvis d'ençà la passada setmana
|
||||||
|
dayOverDayChanges: Canvis d'ençà ahir
|
||||||
|
appearance: Aparença
|
||||||
|
clientSettings: Configuració del client
|
||||||
|
accountSettings: Configuració del compte
|
||||||
|
promotion: Promogut
|
||||||
|
promote: Promoure
|
||||||
|
numberOfDays: Nombre de dies
|
||||||
|
objectStorageBaseUrl: Adreça URL base
|
||||||
|
hideThisNote: Amaga aquest article
|
||||||
|
showFeaturedNotesInTimeline: Mostra els articles destacats a la línea de temps
|
||||||
|
objectStorage: Emmagatzematge d'objectes
|
||||||
|
useObjectStorage: Fes servir l'emmagatzema d'objectes
|
||||||
|
expandTweet: Amplia el tuit
|
||||||
|
themeEditor: Editor de temes
|
||||||
|
description: Descripció
|
||||||
|
leaveConfirm: Hi han canvis que no s'han desat. Els vols descartar?
|
||||||
|
manage: Administració
|
||||||
|
plugins: Afegits
|
||||||
|
preferencesBackups: Preferències de còpies de seguretat
|
||||||
|
undeck: Treure el Deck
|
||||||
|
useBlurEffectForModal: Fes servir efectes de difuminació en les finestres modals
|
||||||
|
useFullReactionPicker: Fes servir el selector de reaccions a tamany complert
|
||||||
|
deck: Deck
|
||||||
|
width: Amplada
|
||||||
|
generateAccessToken: Genera un token d'accés
|
||||||
|
medium: Mitja
|
||||||
|
small: Petit
|
||||||
|
permission: Permisos
|
||||||
|
enableAll: Activa tots
|
||||||
|
tokenRequested: Garantir accés al compte
|
||||||
|
pluginTokenRequestedDescription: Aquest afegit podrà fer servir els permisos configurats
|
||||||
|
aquí.
|
||||||
|
emailServer: Servidor de correu electrònic
|
||||||
|
notificationType: Tipus de notificació
|
||||||
|
edit: Editar
|
||||||
|
emailAddress: Adreça de Correu electrònic
|
||||||
|
smtpConfig: Configuració del servidor SMTP
|
||||||
|
smtpHost: Host
|
||||||
|
enableEmail: Activa la distribució de correu electrònic
|
||||||
|
smtpPort: Port
|
||||||
|
emailConfigInfo: Fet servir per confirmar les adreçats de correu electrònic al registrar-se
|
||||||
|
o si s'oblida la contrasenya
|
||||||
|
email: Correu electrònic
|
||||||
|
smtpSecure: Fes servir SSL/TLS implícit per connectar-se per SMTP
|
||||||
|
emptyToDisableSmtpAuth: Deixa el nom d'usuari i la contrasenya sense emplenar per
|
||||||
|
desactivar la verificació SMTP
|
||||||
|
smtpSecureInfo: Desactiva això quant facis servir STARTTLS
|
||||||
|
testEmail: Envia un correu electrònic de verificació
|
||||||
|
wordMute: Silenciar paraules
|
||||||
|
regexpError: Error a la Expressió Regular
|
||||||
|
regexpErrorDescription: 'Hi ha un error a la expressió regular a la línea {line} de
|
||||||
|
la teva {tab} de paraules silenciades:'
|
||||||
|
userSaysSomething: '{name} va dir alguna cosa'
|
||||||
|
instanceMute: Silenciar instàncies
|
||||||
|
logs: Registres
|
||||||
|
copy: Copiar
|
||||||
|
delayed: Retardat
|
||||||
|
metrics: Mètriques
|
||||||
|
overview: Vista general
|
||||||
|
database: Base de dades
|
||||||
|
regenerateLoginToken: Regenera el token d'inici de sessió
|
||||||
|
reduceUiAnimation: Redueix les animacions de la UI
|
||||||
|
messagingWithGroup: Conversa en grup
|
||||||
|
invites: Invitacions
|
||||||
|
unavailable: No disponible
|
||||||
|
newMessageExists: Tens nous missatges
|
||||||
|
onlyOneFileCanBeAttached: Només pots adjuntar un fitxer per missatge
|
||||||
|
normalPassword: Contrasenya amb seguretat mitjana
|
||||||
|
passwordNotMatched: No hi han coincidències
|
||||||
|
useOsNativeEmojis: Fes servir els emojis per defecte del Sistema Operatiu
|
||||||
|
joinOrCreateGroup: Fes que et convidin a un grup o crea el teu propi.
|
||||||
|
objectStorageBaseUrlDesc: "Es l'adreça URL que serveix com a referència. Específica\
|
||||||
|
\ la adreça URL del CDN o Proxy si fas servir.\nPer fer servir S3 'https://<bucket>.s3.amazonaws.com'\
|
||||||
|
\ i per GCS o serveis semblants 'https://storage.googleapis.com/<bucket>', etc."
|
||||||
|
height: Alçada
|
||||||
|
large: Gran
|
||||||
|
notificationSetting: Preferències de notificacions
|
||||||
|
makeActive: Activar
|
||||||
|
notificationSettingDesc: Tria el tipus de notificació que es veure.
|
||||||
|
notifyAntenna: Notificar noves articles
|
||||||
|
withFileAntenna: Només articles amb fitxers
|
||||||
|
enableServiceworker: Activa les notificacions push per al teu navegador
|
||||||
|
antennaUsersDescription: Escriu un nom d'usuari per línea
|
||||||
|
antennaInstancesDescription: Escriu la adreça d'una instància per línea
|
||||||
|
tags: Etiquetes
|
||||||
|
antennaSource: Font de la antena
|
||||||
|
antennaKeywords: Paraules claus a escolta
|
||||||
|
antennaExcludeKeywords: Paraules clau a excluir
|
||||||
|
antennaKeywordsDescription: Separades amb espais per fer una condició AND i amb una
|
||||||
|
línea nova per fer una condició OR.
|
||||||
|
caseSensitive: Sensible a majúscules i minúscules
|
||||||
|
withReplies: Inclou respostes
|
||||||
|
connectedTo: Aquest(s) compte(s) estan connectats
|
||||||
|
silenceConfirm: Segur que vols posa en silenci aquest usuari?
|
||||||
|
unsilence: Desfés posar en silenci
|
||||||
|
unsilenceConfirm: Segur que vols treure el silenci a aquest usuari?
|
||||||
|
aboutMisskey: Sobre Calckey
|
||||||
|
twoStepAuthentication: Autentificació de dos factors
|
||||||
|
moderator: Moderador
|
||||||
|
moderation: Moderació
|
||||||
|
available: Disponible
|
||||||
|
tapSecurityKey: Escriu la teva clau de seguretat
|
||||||
|
nUsersMentioned: Esmentat per {n} usuari(s)
|
||||||
|
securityKey: Clau de seguretat
|
||||||
|
resetPassword: Restablir contrasenya
|
||||||
|
describeFile: Afegeix un subtítol
|
||||||
|
enterFileDescription: Entra un subtítol
|
||||||
|
author: Autor
|
||||||
|
disableAll: Desactiva tots
|
||||||
|
userSaysSomethingReason: '{name} va dir {reason}'
|
||||||
|
display: Visualització
|
||||||
|
channel: Canals
|
||||||
|
create: Crear
|
||||||
|
useGlobalSetting: Fes servir els ajustos globals
|
||||||
|
useGlobalSettingDesc: Si s'activa, es faran servir els ajustos de notificacions del
|
||||||
|
teu compte. Si es desactiva , es poden fer configuracions individuals.
|
||||||
|
other: Altres
|
||||||
|
|
|
@ -1237,6 +1237,14 @@ _mfm:
|
||||||
sparkleDescription: "Gives content a sparkling particle effect."
|
sparkleDescription: "Gives content a sparkling particle effect."
|
||||||
rotate: "Rotate"
|
rotate: "Rotate"
|
||||||
rotateDescription: "Turns content by a specified angle."
|
rotateDescription: "Turns content by a specified angle."
|
||||||
|
position: "Position"
|
||||||
|
positionDescription: "Move content by a specified amount."
|
||||||
|
scale: "Scale"
|
||||||
|
scaleDescription: "Scale content by a specified amount."
|
||||||
|
foreground: "Foreground color"
|
||||||
|
foregroundDescription: "Change the foreground color of text."
|
||||||
|
background: "Background color"
|
||||||
|
backgroundDescription: "Change the background color of text."
|
||||||
plain: "Plain"
|
plain: "Plain"
|
||||||
plainDescription: "Deactivates the effects of all MFM contained within this MFM\
|
plainDescription: "Deactivates the effects of all MFM contained within this MFM\
|
||||||
\ effect."
|
\ effect."
|
||||||
|
|
43
locales/fi.yml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
username: Käyttäjänimi
|
||||||
|
fetchingAsApObject: Hae Fedeversestä
|
||||||
|
gotIt: Selvä!
|
||||||
|
cancel: Peruuta
|
||||||
|
enterUsername: Anna käyttäjänimi
|
||||||
|
renotedBy: Buustannut {käyttäjä}
|
||||||
|
noNotes: Ei lähetyksiä
|
||||||
|
noNotifications: Ei ilmoituksia
|
||||||
|
instance: Instanssi
|
||||||
|
settings: Asetukset
|
||||||
|
basicSettings: Perusasetukset
|
||||||
|
otherSettings: Muut asetukset
|
||||||
|
openInWindow: Avaa ikkunaan
|
||||||
|
profile: Profiili
|
||||||
|
timeline: Aikajana
|
||||||
|
noAccountDescription: Käyttäjä ei ole vielä kirjoittanut kuvaustaan vielä.
|
||||||
|
login: Kirjaudu sisään
|
||||||
|
loggingIn: Kirjautuu sisään
|
||||||
|
logout: Kirjaudu ulos
|
||||||
|
uploading: Tallentaa ylös...
|
||||||
|
save: Tallenna
|
||||||
|
favorites: Kirjanmerkit
|
||||||
|
unfavorite: Poista kirjanmerkeistä
|
||||||
|
favorited: Lisätty kirjanmerkkeihin.
|
||||||
|
alreadyFavorited: Lisätty jo kirjanmerkkeihin.
|
||||||
|
cantFavorite: Ei voitu lisätä kirjanmerkkeihin.
|
||||||
|
pin: Kiinnitä profiiliin
|
||||||
|
unpin: Irroita profiilista
|
||||||
|
delete: Poista
|
||||||
|
forgotPassword: Unohtunut salasana
|
||||||
|
search: Etsi
|
||||||
|
notifications: Ilmoitukset
|
||||||
|
password: Salasana
|
||||||
|
ok: OK
|
||||||
|
noThankYou: Ei kiitos
|
||||||
|
signup: Rekisteröidy
|
||||||
|
users: Käyttäjät
|
||||||
|
addUser: Lisää käyttäjä
|
||||||
|
addInstance: Lisää instanssi
|
||||||
|
favorite: Lisää kirjanmerkkeihin
|
||||||
|
copyContent: Kopioi sisältö
|
||||||
|
deleteAndEdit: Poista ja muokkaa
|
||||||
|
copyLink: Kopioi linkki
|
|
@ -986,7 +986,7 @@ _registry:
|
||||||
createKey: "Новый ключ"
|
createKey: "Новый ключ"
|
||||||
_aboutMisskey:
|
_aboutMisskey:
|
||||||
about: "Calckey это форк Misskey, сделанный ThatOneCalculator, разработка которого\
|
about: "Calckey это форк Misskey, сделанный ThatOneCalculator, разработка которого\
|
||||||
\ начал с 2022."
|
\ началась с 2022."
|
||||||
contributors: "Основные соавторы"
|
contributors: "Основные соавторы"
|
||||||
allContributors: "Все соавторы"
|
allContributors: "Все соавторы"
|
||||||
source: "Исходный код"
|
source: "Исходный код"
|
||||||
|
|
|
@ -64,7 +64,7 @@ import: "匯入"
|
||||||
export: "匯出"
|
export: "匯出"
|
||||||
files: "檔案"
|
files: "檔案"
|
||||||
download: "下載"
|
download: "下載"
|
||||||
driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的貼文也會跟著消失。\n"
|
driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的貼文也會跟著消失。"
|
||||||
unfollowConfirm: "確定要取消追隨{name}嗎?"
|
unfollowConfirm: "確定要取消追隨{name}嗎?"
|
||||||
exportRequested: "已請求匯出。這可能會花一點時間。結束後檔案將會被放到雲端裡。"
|
exportRequested: "已請求匯出。這可能會花一點時間。結束後檔案將會被放到雲端裡。"
|
||||||
importRequested: "已請求匯入。這可能會花一點時間"
|
importRequested: "已請求匯入。這可能會花一點時間"
|
||||||
|
@ -291,7 +291,7 @@ emptyDrive: "雲端硬碟為空"
|
||||||
emptyFolder: "資料夾為空"
|
emptyFolder: "資料夾為空"
|
||||||
unableToDelete: "無法刪除"
|
unableToDelete: "無法刪除"
|
||||||
inputNewFileName: "輸入檔案名稱"
|
inputNewFileName: "輸入檔案名稱"
|
||||||
inputNewDescription: "請輸入新標題 "
|
inputNewDescription: "請輸入新標題"
|
||||||
inputNewFolderName: "輸入新資料夾的名稱"
|
inputNewFolderName: "輸入新資料夾的名稱"
|
||||||
circularReferenceFolder: "目標文件夾是您要移動的文件夾的子文件夾。"
|
circularReferenceFolder: "目標文件夾是您要移動的文件夾的子文件夾。"
|
||||||
hasChildFilesOrFolders: "此文件夾不是空的,無法刪除。"
|
hasChildFilesOrFolders: "此文件夾不是空的,無法刪除。"
|
||||||
|
@ -324,7 +324,7 @@ yearX: "{year}年"
|
||||||
pages: "頁面"
|
pages: "頁面"
|
||||||
integration: "整合"
|
integration: "整合"
|
||||||
connectService: "己連結"
|
connectService: "己連結"
|
||||||
disconnectService: "己斷開 "
|
disconnectService: "己斷開"
|
||||||
enableLocalTimeline: "開啟本地時間軸"
|
enableLocalTimeline: "開啟本地時間軸"
|
||||||
enableGlobalTimeline: "啟用公開時間軸"
|
enableGlobalTimeline: "啟用公開時間軸"
|
||||||
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"
|
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"
|
||||||
|
@ -336,7 +336,7 @@ driveCapacityPerRemoteAccount: "每個非本地用戶的雲端容量"
|
||||||
inMb: "以Mbps為單位"
|
inMb: "以Mbps為單位"
|
||||||
iconUrl: "圖像URL"
|
iconUrl: "圖像URL"
|
||||||
bannerUrl: "橫幅圖像URL"
|
bannerUrl: "橫幅圖像URL"
|
||||||
backgroundImageUrl: "背景圖片的來源網址 "
|
backgroundImageUrl: "背景圖片的來源網址"
|
||||||
basicInfo: "基本資訊"
|
basicInfo: "基本資訊"
|
||||||
pinnedUsers: "置頂用戶"
|
pinnedUsers: "置頂用戶"
|
||||||
pinnedUsersDescription: "在「發現」頁面中使用換行標記想要置頂的使用者。"
|
pinnedUsersDescription: "在「發現」頁面中使用換行標記想要置頂的使用者。"
|
||||||
|
@ -490,7 +490,7 @@ useObjectStorage: "使用Object Storage"
|
||||||
objectStorageBaseUrl: "Base URL"
|
objectStorageBaseUrl: "Base URL"
|
||||||
objectStorageBaseUrlDesc: "引用時的URL。如果您使用的是CDN或反向代理,请指定其URL,例如S3:“https://<bucket>.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/<bucket>”"
|
objectStorageBaseUrlDesc: "引用時的URL。如果您使用的是CDN或反向代理,请指定其URL,例如S3:“https://<bucket>.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/<bucket>”"
|
||||||
objectStorageBucket: "儲存空間(Bucket)"
|
objectStorageBucket: "儲存空間(Bucket)"
|
||||||
objectStorageBucketDesc: "請指定您正在使用的服務的存儲桶名稱。 "
|
objectStorageBucketDesc: "請指定您正在使用的服務的存儲桶名稱。"
|
||||||
objectStoragePrefix: "前綴"
|
objectStoragePrefix: "前綴"
|
||||||
objectStoragePrefixDesc: "它存儲在此前綴目錄下。"
|
objectStoragePrefixDesc: "它存儲在此前綴目錄下。"
|
||||||
objectStorageEndpoint: "端點(Endpoint)"
|
objectStorageEndpoint: "端點(Endpoint)"
|
||||||
|
@ -560,8 +560,8 @@ disablePlayer: "關閉播放器"
|
||||||
expandTweet: "展開推文"
|
expandTweet: "展開推文"
|
||||||
themeEditor: "主題編輯器"
|
themeEditor: "主題編輯器"
|
||||||
description: "描述"
|
description: "描述"
|
||||||
describeFile: "添加標題 "
|
describeFile: "添加標題"
|
||||||
enterFileDescription: "輸入標題 "
|
enterFileDescription: "輸入標題"
|
||||||
author: "作者"
|
author: "作者"
|
||||||
leaveConfirm: "有未保存的更改。要放棄嗎?"
|
leaveConfirm: "有未保存的更改。要放棄嗎?"
|
||||||
manage: "管理"
|
manage: "管理"
|
||||||
|
@ -865,7 +865,7 @@ driveCapOverrideLabel: "更改這個使用者的雲端硬碟容量上限"
|
||||||
driveCapOverrideCaption: "如果指定0以下的值,就會被取消。"
|
driveCapOverrideCaption: "如果指定0以下的值,就會被取消。"
|
||||||
requireAdminForView: "必須以管理者帳號登入才可以檢視。"
|
requireAdminForView: "必須以管理者帳號登入才可以檢視。"
|
||||||
isSystemAccount: "由系統自動建立與管理的帳號。"
|
isSystemAccount: "由系統自動建立與管理的帳號。"
|
||||||
typeToConfirm: "要執行這項操作,請輸入 {x} "
|
typeToConfirm: "要執行這項操作,請輸入 {x}"
|
||||||
deleteAccount: "刪除帳號"
|
deleteAccount: "刪除帳號"
|
||||||
document: "文件"
|
document: "文件"
|
||||||
numberOfPageCache: "快取頁面數"
|
numberOfPageCache: "快取頁面數"
|
||||||
|
@ -876,7 +876,7 @@ statusbar: "狀態列"
|
||||||
pleaseSelect: "請選擇"
|
pleaseSelect: "請選擇"
|
||||||
reverse: "翻轉"
|
reverse: "翻轉"
|
||||||
colored: "彩色"
|
colored: "彩色"
|
||||||
refreshInterval: "更新間隔"
|
refreshInterval: "更新間隔 "
|
||||||
label: "標籤"
|
label: "標籤"
|
||||||
type: "類型"
|
type: "類型"
|
||||||
speed: "速度"
|
speed: "速度"
|
||||||
|
@ -895,7 +895,7 @@ activeEmailValidationDescription: "積極地驗證用戶的電子郵件地址,
|
||||||
navbar: "導覽列"
|
navbar: "導覽列"
|
||||||
shuffle: "隨機"
|
shuffle: "隨機"
|
||||||
account: "帳戶"
|
account: "帳戶"
|
||||||
move: "移動 "
|
move: "移動"
|
||||||
customKaTeXMacro: "自定義 KaTeX 宏"
|
customKaTeXMacro: "自定義 KaTeX 宏"
|
||||||
customKaTeXMacroDescription: "使用宏來輕鬆的輸入數學表達式吧!宏的用法與 LaTeX 中的命令定義相同。你可以使用 \\newcommand{\\\
|
customKaTeXMacroDescription: "使用宏來輕鬆的輸入數學表達式吧!宏的用法與 LaTeX 中的命令定義相同。你可以使用 \\newcommand{\\\
|
||||||
name}{content} 或 \\newcommand{\\name}[number of arguments]{content} 來輸入數學表達式。舉個例子,\\\
|
name}{content} 或 \\newcommand{\\name}[number of arguments]{content} 來輸入數學表達式。舉個例子,\\\
|
||||||
|
@ -933,11 +933,11 @@ _accountDelete:
|
||||||
inProgress: "正在刪除"
|
inProgress: "正在刪除"
|
||||||
_ad:
|
_ad:
|
||||||
back: "返回"
|
back: "返回"
|
||||||
reduceFrequencyOfThisAd: "降低此廣告的頻率 "
|
reduceFrequencyOfThisAd: "降低此廣告的頻率"
|
||||||
_forgotPassword:
|
_forgotPassword:
|
||||||
enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。"
|
enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。"
|
||||||
ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。 "
|
ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。"
|
||||||
contactAdmin: "此實例不支持電子郵件,請聯繫您的管理員重置您的密碼。 "
|
contactAdmin: "此實例不支持電子郵件,請聯繫您的管理員重置您的密碼。"
|
||||||
_gallery:
|
_gallery:
|
||||||
my: "我的貼文"
|
my: "我的貼文"
|
||||||
liked: "喜歡的貼文"
|
liked: "喜歡的貼文"
|
||||||
|
@ -1000,7 +1000,7 @@ _mfm:
|
||||||
url: "URL"
|
url: "URL"
|
||||||
urlDescription: "可以展示URL位址。"
|
urlDescription: "可以展示URL位址。"
|
||||||
link: "鏈接"
|
link: "鏈接"
|
||||||
linkDescription: "您可以將特定範圍的文章與 URL 相關聯。 "
|
linkDescription: "您可以將特定範圍的文章與 URL 相關聯。"
|
||||||
bold: "粗體"
|
bold: "粗體"
|
||||||
boldDescription: "可以將文字顯示为粗體来強調。"
|
boldDescription: "可以將文字顯示为粗體来強調。"
|
||||||
small: "縮小"
|
small: "縮小"
|
||||||
|
@ -1805,3 +1805,6 @@ migration: 遷移
|
||||||
homeTimeline: 主頁時間軸
|
homeTimeline: 主頁時間軸
|
||||||
swipeOnDesktop: 允許在桌面上進行手機式滑動
|
swipeOnDesktop: 允許在桌面上進行手機式滑動
|
||||||
logoImageUrl: 圖標網址
|
logoImageUrl: 圖標網址
|
||||||
|
addInstance: 增加一個實例
|
||||||
|
noInstances: 沒有實例
|
||||||
|
flagSpeakAsCat: 像貓一樣地說話
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "calckey",
|
"name": "calckey",
|
||||||
"version": "13.2.0-beta8",
|
"version": "13.2.0-rc",
|
||||||
"codename": "aqua",
|
"codename": "aqua",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -40,6 +40,8 @@
|
||||||
"@bull-board/ui": "^4.10.2",
|
"@bull-board/ui": "^4.10.2",
|
||||||
"@napi-rs/cli": "^2.15.0",
|
"@napi-rs/cli": "^2.15.0",
|
||||||
"@tensorflow/tfjs": "^3.21.0",
|
"@tensorflow/tfjs": "^3.21.0",
|
||||||
|
"focus-trap": "^7.2.0",
|
||||||
|
"focus-trap-vue": "^4.0.1",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"seedrandom": "^3.0.5"
|
"seedrandom": "^3.0.5"
|
||||||
},
|
},
|
||||||
|
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 6 KiB |
|
@ -1,25 +1 @@
|
||||||
<svg id="svg10" version="1.1" sodipodi:docname="title_float.svg" inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="1.95 0.97 167.97 103.23">
|
<svg viewBox="1.95 0.97 128 128" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientTransform="rotate(90)"><stop offset="5%" stop-color="#9ccfd8" style="--darkreader-inline-stopcolor:#265760"/><stop offset="95%" stop-color="#31748f" style="--darkreader-inline-stopcolor:#275d72"/></linearGradient><defs><linearGradient xlink:href="#a" id="f" gradientTransform="scale(1.27567 .7839)" x1="-43.77" y1="98.469" x2="-27.05" y2="137.466" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#a" id="d" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#a" id="e" gradientTransform="scale(1.27567 .7839)" x1="-43.77" y1="98.468" x2="-8.156" y2="98.468" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#a" id="c" gradientTransform="scale(1.27567 .7839)" x1="1.571" y1="1.27" x2="133.179" y2="1.27" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#a" id="b" gradientTransform="scale(1.27567 .7839)" x1="1.571" y1="1.27" x2="133.179" y2="1.27" gradientUnits="userSpaceOnUse"/></defs><g style="fill:url(#b)" transform="translate(.934 25.196) scale(.75646)"><g style="fill:url(#c)" fill="url(#a)" word-spacing="0" letter-spacing="0" font-family="'OTADESIGN Rounded'" font-weight="400"><g transform="translate(-55.341 -52.023) scale(.26953)" style="fill:url(#d)"/><g style="fill:url(#e)"><path style="fill:url(#f)" d="M-41.832 77.19c-3.868 0-7.177 1.358-9.93 4.074-2.716 2.752-4.074 6.063-4.074 9.931 0 3.869 1.358 7.04 4.074 9.793 2.753 2.716 6.064 4.073 9.932 4.073 3.831 0 7.122-1.357 9.875-4.073.855-.855 1.283-1.896 1.283-3.123 0-1.228-.428-2.271-1.283-3.127-.856-.855-1.897-1.281-3.123-1.281-1.229 0-2.27.426-3.125 1.281-1.004 1.042-2.213 1.563-3.627 1.563-3.035-.31-5.208-2.263-5.246-5.106.038-2.842 2.21-4.935 5.244-5.246 1.414 0 2.623.52 3.627 1.563.855.855 1.898 1.283 3.127 1.283 1.226 0 2.267-.428 3.123-1.283.855-.856 1.283-1.897 1.283-3.125 0-1.227-.428-2.268-1.283-3.123-2.753-2.716-6.046-4.075-9.877-4.075zm20.902 6.91c-2.88 0-5.353 1.02-7.422 3.06-.642.643-.964 1.426-.964 2.348 0 .923.322 1.706.964 2.35.644.642 1.427.962 2.348.962.924 0 1.707-.32 2.35-.963.754-.783 1.662-1.173 2.724-1.173 1.09 0 2.026.376 2.809 1.13a3.909 3.909 0 0 1 1.135 2.811c0 1.062-.393 1.97-1.176 2.725-.392.419-.868.7-1.426.84-.141.027-.252.012-.336-.044-.056-.084-.028-.168.084-.251l.84-.881c.643-.643.965-1.411.965-2.305 0-.922-.28-1.663-.838-2.223-.559-.559-1.343-.84-2.35-.84-.698 0-1.397.35-2.095 1.05l-4.866 4.822c-.643.643-.964 1.426-.964 2.347 0 .923.321 1.705.964 2.348 1.957 1.93 4.375 2.894 7.254 2.894 2.908 0 5.396-1.034 7.465-3.103 2.041-2.041 3.06-4.5 3.06-7.379 0-2.907-1.019-5.396-3.06-7.465-2.069-2.04-4.557-3.06-7.465-3.06z" transform="translate(208.34 -284.25) scale(3.6954)" clip-rule="evenodd" fill-rule="evenodd"/></g></g></g></svg>
|
||||||
<sodipodi:namedview id="namedview21" pagecolor="#ffffff" bordercolor="#000000" borderopacity="0.25" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" showgrid="false" inkscape:zoom="1.1507704" inkscape:cx="260.69492" inkscape:cy="102.54" inkscape:window-width="1600" inkscape:window-height="931" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg10"/>
|
|
||||||
<metadata id="metadata16">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
|
||||||
<dc:title/>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<linearGradient id="myGradient" gradientTransform="rotate(90)">
|
|
||||||
<stop offset="5%" stop-color="#9ccfd8" id="stop5" style="--darkreader-inline-stopcolor: #265760;" data-darkreader-inline-stopcolor=""/>
|
|
||||||
<stop offset="95%" stop-color="#31748f" id="stop7" style="--darkreader-inline-stopcolor: #275d72;" data-darkreader-inline-stopcolor=""/>
|
|
||||||
</linearGradient>
|
|
||||||
<defs id="defs14"/>
|
|
||||||
<g id="g8" fill="url('#myGradient')" word-spacing="0" letter-spacing="0" font-family="OTADESIGN Rounded" font-weight="400">
|
|
||||||
<g id="g17">
|
|
||||||
<g transform="matrix(.26953 0 0 .26953 -55.341 -52.023)" id="g11"/>
|
|
||||||
<g transform="matrix(3.6954 0 0 3.6954 208.34 -284.25)" clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" id="g15">
|
|
||||||
<path d="m -41.8312,77.19 c -3.8683,0 -7.1782,1.3578 -9.9311,4.0734 -2.716,2.7525 -4.0734,6.0628 -4.0734,9.9311 5.0539,0.04979 6.082,0.01348 8.7525,0.0011 0.0024,-4.51e-4 0.0044,-6.05e-4 0.0069,-0.0011 0.03779,-2.8423 2.2103,-4.9346 5.2451,-5.2451 1.4137,0 2.6227,0.52089 3.6268,1.5629 0.855,0.8548 1.897,1.2822 3.1257,1.2822 1.2258,0 2.2676,-0.42741 3.1236,-1.2822 0.85499,-0.85567 1.2833,-1.8976 1.2833,-3.1257 0,-1.2264 -0.42828,-2.2673 -1.2833,-3.1231 -2.7528,-2.7156 -6.0446,-4.0734 -9.8761,-4.0734 z m -5.252,14.006 c -3.4453,-5.5934 -3.4667,0.08539 -8.7525,-0.0011 0,3.8683 1.3584,7.0406 4.0744,9.7931 2.7528,2.7156 6.0623,4.0734 9.9305,4.0734 3.8315,0 7.1238,-1.3578 9.8766,-4.0734 0.85499,-0.85577 1.2827,-1.8967 1.2827,-3.1231 0,-1.2282 -0.42775,-2.2701 -1.2827,-3.1257 -0.85596,-0.8548 -1.8978,-1.2822 -3.1236,-1.2822 -1.2287,0 -2.2707,0.42741 -3.1257,1.2822 -1.0041,1.042 -2.2136,1.5623 -3.6273,1.5623 -3.0348,-0.31051 -5.2084,-2.2633 -5.2462,-5.1056 -0.0024,1.1e-5 -0.0039,-1.2e-5 -0.0063,0 z m 26.154,-7.0965 c -2.8795,0 -5.3538,1.0204 -7.4227,3.0612 -0.64257,0.64316 -0.96404,1.4255 -0.96404,2.3472 0,0.92303 0.32146,1.7062 0.96404,2.3493 0.64331,0.64243 1.4265,0.96351 2.3477,0.96351 0.92347,0 1.7068,-0.32108 2.3493,-0.96351 0.75465,-0.78308 1.6632,-1.1744 2.7256,-1.1744 1.0894,0 2.0261,0.37695 2.8091,1.1316 0.75536,0.78309 1.1332,1.7201 1.1332,2.8102 0,1.0617 -0.39242,1.9703 -1.1754,2.7256 -0.39149,0.4193 -0.86676,0.69884 -1.4249,0.83878 -0.14116,0.02773 -0.25248,0.01369 -0.33614,-0.04175 -0.05605,-0.08456 -0.02751,-0.16827 0.08456,-0.25211 l 0.83825,-0.88053 c 0.64329,-0.64315 0.9651,-1.412 0.9651,-2.306 0,-0.92236 -0.27937,-1.6632 -0.83825,-2.2225 -0.55888,-0.55932 -1.3422,-0.83878 -2.3493,-0.83878 -0.6986,0 -1.397,0.34902 -2.0956,1.0475 l -4.8651,4.8223 c -0.64328,0.6438 -0.96457,1.4271 -0.96457,2.3488 0,0.92302 0.32128,1.7053 0.96457,2.3477 1.9568,1.9293 4.3751,2.8942 7.2546,2.8942 2.9072,0 5.3945,-1.0343 7.4634,-3.103 2.0412,-2.0409 3.0618,-4.501 3.0618,-7.3804 0,-2.9072 -1.0206,-5.3952 -3.0618,-7.4639 -2.0689,-2.0409 -4.5562,-3.0612 -7.4634,-3.0612 z" clip-rule="evenodd" fill-rule="nonzero" stroke-miterlimit="2" stroke-width="0" id="path13" sodipodi:nodetypes="cccccccscsccccccscscscccccccscscscscccccscsccscscsccc"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 21 KiB |
BIN
packages/backend/assets/inverse wordmark.png
Normal file
After Width: | Height: | Size: 21 KiB |
|
@ -1,65 +1 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg xml:space="preserve" viewBox="0 0 512 512" height="512" width="512" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a"><stop style="stop-color:#31748f;stop-opacity:1" offset="0"/><stop style="stop-color:#9ccfd8;stop-opacity:1" offset="1"/></linearGradient><linearGradient xlink:href="#a" id="b" x1="254.819" y1="411.542" x2="259.34" y2="-1.41" gradientUnits="userSpaceOnUse"/></defs><path style="fill:url(#b);fill-opacity:1;stroke-width:.996356" d="M0 0v512h512V0Z"/><g style="font-weight:400;font-family:"OTADESIGN Rounded";letter-spacing:0;word-spacing:0;fill:#fff"><g transform="translate(-38.55 37.929) scale(.5619)" style="fill:#fff"/><path style="fill:#fff" d="M-41.832 77.19c-3.868 0-7.177 1.358-9.93 4.074-2.716 2.752-4.074 6.063-4.074 9.931 0 3.869 1.358 7.04 4.074 9.793 2.753 2.716 6.064 4.073 9.932 4.073 3.831 0 7.122-1.357 9.875-4.073.855-.855 1.283-1.896 1.283-3.123 0-1.228-.428-2.271-1.283-3.127-.856-.855-1.897-1.281-3.123-1.281-1.229 0-2.27.426-3.125 1.281-1.004 1.042-2.213 1.563-3.627 1.563-3.035-.31-5.208-2.263-5.246-5.106.038-2.842 2.21-4.935 5.244-5.246 1.414 0 2.623.52 3.627 1.563.855.855 1.898 1.283 3.127 1.283 1.226 0 2.267-.428 3.123-1.283.855-.856 1.283-1.897 1.283-3.125 0-1.227-.428-2.268-1.283-3.123-2.753-2.716-6.046-4.075-9.877-4.075zm20.902 6.91c-2.88 0-5.353 1.02-7.422 3.06-.642.643-.964 1.426-.964 2.348 0 .923.322 1.706.964 2.35.644.642 1.427.962 2.348.962.924 0 1.707-.32 2.35-.963.754-.783 1.662-1.173 2.724-1.173 1.09 0 2.026.376 2.809 1.13a3.909 3.909 0 0 1 1.135 2.811c0 1.062-.393 1.97-1.176 2.725-.392.419-.868.7-1.426.84-.141.027-.252.012-.336-.044-.056-.084-.028-.168.084-.251l.84-.881c.643-.643.965-1.411.965-2.305 0-.922-.28-1.663-.838-2.223-.559-.559-1.343-.84-2.35-.84-.698 0-1.397.35-2.095 1.05l-4.866 4.822c-.643.643-.964 1.426-.964 2.347 0 .923.321 1.705.964 2.348 1.957 1.93 4.375 2.894 7.254 2.894 2.908 0 5.396-1.034 7.465-3.103 2.041-2.041 3.06-4.5 3.06-7.379 0-2.907-1.019-5.396-3.06-7.465-2.069-2.04-4.557-3.06-7.465-3.06z" transform="translate(511.15 -446.2) scale(7.70387)" clip-rule="evenodd" fill-rule="evenodd"/></g></svg>
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="512.0px"
|
|
||||||
height="512.0px"
|
|
||||||
viewBox="0 0 512.0 512.0"
|
|
||||||
version="1.1"
|
|
||||||
id="SVGRoot"
|
|
||||||
sodipodi:docname="inverse wordmark.svg"
|
|
||||||
xml:space="preserve"
|
|
||||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
|
||||||
id="namedview71"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#eeeeee"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:zoom="0.50150542"
|
|
||||||
inkscape:cx="59.819892"
|
|
||||||
inkscape:cy="189.42966"
|
|
||||||
inkscape:window-width="1600"
|
|
||||||
inkscape:window-height="931"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1"><inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid77" /></sodipodi:namedview><defs
|
|
||||||
id="defs66" /><g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"><path
|
|
||||||
id="rect136"
|
|
||||||
style="fill:#31748f;fill-opacity:1;stroke-width:0.996356"
|
|
||||||
d="M 0,0 V 512 H 512 V 0 Z" /><g
|
|
||||||
id="g17"
|
|
||||||
style="font-weight:400;font-family:'OTADESIGN Rounded';letter-spacing:0;word-spacing:0;fill:#ffffff"
|
|
||||||
transform="matrix(2.0847185,0,0,2.0847185,76.820648,146.38203)"><g
|
|
||||||
transform="matrix(0.26953,0,0,0.26953,-55.341,-52.023)"
|
|
||||||
id="g11"
|
|
||||||
style="fill:#ffffff" /><g
|
|
||||||
transform="matrix(3.6954,0,0,3.6954,208.34,-284.25)"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
fill-rule="evenodd"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-miterlimit="2"
|
|
||||||
id="g15"
|
|
||||||
style="fill:#ffffff"><path
|
|
||||||
d="m -41.8312,77.19 c -3.8683,0 -7.1782,1.3578 -9.9311,4.0734 -2.716,2.7525 -4.0734,6.0628 -4.0734,9.9311 5.0539,0.04979 6.082,0.01348 8.7525,0.0011 0.0024,-4.51e-4 0.0044,-6.05e-4 0.0069,-0.0011 0.03779,-2.8423 2.2103,-4.9346 5.2451,-5.2451 1.4137,0 2.6227,0.52089 3.6268,1.5629 0.855,0.8548 1.897,1.2822 3.1257,1.2822 1.2258,0 2.2676,-0.42741 3.1236,-1.2822 0.85499,-0.85567 1.2833,-1.8976 1.2833,-3.1257 0,-1.2264 -0.42828,-2.2673 -1.2833,-3.1231 -2.7528,-2.7156 -6.0446,-4.0734 -9.8761,-4.0734 z m -5.252,14.006 c -3.4453,-5.5934 -3.4667,0.08539 -8.7525,-0.0011 0,3.8683 1.3584,7.0406 4.0744,9.7931 2.7528,2.7156 6.0623,4.0734 9.9305,4.0734 3.8315,0 7.1238,-1.3578 9.8766,-4.0734 0.85499,-0.85577 1.2827,-1.8967 1.2827,-3.1231 0,-1.2282 -0.42775,-2.2701 -1.2827,-3.1257 -0.85596,-0.8548 -1.8978,-1.2822 -3.1236,-1.2822 -1.2287,0 -2.2707,0.42741 -3.1257,1.2822 -1.0041,1.042 -2.2136,1.5623 -3.6273,1.5623 -3.0348,-0.31051 -5.2084,-2.2633 -5.2462,-5.1056 -0.0024,1.1e-5 -0.0039,-1.2e-5 -0.0063,0 z m 26.154,-7.0965 c -2.8795,0 -5.3538,1.0204 -7.4227,3.0612 -0.64257,0.64316 -0.96404,1.4255 -0.96404,2.3472 0,0.92303 0.32146,1.7062 0.96404,2.3493 0.64331,0.64243 1.4265,0.96351 2.3477,0.96351 0.92347,0 1.7068,-0.32108 2.3493,-0.96351 0.75465,-0.78308 1.6632,-1.1744 2.7256,-1.1744 1.0894,0 2.0261,0.37695 2.8091,1.1316 0.75536,0.78309 1.1332,1.7201 1.1332,2.8102 0,1.0617 -0.39242,1.9703 -1.1754,2.7256 -0.39149,0.4193 -0.86676,0.69884 -1.4249,0.83878 -0.14116,0.02773 -0.25248,0.01369 -0.33614,-0.04175 -0.05605,-0.08456 -0.02751,-0.16827 0.08456,-0.25211 l 0.83825,-0.88053 c 0.64329,-0.64315 0.9651,-1.412 0.9651,-2.306 0,-0.92236 -0.27937,-1.6632 -0.83825,-2.2225 -0.55888,-0.55932 -1.3422,-0.83878 -2.3493,-0.83878 -0.6986,0 -1.397,0.34902 -2.0956,1.0475 l -4.8651,4.8223 c -0.64328,0.6438 -0.96457,1.4271 -0.96457,2.3488 0,0.92302 0.32128,1.7053 0.96457,2.3477 1.9568,1.9293 4.3751,2.8942 7.2546,2.8942 2.9072,0 5.3945,-1.0343 7.4634,-3.103 2.0412,-2.0409 3.0618,-4.501 3.0618,-7.3804 0,-2.9072 -1.0206,-5.3952 -3.0618,-7.4639 -2.0689,-2.0409 -4.5562,-3.0612 -7.4634,-3.0612 z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
fill-rule="nonzero"
|
|
||||||
stroke-miterlimit="2"
|
|
||||||
stroke-width="0"
|
|
||||||
id="path13"
|
|
||||||
sodipodi:nodetypes="cccccccscsccccccscscscccccccscscscscccccscsccscscsccc"
|
|
||||||
style="fill:#ffffff" /></g></g></g></svg>
|
|
||||||
|
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 20 KiB |
|
@ -26,7 +26,7 @@
|
||||||
"@bull-board/api": "^4.6.4",
|
"@bull-board/api": "^4.6.4",
|
||||||
"@bull-board/koa": "^4.6.4",
|
"@bull-board/koa": "^4.6.4",
|
||||||
"@bull-board/ui": "^4.6.4",
|
"@bull-board/ui": "^4.6.4",
|
||||||
"@calckey/megalodon": "5.1.24",
|
"@calckey/megalodon": "5.2.0",
|
||||||
"@discordapp/twemoji": "14.0.2",
|
"@discordapp/twemoji": "14.0.2",
|
||||||
"@elastic/elasticsearch": "7.17.0",
|
"@elastic/elasticsearch": "7.17.0",
|
||||||
"@koa/cors": "3.4.3",
|
"@koa/cors": "3.4.3",
|
||||||
|
@ -75,9 +75,11 @@
|
||||||
"koa": "2.13.4",
|
"koa": "2.13.4",
|
||||||
"koa-body": "^6.0.1",
|
"koa-body": "^6.0.1",
|
||||||
"koa-bodyparser": "4.3.0",
|
"koa-bodyparser": "4.3.0",
|
||||||
|
"koa-favicon": "2.1.0",
|
||||||
"koa-json-body": "5.3.0",
|
"koa-json-body": "5.3.0",
|
||||||
"koa-logger": "3.2.1",
|
"koa-logger": "3.2.1",
|
||||||
"koa-mount": "4.0.0",
|
"koa-mount": "4.0.0",
|
||||||
|
"koa-remove-trailing-slashes": "2.0.3",
|
||||||
"koa-send": "5.0.1",
|
"koa-send": "5.0.1",
|
||||||
"koa-slow": "2.1.0",
|
"koa-slow": "2.1.0",
|
||||||
"koa-views": "7.0.2",
|
"koa-views": "7.0.2",
|
||||||
|
|
|
@ -20,6 +20,7 @@ import handler from "./api-handler.js";
|
||||||
import signup from "./private/signup.js";
|
import signup from "./private/signup.js";
|
||||||
import signin from "./private/signin.js";
|
import signin from "./private/signin.js";
|
||||||
import signupPending from "./private/signup-pending.js";
|
import signupPending from "./private/signup-pending.js";
|
||||||
|
import verifyEmail from "./private/verify-email.js";
|
||||||
import discord from "./service/discord.js";
|
import discord from "./service/discord.js";
|
||||||
import github from "./service/github.js";
|
import github from "./service/github.js";
|
||||||
import twitter from "./service/twitter.js";
|
import twitter from "./service/twitter.js";
|
||||||
|
@ -177,6 +178,7 @@ for (const endpoint of [...endpoints, ...compatibility]) {
|
||||||
router.post("/signup", signup);
|
router.post("/signup", signup);
|
||||||
router.post("/signin", signin);
|
router.post("/signin", signin);
|
||||||
router.post("/signup-pending", signupPending);
|
router.post("/signup-pending", signupPending);
|
||||||
|
router.post("/verify-email", verifyEmail);
|
||||||
|
|
||||||
router.use(discord.routes());
|
router.use(discord.routes());
|
||||||
router.use(github.routes());
|
router.use(github.routes());
|
||||||
|
|
|
@ -91,6 +91,44 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
router.get("/v1/accounts/relationships", async (ctx) => {
|
||||||
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
|
const accessTokens = ctx.headers.authorization;
|
||||||
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
let users;
|
||||||
|
try {
|
||||||
|
// TODO: this should be body
|
||||||
|
let ids = ctx.request.query ? ctx.request.query["id[]"] : null;
|
||||||
|
if (typeof ids === "string") {
|
||||||
|
ids = [ids];
|
||||||
|
}
|
||||||
|
users = ids;
|
||||||
|
relationshipModel.id = ids?.toString() || "1";
|
||||||
|
if (!ids) {
|
||||||
|
ctx.body = [relationshipModel];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let reqIds = [];
|
||||||
|
for (let i = 0; i < ids.length; i++) {
|
||||||
|
reqIds.push(convertId(ids[i], IdType.CalckeyId));
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await client.getRelationships(reqIds);
|
||||||
|
let resp = data.data;
|
||||||
|
for (let acctIdx = 0; acctIdx < resp.length; acctIdx++) {
|
||||||
|
resp[acctIdx].id = convertId(resp[acctIdx].id, IdType.MastodonId);
|
||||||
|
}
|
||||||
|
ctx.body = resp;
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(e);
|
||||||
|
let data = e.response.data;
|
||||||
|
data.users = users;
|
||||||
|
console.error(data);
|
||||||
|
ctx.status = 401;
|
||||||
|
ctx.body = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
router.get<{ Params: { id: string } }>("/v1/accounts/:id", async (ctx) => {
|
router.get<{ Params: { id: string } }>("/v1/accounts/:id", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
|
@ -340,44 +378,6 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
router.get("/v1/accounts/relationships", async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
let users;
|
|
||||||
try {
|
|
||||||
// TODO: this should be body
|
|
||||||
let ids = ctx.request.query ? ctx.request.query["id[]"] : null;
|
|
||||||
if (typeof ids === "string") {
|
|
||||||
ids = [ids];
|
|
||||||
}
|
|
||||||
users = ids;
|
|
||||||
relationshipModel.id = ids?.toString() || "1";
|
|
||||||
if (!ids) {
|
|
||||||
ctx.body = [relationshipModel];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let reqIds = [];
|
|
||||||
for (let i = 0; i < ids.length; i++) {
|
|
||||||
reqIds.push(convertId(ids[i], IdType.CalckeyId));
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await client.getRelationships(reqIds);
|
|
||||||
let resp = data.data;
|
|
||||||
for (let acctIdx = 0; acctIdx < resp.length; acctIdx++) {
|
|
||||||
resp[acctIdx].id = convertId(resp[acctIdx].id, IdType.MastodonId);
|
|
||||||
}
|
|
||||||
ctx.body = resp;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e);
|
|
||||||
let data = e.response.data;
|
|
||||||
data.users = users;
|
|
||||||
console.error(data);
|
|
||||||
ctx.status = 401;
|
|
||||||
ctx.body = data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.get("/v1/bookmarks", async (ctx) => {
|
router.get("/v1/bookmarks", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
|
|
|
@ -388,7 +388,7 @@ export function statusModel(
|
||||||
emojis: MastodonEntity.Emoji[],
|
emojis: MastodonEntity.Emoji[],
|
||||||
content: string,
|
content: string,
|
||||||
) {
|
) {
|
||||||
const now = Math.floor(new Date().getTime() / 1000);
|
const now = new Date().toISOString();
|
||||||
return {
|
return {
|
||||||
id: "9atm5frjhb",
|
id: "9atm5frjhb",
|
||||||
uri: "https://http.cat/404", // ""
|
uri: "https://http.cat/404", // ""
|
||||||
|
|
|
@ -211,7 +211,7 @@ export function apiTimelineMastodon(router: Router): void {
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.createList((ctx.query as any).title);
|
const data = await client.createList((ctx.request.body as any).title);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
@ -227,7 +227,7 @@ export function apiTimelineMastodon(router: Router): void {
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.updateList(ctx.params.id, ctx.query as any);
|
const data = await client.updateList(ctx.params.id, (ctx.request.body as any).title);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
38
packages/backend/src/server/api/private/verify-email.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import type Koa from "koa";
|
||||||
|
import { Users, UserProfiles } from "@/models/index.js";
|
||||||
|
import { publishMainStream } from "@/services/stream.js";
|
||||||
|
|
||||||
|
export default async (ctx: Koa.Context) => {
|
||||||
|
const body = ctx.request.body;
|
||||||
|
|
||||||
|
const code = body["code"];
|
||||||
|
|
||||||
|
const profile = await UserProfiles.findOneByOrFail({ emailVerifyCode: code });
|
||||||
|
|
||||||
|
if (profile != null) {
|
||||||
|
ctx.body = "Verify succeeded!";
|
||||||
|
|
||||||
|
await UserProfiles.update(
|
||||||
|
{ userId: profile.userId },
|
||||||
|
{
|
||||||
|
emailVerified: true,
|
||||||
|
emailVerifyCode: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
publishMainStream(
|
||||||
|
profile.userId,
|
||||||
|
"meUpdated",
|
||||||
|
await Users.pack(
|
||||||
|
profile.userId,
|
||||||
|
{ id: profile.userId },
|
||||||
|
{
|
||||||
|
detail: true,
|
||||||
|
includeSecrets: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ctx.throw(404);
|
||||||
|
}
|
||||||
|
};
|
|
@ -10,6 +10,7 @@ import Router from "@koa/router";
|
||||||
import mount from "koa-mount";
|
import mount from "koa-mount";
|
||||||
import koaLogger from "koa-logger";
|
import koaLogger from "koa-logger";
|
||||||
import * as slow from "koa-slow";
|
import * as slow from "koa-slow";
|
||||||
|
|
||||||
import { IsNull } from "typeorm";
|
import { IsNull } from "typeorm";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import Logger from "@/services/logger.js";
|
import Logger from "@/services/logger.js";
|
||||||
|
@ -29,6 +30,7 @@ import proxyServer from "./proxy/index.js";
|
||||||
import webServer from "./web/index.js";
|
import webServer from "./web/index.js";
|
||||||
import { initializeStreamingServer } from "./api/streaming.js";
|
import { initializeStreamingServer } from "./api/streaming.js";
|
||||||
import { koaBody } from "koa-body";
|
import { koaBody } from "koa-body";
|
||||||
|
import removeTrailingSlash from "koa-remove-trailing-slashes";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
export const serverLogger = new Logger("server", "gray", false);
|
export const serverLogger = new Logger("server", "gray", false);
|
||||||
|
@ -37,12 +39,7 @@ export const serverLogger = new Logger("server", "gray", false);
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
app.proxy = true;
|
app.proxy = true;
|
||||||
|
|
||||||
// Replace trailing slashes
|
app.use(removeTrailingSlash());
|
||||||
app.use(async (ctx, next) => {
|
|
||||||
if (ctx.request.path !== "/" && ctx.request.path.endsWith("/"))
|
|
||||||
return ctx.redirect(ctx.request.path.replace(/\/$/, ""));
|
|
||||||
else await next();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!["production", "test"].includes(process.env.NODE_ENV || "")) {
|
if (!["production", "test"].includes(process.env.NODE_ENV || "")) {
|
||||||
// Logger
|
// Logger
|
||||||
|
@ -127,40 +124,6 @@ router.get("/identicon/:x", async (ctx) => {
|
||||||
ctx.body = fs.createReadStream(temp).on("close", () => cleanup());
|
ctx.body = fs.createReadStream(temp).on("close", () => cleanup());
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/verify-email/:code", async (ctx) => {
|
|
||||||
const profile = await UserProfiles.findOneBy({
|
|
||||||
emailVerifyCode: ctx.params.code,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (profile != null) {
|
|
||||||
ctx.body = "Verify succeeded!";
|
|
||||||
ctx.status = 200;
|
|
||||||
|
|
||||||
await UserProfiles.update(
|
|
||||||
{ userId: profile.userId },
|
|
||||||
{
|
|
||||||
emailVerified: true,
|
|
||||||
emailVerifyCode: null,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
publishMainStream(
|
|
||||||
profile.userId,
|
|
||||||
"meUpdated",
|
|
||||||
await Users.pack(
|
|
||||||
profile.userId,
|
|
||||||
{ id: profile.userId },
|
|
||||||
{
|
|
||||||
detail: true,
|
|
||||||
includeSecrets: true,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ctx.status = 404;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mastoRouter.get("/oauth/authorize", async (ctx) => {
|
mastoRouter.get("/oauth/authorize", async (ctx) => {
|
||||||
const { client_id, state, redirect_uri } = ctx.request.query;
|
const { client_id, state, redirect_uri } = ctx.request.query;
|
||||||
console.log(ctx.request.req);
|
console.log(ctx.request.req);
|
||||||
|
|
|
@ -8,11 +8,13 @@ import { readFileSync } from "node:fs";
|
||||||
import Koa from "koa";
|
import Koa from "koa";
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import send from "koa-send";
|
import send from "koa-send";
|
||||||
|
import favicon from "koa-favicon";
|
||||||
import views from "koa-views";
|
import views from "koa-views";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { createBullBoard } from "@bull-board/api";
|
import { createBullBoard } from "@bull-board/api";
|
||||||
import { BullAdapter } from "@bull-board/api/bullAdapter.js";
|
import { BullAdapter } from "@bull-board/api/bullAdapter.js";
|
||||||
import { KoaAdapter } from "@bull-board/koa";
|
import { KoaAdapter } from "@bull-board/koa";
|
||||||
|
|
||||||
import { In, IsNull } from "typeorm";
|
import { In, IsNull } from "typeorm";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
|
@ -96,14 +98,8 @@ app.use(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Favicon Router
|
// Serve favicon
|
||||||
app.use(async (ctx, next) => {
|
app.use(favicon(`${_dirname}/../../../assets/favicon.ico`));
|
||||||
if (ctx.path != "/favicon.ico") return next();
|
|
||||||
const meta = await fetchMeta();
|
|
||||||
if (meta.iconUrl === "")
|
|
||||||
ctx.body = readFileSync(`${_dirname}/../../../assets/favicon.ico`);
|
|
||||||
else ctx.redirect(meta.iconUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Common request handler
|
// Common request handler
|
||||||
app.use(async (ctx, next) => {
|
app.use(async (ctx, next) => {
|
||||||
|
|
|
@ -33,7 +33,6 @@ export async function sendEmail(
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: htmlサニタイズ
|
|
||||||
const info = await transporter.sendMail({
|
const info = await transporter.sendMail({
|
||||||
from: meta.email!,
|
from: meta.email!,
|
||||||
to: to,
|
to: to,
|
||||||
|
@ -44,81 +43,23 @@ export async function sendEmail(
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>${subject}</title>
|
<title>${subject}</title>
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
background: #191724;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding: 16px;
|
|
||||||
margin: 0;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #31748f;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: #1f1d2e;
|
|
||||||
color: #e0def4;
|
|
||||||
}
|
|
||||||
main > header {
|
|
||||||
padding: 32px;
|
|
||||||
background: #31748f;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
main > header > img {
|
|
||||||
max-width: 128px;
|
|
||||||
max-height: 72px;
|
|
||||||
vertical-align: bottom;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
main > article {
|
|
||||||
padding: 32px;
|
|
||||||
}
|
|
||||||
main > article > h1 {
|
|
||||||
margin: 0 0 1em 0;
|
|
||||||
}
|
|
||||||
main > footer {
|
|
||||||
padding: 32px;
|
|
||||||
border-top: solid 1px #26233a;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
box-sizing: border-box;
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 16px auto 0 auto;
|
|
||||||
padding: 0 32px;
|
|
||||||
}
|
|
||||||
nav > a {
|
|
||||||
color: #6e6a86;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body style="background: #191724; padding: 16px; margin: 0; font-family: sans-serif; font-size: 14px;">
|
||||||
<main>
|
<main style="max-width: 500px; margin: 0 auto; background: #1f1d2e; color: #e0def4;">
|
||||||
<header>
|
<header style="padding: 32px; background: #31748f; display: flex;">
|
||||||
<img src="${meta.logoImageUrl || meta.iconUrl || iconUrl}" height="100"/>
|
<img src="${meta.logoImageUrl || meta.iconUrl || iconUrl}" style="max-width: 128px; max-height: 72px; vertical-align: bottom; margin-right: 16px;"/>
|
||||||
<h1>${meta.name}</h1>
|
<h1 style="margin: 0 0 1em 0;">${meta.name}</h1>
|
||||||
</header>
|
</header>
|
||||||
<article>
|
<article style="padding: 32px;">
|
||||||
<h1>${subject}</h1>
|
<h1>${subject}</h1>
|
||||||
<div>${html}</div>
|
<div>${html}</div>
|
||||||
</article>
|
</article>
|
||||||
<footer>
|
<footer style="padding: 32px; border-top: solid 1px #26233a;">
|
||||||
<a href="${emailSettingUrl}">${"Email setting"}</a>
|
<a href="${emailSettingUrl}" style="color: #31748f !important;">${"Email setting"}</a>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
<nav>
|
<nav style="box-sizing: border-box; max-width: 500px; margin: 16px auto 0 auto; padding: 0 32px;">
|
||||||
<a href="${config.url}">${config.host}</a>
|
<a href="${config.url}" style="color: #6e6a86 !important;">${config.host}</a>
|
||||||
</nav>
|
</nav>
|
||||||
</body>
|
</body>
|
||||||
</html>`,
|
</html>`,
|
||||||
|
|
|
@ -195,8 +195,7 @@ function onMousedown(evt: MouseEvent): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
outline: solid 2px var(--focus);
|
outline: auto;
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.inline {
|
&.inline {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
|
ref="el"
|
||||||
class="_button"
|
class="_button"
|
||||||
:class="{ showLess: modelValue, fade: !modelValue }"
|
:class="{ showLess: modelValue, fade: !modelValue }"
|
||||||
@click.stop="toggle"
|
@click.stop="toggle"
|
||||||
|
@ -12,7 +13,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { length } from "stringz";
|
import { length } from "stringz";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import { concat } from "@/scripts/array";
|
import { concat } from "@/scripts/array";
|
||||||
|
@ -27,6 +28,8 @@ const emit = defineEmits<{
|
||||||
(ev: "update:modelValue", v: boolean): void;
|
(ev: "update:modelValue", v: boolean): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const el = ref<HTMLElement>();
|
||||||
|
|
||||||
const label = computed(() => {
|
const label = computed(() => {
|
||||||
return concat([
|
return concat([
|
||||||
props.note.text
|
props.note.text
|
||||||
|
@ -43,6 +46,14 @@ const label = computed(() => {
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
emit("update:modelValue", !props.modelValue);
|
emit("update:modelValue", !props.modelValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function focus() {
|
||||||
|
el.value.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
focus
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -62,9 +73,46 @@ const toggle = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:hover > span {
|
&:hover > span, &:focus > span {
|
||||||
background: var(--cwFg) !important;
|
background: var(--cwFg) !important;
|
||||||
color: var(--cwBg) !important;
|
color: var(--cwBg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fade {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--panel);
|
||||||
|
padding: 0.4em 1em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||||
|
}
|
||||||
|
&:hover, &:focus {
|
||||||
|
> span {
|
||||||
|
background: var(--panelHighlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.showLess {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1em;
|
||||||
|
position: sticky;
|
||||||
|
bottom: var(--stickyBottom);
|
||||||
|
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--panel);
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: 0 0 7px 7px var(--bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -95,7 +95,7 @@ export default defineComponent({
|
||||||
h(MkAd, {
|
h(MkAd, {
|
||||||
class: "a", // advertiseの意(ブロッカー対策)
|
class: "a", // advertiseの意(ブロッカー対策)
|
||||||
key: item.id + ":ad",
|
key: item.id + ":ad",
|
||||||
prefer: ["horizontal", "horizontal-big"],
|
prefer: ["inline", "inline-big"],
|
||||||
}),
|
}),
|
||||||
el,
|
el,
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="thumbnail" class="zdjebgpv">
|
<button ref="thumbnail" class="zdjebgpv">
|
||||||
<ImgWithBlurhash
|
<ImgWithBlurhash
|
||||||
v-if="isThumbnailAvailable"
|
v-if="isThumbnailAvailable"
|
||||||
:hash="file.blurhash"
|
:hash="file.blurhash"
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
v-if="isThumbnailAvailable && is === 'video'"
|
v-if="isThumbnailAvailable && is === 'video'"
|
||||||
class="ph-file-video ph-bold ph-lg icon-sub"
|
class="ph-file-video ph-bold ph-lg icon-sub"
|
||||||
></i>
|
></i>
|
||||||
</div>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -88,6 +88,9 @@ const isThumbnailAvailable = computed(() => {
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
> .icon-sub {
|
> .icon-sub {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -1,157 +1,160 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<FocusTrap v-bind:active="isActive">
|
||||||
class="omfetrab"
|
<div
|
||||||
:class="['s' + size, 'w' + width, 'h' + height, { asDrawer }]"
|
class="omfetrab"
|
||||||
:style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"
|
:class="['s' + size, 'w' + width, 'h' + height, { asDrawer }]"
|
||||||
>
|
:style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"
|
||||||
<input
|
tabindex="-1"
|
||||||
ref="search"
|
>
|
||||||
v-model.trim="q"
|
<input
|
||||||
class="search"
|
ref="search"
|
||||||
data-prevent-emoji-insert
|
v-model.trim="q"
|
||||||
:class="{ filled: q != null && q != '' }"
|
class="search"
|
||||||
:placeholder="i18n.ts.search"
|
data-prevent-emoji-insert
|
||||||
type="search"
|
:class="{ filled: q != null && q != '' }"
|
||||||
@paste.stop="paste"
|
:placeholder="i18n.ts.search"
|
||||||
@keyup.enter="done()"
|
type="search"
|
||||||
/>
|
@paste.stop="paste"
|
||||||
<div ref="emojis" class="emojis">
|
@keyup.enter="done()"
|
||||||
<section class="result">
|
/>
|
||||||
<div v-if="searchResultCustom.length > 0" class="body">
|
<div ref="emojis" class="emojis">
|
||||||
<button
|
<section class="result">
|
||||||
v-for="emoji in searchResultCustom"
|
<div v-if="searchResultCustom.length > 0" class="body">
|
||||||
:key="emoji.id"
|
|
||||||
class="_button item"
|
|
||||||
:title="emoji.name"
|
|
||||||
tabindex="0"
|
|
||||||
@click="chosen(emoji, $event)"
|
|
||||||
>
|
|
||||||
<!--<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>-->
|
|
||||||
<img
|
|
||||||
class="emoji"
|
|
||||||
:src="
|
|
||||||
disableShowingAnimatedImages
|
|
||||||
? getStaticImageUrl(emoji.url)
|
|
||||||
: emoji.url
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div v-if="searchResultUnicode.length > 0" class="body">
|
|
||||||
<button
|
|
||||||
v-for="emoji in searchResultUnicode"
|
|
||||||
:key="emoji.name"
|
|
||||||
class="_button item"
|
|
||||||
:title="emoji.name"
|
|
||||||
tabindex="0"
|
|
||||||
@click="chosen(emoji, $event)"
|
|
||||||
>
|
|
||||||
<MkEmoji class="emoji" :emoji="emoji.char" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div v-if="tab === 'index'" class="group index">
|
|
||||||
<section v-if="showPinned">
|
|
||||||
<div class="body">
|
|
||||||
<button
|
<button
|
||||||
v-for="emoji in pinned"
|
v-for="emoji in searchResultCustom"
|
||||||
:key="emoji"
|
:key="emoji.id"
|
||||||
class="_button item"
|
class="_button item"
|
||||||
|
:title="emoji.name"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click="chosen(emoji, $event)"
|
@click="chosen(emoji, $event)"
|
||||||
>
|
>
|
||||||
<MkEmoji
|
<!--<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>-->
|
||||||
|
<img
|
||||||
class="emoji"
|
class="emoji"
|
||||||
:emoji="emoji"
|
:src="
|
||||||
:normal="true"
|
disableShowingAnimatedImages
|
||||||
|
? getStaticImageUrl(emoji.url)
|
||||||
|
: emoji.url
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="searchResultUnicode.length > 0" class="body">
|
||||||
|
<button
|
||||||
|
v-for="emoji in searchResultUnicode"
|
||||||
|
:key="emoji.name"
|
||||||
|
class="_button item"
|
||||||
|
:title="emoji.name"
|
||||||
|
tabindex="0"
|
||||||
|
@click="chosen(emoji, $event)"
|
||||||
|
>
|
||||||
|
<MkEmoji class="emoji" :emoji="emoji.char" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<div v-if="tab === 'index'" class="group index">
|
||||||
<header class="_acrylic">
|
<section v-if="showPinned">
|
||||||
<i class="ph-alarm ph-bold ph-fw ph-lg"></i>
|
<div class="body">
|
||||||
{{ i18n.ts.recentUsed }}
|
<button
|
||||||
</header>
|
v-for="emoji in pinned"
|
||||||
<div class="body">
|
:key="emoji"
|
||||||
<button
|
class="_button item"
|
||||||
v-for="emoji in recentlyUsedEmojis"
|
tabindex="0"
|
||||||
:key="emoji"
|
@click="chosen(emoji, $event)"
|
||||||
class="_button item"
|
>
|
||||||
@click="chosen(emoji, $event)"
|
<MkEmoji
|
||||||
>
|
class="emoji"
|
||||||
<MkEmoji
|
:emoji="emoji"
|
||||||
class="emoji"
|
:normal="true"
|
||||||
:emoji="emoji"
|
/>
|
||||||
:normal="true"
|
</button>
|
||||||
/>
|
</div>
|
||||||
</button>
|
</section>
|
||||||
</div>
|
|
||||||
</section>
|
<section>
|
||||||
|
<header class="_acrylic">
|
||||||
|
<i class="ph-alarm ph-bold ph-fw ph-lg"></i>
|
||||||
|
{{ i18n.ts.recentUsed }}
|
||||||
|
</header>
|
||||||
|
<div class="body">
|
||||||
|
<button
|
||||||
|
v-for="emoji in recentlyUsedEmojis"
|
||||||
|
:key="emoji"
|
||||||
|
class="_button item"
|
||||||
|
@click="chosen(emoji, $event)"
|
||||||
|
>
|
||||||
|
<MkEmoji
|
||||||
|
class="emoji"
|
||||||
|
:emoji="emoji"
|
||||||
|
:normal="true"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div v-once class="group">
|
||||||
|
<header>{{ i18n.ts.customEmojis }}</header>
|
||||||
|
<XSection
|
||||||
|
v-for="category in customEmojiCategories"
|
||||||
|
:key="'custom:' + category"
|
||||||
|
:initial-shown="false"
|
||||||
|
:emojis="
|
||||||
|
customEmojis
|
||||||
|
.filter((e) => e.category === category)
|
||||||
|
.map((e) => ':' + e.name + ':')
|
||||||
|
"
|
||||||
|
@chosen="chosen"
|
||||||
|
>{{ category || i18n.ts.other }}</XSection
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div v-once class="group">
|
||||||
|
<header>{{ i18n.ts.emoji }}</header>
|
||||||
|
<XSection
|
||||||
|
v-for="category in categories"
|
||||||
|
:key="category"
|
||||||
|
:emojis="
|
||||||
|
emojilist
|
||||||
|
.filter((e) => e.category === category)
|
||||||
|
.map((e) => e.char)
|
||||||
|
"
|
||||||
|
@chosen="chosen"
|
||||||
|
>{{ category }}</XSection
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-once class="group">
|
<div class="tabs">
|
||||||
<header>{{ i18n.ts.customEmojis }}</header>
|
<button
|
||||||
<XSection
|
class="_button tab"
|
||||||
v-for="category in customEmojiCategories"
|
:class="{ active: tab === 'index' }"
|
||||||
:key="'custom:' + category"
|
@click="tab = 'index'"
|
||||||
:initial-shown="false"
|
|
||||||
:emojis="
|
|
||||||
customEmojis
|
|
||||||
.filter((e) => e.category === category)
|
|
||||||
.map((e) => ':' + e.name + ':')
|
|
||||||
"
|
|
||||||
@chosen="chosen"
|
|
||||||
>{{ category || i18n.ts.other }}</XSection
|
|
||||||
>
|
>
|
||||||
</div>
|
<i class="ph-asterisk ph-bold ph-lg ph-fw ph-lg"></i>
|
||||||
<div v-once class="group">
|
</button>
|
||||||
<header>{{ i18n.ts.emoji }}</header>
|
<button
|
||||||
<XSection
|
class="_button tab"
|
||||||
v-for="category in categories"
|
:class="{ active: tab === 'custom' }"
|
||||||
:key="category"
|
@click="tab = 'custom'"
|
||||||
:emojis="
|
|
||||||
emojilist
|
|
||||||
.filter((e) => e.category === category)
|
|
||||||
.map((e) => e.char)
|
|
||||||
"
|
|
||||||
@chosen="chosen"
|
|
||||||
>{{ category }}</XSection
|
|
||||||
>
|
>
|
||||||
|
<i class="ph-smiley ph-bold ph-lg ph-fw ph-lg"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="_button tab"
|
||||||
|
:class="{ active: tab === 'unicode' }"
|
||||||
|
@click="tab = 'unicode'"
|
||||||
|
>
|
||||||
|
<i class="ph-leaf ph-bold ph-lg ph-fw ph-lg"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="_button tab"
|
||||||
|
:class="{ active: tab === 'tags' }"
|
||||||
|
@click="tab = 'tags'"
|
||||||
|
>
|
||||||
|
<i class="ph-hash ph-bold ph-lg ph-fw ph-lg"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tabs">
|
</FocusTrap>
|
||||||
<button
|
|
||||||
class="_button tab"
|
|
||||||
:class="{ active: tab === 'index' }"
|
|
||||||
@click="tab = 'index'"
|
|
||||||
>
|
|
||||||
<i class="ph-asterisk ph-bold ph-lg ph-fw ph-lg"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="_button tab"
|
|
||||||
:class="{ active: tab === 'custom' }"
|
|
||||||
@click="tab = 'custom'"
|
|
||||||
>
|
|
||||||
<i class="ph-smiley ph-bold ph-lg ph-fw ph-lg"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="_button tab"
|
|
||||||
:class="{ active: tab === 'unicode' }"
|
|
||||||
@click="tab = 'unicode'"
|
|
||||||
>
|
|
||||||
<i class="ph-leaf ph-bold ph-lg ph-fw ph-lg"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="_button tab"
|
|
||||||
:class="{ active: tab === 'tags' }"
|
|
||||||
@click="tab = 'tags'"
|
|
||||||
>
|
|
||||||
<i class="ph-hash ph-bold ph-lg ph-fw ph-lg"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -171,6 +174,7 @@ import { deviceKind } from "@/scripts/device-kind";
|
||||||
import { emojiCategories, instance } from "@/instance";
|
import { emojiCategories, instance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
import { FocusTrap } from 'focus-trap-vue';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
|
@ -20,9 +20,12 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
compiledFormula(): any {
|
compiledFormula(): any {
|
||||||
return katex.renderToString(this.formula, {
|
const katexString = katex.renderToString(this.formula, {
|
||||||
throwOnError: false,
|
throwOnError: false,
|
||||||
} as any);
|
} as any);
|
||||||
|
return this.block
|
||||||
|
? `<div style="text-align:center">${katexString}</div>`
|
||||||
|
: katexString;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -139,7 +139,7 @@ function close() {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
||||||
&:hover {
|
&:hover, &:focus-visible {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
background: var(--accentedBg);
|
background: var(--accentedBg);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -138,6 +138,10 @@ watch(
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
box-sizing: border-box;
|
||||||
|
&:focus-visible {
|
||||||
|
border: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
> .gif {
|
> .gif {
|
||||||
background-color: var(--fg);
|
background-color: var(--fg);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="el" class="sfhdhdhr">
|
<div ref="el" class="sfhdhdhr" tabindex="-1">
|
||||||
<MkMenu
|
<MkMenu
|
||||||
ref="menu"
|
ref="menu"
|
||||||
:items="items"
|
:items="items"
|
||||||
:align="align"
|
:align="align"
|
||||||
:width="width"
|
:width="width"
|
||||||
:as-drawer="false"
|
:as-drawer="false"
|
||||||
@close="onChildClosed"
|
@close="onChildClosed"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -23,7 +23,6 @@ import {
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import MkMenu from "./MkMenu.vue";
|
import MkMenu from "./MkMenu.vue";
|
||||||
import { MenuItem } from "@/types/menu";
|
import { MenuItem } from "@/types/menu";
|
||||||
import * as os from "@/os";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
items: MenuItem[];
|
items: MenuItem[];
|
||||||
|
|
|
@ -1,191 +1,188 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<FocusTrap v-bind:active="isActive">
|
||||||
<div
|
<div tabindex="-1" v-focus>
|
||||||
ref="itemsEl"
|
<div
|
||||||
v-hotkey="keymap"
|
ref="itemsEl"
|
||||||
class="rrevdjwt _popup _shadow"
|
class="rrevdjwt _popup _shadow"
|
||||||
:class="{ center: align === 'center', asDrawer }"
|
:class="{ center: align === 'center', asDrawer }"
|
||||||
:style="{
|
:style="{
|
||||||
width: width && !asDrawer ? width + 'px' : '',
|
width: width && !asDrawer ? width + 'px' : '',
|
||||||
maxHeight: maxHeight ? maxHeight + 'px' : '',
|
maxHeight: maxHeight ? maxHeight + 'px' : '',
|
||||||
}"
|
}"
|
||||||
@contextmenu.self="(e) => e.preventDefault()"
|
@contextmenu.self="(e) => e.preventDefault()"
|
||||||
>
|
>
|
||||||
<template v-for="(item, i) in items2">
|
<template v-for="(item, i) in items2">
|
||||||
<div v-if="item === null" class="divider"></div>
|
<div v-if="item === null" class="divider"></div>
|
||||||
<span v-else-if="item.type === 'label'" class="label item">
|
<span v-else-if="item.type === 'label'" class="label item">
|
||||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-else-if="item.type === 'pending'"
|
|
||||||
:tabindex="i"
|
|
||||||
class="pending item"
|
|
||||||
>
|
|
||||||
<span><MkEllipsis /></span>
|
|
||||||
</span>
|
|
||||||
<MkA
|
|
||||||
v-else-if="item.type === 'link'"
|
|
||||||
:to="item.to"
|
|
||||||
:tabindex="i"
|
|
||||||
class="_button item"
|
|
||||||
@click.passive="close(true)"
|
|
||||||
@mouseenter.passive="onItemMouseEnter(item)"
|
|
||||||
@mouseleave.passive="onItemMouseLeave(item)"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
v-if="item.icon"
|
|
||||||
class="ph-fw ph-lg"
|
|
||||||
:class="item.icon"
|
|
||||||
></i>
|
|
||||||
<span v-else-if="item.icons">
|
|
||||||
<i
|
|
||||||
v-for="icon in item.icons"
|
|
||||||
class="ph-fw ph-lg"
|
|
||||||
:class="icon"
|
|
||||||
></i>
|
|
||||||
</span>
|
</span>
|
||||||
<MkAvatar
|
<span
|
||||||
v-if="item.avatar"
|
v-else-if="item.type === 'pending'"
|
||||||
:user="item.avatar"
|
class="pending item"
|
||||||
class="avatar"
|
|
||||||
/>
|
|
||||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
|
||||||
<span v-if="item.indicate" class="indicator"
|
|
||||||
><i class="ph-circle ph-fill"></i
|
|
||||||
></span>
|
|
||||||
</MkA>
|
|
||||||
<a
|
|
||||||
v-else-if="item.type === 'a'"
|
|
||||||
:href="item.href"
|
|
||||||
:target="item.target"
|
|
||||||
:download="item.download"
|
|
||||||
:tabindex="i"
|
|
||||||
class="_button item"
|
|
||||||
@click="close(true)"
|
|
||||||
@mouseenter.passive="onItemMouseEnter(item)"
|
|
||||||
@mouseleave.passive="onItemMouseLeave(item)"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
v-if="item.icon"
|
|
||||||
class="ph-fw ph-lg"
|
|
||||||
:class="item.icon"
|
|
||||||
></i>
|
|
||||||
<span v-else-if="item.icons">
|
|
||||||
<i
|
|
||||||
v-for="icon in item.icons"
|
|
||||||
class="ph-fw ph-lg"
|
|
||||||
:class="icon"
|
|
||||||
></i>
|
|
||||||
</span>
|
|
||||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
|
||||||
<span v-if="item.indicate" class="indicator"
|
|
||||||
><i class="ph-circle ph-fill"></i
|
|
||||||
></span>
|
|
||||||
</a>
|
|
||||||
<button
|
|
||||||
v-else-if="item.type === 'user' && !items.hidden"
|
|
||||||
:tabindex="i"
|
|
||||||
class="_button item"
|
|
||||||
:class="{ active: item.active }"
|
|
||||||
:disabled="item.active"
|
|
||||||
@click="clicked(item.action, $event)"
|
|
||||||
@mouseenter.passive="onItemMouseEnter(item)"
|
|
||||||
@mouseleave.passive="onItemMouseLeave(item)"
|
|
||||||
>
|
|
||||||
<MkAvatar :user="item.user" class="avatar" /><MkUserName
|
|
||||||
:user="item.user"
|
|
||||||
/>
|
|
||||||
<span v-if="item.indicate" class="indicator"
|
|
||||||
><i class="ph-circle ph-fill"></i
|
|
||||||
></span>
|
|
||||||
</button>
|
|
||||||
<span
|
|
||||||
v-else-if="item.type === 'switch'"
|
|
||||||
:tabindex="i"
|
|
||||||
class="item"
|
|
||||||
@mouseenter.passive="onItemMouseEnter(item)"
|
|
||||||
@mouseleave.passive="onItemMouseLeave(item)"
|
|
||||||
>
|
|
||||||
<FormSwitch
|
|
||||||
v-model="item.ref"
|
|
||||||
:disabled="item.disabled"
|
|
||||||
class="form-switch"
|
|
||||||
:style="item.textStyle || ''"
|
|
||||||
>{{ item.text }}</FormSwitch
|
|
||||||
>
|
>
|
||||||
|
<span><MkEllipsis /></span>
|
||||||
|
</span>
|
||||||
|
<MkA
|
||||||
|
v-else-if="item.type === 'link'"
|
||||||
|
:to="item.to"
|
||||||
|
class="_button item"
|
||||||
|
@click.passive="close(true)"
|
||||||
|
@mouseenter.passive="onItemMouseEnter(item)"
|
||||||
|
@mouseleave.passive="onItemMouseLeave(item)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
v-if="item.icon"
|
||||||
|
class="ph-fw ph-lg"
|
||||||
|
:class="item.icon"
|
||||||
|
></i>
|
||||||
|
<span v-else-if="item.icons">
|
||||||
|
<i
|
||||||
|
v-for="icon in item.icons"
|
||||||
|
class="ph-fw ph-lg"
|
||||||
|
:class="icon"
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
<MkAvatar
|
||||||
|
v-if="item.avatar"
|
||||||
|
:user="item.avatar"
|
||||||
|
class="avatar"
|
||||||
|
disableLink
|
||||||
|
/>
|
||||||
|
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||||
|
<span v-if="item.indicate" class="indicator"
|
||||||
|
><i class="ph-circle ph-fill"></i
|
||||||
|
></span>
|
||||||
|
</MkA>
|
||||||
|
<a
|
||||||
|
v-else-if="item.type === 'a'"
|
||||||
|
:href="item.href"
|
||||||
|
:target="item.target"
|
||||||
|
:download="item.download"
|
||||||
|
class="_button item"
|
||||||
|
@click="close(true)"
|
||||||
|
@mouseenter.passive="onItemMouseEnter(item)"
|
||||||
|
@mouseleave.passive="onItemMouseLeave(item)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
v-if="item.icon"
|
||||||
|
class="ph-fw ph-lg"
|
||||||
|
:class="item.icon"
|
||||||
|
></i>
|
||||||
|
<span v-else-if="item.icons">
|
||||||
|
<i
|
||||||
|
v-for="icon in item.icons"
|
||||||
|
class="ph-fw ph-lg"
|
||||||
|
:class="icon"
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||||
|
<span v-if="item.indicate" class="indicator"
|
||||||
|
><i class="ph-circle ph-fill"></i
|
||||||
|
></span>
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
v-else-if="item.type === 'user' && !items.hidden"
|
||||||
|
class="_button item"
|
||||||
|
:class="{ active: item.active }"
|
||||||
|
:disabled="item.active"
|
||||||
|
@click="clicked(item.action, $event)"
|
||||||
|
@mouseenter.passive="onItemMouseEnter(item)"
|
||||||
|
@mouseleave.passive="onItemMouseLeave(item)"
|
||||||
|
>
|
||||||
|
<MkAvatar :user="item.user" class="avatar" disableLink /><MkUserName
|
||||||
|
:user="item.user"
|
||||||
|
/>
|
||||||
|
<span v-if="item.indicate" class="indicator"
|
||||||
|
><i class="ph-circle ph-fill"></i
|
||||||
|
></span>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
v-else-if="item.type === 'switch'"
|
||||||
|
class="item"
|
||||||
|
@mouseenter.passive="onItemMouseEnter(item)"
|
||||||
|
@mouseleave.passive="onItemMouseLeave(item)"
|
||||||
|
>
|
||||||
|
<FormSwitch
|
||||||
|
v-model="item.ref"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
class="form-switch"
|
||||||
|
:style="item.textStyle || ''"
|
||||||
|
>{{ item.text }}</FormSwitch
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
v-else-if="item.type === 'parent'"
|
||||||
|
class="_button item parent"
|
||||||
|
:class="{ childShowing: childShowingItem === item }"
|
||||||
|
@mouseenter="showChildren(item, $event)"
|
||||||
|
@click="showChildren(item, $event)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
v-if="item.icon"
|
||||||
|
class="ph-fw ph-lg"
|
||||||
|
:class="item.icon"
|
||||||
|
></i>
|
||||||
|
<span v-else-if="item.icons">
|
||||||
|
<i
|
||||||
|
v-for="icon in item.icons"
|
||||||
|
class="ph-fw ph-lg"
|
||||||
|
:class="icon"
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||||
|
<span class="caret"
|
||||||
|
><i class="ph-caret-right ph-bold ph-lg ph-fw ph-lg"></i
|
||||||
|
></span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-else-if="!item.hidden"
|
||||||
|
class="_button item"
|
||||||
|
:class="{ danger: item.danger, active: item.active }"
|
||||||
|
:disabled="item.active"
|
||||||
|
@click="clicked(item.action, $event)"
|
||||||
|
@mouseenter.passive="onItemMouseEnter(item)"
|
||||||
|
@mouseleave.passive="onItemMouseLeave(item)"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
v-if="item.icon"
|
||||||
|
class="ph-fw ph-lg"
|
||||||
|
:class="item.icon"
|
||||||
|
></i>
|
||||||
|
<span v-else-if="item.icons">
|
||||||
|
<i
|
||||||
|
v-for="icon in item.icons"
|
||||||
|
class="ph-fw ph-lg"
|
||||||
|
:class="icon"
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
<MkAvatar
|
||||||
|
v-if="item.avatar"
|
||||||
|
:user="item.avatar"
|
||||||
|
class="avatar"
|
||||||
|
disableLink
|
||||||
|
/>
|
||||||
|
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||||
|
<span v-if="item.indicate" class="indicator"
|
||||||
|
><i class="ph-circle ph-fill"></i
|
||||||
|
></span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<span v-if="items2.length === 0" class="none item">
|
||||||
|
<span>{{ i18n.ts.none }}</span>
|
||||||
</span>
|
</span>
|
||||||
<button
|
</div>
|
||||||
v-else-if="item.type === 'parent'"
|
<div v-if="childMenu" class="child">
|
||||||
:tabindex="i"
|
<XChild
|
||||||
class="_button item parent"
|
ref="child"
|
||||||
:class="{ childShowing: childShowingItem === item }"
|
:items="childMenu"
|
||||||
@mouseenter="showChildren(item, $event)"
|
:target-element="childTarget"
|
||||||
>
|
:root-element="itemsEl"
|
||||||
<i
|
showing
|
||||||
v-if="item.icon"
|
@actioned="childActioned"
|
||||||
class="ph-fw ph-lg"
|
/>
|
||||||
:class="item.icon"
|
</div>
|
||||||
></i>
|
|
||||||
<span v-else-if="item.icons">
|
|
||||||
<i
|
|
||||||
v-for="icon in item.icons"
|
|
||||||
class="ph-fw ph-lg"
|
|
||||||
:class="icon"
|
|
||||||
></i>
|
|
||||||
</span>
|
|
||||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
|
||||||
<span class="caret"
|
|
||||||
><i class="ph-caret-right ph-bold ph-lg ph-fw ph-lg"></i
|
|
||||||
></span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-else-if="!item.hidden"
|
|
||||||
:tabindex="i"
|
|
||||||
class="_button item"
|
|
||||||
:class="{ danger: item.danger, active: item.active }"
|
|
||||||
:disabled="item.active"
|
|
||||||
@click="clicked(item.action, $event)"
|
|
||||||
@mouseenter.passive="onItemMouseEnter(item)"
|
|
||||||
@mouseleave.passive="onItemMouseLeave(item)"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
v-if="item.icon"
|
|
||||||
class="ph-fw ph-lg"
|
|
||||||
:class="item.icon"
|
|
||||||
></i>
|
|
||||||
<span v-else-if="item.icons">
|
|
||||||
<i
|
|
||||||
v-for="icon in item.icons"
|
|
||||||
class="ph-fw ph-lg"
|
|
||||||
:class="icon"
|
|
||||||
></i>
|
|
||||||
</span>
|
|
||||||
<MkAvatar
|
|
||||||
v-if="item.avatar"
|
|
||||||
:user="item.avatar"
|
|
||||||
class="avatar"
|
|
||||||
/>
|
|
||||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
|
||||||
<span v-if="item.indicate" class="indicator"
|
|
||||||
><i class="ph-circle ph-fill"></i
|
|
||||||
></span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
<span v-if="items2.length === 0" class="none item">
|
|
||||||
<span>{{ i18n.ts.none }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="childMenu" class="child">
|
</FocusTrap>
|
||||||
<XChild
|
|
||||||
ref="child"
|
|
||||||
:items="childMenu"
|
|
||||||
:target-element="childTarget"
|
|
||||||
:root-element="itemsEl"
|
|
||||||
showing
|
|
||||||
@actioned="childActioned"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -206,6 +203,7 @@ import FormSwitch from "@/components/form/switch.vue";
|
||||||
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from "@/types/menu";
|
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from "@/types/menu";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
import { FocusTrap } from 'focus-trap-vue';
|
||||||
|
|
||||||
const XChild = defineAsyncComponent(() => import("./MkMenu.child.vue"));
|
const XChild = defineAsyncComponent(() => import("./MkMenu.child.vue"));
|
||||||
|
|
||||||
|
@ -228,12 +226,6 @@ let items2: InnerMenuItem[] = $ref([]);
|
||||||
|
|
||||||
let child = $ref<InstanceType<typeof XChild>>();
|
let child = $ref<InstanceType<typeof XChild>>();
|
||||||
|
|
||||||
let keymap = computed(() => ({
|
|
||||||
"up|k|shift+tab": focusUp,
|
|
||||||
"down|j|tab": focusDown,
|
|
||||||
esc: close,
|
|
||||||
}));
|
|
||||||
|
|
||||||
let childShowingItem = $ref<MenuItem | null>();
|
let childShowingItem = $ref<MenuItem | null>();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -364,8 +356,7 @@ onBeforeUnmount(() => {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
overflow: hidden;
|
outline: none;
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
|
@ -389,7 +380,7 @@ onBeforeUnmount(() => {
|
||||||
transform: translateY(0em);
|
transform: translateY(0em);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:disabled):hover {
|
&:not(:disabled):hover, &:focus-visible {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
|
@ -397,6 +388,9 @@ onBeforeUnmount(() => {
|
||||||
background: var(--accentedBg);
|
background: var(--accentedBg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&:focus-visible:before {
|
||||||
|
outline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
&.danger {
|
&.danger {
|
||||||
color: #eb6f92;
|
color: #eb6f92;
|
||||||
|
|
|
@ -14,54 +14,59 @@
|
||||||
:duration="transitionDuration"
|
:duration="transitionDuration"
|
||||||
appear
|
appear
|
||||||
@after-leave="emit('closed')"
|
@after-leave="emit('closed')"
|
||||||
|
@keyup.esc="emit('click')"
|
||||||
@enter="emit('opening')"
|
@enter="emit('opening')"
|
||||||
@after-enter="onOpened"
|
@after-enter="onOpened"
|
||||||
>
|
>
|
||||||
<div
|
<FocusTrap v-model:active="isActive">
|
||||||
v-show="manualShowing != null ? manualShowing : showing"
|
|
||||||
v-hotkey.global="keymap"
|
|
||||||
:class="[
|
|
||||||
$style.root,
|
|
||||||
{
|
|
||||||
[$style.drawer]: type === 'drawer',
|
|
||||||
[$style.dialog]: type === 'dialog' || type === 'dialog:top',
|
|
||||||
[$style.popup]: type === 'popup',
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
:style="{
|
|
||||||
zIndex,
|
|
||||||
pointerEvents: (manualShowing != null ? manualShowing : showing)
|
|
||||||
? 'auto'
|
|
||||||
: 'none',
|
|
||||||
'--transformOrigin': transformOrigin,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="_modalBg data-cy-bg"
|
v-show="manualShowing != null ? manualShowing : showing"
|
||||||
|
v-hotkey.global="keymap"
|
||||||
:class="[
|
:class="[
|
||||||
$style.bg,
|
$style.root,
|
||||||
{
|
{
|
||||||
[$style.bgTransparent]: isEnableBgTransparent,
|
[$style.drawer]: type === 'drawer',
|
||||||
'data-cy-transparent': isEnableBgTransparent,
|
[$style.dialog]: type === 'dialog' || type === 'dialog:top',
|
||||||
|
[$style.popup]: type === 'popup',
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
:style="{ zIndex }"
|
:style="{
|
||||||
@click="onBgClick"
|
zIndex,
|
||||||
@mousedown="onBgClick"
|
pointerEvents: (manualShowing != null ? manualShowing : showing)
|
||||||
@contextmenu.prevent.stop="() => {}"
|
? 'auto'
|
||||||
></div>
|
: 'none',
|
||||||
<div
|
'--transformOrigin': transformOrigin,
|
||||||
ref="content"
|
}"
|
||||||
:class="[
|
tabindex="-1"
|
||||||
$style.content,
|
v-focus
|
||||||
{ [$style.fixed]: fixed, top: type === 'dialog:top' },
|
|
||||||
]"
|
|
||||||
:style="{ zIndex }"
|
|
||||||
@click.self="onBgClick"
|
|
||||||
>
|
>
|
||||||
<slot :max-height="maxHeight" :type="type"></slot>
|
<div
|
||||||
|
class="_modalBg data-cy-bg"
|
||||||
|
:class="[
|
||||||
|
$style.bg,
|
||||||
|
{
|
||||||
|
[$style.bgTransparent]: isEnableBgTransparent,
|
||||||
|
'data-cy-transparent': isEnableBgTransparent,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:style="{ zIndex }"
|
||||||
|
@click="onBgClick"
|
||||||
|
@mousedown="onBgClick"
|
||||||
|
@contextmenu.prevent.stop="() => {}"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
ref="content"
|
||||||
|
:class="[
|
||||||
|
$style.content,
|
||||||
|
{ [$style.fixed]: fixed, top: type === 'dialog:top' },
|
||||||
|
]"
|
||||||
|
:style="{ zIndex }"
|
||||||
|
@click.self="onBgClick"
|
||||||
|
>
|
||||||
|
<slot :max-height="maxHeight" :type="type"></slot>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FocusTrap>
|
||||||
</Transition>
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -71,6 +76,7 @@ import * as os from "@/os";
|
||||||
import { isTouchUsing } from "@/scripts/touch";
|
import { isTouchUsing } from "@/scripts/touch";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
import { deviceKind } from "@/scripts/device-kind";
|
import { deviceKind } from "@/scripts/device-kind";
|
||||||
|
import { FocusTrap } from 'focus-trap-vue';
|
||||||
|
|
||||||
function getFixedContainer(el: Element | null): Element | null {
|
function getFixedContainer(el: Element | null): Element | null {
|
||||||
if (el == null || el.tagName === "BODY") return null;
|
if (el == null || el.tagName === "BODY") return null;
|
||||||
|
@ -166,6 +172,7 @@ let transitionDuration = $computed(() =>
|
||||||
|
|
||||||
let contentClicking = false;
|
let contentClicking = false;
|
||||||
|
|
||||||
|
const focusedElement = document.activeElement;
|
||||||
function close(opts: { useSendAnimation?: boolean } = {}) {
|
function close(opts: { useSendAnimation?: boolean } = {}) {
|
||||||
if (opts.useSendAnimation) {
|
if (opts.useSendAnimation) {
|
||||||
useSendAnime = true;
|
useSendAnime = true;
|
||||||
|
@ -175,10 +182,12 @@ function close(opts: { useSendAnimation?: boolean } = {}) {
|
||||||
if (props.src) props.src.style.pointerEvents = "auto";
|
if (props.src) props.src.style.pointerEvents = "auto";
|
||||||
showing = false;
|
showing = false;
|
||||||
emit("close");
|
emit("close");
|
||||||
|
focusedElement.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBgClick() {
|
function onBgClick() {
|
||||||
if (contentClicking) return;
|
if (contentClicking) return;
|
||||||
|
focusedElement.focus();
|
||||||
emit("click");
|
emit("click");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,6 +490,7 @@ defineExpose({
|
||||||
}
|
}
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
|
outline: none;
|
||||||
&.dialog {
|
&.dialog {
|
||||||
> .content {
|
> .content {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
@ -158,6 +158,7 @@ function onContextmenu(ev: MouseEvent) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
contain: content;
|
contain: content;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
--root-margin: 24px;
|
--root-margin: 24px;
|
||||||
|
|
||||||
|
|
|
@ -3,59 +3,64 @@
|
||||||
ref="modal"
|
ref="modal"
|
||||||
:prefer-type="'dialog'"
|
:prefer-type="'dialog'"
|
||||||
@click="onBgClick"
|
@click="onBgClick"
|
||||||
|
@keyup.esc="$emit('close')"
|
||||||
@closed="$emit('closed')"
|
@closed="$emit('closed')"
|
||||||
>
|
>
|
||||||
<div
|
<FocusTrap v-model:active="isActive">
|
||||||
ref="rootEl"
|
<div
|
||||||
class="ebkgoccj"
|
ref="rootEl"
|
||||||
:style="{
|
class="ebkgoccj"
|
||||||
width: `${width}px`,
|
:style="{
|
||||||
height: scroll
|
width: `${width}px`,
|
||||||
? height
|
height: scroll
|
||||||
? `${height}px`
|
? height
|
||||||
: null
|
? `${height}px`
|
||||||
: height
|
: null
|
||||||
? `min(${height}px, 100%)`
|
: height
|
||||||
: '100%',
|
? `min(${height}px, 100%)`
|
||||||
}"
|
: '100%',
|
||||||
@keydown="onKeydown"
|
}"
|
||||||
>
|
@keydown="onKeydown"
|
||||||
<div ref="headerEl" class="header">
|
tabindex="-1"
|
||||||
<button
|
>
|
||||||
v-if="withOkButton"
|
<div ref="headerEl" class="header">
|
||||||
class="_button"
|
<button
|
||||||
@click="$emit('close')"
|
v-if="withOkButton"
|
||||||
>
|
class="_button"
|
||||||
<i class="ph-x ph-bold ph-lg"></i>
|
@click="$emit('close')"
|
||||||
</button>
|
>
|
||||||
<span class="title">
|
<i class="ph-x ph-bold ph-lg"></i>
|
||||||
<slot name="header"></slot>
|
</button>
|
||||||
</span>
|
<span class="title">
|
||||||
<button
|
<slot name="header"></slot>
|
||||||
v-if="!withOkButton"
|
</span>
|
||||||
class="_button"
|
<button
|
||||||
@click="$emit('close')"
|
v-if="!withOkButton"
|
||||||
>
|
class="_button"
|
||||||
<i class="ph-x ph-bold ph-lg"></i>
|
@click="$emit('close')"
|
||||||
</button>
|
>
|
||||||
<button
|
<i class="ph-x ph-bold ph-lg"></i>
|
||||||
v-if="withOkButton"
|
</button>
|
||||||
class="_button"
|
<button
|
||||||
:disabled="okButtonDisabled"
|
v-if="withOkButton"
|
||||||
@click="$emit('ok')"
|
class="_button"
|
||||||
>
|
:disabled="okButtonDisabled"
|
||||||
<i class="ph-check ph-bold ph-lg"></i>
|
@click="$emit('ok')"
|
||||||
</button>
|
>
|
||||||
|
<i class="ph-check ph-bold ph-lg"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<slot :width="bodyWidth" :height="bodyHeight"></slot>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
</FocusTrap>
|
||||||
<slot :width="bodyWidth" :height="bodyHeight"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</MkModal>
|
</MkModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted } from "vue";
|
import { onMounted, onUnmounted } from "vue";
|
||||||
|
import { FocusTrap } from 'focus-trap-vue';
|
||||||
import MkModal from "./MkModal.vue";
|
import MkModal from "./MkModal.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
:detailedView="detailedView"
|
:detailedView="detailedView"
|
||||||
:parentId="appearNote.parentId"
|
:parentId="appearNote.parentId"
|
||||||
@push="(e) => router.push(notePage(e))"
|
@push="(e) => router.push(notePage(e))"
|
||||||
|
@focusfooter="footerEl.focus()"
|
||||||
></MkSubNoteContent>
|
></MkSubNoteContent>
|
||||||
<div v-if="translating || translation" class="translation">
|
<div v-if="translating || translation" class="translation">
|
||||||
<MkLoading v-if="translating" mini />
|
<MkLoading v-if="translating" mini />
|
||||||
|
@ -117,7 +118,7 @@
|
||||||
<MkTime :time="appearNote.createdAt" mode="absolute" />
|
<MkTime :time="appearNote.createdAt" mode="absolute" />
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
<footer ref="el" class="footer" @click.stop>
|
<footer ref="footerEl" class="footer" @click.stop tabindex="-1">
|
||||||
<XReactionsViewer
|
<XReactionsViewer
|
||||||
v-if="enableEmojiReactions"
|
v-if="enableEmojiReactions"
|
||||||
ref="reactionsViewer"
|
ref="reactionsViewer"
|
||||||
|
@ -278,6 +279,7 @@ const isRenote =
|
||||||
note.poll == null;
|
note.poll == null;
|
||||||
|
|
||||||
const el = ref<HTMLElement>();
|
const el = ref<HTMLElement>();
|
||||||
|
const footerEl = ref<HTMLElement>();
|
||||||
const menuButton = ref<HTMLElement>();
|
const menuButton = ref<HTMLElement>();
|
||||||
const starButton = ref<InstanceType<typeof XStarButton>>();
|
const starButton = ref<InstanceType<typeof XStarButton>>();
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||||
|
@ -298,8 +300,8 @@ const keymap = {
|
||||||
r: () => reply(true),
|
r: () => reply(true),
|
||||||
"e|a|plus": () => react(true),
|
"e|a|plus": () => react(true),
|
||||||
q: () => renoteButton.value.renote(true),
|
q: () => renoteButton.value.renote(true),
|
||||||
"up|k|shift+tab": focusBefore,
|
"up|k": focusBefore,
|
||||||
"down|j|tab": focusAfter,
|
"down|j": focusAfter,
|
||||||
esc: blur,
|
esc: blur,
|
||||||
"m|o": () => menu(true),
|
"m|o": () => menu(true),
|
||||||
s: () => showContent.value !== showContent.value,
|
s: () => showContent.value !== showContent.value,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-size="{ min: [350, 500] }" class="fefdfafb">
|
<div v-size="{ min: [350, 500] }" class="fefdfafb">
|
||||||
<MkAvatar class="avatar" :user="$i" />
|
<MkAvatar class="avatar" :user="$i" disableLink />
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<MkUserName :user="$i" />
|
<MkUserName :user="$i" />
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
:note="note"
|
:note="note"
|
||||||
:parentId="appearNote.parentId"
|
:parentId="appearNote.parentId"
|
||||||
:conversation="conversation"
|
:conversation="conversation"
|
||||||
|
@focusfooter="footerEl.focus()"
|
||||||
/>
|
/>
|
||||||
<div v-if="translating || translation" class="translation">
|
<div v-if="translating || translation" class="translation">
|
||||||
<MkLoading v-if="translating" mini />
|
<MkLoading v-if="translating" mini />
|
||||||
|
@ -46,7 +47,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer class="footer" @click.stop>
|
<footer ref="footerEl" class="footer" @click.stop tabindex="-1">
|
||||||
<XReactionsViewer
|
<XReactionsViewer
|
||||||
v-if="enableEmojiReactions"
|
v-if="enableEmojiReactions"
|
||||||
ref="reactionsViewer"
|
ref="reactionsViewer"
|
||||||
|
@ -212,6 +213,7 @@ const isRenote =
|
||||||
note.poll == null;
|
note.poll == null;
|
||||||
|
|
||||||
const el = ref<HTMLElement>();
|
const el = ref<HTMLElement>();
|
||||||
|
const footerEl = ref<HTMLElement>();
|
||||||
const menuButton = ref<HTMLElement>();
|
const menuButton = ref<HTMLElement>();
|
||||||
const starButton = ref<InstanceType<typeof XStarButton>>();
|
const starButton = ref<InstanceType<typeof XStarButton>>();
|
||||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||||
|
|
|
@ -89,7 +89,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tail">
|
<div class="tail" :class="{ collapsed }">
|
||||||
<header>
|
<header>
|
||||||
<span v-if="notification.type === 'pollEnded'">{{
|
<span v-if="notification.type === 'pollEnded'">{{
|
||||||
i18n.ts._notification.pollEnded
|
i18n.ts._notification.pollEnded
|
||||||
|
@ -112,11 +112,11 @@
|
||||||
v-if="notification.type === 'reaction'"
|
v-if="notification.type === 'reaction'"
|
||||||
class="text"
|
class="text"
|
||||||
:to="notePage(notification.note)"
|
:to="notePage(notification.note)"
|
||||||
:title="getNoteSummary(notification.note)"
|
:title="summary"
|
||||||
>
|
>
|
||||||
<i class="ph-quotes ph-fill ph-lg"></i>
|
<i class="ph-quotes ph-fill ph-lg"></i>
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="getNoteSummary(notification.note)"
|
:text="summary"
|
||||||
:plain="true"
|
:plain="true"
|
||||||
:nowrap="!full"
|
:nowrap="!full"
|
||||||
:custom-emojis="notification.note.emojis"
|
:custom-emojis="notification.note.emojis"
|
||||||
|
@ -142,10 +142,10 @@
|
||||||
v-if="notification.type === 'reply'"
|
v-if="notification.type === 'reply'"
|
||||||
class="text"
|
class="text"
|
||||||
:to="notePage(notification.note)"
|
:to="notePage(notification.note)"
|
||||||
:title="getNoteSummary(notification.note)"
|
:title="summary"
|
||||||
>
|
>
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="getNoteSummary(notification.note)"
|
:text="summary"
|
||||||
:plain="true"
|
:plain="true"
|
||||||
:nowrap="!full"
|
:nowrap="!full"
|
||||||
:custom-emojis="notification.note.emojis"
|
:custom-emojis="notification.note.emojis"
|
||||||
|
@ -155,10 +155,10 @@
|
||||||
v-if="notification.type === 'mention'"
|
v-if="notification.type === 'mention'"
|
||||||
class="text"
|
class="text"
|
||||||
:to="notePage(notification.note)"
|
:to="notePage(notification.note)"
|
||||||
:title="getNoteSummary(notification.note)"
|
:title="summary"
|
||||||
>
|
>
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="getNoteSummary(notification.note)"
|
:text="summary"
|
||||||
:plain="true"
|
:plain="true"
|
||||||
:nowrap="!full"
|
:nowrap="!full"
|
||||||
:custom-emojis="notification.note.emojis"
|
:custom-emojis="notification.note.emojis"
|
||||||
|
@ -168,10 +168,10 @@
|
||||||
v-if="notification.type === 'quote'"
|
v-if="notification.type === 'quote'"
|
||||||
class="text"
|
class="text"
|
||||||
:to="notePage(notification.note)"
|
:to="notePage(notification.note)"
|
||||||
:title="getNoteSummary(notification.note)"
|
:title="summary"
|
||||||
>
|
>
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="getNoteSummary(notification.note)"
|
:text="summary"
|
||||||
:plain="true"
|
:plain="true"
|
||||||
:nowrap="!full"
|
:nowrap="!full"
|
||||||
:custom-emojis="notification.note.emojis"
|
:custom-emojis="notification.note.emojis"
|
||||||
|
@ -181,11 +181,11 @@
|
||||||
v-if="notification.type === 'pollVote'"
|
v-if="notification.type === 'pollVote'"
|
||||||
class="text"
|
class="text"
|
||||||
:to="notePage(notification.note)"
|
:to="notePage(notification.note)"
|
||||||
:title="getNoteSummary(notification.note)"
|
:title="summary"
|
||||||
>
|
>
|
||||||
<i class="ph-quotes ph-fill ph-lg"></i>
|
<i class="ph-quotes ph-fill ph-lg"></i>
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="getNoteSummary(notification.note)"
|
:text="summary"
|
||||||
:plain="true"
|
:plain="true"
|
||||||
:nowrap="!full"
|
:nowrap="!full"
|
||||||
:custom-emojis="notification.note.emojis"
|
:custom-emojis="notification.note.emojis"
|
||||||
|
@ -196,11 +196,11 @@
|
||||||
v-if="notification.type === 'pollEnded'"
|
v-if="notification.type === 'pollEnded'"
|
||||||
class="text"
|
class="text"
|
||||||
:to="notePage(notification.note)"
|
:to="notePage(notification.note)"
|
||||||
:title="getNoteSummary(notification.note)"
|
:title="summary"
|
||||||
>
|
>
|
||||||
<i class="ph-quotes ph-fill ph-lg"></i>
|
<i class="ph-quotes ph-fill ph-lg"></i>
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="getNoteSummary(notification.note)"
|
:text="summary"
|
||||||
:plain="true"
|
:plain="true"
|
||||||
:nowrap="!full"
|
:nowrap="!full"
|
||||||
:custom-emojis="notification.note.emojis"
|
:custom-emojis="notification.note.emojis"
|
||||||
|
@ -264,6 +264,7 @@
|
||||||
<span v-if="notification.type === 'app'" class="text">
|
<span v-if="notification.type === 'app'" class="text">
|
||||||
<Mfm :text="notification.body" :nowrap="!full" />
|
<Mfm :text="notification.body" :nowrap="!full" />
|
||||||
</span>
|
</span>
|
||||||
|
<xShowMoreButton v-if="isLong" v-model="collapsed"></xShowMoreButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -274,6 +275,7 @@ import * as misskey from "calckey-js";
|
||||||
import XReactionIcon from "@/components/MkReactionIcon.vue";
|
import XReactionIcon from "@/components/MkReactionIcon.vue";
|
||||||
import MkFollowButton from "@/components/MkFollowButton.vue";
|
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||||
import XReactionTooltip from "@/components/MkReactionTooltip.vue";
|
import XReactionTooltip from "@/components/MkReactionTooltip.vue";
|
||||||
|
import XShowMoreButton from "./MkShowMoreButton.vue";
|
||||||
import { getNoteSummary } from "@/scripts/get-note-summary";
|
import { getNoteSummary } from "@/scripts/get-note-summary";
|
||||||
import { notePage } from "@/filters/note";
|
import { notePage } from "@/filters/note";
|
||||||
import { userPage } from "@/filters/user";
|
import { userPage } from "@/filters/user";
|
||||||
|
@ -299,12 +301,19 @@ const props = withDefaults(
|
||||||
const elRef = ref<HTMLElement>(null);
|
const elRef = ref<HTMLElement>(null);
|
||||||
const reactionRef = ref(null);
|
const reactionRef = ref(null);
|
||||||
|
|
||||||
|
const summary = getNoteSummary(props.notification.note);
|
||||||
|
|
||||||
const showEmojiReactions =
|
const showEmojiReactions =
|
||||||
defaultStore.state.enableEmojiReactions ||
|
defaultStore.state.enableEmojiReactions ||
|
||||||
defaultStore.state.showEmojisInReactionNotifications;
|
defaultStore.state.showEmojisInReactionNotifications;
|
||||||
const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReaction)
|
const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReaction)
|
||||||
? instance.defaultReaction
|
? instance.defaultReaction
|
||||||
: "⭐";
|
: "⭐";
|
||||||
|
const isLong = (summary.split("\n").length > 3 || summary.length > 200);
|
||||||
|
const collapsed = $ref(isLong);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let readObserver: IntersectionObserver | undefined;
|
let readObserver: IntersectionObserver | undefined;
|
||||||
let connection;
|
let connection;
|
||||||
|
@ -486,6 +495,7 @@ useTooltip(reactionRef, (showing) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
> .tail {
|
> .tail {
|
||||||
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
|
@ -526,6 +536,17 @@ useTooltip(reactionRef, (showing) => {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.collapsed > .text {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
max-height: calc(4em + 50px);
|
||||||
|
overflow: hidden;
|
||||||
|
mask: linear-gradient(black calc(100% - 64px), transparent);
|
||||||
|
-webkit-mask: linear-gradient(
|
||||||
|
black calc(100% - 64px),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
:transparent-bg="true"
|
:transparent-bg="true"
|
||||||
@click="modal.close()"
|
@click="modal.close()"
|
||||||
@closed="emit('closed')"
|
@closed="emit('closed')"
|
||||||
|
tabindex="-1"
|
||||||
|
v-focus
|
||||||
>
|
>
|
||||||
<MkMenu
|
<MkMenu
|
||||||
:items="items"
|
:items="items"
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
:class="{ active: showPreview }"
|
:class="{ active: showPreview }"
|
||||||
@click="showPreview = !showPreview"
|
@click="showPreview = !showPreview"
|
||||||
>
|
>
|
||||||
<i class="ph-file-code ph-bold ph-lg"></i>
|
<i class="ph-binoculars ph-bold ph-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="submit _buttonGradate"
|
class="submit _buttonGradate"
|
||||||
|
|
|
@ -154,22 +154,22 @@ export default defineComponent({
|
||||||
? i18n.ts.unmarkAsSensitive
|
? i18n.ts.unmarkAsSensitive
|
||||||
: i18n.ts.markAsSensitive,
|
: i18n.ts.markAsSensitive,
|
||||||
icon: file.isSensitive
|
icon: file.isSensitive
|
||||||
? "ph-eye-slash ph-bold ph-lg"
|
? "ph-eye ph-bold ph-lg"
|
||||||
: "ph-eye ph-bold ph-lg",
|
: "ph-eye-slash ph-bold ph-lg",
|
||||||
action: () => {
|
action: () => {
|
||||||
this.toggleSensitive(file);
|
this.toggleSensitive(file);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: i18n.ts.describeFile,
|
text: i18n.ts.describeFile,
|
||||||
icon: "ph-cursor-text ph-bold ph-lg",
|
icon: "ph-subtitles ph-bold ph-lg",
|
||||||
action: () => {
|
action: () => {
|
||||||
this.describe(file);
|
this.describe(file);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: i18n.ts.attachCancel,
|
text: i18n.ts.attachCancel,
|
||||||
icon: "ph-circle-wavy-warning ph-bold ph-lg",
|
icon: "ph-x ph-bold ph-lg",
|
||||||
action: () => {
|
action: () => {
|
||||||
this.detachMedia(file.id);
|
this.detachMedia(file.id);
|
||||||
},
|
},
|
||||||
|
@ -198,7 +198,6 @@ export default defineComponent({
|
||||||
height: 64px;
|
height: 64px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
|
||||||
cursor: move;
|
cursor: move;
|
||||||
|
|
||||||
&:hover > .remove {
|
&:hover > .remove {
|
||||||
|
|
68
packages/client/src/components/MkShowMoreButton.vue
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
v-if="modelValue"
|
||||||
|
class="fade _button"
|
||||||
|
@click.stop="toggle"
|
||||||
|
>
|
||||||
|
<span>{{ i18n.ts.showMore }}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="!modelValue"
|
||||||
|
class="showLess _button"
|
||||||
|
@click.stop="toggle"
|
||||||
|
>
|
||||||
|
<span>{{ i18n.ts.showLess }}</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: "update:modelValue", v: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
emit("update:modelValue", !props.modelValue);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.fade {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--panel);
|
||||||
|
padding: 0.4em 1em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
> span {
|
||||||
|
background: var(--panelHighlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.showLess {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1em;
|
||||||
|
position: sticky;
|
||||||
|
bottom: var(--stickyBottom);
|
||||||
|
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--panel);
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: 0 0 7px 7px var(--bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -35,7 +35,11 @@
|
||||||
class="content"
|
class="content"
|
||||||
:class="{ collapsed, isLong, showContent: note.cw && !showContent }"
|
:class="{ collapsed, isLong, showContent: note.cw && !showContent }"
|
||||||
>
|
>
|
||||||
<div class="body">
|
<XCwButton ref="cwButton" v-if="note.cw && !showContent" v-model="showContent" :note="note" v-on:keydown="focusFooter" />
|
||||||
|
<div
|
||||||
|
class="body"
|
||||||
|
v-bind="{ 'aria-label': !showContent ? '' : null, 'tabindex': !showContent ? '-1' : null }"
|
||||||
|
>
|
||||||
<span v-if="note.deletedAt" style="opacity: 0.5"
|
<span v-if="note.deletedAt" style="opacity: 0.5"
|
||||||
>({{ i18n.ts.deleted }})</span
|
>({{ i18n.ts.deleted }})</span
|
||||||
>
|
>
|
||||||
|
@ -96,34 +100,27 @@
|
||||||
<XNoteSimple :note="note.renote" />
|
<XNoteSimple :note="note.renote" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<div
|
||||||
|
v-if="note.cw && !showContent"
|
||||||
|
tabindex="0"
|
||||||
|
v-on:focus="cwButton?.focus()"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<XShowMoreButton v-if="isLong" v-model="collapsed"></XShowMoreButton>
|
||||||
v-if="isLong && collapsed"
|
<XCwButton v-if="note.cw && showContent" v-model="showContent" :note="note" />
|
||||||
class="fade _button"
|
|
||||||
@click.stop="collapsed = false"
|
|
||||||
>
|
|
||||||
<span>{{ i18n.ts.showMore }}</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="isLong && !collapsed"
|
|
||||||
class="showLess _button"
|
|
||||||
@click.stop="collapsed = true"
|
|
||||||
>
|
|
||||||
<span>{{ i18n.ts.showLess }}</span>
|
|
||||||
</button>
|
|
||||||
<XCwButton v-if="note.cw" v-model="showContent" :note="note" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {} from "vue";
|
import { ref } from "vue";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import * as mfm from "mfm-js";
|
import * as mfm from "mfm-js";
|
||||||
import XNoteSimple from "@/components/MkNoteSimple.vue";
|
import XNoteSimple from "@/components/MkNoteSimple.vue";
|
||||||
import XMediaList from "@/components/MkMediaList.vue";
|
import XMediaList from "@/components/MkMediaList.vue";
|
||||||
import XPoll from "@/components/MkPoll.vue";
|
import XPoll from "@/components/MkPoll.vue";
|
||||||
import MkUrlPreview from "@/components/MkUrlPreview.vue";
|
import MkUrlPreview from "@/components/MkUrlPreview.vue";
|
||||||
|
import XShowMoreButton from "./MkShowMoreButton.vue";
|
||||||
import XCwButton from "@/components/MkCwButton.vue";
|
import XCwButton from "@/components/MkCwButton.vue";
|
||||||
import { extractUrlFromMfm } from "@/scripts/extract-url-from-mfm";
|
import { extractUrlFromMfm } from "@/scripts/extract-url-from-mfm";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -138,19 +135,29 @@ const props = defineProps<{
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "push", v): void;
|
(ev: "push", v): void;
|
||||||
|
(ev: "focusfooter"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const cwButton = ref<HTMLElement>();
|
||||||
const isLong =
|
const isLong =
|
||||||
!props.detailedView &&
|
!props.detailedView &&
|
||||||
props.note.cw == null &&
|
props.note.cw == null &&
|
||||||
props.note.text != null &&
|
props.note.text != null &&
|
||||||
(props.note.text.split("\n").length > 9 || props.note.text.length > 500);
|
(props.note.text.split("\n").length > 9 || props.note.text.length > 500);
|
||||||
const collapsed = $ref(props.note.cw == null && isLong);
|
const collapsed = $ref(props.note.cw == null && isLong);
|
||||||
|
|
||||||
const urls = props.note.text
|
const urls = props.note.text
|
||||||
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
|
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
let showContent = $ref(false);
|
let showContent = $ref(false);
|
||||||
|
|
||||||
|
|
||||||
|
function focusFooter(ev) {
|
||||||
|
if (ev.key == "Tab" && !ev.getModifierState("Shift")) {
|
||||||
|
emit("focusfooter");
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -242,6 +249,9 @@ let showContent = $ref(false);
|
||||||
margin-top: -50px;
|
margin-top: -50px;
|
||||||
padding-top: 50px;
|
padding-top: 50px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
&.collapsed > .body {
|
&.collapsed > .body {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -264,43 +274,6 @@ let showContent = $ref(false);
|
||||||
top: 40px;
|
top: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.fade) {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
> span {
|
|
||||||
display: inline-block;
|
|
||||||
background: var(--panel);
|
|
||||||
padding: 0.4em 1em;
|
|
||||||
font-size: 0.8em;
|
|
||||||
border-radius: 999px;
|
|
||||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
> span {
|
|
||||||
background: var(--panelHighlight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.showLess) {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 1em;
|
|
||||||
position: sticky;
|
|
||||||
bottom: var(--stickyBottom);
|
|
||||||
|
|
||||||
> span {
|
|
||||||
display: inline-block;
|
|
||||||
background: var(--panel);
|
|
||||||
padding: 6px 10px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
border-radius: 999px;
|
|
||||||
box-shadow: 0 0 7px 7px var(--bg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
v-if="item.type === 'a'"
|
v-if="item.type === 'a'"
|
||||||
:href="item.href"
|
:href="item.href"
|
||||||
:target="item.target"
|
:target="item.target"
|
||||||
:tabindex="i"
|
|
||||||
class="_button item"
|
class="_button item"
|
||||||
:class="{ danger: item.danger, active: item.active }"
|
:class="{ danger: item.danger, active: item.active }"
|
||||||
>
|
>
|
||||||
|
@ -22,7 +21,6 @@
|
||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
v-else-if="item.type === 'button'"
|
v-else-if="item.type === 'button'"
|
||||||
:tabindex="i"
|
|
||||||
class="_button item"
|
class="_button item"
|
||||||
:class="{ danger: item.danger, active: item.active }"
|
:class="{ danger: item.danger, active: item.active }"
|
||||||
:disabled="item.active"
|
:disabled="item.active"
|
||||||
|
@ -38,7 +36,6 @@
|
||||||
<MkA
|
<MkA
|
||||||
v-else
|
v-else
|
||||||
:to="item.to"
|
:to="item.to"
|
||||||
:tabindex="i"
|
|
||||||
class="_button item"
|
class="_button item"
|
||||||
:class="{ danger: item.danger, active: item.active }"
|
:class="{ danger: item.danger, active: item.active }"
|
||||||
>
|
>
|
||||||
|
@ -99,7 +96,7 @@ export default defineComponent({
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin-bottom: 0.3rem;
|
margin-bottom: 0.3rem;
|
||||||
|
|
||||||
&:hover {
|
&:hover, &:focus-visible {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background: var(--panelHighlight);
|
background: var(--panelHighlight);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
/></MkA>
|
/></MkA>
|
||||||
<p class="username"><MkAcct :user="user" /></p>
|
<p class="username"><MkAcct :user="user" /></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description" :class="{ collapsed: isLong && collapsed }">
|
||||||
<Mfm
|
<Mfm
|
||||||
v-if="user.description"
|
v-if="user.description"
|
||||||
:text="user.description"
|
:text="user.description"
|
||||||
|
@ -55,6 +55,32 @@
|
||||||
:custom-emojis="user.emojis"
|
:custom-emojis="user.emojis"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<XShowMoreButton v-if="isLong" v-model="collapsed"></XShowMoreButton>
|
||||||
|
<div v-if="user.fields.length > 0" class="fields">
|
||||||
|
<dl
|
||||||
|
v-for="(field, i) in user.fields"
|
||||||
|
:key="i"
|
||||||
|
class="field"
|
||||||
|
>
|
||||||
|
<dt class="name">
|
||||||
|
<Mfm
|
||||||
|
:text="field.name"
|
||||||
|
:plain="true"
|
||||||
|
:custom-emojis="user.emojis"
|
||||||
|
:colored="false"
|
||||||
|
/>
|
||||||
|
</dt>
|
||||||
|
<dd class="value">
|
||||||
|
<Mfm
|
||||||
|
:text="field.value"
|
||||||
|
:author="user"
|
||||||
|
:i="$i"
|
||||||
|
:custom-emojis="user.emojis"
|
||||||
|
:colored="false"
|
||||||
|
/>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
<div class="status">
|
<div class="status">
|
||||||
<div>
|
<div>
|
||||||
<p>{{ i18n.ts.notes }}</p>
|
<p>{{ i18n.ts.notes }}</p>
|
||||||
|
@ -89,6 +115,7 @@ import * as Acct from "calckey-js/built/acct";
|
||||||
import type * as misskey from "calckey-js";
|
import type * as misskey from "calckey-js";
|
||||||
import MkFollowButton from "@/components/MkFollowButton.vue";
|
import MkFollowButton from "@/components/MkFollowButton.vue";
|
||||||
import { userPage } from "@/filters/user";
|
import { userPage } from "@/filters/user";
|
||||||
|
import XShowMoreButton from "./MkShowMoreButton.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -110,9 +137,14 @@ let user = $ref<misskey.entities.UserDetailed | null>(null);
|
||||||
let top = $ref(0);
|
let top = $ref(0);
|
||||||
let left = $ref(0);
|
let left = $ref(0);
|
||||||
|
|
||||||
|
|
||||||
|
let isLong = $ref(false);
|
||||||
|
let collapsed = $ref(!isLong);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (typeof props.q === "object") {
|
if (typeof props.q === "object") {
|
||||||
user = props.q;
|
user = props.q;
|
||||||
|
isLong = (user.description.split("\n").length > 9 || user.description.length > 400);
|
||||||
} else {
|
} else {
|
||||||
const query = props.q.startsWith("@")
|
const query = props.q.startsWith("@")
|
||||||
? Acct.parse(props.q.substr(1))
|
? Acct.parse(props.q.substr(1))
|
||||||
|
@ -121,9 +153,11 @@ onMounted(() => {
|
||||||
os.api("users/show", query).then((res) => {
|
os.api("users/show", query).then((res) => {
|
||||||
if (!props.showing) return;
|
if (!props.showing) return;
|
||||||
user = res;
|
user = res;
|
||||||
|
isLong = (user.description.split("\n").length > 9 || user.description.length > 400);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const rect = props.source.getBoundingClientRect();
|
const rect = props.source.getBoundingClientRect();
|
||||||
const x =
|
const x =
|
||||||
rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.pageXOffset;
|
rect.left + props.source.offsetWidth / 2 - 300 / 2 + window.pageXOffset;
|
||||||
|
@ -219,6 +253,88 @@ onMounted(() => {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
|
&.collapsed {
|
||||||
|
position: relative;
|
||||||
|
max-height: calc(9em + 50px);
|
||||||
|
mask: linear-gradient(black calc(100% - 64px), transparent);
|
||||||
|
-webkit-mask: linear-gradient(
|
||||||
|
black calc(100% - 64px),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.fade) {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: -2.5em;
|
||||||
|
z-index: 2;
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--panel);
|
||||||
|
padding: 0.4em 1em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
> span {
|
||||||
|
background: var(--panelHighlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.showLess) {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1em;
|
||||||
|
position: sticky;
|
||||||
|
bottom: var(--stickyBottom);
|
||||||
|
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--panel);
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: 0 0 7px 7px var(--bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .fields {
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: .8em;
|
||||||
|
margin-top: 1em;
|
||||||
|
|
||||||
|
> .field {
|
||||||
|
display: flex;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(span) {
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .name {
|
||||||
|
width: 30%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .value {
|
||||||
|
width: 70%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .status {
|
> .status {
|
||||||
|
@ -237,6 +353,9 @@ onMounted(() => {
|
||||||
> span {
|
> span {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
|
:global(span) {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
:user="user"
|
:user="user"
|
||||||
class="avatar"
|
class="avatar"
|
||||||
:show-indicator="true"
|
:show-indicator="true"
|
||||||
|
disableLink
|
||||||
/>
|
/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<MkUserName :user="user" class="name" />
|
<MkUserName :user="user" class="name" />
|
||||||
|
@ -73,6 +74,7 @@
|
||||||
:user="user"
|
:user="user"
|
||||||
class="avatar"
|
class="avatar"
|
||||||
:show-indicator="true"
|
:show-indicator="true"
|
||||||
|
disableLink
|
||||||
/>
|
/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<MkUserName :user="user" class="name" />
|
<MkUserName :user="user" class="name" />
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
>
|
>
|
||||||
<div class="beaffaef">
|
<div class="beaffaef">
|
||||||
<div v-for="u in users" :key="u.id" class="user">
|
<div v-for="u in users" :key="u.id" class="user">
|
||||||
<MkAvatar class="avatar" :user="u" />
|
<MkAvatar class="avatar" :user="u" disableLink />
|
||||||
<MkUserName class="name" :user="u" :nowrap="true" />
|
<MkUserName class="name" :user="u" :nowrap="true" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="users.length < count" class="omitted">
|
<div v-if="users.length < count" class="omitted">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="vjoppmmu">
|
<div class="vjoppmmu">
|
||||||
<template v-if="edit">
|
<template v-if="edit">
|
||||||
<header>
|
<header tabindex="-1" v-focus>
|
||||||
<MkSelect
|
<MkSelect
|
||||||
v-model="widgetAdderSelected"
|
v-model="widgetAdderSelected"
|
||||||
style="margin-bottom: var(--margin)"
|
style="margin-bottom: var(--margin)"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dwzlatin" :class="{ opened }">
|
<div class="dwzlatin" :class="{ opened }">
|
||||||
<div class="header _button" @click="toggle">
|
<button class="header _button" @click="toggle">
|
||||||
<span class="icon"><slot name="icon"></slot></span>
|
<span class="icon"><slot name="icon"></slot></span>
|
||||||
<span class="text"><slot name="label"></slot></span>
|
<span class="text"><slot name="label"></slot></span>
|
||||||
<span class="right">
|
<span class="right">
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
<i v-if="opened" class="ph-caret-up ph-bold ph-lg icon"></i>
|
<i v-if="opened" class="ph-caret-up ph-bold ph-lg icon"></i>
|
||||||
<i v-else class="ph-caret-down ph-bold ph-lg icon"></i>
|
<i v-else class="ph-caret-down ph-bold ph-lg icon"></i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</button>
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<div v-if="openedAtLeastOnce" v-show="opened" class="body">
|
<div v-if="openedAtLeastOnce" v-show="opened" class="body">
|
||||||
<MkSpacer :margin-min="14" :margin-max="22">
|
<MkSpacer :margin-min="14" :margin-max="22">
|
||||||
|
|
|
@ -66,6 +66,9 @@ function toggle(): void {
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: var(--inputBorderHover) !important;
|
border-color: var(--inputBorderHover) !important;
|
||||||
}
|
}
|
||||||
|
&:focus-within {
|
||||||
|
outline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
&.checked {
|
&.checked {
|
||||||
background-color: var(--accentedBg) !important;
|
background-color: var(--accentedBg) !important;
|
||||||
|
|
|
@ -99,6 +99,9 @@ const toggle = () => {
|
||||||
border-color: var(--inputBorderHover) !important;
|
border-color: var(--inputBorderHover) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&:focus-within > .button {
|
||||||
|
outline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
> .label {
|
> .label {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
class="avatar"
|
class="avatar"
|
||||||
:user="$i"
|
:user="$i"
|
||||||
:disable-preview="true"
|
:disable-preview="true"
|
||||||
|
disableLink
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="metadata">
|
<template v-if="metadata">
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
:user="metadata.avatar"
|
:user="metadata.avatar"
|
||||||
:disable-preview="true"
|
:disable-preview="true"
|
||||||
:show-indicator="true"
|
:show-indicator="true"
|
||||||
|
disableLink
|
||||||
/>
|
/>
|
||||||
<i
|
<i
|
||||||
v-else-if="metadata.icon && !narrow"
|
v-else-if="metadata.icon && !narrow"
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
:is="currentPageComponent"
|
:is="currentPageComponent"
|
||||||
:key="key"
|
:key="key"
|
||||||
v-bind="Object.fromEntries(currentPageProps)"
|
v-bind="Object.fromEntries(currentPageProps)"
|
||||||
|
tabindex="-1"
|
||||||
|
v-focus
|
||||||
|
style="outline: none;"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template #fallback>
|
<template #fallback>
|
||||||
|
|
3
packages/client/src/directives/focus.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default {
|
||||||
|
mounted: (el) => el.focus()
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import anim from "./anim";
|
||||||
import clickAnime from "./click-anime";
|
import clickAnime from "./click-anime";
|
||||||
import panel from "./panel";
|
import panel from "./panel";
|
||||||
import adaptiveBorder from "./adaptive-border";
|
import adaptiveBorder from "./adaptive-border";
|
||||||
|
import focus from "./focus";
|
||||||
|
|
||||||
export default function (app: App) {
|
export default function (app: App) {
|
||||||
app.directive("userPreview", userPreview);
|
app.directive("userPreview", userPreview);
|
||||||
|
@ -25,4 +26,5 @@ export default function (app: App) {
|
||||||
app.directive("click-anime", clickAnime);
|
app.directive("click-anime", clickAnime);
|
||||||
app.directive("panel", panel);
|
app.directive("panel", panel);
|
||||||
app.directive("adaptive-border", adaptiveBorder);
|
app.directive("adaptive-border", adaptiveBorder);
|
||||||
|
app.directive("focus", focus);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,23 +76,32 @@ export default {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function showTooltip() {
|
||||||
|
window.clearTimeout(self.showTimer);
|
||||||
|
window.clearTimeout(self.hideTimer);
|
||||||
|
self.showTimer = window.setTimeout(self.show, delay);
|
||||||
|
}
|
||||||
|
function hideTooltip() {
|
||||||
|
window.clearTimeout(self.showTimer);
|
||||||
|
window.clearTimeout(self.hideTimer);
|
||||||
|
self.hideTimer = window.setTimeout(self.close, delay);
|
||||||
|
}
|
||||||
|
|
||||||
el.addEventListener(
|
el.addEventListener(
|
||||||
start,
|
start, showTooltip,
|
||||||
() => {
|
{ passive: true },
|
||||||
window.clearTimeout(self.showTimer);
|
);
|
||||||
window.clearTimeout(self.hideTimer);
|
el.addEventListener(
|
||||||
self.showTimer = window.setTimeout(self.show, delay);
|
"focusin", showTooltip,
|
||||||
},
|
|
||||||
{ passive: true },
|
{ passive: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
el.addEventListener(
|
el.addEventListener(
|
||||||
end,
|
end, hideTooltip,
|
||||||
() => {
|
{ passive: true },
|
||||||
window.clearTimeout(self.showTimer);
|
);
|
||||||
window.clearTimeout(self.hideTimer);
|
el.addEventListener(
|
||||||
self.hideTimer = window.setTimeout(self.close, delay);
|
"focusout", hideTooltip,
|
||||||
},
|
|
||||||
{ passive: true },
|
{ passive: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -313,11 +313,7 @@ onUnmounted(() => {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
|
||||||
&:hover {
|
&:hover, &:focus-visible, &.active {
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="_panel" :class="$style.root">
|
<div class="_panel" :class="$style.root">
|
||||||
<MkSelect v-model="src" style="margin: 0 0 12px 0" small>
|
<MkSelect v-model="src" style="margin: 0 0 12px 0" small>
|
||||||
<option value="notes">Notes</option>
|
<option value="notes">Posts</option>
|
||||||
<option value="active-users">Active users</option>
|
<option value="active-users">Active users</option>
|
||||||
<option value="ap-requests-inbox-received">
|
<option value="ap-requests-inbox-received">
|
||||||
Fediverse Requests: inboxReceived
|
Fediverse Requests: inboxReceived
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
class="user"
|
class="user"
|
||||||
:to="`/user-info/${user.id}`"
|
:to="`/user-info/${user.id}`"
|
||||||
>
|
>
|
||||||
<MkAvatar :user="user" class="avatar" indicator />
|
<MkAvatar :user="user" class="avatar" indicator disableLink />
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
class="avatar"
|
class="avatar"
|
||||||
:user="req.follower"
|
:user="req.follower"
|
||||||
:show-indicator="true"
|
:show-indicator="true"
|
||||||
|
disableLink
|
||||||
/>
|
/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkAd :prefer="['horizontal', 'horizontal-big']" />
|
<MkAd :prefer="['inline', 'inline-big']" />
|
||||||
<MkContainer
|
<MkContainer
|
||||||
:max-height="300"
|
:max-height="300"
|
||||||
:foldable="true"
|
:foldable="true"
|
||||||
|
|
|
@ -341,6 +341,54 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="section _block">
|
||||||
|
<div class="title">{{ i18n.ts._mfm.position }}</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>{{ i18n.ts._mfm.positionDescription }}</p>
|
||||||
|
<div class="preview">
|
||||||
|
<Mfm :text="preview_position" />
|
||||||
|
<MkTextarea v-model="preview_position"
|
||||||
|
><span>MFM</span></MkTextarea
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="section _block">
|
||||||
|
<div class="title">{{ i18n.ts._mfm.scale }}</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>{{ i18n.ts._mfm.scaleDescription }}</p>
|
||||||
|
<div class="preview">
|
||||||
|
<Mfm :text="preview_scale" />
|
||||||
|
<MkTextarea v-model="preview_scale"
|
||||||
|
><span>MFM</span></MkTextarea
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="section _block">
|
||||||
|
<div class="title">{{ i18n.ts._mfm.foreground }}</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>{{ i18n.ts._mfm.foregroundDescription }}</p>
|
||||||
|
<div class="preview">
|
||||||
|
<Mfm :text="preview_fg" />
|
||||||
|
<MkTextarea v-model="preview_fg"
|
||||||
|
><span>MFM</span></MkTextarea
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="section _block">
|
||||||
|
<div class="title">{{ i18n.ts._mfm.background }}</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>{{ i18n.ts._mfm.backgroundDescription }}</p>
|
||||||
|
<div class="preview">
|
||||||
|
<Mfm :text="preview_bg" />
|
||||||
|
<MkTextarea v-model="preview_bg"
|
||||||
|
><span>MFM</span></MkTextarea
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="section _block">
|
<div class="section _block">
|
||||||
<div class="title">{{ i18n.ts._mfm.plain }}</div>
|
<div class="title">{{ i18n.ts._mfm.plain }}</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -402,7 +450,11 @@ let preview_x4 = $ref("$[x4 🍮]");
|
||||||
let preview_blur = $ref(`$[blur ${i18n.ts._mfm.dummy}]`);
|
let preview_blur = $ref(`$[blur ${i18n.ts._mfm.dummy}]`);
|
||||||
let preview_rainbow = $ref("$[rainbow 🍮] $[rainbow.speed=5s 🍮]");
|
let preview_rainbow = $ref("$[rainbow 🍮] $[rainbow.speed=5s 🍮]");
|
||||||
let preview_sparkle = $ref("$[sparkle 🍮]");
|
let preview_sparkle = $ref("$[sparkle 🍮]");
|
||||||
let preview_rotate = $ref("$[rotate 🍮]");
|
let preview_rotate = $ref("$[rotate 🍮]\n$[rotate.deg=45 🍮]\n$[rotate.x,deg=45 Hello, world!]");
|
||||||
|
let preview_position = $ref("$[position.y=-1 Positioning]\n$[position.x=-1 Positioning]");
|
||||||
|
let preview_scale = $ref("$[scale.x=1.3 Scaling]\n$[scale.x=1.3,y=2 Scaling]\n$[scale.y=0.3 Tiny scaling]");
|
||||||
|
let preview_fg = $ref("$[fg.color=ff0000 Text color]");
|
||||||
|
let preview_bg = $ref("$[bg.color=ff0000 Background color]");
|
||||||
let preview_plain = $ref(
|
let preview_plain = $ref(
|
||||||
"<plain>**bold** @mention #hashtag `code` $[x2 🍮]</plain>"
|
"<plain>**bold** @mention #hashtag `code` $[x2 🍮]</plain>"
|
||||||
);
|
);
|
||||||
|
|
|
@ -168,7 +168,7 @@
|
||||||
</template>
|
</template>
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
<MkAd :prefer="['horizontal', 'horizontal-big']" />
|
<MkAd :prefer="['inline', 'inline-big']" />
|
||||||
<MkContainer
|
<MkContainer
|
||||||
:max-height="300"
|
:max-height="300"
|
||||||
:foldable="true"
|
:foldable="true"
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
{{ i18n.ts.addAccount }}</FormButton
|
{{ i18n.ts.addAccount }}</FormButton
|
||||||
>
|
>
|
||||||
|
|
||||||
<div
|
<button
|
||||||
v-for="account in accounts"
|
v-for="account in accounts"
|
||||||
:key="account.id"
|
:key="account.id"
|
||||||
class="_panel _button lcjjdxlm"
|
class="_panel _button lcjjdxlm"
|
||||||
@click="menu(account, $event)"
|
@click="menu(account, $event)"
|
||||||
>
|
>
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<MkAvatar :user="account" class="avatar" />
|
<MkAvatar :user="account" class="avatar" disableLink />
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
<MkAcct :user="account" />
|
<MkAcct :user="account" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -158,6 +158,8 @@ definePageMetadata({
|
||||||
.lcjjdxlm {
|
.lcjjdxlm {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: unset;
|
||||||
|
|
||||||
> .avatar {
|
> .avatar {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -21,9 +21,19 @@
|
||||||
<template #icon
|
<template #icon
|
||||||
><i class="ph-upload-simple ph-bold ph-lg"></i
|
><i class="ph-upload-simple ph-bold ph-lg"></i
|
||||||
></template>
|
></template>
|
||||||
<FormSwitch v-model="signatureCheck" class="_formBlock">
|
<!-- <FormSwitch v-model="signatureCheck" class="_formBlock">
|
||||||
Mastodon import? (not Akkoma!)
|
Mastodon import? (not Akkoma!)
|
||||||
</FormSwitch>
|
</FormSwitch> -->
|
||||||
|
<FormRadios v-model="importType" class="_formBlock">
|
||||||
|
<option value="calckey">Calckey/Misskey</option>
|
||||||
|
<option value="mastodon">Mastodon</option>
|
||||||
|
<!-- <option :disabled="true" value="akkoma">
|
||||||
|
Pleroma/Akkoma (soon)
|
||||||
|
</option>
|
||||||
|
<option :disabled="true" value="twitter">
|
||||||
|
Twitter (soon)
|
||||||
|
</option> -->
|
||||||
|
</FormRadios>
|
||||||
<MkButton
|
<MkButton
|
||||||
primary
|
primary
|
||||||
:class="$style.button"
|
:class="$style.button"
|
||||||
|
@ -177,13 +187,14 @@ import MkButton from "@/components/MkButton.vue";
|
||||||
import FormSection from "@/components/form/section.vue";
|
import FormSection from "@/components/form/section.vue";
|
||||||
import FormFolder from "@/components/form/folder.vue";
|
import FormFolder from "@/components/form/folder.vue";
|
||||||
import FormSwitch from "@/components/form/switch.vue";
|
import FormSwitch from "@/components/form/switch.vue";
|
||||||
|
import FormRadios from "@/components/form/radios.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { selectFile } from "@/scripts/select-file";
|
import { selectFile } from "@/scripts/select-file";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
const excludeMutingUsers = ref(false);
|
const excludeMutingUsers = ref(false);
|
||||||
const signatureCheck = ref(false);
|
const importType = ref("calckey");
|
||||||
const excludeInactiveUsers = ref(false);
|
const excludeInactiveUsers = ref(false);
|
||||||
|
|
||||||
const onExportSuccess = () => {
|
const onExportSuccess = () => {
|
||||||
|
@ -215,7 +226,7 @@ const importPosts = async (ev) => {
|
||||||
const file = await selectFile(ev.currentTarget ?? ev.target);
|
const file = await selectFile(ev.currentTarget ?? ev.target);
|
||||||
os.api("i/import-posts", {
|
os.api("i/import-posts", {
|
||||||
fileId: file.id,
|
fileId: file.id,
|
||||||
signatureCheck: signatureCheck.value,
|
signatureCheck: importType.value === "mastodon" ? true : false,
|
||||||
})
|
})
|
||||||
.then(onImportSuccess)
|
.then(onImportSuccess)
|
||||||
.catch(onError);
|
.catch(onError);
|
||||||
|
|
|
@ -35,5 +35,3 @@ definePageMetadata({
|
||||||
icon: "ph-user ph-bold ph-lg",
|
icon: "ph-user ph-bold ph-lg",
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
|
|
39
packages/client/src/pages/verify-email.vue
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
{{ i18n.ts.processing }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
import * as os from "@/os";
|
||||||
|
import { i18n } from "@/i18n";
|
||||||
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
import { useRouter } from "@/router";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
code: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await os.alert({
|
||||||
|
type: "info",
|
||||||
|
text: i18n.t("clickToFinishEmailVerification", { ok: i18n.ts.gotIt }),
|
||||||
|
});
|
||||||
|
await os.api("verify-email", {
|
||||||
|
code: props.code,
|
||||||
|
});
|
||||||
|
router.push("/");
|
||||||
|
});
|
||||||
|
|
||||||
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
|
const headerTabs = $computed(() => []);
|
||||||
|
|
||||||
|
definePageMetadata({
|
||||||
|
title: "Verify email",
|
||||||
|
icon: "ph-user ph-bold ph-lg",
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -308,6 +308,7 @@ function showMenu(ev) {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .fg {
|
> .fg {
|
||||||
|
|
|
@ -283,6 +283,10 @@ export const routes = [
|
||||||
path: "/signup-complete/:code",
|
path: "/signup-complete/:code",
|
||||||
component: page(() => import("./pages/signup-complete.vue")),
|
component: page(() => import("./pages/signup-complete.vue")),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/verify-email/:code",
|
||||||
|
component: page(() => import("./pages/verify-email.vue")),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/announcements",
|
path: "/announcements",
|
||||||
component: page(() => import("./pages/announcements.vue")),
|
component: page(() => import("./pages/announcements.vue")),
|
||||||
|
|
|
@ -204,10 +204,6 @@ hr {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<MkAvatar
|
<MkAvatar
|
||||||
:user="$i"
|
:user="$i"
|
||||||
class="icon"
|
class="icon"
|
||||||
|
disableLink
|
||||||
/><!-- <MkAcct class="text" :user="$i"/> -->
|
/><!-- <MkAcct class="text" :user="$i"/> -->
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<MkAvatar
|
<MkAvatar
|
||||||
:user="$i"
|
:user="$i"
|
||||||
class="icon"
|
class="icon"
|
||||||
|
disableLink
|
||||||
/><!-- <MkAcct class="text" :user="$i"/> -->
|
/><!-- <MkAcct class="text" :user="$i"/> -->
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -334,6 +335,7 @@ function more(ev: MouseEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
|
&:focus-within,
|
||||||
&.active {
|
&.active {
|
||||||
&:before {
|
&:before {
|
||||||
background: var(--accentLighten);
|
background: var(--accentLighten);
|
||||||
|
@ -398,8 +400,6 @@ function more(ev: MouseEvent) {
|
||||||
padding-left: 30px;
|
padding-left: 30px;
|
||||||
line-height: 2.85rem;
|
line-height: 2.85rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@ -425,9 +425,12 @@ function more(ev: MouseEvent) {
|
||||||
> .text {
|
> .text {
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover,
|
||||||
|
&:focus-within {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--navHoverFg);
|
color: var(--navHoverFg);
|
||||||
transition: all 0.4s ease;
|
transition: all 0.4s ease;
|
||||||
|
@ -438,6 +441,7 @@ function more(ev: MouseEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
|
&:focus-within,
|
||||||
&.active {
|
&.active {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
transition: all 0.4s ease;
|
transition: all 0.4s ease;
|
||||||
|
@ -528,6 +532,7 @@ function more(ev: MouseEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
|
&:focus-within,
|
||||||
&.active {
|
&.active {
|
||||||
&:before {
|
&:before {
|
||||||
background: var(--accentLighten);
|
background: var(--accentLighten);
|
||||||
|
@ -613,6 +618,7 @@ function more(ev: MouseEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
|
&:focus-within,
|
||||||
&.active {
|
&.active {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
|
@ -642,5 +648,12 @@ function more(ev: MouseEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
outline: none;
|
||||||
|
&:focus-visible:before {
|
||||||
|
outline: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -83,6 +83,7 @@
|
||||||
<MkAvatar :user="$i" class="avatar" /><MkAcct
|
<MkAvatar :user="$i" class="avatar" /><MkAcct
|
||||||
class="acct"
|
class="acct"
|
||||||
:user="$i"
|
:user="$i"
|
||||||
|
disableLink
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<div class="post" @click="post">
|
<div class="post" @click="post">
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
class="item _button account"
|
class="item _button account"
|
||||||
@click="openAccountMenu"
|
@click="openAccountMenu"
|
||||||
>
|
>
|
||||||
<MkAvatar :user="$i" class="avatar" /><MkAcct
|
<MkAvatar :user="$i" class="avatar" disableLink /><MkAcct
|
||||||
class="text"
|
class="text"
|
||||||
:user="$i"
|
:user="$i"
|
||||||
/>
|
/>
|
||||||
|
@ -299,6 +299,7 @@ function openInstanceMenu(ev: MouseEvent) {
|
||||||
width: 46px;
|
width: 46px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin-inline: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,6 +373,7 @@ function openInstanceMenu(ev: MouseEvent) {
|
||||||
|
|
||||||
> i {
|
> i {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
> i,
|
> i,
|
||||||
|
|
|
@ -227,6 +227,8 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.gbhvwtnk {
|
.gbhvwtnk {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
$ui-font-size: 1em;
|
$ui-font-size: 1em;
|
||||||
$widgets-hide-threshold: 1200px;
|
$widgets-hide-threshold: 1200px;
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0">
|
<linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0">
|
||||||
<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
|
<stop offset="0%" stop-color="hsl(189, 43%, 73%)"></stop>
|
||||||
<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
|
<stop offset="100%" stop-color="hsl(343, 76%, 68%)"></stop>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<mask
|
<mask
|
||||||
:id="cpuMaskId"
|
:id="cpuMaskId"
|
||||||
|
@ -42,8 +42,8 @@
|
||||||
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0">
|
<linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0">
|
||||||
<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
|
<stop offset="0%" stop-color="hsl(189, 43%, 73%)"></stop>
|
||||||
<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
|
<stop offset="100%" stop-color="hsl(343, 76%, 68%)"></stop>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<mask
|
<mask
|
||||||
:id="memMaskId"
|
:id="memMaskId"
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
||||||
<polygon
|
<polygon
|
||||||
:points="inPolygonPoints"
|
:points="inPolygonPoints"
|
||||||
fill="#94a029"
|
fill="#f6c177"
|
||||||
fill-opacity="0.5"
|
fill-opacity="0.5"
|
||||||
/>
|
/>
|
||||||
<polyline
|
<polyline
|
||||||
:points="inPolylinePoints"
|
:points="inPolylinePoints"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="#94a029"
|
stroke="#f6c177"
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
/>
|
/>
|
||||||
<circle :cx="inHeadX" :cy="inHeadY" r="1.5" fill="#94a029" />
|
<circle :cx="inHeadX" :cy="inHeadY" r="1.5" fill="#f6c177" />
|
||||||
<text x="1" y="5">
|
<text x="1" y="5">
|
||||||
NET rx
|
NET rx
|
||||||
<tspan>{{ bytes(inRecent) }}</tspan>
|
<tspan>{{ bytes(inRecent) }}</tspan>
|
||||||
|
@ -21,16 +21,16 @@
|
||||||
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
<svg :viewBox="`0 0 ${viewBoxX} ${viewBoxY}`">
|
||||||
<polygon
|
<polygon
|
||||||
:points="outPolygonPoints"
|
:points="outPolygonPoints"
|
||||||
fill="#ff9156"
|
fill="#31748f"
|
||||||
fill-opacity="0.5"
|
fill-opacity="0.5"
|
||||||
/>
|
/>
|
||||||
<polyline
|
<polyline
|
||||||
:points="outPolylinePoints"
|
:points="outPolylinePoints"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="#ff9156"
|
stroke="#31748f"
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
/>
|
/>
|
||||||
<circle :cx="outHeadX" :cy="outHeadY" r="1.5" fill="#ff9156" />
|
<circle :cx="outHeadX" :cy="outHeadY" r="1.5" fill="#31748f" />
|
||||||
<text x="1" y="5">
|
<text x="1" y="5">
|
||||||
NET tx
|
NET tx
|
||||||
<tspan>{{ bytes(outRecent) }}</tspan>
|
<tspan>{{ bytes(outRecent) }}</tspan>
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
"@cody@mk.codingneko.com",
|
"@cody@mk.codingneko.com",
|
||||||
"@kate@blahaj.zone",
|
"@kate@blahaj.zone",
|
||||||
"@emtk@mkkey.net",
|
"@emtk@mkkey.net",
|
||||||
|
"@jovikowi@calckey.social",
|
||||||
|
"@padraig@calckey.social",
|
||||||
|
"@pancakes@cats.city",
|
||||||
"Interkosmos Link"
|
"Interkosmos Link"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,12 @@ importers:
|
||||||
'@tensorflow/tfjs':
|
'@tensorflow/tfjs':
|
||||||
specifier: ^3.21.0
|
specifier: ^3.21.0
|
||||||
version: 3.21.0(seedrandom@3.0.5)
|
version: 3.21.0(seedrandom@3.0.5)
|
||||||
|
focus-trap:
|
||||||
|
specifier: ^7.2.0
|
||||||
|
version: 7.2.0
|
||||||
|
focus-trap-vue:
|
||||||
|
specifier: ^4.0.1
|
||||||
|
version: 4.0.1(focus-trap@7.2.0)(vue@3.2.45)
|
||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: 4.1.0
|
specifier: 4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
|
@ -81,8 +87,8 @@ importers:
|
||||||
specifier: ^4.6.4
|
specifier: ^4.6.4
|
||||||
version: 4.10.2
|
version: 4.10.2
|
||||||
'@calckey/megalodon':
|
'@calckey/megalodon':
|
||||||
specifier: 5.1.24
|
specifier: 5.2.0
|
||||||
version: 5.1.24
|
version: 5.2.0
|
||||||
'@discordapp/twemoji':
|
'@discordapp/twemoji':
|
||||||
specifier: 14.0.2
|
specifier: 14.0.2
|
||||||
version: 14.0.2
|
version: 14.0.2
|
||||||
|
@ -227,6 +233,9 @@ importers:
|
||||||
koa-bodyparser:
|
koa-bodyparser:
|
||||||
specifier: 4.3.0
|
specifier: 4.3.0
|
||||||
version: 4.3.0
|
version: 4.3.0
|
||||||
|
koa-favicon:
|
||||||
|
specifier: 2.1.0
|
||||||
|
version: 2.1.0
|
||||||
koa-json-body:
|
koa-json-body:
|
||||||
specifier: 5.3.0
|
specifier: 5.3.0
|
||||||
version: 5.3.0
|
version: 5.3.0
|
||||||
|
@ -236,6 +245,9 @@ importers:
|
||||||
koa-mount:
|
koa-mount:
|
||||||
specifier: 4.0.0
|
specifier: 4.0.0
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
|
koa-remove-trailing-slashes:
|
||||||
|
specifier: 2.0.3
|
||||||
|
version: 2.0.3
|
||||||
koa-send:
|
koa-send:
|
||||||
specifier: 5.0.1
|
specifier: 5.0.1
|
||||||
version: 5.0.1
|
version: 5.0.1
|
||||||
|
@ -1372,8 +1384,8 @@ packages:
|
||||||
'@bull-board/api': 4.10.2
|
'@bull-board/api': 4.10.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@calckey/megalodon@5.1.24:
|
/@calckey/megalodon@5.2.0:
|
||||||
resolution: {integrity: sha512-VRd6x8MFQ2pMF0rnGF67/GVxgp/92CV7lg2XT1wnPAfQZ1NTsjwlDQX3HewEW3fSG/r7Nzh5WbIBXC8WMWKs9g==}
|
resolution: {integrity: sha512-9MEjzKJPyd7o5bHGGlNq4oE1tMt22GUJ8o8tZXcXSpXlrSDb2rSwumirM1KXUWTW8G6NGi1leCM59gOBGLko3w==}
|
||||||
engines: {node: '>=15.0.0'}
|
engines: {node: '>=15.0.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/oauth': 0.9.1
|
'@types/oauth': 0.9.1
|
||||||
|
@ -3797,14 +3809,12 @@ packages:
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
source-map: 0.6.1
|
source-map: 0.6.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/compiler-dom@3.2.45:
|
/@vue/compiler-dom@3.2.45:
|
||||||
resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==}
|
resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-core': 3.2.45
|
'@vue/compiler-core': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/compiler-sfc@2.7.14:
|
/@vue/compiler-sfc@2.7.14:
|
||||||
resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
|
resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
|
||||||
|
@ -3827,14 +3837,12 @@ packages:
|
||||||
magic-string: 0.25.9
|
magic-string: 0.25.9
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
source-map: 0.6.1
|
source-map: 0.6.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/compiler-ssr@3.2.45:
|
/@vue/compiler-ssr@3.2.45:
|
||||||
resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==}
|
resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-dom': 3.2.45
|
'@vue/compiler-dom': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/reactivity-transform@3.2.45:
|
/@vue/reactivity-transform@3.2.45:
|
||||||
resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==}
|
resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==}
|
||||||
|
@ -3844,20 +3852,17 @@ packages:
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
magic-string: 0.25.9
|
magic-string: 0.25.9
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/reactivity@3.2.45:
|
/@vue/reactivity@3.2.45:
|
||||||
resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==}
|
resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/runtime-core@3.2.45:
|
/@vue/runtime-core@3.2.45:
|
||||||
resolution: {integrity: sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==}
|
resolution: {integrity: sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/reactivity': 3.2.45
|
'@vue/reactivity': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/runtime-dom@3.2.45:
|
/@vue/runtime-dom@3.2.45:
|
||||||
resolution: {integrity: sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==}
|
resolution: {integrity: sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==}
|
||||||
|
@ -3865,7 +3870,6 @@ packages:
|
||||||
'@vue/runtime-core': 3.2.45
|
'@vue/runtime-core': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
csstype: 2.6.21
|
csstype: 2.6.21
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/server-renderer@3.2.45(vue@3.2.45):
|
/@vue/server-renderer@3.2.45(vue@3.2.45):
|
||||||
resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==}
|
resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==}
|
||||||
|
@ -3875,11 +3879,9 @@ packages:
|
||||||
'@vue/compiler-ssr': 3.2.45
|
'@vue/compiler-ssr': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
vue: 3.2.45
|
vue: 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vue/shared@3.2.45:
|
/@vue/shared@3.2.45:
|
||||||
resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
|
resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@webassemblyjs/ast@1.11.1:
|
/@webassemblyjs/ast@1.11.1:
|
||||||
resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==}
|
resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==}
|
||||||
|
@ -5908,8 +5910,8 @@ packages:
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/core-js@3.30.0:
|
/core-js@3.30.1:
|
||||||
resolution: {integrity: sha512-hQotSSARoNh1mYPi9O2YaWeiq/cEB95kOrFb4NCrO4RIFt1qqNpKsaE+vy/L3oiqvND5cThqXzUU3r9F7Efztg==}
|
resolution: {integrity: sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -6068,7 +6070,6 @@ packages:
|
||||||
|
|
||||||
/csstype@2.6.21:
|
/csstype@2.6.21:
|
||||||
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
|
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/csstype@3.1.1:
|
/csstype@3.1.1:
|
||||||
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
|
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
|
||||||
|
@ -6973,7 +6974,6 @@ packages:
|
||||||
|
|
||||||
/estree-walker@2.0.2:
|
/estree-walker@2.0.2:
|
||||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/esutils@2.0.3:
|
/esutils@2.0.3:
|
||||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||||
|
@ -7439,6 +7439,22 @@ packages:
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/focus-trap-vue@4.0.1(focus-trap@7.2.0)(vue@3.2.45):
|
||||||
|
resolution: {integrity: sha512-2iqOeoSvgq7Um6aL+255a/wXPskj6waLq2oKCa4gOnMORPo15JX7wN6J5bl1SMhMlTlkHXGSrQ9uJPJLPZDl5w==}
|
||||||
|
peerDependencies:
|
||||||
|
focus-trap: ^7.0.0
|
||||||
|
vue: ^3.0.0
|
||||||
|
dependencies:
|
||||||
|
focus-trap: 7.2.0
|
||||||
|
vue: 3.2.45
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/focus-trap@7.2.0:
|
||||||
|
resolution: {integrity: sha512-v4wY6HDDYvzkBy4735kW5BUEuw6Yz9ABqMYLuTNbzAFPcBOGiGHwwcNVMvUz4G0kgSYh13wa/7TG3XwTeT4O/A==}
|
||||||
|
dependencies:
|
||||||
|
tabbable: 6.1.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/follow-redirects@1.15.2(debug@4.3.4):
|
/follow-redirects@1.15.2(debug@4.3.4):
|
||||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
|
@ -9835,6 +9851,12 @@ packages:
|
||||||
koa-compose: 4.1.0
|
koa-compose: 4.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/koa-favicon@2.1.0:
|
||||||
|
resolution: {integrity: sha512-LvukcooYjxKtnZq0RXdBup+JDhaHwLgnLlDHB/xvjwQEjbc4rbp/0WkmOzpOvaHujc+fIwPear0dpKX1V+dHVg==}
|
||||||
|
dependencies:
|
||||||
|
mz: 2.7.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/koa-json-body@5.3.0:
|
/koa-json-body@5.3.0:
|
||||||
resolution: {integrity: sha512-M2P3zLOs2XiYCpJLGSTXOKij4u5vJ8pbAMXXarXQnwsx4DwDav9qn081tYI2RdZ79B159Pdk4bRfvwl/sazL8A==}
|
resolution: {integrity: sha512-M2P3zLOs2XiYCpJLGSTXOKij4u5vJ8pbAMXXarXQnwsx4DwDav9qn081tYI2RdZ79B159Pdk4bRfvwl/sazL8A==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
|
@ -9862,6 +9884,10 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/koa-remove-trailing-slashes@2.0.3:
|
||||||
|
resolution: {integrity: sha512-NFFF9Sl1wxFo5h0I3OzrHDINdFPaqG+Hx19590F7PNOcmm7yYeFW71p4XicVuSovbcx75GWGb3fi6N6kI6E/3g==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/koa-router@10.1.1:
|
/koa-router@10.1.1:
|
||||||
resolution: {integrity: sha512-z/OzxVjf5NyuNO3t9nJpx7e1oR3FSBAauiwXtMQu4ppcnuNZzTaQ4p21P8A6r2Es8uJJM339oc4oVW+qX7SqnQ==}
|
resolution: {integrity: sha512-z/OzxVjf5NyuNO3t9nJpx7e1oR3FSBAauiwXtMQu4ppcnuNZzTaQ4p21P8A6r2Es8uJJM339oc4oVW+qX7SqnQ==}
|
||||||
engines: {node: '>= 8.0.0'}
|
engines: {node: '>= 8.0.0'}
|
||||||
|
@ -10334,7 +10360,6 @@ packages:
|
||||||
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
|
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
sourcemap-codec: 1.4.8
|
sourcemap-codec: 1.4.8
|
||||||
dev: true
|
|
||||||
|
|
||||||
/mailcheck@1.1.1:
|
/mailcheck@1.1.1:
|
||||||
resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==}
|
resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==}
|
||||||
|
@ -13251,7 +13276,6 @@ packages:
|
||||||
/sourcemap-codec@1.4.8:
|
/sourcemap-codec@1.4.8:
|
||||||
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
||||||
deprecated: Please use @jridgewell/sourcemap-codec instead
|
deprecated: Please use @jridgewell/sourcemap-codec instead
|
||||||
dev: true
|
|
||||||
|
|
||||||
/sparkles@1.0.1:
|
/sparkles@1.0.1:
|
||||||
resolution: {integrity: sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==}
|
resolution: {integrity: sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==}
|
||||||
|
@ -13670,6 +13694,10 @@ packages:
|
||||||
resolution: {integrity: sha512-g9rPT3V1Q4WjWFZ/t5BdGC1mT/FpYnsLdBl+M5e6MlRkuE1RSR+R43wcY/3mKI59B9KEr+vxdWCuWNMD3oNHKA==}
|
resolution: {integrity: sha512-g9rPT3V1Q4WjWFZ/t5BdGC1mT/FpYnsLdBl+M5e6MlRkuE1RSR+R43wcY/3mKI59B9KEr+vxdWCuWNMD3oNHKA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tabbable@6.1.1:
|
||||||
|
resolution: {integrity: sha512-4kl5w+nCB44EVRdO0g/UGoOp3vlwgycUVtkk/7DPyeLZUCuNFFKCFG6/t/DgHLrUPHjrZg6s5tNm+56Q2B0xyg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tapable@2.2.1:
|
/tapable@2.2.1:
|
||||||
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -14760,7 +14788,6 @@ packages:
|
||||||
'@vue/runtime-dom': 3.2.45
|
'@vue/runtime-dom': 3.2.45
|
||||||
'@vue/server-renderer': 3.2.45(vue@3.2.45)
|
'@vue/server-renderer': 3.2.45(vue@3.2.45)
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
dev: true
|
|
||||||
|
|
||||||
/vuedraggable@4.1.0(vue@3.2.45):
|
/vuedraggable@4.1.0(vue@3.2.45):
|
||||||
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
|
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
|
||||||
|
@ -15386,7 +15413,7 @@ packages:
|
||||||
name: plyr
|
name: plyr
|
||||||
version: 3.7.0
|
version: 3.7.0
|
||||||
dependencies:
|
dependencies:
|
||||||
core-js: 3.30.0
|
core-js: 3.30.1
|
||||||
custom-event-polyfill: 1.0.7
|
custom-event-polyfill: 1.0.7
|
||||||
loadjs: 4.2.0
|
loadjs: 4.2.0
|
||||||
rangetouch: 2.0.1
|
rangetouch: 2.0.1
|
||||||
|
|