From a54334b7143a4483eb35b00e2331ccfcfb00b833 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 21 Nov 2025 09:59:07 +0100 Subject: [PATCH 001/123] Increase HTTP read timeout for expensive S3 batch delete operation (#36971) --- app/lib/attachment_batch.rb | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/lib/attachment_batch.rb b/app/lib/attachment_batch.rb index 374abfac49038d..cf08fdcf528683 100644 --- a/app/lib/attachment_batch.rb +++ b/app/lib/attachment_batch.rb @@ -112,10 +112,17 @@ def remove_files keys.each_slice(LIMIT) do |keys_slice| logger.debug { "Deleting #{keys_slice.size} objects" } - bucket.delete_objects(delete: { - objects: keys_slice.map { |key| { key: key } }, - quiet: true, - }) + bucket.delete_objects( + { + delete: { + objects: keys_slice.map { |key| { key: key } }, + quiet: true, + }, + }, + { + http_read_timeout: [Paperclip::Attachment.default_options[:s3_options][:http_read_timeout], 120].max, + } + ) rescue => e retries += 1 From 6151febd73c201f448f1e78d16d2835e4a0da9a9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 21 Nov 2025 04:16:50 -0500 Subject: [PATCH 002/123] Suggest ES image version 7.17.29 in docker compose (#36972) --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2746fd5fc919d4..9140aeb14eceb6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: # es: # restart: always - # image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4 + # image: docker.elastic.co/elasticsearch/elasticsearch:7.17.29 # environment: # - "ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true" # - "xpack.license.self_generated.type=basic" From aa131e538c11f9ddc17cdbc7c8c2affa442920f8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 09:17:57 +0000 Subject: [PATCH 003/123] chore(deps): update dependency core-js to v3.47.0 (#36931) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a56fc1f72e7069..838a26fe96a4b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6062,9 +6062,9 @@ __metadata: linkType: hard "core-js@npm:^3.30.2, core-js@npm:^3.45.0": - version: 3.46.0 - resolution: "core-js@npm:3.46.0" - checksum: 10c0/12d559d39a58227881bc6c86c36d24dcfbe2d56e52dac42e35e8643278172596ab67f57ede98baf40b153ca1b830f37420ea32c3f7417c0c5a1fed46438ae187 + version: 3.47.0 + resolution: "core-js@npm:3.47.0" + checksum: 10c0/9b1a7088b7c660c7b8f1d4c90bb1816a8d5352ebdcb7bc742e3a0e4eb803316b5aa17bacb8769522342196351a5430178f46914644f2bfdb94ce0ced3c7fd523 languageName: node linkType: hard From cfa4f402efdd1517609498f31782bdf0e1d1cf66 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 09:34:55 +0000 Subject: [PATCH 004/123] New Crowdin Translations (automated) (#36976) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ca.json | 5 ++- app/javascript/mastodon/locales/cs.json | 1 + app/javascript/mastodon/locales/cy.json | 1 + app/javascript/mastodon/locales/da.json | 1 + app/javascript/mastodon/locales/de.json | 3 +- app/javascript/mastodon/locales/el.json | 1 + app/javascript/mastodon/locales/es-AR.json | 1 + app/javascript/mastodon/locales/es-MX.json | 1 + app/javascript/mastodon/locales/es.json | 1 + app/javascript/mastodon/locales/et.json | 41 +++++++++++----------- app/javascript/mastodon/locales/fi.json | 1 + app/javascript/mastodon/locales/fil.json | 26 ++++++++++++++ app/javascript/mastodon/locales/ga.json | 1 + app/javascript/mastodon/locales/gl.json | 1 + app/javascript/mastodon/locales/he.json | 1 + app/javascript/mastodon/locales/hu.json | 1 + app/javascript/mastodon/locales/is.json | 1 + app/javascript/mastodon/locales/it.json | 1 + app/javascript/mastodon/locales/pt-BR.json | 1 + app/javascript/mastodon/locales/pt-PT.json | 1 + app/javascript/mastodon/locales/sl.json | 14 ++++++++ app/javascript/mastodon/locales/tok.json | 23 ++++++++++++ app/javascript/mastodon/locales/vi.json | 1 + app/javascript/mastodon/locales/zh-TW.json | 1 + config/locales/da.yml | 2 +- config/locales/de.yml | 4 +-- config/locales/et.yml | 30 ++++++++-------- config/locales/simple_form.ca.yml | 2 ++ config/locales/simple_form.da.yml | 2 +- config/locales/simple_form.et.yml | 18 +++++----- config/locales/simple_form.pt-BR.yml | 2 +- config/locales/simple_form.sl.yml | 8 +++++ 32 files changed, 147 insertions(+), 51 deletions(-) diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 636c6f34dd8af2..81f89f962cf76d 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -250,6 +250,8 @@ "confirmations.missing_alt_text.title": "Hi voleu afegir text alternatiu?", "confirmations.mute.confirm": "Silencia", "confirmations.private_quote_notify.cancel": "Torna a l'edició", + "confirmations.private_quote_notify.confirm": "Publica la publicació", + "confirmations.private_quote_notify.do_not_show_again": "No tornis a mostrar-me aquest missatge", "confirmations.private_quote_notify.message": "La persona que citeu i altres mencionades rebran una notificació i podran veure la vostra publicació, encara que no us segueixen.", "confirmations.private_quote_notify.title": "Voleu compartir amb seguidors i usuaris mencionats?", "confirmations.quiet_post_quote_info.dismiss": "No m'ho tornis a recordar", @@ -341,7 +343,7 @@ "empty_column.bookmarked_statuses": "Encara no has marcat cap tut. Quan en marquis un, apareixerà aquí.", "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per posar-ho tot en marxa!", "empty_column.direct": "Encara no tens mencions privades. Quan n'enviïs o en rebis una, et sortirà aquí.", - "empty_column.disabled_feed": "Aquest canal ha estat desactivat per l'administració del vostre servidor.", + "empty_column.disabled_feed": "L'administració del vostre servidor ha desactivat aquest canal.", "empty_column.domain_blocks": "Encara no hi ha dominis blocats.", "empty_column.explore_statuses": "No hi ha res en tendència ara mateix. Revisa-ho més tard!", "empty_column.favourited_statuses": "Encara no has afavorit cap tut. Quan ho facis, apareixerà aquí.", @@ -366,6 +368,7 @@ "explore.trending_links": "Notícies", "explore.trending_statuses": "Tuts", "explore.trending_tags": "Etiquetes", + "featured_carousel.current": "Publicació {current, number} / {max, number}", "featured_carousel.header": "{count, plural, one {Publicació fixada} other {Publicacions fixades}}", "featured_carousel.slide": "Publicació {current, number} de {max, number}", "filter_modal.added.context_mismatch_explanation": "Aquesta categoria de filtre no s'aplica al context en què has accedit a aquest tut. Si també vols que el tut es filtri en aquest context, hauràs d'editar el filtre.", diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json index 4aa5ddccf99d7c..6f32e5359eac23 100644 --- a/app/javascript/mastodon/locales/cs.json +++ b/app/javascript/mastodon/locales/cs.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Vyčištěno! Nic tu není. Jakmile obdržíš nové notifikace, objeví se zde podle tvého nastavení.", "empty_column.notifications": "Zatím nemáte žádná oznámení. Až s vámi někdo bude interagovat, uvidíte to zde.", "empty_column.public": "Tady nic není! Napište něco veřejně, nebo začněte ručně sledovat uživatele z jiných serverů, aby tu něco přibylo", + "error.no_hashtag_feed_access": "Zaregistrujte se nebo se přihlaste k zobrazení a sledování tohoto hashtagu.", "error.unexpected_crash.explanation": "Kvůli chybě v našem kódu nebo problému s kompatibilitou prohlížeče nemohla být tato stránka správně zobrazena.", "error.unexpected_crash.explanation_addons": "Tuto stránku nelze správně zobrazit. Takovou chybu obvykle způsobuje doplněk prohlížeče nebo nástroje pro automatický překlad.", "error.unexpected_crash.next_steps": "Zkuste stránku načíst znovu. Pokud to nepomůže, zkuste Mastodon používat pomocí jiného prohlížeče nebo nativní aplikace.", diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json index 9f3631a0fe4861..fcf33e4635d529 100644 --- a/app/javascript/mastodon/locales/cy.json +++ b/app/javascript/mastodon/locales/cy.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Dim i boeni amdano! Does dim byd yma. Pan fyddwch yn derbyn hysbysiadau newydd, byddan nhw'n ymddangos yma yn ôl eich gosodiadau.", "empty_column.notifications": "Does gennych chi ddim hysbysiadau eto. Pan fyddwch chi'n rhyngweithio ag eraill, byddwch yn ei weld yma.", "empty_column.public": "Does dim byd yma! Ysgrifennwch rywbeth cyhoeddus, neu dilynwch ddefnyddwyr o weinyddion eraill i'w lanw", + "error.no_hashtag_feed_access": "Ymunwch neu mewngofnodwch i weld a dilyn yr hashnod hwn.", "error.unexpected_crash.explanation": "Oherwydd gwall yn ein cod neu oherwydd problem cysondeb porwr, nid oedd y dudalen hon gallu cael ei dangos yn gywir.", "error.unexpected_crash.explanation_addons": "Does dim modd dangos y dudalen hon yn gywir. Mae'r gwall hwn yn debygol o gael ei achosi gan ategyn porwr neu offer cyfieithu awtomatig.", "error.unexpected_crash.next_steps": "Ceisiwch ail-lwytho'r dudalen. Os nad yw hyn yn eich helpu, efallai gallwch ddefnyddio Mastodon trwy borwr neu ap brodorol gwahanol.", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index b480c2062c89d9..8bd2fdc8f9ae4a 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Alt er klar! Der er intet her. Når der modtages nye notifikationer, fremgår de her jævnfør dine indstillinger.", "empty_column.notifications": "Du har endnu ingen notifikationer. Når andre interagerer med dig, vil det fremgå her.", "empty_column.public": "Der er ikke noget her! Skriv noget offentligt, eller følg manuelt brugere fra andre servere for at se indhold", + "error.no_hashtag_feed_access": "Tilmeld dig eller log ind for at se og følge dette hashtag.", "error.unexpected_crash.explanation": "Grundet en fejl i vores kode, eller en netlæser-kompatibilitetsfejl, kunne siden ikke vises korrekt.", "error.unexpected_crash.explanation_addons": "Denne side kunne ikke vises korrekt. Fejlen skyldes sandsynligvis en browsertilføjelse eller automatiske oversættelsesværktøjer.", "error.unexpected_crash.next_steps": "Prøv at opfriske siden. Hjælper dette ikke, kan Mastodon muligvis stadig bruges via en anden netlæser eller app.", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 40cd74f23fa7f9..d7328b6d612d1b 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Alles klar! Hier gibt es nichts. Wenn Sie neue Mitteilungen erhalten, werden diese entsprechend Ihren Einstellungen hier angezeigt.", "empty_column.notifications": "Du hast noch keine Benachrichtigungen. Sobald andere Personen mit dir interagieren, wirst du hier darüber informiert.", "empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Profilen von anderen Servern, um die Timeline aufzufüllen", + "error.no_hashtag_feed_access": "Registriere dich oder melde dich an, um den Hashtag anzusehen und ihm zu folgen.", "error.unexpected_crash.explanation": "Wegen eines Fehlers in unserem Code oder aufgrund einer Browser-Inkompatibilität kann diese Seite nicht korrekt angezeigt werden.", "error.unexpected_crash.explanation_addons": "Diese Seite konnte nicht korrekt angezeigt werden. Dieser Fehler wird wahrscheinlich durch ein Browser-Add-on oder automatische Übersetzungswerkzeuge verursacht.", "error.unexpected_crash.next_steps": "Versuche, die Seite neu zu laden. Wenn das nicht helfen sollte, kannst du das Webinterface von Mastodon vermutlich über einen anderen Browser erreichen – oder du verwendest eine mobile (native) App.", @@ -759,7 +760,7 @@ "privacy.quote.disabled": "{visibility} – niemand darf zitieren", "privacy.quote.limited": "{visibility} – eingeschränktes Zitieren", "privacy.unlisted.additional": "Das Verhalten ist wie bei „Öffentlich“, jedoch gibt es einige Einschränkungen. Der Beitrag wird nicht in „Live-Feeds“, „Erkunden“, Hashtags oder über die Mastodon-Suchfunktion auffindbar sein – selbst wenn die zugehörige Einstellung aktiviert wurde.", - "privacy.unlisted.long": "Verborgen vor Suchergebnissen, Trends und öffentlichen Timelines auf Mastodon", + "privacy.unlisted.long": "Verborgen vor Suchergebnissen, Trends und öffentlichen Timelines", "privacy.unlisted.short": "Öffentlich (still)", "privacy_policy.last_updated": "Stand: {date}", "privacy_policy.title": "Datenschutzerklärung", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 244c69dd02913f..ffe2eceacb0bfc 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Όλα καθαρά! Δεν υπάρχει τίποτα εδώ. Όταν λαμβάνεις νέες ειδοποιήσεις, αυτές θα εμφανίζονται εδώ σύμφωνα με τις ρυθμίσεις σου.", "empty_column.notifications": "Δεν έχεις ειδοποιήσεις ακόμα. Όταν άλλα άτομα αλληλεπιδράσουν μαζί σου, θα το δεις εδώ.", "empty_column.public": "Δεν υπάρχει τίποτα εδώ! Γράψε κάτι δημόσιο ή ακολούθησε χειροκίνητα χρήστες από άλλους διακομιστές για να τη γεμίσεις", + "error.no_hashtag_feed_access": "Γίνετε μέλος ή συνδεθείτε για να δείτε και να ακολουθήσετε αυτήν την ετικέτα.", "error.unexpected_crash.explanation": "Είτε λόγω σφάλματος στον κώδικά μας ή λόγω ασυμβατότητας με τον περιηγητή, η σελίδα δε μπόρεσε να εμφανιστεί σωστά.", "error.unexpected_crash.explanation_addons": "Η σελίδα δεν μπόρεσε να εμφανιστεί σωστά. Το πρόβλημα οφείλεται πιθανόν σε κάποια επέκταση του φυλλομετρητή ή σε κάποιο αυτόματο εργαλείο μετάφρασης.", "error.unexpected_crash.next_steps": "Δοκίμασε να ανανεώσεις τη σελίδα. Αν αυτό δε βοηθήσει, ίσως να μπορέσεις να χρησιμοποιήσεις το Mastodon μέσω διαφορετικού περιηγητή ή κάποιας εφαρμογής.", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index 596ad83af07e4f..b470c1d523e2eb 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "¡Todo limpio! No hay nada acá. Cuando recibás nuevas notificaciones, aparecerán acá, acorde a tu configuración.", "empty_column.notifications": "Todavía no tenés ninguna notificación. Cuando otras cuentas interactúen con vos, vas a ver la notificación acá.", "empty_column.public": "¡Naranja! Escribí algo públicamente, o seguí usuarios manualmente de otros servidores para ir llenando esta línea temporal", + "error.no_hashtag_feed_access": "Únete o inicia sesión para ver y seguir esta etiqueta.", "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador web, esta página no se pudo mostrar correctamente.", "error.unexpected_crash.explanation_addons": "No se pudo mostrar correctamente esta página. Este error probablemente es causado por un complemento del navegador web o por herramientas de traducción automática.", "error.unexpected_crash.next_steps": "Intentá recargar la página. Si eso no ayuda, podés usar Mastodon a través de un navegador web diferente o aplicación nativa.", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index f7494d34450f06..69dcde9181e588 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "¡Todo limpio! No hay nada aquí. Cuando recibas nuevas notificaciones, aparecerán aquí conforme a tu configuración.", "empty_column.notifications": "No tienes ninguna notificación aún. Interactúa con otros para empezar una conversación.", "empty_column.public": "¡Aquí no hay nada! Escribe algo públicamente o sigue manualmente a usuarios de otros servidores para llenarlo", + "error.no_hashtag_feed_access": "Únete o inicia sesión para ver y seguir esta etiqueta.", "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador, esta página no se ha podido mostrar correctamente.", "error.unexpected_crash.explanation_addons": "No se pudo mostrar correctamente esta página. Este error probablemente fue causado por un complemento del navegador web o por herramientas de traducción automática.", "error.unexpected_crash.next_steps": "Intenta actualizar la página. Si eso no ayuda, es posible que puedas usar Mastodon a través de otro navegador o aplicación nativa.", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 87bb231f6ecbfb..1501531fc3ccfb 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "¡Todo limpio! No hay nada aquí. Cuando recibas nuevas notificaciones, aparecerán aquí conforme a tu configuración.", "empty_column.notifications": "Aún no tienes ninguna notificación. Cuando otras personas interactúen contigo, aparecerán aquí.", "empty_column.public": "¡No hay nada aquí! Escribe algo públicamente, o sigue usuarios de otras instancias manualmente para llenarlo", + "error.no_hashtag_feed_access": "Únete o inicia sesión para ver y seguir esta etiqueta.", "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador, esta página no se ha podido mostrar correctamente.", "error.unexpected_crash.explanation_addons": "No se pudo mostrar correctamente esta página. Este error probablemente fue causado por un complemento del navegador web o por herramientas de traducción automática.", "error.unexpected_crash.next_steps": "Intenta actualizar la página. Si eso no ayuda, quizás puedas usar Mastodon desde otro navegador o aplicación nativa.", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index c645e774285df0..c19aa7d6b9c1b9 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -36,7 +36,7 @@ "account.familiar_followers_two": "Jälgijateks {name1} ja {name2}", "account.featured": "Esiletõstetud", "account.featured.accounts": "Profiilid", - "account.featured.hashtags": "Sildid", + "account.featured.hashtags": "Teemaviited", "account.featured_tags.last_status_at": "Viimane postitus {date}", "account.featured_tags.last_status_never": "Postitusi pole", "account.follow": "Jälgi", @@ -126,8 +126,8 @@ "annual_report.summary.highlighted_post.by_replies": "kõige vastatum postitus", "annual_report.summary.highlighted_post.possessive": "omanik {name}", "annual_report.summary.most_used_app.most_used_app": "enim kasutatud äpp", - "annual_report.summary.most_used_hashtag.most_used_hashtag": "enim kasutatud silt", - "annual_report.summary.most_used_hashtag.none": "Pole", + "annual_report.summary.most_used_hashtag.most_used_hashtag": "enim kasutatud teemaviide", + "annual_report.summary.most_used_hashtag.none": "Puudub", "annual_report.summary.new_posts.new_posts": "uus postitus", "annual_report.summary.percentile.text": "See paneb su top {domain} kasutajate hulka.", "annual_report.summary.percentile.we_wont_tell_bernie": "Vägev.", @@ -204,7 +204,7 @@ "compose.saved.body": "Postitus salvestatud.", "compose_form.direct_message_warning_learn_more": "Vaata lisa", "compose_form.encryption_warning": "Postitused Mastodonis ei ole otsast-otsani krüpteeritud. Ära jaga mingeid delikaatseid andmeid Mastodoni kaudu.", - "compose_form.hashtag_warning": "See postitus ei ilmu ühegi märksõna all, kuna pole avalik. Vaid avalikud postitused on märksõnade kaudu leitavad.", + "compose_form.hashtag_warning": "See postitus ei ilmu ühegi teemaviite all, kuna pole avalik. Vaid avalikud postitused on teemaviidete kaudu leitavad.", "compose_form.lock_disclaimer": "Su konto ei ole {locked}. Igaüks saab sind jälgida, et näha su ainult-jälgijatele postitusi.", "compose_form.lock_disclaimer.lock": "lukus", "compose_form.placeholder": "Millest mõtled?", @@ -332,8 +332,8 @@ "emoji_button.search_results": "Otsitulemused", "emoji_button.symbols": "Sümbolid", "emoji_button.travel": "Reisimine & kohad", - "empty_column.account_featured.me": "Sa pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid silte või või sõbra kasutajakontot?", - "empty_column.account_featured.other": "{acct} pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid silte või või sõbra kasutajakontot?", + "empty_column.account_featured.me": "Sa pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid teemaviiteid või sõbra kasutajakontot?", + "empty_column.account_featured.other": "{acct} pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid teemaviiteid või sõbra kasutajakontot?", "empty_column.account_featured_other.unknown": "See kasutajakonto pole veel midagi esile tõstnud.", "empty_column.account_hides_collections": "See kasutaja otsustas mitte teha seda infot saadavaks", "empty_column.account_suspended": "Konto kustutatud", @@ -349,14 +349,15 @@ "empty_column.favourited_statuses": "Pole veel lemmikpostitusi. Kui märgid mõne, näed neid siin.", "empty_column.favourites": "Keegi pole veel seda postitust lemmikuks märkinud. Kui keegi seda teeb, siis on ta nähtav siin.", "empty_column.follow_requests": "Pole hetkel ühtegi jälgimistaotlust. Kui saad mõne, näed neid siin.", - "empty_column.followed_tags": "Sa ei jälgi veel ühtegi märksõna. Kui jälgid, ilmuvad need siia.", - "empty_column.hashtag": "Selle sildi all ei ole ühtegi postitust.", + "empty_column.followed_tags": "Sa ei jälgi veel ühtegi teemaviidet. Kui jälgid, ilmuvad need siia.", + "empty_column.hashtag": "Selle teemaviite all ei ole ühtegi postitust.", "empty_column.home": "Su koduajajoon on tühi. Jälgi rohkemaid inimesi, et seda täita {suggestions}", "empty_column.list": "Siin loetelus pole veel midagi. Kui loetelu liikmed teevad uusi postitusi, näed neid siin.", "empty_column.mutes": "Sa pole veel ühtegi kasutajat summutanud.", "empty_column.notification_requests": "Kõik tühi! Siin pole mitte midagi. Kui saad uusi teavitusi, ilmuvad need siin vastavalt sinu seadistustele.", "empty_column.notifications": "Ei ole veel teateid. Kui keegi suhtleb sinuga, näed seda siin.", "empty_column.public": "Siin pole midagi! Kirjuta midagi avalikku või jälgi ise kasutajaid täitmaks seda ruumi", + "error.no_hashtag_feed_access": "Selle teemaviite jälgimiseks liitu teenusega või logi sisse oma kasutajakontoga.", "error.unexpected_crash.explanation": "Meie poolse probleemi või veebilehitseja ühilduvusprobleemi tõttu ei suutnud me seda lehekülge korrektselt näidata.", "error.unexpected_crash.explanation_addons": "Seda lehte ei suudetud õigesti kuvada. Selle vea põhjustas arvatavasti mõni lehitseja lisand või automaattõlke tööriist.", "error.unexpected_crash.next_steps": "Proovi lehekülge uuesti avada. Kui see ei aita, võib proovida kasutada Mastodoni mõne muu veebilehitseja või äpi kaudu.", @@ -367,7 +368,7 @@ "explore.title": "Populaarsust koguv", "explore.trending_links": "Uudised", "explore.trending_statuses": "Postitused", - "explore.trending_tags": "Sildid", + "explore.trending_tags": "Teemaviited", "featured_carousel.current": "Postitus {current, number} / {max, number}", "featured_carousel.header": "{count, plural, one {Esiletõstetud postitus} other {Esiletõstetud postitust}}", "featured_carousel.slide": "Postitus {current, number} / {max, number}", @@ -411,7 +412,7 @@ "follow_suggestions.similar_to_recently_followed_longer": "Sarnane profiilile, mida hiljuti jälgima hakkasid", "follow_suggestions.view_all": "Vaata kõiki", "follow_suggestions.who_to_follow": "Keda jälgida", - "followed_tags": "Jälgitavad märksõnad", + "followed_tags": "Jälgitavad teemaviited", "footer.about": "Teave", "footer.about_this_server": "Serveri teave", "footer.directory": "Profiilikataloog", @@ -424,17 +425,17 @@ "generic.saved": "Salvestatud", "getting_started.heading": "Alustamine", "hashtag.admin_moderation": "Ava modereerimisliides #{name} jaoks", - "hashtag.browse": "Sirvi #{hashtag} sildiga postitusi", - "hashtag.browse_from_account": "Sirvi @{name} kasutaja #{hashtag} sildiga postitusi", + "hashtag.browse": "Sirvi #{hashtag} teemaviitega postitusi", + "hashtag.browse_from_account": "Sirvi @{name} kasutaja #{hashtag} teemaviitega postitusi", "hashtag.column_header.tag_mode.all": "ja {additional}", - "hashtag.column_header.tag_mode.any": "või {additional}", - "hashtag.column_header.tag_mode.none": "ilma {additional}", + "hashtag.column_header.tag_mode.any": "või teemaviide {additional}", + "hashtag.column_header.tag_mode.none": "ilma teemaviiteta {additional}", "hashtag.column_settings.select.no_options_message": "Soovitusi ei leitud", "hashtag.column_settings.select.placeholder": "Sisesta sildid…", "hashtag.column_settings.tag_mode.all": "Kõik need", "hashtag.column_settings.tag_mode.any": "Mõni neist", "hashtag.column_settings.tag_mode.none": "Mitte ükski neist", - "hashtag.column_settings.tag_toggle": "Kaasa lisamärked selle tulba jaoks", + "hashtag.column_settings.tag_toggle": "Kaasa lisasildid selle veeru jaoks", "hashtag.counter_by_accounts": "{count, plural, one {{counter} osalejaga} other {{counter} osalejaga}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} postitusega} other {{counter} postitusega}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} postitust} other {{counter} postitust}} täna", @@ -580,7 +581,7 @@ "navigation_bar.favourites": "Lemmikud", "navigation_bar.filters": "Summutatud sõnad", "navigation_bar.follow_requests": "Jälgimistaotlused", - "navigation_bar.followed_tags": "Jälgitavad märksõnad", + "navigation_bar.followed_tags": "Jälgitavad teemaviited", "navigation_bar.follows_and_followers": "Jälgitavad ja jälgijad", "navigation_bar.import_export": "Import ja eksport", "navigation_bar.lists": "Loetelud", @@ -730,7 +731,7 @@ "onboarding.profile.display_name": "Näidatav nimi", "onboarding.profile.display_name_hint": "Su täisnimi või naljanimi…", "onboarding.profile.note": "Elulugu", - "onboarding.profile.note_hint": "Saad @mainida teisi kasutajaid või #sildistada…", + "onboarding.profile.note_hint": "Saad @mainida teisi kasutajaid või lisada #teemaviidet…", "onboarding.profile.save_and_continue": "Salvesta ja jätka", "onboarding.profile.title": "Profiili seadistamine", "onboarding.profile.upload_avatar": "Laadi üles profiilipilt", @@ -758,7 +759,7 @@ "privacy.quote.anyone": "{visibility}, kõik võivad tsiteerida", "privacy.quote.disabled": "{visibility}, tsiteerimine pole lubatud", "privacy.quote.limited": "{visibility}, tsiteerimine on piiratud", - "privacy.unlisted.additional": "See on olemuselt küll avalik, aga postitus ei ilmu voogudes ega märksõnades, lehitsedes ega Mastodoni otsingus, isegi kui konto on seadistustes avalik.", + "privacy.unlisted.additional": "See on olemuselt küll avalik, aga postitus ei ilmu voogudes ega teemaviidetes, lehitsedes ega Mastodoni otsingus, isegi kui konto on seadistustes avalik.", "privacy.unlisted.long": "Peidetud Mastodoni otsingutulemustest, pupulaarsust koguva sisu voost ja avalikult ajajoonelt", "privacy.unlisted.short": "Vaikselt avalik", "privacy_policy.last_updated": "Viimati uuendatud {date}", @@ -845,7 +846,7 @@ "search.placeholder": "Otsi", "search.quick_action.account_search": "Sobivaid profiile {x}", "search.quick_action.go_to_account": "Mine profiili {x}", - "search.quick_action.go_to_hashtag": "Ava silt {x}", + "search.quick_action.go_to_hashtag": "Ava teemaviide {x}", "search.quick_action.open_url": "Ava URL Mastodonis", "search.quick_action.status_search": "Sobivad postitused {x}", "search.search_or_paste": "Otsi või kleebi URL", @@ -861,7 +862,7 @@ "search_results.all": "Kõik", "search_results.hashtags": "Sildid", "search_results.no_results": "Tulemusi pole.", - "search_results.no_search_yet": "Proovi otsida postitusi, profiile või silte.", + "search_results.no_search_yet": "Proovi otsida postitusi, profiile või teemaviiteid.", "search_results.see_all": "Vaata kõiki", "search_results.statuses": "Postitused", "search_results.title": "Otsi märksõna: {q}", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 4aa6c425b20d10..9b3aacb9b4abd7 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Olet ajan tasalla! Täällä ei ole mitään uutta kerrottavaa. Kun saat uusia ilmoituksia, ne näkyvät täällä asetustesi mukaisesti.", "empty_column.notifications": "Sinulla ei ole vielä ilmoituksia. Kun muut ovat vuorovaikutuksessa kanssasi, näet sen täällä.", "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti tai seuraa muiden palvelinten käyttäjiä, niin saat sisältöä", + "error.no_hashtag_feed_access": "Liity tai kirjaudu sisään, niin voit tarkastella ja seurata tätä aihetunnistetta.", "error.unexpected_crash.explanation": "Sivua ei voida näyttää oikein ohjelmointivirheen tai selaimen yhteensopivuusvajeen vuoksi.", "error.unexpected_crash.explanation_addons": "Sivua ei voitu näyttää oikein. Tämä virhe johtuu todennäköisesti selaimen lisäosasta tai automaattisista käännöstyökaluista.", "error.unexpected_crash.next_steps": "Kokeile päivittää sivu. Jos se ei auta, voi Mastodonin käyttö ehkä onnistua eri selaimella tai natiivisovelluksella.", diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json index 37db5a1b1596cb..82c1616ddf2f7f 100644 --- a/app/javascript/mastodon/locales/fil.json +++ b/app/javascript/mastodon/locales/fil.json @@ -1,6 +1,7 @@ { "about.blocks": "Mga pinatimping server", "about.contact": "Kontak:", + "about.default_locale": "Default", "about.disclaimer": "Ang Mastodon ay software na malaya at bukas-na-pinagmulan, at isang tatak-pangkalakal ng Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Hindi makuha ang dahilan", "about.domain_blocks.preamble": "Sa kadalasan, hinahayaan ka ng Mastodon na makita ang mga content sa, at makipag-interact sa users ng, ibang servers sa fediverse. Narito ang exceptions na ginawa sa partikular na server na ito.", @@ -8,6 +9,7 @@ "about.domain_blocks.silenced.title": "Limitado", "about.domain_blocks.suspended.explanation": "Walang data mula sa server na ito ang mapoproseso, maiimbak o maipagpapaplitan. Sa gayon. imposibleng magawa ang interaksiyon o komunikasyon sa ibang users sa server na ito.", "about.domain_blocks.suspended.title": "Suspendido", + "about.language_label": "Wika", "about.not_available": "Hindi available ang impormasyong ito.", "about.powered_by": "Decentralisadong social media na pinapagana ng {mastodon}", "about.rules": "Mga alituntunin ng server", @@ -19,21 +21,31 @@ "account.block_domain": "Hadlangan ang domain na {domain}", "account.block_short": "Hadlangan", "account.blocked": "Hinadlangan", + "account.blocking": "Pagharang", "account.cancel_follow_request": "I-kansela ang pagsunod", "account.copy": "I-sipi ang kawing sa profile", "account.direct": "Palihim banggitin si @{name}", "account.disable_notifications": "I-tigil ang pagpapaalam sa akin tuwing nagpopost si @{name}", + "account.domain_blocking": "Pag-block ng domain", "account.edit_profile": "Baguhin ang profile", + "account.edit_profile_short": "I-edit", "account.enable_notifications": "Ipaalam sa akin kapag nag-post si @{name}", "account.endorse": "I-tampok sa profile", "account.familiar_followers_many": "Sinusundan nina {name1}, {name2}, at {othersCount, plural, one {# iba pa na kilala mo} other {# na iba pa na kilala mo}}", "account.familiar_followers_one": "Sinusindan ni/ng {name1}", "account.familiar_followers_two": "Sinusindan nina {name1} at {name2}", "account.featured": "Itinatampok", + "account.featured.accounts": "Mga Profile", + "account.featured.hashtags": "Mga Hashtag", "account.featured_tags.last_status_at": "Huling post noong {date}", "account.featured_tags.last_status_never": "Walang mga post", "account.follow": "Sundan", "account.follow_back": "Sundan pabalik", + "account.follow_back_short": "I-follow back", + "account.follow_request": "Humiling na mag-follow", + "account.follow_request_cancel": "I-cancel ang request", + "account.follow_request_cancel_short": "Cancel", + "account.follow_request_short": "Request", "account.followers": "Mga tagasunod", "account.followers.empty": "Wala pang sumusunod sa tagagamit na ito.", "account.followers_counter": "{count, plural, one {{counter} tagasunod} other {{counter} tagasunod}}", @@ -56,15 +68,29 @@ "account.mute_notifications_short": "I-mute ang mga abiso", "account.mute_short": "I-mute", "account.muted": "Naka-mute", + "account.muting": "Pag-mute", + "account.mutual": "Pina-follow nyo ang isa't-isa", "account.no_bio": "Walang nakalaan na paglalarawan.", "account.open_original_page": "Buksan ang pinagmulang pahina", "account.posts": "Mga post", + "account.posts_with_replies": "Mga Post at Reply", + "account.remove_from_followers": "Alisin si {name} sa mga follower", "account.report": "I-ulat si/ang @{name}", "account.requested_follow": "Hinihiling ni {name} na sundan ka", + "account.requests_to_follow_you": "Mga Request para i-fillow ka", "account.share": "Ibahagi ang profile ni @{name}", "account.show_reblogs": "Ipakita ang mga pagpapalakas mula sa/kay {name}", + "account.statuses_counter": "{count,plural,one {{counter} i-post} other {{counter} mga post}}", + "account.unblock": "I-unblock si @{name}", + "account.unblock_domain": "I-unblock ang domain {domain}", + "account.unblock_domain_short": "I-unblock", + "account.unblock_short": "I-unblock", "account.unendorse": "Huwag itampok sa profile", "account.unfollow": "Huwag nang sundan", + "account.unmute": "I-unmute si @{name}", + "account.unmute_notifications_short": "I-unmute ang mga notification", + "account.unmute_short": "I-unmute", + "account_note.placeholder": "I-click para magdagdag ng note", "admin.dashboard.retention.cohort_size": "Mga bagong tagagamit", "alert.rate_limited.message": "Mangyaring subukan muli pagkatapos ng {retry_time, time, medium}.", "audio.hide": "Itago ang tunog", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index 5cafc8f61bf6da..ed866f32ce6fff 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Gach soiléir! Níl aon rud anseo. Nuair a gheobhaidh tú fógraí nua, beidh siad le feiceáil anseo de réir do shocruithe.", "empty_column.notifications": "Níl aon fógraí agat fós. Nuair a dhéanann daoine eile idirghníomhú leat, feicfear anseo é.", "empty_column.public": "Faic anseo! Scríobh rud éigin go poiblí, nó lean úsáideoirí ar fhreastalaithe eile chun é a líonadh", + "error.no_hashtag_feed_access": "Bí linn nó logáil isteach chun an haischlib seo a fheiceáil agus a leanúint.", "error.unexpected_crash.explanation": "De bharr fabht inár gcód, nó fadhb le chomhoiriúnacht brabhsálaí, níorbh fhéadfadh an leathanach seo a léiriú i gceart.", "error.unexpected_crash.explanation_addons": "Ní taispeántar an leathanach seo mar is ceart. Is dócha go gcruthaíonn breiseán brabhsálaí nó uirlisí uathaistriúcháin an fhadhb seo.", "error.unexpected_crash.next_steps": "Bain triail as an leathanach a athnuachan. Mura gcabhraíonn sé sin, seans go mbeidh tú fós in ann Mastodon a úsáid trí bhrabhsálaí nó aip dhúchais eile.", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index f8a1c39f6ae457..b9142bcd18da2d 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Todo ben! Nada por aquí. Cando recibas novas notificacións aparecerán aquí seguindo o criterio dos teus axustes.", "empty_column.notifications": "Aínda non tes notificacións. Aparecerán cando outras persoas interactúen contigo.", "empty_column.public": "Nada por aquí! Escribe algo de xeito público, ou segue de xeito manual usuarias doutros servidores para ir enchéndoo", + "error.no_hashtag_feed_access": "Crea unha conta ou accede para ver e seguir este cancelo.", "error.unexpected_crash.explanation": "Debido a un erro no noso código ou a unha compatilidade co teu navegador, esta páxina non pode ser amosada correctamente.", "error.unexpected_crash.explanation_addons": "Non se puido mostrar correctamente a páxina. Habitualmente este erro está causado por algún engadido do navegador ou ferramentas de tradución automática.", "error.unexpected_crash.next_steps": "Tenta actualizar a páxina. Se isto non axuda podes tamén empregar Mastodon noutro navegador ou aplicación nativa.", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index b16e4b9514656c..cdd041df4132d0 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "בום! אין פה כלום. כשיווצרו עוד התראות, הן יופיעו כאן על בסיס ההעדפות שלך.", "empty_column.notifications": "אין התראות עדיין. יאללה, הגיע הזמן להתחיל להתערבב.", "empty_column.public": "אין פה כלום! כדי למלא את הטור הזה אפשר לכתוב משהו, או להתחיל לעקוב אחרי אנשים מקהילות אחרות", + "error.no_hashtag_feed_access": "הצטרפו או התחברו כדי לעקוב אחרי תגית זו.", "error.unexpected_crash.explanation": "עקב תקלה בקוד שלנו או בעיית תאימות דפדפן, לא ניתן להציג דף זה כראוי.", "error.unexpected_crash.explanation_addons": "לא ניתן להציג דף זה כראוי. הבעיה נגרמת כנראה עקב תוסף דפדפן או כלי תרגום אוטומטי.", "error.unexpected_crash.next_steps": "נסה/י לרענן את הדף. אם זה לא עוזר, אולי אפשר עדיין להשתמש במסטודון דרך דפדפן אחר או באמצעות אפליקציה ילידית.", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 85330bfadc4127..08082b53ba54dd 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Minden tiszta! Itt nincs semmi. Ha új értesítéseket kapsz, azok itt jelennek meg a beállításoknak megfelelően.", "empty_column.notifications": "Jelenleg még nincsenek értesítéseid. Ha mások kapcsolatba lépnek veled, ezek itt lesznek láthatóak.", "empty_column.public": "Jelenleg itt nincs semmi! Írj valamit nyilvánosan vagy kövess más kiszolgálón levő felhasználókat, hogy megtöltsd.", + "error.no_hashtag_feed_access": "Csatlakozz vagy jelentkezz be, hogy megtekintsd és kövesd ezt a hashtaget.", "error.unexpected_crash.explanation": "Egy kód- vagy böngészőkompatibilitási hiba miatt ez az oldal nem jeleníthető meg helyesen.", "error.unexpected_crash.explanation_addons": "Ezt az oldalt nem lehet helyesen megjeleníteni. Ezt a hibát valószínűleg egy böngésző kiegészítő vagy egy automatikus fordító okozza.", "error.unexpected_crash.next_steps": "Próbáld frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index e18cdd05c7021e..5af1f32ed52a3a 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Allt hreint! Það er ekkert hér. Þegar þú færð nýjar tilkynningar, munu þær birtast hér í samræmi við stillingarnar þínar.", "empty_column.notifications": "Þú ert ekki ennþá með neinar tilkynningar. Vertu í samskiptum við aðra til að umræður fari af stað.", "empty_column.public": "Það er ekkert hér! Skrifaðu eitthvað opinberlega, eða fylgstu með notendum á öðrum netþjónum til að fylla upp í þetta", + "error.no_hashtag_feed_access": "Skráðu þig inn eða stofnaðu aðgang til að skoða og fylgjast með þessu myllumerki.", "error.unexpected_crash.explanation": "Vegna villu í kóðanum okkar eða samhæfnivandamála í vafra er ekki hægt að birta þessa síðu svo vel sé.", "error.unexpected_crash.explanation_addons": "Ekki er hægt að birta þessa síðu rétt. Þetta er líklega af völdum forritsviðbótar í vafranum eða sjálfvirkra þýðainaverkfæra.", "error.unexpected_crash.next_steps": "Prófaðu að endurlesa síðuna. Ef það hjálpar ekki til, má samt vera að þú getir notað Mastodon í gegnum annan vafra eða forrit.", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index e5c4c22f3cfa7a..9fc0be453152ec 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Tutto chiaro! Non c'è niente qui. Quando ricevi nuove notifiche, verranno visualizzate qui in base alle tue impostazioni.", "empty_column.notifications": "Non hai ancora nessuna notifica. Quando altre persone interagiranno con te, le vedrai qui.", "empty_column.public": "Non c'è nulla qui! Scrivi qualcosa pubblicamente o segui manualmente gli utenti dagli altri server per riempire questo spazio", + "error.no_hashtag_feed_access": "Iscriviti o accedi per visualizzare e seguire questo hashtag.", "error.unexpected_crash.explanation": "A causa di un bug nel nostro codice o di un problema di compatibilità del browser, non è stato possibile visualizzare correttamente questa pagina.", "error.unexpected_crash.explanation_addons": "Impossibile mostrare correttamente questa pagina. Questo errore è probabilmente causato da un addon del browser o da strumenti di traduzione automatica.", "error.unexpected_crash.next_steps": "Prova a ricaricare la pagina. Se non aiuta, potresti comunque utilizzare Mastodon tramite un browser differente o un'app nativa.", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 6251b4c7190a86..d60d1561524f4b 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Tudo limpo! Não há nada aqui. Quando você receber novas notificações, elas aparecerão aqui de acordo com suas configurações.", "empty_column.notifications": "Interaja com outros usuários para começar a conversar.", "empty_column.public": "Publique algo ou siga manualmente usuários de outros servidores", + "error.no_hashtag_feed_access": "Se cadastre ou faça login para ver e seguir esta hashtag.", "error.unexpected_crash.explanation": "Esta página não pôde ser mostrada corretamente. Este erro provavelmente é devido a um bug em nosso código ou um problema de compatibilidade de navegador.", "error.unexpected_crash.explanation_addons": "Esta página não pôde ser mostrada corretamente. Este erro provavelmente é causado por um complemento do navegador ou ferramentas de tradução automática.", "error.unexpected_crash.next_steps": "Tente atualizar a página. Se isso não ajudar, você ainda poderá usar o Mastodon por meio de um navegador diferente ou de um aplicativo nativo.", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index e8b8de206fcbc2..101a4bbd78b41e 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Tudo limpo! Não há nada aqui. Quando receberes novas notificações, elas aparecerão aqui conforme as tuas configurações.", "empty_column.notifications": "Ainda não tens quaisquer notificações. Quando outras pessoas interagirem contigo, verás isso aqui.", "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para veres aqui os conteúdos públicos", + "error.no_hashtag_feed_access": "Inscreva-se ou inicie sessão para ver e seguir esta etiqueta.", "error.unexpected_crash.explanation": "Devido a um erro no nosso código ou a um problema de compatibilidade do navegador, esta página não pode ser apresentada corretamente.", "error.unexpected_crash.explanation_addons": "Esta página não pode ser mostrada corretamente. Este erro provavelmente é causado por um complemento do navegador ou ferramentas de tradução automática.", "error.unexpected_crash.next_steps": "Tenta atualizar a página. Se isso não ajudar, podes usar o Mastodon através de um navegador diferente ou uma aplicação nativa.", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index a93a9569ab77d4..3b246c5b2918f2 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -1,6 +1,7 @@ { "about.blocks": "Moderirani strežniki", "about.contact": "Stik:", + "about.default_locale": "Privzeto", "about.disclaimer": "Mastodon je prosto, odprtokodno programje in blagovna znamka podjetja Mastodon gGmbH.", "about.domain_blocks.no_reason_available": "Razlog ni na voljo", "about.domain_blocks.preamble": "Mastodon vam na splošno omogoča ogled vsebin in interakcijo z uporabniki z vseh drugih strežnikov v fediverzumu. Tu so navedene izjeme, ki jih postavlja ta strežnik.", @@ -8,6 +9,7 @@ "about.domain_blocks.silenced.title": "Omejeno", "about.domain_blocks.suspended.explanation": "Nobeni podatki s tega strežnika ne bodo obdelani, shranjeni ali izmenjani, zaradi česar je nemogoča kakršna koli interakcija ali komunikacija z uporabniki s tega strežnika.", "about.domain_blocks.suspended.title": "Suspendiran", + "about.language_label": "Jezik", "about.not_available": "Ti podatki še niso na voljo na tem strežniku.", "about.powered_by": "Decentraliziran družabni medij, ki ga poganja {mastodon}", "about.rules": "Pravila strežnika", @@ -24,12 +26,16 @@ "account.direct": "Zasebno omeni @{name}", "account.disable_notifications": "Ne obveščaj me več, ko ima @{name} novo objavo", "account.edit_profile": "Uredi profil", + "account.edit_profile_short": "Uredi", "account.enable_notifications": "Obvesti me, ko ima @{name} novo objavo", "account.endorse": "Izpostavi v profilu", + "account.featured.accounts": "Profili", + "account.featured.hashtags": "Ključniki", "account.featured_tags.last_status_at": "Zadnja objava {date}", "account.featured_tags.last_status_never": "Ni objav", "account.follow": "Sledi", "account.follow_back": "Sledi nazaj", + "account.follow_request_cancel_short": "Prekliči", "account.followers": "Sledilci", "account.followers.empty": "Nihče še ne sledi temu uporabniku.", "account.followers_counter": "{count, plural, one {{counter} sledilec} two {{counter} sledilca} few {{counter} sledilci} other {{counter} sledilcev}}", @@ -214,9 +220,15 @@ "confirmations.missing_alt_text.secondary": "Vseeno objavi", "confirmations.missing_alt_text.title": "Dodam nadomestno besedilo?", "confirmations.mute.confirm": "Utišaj", + "confirmations.quiet_post_quote_info.got_it": "Razumem", "confirmations.redraft.confirm": "Izbriši in preoblikuj", "confirmations.redraft.message": "Ali ste prepričani, da želite izbrisati to objavo in jo preoblikovati? Izkazi priljubljenosti in izpostavitve bodo izgubljeni, odgovori na izvirno objavo pa bodo osiroteli.", "confirmations.redraft.title": "Želite izbrisati in preoblikovati objavo?", + "confirmations.remove_from_followers.confirm": "Odstrani sledilca", + "confirmations.remove_from_followers.title": "Ali želite odstraniti sledilca?", + "confirmations.revoke_quote.confirm": "Odstrani objavo", + "confirmations.revoke_quote.message": "Tega dejanja ni možno povrniti.", + "confirmations.revoke_quote.title": "Ali želite odstraniti objavo?", "confirmations.unfollow.confirm": "Ne sledi več", "content_warning.hide": "Skrij objavo", "content_warning.show": "Vseeno pokaži", @@ -682,6 +694,7 @@ "relative_time.minutes": "{number} m", "relative_time.seconds": "{number} s", "relative_time.today": "danes", + "remove_quote_hint.button_label": "Razumem", "reply_indicator.attachments": "{count, plural, one {# priloga} two {# prilogi} few {# priloge} other {# prilog}}", "reply_indicator.cancel": "Prekliči", "reply_indicator.poll": "Anketa", @@ -735,6 +748,7 @@ "report_notification.categories.violation": "Kršitev pravila", "report_notification.categories.violation_sentence": "kršitev pravila", "report_notification.open": "Odpri prijavo", + "search.clear": "Počisti iskanje", "search.no_recent_searches": "Ni nedavnih iskanj", "search.placeholder": "Iskanje", "search.quick_action.account_search": "Profili, ki se ujemajo z {x}", diff --git a/app/javascript/mastodon/locales/tok.json b/app/javascript/mastodon/locales/tok.json index 221c215887600e..2f56042462438a 100644 --- a/app/javascript/mastodon/locales/tok.json +++ b/app/javascript/mastodon/locales/tok.json @@ -28,6 +28,7 @@ "account.disable_notifications": "@{name} li toki la o mu ala e mi", "account.domain_blocking": "mi len e ma ni", "account.edit_profile": "o ante e lipu mi", + "account.edit_profile_short": "o ante", "account.enable_notifications": "@{name} li toki la o toki e toki ona tawa mi", "account.endorse": "lipu jan la o suli e ni", "account.familiar_followers_many": "{name1} en {name2} en {othersCount, plural, other {jan ante #}} li kute e jan ni", @@ -40,6 +41,10 @@ "account.featured_tags.last_status_never": "toki ala li lon", "account.follow": "o kute", "account.follow_back": "jan ni li kute e sina. o kute", + "account.follow_back_short": "jan ni li kute e sina. o kute", + "account.follow_request": "toki e wile kute", + "account.follow_request_cancel": "toki ala e wile kute", + "account.follow_request_cancel_short": "ala", "account.followers": "jan kute", "account.followers.empty": "jan ala li kute e jan ni", "account.followers_counter": "{count, plural, other {jan {counter} li kute e ona}}", @@ -151,6 +156,8 @@ "bundle_modal_error.close": "o pini", "bundle_modal_error.message": "ilo li wile kama e ijo ni, taso pakala li lon.", "bundle_modal_error.retry": "o alasa sin", + "carousel.current": "lipu {current, number} / {max, number}", + "carousel.slide": "lipu {current, number} lon {max, number}", "closed_registrations.other_server_instructions": "kulupu Mastodon la lawa mute li lon. sina ken pali e sijelo lon ma ante la sina awen ken lukin e ijo pi ma ni.", "closed_registrations_modal.description": "tenpo ni la, sina ken ala pali e jan lon ma {domain}. taso sina wile kepeken ilo Mastodon la, sina ken pali e jan lon ma ante lon ala ma {domain}.", "closed_registrations_modal.find_another_server": "o alasa e ma ante", @@ -167,6 +174,8 @@ "column.edit_list": "o ante e kulupu", "column.favourites": "ijo pona", "column.firehose": "toki pi tenpo ni", + "column.firehose_local": "toki pi tenpo ni pi ma ni", + "column.firehose_singular": "toki pi tenpo ni", "column.follow_requests": "wile alasa pi jan ante", "column.home": "lipu open", "column.list_members": "o ante e kulupu jan", @@ -186,6 +195,7 @@ "community.column_settings.local_only": "toki tan ni taso", "community.column_settings.media_only": "sitelen taso", "community.column_settings.remote_only": "toki tan ante taso", + "compose.error.blank_post": "toki ken ala jo e ala.", "compose.language.change": "o ante e nasin toki", "compose.language.search": "o alasa e nasin toki...", "compose.published.body": "toki li pana.", @@ -237,6 +247,9 @@ "confirmations.missing_alt_text.secondary": "o pana a", "confirmations.missing_alt_text.title": "o pana ala pana e toki pi sona lukin?", "confirmations.mute.confirm": "o len", + "confirmations.private_quote_notify.confirm": "o pana e toki ni tawa ale", + "confirmations.private_quote_notify.do_not_show_again": "o toki ala e toki ni", + "confirmations.quiet_post_quote_info.dismiss": "o toki ala e ni tawa mi", "confirmations.quiet_post_quote_info.got_it": "sona", "confirmations.redraft.confirm": "o weka o pali sin e toki", "confirmations.redraft.message": "pali sin e toki ni la sina wile ala wile weka e ona? sina ni la suli pi toki ni en wawa pi toki ni li weka. kin la toki lon toki ni li jo e mama ala.", @@ -246,7 +259,10 @@ "confirmations.remove_from_followers.title": "o kama ala kama kute ala e jan?", "confirmations.revoke_quote.confirm": "o weka e toki tan lipu Mastodon", "confirmations.revoke_quote.title": "sina wile weka ala weka e toki?", + "confirmations.unblock.confirm": "o len ala", + "confirmations.unblock.title": "o len ala e {name}?", "confirmations.unfollow.confirm": "o kute ala", + "confirmations.unfollow.title": "o kute ala e {name}?", "content_warning.hide": "o len", "content_warning.show": "o lukin a", "content_warning.show_more": "o lukin", @@ -287,10 +303,12 @@ "domain_pill.your_handle": "nimi sina:", "domain_pill.your_server": "ni li ma sina lon ilo. toki ale sina li lon ma ni. ma li ike tawa sina la, sina ken tawa ma ante. ni la jan kute sina li tawa sama.", "domain_pill.your_username": "ni li nimi sina. ma sina la, sina taso li jo e ona. jan mute li lon ma ante la, ona li ken jo e nimi sama.", + "dropdown.empty": "o wile e ijo wan", "embed.instructions": "o pana e toki ni la, toki li lon lipu ante. ", "embed.preview": "ni li jo e sitelen ni:", "emoji_button.activity": "musi", "emoji_button.clear": "o weka", + "emoji_button.custom": "pali sin", "emoji_button.flags": "len ma", "emoji_button.food": "moku", "emoji_button.label": "o pana e sitelen pilin", @@ -326,7 +344,10 @@ "explore.trending_links": "sin", "explore.trending_statuses": "toki", "explore.trending_tags": "kulupu pi lipu suli", + "featured_carousel.current": "toki{current, number} / {max, number}", "featured_carousel.header": "{count, plural, other {toki sewi}}", + "featured_carousel.slide": "toki {current, number} lon {max, number}", + "filter_modal.added.review_and_configure_title": "o alasa e lawa", "filter_modal.added.settings_link": "lipu lawa", "filter_modal.select_filter.expired": "tenpo pini", "filter_modal.select_filter.search": "o alasa anu pali", @@ -367,8 +388,10 @@ "hashtag.counter_by_accounts": "{count, plural, other {jan {counter}}}", "hashtag.counter_by_uses": "{count, plural, other {toki {counter}}}", "hashtag.counter_by_uses_today": "{count, plural, other {toki poka {counter}}}", + "hashtag.feature": "lipu jan la o suli e ni", "hashtag.follow": "o kute e kulupu lipu", "hashtag.mute": "o kute ala e kulupu #{hashtag}", + "hashtag.unfeature": "lipu jan la o suli ala e ni", "hashtag.unfollow": "o kute ala e kulupu lipu", "hints.profiles.followers_may_be_missing": "jan kute li ken weka.", "hints.profiles.see_more_followers": "o lukin e jan ni lon ma {domain}: ona li kute e jan ni.", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 3ae73d0fd5e426..d6437549fe9350 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Sạch sẽ! Không còn gì ở đây. Khi bạn nhận được thông báo mới, chúng sẽ xuất hiện ở đây theo cài đặt của bạn.", "empty_column.notifications": "Bạn chưa có thông báo nào. Hãy thử theo dõi hoặc nhắn riêng cho ai đó.", "empty_column.public": "Trống trơn! Bạn hãy viết gì đó hoặc bắt đầu theo dõi những người khác", + "error.no_hashtag_feed_access": "Tham gia hoặc đăng nhập để xem và theo dõi hashtag này.", "error.unexpected_crash.explanation": "Trang này có thể không hiển thị chính xác do lỗi lập trình Mastodon hoặc vấn đề tương thích trình duyệt.", "error.unexpected_crash.explanation_addons": "Trang này không thể hiển thị do xung khắc với add-on của trình duyệt hoặc công cụ tự động dịch ngôn ngữ.", "error.unexpected_crash.next_steps": "Hãy thử làm mới trang. Nếu vẫn không được, bạn hãy vào Mastodon bằng một ứng dụng di động hoặc trình duyệt khác.", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 16089b41065018..cbe6a1e56fb17d 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "清空啦!已經沒有任何推播通知。當您收到新推播通知時,它們將依照您的設定於此顯示。", "empty_column.notifications": "您還沒有收到任何推播通知,當您與別人開始互動時,它將於此顯示。", "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或者跟隨其他伺服器的使用者後,就會有嘟文出現了", + "error.no_hashtag_feed_access": "加入或登入 Mastodon 以檢視與跟隨此主題標籤。", "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,無法正常顯示此頁面。", "error.unexpected_crash.explanation_addons": "此頁面無法被正常顯示,這可能是由瀏覽器附加元件或網頁自動翻譯工具造成的。", "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有改善,您可以使用不同的瀏覽器或應用程式以檢視來使用 Mastodon。", diff --git a/config/locales/da.yml b/config/locales/da.yml index e8053aaf63ff9c..b26de6ab2c7b5c 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1901,7 +1901,7 @@ da: user_domain_block: "%{target_name} blev blokeret" lost_followers: Tabte følgere lost_follows: Mistet følger - preamble: Der kan mistes fulgte objekter og følgere, når et domæne blokeres eller moderatorerne beslutter at suspendere en ekstern server. Når det sker, kan der downloades lister over afbrudte relationer til inspektion og mulig import på anden server. + preamble: Du kan miste fulgte og følgere, når du blokerer et domæne, eller når dine moderatorer beslutter at suspendere en fjernserver. Når det sker, kan du downloade lister over afbrudte forhold til inspektion og eventuelt import til en anden server. purged: Oplysninger om denne server er blevet renset af serveradministratoreren. type: Begivenhed statuses: diff --git a/config/locales/de.yml b/config/locales/de.yml index b085fc4f019d44..df3e5b8887f8f3 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -364,7 +364,7 @@ de: overwrite: Überschreiben shortcode: Shortcode shortcode_hint: Mindestens 2 Zeichen, nur Buchstaben, Ziffern und Unterstriche - title: Eigene Emojis + title: Emojis uncategorized: Unkategorisiert unlist: Nicht anzeigen unlisted: Nicht sichtbar @@ -2150,7 +2150,7 @@ de: follow_limit_reached: Du kannst nicht mehr als %{limit} Profilen folgen go_to_sso_account_settings: Kontoeinstellungen des Identitätsanbieters aufrufen invalid_otp_token: Ungültiger Code der Zwei-Faktor-Authentisierung - otp_lost_help_html: Wenn du beides nicht mehr weißt, melde dich bitte bei uns unter der E-Mail-Adresse %{email} + otp_lost_help_html: Wenn du sowohl die E-Mail-Adresse als auch das Passwort nicht mehr weißt, melde dich bitte bei uns unter %{email} rate_limited: Zu viele Authentisierungsversuche. Bitte versuche es später noch einmal. seamless_external_login: Du bist über einen externen Dienst angemeldet, daher sind Passwort- und E-Mail-Einstellungen nicht verfügbar. signed_in_as: 'Angemeldet als:' diff --git a/config/locales/et.yml b/config/locales/et.yml index 99643e6085971d..0bb6f5acc4d33e 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -384,8 +384,8 @@ et: one: "%{count} ootel raport" other: "%{count} ootel raportit" pending_tags_html: - one: "%{count} ootel silt" - other: "%{count} ootel silti" + one: "%{count} ootel teemaviide" + other: "%{count} ootel teemaviidet" pending_users_html: one: "%{count} ootel kasutaja" other: "%{count} ootel kasutajat" @@ -783,7 +783,7 @@ et: manage_settings: Halda sätteid manage_settings_description: Lubab kasutajatel muuta lehekülje sätteid manage_taxonomies: Halda taksonoomiaid - manage_taxonomies_description: Luba kasutajatel populaarset sisu üle vaadata ning uuendada siltide sätteid + manage_taxonomies_description: Luba kasutajatel populaarset sisu üle vaadata ning uuendada teemaviidete seadistusi manage_user_access: Halda kasutajate ligipääsu manage_user_access_description: Võimaldab kasutajatel keelata teiste kasutajate kaheastmelise autentimise, muuta oma e-posti aadressi ja lähtestada oma parooli manage_users: Kasutajate haldamine @@ -998,8 +998,8 @@ et: reset: Lähtesta review: Vaata olek üle search: Otsi - title: Märksõnad - updated_msg: Sildi sätted edukalt uuendatud + title: Teemaviited + updated_msg: Teemaviite seadistused on uuendatud terms_of_service: back: Tagasi teenuse tingimustesse changelog: Mis on muutunud @@ -1088,14 +1088,14 @@ et: tag_servers_dimension: Populaarseimad serverid tag_servers_measure: erinevat serverit tag_uses_measure: kasutajaid kokku - description_html: Need sildid ilmuvad praegu paljudes postitutes mida su server näeb. See võib aidata su kasutajatel leida seda millest kõige rohkem parajasti räägitakse. Ühtegi silti ei näidata avalikult, enne nende heaks kiitmist. + description_html: Need teemaviited ilmuvad praegu paljudes postitutes, mida su server näeb. See võib aidata su kasutajatel leida seda millest kõige rohkem parajasti räägitakse. Ühtegi teemaviidet ei näidata enne nende heaks kiitmist avalikult. listable: Võib olla soovitatud no_tag_selected: Silte ei muudetud, kuna ühtegi polnud valitud not_listable: Ei soovitata not_trendable: Ei ilmu trendides not_usable: Ei saa kasutada peaked_on_and_decaying: Populaarseim %{date}, nüüd langemas - title: Trendikad sildid + title: Trendikad teemaviited trendable: Võib ilmuda trendides trending_rank: 'Trendides #%{rank}' usable: Kasutatav @@ -1186,7 +1186,7 @@ et: new_trending_statuses: title: Trendikad postitused new_trending_tags: - title: Trendikad sildid + title: Trendikad teemaviited subject: Uued %{instance} trendid ülevaatuseks aliases: add_new: Pane kolimiseks valmis @@ -1428,8 +1428,8 @@ et: featured_tags: add_new: Lisa uus errors: - limit: Oled jõudnud siltide lubatud maksimumarvuni - hint_html: "Mis on esiletõstetud sildid? Neid silte näidatakse su avalikul profiilil esiletõstetult ning need aitavad teistel leida postitusi, millel on selline silt. See on hea viis, kuidas hoida järge loovtöödel või pikaajalistel projektidel." + limit: Oled jõudnud teemaviidete lubatud maksimumarvuni + hint_html: "Mis on esiletõstetud teemaviited? Neid teemaviiteid näidatakse su avalikul profiilil esiletõstetult ning need aitavad teistel leida postitusi, millel on selline teemaviide. See on hea viis, kuidas hoida järge loovtöödel või pikaajalistel projektidel." filters: contexts: account: Profiilid @@ -1815,7 +1815,7 @@ et: content_warning: 'Sisuhoiatus:' descriptions: account: "@%{acct} avalikud postitused" - tag: 'Avalikud postitused sildiga #%{hashtag}' + tag: 'Avalikud postitused teemaviitega #%{hashtag}' scheduled_statuses: over_daily_limit: Lubatud ajastatud postituste arv %{limit} päevas on tänaseks ületatud over_total_limit: Oled jõudnud ajastatud postituste lubatud maksimumarvuni %{limit} @@ -1880,7 +1880,7 @@ et: development: Arendus edit_profile: Muuda profiili export: Eksport - featured_tags: Esile toodud sildid + featured_tags: Esile toodud teemaviited import: Impordi import_and_export: Import / eksport migrate: Konto kolimine @@ -1923,8 +1923,8 @@ et: show: Näita rohkem default_language: Kasutajaliidese keelega sama disallowed_hashtags: - one: 'sisaldab ebasobivat silti: %{tags}' - other: 'sisaldab ebasobivaid silte: %{tags}' + one: 'sisaldab ebasobivat teemaviidet: %{tags}' + other: 'sisaldab ebasobivaid teemaviiteid: %{tags}' edited_at_html: Muudetud %{date} errors: in_reply_not_found: Postitus, millele üritad vastata, ei näi enam eksisteerivat. @@ -2139,7 +2139,7 @@ et: other: "%{people} inimest viimase 2 päeva jooksul" hashtags_subtitle: Avasta, mis viimase 2 päeva jooksul on toimunud hashtags_title: Populaarsed märksõnad - hashtags_view_more: Vaata teisi trendikaid märksõnu + hashtags_view_more: Vaata teisi trendikaid teemaviiteid post_action: Postita post_step: Tervita maailma teksti, fotode, videote või küsitlustega. post_title: Tee oma esimene postitus diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index d1daf27e55c5be..d7ced9c6f58d0c 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -353,7 +353,9 @@ ca: jurisdiction: Jurisdicció min_age: Edat mínima user: + date_of_birth_1i: Any date_of_birth_2i: Mes + date_of_birth_3i: Dia role: Rol time_zone: Zona horària user_role: diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml index 952b228b8797c2..b442e5add0c9aa 100644 --- a/config/locales/simple_form.da.yml +++ b/config/locales/simple_form.da.yml @@ -5,7 +5,7 @@ da: account: attribution_domains: Ét pr. linje. Beskytter mod falske tilskrivninger. discoverable: Dine offentlige indlæg og profil kan blive fremhævet eller anbefalet i forskellige områder af Mastodon, og profilen kan blive foreslået til andre brugere. - display_name: Dit fulde navn eller dit sjove navn. + display_name: Dit fulde navn eller et kaldenavn. fields: Din hjemmeside, dine pronominer, din alder, eller hvad du har lyst til. indexable: Dine offentlige indlæg vil kunne vises i Mastodon-søgeresultater. Folk, som har interageret med dem, vil kunne finde dem uanset. note: 'Du kan @omtale andre personer eller #hashtags.' diff --git a/config/locales/simple_form.et.yml b/config/locales/simple_form.et.yml index 1a56c1e1a6e4b4..92efc6dbadf70a 100644 --- a/config/locales/simple_form.et.yml +++ b/config/locales/simple_form.et.yml @@ -8,7 +8,7 @@ et: display_name: Su täisnimi või naljanimi. fields: Su koduleht, sugu, vanus. Mistahes, mida soovid. indexable: Sinu avalikud postitused võivad ilmuda Mastodoni otsingutulemustes. Inimesed, kes on sinu postitustele reageerinud, saavad neid otsida nii või naa. - note: 'Saad @mainida teisi inimesi või #silte.' + note: 'Saad @mainida teisi inimesi või #teemaviiteid.' show_collections: Inimesed saavad sirvida su jälgijaid ja jälgitavaid. Inimesed, keda sa jälgid, näevad seda sõltumata häälestuse valikust. unlocked: Teised kasutajad saavad sind jälgima hakata nõusolekut küsimata. Eemalda märge, kui soovid jälgimistaotlusi üle vaadata ja valida, kas nõustuda või keelduda uute jälgijatega. account_alias: @@ -16,7 +16,7 @@ et: account_migration: acct: Sisesta kasutajanimi@domeen, kuhu soovid konto siit kolida account_warning_preset: - text: Saab kasutada postituse süntaksi, näiteks URLe, silte ja mainimisi + text: Saad kasutada postituse süntaksi, näiteks võrguaadresse, teemaviiteid ja mainimisi title: Valikuline. Ei ole nähtav saajale admin_account_action: include_statuses: Kasutaja näeb, millised postitused on põhjustanud moderaatori otsuse või hoiatuse @@ -77,7 +77,7 @@ et: domain: See võib olla e-postiaadressis näha olev domeen või MX-kirje, mida aadress kasutab. Kontroll toimub liitumise käigus. with_dns_records: Püütakse lahendada selle domeeni DNS-kirjed ja ühtlasi blokeerida ka selle tulemused featured_tag: - name: 'Siin on mõned nendest siltidest, mida oled viimati kasutanud:' + name: 'Siin on mõned nendest teemaviiteid, mida oled viimati kasutanud:' filters: action: Vali tegevus, kui postitus vastab filtrile actions: @@ -110,7 +110,7 @@ et: theme: Teema, mida näevad sisenemata ning uued kasutajad. thumbnail: Umbes 2:1 mõõdus pilt serveri informatsiooni kõrval. trendable_by_default: Populaarse sisu ülevaatuse vahele jätmine. Pärast seda on siiski võimalik üksikuid üksusi trendidest eemaldada. - trends: Trendid näitavad, millised postitused, sildid ja uudislood koguvad sinu serveris tähelepanu. + trends: Trendid näitavad, millised postitused, teemaviited ja uudislood koguvad sinu serveris tähelepanu. form_challenge: current_password: Turvalisse alasse sisenemine imports: @@ -272,7 +272,7 @@ et: email_domain_block: with_dns_records: Kaasa domeeni MX-kirjed ning IP-aadressid featured_tag: - name: Silt + name: Teemaviide filters: actions: blur: Peida hoiatusega meedia @@ -353,10 +353,10 @@ et: indexable: Kaasa profiilileht otsimootoritesse show_application: Näita, millisest äpist postituse saatsid tag: - listable: Luba sellel sildil ilmuda profiilide kataloogis - name: Silt - trendable: Luba sellel sildil trendida - usable: Luba seda märksõna postitustes kasutada lokaalselt + listable: Luba sellel teemaviitel ilmuda profiilide kataloogis + name: Teemaviide + trendable: Luba sellel teemaviitel olla nähtav viimaste trendide all + usable: Luba seda teemaviidet postitustes kasutada lokaalselt terms_of_service: changelog: Mis on muutunud? effective_date: Jõustumise kuupäev diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index 0677399d38bd27..8de2c8b04aad19 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -253,7 +253,7 @@ pt-BR: setting_expand_spoilers: Sempre expandir toots com Aviso de Conteúdo setting_hide_network: Ocultar suas relações setting_missing_alt_text_modal: Avise-me antes de publicar mídia sem texto alternado - setting_quick_boosting: Ativar aceleração rápida + setting_quick_boosting: Ativar impulsionamento rápido setting_reduce_motion: Reduzir animações setting_system_font_ui: Usar fonte padrão do sistema setting_system_scrollbars_ui: Usar barra de rolagem padrão do sistema diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index ee86870244bfe4..7dc825d526eec3 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -220,7 +220,9 @@ sl: setting_always_send_emails: Vedno pošlji e-obvestila setting_auto_play_gif: Samodejno predvajanje animiranih GIF-ov setting_default_language: Jezik objavljanja + setting_default_quote_policy: Kdo lahko citira setting_default_sensitive: Vedno označi medije kot občutljive + setting_delete_modal: Pred brisanjem objave me opozori setting_disable_hover_cards: Onemogoči predogled profila pod kazalcem setting_disable_swiping: Onemogoči poteze drsanja setting_display_media: Prikaz medijev @@ -305,6 +307,7 @@ sl: follow_request: Pošlji e-pošto, ko vam nekdo želi slediti mention: Pošlji e-pošto, ko vas nekdo omeni pending_account: Pošlji e-pošto, ko je potreben pregled novega računa + quote: Nekdo vas je citiral reblog: Pošlji e-sporočilo, ko nekdo izpostavi vašo objavo report: Novo poročilo je oddano software_updates: @@ -340,7 +343,9 @@ sl: jurisdiction: Pravna pristojnost min_age: Najmanjša starost user: + date_of_birth_1i: Leto date_of_birth_2i: Mesec + date_of_birth_3i: Dan role: Vloga time_zone: Časovni pas user_role: @@ -349,6 +354,9 @@ sl: name: Ime permissions_as_keys: Pravice position: Prioriteta + username_block: + allow_with_approval: Dovoli registracije z odobritvijo + comparison: Metoda primerjave webhook: events: Omogočeni dogodki template: Predloga obremenitev From 7ffa5fa0c4e0e3de712b2d28c48a904cfec96336 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Fri, 21 Nov 2025 11:28:23 +0100 Subject: [PATCH 005/123] Add models to represent "Collections" (#36977) --- app/models/collection.rb | 53 +++++++++++++++++++ app/models/collection_item.rb | 40 ++++++++++++++ app/models/concerns/account/associations.rb | 2 + config/locales/activerecord.en.yml | 6 +++ .../20251118115657_create_collections.rb | 19 +++++++ .../20251119093332_create_collection_items.rb | 18 +++++++ db/schema.rb | 39 +++++++++++++- spec/fabricators/account_fabricator.rb | 4 ++ spec/fabricators/collection_fabricator.rb | 10 ++++ .../fabricators/collection_item_fabricator.rb | 15 ++++++ spec/models/collection_item_spec.rb | 41 ++++++++++++++ spec/models/collection_spec.rb | 45 ++++++++++++++++ 12 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 app/models/collection.rb create mode 100644 app/models/collection_item.rb create mode 100644 db/migrate/20251118115657_create_collections.rb create mode 100644 db/migrate/20251119093332_create_collection_items.rb create mode 100644 spec/fabricators/collection_fabricator.rb create mode 100644 spec/fabricators/collection_item_fabricator.rb create mode 100644 spec/models/collection_item_spec.rb create mode 100644 spec/models/collection_spec.rb diff --git a/app/models/collection.rb b/app/models/collection.rb new file mode 100644 index 00000000000000..320933ea606b5d --- /dev/null +++ b/app/models/collection.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: collections +# +# id :bigint(8) not null, primary key +# description :text not null +# discoverable :boolean not null +# local :boolean not null +# name :string not null +# original_number_of_items :integer +# sensitive :boolean not null +# uri :string +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) not null +# tag_id :bigint(8) +# +class Collection < ApplicationRecord + MAX_ITEMS = 25 + + belongs_to :account + belongs_to :tag, optional: true + + has_many :collection_items, dependent: :delete_all + + validates :name, presence: true + validates :description, presence: true + validates :uri, presence: true, if: :remote? + validates :original_number_of_items, + presence: true, + numericality: { greater_than_or_equal: 0 }, + if: :remote? + validate :tag_is_usable + validate :items_do_not_exceed_limit + + def remote? + !local? + end + + private + + def tag_is_usable + return if tag.blank? + + errors.add(:tag, :unusable) unless tag.usable? + end + + def items_do_not_exceed_limit + errors.add(:collection_items, :too_many, count: MAX_ITEMS) if collection_items.size > MAX_ITEMS + end +end diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb new file mode 100644 index 00000000000000..0ea50e6914282c --- /dev/null +++ b/app/models/collection_item.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: collection_items +# +# id :bigint(8) not null, primary key +# activity_uri :string +# approval_last_verified_at :datetime +# approval_uri :string +# object_uri :string +# position :integer default(1), not null +# state :integer default("pending"), not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) +# collection_id :bigint(8) not null +# +class CollectionItem < ApplicationRecord + belongs_to :collection + belongs_to :account, optional: true + + enum :state, + { pending: 0, accepted: 1, rejected: 2, revoked: 3 }, + validate: true + + delegate :local?, :remote?, to: :collection + + validates :position, numericality: { only_integer: true, greater_than: 0 } + validates :activity_uri, presence: true, if: :local_item_with_remote_account? + validates :approval_uri, absence: true, unless: :local? + validates :account, presence: true, if: :accepted? + validates :object_uri, presence: true, if: -> { account.nil? } + + scope :ordered, -> { order(position: :asc) } + + def local_item_with_remote_account? + local? && account&.remote? + end +end diff --git a/app/models/concerns/account/associations.rb b/app/models/concerns/account/associations.rb index 62c55da5de1adc..e1684d256070d5 100644 --- a/app/models/concerns/account/associations.rb +++ b/app/models/concerns/account/associations.rb @@ -13,6 +13,8 @@ module Account::Associations has_many :account_warnings has_many :aliases, class_name: 'AccountAlias' has_many :bookmarks + has_many :collections + has_many :collection_items has_many :conversations, class_name: 'AccountConversation' has_many :custom_filters has_many :favourites diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 6940d589cab995..fb5ccce89e2065 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -32,6 +32,12 @@ en: attributes: url: invalid: is not a valid URL + collection: + attributes: + collection_items: + too_many: are too many, no more than %{count} are allowed + tag: + unusable: may not be used doorkeeper/application: attributes: website: diff --git a/db/migrate/20251118115657_create_collections.rb b/db/migrate/20251118115657_create_collections.rb new file mode 100644 index 00000000000000..299cc7aade6c38 --- /dev/null +++ b/db/migrate/20251118115657_create_collections.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateCollections < ActiveRecord::Migration[8.0] + def change + create_table :collections do |t| + t.references :account, null: false, foreign_key: true + t.string :name, null: false + t.text :description, null: false + t.string :uri + t.boolean :local, null: false # rubocop:disable Rails/ThreeStateBooleanColumn + t.boolean :sensitive, null: false # rubocop:disable Rails/ThreeStateBooleanColumn + t.boolean :discoverable, null: false # rubocop:disable Rails/ThreeStateBooleanColumn + t.references :tag, foreign_key: true + t.integer :original_number_of_items + + t.timestamps + end + end +end diff --git a/db/migrate/20251119093332_create_collection_items.rb b/db/migrate/20251119093332_create_collection_items.rb new file mode 100644 index 00000000000000..9fc5d99df53c2f --- /dev/null +++ b/db/migrate/20251119093332_create_collection_items.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateCollectionItems < ActiveRecord::Migration[8.0] + def change + create_table :collection_items do |t| + t.references :collection, null: false, foreign_key: { on_delete: :cascade } + t.references :account, foreign_key: true + t.integer :position, null: false, default: 1 + t.string :object_uri, index: { unique: true, where: 'activity_uri IS NOT NULL' } + t.string :approval_uri, index: { unique: true, where: 'approval_uri IS NOT NULL' } + t.string :activity_uri + t.datetime :approval_last_verified_at + t.integer :state, null: false, default: 0 + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 7bdd6c0ce4000e..b8362672916e1d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_10_23_210145) do +ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -351,6 +351,39 @@ t.index ["reference_account_id"], name: "index_canonical_email_blocks_on_reference_account_id" end + create_table "collection_items", force: :cascade do |t| + t.bigint "collection_id", null: false + t.bigint "account_id" + t.integer "position", default: 1, null: false + t.string "object_uri" + t.string "approval_uri" + t.string "activity_uri" + t.datetime "approval_last_verified_at" + t.integer "state", default: 0, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_collection_items_on_account_id" + t.index ["approval_uri"], name: "index_collection_items_on_approval_uri", unique: true, where: "(approval_uri IS NOT NULL)" + t.index ["collection_id"], name: "index_collection_items_on_collection_id" + t.index ["object_uri"], name: "index_collection_items_on_object_uri", unique: true, where: "(activity_uri IS NOT NULL)" + end + + create_table "collections", force: :cascade do |t| + t.bigint "account_id", null: false + t.string "name", null: false + t.text "description", null: false + t.string "uri" + t.boolean "local", null: false + t.boolean "sensitive", null: false + t.boolean "discoverable", null: false + t.bigint "tag_id" + t.integer "original_number_of_items" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_collections_on_account_id" + t.index ["tag_id"], name: "index_collections_on_tag_id" + end + create_table "conversation_mutes", force: :cascade do |t| t.bigint "conversation_id", null: false t.bigint "account_id", null: false @@ -1386,6 +1419,10 @@ add_foreign_key "bulk_import_rows", "bulk_imports", on_delete: :cascade add_foreign_key "bulk_imports", "accounts", on_delete: :cascade add_foreign_key "canonical_email_blocks", "accounts", column: "reference_account_id", on_delete: :cascade + add_foreign_key "collection_items", "accounts" + add_foreign_key "collection_items", "collections", on_delete: :cascade + add_foreign_key "collections", "accounts" + add_foreign_key "collections", "tags" add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade add_foreign_key "custom_filter_keywords", "custom_filters", on_delete: :cascade diff --git a/spec/fabricators/account_fabricator.rb b/spec/fabricators/account_fabricator.rb index 6ec89a1cb651b9..bce8803be75094 100644 --- a/spec/fabricators/account_fabricator.rb +++ b/spec/fabricators/account_fabricator.rb @@ -17,3 +17,7 @@ discoverable true indexable true end + +Fabricator(:remote_account, from: :account) do + domain 'example.com' +end diff --git a/spec/fabricators/collection_fabricator.rb b/spec/fabricators/collection_fabricator.rb new file mode 100644 index 00000000000000..a6a8411ba00b67 --- /dev/null +++ b/spec/fabricators/collection_fabricator.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +Fabricator(:collection) do + account { Fabricate.build(:account) } + name { sequence(:name) { |i| "Collection ##{i}" } } + description 'People to follow' + local true + sensitive false + discoverable true +end diff --git a/spec/fabricators/collection_item_fabricator.rb b/spec/fabricators/collection_item_fabricator.rb new file mode 100644 index 00000000000000..011f9ba5b5ee39 --- /dev/null +++ b/spec/fabricators/collection_item_fabricator.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +Fabricator(:collection_item) do + collection { Fabricate.build(:collection) } + account { Fabricate.build(:account) } + position 1 + state :accepted +end + +Fabricator(:unverified_remote_collection_item, from: :collection_item) do + account nil + state :pending + object_uri { Fabricate.build(:remote_account).uri } + approval_uri { sequence(:uri) { |i| "https://example.com/authorizations/#{i}" } } +end diff --git a/spec/models/collection_item_spec.rb b/spec/models/collection_item_spec.rb new file mode 100644 index 00000000000000..39464b7a34013a --- /dev/null +++ b/spec/models/collection_item_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe CollectionItem do + describe 'Validations' do + subject { Fabricate.build(:collection_item) } + + it { is_expected.to define_enum_for(:state) } + + it { is_expected.to validate_numericality_of(:position).only_integer.is_greater_than(0) } + + context 'when account inclusion is accepted' do + subject { Fabricate.build(:collection_item, state: :accepted) } + + it { is_expected.to validate_presence_of(:account) } + end + + context 'when item is local and account is remote' do + subject { Fabricate.build(:collection_item, account: remote_account) } + + let(:remote_account) { Fabricate.build(:remote_account) } + + it { is_expected.to validate_presence_of(:activity_uri) } + end + + context 'when item is not local' do + subject { Fabricate.build(:collection_item, collection: remote_collection) } + + let(:remote_collection) { Fabricate.build(:collection, local: false) } + + it { is_expected.to validate_absence_of(:approval_uri) } + end + + context 'when account is not present' do + subject { Fabricate.build(:unverified_remote_collection_item) } + + it { is_expected.to validate_presence_of(:object_uri) } + end + end +end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb new file mode 100644 index 00000000000000..c6a500210f62ad --- /dev/null +++ b/spec/models/collection_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Collection do + describe 'Validations' do + subject { Fabricate.build :collection } + + it { is_expected.to validate_presence_of(:name) } + + it { is_expected.to validate_presence_of(:description) } + + context 'when collection is remote' do + subject { Fabricate.build :collection, local: false } + + it { is_expected.to validate_presence_of(:uri) } + + it { is_expected.to validate_presence_of(:original_number_of_items) } + end + + context 'when using a hashtag as category' do + subject { Fabricate.build(:collection, tag:) } + + context 'when hashtag is usable' do + let(:tag) { Fabricate.build(:tag) } + + it { is_expected.to be_valid } + end + + context 'when hashtag is not usable' do + let(:tag) { Fabricate.build(:tag, usable: false) } + + it { is_expected.to_not be_valid } + end + end + + context 'when there are more items than allowed' do + subject { Fabricate.build(:collection, collection_items:) } + + let(:collection_items) { Fabricate.build_times(described_class::MAX_ITEMS + 1, :collection_item, collection: nil) } + + it { is_expected.to_not be_valid } + end + end +end From 687f3a2a01f3d742e5023dbd45b19e89d21582b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:00:29 +0100 Subject: [PATCH 006/123] chore(deps): update dependency vite to v7.2.4 (#36964) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 838a26fe96a4b2..a7a34cd7cfa4a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13988,8 +13988,8 @@ __metadata: linkType: hard "vite@npm:^6.0.0 || ^7.0.0, vite@npm:^7.1.1": - version: 7.2.2 - resolution: "vite@npm:7.2.2" + version: 7.2.4 + resolution: "vite@npm:7.2.4" dependencies: esbuild: "npm:^0.25.0" fdir: "npm:^6.5.0" @@ -14038,7 +14038,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/9c76ee441f8dbec645ddaecc28d1f9cf35670ffa91cff69af7b1d5081545331603f0b1289d437b2fa8dc43cdc77b4d96b5bd9c9aed66310f490cb1a06f9c814c + checksum: 10c0/26aa0cad01d6e00f17c837b2a0587ab52f6bd0d0e64606b4220cfc58fa5fa76a4095ef3ea27c886bea542a346363912c4fad9f9462ef1e6757262fedfd5196b2 languageName: node linkType: hard From 48fe679728d39ca224b3cb1e7d1c32fdccd00e64 Mon Sep 17 00:00:00 2001 From: Shugo Maeda Date: Fri, 21 Nov 2025 22:27:04 +0900 Subject: [PATCH 007/123] Separate remote thumbnails into cache/ directory (#36911) --- app/models/media_attachment.rb | 47 ++++++++++--------- ...14_add_thumbnail_storage_schema_version.rb | 7 +++ db/schema.rb | 1 + 3 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 db/migrate/20251117023614_add_thumbnail_storage_schema_version.rb diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 13ca0d7e3ab7f1..2615eed4e39adf 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -4,29 +4,30 @@ # # Table name: media_attachments # -# id :bigint(8) not null, primary key -# status_id :bigint(8) -# file_file_name :string -# file_content_type :string -# file_file_size :integer -# file_updated_at :datetime -# remote_url :string default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# shortcode :string -# type :integer default("image"), not null -# file_meta :json -# account_id :bigint(8) -# description :text -# scheduled_status_id :bigint(8) -# blurhash :string -# processing :integer -# file_storage_schema_version :integer -# thumbnail_file_name :string -# thumbnail_content_type :string -# thumbnail_file_size :integer -# thumbnail_updated_at :datetime -# thumbnail_remote_url :string +# id :bigint(8) not null, primary key +# blurhash :string +# description :text +# file_content_type :string +# file_file_name :string +# file_file_size :integer +# file_meta :json +# file_storage_schema_version :integer +# file_updated_at :datetime +# processing :integer +# remote_url :string default(""), not null +# shortcode :string +# thumbnail_content_type :string +# thumbnail_file_name :string +# thumbnail_file_size :integer +# thumbnail_remote_url :string +# thumbnail_storage_schema_version :integer +# thumbnail_updated_at :datetime +# type :integer default("image"), not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) +# scheduled_status_id :bigint(8) +# status_id :bigint(8) # class MediaAttachment < ApplicationRecord diff --git a/db/migrate/20251117023614_add_thumbnail_storage_schema_version.rb b/db/migrate/20251117023614_add_thumbnail_storage_schema_version.rb new file mode 100644 index 00000000000000..0a8119642d4ba7 --- /dev/null +++ b/db/migrate/20251117023614_add_thumbnail_storage_schema_version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddThumbnailStorageSchemaVersion < ActiveRecord::Migration[8.0] + def change + add_column :media_attachments, :thumbnail_storage_schema_version, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index b8362672916e1d..e4e7db3868ca33 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -727,6 +727,7 @@ t.integer "thumbnail_file_size" t.datetime "thumbnail_updated_at", precision: nil t.string "thumbnail_remote_url" + t.integer "thumbnail_storage_schema_version" t.index ["account_id", "status_id"], name: "index_media_attachments_on_account_id_and_status_id", order: { status_id: :desc } t.index ["scheduled_status_id"], name: "index_media_attachments_on_scheduled_status_id", where: "(scheduled_status_id IS NOT NULL)" t.index ["shortcode"], name: "index_media_attachments_on_shortcode", unique: true, opclass: :text_pattern_ops, where: "(shortcode IS NOT NULL)" From 8a235dd1873be73a3a4cbf607fcdc152e0150e35 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 21 Nov 2025 14:33:27 +0100 Subject: [PATCH 008/123] Refactor PrivacyDropdown to TypeScript (#36979) --- .../compose/components/privacy_dropdown.jsx | 158 -------------- .../compose/components/privacy_dropdown.tsx | 199 ++++++++++++++++++ .../containers/privacy_dropdown_container.js | 19 -- .../features/ui/components/boost_modal.tsx | 5 +- 4 files changed, 203 insertions(+), 178 deletions(-) delete mode 100644 app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx create mode 100644 app/javascript/mastodon/features/compose/components/privacy_dropdown.tsx delete mode 100644 app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx b/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx deleted file mode 100644 index 1c9434502c01a6..00000000000000 --- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx +++ /dev/null @@ -1,158 +0,0 @@ -import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; - -import { injectIntl, defineMessages } from 'react-intl'; - -import classNames from 'classnames'; - -import Overlay from 'react-overlays/Overlay'; - -import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; -import LockIcon from '@/material-icons/400-24px/lock.svg?react'; -import PublicIcon from '@/material-icons/400-24px/public.svg?react'; -import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react'; -import { DropdownSelector } from 'mastodon/components/dropdown_selector'; -import { Icon } from 'mastodon/components/icon'; - -export const messages = defineMessages({ - public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, - public_long: { id: 'privacy.public.long', defaultMessage: 'Anyone on and off Mastodon' }, - unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Quiet public' }, - unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Hidden from Mastodon search results, trending, and public timelines' }, - private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' }, - private_long: { id: 'privacy.private.long', defaultMessage: 'Only your followers' }, - direct_short: { id: 'privacy.direct.short', defaultMessage: 'Specific people' }, - direct_long: { id: 'privacy.direct.long', defaultMessage: 'Everyone mentioned in the post' }, - change_privacy: { id: 'privacy.change', defaultMessage: 'Change post privacy' }, - unlisted_extra: { id: 'privacy.unlisted.additional', defaultMessage: 'This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.' }, -}); - -class PrivacyDropdown extends PureComponent { - - static propTypes = { - value: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, - noDirect: PropTypes.bool, - container: PropTypes.func, - disabled: PropTypes.bool, - intl: PropTypes.object.isRequired, - }; - - state = { - open: false, - placement: 'bottom', - }; - - handleToggle = () => { - if (this.state.open && this.activeElement) { - this.activeElement.focus({ preventScroll: true }); - } - - this.setState({ open: !this.state.open }); - }; - - handleKeyDown = e => { - switch(e.key) { - case 'Escape': - this.handleClose(); - break; - } - }; - - handleMouseDown = () => { - if (!this.state.open) { - this.activeElement = document.activeElement; - } - }; - - handleButtonKeyDown = (e) => { - switch(e.key) { - case ' ': - case 'Enter': - this.handleMouseDown(); - break; - } - }; - - handleClose = () => { - if (this.state.open && this.activeElement) { - this.activeElement.focus({ preventScroll: true }); - } - this.setState({ open: false }); - }; - - handleChange = value => { - this.props.onChange(value); - }; - - UNSAFE_componentWillMount () { - const { intl: { formatMessage } } = this.props; - - this.options = [ - { icon: 'globe', iconComponent: PublicIcon, value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) }, - { icon: 'unlock', iconComponent: QuietTimeIcon, value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long), extra: formatMessage(messages.unlisted_extra) }, - { icon: 'lock', iconComponent: LockIcon, value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, - ]; - - if (!this.props.noDirect) { - this.options.push( - { icon: 'at', iconComponent: AlternateEmailIcon, value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) }, - ); - } - } - - setTargetRef = c => { - this.target = c; - }; - - findTarget = () => { - return this.target; - }; - - handleOverlayEnter = (state) => { - this.setState({ placement: state.placement }); - }; - - render () { - const { value, container, disabled, intl } = this.props; - const { open, placement } = this.state; - - const valueOption = this.options.find(item => item.value === value); - - return ( -
- - - - {({ props, placement }) => ( -
-
- -
-
- )} -
-
- ); - } - -} - -export default injectIntl(PrivacyDropdown); diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.tsx b/app/javascript/mastodon/features/compose/components/privacy_dropdown.tsx new file mode 100644 index 00000000000000..5a463d86e79a53 --- /dev/null +++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.tsx @@ -0,0 +1,199 @@ +import { useCallback, useRef, useState } from 'react'; + +import { defineMessages, useIntl } from 'react-intl'; + +import classNames from 'classnames'; + +import type { OverlayProps } from 'react-overlays/Overlay'; +import Overlay from 'react-overlays/Overlay'; + +import type { StatusVisibility } from '@/mastodon/api_types/statuses'; +import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; +import LockIcon from '@/material-icons/400-24px/lock.svg?react'; +import PublicIcon from '@/material-icons/400-24px/public.svg?react'; +import QuietTimeIcon from '@/material-icons/400-24px/quiet_time.svg?react'; +import { DropdownSelector } from 'mastodon/components/dropdown_selector'; +import { Icon } from 'mastodon/components/icon'; + +export const messages = defineMessages({ + public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, + public_long: { + id: 'privacy.public.long', + defaultMessage: 'Anyone on and off Mastodon', + }, + unlisted_short: { + id: 'privacy.unlisted.short', + defaultMessage: 'Quiet public', + }, + unlisted_long: { + id: 'privacy.unlisted.long', + defaultMessage: + 'Hidden from Mastodon search results, trending, and public timelines', + }, + private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' }, + private_long: { + id: 'privacy.private.long', + defaultMessage: 'Only your followers', + }, + direct_short: { + id: 'privacy.direct.short', + defaultMessage: 'Specific people', + }, + direct_long: { + id: 'privacy.direct.long', + defaultMessage: 'Everyone mentioned in the post', + }, + change_privacy: { + id: 'privacy.change', + defaultMessage: 'Change post privacy', + }, + unlisted_extra: { + id: 'privacy.unlisted.additional', + defaultMessage: + 'This behaves exactly like public, except the post will not appear in live feeds or hashtags, explore, or Mastodon search, even if you are opted-in account-wide.', + }, +}); + +interface PrivacyDropdownProps { + value: StatusVisibility; + onChange: (value: StatusVisibility) => void; + noDirect?: boolean; + container?: OverlayProps['container']; + disabled?: boolean; +} + +const PrivacyDropdown: React.FC = ({ + value, + onChange, + noDirect, + container, + disabled, +}) => { + const intl = useIntl(); + const overlayTargetRef = useRef(null); + const previousFocusTargetRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + + const handleClose = useCallback(() => { + if (isOpen && previousFocusTargetRef.current) { + previousFocusTargetRef.current.focus({ preventScroll: true }); + } + setIsOpen(false); + }, [isOpen]); + + const handleToggle = useCallback(() => { + if (isOpen) { + handleClose(); + } + setIsOpen((prev) => !prev); + }, [handleClose, isOpen]); + + const registerPreviousFocusTarget = useCallback(() => { + if (!isOpen) { + previousFocusTargetRef.current = document.activeElement as HTMLElement; + } + }, [isOpen]); + + const handleButtonKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if ([' ', 'Enter'].includes(e.key)) { + registerPreviousFocusTarget(); + } + }, + [registerPreviousFocusTarget], + ); + + const options = [ + { + icon: 'globe', + iconComponent: PublicIcon, + value: 'public', + text: intl.formatMessage(messages.public_short), + meta: intl.formatMessage(messages.public_long), + }, + { + icon: 'unlock', + iconComponent: QuietTimeIcon, + value: 'unlisted', + text: intl.formatMessage(messages.unlisted_short), + meta: intl.formatMessage(messages.unlisted_long), + extra: intl.formatMessage(messages.unlisted_extra), + }, + { + icon: 'lock', + iconComponent: LockIcon, + value: 'private', + text: intl.formatMessage(messages.private_short), + meta: intl.formatMessage(messages.private_long), + }, + ]; + + if (!noDirect) { + options.push({ + icon: 'at', + iconComponent: AlternateEmailIcon, + value: 'direct', + text: intl.formatMessage(messages.direct_short), + meta: intl.formatMessage(messages.direct_long), + }); + } + + const selectedOption = + options.find((item) => item.value === value) ?? options.at(0); + + return ( +
+ + + + {({ props, placement }) => ( +
+
+ +
+
+ )} +
+
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export default PrivacyDropdown; diff --git a/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js b/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js deleted file mode 100644 index 803dcb1a4a08f6..00000000000000 --- a/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js +++ /dev/null @@ -1,19 +0,0 @@ -import { connect } from 'react-redux'; - -import { changeComposeVisibility } from '@/mastodon/actions/compose_typed'; - -import PrivacyDropdown from '../components/privacy_dropdown'; - -const mapStateToProps = state => ({ - value: state.getIn(['compose', 'privacy']), -}); - -const mapDispatchToProps = dispatch => ({ - - onChange (value) { - dispatch(changeComposeVisibility(value)); - }, - -}); - -export default connect(mapStateToProps, mapDispatchToProps)(PrivacyDropdown); diff --git a/app/javascript/mastodon/features/ui/components/boost_modal.tsx b/app/javascript/mastodon/features/ui/components/boost_modal.tsx index a55141167b07d2..7e45f5749177dd 100644 --- a/app/javascript/mastodon/features/ui/components/boost_modal.tsx +++ b/app/javascript/mastodon/features/ui/components/boost_modal.tsx @@ -52,7 +52,10 @@ export const BoostModal: React.FC<{ }, [onClose]); const findContainer = useCallback( - () => document.getElementsByClassName('modal-root__container')[0], + () => + document.getElementsByClassName( + 'modal-root__container', + )[0] as HTMLDivElement, [], ); From ad7839e5512fbc5f089c1949e9986a16435f4350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Fri, 21 Nov 2025 13:56:20 +0000 Subject: [PATCH 009/123] Fix the translation of "Latvian" (#36876) --- app/helpers/languages_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb index ce88ebb5a3c419..dbf56f45a04ab6 100644 --- a/app/helpers/languages_helper.rb +++ b/app/helpers/languages_helper.rb @@ -100,7 +100,7 @@ module LanguagesHelper lo: ['Lao', 'ລາວ'].freeze, lt: ['Lithuanian', 'lietuvių kalba'].freeze, lu: ['Luba-Katanga', 'Tshiluba'].freeze, - lv: ['Latvian', 'latviešu valoda'].freeze, + lv: ['Latvian', 'Latviski'].freeze, mg: ['Malagasy', 'fiteny malagasy'].freeze, mh: ['Marshallese', 'Kajin M̧ajeļ'].freeze, mi: ['Māori', 'te reo Māori'].freeze, From d967137adf260f2f8cc14e7c11782f25c924c240 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 21 Nov 2025 09:29:03 -0500 Subject: [PATCH 010/123] Remove unneeded type check on Status in `og_image` partial (#36980) --- app/views/statuses/_og_image.html.haml | 4 ++-- app/views/statuses/show.html.haml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/statuses/_og_image.html.haml b/app/views/statuses/_og_image.html.haml index 1ae97adff67bb7..1f7f57f1562603 100644 --- a/app/views/statuses/_og_image.html.haml +++ b/app/views/statuses/_og_image.html.haml @@ -1,6 +1,6 @@ -- if activity.is_a?(Status) && (activity.non_sensitive_with_media? || (activity.with_media? && Setting.preview_sensitive_media)) +- if status.non_sensitive_with_media? || (status.with_media? && Setting.preview_sensitive_media) - player_card = false - - activity.ordered_media_attachments.each do |media| + - status.ordered_media_attachments.each do |media| - if media.image? = opengraph 'og:image', full_asset_url(media.file.url(:original)) = opengraph 'og:image:type', media.file_content_type diff --git a/app/views/statuses/show.html.haml b/app/views/statuses/show.html.haml index ca2628bbcda73f..7478562ea2e98e 100644 --- a/app/views/statuses/show.html.haml +++ b/app/views/statuses/show.html.haml @@ -20,6 +20,6 @@ = opengraph 'profile:username', acct(@account)[1..] = render 'og_description', activity: @status - = render 'og_image', activity: @status, account: @account + = render 'og_image', status: @status, account: @account = render 'shared/web_app' From 585545d0d5678a6ea4b958af4a3bc6593134d0f6 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 21 Nov 2025 09:46:29 -0500 Subject: [PATCH 011/123] Add coverage for `media#player` scenarios (#35947) --- spec/requests/media_spec.rb | 13 +++++++++++ spec/system/media_spec.rb | 44 ++++++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/spec/requests/media_spec.rb b/spec/requests/media_spec.rb index a448a87492e972..523c4689d6a5c5 100644 --- a/spec/requests/media_spec.rb +++ b/spec/requests/media_spec.rb @@ -87,4 +87,17 @@ end end end + + describe 'GET /media/:medium_id/player' do + context 'when media type is not large format type' do + let(:media) { Fabricate :media_attachment } + + it 'responds with not found' do + get medium_player_path(media) + + expect(response) + .to have_http_status(404) + end + end + end end diff --git a/spec/system/media_spec.rb b/spec/system/media_spec.rb index d014c7e88ef96d..ec069cdcaa9ee3 100644 --- a/spec/system/media_spec.rb +++ b/spec/system/media_spec.rb @@ -4,19 +4,47 @@ RSpec.describe 'Media' do describe 'Player page' do + let(:status) { Fabricate :status } + + before { status.media_attachments << media } + context 'when signed in' do before { sign_in Fabricate(:user) } - it 'visits the media player page and renders the media' do - status = Fabricate :status - media = Fabricate :media_attachment, type: :video - status.media_attachments << media + context 'when media type is video' do + let(:media) { Fabricate :media_attachment, type: :video } + + it 'visits the player page and renders media' do + visit medium_player_path(media) + + expect(page) + .to have_css('body', class: 'player') + .and have_css('div[data-component="Video"] video[controls="controls"] source') + end + end + + context 'when media type is gifv' do + let(:media) { Fabricate :media_attachment, type: :gifv } + + it 'visits the player page and renders media' do + visit medium_player_path(media) + + expect(page) + .to have_css('body', class: 'player') + .and have_css('div[data-component="MediaGallery"] video[loop="loop"] source') + end + end + + context 'when media type is audio' do + let(:media) { Fabricate :media_attachment, type: :audio } - visit medium_player_path(media) + it 'visits the player page and renders media' do + visit medium_player_path(media) - expect(page) - .to have_css('body', class: 'player') - .and have_css('div[data-component="Video"]') + expect(page) + .to have_css('body', class: 'player') + .and have_css('div[data-component="Audio"] audio source') + end end end end From f1bf6e63443d845779c5cabb8b40e7f8649dc4ee Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 24 Nov 2025 02:57:46 -0500 Subject: [PATCH 012/123] Remove unused `from_limited?` method from NotifyService (#36988) --- app/services/notify_service.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index fa2c728e579aef..8d0986adcb93f3 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -61,10 +61,6 @@ def override_for_sender? NotificationPermission.exists?(account: @recipient, from_account: @sender) end - def from_limited? - @sender.silenced? && not_following? - end - def message? @notification.type == :mention end From e44a9c08791d0965e8c07119336d586ea1e19f8a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:08:01 +0100 Subject: [PATCH 013/123] New Crowdin Translations (automated) (#36984) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/be.json | 1 + app/javascript/mastodon/locales/da.json | 22 +++---- app/javascript/mastodon/locales/de.json | 6 +- app/javascript/mastodon/locales/el.json | 4 +- app/javascript/mastodon/locales/es-AR.json | 2 +- app/javascript/mastodon/locales/fo.json | 1 + app/javascript/mastodon/locales/gl.json | 4 +- app/javascript/mastodon/locales/nan.json | 1 + app/javascript/mastodon/locales/ru.json | 2 + app/javascript/mastodon/locales/sk.json | 4 ++ app/javascript/mastodon/locales/sl.json | 67 +++++++++++++++++++++- app/javascript/mastodon/locales/sq.json | 1 + app/javascript/mastodon/locales/sv.json | 24 +++++++- config/locales/activerecord.be.yml | 6 ++ config/locales/activerecord.cs.yml | 6 ++ config/locales/activerecord.cy.yml | 6 ++ config/locales/activerecord.da.yml | 6 ++ config/locales/activerecord.de.yml | 6 ++ config/locales/activerecord.el.yml | 6 ++ config/locales/activerecord.eo.yml | 6 ++ config/locales/activerecord.es-AR.yml | 6 ++ config/locales/activerecord.es-MX.yml | 6 ++ config/locales/activerecord.es.yml | 6 ++ config/locales/activerecord.et.yml | 6 ++ config/locales/activerecord.fo.yml | 6 ++ config/locales/activerecord.ga.yml | 6 ++ config/locales/activerecord.gl.yml | 6 ++ config/locales/activerecord.he.yml | 6 ++ config/locales/activerecord.hu.yml | 6 ++ config/locales/activerecord.is.yml | 6 ++ config/locales/activerecord.pt-BR.yml | 6 ++ config/locales/activerecord.pt-PT.yml | 6 ++ config/locales/activerecord.ru.yml | 6 ++ config/locales/activerecord.sq.yml | 6 ++ config/locales/activerecord.sv.yml | 6 ++ config/locales/activerecord.vi.yml | 6 ++ config/locales/activerecord.zh-TW.yml | 6 ++ config/locales/da.yml | 10 ++-- config/locales/de.yml | 2 +- config/locales/devise.es-MX.yml | 2 +- config/locales/devise.ru.yml | 1 + config/locales/devise.sl.yml | 1 + config/locales/el.yml | 22 +++---- config/locales/es-MX.yml | 16 +++--- config/locales/nan.yml | 56 ++++++++++++++++++ config/locales/simple_form.bg.yml | 5 ++ config/locales/simple_form.el.yml | 8 +-- config/locales/simple_form.es-MX.yml | 4 +- config/locales/simple_form.ru.yml | 1 + config/locales/simple_form.sl.yml | 2 + config/locales/simple_form.sv.yml | 1 + config/locales/sl.yml | 67 ++++++++++++++++++++++ 52 files changed, 427 insertions(+), 54 deletions(-) diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 1d8705234ece86..1740987f9033bd 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Чысціня! Тут нічога няма. Калі Вы будзеце атрымліваць новыя апавяшчэнні, яны будуць з'яўляцца тут у адпаведнасці з Вашымі наладамі.", "empty_column.notifications": "У Вас няма ніякіх апавяшчэнняў. Калі іншыя людзі захочуць узаемадзейнічаць з Вамі, Вы ўбачыце гэта тут.", "empty_column.public": "Тут нічога няма! Апублікуйце што-небудзь, або падпішыцеся на карыстальнікаў з другіх сервераў", + "error.no_hashtag_feed_access": "Далучайцеся або ўвайдзіце, каб праглядзець ці падпісацца на гэты хэштэг.", "error.unexpected_crash.explanation": "Гэта старонка не можа быць адлюстравана карэктна з-за памылкі ў нашым кодзе, або праблемы з сумяшчальнасцю браўзера.", "error.unexpected_crash.explanation_addons": "Гэтая старонка не можа быць адлюстравана карэктна. Верагодна, гэтая памылка выкліканая дадатковым кампанентам браўзера або інструментамі аўтаматычнага перакладу.", "error.unexpected_crash.next_steps": "Паспрабуйце абнавіць старонку. Калі гэта не дапаможа, Вы можаце паспрабаваць іншы браўзер, альбо выкарыстаць усталяваную праграму.", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 8bd2fdc8f9ae4a..0af26cf6c29e57 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -15,7 +15,7 @@ "about.rules": "Serverregler", "account.account_note_header": "Personligt notat", "account.add_or_remove_from_list": "Tilføj eller fjern fra lister", - "account.badges.bot": "Bot", + "account.badges.bot": "Automatisert", "account.badges.group": "Gruppe", "account.block": "Blokér @{name}", "account.block_domain": "Blokér domænet {domain}", @@ -28,7 +28,7 @@ "account.disable_notifications": "Giv mig ikke længere en notifikation, når @{name} laver indlæg", "account.domain_blocking": "Blokerer domæne", "account.edit_profile": "Redigér profil", - "account.edit_profile_short": "Redigér", + "account.edit_profile_short": "Rediger", "account.enable_notifications": "Giv mig besked, når @{name} laver indlæg", "account.endorse": "Fremhæv på profil", "account.familiar_followers_many": "Følges af {name1}, {name2} og {othersCount, plural, one {# mere, man kender} other {# mere, du kender}}", @@ -123,14 +123,14 @@ "annual_report.summary.here_it_is": "Her er dit {year} i sammendrag:", "annual_report.summary.highlighted_post.by_favourites": "mest favoritmarkerede indlæg", "annual_report.summary.highlighted_post.by_reblogs": "mest fremhævede indlæg", - "annual_report.summary.highlighted_post.by_replies": "mest besvarede indlæg", + "annual_report.summary.highlighted_post.by_replies": "indlæg med flest svar", "annual_report.summary.highlighted_post.possessive": "{name}s", "annual_report.summary.most_used_app.most_used_app": "mest benyttede app", "annual_report.summary.most_used_hashtag.most_used_hashtag": "mest benyttede hashtag", "annual_report.summary.most_used_hashtag.none": "Intet", "annual_report.summary.new_posts.new_posts": "nye indlæg", "annual_report.summary.percentile.text": "Dermed er du i topaf {domain}-brugere.", - "annual_report.summary.percentile.we_wont_tell_bernie": "Vi fortæller det ikke til Pernille Skipper.", + "annual_report.summary.percentile.we_wont_tell_bernie": "Vi fortæller det ikke til nogen.", "annual_report.summary.thanks": "Tak for at være en del af Mastodon!", "attachments_list.unprocessed": "(ubehandlet)", "audio.hide": "Skjul lyd", @@ -144,7 +144,7 @@ "block_modal.you_wont_see_mentions": "Du vil ikke se indlæg, som omtaler vedkommende.", "boost_modal.combo": "Du kan trykke {combo} for at springe dette over næste gang", "boost_modal.reblog": "Fremhæv indlæg?", - "boost_modal.undo_reblog": "Fjern fremhævning af indlæg?", + "boost_modal.undo_reblog": "Fjern fremhævelse af indlæg?", "bundle_column_error.copy_stacktrace": "Kopiér fejlrapport", "bundle_column_error.error.body": "Den anmodede side kunne ikke gengives. Dette kan skyldes flere typer fejl.", "bundle_column_error.error.title": "Åh nej!", @@ -152,14 +152,14 @@ "bundle_column_error.network.title": "Netværksfejl", "bundle_column_error.retry": "Forsøg igen", "bundle_column_error.return": "Tilbage til hjem", - "bundle_column_error.routing.body": "Den anmodede side kunne ikke findes. Er du sikker på, at URL'en er korrekt?", + "bundle_column_error.routing.body": "Den ønskede side kunne ikke findes. Er du sikker på, at URL'en i adresselinjen er korrekt?", "bundle_column_error.routing.title": "404", "bundle_modal_error.close": "Luk", "bundle_modal_error.message": "Noget gik galt under indlæsningen af denne skærm.", "bundle_modal_error.retry": "Forsøg igen", "carousel.current": "Dias {current, number} / {max, number}", "carousel.slide": "Dias {current, number} af {max, number}", - "closed_registrations.other_server_instructions": "Da Mastodon er decentraliseret, kan du oprette en konto på en anden server og stadig interagere med denne.", + "closed_registrations.other_server_instructions": "Eftersom Mastodon er decentraliseret, kan du oprette en konto på en anden server og stadig interagere med denne.", "closed_registrations_modal.description": "Oprettelse af en konto på {domain} er i øjeblikket ikke muligt, men husk på, at du ikke behøver en konto specifikt på {domain} for at bruge Mastodon.", "closed_registrations_modal.find_another_server": "Find en anden server", "closed_registrations_modal.preamble": "Mastodon er decentraliseret, så uanset hvor du opretter din konto, vil du være i stand til at følge og interagere med hvem som helst på denne server. Du kan endda selv være vært for den!", @@ -170,7 +170,7 @@ "column.community": "Lokal tidslinje", "column.create_list": "Opret liste", "column.direct": "Private omtaler", - "column.directory": "Tjek profiler", + "column.directory": "Gennemse profiler", "column.domain_blocks": "Blokerede domæner", "column.edit_list": "Redigér liste", "column.favourites": "Favoritter", @@ -275,7 +275,7 @@ "confirmations.withdraw_request.title": "Annullér anmodning om at følge {name}?", "content_warning.hide": "Skjul indlæg", "content_warning.show": "Vis alligevel", - "content_warning.show_more": "Vis flere", + "content_warning.show_more": "Vis mere", "conversation.delete": "Slet samtale", "conversation.mark_as_read": "Markér som læst", "conversation.open": "Vis samtale", @@ -327,7 +327,7 @@ "emoji_button.not_found": "Ingen matchende emojis fundet", "emoji_button.objects": "Objekter", "emoji_button.people": "Personer", - "emoji_button.recent": "Oftest brugt", + "emoji_button.recent": "Ofte brugt", "emoji_button.search": "Søg...", "emoji_button.search_results": "Søgeresultater", "emoji_button.symbols": "Symboler", @@ -339,7 +339,7 @@ "empty_column.account_suspended": "Konto suspenderet", "empty_column.account_timeline": "Ingen indlæg her!", "empty_column.account_unavailable": "Profil utilgængelig", - "empty_column.blocks": "Ingen brugere blokeret endnu.", + "empty_column.blocks": "Du har ikke blokeret nogle brugere endnu.", "empty_column.bookmarked_statuses": "Du har ingen bogmærkede indlæg endnu. Når du bogmærker ét, vil det dukke op hér.", "empty_column.community": "Den lokale tidslinje er tom. Skriv noget offentligt for at sætte tingene i gang!", "empty_column.direct": "Du har ikke nogen private omtaler endnu. Når du sender eller modtager en, vil den blive vist her.", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index d7328b6d612d1b..28cfdd26bd4942 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -927,8 +927,8 @@ "status.quote_error.limited_account_hint.title": "Dieses Profil wurde von den Moderator*innen von {domain} ausgeblendet.", "status.quote_error.muted_account_hint.title": "Dieser Beitrag wurde ausgeblendet, weil du @{name} stummgeschaltet hast.", "status.quote_error.not_available": "Beitrag nicht verfügbar", - "status.quote_error.pending_approval": "Beitragsveröffentlichung ausstehend", - "status.quote_error.pending_approval_popout.body": "Auf Mastodon kann festgelegt werden, ob man zitiert werden möchte. Wir warten auf die Genehmigung des ursprünglichen Profils. Bis dahin steht deine Beitragsveröffentlichung noch aus.", + "status.quote_error.pending_approval": "Veröffentlichung ausstehend", + "status.quote_error.pending_approval_popout.body": "Auf Mastodon kannst du selbst bestimmen, ob du von anderen zitiert werden darfst oder nicht – oder nur nach individueller Genehmigung. Wir warten in diesem Fall noch auf die Genehmigung des ursprünglichen Profils. Bis dahin steht die Veröffentlichung deines Beitrags mit dem zitierten Post noch aus.", "status.quote_error.revoked": "Beitrag durch Autor*in entfernt", "status.quote_followers_only": "Nur Follower können diesen Beitrag zitieren", "status.quote_manual_review": "Zitierte*r überprüft manuell", @@ -1032,7 +1032,7 @@ "visibility_modal.privacy_label": "Sichtbarkeit", "visibility_modal.quote_followers": "Nur Follower", "visibility_modal.quote_label": "Wer darf mich zitieren?", - "visibility_modal.quote_nobody": "Nur ich", + "visibility_modal.quote_nobody": "Nur ich selbst", "visibility_modal.quote_public": "Alle", "visibility_modal.save": "Speichern" } diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index ffe2eceacb0bfc..82be328a229938 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -731,7 +731,7 @@ "onboarding.profile.display_name": "Εμφανιζόμενο όνομα", "onboarding.profile.display_name_hint": "Το πλήρες ή το διασκεδαστικό σου όνομα…", "onboarding.profile.note": "Βιογραφικό", - "onboarding.profile.note_hint": "Μπορείτε να @αναφέρετε άλλα άτομα ή #hashtags…", + "onboarding.profile.note_hint": "Μπορείς να @επισημάνεις άλλα άτομα ή #ετικέτες…", "onboarding.profile.save_and_continue": "Αποθήκευση και συνέχεια", "onboarding.profile.title": "Ρύθμιση προφίλ", "onboarding.profile.upload_avatar": "Μεταφόρτωση εικόνας προφίλ", @@ -770,7 +770,7 @@ "quote_error.quote": "Επιτρέπεται μόνο μία παράθεση τη φορά.", "quote_error.unauthorized": "Δεν είστε εξουσιοδοτημένοι να παραθέσετε αυτή την ανάρτηση.", "quote_error.upload": "Η παράθεση δεν επιτρέπεται με συνημμένα πολυμέσων.", - "recommended": "Προτεινόμενα", + "recommended": "Προτείνεται", "refresh": "Ανανέωση", "regeneration_indicator.please_stand_by": "Παρακαλούμε περίμενε.", "regeneration_indicator.preparing_your_home_feed": "Ετοιμάζουμε την αρχική σου ροή…", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index b470c1d523e2eb..f9398ca59e648b 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -357,7 +357,7 @@ "empty_column.notification_requests": "¡Todo limpio! No hay nada acá. Cuando recibás nuevas notificaciones, aparecerán acá, acorde a tu configuración.", "empty_column.notifications": "Todavía no tenés ninguna notificación. Cuando otras cuentas interactúen con vos, vas a ver la notificación acá.", "empty_column.public": "¡Naranja! Escribí algo públicamente, o seguí usuarios manualmente de otros servidores para ir llenando esta línea temporal", - "error.no_hashtag_feed_access": "Únete o inicia sesión para ver y seguir esta etiqueta.", + "error.no_hashtag_feed_access": "Create una cuenta o iniciá sesión para seguir esta etiqueta.", "error.unexpected_crash.explanation": "Debido a un error en nuestro código o a un problema de compatibilidad con el navegador web, esta página no se pudo mostrar correctamente.", "error.unexpected_crash.explanation_addons": "No se pudo mostrar correctamente esta página. Este error probablemente es causado por un complemento del navegador web o por herramientas de traducción automática.", "error.unexpected_crash.next_steps": "Intentá recargar la página. Si eso no ayuda, podés usar Mastodon a través de un navegador web diferente o aplicación nativa.", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index 29ff95ea1f0f43..3197a8ccaecadd 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Alt er klárt! Her er einki. Tá tú fært nýggjar fráboðanir, síggjast tær her sambært tínum stillingum.", "empty_column.notifications": "Tú hevur ongar fráboðanir enn. Tá onnur samskifta við teg, so sær tú fráboðaninar her.", "empty_column.public": "Einki er her! Skriva okkurt alment ella fylg brúkarum frá øðrum ambætarum fyri at fylla tilfar í", + "error.no_hashtag_feed_access": "Melda til ella rita inn fyri at síggja og fylgja hesum frámerkinum.", "error.unexpected_crash.explanation": "Orsakað av einum feili í okkara kotu ella orsakað av at kagin hjá tær ikki er sambæriligur við skipanina, so bar ikki til at vísa hesa síðuna rætt.", "error.unexpected_crash.explanation_addons": "Hendan síðan kundi ikki vísast rætt. Orsøkin til feilin er sannlíkt vegna eina uppíbygging í kaganum hjá tær ella vegna amboð til sjálvvirkandi umseting.", "error.unexpected_crash.next_steps": "Royn at lesa síðuna inn av nýggjum. Hjálpir tað ikki, so kann vera, at tað ber til at brúka Mastodon við einum øðrum kaga ella við eini app.", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index b9142bcd18da2d..06b6a5c8d60dc5 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -752,7 +752,7 @@ "privacy.change": "Axustar privacidade", "privacy.direct.long": "Todas as mencionadas na publicación", "privacy.direct.short": "Mención privada", - "privacy.private.long": "Só para seguidoras", + "privacy.private.long": "Só quen te segue", "privacy.private.short": "Seguidoras", "privacy.public.long": "Para todas dentro e fóra de Mastodon", "privacy.public.short": "Público", @@ -1032,7 +1032,7 @@ "visibility_modal.privacy_label": "Visibilidade", "visibility_modal.quote_followers": "Só para seguidoras", "visibility_modal.quote_label": "Quen pode citar", - "visibility_modal.quote_nobody": "Só para min", + "visibility_modal.quote_nobody": "Só eu", "visibility_modal.quote_public": "Calquera", "visibility_modal.save": "Gardar" } diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json index 21fc1adb57c57a..c2b298e032fda0 100644 --- a/app/javascript/mastodon/locales/nan.json +++ b/app/javascript/mastodon/locales/nan.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "清hōo空ah!內底無物件。若是lí收著新ê通知,ē根據lí ê設定,佇tsia出現。", "empty_column.notifications": "Lí iáu無收著任何通知。Nā別lâng kap lí互動,lí ē佇tsia看著。", "empty_column.public": "內底無物件!寫beh公開ê PO文,á是主動跟tuè別ê服侍器ê用者,來加添內容。", + "error.no_hashtag_feed_access": "加入á是登入,來看kap跟tuè tsit ê hashtag。", "error.unexpected_crash.explanation": "因為原始碼內底有錯誤,á是瀏覽器相容出tshê,tsit頁bē當正確顯示。", "error.unexpected_crash.explanation_addons": "Tsit頁bē當正確顯示,可能是瀏覽器附ê功能,á是自動翻譯工具所致。", "error.unexpected_crash.next_steps": "請試更新tsit頁。若是bē當改善,lí iáu是ē當改使用無kâng ê瀏覽器,á是app,來用Mastodon。", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index a0ec04a2be232a..2461dcf7e4c21b 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -157,6 +157,7 @@ "bundle_modal_error.close": "Закрыть", "bundle_modal_error.message": "Кое-что пошло не так при загрузке этой страницы.", "bundle_modal_error.retry": "Попробовать снова", + "carousel.slide": "Слайд {current, number} из {max, number}", "closed_registrations.other_server_instructions": "Благодаря тому что Mastodon децентрализован, вы можете взаимодействовать с этим сервером, даже если зарегистрируетесь на другом сервере.", "closed_registrations_modal.description": "Зарегистрироваться на {domain} сейчас не выйдет, но имейте в виду, что вам не нужна учётная запись именно на {domain}, чтобы использовать Mastodon.", "closed_registrations_modal.find_another_server": "Найти другой сервер", @@ -355,6 +356,7 @@ "empty_column.notification_requests": "Здесь ничего нет! Когда вы получите новые уведомления, они здесь появятся согласно вашим настройкам.", "empty_column.notifications": "У вас пока нет уведомлений. Взаимодействуйте с другими, чтобы завести разговор.", "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других серверов, чтобы заполнить ленту", + "error.no_hashtag_feed_access": "Зарегистрируйтесь или войдите, чтобы читать и подписаться на этот хэштег.", "error.unexpected_crash.explanation": "Из-за несовместимого браузера или ошибки в нашем коде эта страница не может быть корректно отображена.", "error.unexpected_crash.explanation_addons": "Эта страница не может быть корректно отображена. Скорее всего, ошибка вызвана расширением браузера или инструментом автоматического перевода.", "error.unexpected_crash.next_steps": "Попробуйте обновить страницу. Если это не поможет, вы, возможно, всё ещё сможете использовать Mastodon в другом браузере или приложении.", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 59e6d42c8000f5..c440c78623792f 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -227,7 +227,9 @@ "confirmations.delete_list.title": "Vymazať zoznam?", "confirmations.discard_draft.confirm": "Zahodiť a pokračovať", "confirmations.discard_draft.edit.cancel": "Pokračovať v úpravách", + "confirmations.discard_draft.edit.title": "Zahodiť zmeny v tvojom príspevku?", "confirmations.discard_draft.post.cancel": "Pokračuj v rozpísanom", + "confirmations.discard_draft.post.title": "Zahodiť tvoj rozpísaný príspevok?", "confirmations.discard_edit_media.confirm": "Zahodiť", "confirmations.discard_edit_media.message": "Máte neuložené zmeny v popise alebo náhľade média, zahodiť ich aj tak?", "confirmations.follow_to_list.confirm": "Nasleduj a pridaj do zoznamu", @@ -244,7 +246,9 @@ "confirmations.private_quote_notify.cancel": "Späť na úpravu", "confirmations.private_quote_notify.confirm": "Zverejni príspevok", "confirmations.private_quote_notify.do_not_show_again": "Neukazuj mi túto spravu znova", + "confirmations.private_quote_notify.title": "Zdieľať z nasledovateľmi a spomenutými užívateľmi?", "confirmations.quiet_post_quote_info.dismiss": "Nepripomínaj mi znova", + "confirmations.quiet_post_quote_info.got_it": "Mám to", "confirmations.redraft.confirm": "Vymazať a prepísať", "confirmations.redraft.message": "Určite chcete tento príspevok vymazať a prepísať? Prídete o jeho zdieľania a ohviezdičkovania a odpovede na pôvodný príspevok budú odlúčené.", "confirmations.redraft.title": "Vymazať a prepísať príspevok?", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json index 3b246c5b2918f2..7f7d8bd9910658 100644 --- a/app/javascript/mastodon/locales/sl.json +++ b/app/javascript/mastodon/locales/sl.json @@ -29,12 +29,16 @@ "account.edit_profile_short": "Uredi", "account.enable_notifications": "Obvesti me, ko ima @{name} novo objavo", "account.endorse": "Izpostavi v profilu", + "account.familiar_followers_one": "Sledi {name1}", + "account.featured": "Izpostavljeni", "account.featured.accounts": "Profili", "account.featured.hashtags": "Ključniki", "account.featured_tags.last_status_at": "Zadnja objava {date}", "account.featured_tags.last_status_never": "Ni objav", "account.follow": "Sledi", "account.follow_back": "Sledi nazaj", + "account.follow_back_short": "Sledi nazaj", + "account.follow_request_cancel": "Prekliči zahtevo", "account.follow_request_cancel_short": "Prekliči", "account.followers": "Sledilci", "account.followers.empty": "Nihče še ne sledi temu uporabniku.", @@ -42,6 +46,7 @@ "account.following": "Sledim", "account.following_counter": "{count, plural, one {{counter} sleden} two {{counter} sledena} few {{counter} sledeni} other {{counter} sledenih}}", "account.follows.empty": "Ta uporabnik še ne sledi nikomur.", + "account.follows_you": "Vam sledi", "account.go_to_profile": "Pojdi na profil", "account.hide_reblogs": "Skrij izpostavitve od @{name}", "account.in_memoriam": "V spomin.", @@ -56,6 +61,7 @@ "account.mute_notifications_short": "Utišaj obvestila", "account.mute_short": "Utišaj", "account.muted": "Utišan", + "account.mutual": "Drug drugemu sledita", "account.no_bio": "Ni opisa.", "account.open_original_page": "Odpri izvirno stran", "account.posts": "Objave", @@ -67,6 +73,7 @@ "account.statuses_counter": "{count, plural, one {{counter} objava} two {{counter} objavi} few {{counter} objave} other {{counter} objav}}", "account.unblock": "Odblokiraj @{name}", "account.unblock_domain": "Odblokiraj domeno {domain}", + "account.unblock_domain_short": "Odblokiraj", "account.unblock_short": "Odblokiraj", "account.unendorse": "Ne vključi v profil", "account.unfollow": "Ne sledi več", @@ -156,6 +163,8 @@ "column.edit_list": "Uredi seznam", "column.favourites": "Priljubljeni", "column.firehose": "Viri v živo", + "column.firehose_local": "Vir v živo za ta strežnik", + "column.firehose_singular": "Vir v živo", "column.follow_requests": "Prošnje za sledenje", "column.home": "Domov", "column.list_members": "Upravljaj člane seznama", @@ -175,6 +184,7 @@ "community.column_settings.local_only": "Samo krajevno", "community.column_settings.media_only": "Samo predstavnosti", "community.column_settings.remote_only": "Samo oddaljeno", + "compose.error.blank_post": "Objava ne sme biti prazna", "compose.language.change": "Spremeni jezik", "compose.language.search": "Poišči jezike ...", "compose.published.body": "Objavljeno.", @@ -207,6 +217,8 @@ "confirmations.delete_list.confirm": "Izbriši", "confirmations.delete_list.message": "Ali ste prepričani, da želite trajno izbrisati ta seznam?", "confirmations.delete_list.title": "Želite izbrisati seznam?", + "confirmations.discard_draft.edit.cancel": "Nadaljuj z urejanjem", + "confirmations.discard_draft.post.cancel": "Nadaljuj na osnutku", "confirmations.discard_edit_media.confirm": "Opusti", "confirmations.discard_edit_media.message": "Spremenjenega opisa predstavnosti ali predogleda niste shranili. Želite spremembe kljub temu opustiti?", "confirmations.follow_to_list.confirm": "Sledi in dodaj na seznam", @@ -220,6 +232,10 @@ "confirmations.missing_alt_text.secondary": "Vseeno objavi", "confirmations.missing_alt_text.title": "Dodam nadomestno besedilo?", "confirmations.mute.confirm": "Utišaj", + "confirmations.private_quote_notify.cancel": "Nazaj k urejanju", + "confirmations.private_quote_notify.confirm": "Objavi objavo", + "confirmations.private_quote_notify.do_not_show_again": "Ne prikazuj mi več tega sporočila", + "confirmations.quiet_post_quote_info.dismiss": "Ne opominjaj me več", "confirmations.quiet_post_quote_info.got_it": "Razumem", "confirmations.redraft.confirm": "Izbriši in preoblikuj", "confirmations.redraft.message": "Ali ste prepričani, da želite izbrisati to objavo in jo preoblikovati? Izkazi priljubljenosti in izpostavitve bodo izgubljeni, odgovori na izvirno objavo pa bodo osiroteli.", @@ -229,7 +245,11 @@ "confirmations.revoke_quote.confirm": "Odstrani objavo", "confirmations.revoke_quote.message": "Tega dejanja ni možno povrniti.", "confirmations.revoke_quote.title": "Ali želite odstraniti objavo?", + "confirmations.unblock.confirm": "Odblokiraj", + "confirmations.unblock.title": "Ali želite odblokirati {name}?", "confirmations.unfollow.confirm": "Ne sledi več", + "confirmations.unfollow.title": "Ali želite prenehati slediti {name}?", + "confirmations.withdraw_request.confirm": "Umakni zahtevo", "content_warning.hide": "Skrij objavo", "content_warning.show": "Vseeno pokaži", "content_warning.show_more": "Pokaži več", @@ -316,6 +336,7 @@ "errors.unexpected_crash.copy_stacktrace": "Kopiraj sled sklada na odložišče", "errors.unexpected_crash.report_issue": "Prijavi težavo", "explore.suggested_follows": "Ljudje", + "explore.title": "V trendu", "explore.trending_links": "Novice", "explore.trending_statuses": "Objave", "explore.trending_tags": "Ključniki", @@ -383,7 +404,10 @@ "hashtag.counter_by_accounts": "{count, plural, one {{counter} udeleženec} two {{counter} udeleženca} few {{counter} udeležencev} other {{counter} udeležencev}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} objava} two {{counter} posts} few {{counter} objavi} other {{counter} objav}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} objava} two {{counter} objavi} few {{counter} objav} other {{counter} objav}}", + "hashtag.feature": "Izpostavi v profilu", "hashtag.follow": "Sledi ključniku", + "hashtag.mute": "Utišaj #{hashtag}", + "hashtag.unfeature": "Ne izpostavljaj v profilu", "hashtag.unfollow": "Nehaj slediti ključniku", "hashtags.and_other": "… in še {count, plural, other {#}}", "hints.profiles.followers_may_be_missing": "Sledilci za ta profil morda manjkajo.", @@ -392,6 +416,7 @@ "hints.profiles.see_more_followers": "Pokaži več sledilcev na {domain}", "hints.profiles.see_more_follows": "Pokaži več sledenih ljudi na zbirališču {domain}", "hints.profiles.see_more_posts": "Pokaži več objav na {domain}", + "home.column_settings.show_quotes": "Pokaži citate", "home.column_settings.show_reblogs": "Pokaži izpostavitve", "home.column_settings.show_replies": "Pokaži odgovore", "home.hide_announcements": "Skrij obvestila", @@ -416,6 +441,7 @@ "interaction_modal.no_account_yet": "Še nimate računa?", "interaction_modal.on_another_server": "Na drugem strežniku", "interaction_modal.on_this_server": "Na tem strežniku", + "interaction_modal.title": "Za nadaljevanje se prijavite", "interaction_modal.username_prompt": "Npr. {example}", "intervals.full.days": "{number, plural, one {# dan} two {# dni} few {# dni} other {# dni}}", "intervals.full.hours": "{number, plural, one {# ura} two {# uri} few {# ure} other {# ur}}", @@ -444,6 +470,7 @@ "keyboard_shortcuts.open_media": "Odpri predstavnost", "keyboard_shortcuts.pinned": "Odpri seznam pripetih objav", "keyboard_shortcuts.profile": "Odpri avtorjev profil", + "keyboard_shortcuts.quote": "Citiraj objavo", "keyboard_shortcuts.reply": "Odgovori na objavo", "keyboard_shortcuts.requests": "Odpri seznam s prošnjami za sledenje", "keyboard_shortcuts.search": "Pozornost na iskalno vrstico", @@ -455,6 +482,8 @@ "keyboard_shortcuts.translate": "za prevod objave", "keyboard_shortcuts.unfocus": "Odstrani pozornost z območja za sestavljanje besedila/iskanje", "keyboard_shortcuts.up": "Premakni navzgor po seznamu", + "learn_more_link.got_it": "Razumem", + "learn_more_link.learn_more": "Več o tem", "lightbox.close": "Zapri", "lightbox.next": "Naslednji", "lightbox.previous": "Prejšnji", @@ -504,8 +533,10 @@ "mute_modal.you_wont_see_mentions": "Objav, ki jih omenjajo, ne boste videli.", "mute_modal.you_wont_see_posts": "Še vedno vidijo vaše objave, vi pa ne njihovih.", "navigation_bar.about": "O Mastodonu", + "navigation_bar.account_settings": "Geslo in varnost", "navigation_bar.administration": "Upravljanje", "navigation_bar.advanced_interface": "Odpri v naprednem spletnem vmesniku", + "navigation_bar.automated_deletion": "Samodejno brisanje objav", "navigation_bar.blocks": "Blokirani uporabniki", "navigation_bar.bookmarks": "Zaznamki", "navigation_bar.direct": "Zasebne omembe", @@ -515,12 +546,15 @@ "navigation_bar.follow_requests": "Prošnje za sledenje", "navigation_bar.followed_tags": "Sledeni ključniki", "navigation_bar.follows_and_followers": "Sledenja in sledilci", + "navigation_bar.import_export": "Uvoz in izvoz", "navigation_bar.lists": "Seznami", "navigation_bar.logout": "Odjava", "navigation_bar.moderation": "Moderiranje", + "navigation_bar.more": "Več", "navigation_bar.mutes": "Utišani uporabniki", "navigation_bar.opened_in_classic_interface": "Objave, računi in druge specifične strani se privzeto odprejo v klasičnem spletnem vmesniku.", "navigation_bar.preferences": "Nastavitve", + "navigation_bar.privacy_and_reach": "Zasebnost in dosegljivost", "navigation_bar.search": "Iskanje", "not_signed_in_indicator.not_signed_in": "Za dostop do tega vira se morate prijaviti.", "notification.admin.report": "{name} je prijavil/a {target}", @@ -543,6 +577,7 @@ "notification.label.mention": "Omemba", "notification.label.private_mention": "Zasebna omemba", "notification.label.private_reply": "Zasebni odgovor", + "notification.label.quote": "{name} je citiral/a vašo objavo", "notification.label.reply": "Odgovori", "notification.mention": "Omemba", "notification.mentioned_you": "{name} vas je omenil/a", @@ -600,6 +635,7 @@ "notifications.column_settings.mention": "Omembe:", "notifications.column_settings.poll": "Rezultati ankete:", "notifications.column_settings.push": "Potisna obvestila", + "notifications.column_settings.quote": "Citati:", "notifications.column_settings.reblog": "Izpostavitve:", "notifications.column_settings.show": "Pokaži v stolpcu", "notifications.column_settings.sound": "Predvajaj zvok", @@ -790,9 +826,13 @@ "status.bookmark": "Dodaj med zaznamke", "status.cancel_reblog_private": "Prekliči izpostavitev", "status.cannot_reblog": "Te objave ni mogoče izpostaviti", + "status.contains_quote": "Vsebuje citat", + "status.context.retry": "Poskusi znova", + "status.context.show": "Pokaži", "status.continued_thread": "Nadaljevanje niti", "status.copy": "Kopiraj povezavo do objave", "status.delete": "Izbriši", + "status.delete.success": "Objava je izbrisana", "status.detailed_status": "Podroben pogled pogovora", "status.direct": "Zasebno omeni @{name}", "status.direct_indicator": "Zasebna omemba", @@ -814,6 +854,14 @@ "status.mute_conversation": "Utišaj pogovor", "status.open": "Razširi to objavo", "status.pin": "Pripni na profil", + "status.quote.cancel": "Prekliči citat", + "status.quote_error.filtered": "Skrito zaradi enega od vaših filtrov", + "status.quote_error.limited_account_hint.action": "Vseeno pokaži", + "status.quote_error.limited_account_hint.title": "Ta račun so moderatorji {domain} skrili.", + "status.quote_error.not_available": "Objava ni na voljo", + "status.quote_followers_only": "Samo sledilci lahko citirajo to objavo", + "status.quote_policy_change": "Spremenite, kdo lahko citira", + "status.quote_private": "Zasebnih objav ni možno citirati", "status.read_more": "Preberi več", "status.reblog": "Izpostavi", "status.reblogged_by": "{name} je izpostavil/a", @@ -821,6 +869,7 @@ "status.redraft": "Izbriši in preoblikuj", "status.remove_bookmark": "Odstrani zaznamek", "status.remove_favourite": "Odstrani iz priljubljenih", + "status.remove_quote": "Odstrani", "status.replied_in_thread": "Odgovor iz niti", "status.replied_to": "Odgovoril/a {name}", "status.reply": "Odgovori", @@ -841,7 +890,10 @@ "subscribed_languages.save": "Shrani spremembe", "subscribed_languages.target": "Spremeni naročene jezike za {target}", "tabs_bar.home": "Domov", + "tabs_bar.menu": "Meni", "tabs_bar.notifications": "Obvestila", + "tabs_bar.publish": "Nova objava", + "tabs_bar.search": "Išči", "terms_of_service.effective_as_of": "Veljavno od {date}", "terms_of_service.title": "Pogoji uporabe", "terms_of_service.upcoming_changes_on": "Spremembe začnejo veljati {date}", @@ -875,6 +927,19 @@ "video.expand": "Razširi video", "video.fullscreen": "Celozaslonski način", "video.hide": "Skrij video", + "video.mute": "Utišaj", "video.pause": "Premor", - "video.play": "Predvajaj" + "video.play": "Predvajaj", + "video.skip_backward": "Preskoči nazaj", + "video.skip_forward": "Preskoči naprej", + "video.unmute": "Odtišaj", + "video.volume_down": "Zmanjšaj glasnost", + "video.volume_up": "Povečaj glasnost", + "visibility_modal.header": "Vidnost in interakcija", + "visibility_modal.privacy_label": "Vidnost", + "visibility_modal.quote_followers": "Samo sledilci", + "visibility_modal.quote_label": "Kdo lahko citira", + "visibility_modal.quote_nobody": "Samo jaz", + "visibility_modal.quote_public": "Vsi", + "visibility_modal.save": "Shrani" } diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index d95d1661c8d7b8..bb43bbdbbc3efe 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -353,6 +353,7 @@ "empty_column.notification_requests": "Gjithçka si duhet! S’ka ç’bëhet këtu. Kur merrni njoftime të reja, do të shfaqen këtu, në përputhje me rregullimet tuaja.", "empty_column.notifications": "Ende s’keni ndonjë njoftim. Ndërveproni me të tjerët që të nisë biseda.", "empty_column.public": "S’ka gjë këtu! Shkruani diçka publikisht, ose ndiqni dorazi përdorues prej instancash të tjera, që kjo të mbushet", + "error.no_hashtag_feed_access": "Që të shihni dhe ndiqni hashtag-ë, regjistrohuni, ose bëni hyrjen.", "error.unexpected_crash.explanation": "Për shkak të një të mete në kodin tonë ose të një problemi përputhshmërie të shfletuesit, kjo faqe s’mund të shfaqet saktë.", "error.unexpected_crash.explanation_addons": "Kjo faqe s’u shfaq dot saktë. Ky gabim ka gjasa të jetë shkaktuar nga një shtesë shfletuesi ose një mjet përkthimi të automatizuar.", "error.unexpected_crash.next_steps": "Provoni të freskoni faqen. Nëse kjo s’bën punë, mundeni ende të jeni në gjendje të përdorni Mastodon-in që nga një shfletues tjetër ose nga ndonjë aplikacion origjinal prej projektit.", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 0cce13e1465d65..bd48262ab3c5a1 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -340,11 +340,11 @@ "empty_column.blocks": "Du har ännu ej blockerat några användare.", "empty_column.bookmarked_statuses": "Du har inte bokmärkt några inlägg än. När du bokmärker ett inlägg kommer det synas här.", "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att sätta bollen i rullning!", - "empty_column.direct": "Du har inga privata omnämninande. När du skickar eller tar emot ett direktmeddelande kommer det att visas här.", + "empty_column.direct": "Du har inga privata omnämnanden. När du skickar eller tar emot ett direktmeddelande kommer det att visas här.", "empty_column.disabled_feed": "Detta flöde har inaktiverats av dina serveradministratörer.", "empty_column.domain_blocks": "Det finns ännu inga dolda domäner.", "empty_column.explore_statuses": "Ingenting är trendigt just nu. Kom tillbaka senare!", - "empty_column.favourited_statuses": "Du har inga favoritmarkerade inlägg ännu. När du favoritmärker ett så kommer det att dyka upp här.", + "empty_column.favourited_statuses": "Du har inga favoritmarkerade inlägg ännu. När du favoritmarkerar ett så kommer det att dyka upp här.", "empty_column.favourites": "Ingen har favoritmarkerat detta inlägg än. När någon gör det kommer de synas här.", "empty_column.follow_requests": "Du har inga följarförfrågningar än. När du får en kommer den visas här.", "empty_column.followed_tags": "Du följer inga hashtaggar ännu. När du gör det kommer de att dyka upp här.", @@ -355,6 +355,7 @@ "empty_column.notification_requests": "Allt klart! Det finns inget mer här. När du får nya meddelanden visas de här enligt dina inställningar.", "empty_column.notifications": "Du har inga meddelanden än. Interagera med andra för att starta konversationen.", "empty_column.public": "Det finns inget här! Skriv något offentligt, eller följ manuellt användarna från andra instanser för att fylla på det", + "error.no_hashtag_feed_access": "Gå med eller logga in för att visa och följa denna hashtag.", "error.unexpected_crash.explanation": "På grund av en bugg i vår kod eller kompatiblitetsproblem i webbläsaren kan den här sidan inte visas korrekt.", "error.unexpected_crash.explanation_addons": "Denna sida kunde inte visas korrekt. Detta beror troligen på ett webbläsartillägg eller ett automatiskt översättningsverktyg.", "error.unexpected_crash.next_steps": "Prova att ladda om sidan. Om det inte hjälper kan du försöka använda Mastodon med en annan webbläsare eller app.", @@ -925,15 +926,23 @@ "status.quote_error.not_available": "Inlägg ej tillgängligt", "status.quote_error.pending_approval": "Väntande inlägg", "status.quote_error.pending_approval_popout.body": "På Mastodon kan du styra om någon kan citera dig. Det här inlägget väntar medan vi får den ursprungliga författarens godkännande.", + "status.quote_error.revoked": "Inlägg borttaget av författaren", + "status.quote_followers_only": "Detta inlägg kan bara citeras av följare", + "status.quote_manual_review": "Författaren kommer att granska manuellt", "status.quote_policy_change": "Ändra vem som kan citera", "status.quote_post_author": "Citerade ett inlägg av @{name}", + "status.quote_private": "Privata inlägg kan inte citeras", + "status.quotes.empty": "Ingen har citerat detta inlägg än. När någon gör det kommer det att synas här.", + "status.quotes.local_other_disclaimer": "Citat som avvisats av författaren kommer inte att visas.", "status.read_more": "Läs mer", "status.reblog": "Boosta", + "status.reblog_private": "Dela igen med dina följare", "status.reblogged_by": "{name} boostade", "status.reblogs.empty": "Ingen har boostat detta inlägg än. När någon gör det kommer de synas här.", "status.redraft": "Radera & gör om", "status.remove_bookmark": "Ta bort bokmärke", "status.remove_favourite": "Ta bort från Favoriter", + "status.remove_quote": "Ta bort", "status.replied_in_thread": "Svarade i tråden", "status.replied_to": "Svarade på {name}", "status.reply": "Svara", @@ -977,6 +986,7 @@ "upload_button.label": "Lägg till media", "upload_error.limit": "Filöverföringsgränsen överskriden.", "upload_error.poll": "Filuppladdning tillåts inte med omröstningar.", + "upload_error.quote": "Filuppladdning tillåts inte med citat.", "upload_form.drag_and_drop.instructions": "För att plocka upp en mediebilaga, tryck på mellanslag eller enter. Använd piltangenterna för att flytta mediebilagan. Tryck på mellanslag eller enter igen för att släppa mediebilagan i sin nya position, eller tryck på escape för att avbryta.", "upload_form.drag_and_drop.on_drag_cancel": "Flytten avbröts. Mediebilagan {item} släpptes.", "upload_form.drag_and_drop.on_drag_end": "Mediebilagan {item} släpptes.", @@ -1001,9 +1011,19 @@ "video.volume_down": "Volym ned", "video.volume_up": "Volym upp", "visibility_modal.button_title": "Ange synlighet", + "visibility_modal.direct_quote_warning.text": "Om du sparar de nuvarande inställningarna kommer det inbäddade citatet bli konverterat till en länk.", + "visibility_modal.direct_quote_warning.title": "Citat kan inte bäddas in i privata omnämnanden", "visibility_modal.header": "Synlighet och interaktion", + "visibility_modal.helper.direct_quoting": "Privata omnämnanden som författats på Mastodon kan inte citeras av andra.", + "visibility_modal.helper.privacy_editing": "Synligheten kan inte ändras efter att ett inlägg är publicerat.", + "visibility_modal.helper.privacy_private_self_quote": "Självcitat av privata inlägg kan inte göras publika.", + "visibility_modal.helper.private_quoting": "Inlägg som endast är för följare och som författats på Mastodon kan inte citeras av andra.", "visibility_modal.helper.unlisted_quoting": "När folk citerar dig, deras inlägg kommer också att döljas från trendiga tidslinjer.", + "visibility_modal.instructions": "Kontrollera vem som kan interagera med detta inlägg. Du kan också använda inställningar för alla framtida inlägg genom att navigera till Preferences > Posting defaults.", + "visibility_modal.privacy_label": "Synlighet", "visibility_modal.quote_followers": "Endast följare", + "visibility_modal.quote_label": "Vem kan citera", + "visibility_modal.quote_nobody": "Bara jag", "visibility_modal.quote_public": "Alla", "visibility_modal.save": "Spara" } diff --git a/config/locales/activerecord.be.yml b/config/locales/activerecord.be.yml index 3ddb3e328eda96..97f95a0e730cb6 100644 --- a/config/locales/activerecord.be.yml +++ b/config/locales/activerecord.be.yml @@ -32,6 +32,12 @@ be: attributes: url: invalid: нядзейсны URL + collection: + attributes: + collection_items: + too_many: занадта шмат, дазволена не больш за %{count} + tag: + unusable: нельга выкарыстаць doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.cs.yml b/config/locales/activerecord.cs.yml index 38708713d2c65b..44390fb32c47a3 100644 --- a/config/locales/activerecord.cs.yml +++ b/config/locales/activerecord.cs.yml @@ -32,6 +32,12 @@ cs: attributes: url: invalid: není platná URL + collection: + attributes: + collection_items: + too_many: je příliš mnoho, ne více než %{count} je povoleno + tag: + unusable: nesmí být použito doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.cy.yml b/config/locales/activerecord.cy.yml index c201016be61243..cfb20eb9d20281 100644 --- a/config/locales/activerecord.cy.yml +++ b/config/locales/activerecord.cy.yml @@ -32,6 +32,12 @@ cy: attributes: url: invalid: nid yw'n URL dilys + collection: + attributes: + collection_items: + too_many: yn ormod, does dim caniatâd i fwy na %{count} + tag: + unusable: does dim caniatâd i'w ddefnyddio doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.da.yml b/config/locales/activerecord.da.yml index 8c405fba13bc4e..ff4e9d97eace7d 100644 --- a/config/locales/activerecord.da.yml +++ b/config/locales/activerecord.da.yml @@ -32,6 +32,12 @@ da: attributes: url: invalid: er ikke en gyldig URL + collection: + attributes: + collection_items: + too_many: er for mange, ikke mere end %{count} er tilladt + tag: + unusable: må ikke anvendes doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.de.yml b/config/locales/activerecord.de.yml index 4ae7aec5dd61a7..b31317ad36f9ff 100644 --- a/config/locales/activerecord.de.yml +++ b/config/locales/activerecord.de.yml @@ -32,6 +32,12 @@ de: attributes: url: invalid: ist keine gültige URL + collection: + attributes: + collection_items: + too_many: zu viele, mehr als %{count} sind nicht erlaubt + tag: + unusable: darf nicht verwendet werden doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.el.yml b/config/locales/activerecord.el.yml index 4bb344bc6dbe15..d6e6bf570d2268 100644 --- a/config/locales/activerecord.el.yml +++ b/config/locales/activerecord.el.yml @@ -32,6 +32,12 @@ el: attributes: url: invalid: δεν είναι έγκυρο URL + collection: + attributes: + collection_items: + too_many: είναι πολλά, δεν επιτρέπονται περισσότερα από %{count} + tag: + unusable: δεν μπορεί να χρησιμοποιηθεί doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.eo.yml b/config/locales/activerecord.eo.yml index 96b7ee89fda4a0..c4bee952fbfad1 100644 --- a/config/locales/activerecord.eo.yml +++ b/config/locales/activerecord.eo.yml @@ -32,6 +32,12 @@ eo: attributes: url: invalid: ne estas valida URL + collection: + attributes: + collection_items: + too_many: estas tro multaj, ne pli ol %{count} estas permesataj + tag: + unusable: ne povus esti uzata doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.es-AR.yml b/config/locales/activerecord.es-AR.yml index 62a409a3535923..927f9e4742a5c4 100644 --- a/config/locales/activerecord.es-AR.yml +++ b/config/locales/activerecord.es-AR.yml @@ -32,6 +32,12 @@ es-AR: attributes: url: invalid: no es una dirección web válida + collection: + attributes: + collection_items: + too_many: son demasiados, no se permiten más de %{count} + tag: + unusable: no podrán ser usadas doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.es-MX.yml b/config/locales/activerecord.es-MX.yml index 02384f1c715409..944197783459db 100644 --- a/config/locales/activerecord.es-MX.yml +++ b/config/locales/activerecord.es-MX.yml @@ -32,6 +32,12 @@ es-MX: attributes: url: invalid: no es una dirección URL válida + collection: + attributes: + collection_items: + too_many: son demasiados, no se permiten más de %{count} + tag: + unusable: podría no utilizarse doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index 94f29365e9129d..cb425fecdde21a 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -32,6 +32,12 @@ es: attributes: url: invalid: no es una URL válida + collection: + attributes: + collection_items: + too_many: son demasiados, no se permiten más de %{count} + tag: + unusable: podría no utilizarse doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.et.yml b/config/locales/activerecord.et.yml index 4e49ae968378ac..a1d4a0c38b81d0 100644 --- a/config/locales/activerecord.et.yml +++ b/config/locales/activerecord.et.yml @@ -32,6 +32,12 @@ et: attributes: url: invalid: pole sobiv URL + collection: + attributes: + collection_items: + too_many: on liiga palju, pole lubatud enam, kui %{count} + tag: + unusable: pole võimalik kasutada doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.fo.yml b/config/locales/activerecord.fo.yml index ce84a1fffd43d7..3f069cd072f8b4 100644 --- a/config/locales/activerecord.fo.yml +++ b/config/locales/activerecord.fo.yml @@ -32,6 +32,12 @@ fo: attributes: url: invalid: er ikki eitt rætt leinki + collection: + attributes: + collection_items: + too_many: er ov nógvir, einans %{count} eru loyvdir + tag: + unusable: kann ikki brúkast doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.ga.yml b/config/locales/activerecord.ga.yml index 853c705663f4d1..bc8bbf2fb3b5a5 100644 --- a/config/locales/activerecord.ga.yml +++ b/config/locales/activerecord.ga.yml @@ -32,6 +32,12 @@ ga: attributes: url: invalid: nach URL bailí é + collection: + attributes: + collection_items: + too_many: an iomarca, níl cead níos mó ná %{count} a fháil + tag: + unusable: ní fhéadfar a úsáid doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.gl.yml b/config/locales/activerecord.gl.yml index f4e6725565578c..b836c3e97734ad 100644 --- a/config/locales/activerecord.gl.yml +++ b/config/locales/activerecord.gl.yml @@ -32,6 +32,12 @@ gl: attributes: url: invalid: non é un URL válido + collection: + attributes: + collection_items: + too_many: son demasiados, non se permiten máis de %{count} + tag: + unusable: non se debería usar doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.he.yml b/config/locales/activerecord.he.yml index 7dff17493b3cf1..27bfe360d99b39 100644 --- a/config/locales/activerecord.he.yml +++ b/config/locales/activerecord.he.yml @@ -32,6 +32,12 @@ he: attributes: url: invalid: כתובת לא חוקית + collection: + attributes: + collection_items: + too_many: יש יותר מדי, מותרות %{count} לכל היותר + tag: + unusable: לא שמישות doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.hu.yml b/config/locales/activerecord.hu.yml index cf2f50a9f9b6cf..5db36592c8104b 100644 --- a/config/locales/activerecord.hu.yml +++ b/config/locales/activerecord.hu.yml @@ -32,6 +32,12 @@ hu: attributes: url: invalid: nem érvényes URL + collection: + attributes: + collection_items: + too_many: túl sok, legfeljebb %{count} engedélyezett + tag: + unusable: nem használható doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.is.yml b/config/locales/activerecord.is.yml index cff90a3476d627..7fe6835a1d7857 100644 --- a/config/locales/activerecord.is.yml +++ b/config/locales/activerecord.is.yml @@ -32,6 +32,12 @@ is: attributes: url: invalid: er ekki gild vefslóð + collection: + attributes: + collection_items: + too_many: eru of mörg, ekki fleiri en %{count} eru leyfileg + tag: + unusable: gæti verið ekki notað doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.pt-BR.yml b/config/locales/activerecord.pt-BR.yml index 8b77ed7e834574..7d4223725a77e2 100644 --- a/config/locales/activerecord.pt-BR.yml +++ b/config/locales/activerecord.pt-BR.yml @@ -32,6 +32,12 @@ pt-BR: attributes: url: invalid: não é uma URL válida + collection: + attributes: + collection_items: + too_many: é demais, não é permitido mais que %{count} + tag: + unusable: não pode ser usado doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.pt-PT.yml b/config/locales/activerecord.pt-PT.yml index 397ed492e528a3..76baf556acb8f6 100644 --- a/config/locales/activerecord.pt-PT.yml +++ b/config/locales/activerecord.pt-PT.yml @@ -32,6 +32,12 @@ pt-PT: attributes: url: invalid: não é um URL válido + collection: + attributes: + collection_items: + too_many: são demasiadas, não são permitidas mais do que %{count} + tag: + unusable: não pode ser utilizada doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.ru.yml b/config/locales/activerecord.ru.yml index 84616eb7673581..5b0f46e382a520 100644 --- a/config/locales/activerecord.ru.yml +++ b/config/locales/activerecord.ru.yml @@ -32,6 +32,12 @@ ru: attributes: url: invalid: не является действительным URL + collection: + attributes: + collection_items: + too_many: слишком много, не разрешено более чем %{count} + tag: + unusable: может не быть использованным doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.sq.yml b/config/locales/activerecord.sq.yml index 2683dd014bf74f..428283a0ef05ac 100644 --- a/config/locales/activerecord.sq.yml +++ b/config/locales/activerecord.sq.yml @@ -32,6 +32,12 @@ sq: attributes: url: invalid: s’është URL e vlefshme + collection: + attributes: + collection_items: + too_many: janë shumë, nuk lejohen më tepër se %{count} + tag: + unusable: mund të mos përdoret dot doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.sv.yml b/config/locales/activerecord.sv.yml index 74b939fda489ac..0b96c16a5c3efe 100644 --- a/config/locales/activerecord.sv.yml +++ b/config/locales/activerecord.sv.yml @@ -32,6 +32,12 @@ sv: attributes: url: invalid: är inte en giltig URL + collection: + attributes: + collection_items: + too_many: är för många, fler än %{count} är inte tillåtet + tag: + unusable: får inte användas doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.vi.yml b/config/locales/activerecord.vi.yml index fe810d94e50027..4a52fbce24a016 100644 --- a/config/locales/activerecord.vi.yml +++ b/config/locales/activerecord.vi.yml @@ -32,6 +32,12 @@ vi: attributes: url: invalid: không phải là một URL hợp lệ + collection: + attributes: + collection_items: + too_many: quá nhiều, không được hơn %{count} + tag: + unusable: không được phép dùng doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.zh-TW.yml b/config/locales/activerecord.zh-TW.yml index f8f630ba3cbc2d..3b61e5ab883bdb 100644 --- a/config/locales/activerecord.zh-TW.yml +++ b/config/locales/activerecord.zh-TW.yml @@ -32,6 +32,12 @@ zh-TW: attributes: url: invalid: 不是有效的 URL + collection: + attributes: + collection_items: + too_many: 數量過多,無法多過 %{count} 個 + tag: + unusable: 無法使用 doorkeeper/application: attributes: website: diff --git a/config/locales/da.yml b/config/locales/da.yml index b26de6ab2c7b5c..ab6abedd3527c8 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1957,12 +1957,12 @@ da: enabled: Slet automatisk gamle indlæg enabled_hint: Sletter automatisk dine indlæg, når disse når en bestemt alder, medmindre de matcher en af undtagelserne nedenfor exceptions: Undtagelser - explanation: Sletning af indlæg er en ressourcekrævende operation, hvorfor dette sker gradvist over tid, når serveren ellers ikke er optaget. Indlæg kan derfor blive slettet efter, at de reelt har passeret aldersgrænsen. + explanation: Da sletning af indlæg er en kostbar operation, foregår dette langsomt over tid, når serveren ikke er optaget af andre opgaver. Af denne grund kan dine indlæg blive slettet et stykke tid efter, at de har nået alderstærsklen. ignore_favs: Ignorér favoritter ignore_reblogs: Ignorér fremhævelser interaction_exceptions: Undtagelser baseret på interaktioner interaction_exceptions_explanation: Bemærk, at det ikke garanteres, at indlæg slettes, hvis de når under favorit- eller fremhævelses-tærsklerne efter én gang at været nået over dem. - keep_direct: Behold direkte besked + keep_direct: Behold direkte beskeder keep_direct_hint: Sletter ingen af dine direkte beskeder keep_media: Behold indlæg med medievedhæftninger keep_media_hint: Sletter ingen af dine indlæg med medievedhæftninger @@ -2004,7 +2004,7 @@ da: title: Tjenestevilkårene for %{domain} ændres themes: contrast: Mastodon (høj kontrast) - default: Mastodont (mørkt) + default: Mastodon (mørkt) mastodon-light: Mastodon (lyst) system: Automatisk (benyt systemtema) time: @@ -2024,8 +2024,8 @@ da: edit: Redigér enabled: Tofaktorgodkendelse aktiveret enabled_success: Tofaktorgodkendelse aktiveret - generate_recovery_codes: Generere gendannelseskoder - lost_recovery_codes: Gendannelseskoder muliggør adgang til din konto, hvis du mister din mobil. Ved mistet gendannelseskoder, kan disse regenerere her. Dine gamle gendannelseskoder ugyldiggøres. + generate_recovery_codes: Generer gendannelseskoder + lost_recovery_codes: Gendannelseskoder giver dig mulighed for at få adgang til din konto igen, hvis du mister din telefon. Hvis du har mistet dine gendannelseskoder, kan du generere dem igen her. Dine gamle gendannelseskoder vil blive ugyldige. methods: Tofaktormetoder otp: Godkendelses-app recovery_codes: Sikkerhedskopieret gendannelseskoder diff --git a/config/locales/de.yml b/config/locales/de.yml index df3e5b8887f8f3..ccd44a0c8d9433 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1941,7 +1941,7 @@ de: pending_approval: Veröffentlichung ausstehend revoked: Beitrag durch Autor*in entfernt quote_policies: - followers: Nur Follower + followers: Nur Follower und ich nobody: Nur ich public: Alle quote_post_author: Zitierte %{acct} diff --git a/config/locales/devise.es-MX.yml b/config/locales/devise.es-MX.yml index f37b1ab5f9e6f4..de2a4a8d3b9d3a 100644 --- a/config/locales/devise.es-MX.yml +++ b/config/locales/devise.es-MX.yml @@ -7,7 +7,7 @@ es-MX: send_paranoid_instructions: Si su dirección de correo electrónico existe en nuestra base de datos, recibirá un correo electrónico con instrucciones sobre cómo confirmar su dirección de correo en pocos minutos. failure: already_authenticated: Usted ya está registrado. - closed_registrations: Su intento de registro ha sido bloqueado debido a una política de red. Si cree que esto es un error, póngase en contacto con %{email}. + closed_registrations: Su intento de registro ha sido bloqueado debido a una política de red. Si cree que se trata de un error, póngase en contacto con %{email}. inactive: Su cuenta no ha sido activada aún. invalid: "%{authentication_keys} o contraseña inválida." last_attempt: Tiene un intento más antes de que tu cuenta sea bloqueada. diff --git a/config/locales/devise.ru.yml b/config/locales/devise.ru.yml index cc95f730615a3a..60d731898130d0 100644 --- a/config/locales/devise.ru.yml +++ b/config/locales/devise.ru.yml @@ -7,6 +7,7 @@ ru: send_paranoid_instructions: В течение нескольких минут вы получите письмо с инструкциями по его подтверждению, при условии что на ваш адрес электронной почты зарегистрирована учётная запись. Если письмо не приходит, проверьте папку «Спам». failure: already_authenticated: Вы уже авторизованы. + closed_registrations: Ваша попытка регистрации была заблокирована в связи с сетевой политикой. Если вы считаете, что произошла ошибка, свяжитесь с %{email}. inactive: Ваша учётная запись ещё не активирована. invalid: "%{authentication_keys} или пароль введён неверно." last_attempt: У вас осталась последняя попытка ввода пароля до блокировки учётной записи. diff --git a/config/locales/devise.sl.yml b/config/locales/devise.sl.yml index 8e708f4ebf565c..cb369c191061d0 100644 --- a/config/locales/devise.sl.yml +++ b/config/locales/devise.sl.yml @@ -7,6 +7,7 @@ sl: send_paranoid_instructions: Če vaš e-poštni naslov obstaja v naši zbirki podatkov, boste v nekaj minutah prejeli e-poštno sporočilo z navodili za potrditev vašega e-poštnega naslova. Če niste prejeli e-poštnega sporočila, preverite mapo neželena pošta. failure: already_authenticated: Ste že prijavljeni. + closed_registrations: Vaš poskus registracije je blokiran zaradi omrežnih pravil. Če ste mnenja, da gre za napako, stopite v stik s/z %{email}. inactive: Vaš račun še ni aktiviran. invalid: Neveljavno %{authentication_keys} ali geslo. last_attempt: Pred zaklepom računa imate še en poskus. diff --git a/config/locales/el.yml b/config/locales/el.yml index 397c03b402e32e..47a558ada79f77 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -1044,7 +1044,7 @@ el: confirm_allow_provider: Σίγουρα θες να επιτρέψεις τους επιλεγμένους παρόχους; confirm_disallow: Σίγουρα θες να απορρίψεις τους επιλεγμένους συνδέσμους; confirm_disallow_provider: Σίγουρα θες να απορρίψεις τους επιλεγμένους παρόχους; - description_html: Αυτοί οι σύνδεσμοι μοιράζονται αρκετά από λογαριασμούς των οποίων τις δημοσιεύσεις, βλέπει ο διακομιστής σας. Μπορεί να βοηθήσει τους χρήστες σας να μάθουν τί συμβαίνει στον κόσμο. Οι σύνδεσμοι δεν εμφανίζονται δημόσια μέχρι να εγκρίνετε τον εκδότη. Μπορείς επίσης να επιτρέψεις ή να απορρίψεις μεμονωμένους συνδέσμους. + description_html: Αυτοί οι σύνδεσμοι κοινοποιούνται αρκετά από λογαριασμούς των οποίων τις αναρτήσεις, βλέπει ο διακομιστής σας. Μπορεί να βοηθήσει τους χρήστες σας να μάθουν τί συμβαίνει στον κόσμο. Οι σύνδεσμοι δεν εμφανίζονται δημόσια μέχρι να εγκρίνετε τον εκδότη. Μπορείτε επίσης να επιτρέψετε ή να απορρίψετε μεμονωμένους συνδέσμους. disallow: Να μην επιτρέπεται ο σύνδεσμος disallow_provider: Να μην επιτρέπεται ο εκδότης no_link_selected: Κανένας σύνδεσμος δεν άλλαξε αφού κανείς δεν επιλέχθηκε @@ -1060,7 +1060,7 @@ el: pending_review: Εκκρεμεί αξιολόγηση preview_card_providers: allowed: Σύνδεσμοι από αυτόν τον εκδότη μπορούν να γίνουν δημοφιλείς - description_html: Αυτοί είναι τομείς από τους οποίους οι σύνδεσμοι συχνά μοιράζονται στον διακομιστή σας. Σύνδεσμοι δεν γίνουν δημοφιλείς δημοσίως εκτός και αν ο τομέας του συνδέσμου εγκριθεί. Η έγκρισή σας (ή απόρριψη) περιλαμβάνει και τους υποτομείς. + description_html: Αυτοί είναι τομείς από τους οποίους οι σύνδεσμοι συχνά κοινοποιούνται στον διακομιστή σας. Οι σύνδεσμοι δεν θα γίνουν δημοφιλείς δημοσίως εκτός και αν ο τομέας του συνδέσμου εγκριθεί. Η έγκρισή σας (ή απόρριψη) περιλαμβάνει και τους υποτομείς. rejected: Σύνδεσμοι από αυτόν τον εκδότη δε θα γίνουν δημοφιλείς title: Εκδότες rejected: Απορρίφθηκε @@ -1077,8 +1077,8 @@ el: no_status_selected: Καμία δημοφιλής ανάρτηση δεν άλλαξε αφού καμία δεν επιλέχθηκε not_discoverable: Ο συντάκτης δεν έχει επιλέξει να είναι ανακαλύψιμος shared_by: - one: Μοιράστηκε ή προστέθηκε στα αγαπημένα μία φορά - other: Μοιράστηκε και προστέθηκε στα αγαπημένα %{friendly_count} φορές + one: Κοινοποιήθηκε ή προστέθηκε στα αγαπημένα μία φορά + other: Κοινοποιήθηκε και προστέθηκε στα αγαπημένα %{friendly_count} φορές title: Αναρτήσεις σε τάση tags: current_score: Τρέχουσα βαθμολογία %{score} @@ -1305,7 +1305,7 @@ el: user_privacy_agreement_html: Έχω διαβάσει και συμφωνώ με την πολιτική απορρήτου author_attribution: example_title: Δείγμα κειμένου - hint_html: Γράφεις ειδήσεις ή blog άρθρα εκτός του Mastodon; Έλεγξε πώς μπορείς να πάρεις τα εύσημα όταν μοιράζονται στο Mastodon. + hint_html: Γράφεις ειδήσεις ή άρθρα blog εκτός του Mastodon; Έλεγξε πώς μπορείς να πάρεις τα εύσημα όταν κοινοποιούνται στο Mastodon. instructions: 'Βεβαιώσου ότι ο κώδικας αυτός είναι στο HTML του άρθρου σου:' more_from_html: Περισσότερα από %{name} s_blog: Ιστολόγιο του/της %{name} @@ -1593,9 +1593,9 @@ el: invalid: Αυτή η πρόσκληση δεν είναι έγκυρη invited_by: 'Σε προσκάλεσε ο/η:' max_uses: - one: 1 χρήσης - other: "%{count} χρήσεων" - max_uses_prompt: Απεριόριστη + one: 1 χρήση + other: "%{count} χρήσεις" + max_uses_prompt: Απεριόριστες prompt: Φτιάξε και μοίρασε συνδέσμους με τρίτους για να δώσεις πρόσβαση σε αυτόν τον διακομιστή table: expires_at: Λήγει @@ -1880,7 +1880,7 @@ el: development: Ανάπτυξη edit_profile: Επεξεργασία προφίλ export: Εξαγωγή - featured_tags: Παρεχόμενες ετικέτες + featured_tags: Προβεβλημένες ετικέτες import: Εισαγωγή import_and_export: Εισαγωγή και εξαγωγή migrate: Μετακόμιση λογαριασμού @@ -2142,7 +2142,7 @@ el: post_step: Πες γεια στον κόσμο με κείμενο, φωτογραφίες, βίντεο ή δημοσκοπήσεις. post_title: Κάνε την πρώτη σου ανάρτηση share_step: Πες στους φίλους σου πώς να σε βρουν στο Mastodon. - share_title: Μοιραστείτε το προφίλ σας στο Mastodon + share_title: Κοινοποίησε το προφίλ σου στο Mastodon sign_in_action: Σύνδεση subject: Καλώς ήρθες στο Mastodon title: Καλώς όρισες, %{name}! @@ -2159,7 +2159,7 @@ el: here_is_how: Δείτε πώς hint_html: Η επαλήθευση της ταυτότητας στο Mastodon είναι για όλους. Βασισμένο σε ανοιχτά πρότυπα ιστού, τώρα και για πάντα δωρεάν. Το μόνο που χρειάζεσαι είναι μια προσωπική ιστοσελίδα που ο κόσμος να σε αναγνωρίζει από αυτή. Όταν συνδέεσαι σε αυτήν την ιστοσελίδα από το προφίλ σου, θα ελέγξουμε ότι η ιστοσελίδα συνδέεται πίσω στο προφίλ σου και θα δείξει μια οπτική ένδειξη σε αυτό. instructions_html: Αντέγραψε και επικόλλησε τον παρακάτω κώδικα στην HTML της ιστοσελίδας σου. Στη συνέχεια, πρόσθεσε τη διεύθυνση της ιστοσελίδας σου σε ένα από τα επιπλέον πεδία στο προφίλ σου από την καρτέλα "Επεξεργασία προφίλ" και αποθήκευσε τις αλλαγές. - verification: Πιστοποίηση + verification: Επαλήθευση verified_links: Οι επαληθευμένοι σύνδεσμοι σας website_verification: Επαλήθευση ιστοτόπου webauthn_credentials: diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index dec5395c376ab2..b39844b1945066 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -839,7 +839,7 @@ es-MX: title: Optar por los usuarios fuera de la indexación en los motores de búsqueda por defecto discovery: follow_recommendations: Recomendaciones de cuentas - preamble: Exponer contenido interesante es fundamental para incorporar nuevos usuarios que pueden no conocer a nadie en Mastodon. Controla cómo funcionan en tu servidor las diferentes opciones de descubrimiento. + preamble: Mostrar contenido interesante es fundamental para atraer a nuevos usuarios que quizá no conozcan a nadie en Mastodon. Controla cómo funcionan las distintas funciones de descubrimiento en tu servidor. privacy: Privacidad profile_directory: Directorio de perfiles public_timelines: Lineas de tiempo públicas @@ -1196,7 +1196,7 @@ es-MX: hint_html: Si deseas migrar de otra cuenta a esta, aquí puedes crear un alias, que es necesario para poder mover seguidores de la cuenta anterior a esta. Esta acción por sí misma es inofensiva y reversible. La migración de la cuenta se inicia desde la cuenta anterior. remove: Desvincular alias appearance: - advanced_settings: Ajustes avanzados + advanced_settings: Configuración avanzada animations_and_accessibility: Animaciones y accesibilidad boosting_preferences: Preferencias de impulso boosting_preferences_info_html: "Consejo: Sin importar los ajustes, pulsar Mayus al hacer clic en el icono de %{icon} Impulsar, dará impulso inmediatamente." @@ -1604,8 +1604,8 @@ es-MX: link_preview: author_html: Por %{name} potentially_sensitive_content: - action: Pulsa para mostrar - confirm_visit: "¿Seguro que quieres abrir este enlace?" + action: Haz clic para mostrar + confirm_visit: "¿Estás seguro de que deseas abrir este enlace?" hide_button: Ocultar label: Contenido potencialmente sensible lists: @@ -1672,7 +1672,7 @@ es-MX: disabled_account: Tu cuenta actual no será completamente utilizable después. Sin embargo, tendrás acceso a la exportación de datos así como a la reactivación. followers: Esta acción migrará a todos los seguidores de la cuenta actual a la nueva cuenta only_redirect_html: Alternativamente, solo puedes poner una redirección en tu perfil. - other_data: Ningún otro dato se moverá automáticamente (esto incluye tus publicaciones y la lista de cuentas que sigues) + other_data: No se transferirán automáticamente otros datos (incluidas tus publicaciones y la lista de cuentas que sigues) redirect: El perfil de tu cuenta actual se actualizará con un aviso de redirección y será excluido de las búsquedas moderation: title: Moderación @@ -1929,7 +1929,7 @@ es-MX: errors: in_reply_not_found: La publicación a la que estás intentando responder no existe. quoted_status_not_found: La publicación que intentas citar no parece existir. - quoted_user_not_mentioned: No se puede citar a un usuario no mencionado en una Mención Privada. + quoted_user_not_mentioned: No se puede citar a un usuario no mencionado en una mención privada. over_character_limit: Límite de caracteres de %{max} superado pin_errors: direct: Las publicaciones que son visibles solo para los usuarios mencionados no pueden fijarse @@ -1939,12 +1939,12 @@ es-MX: quote_error: not_available: Publicación no disponible pending_approval: Publicación pendiente - revoked: Publicación borrada por el autor + revoked: Publicación eliminada por el autor quote_policies: followers: Solo seguidores nobody: Solo yo public: Cualquiera - quote_post_author: Citando una publicación de %{acct} + quote_post_author: Cita de una publicación de %{acct} title: "%{name}: «%{quote}»" visibilities: direct: Mención privada diff --git a/config/locales/nan.yml b/config/locales/nan.yml index af5001d7cfa167..b9e5c09159f4d5 100644 --- a/config/locales/nan.yml +++ b/config/locales/nan.yml @@ -1268,10 +1268,19 @@ nan: sign_in: preamble_html: 請用lí佇 %{domain} ê口座kap密碼登入。若是lí ê口座tī別ê服侍器,lí bē當tī tsia登入。 title: 登入 %{domain} + sign_up: + manual_review: 佇 %{domain} ê註冊ē經過guán ê管理員人工審查。為著tsān guán 進行lí ê註冊,寫tsi̍t點á關係lí家己kap想beh tī %{domain}註冊ê理由。 + preamble: Nā是lí tī tsit ê服侍器有口座,lí ē當tuè別ê佇聯邦宇宙ê lâng,無論伊ê口座tī tó-tsi̍t站。 + title: Lán做伙設定 %{domain}。 status: account_status: 口座ê狀態 confirming: Teh等電子phue確認完成。 functional: Lí ê口座完全ē當用。 + pending: Lán ê人員teh處理lí ê申請。Tse可能愛一段時間。若是申請允准,lí ē收著e-mail。 + redirecting_to: Lí ê口座無活動,因為tann轉kàu %{acct}。 + self_destruct: 因為 %{domain} teh-beh關掉,lí kan-ta ē當有限接近使用lí ê口座。 + view_strikes: 看早前tuì lí ê口座ê警告 + too_fast: 添表ê速度傷緊,請koh試。 use_security_key: 用安全鎖 user_agreement_html: 我有讀而且同意 服務規定 kap 隱私權政策 user_privacy_agreement_html: 我有讀而且同意 隱私權政策 @@ -1306,6 +1315,53 @@ nan: less_than_x_seconds: 頭tú-á over_x_years: "%{count}年" x_days: "%{count}kang" + x_minutes: "%{count}分" + x_months: "%{count}個月" + x_seconds: "%{count}秒" + deletes: + challenge_not_passed: Lí輸入ê資訊毋著 + confirm_password: 輸入lí tsit-má ê密碼,驗證lí ê身份 + confirm_username: 輸入lí ê口座ê名,來確認程序 + proceed: Thâi掉口座 + success_msg: Lí ê口座thâi掉成功 + warning: + before: 佇繼續進前,請斟酌讀下kha ê說明: + caches: 已經hōo其他ê站the̍h著cache ê內容,可能iáu ē有資料 + data_removal: Lí ê PO文kap其他ê資料ē永永thâi掉。 + email_change_html: Lí ē當改lí ê電子phue地址,suah毋免thâi掉lí ê口座。 + email_contact_html: 若是iáu buē收著phue,lí通寄kàu %{email} tshuē幫tsān + email_reconfirmation_html: Nā是lí iáu bē收著驗證phue,lí通koh請求 + irreversible: Lí bē當復原á是重頭啟用lí ê口座 + more_details_html: 其他資訊,請看隱私政策。 + username_available: Lí ê用者名別lâng ē當申請 + username_unavailable: Lí ê口座ē保留,而且別lâng bē當用 + disputes: + strikes: + action_taken: 行ê行動 + appeal: 投訴 + appeal_approved: Tsit ê警告已經投訴成功,bē koh有效。 + appeal_rejected: Tsit ê投訴已經受拒絕 + appeal_submitted_at: 投訴送出去ah + appealed_msg: Lí ê投訴送出去ah。Nā受允准,ē通知lí。 + appeals: + submit: 送投訴 + approve_appeal: 允准投訴 + associated_report: 關聯ê報告 + created_at: 過時ê + description_html: Tsiah ê是 %{instance} ê人員送hōo lí ê,tuì lí ê口座行ê行動kap警告。 + recipient: 送kàu + reject_appeal: 拒絕投訴 + status: 'PO文 #%{id}' + status_removed: 嘟文已經tuì系統thâi掉 + title: "%{action} tuì %{date}" + title_actions: + delete_statuses: Thâi掉PO文 + disable: 冷凍口座 + mark_statuses_as_sensitive: Kā PO文標做敏感ê + none: 警告 + sensitive: Kā 口座文標做敏感ê + silence: 口座制限 + suspend: 停止口座權限 scheduled_statuses: too_soon: Tio̍h用未來ê日期。 statuses: diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml index 723164f3634944..dc6baffb154038 100644 --- a/config/locales/simple_form.bg.yml +++ b/config/locales/simple_form.bg.yml @@ -231,6 +231,7 @@ bg: setting_default_language: Език на публикуване setting_default_quote_policy: Кой може да цитира setting_default_sensitive: Все да се бележи мултимедията като деликатна + setting_delete_modal: Предупреждение преди изтриване на публикация setting_disable_hover_cards: Изключване на прегледа на профила, премествайки показалеца отгоре setting_disable_swiping: Деактивиране на бързо плъзгащи движения setting_display_media: Показване на мултимедия @@ -240,6 +241,7 @@ bg: setting_emoji_style: Стил на емоджито setting_expand_spoilers: Винаги разширяване на публикации, отбелязани с предупреждения за съдържание setting_hide_network: Скриване на социалния ви свързан граф + setting_missing_alt_text_modal: Предупреждение преди публикуване на мултимедия без алтернативен текст setting_quick_boosting: Включване на бързо подсилване setting_reduce_motion: Обездвижване на анимациите setting_system_font_ui: Употреба на стандартния шрифт на системата @@ -274,6 +276,7 @@ bg: content_cache_retention_period: Период на запазване на отдалечено съдържание custom_css: Персонализиран CSS favicon: Сайтоикона + landing_page: Целева страница за нови посетители mascot: Плашило талисман по избор (остаряло) media_cache_retention_period: Период на запазване на мултимедийния кеш min_age: Минимално възрастово изискване @@ -354,7 +357,9 @@ bg: jurisdiction: Законова юрисдикция min_age: Минимална възраст user: + date_of_birth_1i: Година date_of_birth_2i: Месец + date_of_birth_3i: Ден role: Роля time_zone: Часова зона user_role: diff --git a/config/locales/simple_form.el.yml b/config/locales/simple_form.el.yml index 4747e24b7bca14..f033d90dcfaae1 100644 --- a/config/locales/simple_form.el.yml +++ b/config/locales/simple_form.el.yml @@ -77,7 +77,7 @@ el: domain: Αυτό μπορεί να είναι το όνομα τομέα που εμφανίζεται στη διεύθυνση email ή η εγγραφή MX που χρησιμοποιεί. Θα ελέγχονται κατά την εγγραφή. with_dns_records: Θα γίνει απόπειρα ανάλυσης των εγγραφών DNS του τομέα και τα αποτελέσματα θα μπουν και αυτά σε μαύρη λίστα featured_tag: - name: 'Εδώ είναι μερικά από τα hashtags που χρησιμοποιήσατε περισσότερο πρόσφατα:' + name: 'Εδώ είναι μερικές από τις ετικέτες που χρησιμοποιήσατε περισσότερο πρόσφατα:' filters: action: Επιλέξτε ποια ενέργεια θα εκτελεστεί όταν μια ανάρτηση ταιριάζει με το φίλτρο actions: @@ -114,7 +114,7 @@ el: form_challenge: current_password: Μπαίνεις σε ασφαλή περιοχή imports: - data: Αρχείο CSV που έχει εξαχθεί από διαφορετικό κόμβο Mastodon + data: Αρχείο CSV που έχει εξαχθεί από διαφορετικό διακομιστή Mastodon invite_request: text: Αυτό θα μας βοηθήσει να επιθεωρήσουμε την αίτησή σου ip_block: @@ -173,7 +173,7 @@ el: url: Πού θα σταλούν τα γεγονότα labels: account: - attribution_domains: Ιστοσελίδες επιτρέπεται να σας αναφέρουν + attribution_domains: Ιστοσελίδες που επιτρέπεται να σας αναφέρουν discoverable: Παροχή προφίλ και αναρτήσεων σε αλγορίθμους ανακάλυψης fields: name: Περιγραφή @@ -210,7 +210,7 @@ el: text: Εξηγήστε γιατί αυτή η απόφαση πρέπει να αντιστραφεί defaults: autofollow: Προσκάλεσε για να ακολουθήσουν το λογαριασμό σου - avatar: Αβατάρ + avatar: Άβαταρ bot: Αυτός είναι ένας αυτοματοποιημένος λογαριασμός (bot) chosen_languages: Φιλτράρισμα γλωσσών confirm_new_password: Επιβεβαίωσε νέο συνθηματικό diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml index 533c6613017743..ae46605da65200 100644 --- a/config/locales/simple_form.es-MX.yml +++ b/config/locales/simple_form.es-MX.yml @@ -54,10 +54,10 @@ es-MX: password: Usa al menos 8 caracteres phrase: Se aplicará sin importar las mayúsculas o los avisos de contenido de una publicación scopes: Qué APIs de la aplicación tendrán acceso. Si seleccionas el alcance de nivel mas alto, no necesitas seleccionar las individuales. - setting_advanced_layout: Mostrar Mastodon con vista de varias columnas, permitiéndote ver tu cronología, notificaciones y una tercera columna que tú elijas. No recomendado para pantallas pequeñas. + setting_advanced_layout: Mostrar Mastodon en un diseño de varias columnas, lo que te permite ver la cronología, las notificaciones y una tercera columna de tu elección. No se recomienda para pantallas pequeñas. setting_aggregate_reblogs: No mostrar nuevos impulsos para las publicaciones que han sido recientemente impulsadas (sólo afecta a las publicaciones recibidas recientemente) setting_always_send_emails: Normalmente las notificaciones por correo electrónico no se enviarán cuando estés usando Mastodon activamente - setting_boost_modal: Si está activado, impulsar una publicación abrirá una ventana donde podrás cambiar la visibilidad de tu impulso. + setting_boost_modal: Cuando está habilitado, impulsar abrirá primero un cuadro de confirmación en el que podrás cambiar la visibilidad de tu impulso. setting_default_quote_policy_private: Las publicaciones solo para seguidores hechas en Mastodon no pueden ser citadas por otros usuarios. setting_default_quote_policy_unlisted: Cuando las personas te citen, su publicación también se ocultará en las cronologías públicas. setting_default_sensitive: El contenido multimedia sensible está oculto por defecto y puede ser mostrado con un clic diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index 7371c7ae0c7a72..568a45f16c9afa 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -79,6 +79,7 @@ ru: featured_tag: name: 'Вот некоторые хештеги, которые вы использовали чаще других в последнее время:' filters: + action: Выберите действие, которое нужно применить к постам, соответствующим фильтру actions: blur: Скрыть медиа за предупреждением, не скрывая сам текст поста hide: Полностью скрыть отфильтрованный пост, будто бы его не существует diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml index 7dc825d526eec3..e87dd799d2aa91 100644 --- a/config/locales/simple_form.sl.yml +++ b/config/locales/simple_form.sl.yml @@ -219,7 +219,9 @@ sl: setting_aggregate_reblogs: Skupinske izpostavitve na časovnicah setting_always_send_emails: Vedno pošlji e-obvestila setting_auto_play_gif: Samodejno predvajanje animiranih GIF-ov + setting_boost_modal: Nadziraj vidnost objav setting_default_language: Jezik objavljanja + setting_default_privacy: Vidnost objav setting_default_quote_policy: Kdo lahko citira setting_default_sensitive: Vedno označi medije kot občutljive setting_delete_modal: Pred brisanjem objave me opozori diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index ea9c69108e0793..7368426b0c7c9f 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -54,6 +54,7 @@ sv: password: Använd minst 8 tecken phrase: Matchas oavsett användande i text eller innehållsvarning för ett inlägg scopes: 'Vilka API: er applikationen kommer tillåtas åtkomst till. Om du väljer en omfattning på högstanivån behöver du inte välja individuella sådana.' + setting_advanced_layout: Visa Mastodon med en layout med flera kolumner, så att du kan se tidslinjen, aviseringar, och en tredje kolumn som du väljer själv. Rekommenderas inte för mindre skärmar. setting_aggregate_reblogs: Visa inte nya boostar för inlägg som nyligen blivit boostade (påverkar endast nymottagna boostar) setting_always_send_emails: E-postnotiser kommer vanligtvis inte skickas när du aktivt använder Mastodon setting_default_sensitive: Känslig media döljs som standard och kan visas med ett klick diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 6bb4a193bc5def..4cbd1eb43ba013 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -495,6 +495,24 @@ sl: new: title: Uvozi blokade domen no_file: Nobena datoteka ni izbrana + fasp: + debug: + callbacks: + delete: Izbriši + ip: Naslov IP + providers: + base_url: Osnovna povezava + callback: Povratni klic + delete: Izbriši + edit: Uredi ponudnika + finish_registration: Dokončaj registracijo + name: Ime + registrations: + confirm: Potrdi + reject: Zavrni + save: Shrani + sign_in: Prijava + status: Stanje follow_recommendations: description_html: "Sledi priporočilom pomaga novim uporabnikom, da hitro najdejo zanimivo vsebino. Če uporabnik ni dovolj komuniciral z drugimi, da bi oblikoval prilagojena priporočila za sledenje, se namesto tega priporočajo ti računi. Dnevno se ponovno izračunajo iz kombinacije računov z najvišjimi nedavnimi angažiranostmi in najvišjim številom krajevnih sledilcev za določen jezik." language: Za jezik @@ -569,6 +587,9 @@ sl: all: Vse limited: Omejeno title: Moderiranje + moderation_notes: + description_html: Pokaži in pusti opombe drugim moderatorjem in sebi v prihodnosti + title: Opombe moderiranja private_comment: Zasebni komentar public_comment: Javni komentar purge: Očisti @@ -783,11 +804,16 @@ sl: title: Vloge rules: add_new: Dodaj pravilo + add_translation: Dodaj prevod delete: Izbriši description_html: Večina trdi, da so prebrali in da se strinjajo s pogoji rabe storitve, vendar le-teh ponavadi ne preberejo, dokler ne pride do težav. Poenostavite in naredite pravila svojega strežnika vidna na prvi pogled tako, da jih izpišete v označenem seznamu.Posamezna pravila skušajte ohraniti kratka in enostavna, ne razbijajte pa jih v preveč različnih točk. edit: Uredi pravilo empty: Zaenkrat še ni opredeljenih pravil. + move_down: Premakni navzdol + move_up: Premakni navzgor title: Pravila strežnika + translation: Prevod + translations: Prevodi settings: about: manage_rules: Upravljaj pravila strežnika @@ -812,6 +838,7 @@ sl: title: Privzeto izvzemi uporabnike iz indeksiranja iskalnika discovery: follow_recommendations: Sledi priporočilom + privacy: Zasebnost profile_directory: Imenik profilov public_timelines: Javne časovnice publish_statistics: Objavi statistiko @@ -821,6 +848,9 @@ sl: all: Vsem disabled: Nikomur users: Prijavljenim krajevnim uporabnikom + landing_page: + values: + trends: Trendi registrations: moderation_recommandation: Preden prijave odprete za vse poskrbite, da imate v ekipi moderatorjev zadosti aktivnih članov. preamble: Nadzirajte, kdo lahko ustvari račun na vašem strežniku. @@ -874,6 +904,7 @@ sl: no_status_selected: Nobena objava ni bila spremenjena, ker ni bila nobena izbrana open: Odpri objavo original_status: Izvorna objava + quotes: Citati reblogs: Ponovljeni blogi replied_to_html: V odgovor %{acct_link} status_changed: Objava spremenjena @@ -1070,6 +1101,15 @@ sl: two: Uporabili %{count} osebi v zadnjem tednu title: Priporočila in trendi trending: V porastu + username_blocks: + add_new: Dodaj novo + comparison: + contains: Vsebuje + contains_html: Vsebuje %{string} + delete: Izbriši + new: + create: Ustvari pravilo + not_permitted: Ni dovoljeno warning_presets: add_new: Dodaj novo delete: Izbriši @@ -1144,6 +1184,7 @@ sl: hint_html: Če se želite preseliti iz drugega računa v tega, lahko tukaj ustvarite vzdevek, ki je potreben, preden lahko nadaljujete s selitvijo sledilcev iz starega računa v tega. To dejanje je samo po sebi neškodljivo in povratno. Selitev računa sprožite iz starega računa. remove: Razveži vzdevek appearance: + advanced_settings: Napredne nastavitve animations_and_accessibility: Animacije in dostopnost discovery: Odkrito localization: @@ -1333,6 +1374,8 @@ sl: basic_information: Osnovni podatki hint_html: "Prilagodite, kaj ljudje vidijo na vašem javnem profilu in poleg vaših objav. Drugi vam bodo raje sledili nazaj in z vami klepetali, če boste imeli izpolnjen profil in nastavljeno profilno sliko." other: Drugo + emoji_styles: + auto: Samodejno errors: '400': Zahteva, ki ste jo oddali, je neveljavna ali nepravilno oblikovana. '403': Nimate dovoljenja za ogled te strani. @@ -1582,6 +1625,11 @@ sl: expires_at: Poteče uses: Uporabe title: Povabite ljudi + link_preview: + author_html: Avtor/ica %{name} + potentially_sensitive_content: + action: Kliknite za prikaz + hide_button: Skrij lists: errors: limit: Dosegli ste največje število seznamov @@ -1681,6 +1729,9 @@ sl: title: Nova omemba poll: subject: Anketa, ki jo je pripravil/a %{name}, se je iztekla + quote: + body: 'Vašo objavo je citiral/a %{name}:' + title: Nov citat reblog: body: 'Vašo objavo je izpostavil/a %{name}:' subject: "%{name} je izpostavil/a vašo objavo" @@ -1729,6 +1780,7 @@ sl: self_vote: Ne morete glasovati v lastnih anketah too_few_options: mora imeti več kot en element too_many_options: ne more vsebovati več kot %{max} elementov + vote: Glasuj preferences: other: Ostalo posting_defaults: Privzete nastavitev objavljanja @@ -1890,6 +1942,9 @@ sl: two: "%{count} video posnetka" boosted_from_html: Izpostavljeno z računa %{acct_link} content_warning: 'Opozorilo o vsebini: %{warning}' + content_warnings: + hide: Skrij objavo + show: Pokaži več default_language: Enak kot jezik vmesnika disallowed_hashtags: few: 'vsebuje nedovoljene ključnike: %{tags}' @@ -1905,9 +1960,19 @@ sl: limit: Pripeli ste največje število objav ownership: Objava nekoga drugega ne more biti pripeta reblog: Izpostavitev ne more biti pripeta + quote_error: + not_available: Objava ni na voljo + quote_policies: + followers: Samo sledilci + nobody: Samo jaz + public: Vsi title: "%{name}: »%{quote}«" visibilities: + direct: Zasebna omemba + private: Samo sledilci public: Javno + public_long: Vsem, ki so ali niso na Mastodonu + unlisted: Tiho javno statuses_cleanup: enabled: Samodejno izbriši stare objave enabled_hint: Samodejno izbriše vaše objave, ko dosežejo določen starostni prag, razen če ne ustrezajo eni od spodnjih izjem @@ -1952,6 +2017,8 @@ sl: does_not_match_previous_name: se ne ujema s prejšnjim imenom terms_of_service: title: Pogoji uporabe + terms_of_service_interstitial: + title: Spreminjajo se pogoji uporabe domene %{domain} themes: contrast: Mastodon (Visok kontrast) default: Mastodon (Temna) From 1bc13609ab001cea72b80d47d81f0d3be9b1c30b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:42:56 +0100 Subject: [PATCH 014/123] chore(deps): update dependency aws-sdk-s3 to v1.205.0 (#36956) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6194227ed77ce6..0d0977573a607c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,8 +96,8 @@ GEM ast (2.4.3) attr_required (1.0.2) aws-eventstream (1.4.0) - aws-partitions (1.1181.0) - aws-sdk-core (3.237.0) + aws-partitions (1.1186.0) + aws-sdk-core (3.239.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) @@ -105,10 +105,10 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.116.0) + aws-sdk-kms (1.117.0) aws-sdk-core (~> 3, >= 3.234.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.203.1) + aws-sdk-s3 (1.205.0) aws-sdk-core (~> 3, >= 3.234.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) From b16452dd999c11de8a810934a9092e4861ae7124 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 24 Nov 2025 09:48:49 +0100 Subject: [PATCH 015/123] Add shared context for API authentication (#36981) --- spec/fabricators/user_fabricator.rb | 6 ++++++ spec/requests/api/v1/accounts/credentials_spec.rb | 5 +---- spec/requests/api/v1/accounts/endorsements_spec.rb | 6 ++---- spec/requests/api/v1/accounts/familiar_followers_spec.rb | 6 ++---- spec/requests/api/v1/accounts/featured_tags_spec.rb | 8 +++----- spec/requests/api/v1/accounts/follower_accounts_spec.rb | 6 ++---- spec/requests/api/v1/accounts/following_accounts_spec.rb | 6 ++---- spec/requests/api/v1/accounts/identity_proofs_spec.rb | 6 ++---- spec/requests/api/v1/accounts/lists_spec.rb | 6 ++---- spec/requests/api/v1/accounts/lookup_spec.rb | 6 ++---- spec/requests/api/v1/accounts/notes_spec.rb | 6 ++---- spec/requests/api/v1/accounts/relationships_spec.rb | 5 +---- spec/requests/api/v1/accounts/search_spec.rb | 5 +---- spec/requests/api/v1/accounts/statuses_spec.rb | 5 +---- spec/requests/api/v1/accounts_spec.rb | 5 +---- spec/requests/api/v1/admin/account_actions_spec.rb | 6 +----- spec/requests/api/v1/admin/accounts_spec.rb | 6 +----- spec/requests/api/v1/admin/canonical_email_blocks_spec.rb | 6 +----- spec/requests/api/v1/admin/dimensions_spec.rb | 5 ++--- spec/requests/api/v1/admin/domain_allows_spec.rb | 6 +----- spec/requests/api/v1/admin/domain_blocks_spec.rb | 6 +----- spec/requests/api/v1/admin/email_domain_blocks_spec.rb | 7 +------ spec/requests/api/v1/admin/ip_blocks_spec.rb | 6 +----- spec/requests/api/v1/admin/measures_spec.rb | 6 ++---- spec/requests/api/v1/admin/reports_spec.rb | 6 +----- spec/requests/api/v1/admin/retention_spec.rb | 5 ++--- spec/requests/api/v1/admin/tags_spec.rb | 6 ++---- spec/requests/api/v1/admin/trends/links/links_spec.rb | 6 +----- .../v1/admin/trends/links/preview_card_providers_spec.rb | 7 ++----- spec/requests/api/v1/admin/trends/statuses_spec.rb | 7 ++----- spec/requests/api/v1/admin/trends/tags_spec.rb | 7 ++----- spec/requests/api/v1/announcements_spec.rb | 5 +---- spec/requests/api/v1/annual_reports_spec.rb | 4 +--- spec/requests/api/v1/blocks_spec.rb | 5 +---- spec/requests/api/v1/bookmarks_spec.rb | 5 +---- spec/requests/api/v1/conversations_spec.rb | 5 ++--- spec/requests/api/v1/custom_emojis_spec.rb | 4 +--- spec/requests/api/v1/directories_spec.rb | 5 ++--- spec/requests/api/v1/domain_blocks_spec.rb | 5 +---- spec/requests/api/v1/endorsements_spec.rb | 4 +--- spec/requests/api/v1/favourites_spec.rb | 5 +---- spec/requests/api/v1/featured_tags_spec.rb | 5 +---- spec/requests/api/v1/filters_spec.rb | 4 +--- spec/requests/api/v1/follow_requests_spec.rb | 7 +++---- spec/requests/api/v1/followed_tags_spec.rb | 5 +---- spec/requests/api/v1/instance_spec.rb | 4 +--- spec/requests/api/v1/lists_spec.rb | 5 +---- spec/requests/api/v1/markers_spec.rb | 5 +---- spec/requests/api/v1/media_spec.rb | 5 +---- spec/requests/api/v1/mutes_spec.rb | 5 +---- spec/requests/api/v1/notifications_spec.rb | 5 ++--- spec/requests/api/v1/polls_spec.rb | 5 +---- spec/requests/api/v1/preferences_spec.rb | 4 +--- spec/requests/api/v1/profiles_spec.rb | 6 +++--- spec/requests/api/v1/reports_spec.rb | 5 +---- spec/requests/api/v1/scheduled_status_spec.rb | 4 +--- spec/requests/api/v1/statuses_spec.rb | 4 ++-- spec/requests/api/v1/suggestions_spec.rb | 5 +---- spec/requests/api/v1/tags_spec.rb | 5 +---- spec/requests/api/v1_alpha/async_refreshes_spec.rb | 5 ++--- spec/requests/api/v2/filters_spec.rb | 5 +---- spec/requests/api/v2/instance_spec.rb | 4 +--- spec/requests/api/v2/media_spec.rb | 5 +---- spec/requests/api/v2/notifications_spec.rb | 5 ++--- spec/requests/api/v2/search_spec.rb | 5 +---- spec/requests/api/v2/suggestions_spec.rb | 5 +---- spec/requests/api/web/embeds_spec.rb | 4 +--- spec/support/api_authentication.rb | 8 ++++++++ spec/support/examples/api.rb | 1 + 69 files changed, 108 insertions(+), 259 deletions(-) create mode 100644 spec/support/api_authentication.rb diff --git a/spec/fabricators/user_fabricator.rb b/spec/fabricators/user_fabricator.rb index 104d7f99314d2e..300a6d9ba6743b 100644 --- a/spec/fabricators/user_fabricator.rb +++ b/spec/fabricators/user_fabricator.rb @@ -25,3 +25,9 @@ Fabricator(:owner_user, from: :user) do role UserRole.find_by(name: 'Owner') end + +Fabricator(:private_user, from: :user) do + account_attributes do + { discoverable: false, locked: true, indexable: false } + end +end diff --git a/spec/requests/api/v1/accounts/credentials_spec.rb b/spec/requests/api/v1/accounts/credentials_spec.rb index 4316c1409d0574..f68ebbdb2285d8 100644 --- a/spec/requests/api/v1/accounts/credentials_spec.rb +++ b/spec/requests/api/v1/accounts/credentials_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'credentials API' do - let(:user) { Fabricate(:user, account_attributes: { discoverable: false, locked: true, indexable: false }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :private_user, oauth_scopes: 'read:accounts write:accounts' describe 'GET /api/v1/accounts/verify_credentials' do subject do diff --git a/spec/requests/api/v1/accounts/endorsements_spec.rb b/spec/requests/api/v1/accounts/endorsements_spec.rb index 6e0996a1f1bc4b..c8ad5297726c4c 100644 --- a/spec/requests/api/v1/accounts/endorsements_spec.rb +++ b/spec/requests/api/v1/accounts/endorsements_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Pins API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:accounts' + let(:kevin) { Fabricate(:user) } before do diff --git a/spec/requests/api/v1/accounts/familiar_followers_spec.rb b/spec/requests/api/v1/accounts/familiar_followers_spec.rb index c698c2d6892a72..7c71f36a245857 100644 --- a/spec/requests/api/v1/accounts/familiar_followers_spec.rb +++ b/spec/requests/api/v1/accounts/familiar_followers_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Familiar Followers API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:follows' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:follows' + let(:account) { Fabricate(:account) } describe 'GET /api/v1/accounts/familiar_followers' do diff --git a/spec/requests/api/v1/accounts/featured_tags_spec.rb b/spec/requests/api/v1/accounts/featured_tags_spec.rb index 54d92eb1cf0e42..f2aaf8dfd6884b 100644 --- a/spec/requests/api/v1/accounts/featured_tags_spec.rb +++ b/spec/requests/api/v1/accounts/featured_tags_spec.rb @@ -3,11 +3,9 @@ require 'rails_helper' RSpec.describe 'account featured tags API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - let(:account) { Fabricate(:account) } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + + let(:account) { Fabricate(:account) } describe 'GET /api/v1/accounts/:id/featured_tags' do subject do diff --git a/spec/requests/api/v1/accounts/follower_accounts_spec.rb b/spec/requests/api/v1/accounts/follower_accounts_spec.rb index 61987fac1cc667..1f0779701b2049 100644 --- a/spec/requests/api/v1/accounts/follower_accounts_spec.rb +++ b/spec/requests/api/v1/accounts/follower_accounts_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'API V1 Accounts FollowerAccounts' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + let(:account) { Fabricate(:account) } let(:alice) { Fabricate(:account) } let(:bob) { Fabricate(:account) } diff --git a/spec/requests/api/v1/accounts/following_accounts_spec.rb b/spec/requests/api/v1/accounts/following_accounts_spec.rb index aae811467d2243..193cf2196f03f6 100644 --- a/spec/requests/api/v1/accounts/following_accounts_spec.rb +++ b/spec/requests/api/v1/accounts/following_accounts_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'API V1 Accounts FollowingAccounts' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + let(:account) { Fabricate(:account) } let(:alice) { Fabricate(:account) } let(:bob) { Fabricate(:account) } diff --git a/spec/requests/api/v1/accounts/identity_proofs_spec.rb b/spec/requests/api/v1/accounts/identity_proofs_spec.rb index ba04ed45b9f8b5..21b32fd3b40eec 100644 --- a/spec/requests/api/v1/accounts/identity_proofs_spec.rb +++ b/spec/requests/api/v1/accounts/identity_proofs_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Identity Proofs API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + let(:account) { Fabricate(:account) } describe 'GET /api/v1/accounts/identity_proofs' do diff --git a/spec/requests/api/v1/accounts/lists_spec.rb b/spec/requests/api/v1/accounts/lists_spec.rb index cb1ff6b9f28a7c..63b1dc7816ffd6 100644 --- a/spec/requests/api/v1/accounts/lists_spec.rb +++ b/spec/requests/api/v1/accounts/lists_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Lists API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:lists' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:lists' + let(:account) { Fabricate(:account) } let(:list) { Fabricate(:list, account: user.account) } diff --git a/spec/requests/api/v1/accounts/lookup_spec.rb b/spec/requests/api/v1/accounts/lookup_spec.rb index 77c09c0902b502..a4d125a87cdfd2 100644 --- a/spec/requests/api/v1/accounts/lookup_spec.rb +++ b/spec/requests/api/v1/accounts/lookup_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Lookup API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' + let(:account) { Fabricate(:account) } describe 'GET /api/v1/accounts/lookup' do diff --git a/spec/requests/api/v1/accounts/notes_spec.rb b/spec/requests/api/v1/accounts/notes_spec.rb index e616df1e6f4857..e70a465153386b 100644 --- a/spec/requests/api/v1/accounts/notes_spec.rb +++ b/spec/requests/api/v1/accounts/notes_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Accounts Notes API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:accounts' + let(:account) { Fabricate(:account) } let(:comment) { 'foo' } diff --git a/spec/requests/api/v1/accounts/relationships_spec.rb b/spec/requests/api/v1/accounts/relationships_spec.rb index 52aeb0132807ad..d735e4d2410021 100644 --- a/spec/requests/api/v1/accounts/relationships_spec.rb +++ b/spec/requests/api/v1/accounts/relationships_spec.rb @@ -7,10 +7,7 @@ get '/api/v1/accounts/relationships', headers: headers, params: params end - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:follows' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:follows' let(:simon) { Fabricate(:account) } let(:lewis) { Fabricate(:account) } diff --git a/spec/requests/api/v1/accounts/search_spec.rb b/spec/requests/api/v1/accounts/search_spec.rb index dc24813e7392ad..5a01628c9ac0e1 100644 --- a/spec/requests/api/v1/accounts/search_spec.rb +++ b/spec/requests/api/v1/accounts/search_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Accounts Search API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts' describe 'GET /api/v1/accounts/search' do it 'returns http success' do diff --git a/spec/requests/api/v1/accounts/statuses_spec.rb b/spec/requests/api/v1/accounts/statuses_spec.rb index 1e219502874352..0a1daa0488b231 100644 --- a/spec/requests/api/v1/accounts/statuses_spec.rb +++ b/spec/requests/api/v1/accounts/statuses_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'API V1 Accounts Statuses' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:statuses' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:statuses' describe 'GET /api/v1/accounts/:account_id/statuses' do it 'returns expected headers', :aggregate_failures do diff --git a/spec/requests/api/v1/accounts_spec.rb b/spec/requests/api/v1/accounts_spec.rb index 9bbce4877ef663..c6a131062b3c68 100644 --- a/spec/requests/api/v1/accounts_spec.rb +++ b/spec/requests/api/v1/accounts_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe '/api/v1/accounts' do - let(:user) { Fabricate(:user) } - let(:scopes) { '' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/accounts?id[]=:id' do let(:account) { Fabricate(:account) } diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb index 4884dba9c79dfc..c1273f468dd1b0 100644 --- a/spec/requests/api/v1/admin/account_actions_spec.rb +++ b/spec/requests/api/v1/admin/account_actions_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Account actions' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:write admin:write:accounts' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:write admin:write:accounts' shared_examples 'a successful notification delivery' do it 'notifies the user about the action taken', :inline_jobs do diff --git a/spec/requests/api/v1/admin/accounts_spec.rb b/spec/requests/api/v1/admin/accounts_spec.rb index 6a681f9c5e5b2a..d94fcd31e40a39 100644 --- a/spec/requests/api/v1/admin/accounts_spec.rb +++ b/spec/requests/api/v1/admin/accounts_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Accounts' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read:accounts admin:write:accounts' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:accounts admin:write:accounts' describe 'GET /api/v1/admin/accounts' do subject do diff --git a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb index 25af0a26afef4d..b3af0a923d905d 100644 --- a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb +++ b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Canonical Email Blocks' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'admin:read:canonical_email_blocks admin:write:canonical_email_blocks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:canonical_email_blocks admin:write:canonical_email_blocks' describe 'GET /api/v1/admin/canonical_email_blocks' do subject do diff --git a/spec/requests/api/v1/admin/dimensions_spec.rb b/spec/requests/api/v1/admin/dimensions_spec.rb index 3a4cd91716ad5d..488a13ef0f2ff8 100644 --- a/spec/requests/api/v1/admin/dimensions_spec.rb +++ b/spec/requests/api/v1/admin/dimensions_spec.rb @@ -3,9 +3,8 @@ require 'rails_helper' RSpec.describe 'Admin Dimensions' do - let(:user) { Fabricate(:admin_user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user + let(:account) { Fabricate(:account) } describe 'GET /api/v1/admin/dimensions' do diff --git a/spec/requests/api/v1/admin/domain_allows_spec.rb b/spec/requests/api/v1/admin/domain_allows_spec.rb index fba1eb15d373fb..f3ae4076dbc242 100644 --- a/spec/requests/api/v1/admin/domain_allows_spec.rb +++ b/spec/requests/api/v1/admin/domain_allows_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Domain Allows' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' describe 'GET /api/v1/admin/domain_allows' do subject do diff --git a/spec/requests/api/v1/admin/domain_blocks_spec.rb b/spec/requests/api/v1/admin/domain_blocks_spec.rb index 0b01d04f9a0fdb..d532b0d25f980b 100644 --- a/spec/requests/api/v1/admin/domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/domain_blocks_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Domain Blocks' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read:domain_blocks admin:write:domain_blocks' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:domain_blocks admin:write:domain_blocks' describe 'GET /api/v1/admin/domain_blocks' do subject do diff --git a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb index 2788a45a4a2991..a75b5abde4fda5 100644 --- a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb +++ b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb @@ -3,12 +3,7 @@ require 'rails_helper' RSpec.describe 'Email Domain Blocks' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:account) { Fabricate(:account) } - let(:scopes) { 'admin:read:email_domain_blocks admin:write:email_domain_blocks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:email_domain_blocks admin:write:email_domain_blocks' describe 'GET /api/v1/admin/email_domain_blocks' do subject do diff --git a/spec/requests/api/v1/admin/ip_blocks_spec.rb b/spec/requests/api/v1/admin/ip_blocks_spec.rb index 161781c8ed32d8..a9d62752941353 100644 --- a/spec/requests/api/v1/admin/ip_blocks_spec.rb +++ b/spec/requests/api/v1/admin/ip_blocks_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'IP Blocks' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'admin:read:ip_blocks admin:write:ip_blocks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:ip_blocks admin:write:ip_blocks' describe 'GET /api/v1/admin/ip_blocks' do subject do diff --git a/spec/requests/api/v1/admin/measures_spec.rb b/spec/requests/api/v1/admin/measures_spec.rb index b55cd0f1b200cc..6c35da5656d044 100644 --- a/spec/requests/api/v1/admin/measures_spec.rb +++ b/spec/requests/api/v1/admin/measures_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Admin Measures' do - let(:user) { Fabricate(:admin_user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - let(:account) { Fabricate(:account) } + include_context 'with API authentication', user_fabricator: :admin_user + let(:params) do { keys: %w(instance_accounts instance_follows instance_followers), diff --git a/spec/requests/api/v1/admin/reports_spec.rb b/spec/requests/api/v1/admin/reports_spec.rb index 987f0eda7fb816..54dd4c9c8c49fe 100644 --- a/spec/requests/api/v1/admin/reports_spec.rb +++ b/spec/requests/api/v1/admin/reports_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Reports' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read:reports admin:write:reports' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read:reports admin:write:reports' describe 'GET /api/v1/admin/reports' do subject do diff --git a/spec/requests/api/v1/admin/retention_spec.rb b/spec/requests/api/v1/admin/retention_spec.rb index 25e626e259329f..9c7be0981d3042 100644 --- a/spec/requests/api/v1/admin/retention_spec.rb +++ b/spec/requests/api/v1/admin/retention_spec.rb @@ -3,9 +3,8 @@ require 'rails_helper' RSpec.describe 'Admin Retention' do - let(:user) { Fabricate(:admin_user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user + let(:account) { Fabricate(:account) } describe 'GET /api/v1/admin/retention' do diff --git a/spec/requests/api/v1/admin/tags_spec.rb b/spec/requests/api/v1/admin/tags_spec.rb index 3a57432af78c48..c84536d1b57d78 100644 --- a/spec/requests/api/v1/admin/tags_spec.rb +++ b/spec/requests/api/v1/admin/tags_spec.rb @@ -3,10 +3,8 @@ require 'rails_helper' RSpec.describe 'Tags' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' + let(:tag) { Fabricate(:tag) } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } diff --git a/spec/requests/api/v1/admin/trends/links/links_spec.rb b/spec/requests/api/v1/admin/trends/links/links_spec.rb index 51e800734aaaec..e49c9a0709a51d 100644 --- a/spec/requests/api/v1/admin/trends/links/links_spec.rb +++ b/spec/requests/api/v1/admin/trends/links/links_spec.rb @@ -3,11 +3,7 @@ require 'rails_helper' RSpec.describe 'Links' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' describe 'GET /api/v1/admin/trends/links' do subject do diff --git a/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb b/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb index d46d0ff5555404..5fe6cac47f51d1 100644 --- a/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb +++ b/spec/requests/api/v1/admin/trends/links/preview_card_providers_spec.rb @@ -3,11 +3,8 @@ require 'rails_helper' RSpec.describe 'API V1 Admin Trends Links Preview Card Providers' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' + let(:account) { Fabricate(:account) } let(:preview_card_provider) { Fabricate(:preview_card_provider) } diff --git a/spec/requests/api/v1/admin/trends/statuses_spec.rb b/spec/requests/api/v1/admin/trends/statuses_spec.rb index c63d8d925c7c98..90a3d80a2a4e8e 100644 --- a/spec/requests/api/v1/admin/trends/statuses_spec.rb +++ b/spec/requests/api/v1/admin/trends/statuses_spec.rb @@ -3,13 +3,10 @@ require 'rails_helper' RSpec.describe 'API V1 Admin Trends Statuses' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' + let(:account) { Fabricate(:account) } let(:status) { Fabricate(:status) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/admin/trends/statuses' do it 'returns http success' do diff --git a/spec/requests/api/v1/admin/trends/tags_spec.rb b/spec/requests/api/v1/admin/trends/tags_spec.rb index 433cc6c5a6e5f3..750ee8975d3da6 100644 --- a/spec/requests/api/v1/admin/trends/tags_spec.rb +++ b/spec/requests/api/v1/admin/trends/tags_spec.rb @@ -3,13 +3,10 @@ require 'rails_helper' RSpec.describe 'API V1 Admin Trends Tags' do - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + include_context 'with API authentication', user_fabricator: :admin_user, oauth_scopes: 'admin:read admin:write' + let(:account) { Fabricate(:account) } let(:tag) { Fabricate(:tag) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/admin/trends/tags' do it 'returns http success' do diff --git a/spec/requests/api/v1/announcements_spec.rb b/spec/requests/api/v1/announcements_spec.rb index 97a4442aa98855..b7ba47e1d2d873 100644 --- a/spec/requests/api/v1/announcements_spec.rb +++ b/spec/requests/api/v1/announcements_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'API V1 Announcements' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read' let!(:announcement) { Fabricate(:announcement) } diff --git a/spec/requests/api/v1/annual_reports_spec.rb b/spec/requests/api/v1/annual_reports_spec.rb index b9831d17e2c4f9..88a7dbdd8251bd 100644 --- a/spec/requests/api/v1/annual_reports_spec.rb +++ b/spec/requests/api/v1/annual_reports_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'API V1 Annual Reports' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/annual_reports' do context 'when not authorized' do diff --git a/spec/requests/api/v1/blocks_spec.rb b/spec/requests/api/v1/blocks_spec.rb index 498cf932756de8..f02739d911379f 100644 --- a/spec/requests/api/v1/blocks_spec.rb +++ b/spec/requests/api/v1/blocks_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Blocks' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:blocks' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:blocks' describe 'GET /api/v1/blocks' do subject do diff --git a/spec/requests/api/v1/bookmarks_spec.rb b/spec/requests/api/v1/bookmarks_spec.rb index c78e6912365b51..bd61644f22d70c 100644 --- a/spec/requests/api/v1/bookmarks_spec.rb +++ b/spec/requests/api/v1/bookmarks_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Bookmarks' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:bookmarks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:bookmarks' describe 'GET /api/v1/bookmarks' do subject do diff --git a/spec/requests/api/v1/conversations_spec.rb b/spec/requests/api/v1/conversations_spec.rb index 6e2ac1df53e0a8..6c928bc43293eb 100644 --- a/spec/requests/api/v1/conversations_spec.rb +++ b/spec/requests/api/v1/conversations_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'API V1 Conversations' do + include_context 'with API authentication', oauth_scopes: 'read:statuses' + let!(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) } - let(:scopes) { 'read:statuses' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } let(:other) { Fabricate(:user) } diff --git a/spec/requests/api/v1/custom_emojis_spec.rb b/spec/requests/api/v1/custom_emojis_spec.rb index e860fbeb17c105..8b832540eff07d 100644 --- a/spec/requests/api/v1/custom_emojis_spec.rb +++ b/spec/requests/api/v1/custom_emojis_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Custom Emojis' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/custom_emojis' do before do diff --git a/spec/requests/api/v1/directories_spec.rb b/spec/requests/api/v1/directories_spec.rb index 07e65f49b75b54..69bfdc834350c0 100644 --- a/spec/requests/api/v1/directories_spec.rb +++ b/spec/requests/api/v1/directories_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'Directories API' do + include_context 'with API authentication', oauth_scopes: 'read:follows' + let(:user) { Fabricate(:user, confirmed_at: nil) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:follows' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/directories' do context 'with no params' do diff --git a/spec/requests/api/v1/domain_blocks_spec.rb b/spec/requests/api/v1/domain_blocks_spec.rb index 339f49fe761f75..0843c479837320 100644 --- a/spec/requests/api/v1/domain_blocks_spec.rb +++ b/spec/requests/api/v1/domain_blocks_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Domain blocks' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:blocks write:blocks' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:blocks write:blocks' describe 'GET /api/v1/domain_blocks' do subject do diff --git a/spec/requests/api/v1/endorsements_spec.rb b/spec/requests/api/v1/endorsements_spec.rb index 730ba6350cf05e..07ca476f3a89fc 100644 --- a/spec/requests/api/v1/endorsements_spec.rb +++ b/spec/requests/api/v1/endorsements_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Endorsements' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/endorsements' do context 'when not authorized' do diff --git a/spec/requests/api/v1/favourites_spec.rb b/spec/requests/api/v1/favourites_spec.rb index 44d0239556b2ab..dcc0286e0b0313 100644 --- a/spec/requests/api/v1/favourites_spec.rb +++ b/spec/requests/api/v1/favourites_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Favourites' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:favourites' } - let(:headers) { { Authorization: "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:favourites' describe 'GET /api/v1/favourites' do subject do diff --git a/spec/requests/api/v1/featured_tags_spec.rb b/spec/requests/api/v1/featured_tags_spec.rb index 7a5f92cdfd418b..eafbe2185eb360 100644 --- a/spec/requests/api/v1/featured_tags_spec.rb +++ b/spec/requests/api/v1/featured_tags_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'FeaturedTags' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:accounts write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:accounts write:accounts' describe 'GET /api/v1/featured_tags' do context 'with wrong scope' do diff --git a/spec/requests/api/v1/filters_spec.rb b/spec/requests/api/v1/filters_spec.rb index 51f03cc04d44c7..32103a93429796 100644 --- a/spec/requests/api/v1/filters_spec.rb +++ b/spec/requests/api/v1/filters_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'API V1 Filters' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/filters' do let(:scopes) { 'read:filters' } diff --git a/spec/requests/api/v1/follow_requests_spec.rb b/spec/requests/api/v1/follow_requests_spec.rb index f0f73d38ad0d9e..7ebfce3d918053 100644 --- a/spec/requests/api/v1/follow_requests_spec.rb +++ b/spec/requests/api/v1/follow_requests_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'Follow requests' do - let(:user) { Fabricate(:user, account_attributes: { locked: true }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:follows write:follows' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:follows write:follows' + + let(:user) { Fabricate(:user, account_attributes: { locked: true }) } describe 'GET /api/v1/follow_requests' do subject do diff --git a/spec/requests/api/v1/followed_tags_spec.rb b/spec/requests/api/v1/followed_tags_spec.rb index b0191b523fc308..a19162c012f11b 100644 --- a/spec/requests/api/v1/followed_tags_spec.rb +++ b/spec/requests/api/v1/followed_tags_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Followed tags' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:follows' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:follows' describe 'GET /api/v1/followed_tags' do subject do diff --git a/spec/requests/api/v1/instance_spec.rb b/spec/requests/api/v1/instance_spec.rb index 821cbfec6146cf..a0cd32e5ab6f35 100644 --- a/spec/requests/api/v1/instance_spec.rb +++ b/spec/requests/api/v1/instance_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Instances' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/instance' do context 'when not logged in' do diff --git a/spec/requests/api/v1/lists_spec.rb b/spec/requests/api/v1/lists_spec.rb index 226632c5ac2935..04a03998ad573b 100644 --- a/spec/requests/api/v1/lists_spec.rb +++ b/spec/requests/api/v1/lists_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Lists' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:lists write:lists' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:lists write:lists' describe 'GET /api/v1/lists' do subject do diff --git a/spec/requests/api/v1/markers_spec.rb b/spec/requests/api/v1/markers_spec.rb index d7cd78924bcec1..0e6ecc56855b23 100644 --- a/spec/requests/api/v1/markers_spec.rb +++ b/spec/requests/api/v1/markers_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'API Markers' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:statuses write:statuses' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:statuses write:statuses' describe 'GET /api/v1/markers' do before do diff --git a/spec/requests/api/v1/media_spec.rb b/spec/requests/api/v1/media_spec.rb index 4d6e250207d022..347ff4b27979df 100644 --- a/spec/requests/api/v1/media_spec.rb +++ b/spec/requests/api/v1/media_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Media' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write:media' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:media' describe 'GET /api/v1/media/:id' do subject do diff --git a/spec/requests/api/v1/mutes_spec.rb b/spec/requests/api/v1/mutes_spec.rb index 61e32cb9ae0452..4b94a5cb5a85c7 100644 --- a/spec/requests/api/v1/mutes_spec.rb +++ b/spec/requests/api/v1/mutes_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Mutes' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:mutes' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:mutes' describe 'GET /api/v1/mutes' do subject do diff --git a/spec/requests/api/v1/notifications_spec.rb b/spec/requests/api/v1/notifications_spec.rb index 0e8eb6ad3ba2e6..f2ff396795c59d 100644 --- a/spec/requests/api/v1/notifications_spec.rb +++ b/spec/requests/api/v1/notifications_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'Notifications' do + include_context 'with API authentication', oauth_scopes: 'read:notifications write:notifications' + let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:notifications write:notifications' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/notifications/unread_count', :inline_jobs do subject do diff --git a/spec/requests/api/v1/polls_spec.rb b/spec/requests/api/v1/polls_spec.rb index c93231e1ee6a44..f01f112da9de26 100644 --- a/spec/requests/api/v1/polls_spec.rb +++ b/spec/requests/api/v1/polls_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Polls' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:statuses' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:statuses' describe 'GET /api/v1/polls/:id' do subject do diff --git a/spec/requests/api/v1/preferences_spec.rb b/spec/requests/api/v1/preferences_spec.rb index e03b9cf1087cd6..02d63c9d28ce88 100644 --- a/spec/requests/api/v1/preferences_spec.rb +++ b/spec/requests/api/v1/preferences_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Preferences' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/preferences' do context 'when not authorized' do diff --git a/spec/requests/api/v1/profiles_spec.rb b/spec/requests/api/v1/profiles_spec.rb index de7a20b133cacd..131df7a278ed84 100644 --- a/spec/requests/api/v1/profiles_spec.rb +++ b/spec/requests/api/v1/profiles_spec.rb @@ -3,6 +3,8 @@ require 'rails_helper' RSpec.describe 'Deleting profile images' do + include_context 'with API authentication', oauth_scopes: 'write:accounts' + let(:account) do Fabricate( :account, @@ -10,9 +12,7 @@ header: fixture_file_upload('attachment.jpg', 'image/jpeg') ) end - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: account.user.id, scopes: scopes) } - let(:scopes) { 'write:accounts' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + let(:user) { account.user } describe 'DELETE /api/v1/profile' do context 'when deleting an avatar' do diff --git a/spec/requests/api/v1/reports_spec.rb b/spec/requests/api/v1/reports_spec.rb index 1f113c649ee4b9..38d9f542c29b43 100644 --- a/spec/requests/api/v1/reports_spec.rb +++ b/spec/requests/api/v1/reports_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Reports' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write:reports' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:reports' describe 'POST /api/v1/reports' do subject do diff --git a/spec/requests/api/v1/scheduled_status_spec.rb b/spec/requests/api/v1/scheduled_status_spec.rb index 3a1b81ce65c1d4..ad446257b40f47 100644 --- a/spec/requests/api/v1/scheduled_status_spec.rb +++ b/spec/requests/api/v1/scheduled_status_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Scheduled Statuses' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v1/scheduled_statuses' do context 'when not authorized' do diff --git a/spec/requests/api/v1/statuses_spec.rb b/spec/requests/api/v1/statuses_spec.rb index 1f431f79d85661..e63437cc66fdec 100644 --- a/spec/requests/api/v1/statuses_spec.rb +++ b/spec/requests/api/v1/statuses_spec.rb @@ -4,10 +4,10 @@ RSpec.describe '/api/v1/statuses' do context 'with an oauth token' do - let(:user) { Fabricate(:user) } + include_context 'with API authentication' + let(:client_app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: client_app, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v1/statuses?id[]=:id' do let(:status) { Fabricate(:status) } diff --git a/spec/requests/api/v1/suggestions_spec.rb b/spec/requests/api/v1/suggestions_spec.rb index 0a32d8899bce43..13ac6e78558531 100644 --- a/spec/requests/api/v1/suggestions_spec.rb +++ b/spec/requests/api/v1/suggestions_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Suggestions' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read' describe 'GET /api/v1/suggestions' do subject do diff --git a/spec/requests/api/v1/tags_spec.rb b/spec/requests/api/v1/tags_spec.rb index 5beda68db0a85b..42b9180880de82 100644 --- a/spec/requests/api/v1/tags_spec.rb +++ b/spec/requests/api/v1/tags_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Tags' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'write:follows' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write:follows' describe 'GET /api/v1/tags/:id' do subject do diff --git a/spec/requests/api/v1_alpha/async_refreshes_spec.rb b/spec/requests/api/v1_alpha/async_refreshes_spec.rb index 0cd85cf99bf48c..8b29b7599c6959 100644 --- a/spec/requests/api/v1_alpha/async_refreshes_spec.rb +++ b/spec/requests/api/v1_alpha/async_refreshes_spec.rb @@ -3,9 +3,8 @@ require 'rails_helper' RSpec.describe 'AsyncRefreshes' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' + let(:job) { AsyncRefresh.new('test_job') } describe 'GET /api/v1_alpha/async_refreshes/:id' do diff --git a/spec/requests/api/v2/filters_spec.rb b/spec/requests/api/v2/filters_spec.rb index 304afc7bd8dad6..cfa607cff06cfb 100644 --- a/spec/requests/api/v2/filters_spec.rb +++ b/spec/requests/api/v2/filters_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Filters' do - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:filters write:filters' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:filters write:filters' shared_examples 'unauthorized for invalid token' do let(:headers) { { 'Authorization' => '' } } diff --git a/spec/requests/api/v2/instance_spec.rb b/spec/requests/api/v2/instance_spec.rb index 788d30fa699ac0..92a9744e416724 100644 --- a/spec/requests/api/v2/instance_spec.rb +++ b/spec/requests/api/v2/instance_spec.rb @@ -3,9 +3,7 @@ require 'rails_helper' RSpec.describe 'Instances' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication' describe 'GET /api/v2/instance' do context 'when logged out' do diff --git a/spec/requests/api/v2/media_spec.rb b/spec/requests/api/v2/media_spec.rb index 18ebb9cddae441..04e48bc02c3942 100644 --- a/spec/requests/api/v2/media_spec.rb +++ b/spec/requests/api/v2/media_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Media API', :attachment_processing do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'write' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'write' describe 'POST /api/v2/media' do context 'when small media format attachment is processed immediately' do diff --git a/spec/requests/api/v2/notifications_spec.rb b/spec/requests/api/v2/notifications_spec.rb index 69feb6cb6e0a08..4b4aa1b475147c 100644 --- a/spec/requests/api/v2/notifications_spec.rb +++ b/spec/requests/api/v2/notifications_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe 'Notifications' do + include_context 'with API authentication', oauth_scopes: 'read:notifications write:notifications' + let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:notifications write:notifications' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'GET /api/v2/notifications/unread_count', :inline_jobs do subject do diff --git a/spec/requests/api/v2/search_spec.rb b/spec/requests/api/v2/search_spec.rb index 6beab4c8c7dbc4..c60861b48f0c9a 100644 --- a/spec/requests/api/v2/search_spec.rb +++ b/spec/requests/api/v2/search_spec.rb @@ -4,10 +4,7 @@ RSpec.describe 'Search API' do context 'with token' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read:search' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read:search' describe 'GET /api/v2/search' do let!(:bob) { Fabricate(:account, username: 'bob_test') } diff --git a/spec/requests/api/v2/suggestions_spec.rb b/spec/requests/api/v2/suggestions_spec.rb index 578bf1b61b978d..dd876046bcc055 100644 --- a/spec/requests/api/v2/suggestions_spec.rb +++ b/spec/requests/api/v2/suggestions_spec.rb @@ -3,10 +3,7 @@ require 'rails_helper' RSpec.describe 'Suggestions API' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:scopes) { 'read' } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read' describe 'GET /api/v2/suggestions' do let(:bob) { Fabricate(:account) } diff --git a/spec/requests/api/web/embeds_spec.rb b/spec/requests/api/web/embeds_spec.rb index 3cc2f977f87fb5..ad71172e87d505 100644 --- a/spec/requests/api/web/embeds_spec.rb +++ b/spec/requests/api/web/embeds_spec.rb @@ -65,9 +65,7 @@ end context 'with an API token' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') } - let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + include_context 'with API authentication', oauth_scopes: 'read' context 'when the requested status is local' do let(:id) { status.id } diff --git a/spec/support/api_authentication.rb b/spec/support/api_authentication.rb new file mode 100644 index 00000000000000..83f76ab077396a --- /dev/null +++ b/spec/support/api_authentication.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +RSpec.shared_context 'with API authentication' do |oauth_scopes: '', user_fabricator: :user| + let(:user) { Fabricate(user_fabricator) } + let(:scopes) { oauth_scopes } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } +end diff --git a/spec/support/examples/api.rb b/spec/support/examples/api.rb index ddc61fcbe08b6d..350166b10d53d2 100644 --- a/spec/support/examples/api.rb +++ b/spec/support/examples/api.rb @@ -13,6 +13,7 @@ RSpec.shared_examples 'forbidden for wrong role' do |wrong_role| let(:role) { UserRole.find_by(name: wrong_role) } + let(:user) { Fabricate(:user, role:) } it 'returns http forbidden' do # Some examples have a subject which needs to be called to make a request From 57bfe863f3dd8da20d7c69f167c0ea94c81275c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:10:07 +0000 Subject: [PATCH 016/123] chore(deps): update dependency aws-sdk-core to v3.239.1 (#36955) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0d0977573a607c..2c7f591c6b26b5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,7 +97,7 @@ GEM attr_required (1.0.2) aws-eventstream (1.4.0) aws-partitions (1.1186.0) - aws-sdk-core (3.239.0) + aws-sdk-core (3.239.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) From 96d5e573516160a9d94171e5a71f5675a4619f23 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 24 Nov 2025 14:33:37 +0100 Subject: [PATCH 017/123] Revert "Increase HTTP read timeout for expensive S3 batch delete operation (#36971)" (#36996) --- app/lib/attachment_batch.rb | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/app/lib/attachment_batch.rb b/app/lib/attachment_batch.rb index cf08fdcf528683..374abfac49038d 100644 --- a/app/lib/attachment_batch.rb +++ b/app/lib/attachment_batch.rb @@ -112,17 +112,10 @@ def remove_files keys.each_slice(LIMIT) do |keys_slice| logger.debug { "Deleting #{keys_slice.size} objects" } - bucket.delete_objects( - { - delete: { - objects: keys_slice.map { |key| { key: key } }, - quiet: true, - }, - }, - { - http_read_timeout: [Paperclip::Attachment.default_options[:s3_options][:http_read_timeout], 120].max, - } - ) + bucket.delete_objects(delete: { + objects: keys_slice.map { |key| { key: key } }, + quiet: true, + }) rescue => e retries += 1 From 76d8ac3fe680e92d20059c590ccb1cd8f74078c2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:34:10 +0100 Subject: [PATCH 018/123] chore(deps): update dependency i18n-tasks to v1.1.1 (#36997) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2c7f591c6b26b5..19dfbfa5dd4930 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -324,7 +324,7 @@ GEM rainbow (>= 2.0.0) i18n (1.14.7) concurrent-ruby (~> 1.0) - i18n-tasks (1.1.0) + i18n-tasks (1.1.1) activesupport (>= 4.0.2) ast (>= 2.1.0) erubi From f0d7ea61eff546352ef1b4b21c5b7f08b4638c36 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:52:51 +0100 Subject: [PATCH 019/123] New Crowdin Translations (automated) (#37000) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/en-GB.json | 53 +++++++++++++++ app/javascript/mastodon/locales/ia.json | 7 ++ app/javascript/mastodon/locales/pt-BR.json | 44 ++++++------- app/javascript/mastodon/locales/sv.json | 9 +++ app/javascript/mastodon/locales/zh-CN.json | 1 + config/locales/activerecord.en-GB.yml | 6 ++ config/locales/activerecord.fi.yml | 6 ++ config/locales/activerecord.it.yml | 6 ++ config/locales/activerecord.zh-CN.yml | 6 ++ config/locales/da.yml | 2 +- config/locales/devise.en-GB.yml | 1 + config/locales/devise.sv.yml | 1 + config/locales/en-GB.yml | 77 ++++++++++++++++++++++ config/locales/simple_form.en-GB.yml | 32 +++++++++ config/locales/simple_form.pt-BR.yml | 2 +- config/locales/simple_form.sv.yml | 1 + config/locales/sv.yml | 46 +++++++++++++ 17 files changed, 276 insertions(+), 24 deletions(-) diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index f13bd697d1e03d..34c03215322bba 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "All clear! There is nothing here. When you receive new notifications, they will appear here according to your settings.", "empty_column.notifications": "You don't have any notifications yet. When other people interact with you, you will see it here.", "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up", + "error.no_hashtag_feed_access": "Join or log in to view and follow this hashtag.", "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.", "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.", "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.", @@ -767,6 +768,8 @@ "quote_error.poll": "Quoting is not allowed with polls.", "quote_error.private_mentions": "Quoting is not allowed with direct mentions.", "quote_error.quote": "Only one quote at a time is allowed.", + "quote_error.unauthorized": "You are not authorised to quote this post.", + "quote_error.upload": "Quoting is not allowed with media attachments.", "recommended": "Recommended", "refresh": "Refresh", "regeneration_indicator.please_stand_by": "Please stand by.", @@ -782,6 +785,9 @@ "relative_time.minutes": "{number}m", "relative_time.seconds": "{number}s", "relative_time.today": "today", + "remove_quote_hint.button_label": "Got it", + "remove_quote_hint.message": "You can do so from the {icon} options menu.", + "remove_quote_hint.title": "Want to remove your quoted post?", "reply_indicator.attachments": "{count, plural, one {# attachment} other {# attachments}}", "reply_indicator.cancel": "Cancel", "reply_indicator.poll": "Poll", @@ -873,13 +879,23 @@ "status.admin_account": "Open moderation interface for @{name}", "status.admin_domain": "Open moderation interface for {domain}", "status.admin_status": "Open this post in the moderation interface", + "status.all_disabled": "Boosts and quotes are disabled", "status.block": "Block @{name}", "status.bookmark": "Bookmark", "status.cancel_reblog_private": "Unboost", + "status.cannot_quote": "You are not allowed to quote this post", "status.cannot_reblog": "This post cannot be boosted", + "status.contains_quote": "Contains quote", + "status.context.loading": "Loading more replies", + "status.context.loading_error": "Couldn't load new replies", + "status.context.loading_success": "New replies loaded", + "status.context.more_replies_found": "More replies found", + "status.context.retry": "Retry", + "status.context.show": "Show", "status.continued_thread": "Continued thread", "status.copy": "Copy link to status", "status.delete": "Delete", + "status.delete.success": "Post deleted", "status.detailed_status": "Detailed conversation view", "status.direct": "Privately mention @{name}", "status.direct_indicator": "Private mention", @@ -888,6 +904,7 @@ "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}", "status.embed": "Get embed code", "status.favourite": "Favourite", + "status.favourites_count": "{count, plural, one {{counter} favourite} other {{counter} favourites}}", "status.filter": "Filter this post", "status.history.created": "{name} created {date}", "status.history.edited": "{name} edited {date}", @@ -901,19 +918,46 @@ "status.mute_conversation": "Mute conversation", "status.open": "Expand this post", "status.pin": "Pin on profile", + "status.quote": "Quote", + "status.quote.cancel": "Cancel quote", + "status.quote_error.blocked_account_hint.title": "This post is hidden because you've blocked @{name}.", + "status.quote_error.blocked_domain_hint.title": "This post is hidden because you've blocked {domain}.", "status.quote_error.filtered": "Hidden due to one of your filters", + "status.quote_error.limited_account_hint.action": "Show anyway", + "status.quote_error.limited_account_hint.title": "This account has been hidden by the moderators of {domain}.", + "status.quote_error.muted_account_hint.title": "This post is hidden because you've muted @{name}.", + "status.quote_error.not_available": "Post unavailable", + "status.quote_error.pending_approval": "Post pending", + "status.quote_error.pending_approval_popout.body": "On Mastodon, you can control whether someone can quote you. This post is pending while we're getting the original author's approval.", + "status.quote_error.revoked": "Post removed by author", + "status.quote_followers_only": "Only followers can quote this post", + "status.quote_manual_review": "Author will manually review", + "status.quote_noun": "Quote", + "status.quote_policy_change": "Change who can quote", + "status.quote_post_author": "Quoted a post by @{name}", + "status.quote_private": "Private posts cannot be quoted", + "status.quotes.empty": "No one has quoted this post yet. When someone does, it will show up here.", + "status.quotes.local_other_disclaimer": "Quotes rejected by the author will not be shown.", + "status.quotes.remote_other_disclaimer": "Only quotes from {domain} are guaranteed to be shown here. Quotes rejected by the author will not be shown.", + "status.quotes_count": "{count, plural, one {{counter} quote} other {{counter} quotes}}", "status.read_more": "Read more", "status.reblog": "Boost", + "status.reblog_or_quote": "Boost or quote", + "status.reblog_private": "Share again with your followers", "status.reblogged_by": "{name} boosted", "status.reblogs.empty": "No one has boosted this post yet. When someone does, they will show up here.", + "status.reblogs_count": "{count, plural, one {{counter} boost} other {{counter} boosts}}", "status.redraft": "Delete & re-draft", "status.remove_bookmark": "Remove bookmark", "status.remove_favourite": "Remove from favourites", + "status.remove_quote": "Remove", "status.replied_in_thread": "Replied in thread", "status.replied_to": "Replied to {name}", "status.reply": "Reply", "status.replyAll": "Reply to thread", "status.report": "Report @{name}", + "status.request_quote": "Request to quote", + "status.revoke_quote": "Remove my post from @{name}’s post", "status.sensitive_warning": "Sensitive content", "status.share": "Share", "status.show_less_all": "Show less for all", @@ -951,6 +995,7 @@ "upload_button.label": "Add images, a video or an audio file", "upload_error.limit": "File upload limit exceeded.", "upload_error.poll": "File upload not allowed with polls.", + "upload_error.quote": "File upload not allowed with quotes.", "upload_form.drag_and_drop.instructions": "To pick up a media attachment, press space or enter. While dragging, use the arrow keys to move the media attachment in any given direction. Press space or enter again to drop the media attachment in its new position, or press escape to cancel.", "upload_form.drag_and_drop.on_drag_cancel": "Dragging was cancelled. Media attachment {item} was dropped.", "upload_form.drag_and_drop.on_drag_end": "Media attachment {item} was dropped.", @@ -974,6 +1019,14 @@ "video.unmute": "Unmute", "video.volume_down": "Volume down", "video.volume_up": "Volume up", + "visibility_modal.button_title": "Set visibility", + "visibility_modal.direct_quote_warning.text": "If you save the current settings, the embedded quote will be converted to a link.", + "visibility_modal.direct_quote_warning.title": "Quotes can't be embedded in private mentions", + "visibility_modal.header": "Visibility and interaction", + "visibility_modal.helper.direct_quoting": "Private mentions authored on Mastodon can't be quoted by others.", + "visibility_modal.helper.privacy_editing": "Visibility can't be changed after a post is published.", + "visibility_modal.helper.privacy_private_self_quote": "Self-quotes of private posts cannot be made public.", + "visibility_modal.helper.private_quoting": "Follower-only posts authored on Mastodon can't be quoted by others.", "visibility_modal.helper.unlisted_quoting": "When people quote you, their post will also be hidden from trending timelines.", "visibility_modal.instructions": "Control who can interact with this post. You can also apply settings to all future posts by navigating to Preferences > Posting defaults.", "visibility_modal.privacy_label": "Visibility", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index a2247c6d9ef279..4202a855c81b8b 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Iste lista es toto vacue! Quando tu recipe notificationes, illos apparera hic como configurate in tu parametros.", "empty_column.notifications": "Tu non ha ancora notificationes. Quando altere personas interage con te, tu lo videra hic.", "empty_column.public": "Il ha nihil hic! Scribe qualcosa public, o manualmente seque usatores de altere servitores, pro plenar lo", + "error.no_hashtag_feed_access": "Inscribe te o aperi session pro vider e sequer iste hashtag.", "error.unexpected_crash.explanation": "A causa de un defecto in nostre codice o de un problema de compatibilitate del navigator, iste pagina non pote esser visualisate correctemente.", "error.unexpected_crash.explanation_addons": "Iste pagina non pote esser visualisate correctemente. Iste error es probabilemente causate per un additivo al navigator o per un utensile de traduction automatic.", "error.unexpected_crash.next_steps": "Tenta refrescar le pagina. Si isto non remedia le problema, es possibile que tu pote totevia usar Mastodon per medio de un altere navigator o application native.", @@ -919,9 +920,12 @@ "status.pin": "Fixar sur profilo", "status.quote": "Citar", "status.quote.cancel": "Cancellar le citation", + "status.quote_error.blocked_account_hint.title": "Iste message es celate perque tu ha blocate @{name}.", + "status.quote_error.blocked_domain_hint.title": "Iste message es celate perque tu ha blocate {domain}.", "status.quote_error.filtered": "Celate a causa de un de tu filtros", "status.quote_error.limited_account_hint.action": "Monstrar in omne caso", "status.quote_error.limited_account_hint.title": "Iste conto ha essite celate per le moderatores de {domain}.", + "status.quote_error.muted_account_hint.title": "Iste message es celate perque tu ha silentiate @{name}.", "status.quote_error.not_available": "Message indisponibile", "status.quote_error.pending_approval": "Message pendente", "status.quote_error.pending_approval_popout.body": "Sur Mastodon, tu pote controlar si on pote citar te. Iste message attende ora le approbation del autor original.", @@ -935,12 +939,14 @@ "status.quotes.empty": "Necuno ha ancora citate iste message. Quando alcuno lo face, illo apparera hic.", "status.quotes.local_other_disclaimer": "Le citationes rejectate per le autor non essera monstrate.", "status.quotes.remote_other_disclaimer": "Solmente le citationes de {domain} se garanti de esser monstrate hic. Citationes rejectate per le autor non essera monstrate.", + "status.quotes_count": "{count, plural, one {{counter} citation} other {{counter} citationes}}", "status.read_more": "Leger plus", "status.reblog": "Impulsar", "status.reblog_or_quote": "Impulsar o citar", "status.reblog_private": "Re-compartir con tu sequitores", "status.reblogged_by": "Impulsate per {name}", "status.reblogs.empty": "Necuno ha ancora impulsate iste message. Quando alcuno lo face, le impulsos apparera hic.", + "status.reblogs_count": "{count, plural, one {{counter} impulso} other {{counter} impulsos}}", "status.redraft": "Deler e reconciper", "status.remove_bookmark": "Remover marcapagina", "status.remove_favourite": "Remover del favoritos", @@ -1014,6 +1020,7 @@ "video.volume_down": "Abassar le volumine", "video.volume_up": "Augmentar le volumine", "visibility_modal.button_title": "Definir visibilitate", + "visibility_modal.direct_quote_warning.text": "Si tu salva le parametros actual, le citation incorporate se convertera in un ligamine.", "visibility_modal.header": "Visibilitate e interaction", "visibility_modal.helper.direct_quoting": "Le mentiones private scribite sur Mastodon non pote esser citate per alteres.", "visibility_modal.helper.privacy_editing": "Le visibilitate de un message non pote esser cambiate post su publication.", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index d60d1561524f4b..a9c8bbfcb77f88 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -36,7 +36,7 @@ "account.familiar_followers_two": "Seguido por {name1} e {name2}", "account.featured": "Em destaque", "account.featured.accounts": "Perfis", - "account.featured.hashtags": "\"Hashtags\"", + "account.featured.hashtags": "Hashtags", "account.featured_tags.last_status_at": "Última publicação em {date}", "account.featured_tags.last_status_never": "Sem publicações", "account.follow": "Seguir", @@ -98,7 +98,7 @@ "admin.dashboard.retention.cohort_size": "Novos usuários", "admin.impact_report.instance_accounts": "Perfis de contas que isso apagaria", "admin.impact_report.instance_followers": "Seguidores que os nossos usuários perderiam", - "admin.impact_report.instance_follows": "Seguidores que os seus usuários perderiam", + "admin.impact_report.instance_follows": "Seguidores que os usuários deles perderiam", "admin.impact_report.title": "Resumo do impacto", "alert.rate_limited.message": "Tente novamente após {retry_time, time, medium}.", "alert.rate_limited.title": "Tentativas limitadas", @@ -109,8 +109,8 @@ "alt_text_modal.add_text_from_image": "Adicione texto da imagem", "alt_text_modal.cancel": "Cancelar", "alt_text_modal.change_thumbnail": "Alterar miniatura", - "alt_text_modal.describe_for_people_with_hearing_impairments": "Descreva isso para pessoas com deficiências auditivas...", - "alt_text_modal.describe_for_people_with_visual_impairments": "Descreva isso para pessoas com deficiências visuais…", + "alt_text_modal.describe_for_people_with_hearing_impairments": "Descreva isso para pessoas com deficiências auditivas…", + "alt_text_modal.describe_for_people_with_visual_impairments": "Descreva isto para pessoas com deficiências visuais…", "alt_text_modal.done": "Feito", "announcement.announcement": "Comunicados", "annual_report.summary.archetype.booster": "Caçador legal", @@ -120,7 +120,7 @@ "annual_report.summary.archetype.replier": "A borboleta social", "annual_report.summary.followers.followers": "seguidores", "annual_report.summary.followers.total": "{count} total", - "annual_report.summary.here_it_is": "Aqui está seu {year} em revisão:", + "annual_report.summary.here_it_is": "Aqui está seu {year} em retrospectiva:", "annual_report.summary.highlighted_post.by_favourites": "publicação mais favoritada", "annual_report.summary.highlighted_post.by_reblogs": "publicação mais impulsionada", "annual_report.summary.highlighted_post.by_replies": "publicação com mais respostas", @@ -273,7 +273,7 @@ "confirmations.unfollow.title": "Deixar de seguir {name}?", "confirmations.withdraw_request.confirm": "Retirar solicitação", "confirmations.withdraw_request.title": "Cancelar solicitação para seguir {name}?", - "content_warning.hide": "Ocultar post", + "content_warning.hide": "Ocultar publicação", "content_warning.show": "Mostrar mesmo assim", "content_warning.show_more": "Mostrar mais", "conversation.delete": "Excluir conversa", @@ -291,7 +291,7 @@ "disabled_account_banner.text": "Sua conta {disabledAccount} está desativada no momento.", "dismissable_banner.community_timeline": "Estas são as publicações públicas mais recentes das pessoas cujas contas são hospedadas por {domain}.", "dismissable_banner.dismiss": "Dispensar", - "dismissable_banner.public_timeline": "Estas são as publicações mais recentes das pessoas no fediverse que as pessoas do {domain} seguem.", + "dismissable_banner.public_timeline": "Estas são as publicações mais recentes das pessoas no fediverso que as pessoas do {domain} seguem.", "domain_block_modal.block": "Bloquear servidor", "domain_block_modal.block_account_instead": "Bloquear @{name}", "domain_block_modal.they_can_interact_with_old_posts": "Pessoas deste servidor podem interagir com suas publicações antigas.", @@ -304,8 +304,8 @@ "domain_pill.activitypub_lets_connect": "Ele permite que você se conecte e interaja com pessoas não apenas no Mastodon, mas também em diferentes aplicativos sociais.", "domain_pill.activitypub_like_language": "ActivityPub é como a linguagem que o Mastodon fala com outras redes sociais.", "domain_pill.server": "Servidor", - "domain_pill.their_handle": "Seu identificador:", - "domain_pill.their_server": "Sua casa digital, onde ficam todas as suas postagens.", + "domain_pill.their_handle": "Identificador dele/a:", + "domain_pill.their_server": "Casa digital dele/a, onde ficam todas as suas postagens.", "domain_pill.their_username": "Seu identificador exclusivo em seu servidor. É possível encontrar usuários com o mesmo nome de usuário em servidores diferentes.", "domain_pill.username": "Nome de usuário", "domain_pill.whats_in_a_handle": "O que há em um identificador?", @@ -425,8 +425,8 @@ "generic.saved": "Salvo", "getting_started.heading": "Primeiros passos", "hashtag.admin_moderation": "Abrir interface de moderação para #{name}", - "hashtag.browse": "Buscar postagens em #{hashtag}", - "hashtag.browse_from_account": "Procurar mensagens de @{name} em #{hashtag}", + "hashtag.browse": "Buscar publicações em #{hashtag}", + "hashtag.browse_from_account": "Buscar publicações de @{name} em #{hashtag}", "hashtag.column_header.tag_mode.all": "e {additional}", "hashtag.column_header.tag_mode.any": "ou {additional}", "hashtag.column_header.tag_mode.none": "sem {additional}", @@ -445,11 +445,11 @@ "hashtag.unfeature": "Não destacar no perfil", "hashtag.unfollow": "Parar de seguir hashtag", "hashtags.and_other": "…e {count, plural, one {}other {outros #}}", - "hints.profiles.followers_may_be_missing": "Os seguidores deste perfil podem estar faltando.", - "hints.profiles.follows_may_be_missing": "Os seguidores deste perfil podem estar faltando.", + "hints.profiles.followers_may_be_missing": "Pode haver seguidores deste perfil faltando.", + "hints.profiles.follows_may_be_missing": "Pode haver seguidos por este perfil faltando.", "hints.profiles.posts_may_be_missing": "É possível que algumas publicações deste perfil estejam faltando.", "hints.profiles.see_more_followers": "Ver mais seguidores no {domain}", - "hints.profiles.see_more_follows": "Ver mais seguidores no {domain}", + "hints.profiles.see_more_follows": "Ver mais seguidos no {domain}", "hints.profiles.see_more_posts": "Ver mais publicações em {domain}", "home.column_settings.show_quotes": "Mostrar citações", "home.column_settings.show_reblogs": "Mostrar boosts", @@ -636,7 +636,7 @@ "notification.moderation_warning.action_suspend": "Sua conta foi suspensa.", "notification.own_poll": "Sua enquete terminou", "notification.poll": "Uma enquete que você votou terminou", - "notification.quoted_update": "{name} Editou um post seu", + "notification.quoted_update": "{name} editou uma pulicação que você citou", "notification.reblog": "{name} deu boost no teu toot", "notification.reblog.name_and_others_with_link": "{name} e {count, plural, one {# outra} other {# outras}} impulsionaram a publicação", "notification.relationships_severance_event": "Conexões perdidas com {name}", @@ -761,14 +761,14 @@ "privacy.quote.limited": "{visibility} Citações limitadas", "privacy.unlisted.additional": "Isso se comporta exatamente como público, exceto que a publicação não aparecerá nos _feeds ao vivo_ ou nas _hashtags_, explorar, ou barra de busca, mesmo que você seja escolhido em toda a conta.", "privacy.unlisted.long": "Oculto para os resultados de pesquisa do Mastodon, tendências e linhas do tempo públicas", - "privacy.unlisted.short": "Público silenciado", + "privacy.unlisted.short": "Público silencioso", "privacy_policy.last_updated": "Atualizado {date}", "privacy_policy.title": "Política de privacidade", "quote_error.edit": "Citações não podem ser adicionadas durante a edição de uma publicação.", "quote_error.poll": "Citações não permitidas com enquetes.", "quote_error.private_mentions": "Citações não são permitidas com menções diretas.", - "quote_error.quote": "Apenas uma citação por vez é permitido.", - "quote_error.unauthorized": "Você não é autorizado a citar essa publicação.", + "quote_error.quote": "Somente é permitida uma citação por vez.", + "quote_error.unauthorized": "Você não tem autorização para citar essa publicação.", "quote_error.upload": "Citações não são permitidas com mídias anexadas.", "recommended": "Recomendado", "refresh": "Atualizar", @@ -939,14 +939,14 @@ "status.quotes.empty": "Ninguém citou essa publicação até agora. Quando alguém citar aparecerá aqui.", "status.quotes.local_other_disclaimer": "Citações rejeitadas pelo autor não serão exibidas.", "status.quotes.remote_other_disclaimer": "Apenas citações do {domain} têm a garantia de serem exibidas aqui. Citações rejeitadas pelo autor não serão exibidas.", - "status.quotes_count": "{count, plural, one {{counter} mencionar} other {{counter} menções}}", + "status.quotes_count": "{count, plural, one {{counter} citação} other {{counter} citações}}", "status.read_more": "Ler mais", "status.reblog": "Dar boost", - "status.reblog_or_quote": "Acelerar ou citar", + "status.reblog_or_quote": "Dar boost ou citar", "status.reblog_private": "Compartilhar novamente com seus seguidores", "status.reblogged_by": "{name} deu boost", "status.reblogs.empty": "Nada aqui. Quando alguém der boost, o usuário aparecerá aqui.", - "status.reblogs_count": "{count, plural, one {{counter} impulsionar} other {{counter} impulsionados}}", + "status.reblogs_count": "{count, plural, one {{counter} boost} other {{counter} boosts}}", "status.redraft": "Excluir e rascunhar", "status.remove_bookmark": "Remover do Salvos", "status.remove_favourite": "Remover dos favoritos", @@ -1021,7 +1021,7 @@ "video.volume_up": "Aumentar o volume", "visibility_modal.button_title": "Selecionar Visibilidade", "visibility_modal.direct_quote_warning.text": "Se salvar as configurações atuais, a cotação incorporada será convertida em um link.", - "visibility_modal.direct_quote_warning.title": "Cotações não podem ser incorporadas em menções privadas", + "visibility_modal.direct_quote_warning.title": "Citações não podem ser incorporadas em menções privadas", "visibility_modal.header": "Visibilidade e interação", "visibility_modal.helper.direct_quoting": "Menções privadas escritas no Mastodon.", "visibility_modal.helper.privacy_editing": "A visibilidade não pode ser alterada após uma publicação ser publicada.", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index bd48262ab3c5a1..49ea6ea1736a40 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -157,6 +157,8 @@ "bundle_modal_error.close": "Stäng", "bundle_modal_error.message": "Något gick fel när skärmen laddades.", "bundle_modal_error.retry": "Försök igen", + "carousel.current": "Bild{current, number} / {max, number}", + "carousel.slide": "Bild {current, number} av {max, number}", "closed_registrations.other_server_instructions": "Eftersom Mastodon är decentraliserat kan du skapa ett konto på en annan server och fortfarande interagera med denna.", "closed_registrations_modal.description": "Det är för närvarande inte möjligt att skapa ett konto på {domain} men kom ihåg att du inte behöver ett konto specifikt på {domain} för att använda Mastodon.", "closed_registrations_modal.find_another_server": "Hitta en annan server", @@ -902,6 +904,7 @@ "status.edited_x_times": "Redigerad {count, plural, one {{count} gång} other {{count} gånger}}", "status.embed": "Hämta kod för inbäddning", "status.favourite": "Favoritmarkera", + "status.favourites_count": "{count, plural, one {{counter} favorit} other {{counter} favoriter}}", "status.filter": "Filtrera detta inlägg", "status.history.created": "{name} skapade {date}", "status.history.edited": "{name} redigerade {date}", @@ -929,16 +932,21 @@ "status.quote_error.revoked": "Inlägg borttaget av författaren", "status.quote_followers_only": "Detta inlägg kan bara citeras av följare", "status.quote_manual_review": "Författaren kommer att granska manuellt", + "status.quote_noun": "Citat", "status.quote_policy_change": "Ändra vem som kan citera", "status.quote_post_author": "Citerade ett inlägg av @{name}", "status.quote_private": "Privata inlägg kan inte citeras", "status.quotes.empty": "Ingen har citerat detta inlägg än. När någon gör det kommer det att synas här.", "status.quotes.local_other_disclaimer": "Citat som avvisats av författaren kommer inte att visas.", + "status.quotes.remote_other_disclaimer": "Endast citat från {domain} är garanterade att visas här. Citat som avvisats av författaren kommer inte att visas.", + "status.quotes_count": "{count, plural, one {{counter} citat} other {{counter} citat}}", "status.read_more": "Läs mer", "status.reblog": "Boosta", + "status.reblog_or_quote": "Boosta eller citera", "status.reblog_private": "Dela igen med dina följare", "status.reblogged_by": "{name} boostade", "status.reblogs.empty": "Ingen har boostat detta inlägg än. När någon gör det kommer de synas här.", + "status.reblogs_count": "{count, plural, one {{counter} boost} other {{counter} boostar}}", "status.redraft": "Radera & gör om", "status.remove_bookmark": "Ta bort bokmärke", "status.remove_favourite": "Ta bort från Favoriter", @@ -948,6 +956,7 @@ "status.reply": "Svara", "status.replyAll": "Svara på tråden", "status.report": "Rapportera @{name}", + "status.request_quote": "Begär att få citera", "status.revoke_quote": "Ta bort mitt inlägg från @{name}s inlägg", "status.sensitive_warning": "Känsligt innehåll", "status.share": "Dela", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index a5d475f0e4bbf7..4986e61e313235 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "一扫而空!这里没有任何未读通知。当收到新的通知时,将根据你的设置显示在这里。", "empty_column.notifications": "你还没有收到过任何通知,快和其他用户互动吧。", "empty_column.public": "这里什么都没有!写一些公开的嘟文,或者关注其他服务器的用户后,这里就会有嘟文出现了", + "error.no_hashtag_feed_access": "加入或登录以查看和关注此话题标签。", "error.unexpected_crash.explanation": "此页面无法正确显示,这可能是因为我们的代码中有错误,也可能是因为浏览器兼容问题。", "error.unexpected_crash.explanation_addons": "此页面无法正确显示,这个错误很可能是由浏览器附加组件或自动翻译工具造成的。", "error.unexpected_crash.next_steps": "刷新一下页面试试。如果没用,你可以换个浏览器或者用本地应用。", diff --git a/config/locales/activerecord.en-GB.yml b/config/locales/activerecord.en-GB.yml index fe10c5c8a75c83..2402b2f7f253b8 100644 --- a/config/locales/activerecord.en-GB.yml +++ b/config/locales/activerecord.en-GB.yml @@ -32,6 +32,12 @@ en-GB: attributes: url: invalid: is not a valid URL + collection: + attributes: + collection_items: + too_many: are too many, no more than %{count} are allowed + tag: + unusable: may not be used doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.fi.yml b/config/locales/activerecord.fi.yml index c731688a1fadd6..3698092b6d588a 100644 --- a/config/locales/activerecord.fi.yml +++ b/config/locales/activerecord.fi.yml @@ -32,6 +32,12 @@ fi: attributes: url: invalid: ei ole kelvollinen verkko-osoite + collection: + attributes: + collection_items: + too_many: on liian monta, enintään %{count} sallitaan + tag: + unusable: ei ole käytettävissä doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.it.yml b/config/locales/activerecord.it.yml index 9ff385f26e88d2..128ee5e5458171 100644 --- a/config/locales/activerecord.it.yml +++ b/config/locales/activerecord.it.yml @@ -32,6 +32,12 @@ it: attributes: url: invalid: non è un URL valido + collection: + attributes: + collection_items: + too_many: sono troppi, non sono consentiti più di %{count} + tag: + unusable: non può essere utilizzato doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.zh-CN.yml b/config/locales/activerecord.zh-CN.yml index 2aadcc8af9226e..6a3e2e7aa93ec9 100644 --- a/config/locales/activerecord.zh-CN.yml +++ b/config/locales/activerecord.zh-CN.yml @@ -32,6 +32,12 @@ zh-CN: attributes: url: invalid: 网址无效 + collection: + attributes: + collection_items: + too_many: 数量过多,不能多于 %{count} 个 + tag: + unusable: 不可使用 doorkeeper/application: attributes: website: diff --git a/config/locales/da.yml b/config/locales/da.yml index ab6abedd3527c8..3cb95b312026a7 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -66,7 +66,7 @@ da: disabled: Frosset display_name: Visningsnavn domain: Domæne - edit: Redigere + edit: Rediger email: E-mail email_status: E-mailstatus enable: Optø diff --git a/config/locales/devise.en-GB.yml b/config/locales/devise.en-GB.yml index 1127735ca0cab5..118423c966cfa4 100644 --- a/config/locales/devise.en-GB.yml +++ b/config/locales/devise.en-GB.yml @@ -7,6 +7,7 @@ en-GB: send_paranoid_instructions: If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes. Please check your spam folder if you didn't receive this email. failure: already_authenticated: You are already logged in. + closed_registrations: Your registration attempt has been blocked due to a network policy. If you believe this is an error, contact %{email}. inactive: Your account is not activated yet. invalid: Invalid %{authentication_keys} or password. last_attempt: You have one more attempt before your account is locked. diff --git a/config/locales/devise.sv.yml b/config/locales/devise.sv.yml index 1808cfee5ef60b..e9a7aec73e59be 100644 --- a/config/locales/devise.sv.yml +++ b/config/locales/devise.sv.yml @@ -7,6 +7,7 @@ sv: send_paranoid_instructions: Om din e-postadress finns i vår databas får du ett mail med instruktioner för hur du bekräftar din e-postadress inom några minuter. Kontrollera din spammapp om du inte fick det e-postmeddelandet. failure: already_authenticated: Du har redan loggat in. + closed_registrations: Ditt registreringsförsök har blivit blockerat på grund av en nätverkspolicy. Om du anser att detta är ett fel, kontakta %{email}. inactive: Ditt konto är ännu inte aktiverat. invalid: Ogiltig %{authentication_keys} eller lösenord. last_attempt: Du har ytterligare ett försök innan ditt konto är låst. diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 3da8ee0541a792..3d9f3722c560ec 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -190,6 +190,7 @@ en-GB: create_relay: Create Relay create_unavailable_domain: Create Unavailable Domain create_user_role: Create Role + create_username_block: Create Username Rule demote_user: Demote User destroy_announcement: Delete Announcement destroy_canonical_email_block: Delete Email Block @@ -203,6 +204,7 @@ en-GB: destroy_status: Delete Post destroy_unavailable_domain: Delete Unavailable Domain destroy_user_role: Destroy Role + destroy_username_block: Delete Username Rule disable_2fa_user: Disable 2FA disable_custom_emoji: Disable Custom Emoji disable_relay: Disable Relay @@ -237,6 +239,7 @@ en-GB: update_report: Update Report update_status: Update Post update_user_role: Update Role + update_username_block: Update Username Rule actions: approve_appeal_html: "%{name} approved moderation decision appeal from %{target}" approve_user_html: "%{name} approved sign-up from %{target}" @@ -255,6 +258,7 @@ en-GB: create_relay_html: "%{name} created a relay %{target}" create_unavailable_domain_html: "%{name} stopped delivery to domain %{target}" create_user_role_html: "%{name} created %{target} role" + create_username_block_html: "%{name} added rule for usernames containing %{target}" demote_user_html: "%{name} demoted user %{target}" destroy_announcement_html: "%{name} deleted announcement %{target}" destroy_canonical_email_block_html: "%{name} unblocked email with the hash %{target}" @@ -268,6 +272,7 @@ en-GB: destroy_status_html: "%{name} removed post by %{target}" destroy_unavailable_domain_html: "%{name} stopped delivery to domain %{target}" destroy_user_role_html: "%{name} deleted %{target} role" + destroy_username_block_html: "%{name} removed rule for usernames containing %{target}" disable_2fa_user_html: "%{name} disabled two factor requirement for user %{target}" disable_custom_emoji_html: "%{name} disabled emoji %{target}" disable_relay_html: "%{name} disabled the relay %{target}" @@ -302,6 +307,7 @@ en-GB: update_report_html: "%{name} updated report %{target}" update_status_html: "%{name} updated post by %{target}" update_user_role_html: "%{name} changed %{target} role" + update_username_block_html: "%{name} updated rule for usernames containing %{target}" deleted_account: deleted account empty: No logs found. filter_by_action: Filter by action @@ -790,6 +796,8 @@ en-GB: view_dashboard_description: Allows users to access the dashboard and various metrics view_devops: DevOps view_devops_description: Allows users to access Sidekiq and pgHero dashboards + view_feeds: View live and topic feeds + view_feeds_description: Allows users to access the live and topic feeds regardless of server settings title: Roles rules: add_new: Add rule @@ -831,6 +839,7 @@ en-GB: title: Opt users out of search engine indexing by default discovery: follow_recommendations: Follow recommendations + preamble: Surfacing interesting content is instrumental in onboarding new users who may not know anyone on Mastodon. Control how various discovery features work on your server. privacy: Privacy profile_directory: Profile directory public_timelines: Public timelines @@ -841,6 +850,16 @@ en-GB: all: To everyone disabled: To no one users: To logged-in local users + feed_access: + modes: + authenticated: Authenticated users only + disabled: Require specific user role + public: Everyone + landing_page: + values: + about: About + local_feed: Local feed + trends: Trends registrations: moderation_recommandation: Please make sure you have an adequate and reactive moderation team before you open registrations to everyone! preamble: Control who can create an account on your server. @@ -894,6 +913,7 @@ en-GB: no_status_selected: No posts were changed as none were selected open: Open post original_status: Original post + quotes: Quotes reblogs: Reblogs replied_to_html: Replied to %{acct_link} status_changed: Post changed @@ -901,6 +921,7 @@ en-GB: title: Account posts - @%{name} trending: Trending view_publicly: View publicly + view_quoted_post: View quoted post visibility: Visibility with_media: With media strikes: @@ -1084,6 +1105,25 @@ en-GB: other: Used by %{count} people over the last week title: Recommendations & Trends trending: Trending + username_blocks: + add_new: Add new + block_registrations: Block registrations + comparison: + contains: Contains + equals: Equals + contains_html: Contains %{string} + created_msg: Successfully created username rule + delete: Delete + edit: + title: Edit username rule + matches_exactly_html: Equals %{string} + new: + create: Create rule + title: Create new username rule + no_username_block_selected: No username rules were changed, as none were selected + not_permitted: Not permitted + title: Username rules + updated_msg: Successfully updated username rule warning_presets: add_new: Add new delete: Delete @@ -1156,7 +1196,10 @@ en-GB: hint_html: If you want to move from another account to this one, here you can create an alias, which is required before you can proceed with moving followers from the old account to this one. This action by itself is harmless and reversible. The account migration is initiated from the old account. remove: Unlink alias appearance: + advanced_settings: Advanced settings animations_and_accessibility: Animations and accessibility + boosting_preferences: Boosting preferences + boosting_preferences_info_html: "Tip: Regardless of settings, Shift + Click on the %{icon} Boost icon will immediately boost." discovery: Discovery localization: body: Mastodon is translated by volunteers. @@ -1558,6 +1601,13 @@ en-GB: expires_at: Expires uses: Uses title: Invite people + link_preview: + author_html: By %{name} + potentially_sensitive_content: + action: Click to show + confirm_visit: Are you sure you wish to open this link? + hide_button: Hide + label: Potentially sensitive content lists: errors: limit: You have reached the maximum number of lists @@ -1622,6 +1672,7 @@ en-GB: disabled_account: Your current account will not be fully usable afterwards. However, you will have access to data export as well as re-activation. followers: This action will move all followers from the current account to the new account only_redirect_html: Alternatively, you can only put up a redirect on your profile. + other_data: No other data will be moved automatically (including your posts and the list of accounts you follow) redirect: Your current account's profile will be updated with a redirect notice and be excluded from searches moderation: title: Moderation @@ -1657,6 +1708,10 @@ en-GB: title: New mention poll: subject: A poll by %{name} has ended + quote: + body: 'Your post was quoted by %{name}:' + subject: "%{name} quoted your post" + title: New quote reblog: body: 'Your post was boosted by %{name}:' subject: "%{name} boosted your post" @@ -1705,6 +1760,9 @@ en-GB: self_vote: You cannot vote in your own polls too_few_options: must have more than one item too_many_options: can't contain more than %{max} items + vote: Vote + posting_defaults: + explanation: These settings will be used as defaults when you create new posts, but you can edit them per post within the composer. preferences: other: Other posting_defaults: Posting defaults @@ -1860,6 +1918,9 @@ en-GB: other: "%{count} videos" boosted_from_html: Boosted from %{acct_link} content_warning: 'Content warning: %{warning}' + content_warnings: + hide: Hide post + show: Show more default_language: Same as interface language disallowed_hashtags: one: 'contained a disallowed hashtag: %{tags}' @@ -1867,15 +1928,31 @@ en-GB: edited_at_html: Edited %{date} errors: in_reply_not_found: The post you are trying to reply to does not appear to exist. + quoted_status_not_found: The post you are trying to quote does not appear to exist. + quoted_user_not_mentioned: Cannot quote a non-mentioned user in a Private Mention post. over_character_limit: character limit of %{max} exceeded pin_errors: direct: Posts that are only visible to mentioned users cannot be pinned limit: You have already pinned the maximum number of posts ownership: Someone else's post cannot be pinned reblog: A boost cannot be pinned + quote_error: + not_available: Post unavailable + pending_approval: Post pending + revoked: Post removed by author + quote_policies: + followers: Followers only + nobody: Just me + public: Anyone + quote_post_author: Quoted a post by %{acct} title: '%{name}: "%{quote}"' visibilities: + direct: Private mention + private: Followers only public: Public + public_long: Anyone on and off Mastodon + unlisted: Quiet public + unlisted_long: Hidden from Mastodon search results, trending, and public timelines statuses_cleanup: enabled: Automatically delete old posts enabled_hint: Automatically deletes your posts once they reach a specified age threshold, unless they match one of the exceptions below diff --git a/config/locales/simple_form.en-GB.yml b/config/locales/simple_form.en-GB.yml index cb2c485f7784b5..a749302353821d 100644 --- a/config/locales/simple_form.en-GB.yml +++ b/config/locales/simple_form.en-GB.yml @@ -54,13 +54,18 @@ en-GB: password: Use at least 8 characters phrase: Will be matched regardless of casing in text or content warning of a post scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones. + setting_advanced_layout: Display Mastodon as a multi-column layout, allowing you to view the timeline, notifications, and a third column of your choosing. Not recommended for smaller screens. setting_aggregate_reblogs: Do not show new boosts for posts that have been recently boosted (only affects newly-received boosts) setting_always_send_emails: Normally e-mail notifications won't be sent when you are actively using Mastodon + setting_boost_modal: When enabled, boosting will first open a confirmation dialogue in which you can change the visibility of your boost. + setting_default_quote_policy_private: Followers-only posts authored on Mastodon can't be quoted by others. + setting_default_quote_policy_unlisted: When people quote you, their post will also be hidden from trending timelines. setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click setting_display_media_default: Hide media marked as sensitive setting_display_media_hide_all: Always hide media setting_display_media_show_all: Always show media setting_emoji_style: How to display emojis. "Auto" will try using native emoji, but falls back to Twemoji for legacy browsers. + setting_quick_boosting_html: When enabled, clicking on the %{boost_icon} Boost icon will immediately boost instead of opening the boost/quote dropdown menu. Relocates the quoting action to the %{options_icon} (Options) menu. setting_system_scrollbars_ui: Applies only to desktop browsers based on Safari and Chrome setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed @@ -74,6 +79,7 @@ en-GB: featured_tag: name: 'Here are some of the hashtags you used the most recently:' filters: + action: Choose which action to perform when a post matches the filter actions: blur: Hide media behind a warning, without hiding the text itself hide: Completely hide the filtered content, behaving as if it did not exist @@ -82,10 +88,12 @@ en-GB: activity_api_enabled: Counts of locally published posts, active users, and new registrations in weekly buckets app_icon: WEBP, PNG, GIF or JPG. Overrides the default app icon on mobile devices with a custom icon. backups_retention_period: Users have the ability to generate archives of their posts to download later. When set to a positive value, these archives will be automatically deleted from your storage after the specified number of days. + bootstrap_timeline_accounts: These accounts will be pinned to the top of new users' follow recommendations. Provide a comma-separated list of accounts. closed_registrations_message: Displayed when sign-ups are closed content_cache_retention_period: All posts from other servers (including boosts and replies) will be deleted after the specified number of days, without regard to any local user interaction with those posts. This includes posts where a local user has marked it as bookmarks or favorites. Private mentions between users from different instances will also be lost and impossible to restore. Use of this setting is intended for special purpose instances and breaks many user expectations when implemented for general purpose use. custom_css: You can apply custom styles on the web version of Mastodon. favicon: WEBP, PNG, GIF or JPG. Overrides the default Mastodon favicon with a custom icon. + landing_page: Selects what page new visitors see when they first arrive on your server. If you select "Trends", then Trends needs to be enabled in the Discovery Settings. If you select "Local feed", then "Access to live feeds featuring local posts" needs to be set to "Everyone" in the Discovery Settings. mascot: Overrides the illustration in the advanced web interface. media_cache_retention_period: Media files from posts made by remote users are cached on your server. When set to a positive value, media will be deleted after the specified number of days. If the media data is requested after it is deleted, it will be re-downloaded, if the source content is still available. Due to restrictions on how often link preview cards poll third-party sites, it is recommended to set this value to at least 14 days, or link preview cards will not be updated on demand before that time. min_age: Users will be asked to confirm their date of birth during sign-up @@ -145,6 +153,9 @@ en-GB: min_age: Should not be below the minimum age required by the laws of your jurisdiction. user: chosen_languages: When checked, only posts in selected languages will be displayed in public timelines + date_of_birth: + one: We have to make sure you're at least %{count} to use %{domain}. We won't store this. + other: We have to make sure you're at least %{count} to use %{domain}. We won't store this. role: The role controls which permissions the user has. user_role: color: Color to be used for the role throughout the UI, as RGB in hex format @@ -152,6 +163,10 @@ en-GB: name: Public name of the role, if role is set to be displayed as a badge permissions_as_keys: Users with this role will have access to... position: Higher role decides conflict resolution in certain situations. Certain actions can only be performed on roles with a lower priority + username_block: + allow_with_approval: Instead of preventing sign-up outright, matching sign-ups will require your approval + comparison: Please be mindful of the Scunthorpe Problem when blocking partial matches + username: Will be matched regardless of casing and common homoglyphs like "4" for "a" or "3" for "e" webhook: events: Select events to send template: Compose your own JSON payload using variable interpolation. Leave blank for default JSON. @@ -222,9 +237,12 @@ en-GB: setting_aggregate_reblogs: Group boosts in timelines setting_always_send_emails: Always send e-mail notifications setting_auto_play_gif: Auto-play animated GIFs + setting_boost_modal: Control boosting visibility setting_default_language: Posting language + setting_default_privacy: Posting visibility setting_default_quote_policy: Who can quote setting_default_sensitive: Always mark media as sensitive + setting_delete_modal: Warn me before deleting a post setting_disable_hover_cards: Disable profile preview on hover setting_disable_swiping: Disable swiping motions setting_display_media: Media display @@ -234,6 +252,8 @@ en-GB: setting_emoji_style: Emoji style setting_expand_spoilers: Always expand posts marked with content warnings setting_hide_network: Hide your social graph + setting_missing_alt_text_modal: Warn me before posting media without alt text + setting_quick_boosting: Enable quick boosting setting_reduce_motion: Reduce motion in animations setting_system_font_ui: Use system's default font setting_system_scrollbars_ui: Use system's default scrollbar @@ -267,12 +287,17 @@ en-GB: content_cache_retention_period: Remote content retention period custom_css: Custom CSS favicon: Favicon + landing_page: Landing page for new visitors + local_live_feed_access: Access to live feeds featuring local posts + local_topic_feed_access: Access to hashtag and link feeds featuring local posts mascot: Custom mascot (legacy) media_cache_retention_period: Media cache retention period min_age: Minimum age requirement peers_api_enabled: Publish list of discovered servers in the API profile_directory: Enable profile directory registrations_mode: Who can sign-up + remote_live_feed_access: Access to live feeds featuring remote posts + remote_topic_feed_access: Access to hashtag and link feeds featuring remote posts require_invite_text: Require a reason to join show_domain_blocks: Show domain blocks show_domain_blocks_rationale: Show why domains were blocked @@ -311,6 +336,7 @@ en-GB: follow_request: Someone requested to follow you mention: Someone mentioned you pending_account: New account needs review + quote: Someone quoted you reblog: Someone boosted your post report: New report is submitted software_updates: @@ -346,7 +372,9 @@ en-GB: jurisdiction: Legal jurisdiction min_age: Minimum age user: + date_of_birth_1i: Year date_of_birth_2i: Month + date_of_birth_3i: Day role: Role time_zone: Time Zone user_role: @@ -355,6 +383,10 @@ en-GB: name: Name permissions_as_keys: Permissions position: Priority + username_block: + allow_with_approval: Allow registrations with approval + comparison: Method of comparison + username: Word to match webhook: events: Enabled events template: Payload template diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index 8de2c8b04aad19..a63a11220f617f 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -57,7 +57,7 @@ pt-BR: setting_advanced_layout: Exiba o Mastodon em um layout de várias colunas, permitindo que você visualize a linha do tempo, as notificações e uma terceira coluna de sua escolha. Não recomendado para telas menores. setting_aggregate_reblogs: Não mostrar novos impulsos para publicações que já foram impulsionadas recentemente (afeta somente os impulsos mais recentes) setting_always_send_emails: Normalmente, as notificações por e-mail não serão enviadas enquanto você estiver usando ativamente o Mastodon - setting_boost_modal: Quando ativada, a função de impulsionar primeiro abrirá uma caixa de diálogo de confirmação na qual você poderá alterar a visibilidade do seu impulsionamento. + setting_boost_modal: Quando ativada, a função de impulsionar primeiro abrirá uma caixa de diálogo de confirmação onde você poderá alterar a visibilidade do seu impulsionamento. setting_default_quote_policy_private: Publicações exclusivas de seguidores criadas no Mastodon não podem ser citadas por outras pessoas. setting_default_quote_policy_unlisted: Quando as pessoas citarem você, suas publicações também ficarão ocultas da linha do tempo. setting_default_sensitive: Mídia sensível está oculta por padrão e pode ser revelada com um clique diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 7368426b0c7c9f..19d0fd6f9a8d5e 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -57,6 +57,7 @@ sv: setting_advanced_layout: Visa Mastodon med en layout med flera kolumner, så att du kan se tidslinjen, aviseringar, och en tredje kolumn som du väljer själv. Rekommenderas inte för mindre skärmar. setting_aggregate_reblogs: Visa inte nya boostar för inlägg som nyligen blivit boostade (påverkar endast nymottagna boostar) setting_always_send_emails: E-postnotiser kommer vanligtvis inte skickas när du aktivt använder Mastodon + setting_boost_modal: När den är aktiverad kommer boostningen först att öppna en dialogruta där du kan ändra synligheten på din boost. setting_default_sensitive: Känslig media döljs som standard och kan visas med ett klick setting_display_media_default: Dölj media markerad som känslig setting_display_media_hide_all: Dölj alltid all media diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 7503b1548b5c87..58fc19951850d7 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -796,6 +796,8 @@ sv: view_dashboard_description: Ger användare tillgång till instrumentpanelen och olika mätvärden view_devops: DevOps view_devops_description: Ger användare tillgång till instrumentpanelerna Sidekiq och pgHero + view_feeds: Visa live- och ämnesflöden + view_feeds_description: Ger användare tillgång till live-och ämnesflöden oavsett serverinställningar title: Roller rules: add_new: Lägg till regel @@ -837,6 +839,7 @@ sv: title: Undantag användare från sökmotorindexering som standard discovery: follow_recommendations: Följrekommendationer + preamble: Att visa intressant innehåll är avgörande för nya användare som kanske inte känner någon på Mastodon. Styr hur olika upptäcktsfunktioner fungerar på din Server. privacy: Integritet profile_directory: Profilkatalog public_timelines: Offentliga tidslinjer @@ -847,6 +850,16 @@ sv: all: Till alla disabled: För ingen users: För inloggade lokala användare + feed_access: + modes: + authenticated: Endast autentiserade användare + disabled: Kräv specifik användarroll + public: Alla + landing_page: + values: + about: Om + local_feed: Lokalt flöde + trends: Trender registrations: moderation_recommandation: Se till att du har ett tillräckligt och reaktivt modereringsteam innan du öppnar registreringar till alla! preamble: Kontrollera vem som kan skapa ett konto på din server. @@ -900,6 +913,7 @@ sv: no_status_selected: Inga inlägg ändrades eftersom inga valdes open: Öppna inlägg original_status: Ursprungligt inlägg + quotes: Citat reblogs: Ombloggningar replied_to_html: Svarade på %{acct_link} status_changed: Inlägg ändrat @@ -907,6 +921,7 @@ sv: title: Kontoinlägg - @%{name} trending: Trendande view_publicly: Visa offentligt + view_quoted_post: Visa citerat inlägg visibility: Synlighet with_media: Med media strikes: @@ -1181,7 +1196,10 @@ sv: hint_html: Om du vill flytta från ett annat konto till detta kan du skapa ett alias här, detta krävs innan du kan fortsätta med att flytta följare från det gamla kontot till detta. Denna åtgärd är ofarlig och kan ångras. Kontomigreringen initieras från det gamla kontot.. remove: Avlänka alias appearance: + advanced_settings: Avancerade inställningar animations_and_accessibility: Animationer och tillgänglighet + boosting_preferences: Boostinställningar + boosting_preferences_info_html: "Tips: Oavsett inställningar kommer Skifttangenten + Klick på %{icon} boostikonen att boosta omedelbart." discovery: Upptäck localization: body: Mastodon översätts av volontärer. @@ -1583,6 +1601,13 @@ sv: expires_at: Utgår uses: Användningar title: Bjud in andra + link_preview: + author_html: Av %{name} + potentially_sensitive_content: + action: Klicka för att visa + confirm_visit: Är du säker på att du vill öppna den här länken? + hide_button: Dölj + label: Potentiellt känsligt innehåll lists: errors: limit: Du har nått det maximala antalet listor @@ -1647,6 +1672,7 @@ sv: disabled_account: Ditt nuvarande konto kommer inte att kunna användas fullt ut efteråt. Du kommer dock att ha tillgång till dataexport samt återaktivering. followers: Den här åtgärden kommer att flytta alla följare från det nuvarande kontot till det nya kontot only_redirect_html: Alternativt kan du bara sätta upp en omdirigering på din profil. + other_data: Inga andra data kommer att flyttas automatiskt (inklusive dina inlägg och listan över konton du följer) redirect: Ditt nuvarande kontos profil kommer att uppdateras med ett meddelande om omdirigering och uteslutas från sökningar moderation: title: Moderera @@ -1735,6 +1761,8 @@ sv: too_few_options: måste ha mer än ett objekt too_many_options: kan inte innehålla mer än %{max} objekt vote: Rösta + posting_defaults: + explanation: Dessa inställningar kommer att användas som standard när du skapar nya inlägg, men du kan redigera dem per inlägg i kompositören. preferences: other: Annat posting_defaults: Standardinställningar för inlägg @@ -1890,6 +1918,9 @@ sv: other: "%{count} videor" boosted_from_html: Boostad från %{acct_link} content_warning: 'Innehållsvarning: %{warning}' + content_warnings: + hide: Dölj inlägg + show: Visa mer default_language: Samma som användargränssnittet disallowed_hashtags: one: 'innehöll en otillåten hashtag: %{tags}' @@ -1898,15 +1929,30 @@ sv: errors: in_reply_not_found: Inlägget du försöker svara på verkar inte existera. quoted_status_not_found: Inlägget du försöker svara på verkar inte existera. + quoted_user_not_mentioned: Kan inte citera en icke-omnämnt användare i ett privat omnämnandeinlägg. over_character_limit: teckengräns på %{max} har överskridits pin_errors: direct: Inlägg som endast är synliga för nämnda användare kan inte fästas limit: Du har redan fäst det maximala antalet inlägg ownership: Någon annans inlägg kan inte fästas reblog: En boost kan inte fästas + quote_error: + not_available: Inlägg ej tillgängligt + pending_approval: Väntande inlägg + revoked: Inlägg borttaget av författaren + quote_policies: + followers: Endast följare + nobody: Bara jag + public: Alla + quote_post_author: Citerade ett inlägg av %{acct} title: '%{name}: "%{quote}"' visibilities: + direct: Privat omnämnande + private: Endast följare public: Offentlig + public_long: Alla på och utanför Mastodon + unlisted: Offentlig (begränsad) + unlisted_long: Dold från Mastodon-sökresultat, trendar och offentliga tidslinjer statuses_cleanup: enabled: Ta automatiskt bort gamla inlägg enabled_hint: Raderar dina inlägg automatiskt när de når en specifik ålder, såvida de inte matchar något av undantagen nedan From 204143becc41e6c4200c5b5f34964a685f7d61b9 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 25 Nov 2025 11:18:34 +0100 Subject: [PATCH 020/123] Increase HTTP read timeout for expensive S3 batch delete operation (#37004) --- app/lib/attachment_batch.rb | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/lib/attachment_batch.rb b/app/lib/attachment_batch.rb index 374abfac49038d..1443a1ec60cb39 100644 --- a/app/lib/attachment_batch.rb +++ b/app/lib/attachment_batch.rb @@ -112,10 +112,12 @@ def remove_files keys.each_slice(LIMIT) do |keys_slice| logger.debug { "Deleting #{keys_slice.size} objects" } - bucket.delete_objects(delete: { - objects: keys_slice.map { |key| { key: key } }, - quiet: true, - }) + with_overridden_timeout(bucket.client, 120) do + bucket.delete_objects(delete: { + objects: keys_slice.map { |key| { key: key } }, + quiet: true, + }) + end rescue => e retries += 1 @@ -134,6 +136,20 @@ def bucket @bucket ||= records.first.public_send(@attachment_names.first).s3_bucket end + # Currently, the aws-sdk-s3 gem does not offer a way to cleanly override the timeout + # per-request. So we change the client's config instead. As this client will likely + # be re-used for other jobs, restore its original configuration in an `ensure` block. + def with_overridden_timeout(s3_client, longer_read_timeout) + original_timeout = s3_client.config.http_read_timeout + s3_client.config.http_read_timeout = [original_timeout, longer_read_timeout].max + + begin + yield + ensure + s3_client.config.http_read_timeout = original_timeout + end + end + def nullified_attributes @attachment_names.flat_map { |attachment_name| NULLABLE_ATTRIBUTES.map { |attribute| "#{attachment_name}_#{attribute}" } & klass.column_names }.index_with(nil) end From a26636ff1fc7c31e175960d55d115d49c7e02199 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 25 Nov 2025 14:17:44 +0100 Subject: [PATCH 021/123] Fix compose autosuggest always lowercasing token (#36995) --- app/javascript/mastodon/components/autosuggest_input.jsx | 2 +- app/javascript/mastodon/components/autosuggest_textarea.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/components/autosuggest_input.jsx b/app/javascript/mastodon/components/autosuggest_input.jsx index 267c0442158323..f059356f09c7b3 100644 --- a/app/javascript/mastodon/components/autosuggest_input.jsx +++ b/app/javascript/mastodon/components/autosuggest_input.jsx @@ -28,7 +28,7 @@ const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => { return [null, null]; } - word = word.trim().toLowerCase(); + word = word.trim(); if (word.length > 0) { return [left + 1, word]; diff --git a/app/javascript/mastodon/components/autosuggest_textarea.jsx b/app/javascript/mastodon/components/autosuggest_textarea.jsx index a9acc87252d89c..fae078da31b379 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.jsx +++ b/app/javascript/mastodon/components/autosuggest_textarea.jsx @@ -29,7 +29,7 @@ const textAtCursorMatchesToken = (str, caretPosition) => { return [null, null]; } - word = word.trim().toLowerCase(); + word = word.trim(); if (word.length > 0) { return [left + 1, word]; From ca53195b31ef7eb9205f879e022b09b28efc9314 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:21:25 +0000 Subject: [PATCH 022/123] chore(deps): update dependency sass to v1.94.2 (#36826) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a7a34cd7cfa4a1..7d8d08af7e6772 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12090,8 +12090,8 @@ __metadata: linkType: hard "sass@npm:^1.62.1, sass@npm:^1.70.0": - version: 1.93.3 - resolution: "sass@npm:1.93.3" + version: 1.94.2 + resolution: "sass@npm:1.94.2" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" @@ -12102,7 +12102,7 @@ __metadata: optional: true bin: sass: sass.js - checksum: 10c0/b9facc64de10c9d1514272c1dcbb48ca99d5f591a1cd43fd27d641275d9d95948f1299107ab5b309e2abb552667cca84a38a1a3df5116eb72eba4144bf850b6a + checksum: 10c0/49a656dfab58299165ef94e71483a333972daee68c49fa542858d4912accdfb1707338226a165b1a2dfcdb2509fcda5a5b4f3780d14e49b6d38d93c8043475d3 languageName: node linkType: hard From 861625fdca4577658299e1b2ad08bfe3d4fe434f Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 25 Nov 2025 14:21:59 +0100 Subject: [PATCH 023/123] Prevent vertical videos from overflowing the viewport (#36966) --- app/javascript/styles/mastodon/components.scss | 2 ++ app/javascript/styles_new/mastodon/components.scss | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 238fbe2be3232c..5571fd6f47ea1c 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -7699,6 +7699,8 @@ a.status-card { position: relative; background: $base-shadow-color; max-width: 100%; + max-height: max(400px, 60vh); + margin-inline: auto; border-radius: 8px; box-sizing: border-box; color: $white; diff --git a/app/javascript/styles_new/mastodon/components.scss b/app/javascript/styles_new/mastodon/components.scss index 970e3d128f2b14..34a0308402d2c9 100644 --- a/app/javascript/styles_new/mastodon/components.scss +++ b/app/javascript/styles_new/mastodon/components.scss @@ -7492,6 +7492,8 @@ a.status-card { color: var(--color-text-on-media); background: var(--color-bg-media); max-width: 100%; + max-height: max(400px, 60vh); + margin-inline: auto; border-radius: 8px; box-sizing: border-box; display: flex; From 8c772028ac85917c7a46b3af86c1b6884c272d70 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 25 Nov 2025 14:49:45 +0100 Subject: [PATCH 024/123] Replace most unsafe React lifecycle methods (#36970) --- .../mastodon/components/autosuggest_input.jsx | 4 ++-- .../mastodon/components/media_gallery.jsx | 10 +++++----- app/javascript/mastodon/components/modal_root.jsx | 14 ++++++-------- app/javascript/mastodon/features/blocks/index.jsx | 2 +- .../compose/components/emoji_picker_dropdown.jsx | 10 ++++++++-- .../mastodon/features/favourites/index.jsx | 2 +- .../mastodon/features/follow_requests/index.jsx | 2 +- .../mastodon/features/list_timeline/index.jsx | 7 +++---- app/javascript/mastodon/features/mutes/index.jsx | 2 +- .../mastodon/features/pinned_statuses/index.jsx | 2 +- app/javascript/mastodon/features/reblogs/index.jsx | 2 +- .../mastodon/features/ui/components/bundle.jsx | 8 ++++---- app/javascript/mastodon/features/ui/index.jsx | 2 +- 13 files changed, 35 insertions(+), 32 deletions(-) diff --git a/app/javascript/mastodon/components/autosuggest_input.jsx b/app/javascript/mastodon/components/autosuggest_input.jsx index f059356f09c7b3..9e342a353a169e 100644 --- a/app/javascript/mastodon/components/autosuggest_input.jsx +++ b/app/javascript/mastodon/components/autosuggest_input.jsx @@ -159,8 +159,8 @@ export default class AutosuggestInput extends ImmutablePureComponent { this.input.focus(); }; - UNSAFE_componentWillReceiveProps (nextProps) { - if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) { + componentDidUpdate (prevProps) { + if (prevProps.suggestions !== this.props.suggestions && this.props.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) { this.setState({ suggestionsHidden: false }); } } diff --git a/app/javascript/mastodon/components/media_gallery.jsx b/app/javascript/mastodon/components/media_gallery.jsx index 64250236d8b258..1b0ee6d95883dc 100644 --- a/app/javascript/mastodon/components/media_gallery.jsx +++ b/app/javascript/mastodon/components/media_gallery.jsx @@ -242,11 +242,11 @@ class MediaGallery extends PureComponent { window.removeEventListener('resize', this.handleResize); } - UNSAFE_componentWillReceiveProps (nextProps) { - if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) { - this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' }); - } else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) { - this.setState({ visible: nextProps.visible }); + componentDidUpdate (prevProps) { + if (!is(prevProps.media, this.props.media) && this.props.visible === undefined) { + this.setState({ visible: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all' }); + } else if (!is(prevProps.visible, this.props.visible) && this.props.visible !== undefined) { + this.setState({ visible: this.props.visible }); } } diff --git a/app/javascript/mastodon/components/modal_root.jsx b/app/javascript/mastodon/components/modal_root.jsx index 1eae0819af7b2d..61ff19256f8fd1 100644 --- a/app/javascript/mastodon/components/modal_root.jsx +++ b/app/javascript/mastodon/components/modal_root.jsx @@ -61,14 +61,6 @@ class ModalRoot extends PureComponent { this.history = this.props.history || createBrowserHistory(); } - UNSAFE_componentWillReceiveProps (nextProps) { - if (!!nextProps.children && !this.props.children) { - this.activeElement = document.activeElement; - - this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true)); - } - } - componentDidUpdate (prevProps) { if (!this.props.children && !!prevProps.children) { this.getSiblings().forEach(sibling => sibling.removeAttribute('inert')); @@ -85,9 +77,15 @@ class ModalRoot extends PureComponent { this._handleModalClose(); } + if (this.props.children && !prevProps.children) { + this.activeElement = document.activeElement; + + this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true)); + this._handleModalOpen(); } + if (this.props.children) { this._ensureHistoryBuffer(); } diff --git a/app/javascript/mastodon/features/blocks/index.jsx b/app/javascript/mastodon/features/blocks/index.jsx index 57f86042e3b256..2bb3bc14621ecf 100644 --- a/app/javascript/mastodon/features/blocks/index.jsx +++ b/app/javascript/mastodon/features/blocks/index.jsx @@ -38,7 +38,7 @@ class Blocks extends ImmutablePureComponent { multiColumn: PropTypes.bool, }; - UNSAFE_componentWillMount () { + componentDidMount () { this.props.dispatch(fetchBlocks()); } diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx index 1d96427f6b1a4b..ad769fe92444a6 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx @@ -61,8 +61,14 @@ class ModifierPickerMenu extends PureComponent { this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1); }; - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.active) { + componentDidMount() { + if (this.props.active) { + this.attachListeners(); + } + } + + componentDidUpdate() { + if (this.props.active) { this.attachListeners(); } else { this.removeListeners(); diff --git a/app/javascript/mastodon/features/favourites/index.jsx b/app/javascript/mastodon/features/favourites/index.jsx index c5c6abe0818ada..245954ef806081 100644 --- a/app/javascript/mastodon/features/favourites/index.jsx +++ b/app/javascript/mastodon/features/favourites/index.jsx @@ -41,7 +41,7 @@ class Favourites extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - UNSAFE_componentWillMount () { + componentDidMount () { if (!this.props.accountIds) { this.props.dispatch(fetchFavourites(this.props.params.statusId)); } diff --git a/app/javascript/mastodon/features/follow_requests/index.jsx b/app/javascript/mastodon/features/follow_requests/index.jsx index 7d651f2ca69c24..91648412b55799 100644 --- a/app/javascript/mastodon/features/follow_requests/index.jsx +++ b/app/javascript/mastodon/features/follow_requests/index.jsx @@ -45,7 +45,7 @@ class FollowRequests extends ImmutablePureComponent { multiColumn: PropTypes.bool, }; - UNSAFE_componentWillMount () { + componentDidMount () { this.props.dispatch(fetchFollowRequests()); } diff --git a/app/javascript/mastodon/features/list_timeline/index.jsx b/app/javascript/mastodon/features/list_timeline/index.jsx index 9020fd2bee9f46..cc4533ea99305f 100644 --- a/app/javascript/mastodon/features/list_timeline/index.jsx +++ b/app/javascript/mastodon/features/list_timeline/index.jsx @@ -73,11 +73,10 @@ class ListTimeline extends PureComponent { this.disconnect = dispatch(connectListStream(id)); } - UNSAFE_componentWillReceiveProps (nextProps) { - const { dispatch } = this.props; - const { id } = nextProps.params; + componentDidUpdate (prevProps) { + const { dispatch, params: {id} } = this.props; - if (id !== this.props.params.id) { + if (id !== prevProps.params.id) { if (this.disconnect) { this.disconnect(); this.disconnect = null; diff --git a/app/javascript/mastodon/features/mutes/index.jsx b/app/javascript/mastodon/features/mutes/index.jsx index 5a5711da58291b..28c76a04e201b2 100644 --- a/app/javascript/mastodon/features/mutes/index.jsx +++ b/app/javascript/mastodon/features/mutes/index.jsx @@ -40,7 +40,7 @@ class Mutes extends ImmutablePureComponent { multiColumn: PropTypes.bool, }; - UNSAFE_componentWillMount () { + componentDidMount () { this.props.dispatch(fetchMutes()); } diff --git a/app/javascript/mastodon/features/pinned_statuses/index.jsx b/app/javascript/mastodon/features/pinned_statuses/index.jsx index 921e9a60721923..786cbeee9417c8 100644 --- a/app/javascript/mastodon/features/pinned_statuses/index.jsx +++ b/app/javascript/mastodon/features/pinned_statuses/index.jsx @@ -34,7 +34,7 @@ class PinnedStatuses extends ImmutablePureComponent { multiColumn: PropTypes.bool, }; - UNSAFE_componentWillMount () { + componentDidMount () { this.props.dispatch(fetchPinnedStatuses()); } diff --git a/app/javascript/mastodon/features/reblogs/index.jsx b/app/javascript/mastodon/features/reblogs/index.jsx index 8bde919a0f3cbe..24786b62f065d0 100644 --- a/app/javascript/mastodon/features/reblogs/index.jsx +++ b/app/javascript/mastodon/features/reblogs/index.jsx @@ -42,7 +42,7 @@ class Reblogs extends ImmutablePureComponent { intl: PropTypes.object.isRequired, }; - UNSAFE_componentWillMount () { + componentDidMount () { if (!this.props.accountIds) { this.props.dispatch(fetchReblogs(this.props.params.statusId)); } diff --git a/app/javascript/mastodon/features/ui/components/bundle.jsx b/app/javascript/mastodon/features/ui/components/bundle.jsx index 15c4220b3483ad..3617baf4f11c5a 100644 --- a/app/javascript/mastodon/features/ui/components/bundle.jsx +++ b/app/javascript/mastodon/features/ui/components/bundle.jsx @@ -33,13 +33,13 @@ class Bundle extends PureComponent { forceRender: false, }; - UNSAFE_componentWillMount() { + componentDidMount() { this.load(this.props); } - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.fetchComponent !== this.props.fetchComponent) { - this.load(nextProps); + componentDidUpdate(prevProps) { + if (prevProps.fetchComponent !== this.props.fetchComponent) { + this.load(this.props); } } diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 209e4b4a87882d..476c25e32dab28 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -108,7 +108,7 @@ class SwitchingColumnsArea extends PureComponent { forceOnboarding: PropTypes.bool, }; - UNSAFE_componentWillMount () { + componentDidMount () { document.body.classList.toggle('layout-single-column', this.props.singleColumn); document.body.classList.toggle('layout-multiple-columns', !this.props.singleColumn); } From 4ca458e0b4611b6bb199b18fa2ae8442c6899f33 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:53:23 +0000 Subject: [PATCH 025/123] chore(deps): update devdependencies (non-major) (#36802) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 353 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 185 insertions(+), 168 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7d8d08af7e6772..38c04d9fd91a93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -334,7 +334,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.4, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": version: 7.28.5 resolution: "@babel/parser@npm:7.28.5" dependencies: @@ -1209,7 +1209,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.25.4, @babel/types@npm:^7.26.10, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.4.4": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.26.10, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.4.4": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" dependencies: @@ -1226,32 +1226,25 @@ __metadata: languageName: node linkType: hard -"@cacheable/memoize@npm:^2.0.1": - version: 2.0.1 - resolution: "@cacheable/memoize@npm:2.0.1" +"@cacheable/memory@npm:^2.0.5": + version: 2.0.5 + resolution: "@cacheable/memory@npm:2.0.5" dependencies: - "@cacheable/utils": "npm:^2.0.1" - checksum: 10c0/40ab683429132654b95edc1229c175c45045c239d647bffa75165ae572cd8da0ee9852dfb7c6baca4e8ecc0e3005c2555222ae02add8206d4a227106b1c5fc8d + "@cacheable/utils": "npm:^2.3.0" + "@keyv/bigmap": "npm:^1.1.0" + hookified: "npm:^1.12.2" + keyv: "npm:^5.5.4" + checksum: 10c0/bef5b26de58c4ca20d7cce457d053766b5fb13de48bf444e0ecf56481a16e6556a194dafc28f41906ae4e6cd053ef3d57686c770b8e7a2d381648505bd673839 languageName: node linkType: hard -"@cacheable/memory@npm:^2.0.1": - version: 2.0.1 - resolution: "@cacheable/memory@npm:2.0.1" +"@cacheable/utils@npm:^2.3.0": + version: 2.3.1 + resolution: "@cacheable/utils@npm:2.3.1" dependencies: - "@cacheable/memoize": "npm:^2.0.1" - "@cacheable/utils": "npm:^2.0.1" - "@keyv/bigmap": "npm:^1.0.0" - hookified: "npm:^1.12.0" - keyv: "npm:^5.5.1" - checksum: 10c0/dde1caf7fe66febe8dc9d32ac4eaee48b6b1e690881adfaf3c41188a00892a20514cd7847e33a64bad09c4c5d6e1377eb8e373b02b4b9fb5fa27e6a67297c625 - languageName: node - linkType: hard - -"@cacheable/utils@npm:^2.0.1": - version: 2.0.1 - resolution: "@cacheable/utils@npm:2.0.1" - checksum: 10c0/63806cca7f60add1f7bd4acee279c723760322316661a9fc6deab9ba7c03569e6cbe744d88159d4e72c1f6d296bba53b53c396d01d52294d45c9fed04c416ca4 + hashery: "npm:^1.2.0" + keyv: "npm:^5.5.4" + checksum: 10c0/04802bc11293ff569204e5f143cd11314856e3453de3e5757068cfd9df5c974a80aa9974c8400d88b22de3af70a7d8511d2d7fe927356365f41b765693a4c4bb languageName: node linkType: hard @@ -2682,12 +2675,15 @@ __metadata: languageName: node linkType: hard -"@keyv/bigmap@npm:^1.0.0": - version: 1.0.2 - resolution: "@keyv/bigmap@npm:1.0.2" +"@keyv/bigmap@npm:^1.1.0": + version: 1.3.0 + resolution: "@keyv/bigmap@npm:1.3.0" dependencies: - hookified: "npm:^1.12.1" - checksum: 10c0/1fe415265241b015c19891dc6c1909b41a5a033e57339b40f85af27355d2f52b52df01795a3f7ba37d3ec2b67e147c05914965775254ff8dbd1701adab45208a + hashery: "npm:^1.2.0" + hookified: "npm:^1.13.0" + peerDependencies: + keyv: ^5.5.4 + checksum: 10c0/68fe63451097067d8359dc25b7e5b832fe9d99493ca32602686026c8d14c9ca7ecaf19312df9420c7f54df8de1b26e7305f9189fa364a82f39730710eb895f9e languageName: node linkType: hard @@ -4829,62 +4825,62 @@ __metadata: linkType: hard "@vitest/browser-playwright@npm:^4.0.5": - version: 4.0.7 - resolution: "@vitest/browser-playwright@npm:4.0.7" + version: 4.0.13 + resolution: "@vitest/browser-playwright@npm:4.0.13" dependencies: - "@vitest/browser": "npm:4.0.7" - "@vitest/mocker": "npm:4.0.7" + "@vitest/browser": "npm:4.0.13" + "@vitest/mocker": "npm:4.0.13" tinyrainbow: "npm:^3.0.3" peerDependencies: playwright: "*" - vitest: 4.0.7 + vitest: 4.0.13 peerDependenciesMeta: playwright: optional: false - checksum: 10c0/85969557a441c141b14bc13e3b2f1b70f7e7aa3eb8a63489b4b3af253b3fcd8019293cb56e1dea50f338eca9bc845e21ec85f1c88ab6bc3ee494fbdacac153a3 + checksum: 10c0/5a387eb02534736a25cfff442e66e8c41ef97f0db744ffe8360e484af61d66db793cb44ba8681471b8c21ba509db1775f1ba688bc7f50325eee76918773848cb languageName: node linkType: hard -"@vitest/browser@npm:4.0.7, @vitest/browser@npm:^4.0.5": - version: 4.0.7 - resolution: "@vitest/browser@npm:4.0.7" +"@vitest/browser@npm:4.0.13, @vitest/browser@npm:^4.0.5": + version: 4.0.13 + resolution: "@vitest/browser@npm:4.0.13" dependencies: - "@vitest/mocker": "npm:4.0.7" - "@vitest/utils": "npm:4.0.7" - magic-string: "npm:^0.30.19" + "@vitest/mocker": "npm:4.0.13" + "@vitest/utils": "npm:4.0.13" + magic-string: "npm:^0.30.21" pixelmatch: "npm:7.1.0" pngjs: "npm:^7.0.0" sirv: "npm:^3.0.2" tinyrainbow: "npm:^3.0.3" ws: "npm:^8.18.3" peerDependencies: - vitest: 4.0.7 - checksum: 10c0/22b6d666f7ae6220dea8d55afd787b435f4dc19656d22c7892f7caafd3c26b62fc87a091384c6044aa49b84c187581f6cad44fd8a65f1583abef5197a162b5c6 + vitest: 4.0.13 + checksum: 10c0/22c9297888a7288717cad706ca08159b3af05337a2f9b8da98fe74e683d534c8d816e40fece96f218d223a54c06762c5aa2a5db23ce8565c174ab9a70aade7f0 languageName: node linkType: hard "@vitest/coverage-v8@npm:^4.0.5": - version: 4.0.7 - resolution: "@vitest/coverage-v8@npm:4.0.7" + version: 4.0.13 + resolution: "@vitest/coverage-v8@npm:4.0.13" dependencies: "@bcoe/v8-coverage": "npm:^1.0.2" - "@vitest/utils": "npm:4.0.7" - ast-v8-to-istanbul: "npm:^0.3.5" + "@vitest/utils": "npm:4.0.13" + ast-v8-to-istanbul: "npm:^0.3.8" debug: "npm:^4.4.3" istanbul-lib-coverage: "npm:^3.2.2" istanbul-lib-report: "npm:^3.0.1" istanbul-lib-source-maps: "npm:^5.0.6" istanbul-reports: "npm:^3.2.0" - magicast: "npm:^0.3.5" - std-env: "npm:^3.9.0" + magicast: "npm:^0.5.1" + std-env: "npm:^3.10.0" tinyrainbow: "npm:^3.0.3" peerDependencies: - "@vitest/browser": 4.0.7 - vitest: 4.0.7 + "@vitest/browser": 4.0.13 + vitest: 4.0.13 peerDependenciesMeta: "@vitest/browser": optional: true - checksum: 10c0/4a34c6de4d1e8173856af078c053e5a6d4a3ad0085fd613fddbe5067b7083d6d11858788a994a3bd427630ddd56cc5eb948b59b425b2c7ba7dd73094d2f1844f + checksum: 10c0/dd462b13605fca62d20cb5a4f9d7cfda2bfa5e77aedc16ad5a633d8dabb24f68e96382ac4d16c2fdcddb45e7c4717e558f5ac51a70c64857f5e89d12d8700823 languageName: node linkType: hard @@ -4901,17 +4897,17 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:4.0.7": - version: 4.0.7 - resolution: "@vitest/expect@npm:4.0.7" +"@vitest/expect@npm:4.0.13": + version: 4.0.13 + resolution: "@vitest/expect@npm:4.0.13" dependencies: "@standard-schema/spec": "npm:^1.0.0" "@types/chai": "npm:^5.2.2" - "@vitest/spy": "npm:4.0.7" - "@vitest/utils": "npm:4.0.7" - chai: "npm:^6.0.1" + "@vitest/spy": "npm:4.0.13" + "@vitest/utils": "npm:4.0.13" + chai: "npm:^6.2.1" tinyrainbow: "npm:^3.0.3" - checksum: 10c0/366d7be563149b6143a6f275ae37b77ac7b13d96cd5b0992bfece5c801c1ed1cb7f4a1a1921e9fa5c47f8ad4e73bdfb9f3f362acc42be34cedcb907020c313a2 + checksum: 10c0/1cd7dc02cb650d024826f2e20260d23c2b9ab6733341045ffb59be7af73402eecd2422198d7e4eac609710730b6d11f0faf22af0c074d65445ab88d9da7f6556 languageName: node linkType: hard @@ -4934,13 +4930,13 @@ __metadata: languageName: node linkType: hard -"@vitest/mocker@npm:4.0.7": - version: 4.0.7 - resolution: "@vitest/mocker@npm:4.0.7" +"@vitest/mocker@npm:4.0.13": + version: 4.0.13 + resolution: "@vitest/mocker@npm:4.0.13" dependencies: - "@vitest/spy": "npm:4.0.7" + "@vitest/spy": "npm:4.0.13" estree-walker: "npm:^3.0.3" - magic-string: "npm:^0.30.19" + magic-string: "npm:^0.30.21" peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -4949,7 +4945,7 @@ __metadata: optional: true vite: optional: true - checksum: 10c0/a500d2eca0e8b43f63358bd102e1203f3e478c0896ebe41dcdac0ab048e991736dc053bd4129dcf62ba94d4d3d2e43793175cd7deb6552cf54a2a9c8a5bab77b + checksum: 10c0/667ec4fbb77a28ede1b055b9d962beed92c2dd2d83b7bab1ed22239578a7b399180a978e26ef136301c0bc7c57c75ca178cda55ec94081856437e3b4be4a3e19 languageName: node linkType: hard @@ -4962,33 +4958,33 @@ __metadata: languageName: node linkType: hard -"@vitest/pretty-format@npm:4.0.7": - version: 4.0.7 - resolution: "@vitest/pretty-format@npm:4.0.7" +"@vitest/pretty-format@npm:4.0.13": + version: 4.0.13 + resolution: "@vitest/pretty-format@npm:4.0.13" dependencies: tinyrainbow: "npm:^3.0.3" - checksum: 10c0/4084355dbc7b1b9ee4b777adbbc44833dfd4c4a4bb2de8cb5ef28f490bf1c699eb31820157692dc87504b26e6b7bd931d0f316472895bfbc608327a671646032 + checksum: 10c0/c32ebd3457fd4b92fa89800b0ddaa2ca7de84df75be3c64f87ace006f3a3ec546a6ffd4c06f88e3161e80f9e10c83dfee61150e682eaa5a1871240d98c7ef0eb languageName: node linkType: hard -"@vitest/runner@npm:4.0.7": - version: 4.0.7 - resolution: "@vitest/runner@npm:4.0.7" +"@vitest/runner@npm:4.0.13": + version: 4.0.13 + resolution: "@vitest/runner@npm:4.0.13" dependencies: - "@vitest/utils": "npm:4.0.7" + "@vitest/utils": "npm:4.0.13" pathe: "npm:^2.0.3" - checksum: 10c0/717d7ce765eba1493051b309f82755a4b1d8594de6cd9d036864c9464dadc604c703388d96e070fe843b0216d2a7b66e59d18ad3db8055990114b3506bc172bf + checksum: 10c0/e9f95b8a413f875123e5c32322dd92bd523d6e3ba25b054f0e865f42e01f82666b847535fe5ea2ff3238faa2df16cefc7e5845d3d5ccfecb3a96ab924d31e760 languageName: node linkType: hard -"@vitest/snapshot@npm:4.0.7": - version: 4.0.7 - resolution: "@vitest/snapshot@npm:4.0.7" +"@vitest/snapshot@npm:4.0.13": + version: 4.0.13 + resolution: "@vitest/snapshot@npm:4.0.13" dependencies: - "@vitest/pretty-format": "npm:4.0.7" - magic-string: "npm:^0.30.19" + "@vitest/pretty-format": "npm:4.0.13" + magic-string: "npm:^0.30.21" pathe: "npm:^2.0.3" - checksum: 10c0/0382303038ebc58d419047fd329f848336e06b292839ea6ec05063092ce32464eb6a21c00e6487541e42b8155fa0c10b8cc2f6445ff85256b7d73feeb4a8afef + checksum: 10c0/ad3fbe9ff30bc294811556f958e0014cb03888ea06ac7c05ab41e20c582fe8e27d8f4176aaf8a8e230fc6377124af30f5622173fb459b70a30ff9dd622664be2 languageName: node linkType: hard @@ -5001,18 +4997,18 @@ __metadata: languageName: node linkType: hard -"@vitest/spy@npm:4.0.7": - version: 4.0.7 - resolution: "@vitest/spy@npm:4.0.7" - checksum: 10c0/88c8fdffa54cdfb9f4157316d1fb3308ad1630791881866878c7cbf837ad8e6e6aa79041c82635b2598ed551cf93409d34a434c87b779a30a66f55221de636fa +"@vitest/spy@npm:4.0.13": + version: 4.0.13 + resolution: "@vitest/spy@npm:4.0.13" + checksum: 10c0/64dc4c496eb9aacd3137beedccdb3265c895f8cd2626b3f76d7324ad944be5b1567ede2652eee407991796879270a63abdec4453c73185e637a1d7ff9cd1a009 languageName: node linkType: hard "@vitest/ui@npm:^4.0.5": - version: 4.0.7 - resolution: "@vitest/ui@npm:4.0.7" + version: 4.0.13 + resolution: "@vitest/ui@npm:4.0.13" dependencies: - "@vitest/utils": "npm:4.0.7" + "@vitest/utils": "npm:4.0.13" fflate: "npm:^0.8.2" flatted: "npm:^3.3.3" pathe: "npm:^2.0.3" @@ -5020,8 +5016,8 @@ __metadata: tinyglobby: "npm:^0.2.15" tinyrainbow: "npm:^3.0.3" peerDependencies: - vitest: 4.0.7 - checksum: 10c0/e7173da36e9549a8b04ee4015a64882075aa9f49a59cb135616d22c6d881244c938cf41f6ef149d7e1f42ff414dc72f2bb23f5334b4285801adf83280b23a279 + vitest: 4.0.13 + checksum: 10c0/7656762bc6a9c99850639d0809ada53ad4b842e4d9a8c7b82987b60bcf1675c98c077516a3777fce9580255538d0d050c92cb1e6f6296af6365f2387d7a972b9 languageName: node linkType: hard @@ -5036,13 +5032,13 @@ __metadata: languageName: node linkType: hard -"@vitest/utils@npm:4.0.7": - version: 4.0.7 - resolution: "@vitest/utils@npm:4.0.7" +"@vitest/utils@npm:4.0.13": + version: 4.0.13 + resolution: "@vitest/utils@npm:4.0.13" dependencies: - "@vitest/pretty-format": "npm:4.0.7" + "@vitest/pretty-format": "npm:4.0.13" tinyrainbow: "npm:^3.0.3" - checksum: 10c0/a2305c5117a30f1685f362767a0e0cc47265f3602469641f6eb01b4b708e2b1c35c33ccf480314348b21978d70a78311cce3f5bdd09de6456d528c5469093217 + checksum: 10c0/1b64872e82a652f11bfd813c0140eaae9b6e4ece39fc0e460ab2b3111b925892f1128f3b27f3a280471cfc404bb9c9289c59f8ca5387950ab35d024d154e9ec1 languageName: node linkType: hard @@ -5341,7 +5337,7 @@ __metadata: languageName: node linkType: hard -"ast-v8-to-istanbul@npm:^0.3.5": +"ast-v8-to-istanbul@npm:^0.3.8": version: 0.3.8 resolution: "ast-v8-to-istanbul@npm:0.3.8" dependencies: @@ -5691,16 +5687,16 @@ __metadata: languageName: node linkType: hard -"cacheable@npm:^2.0.1": - version: 2.0.1 - resolution: "cacheable@npm:2.0.1" +"cacheable@npm:^2.2.0": + version: 2.2.0 + resolution: "cacheable@npm:2.2.0" dependencies: - "@cacheable/memoize": "npm:^2.0.1" - "@cacheable/memory": "npm:^2.0.1" - "@cacheable/utils": "npm:^2.0.1" - hookified: "npm:^1.12.0" - keyv: "npm:^5.5.1" - checksum: 10c0/c4c16af5997850531a02b0efa150d3a06fff9560eca15a16e4042038a63487e967f00f5f1b63310523877b2ac4038e732faf0e707a5ff5d10cb861c01ed67ca8 + "@cacheable/memory": "npm:^2.0.5" + "@cacheable/utils": "npm:^2.3.0" + hookified: "npm:^1.13.0" + keyv: "npm:^5.5.4" + qified: "npm:^0.5.2" + checksum: 10c0/39b09e68b0a3da6c53dba1ed10dd13b63bf03f1fcb93ee941dd324d758e2d84466f1acb0c760fa78b23377be15ee58dc9acfb1fde85a89d2483d195b2f75e249 languageName: node linkType: hard @@ -5770,10 +5766,10 @@ __metadata: languageName: node linkType: hard -"chai@npm:^6.0.1": - version: 6.2.0 - resolution: "chai@npm:6.2.0" - checksum: 10c0/a4b7d7f5907187e09f1847afa838d6d1608adc7d822031b7900813c4ed5d9702911ac2468bf290676f22fddb3d727b1be90b57c1d0a69b902534ee29cdc6ff8a +"chai@npm:^6.2.1": + version: 6.2.1 + resolution: "chai@npm:6.2.1" + checksum: 10c0/0c2d84392d7c6d44ca5d14d94204f1760e22af68b83d1f4278b5c4d301dabfc0242da70954dd86b1eda01e438f42950de6cf9d569df2103678538e4014abe50b languageName: node linkType: hard @@ -5950,7 +5946,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:^14.0.1": +"commander@npm:^14.0.2": version: 14.0.2 resolution: "commander@npm:14.0.2" checksum: 10c0/245abd1349dbad5414cb6517b7b5c584895c02c4f7836ff5395f301192b8566f9796c82d7bd6c92d07eba8775fe4df86602fca5d86d8d10bcc2aded1e21c2aeb @@ -7401,9 +7397,9 @@ __metadata: linkType: hard "fake-indexeddb@npm:^6.0.1": - version: 6.2.4 - resolution: "fake-indexeddb@npm:6.2.4" - checksum: 10c0/53b7e9e8f7e413c1a45a4f80f6deda00ef02676cc3a4457e73abff06720ec6b4f4f63fd65c1ef6b03298a1e7e4684d4bf65137a8b9ac166dcfade4b2acf229b4 + version: 6.2.5 + resolution: "fake-indexeddb@npm:6.2.5" + checksum: 10c0/6c5e2fe84a61daa06d7ad63699d1041fe61847f15f92db12415634b3db94f363a64be9e08a3c3c4434af9c3c0132086b85c4d5dc5e8e06edae1e7daf70ce1f3c languageName: node linkType: hard @@ -7497,12 +7493,12 @@ __metadata: languageName: node linkType: hard -"file-entry-cache@npm:^10.1.4": - version: 10.1.4 - resolution: "file-entry-cache@npm:10.1.4" +"file-entry-cache@npm:^11.1.0": + version: 11.1.1 + resolution: "file-entry-cache@npm:11.1.1" dependencies: - flat-cache: "npm:^6.1.13" - checksum: 10c0/78a7d6b257c620374a8fc5280f14acffc7bd5cb5d39a5bd3509c640f17209f5194eff6e3b476d19db7cfbe9f97abe85ec8d33260f7ed94225efb2a95a68841a6 + flat-cache: "npm:^6.1.19" + checksum: 10c0/aa639f5dd578f63984a941f34b112180a4bd9d091d03970752437958158932957fc576861b9fbcf4d6eceaeb0779ad5359befdc321cc1bac59aa6f56f6b1d205 languageName: node linkType: hard @@ -7574,14 +7570,14 @@ __metadata: languageName: node linkType: hard -"flat-cache@npm:^6.1.13": - version: 6.1.14 - resolution: "flat-cache@npm:6.1.14" +"flat-cache@npm:^6.1.19": + version: 6.1.19 + resolution: "flat-cache@npm:6.1.19" dependencies: - cacheable: "npm:^2.0.1" + cacheable: "npm:^2.2.0" flatted: "npm:^3.3.3" - hookified: "npm:^1.12.0" - checksum: 10c0/e17eda47414b4742bc557650788f18255068621afb66b23dfc6a47b3ef3e6c366ede7329e71406bf331ef6f4a3b243040803eb175560b4ceb204779066ba6e92 + hookified: "npm:^1.13.0" + checksum: 10c0/80c2d3c6ff2b7327920dacf2ab57e70ba4e865120209e41295689f029fcec17a6b49892ded7ce758d968d2b097fa2f9ab4e52923d971f3cdc90af9faba4680fd languageName: node linkType: hard @@ -8028,6 +8024,15 @@ __metadata: languageName: node linkType: hard +"hashery@npm:^1.2.0": + version: 1.2.0 + resolution: "hashery@npm:1.2.0" + dependencies: + hookified: "npm:^1.13.0" + checksum: 10c0/57905ae4bcb12faedf222b1f39cc05424ae2a2bba1f613b9c582a4e5012b8361c14a25a5a0c16da7eca70ee8338ad2924d6b9566667014c927a36d4b90ad5b72 + languageName: node + linkType: hard + "hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" @@ -8090,10 +8095,10 @@ __metadata: languageName: node linkType: hard -"hookified@npm:^1.12.0, hookified@npm:^1.12.1": - version: 1.12.1 - resolution: "hookified@npm:1.12.1" - checksum: 10c0/fe8d74ee49d1f79677dcdff7606eeb731f7a7dc59f61ec2141a11e3bb94ff6532f870649b900fa9f68568f410c504a338d8732e4d1abe61b426e645c37862e50 +"hookified@npm:^1.12.2, hookified@npm:^1.13.0": + version: 1.13.0 + resolution: "hookified@npm:1.13.0" + checksum: 10c0/26718a60385ab95f20173323c175a23b06efcc1fac613c51714c9c38038c7395ed52d3bea660840c8362d92dc38022ae4469c2a531728f6116f4df53f70505e7 languageName: node linkType: hard @@ -9057,12 +9062,12 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^5.5.1": - version: 5.5.2 - resolution: "keyv@npm:5.5.2" +"keyv@npm:^5.5.4": + version: 5.5.4 + resolution: "keyv@npm:5.5.4" dependencies: "@keyv/serialize": "npm:^1.1.1" - checksum: 10c0/b0a224210e8bbc4a5913535aa7cc8552809dc81ad67311cc78a2ccfe5485b82b23b8e28ea3a1202e8352c18dd2a0b12b49cda5bb933958d2dcf88042db54c9d0 + checksum: 10c0/8dad7f61022c6348c4c691a19468b7c238198252edbd3cc08277d95253c137af7ce5ffd763b6ffded4a75cbe03dc3134f1adcd3dd26c5767c2c9c254e3b39001 languageName: node linkType: hard @@ -9186,10 +9191,10 @@ __metadata: linkType: hard "lint-staged@npm:^16.2.6": - version: 16.2.6 - resolution: "lint-staged@npm:16.2.6" + version: 16.2.7 + resolution: "lint-staged@npm:16.2.7" dependencies: - commander: "npm:^14.0.1" + commander: "npm:^14.0.2" listr2: "npm:^9.0.5" micromatch: "npm:^4.0.8" nano-spawn: "npm:^2.0.0" @@ -9198,7 +9203,7 @@ __metadata: yaml: "npm:^2.8.1" bin: lint-staged: bin/lint-staged.js - checksum: 10c0/6bae38082a0fcb3f699b144d1a4b85394f259f17a1f8a58b22122b9f1c6bb5e8340d6ee4bff12e52dbc4267377d6dde9e5c206157f381f1924a2640717f769c1 + checksum: 10c0/9a677c21a8112d823ae5bc565ba2c9e7b803786f2a021c46827a55fe44ed59def96edb24fc99c06a2545cdbbf366022ad82addcb3bf60c712f3b98ef92069717 languageName: node linkType: hard @@ -9362,7 +9367,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.30.0, magic-string@npm:^0.30.17, magic-string@npm:^0.30.19, magic-string@npm:~0.30.11": +"magic-string@npm:^0.30.0, magic-string@npm:^0.30.17, magic-string@npm:^0.30.21, magic-string@npm:~0.30.11": version: 0.30.21 resolution: "magic-string@npm:0.30.21" dependencies: @@ -9371,14 +9376,14 @@ __metadata: languageName: node linkType: hard -"magicast@npm:^0.3.5": - version: 0.3.5 - resolution: "magicast@npm:0.3.5" +"magicast@npm:^0.5.1": + version: 0.5.1 + resolution: "magicast@npm:0.5.1" dependencies: - "@babel/parser": "npm:^7.25.4" - "@babel/types": "npm:^7.25.4" - source-map-js: "npm:^1.2.0" - checksum: 10c0/a6cacc0a848af84f03e3f5bda7b0de75e4d0aa9ddce5517fd23ed0f31b5ddd51b2d0ff0b7e09b51f7de0f4053c7a1107117edda6b0732dca3e9e39e6c5a68c64 + "@babel/parser": "npm:^7.28.5" + "@babel/types": "npm:^7.28.5" + source-map-js: "npm:^1.2.1" + checksum: 10c0/a00bbf3688b9b3e83c10b3bfe3f106cc2ccbf20c4f2dc1c9020a10556dfe0a6a6605a445ee8e86a6e2b484ec519a657b5e405532684f72678c62e4c0d32f962c languageName: node linkType: hard @@ -11140,6 +11145,15 @@ __metadata: languageName: node linkType: hard +"qified@npm:^0.5.2": + version: 0.5.2 + resolution: "qified@npm:0.5.2" + dependencies: + hookified: "npm:^1.13.0" + checksum: 10c0/4234ba1c0d3b14f31752a39f6788b2490cebad57abd1ce0cee0e04d2fe04a234463785d47559d364affe4d1578aad06fa2fd67b87044688708bf56d4a18ce44a + languageName: node + linkType: hard + "qs@npm:^6.14.0": version: 6.14.0 resolution: "qs@npm:6.14.0" @@ -12459,7 +12473,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": +"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf @@ -12632,10 +12646,10 @@ __metadata: languageName: node linkType: hard -"std-env@npm:^3.9.0": - version: 3.9.0 - resolution: "std-env@npm:3.9.0" - checksum: 10c0/4a6f9218aef3f41046c3c7ecf1f98df00b30a07f4f35c6d47b28329bc2531eef820828951c7d7b39a1c5eb19ad8a46e3ddfc7deb28f0a2f3ceebee11bab7ba50 +"std-env@npm:^3.10.0": + version: 3.10.0 + resolution: "std-env@npm:3.10.0" + checksum: 10c0/1814927a45004d36dde6707eaf17552a546769bc79a6421be2c16ce77d238158dfe5de30910b78ec30d95135cc1c59ea73ee22d2ca170f8b9753f84da34c427f languageName: node linkType: hard @@ -12980,8 +12994,8 @@ __metadata: linkType: hard "stylelint@npm:^16.19.1": - version: 16.25.0 - resolution: "stylelint@npm:16.25.0" + version: 16.26.0 + resolution: "stylelint@npm:16.26.0" dependencies: "@csstools/css-parser-algorithms": "npm:^3.0.5" "@csstools/css-tokenizer": "npm:^3.0.4" @@ -12996,7 +13010,7 @@ __metadata: debug: "npm:^4.4.3" fast-glob: "npm:^3.3.3" fastest-levenshtein: "npm:^1.0.16" - file-entry-cache: "npm:^10.1.4" + file-entry-cache: "npm:^11.1.0" global-modules: "npm:^2.0.0" globby: "npm:^11.1.0" globjoin: "npm:^0.1.4" @@ -13023,7 +13037,7 @@ __metadata: write-file-atomic: "npm:^5.0.1" bin: stylelint: bin/stylelint.mjs - checksum: 10c0/80fa44ff5197419647306d9b2cf3804c10255ac30a96bb3390f2a3f1debee80e2e1cde81bb4e87d9179ababc9cc0ad6c362661835ee1d32a509b7fe44a8d3dd6 + checksum: 10c0/6f501ff051aee4fc7713635c98bf6837f889b22fe86152cfed20365ffeee0acf9d751f94ff265433b532b2a1ab7a228fc1fda3f507859acb57a689268939553d languageName: node linkType: hard @@ -14043,23 +14057,23 @@ __metadata: linkType: hard "vitest@npm:^4.0.5": - version: 4.0.7 - resolution: "vitest@npm:4.0.7" - dependencies: - "@vitest/expect": "npm:4.0.7" - "@vitest/mocker": "npm:4.0.7" - "@vitest/pretty-format": "npm:4.0.7" - "@vitest/runner": "npm:4.0.7" - "@vitest/snapshot": "npm:4.0.7" - "@vitest/spy": "npm:4.0.7" - "@vitest/utils": "npm:4.0.7" + version: 4.0.13 + resolution: "vitest@npm:4.0.13" + dependencies: + "@vitest/expect": "npm:4.0.13" + "@vitest/mocker": "npm:4.0.13" + "@vitest/pretty-format": "npm:4.0.13" + "@vitest/runner": "npm:4.0.13" + "@vitest/snapshot": "npm:4.0.13" + "@vitest/spy": "npm:4.0.13" + "@vitest/utils": "npm:4.0.13" debug: "npm:^4.4.3" es-module-lexer: "npm:^1.7.0" expect-type: "npm:^1.2.2" - magic-string: "npm:^0.30.19" + magic-string: "npm:^0.30.21" pathe: "npm:^2.0.3" picomatch: "npm:^4.0.3" - std-env: "npm:^3.9.0" + std-env: "npm:^3.10.0" tinybench: "npm:^2.9.0" tinyexec: "npm:^0.3.2" tinyglobby: "npm:^0.2.15" @@ -14068,17 +14082,20 @@ __metadata: why-is-node-running: "npm:^2.3.0" peerDependencies: "@edge-runtime/vm": "*" + "@opentelemetry/api": ^1.9.0 "@types/debug": ^4.1.12 "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 - "@vitest/browser-playwright": 4.0.7 - "@vitest/browser-preview": 4.0.7 - "@vitest/browser-webdriverio": 4.0.7 - "@vitest/ui": 4.0.7 + "@vitest/browser-playwright": 4.0.13 + "@vitest/browser-preview": 4.0.13 + "@vitest/browser-webdriverio": 4.0.13 + "@vitest/ui": 4.0.13 happy-dom: "*" jsdom: "*" peerDependenciesMeta: "@edge-runtime/vm": optional: true + "@opentelemetry/api": + optional: true "@types/debug": optional: true "@types/node": @@ -14097,7 +14114,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10c0/d5312e11e9ffbaf239fa5cb5d43e81a43adbdb085bf113c237eff3531805a347e6587ad1dd368a5e9639ff23f8f48650e689fb49c3cf9375d922ef2767a6416a + checksum: 10c0/8582ab1848d5d7dbbac0b3a5eae2625f44d0db887f73da2ee8f588fb13c66fe8ea26dac05c26ebb43673b735bc246764f52969f7c7e25455dfb7c6274659ae2c languageName: node linkType: hard From bd02cd4591ce49d73b4a95e41f2903c353b3d95d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:07:06 +0000 Subject: [PATCH 026/123] chore(deps): update dependency addressable to v2.8.8 (#37002) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 19dfbfa5dd4930..8c2e54bc0e209c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -86,8 +86,8 @@ GEM securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) uri (>= 0.13.1) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) aes_key_wrap (1.1.0) android_key_attestation (0.3.0) annotaterb (4.20.0) From 3e77c3bc8c97eda338e110e453d44d4a6668ee71 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:18:32 +0100 Subject: [PATCH 027/123] chore(deps): update dependency omniauth-rails_csrf_protection to v2 (#36993) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 599686d5baeca2..d50236d2cb8653 100644 --- a/Gemfile +++ b/Gemfile @@ -40,7 +40,7 @@ gem 'net-ldap', '~> 0.18' gem 'omniauth', '~> 2.0' gem 'omniauth-cas', '~> 3.0.0.beta.1' gem 'omniauth_openid_connect', '~> 0.8.0' -gem 'omniauth-rails_csrf_protection', '~> 1.0' +gem 'omniauth-rails_csrf_protection', '~> 2.0' gem 'omniauth-saml', '~> 2.0' gem 'color_diff', '~> 0.1' diff --git a/Gemfile.lock b/Gemfile.lock index 8c2e54bc0e209c..cdfac963e11ca8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -481,7 +481,7 @@ GEM addressable (~> 2.8) nokogiri (~> 1.12) omniauth (~> 2.1) - omniauth-rails_csrf_protection (1.0.2) + omniauth-rails_csrf_protection (2.0.0) actionpack (>= 4.2) omniauth (~> 2.0) omniauth-saml (2.2.4) @@ -638,7 +638,7 @@ GEM faraday-follow_redirects json-jwt (>= 1.11.0) rack (>= 2.1.0) - rack-protection (4.1.1) + rack-protection (4.2.1) base64 (>= 0.1.0) logger (>= 1.6.0) rack (>= 3.0.0, < 4) @@ -1009,7 +1009,7 @@ DEPENDENCIES oj (~> 3.14) omniauth (~> 2.0) omniauth-cas (~> 3.0.0.beta.1) - omniauth-rails_csrf_protection (~> 1.0) + omniauth-rails_csrf_protection (~> 2.0) omniauth-saml (~> 2.0) omniauth_openid_connect (~> 0.8.0) opentelemetry-api (~> 1.7.0) From 09697045a99ea1937ee0fd516d110ad5cad6f9b1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:18:48 +0000 Subject: [PATCH 028/123] chore(deps): update dependency rails-i18n to v8.1.0 (#36992) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index cdfac963e11ca8..95543966c84ef4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -672,7 +672,7 @@ GEM rails-html-sanitizer (1.6.2) loofah (~> 2.21) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - rails-i18n (8.0.2) + rails-i18n (8.1.0) i18n (>= 0.7, < 2) railties (>= 8.0.0, < 9) railties (8.0.3) From 0725afe1a957f0a80e79e6efa48252ebd744c1a2 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Tue, 25 Nov 2025 15:46:50 +0100 Subject: [PATCH 029/123] Collections: Add missing validations for boolean columns (#37005) --- app/models/collection.rb | 3 +++ spec/models/collection_spec.rb | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/app/models/collection.rb b/app/models/collection.rb index 320933ea606b5d..41f9ed0f02a40d 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -27,6 +27,9 @@ class Collection < ApplicationRecord validates :name, presence: true validates :description, presence: true + validates :local, inclusion: [true, false] + validates :sensitive, inclusion: [true, false] + validates :discoverable, inclusion: [true, false] validates :uri, presence: true, if: :remote? validates :original_number_of_items, presence: true, diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index c6a500210f62ad..6c160ecb70af3c 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -10,6 +10,12 @@ it { is_expected.to validate_presence_of(:description) } + it { is_expected.to_not allow_value(nil).for(:local) } + + it { is_expected.to_not allow_value(nil).for(:sensitive) } + + it { is_expected.to_not allow_value(nil).for(:discoverable) } + context 'when collection is remote' do subject { Fabricate.build :collection, local: false } From 6f4f9942b9bbebe933aa8a146d1a82263bd7e35f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:53:38 +0100 Subject: [PATCH 030/123] chore(deps): update dependency connection_pool to v2.5.5 (#37003) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 95543966c84ef4..ba70771a02926e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -167,7 +167,7 @@ GEM cocoon (1.2.15) color_diff (0.1) concurrent-ruby (1.3.5) - connection_pool (2.5.4) + connection_pool (2.5.5) cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) From cd9d1663121a630a979bc2af586159329ccdb3e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:54:11 +0100 Subject: [PATCH 031/123] chore(deps): update dependency rqrcode to v3.1.1 (#37010) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ba70771a02926e..6bbf5d12890c11 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -717,10 +717,10 @@ GEM rotp (6.3.0) rouge (4.6.1) rpam2 (4.0.2) - rqrcode (3.1.0) + rqrcode (3.1.1) chunky_png (~> 1.0) rqrcode_core (~> 2.0) - rqrcode_core (2.0.0) + rqrcode_core (2.0.1) rspec (3.13.1) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) From 384594f4622c23b48d634ca038383f8cca02f49c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:25:41 +0100 Subject: [PATCH 032/123] New Crowdin Translations (automated) (#37018) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/pt-BR.json | 36 ++++++------- app/javascript/mastodon/locales/zh-TW.json | 60 +++++++++++----------- config/locales/da.yml | 8 +-- config/locales/simple_form.da.yml | 2 +- 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index a9c8bbfcb77f88..10fb05fa2d244d 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -45,7 +45,7 @@ "account.follow_request": "Pedir para seguir", "account.follow_request_cancel": "Cancelar solicitação", "account.follow_request_cancel_short": "Cancelar", - "account.follow_request_short": "Solicitação", + "account.follow_request_short": "Solicitar", "account.followers": "Seguidores", "account.followers.empty": "Nada aqui.", "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}", @@ -130,18 +130,18 @@ "annual_report.summary.most_used_hashtag.none": "Nenhuma", "annual_report.summary.new_posts.new_posts": "novas publicações", "annual_report.summary.percentile.text": "Isso lhe coloca no topode usuários de {domain}.", - "annual_report.summary.percentile.we_wont_tell_bernie": "Não contaremos à Bernie.", + "annual_report.summary.percentile.we_wont_tell_bernie": "Não contaremos ao Bernie.", "annual_report.summary.thanks": "Obrigada por fazer parte do Mastodon!", "attachments_list.unprocessed": "(não processado)", "audio.hide": "Ocultar áudio", - "block_modal.remote_users_caveat": "Pediremos ao servidor {domain} que respeite sua decisão. No entanto, a conformidade não é garantida, já que alguns servidores podem lidar com bloqueios de maneira diferente. As postagens públicas ainda podem estar visíveis para usuários não logados.", + "block_modal.remote_users_caveat": "Pediremos ao servidor {domain} que respeite sua decisão. No entanto, a conformidade não é garantida, já que alguns servidores podem lidar com bloqueios de maneira diferente. As publicações públicas ainda podem estar visíveis para usuários não logados.", "block_modal.show_less": "Mostrar menos", "block_modal.show_more": "Mostrar mais", - "block_modal.they_cant_mention": "Eles não podem mencionar ou seguir você.", - "block_modal.they_cant_see_posts": "Eles não podem ver suas postagens e você não verá as deles.", - "block_modal.they_will_know": "Eles podem ver que estão bloqueados.", + "block_modal.they_cant_mention": "Não poderá mencionar ou seguir você.", + "block_modal.they_cant_see_posts": "Não poderá ver suas publicações e você não verá as dele/a.", + "block_modal.they_will_know": "Poderá ver que você bloqueou.", "block_modal.title": "Bloquear usuário?", - "block_modal.you_wont_see_mentions": "Você não verá publicações que os mencionem.", + "block_modal.you_wont_see_mentions": "Você não verá publicações que mencionem o usuário.", "boost_modal.combo": "Pressione {combo} para pular isso na próxima vez", "boost_modal.reblog": "Impulsionar a publicação?", "boost_modal.undo_reblog": "Retirar o impulsionamento do post?", @@ -196,12 +196,12 @@ "community.column_settings.local_only": "Somente local", "community.column_settings.media_only": "Somente mídia", "community.column_settings.remote_only": "Somente global", - "compose.error.blank_post": "A postagem não pode estar em branco.", + "compose.error.blank_post": "A publicação não pode estar em branco.", "compose.language.change": "Alterar idioma", "compose.language.search": "Pesquisar idiomas...", "compose.published.body": "Publicado.", "compose.published.open": "Abrir", - "compose.saved.body": "Postagem salva.", + "compose.saved.body": "Publicação salva.", "compose_form.direct_message_warning_learn_more": "Saiba mais", "compose_form.encryption_warning": "As publicações no Mastodon não são criptografadas de ponta-a-ponta. Não compartilhe nenhuma informação sensível no Mastodon.", "compose_form.hashtag_warning": "Esta publicação não será exibida sob nenhuma hashtag, já que não é pública. Apenas postagens públicas podem ser pesquisadas por meio de hashtags.", @@ -217,7 +217,7 @@ "compose_form.poll.type": "Estilo", "compose_form.publish": "Publicar", "compose_form.reply": "Responder", - "compose_form.save_changes": "Atualização", + "compose_form.save_changes": "Atualizar", "compose_form.spoiler.marked": "Com Aviso de Conteúdo", "compose_form.spoiler.unmarked": "Sem Aviso de Conteúdo", "compose_form.spoiler_placeholder": "Aviso de conteúdo (opcional)", @@ -231,11 +231,11 @@ "confirmations.delete_list.title": "Excluir lista?", "confirmations.discard_draft.confirm": "Descartar e continuar", "confirmations.discard_draft.edit.cancel": "Continuar editando", - "confirmations.discard_draft.edit.message": "Continuar vai descartar quaisquer mudanças feitas ao post sendo editado.", - "confirmations.discard_draft.edit.title": "Descartar mudanças no seu post?", + "confirmations.discard_draft.edit.message": "Continuar vai descartar quaisquer mudanças feitas à publicação sendo editada.", + "confirmations.discard_draft.edit.title": "Descartar mudanças na sua publicação?", "confirmations.discard_draft.post.cancel": "Continuar rascunho", "confirmations.discard_draft.post.message": "Continuar eliminará a publicação que está sendo elaborada no momento.", - "confirmations.discard_draft.post.title": "Eliminar seu esboço de publicação?", + "confirmations.discard_draft.post.title": "Eliminar seu rascunho de publicação?", "confirmations.discard_edit_media.confirm": "Descartar", "confirmations.discard_edit_media.message": "Há mudanças não salvas na descrição ou pré-visualização da mídia. Descartar assim mesmo?", "confirmations.follow_to_list.confirm": "Seguir e adicionar à lista", @@ -246,13 +246,13 @@ "confirmations.logout.title": "Sair da sessão?", "confirmations.missing_alt_text.confirm": "Adicione texto alternativo", "confirmations.missing_alt_text.message": "Seu post contém mídia sem texto alternativo. Adicionar descrições ajuda a tornar seu conteúdo acessível para mais pessoas.", - "confirmations.missing_alt_text.secondary": "Postar mesmo assim", + "confirmations.missing_alt_text.secondary": "Publicar mesmo assim", "confirmations.missing_alt_text.title": "Adicionar texto alternativo?", "confirmations.mute.confirm": "Silenciar", "confirmations.private_quote_notify.cancel": "Voltar à edição", - "confirmations.private_quote_notify.confirm": "Publicar postagem", + "confirmations.private_quote_notify.confirm": "Publicar citação", "confirmations.private_quote_notify.do_not_show_again": "Não me mostre esta mensagem novamente", - "confirmations.private_quote_notify.message": "A pessoa que está citando e outras menções serão notificadas e poderão ver sua postagem, mesmo que não sigam você.", + "confirmations.private_quote_notify.message": "A pessoa que está sendo citada e outras mencionadas serão notificadas e poderão ver sua publicação, mesmo que não sigam você.", "confirmations.private_quote_notify.title": "Compartilhar com seguidores e usuários mencionados?", "confirmations.quiet_post_quote_info.dismiss": "Não me lembrar novamente", "confirmations.quiet_post_quote_info.got_it": "Entendi", @@ -265,14 +265,14 @@ "confirmations.remove_from_followers.message": "{name} vai parar de te seguir. Tem certeza de que deseja continuar?", "confirmations.remove_from_followers.title": "Remover seguidor?", "confirmations.revoke_quote.confirm": "Remover publicação", - "confirmations.revoke_quote.message": "Essa ação não pode ser desfeita.", + "confirmations.revoke_quote.message": "Esta ação não pode ser desfeita.", "confirmations.revoke_quote.title": "Remover publicação?", "confirmations.unblock.confirm": "Desbloquear", "confirmations.unblock.title": "Desbloquear {name}?", "confirmations.unfollow.confirm": "Deixar de seguir", "confirmations.unfollow.title": "Deixar de seguir {name}?", "confirmations.withdraw_request.confirm": "Retirar solicitação", - "confirmations.withdraw_request.title": "Cancelar solicitação para seguir {name}?", + "confirmations.withdraw_request.title": "Retirar solicitação para seguir {name}?", "content_warning.hide": "Ocultar publicação", "content_warning.show": "Mostrar mesmo assim", "content_warning.show_more": "Mostrar mais", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index cbe6a1e56fb17d..c198ff8e3558f6 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -5,7 +5,7 @@ "about.disclaimer": "Mastodon 是一個自由的開源軟體,是 Mastodon gGmbH 之註冊商標。", "about.domain_blocks.no_reason_available": "無法存取的原因", "about.domain_blocks.preamble": "Mastodon 基本上允許您瀏覽聯邦宇宙中任何伺服器的內容並與使用者互動。以下是於本伺服器上設定之例外。", - "about.domain_blocks.silenced.explanation": "一般來說您不會看到來自這個伺服器的個人檔案與內容,除非您明確地打開或著跟隨此個人檔案。", + "about.domain_blocks.silenced.explanation": "一般來說您不會看到來自這個伺服器的個人檔案與內容,除非您明確地檢視或著跟隨此個人檔案。", "about.domain_blocks.silenced.title": "已受限", "about.domain_blocks.suspended.explanation": "來自此伺服器的資料都不會被處理、儲存或交換,也無法和此伺服器上的使用者互動與交流。", "about.domain_blocks.suspended.title": "已停權", @@ -90,7 +90,7 @@ "account.unmute": "解除靜音 @{name}", "account.unmute_notifications_short": "解除靜音推播通知", "account.unmute_short": "解除靜音", - "account_note.placeholder": "按此新增備註", + "account_note.placeholder": "點擊以新增備註", "admin.dashboard.daily_retention": "註冊後使用者存留率(日)", "admin.dashboard.monthly_retention": "註冊後使用者存留率(月)", "admin.dashboard.retention.average": "平均", @@ -138,10 +138,10 @@ "block_modal.show_less": "減少顯示", "block_modal.show_more": "顯示更多", "block_modal.they_cant_mention": "他們無法提及或跟隨您。", - "block_modal.they_cant_see_posts": "他們無法讀取您的嘟文,且您不會見到他們。", + "block_modal.they_cant_see_posts": "他們無法讀取您的嘟文,且您不會見到他們的嘟文。", "block_modal.they_will_know": "他們能見到他們已被封鎖。", "block_modal.title": "是否封鎖該使用者?", - "block_modal.you_wont_see_mentions": "您不會見到提及他們的嘟文。", + "block_modal.you_wont_see_mentions": "您將不會見到提及他們的嘟文。", "boost_modal.combo": "您下次可以按 {combo} 跳過", "boost_modal.reblog": "是否要轉嘟?", "boost_modal.undo_reblog": "是否要取消轉嘟?", @@ -181,7 +181,7 @@ "column.home": "首頁", "column.list_members": "管理列表成員", "column.lists": "列表", - "column.mutes": "已靜音的使用者", + "column.mutes": "已靜音使用者", "column.notifications": "推播通知", "column.pins": "釘選的嘟文", "column.public": "聯邦時間軸", @@ -193,9 +193,9 @@ "column_header.show_settings": "顯示設定", "column_header.unpin": "取消釘選", "column_search.cancel": "取消", - "community.column_settings.local_only": "只顯示本站", - "community.column_settings.media_only": "只顯示媒體", - "community.column_settings.remote_only": "只顯示遠端", + "community.column_settings.local_only": "僅顯示本站", + "community.column_settings.media_only": "僅顯示媒體", + "community.column_settings.remote_only": "僅顯示遠端", "compose.error.blank_post": "嘟文無法為空白。", "compose.language.change": "變更語言", "compose.language.search": "搜尋語言...", @@ -204,14 +204,14 @@ "compose.saved.body": "已儲存嘟文。", "compose_form.direct_message_warning_learn_more": "了解更多", "compose_form.encryption_warning": "Mastodon 上的嘟文並未進行端到端加密。請不要透過 Mastodon 分享任何敏感資訊。", - "compose_form.hashtag_warning": "由於這則嘟文設定為非公開,將不會列於任何主題標籤下。只有公開的嘟文才能藉由主題標籤被找到。", - "compose_form.lock_disclaimer": "您的帳號尚未 {locked}。任何人皆能跟隨您並看到您設定成只對跟隨者顯示的嘟文。", + "compose_form.hashtag_warning": "由於這則嘟文設定為「不公開」,它將不被列於任何主題標籤下。只有公開的嘟文才能藉由主題標籤被找到。", + "compose_form.lock_disclaimer": "您的帳號尚未 {locked}。任何人皆能跟隨您並看到您設定成僅有跟隨者可見的嘟文。", "compose_form.lock_disclaimer.lock": "上鎖", "compose_form.placeholder": "正在想些什麼嗎?", "compose_form.poll.duration": "投票期限", "compose_form.poll.multiple": "多選", "compose_form.poll.option_placeholder": "選項 {number}", - "compose_form.poll.single": "單一選擇", + "compose_form.poll.single": "單選", "compose_form.poll.switch_to_multiple": "變更投票為允許多個選項", "compose_form.poll.switch_to_single": "變更投票為允許單一選項", "compose_form.poll.type": "投票方式", @@ -231,7 +231,7 @@ "confirmations.delete_list.title": "是否刪除該列表?", "confirmations.discard_draft.confirm": "捨棄並繼續", "confirmations.discard_draft.edit.cancel": "恢復編輯", - "confirmations.discard_draft.edit.message": "繼續將會捨棄任何您對正在編輯的此嘟文進行之任何變更。", + "confirmations.discard_draft.edit.message": "繼續將捨棄任何您對正在編輯的此嘟文進行之任何變更。", "confirmations.discard_draft.edit.title": "是否捨棄對您的嘟文之變更?", "confirmations.discard_draft.post.cancel": "恢復草稿", "confirmations.discard_draft.post.message": "繼續將捨棄您正在撰寫中之嘟文。", @@ -259,20 +259,20 @@ "confirmations.quiet_post_quote_info.message": "當引用不於公開時間軸顯示之嘟文時,您的嘟文將自熱門時間軸隱藏。", "confirmations.quiet_post_quote_info.title": "引用不於公開時間軸顯示之嘟文", "confirmations.redraft.confirm": "刪除並重新編輯", - "confirmations.redraft.message": "您確定要刪除這則嘟文並重新編輯嗎?您將失去這則嘟文之轉嘟及最愛,且對此嘟文之回覆會變成獨立的嘟文。", + "confirmations.redraft.message": "您確定要刪除這則嘟文並重新編輯嗎?您將失去此嘟文之轉嘟及最愛,且對原嘟文之回覆將變成獨立嘟文。", "confirmations.redraft.title": "是否刪除並重新編輯該嘟文?", "confirmations.remove_from_followers.confirm": "移除跟隨者", - "confirmations.remove_from_followers.message": "{name} 將會停止跟隨您。您確定要繼續嗎?", + "confirmations.remove_from_followers.message": "{name} 將停止跟隨您。您確定要繼續嗎?", "confirmations.remove_from_followers.title": "是否移除該跟隨者?", "confirmations.revoke_quote.confirm": "移除嘟文", "confirmations.revoke_quote.message": "此動作無法復原。", - "confirmations.revoke_quote.title": "您是否確定移除嘟文?", + "confirmations.revoke_quote.title": "是否移除該嘟文?", "confirmations.unblock.confirm": "解除封鎖", - "confirmations.unblock.title": "解除封鎖 {name}?", + "confirmations.unblock.title": "是否解除封鎖 {name}?", "confirmations.unfollow.confirm": "取消跟隨", - "confirmations.unfollow.title": "取消跟隨 {name}?", + "confirmations.unfollow.title": "是否取消跟隨 {name}?", "confirmations.withdraw_request.confirm": "收回跟隨請求", - "confirmations.withdraw_request.title": "收回對 {name} 之跟隨請求?", + "confirmations.withdraw_request.title": "是否收回對 {name} 之跟隨請求?", "content_warning.hide": "隱藏嘟文", "content_warning.show": "仍要顯示", "content_warning.show_more": "顯示更多", @@ -284,7 +284,7 @@ "copypaste.copied": "已複製", "copypaste.copy_to_clipboard": "複製到剪貼簿", "directory.federated": "來自已知聯邦宇宙", - "directory.local": "僅來自 {domain} 網域", + "directory.local": "僅來自 {domain}", "directory.new_arrivals": "新人", "directory.recently_active": "最近活躍", "disabled_account_banner.account_settings": "帳號設定", @@ -298,9 +298,9 @@ "domain_block_modal.they_cant_follow": "來自此伺服器之使用者將無法跟隨您。", "domain_block_modal.they_wont_know": "他們不會知道他們已被封鎖。", "domain_block_modal.title": "是否封鎖該網域?", - "domain_block_modal.you_will_lose_num_followers": "您將會失去 {followersCount, plural, other {{followersCountDisplay} 個跟隨者}} 與 {followingCount, plural, other {{followingCountDisplay} 個您跟隨之帳號}}.", + "domain_block_modal.you_will_lose_num_followers": "您將失去 {followersCount, plural, other {{followersCountDisplay} 個跟隨者}} 與 {followingCount, plural, other {{followingCountDisplay} 個您跟隨之帳號}}。", "domain_block_modal.you_will_lose_relationships": "您將失去所有的跟隨者與您自此伺服器跟隨之帳號。", - "domain_block_modal.you_wont_see_posts": "您不會見到來自此伺服器使用者之任何嘟文或推播通知。", + "domain_block_modal.you_wont_see_posts": "您將不會見到來自此伺服器使用者之任何嘟文或推播通知。", "domain_pill.activitypub_lets_connect": "它使您能於 Mastodon 及其他不同的社群應用程式與人連結及互動。", "domain_pill.activitypub_like_language": "ActivityPub 像是 Mastodon 與其他社群網路溝通時所用的語言。", "domain_pill.server": "伺服器", @@ -567,8 +567,8 @@ "mute_modal.they_can_mention_and_follow": "他們仍可提及或跟隨您,但您不會見到他們。", "mute_modal.they_wont_know": "他們不會知道他們已被靜音。", "mute_modal.title": "是否靜音該使用者?", - "mute_modal.you_wont_see_mentions": "您不會見到提及他們的嘟文。", - "mute_modal.you_wont_see_posts": "他們仍可讀取您的嘟文,但您不會見到他們的。", + "mute_modal.you_wont_see_mentions": "您將不會見到提及他們的嘟文。", + "mute_modal.you_wont_see_posts": "他們仍可讀取您的嘟文,但您不會見到他們的嘟文。", "navigation_bar.about": "關於", "navigation_bar.account_settings": "密碼與安全性", "navigation_bar.administration": "管理介面", @@ -631,7 +631,7 @@ "notification.moderation_warning.action_disable": "您的帳號已被停用。", "notification.moderation_warning.action_mark_statuses_as_sensitive": "某些您的嘟文已被標記為敏感內容。", "notification.moderation_warning.action_none": "您的帳號已收到管理員警告。", - "notification.moderation_warning.action_sensitive": "即日起,您的嘟文將會被標記為敏感內容。", + "notification.moderation_warning.action_sensitive": "即日起,您的嘟文將被標記為敏感內容。", "notification.moderation_warning.action_silence": "您的帳號已被限制。", "notification.moderation_warning.action_suspend": "您的帳號已被停權。", "notification.own_poll": "您的投票已結束", @@ -649,10 +649,10 @@ "notification_requests.accept": "接受", "notification_requests.accept_multiple": "{count, plural, other {接受 # 則請求...}}", "notification_requests.confirm_accept_multiple.button": "{count, plural, other {接受請求}}", - "notification_requests.confirm_accept_multiple.message": "您將接受 {count, plural, other {# 則推播通知請求}}。您確定要繼續?", + "notification_requests.confirm_accept_multiple.message": "您將接受 {count, plural, other {# 則推播通知請求}}。您確定要繼續嗎?", "notification_requests.confirm_accept_multiple.title": "是否接受推播通知請求?", "notification_requests.confirm_dismiss_multiple.button": "{count, plural, other {忽略請求}}", - "notification_requests.confirm_dismiss_multiple.message": "您將忽略 {count, plural, other {# 則推播通知請求}}。您將不再能輕易存取{count, plural, other {這些}}推播通知。您確定要繼續?", + "notification_requests.confirm_dismiss_multiple.message": "您將忽略 {count, plural, other {# 則推播通知請求}}。您將不再能輕易存取{count, plural, other {這些}}推播通知。您確定要繼續嗎?", "notification_requests.confirm_dismiss_multiple.title": "是否忽略推播通知請求?", "notification_requests.dismiss": "關閉", "notification_requests.dismiss_multiple": "{count, plural, other {忽略 # 則請求...}}", @@ -759,7 +759,7 @@ "privacy.quote.anyone": "{visibility},任何人皆可引用", "privacy.quote.disabled": "{visibility},停用引用嘟文", "privacy.quote.limited": "{visibility},受限的引用嘟文", - "privacy.unlisted.additional": "此與公開嘟文完全相同,但嘟文不會出現於即時內容或主題標籤、探索、及 Mastodon 搜尋中,即使您在帳戶設定中選擇加入。", + "privacy.unlisted.additional": "此與公開嘟文完全相同,但嘟文不會出現於即時內容或主題標籤、探索、及 Mastodon 搜尋中,即使您於帳戶設定中選擇加入。", "privacy.unlisted.long": "不顯示於 Mastodon 之搜尋結果、熱門趨勢、及公開時間軸上", "privacy.unlisted.short": "不公開", "privacy_policy.last_updated": "最後更新:{date}", @@ -792,7 +792,7 @@ "reply_indicator.cancel": "取消", "reply_indicator.poll": "投票", "report.block": "封鎖", - "report.block_explanation": "您將不再看到他們的嘟文。他們將無法看到您的嘟文或是跟隨您。他們會發現他們已被封鎖。", + "report.block_explanation": "您將不再看到他們的嘟文。他們將無法檢視您的嘟文或是跟隨您。他們會發現他們已被封鎖。", "report.categories.legal": "合法性", "report.categories.other": "其他", "report.categories.spam": "垃圾訊息", @@ -806,7 +806,7 @@ "report.forward": "轉寄到 {target}", "report.forward_hint": "這個帳號屬於其他伺服器。要向該伺服器發送匿名的檢舉訊息嗎?", "report.mute": "靜音", - "report.mute_explanation": "您將不再看到他們的嘟文。他們仍能可以跟隨您以及察看您的嘟文,並且不會知道他們已被靜音。", + "report.mute_explanation": "您將不再看到他們的嘟文。他們仍能可以跟隨您以及檢視您的嘟文,並且不會知道他們已被靜音。", "report.next": "繼續", "report.placeholder": "其他備註", "report.reasons.dislike": "我不喜歡", @@ -996,7 +996,7 @@ "upload_error.limit": "已達到檔案上傳限制。", "upload_error.poll": "不允許於投票時上傳檔案。", "upload_error.quote": "引用嘟文無法上傳檔案。", - "upload_form.drag_and_drop.instructions": "請按空白鍵或 Enter 鍵取多媒體附加檔案。使用方向鍵移動多媒體附加檔案。按下空白鍵或 Enter 鍵於新位置放置多媒體附加檔案,或按下 ESC 鍵取消。", + "upload_form.drag_and_drop.instructions": "請按空白鍵或 Enter 鍵選取多媒體附加檔案。使用方向鍵移動多媒體附加檔案。按下空白鍵或 Enter 鍵於新位置放置多媒體附加檔案,或按下 ESC 鍵取消。", "upload_form.drag_and_drop.on_drag_cancel": "移動已取消。多媒體附加檔案 {item} 已被放置。", "upload_form.drag_and_drop.on_drag_end": "多媒體附加檔案 {item} 已被放置。", "upload_form.drag_and_drop.on_drag_over": "多媒體附加檔案 {item} 已被移動。", diff --git a/config/locales/da.yml b/config/locales/da.yml index 3cb95b312026a7..1a900bb84915aa 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1593,13 +1593,13 @@ da: invalid: Denne invitation er ikke gyldig invited_by: 'Du blev inviteret af:' max_uses: - one: 1 benyttelse - other: "%{count} benyttelser" + one: 1 anvendelse + other: "%{count} anvendelser" max_uses_prompt: Ubegrænset prompt: Generér og del links med andre for at give dem adgang til denne server table: expires_at: Udløber - uses: Benyttelser + uses: Anvendelser title: Invitér personer link_preview: author_html: Af %{name} @@ -2122,7 +2122,7 @@ da: feature_audience_title: Opbyg et publikum i tillid feature_control: Man ved selv bedst, hvad man ønsker at se på sit hjemmefeed. Ingen algoritmer eller annoncer til at spilde tiden. Følg alle på tværs af enhver Mastodon-server fra en enkelt konto og modtag deres indlæg i kronologisk rækkefølge, og gør dette hjørne af internet lidt mere personligt. feature_control_title: Hold styr på egen tidslinje - feature_creativity: Mastodon understøtter indlæg med lyd, video og billede, tilgængelighedsbeskrivelser, meningsmålinger, indhold advarsler, animerede avatarer, tilpassede emojis, miniaturebeskæringskontrol og mere, for at gøre det lettere at udtrykke sig online. Uanset om man udgiver sin kunst, musik eller podcast, så står Mastodon til rådighed. + feature_creativity: Mastodon understøtter lyd-, video- og billedindlæg, tilgængelighedsbeskrivelser, afstemninger, indholdsadvarsler, animerede avatarer, brugerdefinerede emojis, kontrol over beskæring af miniaturebilleder og meget mere, så du lettere kan udtrykke dig online. Uanset om du udgiver din kunst, din musik eller din podcast, er Mastodon der for dig. feature_creativity_title: Uovertruffen kreativitet feature_moderation: Mastodon lægger beslutningstagning tilbage i brugerens hænder. Hver server opretter deres egne regler og reguleringer, som håndhæves lokalt og ikke ovenfra som virksomhedernes sociale medier, hvilket gør det til den mest fleksible mht. at reagere på behovene hos forskellige persongrupper. Deltag på en server med de regler, man er enige med, eller driv en egen server. feature_moderation_title: Moderering af måden, tingene bør være på diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml index b442e5add0c9aa..68f114e7fe2bd3 100644 --- a/config/locales/simple_form.da.yml +++ b/config/locales/simple_form.da.yml @@ -227,7 +227,7 @@ da: inbox_url: URL til videreformidlingsindbakken irreversible: Fjern istedet for skjul locale: Grænsefladesprog - max_uses: Maks. antal afbenyttelser + max_uses: Maks. antal anvendelser new_password: Ny adgangskode note: Biografi otp_attempt: Tofaktorkode From 59e48657cf35a28d8ee0593611e7d779e6265b7f Mon Sep 17 00:00:00 2001 From: diondiondion Date: Wed, 26 Nov 2025 11:25:49 +0100 Subject: [PATCH 033/123] Fix issues in new theme tokens (#37019) --- app/javascript/styles_new/mastodon/admin.scss | 6 +++++- app/javascript/styles_new/mastodon/components.scss | 2 +- app/javascript/styles_new/mastodon/css_variables.scss | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/javascript/styles_new/mastodon/admin.scss b/app/javascript/styles_new/mastodon/admin.scss index d0cd3c51374c3c..eaa91936b106c8 100644 --- a/app/javascript/styles_new/mastodon/admin.scss +++ b/app/javascript/styles_new/mastodon/admin.scss @@ -122,7 +122,11 @@ $content-width: 840px; align-items: center; gap: 6px; padding: 15px; - color: var(--color-text-secondary); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); text-decoration: none; transition: all 200ms linear; transition-property: color, background-color; diff --git a/app/javascript/styles_new/mastodon/components.scss b/app/javascript/styles_new/mastodon/components.scss index 34a0308402d2c9..d5b61e6abbf967 100644 --- a/app/javascript/styles_new/mastodon/components.scss +++ b/app/javascript/styles_new/mastodon/components.scss @@ -4494,7 +4494,7 @@ a.status-card { z-index: 1; background: radial-gradient( ellipse, - rgb(from var(--color-bg-brand-softer-base) r g b / 23%) 0%, + rgb(from var(--color-bg-brand-base) r g b / 23%) 0%, transparent 60% ); } diff --git a/app/javascript/styles_new/mastodon/css_variables.scss b/app/javascript/styles_new/mastodon/css_variables.scss index ba3319a6b48cae..b270bd337ff321 100644 --- a/app/javascript/styles_new/mastodon/css_variables.scss +++ b/app/javascript/styles_new/mastodon/css_variables.scss @@ -79,7 +79,7 @@ // Utility --color-bg-ambient: var(--color-bg-primary); - --color-bg-elevated: var(--color-grey-800); + --color-bg-elevated: var(--color-bg-primary); --color-bg-inverted: var(--color-grey-50); --color-bg-media-base: var(--color-black); --color-bg-media-strength: 65%; @@ -87,7 +87,7 @@ var(--color-bg-media-base), var(--color-bg-media-strength) )}; - --color-bg-overlay: var(--color-bg-primary); + --color-bg-overlay: var(--color-black); --color-bg-disabled: var(--color-grey-700); // Brand From 00163e89bf20720bceaa737c83dcf96a8c1024bb Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 26 Nov 2025 11:26:26 +0100 Subject: [PATCH 034/123] Fix `tootctl status remove` removing quoted posts and remote quotes of local posts (#37009) --- lib/mastodon/cli/statuses.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/mastodon/cli/statuses.rb b/lib/mastodon/cli/statuses.rb index 7104181e978713..7188bc970cecbe 100644 --- a/lib/mastodon/cli/statuses.rb +++ b/lib/mastodon/cli/statuses.rb @@ -62,6 +62,8 @@ def remove_statuses AND NOT EXISTS (SELECT 1 FROM mentions WHERE statuses.id = mentions.status_id AND mentions.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL)) AND NOT EXISTS (SELECT 1 FROM favourites WHERE statuses.id = favourites.status_id AND favourites.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL)) AND NOT EXISTS (SELECT 1 FROM bookmarks WHERE statuses.id = bookmarks.status_id AND bookmarks.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL)) + AND NOT EXISTS (SELECT 1 FROM quotes JOIN statuses statuses1 ON quotes.status_id = statuses1.id WHERE quotes.quoted_status_id = statuses.id AND (statuses1.uri IS NULL OR statuses1.local)) + AND NOT EXISTS (SELECT 1 FROM quotes JOIN statuses statuses1 ON quotes.quoted_status_id = statuses1.id WHERE quotes.status_id = statuses.id AND (statuses1.uri IS NULL OR statuses1.local)) #{clean_followed_sql} SQL From cb4f1cc89c732b751ef1d44b1839fb86e68ba0c8 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 26 Nov 2025 05:26:39 -0500 Subject: [PATCH 035/123] Improve `SessionActivation.activate` spec (#36983) --- app/models/session_activation.rb | 4 +--- spec/models/session_activation_spec.rb | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/models/session_activation.rb b/app/models/session_activation.rb index d99ecf8adba43d..55b1428be65b60 100644 --- a/app/models/session_activation.rb +++ b/app/models/session_activation.rb @@ -38,9 +38,7 @@ def active?(id) end def activate(**) - activation = create!(**) - purge_old - activation + create!(**).tap { purge_old } end def deactivate(id) diff --git a/spec/models/session_activation_spec.rb b/spec/models/session_activation_spec.rb index 63d22f02081b5e..036a6848cb3110 100644 --- a/spec/models/session_activation_spec.rb +++ b/spec/models/session_activation_spec.rb @@ -39,20 +39,24 @@ end describe '.activate' do - let(:options) { { user: Fabricate(:user), session_id: '1' } } + let(:user) { Fabricate :user } + let!(:session_activation) { Fabricate :session_activation, user: } - it 'calls create! and purge_old' do - allow(described_class).to receive(:create!).with(**options) - allow(described_class).to receive(:purge_old) - - described_class.activate(**options) - - expect(described_class).to have_received(:create!).with(**options) - expect(described_class).to have_received(:purge_old) + around do |example| + original = Rails.configuration.x.max_session_activations + Rails.configuration.x.max_session_activations = 1 + example.run + Rails.configuration.x.max_session_activations = original end - it 'returns an instance of SessionActivation' do - expect(described_class.activate(**options)).to be_a described_class + it 'creates a new activation and purges older ones' do + result = described_class.activate(user: user, session_id: '123') + + expect(result) + .to be_a(described_class) + .and have_attributes(session_id: '123', user:) + expect { session_activation.reload } + .to raise_error(ActiveRecord::RecordNotFound) end end From 1757a0f0f394bea960d9ed4c6a6c2288a4067f20 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:48:12 +0100 Subject: [PATCH 036/123] chore(deps): update dependency public_suffix to v7 (#36920) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index d50236d2cb8653..9394aa096cf719 100644 --- a/Gemfile +++ b/Gemfile @@ -71,7 +71,7 @@ gem 'oj', '~> 3.14' gem 'ox', '~> 2.14' gem 'parslet' gem 'premailer-rails' -gem 'public_suffix', '~> 6.0' +gem 'public_suffix', '~> 7.0' gem 'pundit', '~> 2.3' gem 'rack-attack', '~> 6.6' gem 'rack-cors', require: 'rack/cors' diff --git a/Gemfile.lock b/Gemfile.lock index 6bbf5d12890c11..612f720ad28569 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -618,7 +618,7 @@ GEM psych (5.2.6) date stringio - public_suffix (6.0.2) + public_suffix (7.0.0) puma (7.1.0) nio4r (~> 2.0) pundit (2.5.2) @@ -1036,7 +1036,7 @@ DEPENDENCIES premailer-rails prometheus_exporter (~> 2.2) propshaft - public_suffix (~> 6.0) + public_suffix (~> 7.0) puma (~> 7.0) pundit (~> 2.3) rack-attack (~> 6.6) From f87f30c1ac73e17197757971c9beefd9430d291f Mon Sep 17 00:00:00 2001 From: diondiondion Date: Wed, 26 Nov 2025 13:56:17 +0100 Subject: [PATCH 037/123] Refactor `Card` component to TypeScript (#36982) --- app/javascript/mastodon/components/status.jsx | 3 +- .../features/status/components/card.jsx | 254 -------------- .../features/status/components/card.tsx | 316 ++++++++++++++++++ .../status/components/detailed_status.tsx | 2 +- 4 files changed, 318 insertions(+), 257 deletions(-) delete mode 100644 app/javascript/mastodon/features/status/components/card.jsx create mode 100644 app/javascript/mastodon/features/status/components/card.tsx diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 1e746e5f710fc4..15f0b9da306d79 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -538,9 +538,8 @@ class Status extends ImmutablePureComponent { } else if (status.get('card') && !status.get('quote')) { media = ( ); diff --git a/app/javascript/mastodon/features/status/components/card.jsx b/app/javascript/mastodon/features/status/components/card.jsx deleted file mode 100644 index 308555b77f6b32..00000000000000 --- a/app/javascript/mastodon/features/status/components/card.jsx +++ /dev/null @@ -1,254 +0,0 @@ -import punycode from 'punycode'; - -import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; - -import { FormattedMessage } from 'react-intl'; - -import classNames from 'classnames'; - - -import { is } from 'immutable'; -import ImmutablePropTypes from 'react-immutable-proptypes'; - -import DescriptionIcon from '@/material-icons/400-24px/description-fill.svg?react'; -import OpenInNewIcon from '@/material-icons/400-24px/open_in_new.svg?react'; -import PlayArrowIcon from '@/material-icons/400-24px/play_arrow-fill.svg?react'; -import { Blurhash } from 'mastodon/components/blurhash'; -import { Icon } from 'mastodon/components/icon'; -import { MoreFromAuthor } from 'mastodon/components/more_from_author'; -import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; -import { useBlurhash } from 'mastodon/initial_state'; - -const IDNA_PREFIX = 'xn--'; - -const decodeIDNA = domain => { - return domain - .split('.') - .map(part => part.indexOf(IDNA_PREFIX) === 0 ? punycode.decode(part.slice(IDNA_PREFIX.length)) : part) - .join('.'); -}; - -const getHostname = url => { - const parser = document.createElement('a'); - parser.href = url; - return parser.hostname; -}; - -const domParser = new DOMParser(); - -const handleIframeUrl = (html, url, providerName) => { - const document = domParser.parseFromString(html, 'text/html').documentElement; - const iframe = document.querySelector('iframe'); - const startTime = new URL(url).searchParams.get('t') - - if (iframe) { - const iframeUrl = new URL(iframe.src) - - iframeUrl.searchParams.set('autoplay', 1) - iframeUrl.searchParams.set('auto_play', 1) - - if (startTime && providerName === "YouTube") iframeUrl.searchParams.set('start', startTime) - - iframe.src = iframeUrl.href - - // DOM parser creates html/body elements around original HTML fragment, - // so we need to get innerHTML out of the body and not the entire document - return document.querySelector('body').innerHTML; - } - - return html; -}; - -export default class Card extends PureComponent { - - static propTypes = { - card: ImmutablePropTypes.map, - onOpenMedia: PropTypes.func.isRequired, - sensitive: PropTypes.bool, - }; - - state = { - previewLoaded: false, - embedded: false, - revealed: !this.props.sensitive, - }; - - UNSAFE_componentWillReceiveProps (nextProps) { - if (!is(this.props.card, nextProps.card)) { - this.setState({ embedded: false, previewLoaded: false }); - } - - if (this.props.sensitive !== nextProps.sensitive) { - this.setState({ revealed: !nextProps.sensitive }); - } - } - - componentDidMount () { - window.addEventListener('resize', this.handleResize, { passive: true }); - } - - componentWillUnmount () { - window.removeEventListener('resize', this.handleResize); - } - - handleEmbedClick = () => { - this.setState({ embedded: true }); - }; - - handleExternalLinkClick = (e) => { - e.stopPropagation(); - }; - - setRef = c => { - this.node = c; - }; - - handleImageLoad = () => { - this.setState({ previewLoaded: true }); - }; - - handleReveal = e => { - e.preventDefault(); - e.stopPropagation(); - this.setState({ revealed: true }); - }; - - renderVideo () { - const { card } = this.props; - const content = { __html: handleIframeUrl(card.get('html'), card.get('url'), card.get('provider_name')) }; - - return ( -
- ); - } - - render () { - const { card } = this.props; - const { embedded, revealed } = this.state; - - if (card === null) { - return null; - } - - const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name'); - const interactive = card.get('type') === 'video'; - const language = card.get('language') || ''; - const largeImage = (card.get('image')?.length > 0 && card.get('width') > card.get('height')) || interactive; - const showAuthor = !!card.getIn(['authors', 0, 'accountId']); - - const description = ( -
- - {provider} - {card.get('published_at') && <> · } - - - {card.get('title')} - - {!showAuthor && (card.get('author_name').length > 0 ? {card.get('author_name')} }} /> : {card.get('description')})} -
- ); - - const thumbnailStyle = { - visibility: revealed ? null : 'hidden', - }; - - if (largeImage && card.get('type') === 'video') { - thumbnailStyle.aspectRatio = `16 / 9`; - } else if (largeImage) { - thumbnailStyle.aspectRatio = '1.91 / 1'; - } else { - thumbnailStyle.aspectRatio = 1; - } - - let embed; - - let canvas = ( - - ); - - const thumbnailDescription = card.get('image_description'); - const thumbnail = {thumbnailDescription}; - - let spoilerButton = ( - - ); - - spoilerButton = ( -
- {spoilerButton} -
- ); - - if (interactive) { - if (embedded) { - embed = this.renderVideo(); - } else { - embed = ( -
- {canvas} - {thumbnail} - - {revealed ? ( -
-
- - -
-
- ) : spoilerButton} -
- ); - } - - return ( -
- {embed} - {description} -
- ); - } else if (card.get('image')) { - embed = ( -
- {canvas} - {thumbnail} -
- ); - } else { - embed = ( -
- -
- ); - } - - return ( - <> - - {embed} - {description} - - - {showAuthor && } - - ); - } - -} diff --git a/app/javascript/mastodon/features/status/components/card.tsx b/app/javascript/mastodon/features/status/components/card.tsx new file mode 100644 index 00000000000000..65826f95f493d0 --- /dev/null +++ b/app/javascript/mastodon/features/status/components/card.tsx @@ -0,0 +1,316 @@ +import punycode from 'node:punycode'; + +import { useCallback, useId, useState } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import classNames from 'classnames'; + +import DescriptionIcon from '@/material-icons/400-24px/description-fill.svg?react'; +import OpenInNewIcon from '@/material-icons/400-24px/open_in_new.svg?react'; +import PlayArrowIcon from '@/material-icons/400-24px/play_arrow-fill.svg?react'; +import { Blurhash } from 'mastodon/components/blurhash'; +import { Icon } from 'mastodon/components/icon'; +import { MoreFromAuthor } from 'mastodon/components/more_from_author'; +import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; +import { useBlurhash } from 'mastodon/initial_state'; +import type { Card as CardType } from 'mastodon/models/status'; + +const IDNA_PREFIX = 'xn--'; + +const decodeIDNA = (domain: string) => { + return domain + .split('.') + .map((part) => + part.startsWith(IDNA_PREFIX) + ? punycode.decode(part.slice(IDNA_PREFIX.length)) + : part, + ) + .join('.'); +}; + +const getHostname = (url: string) => { + const parser = document.createElement('a'); + parser.href = url; + return parser.hostname; +}; + +const domParser = new DOMParser(); + +const handleIframeUrl = (html: string, url: string, providerName: string) => { + const document = domParser.parseFromString(html, 'text/html').documentElement; + const iframe = document.querySelector('iframe'); + const startTime = new URL(url).searchParams.get('t'); + + if (iframe) { + const iframeUrl = new URL(iframe.src); + + iframeUrl.searchParams.set('autoplay', '1'); + iframeUrl.searchParams.set('auto_play', '1'); + + if (startTime && providerName === 'YouTube') + iframeUrl.searchParams.set('start', startTime); + + iframe.src = iframeUrl.href; + + // DOM parser creates html/body elements around original HTML fragment, + // so we need to get innerHTML out of the body and not the entire document + return document.querySelector('body')?.innerHTML ?? ''; + } + + return html; +}; + +interface CardProps { + card: CardType | null; + sensitive?: boolean; +} + +const CardVideo: React.FC> = ({ card }) => ( +
+); + +const Card: React.FC = ({ card, sensitive }) => { + const [previewLoaded, setPreviewLoaded] = useState(false); + const [embedded, setEmbedded] = useState(false); + const [revealed, setRevealed] = useState(!sensitive); + + const handleEmbedClick = useCallback(() => { + setEmbedded(true); + }, []); + + const handleExternalLinkClick = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + }, []); + + const handleImageLoad = useCallback(() => { + setPreviewLoaded(true); + }, []); + + const handleReveal = useCallback((e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + setRevealed(true); + }, []); + + const spoilerButtonId = useId(); + + if (card === null) { + return null; + } + + const provider = + card.get('provider_name').length === 0 + ? decodeIDNA(getHostname(card.get('url'))) + : card.get('provider_name'); + const interactive = card.get('type') === 'video'; + const language = card.get('language') || ''; + const largeImage = + (card.get('image').length > 0 && card.get('width') > card.get('height')) || + interactive; + const showAuthor = !!card.getIn(['authors', 0, 'accountId']); + + const description = ( +
+ + {provider} + {card.get('published_at') && ( + <> + {' '} + · + + )} + + + + {card.get('title')} + + + {!showAuthor && + (card.get('author_name').length > 0 ? ( + + {card.get('author_name')} }} + /> + + ) : ( + + {card.get('description')} + + ))} +
+ ); + + const thumbnailStyle: React.CSSProperties = { + visibility: revealed ? undefined : 'hidden', + aspectRatio: '1', + }; + + if (largeImage && card.get('type') === 'video') { + thumbnailStyle.aspectRatio = `16 / 9`; + } else if (largeImage) { + thumbnailStyle.aspectRatio = '1.91 / 1'; + } + + let embed; + + const canvas = ( + + ); + + const thumbnailDescription = card.get('image_description'); + const thumbnail = ( + {thumbnailDescription} + ); + + const spoilerButton = ( +
+ +
+ ); + + if (interactive) { + if (embedded) { + embed = ; + } else { + embed = ( +
+ {canvas} + {thumbnail} + + {revealed ? ( +
+
+ + + + +
+
+ ) : ( + spoilerButton + )} +
+ ); + } + + return ( +
+ {embed} + + {description} + +
+ ); + } else if (card.get('image')) { + embed = ( +
+ {canvas} + {thumbnail} +
+ ); + } else { + embed = ( +
+ +
+ ); + } + + return ( + <> + + {embed} + {description} + + + {showAuthor && ( + + )} + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default Card; diff --git a/app/javascript/mastodon/features/status/components/detailed_status.tsx b/app/javascript/mastodon/features/status/components/detailed_status.tsx index b0c66b93384af2..1dee2e51477a99 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.tsx +++ b/app/javascript/mastodon/features/status/components/detailed_status.tsx @@ -262,8 +262,8 @@ export const DetailedStatus: React.FC<{ } else if (status.get('card') && !status.get('quote')) { media = ( ); From ee7e756e89ab116fdff09827205cfd2414d2bf23 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Wed, 26 Nov 2025 15:55:40 +0100 Subject: [PATCH 038/123] Fix null access error in card component (#37022) --- app/javascript/mastodon/api_types/statuses.ts | 2 +- app/javascript/mastodon/features/status/components/card.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/api_types/statuses.ts b/app/javascript/mastodon/api_types/statuses.ts index 00418a13d29d40..1451ea82a07686 100644 --- a/app/javascript/mastodon/api_types/statuses.ts +++ b/app/javascript/mastodon/api_types/statuses.ts @@ -51,7 +51,7 @@ export interface ApiPreviewCardJSON { html: string; width: number; height: number; - image: string; + image: string | null; image_description: string; embed_url: string; blurhash: string; diff --git a/app/javascript/mastodon/features/status/components/card.tsx b/app/javascript/mastodon/features/status/components/card.tsx index 65826f95f493d0..d3de36a1e1ce47 100644 --- a/app/javascript/mastodon/features/status/components/card.tsx +++ b/app/javascript/mastodon/features/status/components/card.tsx @@ -117,9 +117,9 @@ const Card: React.FC = ({ card, sensitive }) => { : card.get('provider_name'); const interactive = card.get('type') === 'video'; const language = card.get('language') || ''; + const hasImage = (card.get('image')?.length ?? 0) > 0; const largeImage = - (card.get('image').length > 0 && card.get('width') > card.get('height')) || - interactive; + (hasImage && card.get('width') > card.get('height')) || interactive; const showAuthor = !!card.getIn(['authors', 0, 'accountId']); const description = ( @@ -185,7 +185,7 @@ const Card: React.FC = ({ card, sensitive }) => { const thumbnailDescription = card.get('image_description'); const thumbnail = ( {thumbnailDescription} Date: Wed, 26 Nov 2025 17:18:48 +0100 Subject: [PATCH 039/123] chore(deps): update yarn to v4.12.0 (#36797) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- streaming/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c5fa667d527cb6..e1c91fbda06e45 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@mastodon/mastodon", "license": "AGPL-3.0-or-later", - "packageManager": "yarn@4.10.3", + "packageManager": "yarn@4.12.0", "engines": { "node": ">=20" }, diff --git a/streaming/package.json b/streaming/package.json index 90dd5dd0cf32f4..0f6651b741ac4b 100644 --- a/streaming/package.json +++ b/streaming/package.json @@ -1,7 +1,7 @@ { "name": "@mastodon/streaming", "license": "AGPL-3.0-or-later", - "packageManager": "yarn@4.10.3", + "packageManager": "yarn@4.12.0", "engines": { "node": ">=20" }, From 002632c3bbc6efe5f3201f8be5b62f1dd3592e99 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:18:51 +0100 Subject: [PATCH 040/123] chore(deps): update dependency aws-sdk-core to v3.239.2 (#37015) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 612f720ad28569..9b2c8b47bf26a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,7 +97,7 @@ GEM attr_required (1.0.2) aws-eventstream (1.4.0) aws-partitions (1.1186.0) - aws-sdk-core (3.239.1) + aws-sdk-core (3.239.2) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) From be2caba52732e876f655d98e1038e88a8f47d628 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:29:14 +0100 Subject: [PATCH 041/123] chore(deps): update dependency i18n-tasks to v1.1.2 (#37027) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9b2c8b47bf26a4..f1a35442f87544 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -324,7 +324,7 @@ GEM rainbow (>= 2.0.0) i18n (1.14.7) concurrent-ruby (~> 1.0) - i18n-tasks (1.1.1) + i18n-tasks (1.1.2) activesupport (>= 4.0.2) ast (>= 2.1.0) erubi From 322a4fee53dc1b2566097c2617908b9785b5d193 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Thu, 27 Nov 2025 10:36:44 +0100 Subject: [PATCH 042/123] First steps towards a collection creation service (#37020) Co-authored-by: Claire --- app/services/create_collection_service.rb | 31 +++++++ .../create_collection_service_spec.rb | 82 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 app/services/create_collection_service.rb create mode 100644 spec/services/create_collection_service_spec.rb diff --git a/app/services/create_collection_service.rb b/app/services/create_collection_service.rb new file mode 100644 index 00000000000000..5cf7249fee4543 --- /dev/null +++ b/app/services/create_collection_service.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class CreateCollectionService + def call(params, account) + tag = params.delete(:tag) + account_ids = params.delete(:account_ids) + @collection = Collection.new(params.merge({ account:, local: true, tag: find_or_create_tag(tag) })) + build_items(account_ids) + + @collection.save! + @collection + end + + private + + def find_or_create_tag(name) + return nil if name.blank? + + Tag.find_or_create_by_names(name).first + end + + def build_items(account_ids) + return if account_ids.blank? + + account_ids.each do |account_id| + account = Account.find(account_id) + # TODO: validate preferences + @collection.collection_items.build(account:) + end + end +end diff --git a/spec/services/create_collection_service_spec.rb b/spec/services/create_collection_service_spec.rb new file mode 100644 index 00000000000000..43842c8cc659c4 --- /dev/null +++ b/spec/services/create_collection_service_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe CreateCollectionService do + subject { described_class.new } + + let(:author) { Fabricate.create(:account) } + + describe '#call' do + let(:base_params) do + { + name: 'People to follow', + description: 'All my favourites', + sensitive: false, + discoverable: true, + } + end + + context 'when given valid parameters' do + it 'creates a new local collection' do + collection = nil + + expect do + collection = subject.call(base_params, author) + end.to change(Collection, :count).by(1) + + expect(collection).to be_a(Collection) + expect(collection).to be_local + end + + context 'when given account ids' do + let(:account_ids) do + Fabricate.times(2, :account).map { |a| a.id.to_s } + end + let(:params) do + base_params.merge(account_ids:) + end + + it 'also creates collection items' do + expect do + subject.call(params, author) + end.to change(CollectionItem, :count).by(2) + end + end + + context 'when given a tag' do + let(:params) { base_params.merge(tag: '#people') } + + context 'when the tag exists' do + let!(:tag) { Fabricate.create(:tag, name: 'people') } + + it 'correctly assigns the existing tag' do + collection = subject.call(params, author) + + expect(collection.tag).to eq tag + end + end + + context 'when the tag does not exist' do + it 'creates a new tag' do + collection = nil + + expect do + collection = subject.call(params, author) + end.to change(Tag, :count).by(1) + + expect(collection.tag.name).to eq 'people' + end + end + end + end + + context 'when given invalid parameters' do + it 'raises an exception' do + expect do + subject.call({}, author) + end.to raise_error(ActiveRecord::RecordInvalid) + end + end + end +end From e126cfc76d9f223527ec92fa4cf4856beddca10f Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 27 Nov 2025 10:36:58 +0100 Subject: [PATCH 043/123] Fix error page when logging out or boosting on mobile (#37028) --- app/javascript/mastodon/features/ui/components/modal_root.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/ui/components/modal_root.jsx b/app/javascript/mastodon/features/ui/components/modal_root.jsx index 9afda83908e9b9..f8e4c0bb957946 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.jsx +++ b/app/javascript/mastodon/features/ui/components/modal_root.jsx @@ -136,7 +136,7 @@ export default class ModalRoot extends PureComponent { {visible && ( <> - + {(SpecificComponent) => { return ; }} From 199376a080a5f8df9d716ebea5d846ca7600973f Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 27 Nov 2025 04:38:27 -0500 Subject: [PATCH 044/123] Use existing time format string to generate backup archive filename (#36469) --- app/services/backup_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb index cc86af000537f3..42aa4c93cfb71c 100644 --- a/app/services/backup_service.rb +++ b/app/services/backup_service.rb @@ -63,7 +63,7 @@ def build_archive! dump_actor!(zipfile) end - archive_filename = "#{['archive', Time.now.utc.strftime('%Y%m%d%H%M%S'), SecureRandom.hex(16)].join('-')}.zip" + archive_filename = "#{['archive', Time.current.to_fs(:number), SecureRandom.hex(16)].join('-')}.zip" @backup.dump = ActionDispatch::Http::UploadedFile.new(tempfile: tmp_file, filename: archive_filename) @backup.processed = true From 90466d026263ce6425d18b0fdbe349c90c222f9e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:51:15 +0100 Subject: [PATCH 045/123] New Crowdin Translations (automated) (#37026) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/lad.json | 6 ++++++ app/javascript/mastodon/locales/pl.json | 1 + app/javascript/mastodon/locales/pt-BR.json | 8 ++++---- config/locales/activerecord.lad.yml | 4 ++++ config/locales/devise.lad.yml | 1 + config/locales/lad.yml | 1 + config/locales/simple_form.lad.yml | 2 ++ config/locales/simple_form.pt-BR.yml | 4 ++-- config/locales/simple_form.sv.yml | 9 +++++++++ 9 files changed, 30 insertions(+), 6 deletions(-) diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index cab0580e1a4379..9e6f503983289e 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -31,6 +31,8 @@ "account.edit_profile_short": "Edita", "account.enable_notifications": "Avizame kuando @{name} publike", "account.endorse": "Avalia en profil", + "account.familiar_followers_one": "Segido por {name1}", + "account.familiar_followers_two": "Segido por {name1} i {name2}", "account.featured": "Avaliado", "account.featured.accounts": "Profiles", "account.featured.hashtags": "Etiketas", @@ -46,6 +48,7 @@ "account.followers": "Suivantes", "account.followers.empty": "Por agora dingun no sige a este utilizador.", "account.followers_counter": "{count, plural, one {{counter} suivante} other {{counter} suivantes}}", + "account.followers_you_know_counter": "{counter} ke koneses", "account.following": "Sigiendo", "account.following_counter": "{count, plural, other {Sigiendo a {counter}}}", "account.follows.empty": "Este utilizador ainda no sige a dingun.", @@ -157,6 +160,7 @@ "column.edit_list": "Edita lista", "column.favourites": "Te plazen", "column.firehose": "Linyas en bivo", + "column.firehose_singular": "Linya en bivo", "column.follow_requests": "Solisitudes de segimiento", "column.home": "Linya prinsipala", "column.lists": "Listas", @@ -351,6 +355,7 @@ "follow_suggestions.who_to_follow": "A ken segir", "followed_tags": "Etiketas segidas", "footer.about": "Sovre mozotros", + "footer.about_this_server": "Sovre mozotros", "footer.directory": "Katalogo de profiles", "footer.get_app": "Abasha aplikasyon", "footer.keyboard_shortcuts": "Akortamientos de klaviatura", @@ -522,6 +527,7 @@ "notification.label.mention": "Enmenta", "notification.label.private_mention": "Enmentadura privada", "notification.label.private_reply": "Repuesta privada", + "notification.label.quote": "{name} sito tu publikasyon", "notification.label.reply": "Arisponde", "notification.mention": "Enmenta", "notification.mentioned_you": "{name} te enmento", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index f88f4b6681553c..8f2d33c5fa6875 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -173,6 +173,7 @@ "column.edit_list": "Edytuj listę", "column.favourites": "Ulubione", "column.firehose": "Aktualności", + "column.firehose_singular": "Na żywo", "column.follow_requests": "Prośby o obserwację", "column.home": "Strona główna", "column.list_members": "Zarządzaj osobami na liście", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 10fb05fa2d244d..2bad3ca2b9a519 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -157,8 +157,8 @@ "bundle_modal_error.close": "Fechar", "bundle_modal_error.message": "Algo deu errado ao carregar esta tela.", "bundle_modal_error.retry": "Tente novamente", - "carousel.current": "Deslize {current, number} / {max, number}", - "carousel.slide": "Deslize {current, number} of {max, number}", + "carousel.current": "Slide {current, number} / {max, number}", + "carousel.slide": "Slide {current, number} of {max, number}", "closed_registrations.other_server_instructions": "Como o Mastodon é descentralizado, você pode criar uma conta em outro servidor e ainda pode interagir com este.", "closed_registrations_modal.description": "Não é possível criar uma conta em {domain} no momento, mas atente que você não precisa de uma conta especificamente em {domain} para usar o Mastodon.", "closed_registrations_modal.find_another_server": "Encontrar outro servidor", @@ -369,9 +369,9 @@ "explore.trending_links": "Notícias", "explore.trending_statuses": "Publicações", "explore.trending_tags": "Hashtags", - "featured_carousel.current": "Postar {current, number} / {max, number}", + "featured_carousel.current": "Publicação {current, number} / {max, number}", "featured_carousel.header": "{count, plural, one {Postagem fixada} other {Postagens fixadas}}", - "featured_carousel.slide": "Postar {current, number} of {max, number}", + "featured_carousel.slide": "Publicação {current, number} of {max, number}", "filter_modal.added.context_mismatch_explanation": "Esta categoria de filtro não se aplica ao contexto no qual você acessou esta publicação. Se quiser que a publicação seja filtrada nesse contexto também, você terá que editar o filtro.", "filter_modal.added.context_mismatch_title": "Incompatibilidade de contexto!", "filter_modal.added.expired_explanation": "Esta categoria de filtro expirou, você precisará alterar a data de expiração para aplicar.", diff --git a/config/locales/activerecord.lad.yml b/config/locales/activerecord.lad.yml index 8fd23b53fec7f3..8b1fea0d960b7d 100644 --- a/config/locales/activerecord.lad.yml +++ b/config/locales/activerecord.lad.yml @@ -30,6 +30,10 @@ lad: attributes: url: invalid: no es adreso URL valido + collection: + attributes: + tag: + unusable: no se pueden uzar doorkeeper/application: attributes: website: diff --git a/config/locales/devise.lad.yml b/config/locales/devise.lad.yml index 45e475045024a5..b294c1807bcce9 100644 --- a/config/locales/devise.lad.yml +++ b/config/locales/devise.lad.yml @@ -7,6 +7,7 @@ lad: send_paranoid_instructions: Si tu adreso de posta elektronika existe en muestra baza de datos, risiviras una posta elektronika kon instruksyones sobre komo konfirmar tu adreso de posta elektronika en pokos minutos. failure: already_authenticated: Ya te konektates kon tu kuento. + closed_registrations: Tu prova de enrejistrarte tiene sido blokada por a una politika de red. Si piensas ke esto es un yerro, eskrive a %{email}. inactive: Tu kuento ainda no tyene sido aktivado. invalid: "%{authentication_keys} o kod invalido." last_attempt: Aprova una vez mas antes de ke tu kuento sea blokado. diff --git a/config/locales/lad.yml b/config/locales/lad.yml index 90b04a78f1f941..7747a18f5d7474 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -477,6 +477,7 @@ lad: created_at: Kriyado en delete: Efasa ip: Adreso IP + request_body: Kuerpo de la solisitud providers: active: Aktivo base_url: URL baza diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml index 35fb5fb9f42a9f..b1d0a3e2562149 100644 --- a/config/locales/simple_form.lad.yml +++ b/config/locales/simple_form.lad.yml @@ -313,7 +313,9 @@ lad: domain: Domeno min_age: Edad minima user: + date_of_birth_1i: Anyo date_of_birth_2i: Mez + date_of_birth_3i: Diya role: Rolo time_zone: Zona de tiempo user_role: diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index a63a11220f617f..c0f14f77734a70 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -65,7 +65,7 @@ pt-BR: setting_display_media_hide_all: Sempre ocultar todas as mídias setting_display_media_show_all: Sempre mostrar mídia sensível setting_emoji_style: Como exibir emojis. "Automáticos" tentará usar emojis nativos, mas voltará para o Twemoji para navegadores legados. - setting_quick_boosting_html: Quando ativado, clicar no ícone de impulsionamento %{boost_icon} impulsionará imediatamente o texto, em vez de abrir o menu suspenso de impulsionamento/cotação. Move a ação de cotação para o menu %{options_icon} (Opções). + setting_quick_boosting_html: Quando ativado, clicar no ícone de impulsionamento %{boost_icon} impulsionará imediatamente o texto, em vez de abrir o menu suspenso de impulsionamento/citação. Move a ação de citação para o menu %{options_icon} (Opções). setting_system_scrollbars_ui: Se aplica apenas para navegadores de computador baseado no Safari e Chrome setting_use_blurhash: O blur é baseado nas cores da imagem oculta, ofusca a maioria dos detalhes setting_use_pending_items: Ocultar atualizações da linha do tempo atrás de um clique ao invés de rolar automaticamente @@ -237,7 +237,7 @@ pt-BR: setting_aggregate_reblogs: Agrupar boosts nas linhas setting_always_send_emails: Sempre enviar notificações por e-mail setting_auto_play_gif: Reproduzir GIFs automaticamente - setting_boost_modal: Controle que aumenta a visibilidade + setting_boost_modal: Controlar a visibilidade dos impulsos setting_default_language: Idioma dos toots setting_default_privacy: Visibilidade da publicação setting_default_quote_policy: Quem pode citar diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml index 19d0fd6f9a8d5e..b7089ede18fd68 100644 --- a/config/locales/simple_form.sv.yml +++ b/config/locales/simple_form.sv.yml @@ -232,8 +232,10 @@ sv: setting_always_send_emails: Skicka alltid e-postnotiser setting_auto_play_gif: Spela upp GIF:ar automatiskt setting_default_language: Inläggsspråk + setting_default_privacy: Inläggssynlighet setting_default_quote_policy: Vem kan citera setting_default_sensitive: Markera alltid media som känsligt + setting_delete_modal: Varna mig innan ett inlägg tas bort setting_disable_hover_cards: Inaktivera profilförhandsgranskning vid hovring setting_disable_swiping: Inaktivera svepande rörelser setting_display_media: Mediavisning @@ -243,6 +245,8 @@ sv: setting_emoji_style: Emoji-stil setting_expand_spoilers: Utöka alltid tutningar markerade med innehållsvarningar setting_hide_network: Göm ditt nätverk + setting_missing_alt_text_modal: Varna mig innan jag gör mediainlägg utan alt-text + setting_quick_boosting: Aktivera snabb-boostande setting_reduce_motion: Minska rörelser i animationer setting_system_font_ui: Använd systemets standardfont setting_system_scrollbars_ui: Använd systemets standardrullningsfält @@ -276,12 +280,17 @@ sv: content_cache_retention_period: Förvaringsperiod för fjärrinnehåll custom_css: Anpassad CSS favicon: Favicon + landing_page: Startsida för nya besökare + local_live_feed_access: Åtkomst till live-flöden med lokala inlägg + local_topic_feed_access: Åtkomst till hashtagg och länkflöden med lokala inlägg mascot: Anpassad maskot (tekniskt arv) media_cache_retention_period: Tid för bibehållande av mediecache min_age: Åldersgräns peers_api_enabled: Publicera lista över upptäckta servrar i API:et profile_directory: Aktivera profilkatalog registrations_mode: Vem kan registrera sig + remote_live_feed_access: Åtkomst till live-flöden med fjärrinlägg + remote_topic_feed_access: Åtkomst till hashtagg och länkflöden med fjärrinlägg require_invite_text: Kräv anledning för att gå med show_domain_blocks: Visa domänblockeringar show_domain_blocks_rationale: Visa varför domäner blockerades From 07ecf648ddc25fbb07b2282274a4bd190c8d6f20 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 27 Nov 2025 11:50:48 +0100 Subject: [PATCH 046/123] Remove unused bundle-related Redux actions (#37030) --- app/javascript/mastodon/actions/bundles.js | 25 ------------------- .../features/ui/components/bundle.jsx | 14 +---------- .../features/ui/components/columns_area.jsx | 6 ++--- .../features/ui/components/modal_root.jsx | 7 +++--- .../ui/containers/bundle_container.js | 18 ------------- .../features/ui/util/react_router_helpers.jsx | 6 ++--- 6 files changed, 10 insertions(+), 66 deletions(-) delete mode 100644 app/javascript/mastodon/actions/bundles.js delete mode 100644 app/javascript/mastodon/features/ui/containers/bundle_container.js diff --git a/app/javascript/mastodon/actions/bundles.js b/app/javascript/mastodon/actions/bundles.js deleted file mode 100644 index ecc9c8f7d3ec22..00000000000000 --- a/app/javascript/mastodon/actions/bundles.js +++ /dev/null @@ -1,25 +0,0 @@ -export const BUNDLE_FETCH_REQUEST = 'BUNDLE_FETCH_REQUEST'; -export const BUNDLE_FETCH_SUCCESS = 'BUNDLE_FETCH_SUCCESS'; -export const BUNDLE_FETCH_FAIL = 'BUNDLE_FETCH_FAIL'; - -export function fetchBundleRequest(skipLoading) { - return { - type: BUNDLE_FETCH_REQUEST, - skipLoading, - }; -} - -export function fetchBundleSuccess(skipLoading) { - return { - type: BUNDLE_FETCH_SUCCESS, - skipLoading, - }; -} - -export function fetchBundleFail(error, skipLoading) { - return { - type: BUNDLE_FETCH_FAIL, - error, - skipLoading, - }; -} diff --git a/app/javascript/mastodon/features/ui/components/bundle.jsx b/app/javascript/mastodon/features/ui/components/bundle.jsx index 3617baf4f11c5a..57143f157150a5 100644 --- a/app/javascript/mastodon/features/ui/components/bundle.jsx +++ b/app/javascript/mastodon/features/ui/components/bundle.jsx @@ -2,7 +2,6 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; const emptyComponent = () => null; -const noop = () => { }; class Bundle extends PureComponent { @@ -12,18 +11,12 @@ class Bundle extends PureComponent { error: PropTypes.func, children: PropTypes.func.isRequired, renderDelay: PropTypes.number, - onFetch: PropTypes.func, - onFetchSuccess: PropTypes.func, - onFetchFail: PropTypes.func, }; static defaultProps = { loading: emptyComponent, error: emptyComponent, renderDelay: 0, - onFetch: noop, - onFetchSuccess: noop, - onFetchFail: noop, }; static cache = new Map; @@ -50,7 +43,7 @@ class Bundle extends PureComponent { } load = (props) => { - const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props; + const { fetchComponent, renderDelay } = props || this.props; const cachedMod = Bundle.cache.get(fetchComponent); if (fetchComponent === undefined) { @@ -58,11 +51,8 @@ class Bundle extends PureComponent { return Promise.resolve(); } - onFetch(); - if (cachedMod) { this.setState({ mod: cachedMod.default }); - onFetchSuccess(); return Promise.resolve(); } @@ -77,11 +67,9 @@ class Bundle extends PureComponent { .then((mod) => { Bundle.cache.set(fetchComponent, mod); this.setState({ mod: mod.default }); - onFetchSuccess(); }) .catch((error) => { this.setState({ mod: null }); - onFetchFail(error); }); }; diff --git a/app/javascript/mastodon/features/ui/components/columns_area.jsx b/app/javascript/mastodon/features/ui/components/columns_area.jsx index 77b95d526a840e..753f7e9ac38efe 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.jsx +++ b/app/javascript/mastodon/features/ui/components/columns_area.jsx @@ -5,7 +5,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { scrollRight } from '../../../scroll'; -import BundleContainer from '../containers/bundle_container'; import { Compose, Notifications, @@ -21,6 +20,7 @@ import { } from '../util/async-components'; import { useColumnsContext } from '../util/columns_context'; +import Bundle from './bundle'; import BundleColumnError from './bundle_column_error'; import { ColumnLoading } from './column_loading'; import { ComposePanel, RedirectToMobileComposeIfNeeded } from './compose_panel'; @@ -145,9 +145,9 @@ export default class ColumnsArea extends ImmutablePureComponent { const other = params && params.other ? params.other : {}; return ( - + {SpecificComponent => } - + ); })} diff --git a/app/javascript/mastodon/features/ui/components/modal_root.jsx b/app/javascript/mastodon/features/ui/components/modal_root.jsx index f8e4c0bb957946..6f9b23042d78ee 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.jsx +++ b/app/javascript/mastodon/features/ui/components/modal_root.jsx @@ -21,11 +21,10 @@ import { AnnualReportModal, } from 'mastodon/features/ui/util/async-components'; -import BundleContainer from '../containers/bundle_container'; - import { ActionsModal } from './actions_modal'; import AudioModal from './audio_modal'; import { BoostModal } from './boost_modal'; +import Bundle from './bundle'; import { ConfirmationModal, ConfirmDeleteStatusModal, @@ -136,11 +135,11 @@ export default class ModalRoot extends PureComponent { {visible && ( <> - + {(SpecificComponent) => { return ; }} - + diff --git a/app/javascript/mastodon/features/ui/containers/bundle_container.js b/app/javascript/mastodon/features/ui/containers/bundle_container.js deleted file mode 100644 index 6a476fe2483cf3..00000000000000 --- a/app/javascript/mastodon/features/ui/containers/bundle_container.js +++ /dev/null @@ -1,18 +0,0 @@ -import { connect } from 'react-redux'; - -import { fetchBundleRequest, fetchBundleSuccess, fetchBundleFail } from '../../../actions/bundles'; -import Bundle from '../components/bundle'; - -const mapDispatchToProps = dispatch => ({ - onFetch () { - dispatch(fetchBundleRequest()); - }, - onFetchSuccess () { - dispatch(fetchBundleSuccess()); - }, - onFetchFail (error) { - dispatch(fetchBundleFail(error)); - }, -}); - -export default connect(null, mapDispatchToProps)(Bundle); diff --git a/app/javascript/mastodon/features/ui/util/react_router_helpers.jsx b/app/javascript/mastodon/features/ui/util/react_router_helpers.jsx index ec68b5a4a72491..3728e2e2e7efbd 100644 --- a/app/javascript/mastodon/features/ui/util/react_router_helpers.jsx +++ b/app/javascript/mastodon/features/ui/util/react_router_helpers.jsx @@ -5,9 +5,9 @@ import { Switch, Route, useLocation } from 'react-router-dom'; import StackTrace from 'stacktrace-js'; +import Bundle from '../components/bundle'; import BundleColumnError from '../components/bundle_column_error'; import { ColumnLoading } from '../components/column_loading'; -import BundleContainer from '../containers/bundle_container'; // Small wrapper to pass multiColumn to the route components export const WrappedSwitch = ({ multiColumn, children }) => { @@ -80,9 +80,9 @@ export class WrappedRoute extends Component { } return ( - + {Component => {content}} - + ); }; From 0004ed4c8021ebd400d32eb32a5e479ebd7bcf91 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 10:51:57 +0000 Subject: [PATCH 047/123] chore(deps): update opentelemetry-ruby (non-major) (#37016) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f1a35442f87544..daa470e2d4b688 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -542,15 +542,15 @@ GEM opentelemetry-registry (~> 0.1) opentelemetry-instrumentation-concurrent_ruby (0.24.0) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-excon (0.26.0) + opentelemetry-instrumentation-excon (0.26.1) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-faraday (0.30.0) + opentelemetry-instrumentation-faraday (0.30.1) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-http (0.27.0) + opentelemetry-instrumentation-http (0.27.1) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-http_client (0.26.0) + opentelemetry-instrumentation-http_client (0.26.1) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-net_http (0.26.0) + opentelemetry-instrumentation-net_http (0.26.1) opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-pg (0.33.0) opentelemetry-helpers-sql From 0d2e9522ffbfc045177b9b21a57c29d907fac635 Mon Sep 17 00:00:00 2001 From: Echo Date: Thu, 27 Nov 2025 14:10:55 +0100 Subject: [PATCH 048/123] Replace Rails UJS library (#37031) --- app/javascript/mastodon/common.ts | 8 +-- app/javascript/mastodon/utils/links.ts | 88 ++++++++++++++++++++++++++ package.json | 2 - yarn.lock | 16 ----- 4 files changed, 90 insertions(+), 24 deletions(-) create mode 100644 app/javascript/mastodon/utils/links.ts diff --git a/app/javascript/mastodon/common.ts b/app/javascript/mastodon/common.ts index e621a24e39fe46..33d2b5ad171f40 100644 --- a/app/javascript/mastodon/common.ts +++ b/app/javascript/mastodon/common.ts @@ -1,9 +1,5 @@ -import Rails from '@rails/ujs'; +import { setupLinkListeners } from './utils/links'; export function start() { - try { - Rails.start(); - } catch { - // If called twice - } + setupLinkListeners(); } diff --git a/app/javascript/mastodon/utils/links.ts b/app/javascript/mastodon/utils/links.ts new file mode 100644 index 00000000000000..02b74bde4ddf7f --- /dev/null +++ b/app/javascript/mastodon/utils/links.ts @@ -0,0 +1,88 @@ +import { on } from 'delegated-events'; + +export function setupLinkListeners() { + on('click', 'a[data-confirm]', handleConfirmLink); + + // We don't want to target links with data-confirm here, as those are handled already. + on('click', 'a[data-method]:not([data-confirm])', handleMethodLink); +} + +function handleConfirmLink(event: MouseEvent) { + const anchor = event.currentTarget; + if (!(anchor instanceof HTMLAnchorElement)) { + return; + } + const message = anchor.dataset.confirm; + if (!message || !window.confirm(message)) { + event.preventDefault(); + return; + } + + if (anchor.dataset.method) { + handleMethodLink(event); + } +} + +function handleMethodLink(event: MouseEvent) { + const anchor = event.currentTarget; + if (!(anchor instanceof HTMLAnchorElement)) { + return; + } + + const method = anchor.dataset.method?.toLowerCase(); + if (!method) { + return; + } + event.preventDefault(); + + // Create and submit a form with the specified method. + const form = document.createElement('form'); + form.method = 'post'; + form.action = anchor.href; + + // Add the hidden _method input to simulate other HTTP methods. + const methodInput = document.createElement('input'); + methodInput.type = 'hidden'; + methodInput.name = '_method'; + methodInput.value = method; + form.appendChild(methodInput); + + // Add CSRF token if available for same-origin requests. + const csrf = getCSRFToken(); + if (csrf && !isCrossDomain(anchor.href)) { + const csrfInput = document.createElement('input'); + csrfInput.type = 'hidden'; + csrfInput.name = csrf.param; + csrfInput.value = csrf.token; + form.appendChild(csrfInput); + } + + // The form needs to be in the document to be submitted. + form.style.display = 'none'; + document.body.appendChild(form); + + // We use requestSubmit to ensure any form submit handlers are properly invoked. + form.requestSubmit(); +} + +function getCSRFToken() { + const param = document.querySelector( + 'meta[name="csrf-param"]', + ); + const token = document.querySelector( + 'meta[name="csrf-token"]', + ); + if (param && token) { + return { param: param.content, token: token.content }; + } + return null; +} + +function isCrossDomain(href: string) { + const link = document.createElement('a'); + link.href = href; + return ( + link.protocol !== window.location.protocol || + link.host !== window.location.host + ); +} diff --git a/package.json b/package.json index e1c91fbda06e45..64a1a00193a7d4 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "@gamestdio/websocket": "^0.3.2", "@github/webauthn-json": "^2.1.1", "@optimize-lodash/rollup-plugin": "^5.0.2", - "@rails/ujs": "7.1.600", "@react-spring/web": "^9.7.5", "@reduxjs/toolkit": "^2.0.1", "@use-gesture/react": "^10.3.1", @@ -149,7 +148,6 @@ "@types/object-assign": "^4.0.30", "@types/prop-types": "^15.7.5", "@types/punycode": "^2.1.0", - "@types/rails__ujs": "^6.0.4", "@types/react": "^18.2.7", "@types/react-dom": "^18.2.4", "@types/react-helmet": "^6.1.6", diff --git a/yarn.lock b/yarn.lock index 38c04d9fd91a93..7b91fd6ff1a792 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2708,7 +2708,6 @@ __metadata: "@gamestdio/websocket": "npm:^0.3.2" "@github/webauthn-json": "npm:^2.1.1" "@optimize-lodash/rollup-plugin": "npm:^5.0.2" - "@rails/ujs": "npm:7.1.600" "@react-spring/web": "npm:^9.7.5" "@reduxjs/toolkit": "npm:^2.0.1" "@storybook/addon-a11y": "npm:^10.0.6" @@ -2728,7 +2727,6 @@ __metadata: "@types/object-assign": "npm:^4.0.30" "@types/prop-types": "npm:^15.7.5" "@types/punycode": "npm:^2.1.0" - "@types/rails__ujs": "npm:^6.0.4" "@types/react": "npm:^18.2.7" "@types/react-dom": "npm:^18.2.4" "@types/react-helmet": "npm:^6.1.6" @@ -3205,13 +3203,6 @@ __metadata: languageName: node linkType: hard -"@rails/ujs@npm:7.1.600": - version: 7.1.600 - resolution: "@rails/ujs@npm:7.1.600" - checksum: 10c0/0ccaa68a08fbc7b084ab89a1fe49520a5cba6d99f4b0feaf0cb3d00334c59d8d798932d7e49b84aa388875d039ea1e17eb115ed96a80ad157e408a13eceef53e - languageName: node - linkType: hard - "@react-spring/animated@npm:~9.7.5": version: 9.7.5 resolution: "@react-spring/animated@npm:9.7.5" @@ -4297,13 +4288,6 @@ __metadata: languageName: node linkType: hard -"@types/rails__ujs@npm:^6.0.4": - version: 6.0.4 - resolution: "@types/rails__ujs@npm:6.0.4" - checksum: 10c0/7477cb03a0e1339b9cd5c8ac4a197a153e2ff48742b2f527c5a39dcdf80f01493011e368483290d3717662c63066fada3ab203a335804cbb3573cf575f37007e - languageName: node - linkType: hard - "@types/range-parser@npm:*": version: 1.2.7 resolution: "@types/range-parser@npm:1.2.7" From f07cff42c25f556baeea9215939ce105a99bea84 Mon Sep 17 00:00:00 2001 From: Echo Date: Thu, 27 Nov 2025 16:27:18 +0100 Subject: [PATCH 049/123] Fix media modal misalignment in Safari (#37034) --- app/javascript/styles/mastodon/components.scss | 6 ++++-- app/javascript/styles_new/mastodon/components.scss | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 5571fd6f47ea1c..a775e126b75bd8 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -6204,12 +6204,14 @@ a.status-card { inset-inline-start: 0; inset-inline-end: 0; bottom: 0; - align-items: center; - justify-content: space-around; // If set to center, the fullscreen image overlay is misaligned. > div { flex-shrink: 0; overflow: auto; + display: flex; + align-items: center; + justify-content: center; + width: 100%; } } diff --git a/app/javascript/styles_new/mastodon/components.scss b/app/javascript/styles_new/mastodon/components.scss index d5b61e6abbf967..6edf92490a7e44 100644 --- a/app/javascript/styles_new/mastodon/components.scss +++ b/app/javascript/styles_new/mastodon/components.scss @@ -6085,12 +6085,14 @@ a.status-card { inset-inline-start: 0; inset-inline-end: 0; bottom: 0; - align-items: center; - justify-content: space-around; // If set to center, the fullscreen image overlay is misaligned. > div { flex-shrink: 0; overflow: auto; + display: flex; + align-items: center; + justify-content: center; + width: 100%; } } From 826e9d70472dbccede173026b5f5779a1b8f2b38 Mon Sep 17 00:00:00 2001 From: Eashwar Ranganathan Date: Thu, 27 Nov 2025 10:52:59 -0500 Subject: [PATCH 050/123] Make tootctl aware of 'require approval' for email domains (#34579) --- AUTHORS.md | 2 +- lib/mastodon/cli/email_domain_blocks.rb | 35 +++++++++++-- .../mastodon/cli/email_domain_blocks_spec.rb | 52 +++++++++++++++++-- spec/support/command_line_helpers.rb | 2 + 4 files changed, 83 insertions(+), 8 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 78cc37a17b9350..5d243ed43b1b51 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -538,7 +538,7 @@ and provided thanks to the work of the following contributors: * [Drew Schuster](mailto:dtschust@gmail.com) * [Dryusdan](mailto:dryusdan@dryusdan.fr) * [Eai](mailto:eai@mizle.net) -* [Eashwar Ranganathan](mailto:eranganathan@lyft.com) +* [Eashwar Ranganathan](mailto:eashwar@eashwar.com) * [Ed Knutson](mailto:knutsoned@gmail.com) * [Elizabeth Martín Campos](mailto:me@elizabeth.sh) * [Elizabeth Myers](mailto:elizabeth@interlinked.me) diff --git a/lib/mastodon/cli/email_domain_blocks.rb b/lib/mastodon/cli/email_domain_blocks.rb index 0cc9ccb7058bae..a6093685b9bb35 100644 --- a/lib/mastodon/cli/email_domain_blocks.rb +++ b/lib/mastodon/cli/email_domain_blocks.rb @@ -5,9 +5,33 @@ module Mastodon::CLI class EmailDomainBlocks < Base + option :only_blocked, type: :boolean, defaut: false + option :only_with_approval, type: :boolean, default: false desc 'list', 'List blocked e-mail domains' + long_desc <<-LONG_DESC + By default this command lists all domains in the email domain block list + and their associated MX records (if included). + + If the --only-blocked option is provided, this command will list only email + domains that are fully blocked from signup. + + If the --only-with-approval option is provided, this command will list only + email domains that are allowed to be used but require manual approval. + + The --only-blocked and --only-with-approval options are mutually exclusive. + LONG_DESC def list - EmailDomainBlock.parents.find_each do |parent| + fail_with_message 'Cannot specify both --only-blocked and --only-with-approval' if options[:only_blocked] && options[:only_with_approval] + + base_query = EmailDomainBlock.parents + + if options[:only_blocked] + base_query = base_query.where(allow_with_approval: false) + elsif options[:only_with_approval] + base_query = base_query.where(allow_with_approval: true) + end + + base_query.find_each do |parent| say(parent.domain.to_s, :white) shell.indent do @@ -19,6 +43,7 @@ def list end option :with_dns_records, type: :boolean + option :allow_with_approval, type: :boolean, defaut: false desc 'add DOMAIN...', 'Block e-mail domain(s)' long_desc <<-LONG_DESC Blocking an e-mail domain prevents users from signing up @@ -30,6 +55,9 @@ def list This can be helpful if you are blocking an e-mail server that has many different domains pointing to it as it allows you to essentially block it at the root. + + When the --allow-with-approval option is set, the email domains provided will + have to be manually approved for signup. LONG_DESC def add(*domains) fail_with_message 'No domain(s) given' if domains.empty? @@ -47,19 +75,18 @@ def add(*domains) other_domains = [] other_domains = DomainResource.new(domain).mx if options[:with_dns_records] - email_domain_block = EmailDomainBlock.new(domain: domain, other_domains: other_domains) + email_domain_block = EmailDomainBlock.new(domain: domain, other_domains: other_domains, allow_with_approval: options[:allow_with_approval]) email_domain_block.save! processed += 1 (email_domain_block.other_domains || []).uniq.each do |hostname| - another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: email_domain_block) - if EmailDomainBlock.exists?(domain: hostname) say("#{hostname} is already blocked.", :yellow) skipped += 1 next end + another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: email_domain_block, allow_with_approval: options[:allow_with_approval]) another_email_domain_block.save! processed += 1 end diff --git a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb index 6ce1a7c5f3aee6..1662785f392a71 100644 --- a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb +++ b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb @@ -15,17 +15,62 @@ describe '#list' do let(:action) { :list } + context 'with both --only-blocked and --only-with-approval' do + let(:options) { { only_blocked: true, only_with_approval: true } } + + it 'warns about usage and exits' do + expect { subject } + .to raise_error(Thor::Error, 'Cannot specify both --only-blocked and --only-with-approval') + end + end + context 'with email domain block records' do let!(:parent_block) { Fabricate(:email_domain_block) } let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) } + let!(:parent_allow_block) { Fabricate(:email_domain_block, allow_with_approval: true) } + let!(:child_allow_block) { Fabricate(:email_domain_block, parent: parent_allow_block, allow_with_approval: true) } - it 'lists the blocks' do + it 'lists all the blocks by default' do expect { subject } .to output_results( parent_block.domain, - child_block.domain + child_block.domain, + parent_allow_block.domain, + child_allow_block.domain ) end + + context 'with the --only-blocked flag set' do + let(:options) { { only_blocked: true } } + + it 'lists only blocked domains' do + expect { subject } + .to output_results( + parent_block.domain, + child_block.domain + ) + .and not_output_results( + parent_allow_block.domain, + child_allow_block.domain + ) + end + end + + context 'with the --only-with-approval flag set' do + let(:options) { { only_with_approval: true } } + + it 'lists only manually approvable domains' do + expect { subject } + .to output_results( + parent_allow_block.domain, + child_allow_block.domain + ) + .and not_output_results( + parent_block.domain, + child_block.domain + ) + end + end end end @@ -56,6 +101,7 @@ context 'when no blocks exist' do let(:domain) { 'host.example' } let(:arguments) { [domain] } + let(:options) { { allow_with_approval: false } } it 'adds a new block' do expect { subject } @@ -67,7 +113,7 @@ context 'with --with-dns-records true' do let(:domain) { 'host.example' } let(:arguments) { [domain] } - let(:options) { { with_dns_records: true } } + let(:options) { { allow_with_approval: false, with_dns_records: true } } before do configure_mx(domain: domain, exchange: 'other.host') diff --git a/spec/support/command_line_helpers.rb b/spec/support/command_line_helpers.rb index 09b2b70ba10261..5cd86b10199499 100644 --- a/spec/support/command_line_helpers.rb +++ b/spec/support/command_line_helpers.rb @@ -7,3 +7,5 @@ def output_results(*) ).to_stdout end end + +RSpec::Matchers.define_negated_matcher :not_output_results, :output_results From 44ff2c32d32815691a35355eb4d25ac472b9eb33 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 27 Nov 2025 17:23:11 +0100 Subject: [PATCH 051/123] Fix filters not applying to search results (#36346) --- app/javascript/mastodon/features/search/index.tsx | 4 ++-- app/javascript/mastodon/selectors/index.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/search/index.tsx b/app/javascript/mastodon/features/search/index.tsx index a55255bcde0451..5b4ed807fa949a 100644 --- a/app/javascript/mastodon/features/search/index.tsx +++ b/app/javascript/mastodon/features/search/index.tsx @@ -53,7 +53,7 @@ const renderHashtags = (hashtags: HashtagType[]) => const renderStatuses = (statusIds: string[]) => hidePeek(statusIds).map((id) => ( - + )); type SearchType = 'all' | ApiSearchType; @@ -189,7 +189,7 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({ onClickMore={handleSelectStatuses} > {results.statuses.slice(0, INITIAL_DISPLAY).map((id) => ( - + ))} )} diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js index 471c7af4114f2d..166c3e3e3a6df0 100644 --- a/app/javascript/mastodon/selectors/index.js +++ b/app/javascript/mastodon/selectors/index.js @@ -14,7 +14,7 @@ const getStatusInputSelectors = [ (state, { id }) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]), (state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]), getFilters, - (_, { contextType }) => ['detailed', 'bookmarks', 'favourites'].includes(contextType), + (_, { contextType }) => ['detailed', 'bookmarks', 'favourites', 'search'].includes(contextType), ]; function getStatusResultFunction( From 2bd7c855b092539aaa3c7a5c08437ebd8d7815d9 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 27 Nov 2025 18:05:21 +0100 Subject: [PATCH 052/123] Contain tall videos in full-width video container (#37032) --- app/javascript/styles/mastodon/components.scss | 5 +---- app/javascript/styles_new/mastodon/components.scss | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index a775e126b75bd8..8aa5f757c8aaaa 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -7700,14 +7700,11 @@ a.status-card { overflow: hidden; position: relative; background: $base-shadow-color; - max-width: 100%; - max-height: max(400px, 60vh); - margin-inline: auto; + max-height: 460px; border-radius: 8px; box-sizing: border-box; color: $white; display: flex; - align-items: center; outline: 1px solid var(--media-outline-color); outline-offset: -1px; z-index: 2; diff --git a/app/javascript/styles_new/mastodon/components.scss b/app/javascript/styles_new/mastodon/components.scss index 6edf92490a7e44..0576255560d6ef 100644 --- a/app/javascript/styles_new/mastodon/components.scss +++ b/app/javascript/styles_new/mastodon/components.scss @@ -7493,13 +7493,10 @@ a.status-card { position: relative; color: var(--color-text-on-media); background: var(--color-bg-media); - max-width: 100%; - max-height: max(400px, 60vh); - margin-inline: auto; + max-height: 460px; border-radius: 8px; box-sizing: border-box; display: flex; - align-items: center; outline: 1px solid var(--color-border-media); outline-offset: -1px; z-index: 2; From 5f33ac208fde938d215dcd437c7964fb3c7802db Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 27 Nov 2025 18:05:56 +0100 Subject: [PATCH 053/123] Limit height of tall images in posts (#37035) --- app/javascript/styles/mastodon/components.scss | 7 +++++++ app/javascript/styles_new/mastodon/components.scss | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 8aa5f757c8aaaa..dbf67cec5103a3 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -7375,6 +7375,13 @@ a.status-card { grid-template-rows: 1fr 1fr; gap: 2px; + &--layout-1 { + // The size of single images is determined by their + // aspect-ratio, applied via inline style attribute + width: initial; + max-height: 460px; + } + &--layout-2 { & > .media-gallery__item:nth-child(1) { border-end-end-radius: 0; diff --git a/app/javascript/styles_new/mastodon/components.scss b/app/javascript/styles_new/mastodon/components.scss index 0576255560d6ef..13d19c5b8c98a4 100644 --- a/app/javascript/styles_new/mastodon/components.scss +++ b/app/javascript/styles_new/mastodon/components.scss @@ -7161,6 +7161,13 @@ a.status-card { grid-template-rows: 1fr 1fr; gap: 2px; + &--layout-1 { + // The size of single images is determined by their + // aspect-ratio, applied via inline style attribute + width: initial; + max-height: 460px; + } + &--layout-2 { & > .media-gallery__item:nth-child(1) { border-end-end-radius: 0; From 945ef5a8e185a82c921cc79f0d65c6d59ed98b2f Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 28 Nov 2025 09:58:34 +0100 Subject: [PATCH 054/123] Remove unused data from 2025 annual reports (#37033) --- app/lib/annual_report.rb | 11 +--- .../commonly_interacted_with_accounts.rb | 27 ---------- .../annual_report/most_reblogged_accounts.rb | 27 ---------- app/lib/annual_report/percentiles.rb | 37 -------------- app/lib/annual_report/source.rb | 4 -- app/lib/annual_report/top_hashtags.rb | 2 +- .../commonly_interacted_with_accounts_spec.rb | 50 ------------------- .../most_reblogged_accounts_spec.rb | 49 ------------------ spec/lib/annual_report/percentiles_spec.rb | 46 ----------------- spec/lib/annual_report_spec.rb | 9 ---- 10 files changed, 2 insertions(+), 260 deletions(-) delete mode 100644 app/lib/annual_report/commonly_interacted_with_accounts.rb delete mode 100644 app/lib/annual_report/most_reblogged_accounts.rb delete mode 100644 app/lib/annual_report/percentiles.rb delete mode 100644 spec/lib/annual_report/commonly_interacted_with_accounts_spec.rb delete mode 100644 spec/lib/annual_report/most_reblogged_accounts_spec.rb delete mode 100644 spec/lib/annual_report/percentiles_spec.rb diff --git a/app/lib/annual_report.rb b/app/lib/annual_report.rb index 275cc4b87d3cad..8fab2111ed6098 100644 --- a/app/lib/annual_report.rb +++ b/app/lib/annual_report.rb @@ -8,14 +8,11 @@ class AnnualReport AnnualReport::TypeDistribution, AnnualReport::TopStatuses, AnnualReport::MostUsedApps, - AnnualReport::CommonlyInteractedWithAccounts, AnnualReport::TimeSeries, AnnualReport::TopHashtags, - AnnualReport::MostRebloggedAccounts, - AnnualReport::Percentiles, ].freeze - SCHEMA = 1 + SCHEMA = 2 def self.table_name_prefix 'annual_report_' @@ -26,12 +23,6 @@ def initialize(account, year) @year = year end - def self.prepare(year) - SOURCES.each do |klass| - klass.prepare(year) - end - end - def generate return if GeneratedAnnualReport.exists?(account: @account, year: @year) diff --git a/app/lib/annual_report/commonly_interacted_with_accounts.rb b/app/lib/annual_report/commonly_interacted_with_accounts.rb deleted file mode 100644 index 219c30063a443a..00000000000000 --- a/app/lib/annual_report/commonly_interacted_with_accounts.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -class AnnualReport::CommonlyInteractedWithAccounts < AnnualReport::Source - MINIMUM_INTERACTIONS = 1 - SET_SIZE = 40 - - def generate - { - commonly_interacted_with_accounts: commonly_interacted_with_accounts.map do |(account_id, count)| - { - account_id: account_id.to_s, - count: count, - } - end, - } - end - - private - - def commonly_interacted_with_accounts - report_statuses.not_replying_to_account(@account).group(:in_reply_to_account_id).having(minimum_interaction_count).order(count_all: :desc).limit(SET_SIZE).count - end - - def minimum_interaction_count - Arel.star.count.gt(MINIMUM_INTERACTIONS) - end -end diff --git a/app/lib/annual_report/most_reblogged_accounts.rb b/app/lib/annual_report/most_reblogged_accounts.rb deleted file mode 100644 index df4dedb7344781..00000000000000 --- a/app/lib/annual_report/most_reblogged_accounts.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -class AnnualReport::MostRebloggedAccounts < AnnualReport::Source - MINIMUM_REBLOGS = 1 - SET_SIZE = 10 - - def generate - { - most_reblogged_accounts: most_reblogged_accounts.map do |(account_id, count)| - { - account_id: account_id.to_s, - count: count, - } - end, - } - end - - private - - def most_reblogged_accounts - report_statuses.only_reblogs.joins(reblog: :account).group(accounts: [:id]).having(minimum_reblog_count).order(count_all: :desc).limit(SET_SIZE).count - end - - def minimum_reblog_count - Arel.star.count.gt(MINIMUM_REBLOGS) - end -end diff --git a/app/lib/annual_report/percentiles.rb b/app/lib/annual_report/percentiles.rb deleted file mode 100644 index 2b0305c41554fd..00000000000000 --- a/app/lib/annual_report/percentiles.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -class AnnualReport::Percentiles < AnnualReport::Source - def self.prepare(year) - AnnualReport::StatusesPerAccountCount.connection.exec_query(<<~SQL.squish, nil, [year, Mastodon::Snowflake.id_at(DateTime.new(year).beginning_of_year), Mastodon::Snowflake.id_at(DateTime.new(year).end_of_year)]) - INSERT INTO annual_report_statuses_per_account_counts (year, account_id, statuses_count) - SELECT $1, account_id, count(*) - FROM statuses - WHERE id BETWEEN $2 AND $3 - AND (local OR uri IS NULL) - GROUP BY account_id - ON CONFLICT (year, account_id) DO NOTHING - SQL - end - - def generate - { - percentiles: { - statuses: 100.0 - ((total_with_fewer_statuses / (total_with_any_statuses + 1.0)) * 100), - }, - } - end - - private - - def statuses_created - @statuses_created ||= report_statuses.count - end - - def total_with_fewer_statuses - @total_with_fewer_statuses ||= AnnualReport::StatusesPerAccountCount.where(year: year).where(statuses_count: ...statuses_created).count - end - - def total_with_any_statuses - @total_with_any_statuses ||= AnnualReport::StatusesPerAccountCount.where(year: year).count - end -end diff --git a/app/lib/annual_report/source.rb b/app/lib/annual_report/source.rb index 86528731f51080..7f486553694a9a 100644 --- a/app/lib/annual_report/source.rb +++ b/app/lib/annual_report/source.rb @@ -8,10 +8,6 @@ def initialize(account, year) @year = year end - def self.prepare(_year) - # Use this method if any pre-calculations must be made before individual annual reports are generated - end - def generate raise NotImplementedError end diff --git a/app/lib/annual_report/top_hashtags.rb b/app/lib/annual_report/top_hashtags.rb index 42420a27707120..a775c29bac3298 100644 --- a/app/lib/annual_report/top_hashtags.rb +++ b/app/lib/annual_report/top_hashtags.rb @@ -2,7 +2,7 @@ class AnnualReport::TopHashtags < AnnualReport::Source MINIMUM_TAGGINGS = 1 - SET_SIZE = 40 + SET_SIZE = 5 def generate { diff --git a/spec/lib/annual_report/commonly_interacted_with_accounts_spec.rb b/spec/lib/annual_report/commonly_interacted_with_accounts_spec.rb deleted file mode 100644 index 12bf3810db6d60..00000000000000 --- a/spec/lib/annual_report/commonly_interacted_with_accounts_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe AnnualReport::CommonlyInteractedWithAccounts do - describe '#generate' do - subject { described_class.new(account, Time.zone.now.year) } - - context 'with an inactive account' do - let(:account) { Fabricate :account } - - it 'builds a report for an account' do - expect(subject.generate) - .to include( - commonly_interacted_with_accounts: be_an(Array).and(be_empty) - ) - end - end - - context 'with an active account' do - let(:account) { Fabricate :account } - - let(:other_account) { Fabricate :account } - let(:most_other_account) { Fabricate :account } - - before do - _other = Fabricate :status - - Fabricate :status, account: account, reply: true, in_reply_to_id: Fabricate(:status, account: other_account).id - Fabricate :status, account: account, reply: true, in_reply_to_id: Fabricate(:status, account: other_account).id - - Fabricate :status, account: account, reply: true, in_reply_to_id: Fabricate(:status, account: most_other_account).id - Fabricate :status, account: account, reply: true, in_reply_to_id: Fabricate(:status, account: most_other_account).id - Fabricate :status, account: account, reply: true, in_reply_to_id: Fabricate(:status, account: most_other_account).id - end - - it 'builds a report for an account' do - expect(subject.generate) - .to include( - commonly_interacted_with_accounts: eq( - [ - { account_id: most_other_account.id.to_s, count: 3 }, - { account_id: other_account.id.to_s, count: 2 }, - ] - ) - ) - end - end - end -end diff --git a/spec/lib/annual_report/most_reblogged_accounts_spec.rb b/spec/lib/annual_report/most_reblogged_accounts_spec.rb deleted file mode 100644 index 956549c32527f1..00000000000000 --- a/spec/lib/annual_report/most_reblogged_accounts_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe AnnualReport::MostRebloggedAccounts do - describe '#generate' do - subject { described_class.new(account, Time.zone.now.year) } - - context 'with an inactive account' do - let(:account) { Fabricate :account } - - it 'builds a report for an account' do - expect(subject.generate) - .to include( - most_reblogged_accounts: be_an(Array).and(be_empty) - ) - end - end - - context 'with an active account' do - let(:account) { Fabricate :account } - - let(:other_account) { Fabricate :account } - let(:most_other_account) { Fabricate :account } - - before do - _other = Fabricate :status - Fabricate :status, account: account, reblog: Fabricate(:status, account: other_account) - Fabricate :status, account: account, reblog: Fabricate(:status, account: other_account) - - Fabricate :status, account: account, reblog: Fabricate(:status, account: most_other_account) - Fabricate :status, account: account, reblog: Fabricate(:status, account: most_other_account) - Fabricate :status, account: account, reblog: Fabricate(:status, account: most_other_account) - end - - it 'builds a report for an account' do - expect(subject.generate) - .to include( - most_reblogged_accounts: eq( - [ - { account_id: most_other_account.id.to_s, count: 3 }, - { account_id: other_account.id.to_s, count: 2 }, - ] - ) - ) - end - end - end -end diff --git a/spec/lib/annual_report/percentiles_spec.rb b/spec/lib/annual_report/percentiles_spec.rb deleted file mode 100644 index 11df81cfb6c80a..00000000000000 --- a/spec/lib/annual_report/percentiles_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe AnnualReport::Percentiles do - describe '#generate' do - subject { described_class.new(account, year) } - - let(:year) { Time.zone.now.year } - - context 'with an inactive account' do - let(:account) { Fabricate :account } - - it 'builds a report for an account' do - described_class.prepare(year) - - expect(subject.generate) - .to include( - percentiles: include( - statuses: 100 - ) - ) - end - end - - context 'with an active account' do - let(:account) { Fabricate :account } - - before do - Fabricate.times 2, :status # Others as `account` - Fabricate.times 2, :status, account: account - end - - it 'builds a report for an account' do - described_class.prepare(year) - - expect(subject.generate) - .to include( - percentiles: include( - statuses: 50 - ) - ) - end - end - end -end diff --git a/spec/lib/annual_report_spec.rb b/spec/lib/annual_report_spec.rb index fa898d3ac551bd..bd4d0f33876a06 100644 --- a/spec/lib/annual_report_spec.rb +++ b/spec/lib/annual_report_spec.rb @@ -13,13 +13,4 @@ .to change(GeneratedAnnualReport, :count).by(1) end end - - describe '.prepare' do - before { Fabricate :status } - - it 'generates records from source class which prepare data' do - expect { described_class.prepare(Time.current.year) } - .to change(AnnualReport::StatusesPerAccountCount, :count).by(1) - end - end end From e0912c1729802dfd5042516ad3737aa7df20155f Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 28 Nov 2025 10:50:37 +0100 Subject: [PATCH 055/123] Fix `Card` component using incorrect `punycode` module (#37043) --- app/javascript/mastodon/features/status/components/card.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/status/components/card.tsx b/app/javascript/mastodon/features/status/components/card.tsx index d3de36a1e1ce47..c6e0c1655aa039 100644 --- a/app/javascript/mastodon/features/status/components/card.tsx +++ b/app/javascript/mastodon/features/status/components/card.tsx @@ -1,11 +1,11 @@ -import punycode from 'node:punycode'; - import { useCallback, useId, useState } from 'react'; import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; +import punycode from 'punycode/'; + import DescriptionIcon from '@/material-icons/400-24px/description-fill.svg?react'; import OpenInNewIcon from '@/material-icons/400-24px/open_in_new.svg?react'; import PlayArrowIcon from '@/material-icons/400-24px/play_arrow-fill.svg?react'; From f12f198f6149a61ee2f306c32656ce9f04d3fe9d Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 28 Nov 2025 11:05:54 +0100 Subject: [PATCH 056/123] Fix post navigation in single-column mode when Advanced UI is enabled (#37044) --- app/javascript/mastodon/features/ui/util/focusUtils.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/ui/util/focusUtils.ts b/app/javascript/mastodon/features/ui/util/focusUtils.ts index e46ede3553f8c4..a728a3c5eb293f 100644 --- a/app/javascript/mastodon/features/ui/util/focusUtils.ts +++ b/app/javascript/mastodon/features/ui/util/focusUtils.ts @@ -1,5 +1,3 @@ -import { initialState } from '@/mastodon/initial_state'; - interface FocusColumnOptions { index?: number; focusItem?: 'first' | 'first-visible'; @@ -14,7 +12,10 @@ export function focusColumn({ focusItem = 'first', }: FocusColumnOptions = {}) { // Skip the leftmost drawer in multi-column mode - const indexOffset = initialState?.meta.advanced_layout ? 1 : 0; + const isMultiColumnLayout = !!document.querySelector( + 'body.layout-multiple-columns', + ); + const indexOffset = isMultiColumnLayout ? 1 : 0; const column = document.querySelector( `.column:nth-child(${index + indexOffset})`, From 6b38352b1786d4d82949e47925e9ae314c8baa7c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Nov 2025 11:44:50 +0100 Subject: [PATCH 057/123] New Crowdin Translations (automated) (#37042) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/ja.json | 8 ++++++++ config/locales/simple_form.ja.yml | 3 +++ 2 files changed, 11 insertions(+) diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index fcf7e3134ea7d5..e693387b126005 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -28,6 +28,7 @@ "account.disable_notifications": "@{name}さんの投稿時の通知を停止", "account.domain_blocking": "ブロックしているドメイン", "account.edit_profile": "プロフィール編集", + "account.edit_profile_short": "編集", "account.enable_notifications": "@{name}さんの投稿時に通知", "account.endorse": "プロフィールで紹介する", "account.familiar_followers_many": "{name1}、{name2}、他{othersCount, plural, one {one other you know} other {# others you know}}人のユーザーにフォローされています", @@ -172,6 +173,8 @@ "column.edit_list": "リストを編集", "column.favourites": "お気に入り", "column.firehose": "リアルタイムフィード", + "column.firehose_local": "このサーバーのリアルタイムフィード", + "column.firehose_singular": "リアルタイムフィード", "column.follow_requests": "フォローリクエスト", "column.home": "ホーム", "column.list_members": "リストのメンバーを管理", @@ -251,6 +254,7 @@ "confirmations.remove_from_followers.message": "{name}さんはあなたをフォローしなくなります。本当によろしいですか?", "confirmations.remove_from_followers.title": "フォロワーを削除しますか?", "confirmations.revoke_quote.confirm": "投稿を削除", + "confirmations.revoke_quote.message": "この操作は元に戻せません。", "confirmations.revoke_quote.title": "投稿を削除しますか?", "confirmations.unblock.confirm": "ブロック解除", "confirmations.unblock.title": "@{name}さんのブロックを解除しますか?", @@ -477,6 +481,7 @@ "keyboard_shortcuts.home": "ホームタイムラインを開く", "keyboard_shortcuts.hotkey": "ホットキー", "keyboard_shortcuts.legend": "この一覧を表示", + "keyboard_shortcuts.load_more": "「もっと見る」ボタンに移動", "keyboard_shortcuts.local": "ローカルタイムラインを開く", "keyboard_shortcuts.mention": "メンション", "keyboard_shortcuts.muted": "ミュートしたユーザーのリストを開く", @@ -497,6 +502,7 @@ "keyboard_shortcuts.translate": "投稿を翻訳する", "keyboard_shortcuts.unfocus": "投稿の入力欄・検索欄から離れる", "keyboard_shortcuts.up": "カラム内一つ上に移動", + "learn_more_link.got_it": "了解", "learn_more_link.learn_more": "もっと見る", "lightbox.close": "閉じる", "lightbox.next": "次", @@ -611,6 +617,7 @@ "notification.moderation_warning.action_suspend": "あなたのアカウントは停止されました。", "notification.own_poll": "アンケートが終了しました", "notification.poll": "投票したアンケートが終了しました", + "notification.quoted_update": "あなたが引用した投稿を {name} が編集しました", "notification.reblog": "{name}さんがあなたの投稿をブーストしました", "notification.reblog.name_and_others_with_link": "{name}さんとほか{count, plural, other {#人}}がブーストしました", "notification.relationships_severance_event": "{name} との関係が失われました", @@ -753,6 +760,7 @@ "relative_time.minutes": "{number}分前", "relative_time.seconds": "{number}秒前", "relative_time.today": "今日", + "remove_quote_hint.button_label": "了解", "reply_indicator.attachments": "{count, plural, other {#件のメディア}}", "reply_indicator.cancel": "キャンセル", "reply_indicator.poll": "アンケート", diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 9025e16b18a61f..8c06f285102183 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -60,6 +60,7 @@ ja: setting_display_media_default: 閲覧注意としてマークされたメディアは隠す setting_display_media_hide_all: メディアを常に隠す setting_display_media_show_all: メディアを常に表示する + setting_emoji_style: 絵文字の表示方法。「オート」の場合、可能ならネイティブの絵文字を使用し、レガシーなブラウザではTwemojiで代替します。 setting_system_scrollbars_ui: Safari/Chromeベースのデスクトップブラウザーでのみ有効です setting_use_blurhash: ぼかしはメディアの色を元に生成されますが、細部は見えにくくなっています setting_use_pending_items: 新着があってもタイムラインを自動的にスクロールしないようにします @@ -225,6 +226,7 @@ ja: setting_default_privacy: 投稿の公開範囲 setting_default_quote_policy: 引用できるユーザー setting_default_sensitive: メディアを常に閲覧注意としてマークする + setting_delete_modal: 投稿を削除する前に警告する setting_disable_hover_cards: マウスオーバーでプロフィールをポップアップしない setting_disable_swiping: スワイプでの切り替えを無効にする setting_display_media: メディアの表示 @@ -234,6 +236,7 @@ ja: setting_emoji_style: 絵文字スタイル setting_expand_spoilers: 閲覧注意としてマークされた投稿を常に展開する setting_hide_network: 繋がりを隠す + setting_missing_alt_text_modal: 代替テキストなしでメディアを投稿する前に警告する setting_quick_boosting: クイックブーストの有効化 setting_reduce_motion: アニメーションの動きを減らす setting_system_font_ui: システムのデフォルトフォントを使う From f896bbac3b36632f943aaf073bfa050df680af99 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Fri, 28 Nov 2025 14:30:43 +0100 Subject: [PATCH 058/123] Draft API to create Collections (#37049) --- .../api/v1_alpha/collections_controller.rb | 29 ++++++++++ app/serializers/rest/collection_serializer.rb | 8 +++ config/initializers/doorkeeper.rb | 2 + config/routes/api.rb | 2 + .../requests/api/v1_alpha/collections_spec.rb | 54 +++++++++++++++++++ .../rest/collection_serializer_spec.rb | 30 +++++++++++ 6 files changed, 125 insertions(+) create mode 100644 app/controllers/api/v1_alpha/collections_controller.rb create mode 100644 app/serializers/rest/collection_serializer.rb create mode 100644 spec/requests/api/v1_alpha/collections_spec.rb create mode 100644 spec/serializers/rest/collection_serializer_spec.rb diff --git a/app/controllers/api/v1_alpha/collections_controller.rb b/app/controllers/api/v1_alpha/collections_controller.rb new file mode 100644 index 00000000000000..5583bb395da2d9 --- /dev/null +++ b/app/controllers/api/v1_alpha/collections_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class Api::V1Alpha::CollectionsController < Api::BaseController + rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| + render json: { error: ValidationErrorFormatter.new(e).as_json }, status: 422 + end + + before_action :check_feature_enabled + + before_action -> { doorkeeper_authorize! :write, :'write:collections' }, only: [:create] + + before_action :require_user! + + def create + @collection = CreateCollectionService.new.call(collection_params, current_user.account) + + render json: @collection, serializer: REST::CollectionSerializer + end + + private + + def collection_params + params.permit(:name, :description, :sensitive, :discoverable, :tag, account_ids: []) + end + + def check_feature_enabled + raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled? + end +end diff --git a/app/serializers/rest/collection_serializer.rb b/app/serializers/rest/collection_serializer.rb new file mode 100644 index 00000000000000..c03cc5385661a3 --- /dev/null +++ b/app/serializers/rest/collection_serializer.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class REST::CollectionSerializer < ActiveModel::Serializer + attributes :uri, :name, :description, :local, :sensitive, :discoverable, + :created_at, :updated_at + + belongs_to :account, serializer: REST::AccountSerializer +end diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 516db258dfa6d8..908acb55033fad 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -75,6 +75,7 @@ :'write:accounts', :'write:blocks', :'write:bookmarks', + :'write:collections', :'write:conversations', :'write:favourites', :'write:filters', @@ -89,6 +90,7 @@ :'read:accounts', :'read:blocks', :'read:bookmarks', + :'read:collections', :'read:favourites', :'read:filters', :'read:follows', diff --git a/config/routes/api.rb b/config/routes/api.rb index 34b2e255da655f..2fa3d4d83359ca 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -7,6 +7,8 @@ # Experimental JSON / REST API namespace :v1_alpha do resources :async_refreshes, only: :show + + resources :collections, only: [:create] end # JSON / REST API diff --git a/spec/requests/api/v1_alpha/collections_spec.rb b/spec/requests/api/v1_alpha/collections_spec.rb new file mode 100644 index 00000000000000..5f9c5e5f3475bc --- /dev/null +++ b/spec/requests/api/v1_alpha/collections_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Api::V1Alpha::Collections', feature: :collections do + include_context 'with API authentication', oauth_scopes: 'read:collections write:collections' + + describe 'POST /api/v1_alpha/collections' do + subject do + post '/api/v1_alpha/collections', headers: headers, params: params + end + + let(:params) { {} } + + it_behaves_like 'forbidden for wrong scope', 'read' + + context 'with valid params' do + let(:params) do + { + name: 'Low-traffic bots', + description: 'Really nice bots, please follow', + sensitive: '0', + discoverable: '1', + } + end + + it 'creates a collection and returns http success' do + expect do + subject + end.to change(Collection, :count).by(1) + + expect(response).to have_http_status(200) + end + end + + context 'with invalid params' do + it 'returns http unprocessable content and detailed errors' do + expect do + subject + end.to_not change(Collection, :count) + + expect(response).to have_http_status(422) + expect(response.parsed_body).to include({ + 'error' => a_hash_including({ + 'details' => a_hash_including({ + 'name' => [{ 'error' => 'ERR_BLANK', 'description' => "can't be blank" }], + 'description' => [{ 'error' => 'ERR_BLANK', 'description' => "can't be blank" }], + }), + }), + }) + end + end + end +end diff --git a/spec/serializers/rest/collection_serializer_spec.rb b/spec/serializers/rest/collection_serializer_spec.rb new file mode 100644 index 00000000000000..c498937b508f27 --- /dev/null +++ b/spec/serializers/rest/collection_serializer_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe REST::CollectionSerializer do + subject { serialized_record_json(collection, described_class) } + + let(:collection) do + Fabricate(:collection, + name: 'Exquisite follows', + description: 'Always worth a follow', + local: true, + sensitive: true, + discoverable: false) + end + + it 'includes the relevant attributes' do + expect(subject) + .to include( + 'account' => an_instance_of(Hash), + 'name' => 'Exquisite follows', + 'description' => 'Always worth a follow', + 'local' => true, + 'sensitive' => true, + 'discoverable' => false, + 'created_at' => match_api_datetime_format, + 'updated_at' => match_api_datetime_format + ) + end +end From 84ffb107c38f2e59642721ba9203440323a54f4f Mon Sep 17 00:00:00 2001 From: Echo Date: Fri, 28 Nov 2025 14:37:04 +0100 Subject: [PATCH 059/123] Adjust Chromatic to run conditionally (#37050) --- .github/workflows/chromatic.yml | 45 ++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index e0383b83bcc0a9..fbc0d9da0c2564 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -1,31 +1,51 @@ name: 'Chromatic' +permissions: + contents: read on: push: branches-ignore: - renovate/* - stable-* - paths: - - 'package.json' - - 'yarn.lock' - - '**/*.js' - - '**/*.jsx' - - '**/*.ts' - - '**/*.tsx' - - '**/*.css' - - '**/*.scss' - - '.github/workflows/chromatic.yml' jobs: + pathcheck: + name: Check for relevant changes + runs-on: ubuntu-latest + outputs: + changed: ${{ steps.filter.outputs.src }} + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + src: + - 'package.json' + - 'yarn.lock' + - '**/*.js' + - '**/*.jsx' + - '**/*.ts' + - '**/*.tsx' + - '**/*.css' + - '**/*.scss' + - '.github/workflows/chromatic.yml' + chromatic: name: Run Chromatic runs-on: ubuntu-latest - if: github.repository == 'mastodon/mastodon' + needs: pathcheck + if: github.repository == 'mastodon/mastodon' && needs.pathcheck.outputs.changed == 'true' steps: - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 + - name: Set up Javascript environment uses: ./.github/actions/setup-javascript @@ -35,7 +55,8 @@ jobs: - name: Run Chromatic uses: chromaui/action@v13 with: - # ⚠️ Make sure to configure a `CHROMATIC_PROJECT_TOKEN` repository secret projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} zip: true storybookBuildDir: 'storybook-static' + exitZeroOnChanges: false # Fail workflow if changes are found + autoAcceptChanges: 'main' # Auto-accept changes on main branch only From 37ccffa95a30772b55e3f18d486d699ee6c5f9e8 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 28 Nov 2025 15:45:38 +0100 Subject: [PATCH 060/123] Fix current item in pagination not highlighted with new theme tokens (#37054) --- app/javascript/styles_new/mastodon/accounts.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/styles_new/mastodon/accounts.scss b/app/javascript/styles_new/mastodon/accounts.scss index dab604be2ad40b..d1c35e3f9ec067 100644 --- a/app/javascript/styles_new/mastodon/accounts.scss +++ b/app/javascript/styles_new/mastodon/accounts.scss @@ -113,8 +113,8 @@ } .current { - color: var(--color-bg-inverted); - background: var(--color-text-on-inverted); + color: var(--color-bg-primary); + background: var(--color-text-primary); border-radius: 100px; cursor: default; margin: 0 10px; From 92278796c3d0fa43c08bd59f66815b196fb273a3 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 28 Nov 2025 16:58:46 +0100 Subject: [PATCH 061/123] Remove `theme_tokens` feature flag & make new styles the default (#37056) --- .prettierignore | 1 - app/helpers/theme_helper.rb | 10 +- app/javascript/styles/application.scss | 30 +- .../{styles_new => styles}/common.scss | 0 app/javascript/styles/contrast.scss | 9 +- app/javascript/styles/contrast/diff.scss | 24 +- app/javascript/styles/contrast/variables.scss | 25 - app/javascript/styles/mastodon-light.scss | 11 +- .../styles/mastodon-light/css_variables.scss | 235 +- .../styles/mastodon-light/diff.scss | 530 - .../styles/mastodon-light/variables.scss | 46 - .../styles/mastodon/_functions.scss | 31 - app/javascript/styles/mastodon/_mixins.scss | 19 +- .../mastodon/_theme_utils.scss | 0 .../styles/mastodon/_variables.scss | 88 - app/javascript/styles/mastodon/about.scss | 35 +- app/javascript/styles/mastodon/accounts.scss | 113 +- app/javascript/styles/mastodon/admin.scss | 376 +- .../styles/mastodon/annual_reports.scss | 2 +- app/javascript/styles/mastodon/basics.scss | 20 +- app/javascript/styles/mastodon/branding.scss | 2 +- .../styles/mastodon/components.scss | 2067 ++- .../styles/mastodon/containers.scss | 10 +- .../styles/mastodon/css_variables.scss | 249 +- app/javascript/styles/mastodon/dashboard.scss | 25 +- .../styles/mastodon/emoji_picker.scss | 31 +- app/javascript/styles/mastodon/forms.scss | 269 +- app/javascript/styles/mastodon/modal.scss | 21 +- app/javascript/styles/mastodon/polls.scss | 45 +- app/javascript/styles/mastodon/reset.scss | 32 +- app/javascript/styles/mastodon/rtl.scss | 7 - app/javascript/styles/mastodon/tables.scss | 74 +- app/javascript/styles/mastodon/widgets.scss | 35 +- app/javascript/styles_new/application.scss | 7 - app/javascript/styles_new/contrast.scss | 8 - app/javascript/styles_new/contrast/diff.scss | 54 - .../styles_new/entrypoints/inert.scss | 14 - .../styles_new/entrypoints/mailer.scss | 1030 -- app/javascript/styles_new/fonts/inter.scss | 8 - .../styles_new/fonts/roboto-mono.scss | 13 - app/javascript/styles_new/fonts/roboto.scss | 55 - app/javascript/styles_new/mastodon-light.scss | 9 - .../mastodon-light/css_variables.scss | 214 - .../styles_new/mastodon/_mixins.scss | 45 - .../styles_new/mastodon/_variables.scss | 27 - app/javascript/styles_new/mastodon/about.scss | 130 - .../styles_new/mastodon/accessibility.scss | 13 - .../styles_new/mastodon/accounts.scss | 411 - app/javascript/styles_new/mastodon/admin.scss | 2177 --- .../styles_new/mastodon/annual_reports.scss | 342 - .../styles_new/mastodon/basics.scss | 300 - .../styles_new/mastodon/branding.scss | 5 - .../styles_new/mastodon/components.scss | 11441 ---------------- .../styles_new/mastodon/containers.scss | 166 - .../styles_new/mastodon/css_variables.scss | 228 - .../styles_new/mastodon/dashboard.scss | 120 - .../styles_new/mastodon/emoji_picker.scss | 248 - app/javascript/styles_new/mastodon/forms.scss | 1449 -- app/javascript/styles_new/mastodon/lists.scss | 19 - app/javascript/styles_new/mastodon/modal.scss | 53 - app/javascript/styles_new/mastodon/polls.scss | 232 - app/javascript/styles_new/mastodon/reset.scss | 58 - .../styles_new/mastodon/rich_text.scss | 116 - app/javascript/styles_new/mastodon/rtl.scss | 50 - .../styles_new/mastodon/tables.scss | 375 - .../styles_new/mastodon/widgets.scss | 178 - config/vite/plugin-mastodon-themes.ts | 29 +- stylelint.config.js | 3 +- 68 files changed, 1902 insertions(+), 22197 deletions(-) rename app/javascript/{styles_new => styles}/common.scss (100%) delete mode 100644 app/javascript/styles/contrast/variables.scss delete mode 100644 app/javascript/styles/mastodon-light/diff.scss delete mode 100644 app/javascript/styles/mastodon-light/variables.scss delete mode 100644 app/javascript/styles/mastodon/_functions.scss rename app/javascript/{styles_new => styles}/mastodon/_theme_utils.scss (100%) delete mode 100644 app/javascript/styles_new/application.scss delete mode 100644 app/javascript/styles_new/contrast.scss delete mode 100644 app/javascript/styles_new/contrast/diff.scss delete mode 100644 app/javascript/styles_new/entrypoints/inert.scss delete mode 100644 app/javascript/styles_new/entrypoints/mailer.scss delete mode 100644 app/javascript/styles_new/fonts/inter.scss delete mode 100644 app/javascript/styles_new/fonts/roboto-mono.scss delete mode 100644 app/javascript/styles_new/fonts/roboto.scss delete mode 100644 app/javascript/styles_new/mastodon-light.scss delete mode 100644 app/javascript/styles_new/mastodon-light/css_variables.scss delete mode 100644 app/javascript/styles_new/mastodon/_mixins.scss delete mode 100644 app/javascript/styles_new/mastodon/_variables.scss delete mode 100644 app/javascript/styles_new/mastodon/about.scss delete mode 100644 app/javascript/styles_new/mastodon/accessibility.scss delete mode 100644 app/javascript/styles_new/mastodon/accounts.scss delete mode 100644 app/javascript/styles_new/mastodon/admin.scss delete mode 100644 app/javascript/styles_new/mastodon/annual_reports.scss delete mode 100644 app/javascript/styles_new/mastodon/basics.scss delete mode 100644 app/javascript/styles_new/mastodon/branding.scss delete mode 100644 app/javascript/styles_new/mastodon/components.scss delete mode 100644 app/javascript/styles_new/mastodon/containers.scss delete mode 100644 app/javascript/styles_new/mastodon/css_variables.scss delete mode 100644 app/javascript/styles_new/mastodon/dashboard.scss delete mode 100644 app/javascript/styles_new/mastodon/emoji_picker.scss delete mode 100644 app/javascript/styles_new/mastodon/forms.scss delete mode 100644 app/javascript/styles_new/mastodon/lists.scss delete mode 100644 app/javascript/styles_new/mastodon/modal.scss delete mode 100644 app/javascript/styles_new/mastodon/polls.scss delete mode 100644 app/javascript/styles_new/mastodon/reset.scss delete mode 100644 app/javascript/styles_new/mastodon/rich_text.scss delete mode 100644 app/javascript/styles_new/mastodon/rtl.scss delete mode 100644 app/javascript/styles_new/mastodon/tables.scss delete mode 100644 app/javascript/styles_new/mastodon/widgets.scss diff --git a/.prettierignore b/.prettierignore index bd382506695bc3..098dac6717786f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -68,7 +68,6 @@ docker-compose.override.yml # Ignore vendored CSS reset app/javascript/styles/mastodon/reset.scss -app/javascript/styles_new/mastodon/reset.scss # Ignore Javascript pending https://github.com/mastodon/mastodon/pull/23631 *.js diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb index 32734b93af9fac..00b4a6d2b3f834 100644 --- a/app/helpers/theme_helper.rb +++ b/app/helpers/theme_helper.rb @@ -4,11 +4,11 @@ module ThemeHelper def theme_style_tags(theme) if theme == 'system' ''.html_safe.tap do |tags| - tags << vite_stylesheet_tag(theme_path_for('mastodon-light'), type: :virtual, media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') - tags << vite_stylesheet_tag(theme_path_for('default'), type: :virtual, media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous') + tags << vite_stylesheet_tag('themes/mastodon-light', type: :virtual, media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') + tags << vite_stylesheet_tag('themes/default', type: :virtual, media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous') end else - vite_stylesheet_tag theme_path_for(theme), type: :virtual, media: 'all', crossorigin: 'anonymous' + vite_stylesheet_tag "themes/#{theme}", type: :virtual, media: 'all', crossorigin: 'anonymous' end end @@ -53,8 +53,4 @@ def cached_custom_css_digest def theme_color_for(theme) theme == 'mastodon-light' ? Themes::THEME_COLORS[:light] : Themes::THEME_COLORS[:dark] end - - def theme_path_for(theme) - Mastodon::Feature.theme_tokens_enabled? ? "themes/#{theme}_theme_tokens" : "themes/#{theme}" - end end diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index b328d8ee34229c..e16e5368e7d3c8 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -1,27 +1,7 @@ -@use 'mastodon/functions'; -@use 'mastodon/mixins'; -@use 'mastodon/variables'; @use 'mastodon/css_variables'; -@use 'fonts/roboto'; -@use 'fonts/roboto-mono'; +@use 'mastodon/variables'; +@use 'common'; -@use 'mastodon/reset'; -@use 'mastodon/basics'; -@use 'mastodon/branding'; -@use 'mastodon/containers'; -@use 'mastodon/lists'; -@use 'mastodon/widgets'; -@use 'mastodon/forms'; -@use 'mastodon/accounts'; -@use 'mastodon/components'; -@use 'mastodon/polls'; -@use 'mastodon/modal'; -@use 'mastodon/emoji_picker'; -@use 'mastodon/annual_reports'; -@use 'mastodon/about'; -@use 'mastodon/tables'; -@use 'mastodon/admin'; -@use 'mastodon/dashboard'; -@use 'mastodon/rtl'; -@use 'mastodon/accessibility'; -@use 'mastodon/rich_text'; +html { + color-scheme: dark; +} diff --git a/app/javascript/styles_new/common.scss b/app/javascript/styles/common.scss similarity index 100% rename from app/javascript/styles_new/common.scss rename to app/javascript/styles/common.scss diff --git a/app/javascript/styles/contrast.scss b/app/javascript/styles/contrast.scss index 367be051f1a3b7..af73c88fef6cdd 100644 --- a/app/javascript/styles/contrast.scss +++ b/app/javascript/styles/contrast.scss @@ -1,3 +1,8 @@ -@use 'contrast/variables'; -@use 'application'; +@use 'mastodon/css_variables'; +@use 'mastodon/variables'; +@use 'common'; @use 'contrast/diff'; + +html { + color-scheme: dark; +} diff --git a/app/javascript/styles/contrast/diff.scss b/app/javascript/styles/contrast/diff.scss index 0adf8802eeb38d..f809c7cdc3a0bc 100644 --- a/app/javascript/styles/contrast/diff.scss +++ b/app/javascript/styles/contrast/diff.scss @@ -1,4 +1,16 @@ -@use '../mastodon/variables' as *; +:root { + /* TEXT TOKENS */ + + --color-text-primary: var(--color-grey-50); + --color-text-secondary: var(--color-grey-300); + --color-text-tertiary: var(--color-grey-400); + --color-text-brand: var(--color-indigo-300); + --color-text-status-links: var(--color-text-brand); + + /* BORDER TOKENS */ + + --border-strength-primary: 18%; +} .status__content a, .reply-indicator__content a, @@ -31,16 +43,6 @@ } } -.status__content a, -.reply-indicator__content a, -.edit-indicator__content a { - color: $highlight-text-color; -} - -.report-dialog-modal__textarea::placeholder { - color: $inverted-text-color; -} - .link-button:disabled { cursor: not-allowed; diff --git a/app/javascript/styles/contrast/variables.scss b/app/javascript/styles/contrast/variables.scss deleted file mode 100644 index e057352ba3851a..00000000000000 --- a/app/javascript/styles/contrast/variables.scss +++ /dev/null @@ -1,25 +0,0 @@ -@use '../mastodon/functions' as *; - -// Dependent colors -$black: #000; - -$classic-base-color: hsl(240deg, 16%, 19%); -$classic-primary-color: hsl(240deg, 29%, 70%); -$classic-secondary-color: hsl(255deg, 25%, 88%); -$classic-highlight-color: hsl(240deg, 100%, 69%); - -$ui-base-color: $classic-base-color !default; -$ui-primary-color: $classic-primary-color !default; -$ui-secondary-color: $classic-secondary-color !default; -$ui-highlight-color: $classic-highlight-color !default; - -@use '../mastodon/variables' with ( - $darker-text-color: lighten($ui-primary-color, 20%), - $dark-text-color: lighten($ui-primary-color, 12%), - $secondary-text-color: lighten($ui-secondary-color, 6%), - $highlight-text-color: lighten($ui-highlight-color, 10%), - $action-button-color: lighten($ui-base-color, 50%), - $inverted-text-color: $black, - $lighter-text-color: darken($ui-base-color, 6%), - $light-text-color: $classic-primary-color -); diff --git a/app/javascript/styles/mastodon-light.scss b/app/javascript/styles/mastodon-light.scss index b530616a3c3808..494efdbbde99da 100644 --- a/app/javascript/styles/mastodon-light.scss +++ b/app/javascript/styles/mastodon-light.scss @@ -1,4 +1,9 @@ -@use 'mastodon-light/variables'; @use 'mastodon-light/css_variables'; -@use 'application'; -@use 'mastodon-light/diff'; +@use 'mastodon/variables' with ( + $emojis-requiring-inversion: 'chains' +); +@use 'common'; + +html { + color-scheme: light; +} diff --git a/app/javascript/styles/mastodon-light/css_variables.scss b/app/javascript/styles/mastodon-light/css_variables.scss index d96c368b251eae..70745ec071ad8c 100644 --- a/app/javascript/styles/mastodon-light/css_variables.scss +++ b/app/javascript/styles/mastodon-light/css_variables.scss @@ -1,23 +1,214 @@ -@use 'sass:color'; -@use '../mastodon/variables' as *; -@use 'variables' as *; -@use '../mastodon/functions' as *; - -body { - --dropdown-border-color: hsl(240deg, 25%, 88%); - --dropdown-background-color: #fff; - --modal-border-color: hsl(240deg, 25%, 88%); - --modal-background-color: var(--background-color); - --background-border-color: hsl(240deg, 25%, 88%); - --background-color: #fff; - --background-color-tint: rgba(255, 255, 255, 80%); - --background-filter: blur(10px); - --surface-variant-background-color: #f1ebfb; - --surface-border-color: #cac4d0; - --on-surface-color: #{color.adjust($ui-base-color, $alpha: -0.65)}; - --rich-text-container-color: rgba(255, 216, 231, 100%); - --rich-text-text-color: rgba(114, 47, 83, 100%); - --rich-text-decorations-color: rgba(255, 175, 212, 100%); - --input-placeholder-color: #{color.adjust($dark-text-color, $alpha: -0.5)}; - --input-background-color: #{darken($ui-base-color, 10%)}; +@use '../mastodon/theme_utils' as utils; + +:root { + --color-black: #000; + --color-grey-950: #181821; + --color-grey-800: #292938; + --color-grey-700: #444664; + --color-grey-600: #545778; + --color-grey-500: #696d91; + --color-grey-400: #8b8dac; + --color-grey-300: #b4b6cb; + --color-grey-200: #d8d9e3; + --color-grey-100: #f0f0f5; + --color-grey-50: #f0f1ff; + --color-white: #fff; + --color-indigo-600: #6147e6; + --color-indigo-400: #8886ff; + --color-indigo-300: #a5abfd; + --color-indigo-200: #c8cdfe; + --color-indigo-100: #e0e3ff; + --color-indigo-50: #f0f1ff; + --color-red-500: #ff637e; + --color-red-600: #ec003f; + --color-yellow-400: #ffb900; + --color-yellow-600: #e17100; + --color-green-400: #05df72; + --color-green-600: #00a63e; + + /* TEXT TOKENS */ + + --color-text-primary: var(--color-grey-950); + --color-text-secondary: var(--color-grey-600); + --color-text-tertiary: var(--color-grey-500); + --color-text-on-inverted: var(--color-white); + --color-text-brand: var(--color-indigo-600); + --color-text-brand-soft: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-brand) + ); + --color-text-on-brand-base: var(--color-white); + --color-text-error: var(--color-red-600); + --color-text-on-error-base: var(--color-white); + --color-text-warning: var(--color-yellow-600); + --color-text-on-warning-base: var(--color-white); + --color-text-success: var(--color-green-600); + --color-text-on-success-base: var(--color-white); + --color-text-disabled: var(--color-grey-300); + --color-text-on-disabled: var(--color-grey-200); + --color-text-bookmark-highlight: var(--color-text-error); + --color-text-favourite-highlight: var(--color-text-warning); + --color-text-on-media: var(--color-white); + --color-text-status-links: var(--color-text-brand); + + /* BACKGROUND TOKENS */ + + // Neutrals + --color-bg-primary: var(--color-white); + --overlay-strength-secondary: 5%; + --color-bg-secondary-base: var(--color-grey-600); + --color-bg-secondary: #{color-mix( + in oklab, + var(--color-bg-primary), + var(--color-bg-secondary-base) var(--overlay-strength-secondary) + )}; + --color-bg-secondary-solid: #{color-mix( + in srgb, + var(--color-bg-primary), + var(--color-bg-secondary-base) var(--overlay-strength-secondary) + )}; + --color-bg-tertiary: #{color-mix( + in oklab, + var(--color-bg-primary), + var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary)) + )}; + + // Utility + --color-bg-ambient: var(--color-bg-primary); + --color-bg-elevated: var(--color-bg-primary); + --color-bg-inverted: var(--color-grey-950); + --color-bg-media-base: var(--color-black); + --color-bg-media-strength: 65%; + --color-bg-media: #{utils.css-alpha( + var(--color-bg-media-base), + var(--color-bg-media-strength) + )}; + --color-bg-overlay: var(--color-bg-primary); + --color-bg-disabled: var(--color-grey-400); + + // Brand + --overlay-strength-brand: 8%; + --color-bg-brand-base: var(--color-indigo-600); + --color-bg-brand-base-hover: color-mix( + in oklab, + var(--color-bg-brand-base), + black var(--overlay-strength-brand) + ); + --color-bg-brand-soft: #{utils.css-alpha( + var(--color-bg-brand-base), + calc(var(--overlay-strength-brand) * 1.5) + )}; + --color-bg-brand-softer: #{utils.css-alpha( + var(--color-bg-brand-base), + var(--overlay-strength-brand) + )}; + + // Error + --overlay-strength-error: 12%; + --color-bg-error-base: var(--color-red-600); + --color-bg-error-base-hover: color-mix( + in oklab, + var(--color-bg-error-base), + black var(--overlay-strength-error) + ); + --color-bg-error-soft: #{utils.css-alpha( + var(--color-bg-error-base), + calc(var(--overlay-strength-error) * 1.5) + )}; + --color-bg-error-softer: #{utils.css-alpha( + var(--color-bg-error-base), + var(--overlay-strength-error) + )}; + + // Warning + --overlay-strength-warning: 10%; + --color-bg-warning-base: var(--color-yellow-600); + --color-bg-warning-base-hover: color-mix( + in oklab, + var(--color-bg-warning-base), + black var(--overlay-strength-warning) + ); + --color-bg-warning-soft: #{utils.css-alpha( + var(--color-bg-warning-base), + calc(var(--overlay-strength-warning) * 1.5) + )}; + --color-bg-warning-softer: #{utils.css-alpha( + var(--color-bg-warning-base), + var(--overlay-strength-warning) + )}; + + // Success + --overlay-strength-success: 15%; + --color-bg-success-base: var(--color-green-600); + --color-bg-success-base-hover: color-mix( + in oklab, + var(--color-bg-success-base), + black var(--overlay-strength-success) + ); + --color-bg-success-soft: #{utils.css-alpha( + var(--color-bg-success-base), + calc(var(--overlay-strength-success) * 1.5) + )}; + --color-bg-success-softer: #{utils.css-alpha( + var(--color-bg-success-base), + var(--overlay-strength-success) + )}; + + /* BORDER TOKENS */ + + --border-strength-primary: 15%; + --color-border-primary: color-mix( + in oklab, + var(--color-bg-primary), + var(--color-grey-950) var(--border-strength-primary) + ); + --color-border-media: rgb(252 248 255 / 15%); + --color-border-on-bg-secondary: var(--color-grey-200); + --color-border-on-bg-brand-softer: var(--color-indigo-200); + --color-border-on-bg-error-softer: #{utils.css-alpha( + var(--color-text-error), + 50% + )}; + --color-border-on-bg-warning-softer: #{utils.css-alpha( + var(--color-text-warning), + 50% + )}; + --color-border-on-bg-success-softer: #{utils.css-alpha( + var(--color-text-success), + 50% + )}; + --color-border-on-bg-inverted: var(--color-border-primary); + + /* SHADOW TOKENS */ + + --shadow-strength-primary: 30%; + --color-shadow-primary: #{utils.css-alpha( + var(--color-black), + var(--shadow-strength-primary) + )}; + --dropdown-shadow: + 0 20px 25px -5px var(--color-shadow-primary), + 0 8px 10px -6px var(--color-shadow-primary); + --overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary)); + + /* GRAPHS/CHARTS TOKENS */ + + --color-graph-primary-stroke: var(--color-text-brand); + --color-graph-primary-fill: var(--color-bg-brand-softer); + --color-graph-warning-stroke: var(--color-text-warning); + --color-graph-warning-fill: var(--color-bg-warning-softer); + --color-graph-disabled-stroke: var(--color-text-disabled); + --color-graph-disabled-fill: var(--color-bg-disabled); + + /* LEGACY TOKENS */ + + --rich-text-container-color: rgb(255 216 231 / 100%); + --rich-text-text-color: rgb(114 47 83 / 100%); + --rich-text-decorations-color: rgb(255 175 212 / 100%); + + /* MISCELLANEOUS */ + + --outline-focus-default: 2px solid var(--color-text-brand); + --avatar-border-radius: 8px; } diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss deleted file mode 100644 index 665271c5354037..00000000000000 --- a/app/javascript/styles/mastodon-light/diff.scss +++ /dev/null @@ -1,530 +0,0 @@ -// Notes! -// Sass color functions, "darken" and "lighten" are automatically replaced. -@use 'sass:color'; -@use '../mastodon/functions' as *; -@use '../mastodon/variables' as *; - -.simple_form .button.button-tertiary { - color: $highlight-text-color; - - &:hover, - &:focus, - &:active { - color: $white; - } -} - -.status-card__actions button, -.status-card__actions a { - color: color.change($white, $alpha: 0.8); - - &:hover, - &:active, - &:focus { - color: $white; - } -} - -// Change default background colors of columns -.interaction-modal { - background: $white; - border: 1px solid var(--background-border-color); -} - -.rules-list li::before { - background: $ui-highlight-color; -} - -.directory__card__img { - background: lighten($ui-base-color, 12%); -} - -.account__header { - background: $white; -} - -.column-header__button.active { - color: $ui-highlight-color; - - &:hover, - &:active, - &:focus { - color: $ui-highlight-color; - } -} - -.icon-button:disabled { - color: darken($action-button-color, 25%); -} - -.getting-started__footer a { - color: $ui-secondary-color; - text-decoration: underline; -} - -.confirmation-modal__secondary-button, -.confirmation-modal__cancel-button, -.mute-modal__cancel-button, -.block-modal__cancel-button { - color: lighten($ui-base-color, 26%); - - &:hover, - &:focus, - &:active { - color: $primary-text-color; - } -} - -.getting-started .navigation-bar { - border-top: 1px solid var(--background-border-color); - border-bottom: 1px solid var(--background-border-color); - - @media screen and (max-width: $no-gap-breakpoint) { - border-top: 0; - } -} - -.search__input, -.search__popout, -.setting-text, -.report-dialog-modal__textarea, -.audio-player { - border: 1px solid var(--background-border-color); -} - -.report-dialog-modal .dialog-option .poll__input { - color: $white; -} - -.upload-progress__backdrop { - background: $ui-base-color; -} - -// Change the background colors of statuses -.focusable:focus-visible { - background: lighten($white, 4%); -} - -.account-gallery__item a { - background-color: $ui-base-color; -} - -// Change the text colors on inverted background -.actions-modal ul li:not(:empty) a.active, -.actions-modal ul li:not(:empty) a.active button, -.actions-modal ul li:not(:empty) a:active, -.actions-modal ul li:not(:empty) a:active button, -.actions-modal ul li:not(:empty) a:focus, -.actions-modal ul li:not(:empty) a:focus button, -.actions-modal ul li:not(:empty) a:hover, -.actions-modal ul li:not(:empty) a:hover button, -.simple_form button:not(.button, .link-button) { - color: $white; -} - -.compare-history-modal .report-modal__target, -.report-dialog-modal .poll__option.dialog-option { - border-bottom-color: lighten($ui-base-color, 4%); -} - -.report-dialog-modal__container { - border-top-color: lighten($ui-base-color, 4%); -} - -.dialog-modal__content__preview { - background: #fff; - border-bottom: 1px solid var(--modal-border-color); -} - -.reactions-bar__item:hover, -.reactions-bar__item:focus, -.reactions-bar__item:active { - background-color: $ui-base-color; -} - -.reactions-bar__item.active { - background-color: color.mix($white, $ui-highlight-color, 80%); - border-color: color.mix( - lighten($ui-base-color, 8%), - $ui-highlight-color, - 80% - ); -} - -.media-modal__overlay .picture-in-picture__footer { - border: 0; -} - -.picture-in-picture__header { - border-bottom: 0; -} - -.announcements, -.picture-in-picture__footer { - border-top: 0; -} - -.icon-with-badge__badge { - border-color: $white; - color: $white; -} - -.column-settings__hashtags .column-select__option { - color: $white; -} - -.dashboard__quick-access, -.focal-point__preview strong, -.admin-wrapper .content__heading__tabs a.selected { - color: $white; -} - -.flash-message.warning { - color: lighten($gold-star, 16%); -} - -.boost-modal__action-bar, -.confirmation-modal__action-bar, -.mute-modal__action-bar, -.block-modal__action-bar, -.onboarding-modal__paginator, -.error-modal__footer { - background: darken($ui-base-color, 6%); - - .onboarding-modal__nav, - .error-modal__nav { - &:hover, - &:focus, - &:active { - background-color: darken($ui-base-color, 12%); - } - } -} - -.display-case__case { - background: $white; -} - -.embed-modal .embed-modal__container .embed-modal__html { - background: $white; - border: 1px solid var(--background-border-color); - - &:focus { - border-color: lighten($ui-base-color, 12%); - background: $white; - } -} - -.react-toggle-track { - background: $ui-primary-color; -} - -.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track { - background: lighten($ui-primary-color, 10%); -} - -.react-toggle.react-toggle--checked:hover:not(.react-toggle--disabled) - .react-toggle-track { - background: lighten($ui-highlight-color, 10%); -} - -// Change the default color used for the text in an empty column or on the error column -.empty-column-indicator, -.error-column { - color: $primary-text-color; - background: $white; -} - -// Change the default colors used on some parts of the profile pages -.activity-stream-tabs { - background: $white; - border-bottom-color: lighten($ui-base-color, 8%); -} - -.nothing-here, -.page-header, -.directory__tag > a, -.directory__tag > div { - background: $white; - border: 1px solid var(--background-border-color); -} - -.picture-in-picture-placeholder { - background: $white; - border-color: lighten($ui-base-color, 8%); - color: lighten($ui-base-color, 8%); -} - -.directory__tag > a { - &:hover, - &:active, - &:focus { - background: $ui-base-color; - } -} - -.batch-table { - &__toolbar, - &__row, - .nothing-here { - border-color: lighten($ui-base-color, 8%); - } -} - -.accounts-grid { - .account-grid-card { - .controls { - .icon-button { - color: $darker-text-color; - } - } - - .name { - a { - color: $primary-text-color; - } - } - - .username { - color: $darker-text-color; - } - - .account__header__content { - color: $primary-text-color; - } - } -} - -.simple_form { - .warning { - box-shadow: none; - background: color.change($error-red, $alpha: 0.5); - text-shadow: none; - } - - .recommended { - border-color: $ui-highlight-color; - color: $ui-highlight-color; - background-color: color.change($ui-highlight-color, $alpha: 0.1); - } - - input[type='text'], - input[type='number'], - input[type='email'], - input[type='password'], - input[type='url'], - input[type='datetime-local'], - textarea { - background: darken($ui-base-color, 10%); - } - - select { - background: darken($ui-base-color, 10%) - url("data:image/svg+xml;utf8,") - no-repeat right 8px center / auto 14px; - } -} - -.compose-form .compose-form__warning { - border-color: $ui-highlight-color; - background-color: color.change($ui-highlight-color, $alpha: 0.1); - - &, - a { - color: $ui-highlight-color; - } -} - -.status__content, -.reply-indicator__content { - a { - color: $highlight-text-color; - } -} - -.notification__filter-bar button.active::after, -.account__section-headline a.active::after { - border-color: transparent transparent $white; -} - -.activity-stream, -.nothing-here, -.directory__tag > a, -.directory__tag > div, -.card > a, -.page-header, -.compose-form, -.compose-form__warning { - box-shadow: none; -} - -.card { - &__img { - background: darken($ui-base-color, 10%); - } - - & > a { - &:hover, - &:active, - &:focus { - .card__bar { - background: darken($ui-base-color, 10%); - } - } - } -} - -.status__wrapper-direct { - background-color: color.change($ui-highlight-color, $alpha: 0.1); - - &:focus { - background-color: color.change($ui-highlight-color, $alpha: 0.15); - } -} - -.compose-form__actions .icon-button.active, -.dropdown-button.active, -.language-dropdown__dropdown__results__item:focus, -.language-dropdown__dropdown__results__item.active, -.language-dropdown__dropdown__results__item:focus - .language-dropdown__dropdown__results__item__common-name, -.language-dropdown__dropdown__results__item.active - .language-dropdown__dropdown__results__item__common-name { - color: $white; -} - -.privacy-dropdown__option, -.visibility-dropdown__option { - &:focus, - &.active { - --dropdown-text-color: #{$white}; - } -} - -.compose-form .spoiler-input__input { - color: lighten($ui-highlight-color, 8%); -} - -.emoji-mart-search input, -.language-dropdown__dropdown .emoji-mart-search input, -.poll__option input[type='text'] { - background: darken($ui-base-color, 10%); -} - -.dropdown-button.warning { - border-color: #b3261e; - color: #b3261e; - - &.active { - background-color: #f9dedc; - } -} - -.search__popout__menu__item { - &:hover, - &:active, - &:focus, - &.active { - color: $white; - - mark, - .icon-button { - color: $white; - } - } -} - -.inline-follow-suggestions { - background-color: color.change($ui-highlight-color, $alpha: 0.1); - border-bottom-color: color.change($ui-highlight-color, $alpha: 0.3); - - &.focusable:focus-visible { - background: color.change($ui-highlight-color, $alpha: 0.1); - } -} - -.inline-follow-suggestions__body__scrollable__card { - background: $white; -} - -.inline-follow-suggestions__body__scroll-button__icon { - color: $white; -} - -a.sparkline { - &:hover, - &:focus, - &:active { - background: darken($ui-base-color, 10%); - } -} - -.dashboard__counters { - & > div { - & > a { - &:hover, - &:focus, - &:active { - background: darken($ui-base-color, 10%); - } - } - } -} - -.directory { - &__tag { - & > a { - &:hover, - &:focus, - &:active { - background: darken($ui-base-color, 10%); - } - } - } -} - -.strike-entry { - &:hover, - &:focus, - &:active { - background: darken($ui-base-color, 10%); - } -} - -.setting-text { - background: darken($ui-base-color, 10%); -} - -.report-dialog-modal__textarea { - background: darken($ui-base-color, 10%); -} - -.autosuggest-account { - .display-name__account { - color: $dark-text-color; - } -} - -.notification-group--annual-report { - .notification-group__icon, - .notification-group__main .link-button { - color: var(--indigo-3); - } -} - -@supports not selector(::-webkit-scrollbar) { - html { - scrollbar-color: color.change($action-button-color, $alpha: 0.25) - var(--background-border-color); - } -} - -.custom-scrollbars { - ::-webkit-scrollbar-thumb { - opacity: 0.25; - } -} - -kbd { - background-color: color.change($ui-highlight-color, $alpha: 0.1); -} diff --git a/app/javascript/styles/mastodon-light/variables.scss b/app/javascript/styles/mastodon-light/variables.scss deleted file mode 100644 index a6a5e5556b58aa..00000000000000 --- a/app/javascript/styles/mastodon-light/variables.scss +++ /dev/null @@ -1,46 +0,0 @@ -@use 'sass:color'; - -@use '../mastodon/functions' with ( - $darken-multiplier: 1, - $lighten-multiplier: -1 -); - -$black: #000; // Black -$white: #fff; // White -$blurple-500: #6364ff; // Brand purple -$grey-600: hsl(240deg, 8%, 33%); // Trout -$grey-100: hsl(240deg, 51%, 90%); // Topaz - -$classic-base-color: hsl(240deg, 16%, 19%); -$classic-secondary-color: hsl(255deg, 25%, 88%); -$classic-highlight-color: $blurple-500; - -@use '../mastodon/variables' with ( - $success-green: color.adjust( - hsl(138deg, 32%, 35%), - $lightness: 8%, - $space: hsl - ), - $base-overlay-background: $white, - - $ui-base-color: $classic-secondary-color, - $ui-base-lighter-color: hsl(250deg, 24%, 75%), - $ui-secondary-color: $classic-base-color, - - $ui-button-secondary-color: $grey-600, - $ui-button-secondary-border-color: $grey-600, - $ui-button-secondary-focus-color: $white, - $ui-button-tertiary-color: $blurple-500, - $ui-button-tertiary-border-color: $blurple-500, - - $primary-text-color: $black, - $darker-text-color: $classic-base-color, - $lighter-text-color: $classic-base-color, - $highlight-text-color: $classic-highlight-color, - $dark-text-color: hsl(240deg, 16%, 32%), - $light-text-color: hsl(240deg, 16%, 32%), - $inverted-text-color: $black, - - $action-button-color: hsl(240deg, 16%, 45%), - $emojis-requiring-inversion: 'chains' -); diff --git a/app/javascript/styles/mastodon/_functions.scss b/app/javascript/styles/mastodon/_functions.scss deleted file mode 100644 index a9911edb9d1d5b..00000000000000 --- a/app/javascript/styles/mastodon/_functions.scss +++ /dev/null @@ -1,31 +0,0 @@ -@use 'sass:color'; -@use 'sass:string'; -@use 'sass:meta'; - -$darken-multiplier: -1 !default; -$lighten-multiplier: 1 !default; - -// Invert darkened and lightened colors -@function darken($color, $amount) { - @return color.adjust( - $color, - $lightness: $amount * $darken-multiplier, - $space: hsl - ); -} - -@function lighten($color, $amount) { - @return color.adjust( - $color, - $lightness: $amount * $lighten-multiplier, - $space: hsl - ); -} - -@function hex-color($color) { - @if meta.type-of($color) == 'color' { - $color: string.slice(color.ie-hex-str($color), 4); - } - - @return '%23' + string.unquote($color); -} diff --git a/app/javascript/styles/mastodon/_mixins.scss b/app/javascript/styles/mastodon/_mixins.scss index d66fa405819ef7..effbe82c3d8581 100644 --- a/app/javascript/styles/mastodon/_mixins.scss +++ b/app/javascript/styles/mastodon/_mixins.scss @@ -1,33 +1,30 @@ -@use 'sass:color'; -@use 'variables' as *; - @mixin search-input { outline: 0; box-sizing: border-box; width: 100%; box-shadow: none; font-family: inherit; - background: var(--input-background-color); - color: $darker-text-color; + background: var(--color-bg-secondary); + color: var(--color-text-primary); border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-on-bg-secondary); font-size: 17px; line-height: normal; margin: 0; } @mixin search-popout { - background: $simple-background-color; + background: var(--color-bg-elevated); border-radius: 4px; padding: 10px 14px; padding-bottom: 14px; margin-top: 10px; - color: $light-text-color; - box-shadow: 2px 4px 15px color.change($base-shadow-color, $alpha: 0.4); + color: var(--color-text-secondary); + box-shadow: 2px 4px 15px var(--color-shadow-primary); h4 { text-transform: uppercase; - color: $light-text-color; + color: var(--color-text-secondary); font-size: 13px; font-weight: 500; margin-bottom: 10px; @@ -43,6 +40,6 @@ em { font-weight: 500; - color: $inverted-text-color; + color: var(--color-text-primary); } } diff --git a/app/javascript/styles_new/mastodon/_theme_utils.scss b/app/javascript/styles/mastodon/_theme_utils.scss similarity index 100% rename from app/javascript/styles_new/mastodon/_theme_utils.scss rename to app/javascript/styles/mastodon/_theme_utils.scss diff --git a/app/javascript/styles/mastodon/_variables.scss b/app/javascript/styles/mastodon/_variables.scss index 816a9d9998c089..a948dbc41c55f5 100644 --- a/app/javascript/styles/mastodon/_variables.scss +++ b/app/javascript/styles/mastodon/_variables.scss @@ -1,91 +1,3 @@ -@use 'sass:color'; -@use 'functions' as *; - -// Commonly used web colors -$black: #000; // Black -$white: #fff; // White -$red-600: #b7253d !default; // Deep Carmine -$red-500: #df405a !default; // Cerise -$blurple-600: #563acc; // Iris -$blurple-500: #6364ff; // Brand purple -$blurple-400: #7477fd; // Medium slate blue -$blurple-300: #858afa; // Faded Blue -$grey-600: hsl(240deg, 8%, 33%); // Trout -$grey-100: hsl(240deg, 51%, 90%); // Topaz - -$success-green: #79bd9a !default; // Padua -$error-red: $red-500 !default; // Cerise -$warning-red: #ff5050 !default; // Sunset Orange -$gold-star: #ca8f04 !default; // Dark Goldenrod - -$red-bookmark: $warning-red; - -// Values from the classic Mastodon UI -$classic-base-color: hsl(240deg, 16%, 19%); -$classic-primary-color: hsl(240deg, 29%, 70%); -$classic-secondary-color: hsl(255deg, 25%, 88%); -$classic-highlight-color: $blurple-500; - -// Variables for defaults in UI -$base-shadow-color: $black !default; -$base-overlay-background: $black !default; -$base-border-color: $white !default; -$simple-background-color: $white !default; -$valid-value-color: $success-green !default; -$error-value-color: $error-red !default; - -// Tell UI to use selected colors -$ui-base-color: $classic-base-color !default; // Darkest -$ui-base-lighter-color: lighten( - $ui-base-color, - 26% -) !default; // Lighter darkest -$ui-primary-color: $classic-primary-color !default; // Lighter -$ui-secondary-color: $classic-secondary-color !default; // Lightest -$ui-highlight-color: $classic-highlight-color !default; -$ui-button-color: $white !default; -$ui-button-background-color: $blurple-500 !default; -$ui-button-focus-background-color: $blurple-600 !default; -$ui-button-focus-outline-color: $blurple-400 !default; -$ui-button-focus-outline: solid 2px $ui-button-focus-outline-color !default; - -$ui-button-disabled-color: color.adjust( - $ui-button-background-color, - $alpha: -0.3 -) !default; - -$ui-button-secondary-color: $blurple-500 !default; -$ui-button-secondary-border-color: $blurple-500 !default; -$ui-button-secondary-focus-border-color: $blurple-300 !default; -$ui-button-secondary-focus-color: $blurple-300 !default; - -$ui-button-tertiary-color: $blurple-300 !default; -$ui-button-tertiary-border-color: $blurple-300 !default; -$ui-button-tertiary-focus-background-color: $blurple-600 !default; -$ui-button-tertiary-focus-color: $white !default; - -$ui-button-destructive-background-color: $red-500 !default; -$ui-button-destructive-focus-background-color: $red-600 !default; - -$ui-button-icon-focus-outline: $ui-button-focus-outline !default; -$ui-button-icon-hover-background-color: rgba(140, 141, 255, 40%) !default; - -// Variables for texts -$primary-text-color: $white !default; -$darker-text-color: $ui-primary-color !default; -$dark-text-color: $ui-base-lighter-color !default; -$secondary-text-color: $ui-secondary-color !default; -$highlight-text-color: lighten($ui-highlight-color, 8%) !default; -$action-button-color: $ui-base-lighter-color !default; -$action-button-focus-color: lighten($ui-base-lighter-color, 4%) !default; -$passive-text-color: $gold-star !default; -$active-passive-text-color: $success-green !default; - -// For texts on inverted backgrounds -$inverted-text-color: $ui-base-color !default; -$lighter-text-color: $ui-base-lighter-color !default; -$light-text-color: $ui-primary-color !default; - // Keep this filter a SCSS variable rather than // a CSS Custom Property due to this Safari bug: // https://github.com/mdn/browser-compat-data/issues/25914#issuecomment-2676190245 diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index ba0605b79e9188..0bb2c8c9eb21fb 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -1,5 +1,4 @@ @use 'variables' as *; -@use 'functions' as *; $maximum-width: 1235px; $fluid-breakpoint: $maximum-width + 20px; @@ -28,7 +27,7 @@ $fluid-breakpoint: $maximum-width + 20px; li { position: relative; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 1em 1.75em; padding-inline-start: 3em; font-weight: 500; @@ -68,8 +67,8 @@ $fluid-breakpoint: $maximum-width + 20px; position: absolute; inset-inline-start: 0; top: 1em; - background: $highlight-text-color; - color: $ui-base-color; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); border-radius: 50%; width: 4ch; height: 4ch; @@ -85,13 +84,13 @@ $fluid-breakpoint: $maximum-width + 20px; } &__text { - color: $primary-text-color; + color: var(--color-text-primary); } &__hint { font-size: 14px; font-weight: 400; - color: $darker-text-color; + color: var(--color-text-secondary); } } @@ -104,21 +103,21 @@ $fluid-breakpoint: $maximum-width + 20px; > label { font-size: 14px; font-weight: 600; - color: $primary-text-color; + color: var(--color-text-primary); } - > select { + select { appearance: none; box-sizing: border-box; font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); display: block; width: 100%; outline: 0; font-family: inherit; resize: vertical; - background: $ui-base-color; - border: 1px solid var(--background-border-color); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); border-radius: 4px; padding-inline-start: 10px; padding-inline-end: 30px; @@ -128,18 +127,4 @@ $fluid-breakpoint: $maximum-width + 20px; font-size: 16px; } } - - &::after { - display: block; - position: absolute; - width: 15px; - height: 15px; - content: ''; - mask: url("data:image/svg+xml;utf8,") - no-repeat 50% 50%; - mask-size: contain; - right: 8px; - background-color: lighten($ui-base-color, 12%); - pointer-events: none; - } } diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index da4b9bdaa81d41..d1c35e3f9ec067 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -1,6 +1,5 @@ @use 'sass:color'; @use 'variables' as *; -@use 'functions' as *; .card { & > a { @@ -14,7 +13,7 @@ &:active, &:focus { .card__bar { - background: $ui-base-color; + background: var(--color-bg-brand-softer); } } } @@ -22,8 +21,8 @@ &__img { height: 130px; position: relative; - background: $ui-base-color; - border: 1px solid var(--background-border-color); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); border-bottom: none; img { @@ -45,8 +44,8 @@ display: flex; justify-content: flex-start; align-items: center; - background: var(--background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); border-top: none; .avatar { @@ -61,7 +60,7 @@ display: block; margin: 0; border-radius: 4px; - background: darken($ui-base-color, 8%); + background: var(--color-bg-secondary); object-fit: cover; } } @@ -76,7 +75,7 @@ strong { font-size: 15px; - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; overflow: hidden; text-overflow: ellipsis; @@ -85,7 +84,7 @@ span { display: block; font-size: 14px; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 400; overflow: hidden; text-overflow: ellipsis; @@ -106,7 +105,7 @@ .page, .gap { font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; display: inline-block; padding: 6px 10px; @@ -114,9 +113,9 @@ } .current { - background: $simple-background-color; + color: var(--color-bg-primary); + background: var(--color-text-primary); border-radius: 100px; - color: $inverted-text-color; cursor: default; margin: 0 10px; } @@ -128,7 +127,7 @@ .older, .newer { text-transform: uppercase; - color: $secondary-text-color; + color: var(--color-text-primary); } .older { @@ -143,7 +142,7 @@ .disabled { cursor: default; - color: lighten($inverted-text-color, 10%); + color: var(--color-text-disabled); } @media screen and (width <= 700px) { @@ -161,9 +160,8 @@ } .nothing-here { - background: $ui-base-color; - box-shadow: 0 0 15px color.change($base-shadow-color, $alpha: 0.2); - color: $darker-text-color; + color: var(--color-text-secondary); + background: var(--color-bg-primary); font-size: 14px; font-weight: 500; text-align: center; @@ -174,6 +172,15 @@ border-radius: 4px; padding: 20px; min-height: 30vh; + border: 1px solid var(--color-border-primary); + + @media screen and (min-width: ($no-gap-breakpoint - 1)) { + border-top: 0; + } + + &--no-toolbar { + border-top: 1px solid var(--color-border-primary); + } &--under-tabs { border-radius: 0 0 4px 4px; @@ -196,7 +203,7 @@ font-size: 12px; line-height: 12px; font-weight: 500; - color: $ui-secondary-color; + color: var(--color-text-primary); text-overflow: ellipsis; white-space: nowrap; overflow: hidden; @@ -206,16 +213,24 @@ .simple_form .overridden, .simple_form .recommended, .simple_form .not_recommended { - background-color: color.change($ui-secondary-color, $alpha: 0.1); - border: 1px solid color.change($ui-secondary-color, $alpha: 0.5); + background-color: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); +} + +.information-badge { + &.superapp { + color: var(--color-text-success); + background-color: var(--color-bg-success-softer); + border-color: var(--color-border-on-bg-success-softer); + } } .account-role { display: inline-flex; padding: 4px; padding-inline-end: 8px; - border: 1px solid $highlight-text-color; - color: $highlight-text-color; + border: 1px solid var(--color-text-brand); + color: var(--color-text-brand); font-weight: 500; font-size: 12px; letter-spacing: 0.5px; @@ -238,18 +253,10 @@ } } -.information-badge { - &.superapp { - color: $success-green; - background-color: color.change($success-green, $alpha: 0.1); - border-color: color.change($success-green, $alpha: 0.5); - } -} - .simple_form .not_recommended { - color: lighten($error-red, 12%); - background-color: rgba(lighten($error-red, 12%), 0.1); - border-color: rgba(lighten($error-red, 12%), 0.5); + color: var(--color-text-error); + background-color: var(--color-bg-error-softer); + border-color: var(--color-border-on-bg-error-softer); } .account__header__fields { @@ -257,14 +264,14 @@ padding: 0; margin: 15px -15px -15px; border: 0 none; - border-top: 1px solid lighten($ui-base-color, 12%); - border-bottom: 1px solid lighten($ui-base-color, 12%); + border-top: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); font-size: 14px; line-height: 20px; dl { display: flex; - border-bottom: 1px solid lighten($ui-base-color, 12%); + border-bottom: 1px solid var(--color-border-primary); } dt, @@ -282,17 +289,17 @@ font-weight: 500; width: 120px; flex: 0 0 auto; - color: $secondary-text-color; - background: rgba(darken($ui-base-color, 8%), 0.5); + color: var(--color-text-primary); + background: var(--color-bg-secondary); } dd { flex: 1 1 auto; - color: $darker-text-color; + color: var(--color-text-secondary); } a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: none; &:hover, @@ -303,16 +310,16 @@ } .verified { - border: 1px solid color.change($valid-value-color, $alpha: 0.5); - background: color.change($valid-value-color, $alpha: 0.25); + border: 1px solid var(--color-border-on-bg-success-softer); + background: var(--color-bg-success-softer); a { - color: $valid-value-color; + color: var(--color-text-success); font-weight: 500; } &__mark { - color: $valid-value-color; + color: var(--color-text-success); } } @@ -327,10 +334,10 @@ .pending-account { &__header { - color: $darker-text-color; + color: var(--color-text-secondary); a { - color: $ui-secondary-color; + color: var(--color-text-primary); text-decoration: none; &:hover, @@ -341,7 +348,7 @@ } strong { - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 700; } @@ -356,7 +363,7 @@ } .batch-table__row--muted { - color: lighten($ui-base-color, 26%); + color: var(--color-text-tertiary); } .batch-table__row--muted .pending-account__header, @@ -365,7 +372,7 @@ &, a, strong { - color: lighten($ui-base-color, 26%); + color: var(--color-text-tertiary); } } @@ -377,12 +384,12 @@ tbody td.accounts-table__extra, &__count, &__count small { - color: lighten($ui-base-color, 26%); + color: var(--color-text-tertiary); } } .batch-table__row--attention { - color: $gold-star; + color: var(--color-text-warning); } .batch-table__row--attention .pending-account__header, @@ -391,7 +398,7 @@ &, a, strong { - color: $gold-star; + color: var(--color-text-warning); } } @@ -399,6 +406,6 @@ tbody td.accounts-table__extra, &__count, &__count small { - color: $gold-star; + color: var(--color-text-warning); } } diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index c3a14a4fbde566..eaa91936b106c8 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -1,6 +1,5 @@ @use 'sass:color'; @use 'sass:math'; -@use 'functions' as *; @use 'variables' as *; $no-columns-breakpoint: 890px; @@ -34,7 +33,6 @@ $content-width: 840px; &__inner { display: flex; justify-content: flex-end; - background: var(--background-color); height: 100%; } } @@ -46,8 +44,8 @@ $content-width: 840px; &__toggle { display: none; - background: var(--background-color); - border-bottom: 1px solid lighten($ui-base-color, 4%); + background: var(--color-bg-primary); + border-bottom: 1px solid var(--color-border-primary); align-items: center; &__logo { @@ -61,7 +59,7 @@ $content-width: 840px; &__icon { display: block; - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; flex: 0 0 auto; font-size: 18px; @@ -70,7 +68,7 @@ $content-width: 840px; border-radius: 4px; &:focus { - background: $ui-base-color; + background: var(--color-bg-brand-softer); } .material-close { @@ -124,20 +122,23 @@ $content-width: 840px; align-items: center; gap: 6px; padding: 15px; - color: $darker-text-color; + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); text-decoration: none; transition: all 200ms linear; transition-property: color, background-color; &:hover { - color: $primary-text-color; + color: var(--color-text-primary); transition: all 100ms linear; transition-property: color, background-color; } } ul { - background: var(--background-color); margin: 0; a { @@ -147,12 +148,12 @@ $content-width: 840px; } .warning a { - color: $gold-star; + color: var(--color-text-warning); font-weight: 700; } .simple-navigation-active-leaf a { - color: $highlight-text-color; + color: var(--color-text-brand); border-bottom: 0; } } @@ -227,7 +228,7 @@ $content-width: 840px; align-items: center; padding: 7px 10px; border-radius: 4px; - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; font-weight: 500; gap: 5px; @@ -240,13 +241,13 @@ $content-width: 840px; &:hover, &:focus, &:active { - background: lighten($ui-base-color, 4%); + background: var(--color-bg-secondary); } &.selected { font-weight: 700; - color: $primary-text-color; - background: $ui-highlight-color; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); } } } @@ -270,7 +271,7 @@ $content-width: 840px; font-size: 12px; display: block; font-weight: 500; - color: $darker-text-color; + color: var(--color-text-secondary); line-height: 18px; } @@ -281,14 +282,14 @@ $content-width: 840px; } h2 { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 24px; line-height: 36px; font-weight: 700; } h3 { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 20px; line-height: 28px; font-weight: 400; @@ -299,33 +300,28 @@ $content-width: 840px; text-transform: uppercase; font-size: 13px; font-weight: 700; - color: $darker-text-color; + color: var(--color-text-secondary); padding-top: 24px; margin-bottom: 8px; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); } h6 { font-size: 16px; - color: $secondary-text-color; + color: var(--color-text-primary); line-height: 28px; font-weight: 500; } .fields-group h6 { - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; } - .directory__tag > a, - .directory__tag > div { - box-shadow: none; - } - .directory__tag h4 { font-size: 18px; font-weight: 700; - color: $primary-text-color; + color: var(--color-text-primary); text-transform: none; padding-top: 0; margin-bottom: 0; @@ -339,12 +335,12 @@ $content-width: 840px; &.private-comment { display: block; - color: $darker-text-color; + color: var(--color-text-secondary); } &.public-comment { display: block; - color: $secondary-text-color; + color: var(--color-text-primary); } } } @@ -352,11 +348,11 @@ $content-width: 840px; & > p { font-size: 14px; line-height: 21px; - color: $secondary-text-color; + color: var(--color-text-primary); margin-bottom: 20px; strong { - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; @each $lang in $cjk-langs { @@ -371,7 +367,7 @@ $content-width: 840px; width: 100%; height: 0; border: 0; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); margin: 20px 0; &.spacer { @@ -409,7 +405,7 @@ $content-width: 840px; inset-inline-start: 0; bottom: 0; overflow-y: auto; - background: var(--background-color); + background: var(--color-bg-primary); } } @@ -429,7 +425,7 @@ $content-width: 840px; } ul .simple-navigation-active-leaf a { - border-bottom-color: $ui-highlight-color; + border-bottom-color: var(--color-text-brand); } } } @@ -445,10 +441,10 @@ hr.spacer { body, .admin-wrapper .content { .muted-hint { - color: $darker-text-color; + color: var(--color-text-secondary); a { - color: $highlight-text-color; + color: var(--color-text-brand); } } @@ -468,29 +464,29 @@ body, } .positive-hint { - color: $valid-value-color; + color: var(--color-text-success); font-weight: 500; } .negative-hint { - color: $error-value-color; + color: var(--color-text-error); font-weight: 500; } .neutral-hint { - color: $dark-text-color; + color: var(--color-text-primary); font-weight: 500; } .warning-hint { - color: $gold-star; + color: var(--color-text-warning); font-weight: 500; } } kbd { font-family: Courier, monospace; - background-color: color.change($ui-secondary-color, $alpha: 0.1); + background-color: var(--color-bg-brand-softer); padding: 4px; padding-bottom: 2px; border-radius: 5px; @@ -543,21 +539,21 @@ kbd { a { display: inline-block; - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; text-transform: uppercase; font-size: 12px; font-weight: 500; - border-bottom: 2px solid $ui-base-color; + border-bottom: 2px solid var(--color-bg-secondary); &:hover { - color: $primary-text-color; - border-bottom: 2px solid lighten($ui-base-color, 5%); + color: var(--color-text-primary); + border-bottom: 2px solid var(--color-bg-tertiary); } &.selected { - color: $highlight-text-color; - border-bottom: 2px solid $ui-highlight-color; + color: var(--color-text-brand); + border-bottom: 2px solid var(--color-text-brand); } } } @@ -581,7 +577,7 @@ kbd { font-weight: 500; font-size: 14px; line-height: 18px; - color: $secondary-text-color; + color: var(--color-text-primary); @each $lang in $cjk-langs { &:lang(#{$lang}) { @@ -640,7 +636,7 @@ kbd { font-size: 14px; a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: none; &:hover { @@ -670,25 +666,25 @@ kbd { line-height: 20px; padding: 15px; padding-inline-start: 15px * 2 + 40px; - background: var(--background-color); - border-right: 1px solid var(--background-border-color); - border-left: 1px solid var(--background-border-color); - border-bottom: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border-right: 1px solid var(--color-border-primary); + border-left: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); position: relative; text-decoration: none; - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 14px; &:first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); } &:last-child { border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); } &__avatar { @@ -708,13 +704,13 @@ kbd { } &__timestamp { - color: $dark-text-color; + color: var(--color-text-tertiary); } a, .username, .target { - color: $secondary-text-color; + color: var(--color-text-primary); text-decoration: none; font-weight: 500; } @@ -733,12 +729,12 @@ kbd { line-height: 20px; padding: 15px; padding-inline-start: 15px * 2 + 40px; - background: var(--background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); border-radius: 4px; position: relative; text-decoration: none; - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 14px; margin-bottom: 15px; @@ -759,13 +755,13 @@ kbd { } &__timestamp { - color: $dark-text-color; + color: var(--color-text-secondary); } &:hover, &:focus, &:active { - background: $ui-base-color; + background: var(--color-bg-primary); } } @@ -774,10 +770,10 @@ a.name-tag, a.inline-name-tag, .inline-name-tag { text-decoration: none; - color: $secondary-text-color; + color: var(--color-text-primary); &:hover { - color: $highlight-text-color; + color: var(--color-text-brand); } .username { @@ -787,7 +783,7 @@ a.inline-name-tag, &.suspended { .username { text-decoration: line-through; - color: lighten($error-red, 12%); + color: var(--color-text-error); } .avatar { @@ -820,18 +816,18 @@ a.name-tag, .speech-bubble { margin-bottom: 20px; - border-inline-start: 4px solid $ui-highlight-color; + border-inline-start: 4px solid var(--color-text-brand); &.positive { - border-left-color: $success-green; + border-color: var(--color-text-success); } &.negative { - border-left-color: lighten($error-red, 12%); + border-color: var(--color-text-error); } &.warning { - border-left-color: $gold-star; + border-color: var(--color-text-warning); } &__bubble { @@ -844,7 +840,7 @@ a.name-tag, font-weight: 500; a { - color: $darker-text-color; + color: var(--color-text-secondary); } } @@ -854,13 +850,13 @@ a.name-tag, } time { - color: $dark-text-color; + color: var(--color-text-tertiary); } } .report-card { - background: var(--background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); border-radius: 4px; margin-bottom: 20px; @@ -882,7 +878,7 @@ a.name-tag, &__stats { flex: 0 0 auto; font-weight: 500; - color: $darker-text-color; + color: var(--color-text-secondary); text-transform: uppercase; text-align: end; @@ -893,12 +889,12 @@ a.name-tag, &:focus, &:hover, &:active { - color: $highlight-text-color; + color: var(--color-text-brand); } } .red { - color: $error-value-color; + color: var(--color-text-error); } } } @@ -907,7 +903,7 @@ a.name-tag, &__item { display: flex; justify-content: flex-start; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); &__reported-by, &__assigned { @@ -915,7 +911,7 @@ a.name-tag, flex: 0 0 auto; box-sizing: border-box; width: 150px; - color: $darker-text-color; + color: var(--color-text-secondary); &, .username { @@ -941,10 +937,10 @@ a.name-tag, width: 100%; padding: 15px; text-decoration: none; - color: $darker-text-color; + color: var(--color-text-secondary); &:hover { - color: $highlight-text-color; + color: var(--color-text-brand); } } } @@ -981,18 +977,18 @@ a.name-tag, .account__header__fields, .account__header__content { - background: var(--background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); border-radius: 4px; height: 100%; } .account__header__fields { margin: 0; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); a { - color: $highlight-text-color; + color: var(--color-text-brand); } dl:first-child .verified { @@ -1000,14 +996,14 @@ a.name-tag, } .verified a { - color: $valid-value-color; + color: var(--color-text-success); } } .account__header__content { box-sizing: border-box; padding: 20px; - color: $primary-text-color; + color: var(--color-text-primary); } } @@ -1018,8 +1014,8 @@ a.name-tag, .applications-list__item, .filters-list__item { padding: 15px 0; - background: var(--background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); border-radius: 4px; margin-top: 15px; } @@ -1032,13 +1028,13 @@ a.name-tag, .announcements-list, .filters-list { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; border-bottom: none; &__item { padding: 15px 0; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &__title { padding: 0 15px; @@ -1046,12 +1042,12 @@ a.name-tag, font-weight: 500; font-size: 18px; line-height: 1.5; - color: $secondary-text-color; + color: var(--color-text-primary); text-decoration: none; margin-bottom: 10px; &:hover { - color: $highlight-text-color; + color: var(--color-text-brand); } .account-role { @@ -1067,7 +1063,7 @@ a.name-tag, &:hover, &:focus, &:active { - color: $primary-text-color; + color: var(--color-text-primary); } } @@ -1084,7 +1080,7 @@ a.name-tag, &__meta { padding: 0 15px; - color: $dark-text-color; + color: var(--color-text-tertiary); a { color: inherit; @@ -1127,11 +1123,11 @@ a.name-tag, &.expired { .expiration { - color: lighten($error-red, 12%); + color: var(--color-text-error); } .permissions-list__item__icon { - color: $dark-text-color; + color: var(--color-text-secondary); } } } @@ -1163,7 +1159,7 @@ a.name-tag, &__table { &__number { - color: var(--background-color); + color: var(--color-bg-primary); padding: 10px; } @@ -1185,23 +1181,26 @@ a.name-tag, &__label { font-weight: 700; - color: $darker-text-color; + color: var(--color-text-secondary); } &__box { box-sizing: border-box; - background: var(--background-color); - padding: 10px; - font-weight: 500; - color: $primary-text-color; width: 52px; margin: 1px; + padding: 10px; + font-weight: 500; + color: var(--color-text-primary); + background: var(--color-bg-primary); @for $i from 0 through 10 { &--#{10 * $i} { - background-color: rgba( - $ui-highlight-color, - 1 * (math.div(max(1, $i), 10)) + @if $i > 5 { + color: var(--color-text-on-brand-base); + } + + background-color: rgb( + from var(--color-bg-brand-base) r g b / #{math.div(max(1, $i), 10)} ); } } @@ -1212,9 +1211,9 @@ a.name-tag, .sparkline { display: block; text-decoration: none; - background: var(--background-color); + background: var(--color-bg-primary); border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); padding: 0; position: relative; padding-bottom: 55px + 20px; @@ -1232,22 +1231,22 @@ a.name-tag, margin-inline-end: 10px; font-weight: 500; font-size: 28px; - color: $primary-text-color; + color: var(--color-text-primary); } &__change { display: block; font-weight: 500; font-size: 18px; - color: $darker-text-color; + color: var(--color-text-secondary); margin-bottom: -3px; &.positive { - color: $valid-value-color; + color: var(--color-text-success); } &.negative { - color: $error-value-color; + color: var(--color-text-error); } } } @@ -1256,7 +1255,7 @@ a.name-tag, padding: 0 20px; padding-bottom: 10px; text-transform: uppercase; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 500; } @@ -1271,12 +1270,12 @@ a.name-tag, } path:first-child { - fill: color.change($highlight-text-color, $alpha: 0.25) !important; + fill: var(--color-graph-primary-fill) !important; fill-opacity: 1 !important; } path:last-child { - stroke: lighten($highlight-text-color, 6%) !important; + stroke: var(--color-graph-primary-stroke) !important; fill: none !important; } } @@ -1286,17 +1285,17 @@ a.sparkline { &:hover, &:focus, &:active { - background: $ui-base-color; + background: var(--color-bg-brand-softer); } } .skeleton { - background-color: var(--background-color); + background-color: var(--color-bg-primary); background-image: linear-gradient( 90deg, - lighten($ui-base-color, 8%), - lighten($ui-base-color, 12%), - lighten($ui-base-color, 8%) + var(--color-bg-primary), + var(--color-bg-secondary), + var(--color-bg-primary) ); background-size: 200px 100%; background-repeat: no-repeat; @@ -1327,7 +1326,7 @@ a.sparkline { } &__item { - border-bottom: 1px solid lighten($ui-base-color, 4%); + border-bottom: 1px solid var(--color-border-primary); &__key { font-weight: 500; @@ -1336,7 +1335,7 @@ a.sparkline { &__value { text-align: end; - color: $darker-text-color; + color: var(--color-text-secondary); padding: 11px 10px; } @@ -1345,14 +1344,13 @@ a.sparkline { width: 8px; height: 8px; border-radius: 50%; - background: $ui-highlight-color; + background: var(--color-text-brand); margin-inline-end: 10px; @for $i from 0 through 10 { &--#{10 * $i} { - background-color: rgba( - $ui-highlight-color, - 1 * (math.div(max(1, $i), 10)) + background-color: rgb( + from var(--color-text-brand) r g b / #{math.div(max(1, $i), 10)} ); } } @@ -1363,11 +1361,11 @@ a.sparkline { } &.negative { - color: $error-value-color; + color: var(--color-text-error); font-weight: 700; .dimension__item__value { - color: $error-value-color; + color: var(--color-text-error); } } } @@ -1375,12 +1373,12 @@ a.sparkline { .report-reason-selector { border-radius: 4px; - background: var(--background-color); + background: var(--color-bg-primary); margin-bottom: 20px; &__category { cursor: pointer; - border-bottom: 1px solid darken($ui-base-color, 8%); + border-bottom: 1px solid var(--color-border-primary); &__label { padding: 15px; @@ -1410,7 +1408,7 @@ a.sparkline { &__details { &__item { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 15px 0; &:last-child { @@ -1441,7 +1439,7 @@ a.sparkline { .account-card { border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); position: relative; &__warning-badge { @@ -1453,7 +1451,7 @@ a.sparkline { background: url('@/images/warning-stripes.svg') repeat-y left, url('@/images/warning-stripes.svg') repeat-y right, - var(--background-color); + var(--color-bg-primary); } &__permalink { @@ -1472,7 +1470,7 @@ a.sparkline { width: 100%; height: 100%; object-fit: cover; - background: darken($ui-base-color, 8%); + background: var(--color-bg-secondary); } } @@ -1490,21 +1488,21 @@ a.sparkline { margin: 0; width: 56px; height: 56px; - background-color: darken($ui-base-color, 8%); + background-color: var(--color-bg-secondary); border-radius: 8px; - border: 1px solid $ui-base-color; + border: 1px solid var(--color-border-media); } } .display-name { - color: $darker-text-color; + color: var(--color-text-secondary); padding-bottom: 15px; font-size: 15px; line-height: 20px; bdi { display: block; - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 700; } } @@ -1533,7 +1531,7 @@ a.sparkline { } a { - color: $secondary-text-color; + color: var(--color-text-primary); text-decoration: none; unicode-bidi: isolate; @@ -1584,14 +1582,14 @@ a.sparkline { &__item { padding: 15px 0; text-align: center; - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 600; font-size: 15px; line-height: 21px; small { display: block; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 400; font-size: 13px; line-height: 18px; @@ -1604,11 +1602,11 @@ a.sparkline { margin-bottom: 20px; &__item { - background: var(--background-color); + background: var(--color-bg-primary); position: relative; padding: 15px; padding-inline-start: 15px * 2 + 40px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); &:first-child { border-top-left-radius: 4px; @@ -1630,13 +1628,13 @@ a.sparkline { } &__header { - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 15px; line-height: 20px; margin-bottom: 4px; .username { - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; margin-inline-end: 5px; @@ -1653,7 +1651,7 @@ a.sparkline { } a.timestamp { - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; &:hover, @@ -1674,7 +1672,7 @@ a.sparkline { line-height: 20px; overflow-wrap: break-word; font-weight: 400; - color: $primary-text-color; + color: var(--color-text-primary); p { margin-bottom: 20px; @@ -1687,7 +1685,7 @@ a.sparkline { } a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: none; &:hover { @@ -1731,7 +1729,7 @@ a.sparkline { &__description { padding: 15px; font-size: 14px; - color: $dark-text-color; + color: var(--color-text-tertiary); } } @@ -1759,7 +1757,7 @@ a.sparkline { float: right; a { - color: $ui-highlight-color; + color: var(--color-text-brand); text-decoration: none; &:hover, @@ -1776,14 +1774,14 @@ a.sparkline { line-height: 20px; overflow-wrap: break-word; font-weight: 400; - color: $primary-text-color; + color: var(--color-text-primary); box-sizing: border-box; min-height: 100%; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: none; &:hover { @@ -1808,34 +1806,34 @@ a.sparkline { list-style: disc; padding-inline-start: 15px; margin-bottom: 20px; - color: $darker-text-color; + color: var(--color-text-secondary); &:last-child { margin-bottom: 0; } &__text { - color: $primary-text-color; + color: var(--color-text-primary); } } &__statuses-list { border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); font-size: 13px; line-height: 18px; overflow: hidden; &__item { padding: 16px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &:last-child { border-bottom: 0; } &__meta { - color: $darker-text-color; + color: var(--color-text-secondary); } a { @@ -1872,16 +1870,16 @@ a.sparkline { flex: 0 0 auto; width: 4px; height: 21px; - background: lighten($ui-base-color, 8%); + background: var(--color-bg-secondary); margin: 0 2px; border-radius: 2px; &.positive { - background: $valid-value-color; + background: var(--color-bg-success-base); } &.negative { - background: $error-value-color; + background: var(--color-bg-error-base); } } } @@ -1910,9 +1908,9 @@ a.sparkline { align-items: center; width: calc(1.375rem + 1px); height: calc(1.375rem + 1px); - background: $ui-base-color; - border: 1px solid $highlight-text-color; - color: $highlight-text-color; + background: var(--color-bg-primary); + border: 1px solid var(--color-text-brand); + color: var(--color-text-brand); border-radius: 8px; } @@ -1920,7 +1918,7 @@ a.sparkline { position: absolute; content: ''; width: 1px; - background: $highlight-text-color; + background: var(--color-text-brand); bottom: 0; top: calc(1.875rem + 1px); inset-inline-start: 0.6875rem; @@ -1938,14 +1936,14 @@ a.sparkline { &__entry { h5 { font-weight: 500; - color: $primary-text-color; + color: var(--color-text-primary); line-height: 25px; margin-bottom: 16px; } .status { - border: 1px solid lighten($ui-base-color, 4%); - background: $ui-base-color; + border: 1px solid var(--color-border-primary); + background: var(--color-bg-secondary); border-radius: 4px; } } @@ -1958,8 +1956,8 @@ a.sparkline { line-height: 20px; overflow-wrap: break-word; font-weight: 400; - border: 1px solid lighten($ui-base-color, 4%); - color: $primary-text-color; + border: 1px solid var(--color-border-primary); + color: var(--color-text-primary); box-sizing: border-box; min-height: 100%; @@ -1994,9 +1992,9 @@ a.sparkline { summary { display: block; box-sizing: border-box; - background: var(--nested-card-background); - color: var(--nested-card-text); - border: var(--nested-card-border); + color: var(--color-text-primary); + background: var(--color-bg-brand-softer); + border: 1px solid var(--color-border-on-bg-brand-softer); border-radius: 8px; padding: 8px 13px; position: relative; @@ -2010,7 +2008,7 @@ a.sparkline { display: block; font-size: 15px; line-height: 20px; - color: $highlight-text-color; + color: var(--color-text-brand); cursor: pointer; border: 0; background: transparent; @@ -2053,7 +2051,7 @@ a.sparkline { } .spoiler-button__overlay__label { - outline: 1px solid var(--media-outline-color); + outline: 1px solid var(--color-border-media); } .hide-button { @@ -2062,11 +2060,11 @@ a.sparkline { position: absolute; top: 5px; right: 5px; - color: $white; - border: 0; - outline: 1px solid var(--media-outline-color); - background-color: color.change($black, $alpha: 0.45); + color: var(--color-text-on-media); + background: var(--color-bg-media); backdrop-filter: $backdrop-blur-filter; + outline: 1px solid var(--color-border-media); + border: 0; padding: 3px 12px; border-radius: 99px; font-size: 14px; @@ -2075,7 +2073,7 @@ a.sparkline { &:hover, &:focus { - background-color: color.change($black, $alpha: 0.9); + background-color: rgb(from var(--color-bg-media-base) r g b / 90%); } } @@ -2103,23 +2101,23 @@ a.sparkline { .admin { &__terms-of-service { &__container { - background: var(--surface-background-color); + background: var(--color-bg-tertiary); border-radius: 8px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); overflow: hidden; &__header { padding: 16px; font-size: 14px; line-height: 20px; - color: $secondary-text-color; + color: var(--color-text-primary); display: flex; align-items: center; gap: 12px; } &__body { - background: var(--background-color); + background: var(--color-bg-primary); padding: 16px; overflow-y: scroll; height: 30vh; @@ -2128,7 +2126,7 @@ a.sparkline { &__history { & > li { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &:last-child { border-bottom: 0; @@ -2166,14 +2164,14 @@ a.sparkline { width: 8px; height: 8px; border-radius: 50%; - background: $dark-text-color; + background: var(--color-text-tertiary); } &.success { - color: $valid-value-color; + color: var(--color-text-success); .dot-indicator__indicator { - background-color: $valid-value-color; + background-color: var(--color-bg-success-base); } } } diff --git a/app/javascript/styles/mastodon/annual_reports.scss b/app/javascript/styles/mastodon/annual_reports.scss index 96500a18bb6a16..a9b7e0ddee0120 100644 --- a/app/javascript/styles/mastodon/annual_reports.scss +++ b/app/javascript/styles/mastodon/annual_reports.scss @@ -170,7 +170,7 @@ } path:last-child { - stroke: var(--indigo-3) !important; + stroke: var(--color-graph-primary-stroke) !important; fill: none !important; } } diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index 3d8bba7c00c91e..6298409d157c97 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -1,5 +1,4 @@ @use 'variables' as *; -@use 'functions' as *; html.has-modal { &, @@ -17,11 +16,11 @@ html.has-modal { body { font-family: $font-sans-serif, sans-serif; - background: var(--background-color); + background: var(--color-bg-ambient); font-size: 13px; line-height: 18px; font-weight: 400; - color: $primary-text-color; + color: var(--color-text-primary); text-rendering: optimizelegibility; // Disable kerning for Japanese text to preserve monospaced alignment for readability @@ -118,6 +117,7 @@ body { &.admin { padding: 0; + background: var(--color-bg-primary); } &.error { @@ -157,7 +157,7 @@ body { a { &:focus { border-radius: 4px; - outline: $ui-button-focus-outline; + outline: var(--outline-focus-default); } &:focus:not(:focus-visible) { @@ -212,7 +212,7 @@ button { font-size: 16px; font-weight: 400; line-height: 1.7; - color: lighten($error-red, 4%); + color: var(--color-text-error); text-align: center; & > div { @@ -228,7 +228,7 @@ button { } a { - color: $highlight-text-color; + color: var(--color-text-brand); &:hover, &:focus, @@ -238,11 +238,11 @@ button { } &__footer { - color: $dark-text-color; + color: var(--color-text-secondary); font-size: 13px; a { - color: $dark-text-color; + color: var(--color-text-secondary); } } @@ -250,7 +250,7 @@ button { display: inline; border: 0; background: transparent; - color: $dark-text-color; + color: var(--color-text-secondary); font: inherit; padding: 0; margin: 0; @@ -267,7 +267,7 @@ button { } &.copied { - color: $valid-value-color; + color: var(--mas-status-success-color); transition: none; } } diff --git a/app/javascript/styles/mastodon/branding.scss b/app/javascript/styles/mastodon/branding.scss index 8e8dd3530bc2be..a7cc9c500e2432 100644 --- a/app/javascript/styles/mastodon/branding.scss +++ b/app/javascript/styles/mastodon/branding.scss @@ -1,5 +1,5 @@ @use 'variables' as *; .logo { - color: $primary-text-color; + color: var(--color-text-primary); } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index dbf67cec5103a3..01de2c7d7ace1e 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1,6 +1,5 @@ @use 'sass:color'; @use 'variables' as *; -@use 'functions' as *; @use 'mixins' as *; .app-body { @@ -17,7 +16,7 @@ } .inline-alert { - color: $valid-value-color; + color: var(--color-text-success); font-weight: 400; .no-reduce-motion & { @@ -29,7 +28,7 @@ display: block; font-size: 15px; line-height: 20px; - color: $highlight-text-color; + color: var(--color-text-brand); border: 0; background: transparent; padding: 0; @@ -37,7 +36,7 @@ text-decoration: none; &--destructive { - color: $error-value-color; + color: var(--color-text-error); } &:hover, @@ -46,19 +45,19 @@ } &:disabled { - color: $ui-primary-color; + color: var(--color-text-primary); cursor: default; } &:focus-visible { - outline: $ui-button-icon-focus-outline; + outline: var(--outline-focus-default); } } .help-button { - background: $ui-button-background-color; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); border: 0; - color: $ui-button-color; border-radius: 20px; cursor: pointer; width: 24px; @@ -70,11 +69,11 @@ &:active, &:focus, &:hover { - background-color: $ui-button-focus-background-color; + background: var(--color-bg-brand-base-hover); } &:focus-visible { - outline: $ui-button-icon-focus-outline; + outline: var(--outline-focus-default); } .icon { @@ -84,11 +83,11 @@ } .button { - background-color: $ui-button-background-color; + background-color: var(--color-bg-brand-base); border: 10px none; border-radius: 4px; box-sizing: border-box; - color: $ui-button-color; + color: var(--color-text-on-brand-base); cursor: pointer; display: inline-flex; align-items: center; @@ -111,11 +110,12 @@ &:active, &:focus, &:hover { - background-color: $ui-button-focus-background-color; + background-color: var(--color-bg-brand-base-hover); } &:focus-visible { - outline: $ui-button-icon-focus-outline; + outline: 2px solid var(--color-bg-brand-base); + outline-offset: 2px; } &--compact { @@ -127,13 +127,13 @@ } &--dangerous { - background-color: var(--error-background-color); - color: var(--on-error-color); + background-color: var(--color-bg-error-base); + color: var(--color-text-on-error-base); &:active, &:focus, &:hover { - background-color: var(--error-active-background-color); + background-color: var(--color-bg-error-base-hover); transition: none; } } @@ -142,14 +142,16 @@ &:active, &:focus, &:hover { - background-color: $ui-button-destructive-focus-background-color; + color: var(--color-text-on-error-base); + background-color: var(--color-bg-error-base); transition: none; } } &:disabled, &.disabled { - background-color: $ui-button-disabled-color; + color: var(--color-text-on-disabled); + background-color: var(--color-bg-disabled); cursor: not-allowed; } @@ -158,21 +160,22 @@ } &.copied { - background: $valid-value-color; + color: var(--color-text-on-success-base); + background-color: var(--color-bg-success-base); transition: none; } &.button-secondary { - color: $highlight-text-color; + color: var(--color-text-brand); background: transparent; padding: 6px 17px; - border: 1px solid $highlight-text-color; + border: 1px solid var(--color-text-brand); &:active, &:focus, &:hover { - border-color: lighten($highlight-text-color, 4%); - color: lighten($highlight-text-color, 4%); + border-color: var(--color-text-brand); + color: var(--color-text-brand); background-color: transparent; text-decoration: none; } @@ -181,28 +184,27 @@ &:active, &:focus, &:hover { - border-color: $ui-button-destructive-focus-background-color; - color: $ui-button-destructive-focus-background-color; + border-color: var(--color-text-error); + color: var(--color-text-error); } } &:disabled, &.disabled { - opacity: 0.7; - border-color: $ui-button-disabled-color; - color: $ui-button-disabled-color; + border-color: var(--color-text-disabled); + color: var(--color-text-disabled); &:active, &:focus, &:hover { - border-color: $ui-button-disabled-color; - color: $ui-button-disabled-color; + border-color: var(--color-text-disabled); + color: var(--color-text-disabled); } } } &.button--plain { - color: $highlight-text-color; + color: var(--color-text-brand); background: transparent; padding: 6px; @@ -216,7 +218,7 @@ &:focus, &:hover { border-color: transparent; - color: lighten($highlight-text-color, 4%); + color: var(--color-text-brand-soft); background-color: transparent; text-decoration: none; } @@ -225,57 +227,13 @@ &.disabled { opacity: 0.7; border-color: transparent; - color: $ui-button-disabled-color; + color: var(--color-text-disabled); &:active, &:focus, &:hover { border-color: transparent; - color: $ui-button-disabled-color; - } - } - } - - &.button-tertiary { - background: transparent; - padding: 6px 17px; - color: $ui-button-tertiary-color; - border: 1px solid $ui-button-tertiary-border-color; - - &:active, - &:focus, - &:hover { - background-color: $ui-button-tertiary-focus-background-color; - color: $ui-button-tertiary-focus-color; - border: 0; - padding: 7px 18px; - } - - &:disabled { - opacity: 0.5; - } - - &.button--confirmation { - color: $valid-value-color; - border-color: $valid-value-color; - - &:active, - &:focus, - &:hover { - background: $valid-value-color; - color: $primary-text-color; - } - } - - &.button--destructive { - color: $error-value-color; - border-color: $error-value-color; - - &:active, - &:focus, - &:hover { - background: $error-value-color; - color: $primary-text-color; + color: var(--color-text-disabled); } } } @@ -323,12 +281,17 @@ } .icon-button { + --default-icon-color: var(--color-text-secondary); + --default-bg-color: transparent; + --hover-icon-color: var(--color-text-primary); + --hover-bg-color: var(--color-bg-brand-softer); + display: inline-flex; - color: $action-button-color; + color: var(--default-icon-color); border: 0; padding: 0; border-radius: 4px; - background: transparent; + background: var(--default-bg-color); cursor: pointer; align-items: center; justify-content: center; @@ -345,66 +308,41 @@ &:hover, &:active, &:focus-visible { - color: lighten($action-button-color, 7%); - background-color: color.change($action-button-color, $alpha: 0.15); + color: var(--hover-icon-color); + background-color: var(--hover-bg-color); } &:focus-visible { - outline: $ui-button-icon-focus-outline; + outline: 2px solid var(--color-text-brand); } &.disabled { - color: darken($action-button-color, 13%); - background-color: transparent; + color: var(--color-text-disabled); + background-color: var(--default-bg-color); cursor: default; } &.inverted { - color: $lighter-text-color; - - &:hover, - &:active, - &:focus-visible { - color: darken($lighter-text-color, 7%); - background-color: color.change($lighter-text-color, $alpha: 0.15); - } - - &:focus-visible { - outline: $ui-button-icon-focus-outline; - } - - &.disabled { - color: lighten($lighter-text-color, 7%); - background-color: transparent; - } + --default-icon-color: var(--color-text-primary); + --hover-icon-color: var(--color-text-secondary); } &.active { - color: $highlight-text-color; - - &:hover, - &:active, - &:focus-visible { - color: $highlight-text-color; - background-color: transparent; - } - - &.disabled { - color: lighten($highlight-text-color, 13%); - } + --default-icon-color: var(--color-text-brand); + --hover-icon-color: var(--color-text-brand); + --hover-bg-color: transparent; } &.overlayed { + --default-icon-color: rgb(from var(--color-text-on-media) r g b / 70%); + --default-bg-color: var(--color-bg-media); + --hover-icon-color: var(--color-text-brand); + --hover-bg-color: rgb(from var(--color-bg-media-base) r g b / 90%); + box-sizing: content-box; - background: color.change($black, $alpha: 0.65); backdrop-filter: $backdrop-blur-filter; - color: color.change($white, $alpha: 0.7); border-radius: 4px; padding: 2px; - - &:hover { - background: color.change($black, $alpha: 0.9); - } } &--with-counter { @@ -423,55 +361,14 @@ } &.copied { - border-color: $valid-value-color; - color: $valid-value-color; + color: var(--color-text-success); transition: none; - background-color: color.change($valid-value-color, $alpha: 0.15); + background-color: var(--color-bg-success-softer); + border-color: var(--color-border-on-bg-brand-softer); } } -.text-icon-button { - color: $lighter-text-color; - border: 0; - border-radius: 4px; - background: transparent; - cursor: pointer; - font-weight: 600; - font-size: 11px; - padding: 0 3px; - line-height: 27px; - white-space: nowrap; - - &:hover, - &:active, - &:focus { - color: darken($lighter-text-color, 7%); - background-color: color.change($lighter-text-color, $alpha: 0.15); - } - - &:focus-visible { - outline: $ui-button-icon-focus-outline; - } - - &.disabled { - color: lighten($lighter-text-color, 20%); - background-color: transparent; - cursor: default; - } - - &.active { - color: $highlight-text-color; - - &:hover, - &:active, - &:focus { - color: $highlight-text-color; - background-color: transparent; - } - } -} - -[data-popper-placement] { +body > [data-popper-placement] { z-index: 9999; } @@ -514,10 +411,10 @@ &__suggestions { box-shadow: var(--dropdown-shadow); - background: var(--input-background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); border-radius: 0 0 4px 4px; - color: var(--on-input-color); + color: var(--color-text-primary); font-size: 14px; padding: 0; @@ -530,7 +427,7 @@ font-size: 14px; line-height: 20px; letter-spacing: 0.25px; - color: var(--on-input-color); + color: var(--color-text-primary); &:last-child { border-radius: 0 0 4px 4px; @@ -539,7 +436,7 @@ &:hover, &:focus, &:active { - background: var(--dropdown-border-color); + background: var(--color-bg-secondary); .autosuggest-account .display-name__account { color: inherit; @@ -547,8 +444,8 @@ } &.selected { - background: $ui-highlight-color; - color: $ui-button-color; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); .autosuggest-account .display-name__account { color: inherit; @@ -584,7 +481,7 @@ display: block; line-height: 16px; font-size: 12px; - color: $ui-primary-color; + color: var(--color-text-secondary); } } @@ -639,28 +536,27 @@ flex-direction: column; flex: 0 1 auto; border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-on-bg-secondary); transition: border-color 300ms linear; position: relative; - background: var(--input-background-color); + background: var(--color-bg-secondary); &.active { transition: none; - border-color: $ui-highlight-color; + border-color: var(--color-text-brand); } } &__warning { - color: $inverted-text-color; - background: $ui-primary-color; - box-shadow: 0 2px 6px color.change($base-shadow-color, $alpha: 0.3); + color: var(--color-text-primary); + background: var(--color-bg-warning-softer); + border: 1px solid var(--color-border-on-bg-warning-softer); padding: 8px 10px; border-radius: 4px; font-size: 13px; font-weight: 400; strong { - color: $inverted-text-color; font-weight: 500; @each $lang in $cjk-langs { @@ -671,7 +567,7 @@ } a { - color: $lighter-text-color; + color: var(--color-text-brand); font-weight: 500; text-decoration: underline; @@ -695,7 +591,7 @@ .autosuggest-input { flex: 1 1 auto; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-width: 1px 0; } } @@ -706,8 +602,8 @@ box-sizing: border-box; width: 100%; margin: 0; - color: $secondary-text-color; - background: var(--input-background-color); + color: var(--color-text-primary); + background: transparent; font-family: inherit; font-size: 14px; padding: 12px; @@ -722,8 +618,8 @@ .spoiler-input__input { padding: 12px 12px - 5px; - background: color.change($ui-highlight-color, $alpha: 0.05); - color: $highlight-text-color; + background: var(--color-bg-brand-softer); + color: var(--color-text-brand); } &__dropdowns { @@ -823,8 +719,9 @@ .icon-button { flex: 0 0 auto; - color: $white; - background: rgba(0, 0, 0, 75%); + color: var(--color-text-on-media); + background: var(--color-bg-media); + backdrop-filter: $backdrop-blur-filter; border-radius: 6px; font-size: 12px; line-height: 16px; @@ -857,8 +754,8 @@ padding: 8px; .icon-button.active { - color: #ffbe2e; - background: rgba(0, 0, 0, 75%); + color: var(--color-text-on-warning-base); + background: var(--color-bg-warning-base); } } } @@ -907,22 +804,21 @@ .icon-button { box-sizing: content-box; - color: $highlight-text-color; + color: var(--color-text-brand); &:hover, &:focus, &:active { - color: $highlight-text-color; + color: var(--color-text-brand); } &.disabled { - color: $highlight-text-color; - opacity: 0.5; + color: var(--color-text-disabled); } &.active { - background: $ui-highlight-color; - color: $primary-text-color; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); } } } @@ -946,7 +842,7 @@ .poll__input { width: 17px; height: 17px; - border-color: $darker-text-color; + border-color: var(--color-text-secondary); } &__footer { @@ -959,7 +855,7 @@ &__sep { width: 1px; height: 22px; - background: lighten($ui-base-color, 8%); + background: var(--color-border-primary); flex: 0 0 auto; } } @@ -977,7 +873,7 @@ font-weight: 500; line-height: 16px; letter-spacing: 0.5px; - color: $darker-text-color; + color: var(--color-text-secondary); white-space: nowrap; text-overflow: ellipsis; overflow: hidden; @@ -993,8 +889,8 @@ font-weight: 500; line-height: 20px; letter-spacing: 0.1px; - color: $highlight-text-color; - background-color: var(--input-background-color); + color: var(--color-text-brand); + background-color: var(--color-bg-secondary-solid); white-space: nowrap; text-overflow: ellipsis; overflow: hidden; @@ -1005,7 +901,6 @@ .status__quote { margin: 0 8px; max-height: 220px; - overflow: hidden; // Override .status__content .status__content__text.status__content__text--visible .status__content__text.status__content__text { @@ -1025,10 +920,10 @@ display: flex; align-items: center; gap: 4px; + color: var(--color-text-brand); background: transparent; - color: $highlight-text-color; + border: 1px solid var(--color-text-brand); border-radius: 6px; - border: 1px solid $highlight-text-color; padding: 4px 8px; font-size: 13px; line-height: normal; @@ -1039,9 +934,8 @@ &[disabled] { cursor: default; - color: $highlight-text-color; - border-color: $highlight-text-color; - opacity: 0.5; + color: var(--color-text-disabled); + border-color: var(--color-text-disabled); } .icon { @@ -1058,18 +952,19 @@ } &.active { - background: $ui-highlight-color; - border-color: $ui-highlight-color; - color: $primary-text-color; + color: var(--color-text-on-brand-base); + border-color: var(--color-bg-brand-base); + background: var(--color-bg-brand-base); } &.warning { - border-color: var(--goldenrod-2); - color: var(--goldenrod-2); + color: var(--color-text-warning); + border-color: var(--color-text-warning); &.active { - background-color: var(--goldenrod-2); - color: var(--indigo-1); + color: var(--color-text-on-warning-base); + border-color: var(--color-bg-warning-base); + background-color: var(--color-bg-warning-base); } } } @@ -1080,12 +975,12 @@ font-size: 14px; font-weight: 400; line-height: normal; - color: $darker-text-color; + color: var(--color-text-secondary); flex: 1 0 auto; text-align: end; &.character-counter--over { - color: $error-red; + color: var(--color-text-error); } } @@ -1101,7 +996,7 @@ p { font-size: 15px; line-height: 22px; - color: $secondary-text-color; + color: var(--color-text-primary); margin-bottom: 20px; strong { @@ -1109,7 +1004,7 @@ } a { - color: $secondary-text-color; + color: var(--color-text-primary); text-decoration: none; unicode-bidi: isolate; @@ -1156,7 +1051,7 @@ font-size: 15px; line-height: 22px; padding-top: 2px; - color: $primary-text-color; + color: var(--color-text-primary); &:focus { outline: 0; @@ -1187,7 +1082,7 @@ } a { - color: $secondary-text-color; + color: var(--color-text-status-links); text-decoration: none; unicode-bidi: isolate; @@ -1207,7 +1102,7 @@ } a.unhandled-link { - color: $highlight-text-color; + color: var(--color-text-brand); } .status__content__text { @@ -1263,7 +1158,7 @@ inset-inline-start: 50%; top: 4px; transform: translateX(-50%); - background: lighten($ui-base-color, 8%); + background: var(--color-border-primary); width: 2px; height: calc(100% + 32px - 8px); // Account for gap to next element } @@ -1280,12 +1175,12 @@ padding: 0; max-height: 4 * 20px; overflow: hidden; - color: $darker-text-color; + color: var(--color-text-secondary); } &__attachments { margin-top: 4px; - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 12px; line-height: 16px; display: flex; @@ -1301,11 +1196,11 @@ .edit-indicator { border-radius: 4px 4px 0 0; - background: lighten($ui-base-color, 4%); + background: var(--color-bg-tertiary); padding: 12px; overflow-y: auto; flex: 0 0 auto; - border-bottom: 0.5px solid lighten($ui-base-color, 8%); + border-bottom: 1px solid var(--color-border-primary); display: flex; flex-direction: column; gap: 4px; @@ -1314,7 +1209,7 @@ display: flex; justify-content: space-between; align-items: center; - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 12px; line-height: 16px; overflow: hidden; @@ -1347,7 +1242,7 @@ } &__content { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 14px; line-height: 20px; letter-spacing: 0.25px; @@ -1360,12 +1255,12 @@ overflow: hidden; a { - color: $highlight-text-color; + color: var(--color-text-brand); } } &__attachments { - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 12px; line-height: 16px; opacity: 0.75; @@ -1409,7 +1304,7 @@ } a { - color: $secondary-text-color; + color: var(--color-text-primary); text-decoration: none; &:hover { @@ -1427,7 +1322,7 @@ } &.unhandled-link { - color: $highlight-text-color; + color: var(--color-text-brand); } } } @@ -1442,7 +1337,7 @@ align-items: center; font-size: 15px; line-height: 22px; - color: $highlight-text-color; + color: var(--color-text-brand); border: 0; background: transparent; padding: 0; @@ -1474,11 +1369,11 @@ line-height: 22px; display: flex; justify-content: space-between; - color: $dark-text-color; + color: var(--color-text-tertiary); } .status__wrapper--filtered { - color: $dark-text-color; + color: var(--color-text-tertiary); border: 0; font-size: inherit; text-align: center; @@ -1488,11 +1383,11 @@ box-sizing: border-box; width: 100%; clear: both; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &__button { display: inline; - color: lighten($ui-highlight-color, 8%); + color: var(--color-text-brand); border: 0; background: transparent; padding: 0; @@ -1508,16 +1403,16 @@ .focusable { &:focus-visible { - outline: 2px solid $ui-button-focus-outline-color; + outline: 2px solid var(--color-text-brand); outline-offset: -2px; - background: color.change($ui-highlight-color, $alpha: 0.05); + background: var(--color-bg-brand-softer); } } .status { padding: 16px; min-height: 54px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); cursor: auto; opacity: 1; animation: fade 150ms linear; @@ -1588,12 +1483,14 @@ } &--first-in-thread { - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); } &__line { + --thread-line-color: var(--color-border-primary); + height: 16px - 4px; - border-inline-start: 2px solid lighten($ui-base-color, 8%); + border-inline-start: 2px solid var(--thread-line-color); width: 0; position: absolute; top: 0; @@ -1610,7 +1507,7 @@ top: 16px - 4px; height: 46px + 4px + 4px; width: 2px; - background: $ui-base-color; + background: var(--thread-line-color); inset-inline-start: -2px; } } @@ -1629,7 +1526,7 @@ content: ''; position: absolute; inset: 0; - background: rgb(from $ui-highlight-color r g b / 20%); + background: var(--color-bg-brand-softer); opacity: 0; animation: fade 0.7s reverse both 0.3s; pointer-events: none; @@ -1643,11 +1540,11 @@ height: 40px; order: 2; flex: 0 0 auto; - color: $dark-text-color; + color: var(--color-text-secondary); } .notification__relative_time { - color: $dark-text-color; + color: var(--color-text-tertiary); float: right; font-size: 14px; padding-bottom: 1px; @@ -1664,7 +1561,7 @@ } .status__display-name { - color: $dark-text-color; + color: var(--color-text-secondary); } .status__info .status__display-name { @@ -1714,7 +1611,7 @@ padding: 0 10px; .detailed-status__display-name { - color: $dark-text-color; + color: var(--color-text-tertiary); span { display: inline; @@ -1751,7 +1648,7 @@ font-size: 15px; line-height: 22px; font-weight: 500; - color: $dark-text-color; + color: var(--color-text-secondary); &__icon { display: flex; @@ -1781,10 +1678,10 @@ .notification-ungrouped--direct, .notification-group--direct, .notification-group--annual-report { - background: color.change($ui-highlight-color, $alpha: 0.05); + background: var(--color-bg-brand-softer); &:focus { - background: color.change($ui-highlight-color, $alpha: 0.1); + background: var(--color-bg-brand-soft); } } @@ -1792,7 +1689,7 @@ .notification-ungrouped--direct { .status__prepend, .notification-ungrouped__header { - color: $highlight-text-color; + color: var(--color-text-brand); } } @@ -1814,7 +1711,7 @@ .detailed-status { padding: 16px; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); .status__content { font-size: 19px; @@ -1849,7 +1746,7 @@ .logo { width: 40px; height: 40px; - color: $dark-text-color; + color: var(--color-text-tertiary); } } @@ -1876,12 +1773,12 @@ .detailed-status__meta { margin-top: 24px; - color: $dark-text-color; + color: var(--color-text-tertiary); font-size: 14px; line-height: 18px; &__line { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 8px 0; display: flex; align-items: center; @@ -1903,14 +1800,14 @@ } .animated-number { - color: $secondary-text-color; + color: var(--color-text-primary); font-weight: 500; } } .detailed-status__action-bar { - border-top: 1px solid var(--background-border-color); - border-bottom: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); display: flex; flex-direction: row; padding: 10px 0; @@ -1919,30 +1816,11 @@ .detailed-status__wrapper-direct { .detailed-status, .detailed-status__action-bar { - background: color.mix($ui-base-color, $ui-highlight-color, 95%); - } - - &:focus-visible { - .detailed-status, - .detailed-status__action-bar { - background: color.mix( - lighten($ui-base-color, 4%), - $ui-highlight-color, - 95% - ); - } - } - - .detailed-status__action-bar { - border-top-color: color.mix( - lighten($ui-base-color, 8%), - $ui-highlight-color, - 95% - ); + background: var(--color-bg-brand-softer); } .status__prepend { - color: $highlight-text-color; + color: var(--color-text-brand); } } @@ -1953,11 +1831,12 @@ --quote-margin: var(--status-gutter-width); position: relative; + overflow: hidden; margin-block-start: 16px; margin-inline-start: calc(var(--quote-margin) + var(--thread-margin, 0px)); border-radius: 12px; - color: var(--nested-card-text); - border: 1px solid var(--surface-border-color); + color: var(--color-text-primary); + border: 1px solid var(--color-border-primary); } .status__quote--error { @@ -1991,8 +1870,8 @@ font-weight: 400; line-height: 20px; letter-spacing: 0.25px; - color: $darker-text-color; - background: var(--surface-variant-background-color); + color: var(--color-text-secondary); + background: var(--color-bg-brand-softer); border-radius: 8px; cursor: default; } @@ -2056,14 +1935,14 @@ .domain { padding: 16px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); display: flex; align-items: center; gap: 8px; &__domain-name { flex: 1 1 auto; - color: $primary-text-color; + color: var(--color-text-primary); font-size: 15px; line-height: 21px; font-weight: 500; @@ -2072,14 +1951,14 @@ .account { padding: 16px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); .account__display-name { flex: 1 1 auto; display: flex; align-items: center; gap: 10px; - color: $darker-text-color; + color: var(--color-text-secondary); overflow: hidden; text-decoration: none; font-size: 14px; @@ -2090,6 +1969,7 @@ .display-name strong { display: inline; + color: var(--color-text-primary); } } @@ -2114,24 +1994,24 @@ &__domain-pill { display: inline-flex; - background: color.change($highlight-text-color, $alpha: 0.2); + background: var(--color-bg-brand-softer); border-radius: 4px; border: 0; - color: $highlight-text-color; + color: var(--color-text-brand); font-weight: 500; font-size: 12px; line-height: 16px; padding: 4px 8px; &.active { - color: $white; - background: $ui-highlight-color; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); } &__popout { - background: var(--dropdown-background-color); + background: var(--color-bg-elevated); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--dropdown-border-color); + border: 1px solid var(--color-border-primary); box-shadow: var(--dropdown-shadow); max-width: 320px; padding: 16px; @@ -2141,7 +2021,7 @@ gap: 24px; font-size: 14px; line-height: 20px; - color: $darker-text-color; + color: var(--color-text-secondary); .link-button { display: inline; @@ -2157,8 +2037,8 @@ &__icon { width: 40px; height: 40px; - background: $ui-highlight-color; - color: $white; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); display: flex; align-items: center; justify-content: center; @@ -2169,15 +2049,15 @@ h3 { font-size: 17px; line-height: 22px; - color: $primary-text-color; + color: var(--color-text-primary); } } &__handle { - border: 2px dashed $highlight-text-color; - background: color.change($highlight-text-color, $alpha: 0.1); + border: 2px dashed var(--color-border-on-brand-softer); + background: var(--color-bg-brand-softer); padding: 12px 8px; - color: $highlight-text-color; + color: var(--color-text-brand); border-radius: 4px; &__label { @@ -2213,14 +2093,14 @@ align-items: center; justify-content: center; flex-shrink: 0; - color: $highlight-text-color; + color: var(--color-text-brand); } h6 { font-size: 14px; line-height: 20px; font-weight: 500; - color: $primary-text-color; + color: var(--color-text-primary); } } } @@ -2236,10 +2116,10 @@ -webkit-line-clamp: 1; -webkit-box-orient: vertical; margin-top: 10px; - color: $darker-text-color; + color: var(--color-text-secondary); &--missing { - color: $dark-text-color; + color: var(--color-text-tertiary); } p { @@ -2303,7 +2183,7 @@ display: block; position: relative; border-radius: var(--avatar-border-radius); - background: var(--surface-background-color); + background: var(--color-bg-tertiary); flex-shrink: 0; img { @@ -2315,7 +2195,7 @@ } &--loading { - background-color: var(--surface-background-color); + background-color: var(--color-bg-tertiary); } &--inline { @@ -2346,8 +2226,9 @@ top: 50%; inset-inline-start: 50%; transform: translate(-50%, -50%); - color: $primary-text-color; - text-shadow: 1px 1px 2px $base-shadow-color; + color: var(--color-text-primary); + text-shadow: 1px 1px 2px + rgb(from var(--color-shadow-primary) r g b / 100%); font-weight: 700; font-size: 15px; } @@ -2366,11 +2247,11 @@ border-radius: $height; min-width: $height - 2 * $h-padding; // to ensure that it is never narrower than a circle line-height: $height + 1px; // to visually center the numbers - background-color: $ui-button-background-color; - color: $white; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); border-width: 1px; border-style: solid; - border-color: var(--background-color); + border-color: var(--color-bg-primary); font-size: 11px; font-weight: 500; text-align: center; @@ -2408,7 +2289,7 @@ a .account__avatar { } .account__avatar { - box-shadow: 0 0 0 2px var(--background-color); + box-shadow: 0 0 0 2px var(--color-bg-primary); } } @@ -2433,7 +2314,7 @@ a .account__avatar { .account__relationship, .explore-suggestions-card { .icon-button { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; box-sizing: content-box; padding: 5px; @@ -2473,7 +2354,7 @@ a .account__avatar { .status__display-name, .account__display-name { .display-name strong { - color: $primary-text-color; + color: var(--color-text-primary); } } @@ -2504,7 +2385,7 @@ a.account__display-name { } .detailed-status__display-name { - color: $darker-text-color; + color: var(--color-text-secondary); display: flex; align-items: center; gap: 10px; @@ -2521,7 +2402,7 @@ a.account__display-name { } strong { - color: $primary-text-color; + color: var(--color-text-primary); } } @@ -2529,11 +2410,11 @@ a.account__display-name { .status__content, .status__content p, .status__content a { - color: $dark-text-color; + color: var(--color-text-tertiary); } .status__display-name strong { - color: $dark-text-color; + color: var(--color-text-tertiary); } .status__avatar { @@ -2543,7 +2424,7 @@ a.account__display-name { .notification__report { padding: 16px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); display: flex; gap: 10px; @@ -2556,7 +2437,7 @@ a.account__display-name { display: flex; justify-content: space-between; align-items: center; - color: $darker-text-color; + color: var(--color-text-secondary); gap: 10px; font-size: 15px; line-height: 22px; @@ -2579,7 +2460,7 @@ a.account__display-name { } .notification-group--link { - color: $secondary-text-color; + color: var(--color-text-primary); text-decoration: none; .notification-group__main { @@ -2608,7 +2489,7 @@ a.account__display-name { padding: 16px; padding-bottom: 0; cursor: default; - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 15px; line-height: 22px; font-weight: 500; @@ -2617,13 +2498,13 @@ a.account__display-name { gap: 10px; .icon { - color: $highlight-text-color; + color: var(--color-text-brand); width: 18px; height: 18px; } .icon-star { - color: $gold-star; + color: var(--color-text-favourite-highlight); } > span { @@ -2634,11 +2515,11 @@ a.account__display-name { } .icon-button.star-icon.active { - color: $gold-star; + color: var(--color-text-favourite-highlight); } .icon-button.bookmark-icon.active { - color: $red-bookmark; + color: var(--color-text-bookmark-highlight); } .no-reduce-motion .icon-button.star-icon { @@ -2708,7 +2589,7 @@ a.account__display-name { max-height: $media-modal-media-max-height; width: auto; height: auto; - outline: 1px solid var(--media-outline-color); + outline: 1px solid var(--color-border-media); outline-offset: -1px; border-radius: 8px; touch-action: none; @@ -2738,7 +2619,7 @@ a.account__display-name { max-height: $media-modal-media-max-height; position: absolute; z-index: 1; - outline: 1px solid var(--media-outline-color); + outline: 1px solid var(--color-border-media); outline-offset: -1px; border-radius: 8px; overflow: hidden; @@ -2789,7 +2670,7 @@ a.account__display-name { .icon-button { padding: 8px; - color: $secondary-text-color; + color: var(--color-text-primary); } .icon-button .icon { @@ -2826,15 +2707,15 @@ a.account__display-name { } .dropdown-menu__separator { - border-bottom: 1px solid var(--dropdown-border-color); + border-bottom: 1px solid var(--color-border-primary); margin: 5px 0; height: 0; } .dropdown-menu { - background: var(--dropdown-background-color); + background: var(--color-bg-elevated); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--dropdown-border-color); + border: 1px solid var(--color-border-primary); padding: 4px; border-radius: 4px; box-shadow: var(--dropdown-shadow); @@ -2868,13 +2749,13 @@ a.account__display-name { &__container { &__header { - border-bottom: 1px solid var(--dropdown-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 10px 14px; padding-bottom: 14px; margin-bottom: 4px; font-size: 13px; line-height: 18px; - color: $darker-text-color; + color: var(--color-text-secondary); } &__list { @@ -2918,11 +2799,11 @@ a.account__display-name { display: block; &--dangerous { - color: $error-value-color; + color: var(--color-text-error); } &--highlighted { - color: $highlight-text-color; + color: var(--color-text-brand); } &-content { @@ -2956,7 +2837,7 @@ a.account__display-name { &:hover, &:active { &:not(:disabled, [aria-disabled='true']) { - background: var(--dropdown-border-color); + background: var(--color-bg-secondary); outline: 0; } } @@ -2964,12 +2845,12 @@ a.account__display-name { button:disabled, button[aria-disabled='true'] { - color: $dark-text-color; + color: var(--color-text-disabled); cursor: default; &:focus { - color: rgb(from $dark-text-color r g b / 70%); - background: var(--dropdown-border-color); + color: rgb(from var(--color-text-disabled) r g b / 70%); + background: var(--color-bg-disabled); outline: 0; } } @@ -3049,6 +2930,8 @@ a.account__display-name { flex-direction: column; contain: inline-size layout paint style; container: column / inline-size; + color: var(--color-text-primary); + background-color: var(--color-bg-primary); @media screen and (min-width: $no-gap-breakpoint) { max-width: 600px; @@ -3067,9 +2950,9 @@ a.account__display-name { width: 100%; gap: 8px; padding-bottom: env(safe-area-inset-bottom); - background: var(--background-color); + background: var(--color-bg-primary); backdrop-filter: $backdrop-blur-filter; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); box-sizing: border-box; .layout-multiple-columns & { @@ -3112,7 +2995,7 @@ a.account__display-name { color: inherit; &.active { - color: $highlight-text-color; + color: var(--color-text-brand); } &:focus { @@ -3120,22 +3003,22 @@ a.account__display-name { } &:focus-visible { - border-top-color: $ui-button-focus-outline-color; + border-top-color: var(--color-text-brand); border-radius: 0; } } } .tabs-bar__wrapper { - background: var(--background-color); + background: var(--color-bg-primary); backdrop-filter: $backdrop-blur-filter; position: sticky; top: 0; z-index: 2; - padding-top: 0; + border-top: 0; @media screen and (min-width: $no-gap-breakpoint) { - padding-top: 10px; + border-top: 10px solid var(--color-bg-ambient); } } @@ -3163,13 +3046,13 @@ a.account__display-name { flex-direction: column; > .scrollable { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; border-radius: 0 0 4px 4px; &.about, &.privacy-policy { - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); border-radius: 4px; @media screen and (max-width: $no-gap-breakpoint) { @@ -3239,7 +3122,7 @@ a.account__display-name { display: flex; flex: 1 1 auto; padding: 13px 3px 11px; - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; text-align: center; font-size: 16px; @@ -3372,7 +3255,7 @@ a.account__display-name { .navigation-panel { margin: 0; - border-inline-start: 1px solid var(--background-border-color); + border-inline-start: 1px solid var(--color-border-primary); height: 100dvh; } @@ -3422,7 +3305,7 @@ a.account__display-name { .columns-area__panels__pane--navigational .columns-area__panels__pane__inner { pointer-events: auto; - background: var(--background-color); + background: var(--color-bg-primary); position: fixed; width: 284px + 70px; inset-inline-end: -70px; @@ -3453,7 +3336,7 @@ a.account__display-name { .columns-area__panels__pane--overlay { pointer-events: auto; - background: color.change($base-overlay-background, $alpha: 0.5); + background: rgb(from var(--color-bg-overlay) r g b / 50%); z-index: 3; .columns-area__panels__pane__inner { @@ -3472,7 +3355,7 @@ a.account__display-name { gap: 8px; display: flex; flex-direction: column; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &:last-child { border-bottom: 0; @@ -3481,7 +3364,7 @@ a.account__display-name { &__source { font-size: 13px; line-height: 16px; - color: $dark-text-color; + color: var(--color-text-tertiary); @container (width >= 400px) { padding-inline-start: 60px; @@ -3522,14 +3405,14 @@ a.account__display-name { .display-name { font-size: 15px; line-height: 20px; - color: $secondary-text-color; + color: var(--color-text-primary); strong { font-weight: 700; } &__account { - color: $darker-text-color; + color: var(--color-text-secondary); display: block; } } @@ -3567,14 +3450,14 @@ a.account__display-name { position: absolute; inset-inline-start: 9px; top: -13px; - background: $ui-highlight-color; - border: 2px solid var(--background-color); + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); + border: 2px solid var(--color-bg-primary); padding: 1px 6px; border-radius: 6px; font-size: 10px; font-weight: 500; line-height: 14px; - color: $primary-text-color; } &__issue-badge { @@ -3582,7 +3465,7 @@ a.account__display-name { inset-inline-start: 11px; bottom: 1px; display: block; - background: $error-red; + background: var(--color-text-error); border-radius: 50%; width: 0.625rem; height: 0.625rem; @@ -3590,7 +3473,7 @@ a.account__display-name { } .column-link--transparent .icon-with-badge__badge { - border-color: var(--background-color); + border-color: var(--color-bg-primary); } .column-title { @@ -3608,7 +3491,7 @@ a.account__display-name { font-size: 16px; line-height: 24px; font-weight: 400; - color: $darker-text-color; + color: var(--color-text-secondary); } @media screen and (width >= 600px) { @@ -3617,11 +3500,11 @@ a.account__display-name { } .copy-paste-text { - background: lighten($ui-base-color, 4%); + background: var(--color-bg-secondary); border-radius: 8px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); padding: 16px; - color: $primary-text-color; + color: var(--color-text-primary); font-size: 15px; line-height: 22px; display: flex; @@ -3634,11 +3517,11 @@ a.account__display-name { &.focused { transition: none; outline: 0; - border-color: $highlight-text-color; + border-color: var(--color-text-brand); } &.copied { - border-color: $valid-value-color; + border-color: var(--color-text-success); transition: none; } @@ -3665,7 +3548,7 @@ a.account__display-name { margin-top: -20px; .app-form__avatar-input { - border: 2px solid var(--background-color); + border: 2px solid var(--color-bg-primary); position: absolute; inset-inline-start: -2px; bottom: -40px; @@ -3689,6 +3572,7 @@ a.account__display-name { flex-direction: column; height: calc(100% - 10px); overflow-y: auto; + scrollbar-width: thin; .compose-form { flex: 1 1 auto; @@ -3707,6 +3591,7 @@ a.account__display-name { flex: 1 1 auto; min-height: 0; overflow-y: auto; + scrollbar-width: thin; } &__list-panel { @@ -3718,7 +3603,7 @@ a.account__display-name { &__sep { width: 0; height: 24px; - border-left: 1px solid var(--background-border-color); + border-left: 1px solid var(--color-border-primary); } .column-link { @@ -3818,7 +3703,7 @@ a.account__display-name { flex: 0 0 auto; border: 0; background: transparent; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); margin: 10px 0; } @@ -3835,14 +3720,14 @@ a.account__display-name { overflow: hidden; display: flex; border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); } .drawer__inner { position: absolute; top: 0; inset-inline-start: 0; - background: var(--background-color); + background: var(--color-bg-primary); box-sizing: border-box; padding: 0; display: flex; @@ -3855,12 +3740,12 @@ a.account__display-name { } .drawer__inner__mastodon { - background: var(--background-color) - url('data:image/svg+xml;utf8,') - no-repeat bottom / 100% auto; + position: relative; + background: var(--color-bg-primary); flex: 1; min-height: 47px; display: none; + contain: content; > img { display: block; @@ -3880,7 +3765,7 @@ a.account__display-name { .drawer__header { flex: 0 0 auto; font-size: 16px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); margin-bottom: 10px; display: flex; flex-direction: row; @@ -3890,7 +3775,7 @@ a.account__display-name { a:hover, a:focus, a:active { - color: $primary-text-color; + color: var(--color-text-primary); } } @@ -3899,6 +3784,7 @@ a.account__display-name { overflow-x: hidden; flex: 1 1 auto; -webkit-overflow-scrolling: touch; + scrollbar-width: thin; &.optionally-scrollable { overflow-y: auto; @@ -3936,9 +3822,9 @@ a.account__display-name { box-sizing: border-box; width: 100%; background: transparent; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px 4px 0 0; - color: $highlight-text-color; + color: var(--color-text-brand); cursor: pointer; flex: 0 0 auto; font-size: 16px; @@ -3964,7 +3850,7 @@ a.account__display-name { background: transparent; border: 0; font-family: inherit; - color: $highlight-text-color; + color: var(--color-text-brand); cursor: pointer; white-space: nowrap; font-size: 16px; @@ -3990,13 +3876,19 @@ a.account__display-name { border-radius: 10px; padding: 0; user-select: none; - -webkit-tap-highlight-color: color.change( - $base-overlay-background, - $alpha: 0 - ); -webkit-tap-highlight-color: transparent; } +.react-toggle--focus, +.react-toggle:focus-within { + outline: var(--outline-focus-default); + outline-offset: 2px; + + .react-toggle-track { + border-color: transparent; + } +} + .react-toggle-screenreader-only, .sr-only { border: 0; @@ -4020,15 +3912,25 @@ a.account__display-name { height: 20px; padding: 0; border-radius: 10px; - background-color: $ui-primary-color; -} + background-color: rgb(from var(--color-bg-brand-softer) r g b / 50%); + border: 1px solid rgb(from var(--color-text-brand) r g b / 50%); + box-sizing: border-box; -.react-toggle--focus { - outline: $ui-button-focus-outline; -} + .react-toggle:hover:not(.react-toggle--disabled) & { + background-color: rgb( + from var(--color-bg-brand-softer) r g b / + calc(50% + var(--overlay-strength-brand)) + ); + } + + .react-toggle--checked & { + background-color: var(--color-bg-brand-base); + border-color: var(--color-bg-brand-base); + } -.react-toggle--checked .react-toggle-track { - background-color: $ui-highlight-color; + .react-toggle--checked:not(.react-toggle--disabled):hover & { + background-color: var(--color-bg-brand-base-hover); + } } .react-toggle-track-check, @@ -4043,30 +3945,21 @@ a.account__display-name { width: 16px; height: 16px; border-radius: 50%; - background-color: $ui-button-color; + background-color: var(--color-text-on-brand-base); box-sizing: border-box; transition: all 0.25s ease; transition-property: border-color, left; -} -.react-toggle--checked .react-toggle-thumb { - inset-inline-start: 32px - 16px - 2px; - border-color: $ui-highlight-color; -} - -.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track { - background: darken($ui-primary-color, 5%); -} - -.react-toggle.react-toggle--checked:hover:not(.react-toggle--disabled) - .react-toggle-track { - background: lighten($ui-highlight-color, 5%); + .react-toggle--checked & { + inset-inline-start: 32px - 16px - 2px; + border-color: var(--color-bg-brand-base); + } } .follow_requests-unlocked_explanation, .switch-to-advanced { - color: $light-text-color; - background-color: $ui-base-color; + color: var(--color-text-secondary); + background-color: var(--color-bg-secondary); padding: 15px; border-radius: 4px; margin-top: 4px; @@ -4075,7 +3968,7 @@ a.account__display-name { line-height: 18px; a { - color: $ui-button-tertiary-color; + color: var(--color-text-brand); font-weight: bold; } } @@ -4091,7 +3984,11 @@ a.account__display-name { text-decoration: none; overflow: hidden; white-space: nowrap; - color: $secondary-text-color; + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); background: transparent; border: 0; border-left: 4px solid transparent; @@ -4100,11 +3997,11 @@ a.account__display-name { &:hover, &:focus, &:active { - color: $primary-text-color; + color: var(--color-text-primary); } &.active { - color: $highlight-text-color; + color: var(--color-text-brand); } &:focus { @@ -4112,7 +4009,7 @@ a.account__display-name { } &:focus-visible { - border-color: $ui-button-focus-outline-color; + border-color: var(--color-text-brand); border-radius: 0; } @@ -4134,14 +4031,14 @@ a.account__display-name { font-size: 12px; line-height: 19px; font-weight: 500; - background: $ui-base-color; + background: var(--color-bg-primary); padding: 4px 8px; margin: -6px 10px; } .column-subheading { - background: var(--surface-background-color); - color: $darker-text-color; + background: var(--color-bg-secondary); + color: var(--color-text-secondary); padding: 8px 20px; font-size: 12px; font-weight: 500; @@ -4158,9 +4055,9 @@ a.account__display-name { } .getting-started { - color: $dark-text-color; + color: var(--color-text-tertiary); overflow: auto; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; &__trends { @@ -4170,14 +4067,14 @@ a.account__display-name { margin-top: 10px; h4 { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 10px; font-size: 12px; text-transform: uppercase; font-weight: 500; a { - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; } } @@ -4187,7 +4084,7 @@ a.account__display-name { padding: 10px; &__current { - color: $darker-text-color; + color: var(--color-text-secondary); } } } @@ -4208,9 +4105,6 @@ a.account__display-name { kbd { display: inline-block; - padding: 3px 5px; - background-color: lighten($ui-base-color, 8%); - border: 1px solid darken($ui-base-color, 4%); } } @@ -4219,11 +4113,11 @@ a.account__display-name { align-items: center; position: relative; font-size: 14px; - color: $darker-text-color; + color: var(--color-text-secondary); margin-top: 14px; text-decoration: none; overflow: hidden; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 8px; contain: inline-size layout paint style; @@ -4243,7 +4137,7 @@ a.account__display-name { cursor: pointer; & > div { - background: color.change($base-shadow-color, $alpha: 0.6); + background: rgb(from var(--color-shadow-primary) r g b / 60%); border-radius: 8px; padding: 12px 9px; backdrop-filter: $backdrop-blur-filter; @@ -4256,7 +4150,7 @@ a.account__display-name { button, a { display: inline; - color: $secondary-text-color; + color: var(--color-text-primary); background: transparent; border: 0; padding: 0 8px; @@ -4267,7 +4161,7 @@ a.account__display-name { &:hover, &:active, &:focus { - color: $primary-text-color; + color: var(--color-text-primary); } } @@ -4289,7 +4183,7 @@ a.status-card { .status-card__host, .status-card__author, .status-card__description { - color: $highlight-text-color; + color: var(--color-text-brand); } } } @@ -4305,7 +4199,7 @@ a.status-card { .status-card__host, .status-card__author, .status-card__description { - color: $highlight-text-color; + color: var(--color-text-brand); } } } @@ -4335,7 +4229,7 @@ a.status-card { font-weight: 700; font-size: 19px; line-height: 24px; - color: $primary-text-color; + color: var(--color-text-primary); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @@ -4370,7 +4264,7 @@ a.status-card { display: block; margin-top: 8px; font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -4393,7 +4287,7 @@ a.status-card { flex: 0 0 auto; width: 120px; aspect-ratio: 1; - background: lighten($ui-base-color, 8%); + background: var(--color-bg-secondary); position: relative; & > .icon { @@ -4427,7 +4321,7 @@ a.status-card { top: 0; inset-inline-start: 0; z-index: 0; - background: $base-overlay-background; + background: var(--color-bg-primary); &--hidden { display: none; @@ -4476,7 +4370,7 @@ a.status-card { display: flex; align-items: center; justify-content: center; - color: $dark-text-color; + color: var(--color-text-primary); background-color: transparent; border: 0; font-size: inherit; @@ -4492,11 +4386,11 @@ a.status-card { &:is(button) { &:hover { - background: var(--on-surface-color); + background: var(--color-bg-secondary); } &:focus-visible { - outline: 2px solid $ui-button-focus-outline-color; + outline: 2px solid var(--color-text-brand); outline-offset: -2px; } } @@ -4508,12 +4402,12 @@ a.status-card { } .load-gap { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); } .timeline-hint { text-align: center; - color: $dark-text-color; + color: var(--color-text-secondary); padding: 16px; box-sizing: border-box; width: 100%; @@ -4525,25 +4419,25 @@ a.status-card { } a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: none; &:hover, &:focus, &:active { text-decoration: underline; - color: lighten($highlight-text-color, 4%); + color: var(--color-text-brand-soft); } } &--with-descendants { - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); } } .regeneration-indicator { - color: $darker-text-color; - border: 1px solid var(--background-border-color); + color: var(--color-text-secondary); + border: 1px solid var(--color-border-primary); border-top: 0; cursor: default; display: flex; @@ -4569,7 +4463,7 @@ a.status-card { font-weight: 500; display: block; margin-bottom: 10px; - color: $darker-text-color; + color: var(--color-text-secondary); } span { @@ -4584,7 +4478,7 @@ a.status-card { z-index: 1; &.active { - box-shadow: 0 1px 0 color.change($highlight-text-color, $alpha: 0.3); + box-shadow: 0 1px 0 var(--color-bg-brand-softer); &::before { display: block; @@ -4600,8 +4494,8 @@ a.status-card { z-index: 1; background: radial-gradient( ellipse, - color.change($ui-highlight-color, $alpha: 0.23) 0%, - color.change($ui-highlight-color, $alpha: 0) 60% + rgb(from var(--color-bg-brand-base) r g b / 23%) 0%, + transparent 60% ); } } @@ -4615,7 +4509,7 @@ a.status-card { .column-header__select-row { border-width: 0 1px 1px; border-style: solid; - border-color: var(--background-border-color); + border-color: var(--color-border-primary); padding: 15px; display: flex; align-items: center; @@ -4631,12 +4525,12 @@ a.status-card { &__mode-button { margin-left: auto; - color: $highlight-text-color; + color: var(--color-text-brand); font-weight: bold; font-size: 14px; &:hover { - color: lighten($highlight-text-color, 6%); + color: var(--color-text-brand-soft); } } } @@ -4644,7 +4538,7 @@ a.status-card { .column-header { display: flex; font-size: 16px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px 4px 0 0; flex: 0 0 auto; cursor: pointer; @@ -4670,7 +4564,7 @@ a.status-card { flex: 1; &:focus-visible { - outline: $ui-button-icon-focus-outline; + outline: var(--outline-focus-default); } .logo { @@ -4684,18 +4578,18 @@ a.status-card { .column-header__back-button { flex: 1; - color: $highlight-text-color; + color: var(--color-text-brand); &.compact { flex: 0 0 auto; - color: $primary-text-color; + color: var(--color-text-primary); } } &.active { .column-header__icon { - color: $highlight-text-color; - text-shadow: 0 0 10px color.change($highlight-text-color, $alpha: 0.4); + color: var(--color-text-brand); + text-shadow: 0 0 10px var(--color-bg-brand-softer); } } @@ -4735,7 +4629,7 @@ a.status-card { justify-content: center; align-items: center; border: 0; - color: $darker-text-color; + color: var(--color-text-primary); background: transparent; cursor: pointer; font-size: 16px; @@ -4746,23 +4640,23 @@ a.status-card { } &:hover { - color: lighten($darker-text-color, 4%); + color: var(--color-text-secondary); } &:focus-visible { - outline: $ui-button-focus-outline; + outline: var(--outline-focus-default); } &.active { - color: $primary-text-color; + color: var(--color-text-brand); &:hover { - color: $primary-text-color; + color: var(--color-text-brand); } } &:disabled { - color: $dark-text-color; + color: var(--color-text-disabled); cursor: default; } } @@ -4775,16 +4669,16 @@ a.status-card { max-height: 70vh; overflow: hidden; overflow-y: auto; - color: $darker-text-color; + color: var(--color-text-secondary); transition: max-height 150ms ease-in-out, opacity 300ms linear; opacity: 1; z-index: 1; position: relative; - border-left: 1px solid var(--background-border-color); - border-right: 1px solid var(--background-border-color); - border-bottom: 1px solid var(--background-border-color); + border-left: 1px solid var(--color-border-primary); + border-right: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); @media screen and (max-width: $no-gap-breakpoint) { border-left: 0; @@ -4805,7 +4699,7 @@ a.status-card { height: 0; background: transparent; border: 0; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); margin: 10px 0; } } @@ -4817,7 +4711,7 @@ a.status-card { .column-header__setting-btn { &:hover, &:focus { - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: underline; } } @@ -4858,16 +4752,15 @@ a.status-card { } .column-header__issue-btn { - color: $warning-red; + color: var(--color-text-error); &:hover { - color: $error-red; text-decoration: underline; } } .loading-indicator { - color: $dark-text-color; + color: var(--color-text-secondary); font-size: 12px; font-weight: 400; text-transform: uppercase; @@ -4901,18 +4794,18 @@ a.status-card { } .icon-button .loading-indicator .circular-progress { - color: lighten($ui-base-color, 26%); + color: var(--color-text-tertiary); width: 12px; height: 12px; margin: 6px; } .load-more .loading-indicator .circular-progress { - color: lighten($ui-base-color, 26%); + color: var(--color-text-tertiary); } .circular-progress { - color: lighten($ui-base-color, 26%); + color: var(--color-text-tertiary); animation: 1.4s linear 0s infinite normal none running simple-rotate; circle { @@ -4996,8 +4889,8 @@ a.status-card { .video-error-cover { align-items: center; - background: $base-overlay-background; - color: $primary-text-color; + background: var(--color-bg-primary); + color: var(--color-text-primary); cursor: pointer; display: flex; flex-direction: column; @@ -5035,12 +4928,12 @@ a.status-card { padding: 0; margin: 0; border: 0; - color: $white; + color: var(--color-text-on-media); line-height: 20px; font-size: 14px; &__label { - background-color: color.change($black, $alpha: 0.45); + background-color: var(--color-bg-media); backdrop-filter: $backdrop-blur-filter; border-radius: 8px; padding: 12px 16px; @@ -5060,19 +4953,15 @@ a.status-card { &:hover, &:focus { .spoiler-button__overlay__label { - background-color: color.change($black, $alpha: 0.9); + background-color: rgb(from var(--color-bg-media-base) r g b / 90%); } } } } -.modal-container--preloader { - background: lighten($ui-base-color, 8%); -} - .account--panel { - border-top: 1px solid var(--background-border-color); - border-bottom: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); display: flex; flex-direction: row; padding: 10px 0; @@ -5090,7 +4979,7 @@ a.status-card { &__section { // FIXME: Legacy - color: $darker-text-color; + color: var(--color-text-secondary); cursor: default; display: block; font-weight: 500; @@ -5102,7 +4991,7 @@ a.status-card { section { padding: 16px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &:last-child { border-bottom: 0; @@ -5114,7 +5003,7 @@ a.status-card { line-height: 24px; letter-spacing: 0.5px; font-weight: 500; - color: $primary-text-color; + color: var(--color-text-primary); margin-bottom: 16px; } @@ -5143,7 +5032,11 @@ a.status-card { @include search-input; &::placeholder { - color: lighten($darker-text-color, 4%); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); } &::-moz-focus-inner { @@ -5157,7 +5050,7 @@ a.status-card { } &:focus { - background: lighten($ui-base-color, 4%); + background: var(--color-bg-secondary); } @media screen and (width <= 600px) { @@ -5166,7 +5059,7 @@ a.status-card { } &__placeholder { - color: $dark-text-color; + color: var(--color-text-tertiary); padding-inline-start: 2px; font-size: 12px; } @@ -5176,7 +5069,7 @@ a.status-card { } &__multi-value { - background: lighten($ui-base-color, 8%); + background: var(--color-bg-secondary); &__remove { cursor: pointer; @@ -5184,8 +5077,12 @@ a.status-card { &:hover, &:active, &:focus { - background: lighten($ui-base-color, 12%); - color: lighten($darker-text-color, 4%); + background: var(--color-bg-brand-softer); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); } } } @@ -5193,31 +5090,31 @@ a.status-card { &__multi-value__label, &__input, &__input-container { - color: $darker-text-color; + color: var(--color-text-secondary); } &__clear-indicator, &__dropdown-indicator { cursor: pointer; transition: none; - color: $dark-text-color; + color: var(--color-text-tertiary); &:hover, &:active, &:focus { - color: lighten($dark-text-color, 4%); + color: var(--color-text-secondary); } } &__indicator-separator { - background-color: lighten($ui-base-color, 8%); + background-color: var(--color-border-primary); } &__menu { @include search-popout; padding: 0; - background: $ui-secondary-color; + background: var(--color-bg-elevated); } &__menu-list { @@ -5225,13 +5122,14 @@ a.status-card { } &__option { - color: $inverted-text-color; + color: var(--color-text-primary); border-radius: 4px; font-size: 14px; &--is-focused, &--is-selected { - background: darken($ui-secondary-color, 10%); + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); } } } @@ -5250,12 +5148,12 @@ a.status-card { } .setting-toggle__label { - color: $darker-text-color; + color: var(--color-text-secondary); } .limited-account-hint { p { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 15px; font-weight: 500; margin-bottom: 20px; @@ -5263,7 +5161,7 @@ a.status-card { } .empty-column-indicator { - color: $dark-text-color; + color: var(--color-text-secondary); text-align: center; padding: 20px; font-size: 14px; @@ -5280,7 +5178,7 @@ a.status-card { } a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: none; &:hover { @@ -5307,7 +5205,7 @@ a.status-card { .error-column { padding: 20px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; display: flex; flex: 1 1 auto; @@ -5324,7 +5222,7 @@ a.status-card { &__message { text-align: center; - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 15px; line-height: 22px; @@ -5333,7 +5231,7 @@ a.status-card { line-height: 33px; font-weight: 700; margin-bottom: 15px; - color: $primary-text-color; + color: var(--color-text-primary); } p { @@ -5386,9 +5284,9 @@ a.status-card { position: relative; margin-top: 5px; z-index: 2; - background: var(--dropdown-background-color); + background: var(--color-bg-elevated); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--dropdown-border-color); + border: 1px solid var(--color-border-primary); box-shadow: var(--dropdown-shadow); border-radius: 5px; @@ -5413,8 +5311,8 @@ a.status-card { z-index: 4; top: -5px; inset-inline-start: -9px; - background: var(--dropdown-background-color); - border: 1px solid var(--dropdown-border-color); + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); border-radius: 4px; box-shadow: var(--dropdown-shadow); overflow: hidden; @@ -5429,7 +5327,7 @@ a.status-card { &:hover, &:focus, &:active { - background: var(--dropdown-border-color); + background: var(--color-border-primary); } } @@ -5446,7 +5344,7 @@ a.status-card { .upload-area { align-items: center; - background: color.change($base-overlay-background, $alpha: 0.8); + background: rgb(from var(--color-bg-overlay) r g b / 80%); display: flex; height: 100vh; justify-content: center; @@ -5480,8 +5378,8 @@ a.status-card { inset-inline-start: 0; z-index: -1; border-radius: 4px; - background: $ui-base-color; - box-shadow: 0 0 5px color.change($base-shadow-color, $alpha: 0.2); + background: var(--color-bg-elevated); + box-shadow: 0 0 5px var(--color-shadow-primary); } .upload-area__content { @@ -5490,15 +5388,15 @@ a.status-card { align-items: center; justify-content: center; text-align: center; - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 18px; font-weight: 500; - border: 2px dashed $ui-base-lighter-color; + border: 2px dashed var(--color-border-primary); border-radius: 4px; } .upload-progress { - color: $darker-text-color; + color: var(--color-text-secondary); overflow: hidden; display: flex; gap: 8px; @@ -5508,7 +5406,7 @@ a.status-card { .icon { width: 24px; height: 24px; - color: $ui-highlight-color; + color: var(--color-text-brand); } span { @@ -5527,7 +5425,7 @@ a.status-card { width: 100%; height: 6px; border-radius: 6px; - background: var(--background-color); + background: var(--color-bg-primary); position: relative; margin-top: 5px; } @@ -5537,7 +5435,7 @@ a.status-card { inset-inline-start: 0; top: 0; height: 6px; - background: $ui-highlight-color; + background: var(--color-text-brand); border-radius: 6px; } @@ -5571,7 +5469,7 @@ a.status-card { &:focus-visible { img { - outline: $ui-button-icon-focus-outline; + outline: var(--outline-focus-default); } } } @@ -5585,9 +5483,8 @@ a.status-card { .language-dropdown__dropdown, .visibility-dropdown__dropdown { box-shadow: var(--dropdown-shadow); - background: var(--dropdown-background-color); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--dropdown-border-color); + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); padding: 4px; border-radius: 4px; overflow: hidden; @@ -5613,8 +5510,6 @@ a.status-card { .privacy-dropdown__option, .visibility-dropdown__option { - --dropdown-text-color: $primary-text-color; - font-size: 14px; line-height: 20px; letter-spacing: 0.25px; @@ -5624,7 +5519,7 @@ a.status-card { align-items: center; gap: 12px; border-radius: 4px; - color: var(--dropdown-text-color); + color: var(--color-text-primary); // Make sure adjacent hover/active states don't have a meeting radius. &:hover + &:is(:focus, .active), @@ -5643,13 +5538,13 @@ a.status-card { &:hover, &:active { - background: var(--dropdown-border-color); + background: var(--color-bg-secondary); } &:focus, &.active { - background: $ui-highlight-color; - color: var(--dropdown-text-color); + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); outline: 0; .privacy-dropdown__option__content, @@ -5658,7 +5553,7 @@ a.status-card { .visibility-dropdown__option__content, .visibility-dropdown__option__content strong, .visibility-dropdown__option__additional { - color: var(--dropdown-text-color); + color: var(--color-text-on-brand-base); } } @@ -5666,7 +5561,7 @@ a.status-card { display: flex; align-items: center; justify-content: center; - color: $darker-text-color; + color: var(--color-text-secondary); cursor: help; } } @@ -5681,10 +5576,10 @@ a.status-card { .privacy-dropdown__option__content, .visibility-dropdown__option__content { flex: 1 1 auto; - color: $darker-text-color; + color: var(--color-text-secondary); strong { - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; display: block; @@ -5703,13 +5598,13 @@ a.status-card { .emoji-mart-search { padding: 10px; - background: var(--dropdown-background-color); + background: var(--color-bg-elevated); input { padding: 8px 12px; - background: $ui-base-color; - border: 1px solid var(--background-border-color); - color: $darker-text-color; + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); + color: var(--color-text-secondary); @media screen and (width <= 600px) { font-size: 16px; @@ -5722,7 +5617,7 @@ a.status-card { .emoji-mart-search-icon { inset-inline-end: 15px; opacity: 1; - color: $darker-text-color; + color: var(--color-text-primary); .icon { width: 18px; @@ -5730,13 +5625,13 @@ a.status-card { } &:disabled { - opacity: 0.38; + color: var(--color-text-secondary); } } .emoji-mart-scroll { padding: 0 10px 10px; - background: var(--dropdown-background-color); + background: var(--color-bg-elevated); } &__results { @@ -5745,7 +5640,7 @@ a.status-card { align-items: center; gap: 0.5em; cursor: pointer; - color: $primary-text-color; + color: var(--color-text-primary); font-size: 14px; line-height: 20px; letter-spacing: 0.25px; @@ -5757,23 +5652,23 @@ a.status-card { text-overflow: ellipsis; &__common-name { - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 400; } &:active, &:hover { - background: var(--dropdown-border-color); + background: var(--color-bg-secondary); } &:focus, &.active { - background: $ui-highlight-color; - color: $primary-text-color; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); outline: 0; .language-dropdown__dropdown__results__item__common-name { - color: $primary-text-color; + color: var(--color-text-on-brand-base); } } } @@ -5783,28 +5678,18 @@ a.status-card { .visibility-modal { &__quote-warning { - color: var(--nested-card-text); - background: - /* This is a bit of a silly hack for layering two background colours - * since --nested-card-background is too transparent for a tooltip */ - linear-gradient( - var(--nested-card-background), - var(--nested-card-background) - ), - linear-gradient(var(--background-color), var(--background-color)); - border: var(--nested-card-border); + color: var(--color-text-primary); + background: var(--color-bg-warning-softer); padding: 16px; border-radius: 4px; h3 { font-weight: 500; margin-bottom: 4px; - color: $darker-text-color; } p { font-size: 0.8em; - color: $dark-text-color; } } } @@ -5828,9 +5713,9 @@ a.status-card { &__button { display: flex; align-items: center; - color: $primary-text-color; - background: var(--input-background-color); - border: 1px solid var(--background-border-color); + color: var(--color-text-primary); + background: var(--color-bg-secondary-solid); + border: 1px solid var(--color-border-primary); padding: 8px 12px; width: 100%; text-align: left; @@ -5853,7 +5738,7 @@ a.status-card { &__helper { margin-top: 4px; font-size: 0.8em; - color: $dark-text-color; + color: var(--color-text-tertiary); } } @@ -5872,8 +5757,8 @@ a.status-card { inset-inline-start: 0; margin-top: -2px; width: 100%; - background: var(--input-background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); border-radius: 0 0 4px 4px; box-shadow: var(--dropdown-shadow); z-index: 99; @@ -5882,7 +5767,7 @@ a.status-card { h4 { text-transform: uppercase; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 500; padding: 0 10px; margin-bottom: 10px; @@ -5890,7 +5775,7 @@ a.status-card { .icon-button { padding: 0; - color: $darker-text-color; + color: var(--color-text-secondary); } .icon { @@ -5906,7 +5791,7 @@ a.status-card { } &__message { - color: $darker-text-color; + color: var(--color-text-secondary); padding: 0 10px; } @@ -5917,7 +5802,7 @@ a.status-card { border: 0; font: inherit; background: transparent; - color: $darker-text-color; + color: var(--color-text-secondary); padding: 10px; cursor: pointer; border-radius: 4px; @@ -5939,18 +5824,18 @@ a.status-card { &:focus, &:active, &.selected { - background: $ui-highlight-color; - color: $primary-text-color; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); .icon-button { - color: $primary-text-color; + color: inherit; } } mark { background: transparent; font-weight: 700; - color: $primary-text-color; + color: var(--color-text-primary); } span { @@ -6013,7 +5898,7 @@ a.status-card { grid-area: 1 / 1; transition: all 100ms linear; transition-property: transform, opacity; - color: $darker-text-color; + color: var(--color-text-secondary); } .search__icon.icon-search { @@ -6042,7 +5927,7 @@ a.status-card { } &:focus-visible { - box-shadow: 0 0 0 2px $ui-button-focus-outline-color; + box-shadow: 0 0 0 2px var(--color-text-brand); } &[aria-hidden='true'] { @@ -6053,19 +5938,19 @@ a.status-card { } .search-results__section { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &:last-child { border-bottom: 0; } &__header { - border-bottom: 1px solid var(--background-border-color); - background: var(--surface-background-color); + border-bottom: 1px solid var(--color-border-primary); + background: var(--color-bg-tertiary); padding: 15px; font-weight: 500; font-size: 14px; - color: $darker-text-color; + color: var(--color-text-secondary); display: flex; justify-content: space-between; @@ -6076,7 +5961,7 @@ a.status-card { } button { - color: $highlight-text-color; + color: var(--color-text-brand); padding: 0; border: 0; background: 0; @@ -6098,7 +5983,7 @@ a.status-card { .search-results__info { padding: 20px; - color: $darker-text-color; + color: var(--color-text-secondary); text-align: center; } @@ -6114,7 +5999,7 @@ a.status-card { inset-inline-end: 0; bottom: 0; opacity: 0.9; - background: $base-overlay-background; + background: var(--color-bg-overlay); transition: background 0.5s; } @@ -6174,7 +6059,14 @@ a.status-card { align-items: center; .icon-button { - color: color.change($white, $alpha: 0.7); + --default-icon-color: var(--color-text-on-media); + --default-bg-color: transparent; + --hover-icon-color: var(--color-text-on-media); + --hover-bg-color: rgb( + from var(--color-text-on-media) r g b / + var(--overlay-strength-secondary) + ); + padding: 8px; .icon { @@ -6182,17 +6074,6 @@ a.status-card { height: 24px; filter: var(--overlay-icon-shadow); } - - &:hover, - &:focus, - &:active { - color: $white; - background-color: color.change($white, $alpha: 0.15); - } - - &:focus { - background-color: color.change($white, $alpha: 0.3); - } } } } @@ -6242,23 +6123,30 @@ a.status-card { background: transparent; box-sizing: border-box; border: 0; - color: color.change($white, $alpha: 0.7); + color: var(--color-text-on-media); cursor: pointer; display: flex; align-items: center; font-size: 24px; height: 20vmax; margin: auto 0; - padding: 30px 15px; + padding: 30px 5px; position: absolute; top: 0; bottom: 0; transform: scaleX(var(--text-x-direction)); - &:hover, - &:focus, - &:active { - color: $white; + .icon { + border-radius: 5px; + padding: 10px; + } + + &:hover .icon, + &:focus .icon, + &:active .icon { + background: rgb( + from var(--color-text-on-media) r g b / var(--overlay-strength-secondary) + ); } } @@ -6286,54 +6174,36 @@ a.status-card { padding: 16px; .icon-button { - color: $white; + --default-icon-color: var(--color-text-on-media); + --default-bg-color: transparent; + --hover-icon-color: var(--color-text-on-media); + --hover-bg-color: rgb( + from var(--color-text-on-media) r g b / var(--overlay-strength-brand) + ); .icon { filter: var(--overlay-icon-shadow); } - &:hover, - &:focus, - &:active { - color: $white; - background-color: color.change($white, $alpha: 0.15); - } - - &:focus { - background-color: color.change($white, $alpha: 0.3); - } - &.active { - color: $highlight-text-color; - - &:hover, - &:focus, - &:active { - background: color.change($highlight-text-color, $alpha: 0.15); - } - - &:focus { - background: color.change($highlight-text-color, $alpha: 0.3); - } + --default-icon-color: var(--color-text-brand); + --hover-icon-color: var(--color-text-brand); + --hover-bg-color: var(--color-bg-brand-soft); } &.star-icon.active { - color: $gold-star; - - &:hover, - &:focus, - &:active { - background: color.change($gold-star, $alpha: 0.15); - } - - &:focus { - background: color.change($gold-star, $alpha: 0.3); - } + --default-icon-color: var(--color-text-favourite-highlight); + --hover-icon-color: var(--color-text-favourite-highlight); + --hover-bg-color: rgb( + from var(--color-text-favourite-highlight) r g b / + var(--overlay-strength-brand) + ); } &.disabled { - color: $white; - background-color: transparent; + --default-icon-color: var(--color-text-on-media); + --default-bg-color: transparent; + cursor: default; opacity: 0.4; } @@ -6349,9 +6219,8 @@ a.status-card { .media-modal__page-dot { flex: 0 0 auto; - background-color: $white; + background-color: rgb(from var(--color-text-on-media) r g b / 40%); filter: var(--overlay-icon-shadow); - opacity: 0.4; height: 6px; width: 6px; border-radius: 50%; @@ -6359,15 +6228,16 @@ a.status-card { padding: 0; border: 0; font-size: 0; - transition: opacity 0.2s ease-in-out; + transition: background-color 0.2s ease-in-out; &.active { - opacity: 1; + background-color: var(--color-text-on-media); } &:focus { - outline: 0; - background-color: $highlight-text-color; + opacity: 1; + outline: 2px solid var(--color-text-on-media); + outline-offset: 2px; } } @@ -6375,9 +6245,9 @@ a.status-card { width: 588px; min-height: 478px; flex-direction: column; - background: var(--modal-background-color); + background: var(--color-bg-primary); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--modal-border-color); + border: 1px solid var(--color-border-primary); border-radius: 16px; &__error { @@ -6419,9 +6289,9 @@ a.status-card { gap: 8px; padding: 24px; flex-direction: column; - background: var(--modal-background-color); + background: var(--color-bg-primary); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--modal-border-color); + border: 1px solid var(--color-border-primary); } &__top { @@ -6447,12 +6317,12 @@ a.status-card { align-items: center; font-size: 14px; line-height: 20px; - color: $darker-text-color; + color: var(--color-text-secondary); &__icon { border-radius: 64px; - background: $ui-highlight-color; - color: $white; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); display: flex; align-items: center; justify-content: center; @@ -6469,19 +6339,19 @@ a.status-card { h1 { font-size: 22px; line-height: 28px; - color: $primary-text-color; + color: var(--color-text-primary); } } &__confirmation { font-size: 14px; line-height: 20px; - color: $darker-text-color; + color: var(--color-text-secondary); h1 { font-size: 16px; line-height: 24px; - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; &:not(:only-child) { @@ -6491,12 +6361,12 @@ a.status-card { strong { font-weight: 700; - color: $primary-text-color; + color: var(--color-text-primary); } } &__status { - border: 1px solid var(--modal-border-color); + border: 1px solid var(--color-border-primary); border-radius: 8px; padding: 8px; cursor: pointer; @@ -6506,7 +6376,7 @@ a.status-card { align-items: center; gap: 4px; margin-bottom: 8px; - color: $dark-text-color; + color: var(--color-text-tertiary); bdi { color: inherit; @@ -6517,7 +6387,7 @@ a.status-card { display: -webkit-box; font-size: 15px; line-height: 22px; - color: $dark-text-color; + color: var(--color-text-tertiary); line-clamp: 4; -webkit-line-clamp: 4; -webkit-box-orient: vertical; @@ -6534,7 +6404,7 @@ a.status-card { margin-top: 0; font-size: 15px; line-height: 22px; - color: $dark-text-color; + color: var(--color-text-tertiary); } } @@ -6556,7 +6426,7 @@ a.status-card { } &--deemphasized { - color: $secondary-text-color; + color: var(--color-text-primary); } &__icon { @@ -6608,7 +6478,7 @@ a.status-card { } &.active { - background: var(--modal-background-variant-color); + background: var(--color-bg-secondary); padding-top: 24px; .safety-action-modal__bottom__collapsible { @@ -6626,7 +6496,7 @@ a.status-card { &__hint { font-size: 14px; line-height: 20px; - color: $dark-text-color; + color: var(--color-text-tertiary); } .link-button { @@ -6640,14 +6510,14 @@ a.status-card { width: 588px; max-height: 80vh; flex-direction: column; - background: var(--modal-background-color); + background: var(--color-bg-elevated); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--modal-border-color); + border: 1px solid var(--color-border-primary); border-radius: 16px; &__header { box-sizing: border-box; - border-bottom: 1px solid var(--modal-border-color); + border-bottom: 1px solid var(--color-border-primary); display: flex; align-items: center; justify-content: space-between; @@ -6671,7 +6541,7 @@ a.status-card { &__description { margin: 24px 24px 0; - color: $darker-text-color; + color: var(--color-text-secondary); a { color: inherit; @@ -6692,7 +6562,7 @@ a.status-card { align-items: center; justify-content: center; padding: 24px; - background: #000; + background: var(--color-bg-media-base); img { display: block; @@ -6700,7 +6570,7 @@ a.status-card { img, .gifv video { - outline: 1px solid var(--media-outline-color); + outline: 1px solid var(--color-border-media); outline-offset: -1px; border-radius: 8px; } @@ -6724,9 +6594,9 @@ a.status-card { } &__popout { - background: var(--dropdown-background-color); + background: var(--color-bg-elevated); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--dropdown-border-color); + border: 1px solid var(--color-border-primary); box-shadow: var(--dropdown-shadow); max-width: 320px; padding: 16px; @@ -6734,7 +6604,7 @@ a.status-card { z-index: 9999 !important; font-size: 14px; line-height: 20px; - color: $darker-text-color; + color: var(--color-text-secondary); } .copy-paste-text { @@ -6746,22 +6616,16 @@ a.status-card { display: inline-flex; align-items: center; gap: 4px; - - kbd { - padding: 3px 5px; - border: 1px solid var(--background-border-color); - border-radius: 4px; - } } .boost-modal, .report-modal, .actions-modal, .compare-history-modal { - background: var(--background-color); - color: $primary-text-color; + background: var(--color-bg-primary); + color: var(--color-text-primary); border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); overflow: hidden; max-width: 90vw; width: 480px; @@ -6785,43 +6649,20 @@ a.status-card { } } -.boost-modal__action-bar { - display: flex; - justify-content: space-between; - align-items: center; - background: $ui-secondary-color; - padding: 15px; - - & > div { - flex: 1 1 auto; - text-align: end; - color: $lighter-text-color; - padding-inline-end: 10px; - } - - .icon { - vertical-align: middle; - } - - .button { - flex: 0 0 auto; - } -} - .report-modal { width: 90vw; max-width: 700px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); } .report-dialog-modal { max-width: 90vw; width: 480px; height: 80vh; - background: var(--background-color); - color: $primary-text-color; + background: var(--color-bg-primary); + color: var(--color-text-primary); border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); overflow: hidden; position: relative; flex-direction: column; @@ -6829,7 +6670,7 @@ a.status-card { &__container { box-sizing: border-box; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); padding: 20px; flex-grow: 1; display: flex; @@ -6859,12 +6700,12 @@ a.status-card { &__lead { font-size: 17px; line-height: 22px; - color: $secondary-text-color; + color: var(--color-text-primary); margin-bottom: 30px; a { text-decoration: none; - color: $highlight-text-color; + color: var(--color-text-brand); font-weight: 500; &:hover { @@ -6889,12 +6730,12 @@ a.status-card { } .status__content a { - color: $highlight-text-color; + color: var(--color-text-brand); } .status__content, .status__content p { - color: $primary-text-color; + color: var(--color-text-primary); } .dialog-option { @@ -6903,8 +6744,7 @@ a.status-card { } .dialog-option .poll__input { - border-color: $darker-text-color; - color: $ui-secondary-color; + color: var(--color-text-secondary); display: inline-flex; align-items: center; justify-content: center; @@ -6917,20 +6757,21 @@ a.status-card { &:active, &:focus, &:hover { - border-color: $valid-value-color; + border-color: var(--color-text-success); border-width: 4px; } &.active { - border-color: $valid-value-color; - background: $valid-value-color; + color: var(--color-text-on-success-base); + background: var(--color-bg-success-base); + border-color: var(--color-bg-success-base); } } .poll__option.dialog-option { padding: 15px 0; flex: 0 0 auto; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &:last-child { border-bottom: 0; @@ -6938,13 +6779,13 @@ a.status-card { & > .poll__option__text { font-size: 13px; - color: $secondary-text-color; + color: var(--color-text-primary); strong { font-size: 17px; font-weight: 500; line-height: 22px; - color: $primary-text-color; + color: var(--color-text-primary); display: block; margin-bottom: 4px; @@ -6963,15 +6804,15 @@ a.status-card { display: block; box-sizing: border-box; width: 100%; - color: $primary-text-color; - background: $ui-base-color; + color: var(--color-text-primary); + background: var(--color-bg-secondary); padding: 10px; font-family: inherit; font-size: 17px; line-height: 22px; resize: vertical; border: 0; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); outline: 0; border-radius: 4px; margin: 20px 0; @@ -6996,16 +6837,16 @@ a.status-card { } .button.button-secondary { - border-color: $ui-button-destructive-background-color; - color: $ui-button-destructive-background-color; + border-color: var(--color-text-error); + color: var(--color-text-error); flex: 0 0 auto; &:hover, &:focus, &:active { - background: $ui-button-destructive-background-color; - border-color: $ui-button-destructive-background-color; - color: $white; + color: var(--color-text-on-error-base); + background: var(--color-bg-error-base); + border-color: var(--color-bg-error-base); } } @@ -7024,55 +6865,6 @@ a.status-card { } } -.report-modal__container { - display: flex; - border-top: 1px solid var(--background-border-color); - - @media screen and (width <= 480px) { - flex-wrap: wrap; - overflow-y: auto; - } -} - -.report-modal__statuses, -.report-modal__comment { - box-sizing: border-box; - width: 50%; - min-width: 50%; - - @media screen and (width <= 480px) { - width: 100%; - } -} - -.report-modal__statuses, -.focal-point-modal__content { - flex: 1 1 auto; - min-height: 20vh; - max-height: 80vh; - overflow-y: auto; - overflow-x: hidden; - - .status__content a { - color: $highlight-text-color; - } - - .status__content, - .status__content p { - color: $inverted-text-color; - } - - @media screen and (width <= 480px) { - max-height: 10vh; - } -} - -.focal-point-modal__content { - @media screen and (width <= 480px) { - max-height: 40vh; - } -} - .setting-divider { background: transparent; border: 0; @@ -7084,9 +6876,9 @@ a.status-card { .actions-modal { border-radius: 8px 8px 0 0; - background: var(--dropdown-background-color); + background: var(--color-bg-elevated); backdrop-filter: $backdrop-blur-filter; - border-color: var(--dropdown-border-color); + border-color: var(--color-border-primary); box-shadow: var(--dropdown-shadow); max-height: 80vh; max-width: 80vw; @@ -7113,7 +6905,7 @@ a.status-card { &:hover, &:active, &:focus { - background: var(--dropdown-border-color); + background: var(--color-bg-brand-softer); } } } @@ -7149,7 +6941,7 @@ a.status-card { .compare-history-modal { .report-modal__target { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); } &__container { @@ -7159,7 +6951,7 @@ a.status-card { } .status__content { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 19px; line-height: 24px; @@ -7170,13 +6962,13 @@ a.status-card { } a { - color: $highlight-text-color; + color: var(--color-text-brand); } hr { height: 0.25rem; padding: 0; - background-color: $ui-secondary-color; + background-color: var(--color-text-primary); border: 0; margin: 20px 0; } @@ -7190,7 +6982,7 @@ a.status-card { } .loading-bar { - background-color: $highlight-text-color; + background-color: var(--color-bg-brand-base); height: 3px; position: fixed; top: 0; @@ -7208,10 +7000,10 @@ a.status-card { &__pill { display: block; - color: $white; - border: 0; - background: color.change($black, $alpha: 0.65); + color: var(--color-text-on-media); + background: var(--color-bg-media); backdrop-filter: $backdrop-blur-filter; + border: 0; padding: 3px 12px; border-radius: 99px; font-size: 14px; @@ -7233,10 +7025,10 @@ a.status-card { .relationship-tag { display: block; text-align: center; - color: $white; - border: 0; - background: color.change($black, $alpha: 0.65); + color: var(--color-text-on-media); + background: var(--color-bg-media); backdrop-filter: $backdrop-blur-filter; + border: 0; padding: 3px 8px; border-radius: 4px; font-size: 12px; @@ -7257,8 +7049,8 @@ a.status-card { } .info-tooltip { - color: $white; - background: color.change($black, $alpha: 0.65); + color: var(--color-text-on-media); + background: var(--color-bg-media); backdrop-filter: $backdrop-blur-filter; border-radius: 4px; box-shadow: var(--dropdown-shadow); @@ -7268,18 +7060,12 @@ a.status-card { max-width: 22em; max-height: 30em; overflow-y: auto; + z-index: 10; &--solid { - color: var(--nested-card-text); - background: - /* This is a bit of a silly hack for layering two background colours - * since --nested-card-background is too transparent for a tooltip */ - linear-gradient( - var(--nested-card-background), - var(--nested-card-background) - ), - linear-gradient(var(--background-color), var(--background-color)); - border: var(--nested-card-border); + color: var(--color-text-primary); + background: var(--color-bg-elevated); + border: 1px solid var(--color-border-primary); } h4 { @@ -7304,17 +7090,17 @@ a.status-card { .attachment-list { display: flex; font-size: 14px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; margin-top: 16px; overflow: hidden; &__icon { flex: 0 0 auto; - color: $dark-text-color; + color: var(--color-text-tertiary); padding: 8px 18px; cursor: default; - border-inline-end: 1px solid var(--background-border-color); + border-inline-end: 1px solid var(--color-border-primary); display: flex; flex-direction: column; align-items: center; @@ -7337,7 +7123,7 @@ a.status-card { a { text-decoration: none; - color: $dark-text-color; + color: var(--color-text-tertiary); font-weight: 500; &:hover { @@ -7355,7 +7141,7 @@ a.status-card { } .icon { - color: $dark-text-color; + color: var(--color-text-tertiary); vertical-align: middle; } } @@ -7452,7 +7238,7 @@ a.status-card { position: relative; border-radius: 8px; overflow: hidden; - outline: 1px solid var(--media-outline-color); + outline: 1px solid var(--color-border-media); outline-offset: -1px; z-index: 1; @@ -7488,8 +7274,8 @@ a.status-card { } .icon { - color: $white; - filter: var(--overlay-icon-shadow); + color: var(--color-text-on-media); + filter: drop-shadow(0 0 6px var(--color-bg-media-base)); } } @@ -7502,7 +7288,7 @@ a.status-card { cursor: pointer; display: block; text-decoration: none; - color: $secondary-text-color; + color: var(--color-text-primary); position: relative; z-index: -1; @@ -7525,7 +7311,7 @@ a.status-card { top: 0; inset-inline-start: 0; z-index: -2; - background: $base-overlay-background; + background: var(--color-bg-overlay); &--hidden { display: none; @@ -7562,6 +7348,12 @@ a.status-card { } .audio-player { + /* These are only fallback values, the AudioPlayer component inserts + * the real colours dynamically as inline styles */ + --player-foreground-color: var(--color-text-on-media); + --player-background-color: var(--color-bg-media-base); + --player-accent-color: var(--color-text-brand); + box-sizing: border-box; container: audio-player / inline-size; position: relative; @@ -7571,9 +7363,9 @@ a.status-card { width: 100%; aspect-ratio: 16 / 9; color: var(--player-foreground-color); - background: var(--player-background-color, var(--background-color)); + background: var(--player-background-color); border-radius: 8px; - outline: 1px solid var(--media-outline-color); + outline: 1px solid var(--color-border-media); outline-offset: -1px; &__controls { @@ -7706,13 +7498,13 @@ a.status-card { .video-player { overflow: hidden; position: relative; - background: $base-shadow-color; + color: var(--color-text-on-media); + background: var(--color-bg-media); max-height: 460px; border-radius: 8px; box-sizing: border-box; - color: $white; display: flex; - outline: 1px solid var(--media-outline-color); + outline: 1px solid var(--color-border-media); outline-offset: -1px; z-index: 2; @@ -7746,8 +7538,8 @@ a.status-card { box-sizing: border-box; background: linear-gradient( 0deg, - color.change($base-shadow-color, $alpha: 0.85) 0, - color.change($base-shadow-color, $alpha: 0.45) 60%, + rgb(from var(--color-bg-media-base) r g b / 85%) 0, + rgb(from var(--color-bg-media-base) r g b / 45%) 60%, transparent ); padding: 0 15px; @@ -7787,8 +7579,8 @@ a.status-card { height: 100%; z-index: 4; border: 0; - background: $base-overlay-background; - color: $darker-text-color; + background: var(--color-bg-primary); + color: var(--color-text-secondary); transition: none; pointer-events: none; @@ -7799,7 +7591,11 @@ a.status-card { &:hover, &:active, &:focus { - color: lighten($darker-text-color, 7%); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); } } @@ -7843,13 +7639,13 @@ a.status-card { flex: 0 0 auto; background: transparent; border: 0; - color: color.change($white, $alpha: 0.75); + color: rgb(from var(--color-text-on-media) r g b / 75%); font-weight: 500; &:active, &:hover, &:focus { - color: $white; + color: var(--color-text-on-media); } } } @@ -7868,24 +7664,16 @@ a.status-card { &__time-sep, &__time-total, &__time-current { + color: var(--color-text-on-media); font-size: 14px; font-weight: 500; } - &__time-current { - color: $white; - } - &__time-sep { display: inline-block; margin: 0 6px; } - &__time-sep, - &__time-total { - color: $white; - } - &__volume { flex: 0 0 auto; display: inline-flex; @@ -7907,7 +7695,7 @@ a.status-card { &::before { content: ''; width: 50px; - background: color.change($white, $alpha: 0.35); + background: rgb(from var(--color-text-on-media) r g b / 35%); border-radius: 4px; display: block; position: absolute; @@ -7925,7 +7713,7 @@ a.status-card { inset-inline-start: 0; top: 50%; transform: translate(0, -50%); - background: $white; + background: var(--color-text-on-media); } &__handle { @@ -7938,8 +7726,8 @@ a.status-card { inset-inline-start: 0; margin-inline-start: -6px; transform: translate(0, -50%); - background: $white; - box-shadow: 1px 2px 6px color.change($base-shadow-color, $alpha: 0.2); + background: var(--color-text-on-media); + box-shadow: 1px 2px 6px var(--color-shadow-primary); opacity: 0; .no-reduce-motion & { @@ -7959,7 +7747,7 @@ a.status-card { text-decoration: none; font-size: 14px; font-weight: 500; - color: $white; + color: var(--color-text-on-media); &:hover, &:active, @@ -7977,7 +7765,7 @@ a.status-card { &::before { content: ''; width: 100%; - background: color.change($white, $alpha: 0.35); + background: rgb(from var(--color-text-on-media) r g b / 35%); border-radius: 4px; display: block; position: absolute; @@ -7992,11 +7780,11 @@ a.status-card { height: 4px; border-radius: 4px; top: 14px; - background: $white; + background: var(--color-text-on-media); } &__buffer { - background: color.change($white, $alpha: 0.2); + background: rgb(from var(--color-text-on-media) r g b / 20%); } &__handle { @@ -8008,8 +7796,8 @@ a.status-card { height: 12px; top: 10px; margin-inline-start: -6px; - background: $white; - box-shadow: 1px 2px 6px color.change($base-shadow-color, $alpha: 0.2); + background: var(--color-text-on-media); + box-shadow: 1px 2px 6px var(--color-shadow-primary); .no-reduce-motion & { transition: opacity 0.1s ease; @@ -8033,9 +7821,9 @@ a.status-card { top: 50%; inset-inline-start: 50%; transform: translate(-50%, -50%); - background: color.change($base-shadow-color, $alpha: 0.45); + color: var(--color-text-on-media); + background: var(--color-bg-media); backdrop-filter: $backdrop-blur-filter; - color: $white; border-radius: 8px; padding: 16px 24px; display: flex; @@ -8082,7 +7870,7 @@ a.status-card { .scrollable .account-card__title__avatar { img { - border: 2px solid var(--background-color); + border: 2px solid var(--color-bg-primary); } .account__avatar { @@ -8097,7 +7885,7 @@ a.status-card { } .scrollable .account-card__bio::after { - background: linear-gradient(to left, var(--background-color), transparent); + background: linear-gradient(to left, var(--color-bg-primary), transparent); } .account-gallery__container { @@ -8117,7 +7905,7 @@ a.status-card { .notification__filter-bar, .account__section-headline { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; cursor: default; display: flex; @@ -8138,7 +7926,7 @@ a.status-card { a { display: block; flex: 1 1 auto; - color: $darker-text-color; + color: var(--color-text-secondary); padding: 15px 0; font-size: 14px; font-weight: 500; @@ -8149,7 +7937,7 @@ a.status-card { white-space: nowrap; &.active { - color: $primary-text-color; + color: var(--color-text-primary); &::before { display: block; @@ -8161,7 +7949,7 @@ a.status-card { width: 40px; height: 3px; border-radius: 4px 4px 0 0; - background: $highlight-text-color; + background: var(--color-text-brand); } } } @@ -8173,7 +7961,7 @@ a.status-card { } .filter-form { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &__column { display: flex; @@ -8203,7 +7991,7 @@ a.status-card { text-overflow: ellipsis; cursor: pointer; gap: 10px; - color: $secondary-text-color; + color: var(--color-text-primary); input[type='radio'], input[type='checkbox'] { @@ -8215,7 +8003,7 @@ a.status-card { align-items: center; justify-content: center; position: relative; - border: 2px solid $secondary-text-color; + border: 2px solid var(--color-text-primary); box-sizing: border-box; width: 20px; height: 20px; @@ -8224,7 +8012,7 @@ a.status-card { &.checked, &.indeterminate { - border-color: $ui-highlight-color; + border-color: var(--color-text-brand); } .icon { @@ -8240,7 +8028,7 @@ a.status-card { border-radius: 50%; width: calc(100% - 4px); height: calc(100% - 4px); - background: $ui-highlight-color; + background: var(--color-text-brand); } .check-box { @@ -8251,8 +8039,8 @@ a.status-card { &.checked, &.indeterminate { - background: $ui-highlight-color; - color: $white; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); } } } @@ -8269,11 +8057,11 @@ noscript { div { font-size: 14px; margin: 30px auto; - color: $secondary-text-color; + color: var(--color-text-primary); max-width: 400px; a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: underline; &:hover { @@ -8301,13 +8089,13 @@ noscript { .follow-request-banner, .account-memorial-banner { padding: 20px; - background: var(--surface-background-color); + background: var(--color-bg-tertiary); display: flex; align-items: center; flex-direction: column; &__message { - color: $darker-text-color; + color: var(--color-text-secondary); padding: 8px 0; padding-top: 0; padding-bottom: 4px; @@ -8344,7 +8132,7 @@ noscript { justify-content: flex-start; gap: 15px; align-items: center; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; label { @@ -8368,7 +8156,7 @@ noscript { inset-inline-start: 0; width: 100%; height: 100%; - background: color.change($base-overlay-background, $alpha: 0.5); + background: rgb(from var(--color-bg-overlay) r g b / 50%); } .focal-point { @@ -8388,15 +8176,15 @@ noscript { width: 100px; height: 100px; transform: translate(-50%, -50%); - border: 2px solid #fff; + border: 2px solid var(--color-text-on-media); border-radius: 50%; - box-shadow: 0 0 0 9999em color.change($base-shadow-color, $alpha: 0.35); + box-shadow: 0 0 0 9999em var(--color-shadow-primary); pointer-events: none; } } .account__header__content { - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 14px; font-weight: 400; overflow: hidden; @@ -8448,8 +8236,8 @@ noscript { overflow: hidden; height: 145px; position: relative; - background: darken($ui-base-color, 4%); - border-bottom: 1px solid var(--background-border-color); + background: var(--color-bg-tertiary); + border-bottom: 1px solid var(--color-border-primary); img { object-fit: cover; @@ -8463,15 +8251,15 @@ noscript { &__bar { position: relative; padding: 0 20px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); .avatar { display: block; flex: 0 0 auto; .account__avatar { - background: var(--background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); border-radius: var(--avatar-border-radius); } } @@ -8509,7 +8297,7 @@ noscript { h1 { font-size: 17px; line-height: 22px; - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 600; overflow: hidden; white-space: nowrap; @@ -8521,7 +8309,7 @@ noscript { gap: 4px; font-size: 14px; line-height: 20px; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 400; overflow: hidden; text-overflow: ellipsis; @@ -8592,7 +8380,7 @@ noscript { } .icon-button { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; box-sizing: content-box; padding: 5px; @@ -8603,26 +8391,26 @@ noscript { } &.copied { - border-color: $valid-value-color; + border-color: var(--color-text-success); } } } &__bio { .account__header__content { - color: $primary-text-color; + color: var(--color-text-primary); } .account__header__fields { margin: 0; margin-top: 16px; border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); dl { display: block; padding: 11px 16px; - border-bottom-color: var(--background-border-color); + border-bottom-color: var(--color-border-primary); } dd, @@ -8637,15 +8425,15 @@ noscript { width: auto; background: transparent; text-transform: uppercase; - color: $dark-text-color; + color: var(--color-text-tertiary); } dd { - color: $darker-text-color; + color: var(--color-text-secondary); } a { - color: lighten($ui-highlight-color, 8%); + color: var(--color-text-brand); } .icon { @@ -8654,7 +8442,7 @@ noscript { } .verified { - border: 1px solid color.change($valid-value-color, $alpha: 0.5); + border: 1px solid var(--color-text-success); margin-top: -1px; margin-inline: -1px; @@ -8671,7 +8459,7 @@ noscript { dt, dd { - color: $valid-value-color; + color: var(--color-text-success); } dd { @@ -8685,7 +8473,7 @@ noscript { } a { - color: $valid-value-color; + color: var(--color-text-success); } } } @@ -8696,28 +8484,28 @@ noscript { &__links { font-size: 14px; - color: $darker-text-color; + color: var(--color-text-secondary); margin: 0 -10px; padding-top: 16px; padding-bottom: 10px; a { display: inline-block; - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; padding: 5px 10px; font-weight: 500; strong { font-weight: 700; - color: $primary-text-color; + color: var(--color-text-primary); } } } } &__account-note { - color: $primary-text-color; + color: var(--color-text-primary); font-size: 14px; font-weight: 400; margin-bottom: 10px; @@ -8740,7 +8528,7 @@ noscript { display: block; font-size: 12px; font-weight: 500; - color: $darker-text-color; + color: var(--color-text-secondary); text-transform: uppercase; margin-bottom: 5px; } @@ -8749,7 +8537,7 @@ noscript { display: block; box-sizing: border-box; width: calc(100% + 20px); - color: $secondary-text-color; + color: var(--color-text-primary); background: transparent; padding: 10px; margin: 0 -10px; @@ -8761,12 +8549,12 @@ noscript { border-radius: 4px; &::placeholder { - color: $dark-text-color; + color: var(--color-text-tertiary); opacity: 1; } &:focus { - background: $ui-base-color; + background: var(--color-bg-brand-softer); } } } @@ -8776,12 +8564,12 @@ noscript { align-items: center; gap: 10px; margin-block: 16px; - color: $darker-text-color; + color: var(--color-text-secondary); a:any-link { font-weight: 500; text-decoration: none; - color: $primary-text-color; + color: var(--color-text-primary); } } } @@ -8799,7 +8587,7 @@ noscript { .verified-badge { display: inline-flex; align-items: center; - color: $valid-value-color; + color: var(--color-text-success); gap: 4px; overflow: hidden; white-space: nowrap; @@ -8826,12 +8614,12 @@ noscript { display: flex; align-items: center; padding: 16px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); gap: 8px; &__name { flex: 1 1 auto; - color: $dark-text-color; + color: var(--color-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -8841,7 +8629,7 @@ noscript { } a { - color: $darker-text-color; + color: var(--color-text-primary); text-decoration: none; font-size: 14px; font-weight: 500; @@ -8865,7 +8653,7 @@ noscript { font-size: 24px; font-weight: 500; text-align: end; - color: $secondary-text-color; + color: var(--color-text-primary); text-decoration: none; } @@ -8874,60 +8662,60 @@ noscript { width: 50px; path:first-child { - fill: color.change($highlight-text-color, $alpha: 0.25) !important; + fill: var(--color-graph-primary-fill) !important; fill-opacity: 1 !important; } path:last-child { - stroke: lighten($highlight-text-color, 6%) !important; + stroke: var(--color-graph-primary-stroke) !important; fill: none !important; } } &--requires-review { .trends__item__name { - color: $gold-star; + color: var(--color-text-warning); a { - color: $gold-star; + color: var(--color-text-warning); } } .trends__item__current { - color: $gold-star; + color: var(--color-text-warning); } .trends__item__sparkline { path:first-child { - fill: color.change($gold-star, $alpha: 0.25) !important; + fill: var(--color-graph-warning-fill) !important; } path:last-child { - stroke: lighten($gold-star, 6%) !important; + stroke: var(--color-graph-warning-stroke) !important; } } } &--disabled { .trends__item__name { - color: lighten($ui-base-color, 12%); + color: var(--color-text-disabled); a { - color: lighten($ui-base-color, 12%); + color: var(--color-text-disabled); } } .trends__item__current { - color: lighten($ui-base-color, 12%); + color: var(--color-text-disabled); } .trends__item__sparkline { path:first-child { - fill: rgba(lighten($ui-base-color, 12%), 0.25) !important; + fill: var(--color-graph-disabled-fill) !important; } path:last-child { - stroke: lighten(lighten($ui-base-color, 12%), 6%) !important; + stroke: var(--color-graph-disabled-stroke) !important; } } } @@ -8940,12 +8728,12 @@ noscript { .conversation { display: flex; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 5px; padding-bottom: 0; &:focus { - background: lighten($ui-base-color, 2%); + background: var(--color-bg-secondary); outline: 0; } @@ -8959,7 +8747,7 @@ noscript { &__unread { display: inline-block; - background: $highlight-text-color; + background: var(--color-text-brand); border-radius: 50%; width: 0.625rem; height: 0.625rem; @@ -8981,12 +8769,12 @@ noscript { &__relative-time { font-size: 15px; - color: $darker-text-color; + color: var(--color-text-secondary); padding-inline-start: 15px; } &__names { - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 15px; white-space: nowrap; overflow: hidden; @@ -8996,7 +8784,7 @@ noscript { flex-grow: 1; a { - color: $primary-text-color; + color: var(--color-text-primary); text-decoration: none; &:hover, @@ -9028,7 +8816,7 @@ noscript { } &__root { - background: lighten($ui-base-color, 8%); + background: var(--color-bg-brand-softer); font-size: 13px; display: flex; align-items: flex-end; @@ -9076,7 +8864,7 @@ noscript { top: 19px; inset-inline-end: 19px; display: block; - background: $highlight-text-color; + background: var(--color-text-brand); border-radius: 50%; width: 0.625rem; height: 0.625rem; @@ -9084,7 +8872,7 @@ noscript { &__pagination { padding: 15px; - color: $darker-text-color; + color: var(--color-text-secondary); position: absolute; bottom: 3px; inset-inline-end: 0; @@ -9112,10 +8900,15 @@ noscript { &__item { flex-shrink: 0; - background: lighten($ui-base-color, 12%); - color: $darker-text-color; - border: 0; + background: var(--color-bg-brand-softer); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + border: 1px solid var(--color-border-on-bg-brand-softer); border-radius: 3px; + box-sizing: border-box; margin: 2px; cursor: pointer; user-select: none; @@ -9154,21 +8947,18 @@ noscript { &:hover, &:focus, &:active { - background: lighten($ui-base-color, 16%); + color: var(--color-text-primary); + background: var(--color-bg-brand-soft); transition: all 200ms ease-out; transition-property: background-color, color; - color: lighten($darker-text-color, 4%); } &.active { + color: var(--color-text-brand); + background-color: var(--color-bg-brand-softer); + border-color: var(--color-text-brand); transition: all 100ms ease-in; transition-property: background-color, color; - background-color: color.mix( - lighten($ui-base-color, 12%), - $ui-highlight-color, - 80% - ); - color: lighten($highlight-text-color, 8%); } } @@ -9182,7 +8972,7 @@ noscript { } .emoji-button { - color: $darker-text-color; + color: var(--color-text-secondary); margin: 0; font-size: 16px; width: auto; @@ -9199,7 +8989,11 @@ noscript { &:active, &:focus { opacity: 1; - color: lighten($darker-text-color, 4%); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); transition: all 200ms ease-out; transition-property: background-color, color; } @@ -9229,7 +9023,7 @@ noscript { inset-inline-start: 0; width: 100%; height: 100%; - border-inline-start: 4px solid $highlight-text-color; + border-inline-start: 4px solid var(--color-text-brand); pointer-events: none; } } @@ -9244,9 +9038,9 @@ noscript { &__footer { border-radius: 0 0 4px 4px; - background: var(--modal-background-variant-color); + background: var(--color-bg-secondary); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--modal-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; padding: 12px; display: flex; @@ -9255,9 +9049,9 @@ noscript { &__header { border-radius: 4px 4px 0 0; - background: var(--modal-background-variant-color); + background: var(--color-bg-secondary); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--modal-border-color); + border: 1px solid var(--color-border-primary); border-bottom: 0; padding: 12px; display: flex; @@ -9278,7 +9072,7 @@ noscript { } .display-name { - color: $primary-text-color; + color: var(--color-text-primary); text-decoration: none; strong, @@ -9289,7 +9083,7 @@ noscript { } span { - color: $darker-text-color; + color: var(--color-text-secondary); } } } @@ -9303,7 +9097,7 @@ noscript { .picture-in-picture-placeholder { border-radius: 8px; box-sizing: border-box; - border: 1px dashed var(--background-border-color); + border: 1px dashed var(--color-border-primary); display: flex; flex-direction: column; align-items: center; @@ -9313,7 +9107,7 @@ noscript { line-height: 21px; font-weight: 500; cursor: pointer; - color: $dark-text-color; + color: var(--color-text-tertiary); aspect-ratio: 16 / 9; .icon { @@ -9325,18 +9119,18 @@ noscript { &:hover, &:active, &:focus { - color: $darker-text-color; + color: var(--color-text-secondary); } &:focus-visible { - outline: $ui-button-focus-outline; + outline: var(--outline-focus-default); border-color: transparent; } } .notifications-permission-banner { padding: 30px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); display: flex; flex-direction: column; align-items: center; @@ -9357,7 +9151,7 @@ noscript { } p { - color: $darker-text-color; + color: var(--color-text-secondary); margin-bottom: 15px; text-align: center; @@ -9372,7 +9166,7 @@ noscript { .explore__search-header { justify-content: center; align-items: center; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; border-bottom: 0; padding: 16px; @@ -9384,19 +9178,18 @@ noscript { } .search__input { - border: 1px solid var(--background-border-color); padding-block: 12px; padding-inline-end: 30px; } .search__popout { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); } .search__icon { top: 12px; inset-inline-end: 12px; - color: $dark-text-color; + color: var(--color-text-tertiary); } } @@ -9414,7 +9207,7 @@ noscript { flex-direction: column; @media screen and (min-width: $no-gap-breakpoint) { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; @@ -9424,9 +9217,9 @@ noscript { .story { display: flex; align-items: center; - color: $primary-text-color; + color: var(--color-text-primary); padding: 16px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); gap: 16px; &:last-child { @@ -9437,7 +9230,7 @@ noscript { flex: 1 1 auto; &__publisher { - color: $darker-text-color; + color: var(--color-text-secondary); margin-bottom: 8px; font-size: 14px; line-height: 20px; @@ -9450,26 +9243,26 @@ noscript { font-weight: 500; margin-bottom: 8px; text-decoration: none; - color: $primary-text-color; + color: var(--color-text-primary); &:hover, &:active, &:focus { - color: $highlight-text-color; + color: var(--color-text-brand); } } &__shared { display: flex; align-items: baseline; - color: $darker-text-color; + color: var(--color-text-secondary); gap: 8px; justify-content: space-between; font-size: 14px; line-height: 20px; &__pill { - background: var(--surface-variant-background-color); + background: var(--color-bg-tertiary); border-radius: 4px; color: inherit; text-decoration: none; @@ -9484,14 +9277,14 @@ noscript { display: inline-flex; align-items: center; gap: 4px; - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; text-decoration: none; &:hover, &:active, &:focus { - color: $highlight-text-color; + color: var(--color-text-brand); } } } @@ -9561,7 +9354,7 @@ noscript { &__introduction { font-size: 15px; line-height: 22px; - color: $darker-text-color; + color: var(--color-text-secondary); margin-bottom: 20px; strong { @@ -9588,14 +9381,14 @@ noscript { margin-bottom: 20px; aspect-ratio: 1.9; border: 0; - background: $ui-base-color; + background: var(--color-bg-tertiary); object-fit: cover; } &__description { font-size: 15px; line-height: 22px; - color: $darker-text-color; + color: var(--color-text-secondary); margin-bottom: 20px; } @@ -9613,19 +9406,19 @@ noscript { &__number { font-weight: 600; - color: $primary-text-color; + color: var(--color-text-primary); font-size: 14px; } &__number-label { - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 500; font-size: 14px; } h4 { text-transform: uppercase; - color: $darker-text-color; + color: var(--color-text-secondary); margin-bottom: 10px; font-weight: 600; } @@ -9656,9 +9449,9 @@ noscript { position: relative; display: block; border-radius: 16px; - background: var(--modal-background-color); + background: var(--color-bg-primary); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--modal-border-color); + border: 1px solid var(--color-border-primary); padding: 24px; box-sizing: border-box; @@ -9682,10 +9475,10 @@ noscript { text-align: center; font-size: 17px; line-height: 22px; - color: $darker-text-color; + color: var(--color-text-secondary); strong { - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 700; } } @@ -9710,9 +9503,9 @@ noscript { &__input { @include search-input; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); padding: 4px 6px; - color: $primary-text-color; + color: var(--color-text-primary); font-size: 16px; line-height: 18px; display: flex; @@ -9728,7 +9521,11 @@ noscript { min-width: 0; &::placeholder { - color: lighten($darker-text-color, 4%); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); } &:focus { @@ -9745,11 +9542,11 @@ noscript { margin-top: -1px; padding-top: 5px; padding-bottom: 5px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); } &.invalid &__input { - border-color: $error-red; + border-color: var(--color-text-error); } &.expanded .search__popout { @@ -9774,7 +9571,7 @@ noscript { } p { - color: $darker-text-color; + color: var(--color-text-secondary); margin-bottom: 20px; font-size: 15px; } @@ -9805,49 +9602,6 @@ noscript { } } -.copypaste { - display: flex; - align-items: center; - gap: 10px; - - input { - display: block; - font-family: inherit; - background: darken($ui-base-color, 8%); - border: 1px solid $highlight-text-color; - color: $darker-text-color; - border-radius: 4px; - padding: 6px 9px; - line-height: 22px; - font-size: 14px; - transition: border-color 300ms linear; - flex: 1 1 auto; - overflow: hidden; - - &:focus { - outline: 0; - background: darken($ui-base-color, 4%); - } - } - - .button { - flex: 0 0 auto; - transition: background 300ms linear; - } - - &.copied { - input { - border: 1px solid $valid-value-color; - transition: none; - } - - .button { - background: $valid-value-color; - transition: none; - } - } -} - .privacy-policy { padding: 20px; @@ -9861,7 +9615,7 @@ noscript { } .prose { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 15px; line-height: 22px; @@ -9945,7 +9699,7 @@ noscript { ul > li::before { content: ''; position: absolute; - background-color: $darker-text-color; + background-color: var(--color-text-secondary); border-radius: 50%; width: 0.375em; height: 0.375em; @@ -9996,7 +9750,7 @@ noscript { h6, strong, b { - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 700; } @@ -10006,7 +9760,7 @@ noscript { } a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: underline; &:focus, @@ -10018,14 +9772,14 @@ noscript { code { font-size: 0.875em; - background: darken($ui-base-color, 8%); + background: var(--color-bg-secondary); border-radius: 4px; padding: 0.2em 0.3em; } hr { border: 0; - border-top: 1px solid lighten($ui-base-color, 4%); + border-top: 1px solid var(--color-border-primary); margin-top: 3em; margin-bottom: 3em; } @@ -10063,8 +9817,8 @@ noscript { margin: 10px; margin-bottom: 5px; border-radius: 8px; - border: 1px solid $highlight-text-color; - background: color.change($highlight-text-color, $alpha: 0.15); + border: 1px solid var(--color-border-on-bg-brand-softer); + background: var(--color-bg-brand-softer); overflow: hidden; flex-shrink: 0; @@ -10084,7 +9838,7 @@ noscript { font-size: 15px; line-height: 22px; font-weight: 500; - color: $primary-text-color; + color: var(--color-text-primary); p { margin-bottom: 15px; @@ -10095,7 +9849,7 @@ noscript { } h1 { - color: $highlight-text-color; + color: var(--color-text-brand); font-size: 22px; line-height: 33px; font-weight: 700; @@ -10117,11 +9871,6 @@ noscript { flex-grow: 1; } } - - .button-tertiary { - background: color.change($ui-base-color, $alpha: 0.15); - backdrop-filter: blur(8px); - } } &__action { @@ -10129,22 +9878,22 @@ noscript { padding: 15px 10px; .icon-button { - color: $highlight-text-color; + color: var(--color-text-brand); } } } .warning-banner { - border: 1px solid $warning-red; - background: color.change($warning-red, $alpha: 0.15); + border: 1px solid var(--color-border-on-bg-error-softer); + background: var(--color-bg-error-softer); &__message { h1 { - color: $warning-red; + color: var(--color-text-error); } a { - color: $primary-text-color; + color: var(--color-text-primary); } } } @@ -10192,7 +9941,7 @@ noscript { } p { - color: $dark-text-color; + color: var(--color-text-secondary); margin-bottom: 20px; .version { @@ -10204,7 +9953,7 @@ noscript { } a { - color: $dark-text-color; + color: var(--color-text-secondary); text-decoration: underline; &:hover, @@ -10218,14 +9967,14 @@ noscript { .about { padding: 20px; - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); @media screen and (min-width: $no-gap-breakpoint) { border-radius: 4px; } &__footer { - color: $dark-text-color; + color: var(--color-text-tertiary); text-align: center; font-size: 15px; line-height: 22px; @@ -10239,7 +9988,7 @@ noscript { width: 100%; height: auto; aspect-ratio: 1.9; - background: lighten($ui-base-color, 4%); + background: var(--color-bg-tertiary); border-radius: 8px; margin-bottom: 30px; } @@ -10260,12 +10009,12 @@ noscript { font-size: 16px; line-height: 24px; font-weight: 400; - color: $darker-text-color; + color: var(--color-text-secondary); } } &__meta { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; display: flex; margin-bottom: 30px; @@ -10281,7 +10030,7 @@ noscript { width: 0; border: 0; border-style: solid; - border-color: var(--background-border-color); + border-color: var(--color-border-primary); border-left-width: 1px; min-height: calc(100% - 60px); flex: 0 0 auto; @@ -10290,7 +10039,7 @@ noscript { h4 { font-size: 15px; text-transform: uppercase; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 500; margin-bottom: 20px; } @@ -10341,7 +10090,7 @@ noscript { } &__mail { - color: $primary-text-color; + color: var(--color-text-primary); text-decoration: none; font-weight: 500; @@ -10389,8 +10138,8 @@ noscript { line-height: 22px; padding: 20px; border-radius: 4px; - border: 1px solid var(--background-border-color); - color: $highlight-text-color; + border: 1px solid var(--color-border-primary); + color: var(--color-text-brand); cursor: pointer; width: 100%; background: none; @@ -10401,7 +10150,7 @@ noscript { } &__body { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; padding: 20px; font-size: 15px; @@ -10411,17 +10160,17 @@ noscript { &__domain-blocks { margin-top: 30px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; &__domain { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 10px; font-size: 15px; - color: $darker-text-color; + color: var(--color-text-secondary); &:nth-child(2n) { - background: darken($ui-base-color, 4%); + background: var(--color-bg-secondary); } &:last-child { @@ -10437,7 +10186,7 @@ noscript { } h6 { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: inherit; white-space: nowrap; overflow: hidden; @@ -10472,14 +10221,13 @@ noscript { width: auto; padding: 15px; margin: 0; - color: $white; - background: color.change($black, $alpha: 0.85); + color: var(--color-text-on-inverted); + background: var(--color-bg-inverted); backdrop-filter: blur(8px); - border: 1px solid rgba(lighten($classic-base-color, 4%), 0.85); border-radius: 8px; box-shadow: - 0 10px 15px -3px color.change($base-shadow-color, $alpha: 0.25), - 0 4px 6px -4px color.change($base-shadow-color, $alpha: 0.25); + 0 10px 15px -3px var(--color-shadow-primary), + 0 4px 6px -4px var(--color-shadow-primary); cursor: default; font-size: 15px; line-height: 21px; @@ -10519,7 +10267,7 @@ noscript { background: transparent; text-transform: uppercase; cursor: pointer; - color: $blurple-300; + color: var(--color-text-brand); font-weight: 700; border-radius: 4px; padding: 0 4px; @@ -10527,7 +10275,7 @@ noscript { &:hover, &:focus, &:active { - background: color.change($white, $alpha: 0.15); + background: var(--color-bg-brand-softer); } } @@ -10551,18 +10299,18 @@ noscript { margin-inline-start: 2px; svg { - color: $white; + color: var(--color-text-on-media); height: var(--spinner-size); width: var(--spinner-size); } } .hashtag-header { - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 15px; font-size: 17px; line-height: 22px; - color: $darker-text-color; + color: var(--color-text-secondary); strong { font-weight: 700; @@ -10576,7 +10324,7 @@ noscript { gap: 15px; h1 { - color: $primary-text-color; + color: var(--color-text-primary); white-space: nowrap; text-overflow: ellipsis; overflow: hidden; @@ -10597,7 +10345,7 @@ noscript { } .icon-button { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; box-sizing: content-box; padding: 5px; @@ -10618,21 +10366,25 @@ noscript { font-size: 12px; line-height: 16px; gap: 6px; - color: $darker-text-color; + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); a { display: inline-flex; color: inherit; text-decoration: none; padding: 4px 12px; - background: var(--surface-variant-background-color); + background: var(--color-bg-brand-softer); border-radius: 4px; font-weight: 500; &:hover, &:focus, &:active { - background: var(--surface-variant-active-background-color); + background: var(--color-bg-brand-soft); } } @@ -10650,11 +10402,11 @@ noscript { gap: 12px; padding: 16px 0; padding-bottom: 0; - border-bottom: 1px solid var(--background-border-color); - background: color.change($ui-highlight-color, $alpha: 0.05); + border-bottom: 1px solid var(--color-border-primary); + background: var(--color-bg-brand-softer); &.focusable:focus-visible { - background: color.change($ui-highlight-color, $alpha: 0.05); + background: var(--color-bg-brand-softer); } &__header { @@ -10691,7 +10443,7 @@ noscript { border: none; cursor: pointer; top: 0; - color: $primary-text-color; + color: var(--color-text-primary); opacity: 0.5; &.left { @@ -10704,7 +10456,8 @@ noscript { &__icon { border-radius: 50%; - background: $ui-highlight-color; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); display: flex; align-items: center; justify-content: center; @@ -10721,10 +10474,6 @@ noscript { &:focus, &:active { opacity: 1; - - .inline-follow-suggestions__body__scroll-button__icon { - background: lighten($ui-highlight-color, 4%); - } } } @@ -10742,8 +10491,8 @@ noscript { scrollbar-width: none; &__card { - background: var(--background-color); - border: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border: 1px solid var(--color-border-primary); border-radius: 4px; display: flex; flex-direction: column; @@ -10780,8 +10529,8 @@ noscript { .account__avatar { flex-shrink: 0; align-self: flex-end; - border: 1px solid var(--background-border-color); - background-color: $ui-base-color; + border: 1px solid var(--color-border-primary); + background-color: var(--color-bg-tertiary); } &__text-stack { @@ -10798,7 +10547,7 @@ noscript { &__source { display: inline-flex; align-items: center; - color: $dark-text-color; + color: var(--color-text-tertiary); gap: 4px; overflow: hidden; white-space: nowrap; @@ -10829,12 +10578,12 @@ noscript { &__html { font-size: 15px; font-weight: 500; - color: $secondary-text-color; + color: var(--color-text-primary); } &__account { font-size: 14px; - color: $darker-text-color; + color: var(--color-text-secondary); } } @@ -10855,16 +10604,16 @@ noscript { .filtered-notifications-banner { display: flex; align-items: center; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); padding: 16px 24px; gap: 8px; - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; &:hover, &:active, &:focus { - color: $secondary-text-color; + color: var(--color-text-primary); } .notification-group__icon { @@ -10884,8 +10633,8 @@ noscript { } &__badge { - background: $ui-button-background-color; - color: $white; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); border-radius: 100px; padding: 2px 8px; } @@ -10898,7 +10647,7 @@ noscript { padding: $padding; gap: 8px; position: relative; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &__checkbox { position: absolute; @@ -10930,7 +10679,7 @@ noscript { &__name { flex: 1 1 auto; - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 14px; line-height: 20px; overflow: hidden; @@ -10943,7 +10692,7 @@ noscript { font-size: 16px; letter-spacing: 0.5px; line-height: 24px; - color: $secondary-text-color; + color: var(--color-text-primary); bdi { overflow: hidden; @@ -10953,10 +10702,10 @@ noscript { } .filtered-notifications-banner__badge { - background: $ui-button-background-color; + color: var(--color-text-on-brand-base); + background: var(--color-bg-brand-base); border-radius: 4px; padding: 1px 6px; - color: $white; } } @@ -10967,7 +10716,7 @@ noscript { .icon-button { border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); padding: 5px; } } @@ -10980,7 +10729,7 @@ noscript { cursor: pointer; &:hover { - background: var(--on-surface-color); + background: var(--color-bg-secondary); } .notification-request__checkbox { @@ -11001,9 +10750,9 @@ noscript { .more-from-author { box-sizing: border-box; font-size: 14px; - color: $darker-text-color; - background: var(--surface-background-color); - border: 1px solid var(--background-border-color); + color: var(--color-text-secondary); + background: var(--color-bg-tertiary); + border: 1px solid var(--color-border-primary); border-top: 0; border-radius: 0 0 8px 8px; padding: 15px; @@ -11015,7 +10764,7 @@ noscript { .logo { width: 16px; height: 16px; - color: $darker-text-color; + color: var(--color-text-secondary); } & > span { @@ -11029,14 +10778,14 @@ noscript { align-items: center; gap: 4px; font-weight: 500; - color: $primary-text-color; + color: var(--color-text-primary); text-decoration: none; min-width: 0; &:hover, &:focus, &:active { - color: $highlight-text-color; + color: var(--color-text-brand); } } } @@ -11046,7 +10795,7 @@ noscript { align-items: flex-start; gap: 8px; padding: 16px 24px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &__icon { width: 40px; @@ -11054,7 +10803,7 @@ noscript { align-items: center; justify-content: center; flex: 0 0 auto; - color: $dark-text-color; + color: var(--color-text-tertiary); .icon { width: 28px; @@ -11064,25 +10813,25 @@ noscript { &--follow &__icon, &--follow-request &__icon { - color: $highlight-text-color; + color: var(--color-text-brand); } &--favourite &__icon { - color: $gold-star; + color: var(--color-text-favourite-highlight); } &--reblog &__icon { - color: $valid-value-color; + color: var(--color-text-success); } &--relationships-severance-event &__icon, &--admin-report &__icon, &--admin-sign-up &__icon { - color: $dark-text-color; + color: var(--color-text-tertiary); } &--moderation-warning &__icon { - color: $red-bookmark; + color: var(--color-text-bookmark-highlight); } &--follow-request &__actions { @@ -11091,7 +10840,7 @@ noscript { gap: 8px; .icon-button { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 50%; padding: 1px; } @@ -11121,7 +10870,7 @@ noscript { gap: 2px 8px; font-size: 15px; line-height: 22px; - color: $darker-text-color; + color: var(--color-text-secondary); a { color: inherit; @@ -11130,11 +10879,11 @@ noscript { bdi { font-weight: 700; - color: $primary-text-color; + color: var(--color-text-primary); } time { - color: $dark-text-color; + color: var(--color-text-tertiary); } @container (width < 350px) { @@ -11147,13 +10896,13 @@ noscript { } &__status { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 8px; padding: 8px; } &__additional-content { - color: $dark-text-color; + color: var(--color-text-tertiary); margin-top: -8px; // to offset the parent's `gap` property font-size: 15px; line-height: 22px; @@ -11175,12 +10924,12 @@ noscript { display: flex; align-items: center; gap: 4px; - color: $dark-text-color; + color: var(--color-text-tertiary); font-size: 15px; line-height: 22px; bdi { - color: $darker-text-color; + color: var(--color-text-secondary); } .account__avatar { @@ -11192,7 +10941,7 @@ noscript { display: -webkit-box; font-size: 15px; line-height: 22px; - color: $darker-text-color; + color: var(--color-text-secondary); -webkit-line-clamp: 4; line-clamp: 4; -webkit-box-orient: vertical; @@ -11213,7 +10962,7 @@ noscript { margin-top: 0; font-size: 15px; line-height: 22px; - color: $dark-text-color; + color: var(--color-text-tertiary); } } } @@ -11228,13 +10977,13 @@ noscript { .notification-ungrouped { padding: 16px 24px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &__header { display: flex; align-items: center; gap: 8px; - color: $dark-text-color; + color: var(--color-text-tertiary); font-size: 15px; line-height: 22px; font-weight: 500; @@ -11318,7 +11067,7 @@ noscript { inset-inline-start: 0; width: 100%; height: 100%; - border-inline-start: 4px solid $highlight-text-color; + border-inline-start: 4px solid var(--color-text-brand); pointer-events: none; } } @@ -11330,9 +11079,9 @@ noscript { .hover-card { box-shadow: var(--dropdown-shadow); - background: var(--modal-background-color); + background: var(--color-bg-primary); backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--modal-border-color); + border: 1px solid var(--color-border-primary); border-radius: 8px; padding: 16px; width: 270px; @@ -11363,7 +11112,7 @@ noscript { &__numbers { font-size: 15px; line-height: 22px; - color: $secondary-text-color; + color: var(--color-text-primary); strong { font-weight: 700; @@ -11377,7 +11126,7 @@ noscript { } &__bio { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 14px; line-height: 20px; display: -webkit-box; @@ -11405,7 +11154,7 @@ noscript { &__note { &-label { - color: $dark-text-color; + color: var(--color-text-tertiary); font-size: 12px; font-weight: 500; text-transform: uppercase; @@ -11413,7 +11162,7 @@ noscript { dd { white-space: pre-line; - color: $secondary-text-color; + color: var(--color-text-primary); overflow: hidden; line-clamp: 3; // Not yet supported in browers display: -webkit-box; // The next 3 properties are needed @@ -11433,17 +11182,17 @@ noscript { bdi { font-weight: 500; - color: $primary-text-color; + color: var(--color-text-primary); } &__account { display: block; - color: $dark-text-color; + color: var(--color-text-tertiary); } } .account-fields { - color: $secondary-text-color; + color: var(--color-text-primary); font-size: 14px; line-height: 20px; @@ -11465,7 +11214,7 @@ noscript { dt { flex: 0 1 auto; - color: $dark-text-color; + color: var(--color-text-tertiary); min-width: 0; overflow: hidden; white-space: nowrap; @@ -11490,7 +11239,7 @@ noscript { gap: 4px; overflow: hidden; white-space: nowrap; - color: $valid-value-color; + color: var(--color-text-success); & > span { overflow: hidden; @@ -11514,9 +11263,9 @@ noscript { .content-warning { display: block; box-sizing: border-box; - background: var(--nested-card-background); - color: var(--nested-card-text); - border: var(--nested-card-border); + color: var(--color-text-primary); + background: var(--color-bg-brand-softer); + border: 1px solid var(--color-border-on-bg-brand-softer); border-radius: 8px; padding: 8px (5px + 8px); position: relative; @@ -11536,7 +11285,7 @@ noscript { } &--filter { - color: $darker-text-color; + color: var(--color-text-secondary); p { font-weight: normal; @@ -11544,7 +11293,7 @@ noscript { .filter-name { font-weight: 500; - color: $secondary-text-color; + color: var(--color-text-primary); } } } @@ -11554,7 +11303,7 @@ noscript { align-items: center; gap: 16px; padding-inline-end: 13px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); &__title { display: flex; @@ -11564,13 +11313,13 @@ noscript { flex: 1 1 auto; font-size: 16px; line-height: 24px; - color: $secondary-text-color; + color: var(--color-text-secondary); text-decoration: none; &:is(a):hover, &:is(a):focus, &:is(a):active { - color: $primary-text-color; + color: var(--color-text-primary); } input { @@ -11585,7 +11334,7 @@ noscript { color: inherit; &::placeholder { - color: var(--input-placeholder-color); + color: var(--color-text-secondary); opacity: 1; } @@ -11600,16 +11349,16 @@ noscript { display: flex; gap: 12px; align-items: center; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; border-bottom: 0; padding: 16px; padding-bottom: 8px; input { - background: var(--input-background-color); - border: 1px solid var(--background-border-color); - color: var(--on-input-color); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); + color: var(--color-text-primary); padding: 12px; font-size: 16px; line-height: normal; @@ -11618,7 +11367,7 @@ noscript { flex: 1 1 auto; &::placeholder { - color: var(--input-placeholder-color); + color: var(--color-text-secondary); opacity: 1; } @@ -11639,7 +11388,7 @@ noscript { .featured-carousel { overflow: hidden; flex-shrink: 0; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); touch-action: pan-y; &__slides { @@ -11661,7 +11410,7 @@ noscript { &__header { padding: 8px 16px; - color: $darker-text-color; + color: var(--color-text-secondary); inset-inline-end: 0; display: flex; align-items: center; diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index 7db9ca409d7328..57c62a29e3579b 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -27,7 +27,7 @@ display: flex; justify-content: center; align-items: center; - color: $primary-text-color; + color: var(--color-text-primary); text-decoration: none; outline: 0; padding: 12px 16px; @@ -65,7 +65,7 @@ padding: 20px 0; margin-top: 40px; margin-bottom: 10px; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); @media screen and (width <= 440px) { width: 100%; @@ -89,7 +89,7 @@ .name { flex: 1 1 auto; - color: $secondary-text-color; + color: var(--color-text-primary); .username { display: block; @@ -97,7 +97,7 @@ line-height: 24px; text-overflow: ellipsis; overflow: hidden; - color: $primary-text-color; + color: var(--color-text-primary); } } @@ -148,7 +148,7 @@ } a { - color: $highlight-text-color; + color: var(--color-text-brand); font-weight: 500; text-decoration: none; diff --git a/app/javascript/styles/mastodon/css_variables.scss b/app/javascript/styles/mastodon/css_variables.scss index 78915ae10eed2d..b270bd337ff321 100644 --- a/app/javascript/styles/mastodon/css_variables.scss +++ b/app/javascript/styles/mastodon/css_variables.scss @@ -1,42 +1,221 @@ -@use 'sass:color'; -@use 'functions' as *; -@use 'variables' as *; +@use 'theme_utils' as utils; :root { - --dropdown-border-color: #{lighten($ui-base-color, 4%)}; - --dropdown-background-color: #{rgba(darken($ui-base-color, 8%), 0.9)}; + --color-black: #000; + --color-grey-950: #181821; + --color-grey-800: #292938; + --color-grey-700: #444664; + --color-grey-600: #545778; + --color-grey-500: #696d91; + --color-grey-400: #8b8dac; + --color-grey-300: #b4b6cb; + --color-grey-200: #d8d9e3; + --color-grey-100: #f0f0f5; + --color-grey-50: #f0f1ff; + --color-white: #fff; + --color-indigo-600: #6147e6; + --color-indigo-400: #8886ff; + --color-indigo-300: #a5abfd; + --color-indigo-200: #c8cdfe; + --color-indigo-100: #e0e3ff; + --color-indigo-50: #f0f1ff; + --color-red-500: #ff637e; + --color-red-600: #ec003f; + --color-yellow-400: #ffb900; + --color-yellow-600: #e17100; + --color-green-400: #05df72; + --color-green-600: #00a63e; + + /* TEXT TOKENS */ + + --color-text-primary: var(--color-grey-50); + --color-text-secondary: var(--color-grey-400); + --color-text-tertiary: var(--color-grey-500); + --color-text-on-inverted: var(--color-grey-950); + --color-text-brand: var(--color-indigo-400); + --color-text-brand-soft: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-brand) + ); + --color-text-on-brand-base: var(--color-white); + --color-text-error: var(--color-red-500); + --color-text-on-error-base: var(--color-white); + --color-text-warning: var(--color-yellow-400); + --color-text-on-warning-base: var(--color-white); + --color-text-success: var(--color-green-400); + --color-text-on-success-base: var(--color-white); + --color-text-disabled: var(--color-grey-600); + --color-text-on-disabled: var(--color-grey-400); + --color-text-bookmark-highlight: var(--color-text-error); + --color-text-favourite-highlight: var(--color-text-warning); + --color-text-on-media: var(--color-white); + --color-text-status-links: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); + + /* BACKGROUND TOKENS */ + + // Neutrals + --color-bg-primary: var(--color-grey-950); + --overlay-strength-secondary: 10%; + --color-bg-secondary-base: var(--color-indigo-200); + --color-bg-secondary: #{utils.css-alpha( + var(--color-bg-secondary-base), + var(--overlay-strength-secondary) + )}; + --color-bg-secondary-solid: color-mix( + in srgb, + var(--color-bg-primary), + var(--color-bg-secondary-base) var(--overlay-strength-secondary) + ); + --color-bg-tertiary: color-mix( + in oklab, + var(--color-bg-primary), + var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary)) + ); + + // Utility + --color-bg-ambient: var(--color-bg-primary); + --color-bg-elevated: var(--color-bg-primary); + --color-bg-inverted: var(--color-grey-50); + --color-bg-media-base: var(--color-black); + --color-bg-media-strength: 65%; + --color-bg-media: #{utils.css-alpha( + var(--color-bg-media-base), + var(--color-bg-media-strength) + )}; + --color-bg-overlay: var(--color-black); + --color-bg-disabled: var(--color-grey-700); + + // Brand + --overlay-strength-brand: 10%; + --color-bg-brand-base: var(--color-indigo-600); + --color-bg-brand-base-hover: color-mix( + in oklab, + var(--color-bg-brand-base), + black var(--overlay-strength-brand) + ); + --color-bg-brand-soft: #{utils.css-alpha( + var(--color-bg-brand-base), + calc(var(--overlay-strength-brand) * 1.5) + )}; + --color-bg-brand-softer: #{utils.css-alpha( + var(--color-bg-brand-base), + var(--overlay-strength-brand) + )}; + + // Error + --overlay-strength-error: 12%; + --color-bg-error-base: var(--color-red-600); + --color-bg-error-base-hover: color-mix( + in oklab, + var(--color-bg-error-base), + black var(--overlay-strength-error) + ); + --color-bg-error-soft: #{utils.css-alpha( + var(--color-bg-error-base), + calc(var(--overlay-strength-error) * 1.5) + )}; + --color-bg-error-softer: #{utils.css-alpha( + var(--color-bg-error-base), + var(--overlay-strength-error) + )}; + + // Warning + --overlay-strength-warning: 10%; + --color-bg-warning-base: var(--color-yellow-600); + --color-bg-warning-base-hover: color-mix( + in oklab, + var(--color-bg-warning-base), + black var(--overlay-strength-warning) + ); + --color-bg-warning-soft: #{utils.css-alpha( + var(--color-bg-warning-base), + calc(var(--overlay-strength-warning) * 1.5) + )}; + --color-bg-warning-softer: #{utils.css-alpha( + var(--color-bg-warning-base), + var(--overlay-strength-warning) + )}; + + // Success + --overlay-strength-success: 15%; + --color-bg-success-base: var(--color-green-600); + --color-bg-success-base-hover: color-mix( + in oklab, + var(--color-bg-success-base), + black var(--overlay-strength-success) + ); + --color-bg-success-soft: #{utils.css-alpha( + var(--color-bg-success-base), + calc(var(--overlay-strength-success) * 1.5) + )}; + --color-bg-success-softer: #{utils.css-alpha( + var(--color-bg-success-base), + var(--overlay-strength-success) + )}; + + /* BORDER TOKENS */ + + --border-strength-primary: 18%; + --color-border-primary: #{utils.css-alpha( + var(--color-indigo-200), + var(--border-strength-primary) + )}; + --color-border-media: rgb(252 248 255 / 15%); + --color-border-on-bg-secondary: #{utils.css-alpha( + var(--color-indigo-200), + calc(var(--border-strength-primary) / 1.5) + )}; + --color-border-on-bg-brand-softer: var(--color-border-primary); + --color-border-on-bg-error-softer: #{utils.css-alpha( + var(--color-text-error), + 50% + )}; + --color-border-on-bg-warning-softer: #{utils.css-alpha( + var(--color-text-warning), + 50% + )}; + --color-border-on-bg-success-softer: #{utils.css-alpha( + var(--color-text-success), + 50% + )}; + --color-border-on-bg-inverted: var(--color-border-primary); + + /* SHADOW TOKENS */ + + --shadow-strength-primary: 80%; + --color-shadow-primary: #{utils.css-alpha( + var(--color-black), + var(--shadow-strength-primary) + )}; --dropdown-shadow: - 0 20px 25px -5px #{color.change($base-shadow-color, $alpha: 0.25)}, - 0 8px 10px -6px #{color.change($base-shadow-color, $alpha: 0.25)}; - --modal-background-color: #{rgba(darken($ui-base-color, 8%), 0.7)}; - --modal-background-variant-color: #{color.change($ui-base-color, $alpha: 0.7)}; - --modal-border-color: #{lighten($ui-base-color, 4%)}; - --background-border-color: #{lighten($ui-base-color, 4%)}; - --background-color: #{darken($ui-base-color, 8%)}; - --background-color-tint: #{rgba(darken($ui-base-color, 8%), 0.9)}; - --surface-background-color: #{darken($ui-base-color, 4%)}; - --surface-variant-background-color: #{$ui-base-color}; - --surface-variant-active-background-color: #{lighten($ui-base-color, 4%)}; - --surface-border-color: #{lighten($ui-base-color, 8%)}; - --on-surface-color: #{color.adjust($ui-base-color, $alpha: -0.5)}; + 0 20px 25px -5px var(--color-shadow-primary), + 0 8px 10px -6px var(--color-shadow-primary); + --overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary)); + + /* GRAPHS/CHARTS TOKENS */ + + --color-graph-primary-stroke: var(--color-text-brand); + --color-graph-primary-fill: var(--color-bg-brand-softer); + --color-graph-warning-stroke: var(--color-text-warning); + --color-graph-warning-fill: var(--color-bg-warning-softer); + --color-graph-disabled-stroke: var(--color-text-disabled); + --color-graph-disabled-fill: var(--color-bg-disabled); + + /* LEGACY TOKENS */ + + --rich-text-container-color: rgb(87 24 60 / 100%); + --rich-text-text-color: rgb(255 175 212 / 100%); + --rich-text-decorations-color: rgb(128 58 95 / 100%); + + /* MISCELLANEOUS */ + + --outline-focus-default: 2px solid var(--color-text-brand); --avatar-border-radius: 8px; - --media-outline-color: #{rgba(#fcf8ff, 0.15)}; - --overlay-icon-shadow: drop-shadow( - 0 0 8px #{color.change($base-shadow-color, $alpha: 0.35)} - ); - --error-background-color: #{darken($error-red, 16%)}; - --error-active-background-color: #{darken($error-red, 12%)}; - --on-error-color: #fff; - --rich-text-container-color: rgba(87, 24, 60, 100%); - --rich-text-text-color: rgba(255, 175, 212, 100%); - --rich-text-decorations-color: rgba(128, 58, 95, 100%); - --nested-card-background: color(from #{$ui-highlight-color} srgb r g b / 5%); - --nested-card-text: #{$secondary-text-color}; - --nested-card-border: 1px solid - color(from #{$ui-highlight-color} srgb r g b / 15%); - --input-placeholder-color: #{$dark-text-color}; - --input-background-color: var(--surface-variant-background-color); - --on-input-color: #{$secondary-text-color}; } body { diff --git a/app/javascript/styles/mastodon/dashboard.scss b/app/javascript/styles/mastodon/dashboard.scss index 8326022f71ab9e..db3f0e8a8425e9 100644 --- a/app/javascript/styles/mastodon/dashboard.scss +++ b/app/javascript/styles/mastodon/dashboard.scss @@ -1,4 +1,3 @@ -@use 'functions' as *; @use 'variables' as *; .dashboard__counters { @@ -16,9 +15,9 @@ & > div, & > a { padding: 20px; - background: var(--background-color); + background: var(--color-bg-primary); border-radius: 4px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); box-sizing: border-box; height: 100%; } @@ -31,7 +30,7 @@ &:hover, &:focus, &:active { - background: $ui-base-color; + background: var(--color-bg-brand-softer); } } } @@ -41,7 +40,7 @@ text-align: center; font-weight: 500; font-size: 24px; - color: $primary-text-color; + color: var(--color-text-primary); margin-bottom: 20px; line-height: 30px; } @@ -52,7 +51,7 @@ &__label { font-size: 14px; - color: $darker-text-color; + color: var(--color-text-secondary); text-align: center; font-weight: 500; } @@ -85,8 +84,8 @@ display: flex; align-items: baseline; border-radius: 4px; - background: $ui-button-background-color; - color: $primary-text-color; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); transition: all 100ms ease-in; font-size: 14px; padding: 8px 16px; @@ -96,18 +95,18 @@ &:active, &:focus, &:hover { - background-color: $ui-button-focus-background-color; + background-color: var(--color-bg-brand-base-hover); transition: all 200ms ease-out; } &.positive { - background: lighten($ui-base-color, 4%); - color: $valid-value-color; + background: var(--color-bg-success-softer); + color: var(--color-text-success); } &.negative { - background: lighten($ui-base-color, 4%); - color: $error-value-color; + background: var(--color-bg-error-softer); + color: var(--color-text-error); } span { diff --git a/app/javascript/styles/mastodon/emoji_picker.scss b/app/javascript/styles/mastodon/emoji_picker.scss index 716c790e65650a..ad2f2f630db740 100644 --- a/app/javascript/styles/mastodon/emoji_picker.scss +++ b/app/javascript/styles/mastodon/emoji_picker.scss @@ -1,5 +1,4 @@ @use 'variables' as *; -@use 'functions' as *; .emoji-mart { font-size: 13px; @@ -18,7 +17,8 @@ .emoji-mart-bar { &:first-child { - background: var(--dropdown-border-color); + background: var(--color-bg-tertiary); + border-bottom: 1px solid var(--color-border-primary); } } @@ -39,18 +39,22 @@ cursor: pointer; background: transparent; border: 0; - color: $darker-text-color; + color: var(--color-text-secondary); &:hover { - color: lighten($darker-text-color, 4%); + color: color-mix( + in oklab, + var(--color-text-primary), + var(--color-text-secondary) + ); } } .emoji-mart-anchor-selected { - color: $highlight-text-color; + color: var(--color-text-brand); &:hover { - color: lighten($highlight-text-color, 4%); + color: var(--color-text-brand-soft); } .emoji-mart-anchor-bar { @@ -64,7 +68,7 @@ inset-inline-start: 0; width: 100%; height: 4px; - background-color: $highlight-text-color; + background-color: var(--color-text-brand); } .emoji-mart-anchors { @@ -101,9 +105,9 @@ font-family: inherit; display: block; width: 100%; - background: $ui-base-color; - color: $darker-text-color; - border: 1px solid var(--background-border-color); + background: var(--color-bg-secondary); + color: var(--color-text-secondary); + border: 1px solid var(--color-border-primary); border-radius: 4px; &::-moz-focus-inner { @@ -133,7 +137,6 @@ transition: all 100ms linear; transition-property: opacity; pointer-events: auto; - opacity: 0.7; &:disabled { cursor: default; @@ -141,7 +144,7 @@ } svg { - fill: $darker-text-color; + fill: currentColor; } } @@ -165,7 +168,7 @@ inset-inline-start: 0; width: 100%; height: 100%; - background-color: var(--dropdown-border-color); + background-color: var(--color-bg-brand-softer); border-radius: 100%; } } @@ -225,7 +228,7 @@ .emoji-mart-no-results { font-size: 14px; - color: $dark-text-color; + color: var(--color-text-tertiary); text-align: center; padding: 5px 6px; padding-top: 70px; diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index e0ccd0a27fd811..9f716c2126d836 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -1,6 +1,5 @@ @use 'sass:color'; @use 'variables' as *; -@use 'functions' as *; code { font-family: $font-monospace, monospace; @@ -16,7 +15,7 @@ code { .form-section { border-radius: 8px; - background: var(--surface-background-color); + background: var(--color-bg-secondary); padding: 24px; margin-bottom: 24px; } @@ -33,7 +32,7 @@ code { display: block; background: linear-gradient( to bottom, - var(--surface-background-color), + var(--color-bg-secondary-solid), transparent ); position: absolute; @@ -58,14 +57,16 @@ code { width: 40px; height: 40px; border-radius: 50%; - color: $primary-text-color; + color: var(--color-text-primary); &.success { - background: $success-green; + color: var(--color-text-on-success-base); + background: var(--color-bg-success-base); } &.failure { - background: $error-red; + color: var(--color-text-on-error-base); + background: var(--color-bg-error-base); } } @@ -126,7 +127,7 @@ code { font-family: inherit; font-size: 14px; padding-top: 5px; - color: $primary-text-color; + color: var(--color-text-primary); display: block; width: auto; } @@ -147,7 +148,7 @@ code { } label a { - color: $highlight-text-color; + color: var(--color-text-brand); text-decoration: underline; &:hover, @@ -189,7 +190,7 @@ code { .lead { font-size: 17px; line-height: 22px; - color: $secondary-text-color; + color: var(--color-text-primary); margin-bottom: 30px; &.invited-by { @@ -197,7 +198,7 @@ code { } a { - color: $highlight-text-color; + color: var(--color-text-brand); } } @@ -208,16 +209,16 @@ code { } .hint { - color: $darker-text-color; + color: var(--color-text-secondary); a { - color: $highlight-text-color; + color: var(--color-text-brand); } code { border-radius: 3px; padding: 0.2em 0.4em; - background: darken($ui-base-color, 12%); + background: var(--color-bg-secondary); } li { @@ -242,7 +243,7 @@ code { p.hint { margin-bottom: 15px; - color: $darker-text-color; + color: var(--color-text-secondary); &.subtle-hint { text-align: center; @@ -278,7 +279,7 @@ code { & > label { font-family: inherit; font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 500; min-width: 150px; flex: 0 0 auto; @@ -300,7 +301,7 @@ code { .label_input > label { font-family: inherit; font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); display: block; margin-bottom: 8px; overflow-wrap: break-word; @@ -322,7 +323,7 @@ code { & > label { font-family: inherit; font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); display: block; font-weight: 600; line-height: 20px; @@ -381,7 +382,7 @@ code { .required abbr { text-decoration: none; - color: lighten($error-value-color, 12%); + color: var(--color-text-error); } .fields-group { @@ -474,7 +475,7 @@ code { margin-bottom: 5px; font-family: inherit; font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); display: block; width: auto; } @@ -484,7 +485,7 @@ code { label { font-family: inherit; font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); display: inline-block; width: auto; position: relative; @@ -505,7 +506,7 @@ code { .input.static .label_input__wrapper { font-size: 14px; padding: 10px; - border: 1px solid $dark-text-color; + border: 1px solid var(--color-border-primary); border-radius: 4px; } @@ -519,14 +520,14 @@ code { box-sizing: border-box; font-size: 14px; line-height: 20px; - color: $primary-text-color; + color: var(--color-text-primary); display: block; width: 100%; outline: 0; font-family: inherit; resize: vertical; - background: $ui-base-color; - border: 1px solid var(--background-border-color); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); border-radius: 4px; padding: 10px 16px; @@ -535,7 +536,7 @@ code { } &:required:valid { - border-color: $valid-value-color; + border-color: var(--color-text-success); } @media screen and (width <= 600px) { @@ -550,13 +551,13 @@ code { input[type='datetime-local'] { &:focus:invalid:not(:placeholder-shown), &:required:invalid:not(:placeholder-shown) { - border-color: lighten($error-red, 12%); + border-color: var(--color-text-error); } } .input.field_with_errors { label { - color: lighten($error-red, 12%); + color: var(--color-text-error); } input[type='text'], @@ -566,13 +567,13 @@ code { input[type='datetime-local'], textarea, select { - border-color: lighten($error-red, 12%); + border-color: var(--color-text-error); } .error { display: block; font-weight: 500; - color: lighten($error-red, 12%); + color: var(--color-text-error); margin-top: 4px; } } @@ -605,8 +606,8 @@ code { width: 100%; border: 0; border-radius: 4px; - background: $ui-button-background-color; - color: $ui-button-color; + background: var(--color-bg-brand-base); + color: var(--color-text-on-brand-base); font-size: 15px; line-height: 22px; height: auto; @@ -625,21 +626,22 @@ code { &:active, &:focus, &:hover { - background-color: $ui-button-focus-background-color; + background: var(--color-bg-brand-base-hover); } &:disabled, &:disabled:hover { - background-color: $ui-button-disabled-color; + color: var(--color-text-on-disabled); + background: var(--color-bg-disabled); } &.negative { - background: $ui-button-destructive-background-color; + background: var(--color-bg-error-base); &:hover, &:active, &:focus { - background-color: $ui-button-destructive-focus-background-color; + background: var(--color-bg-error-base-hover); } } } @@ -648,16 +650,14 @@ code { appearance: none; box-sizing: border-box; font-size: 14px; - color: $primary-text-color; + color: var(--color-text-primary); display: block; width: 100%; outline: 0; font-family: inherit; resize: vertical; - background: $ui-base-color - url("data:image/svg+xml;utf8,") - no-repeat right 8px center / auto 14px; - border: 1px solid var(--background-border-color); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); border-radius: 4px; padding-inline-start: 10px; padding-inline-end: 30px; @@ -695,7 +695,7 @@ code { top: 1px; padding: 10px; font-size: 14px; - color: $dark-text-color; + color: var(--color-text-tertiary); font-family: inherit; pointer-events: none; cursor: default; @@ -721,6 +721,29 @@ code { } } +/* Double-chevron icon for custom select components */ +.select-wrapper, +.select .label_input__wrapper { + width: 100%; + + &::after { + --icon-size: 11px; + + content: ''; + position: absolute; + top: 0; + bottom: 0; + inset-inline-end: 9px; + width: var(--icon-size); + background-color: var(--color-text-tertiary); + pointer-events: none; + mask-image: url("data:image/svg+xml;utf8,"); + mask-position: right center; + mask-size: var(--icon-size); + mask-repeat: no-repeat; + } +} + .block-icon { display: block; margin: 0 auto; @@ -729,30 +752,30 @@ code { } .flash-message { - background: var(--background-color); - color: $highlight-text-color; - border: 1px solid $highlight-text-color; + color: var(--color-text-brand); + background: transparent; + border: 1px solid var(--color-text-brand); border-radius: 4px; padding: 15px 10px; margin-bottom: 30px; text-align: center; &.notice { - border: 1px solid color.change($valid-value-color, $alpha: 0.5); - background: color.change($valid-value-color, $alpha: 0.25); - color: $valid-value-color; + border: 1px solid var(--color-border-on-bg-success-softer); + background: var(--color-bg-success-softer); + color: var(--color-text-success); } &.warning { - border: 1px solid color.change($gold-star, $alpha: 0.5); - background: color.change($gold-star, $alpha: 0.25); - color: $gold-star; + border: 1px solid var(--color-border-on-bg-warning-softer); + background: var(--color-bg-warning-softer); + color: var(--color-text-warning); } &.alert { - border: 1px solid color.change($error-value-color, $alpha: 0.5); - background: color.change($error-value-color, $alpha: 0.1); - color: $error-value-color; + border: 1px solid var(--color-border-on-bg-error-softer); + background: var(--color-bg-error-softer); + color: var(--color-text-error); } &.hidden { @@ -767,11 +790,11 @@ code { a { display: inline-block; - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; &:hover { - color: $primary-text-color; + color: var(--color-text-primary); text-decoration: underline; } } @@ -801,8 +824,8 @@ code { border: 0; padding: 10px; font-family: $font-monospace, monospace; - background: $ui-base-color; - color: $primary-text-color; + background: var(--color-bg-secondary); + color: var(--color-text-primary); font-size: 14px; margin: 0; @@ -817,7 +840,7 @@ code { } &:focus { - background: lighten($ui-base-color, 4%); + background: var(--color-bg-brand-softer); } } @@ -873,7 +896,7 @@ code { text-align: center; a { - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; &:hover { @@ -893,7 +916,7 @@ code { } a { - color: $highlight-text-color; + color: var(--color-text-brand); text-transform: uppercase; text-decoration: none; font-weight: 700; @@ -901,7 +924,7 @@ code { &:hover, &:focus, &:active { - color: lighten($highlight-text-color, 8%); + color: var(--color-text-brand-soft); } } } @@ -909,7 +932,7 @@ code { .oauth-prompt, .follow-prompt { margin-bottom: 30px; - color: $darker-text-color; + color: var(--color-text-secondary); h2 { font-size: 16px; @@ -918,7 +941,7 @@ code { } strong { - color: $secondary-text-color; + color: var(--color-text-primary); font-weight: 500; @each $lang in $cjk-langs { @@ -931,7 +954,7 @@ code { .oauth-prompt { h3 { - color: $ui-secondary-color; + color: var(--color-text-primary); font-size: 17px; line-height: 22px; font-weight: 500; @@ -945,9 +968,9 @@ code { } .permissions-list { - border: 1px solid $ui-base-color; + border: 1px solid var(--color-border-primary); border-radius: 4px; - background: darken($ui-base-color, 4%); + background: var(--color-bg-secondary); margin-bottom: 30px; } @@ -973,10 +996,10 @@ code { .qr-code { flex: 0 0 auto; - background: $simple-background-color; + background: white; padding: 4px; margin: 0 10px 20px 0; - box-shadow: 0 0 15px color.change($base-shadow-color, $alpha: 0.2); + box-shadow: 0 0 15px var(--color-shadow-primary); display: inline-block; svg { @@ -987,7 +1010,7 @@ code { .qr-alternative { margin-bottom: 20px; - color: $secondary-text-color; + color: var(--color-text-primary); flex: 150px; samp { @@ -996,42 +1019,6 @@ code { } } -.simple_form { - .warning { - box-sizing: border-box; - background: color.change($error-value-color, $alpha: 0.5); - color: $primary-text-color; - text-shadow: 1px 1px 0 color.change($base-shadow-color, $alpha: 0.3); - box-shadow: 0 2px 6px color.change($base-shadow-color, $alpha: 0.4); - border-radius: 4px; - padding: 10px; - margin-bottom: 15px; - - a { - color: $primary-text-color; - text-decoration: underline; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - } - - strong { - font-weight: 600; - display: block; - margin-bottom: 5px; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - } -} - .action-pagination { display: flex; flex-wrap: wrap; @@ -1051,7 +1038,7 @@ code { .post-follow-actions { text-align: center; - color: $darker-text-color; + color: var(--color-text-secondary); div { margin-bottom: 4px; @@ -1064,7 +1051,7 @@ code { h4 { font-size: 16px; - color: $primary-text-color; + color: var(--color-text-primary); text-align: center; margin-bottom: 20px; border: 0; @@ -1077,7 +1064,7 @@ code { } .scope-danger { - color: $warning-red; + color: var(--color-text-error); } .form_admin_settings_site_short_description, @@ -1092,8 +1079,9 @@ code { } .input-copy { - background: darken($ui-base-color, 10%); - border: 1px solid darken($ui-base-color, 14%); + color: var(--color-text-primary); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); border-radius: 4px; display: flex; align-items: center; @@ -1127,11 +1115,11 @@ code { } &.copied { - border-color: $valid-value-color; + border-color: var(--color-text-success); transition: none; button { - background: $valid-value-color; + background: var(--color-bg-success-base); transition: none; } } @@ -1153,8 +1141,8 @@ code { .permissions-list { &__item { padding: 15px; - color: $ui-secondary-color; - border-bottom: 1px solid lighten($ui-base-color, 4%); + color: var(--color-text-primary); + border-bottom: 1px solid var(--color-border-primary); display: flex; align-items: center; @@ -1166,7 +1154,7 @@ code { } &__type { - color: $darker-text-color; + color: var(--color-text-secondary); overflow-wrap: anywhere; } } @@ -1175,7 +1163,7 @@ code { flex: 0 0 auto; font-size: 18px; width: 30px; - color: $valid-value-color; + color: var(--color-text-success); display: flex; align-items: center; } @@ -1230,7 +1218,6 @@ code { align-items: center; padding-bottom: 30px; margin-bottom: 30px; - color: $white; li { flex: 0 0 auto; @@ -1239,11 +1226,11 @@ code { .separator { height: 2px; - background: $ui-base-lighter-color; + background: var(--color-border-primary); flex: 1 1 auto; &.completed { - background: $highlight-text-color; + background: var(--color-text-brand); } } @@ -1253,7 +1240,7 @@ code { width: 30px; height: 30px; border-radius: 50%; - border: 2px solid $ui-base-lighter-color; + border: 2px solid var(--color-border-primary); flex: 0 0 auto; display: flex; align-items: center; @@ -1268,7 +1255,7 @@ code { position: absolute; font-size: 14px; font-weight: 500; - color: $secondary-text-color; + color: var(--color-text-primary); padding-top: 10px; text-align: center; width: 100px; @@ -1291,14 +1278,14 @@ code { } .active .circle { - border-color: $highlight-text-color; + border-color: var(--color-text-brand); &::before { content: ''; width: 10px; height: 10px; border-radius: 50%; - background: $highlight-text-color; + background: var(--color-text-brand); position: absolute; left: 50%; top: 50%; @@ -1307,8 +1294,8 @@ code { } .completed .circle { - border-color: $highlight-text-color; - background: $highlight-text-color; + border-color: var(--color-text-brand); + background: var(--color-text-brand); } } @@ -1319,10 +1306,14 @@ code { &__header-input { display: block; border-radius: 8px; - background: var(--surface-variant-background-color); + background: var(--color-bg-secondary); position: relative; cursor: pointer; + &:hover { + background-color: var(--color-bg-brand-softer); + } + img { position: absolute; width: 100%; @@ -1337,12 +1328,12 @@ code { inset-inline-start: 50%; top: 50%; transform: translate(-50%, -50%); - color: $darker-text-color; + color: var(--color-text-secondary); z-index: 3; } &.selected .icon { - color: $primary-text-color; + color: var(--color-text-primary); transform: none; inset-inline-start: auto; inset-inline-end: 8px; @@ -1351,7 +1342,7 @@ code { } &.invalid img { - outline: 1px solid $error-value-color; + outline: 1px solid var(--color-text-error); outline-offset: -1px; } @@ -1361,14 +1352,10 @@ code { width: 100%; height: 100%; position: absolute; - background: color.change($error-value-color, $alpha: 0.25); + background: var(--color-bg-error-softer); z-index: 2; border-radius: 8px; } - - &:hover { - background-color: var(--surface-variant-active-background-color); - } } &__avatar-input { @@ -1384,7 +1371,7 @@ code { display: flex; align-items: center; gap: 16px; - color: $darker-text-color; + color: var(--color-text-secondary); font-size: 14px; line-height: 20px; @@ -1401,14 +1388,14 @@ code { flex: 1 1 auto; strong { - color: $primary-text-color; + color: var(--color-text-primary); font-weight: 600; } .hint { display: block; font-size: 14px; - color: $darker-text-color; + color: var(--color-text-secondary); } .recommended { @@ -1426,7 +1413,7 @@ code { &__toggle > div { display: flex; - border-inline-start: 1px solid var(--background-border-color); + border-inline-start: 1px solid var(--color-border-primary); padding-inline-start: 16px; } } @@ -1437,19 +1424,19 @@ code { padding: 8px 0; align-items: center; text-decoration: none; - color: $primary-text-color; + color: var(--color-text-primary); margin-bottom: 16px; &__text { flex: 1 1 auto; font-size: 14px; line-height: 20px; - color: $darker-text-color; + color: var(--color-text-secondary); strong { font-weight: 600; display: block; - color: $primary-text-color; + color: var(--color-text-primary); } .icon { diff --git a/app/javascript/styles/mastodon/modal.scss b/app/javascript/styles/mastodon/modal.scss index 7d060a2681a39b..6af2a182b63449 100644 --- a/app/javascript/styles/mastodon/modal.scss +++ b/app/javascript/styles/mastodon/modal.scss @@ -1,10 +1,7 @@ @use 'variables' as *; -@use 'functions' as *; .modal-layout { - background: darken($ui-base-color, 4%) - url('data:image/svg+xml;utf8,') - repeat-x bottom fixed; + background: var(--color-bg-brand-softer); display: flex; flex-direction: column; height: 100vh; @@ -38,3 +35,19 @@ margin-top: 0; } } + +.with-zig-zag-decoration { + &::after { + content: ''; + position: absolute; + inset: auto 0 0; + height: 32px; + background-color: var(--color-bg-brand-softer); + + /* Decorative zig-zag pattern at the bottom of the page */ + mask-image: url('data:image/svg+xml;utf8,'); + mask-position: bottom; + mask-repeat: repeat-x; + z-index: -1; + } +} diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss index e8f2f5c39400a9..19fb8dd505f72b 100644 --- a/app/javascript/styles/mastodon/polls.scss +++ b/app/javascript/styles/mastodon/polls.scss @@ -1,6 +1,5 @@ @use 'sass:color'; @use 'variables' as *; -@use 'functions' as *; .poll { margin-top: 16px; @@ -14,12 +13,12 @@ &__chart { border-radius: 4px; display: block; - background: darken($ui-primary-color, 5%); + background: rgb(from var(--color-text-brand) r g b / 60%); height: 5px; min-width: 1%; &.leading { - background: $ui-highlight-color; + background: var(--color-text-brand); } } @@ -39,12 +38,12 @@ // duplication &::-moz-progress-bar { border-radius: 4px; - background: darken($ui-primary-color, 5%); + background: rgb(from var(--color-text-brand) r g b / 60%); } &::-webkit-progress-value { border-radius: 4px; - background: darken($ui-primary-color, 5%); + background: rgb(from var(--color-text-brand) r g b / 60%); } } @@ -78,16 +77,16 @@ box-sizing: border-box; width: 100%; font-size: 14px; - color: $secondary-text-color; + color: var(--color-text-primary); outline: 0; font-family: inherit; - background: $ui-base-color; - border: 1px solid $darker-text-color; + background: var(--color-bg-primary); + border: 1px solid var(--color-text-secondary); border-radius: 4px; padding: 8px 12px; &:focus { - border-color: $ui-highlight-color; + border-color: var(--color-text-brand); } @media screen and (width <= 600px) { @@ -111,7 +110,7 @@ &__input { display: block; position: relative; - border: 1px solid $ui-primary-color; + border: 1px solid var(--color-text-secondary); box-sizing: border-box; width: 17px; height: 17px; @@ -125,13 +124,13 @@ &:active, &:focus, &:hover { - border-color: lighten($valid-value-color, 15%); + border-color: var(--color-text-success); border-width: 4px; } &.active { - background-color: $valid-value-color; - border-color: $valid-value-color; + background-color: var(--color-bg-success-base); + border-color: var(--color-text-success); } &::-moz-focus-inner { @@ -145,16 +144,16 @@ } &.disabled { - border-color: $dark-text-color; + border-color: var(--color-text-disabled); &.active { - background: $dark-text-color; + background: var(--color-text-disabled); } &:active, &:focus, &:hover { - border-color: $dark-text-color; + border-color: var(--color-text-disabled); border-width: 1px; } } @@ -165,7 +164,7 @@ &:active, &:focus, &:hover { - border-color: $ui-primary-color; + border-color: var(--color-text-primary); border-width: 1px; } } @@ -189,7 +188,7 @@ &__footer { padding-top: 6px; padding-bottom: 5px; - color: $dark-text-color; + color: var(--color-text-tertiary); } &__link { @@ -198,7 +197,7 @@ padding: 0; margin: 0; border: 0; - color: $dark-text-color; + color: var(--color-text-tertiary); text-decoration: underline; font-size: inherit; @@ -208,7 +207,7 @@ &:active, &:focus { - background-color: color.change($dark-text-color, $alpha: 0.1); + background-color: var(--color-bg-secondary); } } @@ -221,13 +220,13 @@ } .muted .poll { - color: $dark-text-color; + color: var(--color-text-tertiary); &__chart { - background: rgba(darken($ui-primary-color, 14%), 0.7); + background: rgb(from var(--color-text-brand) r g b / 40%); &.leading { - background: color.change($ui-highlight-color, $alpha: 0.5); + background: rgb(from var(--color-text-brand) r g b / 60%); } } } diff --git a/app/javascript/styles/mastodon/reset.scss b/app/javascript/styles/mastodon/reset.scss index 2dce637a0618e5..3644b94cdf68a0 100644 --- a/app/javascript/styles/mastodon/reset.scss +++ b/app/javascript/styles/mastodon/reset.scss @@ -1,5 +1,3 @@ -@use 'variables' as *; - /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) @@ -55,32 +53,6 @@ table { border-spacing: 0; } -@supports not selector(::-webkit-scrollbar) { - html { - scrollbar-color: $action-button-color var(--background-border-color); - } -} - -.custom-scrollbars { - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - ::-webkit-scrollbar-thumb { - background-color: $action-button-color; - border: 2px var(--background-border-color); - border-radius: 12px; - width: 6px; - box-shadow: inset 0 0 0 2px var(--background-border-color); - } - - ::-webkit-scrollbar-track { - background-color: var(--background-border-color); - border-radius: 0px; - } - - ::-webkit-scrollbar-corner { - background: transparent; - } +html:has(body.custom-scrollbars) { + scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary); } diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss index 6aa94a97bcf40a..48935b75d74a97 100644 --- a/app/javascript/styles/mastodon/rtl.scss +++ b/app/javascript/styles/mastodon/rtl.scss @@ -1,4 +1,3 @@ -@use 'functions' as *; @use 'variables' as *; body.rtl { @@ -42,12 +41,6 @@ body.rtl { transform: scale(-1, 1); } - .simple_form select { - background: $ui-base-color - url("data:image/svg+xml;utf8,") - no-repeat left 8px center / auto 16px; - } - .dismissable-banner, .warning-banner { &__action { diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index e82f2b551c1f0f..8e303aff685c1e 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -1,5 +1,4 @@ @use 'variables' as *; -@use 'functions' as *; .table { width: 100%; @@ -12,13 +11,13 @@ padding: 8px; line-height: 18px; vertical-align: top; - border-bottom: 1px solid var(--background-border-color); + border-bottom: 1px solid var(--color-border-primary); text-align: start; - background: var(--background-color); + background: var(--color-bg-primary); &.critical { font-weight: 700; - color: $gold-star; + color: var(--color-text-warning); } } @@ -33,7 +32,7 @@ & > tbody > tr:nth-child(odd) > td, & > tbody > tr:nth-child(odd) > th { - background: var(--background-color); + background: var(--color-bg-primary); } & > tbody > tr:last-child > td, @@ -42,11 +41,11 @@ } a { - color: $darker-text-color; + color: var(--color-text-secondary); text-decoration: none; &:hover { - color: $highlight-text-color; + color: var(--color-text-brand); } } @@ -84,30 +83,30 @@ & > tbody > tr > td { padding: 11px 10px; background: transparent; - border: 1px solid var(--background-border-color); - color: $secondary-text-color; + border: 1px solid var(--color-border-primary); + color: var(--color-text-primary); } & > tbody > tr > th { - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 600; } } &.batch-table { & > thead > tr > th { - background: var(--background-color); - border-top: 1px solid var(--background-border-color); - border-bottom: 1px solid var(--background-border-color); + background: var(--color-bg-primary); + border-top: 1px solid var(--color-border-primary); + border-bottom: 1px solid var(--color-border-primary); &:first-child { border-radius: 4px 0 0; - border-inline-start: 1px solid var(--background-border-color); + border-inline-start: 1px solid var(--color-border-primary); } &:last-child { border-radius: 0 4px 0 0; - border-inline-end: 1px solid var(--background-border-color); + border-inline-end: 1px solid var(--color-border-primary); } } } @@ -138,12 +137,12 @@ a.table-action-link { display: inline-block; margin-inline-end: 5px; padding: 0 10px; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 500; white-space: nowrap; &:hover { - color: $highlight-text-color; + color: var(--color-text-brand); } &:first-child { @@ -196,8 +195,8 @@ a.table-action-link { position: sticky; top: 0; z-index: 200; - border: 1px solid var(--background-border-color); - background: var(--background-color); + border: 1px solid var(--color-border-primary); + background: var(--color-bg-primary); border-radius: 4px 4px 0 0; height: 47px; align-items: center; @@ -213,13 +212,13 @@ a.table-action-link { } &__select-all { - background: var(--background-color); + background: var(--color-bg-primary); height: 47px; align-items: center; justify-content: center; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; - color: $secondary-text-color; + color: var(--color-text-primary); display: none; &.active { @@ -248,7 +247,7 @@ a.table-action-link { background: transparent; border: 0; font: inherit; - color: $highlight-text-color; + color: var(--color-text-brand); border-radius: 4px; font-weight: 700; padding: 8px; @@ -256,16 +255,16 @@ a.table-action-link { &:hover, &:focus, &:active { - background: lighten($ui-base-color, 8%); + background: var(--color-bg-secondary); } } } &__form { padding: 16px; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; - background: var(--background-color); + background: var(--color-bg-primary); .fields-row { padding-top: 0; @@ -274,13 +273,13 @@ a.table-action-link { } &__row { - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-top: 0; - background: var(--background-color); + background: var(--color-bg-primary); @media screen and (max-width: $no-gap-breakpoint) { .optional &:first-child { - border-top: 1px solid var(--background-border-color); + border-top: 1px solid var(--color-border-primary); } } @@ -331,7 +330,7 @@ a.table-action-link { &__extra { flex: 0 0 auto; text-align: end; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 500; } } @@ -362,21 +361,6 @@ a.table-action-link { padding: 0; } - .nothing-here { - border: 1px solid var(--background-border-color); - border-top: 0; - box-shadow: none; - background: var(--background-color); - - @media screen and (max-width: $no-gap-breakpoint) { - border-top: 1px solid var(--background-border-color); - } - - &--no-toolbar { - border-top: 1px solid var(--background-border-color); - } - } - @media screen and (width <= 870px) { .accounts-table tbody td.optional { display: none; diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss index f3ddf341d27e26..69c79cd1e6f2a0 100644 --- a/app/javascript/styles/mastodon/widgets.scss +++ b/app/javascript/styles/mastodon/widgets.scss @@ -1,6 +1,5 @@ @use 'sass:color'; @use 'variables' as *; -@use 'functions' as *; .directory { &__tag { @@ -12,24 +11,24 @@ display: flex; align-items: center; justify-content: space-between; - border: 1px solid var(--background-border-color); + border: 1px solid var(--color-border-primary); border-radius: 4px; padding: 15px; text-decoration: none; color: inherit; - box-shadow: 0 0 15px color.change($base-shadow-color, $alpha: 0.2); + box-shadow: 0 0 15px var(--color-shadow-primary); } & > a { &:hover, &:active, &:focus { - background: $ui-base-color; + background: var(--color-bg-primary); } } &.active > a { - background: $ui-highlight-color; + background: var(--color-bg-brand-base); cursor: default; } @@ -42,7 +41,7 @@ flex: 1 1 auto; font-size: 18px; font-weight: 700; - color: $primary-text-color; + color: var(--color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -52,7 +51,7 @@ font-weight: 400; font-size: 15px; margin-top: 8px; - color: $darker-text-color; + color: var(--color-text-secondary); } } @@ -60,7 +59,7 @@ &, small, .trends__item__current { - color: $primary-text-color; + color: var(--color-text-primary); } } @@ -70,7 +69,7 @@ } &.active .avatar-stack .account__avatar { - border-color: $ui-highlight-color; + border-color: var(--color-text-brand); } .trends__item__current { @@ -95,7 +94,7 @@ thead th { text-align: center; text-transform: uppercase; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 700; padding: 10px; @@ -107,7 +106,7 @@ tbody td { padding: 15px 0; vertical-align: middle; - border-bottom: 1px solid lighten($ui-base-color, 8%); + border-bottom: 1px solid var(--color-border-primary); } tbody tr:last-child td { @@ -119,11 +118,11 @@ text-align: center; font-size: 15px; font-weight: 500; - color: $primary-text-color; + color: var(--color-text-primary); small { display: block; - color: $darker-text-color; + color: var(--color-text-secondary); font-weight: 400; font-size: 14px; } @@ -132,7 +131,7 @@ tbody td.accounts-table__extra { width: 120px; text-align: end; - color: $darker-text-color; + color: var(--color-text-secondary); padding-inline-end: 16px; a { @@ -142,7 +141,7 @@ &:focus, &:hover, &:active { - color: $highlight-text-color; + color: var(--color-text-brand); } } } @@ -159,15 +158,15 @@ .icon { &.active { - color: $highlight-text-color; + color: var(--color-text-brand); } &.passive { - color: $passive-text-color; + color: var(--color-text-warning); } &.active.passive { - color: $active-passive-text-color; + color: var(--color-text-success); } } diff --git a/app/javascript/styles_new/application.scss b/app/javascript/styles_new/application.scss deleted file mode 100644 index e16e5368e7d3c8..00000000000000 --- a/app/javascript/styles_new/application.scss +++ /dev/null @@ -1,7 +0,0 @@ -@use 'mastodon/css_variables'; -@use 'mastodon/variables'; -@use 'common'; - -html { - color-scheme: dark; -} diff --git a/app/javascript/styles_new/contrast.scss b/app/javascript/styles_new/contrast.scss deleted file mode 100644 index af73c88fef6cdd..00000000000000 --- a/app/javascript/styles_new/contrast.scss +++ /dev/null @@ -1,8 +0,0 @@ -@use 'mastodon/css_variables'; -@use 'mastodon/variables'; -@use 'common'; -@use 'contrast/diff'; - -html { - color-scheme: dark; -} diff --git a/app/javascript/styles_new/contrast/diff.scss b/app/javascript/styles_new/contrast/diff.scss deleted file mode 100644 index f809c7cdc3a0bc..00000000000000 --- a/app/javascript/styles_new/contrast/diff.scss +++ /dev/null @@ -1,54 +0,0 @@ -:root { - /* TEXT TOKENS */ - - --color-text-primary: var(--color-grey-50); - --color-text-secondary: var(--color-grey-300); - --color-text-tertiary: var(--color-grey-400); - --color-text-brand: var(--color-indigo-300); - --color-text-status-links: var(--color-text-brand); - - /* BORDER TOKENS */ - - --border-strength-primary: 18%; -} - -.status__content a, -.reply-indicator__content a, -.edit-indicator__content a, -.link-footer a, -.status__content__read-more-button, -.status__content__translate-button { - text-decoration: underline; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - - &.mention { - text-decoration: none; - - span { - text-decoration: underline; - } - - &:hover, - &:focus, - &:active { - span { - text-decoration: none; - } - } - } -} - -.link-button:disabled { - cursor: not-allowed; - - &:hover, - &:focus, - &:active { - text-decoration: none !important; - } -} diff --git a/app/javascript/styles_new/entrypoints/inert.scss b/app/javascript/styles_new/entrypoints/inert.scss deleted file mode 100644 index a60045d7be80b2..00000000000000 --- a/app/javascript/styles_new/entrypoints/inert.scss +++ /dev/null @@ -1,14 +0,0 @@ -/* This is needed for the wicg-inert polyfill */ - -[inert] { - pointer-events: none; - cursor: default; -} - -[inert], -[inert] * { - user-select: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; -} diff --git a/app/javascript/styles_new/entrypoints/mailer.scss b/app/javascript/styles_new/entrypoints/mailer.scss deleted file mode 100644 index fcbbd66f4c7747..00000000000000 --- a/app/javascript/styles_new/entrypoints/mailer.scss +++ /dev/null @@ -1,1030 +0,0 @@ -@use '../fonts/inter'; - -body { - accent-color: #6364ff; - overflow-wrap: anywhere; - margin: 0; - background-color: #f3f2f5; - padding: 0; - -webkit-text-size-adjust: none; - text-size-adjust: none; -} - -p, -h1, -h2, -h3, -h4, -h5, -h6 { - margin: 0; - background-color: transparent; - padding: 0; - border: none; - font-family: Inter, 'Lucida Grande', sans-serif; -} - -img { - max-width: 100%; - height: auto; - border: none; - text-indent: 0; - vertical-align: middle; - color: inherit; - font-family: inherit; -} - -table { - border: none; -} - -table + p { - margin-top: 16px; -} - -.email { - min-width: 280px; - font-family: Inter, 'Lucida Grande', sans-serif; - overflow-wrap: anywhere; - color: #17063b; - background-color: #f3f2f5; -} - -.email-container { - max-width: 740px; - margin: 0 auto; - width: 100%; -} - -// Outer email card -.email-card-table { - border-collapse: collapse; - width: 100%; -} - -.email-card-td { - overflow: hidden; - box-shadow: 0 4px 16px 0 rgba(23, 6, 59, 4%); - background-color: #fff; -} - -// Inner email card -.email-inner-card-table { - border-collapse: separate; - width: 100%; - border-radius: 12px; -} - -.email-inner-card-td-without-padding, -.email-inner-card-td { - border-radius: 12px; - overflow: hidden; - box-shadow: 0 4px 16px 0 rgba(23, 6, 59, 8%); - background-color: #fff; - border: 1px solid #dfdee3; -} - -.email-inner-card-td { - padding: 24px; -} - -.email-inner-nested-card-td { - border-radius: 12px; - padding: 18px; - overflow: hidden; - background-color: #fff; - border: 1px solid #dfdee3; -} - -// Account -.email-account-banner-table { - background-color: #f3f2f5; - border-top-left-radius: 12px; - border-top-right-radius: 12px; -} - -.email-account-banner-td { - border-top-left-radius: 12px; - border-top-right-radius: 12px; - height: 140px; - vertical-align: bottom; - background-position: center !important; - background-size: cover !important; -} - -.email-account-banner-inner-td { - padding: 24px 24px 0; - mso-padding-alt: 24px; -} - -.email-account-banner-overlap-div { - max-height: 42px; -} - -.email-account-banner-icon-table { - width: auto; - margin: 0; - overflow: hidden; - border-radius: 8px; - border-collapse: separate; - background-color: #fff; - border: 2px solid #fff; - - img { - display: block; - max-width: 100%; - border: none; - border-radius: 6px; - } -} - -.email-account-body-td { - padding: 56px 24px 24px; - mso-padding-alt: 24px; -} - -.email-account-name { - font-size: 16px; - font-weight: 600; - line-height: 24px; - color: #17063b; -} - -.email-account-handle { - font-size: 14px; - line-height: 20px; - color: #746a89; -} - -.email-account-stats-table { - td { - padding-right: 16px; - font-size: 14px; - line-height: 20px; - color: #746a89; - } - - b { - font-weight: 600; - color: #17063b; - } - - span { - white-space: nowrap; - } -} - -// Utility classes -.email-w-full { - table-layout: fixed; - width: 100%; -} - -.email-prose { - p, - ul, - ol { - color: #17063b; - font-size: 14px; - line-height: 20px; - - &:not(:last-child) { - margin-bottom: 16px; - } - - a:not([class]) { - color: #6364ff; - text-decoration: none; - - &:hover { - color: #563acc !important; - } - } - } -} - -.email-dir-rtl { - direction: rtl; - - [dir='rtl'] & { - direction: ltr; - } -} - -.email-dir-ltr { - direction: ltr; -} - -.email-padding-24 { - padding: 24px; -} - -.email-padding-top-24 { - padding-top: 24px; -} - -.email-padding-top-16 { - padding-top: 16px; -} - -.email-padding-top-0 { - padding-top: 0; -} - -.email-border-top { - border-top: 1px solid #dfdee3; -} - -.email-border-bottom { - border-bottom: 1px solid #dfdee3; -} - -.email-desktop-flex { - font-size: 0; - max-width: 740px; - margin-left: auto; - margin-right: auto; - - &.email-dir-rtl > .email-desktop-column { - direction: ltr; - - [dir='rtl'] & { - direction: rtl; - } - } -} - -.email-desktop-column { - display: inline-block; - width: 100%; - max-width: none; - text-align: start; - vertical-align: top; - font-size: 16px; -} - -// Header -.email-header-td { - padding: 16px 32px; - background-color: #1b001f; - background-image: url('../../images/mailer-new/common/header-bg-start.png'); - background-position: left top; - background-repeat: repeat; -} - -.email-header-logo-table { - width: auto; - margin: 0; -} - -.email-header-logo-td { - padding: 16px 0; - font-size: 0; - - img { - color: #fff; - font-size: 16px; - font-weight: bold; - max-height: 40px; - } -} - -.email-header-logo-a { - display: inline-block; - - img { - display: inline-block; - color: #fff; - } -} - -.email-header-logo-div { - max-height: 0; -} - -.email-header-logo-p { - word-break: break-all; - padding-left: 40px; - padding-top: 26px; - font-size: 11px; - line-height: 13px; - color: #8d808f; - text-align: left; -} - -.email-header-logo-span { - display: block; - text-align: right; -} - -.email-header-heading-td { - padding: 16px 0; -} - -.email-header-heading-img-td { - width: 56px; - text-align: left; - vertical-align: top; - - img { - width: 56px; - height: 56px; - border-radius: 12px; - } -} - -.email-header-heading-txt-td { - vertical-align: middle; - padding-left: 16px; - padding-right: 16px; - - h1 { - margin-bottom: 5px; - color: #fff; - font-size: 24px; - line-height: 28px; - font-weight: 600; - } - - p { - color: #a399a5; - font-size: 18px; - line-height: 21.6px; - font-weight: 500; - } - - &:only-child { - padding-left: 0; - padding-right: 0; - } -} - -.email-header-card-table { - width: 100%; - border-collapse: separate; - overflow: hidden; - border-radius: 12px; - background-color: #fff; - border: 2px solid #fff; - box-shadow: 0 4px 16px 0 rgba(23, 6, 59, 8%); -} - -.email-header-card { - position: relative; - max-height: 100px; -} - -.email-header-card-banner-td { - border-radius: 12px 12px 0 0; - width: 236px; - height: 80px; - background-color: #f3f2f5 !important; - background-position: center !important; - background-size: cover !important; -} - -.email-header-card-body-td { - padding: 12px; - - .email-btn-table { - width: 100%; - max-width: 212px; - } -} - -.email-header-card-instance { - margin-bottom: 4px; - overflow: hidden; - text-overflow: ellipsis; - word-break: break-all; - color: #17063b; - font-size: 14px; - line-height: 20px; - font-weight: 600; - - &:only-of-type { - margin-bottom: 12px; - } -} - -.email-header-card-description { - margin-bottom: 12px; - color: #746a89; - font-size: 12px; - line-height: 16px; - max-height: 32px; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -} - -// To make the design work with images off -// we create an empty div that overlaps with -// the rest of the content with a dark background. -.email-header-after-div { - max-height: 0; -} - -.email-header-after-inside-div { - height: 30px; - background-color: #1b001f; -} - -// Body content -.email-body-td { - background-image: url('../../images/mailer-new/common/header-bg-end.png'); - background-position: left top; - background-repeat: no-repeat; -} - -.email-body-padding-td { - padding: 0 32px 32px; - mso-padding-alt: 32px; -} - -.email-body-columns-td { - border-top: 1px solid #dfdee3; - padding: 32px 24px 8px; -} - -.email-body-huge-padding-td { - padding: 110px 32px 32px; - mso-padding-alt: 32px; -} - -.email-body-padding-td { - & > p { - font-size: 14px; - line-height: 20px; - color: #17063b; - - a { - color: #6364ff; - text-decoration: none; - - &:hover { - color: #563acc !important; - } - } - } -} - -// Texts -.email-h2 { - margin-bottom: 4px; - color: #17063b; - font-size: 18px; - font-weight: 600; - line-height: 28px; -} - -.email-h-sub { - margin-bottom: 16px; - color: #746a89; - font-size: 14px; - line-height: 16px; -} - -.email-p { - margin-bottom: 16px; - color: #746a89; - font-size: 14px; - font-weight: 400; - line-height: 20px; -} - -// Footer -.email-footer-td { - padding: 28px 32px 32px; - text-align: center; -} - -.email-footer-logo-a { - display: inline-block; -} - -.email-footer-p { - color: #9b94ab; - text-align: center; - font-size: 12px; - line-height: 20px; - - a { - color: #9b94ab; - text-decoration: underline; - } - - &:first-child { - margin-bottom: 12px; - } -} - -// Button -.email-btn-table { - margin: 0; - max-width: 100%; - border-collapse: separate; - border-radius: 8px; - background-color: #6364ff; -} - -.email-btn-td { - height: 40px; - text-align: center; - mso-padding-alt: 0 35px; - word-break: normal; -} - -.email-btn-a { - display: block; - border-radius: 8px; - padding-left: 35px; - padding-right: 35px; - padding-top: 10px; - padding-bottom: 10px; - text-align: center; - font-family: Inter, 'Lucida Grande', sans-serif; - font-size: 14px; - font-weight: 600; - line-height: 20px; - color: #fff; - text-decoration: none; - transition: background-color 0.3s ease-in-out; -} - -// Status -.email-status-header-img { - vertical-align: top; - width: 48px; - - img { - width: 48px; - height: 48px; - border-radius: 8px; - overflow: hidden; - } -} - -.email-quote-header-img { - width: 34px; - - img { - width: 34px; - height: 34px; - border-radius: 8px; - overflow: hidden; - } -} - -.email-status-header-text { - padding-left: 16px; - padding-right: 16px; - vertical-align: middle; -} - -.email-quote-header-text { - padding-left: 14px; - padding-right: 14px; - vertical-align: middle; -} - -.email-status-header-name { - font-size: 16px; - font-weight: 600; - line-height: 24px; - color: #17063b; -} - -.email-status-header-handle { - font-size: 14px; - line-height: 20px; - color: #746a89; -} - -.email-quote-header-name { - font-size: 14px; - font-weight: 600; - line-height: 18px; - color: #17063b; -} - -.email-quote-header-handle { - font-size: 13px; - line-height: 18px; - color: #746a89; -} - -.email-status-content { - padding-top: 24px; -} - -.email-status-spoiler { - color: #746a89; - font-style: italic; - margin-bottom: 8px; -} - -.email-status-prose { - .quote-inline { - display: none; - } - - p { - font-size: 14px; - line-height: 20px; - margin-bottom: 20px; - color: #17063b; - white-space: pre-wrap; - unicode-bidi: plaintext; - } - - a { - color: #6364ff; - text-decoration: none; - - &:hover { - color: #563acc !important; - } - } - - .invisible { - font-size: 0; - line-height: 0; - display: inline-block; - width: 0; - height: 0; - position: absolute; - } - - .ellipsis { - &::after { - content: '…'; - } - } -} - -.email-status-media { - margin-top: 16px; - font-size: 14px; - line-height: 20px; - color: #17063b; - - img { - border-radius: 8px; - } - - a { - color: #6364ff; - text-decoration: none; - - &:hover { - color: #563acc !important; - } - } -} - -.email-status-footer { - margin-top: 16px; - font-size: 12px; - line-height: 16px; - color: #746a89; - - a { - color: #746a89; - } - - a:hover { - color: #746a89 !important; - text-decoration: underline !important; - } -} - -// Purple frame for emphasis -.email-frame-table { - background-color: #efefff; - border-radius: 8px; -} - -.email-frame-td { - padding: 16px; -} - -.email-frame-wrapper-td { - padding-bottom: 16px; -} - -.email-frame-td > p { - text-align: center; - font-size: 16px; - line-height: 24px; -} - -// Checklist item -.email-checklist-wrapper-td { - padding: 4px 0; -} - -.email-checklist-table { - border-radius: 12px; - border-width: 1px; - border-style: solid; - border-color: #efefff; - background-color: #fff; -} - -.email-checklist-checked { - border-color: #c4e6d7; - background-color: #eaf6f1; -} - -.email-checklist-td { - padding: 16px 16px 6px; -} - -.email-checklist-icons-td { - width: 84px; - vertical-align: top; -} - -.email-checklist-icons-checkbox-td { - width: 20px; - vertical-align: middle; - - img { - max-width: 100%; - width: 20px; - } -} - -.email-checklist-icons-step-td { - width: 64px; - text-align: center; - vertical-align: middle; - - img { - max-width: 100%; - width: 40px; - } -} - -.email-checklist-text-td { - h3 { - margin: 0 0 4px; - color: #17063b; - font-size: 14px; - font-weight: 600; - line-height: 16.8px; - - .email-checklist-checked & { - color: #746a89; - text-decoration: line-through; - } - } - - p { - margin: 0 0 12px; - color: #746a89; - font-size: 14px; - line-height: 16.8px; - } - - .email-btn-table { - width: 100px; - } - - .email-btn-td { - mso-padding-alt: 10px; - } - - .email-btn-a { - padding-left: 10px; - padding-right: 10px; - } - - div + div { - margin-inline-start: auto; - margin-bottom: 12px; - } -} - -// Welcome email -.email-welcome-apps-btns { - font-size: 12px; - line-height: 44px; -} - -.email-column-td { - padding: 0 8px; - vertical-align: top; -} - -.email-link-with-arrow { - color: #6364ff; - font-size: 14px; - font-weight: 600; - line-height: 16.8px; - - &:hover { - color: #563acc !important; - } - - span { - font-size: 12px; - font-weight: 400; - } -} - -.email-column-action-td { - padding: 24px 0; - color: #6364ff; - font-size: 14px; - font-weight: 600; - line-height: 16.8px; - text-align: center; -} - -// Follow and hashtags -.email-mini-wrapper-td { - padding: 4px 0; - - table { - table-layout: fixed; - } -} - -.email-mini-td { - border-radius: 12px; - border: 1px solid #e8e6eb; - background-color: #fff; - padding: 15px 16px; -} - -.email-mini-follow-img-td { - width: 40px; - vertical-align: top; - - img { - border-radius: 8px; - } -} - -.email-mini-follow-text-td { - padding-left: 8px; - padding-right: 16px; - vertical-align: top; - - h3 { - color: #17063b; - font-size: 14px; - font-weight: 600; - line-height: 20px; - } - - p { - color: #746a89; - font-size: 12px; - font-weight: 400; - line-height: 16px; - } -} - -.email-mini-follow-btn-td { - width: 68px; - vertical-align: top; - - .email-btn-table { - width: 100%; - } - - .email-btn-td { - mso-padding-alt: 10px; - } - - .email-btn-a { - padding-left: 10px; - padding-right: 10px; - } -} - -.email-mini-hashtag-td { - height: 40px; - - td { - vertical-align: middle; - } - - h3 { - color: #17063b; - font-size: 14px; - font-weight: 600; - line-height: 20px; - } - - p { - color: #746a89; - font-size: 12px; - font-weight: 400; - line-height: 16px; - word-break: break-all; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} - -.email-mini-hashtag-img-td { - width: 40px; - height: 20px; - white-space: nowrap; - text-indent: -2px; - font-size: 0; - - & + td { - padding-left: 8px; - } -} - -.email-mini-hashtag-img-span { - display: inline-block; - max-width: 12px; - font-size: 12px; - - img { - width: 16px; - height: 16px; - border-radius: 50%; - max-width: none; - border: 2px solid #fff; - vertical-align: middle; - } -} - -// Extra content on light purple background -.email-extra-wave { - height: 42px; - background-image: url('../../images/mailer-new/welcome/purple-extra-soft-wave.png'); - background-position: bottom center; - background-repeat: no-repeat; -} - -.email-extra-td { - padding: 32px 32px 24px; - background-color: #f0f0ff; - background-image: url('../../images/mailer-new/welcome/purple-extra-soft-spacer.png'); // Using an image to maintain the color even in forced dark modes - - .email-column-td { - padding-top: 8px; - padding-bottom: 8px; - } -} - -// Feature card -.email-feature-wrapper-td { - padding: 8px 0; -} - -.email-feature-td { - padding: 24px; - background-color: #fff; - border: 1px solid #e8e6eb; - border-radius: 12px; -} - -// Responsive -/* stylelint-disable-next-line media-feature-range-notation -- Basic media queries have better support across email clients. */ -@media only screen and (min-width: 740px) { - .email-desktop-p-8 { - padding: 32px !important; - } - - .email-desktop-rounded-16px { - border-radius: 16px !important; - } - - .email-header-td { - border-radius: 16px 16px 0 0 !important; - } - - .email-desktop-flex { - display: flex; - } - - .email-header-left { - padding-right: 32px; - } - - .email-header-right { - width: 240px; - margin-inline-start: auto; - } - - .email-desktop-column { - max-width: 346px !important; - } - - .email-desktop-text-right { - text-align: right; - } -} diff --git a/app/javascript/styles_new/fonts/inter.scss b/app/javascript/styles_new/fonts/inter.scss deleted file mode 100644 index 816fc75b85a198..00000000000000 --- a/app/javascript/styles_new/fonts/inter.scss +++ /dev/null @@ -1,8 +0,0 @@ -@font-face { - font-family: Inter; - src: url('../../fonts/inter/inter-variable-font-slnt-wght.woff2') - format('woff2-variations'); - font-weight: 100 900; - font-style: normal; - mso-generic-font-family: swiss; -} diff --git a/app/javascript/styles_new/fonts/roboto-mono.scss b/app/javascript/styles_new/fonts/roboto-mono.scss deleted file mode 100644 index d51cb01c91f60a..00000000000000 --- a/app/javascript/styles_new/fonts/roboto-mono.scss +++ /dev/null @@ -1,13 +0,0 @@ -@font-face { - font-family: mastodon-font-monospace; - src: - local('Roboto Mono'), - url('@/fonts/roboto-mono/robotomono-regular-webfont.woff2') format('woff2'), - url('@/fonts/roboto-mono/robotomono-regular-webfont.woff') format('woff'), - url('@/fonts/roboto-mono/robotomono-regular-webfont.ttf') format('truetype'), - url('@/fonts/roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular') - format('svg'); - font-weight: 400; - font-display: swap; - font-style: normal; -} diff --git a/app/javascript/styles_new/fonts/roboto.scss b/app/javascript/styles_new/fonts/roboto.scss deleted file mode 100644 index ea4b842b0c44a4..00000000000000 --- a/app/javascript/styles_new/fonts/roboto.scss +++ /dev/null @@ -1,55 +0,0 @@ -@font-face { - font-family: mastodon-font-sans-serif; - src: - local('Roboto Italic'), - url('@/fonts/roboto/roboto-italic-webfont.woff2') format('woff2'), - url('@/fonts/roboto/roboto-italic-webfont.woff') format('woff'), - url('@/fonts/roboto/roboto-italic-webfont.ttf') format('truetype'), - url('@/fonts/roboto/roboto-italic-webfont.svg#roboto-italic-webfont') - format('svg'); - font-weight: normal; - font-display: swap; - font-style: italic; -} - -@font-face { - font-family: mastodon-font-sans-serif; - src: - local('Roboto Bold'), - url('@/fonts/roboto/roboto-bold-webfont.woff2') format('woff2'), - url('@/fonts/roboto/roboto-bold-webfont.woff') format('woff'), - url('@/fonts/roboto/roboto-bold-webfont.ttf') format('truetype'), - url('@/fonts/roboto/roboto-bold-webfont.svg#roboto-bold-webfont') - format('svg'); - font-weight: bold; - font-display: swap; - font-style: normal; -} - -@font-face { - font-family: mastodon-font-sans-serif; - src: - local('Roboto Medium'), - url('@/fonts/roboto/roboto-medium-webfont.woff2') format('woff2'), - url('@/fonts/roboto/roboto-medium-webfont.woff') format('woff'), - url('@/fonts/roboto/roboto-medium-webfont.ttf') format('truetype'), - url('@/fonts/roboto/roboto-medium-webfont.svg#roboto-medium-webfont') - format('svg'); - font-weight: 500; - font-display: swap; - font-style: normal; -} - -@font-face { - font-family: mastodon-font-sans-serif; - src: - local('Roboto'), - url('@/fonts/roboto/roboto-regular-webfont.woff2') format('woff2'), - url('@/fonts/roboto/roboto-regular-webfont.woff') format('woff'), - url('@/fonts/roboto/roboto-regular-webfont.ttf') format('truetype'), - url('@/fonts/roboto/roboto-regular-webfont.svg#roboto-regular-webfont') - format('svg'); - font-weight: normal; - font-display: swap; - font-style: normal; -} diff --git a/app/javascript/styles_new/mastodon-light.scss b/app/javascript/styles_new/mastodon-light.scss deleted file mode 100644 index 494efdbbde99da..00000000000000 --- a/app/javascript/styles_new/mastodon-light.scss +++ /dev/null @@ -1,9 +0,0 @@ -@use 'mastodon-light/css_variables'; -@use 'mastodon/variables' with ( - $emojis-requiring-inversion: 'chains' -); -@use 'common'; - -html { - color-scheme: light; -} diff --git a/app/javascript/styles_new/mastodon-light/css_variables.scss b/app/javascript/styles_new/mastodon-light/css_variables.scss deleted file mode 100644 index 70745ec071ad8c..00000000000000 --- a/app/javascript/styles_new/mastodon-light/css_variables.scss +++ /dev/null @@ -1,214 +0,0 @@ -@use '../mastodon/theme_utils' as utils; - -:root { - --color-black: #000; - --color-grey-950: #181821; - --color-grey-800: #292938; - --color-grey-700: #444664; - --color-grey-600: #545778; - --color-grey-500: #696d91; - --color-grey-400: #8b8dac; - --color-grey-300: #b4b6cb; - --color-grey-200: #d8d9e3; - --color-grey-100: #f0f0f5; - --color-grey-50: #f0f1ff; - --color-white: #fff; - --color-indigo-600: #6147e6; - --color-indigo-400: #8886ff; - --color-indigo-300: #a5abfd; - --color-indigo-200: #c8cdfe; - --color-indigo-100: #e0e3ff; - --color-indigo-50: #f0f1ff; - --color-red-500: #ff637e; - --color-red-600: #ec003f; - --color-yellow-400: #ffb900; - --color-yellow-600: #e17100; - --color-green-400: #05df72; - --color-green-600: #00a63e; - - /* TEXT TOKENS */ - - --color-text-primary: var(--color-grey-950); - --color-text-secondary: var(--color-grey-600); - --color-text-tertiary: var(--color-grey-500); - --color-text-on-inverted: var(--color-white); - --color-text-brand: var(--color-indigo-600); - --color-text-brand-soft: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-brand) - ); - --color-text-on-brand-base: var(--color-white); - --color-text-error: var(--color-red-600); - --color-text-on-error-base: var(--color-white); - --color-text-warning: var(--color-yellow-600); - --color-text-on-warning-base: var(--color-white); - --color-text-success: var(--color-green-600); - --color-text-on-success-base: var(--color-white); - --color-text-disabled: var(--color-grey-300); - --color-text-on-disabled: var(--color-grey-200); - --color-text-bookmark-highlight: var(--color-text-error); - --color-text-favourite-highlight: var(--color-text-warning); - --color-text-on-media: var(--color-white); - --color-text-status-links: var(--color-text-brand); - - /* BACKGROUND TOKENS */ - - // Neutrals - --color-bg-primary: var(--color-white); - --overlay-strength-secondary: 5%; - --color-bg-secondary-base: var(--color-grey-600); - --color-bg-secondary: #{color-mix( - in oklab, - var(--color-bg-primary), - var(--color-bg-secondary-base) var(--overlay-strength-secondary) - )}; - --color-bg-secondary-solid: #{color-mix( - in srgb, - var(--color-bg-primary), - var(--color-bg-secondary-base) var(--overlay-strength-secondary) - )}; - --color-bg-tertiary: #{color-mix( - in oklab, - var(--color-bg-primary), - var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary)) - )}; - - // Utility - --color-bg-ambient: var(--color-bg-primary); - --color-bg-elevated: var(--color-bg-primary); - --color-bg-inverted: var(--color-grey-950); - --color-bg-media-base: var(--color-black); - --color-bg-media-strength: 65%; - --color-bg-media: #{utils.css-alpha( - var(--color-bg-media-base), - var(--color-bg-media-strength) - )}; - --color-bg-overlay: var(--color-bg-primary); - --color-bg-disabled: var(--color-grey-400); - - // Brand - --overlay-strength-brand: 8%; - --color-bg-brand-base: var(--color-indigo-600); - --color-bg-brand-base-hover: color-mix( - in oklab, - var(--color-bg-brand-base), - black var(--overlay-strength-brand) - ); - --color-bg-brand-soft: #{utils.css-alpha( - var(--color-bg-brand-base), - calc(var(--overlay-strength-brand) * 1.5) - )}; - --color-bg-brand-softer: #{utils.css-alpha( - var(--color-bg-brand-base), - var(--overlay-strength-brand) - )}; - - // Error - --overlay-strength-error: 12%; - --color-bg-error-base: var(--color-red-600); - --color-bg-error-base-hover: color-mix( - in oklab, - var(--color-bg-error-base), - black var(--overlay-strength-error) - ); - --color-bg-error-soft: #{utils.css-alpha( - var(--color-bg-error-base), - calc(var(--overlay-strength-error) * 1.5) - )}; - --color-bg-error-softer: #{utils.css-alpha( - var(--color-bg-error-base), - var(--overlay-strength-error) - )}; - - // Warning - --overlay-strength-warning: 10%; - --color-bg-warning-base: var(--color-yellow-600); - --color-bg-warning-base-hover: color-mix( - in oklab, - var(--color-bg-warning-base), - black var(--overlay-strength-warning) - ); - --color-bg-warning-soft: #{utils.css-alpha( - var(--color-bg-warning-base), - calc(var(--overlay-strength-warning) * 1.5) - )}; - --color-bg-warning-softer: #{utils.css-alpha( - var(--color-bg-warning-base), - var(--overlay-strength-warning) - )}; - - // Success - --overlay-strength-success: 15%; - --color-bg-success-base: var(--color-green-600); - --color-bg-success-base-hover: color-mix( - in oklab, - var(--color-bg-success-base), - black var(--overlay-strength-success) - ); - --color-bg-success-soft: #{utils.css-alpha( - var(--color-bg-success-base), - calc(var(--overlay-strength-success) * 1.5) - )}; - --color-bg-success-softer: #{utils.css-alpha( - var(--color-bg-success-base), - var(--overlay-strength-success) - )}; - - /* BORDER TOKENS */ - - --border-strength-primary: 15%; - --color-border-primary: color-mix( - in oklab, - var(--color-bg-primary), - var(--color-grey-950) var(--border-strength-primary) - ); - --color-border-media: rgb(252 248 255 / 15%); - --color-border-on-bg-secondary: var(--color-grey-200); - --color-border-on-bg-brand-softer: var(--color-indigo-200); - --color-border-on-bg-error-softer: #{utils.css-alpha( - var(--color-text-error), - 50% - )}; - --color-border-on-bg-warning-softer: #{utils.css-alpha( - var(--color-text-warning), - 50% - )}; - --color-border-on-bg-success-softer: #{utils.css-alpha( - var(--color-text-success), - 50% - )}; - --color-border-on-bg-inverted: var(--color-border-primary); - - /* SHADOW TOKENS */ - - --shadow-strength-primary: 30%; - --color-shadow-primary: #{utils.css-alpha( - var(--color-black), - var(--shadow-strength-primary) - )}; - --dropdown-shadow: - 0 20px 25px -5px var(--color-shadow-primary), - 0 8px 10px -6px var(--color-shadow-primary); - --overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary)); - - /* GRAPHS/CHARTS TOKENS */ - - --color-graph-primary-stroke: var(--color-text-brand); - --color-graph-primary-fill: var(--color-bg-brand-softer); - --color-graph-warning-stroke: var(--color-text-warning); - --color-graph-warning-fill: var(--color-bg-warning-softer); - --color-graph-disabled-stroke: var(--color-text-disabled); - --color-graph-disabled-fill: var(--color-bg-disabled); - - /* LEGACY TOKENS */ - - --rich-text-container-color: rgb(255 216 231 / 100%); - --rich-text-text-color: rgb(114 47 83 / 100%); - --rich-text-decorations-color: rgb(255 175 212 / 100%); - - /* MISCELLANEOUS */ - - --outline-focus-default: 2px solid var(--color-text-brand); - --avatar-border-radius: 8px; -} diff --git a/app/javascript/styles_new/mastodon/_mixins.scss b/app/javascript/styles_new/mastodon/_mixins.scss deleted file mode 100644 index effbe82c3d8581..00000000000000 --- a/app/javascript/styles_new/mastodon/_mixins.scss +++ /dev/null @@ -1,45 +0,0 @@ -@mixin search-input { - outline: 0; - box-sizing: border-box; - width: 100%; - box-shadow: none; - font-family: inherit; - background: var(--color-bg-secondary); - color: var(--color-text-primary); - border-radius: 4px; - border: 1px solid var(--color-border-on-bg-secondary); - font-size: 17px; - line-height: normal; - margin: 0; -} - -@mixin search-popout { - background: var(--color-bg-elevated); - border-radius: 4px; - padding: 10px 14px; - padding-bottom: 14px; - margin-top: 10px; - color: var(--color-text-secondary); - box-shadow: 2px 4px 15px var(--color-shadow-primary); - - h4 { - text-transform: uppercase; - color: var(--color-text-secondary); - font-size: 13px; - font-weight: 500; - margin-bottom: 10px; - } - - li { - padding: 4px 0; - } - - ul { - margin-bottom: 10px; - } - - em { - font-weight: 500; - color: var(--color-text-primary); - } -} diff --git a/app/javascript/styles_new/mastodon/_variables.scss b/app/javascript/styles_new/mastodon/_variables.scss deleted file mode 100644 index a948dbc41c55f5..00000000000000 --- a/app/javascript/styles_new/mastodon/_variables.scss +++ /dev/null @@ -1,27 +0,0 @@ -// Keep this filter a SCSS variable rather than -// a CSS Custom Property due to this Safari bug: -// https://github.com/mdn/browser-compat-data/issues/25914#issuecomment-2676190245 -$backdrop-blur-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%); - -// Language codes that uses CJK fonts -$cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW; - -// Variables for components -$media-modal-media-max-width: 100%; - -// put margins on top and bottom of image to avoid the screen covered by image. -$media-modal-media-max-height: 80%; - -$no-gap-breakpoint: 1175px; -$mobile-menu-breakpoint: 760px; -$mobile-breakpoint: 630px; -$no-columns-breakpoint: 600px; - -$font-sans-serif: 'mastodon-font-sans-serif' !default; -$font-display: 'mastodon-font-display' !default; -$font-monospace: 'mastodon-font-monospace' !default; - -$emojis-requiring-inversion: 'back' 'copyright' 'curly_loop' 'currency_exchange' - 'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' - 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'on' - 'registered' 'soon' 'spider' 'telephone_receiver' 'tm' 'top' 'wavy_dash' !default; diff --git a/app/javascript/styles_new/mastodon/about.scss b/app/javascript/styles_new/mastodon/about.scss deleted file mode 100644 index 0bb2c8c9eb21fb..00000000000000 --- a/app/javascript/styles_new/mastodon/about.scss +++ /dev/null @@ -1,130 +0,0 @@ -@use 'variables' as *; - -$maximum-width: 1235px; -$fluid-breakpoint: $maximum-width + 20px; - -.container { - box-sizing: border-box; - max-width: $maximum-width; - margin: 0 auto; - position: relative; - - @media screen and (max-width: $fluid-breakpoint) { - width: 100%; - padding: 0 10px; - } -} - -.brand { - position: relative; - text-decoration: none; -} - -.rules-list { - font-size: 15px; - line-height: 22px; - counter-reset: list-counter; - - li { - position: relative; - border-bottom: 1px solid var(--color-border-primary); - padding: 1em 1.75em; - padding-inline-start: 3em; - font-weight: 500; - counter-increment: list-counter; - min-height: 4ch; - - button { - background: transparent; - border: 0; - padding: 0; - margin: 0; - text-align: start; - font: inherit; - - &:hover, - &:focus, - &:active { - background: transparent; - } - - &[aria-expanded='false'] .rules-list__hint { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - @supports (-webkit-line-clamp: 2) { - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - white-space: normal; - } - } - } - - &::before { - content: counter(list-counter); - position: absolute; - inset-inline-start: 0; - top: 1em; - background: var(--color-bg-brand-base); - color: var(--color-text-on-brand-base); - border-radius: 50%; - width: 4ch; - height: 4ch; - font-weight: 500; - display: flex; - justify-content: center; - align-items: center; - } - - &:last-child { - border-bottom: 0; - } - } - - &__text { - color: var(--color-text-primary); - } - - &__hint { - font-size: 14px; - font-weight: 400; - color: var(--color-text-secondary); - } -} - -.rules-languages { - display: flex; - gap: 1rem; - align-items: center; - position: relative; - - > label { - font-size: 14px; - font-weight: 600; - color: var(--color-text-primary); - } - - select { - appearance: none; - box-sizing: border-box; - font-size: 14px; - color: var(--color-text-primary); - display: block; - width: 100%; - outline: 0; - font-family: inherit; - resize: vertical; - background: var(--color-bg-secondary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - padding-inline-start: 10px; - padding-inline-end: 30px; - height: 41px; - - @media screen and (width <= 600px) { - font-size: 16px; - } - } -} diff --git a/app/javascript/styles_new/mastodon/accessibility.scss b/app/javascript/styles_new/mastodon/accessibility.scss deleted file mode 100644 index 7cd2d4eae39781..00000000000000 --- a/app/javascript/styles_new/mastodon/accessibility.scss +++ /dev/null @@ -1,13 +0,0 @@ -@use 'variables' as *; - -%emoji-color-inversion { - filter: invert(1); -} - -.emojione { - @each $emoji in $emojis-requiring-inversion { - &[title=':#{$emoji}:'] { - @extend %emoji-color-inversion; - } - } -} diff --git a/app/javascript/styles_new/mastodon/accounts.scss b/app/javascript/styles_new/mastodon/accounts.scss deleted file mode 100644 index d1c35e3f9ec067..00000000000000 --- a/app/javascript/styles_new/mastodon/accounts.scss +++ /dev/null @@ -1,411 +0,0 @@ -@use 'sass:color'; -@use 'variables' as *; - -.card { - & > a { - display: block; - text-decoration: none; - color: inherit; - overflow: hidden; - border-radius: 4px; - - &:hover, - &:active, - &:focus { - .card__bar { - background: var(--color-bg-brand-softer); - } - } - } - - &__img { - height: 130px; - position: relative; - background: var(--color-bg-secondary); - border: 1px solid var(--color-border-primary); - border-bottom: none; - - img { - display: block; - width: 100%; - height: 100%; - margin: 0; - object-fit: cover; - } - - @media screen and (width <= 600px) { - height: 200px; - } - } - - &__bar { - position: relative; - padding: 15px; - display: flex; - justify-content: flex-start; - align-items: center; - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - border-top: none; - - .avatar { - flex: 0 0 auto; - width: 48px; - height: 48px; - padding-top: 2px; - - img { - width: 100%; - height: 100%; - display: block; - margin: 0; - border-radius: 4px; - background: var(--color-bg-secondary); - object-fit: cover; - } - } - - .display-name { - margin-inline-start: 15px; - text-align: start; - - svg[data-hidden] { - display: none; - } - - strong { - font-size: 15px; - color: var(--color-text-primary); - font-weight: 500; - overflow: hidden; - text-overflow: ellipsis; - } - - span { - display: block; - font-size: 14px; - color: var(--color-text-secondary); - font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - } - } - } -} - -.pagination { - padding: 30px 0; - text-align: center; - overflow: hidden; - - a, - .current, - .newer, - .older, - .page, - .gap { - font-size: 14px; - color: var(--color-text-primary); - font-weight: 500; - display: inline-block; - padding: 6px 10px; - text-decoration: none; - } - - .current { - color: var(--color-bg-primary); - background: var(--color-text-primary); - border-radius: 100px; - cursor: default; - margin: 0 10px; - } - - .gap { - cursor: default; - } - - .older, - .newer { - text-transform: uppercase; - color: var(--color-text-primary); - } - - .older { - float: left; - padding-inline-start: 0; - } - - .newer { - float: right; - padding-inline-end: 0; - } - - .disabled { - cursor: default; - color: var(--color-text-disabled); - } - - @media screen and (width <= 700px) { - padding: 30px 20px; - - .page { - display: none; - } - - .newer, - .older { - display: inline-block; - } - } -} - -.nothing-here { - color: var(--color-text-secondary); - background: var(--color-bg-primary); - font-size: 14px; - font-weight: 500; - text-align: center; - display: flex; - justify-content: center; - align-items: center; - cursor: default; - border-radius: 4px; - padding: 20px; - min-height: 30vh; - border: 1px solid var(--color-border-primary); - - @media screen and (min-width: ($no-gap-breakpoint - 1)) { - border-top: 0; - } - - &--no-toolbar { - border-top: 1px solid var(--color-border-primary); - } - - &--under-tabs { - border-radius: 0 0 4px 4px; - } - - &--flexible { - box-sizing: border-box; - min-height: 100%; - } -} - -.information-badge, -.simple_form .overridden, -.simple_form .recommended, -.simple_form .not_recommended { - display: inline-block; - padding: 4px 6px; - cursor: default; - border-radius: 4px; - font-size: 12px; - line-height: 12px; - font-weight: 500; - color: var(--color-text-primary); - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} - -.information-badge, -.simple_form .overridden, -.simple_form .recommended, -.simple_form .not_recommended { - background-color: var(--color-bg-secondary); - border: 1px solid var(--color-border-primary); -} - -.information-badge { - &.superapp { - color: var(--color-text-success); - background-color: var(--color-bg-success-softer); - border-color: var(--color-border-on-bg-success-softer); - } -} - -.account-role { - display: inline-flex; - padding: 4px; - padding-inline-end: 8px; - border: 1px solid var(--color-text-brand); - color: var(--color-text-brand); - font-weight: 500; - font-size: 12px; - letter-spacing: 0.5px; - line-height: 16px; - gap: 4px; - border-radius: 6px; - align-items: center; - - svg { - width: auto; - height: 15px; - opacity: 0.85; - fill: currentColor; - } - - &__domain { - font-weight: 400; - opacity: 0.75; - letter-spacing: 0; - } -} - -.simple_form .not_recommended { - color: var(--color-text-error); - background-color: var(--color-bg-error-softer); - border-color: var(--color-border-on-bg-error-softer); -} - -.account__header__fields { - max-width: 100vw; - padding: 0; - margin: 15px -15px -15px; - border: 0 none; - border-top: 1px solid var(--color-border-primary); - border-bottom: 1px solid var(--color-border-primary); - font-size: 14px; - line-height: 20px; - - dl { - display: flex; - border-bottom: 1px solid var(--color-border-primary); - } - - dt, - dd { - box-sizing: border-box; - padding: 14px; - text-align: center; - max-height: 48px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - dt { - font-weight: 500; - width: 120px; - flex: 0 0 auto; - color: var(--color-text-primary); - background: var(--color-bg-secondary); - } - - dd { - flex: 1 1 auto; - color: var(--color-text-secondary); - } - - a { - color: var(--color-text-brand); - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } - - .verified { - border: 1px solid var(--color-border-on-bg-success-softer); - background: var(--color-bg-success-softer); - - a { - color: var(--color-text-success); - font-weight: 500; - } - - &__mark { - color: var(--color-text-success); - } - } - - dl:last-child { - border-bottom: 0; - } -} - -.directory__tag .trends__item__current { - width: auto; -} - -.pending-account { - &__header { - color: var(--color-text-secondary); - - a { - color: var(--color-text-primary); - text-decoration: none; - - &:hover, - &:active, - &:focus { - text-decoration: underline; - } - } - - strong { - color: var(--color-text-primary); - font-weight: 700; - } - - .warning-hint { - font-weight: normal !important; - } - } - - &__body { - margin-top: 10px; - } -} - -.batch-table__row--muted { - color: var(--color-text-tertiary); -} - -.batch-table__row--muted .pending-account__header, -.batch-table__row--muted .accounts-table, -.batch-table__row--muted .name-tag { - &, - a, - strong { - color: var(--color-text-tertiary); - } -} - -.batch-table__row--muted .name-tag .avatar { - opacity: 0.5; -} - -.batch-table__row--muted .accounts-table { - tbody td.accounts-table__extra, - &__count, - &__count small { - color: var(--color-text-tertiary); - } -} - -.batch-table__row--attention { - color: var(--color-text-warning); -} - -.batch-table__row--attention .pending-account__header, -.batch-table__row--attention .accounts-table, -.batch-table__row--attention .name-tag { - &, - a, - strong { - color: var(--color-text-warning); - } -} - -.batch-table__row--attention .accounts-table { - tbody td.accounts-table__extra, - &__count, - &__count small { - color: var(--color-text-warning); - } -} diff --git a/app/javascript/styles_new/mastodon/admin.scss b/app/javascript/styles_new/mastodon/admin.scss deleted file mode 100644 index eaa91936b106c8..00000000000000 --- a/app/javascript/styles_new/mastodon/admin.scss +++ /dev/null @@ -1,2177 +0,0 @@ -@use 'sass:color'; -@use 'sass:math'; -@use 'variables' as *; - -$no-columns-breakpoint: 890px; -$sidebar-width: 300px; -$content-width: 840px; - -.admin-wrapper { - display: flex; - justify-content: center; - box-sizing: border-box; - width: 100%; - min-height: 100vh; - min-height: 100dvh; - padding: env(safe-area-inset-top) env(safe-area-inset-right) - env(safe-area-inset-bottom) env(safe-area-inset-left); - - .icon { - width: 16px; - height: 16px; - vertical-align: top; - margin: 0 2px; - } - - .sidebar-wrapper { - min-height: 100vh; - min-height: 100dvh; - overflow: hidden; - pointer-events: none; - flex: 1 1 auto; - - &__inner { - display: flex; - justify-content: flex-end; - height: 100%; - } - } - - .sidebar { - width: $sidebar-width; - padding: 0; - pointer-events: auto; - - &__toggle { - display: none; - background: var(--color-bg-primary); - border-bottom: 1px solid var(--color-border-primary); - align-items: center; - - &__logo { - flex: 1 1 auto; - - a { - display: block; - padding: 15px; - } - } - - &__icon { - display: block; - color: var(--color-text-secondary); - text-decoration: none; - flex: 0 0 auto; - font-size: 18px; - padding: 10px; - margin: 5px 10px; - border-radius: 4px; - - &:focus { - background: var(--color-bg-brand-softer); - } - - .material-close { - display: none; - } - - &.active { - .material-close { - display: block; - } - - .material-menu { - display: none; - } - } - } - } - - .logo { - display: block; - margin: 40px auto; - width: 100px; - height: 100px; - } - - .logo--wordmark { - display: inherit; - margin: inherit; - width: inherit; - height: 25px; - } - - @media screen and (max-width: $no-columns-breakpoint) { - & > a:first-child { - display: none; - } - } - - ul { - list-style: none; - overflow: hidden; - margin-bottom: 20px; - - @media screen and (max-width: $no-columns-breakpoint) { - margin-bottom: 0; - } - - a { - font-size: 14px; - display: flex; - align-items: center; - gap: 6px; - padding: 15px; - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - text-decoration: none; - transition: all 200ms linear; - transition-property: color, background-color; - - &:hover { - color: var(--color-text-primary); - transition: all 100ms linear; - transition-property: color, background-color; - } - } - - ul { - margin: 0; - - a { - border: 0; - padding: 15px 35px; - } - } - - .warning a { - color: var(--color-text-warning); - font-weight: 700; - } - - .simple-navigation-active-leaf a { - color: var(--color-text-brand); - border-bottom: 0; - } - } - } - - .content-wrapper { - box-sizing: border-box; - width: 100%; - max-width: $content-width; - flex: 1 1 auto; - } - - @media screen and (max-width: ($content-width + $sidebar-width)) { - .sidebar-wrapper--empty { - display: none; - } - - .sidebar-wrapper { - width: $sidebar-width; - flex: 0 0 auto; - } - } - - @media screen and (max-width: $no-columns-breakpoint) { - .sidebar-wrapper { - width: 100%; - } - } - - .content { - padding-top: 55px; - padding-bottom: 20px; - padding-inline-start: 25px; - padding-inline-end: 15px; - - @media screen and (max-width: $no-columns-breakpoint) { - max-width: none; - padding: 15px; - padding-top: 30px; - } - - &__heading { - margin-bottom: 45px; - - &__row { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - margin-top: -15px; - margin-inline-end: -15px; - - & > * { - margin-top: 15px; - margin-inline-end: 15px; - } - } - - &__tabs { - margin-top: 30px; - width: 100%; - - & > div { - display: flex; - flex-wrap: wrap; - gap: 5px; - } - - a { - font-size: 14px; - display: inline-flex; - align-items: center; - padding: 7px 10px; - border-radius: 4px; - color: var(--color-text-secondary); - text-decoration: none; - font-weight: 500; - gap: 5px; - white-space: nowrap; - - @media screen and (max-width: $mobile-breakpoint) { - flex: 1 0 50%; - } - - &:hover, - &:focus, - &:active { - background: var(--color-bg-secondary); - } - - &.selected { - font-weight: 700; - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - } - } - } - - &__actions { - display: inline-flex; - flex-flow: wrap; - gap: 5px; - align-items: center; - - .time-period { - padding: 0 10px; - } - - .back-link { - margin-bottom: 0; - } - } - - h2 small { - font-size: 12px; - display: block; - font-weight: 500; - color: var(--color-text-secondary); - line-height: 18px; - } - - @media screen and (max-width: $no-columns-breakpoint) { - border-bottom: 0; - padding-bottom: 0; - } - } - - h2 { - color: var(--color-text-primary); - font-size: 24px; - line-height: 36px; - font-weight: 700; - } - - h3 { - color: var(--color-text-primary); - font-size: 20px; - line-height: 28px; - font-weight: 400; - margin-bottom: 30px; - } - - h4 { - text-transform: uppercase; - font-size: 13px; - font-weight: 700; - color: var(--color-text-secondary); - padding-top: 24px; - margin-bottom: 8px; - border-top: 1px solid var(--color-border-primary); - } - - h6 { - font-size: 16px; - color: var(--color-text-primary); - line-height: 28px; - font-weight: 500; - } - - .fields-group h6 { - color: var(--color-text-primary); - font-weight: 500; - } - - .directory__tag h4 { - font-size: 18px; - font-weight: 700; - color: var(--color-text-primary); - text-transform: none; - padding-top: 0; - margin-bottom: 0; - border-top: 0; - - .comment { - display: block; - overflow: hidden; - text-overflow: ellipsis; - margin-top: 4px; - - &.private-comment { - display: block; - color: var(--color-text-secondary); - } - - &.public-comment { - display: block; - color: var(--color-text-primary); - } - } - } - - & > p { - font-size: 14px; - line-height: 21px; - color: var(--color-text-primary); - margin-bottom: 20px; - - strong { - color: var(--color-text-primary); - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - } - - hr { - width: 100%; - height: 0; - border: 0; - border-bottom: 1px solid var(--color-border-primary); - margin: 20px 0; - - &.spacer { - height: 1px; - border: 0; - } - } - } - - @media screen and (max-width: $no-columns-breakpoint) { - display: block; - - .sidebar-wrapper { - min-height: 0; - } - - .sidebar { - width: 100%; - padding: 0; - height: auto; - - &__toggle { - display: flex; - } - - & > ul { - display: none; - - &.visible { - display: block; - position: fixed; - z-index: 10; - width: 100%; - height: calc(100% - 56px); - inset-inline-start: 0; - bottom: 0; - overflow-y: auto; - background: var(--color-bg-primary); - } - } - - ul a, - ul ul a { - font-size: 16px; - border-radius: 0; - transition: none; - - &:hover { - transition: none; - } - } - - ul ul { - border-radius: 0; - } - - ul .simple-navigation-active-leaf a { - border-bottom-color: var(--color-text-brand); - } - } - } -} - -hr.spacer { - width: 100%; - border: 0; - margin: 20px 0; - height: 1px; -} - -body, -.admin-wrapper .content { - .muted-hint { - color: var(--color-text-secondary); - - a { - color: var(--color-text-brand); - } - } - - .positive-hint, - .negative-hint, - .neutral-hint { - a { - color: inherit; - text-decoration: underline; - - &:focus, - &:hover, - &:active { - text-decoration: none; - } - } - } - - .positive-hint { - color: var(--color-text-success); - font-weight: 500; - } - - .negative-hint { - color: var(--color-text-error); - font-weight: 500; - } - - .neutral-hint { - color: var(--color-text-primary); - font-weight: 500; - } - - .warning-hint { - color: var(--color-text-warning); - font-weight: 500; - } -} - -kbd { - font-family: Courier, monospace; - background-color: var(--color-bg-brand-softer); - padding: 4px; - padding-bottom: 2px; - border-radius: 5px; -} - -.filters { - display: flex; - flex-wrap: wrap; - gap: 40px; - - .filter-subset { - flex: 0 0 auto; - margin-bottom: 20px; - - &:last-child { - margin-bottom: 30px; - } - - ul { - margin-top: 5px; - list-style: none; - - li { - display: inline-block; - margin-inline-end: 5px; - } - } - - & > div { - display: flex; - gap: 5px; - } - - strong { - font-weight: 500; - text-transform: uppercase; - font-size: 12px; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - &--with-select strong { - display: block; - margin-bottom: 10px; - } - - a { - display: inline-block; - color: var(--color-text-secondary); - text-decoration: none; - text-transform: uppercase; - font-size: 12px; - font-weight: 500; - border-bottom: 2px solid var(--color-bg-secondary); - - &:hover { - color: var(--color-text-primary); - border-bottom: 2px solid var(--color-bg-tertiary); - } - - &.selected { - color: var(--color-text-brand); - border-bottom: 2px solid var(--color-text-brand); - } - } - } -} - -.report-accounts { - display: flex; - flex-wrap: wrap; - margin-bottom: 20px; -} - -.report-accounts__item { - display: flex; - flex: 250px; - flex-direction: column; - margin: 0 5px; - - & > strong { - display: block; - margin: 0 0 10px -5px; - font-weight: 500; - font-size: 14px; - line-height: 18px; - color: var(--color-text-primary); - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - .account-card { - flex: 1 1 auto; - } -} - -.report-status, -.account-status { - display: flex; - margin-bottom: 10px; -} - -.report-status__actions, -.account-status__actions { - flex: 0 0 auto; - display: flex; - flex-direction: column; - - .icon-button { - font-size: 24px; - width: 24px; - text-align: center; - margin-bottom: 10px; - } -} - -.simple_form.new_report_note, -.simple_form.new_account_moderation_note { - max-width: 100%; -} - -.batch-form-box { - display: flex; - flex-wrap: wrap; - margin-bottom: 5px; - - #form_status_batch_action { - margin: 0 5px 5px 0; - font-size: 14px; - } - - input.button { - margin: 0 5px 5px 0; - } -} - -.back-link { - margin-bottom: 10px; - font-size: 14px; - - a { - color: var(--color-text-brand); - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } -} - -.special-action-button, -.back-link { - text-align: end; - flex: 1 1 auto; -} - -.action-buttons { - display: flex; - overflow: hidden; - justify-content: space-between; -} - -.spacer { - flex: 1 1 auto; -} - -.log-entry { - display: block; - line-height: 20px; - padding: 15px; - padding-inline-start: 15px * 2 + 40px; - background: var(--color-bg-primary); - border-right: 1px solid var(--color-border-primary); - border-left: 1px solid var(--color-border-primary); - border-bottom: 1px solid var(--color-border-primary); - position: relative; - text-decoration: none; - color: var(--color-text-secondary); - font-size: 14px; - - &:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-top: 1px solid var(--color-border-primary); - } - - &:last-child { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom: 1px solid var(--color-border-primary); - } - - &__avatar { - position: absolute; - inset-inline-start: 15px; - top: 15px; - - .avatar { - border-radius: var(--avatar-border-radius); - width: 40px; - height: 40px; - } - } - - &__title { - overflow-wrap: break-word; - } - - &__timestamp { - color: var(--color-text-tertiary); - } - - a, - .username, - .target { - color: var(--color-text-primary); - text-decoration: none; - font-weight: 500; - } - - a { - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } -} - -.strike-entry { - display: block; - line-height: 20px; - padding: 15px; - padding-inline-start: 15px * 2 + 40px; - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - position: relative; - text-decoration: none; - color: var(--color-text-secondary); - font-size: 14px; - margin-bottom: 15px; - - &__avatar { - position: absolute; - inset-inline-start: 15px; - top: 15px; - - .avatar { - border-radius: var(--avatar-border-radius); - width: 40px; - height: 40px; - } - } - - &__title { - overflow-wrap: break-word; - } - - &__timestamp { - color: var(--color-text-secondary); - } - - &:hover, - &:focus, - &:active { - background: var(--color-bg-primary); - } -} - -a.name-tag, -.name-tag, -a.inline-name-tag, -.inline-name-tag { - text-decoration: none; - color: var(--color-text-primary); - - &:hover { - color: var(--color-text-brand); - } - - .username { - font-weight: 500; - } - - &.suspended { - .username { - text-decoration: line-through; - color: var(--color-text-error); - } - - .avatar { - filter: grayscale(100%); - opacity: 0.8; - } - } -} - -a.name-tag, -.name-tag { - display: inline-flex; - align-items: center; - vertical-align: top; - - .avatar { - display: block; - margin: 0; - margin-inline-end: 5px; - border-radius: 50%; - } - - &.suspended { - .avatar { - filter: grayscale(100%); - opacity: 0.8; - } - } -} - -.speech-bubble { - margin-bottom: 20px; - border-inline-start: 4px solid var(--color-text-brand); - - &.positive { - border-color: var(--color-text-success); - } - - &.negative { - border-color: var(--color-text-error); - } - - &.warning { - border-color: var(--color-text-warning); - } - - &__bubble { - padding: 16px; - padding-inline-start: 14px; - font-size: 15px; - line-height: 20px; - border-radius: 4px 4px 4px 0; - position: relative; - font-weight: 500; - - a { - color: var(--color-text-secondary); - } - } - - &__owner { - padding: 8px; - padding-inline-start: 12px; - } - - time { - color: var(--color-text-tertiary); - } -} - -.report-card { - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - margin-bottom: 20px; - - &__profile { - display: flex; - justify-content: space-between; - align-items: center; - padding: 15px; - - .account { - padding: 0; - border: none; - - &__avatar-wrapper { - margin-inline-start: 0; - } - } - - &__stats { - flex: 0 0 auto; - font-weight: 500; - color: var(--color-text-secondary); - text-transform: uppercase; - text-align: end; - - a { - color: inherit; - text-decoration: none; - - &:focus, - &:hover, - &:active { - color: var(--color-text-brand); - } - } - - .red { - color: var(--color-text-error); - } - } - } - - &__summary { - &__item { - display: flex; - justify-content: flex-start; - border-top: 1px solid var(--color-border-primary); - - &__reported-by, - &__assigned { - padding: 15px; - flex: 0 0 auto; - box-sizing: border-box; - width: 150px; - color: var(--color-text-secondary); - - &, - .username { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - } - - &__content { - flex: 1 1 auto; - max-width: calc(100% - 300px); - - &__icon { - margin-inline-end: 4px; - font-weight: 500; - } - } - - &__content a { - display: block; - box-sizing: border-box; - width: 100%; - padding: 15px; - text-decoration: none; - color: var(--color-text-secondary); - - &:hover { - color: var(--color-text-brand); - } - } - } - } -} - -.one-line { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.ellipsized-ip { - display: inline-block; - max-width: 120px; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: middle; -} - -.admin-account-bio { - display: flex; - flex-wrap: wrap; - margin: 0 -5px; - margin-top: 20px; - - > div { - box-sizing: border-box; - padding: 0 5px; - margin-bottom: 10px; - flex: 1 0 50%; - max-width: 100%; - } - - .account__header__fields, - .account__header__content { - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - height: 100%; - } - - .account__header__fields { - margin: 0; - border: 1px solid var(--color-border-primary); - - a { - color: var(--color-text-brand); - } - - dl:first-child .verified { - border-radius: 0 4px 0 0; - } - - .verified a { - color: var(--color-text-success); - } - } - - .account__header__content { - box-sizing: border-box; - padding: 20px; - color: var(--color-text-primary); - } -} - -.center-text { - text-align: center; -} - -.applications-list__item, -.filters-list__item { - padding: 15px 0; - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - margin-top: 15px; -} - -.applications-list { - .icon { - vertical-align: middle; - } -} - -.announcements-list, -.filters-list { - border: 1px solid var(--color-border-primary); - border-radius: 4px; - border-bottom: none; - - &__item { - padding: 15px 0; - border-bottom: 1px solid var(--color-border-primary); - - &__title { - padding: 0 15px; - display: block; - font-weight: 500; - font-size: 18px; - line-height: 1.5; - color: var(--color-text-primary); - text-decoration: none; - margin-bottom: 10px; - - &:hover { - color: var(--color-text-brand); - } - - .account-role { - vertical-align: middle; - } - } - - .icon { - vertical-align: middle; - } - - a.announcements-list__item__title { - &:hover, - &:focus, - &:active { - color: var(--color-text-primary); - } - } - - &__action-bar { - display: flex; - justify-content: space-between; - align-items: center; - gap: 8px; - - &:not(.no-wrap) { - flex-wrap: wrap; - } - } - - &__meta { - padding: 0 15px; - color: var(--color-text-tertiary); - - a { - color: inherit; - text-decoration: underline; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - } - } - - &__actions { - margin-inline-start: auto; - } - - &__permissions { - margin-top: 10px; - } - } -} - -.filters-list__item { - &__title { - display: flex; - justify-content: space-between; - margin-bottom: 0; - overflow-wrap: anywhere; - } - - &__permissions { - margin-top: 0; - margin-bottom: 10px; - } - - .expiration { - font-size: 13px; - } - - &.expired { - .expiration { - color: var(--color-text-error); - } - - .permissions-list__item__icon { - color: var(--color-text-secondary); - } - } -} - -.rule-actions { - display: flex; - flex-direction: column; - - a.table-action-link { - padding-inline-start: 0; - } -} - -.dashboard__counters.admin-account-counters { - margin-top: 10px; -} - -.account-badges { - margin: -2px 0; -} - -.retention { - overflow: auto; - - > h4 { - position: sticky; - inset-inline-start: 0; - } - - &__table { - &__number { - color: var(--color-bg-primary); - padding: 10px; - } - - &__date { - white-space: nowrap; - padding: 10px 0; - text-align: start; - min-width: 120px; - - &.retention__table__average { - font-weight: 700; - } - } - - &__size { - text-align: center; - padding: 10px; - } - - &__label { - font-weight: 700; - color: var(--color-text-secondary); - } - - &__box { - box-sizing: border-box; - width: 52px; - margin: 1px; - padding: 10px; - font-weight: 500; - color: var(--color-text-primary); - background: var(--color-bg-primary); - - @for $i from 0 through 10 { - &--#{10 * $i} { - @if $i > 5 { - color: var(--color-text-on-brand-base); - } - - background-color: rgb( - from var(--color-bg-brand-base) r g b / #{math.div(max(1, $i), 10)} - ); - } - } - } - } -} - -.sparkline { - display: block; - text-decoration: none; - background: var(--color-bg-primary); - border-radius: 4px; - border: 1px solid var(--color-border-primary); - padding: 0; - position: relative; - padding-bottom: 55px + 20px; - overflow: hidden; - - &__value { - display: flex; - line-height: 33px; - align-items: flex-end; - padding: 20px; - padding-bottom: 10px; - - &__total { - display: block; - margin-inline-end: 10px; - font-weight: 500; - font-size: 28px; - color: var(--color-text-primary); - } - - &__change { - display: block; - font-weight: 500; - font-size: 18px; - color: var(--color-text-secondary); - margin-bottom: -3px; - - &.positive { - color: var(--color-text-success); - } - - &.negative { - color: var(--color-text-error); - } - } - } - - &__label { - padding: 0 20px; - padding-bottom: 10px; - text-transform: uppercase; - color: var(--color-text-secondary); - font-weight: 500; - } - - &__graph { - position: absolute; - bottom: 0; - width: 100%; - - svg { - display: block; - margin: 0; - } - - path:first-child { - fill: var(--color-graph-primary-fill) !important; - fill-opacity: 1 !important; - } - - path:last-child { - stroke: var(--color-graph-primary-stroke) !important; - fill: none !important; - } - } -} - -a.sparkline { - &:hover, - &:focus, - &:active { - background: var(--color-bg-brand-softer); - } -} - -.skeleton { - background-color: var(--color-bg-primary); - background-image: linear-gradient( - 90deg, - var(--color-bg-primary), - var(--color-bg-secondary), - var(--color-bg-primary) - ); - background-size: 200px 100%; - background-repeat: no-repeat; - border-radius: 4px; - display: inline-block; - line-height: 1; - width: 100%; - animation: skeleton 1.2s ease-in-out infinite; - - .reduce-motion & { - animation: none; - } -} - -@keyframes skeleton { - 0% { - background-position: -200px 0; - } - - 100% { - background-position: calc(200px + 100%) 0; - } -} - -.dimension { - table { - width: 100%; - } - - &__item { - border-bottom: 1px solid var(--color-border-primary); - - &__key { - font-weight: 500; - padding: 11px 10px; - } - - &__value { - text-align: end; - color: var(--color-text-secondary); - padding: 11px 10px; - } - - &__indicator { - display: inline-block; - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--color-text-brand); - margin-inline-end: 10px; - - @for $i from 0 through 10 { - &--#{10 * $i} { - background-color: rgb( - from var(--color-text-brand) r g b / #{math.div(max(1, $i), 10)} - ); - } - } - } - - &:last-child { - border-bottom: 0; - } - - &.negative { - color: var(--color-text-error); - font-weight: 700; - - .dimension__item__value { - color: var(--color-text-error); - } - } - } -} - -.report-reason-selector { - border-radius: 4px; - background: var(--color-bg-primary); - margin-bottom: 20px; - - &__category { - cursor: pointer; - border-bottom: 1px solid var(--color-border-primary); - - &__label { - padding: 15px; - display: flex; - gap: 8px; - align-items: center; - } - - &__rules { - margin-inline-start: 30px; - } - } - - &__rule { - cursor: pointer; - padding: 15px; - display: flex; - gap: 8px; - align-items: center; - } -} - -.report-header { - display: grid; - gap: 15px; - grid-template-columns: minmax(0, 1fr) 300px; - - &__details { - &__item { - border-bottom: 1px solid var(--color-border-primary); - padding: 15px 0; - - &:last-child { - border-bottom: 0; - } - - &__header { - font-weight: 600; - padding: 4px 0; - } - } - - &--horizontal { - display: grid; - grid-auto-columns: minmax(0, 1fr); - grid-auto-flow: column; - - .report-header__details__item { - border-bottom: 0; - } - } - } - - @media screen and (width <= 930px) { - grid-template-columns: minmax(0, 1fr); - } -} - -.account-card { - border-radius: 4px; - border: 1px solid var(--color-border-primary); - position: relative; - - &__warning-badge { - position: absolute; - padding: 4px 10px; - top: 10px; - inset-inline-start: 10px; - border-radius: 4px; - background: - url('@/images/warning-stripes.svg') repeat-y left, - url('@/images/warning-stripes.svg') repeat-y right, - var(--color-bg-primary); - } - - &__permalink { - color: inherit; - text-decoration: none; - } - - &__header { - padding: 4px; - border-radius: 4px; - height: 128px; - - img { - display: block; - margin: 0; - width: 100%; - height: 100%; - object-fit: cover; - background: var(--color-bg-secondary); - } - } - - &__title { - margin-top: -(15px + 8px); - display: flex; - align-items: flex-end; - - &__avatar { - padding: 14px; - - img, - .account__avatar { - display: block; - margin: 0; - width: 56px; - height: 56px; - background-color: var(--color-bg-secondary); - border-radius: 8px; - border: 1px solid var(--color-border-media); - } - } - - .display-name { - color: var(--color-text-secondary); - padding-bottom: 15px; - font-size: 15px; - line-height: 20px; - - bdi { - display: block; - color: var(--color-text-primary); - font-weight: 700; - } - } - } - - &__bio { - padding: 0 15px; - margin: 8px 0; - overflow: hidden; - text-overflow: ellipsis; - overflow-wrap: break-word; - max-height: 21px * 2; - position: relative; - font-size: 15px; - line-height: 21px; - - &::after { - display: block; - content: ''; - width: 50px; - height: 21px; - position: absolute; - bottom: 0; - inset-inline-end: 15px; - pointer-events: none; - } - - a { - color: var(--color-text-primary); - text-decoration: none; - unicode-bidi: isolate; - - &:hover { - text-decoration: underline; - } - - &.mention { - &:hover { - text-decoration: none; - - span { - text-decoration: underline; - } - } - } - } - } - - &__actions { - display: flex; - justify-content: space-between; - align-items: center; - - &__button { - flex-shrink: 1; - padding: 0 15px; - overflow: hidden; - - .button { - min-width: 0; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - max-width: 100%; - } - } - } - - &__counters { - flex: 1 1 auto; - display: grid; - grid-auto-columns: minmax(0, 1fr); - grid-auto-flow: column; - max-width: 340px; - min-width: 65px * 3; - - &__item { - padding: 15px 0; - text-align: center; - color: var(--color-text-primary); - font-weight: 600; - font-size: 15px; - line-height: 21px; - - small { - display: block; - color: var(--color-text-secondary); - font-weight: 400; - font-size: 13px; - line-height: 18px; - } - } - } -} - -.report-notes { - margin-bottom: 20px; - - &__item { - background: var(--color-bg-primary); - position: relative; - padding: 15px; - padding-inline-start: 15px * 2 + 40px; - border: 1px solid var(--color-border-primary); - - &:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - } - - &:last-child { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - } - - &__avatar { - position: absolute; - inset-inline-start: 15px; - top: 15px; - border-radius: var(--avatar-border-radius); - width: 40px; - height: 40px; - } - - &__header { - color: var(--color-text-secondary); - font-size: 15px; - line-height: 20px; - margin-bottom: 4px; - - .username { - color: var(--color-text-primary); - font-weight: 500; - margin-inline-end: 5px; - - a { - color: inherit; - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } - } - - a.timestamp { - color: var(--color-text-secondary); - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } - - time { - margin-inline-start: 5px; - vertical-align: baseline; - } - } - - &__content { - font-size: 15px; - line-height: 20px; - overflow-wrap: break-word; - font-weight: 400; - color: var(--color-text-primary); - - p { - margin-bottom: 20px; - white-space: pre-wrap; - unicode-bidi: plaintext; - - &:last-child { - margin-bottom: 0; - } - } - - a { - color: var(--color-text-brand); - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } - } - - &__actions { - position: absolute; - top: 15px; - inset-inline-end: 15px; - text-align: end; - } - } -} - -.report-actions { - &__item { - display: flex; - align-items: center; - line-height: 18px; - - &:last-child { - border-bottom: 0; - } - - &__button { - box-sizing: border-box; - flex: 0 0 auto; - width: 200px; - padding: 15px; - padding-inline-end: 0; - - .button { - display: block; - width: 100%; - } - } - - &__description { - padding: 15px; - font-size: 14px; - color: var(--color-text-tertiary); - } - } - - @media screen and (width <= 800px) { - border: 0; - - &__item { - flex-direction: column; - border: 0; - - &__button { - width: 100%; - padding: 15px 0; - } - - &__description { - padding: 0; - padding-bottom: 15px; - } - } - } -} - -.section-skip-link { - float: right; - - a { - color: var(--color-text-brand); - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } -} - -.strike-card { - padding: 15px; - font-size: 15px; - line-height: 20px; - overflow-wrap: break-word; - font-weight: 400; - color: var(--color-text-primary); - box-sizing: border-box; - min-height: 100%; - border: 1px solid var(--color-border-primary); - border-radius: 4px; - - a { - color: var(--color-text-brand); - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } - - p { - margin-bottom: 20px; - unicode-bidi: plaintext; - - &:last-child { - margin-bottom: 0; - } - - strong { - font-weight: 700; - } - } - - &__rules { - list-style: disc; - padding-inline-start: 15px; - margin-bottom: 20px; - color: var(--color-text-secondary); - - &:last-child { - margin-bottom: 0; - } - - &__text { - color: var(--color-text-primary); - } - } - - &__statuses-list { - border-radius: 4px; - border: 1px solid var(--color-border-primary); - font-size: 13px; - line-height: 18px; - overflow: hidden; - - &__item { - padding: 16px; - border-bottom: 1px solid var(--color-border-primary); - - &:last-child { - border-bottom: 0; - } - - &__meta { - color: var(--color-text-secondary); - } - - a { - color: inherit; - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } - } - } -} - -.availability-indicator { - display: flex; - align-items: center; - margin-bottom: 30px; - font-size: 14px; - line-height: 21px; - - &__hint { - padding: 0 15px; - } - - &__graphic { - display: flex; - margin: 0 -2px; - - &__item { - display: block; - flex: 0 0 auto; - width: 4px; - height: 21px; - background: var(--color-bg-secondary); - margin: 0 2px; - border-radius: 2px; - - &.positive { - background: var(--color-bg-success-base); - } - - &.negative { - background: var(--color-bg-error-base); - } - } - } -} - -.history { - counter-reset: step 0; - font-size: 15px; - line-height: 22px; - - > li { - counter-increment: step 1; - padding-inline-start: 2.5rem; - padding-bottom: 8px; - position: relative; - margin-bottom: 8px; - - &::before { - position: absolute; - content: counter(step); - font-size: 0.625rem; - font-weight: 500; - inset-inline-start: 0; - display: flex; - justify-content: center; - align-items: center; - width: calc(1.375rem + 1px); - height: calc(1.375rem + 1px); - background: var(--color-bg-primary); - border: 1px solid var(--color-text-brand); - color: var(--color-text-brand); - border-radius: 8px; - } - - &::after { - position: absolute; - content: ''; - width: 1px; - background: var(--color-text-brand); - bottom: 0; - top: calc(1.875rem + 1px); - inset-inline-start: 0.6875rem; - } - - &:last-child { - margin-bottom: 0; - - &::after { - display: none; - } - } - } - - &__entry { - h5 { - font-weight: 500; - color: var(--color-text-primary); - line-height: 25px; - margin-bottom: 16px; - } - - .status { - border: 1px solid var(--color-border-primary); - background: var(--color-bg-secondary); - border-radius: 4px; - } - } -} - -.status__card { - padding: 15px; - border-radius: 4px; - font-size: 15px; - line-height: 20px; - overflow-wrap: break-word; - font-weight: 400; - border: 1px solid var(--color-border-primary); - color: var(--color-text-primary); - box-sizing: border-box; - min-height: 100%; - - &.status--has-quote { - .quote-inline { - display: none; - } - } - - .status__quote & { - // Remove the border from the .status__card within .status__quote - border: none; - - .display-name__account { - line-height: inherit; - } - - .status__avatar, - .status__avatar .account__avatar { - width: 32px; - height: 32px; - } - } - - .status__prepend { - padding: 0 0 15px; - gap: 4px; - align-items: center; - } - - > details { - summary { - display: block; - box-sizing: border-box; - color: var(--color-text-primary); - background: var(--color-bg-brand-softer); - border: 1px solid var(--color-border-on-bg-brand-softer); - border-radius: 8px; - padding: 8px 13px; - position: relative; - font-size: 15px; - line-height: 22px; - cursor: pointer; - - &::after { - content: attr(data-show, 'Show more'); - margin-top: 8px; - display: block; - font-size: 15px; - line-height: 20px; - color: var(--color-text-brand); - cursor: pointer; - border: 0; - background: transparent; - padding: 0; - text-decoration: none; - font-weight: 500; - } - - &:hover, - &:focus-visible { - &::after { - text-decoration: underline !important; - } - } - } - - &[open] summary { - margin-bottom: 16px; - - &::after { - content: attr(data-hide, 'Hide post'); - } - } - } - - .preview-card { - position: relative; - max-width: 566px; - - .status-card__image { - &--video { - aspect-ratio: 16 / 9; - } - - &--large { - aspect-ratio: 1.91 / 1; - } - - aspect-ratio: 1; - } - - .spoiler-button__overlay__label { - outline: 1px solid var(--color-border-media); - } - - .hide-button { - // Toggled to appear when the preview-card is unblurred: - display: none; - position: absolute; - top: 5px; - right: 5px; - color: var(--color-text-on-media); - background: var(--color-bg-media); - backdrop-filter: $backdrop-blur-filter; - outline: 1px solid var(--color-border-media); - border: 0; - padding: 3px 12px; - border-radius: 99px; - font-size: 14px; - font-weight: 700; - line-height: 20px; - - &:hover, - &:focus { - background-color: rgb(from var(--color-bg-media-base) r g b / 90%); - } - } - - &.preview-card--image-visible { - .hide-button { - display: block; - } - - .spoiler-button__overlay, - .status-card__image-preview { - display: none; - } - } - } - - .detailed-status__meta { - .detailed-status__application, - .detailed-status__datetime, - .detailed-status__link { - color: inherit; - } - } -} - -.admin { - &__terms-of-service { - &__container { - background: var(--color-bg-tertiary); - border-radius: 8px; - border: 1px solid var(--color-border-primary); - overflow: hidden; - - &__header { - padding: 16px; - font-size: 14px; - line-height: 20px; - color: var(--color-text-primary); - display: flex; - align-items: center; - gap: 12px; - } - - &__body { - background: var(--color-bg-primary); - padding: 16px; - overflow-y: scroll; - height: 30vh; - } - } - - &__history { - & > li { - border-bottom: 1px solid var(--color-border-primary); - - &:last-child { - border-bottom: 0; - } - } - - &__item { - padding: 16px 0; - padding-bottom: 8px; - - h5 { - font-size: 14px; - line-height: 20px; - font-weight: 600; - margin-bottom: 16px; - - a { - color: inherit; - text-decoration: none; - } - } - } - } - } -} - -.dot-indicator { - display: inline-flex; - align-items: center; - gap: 8px; - font-weight: 500; - - &__indicator { - display: inline-block; - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--color-text-tertiary); - } - - &.success { - color: var(--color-text-success); - - .dot-indicator__indicator { - background-color: var(--color-bg-success-base); - } - } -} diff --git a/app/javascript/styles_new/mastodon/annual_reports.scss b/app/javascript/styles_new/mastodon/annual_reports.scss deleted file mode 100644 index a9b7e0ddee0120..00000000000000 --- a/app/javascript/styles_new/mastodon/annual_reports.scss +++ /dev/null @@ -1,342 +0,0 @@ -@use 'variables' as *; - -:root { - --indigo-1: #17063b; - --indigo-2: #2f0c7a; - --indigo-3: #562cfc; - --indigo-5: #858afa; - --indigo-6: #cccfff; - --lime: #baff3b; - --goldenrod-2: #ffc954; -} - -.annual-report { - flex: 0 0 auto; - background: var(--indigo-1); - padding: 24px; - - &__header { - margin-bottom: 16px; - - h1 { - font-size: 25px; - font-weight: 600; - line-height: 30px; - color: var(--lime); - margin-bottom: 8px; - } - - p { - font-size: 16px; - font-weight: 600; - line-height: 20px; - color: var(--indigo-6); - } - } - - &__bento { - display: grid; - gap: 8px; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); - grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto) minmax( - 0, - auto - ); - - &__box { - padding: 16px; - border-radius: 8px; - background: var(--indigo-2); - color: var(--indigo-5); - } - } - - &__summary { - &__most-boosted-post { - grid-column: span 2; - grid-row: span 2; - padding: 0; - - .status__content, - .content-warning { - color: var(--indigo-6); - } - - .detailed-status { - border: 0; - } - - .content-warning { - border: 0; - background: var(--indigo-1); - - .link-button { - color: var(--indigo-5); - } - } - - .detailed-status__meta__line { - border-bottom-color: var(--indigo-3); - } - - .detailed-status__meta { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - - .detailed-status__meta, - .poll__footer, - .poll__link, - .detailed-status .logo, - .detailed-status__display-name { - color: var(--indigo-5); - } - - .detailed-status__meta .animated-number, - .detailed-status__display-name strong { - color: var(--indigo-6); - } - - .poll__chart { - background-color: var(--indigo-3); - - &.leading { - background-color: var(--goldenrod-2); - } - } - - .status-card, - .hashtag-bar { - display: none; - } - } - - &__followers { - grid-column: span 1; - text-align: center; - position: relative; - overflow: hidden; - padding-block-start: 24px; - padding-block-end: 24px; - - --sparkline-gradient-top: rgba(86, 44, 252, 50%); - --sparkline-gradient-bottom: rgba(86, 44, 252, 0%); - - &__foreground { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 8px; - position: relative; - z-index: 1; - } - - &__number { - font-size: 31px; - font-weight: 600; - line-height: 37px; - color: var(--lime); - } - - &__label { - font-size: 14px; - font-weight: 600; - line-height: 17px; - color: var(--indigo-6); - } - - &__footnote { - display: block; - font-weight: 400; - opacity: 0.5; - } - - svg { - position: absolute; - bottom: 0; - inset-inline-end: 0; - pointer-events: none; - z-index: 0; - height: 70%; - width: auto; - - path:first-child { - fill: url('#gradient') !important; - fill-opacity: 1 !important; - } - - path:last-child { - stroke: var(--color-graph-primary-stroke) !important; - fill: none !important; - } - } - } - - &__archetype { - grid-column: span 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; - gap: 8px; - padding: 0; - - img { - display: block; - width: 100%; - height: auto; - border-radius: 8px; - } - - &__label { - padding: 16px; - padding-bottom: 8px; - font-size: 14px; - line-height: 17px; - font-weight: 600; - color: var(--lime); - } - } - - &__most-used-app { - grid-column: span 1; - text-align: center; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 8px; - box-sizing: border-box; - - &__label { - font-size: 14px; - line-height: 17px; - font-weight: 600; - color: var(--indigo-6); - } - - &__icon { - font-size: 14px; - line-height: 17px; - font-weight: 600; - color: var(--goldenrod-2); - } - } - - &__percentile { - grid-row: span 2; - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-between; - text-align: center; - text-wrap: balance; - padding: 16px 8px; - - &__label { - font-size: 14px; - line-height: 17px; - } - - &__number { - font-size: 54px; - font-weight: 600; - line-height: 73px; - color: var(--goldenrod-2); - } - - &__footnote { - font-size: 11px; - line-height: 14px; - opacity: 0.5; - } - } - - &__new-posts { - grid-column: span 2; - text-align: center; - position: relative; - overflow: hidden; - - &__label { - font-size: 20px; - font-weight: 600; - line-height: 24px; - color: var(--indigo-6); - z-index: 1; - position: relative; - } - - &__number { - font-size: 76px; - font-weight: 600; - line-height: 91px; - color: var(--goldenrod-2); - z-index: 1; - position: relative; - } - - svg { - position: absolute; - inset-inline-start: -7px; - top: -4px; - z-index: 0; - } - } - - &__most-used-hashtag { - grid-column: span 2; - text-align: center; - overflow: hidden; - - &__hashtag { - font-size: 42px; - font-weight: 600; - line-height: 58px; - color: var(--indigo-6); - margin-inline-start: -100%; - margin-inline-end: -100%; - } - - &__label { - font-size: 14px; - font-weight: 600; - line-height: 17px; - } - } - } -} - -.annual-report-modal { - max-width: 600px; - background: var(--indigo-1); - border-radius: 16px; - display: flex; - flex-direction: column; - overflow-y: auto; - - .loading-indicator .circular-progress { - color: var(--lime); - } - - @media screen and (max-width: $no-columns-breakpoint) { - border-bottom: 0; - border-radius: 16px 16px 0 0; - } -} - -.notification-group--annual-report { - .notification-group__icon { - color: var(--lime); - } - - .notification-group__main .link-button { - font-weight: 500; - color: var(--lime); - } -} diff --git a/app/javascript/styles_new/mastodon/basics.scss b/app/javascript/styles_new/mastodon/basics.scss deleted file mode 100644 index 6298409d157c97..00000000000000 --- a/app/javascript/styles_new/mastodon/basics.scss +++ /dev/null @@ -1,300 +0,0 @@ -@use 'variables' as *; - -html.has-modal { - &, - body { - touch-action: none; - overscroll-behavior: none; - -webkit-overflow-scrolling: auto; - scrollbar-gutter: stable; - } - - body { - overflow: hidden !important; - } -} - -body { - font-family: $font-sans-serif, sans-serif; - background: var(--color-bg-ambient); - font-size: 13px; - line-height: 18px; - font-weight: 400; - color: var(--color-text-primary); - text-rendering: optimizelegibility; - - // Disable kerning for Japanese text to preserve monospaced alignment for readability - &:not(:lang(ja)) { - font-feature-settings: 'kern'; - } - - text-size-adjust: none; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0%); - -webkit-tap-highlight-color: transparent; - - &.system-font { - // system-ui => standard property (Chrome/Android WebView 56+, Opera 43+, Safari 11+) - // -apple-system => Safari <11 specific - // BlinkMacSystemFont => Chrome <56 on macOS specific - // Segoe UI => Windows 7/8/10 - // Oxygen => KDE - // Ubuntu => Unity/Ubuntu - // Cantarell => GNOME - // Fira Sans => Firefox OS - // Droid Sans => Older Androids (<4.0) - // Helvetica Neue => Older macOS <10.11 - // $font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0) - font-family: - system-ui, - -apple-system, - BlinkMacSystemFont, - 'Segoe UI', - Oxygen, - Ubuntu, - Cantarell, - 'Fira Sans', - 'Droid Sans', - 'Helvetica Neue', - $font-sans-serif, - sans-serif; - } - - &.app-body { - padding: 0; - padding-left: env(safe-area-inset-left); - padding-right: env(safe-area-inset-right); - box-sizing: border-box; - - &.layout-single-column { - height: auto; - min-height: 100vh; - min-height: 100dvh; - overflow-y: scroll; - } - - &.layout-multiple-columns { - position: absolute; - width: 100%; - height: 100%; - padding-bottom: env(safe-area-inset-bottom); - } - } - - &.player { - padding: 0; - margin: 0; - position: absolute; - width: 100%; - height: 100%; - overflow: hidden; - - & > div { - height: 100%; - } - - .video-player video { - width: 100%; - height: 100%; - max-height: 100vh; - } - - .media-gallery { - margin-top: 0; - height: 100% !important; - border-radius: 0; - } - - .media-gallery__item { - border-radius: 0; - } - } - - &.embed { - margin: 0; - padding-bottom: 0; - overflow: hidden; - } - - &.admin { - padding: 0; - background: var(--color-bg-primary); - } - - &.error { - position: absolute; - text-align: center; - width: 100%; - height: 100%; - padding: 0; - display: flex; - justify-content: center; - align-items: center; - - .dialog { - vertical-align: middle; - margin: 20px; - - &__illustration { - img { - display: block; - max-width: 470px; - width: 100%; - height: auto; - margin-top: -120px; - margin-bottom: -45px; - } - } - - h1 { - font-size: 20px; - line-height: 28px; - font-weight: 400; - } - } - } -} - -a { - &:focus { - border-radius: 4px; - outline: var(--outline-focus-default); - } - - &:focus:not(:focus-visible) { - outline: none; - } -} - -button { - font-family: inherit; - cursor: pointer; - - &:focus:not(:focus-visible) { - outline: none; - } -} - -.app-holder { - &, - & > div, - & > noscript { - display: flex; - width: 100%; - align-items: center; - justify-content: center; - outline: 0 !important; - } - - & > noscript { - min-height: 100vh; - min-height: 100dvh; - } -} - -.layout-single-column .app-holder { - &, - & > div { - min-height: 100vh; - min-height: 100dvh; - } -} - -.layout-multiple-columns .app-holder { - &, - & > div { - height: 100%; - } -} - -.error-boundary, -.app-holder noscript { - flex-direction: column; - font-size: 16px; - font-weight: 400; - line-height: 1.7; - color: var(--color-text-error); - text-align: center; - - & > div { - max-width: 500px; - } - - p { - margin-bottom: 0.85em; - - &:last-child { - margin-bottom: 0; - } - } - - a { - color: var(--color-text-brand); - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - } - - &__footer { - color: var(--color-text-secondary); - font-size: 13px; - - a { - color: var(--color-text-secondary); - } - } - - button { - display: inline; - border: 0; - background: transparent; - color: var(--color-text-secondary); - font: inherit; - padding: 0; - margin: 0; - line-height: inherit; - cursor: pointer; - outline: 0; - transition: color 300ms linear; - text-decoration: underline; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - - &.copied { - color: var(--mas-status-success-color); - transition: none; - } - } -} - -.logo-resources { - // Not using display: none because of https://bugs.chromium.org/p/chromium/issues/detail?id=258029 - visibility: hidden; - user-select: none; - pointer-events: none; - width: 0; - height: 0; - overflow: hidden; - position: absolute; - top: 0; - inset-inline-start: 0; - z-index: -1000; -} - -// NoScript adds a __ns__pop2top class to the full ancestry of blocked elements, -// to set the z-index to a high value, which messes with modals and dropdowns. -// Blocked elements can in theory only be media and frames/embeds, so they -// should only appear in statuses, under divs and articles. -body, -div, -article { - .__ns__pop2top { - z-index: unset !important; - } -} diff --git a/app/javascript/styles_new/mastodon/branding.scss b/app/javascript/styles_new/mastodon/branding.scss deleted file mode 100644 index a7cc9c500e2432..00000000000000 --- a/app/javascript/styles_new/mastodon/branding.scss +++ /dev/null @@ -1,5 +0,0 @@ -@use 'variables' as *; - -.logo { - color: var(--color-text-primary); -} diff --git a/app/javascript/styles_new/mastodon/components.scss b/app/javascript/styles_new/mastodon/components.scss deleted file mode 100644 index 13d19c5b8c98a4..00000000000000 --- a/app/javascript/styles_new/mastodon/components.scss +++ /dev/null @@ -1,11441 +0,0 @@ -@use 'sass:color'; -@use 'variables' as *; -@use 'mixins' as *; - -.app-body { - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar; -} - -.animated-number { - display: inline-flex; - flex-direction: column; - align-items: stretch; - overflow: hidden; - position: relative; -} - -.inline-alert { - color: var(--color-text-success); - font-weight: 400; - - .no-reduce-motion & { - transition: opacity 200ms ease; - } -} - -.link-button { - display: block; - font-size: 15px; - line-height: 20px; - color: var(--color-text-brand); - border: 0; - background: transparent; - padding: 0; - cursor: pointer; - text-decoration: none; - - &--destructive { - color: var(--color-text-error); - } - - &:hover, - &:active { - text-decoration: underline; - } - - &:disabled { - color: var(--color-text-primary); - cursor: default; - } - - &:focus-visible { - outline: var(--outline-focus-default); - } -} - -.help-button { - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - border: 0; - border-radius: 20px; - cursor: pointer; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - - &:active, - &:focus, - &:hover { - background: var(--color-bg-brand-base-hover); - } - - &:focus-visible { - outline: var(--outline-focus-default); - } - - .icon { - width: 14px; - height: 14px; - } -} - -.button { - background-color: var(--color-bg-brand-base); - border: 10px none; - border-radius: 4px; - box-sizing: border-box; - color: var(--color-text-on-brand-base); - cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - font-family: inherit; - font-size: 15px; - font-weight: 500; - letter-spacing: 0; - line-height: 22px; - overflow: hidden; - padding: 7px 18px; - position: relative; - text-align: center; - text-decoration: none; - text-overflow: ellipsis; - white-space: nowrap; - width: auto; - - &:active, - &:focus, - &:hover { - background-color: var(--color-bg-brand-base-hover); - } - - &:focus-visible { - outline: 2px solid var(--color-bg-brand-base); - outline-offset: 2px; - } - - &--compact { - font-size: 14px; - line-height: normal; - font-weight: 700; - padding: 5px 12px; - border-radius: 4px; - } - - &--dangerous { - background-color: var(--color-bg-error-base); - color: var(--color-text-on-error-base); - - &:active, - &:focus, - &:hover { - background-color: var(--color-bg-error-base-hover); - transition: none; - } - } - - &--destructive { - &:active, - &:focus, - &:hover { - color: var(--color-text-on-error-base); - background-color: var(--color-bg-error-base); - transition: none; - } - } - - &:disabled, - &.disabled { - color: var(--color-text-on-disabled); - background-color: var(--color-bg-disabled); - cursor: not-allowed; - } - - &.copyable { - transition: background 300ms linear; - } - - &.copied { - color: var(--color-text-on-success-base); - background-color: var(--color-bg-success-base); - transition: none; - } - - &.button-secondary { - color: var(--color-text-brand); - background: transparent; - padding: 6px 17px; - border: 1px solid var(--color-text-brand); - - &:active, - &:focus, - &:hover { - border-color: var(--color-text-brand); - color: var(--color-text-brand); - background-color: transparent; - text-decoration: none; - } - - &.button--destructive { - &:active, - &:focus, - &:hover { - border-color: var(--color-text-error); - color: var(--color-text-error); - } - } - - &:disabled, - &.disabled { - border-color: var(--color-text-disabled); - color: var(--color-text-disabled); - - &:active, - &:focus, - &:hover { - border-color: var(--color-text-disabled); - color: var(--color-text-disabled); - } - } - } - - &.button--plain { - color: var(--color-text-brand); - background: transparent; - padding: 6px; - - // The button has no outline, so we use negative margin to - // visually align its label with its surroundings while maintaining - // a generous click target - margin-inline: -6px; - border: 1px solid transparent; - - &:active, - &:focus, - &:hover { - border-color: transparent; - color: var(--color-text-brand-soft); - background-color: transparent; - text-decoration: none; - } - - &:disabled, - &.disabled { - opacity: 0.7; - border-color: transparent; - color: var(--color-text-disabled); - - &:active, - &:focus, - &:hover { - border-color: transparent; - color: var(--color-text-disabled); - } - } - } - - &.button--block { - width: 100%; - } - - &.loading { - cursor: wait; - - .button__label-wrapper { - // Hide the label only visually, so that - // it keeps its layout and accessibility - opacity: 0; - } - - .loading-indicator { - position: absolute; - inset: 0; - } - } - - .icon { - width: 18px; - height: 18px; - } -} - -.column__wrapper { - display: flex; - flex: 1 1 auto; - position: relative; -} - -.icon { - flex: 0 0 auto; - width: 24px; - height: 24px; - aspect-ratio: 1; - - path { - fill: currentColor; - } -} - -.icon-button { - --default-icon-color: var(--color-text-secondary); - --default-bg-color: transparent; - --hover-icon-color: var(--color-text-primary); - --hover-bg-color: var(--color-bg-brand-softer); - - display: inline-flex; - color: var(--default-icon-color); - border: 0; - padding: 0; - border-radius: 4px; - background: var(--default-bg-color); - cursor: pointer; - align-items: center; - justify-content: center; - text-decoration: none; - gap: 4px; - flex: 0 0 auto; - - a { - display: flex; - color: inherit; - text-decoration: none; - } - - &:hover, - &:active, - &:focus-visible { - color: var(--hover-icon-color); - background-color: var(--hover-bg-color); - } - - &:focus-visible { - outline: 2px solid var(--color-text-brand); - } - - &.disabled { - color: var(--color-text-disabled); - background-color: var(--default-bg-color); - cursor: default; - } - - &.inverted { - --default-icon-color: var(--color-text-primary); - --hover-icon-color: var(--color-text-secondary); - } - - &.active { - --default-icon-color: var(--color-text-brand); - --hover-icon-color: var(--color-text-brand); - --hover-bg-color: transparent; - } - - &.overlayed { - --default-icon-color: rgb(from var(--color-text-on-media) r g b / 70%); - --default-bg-color: var(--color-bg-media); - --hover-icon-color: var(--color-text-brand); - --hover-bg-color: rgb(from var(--color-bg-media-base) r g b / 90%); - - box-sizing: content-box; - backdrop-filter: $backdrop-blur-filter; - border-radius: 4px; - padding: 2px; - } - - &--with-counter { - padding-inline-end: 4px; - } - - &__counter { - display: block; - width: auto; - font-size: 12px; - font-weight: 500; - } - - &.copyable { - transition: all 300ms linear; - } - - &.copied { - color: var(--color-text-success); - transition: none; - background-color: var(--color-bg-success-softer); - border-color: var(--color-border-on-bg-brand-softer); - } -} - -body > [data-popper-placement] { - z-index: 9999; -} - -.invisible { - font-size: 0; - line-height: 0; - display: inline-block; - width: 0; - height: 0; - position: absolute; - - img, - svg { - margin: 0 !important; - border: 0 !important; - padding: 0 !important; - width: 0 !important; - height: 0 !important; - } -} - -.ellipsis { - &::after { - content: '…'; - } -} - -.autosuggest-textarea { - &__textarea { - background: transparent; - min-height: 100px; - padding-bottom: 0; - resize: none; - scrollbar-color: initial; - - &::-webkit-scrollbar { - all: unset; - } - } - - &__suggestions { - box-shadow: var(--dropdown-shadow); - background: var(--color-bg-elevated); - border: 1px solid var(--color-border-primary); - border-radius: 0 0 4px 4px; - color: var(--color-text-primary); - font-size: 14px; - padding: 0; - - &__item { - box-sizing: border-box; - display: flex; - align-items: center; - height: 48px; - cursor: pointer; - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - color: var(--color-text-primary); - - &:last-child { - border-radius: 0 0 4px 4px; - } - - &:hover, - &:focus, - &:active { - background: var(--color-bg-secondary); - - .autosuggest-account .display-name__account { - color: inherit; - } - } - - &.selected { - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - - .autosuggest-account .display-name__account { - color: inherit; - } - } - } - } -} - -.autosuggest-account, -.autosuggest-emoji, -.autosuggest-hashtag { - flex: 1 0 0; - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; - gap: 12px; - padding: 8px 12px; - overflow: hidden; - text-overflow: ellipsis; -} - -.autosuggest-account { - .display-name { - font-weight: 400; - display: flex; - flex-direction: column; - flex: 1 0 0; - } - - .display-name__account { - display: block; - line-height: 16px; - font-size: 12px; - color: var(--color-text-secondary); - } -} - -.autosuggest-hashtag { - justify-content: space-between; - - &__name { - flex: 1 1 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - &__uses { - flex: 0 0 auto; - text-align: end; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } -} - -.autosuggest-emoji { - &__name { - flex: 1 0 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } -} - -.autosuggest-account .account__avatar, -.autosuggest-emoji img { - display: block; - width: 24px; - height: 24px; - flex: 0 0 auto; -} - -.compose-form { - display: flex; - flex-direction: column; - gap: 32px; - - .layout-multiple-columns &, - .column & { - padding: 15px; - } - - &__highlightable { - display: flex; - flex-direction: column; - flex: 0 1 auto; - border-radius: 4px; - border: 1px solid var(--color-border-on-bg-secondary); - transition: border-color 300ms linear; - position: relative; - background: var(--color-bg-secondary); - - &.active { - transition: none; - border-color: var(--color-text-brand); - } - } - - &__warning { - color: var(--color-text-primary); - background: var(--color-bg-warning-softer); - border: 1px solid var(--color-border-on-bg-warning-softer); - padding: 8px 10px; - border-radius: 4px; - font-size: 13px; - font-weight: 400; - - strong { - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - a { - color: var(--color-text-brand); - font-weight: 500; - text-decoration: underline; - - &:hover, - &:active, - &:focus { - text-decoration: none; - } - } - } - - .spoiler-input { - display: flex; - align-items: stretch; - - &__border { - background: url('@/images/warning-stripes.svg') repeat-y; - width: 5px; - flex: 0 0 auto; - } - - .autosuggest-input { - flex: 1 1 auto; - border: 1px solid var(--color-border-primary); - border-width: 1px 0; - } - } - - .autosuggest-textarea__textarea, - .spoiler-input__input { - display: block; - box-sizing: border-box; - width: 100%; - margin: 0; - color: var(--color-text-primary); - background: transparent; - font-family: inherit; - font-size: 14px; - padding: 12px; - line-height: normal; - border: 0; - outline: 0; - - &:focus { - outline: 0; - } - } - - .spoiler-input__input { - padding: 12px 12px - 5px; - background: var(--color-bg-brand-softer); - color: var(--color-text-brand); - } - - &__dropdowns { - display: flex; - align-items: center; - gap: 8px; - margin: 8px; - flex-wrap: wrap; - - & > div { - overflow: hidden; - display: flex; - } - } - - &__uploads { - padding: 0 12px; - aspect-ratio: 3/2; - flex-shrink: 0; - } - - .media-gallery { - gap: 8px; - } - - &__upload { - position: relative; - - &.draggable { - will-change: transform, opacity; - touch-action: none; - cursor: grab; - } - - &.dragging { - opacity: 0; - } - - &.overlay { - height: 100%; - border-radius: 8px; - pointer-events: none; - } - - &__actions { - display: flex; - align-items: flex-start; - justify-content: space-between; - padding: 8px; - } - - &__preview, - &__visualizer { - position: absolute; - width: 100%; - height: 100%; - z-index: -1; - top: 0; - } - - &__preview { - border-radius: 6px; - inset-inline-start: 0; - } - - &__visualizer { - padding: 16px; - box-sizing: border-box; - - .audio-player__visualizer { - margin: 0 auto; - display: block; - height: 100%; - } - - .icon { - position: absolute; - top: 50%; - inset-inline-start: 50%; - transform: translate(-50%, -50%); - opacity: 0.75; - color: var(--player-foreground-color); - filter: var(--overlay-icon-shadow); - width: 48px; - height: 48px; - } - } - - &__thumbnail { - width: 100%; - height: 100%; - background-position: center; - background-size: cover; - background-repeat: no-repeat; - overflow: hidden; - } - - .icon-button { - flex: 0 0 auto; - color: var(--color-text-on-media); - background: var(--color-bg-media); - backdrop-filter: $backdrop-blur-filter; - border-radius: 6px; - font-size: 12px; - line-height: 16px; - font-weight: 500; - padding: 4px 8px; - font-family: inherit; - - .icon { - width: 15px; - height: 15px; - } - } - - .icon-button.compose-form__upload__delete { - padding: 2px; - border-radius: 50%; - - .icon { - width: 20px; - height: 20px; - } - } - - &__warning { - position: absolute; - z-index: 2; - bottom: 0; - inset-inline-start: 0; - inset-inline-end: 0; - padding: 8px; - - .icon-button.active { - color: var(--color-text-on-warning-base); - background: var(--color-bg-warning-base); - } - } - } - - &__footer { - display: flex; - flex-direction: column; - gap: 12px; - padding: 12px; - } - - &__submit { - display: flex; - align-items: center; - flex: 1 1 auto; - max-width: 100%; - overflow: hidden; - } - - &__buttons { - display: flex; - gap: 8px; - align-items: center; - flex: 1 1 auto; - - & > div { - display: flex; - } - - .icon-button { - padding: 2px; - } - - .icon-button .icon { - width: 20px; - height: 20px; - } - } - - &__actions { - display: flex; - align-items: center; - flex: 0 0 auto; - gap: 12px; - flex-wrap: wrap; - - .icon-button { - box-sizing: content-box; - color: var(--color-text-brand); - - &:hover, - &:focus, - &:active { - color: var(--color-text-brand); - } - - &.disabled { - color: var(--color-text-disabled); - } - - &.active { - background: var(--color-bg-brand-base); - color: var(--color-text-on-brand-base); - } - } - } - - &__poll { - margin-top: 8px; - display: flex; - flex-direction: column; - align-self: stretch; - gap: 8px; - - .poll__option { - padding: 0 12px; - gap: 8px; - - &.empty:not(:focus-within) { - opacity: 0.5; - } - } - - .poll__input { - width: 17px; - height: 17px; - border-color: var(--color-text-secondary); - } - - &__footer { - display: flex; - align-items: center; - gap: 16px; - padding-inline-start: 37px; - padding-inline-end: 40px; - - &__sep { - width: 1px; - height: 22px; - background: var(--color-border-primary); - flex: 0 0 auto; - } - } - - &__select { - display: flex; - flex-direction: column; - gap: 2px; - flex: 1 1 auto; - min-width: 0; - - &__label { - flex: 0 0 auto; - font-size: 11px; - font-weight: 500; - line-height: 16px; - letter-spacing: 0.5px; - color: var(--color-text-secondary); - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - } - - &__value { - flex: 0 0 auto; - appearance: none; - background: transparent; - border: none; - padding: 0; - font-size: 14px; - font-weight: 500; - line-height: 20px; - letter-spacing: 0.1px; - color: var(--color-text-brand); - background-color: var(--color-bg-secondary-solid); - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - } - } - } - - .status__quote { - margin: 0 8px; - max-height: 220px; - - // Override .status__content .status__content__text.status__content__text--visible - .status__content__text.status__content__text { - display: -webkit-box; - } - - .status__content__text { - -webkit-line-clamp: 4; - line-clamp: 4; - -webkit-box-orient: vertical; - overflow: hidden; - } - } -} - -.dropdown-button { - display: flex; - align-items: center; - gap: 4px; - color: var(--color-text-brand); - background: transparent; - border: 1px solid var(--color-text-brand); - border-radius: 6px; - padding: 4px 8px; - font-size: 13px; - line-height: normal; - font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &[disabled] { - cursor: default; - color: var(--color-text-disabled); - border-color: var(--color-text-disabled); - } - - .icon { - width: 15px; - height: 15px; - flex: 0 0 auto; - } - - &__label { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1 1 auto; - } - - &.active { - color: var(--color-text-on-brand-base); - border-color: var(--color-bg-brand-base); - background: var(--color-bg-brand-base); - } - - &.warning { - color: var(--color-text-warning); - border-color: var(--color-text-warning); - - &.active { - color: var(--color-text-on-warning-base); - border-color: var(--color-bg-warning-base); - background-color: var(--color-bg-warning-base); - } - } -} - -.character-counter { - cursor: default; - font-family: $font-sans-serif, sans-serif; - font-size: 14px; - font-weight: 400; - line-height: normal; - color: var(--color-text-secondary); - flex: 1 0 auto; - text-align: end; - - &.character-counter--over { - color: var(--color-text-error); - } -} - -.no-reduce-motion .spoiler-input { - transition: - height 0.4s ease, - opacity 0.4s ease; -} - -.sign-in-banner { - padding: 10px; - - p { - font-size: 15px; - line-height: 22px; - color: var(--color-text-primary); - margin-bottom: 20px; - - strong { - font-weight: 700; - } - - a { - color: var(--color-text-primary); - text-decoration: none; - unicode-bidi: isolate; - - &:hover { - text-decoration: underline; - } - } - } - - .button { - margin-bottom: 10px; - } -} - -.emojione { - font-size: inherit; - vertical-align: middle; - object-fit: contain; - margin: -0.2ex 0.15em 0.2ex; - width: 16px; - height: 16px; - - img { - width: auto; - } -} - -.status__content--with-action { - cursor: pointer; -} - -.status__content { - clear: both; -} - -.status__content, -.edit-indicator__content, -.reply-indicator__content { - position: relative; - overflow-wrap: break-word; - font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - font-size: 15px; - line-height: 22px; - padding-top: 2px; - color: var(--color-text-primary); - - &:focus { - outline: 0; - } - - &.status__content--with-spoiler { - white-space: normal; - - .status__content__text { - white-space: pre-wrap; - } - } - - .emojione { - width: 20px; - height: 20px; - margin: -3px 0 0; - } - - p { - margin-bottom: 22px; - white-space: pre-wrap; - unicode-bidi: plaintext; - - &:last-child { - margin-bottom: 0; - } - } - - a { - color: var(--color-text-status-links); - text-decoration: none; - unicode-bidi: isolate; - - &:hover { - text-decoration: underline; - } - - &.mention { - &:hover { - text-decoration: none; - - span { - text-decoration: underline; - } - } - } - } - - a.unhandled-link { - color: var(--color-text-brand); - } - - .status__content__text { - display: none; - - &.status__content__text--visible { - display: block; - } - } -} - -.reply-indicator { - display: grid; - grid-template-columns: 46px minmax(0, 1fr); - grid-template-rows: 46px max-content; - gap: 0 10px; - - .detailed-status__display-name { - margin-bottom: 4px; - } - - .detailed-status__display-avatar { - grid-column-start: 1; - grid-row-start: 1; - grid-row-end: span 1; - } - - &__main { - grid-column-start: 2; - grid-row-start: 1; - grid-row-end: span 2; - } - - .display-name { - font-size: 14px; - line-height: 16px; - - &__account { - display: none; - } - } - - &__line { - grid-column-start: 1; - grid-row-start: 2; - grid-row-end: span 1; - position: relative; - - &::before { - display: block; - content: ''; - position: absolute; - inset-inline-start: 50%; - top: 4px; - transform: translateX(-50%); - background: var(--color-border-primary); - width: 2px; - height: calc(100% + 32px - 8px); // Account for gap to next element - } - } - - &__content { - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - display: -webkit-box; - line-clamp: 4; - -webkit-line-clamp: 4; - -webkit-box-orient: vertical; - padding: 0; - max-height: 4 * 20px; - overflow: hidden; - color: var(--color-text-secondary); - } - - &__attachments { - margin-top: 4px; - color: var(--color-text-secondary); - font-size: 12px; - line-height: 16px; - display: flex; - align-items: center; - gap: 4px; - - .icon { - width: 18px; - height: 18px; - } - } -} - -.edit-indicator { - border-radius: 4px 4px 0 0; - background: var(--color-bg-tertiary); - padding: 12px; - overflow-y: auto; - flex: 0 0 auto; - border-bottom: 1px solid var(--color-border-primary); - display: flex; - flex-direction: column; - gap: 4px; - - &__header { - display: flex; - justify-content: space-between; - align-items: center; - color: var(--color-text-secondary); - font-size: 12px; - line-height: 16px; - overflow: hidden; - text-overflow: ellipsis; - } - - &__cancel { - display: flex; - - .icon { - width: 18px; - height: 18px; - } - } - - &__display-name { - display: flex; - gap: 4px; - - a { - color: inherit; - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } - } - - &__content { - color: var(--color-text-primary); - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - padding-top: 0 !important; - display: -webkit-box; - line-clamp: 4; - -webkit-line-clamp: 4; - -webkit-box-orient: vertical; - max-height: 4 * 20px; - overflow: hidden; - - a { - color: var(--color-text-brand); - } - } - - &__attachments { - color: var(--color-text-secondary); - font-size: 12px; - line-height: 16px; - opacity: 0.75; - display: flex; - align-items: center; - gap: 4px; - - .icon { - width: 18px; - height: 18px; - } - } -} - -.edit-indicator__content, -.reply-indicator__content { - .emojione { - width: 18px; - height: 18px; - margin: -3px 0 0; - } -} - -.announcements__content { - overflow-wrap: break-word; - overflow-y: auto; - - .emojione { - width: 20px; - height: 20px; - margin: -3px 0 0; - } - - p { - margin-bottom: 10px; - white-space: pre-wrap; - - &:last-child { - margin-bottom: 0; - } - } - - a { - color: var(--color-text-primary); - text-decoration: none; - - &:hover { - text-decoration: underline; - } - - &.mention { - &:hover { - text-decoration: none; - - span { - text-decoration: underline; - } - } - } - - &.unhandled-link { - color: var(--color-text-brand); - } - } -} - -.status__content.status__content--collapsed { - max-height: 22px * 15; // 15 lines is roughly above 500 characters -} - -.status__content__read-more-button, -.status__content__translate-button { - display: flex; - align-items: center; - font-size: 15px; - line-height: 22px; - color: var(--color-text-brand); - border: 0; - background: transparent; - padding: 0; - margin-top: 16px; - text-decoration: none; - text-wrap: nowrap; - - .status--is-quote & { - // Needed to prevent buttons from stretching across whole - // status width in Safari due to line-clamp - width: min-content; - white-space: nowrap; - } - - &:hover, - &:active { - text-decoration: underline; - } - - .icon { - width: 15px; - height: 15px; - } -} - -.translate-button { - margin-top: 16px; - font-size: 15px; - line-height: 22px; - display: flex; - justify-content: space-between; - color: var(--color-text-tertiary); -} - -.status__wrapper--filtered { - color: var(--color-text-tertiary); - border: 0; - font-size: inherit; - text-align: center; - line-height: inherit; - margin: 0; - padding: 15px; - box-sizing: border-box; - width: 100%; - clear: both; - border-bottom: 1px solid var(--color-border-primary); - - &__button { - display: inline; - color: var(--color-text-brand); - border: 0; - background: transparent; - padding: 0; - font-size: inherit; - line-height: inherit; - - &:hover, - &:active { - text-decoration: underline; - } - } -} - -.focusable { - &:focus-visible { - outline: 2px solid var(--color-text-brand); - outline-offset: -2px; - background: var(--color-bg-brand-softer); - } -} - -.status { - padding: 16px; - min-height: 54px; - border-bottom: 1px solid var(--color-border-primary); - cursor: auto; - opacity: 1; - animation: fade 150ms linear; - - @keyframes fade { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - } - } - - .content-warning { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } - } - - .media-gallery, - .video-player, - .audio-player, - .attachment-list { - margin-top: 16px; - } - - &--in-thread { - --thread-margin: calc(46px + 8px); - - border-bottom: 0; - - & > .status__content, - & > .status__action-bar, - & > .media-gallery, - & > div > .video-player, - & > .audio-player, - & > .attachment-list, - & > .picture-in-picture-placeholder, - & > .more-from-author, - & > .status-card, - & > .hashtag-bar, - & > .content-warning, - & > .filter-warning, - & > .status__quote { - margin-inline-start: var(--thread-margin); - width: calc(100% - var(--thread-margin)); - } - - .more-from-author { - width: calc(100% - var(--thread-margin) + 2px); - } - - .status__content__read-more-button { - margin-inline-start: var(--thread-margin); - } - } - - &__action-bar__button-wrapper { - flex-basis: 0; - flex-grow: 1; - - &:last-child { - flex-grow: 0; - } - } - - &--first-in-thread { - border-top: 1px solid var(--color-border-primary); - } - - &__line { - --thread-line-color: var(--color-border-primary); - - height: 16px - 4px; - border-inline-start: 2px solid var(--thread-line-color); - width: 0; - position: absolute; - top: 0; - inset-inline-start: 16px + ((46px - 2px) * 0.5); - - &--full { - top: 0; - height: 100%; - - &::before { - content: ''; - display: block; - position: absolute; - top: 16px - 4px; - height: 46px + 4px + 4px; - width: 2px; - background: var(--thread-line-color); - inset-inline-start: -2px; - } - } - - &--first { - top: 16px + 46px + 4px; - height: calc(100% - (16px + 46px + 4px)); - - &::before { - display: none; - } - } - } - - .no-reduce-motion &--highlighted-entry::before { - content: ''; - position: absolute; - inset: 0; - background: var(--color-bg-brand-softer); - opacity: 0; - animation: fade 0.7s reverse both 0.3s; - pointer-events: none; - } -} - -.status__relative-time { - display: block; - font-size: 15px; - line-height: 22px; - height: 40px; - order: 2; - flex: 0 0 auto; - color: var(--color-text-secondary); -} - -.notification__relative_time { - color: var(--color-text-tertiary); - float: right; - font-size: 14px; - padding-bottom: 1px; -} - -.status__visibility-icon { - padding: 0 4px; - - .icon { - width: 1em; - height: 1em; - margin-bottom: -2px; - } -} - -.status__display-name { - color: var(--color-text-secondary); -} - -.status__info .status__display-name { - max-width: 100%; - display: flex; - font-size: 15px; - line-height: 22px; - align-items: center; - gap: 10px; - overflow: hidden; - margin-inline-end: auto; - - .display-name { - bdi { - overflow: hidden; - text-overflow: ellipsis; - } - - &__account { - white-space: nowrap; - display: block; - overflow: hidden; - text-overflow: ellipsis; - } - } -} - -.status__quote-cancel { - align-self: self-start; - order: 5; -} - -.status__info { - font-size: 15px; - padding-bottom: 10px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - cursor: pointer; -} - -.status-check-box__status { - display: block; - box-sizing: border-box; - width: 100%; - padding: 0 10px; - - .detailed-status__display-name { - color: var(--color-text-tertiary); - - span { - display: inline; - } - - &:hover strong { - text-decoration: none; - } - } - - .media-gallery, - .audio-player, - .video-player { - margin-top: 15px; - max-width: 250px; - } - - .status__content { - padding: 0; - white-space: normal; - } - - .media-gallery__item-thumbnail { - cursor: default; - } -} - -.status__prepend { - padding: 16px; - padding-bottom: 0; - display: flex; - align-items: center; - gap: 8px; - font-size: 15px; - line-height: 22px; - font-weight: 500; - color: var(--color-text-secondary); - - &__icon { - display: flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - - .icon { - width: 16px; - height: 16px; - } - } - - a { - color: inherit; - text-decoration: none; - } - - > span { - display: block; - overflow: hidden; - text-overflow: ellipsis; - } -} - -.status__wrapper-direct, -.notification-ungrouped--direct, -.notification-group--direct, -.notification-group--annual-report { - background: var(--color-bg-brand-softer); - - &:focus { - background: var(--color-bg-brand-soft); - } -} - -.status__wrapper-direct, -.notification-ungrouped--direct { - .status__prepend, - .notification-ungrouped__header { - color: var(--color-text-brand); - } -} - -.status__action-bar { - display: flex; - justify-content: space-between; - align-items: center; - gap: 18px; - margin-top: 16px; -} - -.detailed-status__action-bar-dropdown { - flex: 1 1 auto; - display: flex; - align-items: center; - justify-content: center; - position: relative; -} - -.detailed-status { - padding: 16px; - border-top: 1px solid var(--color-border-primary); - - .status__content { - font-size: 19px; - line-height: 24px; - - .emojione { - width: 24px; - height: 24px; - margin: -1px 0 0; - } - } - - .media-gallery, - .video-player, - .audio-player { - margin-top: 16px; - } - - .status__prepend { - padding: 0; - margin-bottom: 16px; - } - - .content-warning { - margin-bottom: 16px; - - &:last-child { - margin-bottom: 0; - } - } - - .logo { - width: 40px; - height: 40px; - color: var(--color-text-tertiary); - } -} - -.embed { - position: relative; - - &__overlay { - display: block; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - } - - .detailed-status { - border-top: 0; - } -} - -.scrollable > div:first-child .detailed-status { - border-top: 0; -} - -.detailed-status__meta { - margin-top: 24px; - color: var(--color-text-tertiary); - font-size: 14px; - line-height: 18px; - - &__line { - border-bottom: 1px solid var(--color-border-primary); - padding: 8px 0; - display: flex; - align-items: center; - gap: 8px; - - &:first-child { - padding-top: 0; - } - - &:last-child { - padding-bottom: 0; - border-bottom: 0; - } - } - - .icon { - width: 18px; - height: 18px; - } - - .animated-number { - color: var(--color-text-primary); - font-weight: 500; - } -} - -.detailed-status__action-bar { - border-top: 1px solid var(--color-border-primary); - border-bottom: 1px solid var(--color-border-primary); - display: flex; - flex-direction: row; - padding: 10px 0; -} - -.detailed-status__wrapper-direct { - .detailed-status, - .detailed-status__action-bar { - background: var(--color-bg-brand-softer); - } - - .status__prepend { - color: var(--color-text-brand); - } -} - -.status__quote { - // --status-gutter-width is currently only set inside of - // .notification-ungrouped, so everywhere else this will fall back - // to the pixel values - --quote-margin: var(--status-gutter-width); - - position: relative; - overflow: hidden; - margin-block-start: 16px; - margin-inline-start: calc(var(--quote-margin) + var(--thread-margin, 0px)); - border-radius: 12px; - color: var(--color-text-primary); - border: 1px solid var(--color-border-primary); -} - -.status__quote--error { - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 12px; - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - min-height: 56px; - - .link-button { - font-size: inherit; - line-height: inherit; - letter-spacing: inherit; - } -} - -.status__quote-author-button { - position: relative; - overflow: hidden; - display: flex; - margin-top: 8px; - padding: 8px 12px; - align-items: center; - font-family: inherit; - font-size: 14px; - font-weight: 400; - line-height: 20px; - letter-spacing: 0.25px; - color: var(--color-text-secondary); - background: var(--color-bg-brand-softer); - border-radius: 8px; - cursor: default; -} - -.status--is-quote { - border: none; - padding: 12px; - - .status__info { - padding-bottom: 8px; - } - - .display-name, - .status__relative-time { - font-size: 14px; - line-height: 20px; - letter-spacing: 0.1px; - } - - .display-name__account { - font-size: 12px; - line-height: 16px; - letter-spacing: 0.5px; - } - - .status__content { - display: -webkit-box; - font-size: 14px; - letter-spacing: 0.25px; - line-height: 20px; - -webkit-line-clamp: 4; - line-clamp: 4; - -webkit-box-orient: vertical; - overflow: hidden; - - p { - margin-bottom: 20px; - - &:last-child { - margin-bottom: 0; - } - } - } - - .media-gallery, - .video-player, - .audio-player, - .attachment-list, - .poll { - margin-top: 8px; - } -} - -.detailed-status__link { - display: inline-flex; - align-items: center; - color: inherit; - text-decoration: none; - gap: 6px; -} - -.domain { - padding: 16px; - border-bottom: 1px solid var(--color-border-primary); - display: flex; - align-items: center; - gap: 8px; - - &__domain-name { - flex: 1 1 auto; - color: var(--color-text-primary); - font-size: 15px; - line-height: 21px; - font-weight: 500; - } -} - -.account { - padding: 16px; - border-bottom: 1px solid var(--color-border-primary); - - .account__display-name { - flex: 1 1 auto; - display: flex; - align-items: center; - gap: 10px; - color: var(--color-text-secondary); - overflow: hidden; - text-decoration: none; - font-size: 14px; - - .display-name { - margin-bottom: 4px; - } - - .display-name strong { - display: inline; - color: var(--color-text-primary); - } - } - - &--minimal { - .account__display-name { - .display-name { - margin-bottom: 0; - } - - .display-name strong { - display: block; - } - } - } - - &__avatar-wrapper .account__avatar { - @container (width < 360px) { - width: 35px !important; - height: 35px !important; - } - } - - &__domain-pill { - display: inline-flex; - background: var(--color-bg-brand-softer); - border-radius: 4px; - border: 0; - color: var(--color-text-brand); - font-weight: 500; - font-size: 12px; - line-height: 16px; - padding: 4px 8px; - - &.active { - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - } - - &__popout { - background: var(--color-bg-elevated); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - box-shadow: var(--dropdown-shadow); - max-width: 320px; - padding: 16px; - border-radius: 8px; - display: flex; - flex-direction: column; - gap: 24px; - font-size: 14px; - line-height: 20px; - color: var(--color-text-secondary); - - .link-button { - display: inline; - font-size: inherit; - line-height: inherit; - } - - &__header { - display: flex; - align-items: center; - gap: 12px; - - &__icon { - width: 40px; - height: 40px; - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - flex-shrink: 0; - } - - h3 { - font-size: 17px; - line-height: 22px; - color: var(--color-text-primary); - } - } - - &__handle { - border: 2px dashed var(--color-border-on-brand-softer); - background: var(--color-bg-brand-softer); - padding: 12px 8px; - color: var(--color-text-brand); - border-radius: 4px; - - &__label { - font-size: 11px; - line-height: 16px; - font-weight: 500; - } - - &__handle { - overflow: hidden; - text-overflow: ellipsis; - user-select: all; - } - } - - &__parts { - display: flex; - flex-direction: column; - gap: 8px; - font-size: 12px; - line-height: 16px; - - & > div { - display: flex; - align-items: flex-start; - gap: 12px; - } - - &__icon { - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - color: var(--color-text-brand); - } - - h6 { - font-size: 14px; - line-height: 20px; - font-weight: 500; - color: var(--color-text-primary); - } - } - } - } - - &__note { - font-size: 14px; - font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - line-clamp: 1; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - margin-top: 10px; - color: var(--color-text-secondary); - - &--missing { - color: var(--color-text-tertiary); - } - - p { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } - } - - a { - color: inherit; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - } - } -} - -.learn-more__popout { - gap: 8px; - - &__content { - display: flex; - flex-direction: column; - gap: 4px; - } - - h6 { - font-size: inherit; - font-weight: 500; - line-height: inherit; - letter-spacing: 0.1px; - } - - .link-button { - font-weight: 500; - } -} - -.account__wrapper { - display: flex; - gap: 10px; - align-items: center; - justify-content: end; -} - -.account__wrapper--with-bio { - align-items: start; -} - -.account__info-wrapper { - flex: 1 1 auto; - min-width: 0; -} - -.account__avatar { - display: block; - position: relative; - border-radius: var(--avatar-border-radius); - background: var(--color-bg-tertiary); - flex-shrink: 0; - - img { - width: 100%; - height: 100%; - object-fit: cover; - border-radius: var(--avatar-border-radius); - display: inline-block; // to not show broken images - } - - &--loading { - background-color: var(--color-bg-tertiary); - } - - &--inline { - display: inline-block; - vertical-align: middle; - margin-inline-end: 5px; - } - - &-composite { - border-radius: 50%; - overflow: hidden; - position: relative; - - & > div { - float: left; - position: relative; - box-sizing: border-box; - } - - .account__avatar { - width: 100% !important; - height: 100% !important; - } - - &__label { - display: block; - position: absolute; - top: 50%; - inset-inline-start: 50%; - transform: translate(-50%, -50%); - color: var(--color-text-primary); - text-shadow: 1px 1px 2px - rgb(from var(--color-shadow-primary) r g b / 100%); - font-weight: 700; - font-size: 15px; - } - } - - &__counter { - $height: 16px; - $h-padding: 5px; - - position: absolute; - bottom: -3px; - inset-inline-end: -3px; - padding-left: $h-padding; - padding-right: $h-padding; - height: $height; - border-radius: $height; - min-width: $height - 2 * $h-padding; // to ensure that it is never narrower than a circle - line-height: $height + 1px; // to visually center the numbers - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - border-width: 1px; - border-style: solid; - border-color: var(--color-bg-primary); - font-size: 11px; - font-weight: 500; - text-align: center; - } -} - -a .account__avatar { - cursor: pointer; -} - -.avatar-group { - display: flex; - - --avatar-height: 28px; - - &:not(.avatar-group--compact) { - gap: 8px; - flex-wrap: wrap; - height: var(--avatar-height); - overflow-y: clip; - } -} - -.avatar-group--compact { - & > :not(:first-child) { - margin-inline-start: -12px; - } - - & > :first-child { - transform: rotate(-4deg); - } - - & > :nth-child(2) { - transform: rotate(-2deg); - } - - .account__avatar { - box-shadow: 0 0 0 2px var(--color-bg-primary); - } -} - -.account__avatar-overlay { - position: relative; - - &-overlay { - position: absolute; - bottom: 0; - inset-inline-end: 0; - z-index: 1; - } -} - -.account__relationship { - white-space: nowrap; - display: flex; - align-items: center; - gap: 8px; -} - -.account__relationship, -.explore-suggestions-card { - .icon-button { - border: 1px solid var(--color-border-primary); - border-radius: 4px; - box-sizing: content-box; - padding: 5px; - - .icon { - width: 24px; - height: 24px; - } - } -} - -.account-authorize { - padding: 14px 10px; - - .detailed-status__display-name { - display: block; - margin-bottom: 15px; - overflow: hidden; - } -} - -.account-authorize__avatar { - float: left; - margin-inline-end: 10px; -} - -.status__display-name, -.status__relative-time, -.detailed-status__display-name, -.detailed-status__datetime, -.detailed-status__application, -.detailed-status__link, -.account__display-name { - text-decoration: none; -} - -.status__display-name, -.account__display-name { - .display-name strong { - color: var(--color-text-primary); - } -} - -.muted { - .emojione { - opacity: 0.5; - } -} - -.status__display-name, -.detailed-status__display-name, -a.account__display-name { - &:hover .display-name strong { - text-decoration: underline; - } -} - -.account__display-name .display-name strong { - display: block; - overflow: hidden; - text-overflow: ellipsis; -} - -.detailed-status__application, -.detailed-status__datetime, -.detailed-status__link { - color: inherit; -} - -.detailed-status__display-name { - color: var(--color-text-secondary); - display: flex; - align-items: center; - gap: 10px; - font-size: 15px; - line-height: 22px; - margin-bottom: 16px; - overflow: hidden; - - strong, - span { - display: block; - text-overflow: ellipsis; - overflow: hidden; - } - - strong { - color: var(--color-text-primary); - } -} - -.muted { - .status__content, - .status__content p, - .status__content a { - color: var(--color-text-tertiary); - } - - .status__display-name strong { - color: var(--color-text-tertiary); - } - - .status__avatar { - opacity: 0.5; - } -} - -.notification__report { - padding: 16px; - border-bottom: 1px solid var(--color-border-primary); - display: flex; - gap: 10px; - - &__avatar { - flex: 0 0 auto; - } - - &__details { - flex: 1 1 auto; - display: flex; - justify-content: space-between; - align-items: center; - color: var(--color-text-secondary); - gap: 10px; - font-size: 15px; - line-height: 22px; - white-space: nowrap; - overflow: hidden; - - & > div { - overflow: hidden; - text-overflow: ellipsis; - } - - strong { - font-weight: 500; - } - } - - &__actions { - flex: 0 0 auto; - } -} - -.notification-group--link { - color: var(--color-text-primary); - text-decoration: none; - - .notification-group__main { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 8px; - flex-grow: 1; - font-size: 15px; - line-height: 22px; - - strong, - bdi { - font-weight: 700; - } - - .link-button { - font-size: inherit; - line-height: inherit; - font-weight: inherit; - } - } -} - -.notification__message { - padding: 16px; - padding-bottom: 0; - cursor: default; - color: var(--color-text-secondary); - font-size: 15px; - line-height: 22px; - font-weight: 500; - display: flex; - align-items: center; - gap: 10px; - - .icon { - color: var(--color-text-brand); - width: 18px; - height: 18px; - } - - .icon-star { - color: var(--color-text-favourite-highlight); - } - - > span { - display: inline; - overflow: hidden; - text-overflow: ellipsis; - } -} - -.icon-button.star-icon.active { - color: var(--color-text-favourite-highlight); -} - -.icon-button.bookmark-icon.active { - color: var(--color-text-bookmark-highlight); -} - -.no-reduce-motion .icon-button.star-icon { - &.activate { - & > .icon { - animation: spring-rotate-in 1s linear; - transform-origin: 50% 52%; - } - } - - &.deactivate { - & > .icon { - animation: spring-rotate-out 1s linear; - transform-origin: 50% 52%; - } - } -} - -.notification__display-name { - color: inherit; - font-weight: 500; - text-decoration: none; - - &:hover { - text-decoration: underline; - } -} - -.display-name { - display: block; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &__account { - text-overflow: ellipsis; - overflow: hidden; - } -} - -.display-name__html { - font-weight: 500; -} - -.status__relative-time, -.detailed-status__datetime, -.detailed-status__link { - &:is(a):hover { - text-decoration: underline; - } -} - -.zoomable-image { - position: relative; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - scrollbar-width: none; - overflow: hidden; - user-select: none; - - img { - max-width: $media-modal-media-max-width; - max-height: $media-modal-media-max-height; - width: auto; - height: auto; - outline: 1px solid var(--color-border-media); - outline-offset: -1px; - border-radius: 8px; - touch-action: none; - user-select: none; - } - - &--zoomed-in { - z-index: 9999; - cursor: grab; - - img { - outline: none; - border-radius: 0; - } - } - - &--dragging { - cursor: grabbing; - } - - &--error img { - visibility: hidden; - } - - &__preview { - max-width: $media-modal-media-max-width; - max-height: $media-modal-media-max-height; - position: absolute; - z-index: 1; - outline: 1px solid var(--color-border-media); - outline-offset: -1px; - border-radius: 8px; - overflow: hidden; - - canvas { - position: absolute; - width: 100%; - height: 100%; - object-fit: cover; - z-index: -1; - } - } - - .loading-indicator { - z-index: 2; - mix-blend-mode: luminosity; - } -} - -.navigation-bar { - display: flex; - align-items: center; - flex-shrink: 0; - cursor: default; - gap: 10px; - - .column > & { - padding: 15px; - } - - .account { - border-bottom: 0; - padding: 0; - flex: 1 1 auto; - min-width: 0; - - &__display-name { - font-size: 14px; - line-height: 20px; - font-weight: 500; - - .display-name__account { - font-size: 14px; - font-weight: 400; - } - } - } - - .icon-button { - padding: 8px; - color: var(--color-text-primary); - } - - .icon-button .icon { - width: 24px; - height: 24px; - } -} - -.dropdown-animation { - animation: dropdown 250ms cubic-bezier(0.1, 0.7, 0.1, 1); - - @keyframes dropdown { - from { - opacity: 0; - } - - to { - opacity: 1; - } - } - - .reduce-motion & { - animation: none; - } -} - -.dropdown { - display: inline-block; -} - -.dropdown__content { - display: none; - position: absolute; -} - -.dropdown-menu__separator { - border-bottom: 1px solid var(--color-border-primary); - margin: 5px 0; - height: 0; -} - -.dropdown-menu { - background: var(--color-bg-elevated); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - padding: 4px; - border-radius: 4px; - box-shadow: var(--dropdown-shadow); - z-index: 9999; - - &__text-button { - display: inline-flex; - align-items: center; - color: inherit; - background: transparent; - border: 0; - margin: 0; - padding: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; - - &:focus-visible { - outline: 1px dotted; - } - - &:hover { - text-decoration: underline; - } - - .icon { - width: 15px; - height: 15px; - } - } - - &__container { - &__header { - border-bottom: 1px solid var(--color-border-primary); - padding: 10px 14px; - padding-bottom: 14px; - margin-bottom: 4px; - font-size: 13px; - line-height: 18px; - color: var(--color-text-secondary); - } - - &__list { - list-style: none; - - &--scrollable { - max-height: 300px; - overflow-y: scroll; - } - } - - &--loading { - display: flex; - align-items: center; - justify-content: center; - padding: 30px 45px; - } - } - - &.left { - transform-origin: 100% 50%; - } - - &.top { - transform-origin: 50% 100%; - } - - &.bottom { - transform-origin: 50% 0; - } - - &.right { - transform-origin: 0 50%; - } -} - -.dropdown-menu__item { - font-size: 13px; - line-height: 18px; - font-weight: 500; - display: block; - - &--dangerous { - color: var(--color-text-error); - } - - &--highlighted { - color: var(--color-text-brand); - } - - &-content { - display: flex; - flex-direction: column; - } - - &-subtitle { - font-weight: 400; - } - - a, - button { - font: inherit; - display: flex; - align-items: center; - gap: 8px; - white-space: inherit; - width: 100%; - padding: 10px 14px; - border: 0; - margin: 0; - background: transparent; - box-sizing: border-box; - text-decoration: none; - color: inherit; - text-align: inherit; - border-radius: 4px; - - &:focus, - &:hover, - &:active { - &:not(:disabled, [aria-disabled='true']) { - background: var(--color-bg-secondary); - outline: 0; - } - } - } - - button:disabled, - button[aria-disabled='true'] { - color: var(--color-text-disabled); - cursor: default; - - &:focus { - color: rgb(from var(--color-text-disabled) r g b / 70%); - background: var(--color-bg-disabled); - outline: 0; - } - } -} - -.reblog-menu-item { - max-width: 360px; -} - -.inline-account { - display: inline-flex; - align-items: center; - vertical-align: top; - - .account__avatar { - margin-inline-end: 5px; - border-radius: 50%; - } - - strong { - font-weight: 600; - } -} - -.columns-area { - display: flex; - flex: 1 1 auto; - flex-direction: row; - justify-content: flex-start; - position: relative; - - .layout-multiple-columns & { - overflow-x: auto; - - &.unscrollable { - overflow-x: hidden; - } - } - - &__panels { - box-sizing: border-box; - display: flex; - justify-content: center; - gap: 16px; - width: 100%; - height: 100%; - min-height: 100vh; - min-height: 100dvh; - padding-inline: 10px; - padding-bottom: env(safe-area-inset-bottom); - - &__pane { - height: 100%; - overflow: hidden; - display: flex; - justify-content: flex-end; - min-width: 285px; - - &--start { - justify-content: flex-start; - } - - &__inner { - position: fixed; - width: 285px; - height: 100%; - } - } - - &__main { - --column-header-height: 62px; - - box-sizing: border-box; - width: 100%; - flex: 0 1 auto; - display: flex; - flex-direction: column; - contain: inline-size layout paint style; - container: column / inline-size; - color: var(--color-text-primary); - background-color: var(--color-bg-primary); - - @media screen and (min-width: $no-gap-breakpoint) { - max-width: 600px; - } - } - } -} - -.ui__navigation-bar { - position: fixed; - bottom: 0; - z-index: 3; - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - gap: 8px; - padding-bottom: env(safe-area-inset-bottom); - background: var(--color-bg-primary); - backdrop-filter: $backdrop-blur-filter; - border-top: 1px solid var(--color-border-primary); - box-sizing: border-box; - - .layout-multiple-columns & { - display: none; - } - - &__items { - display: grid; - grid-auto-columns: minmax(0, 1fr); - grid-auto-flow: column; - padding: 0 16px; - - &.active { - flex: 1; - padding: 0; - } - } - - &__sign-up { - display: flex; - align-items: center; - gap: 4px; - padding-inline-start: 16px; - } - - &__item { - display: flex; - flex-direction: column; - align-items: center; - background: transparent; - border: none; - gap: 8px; - font-size: 12px; - font-weight: 500; - line-height: 16px; - padding-top: 11px; - padding-bottom: 15px; - border-top: 4px solid transparent; - text-decoration: none; - color: inherit; - - &.active { - color: var(--color-text-brand); - } - - &:focus { - outline: 0; - } - - &:focus-visible { - border-top-color: var(--color-text-brand); - border-radius: 0; - } - } -} - -.tabs-bar__wrapper { - background: var(--color-bg-primary); - backdrop-filter: $backdrop-blur-filter; - position: sticky; - top: 0; - z-index: 2; - border-top: 0; - - @media screen and (min-width: $no-gap-breakpoint) { - border-top: 10px solid var(--color-bg-ambient); - } -} - -.react-swipeable-view-container { - &, - .columns-area, - .drawer, - .column { - height: 100%; - } -} - -.react-swipeable-view-container > * { - display: flex; - align-items: center; - justify-content: center; - height: 100%; -} - -.column { - width: clamp(380px, calc((100% - 350px) / 4), 400px); - position: relative; - box-sizing: border-box; - display: flex; - flex-direction: column; - - > .scrollable { - border: 1px solid var(--color-border-primary); - border-top: 0; - border-radius: 0 0 4px 4px; - - &.about, - &.privacy-policy { - border-top: 1px solid var(--color-border-primary); - border-radius: 4px; - - @media screen and (max-width: $no-gap-breakpoint) { - border-top: 0; - border-bottom: 0; - } - } - } -} - -.column__alert { - --alert-height: 54px; - - position: sticky; - bottom: 0; - z-index: 10; - box-sizing: border-box; - display: grid; - grid-template-rows: minmax(var(--alert-height), max-content); - align-items: end; - width: 100%; - max-width: 360px; - padding: 1rem; - margin: auto auto 0; - overflow: clip; - pointer-events: none; - - @media (max-width: #{$mobile-menu-breakpoint - 1}) { - // Compensate for mobile menubar - bottom: var(--mobile-bottom-nav-height); - } - - & > * { - // Make all nested alerts occupy the same space - // rather than stack - grid-area: 1 / 1; - pointer-events: initial; - } -} - -.ui { - --mobile-bottom-nav-height: 55px; - --last-content-item-border-width: 2px; - - flex: 0 0 auto; - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - - @media (max-width: #{$mobile-menu-breakpoint - 1}) { - padding-bottom: calc( - var(--mobile-bottom-nav-height) - var(--last-content-item-border-width) - ); - } -} - -.drawer { - width: 350px; - box-sizing: border-box; - display: flex; - flex-direction: column; - overflow-y: hidden; -} - -.drawer__tab { - display: flex; - flex: 1 1 auto; - padding: 13px 3px 11px; - color: var(--color-text-secondary); - text-decoration: none; - text-align: center; - font-size: 16px; - align-items: center; - justify-content: center; -} - -.column, -.drawer { - flex: 1 1 100%; -} - -@media screen and (width > $mobile-breakpoint) { - .columns-area { - padding: 0; - } - - .column, - .drawer { - flex: 0 0 auto; - padding: 10px; - padding-inline-start: 5px; - padding-inline-end: 5px; - - &:first-child { - padding-inline-start: 10px; - } - - &:last-child { - padding-inline-end: 10px; - } - } - - .columns-area > div { - .column, - .drawer { - padding-inline-start: 5px; - padding-inline-end: 5px; - } - } -} - -.columns-area--mobile { - flex-direction: column; - width: 100%; - height: 100%; - margin: 0 auto; - - .column, - .drawer { - width: 100%; - height: 100%; - padding: 0; - } - - .account-card { - margin-bottom: 0; - } - - .filter-form { - display: flex; - flex-wrap: wrap; - } - - .autosuggest-textarea__textarea { - font-size: 16px; - } - - .search__input { - line-height: 18px; - font-size: 16px; - padding-block: 15px; - padding-inline-end: 30px; - } - - .scrollable { - overflow: visible; - - @supports (display: grid) { - contain: content; - } - } - - @media screen and (min-width: $no-gap-breakpoint) { - padding: 10px 0; - padding-top: 0; - } -} - -@media screen and (min-width: $no-gap-breakpoint) { - .react-swipeable-view-container .columns-area--mobile { - height: calc(100% - 10px) !important; - } - - .getting-started__wrapper { - margin-bottom: 10px; - } - - .search-page .search { - display: none; - } - - .navigation-panel__legal, - .navigation-panel__compose-button, - .navigation-panel .navigation-bar { - display: none !important; - } -} - -@media screen and (max-width: ($no-gap-breakpoint - 1px)) { - $sidebar-width: 285px; - - .columns-area__panels__main { - width: calc(100% - $sidebar-width); - } - - .columns-area__panels { - min-height: 100vh; - min-height: 100dvh; - gap: 0; - padding-inline: 0; - } - - .columns-area__panels__pane--navigational { - min-width: $sidebar-width; - - .columns-area__panels__pane__inner { - width: $sidebar-width; - } - - .navigation-panel { - margin: 0; - border-inline-start: 1px solid var(--color-border-primary); - height: 100dvh; - } - - .navigation-panel__banner, - .navigation-panel__logo, - .getting-started__trends { - display: none; - } - } - - .layout-single-column { - .column > .scrollable, - .tabs-bar__wrapper .column-header, - .tabs-bar__wrapper .column-back-button, - .explore__search-header, - .column-search-header { - border-left: 0; - border-right: 0; - } - - .column-header, - .column-back-button, - .scrollable, - .error-column { - border-radius: 0 !important; - } - - .column-header, - .column-back-button { - border-top: 0; - } - } -} - -@media screen and (width <= 759px) { - .columns-area__panels__main { - width: 100%; - } - - .columns-area__panels__pane--navigational { - position: fixed; - inset-inline-end: 0; - width: 100%; - height: 100%; - pointer-events: none; - } - - .columns-area__panels__pane--navigational .columns-area__panels__pane__inner { - pointer-events: auto; - background: var(--color-bg-primary); - position: fixed; - width: 284px + 70px; - inset-inline-end: -70px; - touch-action: pan-y; - - .navigation-panel { - width: 284px; - overflow-y: auto; - scrollbar-width: thin; - - &__menu { - flex-shrink: 0; - min-height: none; - overflow: hidden; - padding-bottom: calc(65px + env(safe-area-inset-bottom)); - } - - &__logo { - display: none; - } - } - } -} - -.columns-area__panels__pane--navigational { - transition: background 500ms; -} - -.columns-area__panels__pane--overlay { - pointer-events: auto; - background: rgb(from var(--color-bg-overlay) r g b / 50%); - z-index: 3; - - .columns-area__panels__pane__inner { - box-shadow: var(--dropdown-shadow); - } -} - -@media screen and (width >= 760px) { - .ui__navigation-bar { - display: none; - } -} - -.explore-suggestions-card { - padding: 12px 16px; - gap: 8px; - display: flex; - flex-direction: column; - border-bottom: 1px solid var(--color-border-primary); - - &:last-child { - border-bottom: 0; - } - - &__source { - font-size: 13px; - line-height: 16px; - color: var(--color-text-tertiary); - - @container (width >= 400px) { - padding-inline-start: 60px; - } - } - - &__body { - display: flex; - gap: 12px; - align-items: center; - justify-content: end; - } - - &__avatar { - flex-shrink: 0; - - @container (width < 360px) { - width: 35px !important; - height: 35px !important; - } - } - - &__link { - flex: 1 1 auto; - display: flex; - gap: 12px; - align-items: center; - text-decoration: none; - min-width: 0; - - &:hover, - &:focus-visible { - .display-name__html { - text-decoration: underline; - } - } - - .display-name { - font-size: 15px; - line-height: 20px; - color: var(--color-text-primary); - - strong { - font-weight: 700; - } - - &__account { - color: var(--color-text-secondary); - display: block; - } - } - } - - &__actions { - display: flex; - align-items: center; - gap: 8px; - justify-content: end; - - .button { - min-width: 80px; - } - } - - &__dismiss-button { - @container (width < 400px) { - display: none; - } - } -} - -@media screen and (max-width: ($no-gap-breakpoint - 1px)) { - .columns-area__panels__pane--compositional { - display: none; - } -} - -.icon-with-badge { - display: inline-flex; - position: relative; - - &__badge { - position: absolute; - inset-inline-start: 9px; - top: -13px; - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - border: 2px solid var(--color-bg-primary); - padding: 1px 6px; - border-radius: 6px; - font-size: 10px; - font-weight: 500; - line-height: 14px; - } - - &__issue-badge { - position: absolute; - inset-inline-start: 11px; - bottom: 1px; - display: block; - background: var(--color-text-error); - border-radius: 50%; - width: 0.625rem; - height: 0.625rem; - } -} - -.column-link--transparent .icon-with-badge__badge { - border-color: var(--color-bg-primary); -} - -.column-title { - text-align: center; - padding-bottom: 32px; - - h3 { - font-size: 24px; - line-height: 1.5; - font-weight: 700; - margin-bottom: 10px; - } - - p { - font-size: 16px; - line-height: 24px; - font-weight: 400; - color: var(--color-text-secondary); - } - - @media screen and (width >= 600px) { - padding: 40px; - } -} - -.copy-paste-text { - background: var(--color-bg-secondary); - border-radius: 8px; - border: 1px solid var(--color-border-primary); - padding: 16px; - color: var(--color-text-primary); - font-size: 15px; - line-height: 22px; - display: flex; - flex-direction: column; - align-items: flex-end; - transition: border-color 300ms linear; - margin-bottom: 30px; - - &:focus, - &.focused { - transition: none; - outline: 0; - border-color: var(--color-text-brand); - } - - &.copied { - border-color: var(--color-text-success); - transition: none; - } - - textarea { - width: 100%; - height: auto; - background: transparent; - color: inherit; - font: inherit; - border: 0; - padding: 0; - margin-bottom: 30px; - resize: none; - - &:focus { - outline: 0; - } - } -} - -.onboarding__profile { - position: relative; - margin-bottom: 40px + 20px; - margin-top: -20px; - - .app-form__avatar-input { - border: 2px solid var(--color-bg-primary); - position: absolute; - inset-inline-start: -2px; - bottom: -40px; - z-index: 2; - } - - .app-form__header-input { - margin: 0 -20px; - border-radius: 0; - - img { - border-radius: 0; - } - } -} - -.compose-panel { - width: 285px; - margin-top: 10px; - display: flex; - flex-direction: column; - height: calc(100% - 10px); - overflow-y: auto; - scrollbar-width: thin; - - .compose-form { - flex: 1 1 auto; - } -} - -.navigation-panel { - margin-top: 10px; - margin-bottom: 10px; - height: calc(100% - 20px); - overflow: hidden; - display: flex; - flex-direction: column; - - &__menu { - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; - scrollbar-width: thin; - } - - &__list-panel { - &__header { - display: flex; - align-items: center; - padding-inline-end: 4px; - - &__sep { - width: 0; - height: 24px; - border-left: 1px solid var(--color-border-primary); - } - - .column-link { - flex: 1 1 auto; - } - - .icon-button { - padding: 8px; - } - } - - &__items { - padding-inline-start: 24px + 8px; - - .icon { - display: none; - } - } - } - - &__compose-button { - display: flex; - justify-content: flex-start; - padding-top: 8px; - padding-bottom: 8px; - padding-inline-start: 12px - 7px; - padding-inline-end: 12px; - gap: 8px; - margin: 12px; - border-radius: 6px; - font-size: 16px; - font-weight: 600; - line-height: 18px; - - .icon { - width: 24px; - height: 24px; - } - } - - .navigation-bar { - padding: 16px; - } - - .search { - margin: 16px; - margin-bottom: 12px; - } - - .logo { - height: 30px; - width: auto; - } - - &__logo { - margin-bottom: 12px; - } - - .getting-started__trends h4 { - padding: 10px 12px; - padding-inline-start: 16px; - } - - .getting-started__trends .trends__item { - padding: 10px 12px; - padding-inline-start: 16px; - } - - @media screen and (height <= 930px) { - &__portal .trends__item:nth-child(n + 5) { - display: none; - } - } - - @media screen and (height <= (930px - 56px)) { - &__portal .trends__item:nth-child(n + 4) { - display: none; - } - } - - @media screen and (height <= (930px - 56px * 2)) { - &__portal .trends__item:nth-child(n + 3) { - display: none; - } - } - - @media screen and (height <= (930px - 56px * 3)) { - &__portal { - display: none; - } - } -} - -.navigation-panel, -.compose-panel { - hr { - flex: 0 0 auto; - border: 0; - background: transparent; - border-top: 1px solid var(--color-border-primary); - margin: 10px 0; - } - - .flex-spacer { - background: transparent; - } -} - -.drawer__pager { - box-sizing: border-box; - padding: 0; - flex-grow: 1; - position: relative; - overflow: hidden; - display: flex; - border-radius: 4px; - border: 1px solid var(--color-border-primary); -} - -.drawer__inner { - position: absolute; - top: 0; - inset-inline-start: 0; - background: var(--color-bg-primary); - box-sizing: border-box; - padding: 0; - display: flex; - flex-direction: column; - overflow: hidden; - overflow-y: auto; - width: 100%; - height: 100%; - z-index: 0; -} - -.drawer__inner__mastodon { - position: relative; - background: var(--color-bg-primary); - flex: 1; - min-height: 47px; - display: none; - contain: content; - - > img { - display: block; - object-fit: contain; - object-position: bottom left; - width: 85%; - height: 100%; - pointer-events: none; - user-select: none; - } - - @media screen and (height >= 640px) { - display: block; - } -} - -.drawer__header { - flex: 0 0 auto; - font-size: 16px; - border: 1px solid var(--color-border-primary); - margin-bottom: 10px; - display: flex; - flex-direction: row; - border-radius: 4px; - overflow: hidden; - - a:hover, - a:focus, - a:active { - color: var(--color-text-primary); - } -} - -.scrollable { - overflow-y: scroll; - overflow-x: hidden; - flex: 1 1 auto; - -webkit-overflow-scrolling: touch; - scrollbar-width: thin; - - &.optionally-scrollable { - overflow-y: auto; - } - - @supports (display: grid) { - // hack to fix Chrome <57 - contain: strict; - } - - &--flex { - display: flex; - flex-direction: column; - } - - &__append { - flex: 1 1 auto; - position: relative; - min-height: 120px; - } - - .scrollable { - flex: 1 1 auto; - } -} - -.scrollable.fullscreen { - @supports (display: grid) { - // hack to fix Chrome <57 - contain: none; - } -} - -.column-back-button { - box-sizing: border-box; - width: 100%; - background: transparent; - border: 1px solid var(--color-border-primary); - border-radius: 4px 4px 0 0; - color: var(--color-text-brand); - cursor: pointer; - flex: 0 0 auto; - font-size: 16px; - line-height: inherit; - text-align: unset; - padding: 13px; - margin: 0; - z-index: 3; - outline: 0; - display: flex; - align-items: center; - gap: 5px; - - &:hover { - text-decoration: underline; - } -} - -.column-header__back-button { - display: flex; - align-items: center; - gap: 5px; - background: transparent; - border: 0; - font-family: inherit; - color: var(--color-text-brand); - cursor: pointer; - white-space: nowrap; - font-size: 16px; - padding: 13px; - z-index: 3; - - &:hover { - text-decoration: underline; - } - - &.compact { - padding-inline-end: 5px; - flex: 0 0 auto; - } -} - -.react-toggle { - display: inline-block; - position: relative; - cursor: pointer; - background-color: transparent; - border: 0; - border-radius: 10px; - padding: 0; - user-select: none; - -webkit-tap-highlight-color: transparent; -} - -.react-toggle--focus, -.react-toggle:focus-within { - outline: var(--outline-focus-default); - outline-offset: 2px; - - .react-toggle-track { - border-color: transparent; - } -} - -.react-toggle-screenreader-only, -.sr-only { - border: 0; - clip-path: inset(50%); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} - -.react-toggle--disabled { - cursor: not-allowed; - opacity: 0.5; - transition: opacity 0.25s; -} - -.react-toggle-track { - width: 32px; - height: 20px; - padding: 0; - border-radius: 10px; - background-color: rgb(from var(--color-bg-brand-softer) r g b / 50%); - border: 1px solid rgb(from var(--color-text-brand) r g b / 50%); - box-sizing: border-box; - - .react-toggle:hover:not(.react-toggle--disabled) & { - background-color: rgb( - from var(--color-bg-brand-softer) r g b / - calc(50% + var(--overlay-strength-brand)) - ); - } - - .react-toggle--checked & { - background-color: var(--color-bg-brand-base); - border-color: var(--color-bg-brand-base); - } - - .react-toggle--checked:not(.react-toggle--disabled):hover & { - background-color: var(--color-bg-brand-base-hover); - } -} - -.react-toggle-track-check, -.react-toggle-track-x { - display: none; -} - -.react-toggle-thumb { - position: absolute; - top: 2px; - inset-inline-start: 2px; - width: 16px; - height: 16px; - border-radius: 50%; - background-color: var(--color-text-on-brand-base); - box-sizing: border-box; - transition: all 0.25s ease; - transition-property: border-color, left; - - .react-toggle--checked & { - inset-inline-start: 32px - 16px - 2px; - border-color: var(--color-bg-brand-base); - } -} - -.follow_requests-unlocked_explanation, -.switch-to-advanced { - color: var(--color-text-secondary); - background-color: var(--color-bg-secondary); - padding: 15px; - border-radius: 4px; - margin-top: 4px; - margin-bottom: 12px; - font-size: 13px; - line-height: 18px; - - a { - color: var(--color-text-brand); - font-weight: bold; - } -} - -.column-link { - display: flex; - align-items: center; - gap: 8px; - width: 100%; - padding: 12px; - font-size: 16px; - font-weight: 400; - text-decoration: none; - overflow: hidden; - white-space: nowrap; - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - background: transparent; - border: 0; - border-left: 4px solid transparent; - box-sizing: border-box; - - &:hover, - &:focus, - &:active { - color: var(--color-text-primary); - } - - &.active { - color: var(--color-text-brand); - } - - &:focus { - outline: 0; - } - - &:focus-visible { - border-color: var(--color-text-brand); - border-radius: 0; - } - - &--logo { - background: transparent; - padding: 10px; - - &:hover, - &:focus, - &:active { - background: transparent; - } - } -} - -.column-link__badge { - display: inline-block; - border-radius: 4px; - font-size: 12px; - line-height: 19px; - font-weight: 500; - background: var(--color-bg-primary); - padding: 4px 8px; - margin: -6px 10px; -} - -.column-subheading { - background: var(--color-bg-secondary); - color: var(--color-text-secondary); - padding: 8px 20px; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - cursor: default; -} - -.getting-started__wrapper { - flex: 0 0 auto; -} - -.flex-spacer { - flex: 1 1 auto; -} - -.getting-started { - color: var(--color-text-tertiary); - overflow: auto; - border: 1px solid var(--color-border-primary); - border-top: 0; - - &__trends { - flex: 0 1 auto; - opacity: 1; - animation: fade 150ms linear; - margin-top: 10px; - - h4 { - border-bottom: 1px solid var(--color-border-primary); - padding: 10px; - font-size: 12px; - text-transform: uppercase; - font-weight: 500; - - a { - color: var(--color-text-secondary); - text-decoration: none; - } - } - - .trends__item { - border-bottom: 0; - padding: 10px; - - &__current { - color: var(--color-text-secondary); - } - } - } -} - -.keyboard-shortcuts { - padding: 8px 0 0; - overflow: hidden; - - thead { - position: absolute; - inset-inline-start: -9999px; - } - - td { - padding: 0 10px 8px; - } - - kbd { - display: inline-block; - } -} - -.status-card { - display: flex; - align-items: center; - position: relative; - font-size: 14px; - color: var(--color-text-secondary); - margin-top: 14px; - text-decoration: none; - overflow: hidden; - border: 1px solid var(--color-border-primary); - border-radius: 8px; - contain: inline-size layout paint style; - - &.bottomless { - border-radius: 8px 8px 0 0; - } - - &__actions { - bottom: 0; - inset-inline-start: 0; - position: absolute; - inset-inline-end: 0; - top: 0; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - - & > div { - background: rgb(from var(--color-shadow-primary) r g b / 60%); - border-radius: 8px; - padding: 12px 9px; - backdrop-filter: $backdrop-blur-filter; - flex: 0 0 auto; - display: flex; - justify-content: center; - align-items: center; - } - - button, - a { - display: inline; - color: var(--color-text-primary); - background: transparent; - border: 0; - padding: 0 8px; - text-decoration: none; - font-size: 18px; - line-height: 18px; - - &:hover, - &:active, - &:focus { - color: var(--color-text-primary); - } - } - - a { - font-size: 19px; - position: relative; - bottom: -1px; - } - } -} - -a.status-card { - cursor: pointer; - - &:hover, - &:focus, - &:active { - .status-card__title, - .status-card__host, - .status-card__author, - .status-card__description { - color: var(--color-text-brand); - } - } -} - -.status-card a { - color: inherit; - text-decoration: none; - - &:hover, - &:focus, - &:active { - .status-card__title, - .status-card__host, - .status-card__author, - .status-card__description { - color: var(--color-text-brand); - } - } -} - -.status-card-photo { - cursor: zoom-in; - display: block; - text-decoration: none; - width: 100%; - height: auto; - margin: 0; -} - -.status-card-video { - // Firefox has a bug where frameborder=0 iframes add some extra blank space - // see https://bugzilla.mozilla.org/show_bug.cgi?id=155174 - overflow: hidden; - - iframe { - width: 100%; - height: 100%; - } -} - -.status-card__title { - display: block; - font-weight: 700; - font-size: 19px; - line-height: 24px; - color: var(--color-text-primary); - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.status-card.expanded .status-card__title { - white-space: normal; - display: -webkit-box; - line-clamp: 2; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -} - -.status-card__content { - flex: 1 1 auto; - overflow: hidden; - padding: 15px; - box-sizing: border-box; - max-width: 100%; -} - -.status-card__host { - display: block; - font-size: 14px; - margin-bottom: 8px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.status-card__author { - display: block; - margin-top: 8px; - font-size: 14px; - color: var(--color-text-primary); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - strong { - font-weight: 500; - } -} - -.status-card__description { - display: block; - margin-top: 8px; - font-size: 14px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.status-card__image { - flex: 0 0 auto; - width: 120px; - aspect-ratio: 1; - background: var(--color-bg-secondary); - position: relative; - - & > .icon { - width: 18px; - height: 18px; - position: absolute; - transform-origin: 50% 50%; - top: 50%; - inset-inline-start: 50%; - transform: translate(-50%, -50%); - } -} - -.status-card__image-image { - display: block; - margin: 0; - width: 100%; - height: 100%; - object-fit: cover; - background-size: cover; - background-position: center center; -} - -.status-card__image-preview { - display: block; - margin: 0; - width: 100%; - height: 100%; - object-fit: fill; - position: absolute; - top: 0; - inset-inline-start: 0; - z-index: 0; - background: var(--color-bg-primary); - - &--hidden { - display: none; - } -} - -.status-card.expanded { - flex-direction: column; - align-items: flex-start; -} - -.status-card.expanded .status-card__image { - width: 100%; - aspect-ratio: auto; -} - -.status-card__image, -.status-card__image-image, -.status-card__image-preview { - border-start-start-radius: 8px; - border-start-end-radius: 0; - border-end-end-radius: 0; - border-end-start-radius: 8px; -} - -.status-card.expanded .status-card__image, -.status-card.expanded .status-card__image-image, -.status-card.expanded .status-card__image-preview { - border-start-end-radius: 8px; - border-end-end-radius: 0; - border-end-start-radius: 0; -} - -.status-card.bottomless .status-card__image, -.status-card.bottomless .status-card__image-image, -.status-card.bottomless .status-card__image-preview { - border-end-end-radius: 0; - border-end-start-radius: 0; -} - -.status-card.expanded > a { - width: 100%; -} - -.load-more { - display: flex; - align-items: center; - justify-content: center; - color: var(--color-text-primary); - background-color: transparent; - border: 0; - font-size: inherit; - line-height: inherit; - width: 100%; - padding: 15px; - box-sizing: border-box; - text-decoration: none; - - &--large { - padding-block: 32px; - } - - &:is(button) { - &:hover { - background: var(--color-bg-secondary); - } - - &:focus-visible { - outline: 2px solid var(--color-text-brand); - outline-offset: -2px; - } - } - - .icon { - width: 22px; - height: 22px; - } -} - -.load-gap { - border-bottom: 1px solid var(--color-border-primary); -} - -.timeline-hint { - text-align: center; - color: var(--color-text-secondary); - padding: 16px; - box-sizing: border-box; - width: 100%; - font-size: 14px; - line-height: 21px; - - strong { - font-weight: 500; - } - - a { - color: var(--color-text-brand); - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - color: var(--color-text-brand-soft); - } - } - - &--with-descendants { - border-top: 1px solid var(--color-border-primary); - } -} - -.regeneration-indicator { - color: var(--color-text-secondary); - border: 1px solid var(--color-border-primary); - border-top: 0; - cursor: default; - display: flex; - flex: 1 1 auto; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 20px; - - &__figure { - display: block; - width: 100%; - height: auto; - max-width: 350px; - margin-top: -50px; - } - - &__label { - text-align: center; - font-size: 16px; - - strong { - font-weight: 500; - display: block; - margin-bottom: 10px; - color: var(--color-text-secondary); - } - - span { - font-size: 15px; - } - } -} - -.column-header__wrapper { - position: relative; - flex: 0 0 auto; - z-index: 1; - - &.active { - box-shadow: 0 1px 0 var(--color-bg-brand-softer); - - &::before { - display: block; - content: ''; - position: absolute; - bottom: -13px; - inset-inline-start: 0; - inset-inline-end: 0; - margin: 0 auto; - width: 60%; - pointer-events: none; - height: 28px; - z-index: 1; - background: radial-gradient( - ellipse, - rgb(from var(--color-bg-brand-base) r g b / 23%) 0%, - transparent 60% - ); - } - } - - .announcements { - z-index: 1; - position: relative; - } -} - -.column-header__select-row { - border-width: 0 1px 1px; - border-style: solid; - border-color: var(--color-border-primary); - padding: 15px; - display: flex; - align-items: center; - gap: 8px; - - &__checkbox .check-box { - display: flex; - } - - &__select-menu:disabled { - visibility: hidden; - } - - &__mode-button { - margin-left: auto; - color: var(--color-text-brand); - font-weight: bold; - font-size: 14px; - - &:hover { - color: var(--color-text-brand-soft); - } - } -} - -.column-header { - display: flex; - font-size: 16px; - border: 1px solid var(--color-border-primary); - border-radius: 4px 4px 0 0; - flex: 0 0 auto; - cursor: pointer; - position: relative; - z-index: 2; - outline: 0; - - &__title { - display: flex; - align-items: center; - gap: 5px; - margin: 0; - border: 0; - padding: 13px; - padding-inline-end: 0; - color: inherit; - background: transparent; - font: inherit; - text-align: start; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - flex: 1; - - &:focus-visible { - outline: var(--outline-focus-default); - } - - .logo { - height: 24px; - } - } - - .column-header__back-button + &__title { - padding-inline-start: 0; - } - - .column-header__back-button { - flex: 1; - color: var(--color-text-brand); - - &.compact { - flex: 0 0 auto; - color: var(--color-text-primary); - } - } - - &.active { - .column-header__icon { - color: var(--color-text-brand); - text-shadow: 0 0 10px var(--color-bg-brand-softer); - } - } - - &:focus, - &:active { - outline: 0; - } - - &__advanced-buttons { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px; - padding-top: 0; - - &:first-child { - padding-top: 16px; - } - } -} - -.column-header__buttons { - height: 48px; - display: flex; -} - -.column-header__links { - margin-bottom: 14px; -} - -.column-header__links .text-btn { - margin-inline-end: 10px; -} - -.column-header__button { - display: flex; - justify-content: center; - align-items: center; - border: 0; - color: var(--color-text-primary); - background: transparent; - cursor: pointer; - font-size: 16px; - padding: 0 15px; - - &:last-child { - border-start-end-radius: 4px; - } - - &:hover { - color: var(--color-text-secondary); - } - - &:focus-visible { - outline: var(--outline-focus-default); - } - - &.active { - color: var(--color-text-brand); - - &:hover { - color: var(--color-text-brand); - } - } - - &:disabled { - color: var(--color-text-disabled); - cursor: default; - } -} - -.no-reduce-motion .column-header__button .icon-sliders { - transition: transform 150ms ease-in-out; -} - -.column-header__collapsible { - max-height: 70vh; - overflow: hidden; - overflow-y: auto; - color: var(--color-text-secondary); - transition: - max-height 150ms ease-in-out, - opacity 300ms linear; - opacity: 1; - z-index: 1; - position: relative; - border-left: 1px solid var(--color-border-primary); - border-right: 1px solid var(--color-border-primary); - border-bottom: 1px solid var(--color-border-primary); - - @media screen and (max-width: $no-gap-breakpoint) { - border-left: 0; - border-right: 0; - } - - &.collapsed { - max-height: 0; - opacity: 0.5; - border-bottom: 0; - } - - &.animating { - overflow-y: hidden; - } - - hr { - height: 0; - background: transparent; - border: 0; - border-top: 1px solid var(--color-border-primary); - margin: 10px 0; - } -} - -.column-header__collapsible-inner { - border-top: 0; -} - -.column-header__setting-btn { - &:hover, - &:focus { - color: var(--color-text-secondary); - text-decoration: underline; - } -} - -.column-header__collapsible__extra + .column-header__setting-btn { - padding-top: 5px; -} - -.column-header__permission-btn { - display: inline; - font-weight: inherit; - text-decoration: underline; -} - -.column-header__setting-arrows { - display: flex; - align-items: center; -} - -.text-btn { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 0; - font-family: inherit; - font-size: inherit; - font-weight: inherit; - color: inherit; - border: 0; - background: transparent; - cursor: pointer; - text-decoration: none; - - .icon { - width: 13px; - height: 13px; - } -} - -.column-header__issue-btn { - color: var(--color-text-error); - - &:hover { - text-decoration: underline; - } -} - -.loading-indicator { - color: var(--color-text-secondary); - font-size: 12px; - font-weight: 400; - text-transform: uppercase; - overflow: visible; - position: absolute; - top: 50%; - inset-inline-start: 50%; - transform: translate(-50%, -50%); - display: flex; - align-items: center; - justify-content: center; -} - -.load-more .loading-indicator, -.button .loading-indicator, -.icon-button .loading-indicator { - position: static; - transform: none; - color: inherit; - - .circular-progress { - color: inherit; - width: 22px; - height: 22px; - } -} - -.button--compact .loading-indicator .circular-progress { - width: 17px; - height: 17px; -} - -.icon-button .loading-indicator .circular-progress { - color: var(--color-text-tertiary); - width: 12px; - height: 12px; - margin: 6px; -} - -.load-more .loading-indicator .circular-progress { - color: var(--color-text-tertiary); -} - -.circular-progress { - color: var(--color-text-tertiary); - animation: 1.4s linear 0s infinite normal none running simple-rotate; - - circle { - stroke: currentColor; - stroke-dasharray: 80px, 200px; - stroke-dashoffset: 0; - animation: circular-progress 1.4s ease-in-out infinite; - } -} - -@keyframes circular-progress { - 0% { - stroke-dasharray: 1px, 200px; - stroke-dashoffset: 0; - } - - 50% { - stroke-dasharray: 100px, 200px; - stroke-dashoffset: -15px; - } - - 100% { - stroke-dasharray: 100px, 200px; - stroke-dashoffset: -125px; - } -} - -@keyframes simple-rotate { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(360deg); - } -} - -@keyframes spring-rotate-in { - 0% { - transform: rotate(0deg); - } - - 30% { - transform: rotate(-484.8deg); - } - - 60% { - transform: rotate(-316.7deg); - } - - 90% { - transform: rotate(-375deg); - } - - 100% { - transform: rotate(-360deg); - } -} - -@keyframes spring-rotate-out { - 0% { - transform: rotate(-360deg); - } - - 30% { - transform: rotate(124.8deg); - } - - 60% { - transform: rotate(-43.27deg); - } - - 90% { - transform: rotate(15deg); - } - - 100% { - transform: rotate(0deg); - } -} - -.video-error-cover { - align-items: center; - background: var(--color-bg-primary); - color: var(--color-text-primary); - cursor: pointer; - display: flex; - flex-direction: column; - height: 100%; - justify-content: center; - margin-top: 8px; - position: relative; - text-align: center; - z-index: 100; -} - -.spoiler-button { - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100%; - position: absolute; - z-index: 100; - - &--hidden { - display: none; - } - - &--click-thru { - pointer-events: none; - } - - &__overlay { - display: flex; - align-items: center; - justify-content: center; - background: transparent; - width: 100%; - height: 100%; - padding: 0; - margin: 0; - border: 0; - color: var(--color-text-on-media); - line-height: 20px; - font-size: 14px; - - &__label { - background-color: var(--color-bg-media); - backdrop-filter: $backdrop-blur-filter; - border-radius: 8px; - padding: 12px 16px; - display: flex; - align-items: center; - justify-content: center; - gap: 4px; - flex-direction: column; - font-weight: 600; - } - - &__action { - font-weight: 400; - font-size: 13px; - } - - &:hover, - &:focus { - .spoiler-button__overlay__label { - background-color: rgb(from var(--color-bg-media-base) r g b / 90%); - } - } - } -} - -.account--panel { - border-top: 1px solid var(--color-border-primary); - border-bottom: 1px solid var(--color-border-primary); - display: flex; - flex-direction: row; - padding: 10px 0; -} - -.account--panel__button, -.detailed-status__button { - flex: 1 1 auto; - text-align: center; -} - -.column-settings { - display: flex; - flex-direction: column; - - &__section { - // FIXME: Legacy - color: var(--color-text-secondary); - cursor: default; - display: block; - font-weight: 500; - } - - .column-header__links { - margin: 0; - } - - section { - padding: 16px; - border-bottom: 1px solid var(--color-border-primary); - - &:last-child { - border-bottom: 0; - } - } - - h3 { - font-size: 16px; - line-height: 24px; - letter-spacing: 0.5px; - font-weight: 500; - color: var(--color-text-primary); - margin-bottom: 16px; - } - - &__row { - display: flex; - flex-direction: column; - gap: 12px; - } - - .app-form__toggle { - &__toggle > div { - border: 0; - } - } -} - -.column-settings__hashtags { - margin-top: 15px; - - .column-settings__row { - margin-bottom: 15px; - } - - .column-select { - &__control { - @include search-input; - - &::placeholder { - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - } - - &::-moz-focus-inner { - border: 0; - } - - &::-moz-focus-inner, - &:focus, - &:active { - outline: 0 !important; - } - - &:focus { - background: var(--color-bg-secondary); - } - - @media screen and (width <= 600px) { - font-size: 16px; - } - } - - &__placeholder { - color: var(--color-text-tertiary); - padding-inline-start: 2px; - font-size: 12px; - } - - &__value-container { - padding-inline-start: 6px; - } - - &__multi-value { - background: var(--color-bg-secondary); - - &__remove { - cursor: pointer; - - &:hover, - &:active, - &:focus { - background: var(--color-bg-brand-softer); - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - } - } - } - - &__multi-value__label, - &__input, - &__input-container { - color: var(--color-text-secondary); - } - - &__clear-indicator, - &__dropdown-indicator { - cursor: pointer; - transition: none; - color: var(--color-text-tertiary); - - &:hover, - &:active, - &:focus { - color: var(--color-text-secondary); - } - } - - &__indicator-separator { - background-color: var(--color-border-primary); - } - - &__menu { - @include search-popout; - - padding: 0; - background: var(--color-bg-elevated); - } - - &__menu-list { - padding: 6px; - } - - &__option { - color: var(--color-text-primary); - border-radius: 4px; - font-size: 14px; - - &--is-focused, - &--is-selected { - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - } - } - } -} - -.column-settings__row { - .text-btn:not(.column-header__permission-btn) { - margin-bottom: 15px; - } -} - -.setting-toggle { - display: flex; - align-items: center; - gap: 8px; -} - -.setting-toggle__label { - color: var(--color-text-secondary); -} - -.limited-account-hint { - p { - color: var(--color-text-primary); - font-size: 15px; - font-weight: 500; - margin-bottom: 20px; - } -} - -.empty-column-indicator { - color: var(--color-text-secondary); - text-align: center; - padding: 20px; - font-size: 14px; - line-height: 20px; - font-weight: 400; - cursor: default; - display: flex; - flex: 1 1 auto; - align-items: center; - justify-content: center; - - & > span { - max-width: 500px; - } - - a { - color: var(--color-text-brand); - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } -} - -.empty-column-indicator { - &__arrow { - position: absolute; - top: 50%; - inset-inline-start: 50%; - pointer-events: none; - transform: translate(100%, -100%) rotate(12deg); - transform-origin: center; - } -} - -.follow_requests-unlocked_explanation { - margin: 16px; - margin-bottom: 0; -} - -.error-column { - padding: 20px; - border: 1px solid var(--color-border-primary); - border-radius: 4px; - display: flex; - flex: 1 1 auto; - align-items: center; - justify-content: center; - flex-direction: column; - cursor: default; - - &__image { - width: 70%; - max-width: 350px; - margin-top: -50px; - } - - &__message { - text-align: center; - color: var(--color-text-secondary); - font-size: 15px; - line-height: 22px; - - h1 { - font-size: 28px; - line-height: 33px; - font-weight: 700; - margin-bottom: 15px; - color: var(--color-text-primary); - } - - p { - max-width: 48ch; - } - - &__actions { - margin-top: 30px; - display: flex; - gap: 10px; - align-items: center; - justify-content: center; - } - } -} - -@keyframes heartbeat { - 0% { - transform: scale(1); - animation-timing-function: ease-out; - } - - 10% { - transform: scale(0.91); - animation-timing-function: ease-in; - } - - 17% { - transform: scale(0.98); - animation-timing-function: ease-out; - } - - 33% { - transform: scale(0.87); - animation-timing-function: ease-in; - } - - 45% { - transform: scale(1); - animation-timing-function: ease-out; - } -} - -.no-reduce-motion .pulse-loading { - transform-origin: center center; - animation: heartbeat 1.5s ease-in-out infinite both; -} - -.emoji-picker-dropdown__menu { - position: relative; - margin-top: 5px; - z-index: 2; - background: var(--color-bg-elevated); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - box-shadow: var(--dropdown-shadow); - border-radius: 5px; - - .emoji-mart-scroll { - transition: opacity 200ms ease; - } - - &.selecting .emoji-mart-scroll { - opacity: 0.5; - } -} - -.emoji-picker-dropdown__modifiers { - position: absolute; - top: 60px; - inset-inline-end: 11px; - cursor: pointer; -} - -.emoji-picker-dropdown__modifiers__menu { - position: absolute; - z-index: 4; - top: -5px; - inset-inline-start: -9px; - background: var(--color-bg-elevated); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - box-shadow: var(--dropdown-shadow); - overflow: hidden; - - button { - display: block; - cursor: pointer; - border: 0; - padding: 4px 8px; - background: transparent; - - &:hover, - &:focus, - &:active { - background: var(--color-border-primary); - } - } - - .emoji-mart-emoji { - height: 22px; - } -} - -.emoji-mart-emoji { - span { - background-repeat: no-repeat; - } -} - -.upload-area { - align-items: center; - background: rgb(from var(--color-bg-overlay) r g b / 80%); - display: flex; - height: 100vh; - justify-content: center; - inset-inline-start: 0; - opacity: 0; - position: fixed; - top: 0; - visibility: hidden; - width: 100vw; - z-index: 2000; - - * { - pointer-events: none; - } -} - -.upload-area__drop { - width: 320px; - height: 160px; - display: flex; - box-sizing: border-box; - position: relative; - padding: 8px; -} - -.upload-area__background { - position: absolute; - top: 0; - inset-inline-end: 0; - bottom: 0; - inset-inline-start: 0; - z-index: -1; - border-radius: 4px; - background: var(--color-bg-elevated); - box-shadow: 0 0 5px var(--color-shadow-primary); -} - -.upload-area__content { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - text-align: center; - color: var(--color-text-primary); - font-size: 18px; - font-weight: 500; - border: 2px dashed var(--color-border-primary); - border-radius: 4px; -} - -.upload-progress { - color: var(--color-text-secondary); - overflow: hidden; - display: flex; - gap: 8px; - align-items: center; - padding: 0 12px; - - .icon { - width: 24px; - height: 24px; - color: var(--color-text-brand); - } - - span { - font-size: 12px; - text-transform: uppercase; - font-weight: 500; - display: block; - } -} - -.upload-progress__message { - flex: 1 1 auto; -} - -.upload-progress__backdrop { - width: 100%; - height: 6px; - border-radius: 6px; - background: var(--color-bg-primary); - position: relative; - margin-top: 5px; -} - -.upload-progress__tracker { - position: absolute; - inset-inline-start: 0; - top: 0; - height: 6px; - background: var(--color-text-brand); - border-radius: 6px; -} - -.emoji-button { - display: block; - padding-top: 5px; - padding-bottom: 2px; - padding-inline-start: 2px; - padding-inline-end: 5px; - outline: 0; - cursor: pointer; - - img { - filter: grayscale(100%); - opacity: 0.8; - display: block; - margin: 0; - width: 22px; - height: 22px; - } - - &:hover, - &:active, - &:focus { - img { - opacity: 1; - filter: none; - border-radius: 100%; - } - } - - &:focus-visible { - img { - outline: var(--outline-focus-default); - } - } -} - -.dropdown--active .emoji-button img { - opacity: 1; - filter: none; -} - -.privacy-dropdown__dropdown, -.language-dropdown__dropdown, -.visibility-dropdown__dropdown { - box-shadow: var(--dropdown-shadow); - background: var(--color-bg-elevated); - border: 1px solid var(--color-border-primary); - padding: 4px; - border-radius: 4px; - overflow: hidden; - z-index: 2; - - &.top { - transform-origin: 50% 100%; - } - - &.bottom { - transform-origin: 50% 0; - } -} - -.modal-root__container .privacy-dropdown { - flex-grow: 0; -} - -.modal-root__container .privacy-dropdown__dropdown { - pointer-events: auto; - z-index: 9999; -} - -.privacy-dropdown__option, -.visibility-dropdown__option { - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - padding: 8px 12px; - cursor: pointer; - display: flex; - align-items: center; - gap: 12px; - border-radius: 4px; - color: var(--color-text-primary); - - // Make sure adjacent hover/active states don't have a meeting radius. - &:hover + &:is(:focus, .active), - &:is(:focus, .active) + &:hover, - &:is(:focus, .active) + &:is(:focus, .active) { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - - &:hover:has(+ :focus, .active), - &:is(:focus, .active):has(+ :hover), - &:is(:focus, .active):has(+ :is(:focus, .active)) { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - - &:hover, - &:active { - background: var(--color-bg-secondary); - } - - &:focus, - &.active { - background: var(--color-bg-brand-base); - color: var(--color-text-on-brand-base); - outline: 0; - - .privacy-dropdown__option__content, - .privacy-dropdown__option__content strong, - .privacy-dropdown__option__additional, - .visibility-dropdown__option__content, - .visibility-dropdown__option__content strong, - .visibility-dropdown__option__additional { - color: var(--color-text-on-brand-base); - } - } - - &__additional { - display: flex; - align-items: center; - justify-content: center; - color: var(--color-text-secondary); - cursor: help; - } -} - -.privacy-dropdown__option__icon, -.visibility-dropdown__option__icon { - display: flex; - align-items: center; - justify-content: center; -} - -.privacy-dropdown__option__content, -.visibility-dropdown__option__content { - flex: 1 1 auto; - color: var(--color-text-secondary); - - strong { - color: var(--color-text-primary); - font-weight: 500; - display: block; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } -} - -.language-dropdown { - &__dropdown { - width: 300px; - padding: 0; - - .emoji-mart-search { - padding: 10px; - background: var(--color-bg-elevated); - - input { - padding: 8px 12px; - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - color: var(--color-text-secondary); - - @media screen and (width <= 600px) { - font-size: 16px; - line-height: 24px; - letter-spacing: 0.5px; - } - } - } - - .emoji-mart-search-icon { - inset-inline-end: 15px; - opacity: 1; - color: var(--color-text-primary); - - .icon { - width: 18px; - height: 18px; - } - - &:disabled { - color: var(--color-text-secondary); - } - } - - .emoji-mart-scroll { - padding: 0 10px 10px; - background: var(--color-bg-elevated); - } - - &__results { - &__item { - display: flex; - align-items: center; - gap: 0.5em; - cursor: pointer; - color: var(--color-text-primary); - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - font-weight: 500; - padding: 8px 12px; - border-radius: 4px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - - &__common-name { - color: var(--color-text-secondary); - font-weight: 400; - } - - &:active, - &:hover { - background: var(--color-bg-secondary); - } - - &:focus, - &.active { - background: var(--color-bg-brand-base); - color: var(--color-text-on-brand-base); - outline: 0; - - .language-dropdown__dropdown__results__item__common-name { - color: var(--color-text-on-brand-base); - } - } - } - } - } -} - -.visibility-modal { - &__quote-warning { - color: var(--color-text-primary); - background: var(--color-bg-warning-softer); - padding: 16px; - border-radius: 4px; - - h3 { - font-weight: 500; - margin-bottom: 4px; - } - - p { - font-size: 0.8em; - } - } -} - -.visibility-dropdown { - &__overlay[data-popper-placement] { - z-index: 9999; - } - - &.disabled { - opacity: 0.6; - cursor: default; - } - - &__label { - display: block; - font-weight: 500; - margin-bottom: 8px; - } - - &__button { - display: flex; - align-items: center; - color: var(--color-text-primary); - background: var(--color-bg-secondary-solid); - border: 1px solid var(--color-border-primary); - padding: 8px 12px; - width: 100%; - text-align: left; - border-radius: 4px; - font-size: 14px; - height: 40px; - - &:disabled { - cursor: default; - } - } - - &__icon { - margin-inline: auto -4px; - width: 18px; - height: 18px; - opacity: 0.5; - } - - &__helper { - margin-top: 4px; - font-size: 0.8em; - color: var(--color-text-tertiary); - } -} - -.search { - margin-bottom: 32px; - position: relative; - - .layout-multiple-columns & { - margin-bottom: 10px; - } - - &__popout { - box-sizing: border-box; - display: none; - position: absolute; - inset-inline-start: 0; - margin-top: -2px; - width: 100%; - background: var(--color-bg-elevated); - border: 1px solid var(--color-border-primary); - border-radius: 0 0 4px 4px; - box-shadow: var(--dropdown-shadow); - z-index: 99; - font-size: 13px; - padding: 15px 5px; - - h4 { - text-transform: uppercase; - color: var(--color-text-secondary); - font-weight: 500; - padding: 0 10px; - margin-bottom: 10px; - } - - .icon-button { - padding: 0; - color: var(--color-text-secondary); - } - - .icon { - width: 18px; - height: 18px; - } - - &__menu { - margin-bottom: 20px; - - &:last-child { - margin-bottom: 0; - } - - &__message { - color: var(--color-text-secondary); - padding: 0 10px; - } - - &__item { - display: block; - box-sizing: border-box; - width: 100%; - border: 0; - font: inherit; - background: transparent; - color: var(--color-text-secondary); - padding: 10px; - cursor: pointer; - border-radius: 4px; - text-align: start; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - - &--flex { - display: flex; - justify-content: space-between; - } - - .icon-button { - transition: none; - } - - &:hover, - &:focus, - &:active, - &.selected { - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - - .icon-button { - color: inherit; - } - } - - mark { - background: transparent; - font-weight: 700; - color: var(--color-text-primary); - } - - span { - overflow: inherit; - text-overflow: inherit; - } - } - } - } - - &.active { - .search__input { - border-radius: 4px 4px 0 0; - } - - .search__popout { - display: block; - } - } -} - -.search__input { - @include search-input; - - display: block; - padding: 12px 16px; - padding-inline-start: 16px + 15px + 8px; - line-height: normal; - - &::-moz-focus-inner { - border: 0; - } - - &::-moz-focus-inner, - &:focus, - &:active { - outline: 0 !important; - } -} - -.search__icon-wrapper { - position: absolute; - top: 14px; - display: grid; - margin-inline-start: 16px - 2px; - width: 20px; - height: 20px; - - .icon { - width: 100%; - height: 100%; - } - - &:not(.has-value) { - pointer-events: none; - } -} - -.search__icon { - grid-area: 1 / 1; - transition: all 100ms linear; - transition-property: transform, opacity; - color: var(--color-text-secondary); -} - -.search__icon.icon-search { - .has-value & { - pointer-events: none; - opacity: 0; - transform: rotate(90deg); - } -} - -.search__icon--clear-button { - background: transparent; - border: 0; - padding: 0; - width: 20px; - height: 20px; - border-radius: 100%; - - &::-moz-focus-inner { - border: 0; - } - - &::-moz-focus-inner, - &:focus { - outline: 0 !important; - } - - &:focus-visible { - box-shadow: 0 0 0 2px var(--color-text-brand); - } - - &[aria-hidden='true'] { - pointer-events: none; - opacity: 0; - transform: rotate(-90deg); - } -} - -.search-results__section { - border-bottom: 1px solid var(--color-border-primary); - - &:last-child { - border-bottom: 0; - } - - &__header { - border-bottom: 1px solid var(--color-border-primary); - background: var(--color-bg-tertiary); - padding: 15px; - font-weight: 500; - font-size: 14px; - color: var(--color-text-secondary); - display: flex; - justify-content: space-between; - - h3 { - display: flex; - align-items: center; - gap: 5px; - } - - button { - color: var(--color-text-brand); - padding: 0; - border: 0; - background: 0; - font: inherit; - - &:hover, - &:active, - &:focus { - text-decoration: underline; - } - } - } - - .account:last-child, - & > div:last-child .status { - border-bottom: 0; - } -} - -.search-results__info { - padding: 20px; - color: var(--color-text-secondary); - text-align: center; -} - -.modal-root { - position: relative; - z-index: 9998; -} - -.modal-root__overlay { - position: fixed; - top: 0; - inset-inline-start: 0; - inset-inline-end: 0; - bottom: 0; - opacity: 0.9; - background: var(--color-bg-overlay); - transition: background 0.5s; -} - -.modal-root__container { - position: fixed; - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100%; - max-width: 100vw; - max-height: 100vh; - box-sizing: border-box; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - align-content: space-around; - z-index: 9999; - pointer-events: none; - user-select: none; - overscroll-behavior: none; -} - -.modal-root__modal { - pointer-events: auto; - user-select: text; - display: flex; - max-width: 100vw; - - @media screen and (width <= $mobile-breakpoint) { - margin-top: auto; - } -} - -.video-modal .video-player { - max-height: 80vh; - max-width: 100vw; -} - -.audio-modal__container { - width: 50vw; -} - -.media-modal { - width: 100%; - height: 100%; - position: relative; - touch-action: pan-y; - - &__buttons { - position: absolute; - inset-inline-end: 8px; - top: 8px; - z-index: 100; - display: flex; - gap: 8px; - align-items: center; - - .icon-button { - --default-icon-color: var(--color-text-on-media); - --default-bg-color: transparent; - --hover-icon-color: var(--color-text-on-media); - --hover-bg-color: rgb( - from var(--color-text-on-media) r g b / - var(--overlay-strength-secondary) - ); - - padding: 8px; - - .icon { - width: 24px; - height: 24px; - filter: var(--overlay-icon-shadow); - } - } - } -} - -.media-modal__closer { - display: flex; - position: absolute; - top: 0; - inset-inline-start: 0; - inset-inline-end: 0; - bottom: 0; - - > div { - flex-shrink: 0; - overflow: auto; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - } -} - -.media-modal__navigation { - position: absolute; - top: 0; - inset-inline-start: 0; - inset-inline-end: 0; - bottom: 0; - pointer-events: none; - transition: opacity 0.3s linear; - will-change: opacity; - - * { - pointer-events: auto; - } - - &.media-modal__navigation--hidden { - opacity: 0; - - * { - pointer-events: none; - } - } -} - -.media-modal__nav { - background: transparent; - box-sizing: border-box; - border: 0; - color: var(--color-text-on-media); - cursor: pointer; - display: flex; - align-items: center; - font-size: 24px; - height: 20vmax; - margin: auto 0; - padding: 30px 5px; - position: absolute; - top: 0; - bottom: 0; - transform: scaleX(var(--text-x-direction)); - - .icon { - border-radius: 5px; - padding: 10px; - } - - &:hover .icon, - &:focus .icon, - &:active .icon { - background: rgb( - from var(--color-text-on-media) r g b / var(--overlay-strength-secondary) - ); - } -} - -.media-modal__nav--prev { - inset-inline-start: 0; -} - -.media-modal__nav--next { - inset-inline-end: 0; -} - -.media-modal__overlay { - max-width: 600px; - position: absolute; - inset-inline-start: 0; - inset-inline-end: 0; - bottom: 0; - margin: 0 auto; - - .picture-in-picture__footer { - border-radius: 0; - border: none; - background: transparent; - backdrop-filter: none; - padding: 16px; - - .icon-button { - --default-icon-color: var(--color-text-on-media); - --default-bg-color: transparent; - --hover-icon-color: var(--color-text-on-media); - --hover-bg-color: rgb( - from var(--color-text-on-media) r g b / var(--overlay-strength-brand) - ); - - .icon { - filter: var(--overlay-icon-shadow); - } - - &.active { - --default-icon-color: var(--color-text-brand); - --hover-icon-color: var(--color-text-brand); - --hover-bg-color: var(--color-bg-brand-soft); - } - - &.star-icon.active { - --default-icon-color: var(--color-text-favourite-highlight); - --hover-icon-color: var(--color-text-favourite-highlight); - --hover-bg-color: rgb( - from var(--color-text-favourite-highlight) r g b / - var(--overlay-strength-brand) - ); - } - - &.disabled { - --default-icon-color: var(--color-text-on-media); - --default-bg-color: transparent; - - cursor: default; - opacity: 0.4; - } - } - } -} - -.media-modal__pagination { - display: flex; - justify-content: center; - margin-bottom: 20px; -} - -.media-modal__page-dot { - flex: 0 0 auto; - background-color: rgb(from var(--color-text-on-media) r g b / 40%); - filter: var(--overlay-icon-shadow); - height: 6px; - width: 6px; - border-radius: 50%; - margin: 0 4px; - padding: 0; - border: 0; - font-size: 0; - transition: background-color 0.2s ease-in-out; - - &.active { - background-color: var(--color-text-on-media); - } - - &:focus { - opacity: 1; - outline: 2px solid var(--color-text-on-media); - outline-offset: 2px; - } -} - -.modal-placeholder { - width: 588px; - min-height: 478px; - flex-direction: column; - background: var(--color-bg-primary); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - border-radius: 16px; - - &__error { - padding: 24px; - display: flex; - align-items: center; - flex-direction: column; - - &__image { - width: 70%; - max-width: 350px; - } - - &__message { - text-align: center; - text-wrap: balance; - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - - &__actions { - margin-top: 24px; - display: flex; - gap: 10px; - align-items: center; - justify-content: center; - } - } - } -} - -.safety-action-modal { - width: 600px; - flex-direction: column; - - &__top, - &__bottom { - display: flex; - gap: 8px; - padding: 24px; - flex-direction: column; - background: var(--color-bg-primary); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - } - - &__top { - border-radius: 16px 16px 0 0; - border-bottom: 0; - gap: 16px; - } - - &__bottom { - border-radius: 0 0 16px 16px; - border-top: 0; - - @media screen and (max-width: $mobile-breakpoint) { - border-radius: 0; - border-bottom: 0; - padding-bottom: 32px; - } - } - - &__header { - display: flex; - gap: 16px; - align-items: center; - font-size: 14px; - line-height: 20px; - color: var(--color-text-secondary); - - &__icon { - border-radius: 64px; - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - display: flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - flex-shrink: 0; - - .icon { - width: 24px; - height: 24px; - } - } - - h1 { - font-size: 22px; - line-height: 28px; - color: var(--color-text-primary); - } - } - - &__confirmation { - font-size: 14px; - line-height: 20px; - color: var(--color-text-secondary); - - h1 { - font-size: 16px; - line-height: 24px; - color: var(--color-text-primary); - font-weight: 500; - - &:not(:only-child) { - margin-bottom: 8px; - } - } - - strong { - font-weight: 700; - color: var(--color-text-primary); - } - } - - &__status { - border: 1px solid var(--color-border-primary); - border-radius: 8px; - padding: 8px; - cursor: pointer; - - &__account { - display: flex; - align-items: center; - gap: 4px; - margin-bottom: 8px; - color: var(--color-text-tertiary); - - bdi { - color: inherit; - } - } - - &__content { - display: -webkit-box; - font-size: 15px; - line-height: 22px; - color: var(--color-text-tertiary); - line-clamp: 4; - -webkit-line-clamp: 4; - -webkit-box-orient: vertical; - max-height: 4 * 22px; - overflow: hidden; - - p, - a { - color: inherit; - } - } - - .reply-indicator__attachments { - margin-top: 0; - font-size: 15px; - line-height: 22px; - color: var(--color-text-tertiary); - } - } - - &__bullet-points { - display: flex; - flex-direction: column; - gap: 8px; - font-size: 16px; - line-height: 24px; - - & > div { - display: flex; - gap: 16px; - align-items: center; - - strong { - font-weight: 700; - } - } - - &--deemphasized { - color: var(--color-text-primary); - } - - &__icon { - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - - .icon { - width: 24px; - height: 24px; - } - } - } - - &__field-group { - display: flex; - flex-direction: column; - - label { - display: flex; - gap: 16px; - align-items: center; - font-size: 16px; - line-height: 24px; - height: 32px; - padding: 0 12px; - } - } - - &__caveats { - font-size: 14px; - padding: 0 12px; - - strong { - font-weight: 500; - } - } - - &__bottom { - padding-top: 0; - - &__collapsible { - display: none; - flex-direction: column; - gap: 16px; - } - - &.active { - background: var(--color-bg-secondary); - padding-top: 24px; - - .safety-action-modal__bottom__collapsible { - display: flex; - } - } - } - - &__actions { - display: flex; - align-items: center; - gap: 8px; - justify-content: flex-end; - - &__hint { - font-size: 14px; - line-height: 20px; - color: var(--color-text-tertiary); - } - - .link-button { - padding: 10px 12px; - font-weight: 600; - } - } -} - -.dialog-modal { - width: 588px; - max-height: 80vh; - flex-direction: column; - background: var(--color-bg-elevated); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - border-radius: 16px; - - &__header { - box-sizing: border-box; - border-bottom: 1px solid var(--color-border-primary); - display: flex; - align-items: center; - justify-content: space-between; - flex-direction: row-reverse; - padding: 12px 24px; - min-height: 61px; - - &__title { - font-size: 16px; - line-height: 24px; - font-weight: 500; - letter-spacing: 0.15px; - } - } - - &__content { - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - overflow-y: auto; - - &__description { - margin: 24px 24px 0; - color: var(--color-text-secondary); - - a { - color: inherit; - } - } - - &__form { - display: flex; - flex-direction: column; - gap: 16px; - padding: 24px; - } - - &__preview { - display: flex; - flex-direction: column; - gap: 16px; - align-items: center; - justify-content: center; - padding: 24px; - background: var(--color-bg-media-base); - - img { - display: block; - } - - img, - .gifv video { - outline: 1px solid var(--color-border-media); - outline-offset: -1px; - border-radius: 8px; - } - - img, - .gifv video, - .video-player, - .audio-player { - max-width: 360px; - max-height: 45vh; - } - } - - &__actions { - display: flex; - align-items: center; - gap: 8px; - justify-content: flex-end; - padding: 0 24px 24px; - } - } - - &__popout { - background: var(--color-bg-elevated); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - box-shadow: var(--dropdown-shadow); - max-width: 320px; - padding: 16px; - border-radius: 8px; - z-index: 9999 !important; - font-size: 14px; - line-height: 20px; - color: var(--color-text-secondary); - } - - .copy-paste-text { - margin-bottom: 0; - } -} - -.hotkey-combination { - display: inline-flex; - align-items: center; - gap: 4px; -} - -.boost-modal, -.report-modal, -.actions-modal, -.compare-history-modal { - background: var(--color-bg-primary); - color: var(--color-text-primary); - border-radius: 4px; - border: 1px solid var(--color-border-primary); - overflow: hidden; - max-width: 90vw; - width: 480px; - position: relative; - flex-direction: column; - - @media screen and (max-width: $no-columns-breakpoint) { - border-bottom: 0; - border-radius: 4px 4px 0 0; - padding-bottom: env(safe-area-inset-bottom); - } -} - -.boost-modal__container { - overflow-y: auto; - padding: 10px; - - .status { - user-select: text; - border-bottom: 0; - } -} - -.report-modal { - width: 90vw; - max-width: 700px; - border: 1px solid var(--color-border-primary); -} - -.report-dialog-modal { - max-width: 90vw; - width: 480px; - height: 80vh; - background: var(--color-bg-primary); - color: var(--color-text-primary); - border-radius: 4px; - border: 1px solid var(--color-border-primary); - overflow: hidden; - position: relative; - flex-direction: column; - display: flex; - - &__container { - box-sizing: border-box; - border-top: 1px solid var(--color-border-primary); - padding: 20px; - flex-grow: 1; - display: flex; - flex-direction: column; - min-height: 0; - overflow: auto; - } - - &__title { - font-size: 28px; - line-height: 33px; - font-weight: 700; - margin-bottom: 15px; - - @media screen and (height <= 800px) { - font-size: 22px; - } - } - - &__subtitle { - font-size: 17px; - font-weight: 600; - line-height: 22px; - margin-bottom: 4px; - } - - &__lead { - font-size: 17px; - line-height: 22px; - color: var(--color-text-primary); - margin-bottom: 30px; - - a { - text-decoration: none; - color: var(--color-text-brand); - font-weight: 500; - - &:hover { - text-decoration: underline; - } - } - } - - &__actions { - margin-top: 30px; - display: flex; - - .button { - flex: 1 1 auto; - } - } - - &__statuses { - flex-grow: 1; - min-height: 0; - overflow: auto; - } - - .status__content a { - color: var(--color-text-brand); - } - - .status__content, - .status__content p { - color: var(--color-text-primary); - } - - .dialog-option { - align-items: center; - gap: 12px; - } - - .dialog-option .poll__input { - color: var(--color-text-secondary); - display: inline-flex; - align-items: center; - justify-content: center; - - svg { - width: 15px; - height: 15px; - } - - &:active, - &:focus, - &:hover { - border-color: var(--color-text-success); - border-width: 4px; - } - - &.active { - color: var(--color-text-on-success-base); - background: var(--color-bg-success-base); - border-color: var(--color-bg-success-base); - } - } - - .poll__option.dialog-option { - padding: 15px 0; - flex: 0 0 auto; - border-bottom: 1px solid var(--color-border-primary); - - &:last-child { - border-bottom: 0; - } - - & > .poll__option__text { - font-size: 13px; - color: var(--color-text-primary); - - strong { - font-size: 17px; - font-weight: 500; - line-height: 22px; - color: var(--color-text-primary); - display: block; - margin-bottom: 4px; - - &:last-child { - margin-bottom: 0; - } - } - } - } - - .flex-spacer { - background: transparent; - } - - &__textarea { - display: block; - box-sizing: border-box; - width: 100%; - color: var(--color-text-primary); - background: var(--color-bg-secondary); - padding: 10px; - font-family: inherit; - font-size: 17px; - line-height: 22px; - resize: vertical; - border: 0; - border: 1px solid var(--color-border-primary); - outline: 0; - border-radius: 4px; - margin: 20px 0; - - &:focus { - outline: 0; - } - } - - &__toggle { - display: flex; - align-items: center; - margin-bottom: 16px; - gap: 8px; - - & > span { - display: block; - font-size: 14px; - font-weight: 500; - line-height: 20px; - } - } - - .button.button-secondary { - border-color: var(--color-text-error); - color: var(--color-text-error); - flex: 0 0 auto; - - &:hover, - &:focus, - &:active { - color: var(--color-text-on-error-base); - background: var(--color-bg-error-base); - border-color: var(--color-bg-error-base); - } - } - - hr { - border: 0; - background: transparent; - margin: 15px 0; - } - - .emoji-mart-search { - padding-inline-end: 10px; - } - - .emoji-mart-search-icon { - inset-inline-end: 10px + 5px; - } -} - -.setting-divider { - background: transparent; - border: 0; - margin: 0; - width: 100%; - height: 1px; - margin-bottom: 29px; -} - -.actions-modal { - border-radius: 8px 8px 0 0; - background: var(--color-bg-elevated); - backdrop-filter: $backdrop-blur-filter; - border-color: var(--color-border-primary); - box-shadow: var(--dropdown-shadow); - max-height: 80vh; - max-width: 80vw; - - ul { - overflow-y: auto; - padding-bottom: 8px; - } - - a, - button { - color: inherit; - display: flex; - padding: 16px; - font-size: 15px; - line-height: 21px; - background: transparent; - border: none; - align-items: center; - text-decoration: none; - width: 100%; - box-sizing: border-box; - - &:hover, - &:active, - &:focus { - background: var(--color-bg-brand-softer); - } - } -} - -.report-modal__target { - padding: 30px; - font-size: 16px; - - strong { - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } -} - -.report-modal__target { - text-align: center; -} - -.report-modal__target { - padding: 15px; - - .report-modal__close { - position: absolute; - top: 10px; - inset-inline-end: 10px; - } -} - -.compare-history-modal { - .report-modal__target { - border-bottom: 1px solid var(--color-border-primary); - } - - &__container { - padding: 30px; - pointer-events: all; - overflow-y: auto; - } - - .status__content { - color: var(--color-text-primary); - font-size: 19px; - line-height: 24px; - - .emojione { - width: 24px; - height: 24px; - margin: -1px 0 0; - } - - a { - color: var(--color-text-brand); - } - - hr { - height: 0.25rem; - padding: 0; - background-color: var(--color-text-primary); - border: 0; - margin: 20px 0; - } - } - - .media-gallery, - .audio-player, - .video-player { - margin-top: 15px; - } -} - -.loading-bar { - background-color: var(--color-bg-brand-base); - height: 3px; - position: fixed; - top: 0; - inset-inline-start: 0; - z-index: 9999; -} - -.media-gallery__actions { - position: absolute; - top: 6px; - inset-inline-end: 6px; - display: flex; - gap: 2px; - z-index: 2; - - &__pill { - display: block; - color: var(--color-text-on-media); - background: var(--color-bg-media); - backdrop-filter: $backdrop-blur-filter; - border: 0; - padding: 3px 12px; - border-radius: 99px; - font-size: 14px; - font-weight: 700; - line-height: 20px; - } -} - -.media-gallery__item__badges { - position: absolute; - bottom: 8px; - inset-inline-end: 8px; - display: flex; - gap: 2px; - pointer-events: none; -} - -.media-gallery__alt__label, -.relationship-tag { - display: block; - text-align: center; - color: var(--color-text-on-media); - background: var(--color-bg-media); - backdrop-filter: $backdrop-blur-filter; - border: 0; - padding: 3px 8px; - border-radius: 4px; - font-size: 12px; - font-weight: 700; - z-index: 1; - line-height: 20px; - cursor: pointer; - pointer-events: auto; - - &--non-interactive { - pointer-events: none; - } -} - -.relationship-tag { - text-transform: uppercase; - cursor: default; -} - -.info-tooltip { - color: var(--color-text-on-media); - background: var(--color-bg-media); - backdrop-filter: $backdrop-blur-filter; - border-radius: 4px; - box-shadow: var(--dropdown-shadow); - padding: 16px; - min-width: 16em; - min-height: 2em; - max-width: 22em; - max-height: 30em; - overflow-y: auto; - z-index: 10; - - &--solid { - color: var(--color-text-primary); - background: var(--color-bg-elevated); - border: 1px solid var(--color-border-primary); - } - - h4 { - font-size: 15px; - line-height: 20px; - font-weight: 500; - margin-bottom: 8px; - } - - p { - font-size: 15px; - line-height: 20px; - opacity: 0.85; - white-space: pre-line; - } - - .button { - margin-block-start: 8px; - } -} - -.attachment-list { - display: flex; - font-size: 14px; - border: 1px solid var(--color-border-primary); - border-radius: 4px; - margin-top: 16px; - overflow: hidden; - - &__icon { - flex: 0 0 auto; - color: var(--color-text-tertiary); - padding: 8px 18px; - cursor: default; - border-inline-end: 1px solid var(--color-border-primary); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: 26px; - } - - &__list { - list-style: none; - padding: 4px 0; - padding-inline-start: 8px; - display: flex; - flex-direction: column; - justify-content: center; - - li { - display: block; - padding: 4px 0; - } - - a { - text-decoration: none; - color: var(--color-text-tertiary); - font-weight: 500; - - &:hover { - text-decoration: underline; - } - } - } - - &.compact { - border: 0; - - .attachment-list__list { - padding: 0; - display: block; - } - - .icon { - color: var(--color-text-tertiary); - vertical-align: middle; - } - } -} - -/* Media Gallery */ -.media-gallery { - box-sizing: border-box; - margin-top: 8px; - overflow: hidden; - border-radius: 8px; - position: relative; - width: 100%; - min-height: 64px; - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: 1fr 1fr; - gap: 2px; - - &--layout-1 { - // The size of single images is determined by their - // aspect-ratio, applied via inline style attribute - width: initial; - max-height: 460px; - } - - &--layout-2 { - & > .media-gallery__item:nth-child(1) { - border-end-end-radius: 0; - border-start-end-radius: 0; - } - - & > .media-gallery__item:nth-child(2) { - border-start-start-radius: 0; - border-end-start-radius: 0; - } - } - - &--layout-3 { - min-height: calc(64px * 2 + 8px); - - & > .media-gallery__item:nth-child(1) { - border-end-end-radius: 0; - border-start-end-radius: 0; - } - - & > .media-gallery__item:nth-child(2) { - border-start-start-radius: 0; - border-end-start-radius: 0; - border-end-end-radius: 0; - } - - & > .media-gallery__item:nth-child(3) { - border-start-start-radius: 0; - border-end-start-radius: 0; - border-start-end-radius: 0; - } - } - - &--layout-4 { - min-height: calc(64px * 2 + 8px); - - & > .media-gallery__item:nth-child(1) { - border-end-end-radius: 0; - border-start-end-radius: 0; - border-end-start-radius: 0; - } - - & > .media-gallery__item:nth-child(2) { - border-start-start-radius: 0; - border-end-start-radius: 0; - border-end-end-radius: 0; - } - - & > .media-gallery__item:nth-child(3) { - border-start-start-radius: 0; - border-start-end-radius: 0; - border-end-start-radius: 0; - border-end-end-radius: 0; - } - - & > .media-gallery__item:nth-child(4) { - border-start-start-radius: 0; - border-end-start-radius: 0; - border-start-end-radius: 0; - } - } -} - -.media-gallery__item { - border: 0; - box-sizing: border-box; - display: block; - position: relative; - border-radius: 8px; - overflow: hidden; - outline: 1px solid var(--color-border-media); - outline-offset: -1px; - z-index: 1; - - &--tall { - grid-row: span 2; - } - - &--wide { - grid-column: span 2; - } - - &--square { - aspect-ratio: 1; - } - - &__overlay { - position: absolute; - top: 0; - inset-inline-start: 0; - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - width: 100%; - height: 100%; - pointer-events: none; - padding: 8px; - z-index: 1; - - &--corner { - align-items: flex-start; - justify-content: flex-end; - } - - .icon { - color: var(--color-text-on-media); - filter: drop-shadow(0 0 6px var(--color-bg-media-base)); - } - } - - &--error img { - visibility: hidden; - } -} - -.media-gallery__item-thumbnail { - cursor: pointer; - display: block; - text-decoration: none; - color: var(--color-text-primary); - position: relative; - z-index: -1; - - &, - img { - height: 100%; - width: 100%; - } - - img { - object-fit: cover; - } -} - -.media-gallery__preview { - width: 100%; - height: 100%; - object-fit: cover; - position: absolute; - top: 0; - inset-inline-start: 0; - z-index: -2; - background: var(--color-bg-overlay); - - &--hidden { - display: none; - } -} - -.media-gallery__gifv { - height: 100%; - overflow: hidden; - position: relative; - width: 100%; - z-index: -1; -} - -.media-gallery__item-gifv-thumbnail { - cursor: pointer; - height: 100%; - object-fit: cover; - width: 100%; -} - -/* End Media Gallery */ - -.detailed, -.fullscreen { - .video-player__volume__current, - .video-player__volume::before { - bottom: 27px; - } - - .video-player__volume__handle { - bottom: 23px; - } -} - -.audio-player { - /* These are only fallback values, the AudioPlayer component inserts - * the real colours dynamically as inline styles */ - --player-foreground-color: var(--color-text-on-media); - --player-background-color: var(--color-bg-media-base); - --player-accent-color: var(--color-text-brand); - - box-sizing: border-box; - container: audio-player / inline-size; - position: relative; - overflow: hidden; - display: flex; - flex-direction: column; - width: 100%; - aspect-ratio: 16 / 9; - color: var(--player-foreground-color); - background: var(--player-background-color); - border-radius: 8px; - outline: 1px solid var(--color-border-media); - outline-offset: -1px; - - &__controls { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - height: 100%; - - &__play { - display: flex; - align-items: center; - justify-content: center; - position: relative; - - .player-button { - position: absolute; - top: 50%; - inset-inline-start: 50%; - transform: translate(-50%, -50%); - } - - .icon { - filter: var(--overlay-icon-shadow); - } - } - - .player-button { - display: inline-block; - outline: 0; - padding: 5px; - flex: 0 0 auto; - background: transparent; - border: 0; - color: var(--player-foreground-color); - opacity: 0.75; - - .icon { - width: 48px; - height: 48px; - } - - &:active, - &:hover, - &:focus { - opacity: 1; - } - } - } - - &__visualizer { - width: 100%; - max-width: 200px; - } - - .video-player__seek { - position: absolute; - inset: 0 0 auto; - height: 24px; - z-index: 1; /* Ensure this renders on top of audio player controls */ - } - - &.inactive { - .video-player__seek, - .audio-player__controls, - .video-player__controls { - visibility: hidden; - } - } - - .video-player__volume::before, - .video-player__seek::before { - background: currentColor; - opacity: 0.15; - } - - .video-player__seek__buffer { - background: currentColor; - opacity: 0.2; - } - - .video-player__seek__progress, - .video-player__seek__handle, - .video-player__volume__current, - .video-player__volume__handle { - background-color: var(--player-accent-color); - } - - .video-player__buttons button, - .video-player__buttons a { - color: currentColor; - opacity: 0.75; - - &:active, - &:hover, - &:focus { - color: currentColor; - opacity: 1; - } - } - - .video-player__time-sep, - .video-player__time-total, - .video-player__time-current { - color: currentColor; - } - - @container audio-player (max-width: 400px) { - .video-player__time, - .player-button.video-player__download__icon { - display: none; - } - } - - .video-player__seek::before, - .video-player__seek__buffer, - .video-player__seek__progress { - top: 0; - } - - .video-player__seek__handle { - top: -4px; - } - - .video-player__controls { - padding-top: 10px; - background: transparent; - z-index: 1; - } -} - -.video-player { - overflow: hidden; - position: relative; - color: var(--color-text-on-media); - background: var(--color-bg-media); - max-height: 460px; - border-radius: 8px; - box-sizing: border-box; - display: flex; - outline: 1px solid var(--color-border-media); - outline-offset: -1px; - z-index: 2; - - video { - display: block; - z-index: -2; - } - - &.fullscreen { - width: 100% !important; - height: 100% !important; - margin: 0; - aspect-ratio: auto !important; - outline: none; - border-radius: 0; - - video { - width: 100% !important; - height: 100% !important; - outline: 0; - } - } - - &__controls { - position: absolute; - direction: ltr; - z-index: -1; - bottom: 0; - inset-inline-start: 0; - inset-inline-end: 0; - box-sizing: border-box; - background: linear-gradient( - 0deg, - rgb(from var(--color-bg-media-base) r g b / 85%) 0, - rgb(from var(--color-bg-media-base) r g b / 45%) 60%, - transparent - ); - padding: 0 15px; - opacity: 0; - pointer-events: none; - transition: opacity 0.1s ease; - will-change: opacity, pointer-events; - - &.active { - opacity: 1; - pointer-events: auto; - } - } - - .media-gallery__actions { - opacity: 0; - transition: opacity 0.1s ease; - - &.active { - opacity: 1; - } - } - - &.inactive { - video, - .video-player__controls { - visibility: hidden; - } - } - - &__spoiler { - display: none; - position: absolute; - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100%; - z-index: 4; - border: 0; - background: var(--color-bg-primary); - color: var(--color-text-secondary); - transition: none; - pointer-events: none; - - &.active { - display: block; - pointer-events: auto; - - &:hover, - &:active, - &:focus { - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - } - } - - &__title { - display: block; - font-size: 14px; - } - - &__subtitle { - display: block; - font-size: 11px; - font-weight: 500; - } - } - - &__buttons-bar { - display: flex; - justify-content: space-between; - padding-bottom: 8px; - margin: 0 -5px; - - .video-player__download__icon { - color: inherit; - } - } - - &__buttons { - display: flex; - flex: 0 1 auto; - min-width: 30px; - align-items: center; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - gap: 5px; - - .player-button { - display: inline-block; - outline: 0; - padding: 5px; - flex: 0 0 auto; - background: transparent; - border: 0; - color: rgb(from var(--color-text-on-media) r g b / 75%); - font-weight: 500; - - &:active, - &:hover, - &:focus { - color: var(--color-text-on-media); - } - } - } - - &__time { - display: inline; - flex: 0 1 auto; - overflow: hidden; - text-overflow: ellipsis; - margin: 0 5px; - - // Ensure digits maintain a consistent width - font-variant-numeric: tabular-nums; - } - - &__time-sep, - &__time-total, - &__time-current { - color: var(--color-text-on-media); - font-size: 14px; - font-weight: 500; - } - - &__time-sep { - display: inline-block; - margin: 0 6px; - } - - &__volume { - flex: 0 0 auto; - display: inline-flex; - cursor: pointer; - height: 24px; - position: relative; - overflow: hidden; - - .no-reduce-motion & { - transition: all 100ms linear; - } - - &.active { - overflow: visible; - width: 50px; - margin-inline-end: 16px; - } - - &::before { - content: ''; - width: 50px; - background: rgb(from var(--color-text-on-media) r g b / 35%); - border-radius: 4px; - display: block; - position: absolute; - height: 4px; - inset-inline-start: 0; - top: 50%; - transform: translate(0, -50%); - } - - &__current { - display: block; - position: absolute; - height: 4px; - border-radius: 4px; - inset-inline-start: 0; - top: 50%; - transform: translate(0, -50%); - background: var(--color-text-on-media); - } - - &__handle { - position: absolute; - z-index: 3; - border-radius: 50%; - width: 12px; - height: 12px; - top: 50%; - inset-inline-start: 0; - margin-inline-start: -6px; - transform: translate(0, -50%); - background: var(--color-text-on-media); - box-shadow: 1px 2px 6px var(--color-shadow-primary); - opacity: 0; - - .no-reduce-motion & { - transition: opacity 100ms linear; - } - } - - &.active &__handle { - opacity: 1; - } - } - - &__link { - padding: 2px 10px; - - a { - text-decoration: none; - font-size: 14px; - font-weight: 500; - color: var(--color-text-on-media); - - &:hover, - &:active, - &:focus { - text-decoration: underline; - } - } - } - - &__seek { - cursor: pointer; - height: 24px; - position: relative; - - &::before { - content: ''; - width: 100%; - background: rgb(from var(--color-text-on-media) r g b / 35%); - border-radius: 4px; - display: block; - position: absolute; - height: 4px; - top: 14px; - } - - &__progress, - &__buffer { - display: block; - position: absolute; - height: 4px; - border-radius: 4px; - top: 14px; - background: var(--color-text-on-media); - } - - &__buffer { - background: rgb(from var(--color-text-on-media) r g b / 20%); - } - - &__handle { - position: absolute; - z-index: 3; - opacity: 0; - border-radius: 50%; - width: 12px; - height: 12px; - top: 10px; - margin-inline-start: -6px; - background: var(--color-text-on-media); - box-shadow: 1px 2px 6px var(--color-shadow-primary); - - .no-reduce-motion & { - transition: opacity 0.1s ease; - } - - &.active { - opacity: 1; - cursor: grabbing; - } - } - - &:hover { - .video-player__seek__handle { - opacity: 1; - } - } - } - - &__hotkey-indicator { - position: absolute; - top: 50%; - inset-inline-start: 50%; - transform: translate(-50%, -50%); - color: var(--color-text-on-media); - background: var(--color-bg-media); - backdrop-filter: $backdrop-blur-filter; - border-radius: 8px; - padding: 16px 24px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 8px; - - &__label { - font-size: 15px; - font-weight: 500; - } - } - - &.detailed, - &.fullscreen { - .video-player__buttons { - .player-button { - padding-top: 10px; - padding-bottom: 10px; - } - } - } -} - -.gifv { - position: relative; - - canvas { - position: absolute; - width: 100%; - height: 100%; - } - - video { - max-width: 100vw; - max-height: 80vh; - } -} - -.scrollable .account-card { - margin: 10px; -} - -.scrollable .account-card__title__avatar { - img { - border: 2px solid var(--color-bg-primary); - } - - .account__avatar { - border: none; - } -} - -.scrollable .account-card__header { - img { - border-radius: 4px; - } -} - -.scrollable .account-card__bio::after { - background: linear-gradient(to left, var(--color-bg-primary), transparent); -} - -.account-gallery__container { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - gap: 2px; - - .media-gallery__item { - border-radius: 0; - } - - .load-more, - .timeline-hint { - grid-column: span 3; - } -} - -.notification__filter-bar, -.account__section-headline { - border: 1px solid var(--color-border-primary); - border-top: 0; - cursor: default; - display: flex; - flex-shrink: 0; - - @media screen and (max-width: ($no-gap-breakpoint - 1px)) { - border-right: 0; - border-left: 0; - } - - button { - background: transparent; - border: 0; - margin: 0; - } - - button, - a { - display: block; - flex: 1 1 auto; - color: var(--color-text-secondary); - padding: 15px 0; - font-size: 14px; - font-weight: 500; - text-align: center; - text-decoration: none; - position: relative; - width: 100%; - white-space: nowrap; - - &.active { - color: var(--color-text-primary); - - &::before { - display: block; - content: ''; - position: absolute; - bottom: -1px; - left: 50%; - transform: translateX(-50%); - width: 40px; - height: 3px; - border-radius: 4px 4px 0 0; - background: var(--color-text-brand); - } - } - } - - .scrollable & { - border-left: 0; - border-right: 0; - } -} - -.filter-form { - border-bottom: 1px solid var(--color-border-primary); - - &__column { - display: flex; - flex-direction: column; - gap: 15px; - padding: 15px; - } - - .radio-button { - display: flex; - } -} - -.column-settings__row .radio-button { - display: flex; -} - -.radio-button, -.check-box { - font-size: 14px; - position: relative; - display: inline-flex; - align-items: center; - line-height: 18px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - cursor: pointer; - gap: 10px; - color: var(--color-text-primary); - - input[type='radio'], - input[type='checkbox'] { - display: none; - } - - &__input { - display: flex; - align-items: center; - justify-content: center; - position: relative; - border: 2px solid var(--color-text-primary); - box-sizing: border-box; - width: 20px; - height: 20px; - flex: 0 0 auto; - border-radius: 50%; - - &.checked, - &.indeterminate { - border-color: var(--color-text-brand); - } - - .icon { - width: 18px; - height: 18px; - } - } -} - -.radio-button__input.checked::before { - content: ''; - display: block; - border-radius: 50%; - width: calc(100% - 4px); - height: calc(100% - 4px); - background: var(--color-text-brand); -} - -.check-box { - &__input { - width: 18px; - height: 18px; - border-radius: 2px; - - &.checked, - &.indeterminate { - background: var(--color-bg-brand-base); - color: var(--color-text-on-brand-base); - } - } -} - -noscript { - text-align: center; - - img { - width: 200px; - opacity: 0.5; - animation: flicker 4s infinite; - } - - div { - font-size: 14px; - margin: 30px auto; - color: var(--color-text-primary); - max-width: 400px; - - a { - color: var(--color-text-brand); - text-decoration: underline; - - &:hover { - text-decoration: none; - } - } - } -} - -@keyframes flicker { - 0% { - opacity: 1; - } - - 30% { - opacity: 0.75; - } - - 100% { - opacity: 1; - } -} - -.moved-account-banner, -.follow-request-banner, -.account-memorial-banner { - padding: 20px; - background: var(--color-bg-tertiary); - display: flex; - align-items: center; - flex-direction: column; - - &__message { - color: var(--color-text-secondary); - padding: 8px 0; - padding-top: 0; - padding-bottom: 4px; - font-size: 14px; - font-weight: 500; - text-align: center; - margin-bottom: 16px; - } - - &__action { - display: flex; - justify-content: space-between; - align-items: center; - gap: 15px; - width: 100%; - } - - .detailed-status__display-name { - margin-bottom: 0; - } -} - -.follow-request-banner .button { - width: 100%; -} - -.account-memorial-banner__message { - margin-bottom: 0; -} - -.column-inline-form { - padding: 15px; - display: flex; - justify-content: flex-start; - gap: 15px; - align-items: center; - border: 1px solid var(--color-border-primary); - border-top: 0; - - label { - flex: 1 1 auto; - - input { - width: 100%; - } - } - - @media screen and (max-width: $no-gap-breakpoint) { - border-left: 0; - border-right: 0; - } -} - -.drawer__backdrop { - cursor: pointer; - position: absolute; - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100%; - background: rgb(from var(--color-bg-overlay) r g b / 50%); -} - -.focal-point { - position: relative; - cursor: grab; - overflow: hidden; - display: flex; - justify-content: center; - align-items: center; - - &.dragging { - cursor: grabbing; - } - - &__reticle { - position: absolute; - width: 100px; - height: 100px; - transform: translate(-50%, -50%); - border: 2px solid var(--color-text-on-media); - border-radius: 50%; - box-shadow: 0 0 0 9999em var(--color-shadow-primary); - pointer-events: none; - } -} - -.account__header__content { - color: var(--color-text-secondary); - font-size: 14px; - font-weight: 400; - overflow: hidden; - word-break: normal; - overflow-wrap: break-word; - - p { - margin-bottom: 20px; - unicode-bidi: plaintext; - - &:last-child { - margin-bottom: 0; - } - } - - a { - color: inherit; - text-decoration: underline; - - &:hover { - text-decoration: none; - } - } -} - -.account__header { - overflow: hidden; - container: account-header / inline-size; - - &.inactive { - opacity: 0.5; - - .account__header__image, - .account__avatar { - filter: grayscale(100%); - } - } - - &__info { - position: absolute; - top: 20px; - inset-inline-end: 20px; - display: flex; - flex-wrap: wrap; - gap: 2px; - } - - &__image { - overflow: hidden; - height: 145px; - position: relative; - background: var(--color-bg-tertiary); - border-bottom: 1px solid var(--color-border-primary); - - img { - object-fit: cover; - display: block; - width: 100%; - height: 100%; - margin: 0; - } - } - - &__bar { - position: relative; - padding: 0 20px; - border-bottom: 1px solid var(--color-border-primary); - - .avatar { - display: block; - flex: 0 0 auto; - - .account__avatar { - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - border-radius: var(--avatar-border-radius); - } - } - } - - &__badges { - display: flex; - flex-wrap: wrap; - gap: 8px; - - .account-role { - line-height: unset; - } - } - - &__tabs { - display: flex; - align-items: flex-start; - justify-content: space-between; - margin-top: -55px; - padding-top: 10px; - gap: 8px; - overflow: hidden; - margin-inline-start: -2px; // aligns the pfp with content below - - &__name { - margin-top: 16px; - margin-bottom: 16px; - - .emojione { - width: 22px; - height: 22px; - } - - h1 { - font-size: 17px; - line-height: 22px; - color: var(--color-text-primary); - font-weight: 600; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - - small { - display: flex; - align-items: center; - gap: 4px; - font-size: 14px; - line-height: 20px; - color: var(--color-text-secondary); - font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - - span { - overflow: hidden; - text-overflow: ellipsis; - user-select: all; - } - - .icon-lock { - height: 18px; - width: 18px; - } - } - } - } - - .spacer { - flex: 1 1 auto; - } - } - - &__follow-button { - flex-grow: 1; - } - - &__buttons { - display: flex; - align-items: center; - gap: 8px; - - $button-breakpoint: 420px; - $button-fallback-breakpoint: #{$button-breakpoint} + 55px; - - &--desktop { - margin-top: 55px; - - @container (width < #{$button-breakpoint}) { - display: none; - } - - @supports (not (container-type: inline-size)) { - @media (max-width: #{$button-fallback-breakpoint}) { - display: none; - } - } - } - - &--mobile { - margin-block: 16px; - - @container (width >= #{$button-breakpoint}) { - display: none; - } - - @supports (not (container-type: inline-size)) { - @media (min-width: (#{$button-fallback-breakpoint} + 1px)) { - display: none; - } - } - } - - .button { - flex-shrink: 1; - white-space: nowrap; - min-width: 80px; - } - - .icon-button { - border: 1px solid var(--color-border-primary); - border-radius: 4px; - box-sizing: content-box; - padding: 5px; - - .icon { - width: 24px; - height: 24px; - } - - &.copied { - border-color: var(--color-text-success); - } - } - } - - &__bio { - .account__header__content { - color: var(--color-text-primary); - } - - .account__header__fields { - margin: 0; - margin-top: 16px; - border-radius: 4px; - border: 1px solid var(--color-border-primary); - - dl { - display: block; - padding: 11px 16px; - border-bottom-color: var(--color-border-primary); - } - - dd, - dt { - font-size: 13px; - line-height: 18px; - padding: 0; - text-align: initial; - } - - dt { - width: auto; - background: transparent; - text-transform: uppercase; - color: var(--color-text-tertiary); - } - - dd { - color: var(--color-text-secondary); - } - - a { - color: var(--color-text-brand); - } - - .icon { - width: 18px; - height: 18px; - } - - .verified { - border: 1px solid var(--color-text-success); - margin-top: -1px; - margin-inline: -1px; - - &:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - } - - &:last-child { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - margin-bottom: -1px; - } - - dt, - dd { - color: var(--color-text-success); - } - - dd { - display: flex; - align-items: center; - gap: 4px; - - span { - display: flex; - } - } - - a { - color: var(--color-text-success); - } - } - } - } - - &__extra { - margin-top: 16px; - - &__links { - font-size: 14px; - color: var(--color-text-secondary); - margin: 0 -10px; - padding-top: 16px; - padding-bottom: 10px; - - a { - display: inline-block; - color: var(--color-text-secondary); - text-decoration: none; - padding: 5px 10px; - font-weight: 500; - - strong { - font-weight: 700; - color: var(--color-text-primary); - } - } - } - } - - &__account-note { - color: var(--color-text-primary); - font-size: 14px; - font-weight: 400; - margin-bottom: 10px; - - &__loading-indicator-wrapper { - position: relative; - height: 37px; - - .loading-indicator { - left: 10px; - } - - .circular-progress { - width: 14px; - height: 14px; - } - } - - label { - display: block; - font-size: 12px; - font-weight: 500; - color: var(--color-text-secondary); - text-transform: uppercase; - margin-bottom: 5px; - } - - textarea { - display: block; - box-sizing: border-box; - width: calc(100% + 20px); - color: var(--color-text-primary); - background: transparent; - padding: 10px; - margin: 0 -10px; - font-family: inherit; - font-size: 14px; - resize: none; - border: 0; - outline: 0; - border-radius: 4px; - - &::placeholder { - color: var(--color-text-tertiary); - opacity: 1; - } - - &:focus { - background: var(--color-bg-brand-softer); - } - } - } - - &__familiar-followers { - display: flex; - align-items: center; - gap: 10px; - margin-block: 16px; - color: var(--color-text-secondary); - - a:any-link { - font-weight: 500; - text-decoration: none; - color: var(--color-text-primary); - } - } -} - -.account__contents { - overflow: hidden; -} - -.account__details { - display: flex; - flex-wrap: wrap; - column-gap: 1em; -} - -.verified-badge { - display: inline-flex; - align-items: center; - color: var(--color-text-success); - gap: 4px; - overflow: hidden; - white-space: nowrap; - - > span { - overflow: hidden; - text-overflow: ellipsis; - } - - a { - color: inherit; - font-weight: 500; - text-decoration: none; - } - - .icon { - width: 16px; - height: 16px; - } -} - -.trends { - &__item { - display: flex; - align-items: center; - padding: 16px; - border-bottom: 1px solid var(--color-border-primary); - gap: 8px; - - &__name { - flex: 1 1 auto; - color: var(--color-text-secondary); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - strong { - font-weight: 500; - } - - a { - color: var(--color-text-primary); - text-decoration: none; - font-size: 14px; - font-weight: 500; - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &:hover, - &:focus, - &:active { - span { - text-decoration: underline; - } - } - } - } - - &__current { - flex: 0 0 auto; - font-size: 24px; - font-weight: 500; - text-align: end; - color: var(--color-text-primary); - text-decoration: none; - } - - &__sparkline { - flex: 0 0 auto; - width: 50px; - - path:first-child { - fill: var(--color-graph-primary-fill) !important; - fill-opacity: 1 !important; - } - - path:last-child { - stroke: var(--color-graph-primary-stroke) !important; - fill: none !important; - } - } - - &--requires-review { - .trends__item__name { - color: var(--color-text-warning); - - a { - color: var(--color-text-warning); - } - } - - .trends__item__current { - color: var(--color-text-warning); - } - - .trends__item__sparkline { - path:first-child { - fill: var(--color-graph-warning-fill) !important; - } - - path:last-child { - stroke: var(--color-graph-warning-stroke) !important; - } - } - } - - &--disabled { - .trends__item__name { - color: var(--color-text-disabled); - - a { - color: var(--color-text-disabled); - } - } - - .trends__item__current { - color: var(--color-text-disabled); - } - - .trends__item__sparkline { - path:first-child { - fill: var(--color-graph-disabled-fill) !important; - } - - path:last-child { - stroke: var(--color-graph-disabled-stroke) !important; - } - } - } - } - - &--compact &__item { - padding: 12px; - } -} - -.conversation { - display: flex; - border-bottom: 1px solid var(--color-border-primary); - padding: 5px; - padding-bottom: 0; - - &:focus { - background: var(--color-bg-secondary); - outline: 0; - } - - &__avatar { - flex: 0 0 auto; - padding: 10px; - padding-top: 12px; - position: relative; - cursor: pointer; - } - - &__unread { - display: inline-block; - background: var(--color-text-brand); - border-radius: 50%; - width: 0.625rem; - height: 0.625rem; - margin: -0.1ex 0.15em 0.1ex; - } - - &__content { - flex: 1 1 auto; - padding: 10px 5px; - padding-inline-end: 15px; - overflow: hidden; - - &__info { - overflow: hidden; - display: flex; - flex-direction: row-reverse; - justify-content: space-between; - } - - &__relative-time { - font-size: 15px; - color: var(--color-text-secondary); - padding-inline-start: 15px; - } - - &__names { - color: var(--color-text-secondary); - font-size: 15px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-bottom: 4px; - flex-basis: 90px; - flex-grow: 1; - - a { - color: var(--color-text-primary); - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } - } - - a { - overflow-wrap: anywhere; - } - } -} - -.announcements { - width: calc(100% - 124px); - flex: 0 0 auto; - position: relative; - overflow: hidden; - - .layout-multiple-columns & { - width: 100%; - } - - @media screen and (max-width: (124px + 300px)) { - width: 100%; - } - - &__root { - background: var(--color-bg-brand-softer); - font-size: 13px; - display: flex; - align-items: flex-end; - } - - &__mastodon { - width: 124px; - flex: 0 0 auto; - - @media screen and (max-width: (124px + 300px)) { - display: none; - } - } - - &__slides { - display: flex; - flex-wrap: nowrap; - align-items: start; - } - - &__slide { - box-sizing: border-box; - width: 100%; - flex: 0 0 100%; - padding: 15px; - position: relative; - font-size: 15px; - line-height: 20px; - overflow-wrap: break-word; - font-weight: 400; - max-height: 50vh; - overflow: hidden; - flex-direction: column; - } - - &__range { - display: block; - font-weight: 500; - margin-bottom: 10px; - padding-inline-end: 18px; - } - - &__unread { - position: absolute; - top: 19px; - inset-inline-end: 19px; - display: block; - background: var(--color-text-brand); - border-radius: 50%; - width: 0.625rem; - height: 0.625rem; - } - - &__pagination { - padding: 15px; - color: var(--color-text-secondary); - position: absolute; - bottom: 3px; - inset-inline-end: 0; - display: flex; - align-items: center; - z-index: 1; - } -} - -.layout-multiple-columns .announcements__mastodon { - display: none; -} - -.layout-multiple-columns .announcements__container { - width: 100%; -} - -.reactions-bar { - display: flex; - flex-wrap: wrap; - align-items: center; - margin-top: 15px; - margin-inline-start: -2px; - width: calc(100% - (90px - 33px)); - - &__item { - flex-shrink: 0; - background: var(--color-bg-brand-softer); - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - border: 1px solid var(--color-border-on-bg-brand-softer); - border-radius: 3px; - box-sizing: border-box; - margin: 2px; - cursor: pointer; - user-select: none; - padding: 0 6px; - display: flex; - align-items: center; - transition: all 100ms ease-in; - transition-property: background-color, color; - - &__emoji { - display: block; - margin: 3px 0; - width: 16px; - height: 16px; - - img { - display: block; - margin: 0; - width: 100%; - height: 100%; - min-width: auto; - min-height: auto; - object-fit: contain; - } - } - - &__count { - display: block; - min-width: 9px; - font-size: 13px; - font-weight: 500; - text-align: center; - margin-inline-start: 6px; - } - - &:hover, - &:focus, - &:active { - color: var(--color-text-primary); - background: var(--color-bg-brand-soft); - transition: all 200ms ease-out; - transition-property: background-color, color; - } - - &.active { - color: var(--color-text-brand); - background-color: var(--color-bg-brand-softer); - border-color: var(--color-text-brand); - transition: all 100ms ease-in; - transition-property: background-color, color; - } - } - - .emoji-picker-dropdown { - display: flex; - margin: 2px; - } - - &:hover .emoji-button { - opacity: 0.85; - } - - .emoji-button { - color: var(--color-text-secondary); - margin: 0; - font-size: 16px; - width: auto; - flex-shrink: 0; - padding: 0 6px; - height: 22px; - display: flex; - align-items: center; - opacity: 0.5; - transition: all 100ms ease-in; - transition-property: background-color, color; - - &:hover, - &:active, - &:focus { - opacity: 1; - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - transition: all 200ms ease-out; - transition-property: background-color, color; - } - } - - &--empty { - .emoji-button { - padding: 0; - } - } -} - -.notification, -.status__wrapper, -.conversation { - position: relative; - - // When scrolling these elements into view, take into account - // the column header height - scroll-margin-top: var(--column-header-height, 0); - - &.unread { - &::before { - content: ''; - position: absolute; - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100%; - border-inline-start: 4px solid var(--color-text-brand); - pointer-events: none; - } - } -} - -.picture-in-picture { - position: fixed; - bottom: 20px; - inset-inline-end: 20px; - width: 300px; - box-shadow: var(--dropdown-shadow); - - &__footer { - border-radius: 0 0 4px 4px; - background: var(--color-bg-secondary); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - border-top: 0; - padding: 12px; - display: flex; - justify-content: space-between; - } - - &__header { - border-radius: 4px 4px 0 0; - background: var(--color-bg-secondary); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - border-bottom: 0; - padding: 12px; - display: flex; - justify-content: space-between; - - .icon-button { - padding: 6px; - } - - &__account { - display: flex; - text-decoration: none; - overflow: hidden; - } - - .account__avatar { - margin-inline-end: 8px; - } - - .display-name { - color: var(--color-text-primary); - text-decoration: none; - - strong, - span { - display: block; - text-overflow: ellipsis; - overflow: hidden; - } - - span { - color: var(--color-text-secondary); - } - } - } - - .video-player, - .audio-player { - border-radius: 0; - } -} - -.picture-in-picture-placeholder { - border-radius: 8px; - box-sizing: border-box; - border: 1px dashed var(--color-border-primary); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - margin-top: 16px; - font-size: 15px; - line-height: 21px; - font-weight: 500; - cursor: pointer; - color: var(--color-text-tertiary); - aspect-ratio: 16 / 9; - - .icon { - width: 48px; - height: 48px; - margin-bottom: 8px; - } - - &:hover, - &:active, - &:focus { - color: var(--color-text-secondary); - } - - &:focus-visible { - outline: var(--outline-focus-default); - border-color: transparent; - } -} - -.notifications-permission-banner { - padding: 30px; - border-bottom: 1px solid var(--color-border-primary); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - - &__close { - position: absolute; - top: 10px; - inset-inline-end: 10px; - } - - h2 { - font-size: 16px; - font-weight: 500; - margin-bottom: 15px; - text-align: center; - } - - p { - color: var(--color-text-secondary); - margin-bottom: 15px; - text-align: center; - - .icon { - width: 20px; - height: 20px; - vertical-align: middle; - } - } -} - -.explore__search-header { - justify-content: center; - align-items: center; - border: 1px solid var(--color-border-primary); - border-top: 0; - border-bottom: 0; - padding: 16px; - padding-bottom: 8px; - - .search { - width: 100%; - margin-bottom: 0; - } - - .search__input { - padding-block: 12px; - padding-inline-end: 30px; - } - - .search__popout { - border: 1px solid var(--color-border-primary); - } - - .search__icon { - top: 12px; - inset-inline-end: 12px; - color: var(--color-text-tertiary); - } -} - -.layout-single-column .explore__search-header { - display: none; - - @media screen and (max-width: ($no-gap-breakpoint - 1px)) { - display: flex; - } -} - -.explore__search-results { - flex: 1 1 auto; - display: flex; - flex-direction: column; - - @media screen and (min-width: $no-gap-breakpoint) { - border: 1px solid var(--color-border-primary); - border-top: 0; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - } -} - -.story { - display: flex; - align-items: center; - color: var(--color-text-primary); - padding: 16px; - border-bottom: 1px solid var(--color-border-primary); - gap: 16px; - - &:last-child { - border-bottom: 0; - } - - &__details { - flex: 1 1 auto; - - &__publisher { - color: var(--color-text-secondary); - margin-bottom: 8px; - font-size: 14px; - line-height: 20px; - } - - &__title { - display: block; - font-size: 19px; - line-height: 24px; - font-weight: 500; - margin-bottom: 8px; - text-decoration: none; - color: var(--color-text-primary); - - &:hover, - &:active, - &:focus { - color: var(--color-text-brand); - } - } - - &__shared { - display: flex; - align-items: baseline; - color: var(--color-text-secondary); - gap: 8px; - justify-content: space-between; - font-size: 14px; - line-height: 20px; - - &__pill { - background: var(--color-bg-tertiary); - border-radius: 4px; - color: inherit; - text-decoration: none; - padding: 4px 12px; - font-size: 12px; - font-weight: 500; - line-height: 16px; - flex-shrink: 0; - } - - &__author-link { - display: inline-flex; - align-items: center; - gap: 4px; - color: var(--color-text-primary); - font-weight: 500; - text-decoration: none; - - &:hover, - &:active, - &:focus { - color: var(--color-text-brand); - } - } - } - - strong { - font-weight: 500; - } - } - - &__thumbnail { - flex: 0 0 auto; - position: relative; - width: 120px; - aspect-ratio: 1; - - .skeleton { - width: 100%; - height: 100%; - } - - img { - border-radius: 8px; - display: block; - margin: 0; - width: 100%; - height: 100%; - object-fit: cover; - } - - &__preview { - border-radius: 8px; - display: block; - margin: 0; - width: 100%; - height: 100%; - object-fit: fill; - position: absolute; - top: 0; - inset-inline-start: 0; - z-index: 0; - - &--hidden { - display: none; - } - } - } - - &.expanded { - flex-direction: column; - - .story__thumbnail { - order: 1; - width: 100%; - height: auto; - aspect-ratio: 1.91 / 1; - } - - .story__details { - order: 2; - width: 100%; - flex: 0 0 auto; - } - } -} - -.server-banner { - &__introduction { - font-size: 15px; - line-height: 22px; - color: var(--color-text-secondary); - margin-bottom: 20px; - - strong { - font-weight: 700; - } - - a { - color: inherit; - text-decoration: underline; - - &:hover, - &:active, - &:focus { - text-decoration: none; - } - } - } - - &__hero { - display: block; - border-radius: 4px; - width: 100%; - height: auto; - margin-bottom: 20px; - aspect-ratio: 1.9; - border: 0; - background: var(--color-bg-tertiary); - object-fit: cover; - } - - &__description { - font-size: 15px; - line-height: 22px; - color: var(--color-text-secondary); - margin-bottom: 20px; - } - - &__meta { - display: flex; - gap: 10px; - max-width: 100%; - - &__column { - flex: 0 0 auto; - width: calc(50% - 5px); - overflow: hidden; - } - } - - &__number { - font-weight: 600; - color: var(--color-text-primary); - font-size: 14px; - } - - &__number-label { - color: var(--color-text-secondary); - font-weight: 500; - font-size: 14px; - } - - h4 { - text-transform: uppercase; - color: var(--color-text-secondary); - margin-bottom: 10px; - font-weight: 600; - } - - .account { - padding: 0; - border: 0; - } - - .account__avatar-wrapper { - margin-inline-start: 0; - } - - .spacer { - margin: 10px 0; - } -} - -.safety-action-modal, -.interaction-modal { - max-width: 100vw; - width: 600px; - overflow-y: auto; -} - -.interaction-modal { - overflow: visible; - position: relative; - display: block; - border-radius: 16px; - background: var(--color-bg-primary); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - padding: 24px; - box-sizing: border-box; - - @media screen and (max-width: $mobile-breakpoint) { - border-radius: 16px 16px 0 0; - border-bottom: 0; - padding-bottom: 32px; - } - - h3 { - font-size: 22px; - line-height: 33px; - font-weight: 700; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - } - - p { - text-align: center; - font-size: 17px; - line-height: 22px; - color: var(--color-text-secondary); - - strong { - color: var(--color-text-primary); - font-weight: 700; - } - } - - p.hint { - margin-bottom: 14px; - font-size: 14px; - } - - &__lead { - margin-bottom: 20px; - - h3 { - margin-bottom: 15px; - } - } - - &__login { - position: relative; - margin-bottom: 20px; - - &__input { - @include search-input; - - border: 1px solid var(--color-border-primary); - padding: 4px 6px; - color: var(--color-text-primary); - font-size: 16px; - line-height: 18px; - display: flex; - align-items: center; - - input { - background: transparent; - color: inherit; - font: inherit; - border: 0; - padding: 15px - 4px 15px - 6px; - flex: 1 1 auto; - min-width: 0; - - &::placeholder { - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - } - - &:focus { - outline: 0; - } - } - - .button { - flex: 0 0 auto; - } - } - - .search__popout { - margin-top: -1px; - padding-top: 5px; - padding-bottom: 5px; - border: 1px solid var(--color-border-primary); - } - - &.invalid &__input { - border-color: var(--color-text-error); - } - - &.expanded .search__popout { - display: block; - } - - &.expanded &__input { - border-radius: 4px 4px 0 0; - } - } - - &__choices { - display: flex; - gap: 40px; - - &__choice { - flex: 1; - box-sizing: border-box; - - h3 { - margin-bottom: 20px; - } - - p { - color: var(--color-text-secondary); - margin-bottom: 20px; - font-size: 15px; - } - - .button { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } - } - } - } - - @media screen and (max-width: ($no-gap-breakpoint - 1px)) { - &__choices { - flex-direction: column; - - &__choice { - margin-top: 40px; - } - } - } - - .link-button { - font-size: inherit; - display: inline; - } -} - -.privacy-policy { - padding: 20px; - - @media screen and (min-width: $no-gap-breakpoint) { - border-radius: 4px; - } - - &__body { - margin-top: 20px; - } -} - -.prose { - color: var(--color-text-primary); - font-size: 15px; - line-height: 22px; - - p, - ul, - ol { - margin-top: 1.25em; - margin-bottom: 1.25em; - } - - img { - margin-top: 2em; - margin-bottom: 2em; - max-width: 100%; - } - - video { - margin-top: 2em; - margin-bottom: 2em; - max-width: 100%; - } - - figure { - margin-top: 2em; - margin-bottom: 2em; - - figcaption { - font-size: 0.875em; - line-height: 1.4285714; - margin-top: 0.8571429em; - } - } - - figure > * { - margin-top: 0; - margin-bottom: 0; - } - - h1 { - font-size: 1.5em; - margin-top: 0; - margin-bottom: 1em; - line-height: 1.33; - } - - h2 { - font-size: 1.25em; - margin-top: 1.6em; - margin-bottom: 0.6em; - line-height: 1.6; - } - - h3, - h4, - h5, - h6 { - margin-top: 1.5em; - margin-bottom: 0.5em; - line-height: 1.5; - } - - ol { - counter-reset: list-counter; - } - - li { - margin-top: 0.5em; - margin-bottom: 0.5em; - } - - ol > li { - counter-increment: list-counter; - - &::before { - content: counter(list-counter) '.'; - position: absolute; - inset-inline-start: 0; - } - } - - ul > li::before { - content: ''; - position: absolute; - background-color: var(--color-text-secondary); - border-radius: 50%; - width: 0.375em; - height: 0.375em; - top: 0.5em; - inset-inline-start: 0.25em; - } - - ul > li, - ol > li { - position: relative; - padding-inline-start: 1.75em; - } - - & > ul > li p { - margin-top: 0.75em; - margin-bottom: 0.75em; - } - - & > ul > li > *:first-child { - margin-top: 1.25em; - } - - & > ul > li > *:last-child { - margin-bottom: 1.25em; - } - - & > ol > li > *:first-child { - margin-top: 1.25em; - } - - & > ol > li > *:last-child { - margin-bottom: 1.25em; - } - - ul ul, - ul ol, - ol ul, - ol ol { - margin-top: 0.75em; - margin-bottom: 0.75em; - } - - h1, - h2, - h3, - h4, - h5, - h6, - strong, - b { - color: var(--color-text-primary); - font-weight: 700; - } - - em, - i { - font-style: italic; - } - - a { - color: var(--color-text-brand); - text-decoration: underline; - - &:focus, - &:hover, - &:active { - text-decoration: none; - } - } - - code { - font-size: 0.875em; - background: var(--color-bg-secondary); - border-radius: 4px; - padding: 0.2em 0.3em; - } - - hr { - border: 0; - border-top: 1px solid var(--color-border-primary); - margin-top: 3em; - margin-bottom: 3em; - } - - hr + * { - margin-top: 0; - } - - h2 + * { - margin-top: 0; - } - - h3 + * { - margin-top: 0; - } - - h4 + *, - h5 + *, - h6 + * { - margin-top: 0; - } - - & > :first-child { - margin-top: 0; - } - - & > :last-child { - margin-bottom: 0; - } -} - -.dismissable-banner, -.warning-banner { - position: relative; - margin: 10px; - margin-bottom: 5px; - border-radius: 8px; - border: 1px solid var(--color-border-on-bg-brand-softer); - background: var(--color-bg-brand-softer); - overflow: hidden; - flex-shrink: 0; - - &__background-image { - width: 125%; - position: absolute; - bottom: -25%; - inset-inline-end: -25%; - z-index: -1; - opacity: 0.15; - mix-blend-mode: luminosity; - } - - &__message { - flex: 1 1 auto; - padding: 15px; - font-size: 15px; - line-height: 22px; - font-weight: 500; - color: var(--color-text-primary); - - p { - margin-bottom: 15px; - - &:last-child { - margin-bottom: 0; - } - } - - h1 { - color: var(--color-text-brand); - font-size: 22px; - line-height: 33px; - font-weight: 700; - margin-bottom: 15px; - } - - &__actions { - display: flex; - flex-wrap: wrap; - gap: 4px; - - &__wrapper { - display: flex; - margin-top: 30px; - } - - .button { - display: block; - flex-grow: 1; - } - } - } - - &__action { - float: right; - padding: 15px 10px; - - .icon-button { - color: var(--color-text-brand); - } - } -} - -.warning-banner { - border: 1px solid var(--color-border-on-bg-error-softer); - background: var(--color-bg-error-softer); - - &__message { - h1 { - color: var(--color-text-error); - } - - a { - color: var(--color-text-primary); - } - } -} - -.image { - position: relative; - overflow: hidden; - - &__preview { - position: absolute; - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100%; - object-fit: cover; - } - - &.loaded &__preview { - display: none; - } - - img { - display: block; - width: 100%; - height: 100%; - object-fit: cover; - border: 0; - background: transparent; - opacity: 0; - } - - &.loaded img { - opacity: 1; - } -} - -.link-footer { - flex: 0 0 auto; - padding-top: 20px; - z-index: 1; - font-size: 13px; - - .column & { - padding: 15px; - } - - p { - color: var(--color-text-secondary); - margin-bottom: 20px; - - .version { - white-space: nowrap; - } - - strong { - font-weight: 500; - } - - a { - color: var(--color-text-secondary); - text-decoration: underline; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - } - } -} - -.about { - padding: 20px; - border-top: 1px solid var(--color-border-primary); - - @media screen and (min-width: $no-gap-breakpoint) { - border-radius: 4px; - } - - &__footer { - color: var(--color-text-tertiary); - text-align: center; - font-size: 15px; - line-height: 22px; - margin-top: 20px; - } - - &__header { - margin-bottom: 30px; - - &__hero { - width: 100%; - height: auto; - aspect-ratio: 1.9; - background: var(--color-bg-tertiary); - border-radius: 8px; - margin-bottom: 30px; - } - - h1, - p { - text-align: center; - } - - h1 { - font-size: 24px; - line-height: 1.5; - font-weight: 700; - margin-bottom: 10px; - } - - p { - font-size: 16px; - line-height: 24px; - font-weight: 400; - color: var(--color-text-secondary); - } - } - - &__meta { - border: 1px solid var(--color-border-primary); - border-radius: 4px; - display: flex; - margin-bottom: 30px; - font-size: 15px; - - &__column { - box-sizing: border-box; - width: 50%; - padding: 20px; - } - - &__divider { - width: 0; - border: 0; - border-style: solid; - border-color: var(--color-border-primary); - border-left-width: 1px; - min-height: calc(100% - 60px); - flex: 0 0 auto; - } - - h4 { - font-size: 15px; - text-transform: uppercase; - color: var(--color-text-secondary); - font-weight: 500; - margin-bottom: 20px; - } - - @media screen and (width <= 600px) { - display: block; - - h4 { - text-align: center; - } - - &__column { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - } - - &__divider { - min-height: 0; - width: 100%; - border-left-width: 0; - border-top-width: 1px; - } - } - - .layout-multiple-columns & { - display: block; - - h4 { - text-align: center; - } - - &__column { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - } - - &__divider { - min-height: 0; - width: 100%; - border-left-width: 0; - border-top-width: 1px; - } - } - } - - &__mail { - color: var(--color-text-primary); - text-decoration: none; - font-weight: 500; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } - - .link-footer { - padding: 0; - margin-top: 60px; - text-align: center; - font-size: 15px; - line-height: 22px; - - @media screen and (min-width: $no-gap-breakpoint) { - display: none; - } - } - - .account { - padding: 0; - border: 0; - } - - .account__avatar-wrapper { - margin-inline-start: 0; - } - - .account__relationship { - display: none; - } - - &__section { - margin-bottom: 10px; - - &__title { - display: flex; - align-items: center; - gap: 6px; - font-size: 17px; - font-weight: 600; - line-height: 22px; - padding: 20px; - border-radius: 4px; - border: 1px solid var(--color-border-primary); - color: var(--color-text-brand); - cursor: pointer; - width: 100%; - background: none; - } - - &.active &__title { - border-radius: 4px 4px 0 0; - } - - &__body { - border: 1px solid var(--color-border-primary); - border-top: 0; - padding: 20px; - font-size: 15px; - line-height: 22px; - } - } - - &__domain-blocks { - margin-top: 30px; - border: 1px solid var(--color-border-primary); - border-radius: 4px; - - &__domain { - border-bottom: 1px solid var(--color-border-primary); - padding: 10px; - font-size: 15px; - color: var(--color-text-secondary); - - &:nth-child(2n) { - background: var(--color-bg-secondary); - } - - &:last-child { - border-bottom: 0; - } - - &__header { - display: flex; - gap: 10px; - justify-content: space-between; - font-weight: 500; - margin-bottom: 4px; - } - - h6 { - color: var(--color-text-primary); - font-size: inherit; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - p { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - } - } -} - -.notification-list { - position: fixed; - bottom: 2rem; - inset-inline-start: 1rem; - z-index: 9999; - display: flex; - flex-direction: column; - gap: 4px; -} - -.notification-bar { - --alert-edge-spacing: 1rem; - - display: flex; - gap: 10px; - flex: 0 0 auto; - width: auto; - padding: 15px; - margin: 0; - color: var(--color-text-on-inverted); - background: var(--color-bg-inverted); - backdrop-filter: blur(8px); - border: 1px solid var(--color-border-on-bg-inverted); - border-radius: 8px; - box-shadow: - 0 10px 15px -3px var(--color-shadow-primary), - 0 4px 6px -4px var(--color-shadow-primary); - cursor: default; - font-size: 15px; - line-height: 21px; - - &.from-side { - translate: calc( - -1 * (100% + var(--alert-edge-spacing)) * var(--text-x-direction) - ); - } - - &.from-below { - translate: 0 calc(100% + var(--alert-edge-spacing)); - } - - &.notification-bar--active { - translate: none; - } - - .no-reduce-motion & { - transition: 0.5s cubic-bezier(0.89, 0.01, 0.5, 1.1); - will-change: translate; - } -} - -.notification-bar__content { - margin-inline-end: auto; -} - -.notification-bar__title { - margin-inline-end: 5px; - font-weight: 700; -} - -.notification-bar__action { - display: inline-block; - border: 0; - background: transparent; - text-transform: uppercase; - cursor: pointer; - color: var(--color-text-brand); - font-weight: 700; - border-radius: 4px; - padding: 0 4px; - - &:hover, - &:focus, - &:active { - background: var(--color-bg-brand-softer); - } -} - -.notification-bar__dismiss-button { - margin-top: -2px; - color: rgb(from currentColor r g b / 85%); - - &:hover, - &:focus, - &:active { - color: currentColor; - } -} - -.notification-bar__loading-indicator { - --spinner-size: 22px; - - position: relative; - height: var(--spinner-size); - width: var(--spinner-size); - margin-inline-start: 2px; - - svg { - color: var(--color-text-on-media); - height: var(--spinner-size); - width: var(--spinner-size); - } -} - -.hashtag-header { - border-bottom: 1px solid var(--color-border-primary); - padding: 15px; - font-size: 17px; - line-height: 22px; - color: var(--color-text-secondary); - - strong { - font-weight: 700; - } - - &__header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 15px; - gap: 15px; - - h1 { - color: var(--color-text-primary); - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - font-size: 22px; - line-height: 33px; - font-weight: 700; - } - - &__buttons { - display: flex; - align-items: center; - gap: 8px; - - .button { - flex-shrink: 1; - white-space: nowrap; - min-width: 80px; - } - - .icon-button { - border: 1px solid var(--color-border-primary); - border-radius: 4px; - box-sizing: content-box; - padding: 5px; - - .icon { - width: 24px; - height: 24px; - } - } - } - } -} - -.hashtag-bar { - margin-top: 16px; - display: flex; - flex-wrap: wrap; - font-size: 12px; - line-height: 16px; - gap: 6px; - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - - a { - display: inline-flex; - color: inherit; - text-decoration: none; - padding: 4px 12px; - background: var(--color-bg-brand-softer); - border-radius: 4px; - font-weight: 500; - - &:hover, - &:focus, - &:active { - background: var(--color-bg-brand-soft); - } - } - - .link-button { - color: inherit; - font-size: inherit; - line-height: inherit; - padding: 0; - } -} - -.inline-follow-suggestions { - display: flex; - flex-direction: column; - gap: 12px; - padding: 16px 0; - padding-bottom: 0; - border-bottom: 1px solid var(--color-border-primary); - background: var(--color-bg-brand-softer); - - &.focusable:focus-visible { - background: var(--color-bg-brand-softer); - } - - &__header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 16px; - - h3 { - font-size: 15px; - line-height: 22px; - font-weight: 500; - } - - &__actions { - display: flex; - align-items: center; - gap: 24px; - } - - .link-button { - font-size: 13px; - font-weight: 500; - } - } - - &__body { - position: relative; - - &__scroll-button { - position: absolute; - height: 100%; - background: transparent; - border: none; - cursor: pointer; - top: 0; - color: var(--color-text-primary); - opacity: 0.5; - - &.left { - left: 0; - } - - &.right { - right: 0; - } - - &__icon { - border-radius: 50%; - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - display: flex; - align-items: center; - justify-content: center; - aspect-ratio: 1; - padding: 8px; - - .icon { - width: 24px; - height: 24px; - } - } - - &:hover, - &:focus, - &:active { - opacity: 1; - } - } - - &__scrollable { - box-sizing: border-box; - display: flex; - flex-wrap: nowrap; - min-height: 228px; - gap: 16px; - padding: 16px; - scroll-snap-type: x mandatory; - scroll-padding: 16px; - scroll-behavior: smooth; - overflow-x: scroll; - scrollbar-width: none; - - &__card { - background: var(--color-bg-primary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - display: flex; - flex-direction: column; - gap: 12px; - align-items: center; - padding: 12px; - scroll-snap-align: start; - flex: 0 0 auto; - width: 200px; - box-sizing: border-box; - position: relative; - - a { - text-decoration: none; - } - - & > .icon-button { - position: absolute; - inset-inline-end: 8px; - top: 8px; - opacity: 0.75; - } - - &__avatar { - height: 48px; - display: flex; - - a { - display: flex; - text-decoration: none; - } - } - - .account__avatar { - flex-shrink: 0; - align-self: flex-end; - border: 1px solid var(--color-border-primary); - background-color: var(--color-bg-tertiary); - } - - &__text-stack { - display: flex; - flex-direction: column; - gap: 4px; - align-items: center; - max-width: 100%; - - a { - max-width: 100%; - } - - &__source { - display: inline-flex; - align-items: center; - color: var(--color-text-tertiary); - gap: 4px; - overflow: hidden; - white-space: nowrap; - cursor: help; - - > span { - overflow: hidden; - text-overflow: ellipsis; - } - - .icon { - width: 16px; - height: 16px; - } - } - } - - .display-name { - display: flex; - flex-direction: column; - gap: 4px; - align-items: center; - - & > * { - max-width: 100%; - } - - &__html { - font-size: 15px; - font-weight: 500; - color: var(--color-text-primary); - } - - &__account { - font-size: 14px; - color: var(--color-text-secondary); - } - } - - .verified-badge { - font-size: 14px; - max-width: 100%; - } - - .button { - display: block; - width: 100%; - } - } - } - } -} - -.filtered-notifications-banner { - display: flex; - align-items: center; - border-bottom: 1px solid var(--color-border-primary); - padding: 16px 24px; - gap: 8px; - color: var(--color-text-secondary); - text-decoration: none; - - &:hover, - &:active, - &:focus { - color: var(--color-text-primary); - } - - .notification-group__icon { - color: inherit; - } - - &__text { - flex: 1 1 auto; - font-size: 14px; - line-height: 20px; - - strong { - font-size: 16px; - line-height: 24px; - display: block; - } - } - - &__badge { - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - border-radius: 100px; - padding: 2px 8px; - } -} - -.notification-request { - $padding: 15px; - - display: flex; - padding: $padding; - gap: 8px; - position: relative; - border-bottom: 1px solid var(--color-border-primary); - - &__checkbox { - position: absolute; - inset-inline-start: $padding; - top: 50%; - transform: translateY(-50%); - width: 0; - overflow: hidden; - opacity: 0; - - .check-box { - display: flex; - } - } - - &__link { - display: flex; - align-items: center; - gap: 12px; - flex: 1 1 auto; - text-decoration: none; - color: inherit; - overflow: hidden; - - .account__avatar { - flex-shrink: 0; - } - } - - &__name { - flex: 1 1 auto; - color: var(--color-text-secondary); - font-size: 14px; - line-height: 20px; - overflow: hidden; - text-overflow: ellipsis; - - &__display-name { - display: flex; - align-items: center; - gap: 6px; - font-size: 16px; - letter-spacing: 0.5px; - line-height: 24px; - color: var(--color-text-primary); - - bdi { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - } - - .filtered-notifications-banner__badge { - color: var(--color-text-on-brand-base); - background: var(--color-bg-brand-base); - border-radius: 4px; - padding: 1px 6px; - } - } - - &__actions { - display: flex; - align-items: center; - gap: 8px; - - .icon-button { - border-radius: 4px; - border: 1px solid var(--color-border-primary); - padding: 5px; - } - } - - .notification-request__link { - transition: padding-inline-start 0.1s ease-in-out; - } - - &--forced-checkbox { - cursor: pointer; - - &:hover { - background: var(--color-bg-secondary); - } - - .notification-request__checkbox { - opacity: 1; - width: 30px; - } - - .notification-request__link { - padding-inline-start: 30px; - } - - .notification-request__actions { - display: none; - } - } -} - -.more-from-author { - box-sizing: border-box; - font-size: 14px; - color: var(--color-text-secondary); - background: var(--color-bg-tertiary); - border: 1px solid var(--color-border-primary); - border-top: 0; - border-radius: 0 0 8px 8px; - padding: 15px; - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 4px 8px; - - .logo { - width: 16px; - height: 16px; - color: var(--color-text-secondary); - } - - & > span { - display: flex; - align-items: center; - gap: 8px; - } - - a { - display: inline-flex; - align-items: center; - gap: 4px; - font-weight: 500; - color: var(--color-text-primary); - text-decoration: none; - min-width: 0; - - &:hover, - &:focus, - &:active { - color: var(--color-text-brand); - } - } -} - -.notification-group { - display: flex; - align-items: flex-start; - gap: 8px; - padding: 16px 24px; - border-bottom: 1px solid var(--color-border-primary); - - &__icon { - width: 40px; - display: flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - color: var(--color-text-tertiary); - - .icon { - width: 28px; - height: 28px; - } - } - - &--follow &__icon, - &--follow-request &__icon { - color: var(--color-text-brand); - } - - &--favourite &__icon { - color: var(--color-text-favourite-highlight); - } - - &--reblog &__icon { - color: var(--color-text-success); - } - - &--relationships-severance-event &__icon, - &--admin-report &__icon, - &--admin-sign-up &__icon { - color: var(--color-text-tertiary); - } - - &--moderation-warning &__icon { - color: var(--color-text-bookmark-highlight); - } - - &--follow-request &__actions { - align-items: center; - display: flex; - gap: 8px; - - .icon-button { - border: 1px solid var(--color-border-primary); - border-radius: 50%; - padding: 1px; - } - } - - &__main { - display: flex; - flex-direction: column; - gap: 8px; - flex: 1 1 auto; - overflow: hidden; - container-type: inline-size; - - &__header { - display: flex; - flex-direction: column; - gap: 8px; - - &__wrapper { - display: flex; - justify-content: space-between; - } - - &__label { - display: flex; - flex-wrap: wrap; - gap: 2px 8px; - font-size: 15px; - line-height: 22px; - color: var(--color-text-secondary); - - a { - color: inherit; - text-decoration: none; - } - - bdi { - font-weight: 700; - color: var(--color-text-primary); - } - - time { - color: var(--color-text-tertiary); - } - - @container (width < 350px) { - time, - &-separator { - display: none; - } - } - } - } - - &__status { - border: 1px solid var(--color-border-primary); - border-radius: 8px; - padding: 8px; - } - - &__additional-content { - color: var(--color-text-tertiary); - margin-top: -8px; // to offset the parent's `gap` property - font-size: 15px; - line-height: 22px; - } - } - - .status { - padding: 0; - border: 0; - } - - &__embedded-status { - display: flex; - flex-direction: column; - gap: 8px; - cursor: pointer; - - &__account { - display: flex; - align-items: center; - gap: 4px; - color: var(--color-text-tertiary); - font-size: 15px; - line-height: 22px; - - bdi { - color: var(--color-text-secondary); - } - - .account__avatar { - flex: 0 0 auto; - } - } - - &__content { - display: -webkit-box; - font-size: 15px; - line-height: 22px; - color: var(--color-text-secondary); - -webkit-line-clamp: 4; - line-clamp: 4; - -webkit-box-orient: vertical; - max-height: none; - overflow: hidden; - - p, - a { - color: inherit; - } - - p { - margin-bottom: 8px; - } - } - - .reply-indicator__attachments { - margin-top: 0; - font-size: 15px; - line-height: 22px; - color: var(--color-text-tertiary); - } - } -} - -.notification-group__actions, -.compose-form__actions { - .button { - display: block; // Otherwise text-ellipsis doesn't work - flex: 1 1 auto; - } -} - -.notification-ungrouped { - padding: 16px 24px; - border-bottom: 1px solid var(--color-border-primary); - - &__header { - display: flex; - align-items: center; - gap: 8px; - color: var(--color-text-tertiary); - font-size: 15px; - line-height: 22px; - font-weight: 500; - padding-inline-start: 24px; - margin-bottom: 16px; - - &__icon { - display: flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - - .icon { - width: 16px; - height: 16px; - } - } - - a { - color: inherit; - text-decoration: none; - } - } - - .status:not(.status--is-quote) { - border: 0; - padding: 0; - } - - .status__wrapper-direct { - background: transparent; - } - - .status { - // 40px avatar + 8px gap - --status-gutter-width: 48px; - } - - .status--is-quote { - --status-gutter-width: 0; - } - - .status__content, - .status__action-bar, - .media-gallery, - .video-player, - .audio-player, - .attachment-list, - .picture-in-picture-placeholder, - .more-from-author, - .status-card, - .hashtag-bar, - .content-warning, - .filter-warning { - margin-inline-start: var(--status-gutter-width); - width: calc(100% - var(--status-gutter-width)); - } - - .more-from-author { - width: calc(100% - var(--status-gutter-width) + 2px); - } - - .status__content__read-more-button { - margin-inline-start: var(--status-gutter-width); - } - - .notification__report { - border: 0; - padding: 0; - } -} - -.notification-group--unread, -.notification-ungrouped--unread { - position: relative; - - &::before { - content: ''; - position: absolute; - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100%; - border-inline-start: 4px solid var(--color-text-brand); - pointer-events: none; - } -} - -.hover-card-controller[data-popper-reference-hidden='true'] { - opacity: 0; - pointer-events: none; -} - -.hover-card { - box-shadow: var(--dropdown-shadow); - background: var(--color-bg-primary); - backdrop-filter: $backdrop-blur-filter; - border: 1px solid var(--color-border-primary); - border-radius: 8px; - padding: 16px; - width: 270px; - display: flex; - flex-direction: column; - gap: 12px; - - &--loading { - position: relative; - min-height: 100px; - } - - &__name { - display: flex; - gap: 12px; - text-decoration: none; - color: inherit; - } - - &__numbers, - &__familiar-followers { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 2px 10px; - } - - &__numbers { - font-size: 15px; - line-height: 22px; - color: var(--color-text-primary); - - strong { - font-weight: 700; - } - } - - &__text-row { - display: flex; - flex-direction: column; - gap: 8px; - } - - &__bio { - color: var(--color-text-primary); - font-size: 14px; - line-height: 20px; - display: -webkit-box; - line-clamp: 2; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - max-height: 2 * 20px; - overflow: hidden; - - p { - margin-bottom: 0; - } - - a { - color: inherit; - text-decoration: underline; - - &:hover, - &:focus, - &:active { - text-decoration: none; - } - } - } - - &__note { - &-label { - color: var(--color-text-tertiary); - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - } - - dd { - white-space: pre-line; - color: var(--color-text-primary); - overflow: hidden; - line-clamp: 3; // Not yet supported in browers - display: -webkit-box; // The next 3 properties are needed - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - } - } - - &__limited-account-note { - text-align: center; - font-weight: 500; - } - - .display-name { - font-size: 15px; - line-height: 22px; - - bdi { - font-weight: 500; - color: var(--color-text-primary); - } - - &__account { - display: block; - color: var(--color-text-tertiary); - } - } - - .account-fields { - color: var(--color-text-primary); - font-size: 14px; - line-height: 20px; - - a { - color: inherit; - text-decoration: none; - - &:focus, - &:hover, - &:active { - text-decoration: underline; - } - } - - dl { - display: flex; - align-items: center; - gap: 4px; - - dt { - flex: 0 1 auto; - color: var(--color-text-tertiary); - min-width: 0; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - dd { - flex: 1 1 auto; - font-weight: 500; - min-width: 0; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - text-align: end; - } - - &.verified { - dd { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 4px; - overflow: hidden; - white-space: nowrap; - color: var(--color-text-success); - - & > span { - overflow: hidden; - text-overflow: ellipsis; - } - - a { - font-weight: 500; - } - - .icon { - width: 16px; - height: 16px; - } - } - } - } - } -} - -.content-warning { - display: block; - box-sizing: border-box; - color: var(--color-text-primary); - background: var(--color-bg-brand-softer); - border: 1px solid var(--color-border-on-bg-brand-softer); - border-radius: 8px; - padding: 8px (5px + 8px); - position: relative; - font-size: 15px; - line-height: 22px; - cursor: pointer; - - p { - margin-bottom: 8px; - font-weight: 500; - } - - .link-button { - font-size: inherit; - line-height: inherit; - font-weight: 500; - } - - &--filter { - color: var(--color-text-secondary); - - p { - font-weight: normal; - } - - .filter-name { - font-weight: 500; - color: var(--color-text-primary); - } - } -} - -.lists__item { - display: flex; - align-items: center; - gap: 16px; - padding-inline-end: 13px; - border-bottom: 1px solid var(--color-border-primary); - - &__title { - display: flex; - align-items: center; - gap: 5px; - padding: 16px 13px; - flex: 1 1 auto; - font-size: 16px; - line-height: 24px; - color: var(--color-text-secondary); - text-decoration: none; - - &:is(a):hover, - &:is(a):focus, - &:is(a):active { - color: var(--color-text-primary); - } - - input { - display: block; - width: 100%; - background: transparent; - border: 0; - padding: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; - color: inherit; - - &::placeholder { - color: var(--color-text-secondary); - opacity: 1; - } - - &:focus { - outline: 0; - } - } - } -} - -.column-search-header { - display: flex; - gap: 12px; - align-items: center; - border: 1px solid var(--color-border-primary); - border-top: 0; - border-bottom: 0; - padding: 16px; - padding-bottom: 8px; - - input { - background: var(--color-bg-secondary); - border: 1px solid var(--color-border-primary); - color: var(--color-text-primary); - padding: 12px; - font-size: 16px; - line-height: normal; - border-radius: 4px; - display: block; - flex: 1 1 auto; - - &::placeholder { - color: var(--color-text-secondary); - opacity: 1; - } - - &:focus { - outline: 0; - } - } -} - -.column-footer { - padding: 16px; -} - -.lists-scrollable { - min-height: 50vh; -} - -.featured-carousel { - overflow: hidden; - flex-shrink: 0; - border-bottom: 1px solid var(--color-border-primary); - touch-action: pan-y; - - &__slides { - display: flex; - flex-wrap: nowrap; - align-items: start; - } - - &__slide { - flex: 0 0 auto; - flex-basis: 100%; - width: 100%; - overflow: hidden; - } - - .status { - border-bottom: 0; - } - - &__header { - padding: 8px 16px; - color: var(--color-text-secondary); - inset-inline-end: 0; - display: flex; - align-items: center; - gap: 4px; - } - - &__title { - flex-grow: 1; - font-size: 12px; - font-weight: 500; - text-transform: uppercase; - display: flex; - align-items: center; - gap: 4px; - - .icon { - width: 16px; - height: 16px; - } - } - - &__pagination { - display: flex; - align-items: center; - gap: 4px; - } -} diff --git a/app/javascript/styles_new/mastodon/containers.scss b/app/javascript/styles_new/mastodon/containers.scss deleted file mode 100644 index 57c62a29e3579b..00000000000000 --- a/app/javascript/styles_new/mastodon/containers.scss +++ /dev/null @@ -1,166 +0,0 @@ -@use 'variables' as *; - -.container-alt { - width: 700px; - margin: 0 auto; - - @media screen and (width <= 740px) { - width: 100%; - margin: 0; - } -} - -.logo-container { - margin: 50px auto; - - h1 { - display: flex; - justify-content: center; - align-items: center; - - .logo { - height: 42px; - margin-inline-end: 10px; - } - - a { - display: flex; - justify-content: center; - align-items: center; - color: var(--color-text-primary); - text-decoration: none; - outline: 0; - padding: 12px 16px; - line-height: 32px; - font-weight: 500; - font-size: 14px; - } - } -} - -.compose-standalone { - .compose-form { - width: 400px; - margin: 0 auto; - padding: 10px 0; - padding-bottom: 20px; - box-sizing: border-box; - - @media screen and (width <= 400px) { - width: 100%; - padding: 20px; - } - } -} - -.account-header { - width: 400px; - margin: 0 auto; - display: flex; - align-items: center; - gap: 10px; - font-size: 14px; - line-height: 20px; - box-sizing: border-box; - padding: 20px 0; - margin-top: 40px; - margin-bottom: 10px; - border-bottom: 1px solid var(--color-border-primary); - - @media screen and (width <= 440px) { - width: 100%; - margin: 0; - padding: 20px; - } - - .avatar { - width: 48px; - height: 48px; - flex: 0 0 auto; - - img { - width: 100%; - height: 100%; - display: block; - margin: 0; - border-radius: var(--avatar-border-radius); - } - } - - .name { - flex: 1 1 auto; - color: var(--color-text-primary); - - .username { - display: block; - font-size: 16px; - line-height: 24px; - text-overflow: ellipsis; - overflow: hidden; - color: var(--color-text-primary); - } - } - - .logout-link { - display: block; - font-size: 32px; - line-height: 40px; - flex: 0 0 auto; - } -} - -.redirect { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100vh; - font-size: 14px; - line-height: 18px; - - &__logo { - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 30px; - - img { - height: 48px; - } - } - - &__message { - text-align: center; - - h1 { - font-size: 17px; - line-height: 22px; - font-weight: 700; - margin-bottom: 30px; - } - - p { - margin-bottom: 30px; - - &:last-child { - margin-bottom: 0; - } - } - - a { - color: var(--color-text-brand); - font-weight: 500; - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - } - } - - &__link { - margin-top: 15px; - } -} diff --git a/app/javascript/styles_new/mastodon/css_variables.scss b/app/javascript/styles_new/mastodon/css_variables.scss deleted file mode 100644 index b270bd337ff321..00000000000000 --- a/app/javascript/styles_new/mastodon/css_variables.scss +++ /dev/null @@ -1,228 +0,0 @@ -@use 'theme_utils' as utils; - -:root { - --color-black: #000; - --color-grey-950: #181821; - --color-grey-800: #292938; - --color-grey-700: #444664; - --color-grey-600: #545778; - --color-grey-500: #696d91; - --color-grey-400: #8b8dac; - --color-grey-300: #b4b6cb; - --color-grey-200: #d8d9e3; - --color-grey-100: #f0f0f5; - --color-grey-50: #f0f1ff; - --color-white: #fff; - --color-indigo-600: #6147e6; - --color-indigo-400: #8886ff; - --color-indigo-300: #a5abfd; - --color-indigo-200: #c8cdfe; - --color-indigo-100: #e0e3ff; - --color-indigo-50: #f0f1ff; - --color-red-500: #ff637e; - --color-red-600: #ec003f; - --color-yellow-400: #ffb900; - --color-yellow-600: #e17100; - --color-green-400: #05df72; - --color-green-600: #00a63e; - - /* TEXT TOKENS */ - - --color-text-primary: var(--color-grey-50); - --color-text-secondary: var(--color-grey-400); - --color-text-tertiary: var(--color-grey-500); - --color-text-on-inverted: var(--color-grey-950); - --color-text-brand: var(--color-indigo-400); - --color-text-brand-soft: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-brand) - ); - --color-text-on-brand-base: var(--color-white); - --color-text-error: var(--color-red-500); - --color-text-on-error-base: var(--color-white); - --color-text-warning: var(--color-yellow-400); - --color-text-on-warning-base: var(--color-white); - --color-text-success: var(--color-green-400); - --color-text-on-success-base: var(--color-white); - --color-text-disabled: var(--color-grey-600); - --color-text-on-disabled: var(--color-grey-400); - --color-text-bookmark-highlight: var(--color-text-error); - --color-text-favourite-highlight: var(--color-text-warning); - --color-text-on-media: var(--color-white); - --color-text-status-links: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - - /* BACKGROUND TOKENS */ - - // Neutrals - --color-bg-primary: var(--color-grey-950); - --overlay-strength-secondary: 10%; - --color-bg-secondary-base: var(--color-indigo-200); - --color-bg-secondary: #{utils.css-alpha( - var(--color-bg-secondary-base), - var(--overlay-strength-secondary) - )}; - --color-bg-secondary-solid: color-mix( - in srgb, - var(--color-bg-primary), - var(--color-bg-secondary-base) var(--overlay-strength-secondary) - ); - --color-bg-tertiary: color-mix( - in oklab, - var(--color-bg-primary), - var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary)) - ); - - // Utility - --color-bg-ambient: var(--color-bg-primary); - --color-bg-elevated: var(--color-bg-primary); - --color-bg-inverted: var(--color-grey-50); - --color-bg-media-base: var(--color-black); - --color-bg-media-strength: 65%; - --color-bg-media: #{utils.css-alpha( - var(--color-bg-media-base), - var(--color-bg-media-strength) - )}; - --color-bg-overlay: var(--color-black); - --color-bg-disabled: var(--color-grey-700); - - // Brand - --overlay-strength-brand: 10%; - --color-bg-brand-base: var(--color-indigo-600); - --color-bg-brand-base-hover: color-mix( - in oklab, - var(--color-bg-brand-base), - black var(--overlay-strength-brand) - ); - --color-bg-brand-soft: #{utils.css-alpha( - var(--color-bg-brand-base), - calc(var(--overlay-strength-brand) * 1.5) - )}; - --color-bg-brand-softer: #{utils.css-alpha( - var(--color-bg-brand-base), - var(--overlay-strength-brand) - )}; - - // Error - --overlay-strength-error: 12%; - --color-bg-error-base: var(--color-red-600); - --color-bg-error-base-hover: color-mix( - in oklab, - var(--color-bg-error-base), - black var(--overlay-strength-error) - ); - --color-bg-error-soft: #{utils.css-alpha( - var(--color-bg-error-base), - calc(var(--overlay-strength-error) * 1.5) - )}; - --color-bg-error-softer: #{utils.css-alpha( - var(--color-bg-error-base), - var(--overlay-strength-error) - )}; - - // Warning - --overlay-strength-warning: 10%; - --color-bg-warning-base: var(--color-yellow-600); - --color-bg-warning-base-hover: color-mix( - in oklab, - var(--color-bg-warning-base), - black var(--overlay-strength-warning) - ); - --color-bg-warning-soft: #{utils.css-alpha( - var(--color-bg-warning-base), - calc(var(--overlay-strength-warning) * 1.5) - )}; - --color-bg-warning-softer: #{utils.css-alpha( - var(--color-bg-warning-base), - var(--overlay-strength-warning) - )}; - - // Success - --overlay-strength-success: 15%; - --color-bg-success-base: var(--color-green-600); - --color-bg-success-base-hover: color-mix( - in oklab, - var(--color-bg-success-base), - black var(--overlay-strength-success) - ); - --color-bg-success-soft: #{utils.css-alpha( - var(--color-bg-success-base), - calc(var(--overlay-strength-success) * 1.5) - )}; - --color-bg-success-softer: #{utils.css-alpha( - var(--color-bg-success-base), - var(--overlay-strength-success) - )}; - - /* BORDER TOKENS */ - - --border-strength-primary: 18%; - --color-border-primary: #{utils.css-alpha( - var(--color-indigo-200), - var(--border-strength-primary) - )}; - --color-border-media: rgb(252 248 255 / 15%); - --color-border-on-bg-secondary: #{utils.css-alpha( - var(--color-indigo-200), - calc(var(--border-strength-primary) / 1.5) - )}; - --color-border-on-bg-brand-softer: var(--color-border-primary); - --color-border-on-bg-error-softer: #{utils.css-alpha( - var(--color-text-error), - 50% - )}; - --color-border-on-bg-warning-softer: #{utils.css-alpha( - var(--color-text-warning), - 50% - )}; - --color-border-on-bg-success-softer: #{utils.css-alpha( - var(--color-text-success), - 50% - )}; - --color-border-on-bg-inverted: var(--color-border-primary); - - /* SHADOW TOKENS */ - - --shadow-strength-primary: 80%; - --color-shadow-primary: #{utils.css-alpha( - var(--color-black), - var(--shadow-strength-primary) - )}; - --dropdown-shadow: - 0 20px 25px -5px var(--color-shadow-primary), - 0 8px 10px -6px var(--color-shadow-primary); - --overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary)); - - /* GRAPHS/CHARTS TOKENS */ - - --color-graph-primary-stroke: var(--color-text-brand); - --color-graph-primary-fill: var(--color-bg-brand-softer); - --color-graph-warning-stroke: var(--color-text-warning); - --color-graph-warning-fill: var(--color-bg-warning-softer); - --color-graph-disabled-stroke: var(--color-text-disabled); - --color-graph-disabled-fill: var(--color-bg-disabled); - - /* LEGACY TOKENS */ - - --rich-text-container-color: rgb(87 24 60 / 100%); - --rich-text-text-color: rgb(255 175 212 / 100%); - --rich-text-decorations-color: rgb(128 58 95 / 100%); - - /* MISCELLANEOUS */ - - --outline-focus-default: 2px solid var(--color-text-brand); - --avatar-border-radius: 8px; -} - -body { - // Variable for easily inverting directional UI elements, - --text-x-direction: 1; - - &.rtl { - --text-x-direction: -1; - } -} diff --git a/app/javascript/styles_new/mastodon/dashboard.scss b/app/javascript/styles_new/mastodon/dashboard.scss deleted file mode 100644 index db3f0e8a8425e9..00000000000000 --- a/app/javascript/styles_new/mastodon/dashboard.scss +++ /dev/null @@ -1,120 +0,0 @@ -@use 'variables' as *; - -.dashboard__counters { - display: flex; - flex-wrap: wrap; - margin: 0 -5px; - margin-bottom: 20px; - - & > div { - box-sizing: border-box; - flex: 0 0 33.333%; - padding: 0 5px; - margin-bottom: 10px; - - & > div, - & > a { - padding: 20px; - background: var(--color-bg-primary); - border-radius: 4px; - border: 1px solid var(--color-border-primary); - box-sizing: border-box; - height: 100%; - } - - & > a { - text-decoration: none; - color: inherit; - display: block; - - &:hover, - &:focus, - &:active { - background: var(--color-bg-brand-softer); - } - } - } - - &__num, - &__text { - text-align: center; - font-weight: 500; - font-size: 24px; - color: var(--color-text-primary); - margin-bottom: 20px; - line-height: 30px; - } - - &__text { - font-size: 18px; - } - - &__label { - font-size: 14px; - color: var(--color-text-secondary); - text-align: center; - font-weight: 500; - } -} - -.dashboard { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); - gap: 10px; - - @media screen and (width <= 1350px) { - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); - } - - &__item { - &--span-double-column { - grid-column: span 2; - } - - &--span-double-row { - grid-row: span 2; - } - - h4 { - padding-top: 20px; - } - } - - &__quick-access { - display: flex; - align-items: baseline; - border-radius: 4px; - background: var(--color-bg-brand-base); - color: var(--color-text-on-brand-base); - transition: all 100ms ease-in; - font-size: 14px; - padding: 8px 16px; - text-decoration: none; - margin-bottom: 4px; - - &:active, - &:focus, - &:hover { - background-color: var(--color-bg-brand-base-hover); - transition: all 200ms ease-out; - } - - &.positive { - background: var(--color-bg-success-softer); - color: var(--color-text-success); - } - - &.negative { - background: var(--color-bg-error-softer); - color: var(--color-text-error); - } - - span { - flex: 1 1 auto; - } - - strong { - font-weight: 700; - } - } -} diff --git a/app/javascript/styles_new/mastodon/emoji_picker.scss b/app/javascript/styles_new/mastodon/emoji_picker.scss deleted file mode 100644 index ad2f2f630db740..00000000000000 --- a/app/javascript/styles_new/mastodon/emoji_picker.scss +++ /dev/null @@ -1,248 +0,0 @@ -@use 'variables' as *; - -.emoji-mart { - font-size: 13px; - display: inline-block; - - &, - * { - box-sizing: border-box; - line-height: 1.15; - } - - .emoji-mart-emoji { - padding: 6px; - } -} - -.emoji-mart-bar { - &:first-child { - background: var(--color-bg-tertiary); - border-bottom: 1px solid var(--color-border-primary); - } -} - -.emoji-mart-anchors { - display: flex; - justify-content: space-between; - padding: 0 6px; - line-height: 0; -} - -.emoji-mart-anchor { - position: relative; - flex: 1; - text-align: center; - padding: 12px 4px; - overflow: hidden; - transition: color 0.1s ease-out; - cursor: pointer; - background: transparent; - border: 0; - color: var(--color-text-secondary); - - &:hover { - color: color-mix( - in oklab, - var(--color-text-primary), - var(--color-text-secondary) - ); - } -} - -.emoji-mart-anchor-selected { - color: var(--color-text-brand); - - &:hover { - color: var(--color-text-brand-soft); - } - - .emoji-mart-anchor-bar { - bottom: -1px; - } -} - -.emoji-mart-anchor-bar { - position: absolute; - bottom: -5px; - inset-inline-start: 0; - width: 100%; - height: 4px; - background-color: var(--color-text-brand); -} - -.emoji-mart-anchors { - i { - display: inline-block; - width: 100%; - max-width: 22px; - } - - svg { - fill: currentColor; - max-height: 18px; - } -} - -.emoji-mart-scroll { - overflow-y: scroll; - height: 270px; - max-height: 35vh; - padding: 0 6px 6px; - will-change: transform; -} - -.emoji-mart-search { - padding: 10px; - padding-inline-end: 45px; - position: relative; - - input { - font-size: 16px; - font-weight: 400; - padding: 7px 9px; - padding-inline-end: 25px; - font-family: inherit; - display: block; - width: 100%; - background: var(--color-bg-secondary); - color: var(--color-text-secondary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - - &::-moz-focus-inner { - border: 0; - } - - &:active, - &:focus { - outline: none !important; - border-width: 1px !important; - } - - &::-webkit-search-cancel-button { - display: none; - } - } -} - -.emoji-mart-search-icon { - position: absolute; - top: 18px; - inset-inline-end: 45px + 5px; - z-index: 2; - padding: 2px 5px 1px; - border: 0; - background: none; - transition: all 100ms linear; - transition-property: opacity; - pointer-events: auto; - - &:disabled { - cursor: default; - pointer-events: none; - } - - svg { - fill: currentColor; - } -} - -.emoji-mart-category .emoji-mart-emoji { - cursor: pointer; - - span { - z-index: 1; - position: relative; - text-align: center; - display: inline-flex !important; - align-items: center; - justify-content: center; - } - - &:hover::before { - z-index: -1; - content: ''; - position: absolute; - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100%; - background-color: var(--color-bg-brand-softer); - border-radius: 100%; - } -} - -.emoji-mart-category-label { - z-index: 2; - position: relative; - position: -webkit-sticky; - position: sticky; - top: 0; - - span { - display: block; - width: 100%; - font-weight: 500; - padding: 5px 6px; - } -} - -/* For screenreaders only, via https://stackoverflow.com/a/19758620 */ -.emoji-mart-sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip-path: inset(50%); - border: 0; -} - -.emoji-mart-category-list { - margin: 0; - padding: 0; -} - -.emoji-mart-category-list li { - list-style: none; - margin: 0; - padding: 0; - display: inline-block; -} - -.emoji-mart-emoji { - position: relative; - display: inline-block; - background: transparent; - border: 0; - padding: 0; - font-size: 0; - - span { - width: 22px; - height: 22px; - } -} - -.emoji-mart-no-results { - font-size: 14px; - color: var(--color-text-tertiary); - text-align: center; - padding: 5px 6px; - padding-top: 70px; - - .emoji-mart-no-results-label { - margin-top: 0.2em; - } - - .emoji-mart-emoji:hover::before { - cursor: default; - content: none; - } -} - -.emoji-mart-preview { - display: none; -} diff --git a/app/javascript/styles_new/mastodon/forms.scss b/app/javascript/styles_new/mastodon/forms.scss deleted file mode 100644 index 9f716c2126d836..00000000000000 --- a/app/javascript/styles_new/mastodon/forms.scss +++ /dev/null @@ -1,1449 +0,0 @@ -@use 'sass:color'; -@use 'variables' as *; - -code { - font-family: $font-monospace, monospace; - font-weight: 400; -} - -.form-container { - max-width: 450px; - padding: 20px; - padding-bottom: 50px; - margin: 50px auto; -} - -.form-section { - border-radius: 8px; - background: var(--color-bg-secondary); - padding: 24px; - margin-bottom: 24px; -} - -.fade-out-top { - position: relative; - overflow: hidden; - height: 160px; - max-width: 566px; - margin-inline: auto; - - &::after { - content: ''; - display: block; - background: linear-gradient( - to bottom, - var(--color-bg-secondary-solid), - transparent - ); - position: absolute; - top: 0; - inset-inline-start: 0; - width: 100%; - height: 100px; - pointer-events: none; - } - - & > div { - position: absolute; - inset-inline-start: 0; - bottom: 0; - } -} - -.indicator-icon { - display: flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - border-radius: 50%; - color: var(--color-text-primary); - - &.success { - color: var(--color-text-on-success-base); - background: var(--color-bg-success-base); - } - - &.failure { - color: var(--color-text-on-error-base); - background: var(--color-bg-error-base); - } -} - -.simple_form { - &.hidden { - display: none; - } - - .input { - margin-bottom: 16px; - overflow: hidden; - - &:last-child { - margin-bottom: 0; - } - - &__toolbar { - margin-top: 16px; - display: flex; - align-items: center; - gap: 16px; - - .character-counter { - flex: 0 0 auto; - } - } - - &.hidden { - margin: 0; - } - - &.radio_buttons { - .radio { - margin-bottom: 15px; - - &:last-child { - margin-bottom: 0; - } - } - - .radio > label { - position: relative; - padding-inline-start: 28px; - - input { - position: absolute; - top: -2px; - inset-inline-start: 0; - } - } - } - - &.boolean { - position: relative; - margin-bottom: 0; - - .label_input > label { - font-family: inherit; - font-size: 14px; - padding-top: 5px; - color: var(--color-text-primary); - display: block; - width: auto; - } - - .label_input, - .hint { - padding-inline-start: 28px; - } - - .label_input__wrapper { - position: static; - } - - label.checkbox { - position: absolute; - top: 2px; - inset-inline-start: 0; - } - - label a { - color: var(--color-text-brand); - text-decoration: underline; - - &:hover, - &:active, - &:focus { - text-decoration: none; - } - } - - .overridden, - .recommended, - .not_recommended { - position: absolute; - margin: 0 4px; - margin-top: -2px; - } - } - } - - .row { - display: flex; - margin: 0 -5px; - - .input { - box-sizing: border-box; - flex: 1 1 auto; - width: 50%; - padding: 0 5px; - } - } - - .title { - font-size: 28px; - line-height: 33px; - font-weight: 700; - margin-bottom: 15px; - } - - .lead { - font-size: 17px; - line-height: 22px; - color: var(--color-text-primary); - margin-bottom: 30px; - - &.invited-by { - margin-bottom: 15px; - } - - a { - color: var(--color-text-brand); - } - } - - .rules-list { - font-size: 17px; - line-height: 22px; - margin-bottom: 30px; - } - - .hint { - color: var(--color-text-secondary); - - a { - color: var(--color-text-brand); - } - - code { - border-radius: 3px; - padding: 0.2em 0.4em; - background: var(--color-bg-secondary); - } - - li { - list-style: disc; - margin-inline-start: 18px; - } - - .icon { - vertical-align: -3px; - } - } - - ul.hint { - margin-bottom: 15px; - } - - span.hint { - display: block; - font-size: 12px; - margin-top: 4px; - } - - p.hint { - margin-bottom: 15px; - color: var(--color-text-secondary); - - &.subtle-hint { - text-align: center; - font-size: 12px; - line-height: 18px; - margin-top: 15px; - margin-bottom: 0; - } - } - - .authentication-hint { - margin-bottom: 25px; - } - - .card { - margin-bottom: 15px; - } - - strong { - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - .input.with_floating_label { - .label_input { - display: flex; - - & > label { - font-family: inherit; - font-size: 14px; - color: var(--color-text-primary); - font-weight: 500; - min-width: 150px; - flex: 0 0 auto; - } - - input, - select { - flex: 1 1 auto; - } - } - - &.select .hint { - margin-top: 6px; - margin-inline-start: 150px; - } - } - - .input.with_label { - .label_input > label { - font-family: inherit; - font-size: 14px; - color: var(--color-text-primary); - display: block; - margin-bottom: 8px; - overflow-wrap: break-word; - font-weight: 500; - } - - .hint { - margin-top: 6px; - } - - ul { - flex: 390px; - } - } - - .input.with_block_label { - max-width: none; - - & > label { - font-family: inherit; - font-size: 14px; - color: var(--color-text-primary); - display: block; - font-weight: 600; - line-height: 20px; - } - - .hint { - line-height: 16px; - margin-bottom: 12px; - } - - ul { - columns: 2; - - @media screen and (max-width: $mobile-breakpoint) { - columns: 1; - } - } - } - - .input.with_block_label.user_role_permissions_as_keys ul { - columns: unset; - } - - .input.datetime .label_input, - .input.date .label_input { - display: flex; - gap: 4px; - align-items: center; - - select { - display: inline-block; - width: auto; - flex: 0; - } - } - - .input.date_of_birth .label_input { - display: flex; - gap: 8px; - align-items: center; - - input { - box-sizing: content-box; - width: 32px; - flex: 0; - - &:last-child { - width: 64px; - } - } - } - - .input.select.select--languages { - min-width: 32ch; - } - - .required abbr { - text-decoration: none; - color: var(--color-text-error); - } - - .fields-group { - margin-bottom: 25px; - - .input:last-child { - margin-bottom: 0; - } - - &__thumbnail { - display: block; - margin: 0; - margin-bottom: 10px; - max-width: 100%; - height: auto; - border-radius: var(--avatar-border-radius); - background: url('@/images/void.png'); - - &[src$='missing.png'] { - visibility: hidden; - } - - &:last-child { - margin-bottom: 0; - } - - &#account_avatar-preview { - width: 90px; - height: 90px; - object-fit: cover; - } - } - } - - .fields-row { - display: flex; - margin: 0 -10px; - padding-top: 5px; - margin-bottom: 25px; - - .input { - max-width: none; - } - - &__column { - box-sizing: border-box; - padding: 0 10px; - flex: 1 1 auto; - min-height: 1px; - - &-6 { - max-width: 50%; - } - - .actions { - margin-top: 27px; - } - } - - .fields-group:last-child, - .fields-row__column.fields-group { - margin-bottom: 0; - } - - @media screen and (max-width: $no-columns-breakpoint) { - display: block; - margin-bottom: 0; - - &__column { - max-width: none; - } - - .fields-group:last-child, - .fields-row__column.fields-group, - .fields-row__column { - margin-bottom: 25px; - } - } - - .fields-group.invited-by { - margin-bottom: 30px; - - .hint { - text-align: center; - } - } - } - - .input.radio_buttons .radio label { - margin-bottom: 5px; - font-family: inherit; - font-size: 14px; - color: var(--color-text-primary); - display: block; - width: auto; - } - - .check_boxes { - .checkbox { - label { - font-family: inherit; - font-size: 14px; - color: var(--color-text-primary); - display: inline-block; - width: auto; - position: relative; - padding-top: 5px; - padding-inline-start: 25px; - flex: 1 1 auto; - } - - input[type='checkbox'] { - position: absolute; - inset-inline-start: 0; - top: 5px; - margin: 0; - } - } - } - - .input.static .label_input__wrapper { - font-size: 14px; - padding: 10px; - border: 1px solid var(--color-border-primary); - border-radius: 4px; - } - - input[type='text'], - input[type='number'], - input[type='email'], - input[type='password'], - input[type='url'], - input[type='datetime-local'], - textarea { - box-sizing: border-box; - font-size: 14px; - line-height: 20px; - color: var(--color-text-primary); - display: block; - width: 100%; - outline: 0; - font-family: inherit; - resize: vertical; - background: var(--color-bg-secondary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - padding: 10px 16px; - - &:invalid { - box-shadow: none; - } - - &:required:valid { - border-color: var(--color-text-success); - } - - @media screen and (width <= 600px) { - font-size: 16px; - } - } - - input[type='text'], - input[type='number'], - input[type='email'], - input[type='password'], - input[type='datetime-local'] { - &:focus:invalid:not(:placeholder-shown), - &:required:invalid:not(:placeholder-shown) { - border-color: var(--color-text-error); - } - } - - .input.field_with_errors { - label { - color: var(--color-text-error); - } - - input[type='text'], - input[type='number'], - input[type='email'], - input[type='password'], - input[type='datetime-local'], - textarea, - select { - border-color: var(--color-text-error); - } - - .error { - display: block; - font-weight: 500; - color: var(--color-text-error); - margin-top: 4px; - } - } - - .input.disabled { - opacity: 0.5; - } - - .actions { - margin-top: 30px; - display: flex; - gap: 10px; - - &.actions--top { - margin-top: 0; - margin-bottom: 30px; - } - } - - .stacked-actions { - display: flex; - flex-direction: column; - gap: 10px; - margin-top: 30px; - margin-bottom: 15px; - } - - .btn { - display: block; - width: 100%; - border: 0; - border-radius: 4px; - background: var(--color-bg-brand-base); - color: var(--color-text-on-brand-base); - font-size: 15px; - line-height: 22px; - height: auto; - padding: 7px 18px; - text-decoration: none; - text-align: center; - box-sizing: border-box; - cursor: pointer; - font-weight: 500; - outline: 0; - - &:last-child { - margin-inline-end: 0; - } - - &:active, - &:focus, - &:hover { - background: var(--color-bg-brand-base-hover); - } - - &:disabled, - &:disabled:hover { - color: var(--color-text-on-disabled); - background: var(--color-bg-disabled); - } - - &.negative { - background: var(--color-bg-error-base); - - &:hover, - &:active, - &:focus { - background: var(--color-bg-error-base-hover); - } - } - } - - select { - appearance: none; - box-sizing: border-box; - font-size: 14px; - color: var(--color-text-primary); - display: block; - width: 100%; - outline: 0; - font-family: inherit; - resize: vertical; - background: var(--color-bg-secondary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - padding-inline-start: 10px; - padding-inline-end: 30px; - height: 41px; - - @media screen and (width <= 600px) { - font-size: 16px; - } - } - - h4 { - margin-bottom: 15px !important; - } - - .label_input { - position: relative; - - &__loading-indicator { - box-sizing: border-box; - position: absolute; - top: 0; - inset-inline-start: 0; - border: 1px solid transparent; - padding: 10px 16px; - width: 100%; - } - - &__wrapper { - position: relative; - } - - &__append { - position: absolute; - inset-inline-end: 3px; - top: 1px; - padding: 10px; - font-size: 14px; - color: var(--color-text-tertiary); - font-family: inherit; - pointer-events: none; - cursor: default; - max-width: 50%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - &::after { - content: ''; - display: block; - position: absolute; - top: 0; - inset-inline-end: 0; - bottom: 1px; - width: 5px; - } - } - } - - .status-card { - contain: unset; - } -} - -/* Double-chevron icon for custom select components */ -.select-wrapper, -.select .label_input__wrapper { - width: 100%; - - &::after { - --icon-size: 11px; - - content: ''; - position: absolute; - top: 0; - bottom: 0; - inset-inline-end: 9px; - width: var(--icon-size); - background-color: var(--color-text-tertiary); - pointer-events: none; - mask-image: url("data:image/svg+xml;utf8,"); - mask-position: right center; - mask-size: var(--icon-size); - mask-repeat: no-repeat; - } -} - -.block-icon { - display: block; - margin: 0 auto; - margin-bottom: 10px; - font-size: 24px; -} - -.flash-message { - color: var(--color-text-brand); - background: transparent; - border: 1px solid var(--color-text-brand); - border-radius: 4px; - padding: 15px 10px; - margin-bottom: 30px; - text-align: center; - - &.notice { - border: 1px solid var(--color-border-on-bg-success-softer); - background: var(--color-bg-success-softer); - color: var(--color-text-success); - } - - &.warning { - border: 1px solid var(--color-border-on-bg-warning-softer); - background: var(--color-bg-warning-softer); - color: var(--color-text-warning); - } - - &.alert { - border: 1px solid var(--color-border-on-bg-error-softer); - background: var(--color-bg-error-softer); - color: var(--color-text-error); - } - - &.hidden { - display: none; - } - - &.hidden-on-touch-devices { - @media screen and (pointer: coarse) { - display: none; - } - } - - a { - display: inline-block; - color: var(--color-text-secondary); - text-decoration: none; - - &:hover { - color: var(--color-text-primary); - text-decoration: underline; - } - } - - &.warning a { - font-weight: 700; - color: inherit; - text-decoration: underline; - - &:hover, - &:focus, - &:active { - text-decoration: none; - color: inherit; - } - } - - p { - margin-bottom: 15px; - } - - .oauth-code { - outline: 0; - box-sizing: border-box; - display: block; - width: 100%; - border: 0; - padding: 10px; - font-family: $font-monospace, monospace; - background: var(--color-bg-secondary); - color: var(--color-text-primary); - font-size: 14px; - margin: 0; - - &::-moz-focus-inner { - border: 0; - } - - &::-moz-focus-inner, - &:focus, - &:active { - outline: 0 !important; - } - - &:focus { - background: var(--color-bg-brand-softer); - } - } - - strong { - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - @media screen and (440px < width <= 740px) { - margin-top: 40px; - } - - &.translation-prompt { - text-align: unset; - color: unset; - - a { - text-decoration: underline; - } - } -} - -.flash-message-stack { - margin-bottom: 30px; - - .flash-message { - border-radius: 0; - margin-bottom: 0; - border-top-width: 0; - - &:first-child { - border-radius: 4px 4px 0 0; - border-top-width: 1px; - } - - &:last-child { - border-radius: 0 0 4px 4px; - - &:first-child { - border-radius: 4px; - } - } - } -} - -.form-footer { - margin-top: 30px; - text-align: center; - - a { - color: var(--color-text-secondary); - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } -} - -.quick-nav { - list-style: none; - margin-bottom: 25px; - font-size: 14px; - - li { - display: inline-block; - margin-inline-end: 10px; - } - - a { - color: var(--color-text-brand); - text-transform: uppercase; - text-decoration: none; - font-weight: 700; - - &:hover, - &:focus, - &:active { - color: var(--color-text-brand-soft); - } - } -} - -.oauth-prompt, -.follow-prompt { - margin-bottom: 30px; - color: var(--color-text-secondary); - - h2 { - font-size: 16px; - margin-bottom: 30px; - text-align: center; - } - - strong { - color: var(--color-text-primary); - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } -} - -.oauth-prompt { - h3 { - color: var(--color-text-primary); - font-size: 17px; - line-height: 22px; - font-weight: 500; - margin-bottom: 30px; - } - - p { - font-size: 14px; - line-height: 18px; - margin-bottom: 30px; - } - - .permissions-list { - border: 1px solid var(--color-border-primary); - border-radius: 4px; - background: var(--color-bg-secondary); - margin-bottom: 30px; - } - - .actions { - margin: 0 -10px; - display: flex; - - form { - box-sizing: border-box; - padding: 0 10px; - flex: 1 1 auto; - min-height: 1px; - width: 50%; - } - } -} - -.qr-wrapper { - display: flex; - flex-wrap: wrap; - align-items: flex-start; -} - -.qr-code { - flex: 0 0 auto; - background: white; - padding: 4px; - margin: 0 10px 20px 0; - box-shadow: 0 0 15px var(--color-shadow-primary); - display: inline-block; - - svg { - display: block; - margin: 0; - } -} - -.qr-alternative { - margin-bottom: 20px; - color: var(--color-text-primary); - flex: 150px; - - samp { - display: block; - font-size: 14px; - } -} - -.action-pagination { - display: flex; - flex-wrap: wrap; - align-items: center; - - .actions, - .pagination { - flex: 1 1 auto; - } - - .actions { - padding: 30px 0; - padding-inline-end: 20px; - flex: 0 0 auto; - } -} - -.post-follow-actions { - text-align: center; - color: var(--color-text-secondary); - - div { - margin-bottom: 4px; - } -} - -.alternative-login { - margin-top: 20px; - margin-bottom: 20px; - - h4 { - font-size: 16px; - color: var(--color-text-primary); - text-align: center; - margin-bottom: 20px; - border: 0; - padding: 0; - } - - .button { - display: block; - } -} - -.scope-danger { - color: var(--color-text-error); -} - -.form_admin_settings_site_short_description, -.form_admin_settings_site_description, -.form_admin_settings_site_extended_description, -.form_admin_settings_site_terms, -.form_admin_settings_custom_css, -.form_admin_settings_closed_registrations_message { - textarea { - font-family: $font-monospace, monospace; - } -} - -.input-copy { - color: var(--color-text-primary); - background: var(--color-bg-secondary); - border: 1px solid var(--color-border-primary); - border-radius: 4px; - display: flex; - align-items: center; - padding-inline-end: 4px; - position: relative; - top: 1px; - transition: border-color 300ms linear; - - &__wrapper { - flex: 1 1 auto; - } - - input[type='text'] { - background: transparent; - border: 0; - padding: 10px; - font-size: 14px; - font-family: $font-monospace, monospace; - } - - button { - flex: 0 0 auto; - margin: 4px; - text-transform: none; - font-weight: 400; - font-size: 14px; - padding: 7px 18px; - padding-bottom: 6px; - width: auto; - transition: background 300ms linear; - } - - &.copied { - border-color: var(--color-text-success); - transition: none; - - button { - background: var(--color-bg-success-base); - transition: none; - } - } -} - -.input.user_confirm_password, -.input.user_website { - &:not(.field_with_errors) { - display: none; - } -} - -.simple_form .h-captcha { - display: flex; - justify-content: center; - margin-bottom: 30px; -} - -.permissions-list { - &__item { - padding: 15px; - color: var(--color-text-primary); - border-bottom: 1px solid var(--color-border-primary); - display: flex; - align-items: center; - - &__text { - flex: 1 1 auto; - - &__title { - font-weight: 500; - } - - &__type { - color: var(--color-text-secondary); - overflow-wrap: anywhere; - } - } - - &__icon { - flex: 0 0 auto; - font-size: 18px; - width: 30px; - color: var(--color-text-success); - display: flex; - align-items: center; - } - - &:last-child { - border-bottom: 0; - } - } -} - -// Only remove padding when listing applications, to prevent styling issues on -// the Authorization page. -.applications-list { - .permissions-list__item:last-child { - padding-bottom: 0; - } -} - -.keywords-table { - thead { - th { - white-space: nowrap; - } - - th:first-child { - width: 100%; - } - } - - tfoot { - td { - border: 0; - } - } - - .input.string { - margin-bottom: 0; - } - - .label_input__wrapper { - margin-top: 10px; - } - - .table-action-link { - margin-top: 10px; - white-space: nowrap; - } -} - -.progress-tracker { - display: flex; - align-items: center; - padding-bottom: 30px; - margin-bottom: 30px; - - li { - flex: 0 0 auto; - position: relative; - } - - .separator { - height: 2px; - background: var(--color-border-primary); - flex: 1 1 auto; - - &.completed { - background: var(--color-text-brand); - } - } - - .circle { - box-sizing: border-box; - position: relative; - width: 30px; - height: 30px; - border-radius: 50%; - border: 2px solid var(--color-border-primary); - flex: 0 0 auto; - display: flex; - align-items: center; - justify-content: center; - - svg { - width: 16px; - } - } - - .label { - position: absolute; - font-size: 14px; - font-weight: 500; - color: var(--color-text-primary); - padding-top: 10px; - text-align: center; - width: 100px; - left: 50%; - transform: translateX(-50%); - } - - li:first-child .label { - inset-inline-start: 0; - inset-inline-end: auto; - text-align: start; - transform: none; - } - - li:last-child .label { - inset-inline-start: auto; - inset-inline-end: 0; - text-align: end; - transform: none; - } - - .active .circle { - border-color: var(--color-text-brand); - - &::before { - content: ''; - width: 10px; - height: 10px; - border-radius: 50%; - background: var(--color-text-brand); - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - } - } - - .completed .circle { - border-color: var(--color-text-brand); - background: var(--color-text-brand); - } -} - -.app-form { - padding: 16px; - - &__avatar-input, - &__header-input { - display: block; - border-radius: 8px; - background: var(--color-bg-secondary); - position: relative; - cursor: pointer; - - &:hover { - background-color: var(--color-bg-brand-softer); - } - - img { - position: absolute; - width: 100%; - height: 100%; - object-fit: cover; - border-radius: 8px; - z-index: 0; - } - - .icon { - position: absolute; - inset-inline-start: 50%; - top: 50%; - transform: translate(-50%, -50%); - color: var(--color-text-secondary); - z-index: 3; - } - - &.selected .icon { - color: var(--color-text-primary); - transform: none; - inset-inline-start: auto; - inset-inline-end: 8px; - top: auto; - bottom: 8px; - } - - &.invalid img { - outline: 1px solid var(--color-text-error); - outline-offset: -1px; - } - - &.invalid::before { - display: block; - content: ''; - width: 100%; - height: 100%; - position: absolute; - background: var(--color-bg-error-softer); - z-index: 2; - border-radius: 8px; - } - } - - &__avatar-input { - width: 80px; - height: 80px; - } - - &__header-input { - aspect-ratio: 580/193; - } - - &__toggle { - display: flex; - align-items: center; - gap: 16px; - color: var(--color-text-secondary); - font-size: 14px; - line-height: 20px; - - .icon { - flex: 0 0 auto; - } - - .icon { - width: 24px; - height: 24px; - } - - &__label { - flex: 1 1 auto; - - strong { - color: var(--color-text-primary); - font-weight: 600; - } - - .hint { - display: block; - font-size: 14px; - color: var(--color-text-secondary); - } - - .recommended { - position: absolute; - margin: 0 4px; - margin-top: -2px; - } - } - - &__toggle { - flex: 0 0 auto; - display: flex; - align-items: center; - } - - &__toggle > div { - display: flex; - border-inline-start: 1px solid var(--color-border-primary); - padding-inline-start: 16px; - } - } - - &__link { - display: flex; - gap: 16px; - padding: 8px 0; - align-items: center; - text-decoration: none; - color: var(--color-text-primary); - margin-bottom: 16px; - - &__text { - flex: 1 1 auto; - font-size: 14px; - line-height: 20px; - color: var(--color-text-secondary); - - strong { - font-weight: 600; - display: block; - color: var(--color-text-primary); - } - - .icon { - vertical-align: -5px; - width: 20px; - height: 20px; - } - } - } -} diff --git a/app/javascript/styles_new/mastodon/lists.scss b/app/javascript/styles_new/mastodon/lists.scss deleted file mode 100644 index 6019cd800283d7..00000000000000 --- a/app/javascript/styles_new/mastodon/lists.scss +++ /dev/null @@ -1,19 +0,0 @@ -.no-list { - list-style: none; - - li { - display: inline-block; - margin: 0 5px; - } -} - -.recovery-codes { - list-style: none; - margin: 0 auto; - - li { - font-size: 125%; - line-height: 1.5; - letter-spacing: 1px; - } -} diff --git a/app/javascript/styles_new/mastodon/modal.scss b/app/javascript/styles_new/mastodon/modal.scss deleted file mode 100644 index 6af2a182b63449..00000000000000 --- a/app/javascript/styles_new/mastodon/modal.scss +++ /dev/null @@ -1,53 +0,0 @@ -@use 'variables' as *; - -.modal-layout { - background: var(--color-bg-brand-softer); - display: flex; - flex-direction: column; - height: 100vh; - padding: 0; -} - -.modal-layout__mastodon { - display: flex; - flex: 1; - flex-direction: column; - justify-content: flex-end; - - > div { - flex: 1; - max-height: 235px; - position: relative; - - img { - max-height: 100%; - max-width: 100%; - height: 100%; - position: absolute; - bottom: 0; - inset-inline-start: 0; - } - } -} - -@media screen and (width <= 600px) { - .account-header { - margin-top: 0; - } -} - -.with-zig-zag-decoration { - &::after { - content: ''; - position: absolute; - inset: auto 0 0; - height: 32px; - background-color: var(--color-bg-brand-softer); - - /* Decorative zig-zag pattern at the bottom of the page */ - mask-image: url('data:image/svg+xml;utf8,'); - mask-position: bottom; - mask-repeat: repeat-x; - z-index: -1; - } -} diff --git a/app/javascript/styles_new/mastodon/polls.scss b/app/javascript/styles_new/mastodon/polls.scss deleted file mode 100644 index 19fb8dd505f72b..00000000000000 --- a/app/javascript/styles_new/mastodon/polls.scss +++ /dev/null @@ -1,232 +0,0 @@ -@use 'sass:color'; -@use 'variables' as *; - -.poll { - margin-top: 16px; - font-size: 14px; - - li { - margin-bottom: 10px; - position: relative; - } - - &__chart { - border-radius: 4px; - display: block; - background: rgb(from var(--color-text-brand) r g b / 60%); - height: 5px; - min-width: 1%; - - &.leading { - background: var(--color-text-brand); - } - } - - progress { - border: 0; - display: block; - width: 100%; - height: 5px; - appearance: none; - background: transparent; - - &::-webkit-progress-bar { - background: transparent; - } - - // Those rules need to be entirely separate or they won't work, hence the - // duplication - &::-moz-progress-bar { - border-radius: 4px; - background: rgb(from var(--color-text-brand) r g b / 60%); - } - - &::-webkit-progress-value { - border-radius: 4px; - background: rgb(from var(--color-text-brand) r g b / 60%); - } - } - - &__option { - position: relative; - display: flex; - align-items: flex-start; - gap: 8px; - padding: 6px 0; - line-height: 18px; - cursor: default; - overflow: hidden; - - &__text { - display: inline-block; - overflow-wrap: break-word; - max-width: calc(100% - 45px - 25px); - } - - input[type='radio'], - input[type='checkbox'] { - display: none; - } - - .autosuggest-input { - flex: 1 1 auto; - } - - input[type='text'] { - display: block; - box-sizing: border-box; - width: 100%; - font-size: 14px; - color: var(--color-text-primary); - outline: 0; - font-family: inherit; - background: var(--color-bg-primary); - border: 1px solid var(--color-text-secondary); - border-radius: 4px; - padding: 8px 12px; - - &:focus { - border-color: var(--color-text-brand); - } - - @media screen and (width <= 600px) { - font-size: 16px; - line-height: 24px; - letter-spacing: 0.5px; - } - } - - &.selectable { - cursor: pointer; - } - - &.editable, - &.disabled { - align-items: center; - overflow: visible; - } - } - - &__input { - display: block; - position: relative; - border: 1px solid var(--color-text-secondary); - box-sizing: border-box; - width: 17px; - height: 17px; - border-radius: 50%; - flex: 0 0 auto; - - &.checkbox { - border-radius: 4px; - } - - &:active, - &:focus, - &:hover { - border-color: var(--color-text-success); - border-width: 4px; - } - - &.active { - background-color: var(--color-bg-success-base); - border-color: var(--color-text-success); - } - - &::-moz-focus-inner { - outline: 0 !important; - border: 0; - } - - &:focus, - &:active { - outline: 0 !important; - } - - &.disabled { - border-color: var(--color-text-disabled); - - &.active { - background: var(--color-text-disabled); - } - - &:active, - &:focus, - &:hover { - border-color: var(--color-text-disabled); - border-width: 1px; - } - } - } - - &__option.editable &__input, - &__option.disabled &__input { - &:active, - &:focus, - &:hover { - border-color: var(--color-text-primary); - border-width: 1px; - } - } - - &__number { - display: inline-block; - width: 45px; - font-weight: 700; - flex: 0 0 45px; - } - - &__voted { - padding: 0 5px; - display: inline-block; - - &__mark { - font-size: 18px; - } - } - - &__footer { - padding-top: 6px; - padding-bottom: 5px; - color: var(--color-text-tertiary); - } - - &__link { - display: inline; - background: transparent; - padding: 0; - margin: 0; - border: 0; - color: var(--color-text-tertiary); - text-decoration: underline; - font-size: inherit; - - &:hover { - text-decoration: none; - } - - &:active, - &:focus { - background-color: var(--color-bg-secondary); - } - } - - .button { - height: 36px; - padding: 0 16px; - margin-inline-end: 10px; - font-size: 14px; - } -} - -.muted .poll { - color: var(--color-text-tertiary); - - &__chart { - background: rgb(from var(--color-text-brand) r g b / 40%); - - &.leading { - background: rgb(from var(--color-text-brand) r g b / 60%); - } - } -} diff --git a/app/javascript/styles_new/mastodon/reset.scss b/app/javascript/styles_new/mastodon/reset.scss deleted file mode 100644 index 3644b94cdf68a0..00000000000000 --- a/app/javascript/styles_new/mastodon/reset.scss +++ /dev/null @@ -1,58 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} - -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} - -body { - line-height: 1; -} - -ol, ul { - list-style: none; -} - -blockquote, q { - quotes: none; -} - -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -html:has(body.custom-scrollbars) { - scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary); -} diff --git a/app/javascript/styles_new/mastodon/rich_text.scss b/app/javascript/styles_new/mastodon/rich_text.scss deleted file mode 100644 index 4ac3f025fec261..00000000000000 --- a/app/javascript/styles_new/mastodon/rich_text.scss +++ /dev/null @@ -1,116 +0,0 @@ -.status__content__text, -.e-content, -.edit-indicator__content, -.reply-indicator__content { - code { - background: var(--rich-text-container-color); - padding: 4px; - border-radius: 4px; - color: var(--rich-text-text-color); - font-size: 0.85em; - } - - pre { - background: var(--rich-text-container-color); - padding: 8px; - border-radius: 4px; - color: var(--rich-text-text-color); - - code { - padding: 0; - background: transparent; - } - } - - pre, - blockquote { - margin-bottom: 22px; - white-space: pre-wrap; - unicode-bidi: plaintext; - - &:last-child { - margin-bottom: 0; - } - } - - blockquote { - padding-inline-start: 32px; - color: var(--rich-text-text-color); - white-space: normal; - position: relative; - - &::before { - display: block; - content: ''; - width: 24px; - height: 20px; - mask-image: url('@/images/quote.svg'); - background-color: var(--rich-text-decorations-color); - position: absolute; - inset-inline-start: 0; - top: 0; - } - - blockquote { - margin-top: 4px; - border-inline-start: 3px solid var(--rich-text-decorations-color); - padding-inline-start: 16px; - - &::before { - display: none; - } - } - - p:last-of-type { - margin-bottom: 0; - } - } - - & > ul, - & > ol { - margin-bottom: 22px; - - &:last-child { - margin-bottom: 0; - } - } - - b, - strong { - font-weight: 700; - } - - em, - i { - font-style: italic; - } - - ul, - ol { - padding-inline-start: 24px; - - li { - padding-inline-start: 8px; - - &::marker { - text-align: end; - } - } - - p { - margin: 0; - } - } - - ul { - list-style-type: '•'; - - li::marker { - text-align: start; - } - } - - ol { - list-style-type: decimal; - } -} diff --git a/app/javascript/styles_new/mastodon/rtl.scss b/app/javascript/styles_new/mastodon/rtl.scss deleted file mode 100644 index 48935b75d74a97..00000000000000 --- a/app/javascript/styles_new/mastodon/rtl.scss +++ /dev/null @@ -1,50 +0,0 @@ -@use 'variables' as *; - -body.rtl { - direction: rtl; - - .reactions-bar { - direction: rtl; - } - - .announcements__mastodon, - .drawer__inner__mastodon > img { - transform: scaleX(-1); - } - - .compose-form .autosuggest-textarea__textarea { - padding-right: 10px; - padding-left: 10px + 22px; - } - - .columns-area { - direction: rtl; - } - - .account__avatar-wrapper { - float: right; - } - - .column-header__setting-arrows { - float: left; - } - - .admin-wrapper { - direction: rtl; - } - - .react-swipeable-view-container > * { - direction: rtl; - } - - .column-back-button__icon { - transform: scale(-1, 1); - } - - .dismissable-banner, - .warning-banner { - &__action { - float: left; - } - } -} diff --git a/app/javascript/styles_new/mastodon/tables.scss b/app/javascript/styles_new/mastodon/tables.scss deleted file mode 100644 index 8e303aff685c1e..00000000000000 --- a/app/javascript/styles_new/mastodon/tables.scss +++ /dev/null @@ -1,375 +0,0 @@ -@use 'variables' as *; - -.table { - width: 100%; - max-width: 100%; - border-spacing: 0; - border-collapse: collapse; - - th, - td { - padding: 8px; - line-height: 18px; - vertical-align: top; - border-bottom: 1px solid var(--color-border-primary); - text-align: start; - background: var(--color-bg-primary); - - &.critical { - font-weight: 700; - color: var(--color-text-warning); - } - } - - & > thead > tr > th { - vertical-align: bottom; - font-weight: 500; - } - - & > tbody > tr > th { - font-weight: 500; - } - - & > tbody > tr:nth-child(odd) > td, - & > tbody > tr:nth-child(odd) > th { - background: var(--color-bg-primary); - } - - & > tbody > tr:last-child > td, - & > tbody > tr:last-child > th { - border-bottom: 0; - } - - a { - color: var(--color-text-secondary); - text-decoration: none; - - &:hover { - color: var(--color-text-brand); - } - } - - strong { - font-weight: 500; - - @each $lang in $cjk-langs { - &:lang(#{$lang}) { - font-weight: 700; - } - } - } - - &.inline-table { - & > tbody > tr:nth-child(odd) { - & > td, - & > th { - background: transparent; - } - } - - & > tbody > tr:first-child { - & > td, - & > th { - border-top: 0; - } - } - } - - &.horizontal-table { - border-collapse: collapse; - border-style: hidden; - - & > tbody > tr > th, - & > tbody > tr > td { - padding: 11px 10px; - background: transparent; - border: 1px solid var(--color-border-primary); - color: var(--color-text-primary); - } - - & > tbody > tr > th { - color: var(--color-text-secondary); - font-weight: 600; - } - } - - &.batch-table { - & > thead > tr > th { - background: var(--color-bg-primary); - border-top: 1px solid var(--color-border-primary); - border-bottom: 1px solid var(--color-border-primary); - - &:first-child { - border-radius: 4px 0 0; - border-inline-start: 1px solid var(--color-border-primary); - } - - &:last-child { - border-radius: 0 4px 0 0; - border-inline-end: 1px solid var(--color-border-primary); - } - } - } - - &--invites tbody td { - vertical-align: middle; - } -} - -.table-wrapper { - overflow: auto; - margin-bottom: 20px; -} - -samp { - font-family: $font-monospace, monospace; -} - -button.table-action-link { - background: transparent; - border: 0; - font: inherit; -} - -button.table-action-link, -a.table-action-link { - text-decoration: none; - display: inline-block; - margin-inline-end: 5px; - padding: 0 10px; - color: var(--color-text-secondary); - font-weight: 500; - white-space: nowrap; - - &:hover { - color: var(--color-text-brand); - } - - &:first-child { - padding-inline-start: 0; - } -} - -.batch-table { - &--no-toolbar { - .batch-table__toolbar { - position: static; - height: 4px; - border-bottom: none; - } - } - - &__toolbar, - &__row { - display: flex; - - &__select { - box-sizing: border-box; - padding: 8px 16px; - cursor: pointer; - min-height: 100%; - - input { - margin-top: 8px; - } - - &--aligned { - display: flex; - align-items: center; - - input { - margin-top: 0; - } - } - } - - &__actions, - &__content { - padding: 8px 0; - padding-inline-end: 16px; - flex: 1 1 auto; - } - } - - &__toolbar { - position: sticky; - top: 0; - z-index: 200; - border: 1px solid var(--color-border-primary); - background: var(--color-bg-primary); - border-radius: 4px 4px 0 0; - height: 47px; - align-items: center; - - &__actions { - text-align: end; - padding-inline-end: 16px - 5px; - - .table-action-link { - padding: 0; - } - } - } - - &__select-all { - background: var(--color-bg-primary); - height: 47px; - align-items: center; - justify-content: center; - border: 1px solid var(--color-border-primary); - border-top: 0; - color: var(--color-text-primary); - display: none; - - &.active { - display: flex; - } - - .selected, - .not-selected { - display: none; - - &.active { - display: block; - } - } - - strong { - font-weight: 700; - } - - span { - padding: 8px; - display: inline-block; - } - - button { - background: transparent; - border: 0; - font: inherit; - color: var(--color-text-brand); - border-radius: 4px; - font-weight: 700; - padding: 8px; - - &:hover, - &:focus, - &:active { - background: var(--color-bg-secondary); - } - } - } - - &__form { - padding: 16px; - border: 1px solid var(--color-border-primary); - border-top: 0; - background: var(--color-bg-primary); - - .fields-row { - padding-top: 0; - margin-bottom: 0; - } - } - - &__row { - border: 1px solid var(--color-border-primary); - border-top: 0; - background: var(--color-bg-primary); - - @media screen and (max-width: $no-gap-breakpoint) { - .optional &:first-child { - border-top: 1px solid var(--color-border-primary); - } - } - - &:last-child { - border-radius: 0 0 4px 4px; - } - - &__content { - padding-top: 12px; - padding-bottom: 16px; - overflow: hidden; - - &--unpadded { - padding: 0; - } - - &--padded { - padding: 12px 16px 16px; - } - - &--with-image { - display: flex; - align-items: center; - } - - &__image { - flex: 0 0 auto; - display: flex; - justify-content: center; - align-items: center; - margin-inline-end: 10px; - - .emojione { - width: 32px; - height: 32px; - } - } - - &__text { - flex: 1 1 auto; - } - - &__quote { - padding: 12px; - padding-top: 0; - } - - &__extra { - flex: 0 0 auto; - text-align: end; - color: var(--color-text-secondary); - font-weight: 500; - } - } - - .directory__tag { - margin: 0; - width: 100%; - - a { - background: transparent; - border-radius: 0; - } - } - } - - &.optional .batch-table__toolbar, - &.optional .batch-table__row__select { - @media screen and (max-width: $no-gap-breakpoint) { - display: none; - } - } - - // Reset the status card to not have borders, background or padding when - // inline in the table of statuses - .batch-table__row__content > .status__card { - border: none; - background: none; - padding: 0; - } - - @media screen and (width <= 870px) { - .accounts-table tbody td.optional { - display: none; - } - } -} - -.one-liner { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} diff --git a/app/javascript/styles_new/mastodon/widgets.scss b/app/javascript/styles_new/mastodon/widgets.scss deleted file mode 100644 index 69c79cd1e6f2a0..00000000000000 --- a/app/javascript/styles_new/mastodon/widgets.scss +++ /dev/null @@ -1,178 +0,0 @@ -@use 'sass:color'; -@use 'variables' as *; - -.directory { - &__tag { - box-sizing: border-box; - margin-bottom: 10px; - - & > a, - & > div { - display: flex; - align-items: center; - justify-content: space-between; - border: 1px solid var(--color-border-primary); - border-radius: 4px; - padding: 15px; - text-decoration: none; - color: inherit; - box-shadow: 0 0 15px var(--color-shadow-primary); - } - - & > a { - &:hover, - &:active, - &:focus { - background: var(--color-bg-primary); - } - } - - &.active > a { - background: var(--color-bg-brand-base); - cursor: default; - } - - &.disabled > div { - opacity: 0.5; - cursor: default; - } - - h4 { - flex: 1 1 auto; - font-size: 18px; - font-weight: 700; - color: var(--color-text-primary); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - small { - display: block; - font-weight: 400; - font-size: 15px; - margin-top: 8px; - color: var(--color-text-secondary); - } - } - - &.active h4 { - &, - small, - .trends__item__current { - color: var(--color-text-primary); - } - } - - .avatar-stack { - flex: 0 0 auto; - width: (36px + 4px) * 3; - } - - &.active .avatar-stack .account__avatar { - border-color: var(--color-text-brand); - } - - .trends__item__current { - padding-inline-end: 0; - } - } -} - -.accounts-table { - width: 100%; - - .account { - max-width: calc(56px + 30ch); - padding: 0; - border: 0; - } - - strong { - font-weight: 700; - } - - thead th { - text-align: center; - text-transform: uppercase; - color: var(--color-text-secondary); - font-weight: 700; - padding: 10px; - - &:first-child { - text-align: start; - } - } - - tbody td { - padding: 15px 0; - vertical-align: middle; - border-bottom: 1px solid var(--color-border-primary); - } - - tbody tr:last-child td { - border-bottom: 0; - } - - &__count { - width: 120px; - text-align: center; - font-size: 15px; - font-weight: 500; - color: var(--color-text-primary); - - small { - display: block; - color: var(--color-text-secondary); - font-weight: 400; - font-size: 14px; - } - } - - tbody td.accounts-table__extra { - width: 120px; - text-align: end; - color: var(--color-text-secondary); - padding-inline-end: 16px; - - a { - text-decoration: none; - color: inherit; - - &:focus, - &:hover, - &:active { - color: var(--color-text-brand); - } - } - } - - &__comment { - width: 50%; - vertical-align: initial !important; - } - - tbody td.accounts-table__interrelationships { - width: 21px; - padding-inline-end: 16px; - } - - .icon { - &.active { - color: var(--color-text-brand); - } - - &.passive { - color: var(--color-text-warning); - } - - &.active.passive { - color: var(--color-text-success); - } - } - - @media screen and (max-width: $no-gap-breakpoint) { - tbody td.optional { - display: none; - } - } -} diff --git a/config/vite/plugin-mastodon-themes.ts b/config/vite/plugin-mastodon-themes.ts index 98bda4cab0eaf2..a8d75b25f9565e 100644 --- a/config/vite/plugin-mastodon-themes.ts +++ b/config/vite/plugin-mastodon-themes.ts @@ -40,14 +40,7 @@ export function MastodonThemes(): Plugin { // Get all files mentioned in the themes.yml file. const themes = await loadThemesFromConfig(projectRoot); - const allThemes = { - ...themes, - default_theme_tokens: 'styles_new/application.scss', - 'mastodon-light_theme_tokens': 'styles_new/mastodon-light.scss', - contrast_theme_tokens: 'styles_new/contrast.scss', - }; - - for (const [themeName, themePath] of Object.entries(allThemes)) { + for (const [themeName, themePath] of Object.entries(themes)) { entrypoints[`themes/${themeName}`] = path.resolve(jsRoot, themePath); } @@ -70,11 +63,7 @@ export function MastodonThemes(): Plugin { // Rewrite the URL to the entrypoint if it matches a theme. if (isThemeFile(req.url ?? '', themes)) { const themeName = pathToThemeName(req.url ?? ''); - const themePath = `/packs-dev/${themes[themeName]}`; - const isThemeTokenRequest = req.url.includes('_theme_tokens'); - req.url = isThemeTokenRequest - ? themePath.replace('styles/', 'styles_new/') - : themePath; + req.url = `/packs-dev/${themes[themeName]}`; } next(); }); @@ -87,7 +76,7 @@ export function MastodonThemes(): Plugin { const themePathToName = new Map( Object.entries(themes).map(([themeName, themePath]) => [ path.resolve(jsRoot, themePath), - `/themes/${areThemeTokensEnabled() ? `${themeName}_theme_tokens` : themeName}`, + `/themes/${themeName}`, ]), ); const themeNames = new Set(); @@ -150,7 +139,6 @@ async function loadThemesFromConfig(root: string) { console.warn(`Invalid theme path "${themePath}" in themes.yml, skipping`); continue; } - themes[themeName] = themePath; } @@ -162,7 +150,7 @@ async function loadThemesFromConfig(root: string) { } function pathToThemeName(file: string) { - const basename = path.basename(file.replace('_theme_tokens', '')); + const basename = path.basename(file); return basename.split(/[.?]/)[0] ?? ''; } @@ -174,12 +162,3 @@ function isThemeFile(file: string, themes: Themes) { const basename = pathToThemeName(file); return basename in themes; } - -function areThemeTokensEnabled() { - const raw = process.env.EXPERIMENTAL_FEATURES ?? ''; - const features = raw - .split(',') - .map((s) => s.trim()) - .filter(Boolean); - return features.includes('theme_tokens'); -} diff --git a/stylelint.config.js b/stylelint.config.js index 29fe8262aa1244..b1f34b7f19061b 100644 --- a/stylelint.config.js +++ b/stylelint.config.js @@ -2,7 +2,6 @@ module.exports = { extends: ['stylelint-config-standard-scss', 'stylelint-config-prettier-scss'], ignoreFiles: [ 'app/javascript/styles/mastodon/reset.scss', - 'app/javascript/styles_new/mastodon/reset.scss', 'coverage/**/*', 'node_modules/**/*', 'public/assets/**/*', @@ -32,7 +31,7 @@ module.exports = { }, overrides: [ { - 'files': ['app/javascript/styles/entrypoints/mailer.scss', 'app/javascript/styles_new/entrypoints/mailer.scss'], + 'files': ['app/javascript/styles/entrypoints/mailer.scss'], rules: { 'property-no-unknown': [ true, From 801672e3cbf8a42e823fafa5792c4b79740f7be2 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 28 Nov 2025 17:19:23 +0100 Subject: [PATCH 062/123] Add method to quickly test for AnnualReport account eligibility (#37045) --- app/lib/annual_report.rb | 6 ++++++ app/lib/annual_report/source.rb | 4 ++++ app/lib/annual_report/top_hashtags.rb | 4 ++++ app/lib/annual_report/top_statuses.rb | 4 ++++ 4 files changed, 18 insertions(+) diff --git a/app/lib/annual_report.rb b/app/lib/annual_report.rb index 8fab2111ed6098..035dc4cde75c31 100644 --- a/app/lib/annual_report.rb +++ b/app/lib/annual_report.rb @@ -23,6 +23,12 @@ def initialize(account, year) @year = year end + def eligible? + with_read_replica do + SOURCES.all? { |klass| klass.new(@account, @year).eligible? } + end + end + def generate return if GeneratedAnnualReport.exists?(account: @account, year: @year) diff --git a/app/lib/annual_report/source.rb b/app/lib/annual_report/source.rb index 7f486553694a9a..2898c6a5a265fb 100644 --- a/app/lib/annual_report/source.rb +++ b/app/lib/annual_report/source.rb @@ -12,6 +12,10 @@ def generate raise NotImplementedError end + def eligible? + true + end + protected def report_statuses diff --git a/app/lib/annual_report/top_hashtags.rb b/app/lib/annual_report/top_hashtags.rb index a775c29bac3298..f3a2160341d6bf 100644 --- a/app/lib/annual_report/top_hashtags.rb +++ b/app/lib/annual_report/top_hashtags.rb @@ -15,6 +15,10 @@ def generate } end + def eligible? + report_statuses.joins(:tags).exists? + end + private def top_hashtags diff --git a/app/lib/annual_report/top_statuses.rb b/app/lib/annual_report/top_statuses.rb index f32bd09a15aee1..4dcc31892bbf23 100644 --- a/app/lib/annual_report/top_statuses.rb +++ b/app/lib/annual_report/top_statuses.rb @@ -11,6 +11,10 @@ def generate } end + def eligible? + report_statuses.public_visibility.exists? + end + private def status_identifier(status) From 9334bd9ede642ecce8383f01c25a20b024614b6e Mon Sep 17 00:00:00 2001 From: diondiondion Date: Fri, 28 Nov 2025 17:19:23 +0100 Subject: [PATCH 063/123] Don't reset scroll when using hotkeys to focus columns, add hotkey `0` to scroll to top (#37052) --- .../mastodon/components/hotkeys/index.tsx | 1 + .../features/keyboard_shortcuts/index.jsx | 8 +- app/javascript/mastodon/features/ui/index.jsx | 19 ++-- .../mastodon/features/ui/util/focusUtils.ts | 93 ++++++++++++------- app/javascript/mastodon/locales/en.json | 1 + 5 files changed, 78 insertions(+), 44 deletions(-) diff --git a/app/javascript/mastodon/components/hotkeys/index.tsx b/app/javascript/mastodon/components/hotkeys/index.tsx index 81ca28eb872fa8..c62fc0c20acd77 100644 --- a/app/javascript/mastodon/components/hotkeys/index.tsx +++ b/app/javascript/mastodon/components/hotkeys/index.tsx @@ -111,6 +111,7 @@ const hotkeyMatcherMap = { openProfile: just('p'), moveDown: just('j'), moveUp: just('k'), + moveToTop: just('0'), toggleHidden: just('x'), toggleSensitive: just('h'), toggleComposeSpoilers: optionPlus('x'), diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx index 01a4f0e1fd95c4..8a6ebe6def861a 100644 --- a/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx +++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx @@ -95,13 +95,17 @@ class KeyboardShortcuts extends ImmutablePureComponent { - l - + 0 + 1-9 + + l + + n diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx index 476c25e32dab28..0d731068081718 100644 --- a/app/javascript/mastodon/features/ui/index.jsx +++ b/app/javascript/mastodon/features/ui/index.jsx @@ -78,7 +78,7 @@ import { Quotes, } from './util/async-components'; import { ColumnsContextProvider } from './util/columns_context'; -import { focusColumn, getFocusedItemIndex, focusItemSibling } from './util/focusUtils'; +import { focusColumn, getFocusedItemIndex, focusItemSibling, focusFirstItem } from './util/focusUtils'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; // Dummy import, to make sure that ends up in the application bundle. @@ -449,20 +449,21 @@ class UI extends PureComponent { }; handleHotkeyFocusColumn = e => { - focusColumn({index: e.key * 1}); + focusColumn(e.key * 1); }; handleHotkeyLoadMore = () => { document.querySelector('.load-more')?.focus(); }; + handleMoveToTop = () => { + focusFirstItem(); + }; + handleMoveUp = () => { const currentItemIndex = getFocusedItemIndex(); if (currentItemIndex === -1) { - focusColumn({ - index: 1, - focusItem: 'first-visible', - }); + focusColumn(1); } else { focusItemSibling(currentItemIndex, -1); } @@ -471,10 +472,7 @@ class UI extends PureComponent { handleMoveDown = () => { const currentItemIndex = getFocusedItemIndex(); if (currentItemIndex === -1) { - focusColumn({ - index: 1, - focusItem: 'first-visible', - }); + focusColumn(1); } else { focusItemSibling(currentItemIndex, 1); } @@ -562,6 +560,7 @@ class UI extends PureComponent { focusLoadMore: this.handleHotkeyLoadMore, moveDown: this.handleMoveDown, moveUp: this.handleMoveUp, + moveToTop: this.handleMoveToTop, back: this.handleHotkeyBack, goToHome: this.handleHotkeyGoToHome, goToNotifications: this.handleHotkeyGoToNotifications, diff --git a/app/javascript/mastodon/features/ui/util/focusUtils.ts b/app/javascript/mastodon/features/ui/util/focusUtils.ts index a728a3c5eb293f..f140cb0b3560f5 100644 --- a/app/javascript/mastodon/features/ui/util/focusUtils.ts +++ b/app/javascript/mastodon/features/ui/util/focusUtils.ts @@ -1,16 +1,30 @@ -interface FocusColumnOptions { - index?: number; - focusItem?: 'first' | 'first-visible'; +/** + * Out of a list of elements, return the first one whose top edge + * is inside of the viewport, and return the element and its BoundingClientRect. + */ +function findFirstVisibleWithRect( + items: HTMLElement[], +): { item: HTMLElement; rect: DOMRect } | null { + const viewportHeight = + window.innerHeight || document.documentElement.clientHeight; + + for (const item of items) { + const rect = item.getBoundingClientRect(); + const isVisible = rect.top >= 0 && rect.top < viewportHeight; + + if (isVisible) { + return { item, rect }; + } + } + + return null; } /** * Move focus to the column of the passed index (1-based). - * Can either focus the topmost item or the first one in the viewport + * Focus is placed on the topmost visible item */ -export function focusColumn({ - index = 1, - focusItem = 'first', -}: FocusColumnOptions = {}) { +export function focusColumn(index = 1) { // Skip the leftmost drawer in multi-column mode const isMultiColumnLayout = !!document.querySelector( 'body.layout-multiple-columns', @@ -27,33 +41,28 @@ export function focusColumn({ if (!container) return; - let itemToFocus: HTMLElement | null = null; - - if (focusItem === 'first-visible') { - const focusableItems = Array.from( - container.querySelectorAll( - '.focusable:not(.status__quote .focusable)', - ), - ); - - const viewportHeight = - window.innerHeight || document.documentElement.clientHeight; - - // Find first item visible in the viewport - itemToFocus = - focusableItems.find((item) => { - const { top } = item.getBoundingClientRect(); - return top >= 0 && top < viewportHeight; - }) ?? null; - } else { - itemToFocus = container.querySelector('.focusable'); - } + const focusableItems = Array.from( + container.querySelectorAll( + '.focusable:not(.status__quote .focusable)', + ), + ); + + // Find first item visible in the viewport + const itemToFocus = findFirstVisibleWithRect(focusableItems); if (itemToFocus) { - if (container.scrollTop > itemToFocus.offsetTop) { - itemToFocus.scrollIntoView(true); + const viewportWidth = + window.innerWidth || document.documentElement.clientWidth; + const { item, rect } = itemToFocus; + + if ( + container.scrollTop > item.offsetTop || + rect.right > viewportWidth || + rect.left < 0 + ) { + itemToFocus.item.scrollIntoView(true); } - itemToFocus.focus(); + itemToFocus.item.focus(); } } @@ -71,6 +80,26 @@ export function getFocusedItemIndex() { return items.indexOf(focusedItem); } +/** + * Focus the topmost item of the column that currently has focus, + * or the first column if none + */ +export function focusFirstItem() { + const focusedElement = document.activeElement; + const container = + focusedElement?.closest('.scrollable') ?? + document.querySelector('.scrollable'); + + if (!container) return; + + const itemToFocus = container.querySelector('.focusable'); + + if (itemToFocus) { + container.scrollTo(0, 0); + itemToFocus.focus(); + } +} + /** * Focus the item next to the one with the provided index */ diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 1108f8194eb730..bc8dd40d9303d8 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Show/hide text behind CW", "keyboard_shortcuts.toggle_sensitivity": "Show/hide media", "keyboard_shortcuts.toot": "Start a new post", + "keyboard_shortcuts.top": "Move to top of list", "keyboard_shortcuts.translate": "to translate a post", "keyboard_shortcuts.unfocus": "Unfocus compose textarea/search", "keyboard_shortcuts.up": "Move up in the list", From f562ad9f67af5d06c2e12b8ad14dbe4e6d4a366c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:42:49 +0100 Subject: [PATCH 064/123] New Crowdin Translations (automated) (#37063) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/be.json | 1 + app/javascript/mastodon/locales/da.json | 1 + app/javascript/mastodon/locales/de.json | 3 +- app/javascript/mastodon/locales/el.json | 1 + app/javascript/mastodon/locales/es-AR.json | 1 + app/javascript/mastodon/locales/es-MX.json | 1 + app/javascript/mastodon/locales/es.json | 1 + app/javascript/mastodon/locales/et.json | 1 + app/javascript/mastodon/locales/fi.json | 5 +- app/javascript/mastodon/locales/fo.json | 1 + app/javascript/mastodon/locales/fr-CA.json | 2 + app/javascript/mastodon/locales/fr.json | 4 +- app/javascript/mastodon/locales/ga.json | 1 + app/javascript/mastodon/locales/he.json | 1 + app/javascript/mastodon/locales/is.json | 1 + app/javascript/mastodon/locales/it.json | 1 + app/javascript/mastodon/locales/nan.json | 7 +- app/javascript/mastodon/locales/pt-BR.json | 1 + app/javascript/mastodon/locales/pt-PT.json | 1 + app/javascript/mastodon/locales/sq.json | 1 + app/javascript/mastodon/locales/sv.json | 1 + app/javascript/mastodon/locales/tr.json | 2 + app/javascript/mastodon/locales/vi.json | 1 + app/javascript/mastodon/locales/zh-CN.json | 1 + app/javascript/mastodon/locales/zh-TW.json | 1 + config/locales/activerecord.fr-CA.yml | 6 ++ config/locales/activerecord.fr.yml | 6 ++ config/locales/activerecord.tr.yml | 6 ++ config/locales/doorkeeper.fi.yml | 4 +- config/locales/fi.yml | 2 +- config/locales/fr-CA.yml | 15 +++ config/locales/fr.yml | 15 +++ config/locales/nan.yml | 72 ++++++++++++- config/locales/ru.yml | 119 +++++++++++---------- config/locales/simple_form.fi.yml | 12 +-- config/locales/simple_form.fr-CA.yml | 17 +++ config/locales/simple_form.fr.yml | 17 +++ config/locales/simple_form.ja.yml | 2 + 38 files changed, 255 insertions(+), 80 deletions(-) diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json index 1740987f9033bd..9da64c8968e8bf 100644 --- a/app/javascript/mastodon/locales/be.json +++ b/app/javascript/mastodon/locales/be.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Паказаць/схаваць тэкст за папярэджаннем пра кантэнт", "keyboard_shortcuts.toggle_sensitivity": "Паказаць/схаваць медыя", "keyboard_shortcuts.toot": "Стварыць новы допіс", + "keyboard_shortcuts.top": "Перайсці да вяршыні спіса", "keyboard_shortcuts.translate": "каб перакласці допіс", "keyboard_shortcuts.unfocus": "Расфакусіраваць тэкставую вобласць/пошукавы радок", "keyboard_shortcuts.up": "Перамясціцца ўверх па спісе", diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 0af26cf6c29e57..42b7817dcde2c6 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Vis/skjul tekst bag indholdsadvarsel", "keyboard_shortcuts.toggle_sensitivity": "Vis/skjul medier", "keyboard_shortcuts.toot": "Påbegynd nyt indlæg", + "keyboard_shortcuts.top": "Flyt til toppen af listen", "keyboard_shortcuts.translate": "for at oversætte et indlæg", "keyboard_shortcuts.unfocus": "Fjern fokus fra tekstskrivningsområde/søgning", "keyboard_shortcuts.up": "Flyt opad på listen", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 28cfdd26bd4942..f959e7186da510 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -388,7 +388,7 @@ "filter_modal.select_filter.subtitle": "Einem vorhandenen Filter hinzufügen oder einen neuen erstellen", "filter_modal.select_filter.title": "Diesen Beitrag filtern", "filter_modal.title.status": "Beitrag per Filter ausblenden", - "filter_warning.matches_filter": "Ausgeblendet wegen des Filters „{title}“", + "filter_warning.matches_filter": "Ausgeblendet wegen deines Filters „{title}“", "filtered_notifications_banner.pending_requests": "Von {count, plural, =0 {keinem Profil, das dir möglicherweise bekannt ist} one {einem Profil, das dir möglicherweise bekannt ist} other {# Profilen, die dir möglicherweise bekannt sind}}", "filtered_notifications_banner.title": "Gefilterte Benachrichtigungen", "firehose.all": "Alle Server", @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Beitragstext hinter der Inhaltswarnung anzeigen/ausblenden", "keyboard_shortcuts.toggle_sensitivity": "Medien anzeigen/ausblenden", "keyboard_shortcuts.toot": "Neuen Beitrag erstellen", + "keyboard_shortcuts.top": "Zum Listenanfang springen", "keyboard_shortcuts.translate": "Beitrag übersetzen", "keyboard_shortcuts.unfocus": "Eingabefeld/Suche nicht mehr fokussieren", "keyboard_shortcuts.up": "Ansicht nach oben bewegen", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 82be328a229938..5ffeffdf8b64bf 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Εμφάνιση/απόκρυψη κειμένου πίσω από το CW", "keyboard_shortcuts.toggle_sensitivity": "Εμφάνιση/απόκρυψη πολυμέσων", "keyboard_shortcuts.toot": "Δημιουργία νέας ανάρτησης", + "keyboard_shortcuts.top": "Μετακίνηση στην κορυφή της λίστας", "keyboard_shortcuts.translate": "για να μεταφραστεί μια ανάρτηση", "keyboard_shortcuts.unfocus": "Αποεστίαση του πεδίου σύνθεσης/αναζήτησης", "keyboard_shortcuts.up": "Μετακίνηση προς τα πάνω στη λίστα", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index f9398ca59e648b..e1f9d04c889def 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostrar/ocultar texto detrás de la advertencia de contenido (\"CW\")", "keyboard_shortcuts.toggle_sensitivity": "Mostrar/ocultar medios", "keyboard_shortcuts.toot": "Comenzar un mensaje nuevo", + "keyboard_shortcuts.top": "Mover al principio de la lista", "keyboard_shortcuts.translate": "para traducir un mensaje", "keyboard_shortcuts.unfocus": "Quitar el foco del área de texto de redacción o de búsqueda", "keyboard_shortcuts.up": "Subir en la lista", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index 69dcde9181e588..f56df63cb6e208 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostrar/ocultar texto detrás de AC", "keyboard_shortcuts.toggle_sensitivity": "Mostrar/ocultar multimedia", "keyboard_shortcuts.toot": "Comenzar una nueva publicación", + "keyboard_shortcuts.top": "Mover al principio de la lista", "keyboard_shortcuts.translate": "para traducir una publicación", "keyboard_shortcuts.unfocus": "Desenfocar área de redacción/búsqueda", "keyboard_shortcuts.up": "Ascender en la lista", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 1501531fc3ccfb..0d38f064c90db5 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostrar/ocultar texto tras aviso de contenido (CW)", "keyboard_shortcuts.toggle_sensitivity": "Mostrar/ocultar multimedia", "keyboard_shortcuts.toot": "Comenzar una nueva publicación", + "keyboard_shortcuts.top": "Mover al principio de la lista", "keyboard_shortcuts.translate": "para traducir una publicación", "keyboard_shortcuts.unfocus": "Quitar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "Moverse hacia arriba en la lista", diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json index c19aa7d6b9c1b9..4f12c302ed60c4 100644 --- a/app/javascript/mastodon/locales/et.json +++ b/app/javascript/mastodon/locales/et.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Näita/peida teksti hoiatuse taga", "keyboard_shortcuts.toggle_sensitivity": "Näita/peida meediat", "keyboard_shortcuts.toot": "Alusta uut postitust", + "keyboard_shortcuts.top": "Tõsta loendi algusesse", "keyboard_shortcuts.translate": "postituse tõlkimiseks", "keyboard_shortcuts.unfocus": "Fookus tekstialalt/otsingult ära", "keyboard_shortcuts.up": "Liigu loetelus üles", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 9b3aacb9b4abd7..2429b22e107576 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -498,7 +498,7 @@ "keyboard_shortcuts.home": "Avaa kotiaikajana", "keyboard_shortcuts.hotkey": "Pikanäppäin", "keyboard_shortcuts.legend": "Näytä tämä ohje", - "keyboard_shortcuts.load_more": "Kohdista ”Lataa lisää” -painikkeeseen", + "keyboard_shortcuts.load_more": "Kohdista ”Lataa lisää” -⁠painikkeeseen", "keyboard_shortcuts.local": "Avaa paikallinen aikajana", "keyboard_shortcuts.mention": "Mainitse tekijä", "keyboard_shortcuts.muted": "Avaa mykistettyjen käyttäjien luettelo", @@ -512,10 +512,11 @@ "keyboard_shortcuts.requests": "Avaa seurantapyyntöjen luettelo", "keyboard_shortcuts.search": "Kohdista hakukenttään", "keyboard_shortcuts.spoilers": "Näytä tai piilota sisältövaroituskenttä", - "keyboard_shortcuts.start": "Avaa Näin pääset alkuun -sarake", + "keyboard_shortcuts.start": "Avaa ”Näin pääset alkuun” -⁠sarake", "keyboard_shortcuts.toggle_hidden": "Näytä tai piilota sisältövaroituksella merkitty teksti", "keyboard_shortcuts.toggle_sensitivity": "Näytä tai piilota media", "keyboard_shortcuts.toot": "Luo uusi julkaisu", + "keyboard_shortcuts.top": "Siirry luettelon alkuun", "keyboard_shortcuts.translate": "Käännä julkaisu", "keyboard_shortcuts.unfocus": "Poistu kirjoitus- tai hakukentästä", "keyboard_shortcuts.up": "Siirry luettelossa taaksepäin", diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json index 3197a8ccaecadd..b23bd0694d93ec 100644 --- a/app/javascript/mastodon/locales/fo.json +++ b/app/javascript/mastodon/locales/fo.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Vís/fjal tekst handan CW", "keyboard_shortcuts.toggle_sensitivity": "Vís ella fjal innihald", "keyboard_shortcuts.toot": "Byrja nýggjan post", + "keyboard_shortcuts.top": "Flyt til ovast á listanum", "keyboard_shortcuts.translate": "at umseta ein post", "keyboard_shortcuts.unfocus": "Tak skrivi-/leiti-økið úr miðdeplinum", "keyboard_shortcuts.up": "Flyt upp á listanum", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index cc896dfadce9c8..70466504c25c0d 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "C'est fini ! Il n'y a plus rien ici. Lorsque vous recevez de nouvelles notifications, elles apparaitront ici conformément à vos préférences.", "empty_column.notifications": "Vous n'avez pas encore de notifications. Quand d'autres personnes interagissent avec vous, vous en verrez ici.", "empty_column.public": "Il n’y a rien ici! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres serveurs pour remplir le fil public", + "error.no_hashtag_feed_access": "Rejoindre ou se connecter pour voir et suivre cet hashtag.", "error.unexpected_crash.explanation": "En raison d’un bogue dans notre code ou d’un problème de compatibilité avec votre navigateur, cette page n’a pas pu être affichée correctement.", "error.unexpected_crash.explanation_addons": "Cette page n’a pas pu être affichée correctement. Cette erreur est probablement causée par une extension de navigateur ou des outils de traduction automatique.", "error.unexpected_crash.next_steps": "Essayez de rafraîchir la page. Si cela n’aide pas, vous pouvez toujours utiliser Mastodon via un autre navigateur ou une application native.", @@ -515,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Déplier/replier texte derrière avertissement", "keyboard_shortcuts.toggle_sensitivity": "Afficher/cacher médias", "keyboard_shortcuts.toot": "Commencer un nouveau message", + "keyboard_shortcuts.top": "Mettre en tête de liste", "keyboard_shortcuts.translate": "traduire un message", "keyboard_shortcuts.unfocus": "Ne plus se concentrer sur la zone de rédaction/barre de recherche", "keyboard_shortcuts.up": "Monter dans la liste", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 1b9a3580e36337..91fdeff324d0f5 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "C'est fini ! Il n'y a plus rien ici. Lorsque vous recevez de nouvelles notifications, elles apparaitront ici conformément à vos préférences.", "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres personnes pour débuter la conversation.", "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres serveurs pour remplir le fil public", + "error.no_hashtag_feed_access": "Rejoindre ou se connecter pour voir et suivre cet hashtag.", "error.unexpected_crash.explanation": "En raison d’un bug dans notre code ou d’un problème de compatibilité avec votre navigateur, cette page n’a pas pu être affichée correctement.", "error.unexpected_crash.explanation_addons": "Cette page n’a pas pu être affichée correctement. Cette erreur est probablement causée par une extension de navigateur ou des outils de traduction automatique.", "error.unexpected_crash.next_steps": "Essayez de rafraîchir la page. Si cela n’aide pas, vous pouvez toujours utiliser Mastodon via un autre navigateur ou une application native.", @@ -365,7 +366,7 @@ "errors.unexpected_crash.report_issue": "Signaler le problème", "explore.suggested_follows": "Personnes", "explore.title": "Tendances", - "explore.trending_links": "Nouvelles", + "explore.trending_links": "Actualités", "explore.trending_statuses": "Messages", "explore.trending_tags": "Hashtags", "featured_carousel.current": "Message {current, number} / {max, number}", @@ -515,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Déplier/replier le texte derrière un CW", "keyboard_shortcuts.toggle_sensitivity": "Afficher/cacher les médias", "keyboard_shortcuts.toot": "Commencer un nouveau message", + "keyboard_shortcuts.top": "Mettre en tête de liste", "keyboard_shortcuts.translate": "traduire un message", "keyboard_shortcuts.unfocus": "Quitter la zone de rédaction/barre de recherche", "keyboard_shortcuts.up": "Monter dans la liste", diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json index ed866f32ce6fff..3f92c863e10526 100644 --- a/app/javascript/mastodon/locales/ga.json +++ b/app/javascript/mastodon/locales/ga.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Taispeáin/folaigh an téacs taobh thiar de CW", "keyboard_shortcuts.toggle_sensitivity": "Taispeáin / cuir i bhfolach meáin", "keyboard_shortcuts.toot": "Cuir tús le postáil nua", + "keyboard_shortcuts.top": "Bog go barr an liosta", "keyboard_shortcuts.translate": "post a aistriú", "keyboard_shortcuts.unfocus": "Unfocus cum textarea/search", "keyboard_shortcuts.up": "Bog suas ar an liosta", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index cdd041df4132d0..a207c23e42d5a4 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "הצגת/הסתרת טקסט מוסתר מאחורי אזהרת תוכן", "keyboard_shortcuts.toggle_sensitivity": "הצגת/הסתרת מדיה", "keyboard_shortcuts.toot": "להתחיל חיצרוץ חדש", + "keyboard_shortcuts.top": "העברה לראש הרשימה", "keyboard_shortcuts.translate": "לתרגם הודעה", "keyboard_shortcuts.unfocus": "לצאת מתיבת חיבור/חיפוש", "keyboard_shortcuts.up": "לנוע במעלה הרשימה", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 5af1f32ed52a3a..6725d2b0cb038d 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Birta/fela texta á bak við aðvörun vegna efnis", "keyboard_shortcuts.toggle_sensitivity": "Birta/fela myndir", "keyboard_shortcuts.toot": "Byrja nýja færslu", + "keyboard_shortcuts.top": "Færa efst á listann", "keyboard_shortcuts.translate": "að þýða færslu", "keyboard_shortcuts.unfocus": "Taka virkni úr textainnsetningarreit eða leit", "keyboard_shortcuts.up": "Fara ofar í listanum", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 9fc0be453152ec..6ed16f85174cda 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Mostra/Nasconde il testo dietro CW", "keyboard_shortcuts.toggle_sensitivity": "Mostra/Nasconde media", "keyboard_shortcuts.toot": "Crea un nuovo post", + "keyboard_shortcuts.top": "Sposta all'inizio della lista", "keyboard_shortcuts.translate": "Traduce un post", "keyboard_shortcuts.unfocus": "Rimuove il focus sull'area di composizione testuale/ricerca", "keyboard_shortcuts.up": "Scorre in su nell'elenco", diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json index c2b298e032fda0..75b2f7b63a0df0 100644 --- a/app/javascript/mastodon/locales/nan.json +++ b/app/javascript/mastodon/locales/nan.json @@ -18,7 +18,7 @@ "account.badges.bot": "機器lâng", "account.badges.group": "群組", "account.block": "封鎖 @{name}", - "account.block_domain": "封鎖網域 {domain}", + "account.block_domain": "封鎖域名 {domain}", "account.block_short": "封鎖", "account.blocked": "Hőng封鎖", "account.blocking": "Teh封鎖", @@ -26,7 +26,7 @@ "account.copy": "Khóo-pih個人資料ê連結", "account.direct": "私人提起 @{name}", "account.disable_notifications": "停止佇 {name} PO文ê時通知我", - "account.domain_blocking": "Teh封鎖ê網域", + "account.domain_blocking": "Teh封鎖ê域名", "account.edit_profile": "編輯個人資料", "account.edit_profile_short": "編輯", "account.enable_notifications": "佇 {name} PO文ê時通知我", @@ -344,7 +344,7 @@ "empty_column.community": "本站時間線是空ê。緊來公開PO文oh!", "empty_column.direct": "Lí iáu無任何ê私人訊息。Nā是lí送á是收著私人訊息,ē佇tsia顯示。.", "empty_column.disabled_feed": "Tsit ê feed已經hōo lí ê服侍器ê管理員停用。", - "empty_column.domain_blocks": "Iáu無封鎖任何網域。", + "empty_column.domain_blocks": "Iáu無封鎖任何域名。", "empty_column.explore_statuses": "目前iáu無有流行ê趨勢,請sió等tsi̍t-ē,koh確認。", "empty_column.favourited_statuses": "Lí iáu無加添任何收藏 ê PO文。Nā是lí加收藏,伊ē佇tsia顯示。", "empty_column.favourites": "Iáu無lâng收藏tsit篇PO文。Nā是有lâng收藏,ē佇tsia顯示。", @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "顯示/隱藏內容警告後壁ê PO文", "keyboard_shortcuts.toggle_sensitivity": "顯示/tshàng媒體", "keyboard_shortcuts.toot": "PO新PO文", + "keyboard_shortcuts.top": "Súa kàu列單ê頭", "keyboard_shortcuts.translate": "kā PO文翻譯", "keyboard_shortcuts.unfocus": "離開輸入框仔/tshiau-tshuē格仔", "keyboard_shortcuts.up": "佇列單內kā suá khah面頂", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 2bad3ca2b9a519..1c7ef074a2786a 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "expandir/ocultar aviso de conteúdo", "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar mídia", "keyboard_shortcuts.toot": "compor novo toot", + "keyboard_shortcuts.top": "Mover para o topo da lista", "keyboard_shortcuts.translate": "Para traduzir um post", "keyboard_shortcuts.unfocus": "desfocar de tudo", "keyboard_shortcuts.up": "mover para cima", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 101a4bbd78b41e..d6ab3939e0c185 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "mostrar / esconder texto atrás do aviso de conteúdo", "keyboard_shortcuts.toggle_sensitivity": "mostrar / ocultar multimédia", "keyboard_shortcuts.toot": "criar uma nova publicação", + "keyboard_shortcuts.top": "Mover para o topo da lista", "keyboard_shortcuts.translate": "traduzir uma publicação", "keyboard_shortcuts.unfocus": "remover o foco da área de texto / pesquisa", "keyboard_shortcuts.up": "mover para cima na lista", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index bb43bbdbbc3efe..af268b1e494de0 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -512,6 +512,7 @@ "keyboard_shortcuts.toggle_hidden": "Për shfaqje/fshehje teksti pas CW", "keyboard_shortcuts.toggle_sensitivity": "Për shfaqje/fshehje mediash", "keyboard_shortcuts.toot": "Për të filluar një mesazh të ri", + "keyboard_shortcuts.top": "Shpjere në krye të listës", "keyboard_shortcuts.translate": "për të përkthyer një postim", "keyboard_shortcuts.unfocus": "Për heqjen e fokusit nga fusha e hartimit të mesazheve apo kërkimeve", "keyboard_shortcuts.up": "Për ngjitje sipër nëpër listë", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 49ea6ea1736a40..368498b1ce8c79 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "Visa/gömma text bakom CW", "keyboard_shortcuts.toggle_sensitivity": "Visa/gömma media", "keyboard_shortcuts.toot": "Starta nytt inlägg", + "keyboard_shortcuts.top": "Flytta till början av listan", "keyboard_shortcuts.translate": "för att översätta ett inlägg", "keyboard_shortcuts.unfocus": "Avfokusera skrivfält/sökfält", "keyboard_shortcuts.up": "Flytta uppåt i listan", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index b7b4a08cdb6129..21f2b7ed6332da 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -357,6 +357,7 @@ "empty_column.notification_requests": "Hepsi tamam! Burada yeni bir şey yok. Yeni bildirim aldığınızda, ayarlarınıza göre burada görüntülenecekler.", "empty_column.notifications": "Henüz bildiriminiz yok. Sohbete başlamak için başkalarıyla etkileşim kurun.", "empty_column.public": "Burada hiçbir şey yok! Herkese açık bir şeyler yazın veya burayı doldurmak için diğer sunuculardaki kullanıcıları takip edin", + "error.no_hashtag_feed_access": "Bu etiketi görüntülemek ve takip etmek için katılın veya giriş yapın.", "error.unexpected_crash.explanation": "Bizim kodumuzdaki bir hatadan ya da tarayıcı uyumluluk sorunundan dolayı, bu sayfa düzgün görüntülenemedi.", "error.unexpected_crash.explanation_addons": "Bu sayfa doğru görüntülenemedi. Bu hata büyük olasılıkla bir tarayıcı eklentisinden veya otomatik çeviri araçlarından kaynaklanır.", "error.unexpected_crash.next_steps": "Sayfayı yenilemeyi deneyin. Eğer bu yardımcı olmazsa, Mastodon'u farklı bir tarayıcı ya da yerel uygulama üzerinden kullanabilirsiniz.", @@ -515,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "CW'den önceki yazıyı göstermek/gizlemek için", "keyboard_shortcuts.toggle_sensitivity": "Medyayı göstermek/gizlemek için", "keyboard_shortcuts.toot": "Yeni bir gönderi başlat", + "keyboard_shortcuts.top": "Listenin üstüne taşı", "keyboard_shortcuts.translate": "bir gönderiyi çevirmek için", "keyboard_shortcuts.unfocus": "Aramada bir gönderiye odaklanmamak için", "keyboard_shortcuts.up": "Listede yukarıya çıkmak için", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index d6437549fe9350..5cc47ccf0478fe 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "ẩn/hiện nội dung ẩn", "keyboard_shortcuts.toggle_sensitivity": "ẩn/hiện ảnh hoặc video", "keyboard_shortcuts.toot": "soạn tút mới", + "keyboard_shortcuts.top": "Chuyển đến đầu danh sách", "keyboard_shortcuts.translate": "dịch tút", "keyboard_shortcuts.unfocus": "đưa con trỏ ra khỏi ô soạn thảo hoặc ô tìm kiếm", "keyboard_shortcuts.up": "di chuyển lên trên danh sách", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 4986e61e313235..a0284620f5858e 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "显示或隐藏被折叠的正文", "keyboard_shortcuts.toggle_sensitivity": "显示/隐藏媒体", "keyboard_shortcuts.toot": "发送新嘟文", + "keyboard_shortcuts.top": "移动到列表顶部", "keyboard_shortcuts.translate": "翻译嘟文", "keyboard_shortcuts.unfocus": "取消输入/搜索", "keyboard_shortcuts.up": "在列表中让光标上移", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index c198ff8e3558f6..ff8f6e357d8f60 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -516,6 +516,7 @@ "keyboard_shortcuts.toggle_hidden": "顯示或隱藏於內容警告之後的嘟文", "keyboard_shortcuts.toggle_sensitivity": "顯示或隱藏媒體", "keyboard_shortcuts.toot": "發個新嘟文", + "keyboard_shortcuts.top": "移至列表最上方", "keyboard_shortcuts.translate": "翻譯嘟文", "keyboard_shortcuts.unfocus": "跳離文字撰寫區塊或搜尋框", "keyboard_shortcuts.up": "向上移動", diff --git a/config/locales/activerecord.fr-CA.yml b/config/locales/activerecord.fr-CA.yml index a966bb5a8ab4bc..b5db3cc78b8619 100644 --- a/config/locales/activerecord.fr-CA.yml +++ b/config/locales/activerecord.fr-CA.yml @@ -32,6 +32,12 @@ fr-CA: attributes: url: invalid: n’est pas un URL valide + collection: + attributes: + collection_items: + too_many: trop d'éléments, seuls %{count} éléments sont permis + tag: + unusable: ne peut pas être utilisé doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.fr.yml b/config/locales/activerecord.fr.yml index 94d2fa3c6f4847..5d5efae04438bc 100644 --- a/config/locales/activerecord.fr.yml +++ b/config/locales/activerecord.fr.yml @@ -32,6 +32,12 @@ fr: attributes: url: invalid: n’est pas une URL valide + collection: + attributes: + collection_items: + too_many: trop d'éléments, seuls %{count} éléments sont permis + tag: + unusable: ne peut pas être utilisé doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.tr.yml b/config/locales/activerecord.tr.yml index db9317afa2f343..2f5fb50f2dafa0 100644 --- a/config/locales/activerecord.tr.yml +++ b/config/locales/activerecord.tr.yml @@ -32,6 +32,12 @@ tr: attributes: url: invalid: geçerli bir URL değil + collection: + attributes: + collection_items: + too_many: çok fazla, en fazla %{count} olabilir + tag: + unusable: kullanılamaz doorkeeper/application: attributes: website: diff --git a/config/locales/doorkeeper.fi.yml b/config/locales/doorkeeper.fi.yml index 7b80882af01e1b..73730a8689072b 100644 --- a/config/locales/doorkeeper.fi.yml +++ b/config/locales/doorkeeper.fi.yml @@ -127,7 +127,7 @@ fi: blocks: Estot bookmarks: Kirjanmerkit conversations: Keskustelut - crypto: Päästä päähän -salaus + crypto: Päästä päähän -⁠salaus favourites: Suosikit filters: Suodattimet follow: Seurattavat, mykistykset ja estot @@ -165,7 +165,7 @@ fi: admin:write:email_domain_blocks: suorita moderointitoimia estetyille sähköpostiverkkotunnuksille admin:write:ip_blocks: suorita moderointitoimia estetyille IP-osoitteille admin:write:reports: suorita moderointitoimia raporteille - crypto: käytä päästä päähän -salausta + crypto: käytä päästä päähän -⁠salausta follow: muokkaa tilin seurantasuhteita profile: lue vain tilisi profiilitietoja push: vastaanota puskuilmoituksesi diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 93d0b87433c7f8..a963d57e728705 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -2158,7 +2158,7 @@ fi: extra_instructions_html: Vinkki: Verkkosivustollasi oleva linkki voi olla myös näkymätön. Olennainen osuus on rel="me", joka estää toiseksi henkilöksi tekeytymisen verkkosivustoilla, joilla on käyttäjien luomaa sisältöä. Voit käyttää jopa link-elementtiä sivun head-osassa elementin a sijaan, mutta HTML:n pitää olla käytettävissä ilman JavaScript-koodin suorittamista. here_is_how: Näin se onnistuu hint_html: "Henkilöllisyyden vahvistaminen on Mastodonissa jokaisen käyttäjän ulottuvilla. Se perustuu avoimiin standardeihin ja on maksutonta nyt ja aina. Tarvitset vain henkilökohtaisen verkkosivuston, jonka perusteella sinut voidaan tunnistaa. Kun teet linkin tuolle verkkosivulle profiilistasi, tarkistamme, että verkkosivustolla on linkki takaisin profiiliisi, ja näytämme profiilissasi visuaalisen ilmaisimen." - instructions_html: Kopioi ja liitä seuraava koodi verkkosivustosi HTML-lähdekoodiin. Lisää sitten verkkosivustosi osoite johonkin profiilisi lisäkentistä ”Muokkaa profiilia” -välilehdellä ja tallenna muutokset. + instructions_html: Kopioi ja liitä seuraava koodi verkkosivustosi HTML-lähdekoodiin. Lisää sitten verkkosivustosi osoite johonkin profiilisi lisäkentistä ”Muokkaa profiilia” -⁠välilehdellä ja tallenna muutokset. verification: Vahvistus verified_links: Vahvistetut linkkisi website_verification: Verkkosivuston vahvistus diff --git a/config/locales/fr-CA.yml b/config/locales/fr-CA.yml index a0e3f1d584fd5a..3a46a17f5546b9 100644 --- a/config/locales/fr-CA.yml +++ b/config/locales/fr-CA.yml @@ -506,6 +506,7 @@ fr-CA: finish_registration: Terminer l'inscription name: Nom providers: Fournisseur + public_key_fingerprint: Empreinte de la clé publique registration_requested: Inscription demandée registrations: confirm: Confirmer @@ -821,6 +822,7 @@ fr-CA: rules_hint: Il y a un espace dédié pour les règles auxquelles vos utilisateurs sont invités à adhérer. title: À propos allow_referrer_origin: + desc: Quand les utilisateurs cliquent sur un lien externe, leur navigateur peut envoyer l'adresse du serveur Mastodon en tant qu'adresse d'origine. À désactiver si cela permet d'identifier les utilisateurs, par exemple s'il s'agit d'un serveur Mastodon personnel. title: Autoriser les sites externes à voir votre serveur Mastodon comme une source de trafic appearance: preamble: Personnaliser l'interface web de Mastodon. @@ -840,6 +842,7 @@ fr-CA: title: Par défaut, ne pas indexer les comptes dans les moteurs de recherche discovery: follow_recommendations: Suivre les recommandations + preamble: Il est essentiel de donner de la visibilité à des contenus intéressants pour attirer des utilisateur⋅rice⋅s néophytes qui ne connaissent peut-être personne sur Mastodon. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur. privacy: Vie privée profile_directory: Annuaire des profils public_timelines: Fils publics @@ -853,6 +856,7 @@ fr-CA: feed_access: modes: authenticated: Utilisateurs authentifiés uniquement + disabled: Nécessite un rôle spécifique public: Tout le monde landing_page: values: @@ -938,6 +942,8 @@ fr-CA: system_checks: database_schema_check: message_html: Vous avez des migrations de base de données en attente. Veuillez les exécuter pour vous assurer que l'application se comporte comme prévu + elasticsearch_analysis_index_mismatch: + message_html: Les paramètres de l'analyseur d'index d'Elasticsearch ne sont plus à jour. Veuillez lancer tootctl search deploy --only=%{value} elasticsearch_health_red: message_html: Le cluster Elasticsearch n’est pas sain (statut rouge), les fonctionnalités de recherche ne sont pas disponible elasticsearch_health_yellow: @@ -1113,11 +1119,14 @@ fr-CA: delete: Supprimer edit: title: Modifier la règle de nom d'utilisateur + matches_exactly_html: Égal à %{string} new: create: Créer une règle title: Créer une nouvelle règle de nom d'utilisateur no_username_block_selected: Aucune règle de nom d'utilisateur n'a été modifiée car aucune n'a été sélectionnée + not_permitted: Non autorisé title: Règles de nom d'utilisateur + updated_msg: Règle de nom d'utilisateur mise à jour warning_presets: add_new: Ajouter un nouveau delete: Supprimer @@ -1193,6 +1202,7 @@ fr-CA: advanced_settings: Paramètres avancés animations_and_accessibility: Animations et accessibilité boosting_preferences: Préférences de partage + boosting_preferences_info_html: "Astuce : indépendamment des paramètres, Maj + Clic sur l'icône %{icon} Boost va immédiatement partager." discovery: Découverte localization: body: Mastodon est traduit par des volontaires. @@ -1597,6 +1607,7 @@ fr-CA: link_preview: author_html: Par %{name} potentially_sensitive_content: + action: Cliquer pour afficher confirm_visit: Voulez-vous vraiment ouvrir ce lien ? hide_button: Masquer label: Contenu potentiellement sensible @@ -1664,6 +1675,7 @@ fr-CA: disabled_account: Votre compte actuel ne sera pas entièrement utilisable par la suite. Cependant, vous aurez accès à l'exportation de données et à la réactivation. followers: Cette action va déménager tou·te·s les abonné·e·s du compte actuel vers le nouveau compte only_redirect_html: Alternativement, vous pouvez seulement appliquer une redirection sur votre profil. + other_data: Aucune autre donnée ne sera déplacée automatiquement (y compris vos messages et la liste des comptes suivis) redirect: Le profil de votre compte actuel sera mis à jour avec un avis de redirection et sera exclu des recherches moderation: title: Modération @@ -1920,6 +1932,7 @@ fr-CA: errors: in_reply_not_found: Le message auquel vous essayez de répondre ne semble pas exister. quoted_status_not_found: Le message que vous essayez de citer ne semble pas exister. + quoted_user_not_mentioned: Impossible de citer un compte non mentionné dans une mention privée. over_character_limit: limite de %{max} caractères dépassée pin_errors: direct: Les messages qui ne sont visibles que pour les utilisateur·rice·s mentionné·e·s ne peuvent pas être épinglés @@ -1988,6 +2001,8 @@ fr-CA: terms_of_service: title: Conditions d'utilisation terms_of_service_interstitial: + future_preamble_html: Nous avons modifié nos conditions d'utilisation, ces changements entreront en vigueur le %{date}. Nous vous invitons à prendre connaissance des nouvelles conditions. + past_preamble_html: Nous avons modifié nos conditions d'utilisation depuis votre dernière visite. Nous vous invitons à prendre connaissance des nouvelles conditions. review_link: Vérifier les conditions d'utilisation title: Les conditions d'utilisation de %{domain} ont changées themes: diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 9d43b6efefae13..28be8c9641f105 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -506,6 +506,7 @@ fr: finish_registration: Terminer l'inscription name: Nom providers: Fournisseur + public_key_fingerprint: Empreinte de la clé publique registration_requested: Inscription demandée registrations: confirm: Confirmer @@ -821,6 +822,7 @@ fr: rules_hint: Il y a un espace dédié pour les règles auxquelles vos utilisateurs sont invités à adhérer. title: À propos allow_referrer_origin: + desc: Quand les utilisateurs cliquent sur un lien externe, leur navigateur peut envoyer l'adresse du serveur Mastodon en tant qu'adresse d'origine. À désactiver si cela permet d'identifier les utilisateurs, par exemple s'il s'agit d'un serveur Mastodon personnel. title: Autoriser les sites externes à voir votre serveur Mastodon comme une source de trafic appearance: preamble: Personnaliser l'interface web de Mastodon. @@ -840,6 +842,7 @@ fr: title: Par défaut, ne pas indexer les comptes dans les moteurs de recherche discovery: follow_recommendations: Suivre les recommandations + preamble: Il est essentiel de donner de la visibilité à des contenus intéressants pour attirer des utilisateur⋅rice⋅s néophytes qui ne connaissent peut-être personne sur Mastodon. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur. privacy: Vie privée profile_directory: Annuaire des profils public_timelines: Fils publics @@ -853,6 +856,7 @@ fr: feed_access: modes: authenticated: Utilisateurs authentifiés uniquement + disabled: Nécessite un rôle spécifique public: Tout le monde landing_page: values: @@ -938,6 +942,8 @@ fr: system_checks: database_schema_check: message_html: Vous avez des migrations de base de données en attente. Veuillez les exécuter pour vous assurer que l'application se comporte comme prévu + elasticsearch_analysis_index_mismatch: + message_html: Les paramètres de l'analyseur d'index d'Elasticsearch ne sont plus à jour. Veuillez lancer tootctl search deploy --only=%{value} elasticsearch_health_red: message_html: Le cluster Elasticsearch n’est pas sain (statut rouge), les fonctionnalités de recherche ne sont pas disponible elasticsearch_health_yellow: @@ -1113,11 +1119,14 @@ fr: delete: Supprimer edit: title: Modifier la règle de nom d'utilisateur + matches_exactly_html: Égal à %{string} new: create: Créer une règle title: Créer une nouvelle règle de nom d'utilisateur no_username_block_selected: Aucune règle de nom d'utilisateur n'a été modifiée car aucune n'a été sélectionnée + not_permitted: Non autorisé title: Règles de nom d'utilisateur + updated_msg: Règle de nom d'utilisateur mise à jour warning_presets: add_new: Ajouter un nouveau delete: Supprimer @@ -1193,6 +1202,7 @@ fr: advanced_settings: Paramètres avancés animations_and_accessibility: Animations et accessibilité boosting_preferences: Préférences de partage + boosting_preferences_info_html: "Astuce : indépendamment des paramètres, Maj + Clic sur l'icône %{icon} Boost va immédiatement partager." discovery: Découverte localization: body: Mastodon est traduit par des volontaires. @@ -1597,6 +1607,7 @@ fr: link_preview: author_html: Par %{name} potentially_sensitive_content: + action: Cliquer pour afficher confirm_visit: Voulez-vous vraiment ouvrir ce lien ? hide_button: Masquer label: Contenu potentiellement sensible @@ -1664,6 +1675,7 @@ fr: disabled_account: Votre compte actuel ne sera pas entièrement utilisable par la suite. Cependant, vous aurez accès à l'exportation de données et à la réactivation. followers: Cette action va déménager tou·te·s les abonné·e·s du compte actuel vers le nouveau compte only_redirect_html: Alternativement, vous pouvez seulement appliquer une redirection sur votre profil. + other_data: Aucune autre donnée ne sera déplacée automatiquement (y compris vos messages et la liste des comptes suivis) redirect: Le profil de votre compte actuel sera mis à jour avec un avis de redirection et sera exclu des recherches moderation: title: Modération @@ -1920,6 +1932,7 @@ fr: errors: in_reply_not_found: Le message auquel vous essayez de répondre ne semble pas exister. quoted_status_not_found: Le message que vous essayez de citer ne semble pas exister. + quoted_user_not_mentioned: Impossible de citer un compte non mentionné dans une mention privée. over_character_limit: limite de %{max} caractères dépassée pin_errors: direct: Les messages qui ne sont visibles que pour les utilisateur·rice·s mentionné·e·s ne peuvent pas être épinglés @@ -1988,6 +2001,8 @@ fr: terms_of_service: title: Conditions d'utilisation terms_of_service_interstitial: + future_preamble_html: Nous avons modifié nos conditions d'utilisation, ces changements entreront en vigueur le %{date}. Nous vous invitons à prendre connaissance des nouvelles conditions. + past_preamble_html: Nous avons modifié nos conditions d'utilisation depuis votre dernière visite. Nous vous invitons à prendre connaissance des nouvelles conditions. review_link: Vérifier les conditions d'utilisation title: Les conditions d'utilisation de %{domain} ont changées themes: diff --git a/config/locales/nan.yml b/config/locales/nan.yml index b9e5c09159f4d5..5c8109b214ac26 100644 --- a/config/locales/nan.yml +++ b/config/locales/nan.yml @@ -31,7 +31,7 @@ nan: created_msg: 管理記錄成功建立! destroyed_msg: 管理記錄成功thâi掉! accounts: - add_email_domain_block: 封鎖電子phue ê網域 + add_email_domain_block: 封鎖電子phue ê域名 approve: 允准 approved_msg: 成功審核 %{username} ê註冊申請ah are_you_sure: Lí kám確定? @@ -412,7 +412,7 @@ nan: stop_communication: Lí ê服侍器ē停止kap hia ê服侍器聯絡。 title: 確認封鎖域名 %{domain} undo_relationships: Tse ē取消任何ê佇in ê服侍器ê口座kap lí ê之間ê跟tuè關係。 - created_msg: 當leh封鎖網域 + created_msg: 當leh封鎖域名 destroyed_msg: 已經取消封鎖域名 domain: 域名 edit: 編輯域名封鎖 @@ -457,12 +457,12 @@ nan: new: create: 加添域名 resolve: 解析域名 - title: 封鎖新ê電子phue網域 + title: 封鎖新ê電子phue ê域名 no_email_domain_block_selected: 因為無揀任何電子phue域名封鎖,所以lóng無改變 not_permitted: 無允准 resolved_dns_records_hint_html: 域名解析做下kha ê MX域名,tsiah ê域名上後負責收電子phue。封鎖MX域名ē封任何有siâng款MX域名ê電子郵件ê註冊,就算通看見ê域名無kâng,mā án-ne。Tio̍h細膩,m̄通封鎖主要ê電子phue提供者。 resolved_through_html: 通過 %{domain} 解析 - title: 封鎖ê電子phue網域 + title: 封鎖ê電子phue域名 export_domain_allows: new: title: 輸入允准ê域名 @@ -659,7 +659,7 @@ nan: are_you_sure: Lí kám確定? assign_to_self: 分配hōo家kī assigned: 分配管理者 - by_target_domain: 受檢舉ê口座ê網域 + by_target_domain: 受檢舉ê口座ê域名 cancel: 取消 category: 類別 category_description_html: Tsit ê 受檢舉ê口座kap/á是內容,ē佇kap tsit ê口座ê聯絡內底引用。 @@ -1362,6 +1362,68 @@ nan: sensitive: Kā 口座文標做敏感ê silence: 口座制限 suspend: 停止口座權限 + your_appeal_approved: Lí ê投訴受允准 + your_appeal_pending: Lí有送投訴 + your_appeal_rejected: Lí ê投訴已經受拒絕 + edit_profile: + basic_information: 基本ê資訊 + hint_html: "自訂lâng佇lí ê個人資料kap lí ê Po文邊仔所通看ê。Nā是lí有添好個人資料kap標á,別lâng較有可能kā lí跟tuè轉去,kap lí互動。" + other: 其他 + emoji_styles: + auto: 自動 + native: 原底ê + twemoji: Twemoji + errors: + '400': Lí所送ê請求無效,á是格式毋著。 + '403': Lí bô權限來看tsit頁。 + '404': Lí teh tshuē ê頁無佇leh。 + '406': Tsit頁bē當照lí所請求ê格式提供。 + '410': Lí所tshuē ê頁,無佇tsia ah。 + '422': + content: 安全驗證出tshê。Lí kám有封鎖cookie? + title: 安全驗證失敗 + '429': 請求siunn tsē + '500': + content: 歹勢,guán ê後臺出問題ah。 + title: Tsit頁無正確 + '503': Tsit頁bē當提供,因為服侍器暫時出tshê。 + noscript_html: Beh用Mastodon web app,請拍開JavaScript。另外,請試Mastodon hōo lí ê平臺用ê app。 + existing_username_validator: + not_found: bē當用tsit ê名tshuē著本地ê用者 + not_found_multiple: tshuē無 %{usernames} + exports: + archive_takeout: + date: 日期 + download: Kā lí ê檔案載落 + hint_html: Lí ē當請求lí ê PO文kap所傳ê媒體ê檔案。輸出ê資料ē用ActivityPub格式,通hōo適用ê軟體讀。Lí ē當每7 kang請求tsi̍t kái。 + in_progress: Teh備辦lí ê檔案…… + request: 請求lí ê檔案 + size: Sài-suh + blocks: Lí封鎖ê + bookmarks: 冊籤 + csv: CSV + domain_blocks: 域名封鎖 + lists: 列單 + mutes: Lí消音ê + storage: 媒體儲存 + featured_tags: + add_new: 加新ê + errors: + limit: Lí已經kā hashtag推薦kàu盡磅ah。 + hint_html: "佇個人資料推薦lí上重要ê hashtag。Tse是誠好ê家私,通the̍h來追蹤lí ê創意作品kap長期計畫。推薦ê hashtag ē佇lí ê個人資料顯目展示,koh允准緊緊接近使用lí家tī ê PO文。" + filters: + contexts: + account: 個人資料 + home: Tshù kap列單 + notifications: 通知 + public: 公共ê時間線 + thread: 會話 + edit: + add_keyword: 加關鍵字 + keywords: 關鍵字 + statuses: 個別ê PO文 + statuses_hint_html: Tsit ê過濾器應用佇所揀ê個別ê PO文,毋管in敢有符合下kha ê關鍵字重頭看,á是kā PO文tuì tsit ê過濾器suá掉。 + title: 編輯過濾器 scheduled_statuses: too_soon: Tio̍h用未來ê日期。 statuses: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index fa81b360fa0038..4e61fd52b392f8 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -29,9 +29,9 @@ ru: admin: account_actions: action: Выполнить действие - already_silenced: Эта учетная запись уже ограничена. - already_suspended: Эта учетная запись уже заблокирована. - title: Осуществить модерацию в отношении учётной записи %{acct} + already_silenced: Эта учётная запись уже ограничена. + already_suspended: Эта учётная запись уже заблокирована. + title: Произвести модерацию учётной записи %{acct} account_moderation_notes: create: Создать заметку created_msg: Заметка для модераторов добавлена @@ -63,9 +63,9 @@ ru: delete: Удалить данные deleted: Удалена demote: Разжаловать - destroyed_msg: Данные %{username} поставлены в очередь на удаление + destroyed_msg: Данные %{username} будут удалены в ближайшее время disable: Отключить - disable_sign_in_token_auth: Отключить аутентификацию по e-mail кодам + disable_sign_in_token_auth: Отключить аутентификацию с помощью одноразового пароля по эл. почте disable_two_factor_authentication: Отключить 2FA disabled: Отключена display_name: Отображаемое имя @@ -74,9 +74,9 @@ ru: email: Адрес электронной почты email_status: Подтверждение учётной записи enable: Включить - enable_sign_in_token_auth: Включить аутентификацию по e-mail кодам - enabled: Включен - enabled_msg: Учётная запись %{username} успешно разморожена + enable_sign_in_token_auth: Включить аутентификацию с помощью одноразового пароля по эл. почте + enabled: Включена + enabled_msg: Отменено отключение учётной записи %{username} followers: Подписчики follows: Подписки header: Обложка профиля @@ -92,9 +92,9 @@ ru: title: По расположению login_status: Состояние учётной записи media_attachments: Медиавложения - memorialize: Сделать мемориалом - memorialized: In memoriam - memorialized_msg: "%{username} успешно превращён в памятник" + memorialize: Почтить память + memorialized: Мемориальная + memorialized_msg: Учётная запись %{username} превращена в мемориальную moderation: active: Действующие all: Все @@ -109,7 +109,7 @@ ru: no_account_selected: Ничего не изменилось, так как ни одна учётная запись не была выделена no_limits_imposed: Ограничения отсутствуют no_role_assigned: Роль не присвоена - not_subscribed: Не подписаны + not_subscribed: Подписка отсутствует pending: Ожидает одобрения perform_full_suspension: Заблокировать previous_strikes: Зафиксированные нарушения @@ -118,9 +118,9 @@ ru: many: За этой учётной записью числится %{count} нарушений. one: За этой учётной записью числится %{count} нарушение. other: За этой учётной записью числится %{count} нарушений. - promote: Повысить + promote: Сделать администратором protocol: Протокол - public: Публичный + public: Профиль push_subscription_expires: Подписка PuSH истекает redownload: Обновить аватар redownloaded_msg: Профиль %{username} успешно обновлен из оригинала @@ -138,7 +138,7 @@ ru: success: Ссылка для подтверждения успешно отправлена! reset: Сбросить reset_password: Сбросить пароль - resubscribe: Переподписаться + resubscribe: Пересоздать подписку role: Роль search: Поиск search_same_email_domain: Другие пользователи с тем же доменом эл. почты @@ -169,9 +169,9 @@ ru: undo_sensitized: Не скрывать медиа undo_silenced: Не ограничивать undo_suspension: Разблокировать - unsilenced_msg: Ограничения с учётной записи %{username} сняты успешно + unsilenced_msg: Отменено ограничение учётной записи %{username} unsubscribe: Отписаться - unsuspended_msg: Учётная запись %{username} успешно разморожена + unsuspended_msg: Учётная запись %{username} разблокирована username: Имя пользователя view_domain: Просмотр сводки по домену warn: Предупредить @@ -214,11 +214,11 @@ ru: disable_2fa_user: Отключение 2FA disable_custom_emoji: Отключение эмодзи disable_relay: Отключение ретранслятора - disable_sign_in_token_auth_user: Отключение аутентификации по e-mail кодам + disable_sign_in_token_auth_user: Отключение аутентификации с помощью одноразового пароля по эл. почте disable_user: Заморозка пользователей enable_custom_emoji: Включение эмодзи enable_relay: Включение ретранслятора - enable_sign_in_token_auth_user: Включение аутентификации по e-mail кодам + enable_sign_in_token_auth_user: Включение аутентификации с помощью одноразового пароля по эл. почте enable_user: Разморозка пользователей memorialize_account: Присвоение пользователям статуса «мемориала» promote_user: Повышение пользователей @@ -282,11 +282,11 @@ ru: disable_2fa_user_html: "%{name} отключил(а) требование двухэтапной авторизации для пользователя %{target}" disable_custom_emoji_html: "%{name} отключил(а) эмодзи %{target}" disable_relay_html: "%{name} отключил(а) ретранслятор %{target}" - disable_sign_in_token_auth_user_html: "%{name} отключил(а) аутентификацию по e-mail кодам для %{target}" + disable_sign_in_token_auth_user_html: "%{name} отключил(а) аутентификацию с помощью одноразового пароля по эл. почте для %{target}" disable_user_html: "%{name} заморозил(а) пользователя %{target}" enable_custom_emoji_html: "%{name} включил(а) эмодзи %{target}" enable_relay_html: "%{name} включил(а) ретранслятор %{target}" - enable_sign_in_token_auth_user_html: "%{name} включил(а) аутентификацию по e-mail кодам для %{target}" + enable_sign_in_token_auth_user_html: "%{name} включил(а) аутентификацию с помощью одноразового пароля по эл. почте для %{target}" enable_user_html: "%{name} разморозил(а) пользователя %{target}" memorialize_account_html: "%{name} перевел(а) учётную запись пользователя %{target} в статус памятника" promote_user_html: "%{name} повысил(а) пользователя %{target}" @@ -342,7 +342,7 @@ ru: unpublish: Скрыть unpublished_msg: Объявление скрыто updated_msg: Объявление отредактировано - critical_update_pending: Ожидается обновление критического уровня + critical_update_pending: Доступно критическое обновление custom_emojis: assign_category: Задать категорию by_domain: Домен @@ -384,25 +384,25 @@ ru: new_users: новые пользователи opened_reports: новые жалобы pending_appeals_html: - few: "%{count} ожидают аппеляции" - many: "%{count} ожидают апелляции" - one: "%{count} ожидает апелляции" - other: 'Ожидают апелляции: %{count}' + few: "%{count} открытые апелляции" + many: "%{count} открытых апелляций" + one: "%{count} открытая апелляция" + other: "%{count} открытых апелляций" pending_reports_html: - few: "%{count} ожидающих отчета" - many: "%{count} ожидающих отчетов" - one: "%{count} ожидающий отчет" - other: "%{count} ожидающих отчетов" + few: "%{count} открытые жалобы" + many: "%{count} открытых жалоб" + one: "%{count} открытая жалоба" + other: "%{count} открытых жалоб" pending_tags_html: - few: "%{count} ожидающих хэштега" - many: "%{count} ожидающих хэштегов" - one: "%{count} ожидающий хэштег" - other: "%{count} ожидающих хэштегов" + few: "%{count} нерассмотренных хештега" + many: "%{count} нерассмотренных хештегов" + one: "%{count} нерассмотренный хештег" + other: "%{count} нерассмотренных хештегов" pending_users_html: - few: "%{count} ожидающих пользователя" - many: "%{count} ожидающих пользователей" - one: "%{count} ожидающий пользователь" - other: "%{count} ожидающих пользователей" + few: "%{count} заявки на регистрацию" + many: "%{count} заявок на регистрацию" + one: "%{count} заявка на регистрацию" + other: "%{count} заявок на регистрацию" resolved_reports: жалоб решено software: Программное обеспечение sources: Источники регистрации @@ -416,12 +416,12 @@ ru: empty: Апелляций не найдено. title: Апелляции domain_allows: - add_new: Внести в белый список - created_msg: Домен добавлен в белый список - destroyed_msg: Домен убран из белого списка + add_new: Внести домен в белый список + created_msg: Домен внесён в белый список, федерация с ним теперь разрешена + destroyed_msg: Домен исключён из белого списка, федерация с ним больше не разрешена export: Экспорт import: Импорт - undo: Убрать из белого списка + undo: Исключить из белого списка domain_blocks: add_new: Заблокировать домен confirm_suspension: @@ -468,28 +468,28 @@ ru: add_new: Добавить allow_registrations_with_approval: Разрешить регистрацию с одобрением attempts_over_week: - few: "%{count} попытки за последнюю неделю" - many: "%{count} попыток за последнюю неделю" - one: "%{count} попытка за последнюю неделю" + few: "%{count} попытки регистрации за последнюю неделю" + many: "%{count} попыток регистрации за последнюю неделю" + one: "%{count} попытка регистрации за последнюю неделю" other: "%{count} попыток регистрации за последнюю неделю" - created_msg: Домен email забанен, ура - delete: Удалить + created_msg: Домен электронной почты заблокирован + delete: Разблокировать dns: types: mx: Запись MX domain: Домен new: - create: Создать блокировку - resolve: Проверить домен - title: Блокировка нового почтового домена - no_email_domain_block_selected: Блокировки почтовых доменов не были изменены, так как ни один из них не был выбран - not_permitted: Не разрешено - resolved_dns_records_hint_html: Доменное имя указывает на следующие MX-домены, которые в конечном итоге отвечают за прием электронной почты. Блокировка MX-домена будет блокировать регистрации с любого адреса электронной почты, который использует тот же MX-домен, даже если видимое доменное имя отличается от него. Будьте осторожны, чтобы не заблокировать основных провайдеров электронной почты - resolved_through_html: Разрешено через %{domain} + create: Заблокировать домен + resolve: Проверить DNS-записи + title: Заблокировать домен электронной почты + no_email_domain_block_selected: Ничего не изменилось, так как ни один почтовый домен не был выделен + not_permitted: Недостаточно прав + resolved_dns_records_hint_html: Доменное имя указывает на следующие MX-домены, которые в конечном итоге отвечают за приём электронной почты. Блокировка MX-домена приведёт к блокировке регистраций со всех адресов электронной почты, которые используют тот же MX-домен, даже если видимое доменное имя отличается от него. Будьте осторожны, чтобы не заблокировать основных провайдеров электронной почты + resolved_through_html: Из DNS-записей домена %{domain} title: Блокировки по домену эл. почты export_domain_allows: new: - title: Импорт домена разрешён + title: Импорт белого списка доменов no_file: Файл не выбран export_domain_blocks: import: @@ -527,12 +527,12 @@ ru: status: Пост title: FASP follow_recommendations: - description_html: "Следуйте рекомендациям, чтобы помочь новым пользователям быстро находить интересный контент. Если пользователь не взаимодействовал с другими в достаточной степени, чтобы сформировать персонализированные рекомендации, вместо этого рекомендуется использовать эти учетные записи. Они пересчитываются на ежедневной основе на основе комбинации аккаунтов с наибольшим количеством недавних взаимодействий и наибольшим количеством местных подписчиков для данного языка." - language: Для языка - status: Статус + description_html: "Рекомендации профилей помогают новым пользователям быстрее найти что-нибудь интересное. Если пользователь мало взаимодействовал с другими и составить персонализированные рекомендации не получается, будут предложены указанные здесь профили. Эти рекомендации обновляются ежедневно из совокупности учётных записей с наибольшим количеством недавних взаимодействий и наибольшим количеством подписчиков с этого сервера для данного языка." + language: Фильтр по языку + status: Фильтр по состоянию suppress: Скрыть рекомендацию suppressed: Скрыта - title: Рекомендации подписок + title: Рекомендации профилей unsuppress: Восстановить рекомендацию instances: audit_log: @@ -1666,6 +1666,7 @@ ru: authentication_methods: otp: приложения для генерации кодов password: пароля + sign_in_token: одноразового пароля по эл. почте webauthn: электронного ключа description_html: Если вы заметили действия, которых не совершали, вам следует сменить пароль и включить двухфакторную аутентификацию. empty: История входов отсутствует diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index 63d63b32917311..7b2963095f2f0e 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -19,7 +19,7 @@ fi: text: Voit käyttää julkaisun syntaksia, kuten URL-osoitteita, aihetunnisteita ja mainintoja title: Valinnainen. Ei näy vastaanottajalle admin_account_action: - include_statuses: Käyttäjä näkee, mitkä julkaisut johtivat moderointitoimeen tai -varoitukseen + include_statuses: Käyttäjä näkee, mitkä julkaisut johtivat moderointitoimeen tai -⁠varoitukseen send_email_notification: Käyttäjä saa selvityksen siitä, mitä hänen tililleen tapahtui text_html: Valinnainen. Voit käyttää julkaisun syntaksia. Voit lisätä varoitusasetuksia säästääksesi aikaa type_html: Valitse mitä teet käyttäjälle %{acct} @@ -28,7 +28,7 @@ fi: none: Käytä tätä lähettääksesi varoitus käyttäjälle käynnistämättä mitään muita toimia. sensitive: Pakota kaikki tämän käyttäjän mediatiedostot arkaluonteisiksi. silence: Estä käyttäjää lähettämästä julkaisuja, joiden näkyvyys on julkinen, sekä piilota hänen julkaisunsa ja häneen liittyvät ilmoitukset niiltä, jotka eivät seuraa häntä. Sulkee kaikki tiliin kohdistuvat raportit. - suspend: Estä kaikki vuorovaikutus tältä tililtä ja tälle tilille sekä poista kaikki sen sisältö. Peruttavissa 30 päivän ajan. Sulkee kaikki tiliin kohdistuvat raportit. + suspend: Estä kaikki vuorovaikutus tältä tililtä ja tälle tilille sekä poista kaikki sen sisältö. Peruttavissa 30 päivän ajan. Sulkee kaikki tiliin kohdistuvat raportit. warning_preset_id: Valinnainen. Voit silti lisätä mukautetun tekstin esiasetuksen loppuun announcement: all_day: Kun valittuna, vain aikavälin päivät näytetään @@ -65,12 +65,12 @@ fi: setting_display_media_hide_all: Piilota mediasisältö aina setting_display_media_show_all: Näytä mediasisältö aina setting_emoji_style: Miten emojit näkyvät. ”Automaattinen” pyrkii käyttämään natiiveja emojeita, mutta Twemoji-emojeita käytetään varavaihtoehtoina vanhoissa selaimissa. - setting_quick_boosting_html: Kun käytössä, %{boost_icon} Tehosta-kuvakkeen painaminen tehostaa välittömästi sen sijaan, että Tehosta/Lainaa-pudotusvalikko avautuisi. Siirtää lainaustoiminnon %{options_icon} (Valinnat) -valikkoon. + setting_quick_boosting_html: Kun käytössä, %{boost_icon} Tehosta-kuvakkeen napsauttaminen tehostaa välittömästi sen sijaan, että Tehosta/Lainaa-pudotusvalikko avautuisi. Siirtää lainaustoiminnon %{options_icon} (Valinnat) -⁠valikkoon. setting_system_scrollbars_ui: Koskee vain Safari- ja Chrome-pohjaisia työpöytäselaimia setting_use_blurhash: Liukuvärit perustuvat piilotettujen kuvien väreihin mutta sumentavat yksityiskohdat setting_use_pending_items: Piilota aikajanan päivitykset napsautuksen taakse syötteen automaattisen vierityksen sijaan username: Voit käyttää kirjaimia, numeroita ja alaviivoja - whole_word: Kun avainsana tai -fraasi on täysin aakkosnumeerinen, suodatin aktivoituu vain sen täysvastineille + whole_word: Kun avainsana tai -⁠fraasi on kokonaan aakkosnumeerinen, suodatin tulee voimaan vain sen täysvastineille domain_allow: domain: Tämä verkkotunnus voi noutaa tietoja tältä palvelimelta, ja sieltä saapuvat tiedot käsitellään ja tallennetaan email_domain_block: @@ -99,7 +99,7 @@ fi: min_age: Käyttäjiä pyydetään rekisteröitymisen aikana vahvistamaan syntymäpäivänsä peers_api_enabled: Luettelo verkkotunnuksista, jotka tämä palvelin on kohdannut fediversumissa. Se ei kerro, federoitko tietyn palvelimen kanssa, vaan että palvelimesi on ylipäätään tietoinen siitä. Tätä tietoa käytetään palveluissa, jotka keräävät tilastoja federoinnista yleisellä tasolla. profile_directory: Profiilihakemisto luetteloi kaikki käyttäjät, jotka ovat valinneet olla löydettävissä. - require_invite_text: Kun rekisteröityminen vaatii manuaalisen hyväksynnän, tee ”Miksi haluat liittyä?” -tekstikentästä pakollinen vapaaehtoisen sijaan + require_invite_text: Kun rekisteröityminen vaatii manuaalisen hyväksynnän, tee ”Miksi haluat liittyä?” -⁠tekstikentästä pakollinen vapaaehtoisen sijaan site_contact_email: Miten sinut voi tavoittaa oikeus- tai tukikysymyksissä. site_contact_username: Miten sinut voi tavoittaa Mastodonissa. site_extended_description: Kaikki lisätiedot, jotka voivat olla hyödyllisiä kävijöille ja käyttäjille. Voidaan jäsentää Markdown-syntaksilla. @@ -146,7 +146,7 @@ fi: arbitration_address: Voi olla sama kuin edellä mainittu Fyysinen osoite tai ”N/A”, jos käytät sähköpostia. arbitration_website: Voi olla verkkolomake tai ”N/A”, jos käytät sähköpostia. choice_of_law: Kaupunki, alue, territorio tai valtio, jonka sisäinen aineellinen oikeus säätelee kaikkia vaatimuksia. - dmca_address: Yhdysvaltalaisten operaattoreiden on käytettävä DMCA Designated Agent Directory -luetteloon rekisteröityä osoitetta. Postilokeroluettelo on saatavissa suoralla pyynnöllä, joten käytä DMCA Designated Agent Post Office Box Waiver Request -lomaketta lähettääksesi sähköpostia tekijänoikeusvirastolle ja kuvaile, että olet kotona toimiva sisältömoderaattori, joka pelkää kostoa tai rangaistusta toimistaan ja tarvitsee postilokeroa pitääkseen kotiosoitteensa poissa julkisuudesta. + dmca_address: Yhdysvaltalaisten operaattoreiden on käytettävä DMCA Designated Agent Directory -⁠luetteloon rekisteröityä osoitetta. Postilokeroluettelo on saatavissa suoralla pyynnöllä, joten käytä DMCA Designated Agent Post Office Box Waiver Request -⁠lomaketta lähettääksesi sähköpostia tekijänoikeusvirastolle ja kuvaile, että olet kotona toimiva sisältömoderaattori, joka pelkää kostoa tai rangaistusta toimistaan ja tarvitsee postilokeroa pitääkseen kotiosoitteensa poissa julkisuudesta. dmca_email: Voi olla sama kuin edellä mainittu ”Sähköpostiosoite oikeudellisille ilmoituksille”. domain: Tarjoamasi verkkopalvelun yksilöllinen tunniste. jurisdiction: Mainitse valtio, jossa laskujen maksaja asuu. Jos kyseessä on yritys tai muu yhteisö, mainitse valtio, johon se on rekisteröity, ja tarvittaessa kaupunki, alue, territorio tai osavaltio. diff --git a/config/locales/simple_form.fr-CA.yml b/config/locales/simple_form.fr-CA.yml index 2fc9fd53145d46..265782086c92f8 100644 --- a/config/locales/simple_form.fr-CA.yml +++ b/config/locales/simple_form.fr-CA.yml @@ -57,11 +57,15 @@ fr-CA: setting_advanced_layout: Afficher Mastodon avec une mise en page multicolonnes, vous permettant de visualiser le flux, les notifications et une troisième colonne de votre choix. Non recommandé pour les écrans plus petits. setting_aggregate_reblogs: Ne pas afficher les nouveaux partages pour les messages déjà récemment partagés (n’affecte que les partages futurs) setting_always_send_emails: Normalement, les notifications par courriel ne seront pas envoyées lorsque vous utilisez Mastodon activement + setting_boost_modal: Lorsque cette option est activée, avant de partager un message un écran de confirmation vous permettra de changer la visibilité du partage. + setting_default_quote_policy_private: Les messages limités aux personnes qui vous suivent publiés depuis Mastodon ne peuvent pas être cités. setting_default_quote_policy_unlisted: Lorsque des personnes vous citent, leur message sera également masqué des fils des tendances. setting_default_sensitive: Les médias sensibles sont cachés par défaut et peuvent être révélés d’un simple clic setting_display_media_default: Masquer les médias marqués comme sensibles setting_display_media_hide_all: Toujours masquer les médias setting_display_media_show_all: Toujours afficher les médias + setting_emoji_style: Manière d'afficher les émojis. Utiliser « Auto » pour essayer d'utiliser les émojis natifs, mais Twemoji sera utilisé pour les anciens navigateurs. + setting_quick_boosting_html: Lorsque cette option est activée, cliquer sur l'icône de partage %{boost_icon} va immédiatement partager le message au lieu d'ouvrir le menu déroulant Partage/Citation. L'action de citation est déplacée dans le menu %{options_icon} (options). setting_system_scrollbars_ui: S'applique uniquement aux navigateurs basés sur Safari et Chrome setting_use_blurhash: Les dégradés sont basés sur les couleurs des images cachées mais n’en montrent pas les détails setting_use_pending_items: Cacher les mises à jour des fils d’actualités derrière un clic, au lieu de les afficher automatiquement @@ -75,6 +79,7 @@ fr-CA: featured_tag: name: 'Voici quelques hashtags que vous avez utilisés récemment :' filters: + action: Choisir l'action à effectuer quand un message correspond au filtre actions: blur: Cacher les médias derrière un avertissement, sans cacher le texte hide: Cacher complètement le contenu filtré, faire comme s'il n'existait pas @@ -83,10 +88,12 @@ fr-CA: activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire app_icon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée. backups_retention_period: Les utilisateur·rice·s ont la possibilité de générer des archives de leurs messages pour les télécharger plus tard. Lorsqu'elles sont définies à une valeur positive, ces archives seront automatiquement supprimées de votre stockage après le nombre de jours spécifié. + bootstrap_timeline_accounts: Ces comptes seront épinglés en haut des recommandations pour les nouveaux utilisateurs. Accepte une liste de comptes séparés par des virgules. closed_registrations_message: Affiché lorsque les inscriptions sont fermées content_cache_retention_period: Tous les messages provenant d'autres serveurs (y compris les partages et les réponses) seront supprimés passé le nombre de jours spécifié, sans tenir compte de l'interaction de l'utilisateur·rice local·e avec ces messages. Cela inclut les messages qu'un·e utilisateur·rice aurait marqué comme signets ou comme favoris. Les mentions privées entre utilisateur·rice·s de différentes instances seront également perdues et impossibles à restaurer. L'utilisation de ce paramètre est destinée à des instances spécifiques et contrevient à de nombreuses attentes des utilisateurs lorsqu'elle est appliquée à des fins d'utilisation ordinaires. custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon. favicon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée. + landing_page: Sélectionner la page à afficher aux nouveaux visiteurs quand ils arrivent sur votre serveur. Pour utiliser « Tendances » les tendances doivent être activées dans les paramètres de découverte. Pour utiliser « Fil local » le paramètre « Accès au flux en direct de ce serveur » doit être défini sur « Tout le monde » dans les paramètres de découverte. mascot: Remplace l'illustration dans l'interface Web avancée. media_cache_retention_period: Les fichiers médias des messages publiés par des utilisateurs distants sont mis en cache sur votre serveur. Lorsque cette valeur est positive, les médias sont supprimés au terme du nombre de jours spécifié. Si les données des médias sont demandées après leur suppression, elles seront téléchargées à nouveau, dans la mesure où le contenu source est toujours disponible. En raison des restrictions concernant la fréquence à laquelle les cartes de prévisualisation des liens interrogent des sites tiers, il est recommandé de fixer cette valeur à au moins 14 jours, faute de quoi les cartes de prévisualisation des liens ne seront pas mises à jour à la demande avant cette échéance. min_age: Les utilisateurs seront invités à confirmer leur date de naissance lors de l'inscription @@ -146,6 +153,9 @@ fr-CA: min_age: Ne doit pas être en dessous de l’âge minimum requis par les lois de votre juridiction. user: chosen_languages: Lorsque coché, seuls les messages dans les langues sélectionnées seront affichés sur les fils publics + date_of_birth: + one: Nous devons vérifier que vous avez au moins %{count} an pour utiliser %{domain}. Cette information ne sera pas conservée. + other: Nous devons vérifier que vous avez au moins %{count} ans pour utiliser %{domain}. Cette information ne sera pas conservée. role: Le rôle définit quelles autorisations a l'utilisateur⋅rice. user_role: color: Couleur à attribuer au rôle dans l'interface, au format hexadécimal RVB @@ -154,7 +164,9 @@ fr-CA: permissions_as_keys: Les utilisateur·rice·s ayant ce rôle auront accès à … position: Dans certaines situations, un rôle supérieur peut trancher la résolution d'un conflit. Mais certaines opérations ne peuvent être effectuées que sur des rôles ayant une priorité inférieure username_block: + allow_with_approval: Au lieu de bloquer l'inscription, les inscriptions correspondantes nécessiteront votre approbation comparison: Veuillez garder à l'esprit le problème de Scunthorpe lors du blocage des correspondances partielles + username: Correspondra peu importe la casse utilisée et pour les homoglyphes courants – par exemple « 4 » pour « a » ou « 3 » pour « e » webhook: events: Sélectionnez les événements à envoyer template: Écrivez votre propre bloc JSON avec la possibilité d’utiliser de l’interpolation de variables. Laissez vide pour le bloc JSON par défaut. @@ -276,12 +288,16 @@ fr-CA: custom_css: CSS personnalisé favicon: Favicon landing_page: Page d'accueil pour les nouveaux visiteurs + local_live_feed_access: Accès au flux en direct de ce serveur + local_topic_feed_access: Accès aux flux hashtag et lien de ce serveur mascot: Mascotte personnalisée (héritée) media_cache_retention_period: Durée de rétention des médias dans le cache min_age: Âge minimum requis peers_api_enabled: Publie la liste des serveurs découverts dans l'API profile_directory: Activer l’annuaire des profils registrations_mode: Qui peut s’inscrire + remote_live_feed_access: Accès au flux en direct des autres serveurs + remote_topic_feed_access: Accès aux flux hashtag et lien des autres serveurs require_invite_text: Exiger une raison pour s’inscrire show_domain_blocks: Afficher les blocages de domaines show_domain_blocks_rationale: Montrer pourquoi les domaines ont été bloqués @@ -356,6 +372,7 @@ fr-CA: jurisdiction: Juridiction min_age: Âge minimum user: + date_of_birth_1i: Année date_of_birth_2i: Mois date_of_birth_3i: Jour role: Rôle diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index 2b996ce94a304a..a40211f36d657a 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -57,11 +57,15 @@ fr: setting_advanced_layout: Afficher Mastodon avec une mise en page multicolonnes, vous permettant de visualiser le flux, les notifications et une troisième colonne de votre choix. Non recommandé pour les écrans plus petits. setting_aggregate_reblogs: Ne pas afficher les nouveaux partages pour les messages déjà récemment partagés (n’affecte que les partages futurs) setting_always_send_emails: Normalement, les notifications par courriel ne seront pas envoyées lorsque vous utilisez Mastodon activement + setting_boost_modal: Lorsque cette option est activée, avant de partager un message un écran de confirmation vous permettra de changer la visibilité du partage. + setting_default_quote_policy_private: Les messages limités aux personnes qui vous suivent publiés depuis Mastodon ne peuvent pas être cités. setting_default_quote_policy_unlisted: Lorsque des personnes vous citent, leur message sera également masqué des fils des tendances. setting_default_sensitive: Les médias sensibles sont cachés par défaut et peuvent être révélés d’un simple clic setting_display_media_default: Masquer les médias marqués comme sensibles setting_display_media_hide_all: Toujours masquer les médias setting_display_media_show_all: Toujours afficher les médias + setting_emoji_style: Manière d'afficher les émojis. Utiliser « Auto » pour essayer d'utiliser les émojis natifs, mais Twemoji sera utilisé pour les anciens navigateurs. + setting_quick_boosting_html: Lorsque cette option est activée, cliquer sur l'icône de partage %{boost_icon} va immédiatement partager le message au lieu d'ouvrir le menu déroulant Partage/Citation. L'action de citation est déplacée dans le menu %{options_icon} (options). setting_system_scrollbars_ui: S'applique uniquement aux navigateurs basés sur Safari et Chrome setting_use_blurhash: Les dégradés sont basés sur les couleurs des images cachées mais n’en montrent pas les détails setting_use_pending_items: Cacher les mises à jour des fils d’actualités derrière un clic, au lieu de les afficher automatiquement @@ -75,6 +79,7 @@ fr: featured_tag: name: 'Voici quelques hashtags que vous avez utilisés récemment :' filters: + action: Choisir l'action à effectuer quand un message correspond au filtre actions: blur: Cacher les médias derrière un avertissement, sans cacher le texte hide: Cacher complètement le contenu filtré, faire comme s'il n'existait pas @@ -83,10 +88,12 @@ fr: activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire app_icon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée. backups_retention_period: Les utilisateur·rice·s ont la possibilité de générer des archives de leurs messages pour les télécharger plus tard. Lorsqu'elles sont définies à une valeur positive, ces archives seront automatiquement supprimées de votre stockage après le nombre de jours spécifié. + bootstrap_timeline_accounts: Ces comptes seront épinglés en haut des recommandations pour les nouveaux utilisateurs. Accepte une liste de comptes séparés par des virgules. closed_registrations_message: Affiché lorsque les inscriptions sont fermées content_cache_retention_period: Tous les messages provenant d'autres serveurs (y compris les partages et les réponses) seront supprimés passé le nombre de jours spécifié, sans tenir compte de l'interaction de l'utilisateur·rice local·e avec ces messages. Cela inclut les messages qu'un·e utilisateur·rice aurait marqué comme signets ou comme favoris. Les mentions privées entre utilisateur·rice·s de différentes instances seront également perdues et impossibles à restaurer. L'utilisation de ce paramètre est destinée à des instances spécifiques et contrevient à de nombreuses attentes des utilisateurs lorsqu'elle est appliquée à des fins d'utilisation ordinaires. custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon. favicon: WEBP, PNG, GIF ou JPG. Remplace la favicon Mastodon par défaut avec une icône personnalisée. + landing_page: Sélectionner la page à afficher aux nouveaux visiteurs quand ils arrivent sur votre serveur. Pour utiliser « Tendances » les tendances doivent être activées dans les paramètres de découverte. Pour utiliser « Fil local » le paramètre « Accès au flux en direct de ce serveur » doit être défini sur « Tout le monde » dans les paramètres de découverte. mascot: Remplace l'illustration dans l'interface Web avancée. media_cache_retention_period: Les fichiers médias des messages publiés par des utilisateurs distants sont mis en cache sur votre serveur. Lorsque cette valeur est positive, les médias sont supprimés au terme du nombre de jours spécifié. Si les données des médias sont demandées après leur suppression, elles seront téléchargées à nouveau, dans la mesure où le contenu source est toujours disponible. En raison des restrictions concernant la fréquence à laquelle les cartes de prévisualisation des liens interrogent des sites tiers, il est recommandé de fixer cette valeur à au moins 14 jours, faute de quoi les cartes de prévisualisation des liens ne seront pas mises à jour à la demande avant cette échéance. min_age: Les utilisateurs seront invités à confirmer leur date de naissance lors de l'inscription @@ -146,6 +153,9 @@ fr: min_age: Ne doit pas être en dessous de l’âge minimum requis par les lois de votre juridiction. user: chosen_languages: Lorsque coché, seuls les messages dans les langues sélectionnées seront affichés sur les fils publics + date_of_birth: + one: Nous devons vérifier que vous avez au moins %{count} an pour utiliser %{domain}. Cette information ne sera pas conservée. + other: Nous devons vérifier que vous avez au moins %{count} ans pour utiliser %{domain}. Cette information ne sera pas conservée. role: Le rôle définit quelles autorisations a l'utilisateur⋅rice. user_role: color: Couleur à attribuer au rôle dans l'interface, au format hexadécimal RVB @@ -154,7 +164,9 @@ fr: permissions_as_keys: Les utilisateur·rice·s ayant ce rôle auront accès à … position: Dans certaines situations, un rôle supérieur peut trancher la résolution d'un conflit. Mais certaines opérations ne peuvent être effectuées que sur des rôles ayant une priorité inférieure username_block: + allow_with_approval: Au lieu de bloquer l'inscription, les inscriptions correspondantes nécessiteront votre approbation comparison: Veuillez garder à l'esprit le problème de Scunthorpe lors du blocage des correspondances partielles + username: Correspondra peu importe la casse utilisée et pour les homoglyphes courants – par exemple « 4 » pour « a » ou « 3 » pour « e » webhook: events: Sélectionnez les événements à envoyer template: Écrivez votre propre bloc JSON avec la possibilité d’utiliser de l’interpolation de variables. Laissez vider pour utiliser le bloc JSON par défaut. @@ -276,12 +288,16 @@ fr: custom_css: CSS personnalisé favicon: Favicon landing_page: Page d'accueil pour les nouveaux visiteurs + local_live_feed_access: Accès au flux en direct de ce serveur + local_topic_feed_access: Accès aux flux hashtag et lien de ce serveur mascot: Mascotte personnalisée (héritée) media_cache_retention_period: Durée de rétention des médias dans le cache min_age: Âge minimum requis peers_api_enabled: Publie la liste des serveurs découverts dans l'API profile_directory: Activer l’annuaire des profils registrations_mode: Qui peut s’inscrire + remote_live_feed_access: Accès au flux en direct des autres serveurs + remote_topic_feed_access: Accès aux flux hashtag et lien des autres serveurs require_invite_text: Exiger une raison pour s’inscrire show_domain_blocks: Afficher les blocages de domaines show_domain_blocks_rationale: Montrer pourquoi les domaines ont été bloqués @@ -356,6 +372,7 @@ fr: jurisdiction: Juridiction min_age: Âge minimum user: + date_of_birth_1i: Année date_of_birth_2i: Mois date_of_birth_3i: Jour role: Rôle diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 8c06f285102183..cb18817ecd7205 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -56,6 +56,7 @@ ja: scopes: アプリの API に許可するアクセス権を選択してください。最上位のスコープを選択する場合、個々のスコープを選択する必要はありません。 setting_aggregate_reblogs: 最近ブーストされた投稿が新たにブーストされても表示しません (設定後受信したものにのみ影響) setting_always_send_emails: 通常、Mastodon からメール通知は行われません。 + setting_boost_modal: 有効にすると、ブーストによって、まず、ブーストの公開範囲を設定できる確認ダイアログが表示されます。 setting_default_sensitive: 閲覧注意状態のメディアはデフォルトでは内容が伏せられ、クリックして初めて閲覧できるようになります setting_display_media_default: 閲覧注意としてマークされたメディアは隠す setting_display_media_hide_all: メディアを常に隠す @@ -222,6 +223,7 @@ ja: setting_aggregate_reblogs: ブーストをまとめる setting_always_send_emails: 常にメール通知を送信する setting_auto_play_gif: アニメーションGIFを自動再生する + setting_boost_modal: ブーストの公開範囲の設定 setting_default_language: 投稿する言語 setting_default_privacy: 投稿の公開範囲 setting_default_quote_policy: 引用できるユーザー From edfbcfb3f515aef0b7eb487c835c63f6c593f521 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 1 Dec 2025 11:52:06 +0100 Subject: [PATCH 065/123] Fix error handling when re-fetching already-known statuses (#37077) --- app/javascript/mastodon/actions/statuses.js | 2 ++ app/javascript/mastodon/reducers/statuses.js | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 5602fcadb694fb..b883e986ddd7e2 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -85,6 +85,8 @@ export function fetchStatus(id, { dispatch(fetchStatusSuccess(skipLoading)); }).catch(error => { dispatch(fetchStatusFail(id, error, skipLoading, parentQuotePostId)); + if (error.status === 404) + dispatch(deleteFromTimelines(id)); }); }; } diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js index a334709db0a88f..cf17c066c3722d 100644 --- a/app/javascript/mastodon/reducers/statuses.js +++ b/app/javascript/mastodon/reducers/statuses.js @@ -65,6 +65,10 @@ const statusTranslateUndo = (state, id) => { }); }; +const removeStatusStub = (state, id) => { + return state.getIn([id, 'id']) ? state.deleteIn([id, 'isLoading']) : state.delete(id); +} + /** @type {ImmutableMap} */ const initialState = ImmutableMap(); @@ -92,11 +96,10 @@ export default function statuses(state = initialState, action) { return state.setIn([action.id, 'isLoading'], true); case STATUS_FETCH_FAIL: { if (action.parentQuotePostId && action.error.status === 404) { - return state - .delete(action.id) + return removeStatusStub(state, action.id) .setIn([action.parentQuotePostId, 'quote', 'state'], 'deleted') } else { - return state.delete(action.id); + return removeStatusStub(state, action.id); } } case STATUS_IMPORT: From e5651e7e048f644cc521b067186e738a6cfade16 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 1 Dec 2025 15:11:49 +0100 Subject: [PATCH 066/123] Make settings-related database migrations more robust (#37079) --- .../20251002140103_migrate_timeline_preview_setting.rb | 5 ++++- db/migrate/20251023210145_migrate_landing_page_setting.rb | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/db/migrate/20251002140103_migrate_timeline_preview_setting.rb b/db/migrate/20251002140103_migrate_timeline_preview_setting.rb index 9a18a6451ce421..ad0d4ab4c2f454 100644 --- a/db/migrate/20251002140103_migrate_timeline_preview_setting.rb +++ b/db/migrate/20251002140103_migrate_timeline_preview_setting.rb @@ -4,6 +4,8 @@ class MigrateTimelinePreviewSetting < ActiveRecord::Migration[8.0] class Setting < ApplicationRecord; end def up + Setting.reset_column_information + setting = Setting.find_by(var: 'timeline_preview') return unless setting.present? && setting.attributes['value'].present? @@ -12,7 +14,8 @@ def up Setting.upsert_all( %w(local_live_feed_access remote_live_feed_access local_topic_feed_access remote_topic_feed_access).map do |var| { var: var, value: value ? "--- public\n" : "--- authenticated\n" } - end + end, + unique_by: index_exists?(:settings, [:thing_type, :thing_id, :var]) ? [:thing_type, :thing_id, :var] : :var ) end diff --git a/db/migrate/20251023210145_migrate_landing_page_setting.rb b/db/migrate/20251023210145_migrate_landing_page_setting.rb index e8448bc75e3b26..db9dc333b9afe0 100644 --- a/db/migrate/20251023210145_migrate_landing_page_setting.rb +++ b/db/migrate/20251023210145_migrate_landing_page_setting.rb @@ -4,6 +4,8 @@ class MigrateLandingPageSetting < ActiveRecord::Migration[8.0] class Setting < ApplicationRecord; end def up + Setting.reset_column_information + setting = Setting.find_by(var: 'trends_as_landing_page') return unless setting.present? && setting.attributes['value'].present? @@ -12,7 +14,8 @@ def up Setting.upsert({ var: 'landing_page', value: value ? "--- trends\n" : "--- about\n", - }) + }, + unique_by: index_exists?(:settings, [:thing_type, :thing_id, :var]) ? [:thing_type, :thing_id, :var] : :var) end def down; end From f5d6f60ca70aeab12e26878bea24c8316c46b09f Mon Sep 17 00:00:00 2001 From: diondiondion Date: Mon, 1 Dec 2025 15:16:27 +0100 Subject: [PATCH 067/123] Ensure all pages have a solid background color (#37081) --- app/javascript/styles/mastodon/basics.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index 6298409d157c97..db584f67f1e153 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -1,5 +1,10 @@ @use 'variables' as *; +html { + color: var(--color-text-primary); + background: var(--color-bg-ambient); +} + html.has-modal { &, body { From 048746e56b69a7192990ed72be50be465474e46b Mon Sep 17 00:00:00 2001 From: Echo Date: Mon, 1 Dec 2025 15:24:24 +0100 Subject: [PATCH 068/123] Hide domain for local emojis in admin (#37080) --- app/controllers/admin/custom_emojis_controller.rb | 9 +++++++++ app/views/admin/custom_emojis/index.html.haml | 8 +++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index e3da834fcd3d07..b6fc9ef27c633d 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -5,6 +5,15 @@ class CustomEmojisController < BaseController def index authorize :custom_emoji, :index? + # If filtering by local emojis, remove by_domain filter. + params.delete(:by_domain) if params[:local].present? + + # If filtering by domain, ensure remote filter is set. + if params[:by_domain].present? + params.delete(:local) + params[:remote] = '1' + end + @custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page]) @form = Form::CustomEmojiBatch.new end diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index b9e11a17d1ea98..b7f2d4f631f9cf 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -9,15 +9,15 @@ .filter-subset %strong= t('admin.accounts.location.title') %ul - %li= filter_link_to t('admin.accounts.location.all'), local: nil, remote: nil + %li= filter_link_to t('admin.accounts.location.all'), local: nil, remote: nil, by_domain: nil %li - if selected? local: '1', remote: nil = filter_link_to t('admin.accounts.location.local'), { local: nil, remote: nil }, { local: '1', remote: nil } - else - = filter_link_to t('admin.accounts.location.local'), local: '1', remote: nil + = filter_link_to t('admin.accounts.location.local'), local: '1', remote: nil, by_domain: nil %li - if selected? remote: '1', local: nil - = filter_link_to t('admin.accounts.location.remote'), { remote: nil, local: nil }, { remote: '1', local: nil } + = filter_link_to t('admin.accounts.location.remote'), { remote: nil, local: nil, by_domain: nil }, { remote: '1', local: nil } - else = filter_link_to t('admin.accounts.location.remote'), remote: '1', local: nil @@ -27,6 +27,8 @@ = form.hidden_field key, value: params[key] if params[key].present? - %i(shortcode by_domain).each do |key| + - next if key == :by_domain && params[:local] == '1' + .input.string.optional = form.text_field key, value: params[key], From 1f9ddb7cf6fcf7a5193bd0b856c69e070323a807 Mon Sep 17 00:00:00 2001 From: Christian Oelschlegel <18003795+oelison@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:56:52 +0100 Subject: [PATCH 069/123] fix(tag): prevent dupl. tags on concurrent inserts (#35792) Co-authored-by: Christian Oelschlegel Co-authored-by: Claire --- app/models/tag.rb | 10 ++++++++-- spec/models/tag_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/models/tag.rb b/app/models/tag.rb index f9eb6bfd33ed42..c59b0f36a87646 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -112,8 +112,14 @@ def find_or_create_by_names(name_or_names) names = Array(name_or_names).map { |str| [normalize(str), str] }.uniq(&:first) names.map do |(normalized_name, display_name)| - tag = matching_name(normalized_name).first || create(name: normalized_name, - display_name: display_name.gsub(HASHTAG_INVALID_CHARS_RE, '')) + tag = begin + matching_name(normalized_name).first || create!( + name: normalized_name, + display_name: display_name.gsub(HASHTAG_INVALID_CHARS_RE, '') + ) + rescue ActiveRecord::RecordNotUnique + find_normalized(normalized_name) + end yield tag if block_given? diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index d41d3a9e21e1e5..9a68ae36d63c23 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -265,6 +265,27 @@ def previous_name_error_message end end + describe '.find_or_create_by_names_race_condition' do + it 'handles simultaneous inserts of the same tag in different cases without error' do + tag_name_upper = 'Rails' + tag_name_lower = 'rails' + + threads = [] + + 2.times do |i| + threads << Thread.new do + described_class.find_or_create_by_names(i.zero? ? tag_name_upper : tag_name_lower) + end + end + + threads.each(&:join) + + tags = described_class.where('lower(name) = ?', tag_name_lower.downcase) + expect(tags.count).to eq(1) + expect(tags.first.name.downcase).to eq(tag_name_lower.downcase) + end + end + describe '.search_for' do it 'finds tag records with matching names' do tag = Fabricate(:tag, name: 'match') From 2d203ca72aeeb0ab0a08a68757565686affb7317 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Mon, 1 Dec 2025 16:04:52 +0100 Subject: [PATCH 070/123] First draft of API to get a single collection (#37053) --- .../api/v1_alpha/collections_controller.rb | 9 +++- app/models/collection.rb | 8 ++++ app/models/collection_item.rb | 2 + .../rest/collection_item_serializer.rb | 9 ++++ app/serializers/rest/collection_serializer.rb | 7 +++ config/routes/api.rb | 2 +- .../fabricators/collection_item_fabricator.rb | 2 +- spec/models/collection_spec.rb | 24 ++++++++++ .../requests/api/v1_alpha/collections_spec.rb | 44 +++++++++++++++++++ .../rest/collection_item_serializer_spec.rb | 36 +++++++++++++++ .../rest/collection_serializer_spec.rb | 14 +++++- 11 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 app/serializers/rest/collection_item_serializer.rb create mode 100644 spec/serializers/rest/collection_item_serializer_spec.rb diff --git a/app/controllers/api/v1_alpha/collections_controller.rb b/app/controllers/api/v1_alpha/collections_controller.rb index 5583bb395da2d9..7c33d3bfa24807 100644 --- a/app/controllers/api/v1_alpha/collections_controller.rb +++ b/app/controllers/api/v1_alpha/collections_controller.rb @@ -9,7 +9,14 @@ class Api::V1Alpha::CollectionsController < Api::BaseController before_action -> { doorkeeper_authorize! :write, :'write:collections' }, only: [:create] - before_action :require_user! + before_action :require_user!, only: [:create] + + def show + cache_if_unauthenticated! + @collection = Collection.find(params[:id]) + + render json: @collection, serializer: REST::CollectionSerializer + end def create @collection = CreateCollectionService.new.call(collection_params, current_user.account) diff --git a/app/models/collection.rb b/app/models/collection.rb index 41f9ed0f02a40d..308e517e26cf9a 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -38,10 +38,18 @@ class Collection < ApplicationRecord validate :tag_is_usable validate :items_do_not_exceed_limit + scope :with_items, -> { includes(:collection_items).merge(CollectionItem.with_accounts) } + def remote? !local? end + def items_for(account = nil) + result = collection_items.with_accounts + result = result.not_blocked_by(account) unless account.nil? + result + end + private def tag_is_usable diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb index 0ea50e6914282c..48a18592fdad1a 100644 --- a/app/models/collection_item.rb +++ b/app/models/collection_item.rb @@ -33,6 +33,8 @@ class CollectionItem < ApplicationRecord validates :object_uri, presence: true, if: -> { account.nil? } scope :ordered, -> { order(position: :asc) } + scope :with_accounts, -> { includes(account: [:account_stat, :user]) } + scope :not_blocked_by, ->(account) { where.not(accounts: { id: account.blocking }) } def local_item_with_remote_account? local? && account&.remote? diff --git a/app/serializers/rest/collection_item_serializer.rb b/app/serializers/rest/collection_item_serializer.rb new file mode 100644 index 00000000000000..c0acc87bfd4e02 --- /dev/null +++ b/app/serializers/rest/collection_item_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class REST::CollectionItemSerializer < ActiveModel::Serializer + delegate :accepted?, to: :object + + attributes :position, :state + + belongs_to :account, serializer: REST::AccountSerializer, if: :accepted? +end diff --git a/app/serializers/rest/collection_serializer.rb b/app/serializers/rest/collection_serializer.rb index c03cc5385661a3..3d63e753983189 100644 --- a/app/serializers/rest/collection_serializer.rb +++ b/app/serializers/rest/collection_serializer.rb @@ -5,4 +5,11 @@ class REST::CollectionSerializer < ActiveModel::Serializer :created_at, :updated_at belongs_to :account, serializer: REST::AccountSerializer + belongs_to :tag, serializer: REST::StatusSerializer::TagSerializer + + has_many :items, serializer: REST::CollectionItemSerializer + + def items + object.items_for(current_user&.account) + end end diff --git a/config/routes/api.rb b/config/routes/api.rb index 2fa3d4d83359ca..32685d791ffc25 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -8,7 +8,7 @@ namespace :v1_alpha do resources :async_refreshes, only: :show - resources :collections, only: [:create] + resources :collections, only: [:show, :create] end # JSON / REST API diff --git a/spec/fabricators/collection_item_fabricator.rb b/spec/fabricators/collection_item_fabricator.rb index 011f9ba5b5ee39..db55f7d23737a5 100644 --- a/spec/fabricators/collection_item_fabricator.rb +++ b/spec/fabricators/collection_item_fabricator.rb @@ -3,7 +3,7 @@ Fabricator(:collection_item) do collection { Fabricate.build(:collection) } account { Fabricate.build(:account) } - position 1 + position { sequence(:position, 1) } state :accepted end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index 6c160ecb70af3c..3d367b9d908dcc 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -48,4 +48,28 @@ it { is_expected.to_not be_valid } end end + + describe '#item_for' do + subject { Fabricate(:collection) } + + let!(:items) { Fabricate.times(2, :collection_item, collection: subject) } + + context 'when given no account' do + it 'returns all items' do + expect(subject.items_for).to match_array(items) + end + end + + context 'when given an account' do + let(:account) { Fabricate(:account) } + + before do + account.block!(items.first.account) + end + + it 'does not return items blocked by this account' do + expect(subject.items_for(account)).to contain_exactly(items.last) + end + end + end end diff --git a/spec/requests/api/v1_alpha/collections_spec.rb b/spec/requests/api/v1_alpha/collections_spec.rb index 5f9c5e5f3475bc..d576fbf98bd971 100644 --- a/spec/requests/api/v1_alpha/collections_spec.rb +++ b/spec/requests/api/v1_alpha/collections_spec.rb @@ -5,6 +5,50 @@ RSpec.describe 'Api::V1Alpha::Collections', feature: :collections do include_context 'with API authentication', oauth_scopes: 'read:collections write:collections' + describe 'GET /api/v1_alpha/collections/:id' do + subject do + get "/api/v1_alpha/collections/#{collection.id}", headers: headers + end + + let(:collection) { Fabricate(:collection) } + let!(:items) { Fabricate.times(2, :collection_item, collection:) } + + shared_examples 'unfiltered, successful request' do + it 'includes all items in the response' do + subject + + expect(response).to have_http_status(200) + expect(response.parsed_body[:items].size).to eq 2 + end + end + + context 'when user is not signed in' do + let(:headers) { {} } + + it_behaves_like 'unfiltered, successful request' + end + + context 'when user is signed in' do + context 'when the user has not blocked or muted anyone' do + it_behaves_like 'unfiltered, successful request' + end + + context 'when the user has blocked an account' do + before do + user.account.block!(items.first.account) + end + + it 'only includes the non-blocked account in the response' do + subject + + expect(response).to have_http_status(200) + expect(response.parsed_body[:items].size).to eq 1 + expect(response.parsed_body[:items][0]['position']).to eq items.last.position + end + end + end + end + describe 'POST /api/v1_alpha/collections' do subject do post '/api/v1_alpha/collections', headers: headers, params: params diff --git a/spec/serializers/rest/collection_item_serializer_spec.rb b/spec/serializers/rest/collection_item_serializer_spec.rb new file mode 100644 index 00000000000000..bcb7458c4de259 --- /dev/null +++ b/spec/serializers/rest/collection_item_serializer_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe REST::CollectionItemSerializer do + subject { serialized_record_json(collection_item, described_class) } + + let(:collection_item) do + Fabricate(:collection_item, + state:, + position: 4) + end + + context 'when state is `accepted`' do + let(:state) { :accepted } + + it 'includes the relevant attributes including the account' do + expect(subject) + .to include( + 'account' => an_instance_of(Hash), + 'state' => 'accepted', + 'position' => 4 + ) + end + end + + %i(pending rejected revoked).each do |unaccepted_state| + context "when state is `#{unaccepted_state}`" do + let(:state) { unaccepted_state } + + it 'does not include an account' do + expect(subject.keys).to_not include('account') + end + end + end +end diff --git a/spec/serializers/rest/collection_serializer_spec.rb b/spec/serializers/rest/collection_serializer_spec.rb index c498937b508f27..10bf9ee2b5b3eb 100644 --- a/spec/serializers/rest/collection_serializer_spec.rb +++ b/spec/serializers/rest/collection_serializer_spec.rb @@ -3,15 +3,24 @@ require 'rails_helper' RSpec.describe REST::CollectionSerializer do - subject { serialized_record_json(collection, described_class) } + subject do + serialized_record_json(collection, described_class, options: { + scope: current_user, + scope_name: :current_user, + }) + end + + let(:current_user) { nil } + let(:tag) { Fabricate(:tag, name: 'discovery') } let(:collection) do Fabricate(:collection, name: 'Exquisite follows', description: 'Always worth a follow', local: true, sensitive: true, - discoverable: false) + discoverable: false, + tag:) end it 'includes the relevant attributes' do @@ -23,6 +32,7 @@ 'local' => true, 'sensitive' => true, 'discoverable' => false, + 'tag' => a_hash_including('name' => 'discovery'), 'created_at' => match_api_datetime_format, 'updated_at' => match_api_datetime_format ) From 2e5744e8c67ce14a4b31d81d9147c30e5a26c25e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 11:20:40 +0100 Subject: [PATCH 071/123] New Crowdin Translations (automated) (#37088) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/hi.json | 16 +++ app/javascript/mastodon/locales/lt.json | 142 +++++++++++++++++++++++- config/locales/de.yml | 2 +- config/locales/lt.yml | 22 +++- config/locales/simple_form.fi.yml | 2 +- config/locales/simple_form.lt.yml | 11 +- 6 files changed, 187 insertions(+), 8 deletions(-) diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json index ba3730d7b12072..c8fa5f86f5c7c4 100644 --- a/app/javascript/mastodon/locales/hi.json +++ b/app/javascript/mastodon/locales/hi.json @@ -9,9 +9,11 @@ "about.domain_blocks.silenced.title": "सीमित", "about.domain_blocks.suspended.explanation": "इस सर्वर से कोई डेटा संसाधित, संग्रहीत या आदान-प्रदान नहीं किया जाएगा, जिससे इस सर्वर से उपयोगकर्ताओं के साथ कोई भी बातचीत या संचार असंभव हो जाएगा", "about.domain_blocks.suspended.title": "सस्पेंड किआ गया है!", + "about.language_label": "भाषा", "about.not_available": "यह जानकारी इस सर्वर पर उपलब्ध नहीं कराई गई है।", "about.powered_by": "{mastodon} द्वारा संचालित डेसेंट्रलीसेड सोशल मीडिया प्लैटफ़ॉर्म!", "about.rules": "सर्वर के नियम", + "account.account_note_header": "व्यक्तिगत नोंध", "account.add_or_remove_from_list": "सूची में जोड़ें या हटाए", "account.badges.bot": "बॉट", "account.badges.group": "समूह", @@ -19,17 +21,31 @@ "account.block_domain": "{domain} के सारी चीज़े छुपाएं", "account.block_short": "ब्लॉक किया गया", "account.blocked": "ब्लॉक", + "account.blocking": "प्रतिबंधित करना", "account.cancel_follow_request": "फॉलो रिक्वेस्ट वापस लें", "account.copy": "प्रोफाइल पर लिंक कॉपी करें", "account.direct": "निजि तरीके से उल्लेख करे @{name}", "account.disable_notifications": "@{name} पोस्ट के लिए मुझे सूचित मत करो", + "account.domain_blocking": "डोमेन ब्लॉक करें", "account.edit_profile": "प्रोफ़ाइल संपादित करें", + "account.edit_profile_short": "संपादित करें", "account.enable_notifications": "जब @{name} पोस्ट मौजूद हो सूचित करें", "account.endorse": "प्रोफ़ाइल पर दिखाए", + "account.familiar_followers_many": "{name1}{name2} और {othersCount, plural, one {एक और जिन्हे आप जानते है} other {# और जिन्हे आप जानते है}}", + "account.familiar_followers_one": "{name1} ने अनुसरण किया है", + "account.familiar_followers_two": "{name1} और {name2} ने अनुसरण किया है", + "account.featured": "प्रचलित", + "account.featured.accounts": "प्रोफ़ाइल", + "account.featured.hashtags": "हैशटैग्स", "account.featured_tags.last_status_at": "{date} का अंतिम पोस्ट", "account.featured_tags.last_status_never": "कोई पोस्ट नहीं है", "account.follow": "फॉलो करें", "account.follow_back": "फॉलो करें", + "account.follow_back_short": "वापस अनुसरण करें", + "account.follow_request": "अनुसरण करने की बिनती करें", + "account.follow_request_cancel": "अनुरोध रद्द करें", + "account.follow_request_cancel_short": "रद्द करें", + "account.follow_request_short": "अनुरोध करें", "account.followers": "फॉलोवर", "account.followers.empty": "कोई भी इस यूज़र् को फ़ॉलो नहीं करता है", "account.following": "फॉलोइंग", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 501ff11eb1441d..06026984dec8d5 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -1,6 +1,7 @@ { "about.blocks": "Prižiūrimi serveriai", "about.contact": "Kontaktai:", + "about.default_locale": "Numatyta", "about.disclaimer": "„Mastodon“ – tai nemokama atvirojo kodo programinė įranga ir „Mastodon gGmbH“ prekės ženklas.", "about.domain_blocks.no_reason_available": "Priežastis nepateikta", "about.domain_blocks.preamble": "„Mastodon“ paprastai leidžia peržiūrėti turinį ir bendrauti su naudotojais iš bet kurio kito fediverse esančio serverio. Šios yra išimtys, kurios buvo padarytos šiame konkrečiame serveryje.", @@ -8,6 +9,7 @@ "about.domain_blocks.silenced.title": "Apribota", "about.domain_blocks.suspended.explanation": "Jokie duomenys iš šio serverio nebus apdorojami, saugomi ar keičiami, todėl bet kokia sąveika ar bendravimas su šio serverio naudotojais bus neįmanomas.", "about.domain_blocks.suspended.title": "Pristabdyta", + "about.language_label": "Kalba", "about.not_available": "Ši informacija nebuvo pateikta šiame serveryje.", "about.powered_by": "Decentralizuota socialinė medija, veikianti pagal „{mastodon}“", "about.rules": "Serverio taisyklės", @@ -26,8 +28,12 @@ "account.disable_notifications": "Nustoti man pranešti, kai @{name} paskelbia", "account.domain_blocking": "Blokuoti domeną", "account.edit_profile": "Redaguoti profilį", + "account.edit_profile_short": "Redaguoti", "account.enable_notifications": "Pranešti man, kai @{name} paskelbia", "account.endorse": "Rodyti profilyje", + "account.familiar_followers_many": "Sekama {name1}, {name2}, ir {othersCount, plural, one {dar vieno} few {# dar keleto} many {# dar kelių} other {# kitų pažystąmų}}", + "account.familiar_followers_one": "Seka {name1}", + "account.familiar_followers_two": "{name1} ir {name2} seka", "account.featured": "Rodomi", "account.featured.accounts": "Profiliai", "account.featured.hashtags": "Saitažodžiai", @@ -35,9 +41,15 @@ "account.featured_tags.last_status_never": "Nėra įrašų", "account.follow": "Sekti", "account.follow_back": "Sekti atgal", + "account.follow_back_short": "Sekti atgal", + "account.follow_request": "Prašyti sekti", + "account.follow_request_cancel": "Atšaukti prašymą", + "account.follow_request_cancel_short": "Atšaukti", + "account.follow_request_short": "Prašymas", "account.followers": "Sekėjai", "account.followers.empty": "Šio naudotojo dar niekas neseka.", "account.followers_counter": "{count, plural, one {{counter} sekėjas} few {{counter} sekėjai} many {{counter} sekėjo} other {{counter} sekėjų}}", + "account.followers_you_know_counter": "{counter} žinomas", "account.following": "Sekama", "account.following_counter": "{count, plural, one {{counter} sekimas} few {{counter} sekimai} many {{counter} sekimo} other {{counter} sekimų}}", "account.follows.empty": "Šis naudotojas dar nieko neseka.", @@ -145,6 +157,7 @@ "bundle_modal_error.close": "Uždaryti", "bundle_modal_error.message": "Įkeliant šį ekraną kažkas nutiko ne taip.", "bundle_modal_error.retry": "Bandyti dar kartą", + "carousel.slide": "Rodoma {current, number} iš {max, number}", "closed_registrations.other_server_instructions": "Kadangi „Mastodon“ yra decentralizuotas, gali susikurti paskyrą kitame serveryje ir vis tiek bendrauti su šiuo serveriu.", "closed_registrations_modal.description": "Sukurti paskyrą serveryje {domain} šiuo metu neįmanoma, bet nepamiršk, kad norint naudotis „Mastodon“ nebūtina turėti paskyrą serveryje {domain}.", "closed_registrations_modal.find_another_server": "Rasti kitą serverį", @@ -161,6 +174,8 @@ "column.edit_list": "Redaguoti sąrašą", "column.favourites": "Mėgstami", "column.firehose": "Tiesioginiai srautai", + "column.firehose_local": "Tiesioginis srautas iš šio serverio", + "column.firehose_singular": "Tiesioginis srautas", "column.follow_requests": "Sekimo prašymai", "column.home": "Pagrindinis", "column.list_members": "Tvarkyti sąrašo narius", @@ -180,6 +195,7 @@ "community.column_settings.local_only": "Tik vietinis", "community.column_settings.media_only": "Tik medija", "community.column_settings.remote_only": "Tik nuotolinis", + "compose.error.blank_post": "Įrašas negali būti tuščias.", "compose.language.change": "Keisti kalbą", "compose.language.search": "Ieškoti kalbų...", "compose.published.body": "Įrašas paskelbtas.", @@ -212,6 +228,13 @@ "confirmations.delete_list.confirm": "Ištrinti", "confirmations.delete_list.message": "Ar tikrai nori negrįžtamai ištrinti šį sąrašą?", "confirmations.delete_list.title": "Ištrinti sąrašą?", + "confirmations.discard_draft.confirm": "Išsaugoti ir tęsti", + "confirmations.discard_draft.edit.cancel": "Tęsti redagavimą", + "confirmations.discard_draft.edit.message": "Tęsdami, jūs prarasite visus pakeitimus, kuriuos padarėte šiuo metu redaguojamame įraše.", + "confirmations.discard_draft.edit.title": "Atmesti pakeitimus savo įraše?", + "confirmations.discard_draft.post.cancel": "Tęsti juodraščio redagavimą", + "confirmations.discard_draft.post.message": "Tęsdami ištrinsite šiuo metu kuriamą įrašą.", + "confirmations.discard_draft.post.title": "Atmesti savo įrašo juodraštį?", "confirmations.discard_edit_media.confirm": "Atmesti", "confirmations.discard_edit_media.message": "Turi neišsaugotų medijos aprašymo ar peržiūros pakeitimų. Vis tiek juos atmesti?", "confirmations.follow_to_list.confirm": "Sekti ir pridėti prie sąrašo", @@ -225,13 +248,30 @@ "confirmations.missing_alt_text.secondary": "Siųsti vis tiek", "confirmations.missing_alt_text.title": "Pridėti alternatyvųjį tekstą?", "confirmations.mute.confirm": "Nutildyti", + "confirmations.private_quote_notify.cancel": "Grįžti prie redagavimo", + "confirmations.private_quote_notify.confirm": "Paskelbti įrašą", + "confirmations.private_quote_notify.do_not_show_again": "Neberodyti šio pranešimo dar kartą", + "confirmations.private_quote_notify.message": "Asmuo, kurį paminite, ir kiti paminėti asmenys bus informuoti ir galės peržiūrėti jūsų įrašą, net jei jie neseka jūsų.", + "confirmations.private_quote_notify.title": "Dalytis su sekėjais ir paminėtais vartotojais?", + "confirmations.quiet_post_quote_info.dismiss": "Daugiau man nepriminti", + "confirmations.quiet_post_quote_info.got_it": "Supratau", + "confirmations.quiet_post_quote_info.message": "Kai norite paminėti tylų viešą įrašą, jūsų įrašas bus paslėptas Tendencijų sąrašuose.", + "confirmations.quiet_post_quote_info.title": "Kai paminite tylius viešus įrašus", "confirmations.redraft.confirm": "Ištrinti ir iš naujo parengti", "confirmations.redraft.message": "Ar tikrai nori ištrinti šį įrašą ir parengti jį iš naujo? Bus prarasti mėgstami ir pasidalinimai, o atsakymai į originalų įrašą bus panaikinti.", "confirmations.redraft.title": "Ištrinti ir iš naujo parengti įrašą?", "confirmations.remove_from_followers.confirm": "Šalinti sekėją", "confirmations.remove_from_followers.message": "{name} nustos jus sekti. Ar tikrai norite tęsti?", "confirmations.remove_from_followers.title": "Šalinti sekėją?", + "confirmations.revoke_quote.confirm": "Pašalinti įrašą", + "confirmations.revoke_quote.message": "Šio veiksmo negalima anuliuoti.", + "confirmations.revoke_quote.title": "Pašalinti įrašą?", + "confirmations.unblock.confirm": "Atblokuoti", + "confirmations.unblock.title": "Atblokuoti {name}?", "confirmations.unfollow.confirm": "Nebesekti", + "confirmations.unfollow.title": "Nebesekti {name}?", + "confirmations.withdraw_request.confirm": "Atšaukti prašymą", + "confirmations.withdraw_request.title": "Atšaukti prašymą sekti {name}?", "content_warning.hide": "Slėpti įrašą", "content_warning.show": "Rodyti vis tiek", "content_warning.show_more": "Rodyti daugiau", @@ -250,6 +290,7 @@ "disabled_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu išjungta.", "dismissable_banner.community_timeline": "Tai – naujausi vieši įrašai iš žmonių, kurių paskyros talpinamos {domain}.", "dismissable_banner.dismiss": "Atmesti", + "dismissable_banner.public_timeline": "Tai yra naujausi vieši įrašai iš fediverse, kuriuos seka {domain} vartotojai.", "domain_block_modal.block": "Blokuoti serverį", "domain_block_modal.block_account_instead": "Blokuoti @{name} vietoj to", "domain_block_modal.they_can_interact_with_old_posts": "Žmonės iš šio serverio gali bendrauti su tavo senomis įrašomis.", @@ -272,6 +313,7 @@ "domain_pill.your_handle": "Tavo socialinis medijos vardas:", "domain_pill.your_server": "Tavo skaitmeniniai namai, kuriuose saugomi visi tavo įrašai. Nepatinka šis? Bet kada perkelk serverius ir atsivesk ir savo sekėjus.", "domain_pill.your_username": "Tavo unikalus identifikatorius šiame serveryje. Skirtinguose serveriuose galima rasti naudotojų su tuo pačiu naudotojo vardu.", + "dropdown.empty": "Pasirinkite variantą", "embed.instructions": "Įterpk šį įrašą į savo svetainę nukopijuojant toliau pateiktą kodą.", "embed.preview": "Štai kaip tai atrodys:", "emoji_button.activity": "Veikla", @@ -289,6 +331,8 @@ "emoji_button.search_results": "Paieškos rezultatai", "emoji_button.symbols": "Simboliai", "emoji_button.travel": "Kelionės ir vietos", + "empty_column.account_featured.me": "Jūs dar nieko neparyškinote. Ar žinojote, kad savo profilyje galite parodyti dažniausiai naudojamas žymes ir netgi savo draugų paskyras?", + "empty_column.account_featured.other": "{acct} dar nieko neparyškino. Ar žinojote, kad savo profilyje galite pateikti dažniausiai naudojamus žymes ir netgi savo draugų paskyras?", "empty_column.account_featured_other.unknown": "Ši paskyra dar nieko neparodė.", "empty_column.account_hides_collections": "Šis (-i) naudotojas (-a) pasirinko nepadaryti šią informaciją prieinamą.", "empty_column.account_suspended": "Paskyra pristabdyta.", @@ -298,6 +342,7 @@ "empty_column.bookmarked_statuses": "Dar neturi nė vienos įrašo pridėtos žymės. Kai vieną iš jų pridėsi į žymes, jis bus rodomas čia.", "empty_column.community": "Vietinė laiko skalė yra tuščia. Parašyk ką nors viešai, kad pradėtum sąveikauti.", "empty_column.direct": "Dar neturi jokių privačių paminėjimų. Kai išsiųsi arba gausi vieną iš jų, jis bus rodomas čia.", + "empty_column.disabled_feed": "Šis srautas buvo išjungtas jūsų serverio administratorių.", "empty_column.domain_blocks": "Kol kas nėra užblokuotų serverių.", "empty_column.explore_statuses": "Šiuo metu niekas nėra tendencinga. Patikrinkite vėliau!", "empty_column.favourited_statuses": "Dar neturi mėgstamų įrašų. Kai vieną iš jų pamėgsi, jis bus rodomas čia.", @@ -311,6 +356,7 @@ "empty_column.notification_requests": "Viskas švaru! Čia nieko nėra. Kai gausi naujų pranešimų, jie bus rodomi čia pagal tavo nustatymus.", "empty_column.notifications": "Dar neturi jokių pranešimų. Kai kiti žmonės su tavimi sąveikaus, matysi tai čia.", "empty_column.public": "Čia nieko nėra. Parašyk ką nors viešai arba rankiniu būdu sek naudotojus iš kitų serverių, kad jį užpildytum.", + "error.no_hashtag_feed_access": "Registruokitės arba prisijunkite, kad galėtumėte peržiūrėti ir sekti šį žymę.", "error.unexpected_crash.explanation": "Dėl mūsų kodo riktos arba naršyklės suderinamumo problemos šis puslapis negalėjo būti rodomas teisingai.", "error.unexpected_crash.explanation_addons": "Šį puslapį nepavyko parodyti teisingai. Šią klaidą greičiausiai sukėlė naršyklės priedas arba automatinio vertimo įrankiai.", "error.unexpected_crash.next_steps": "Pabandyk atnaujinti puslapį. Jei tai nepadeda, galbūt vis dar galėsi naudotis Mastodon per kitą naršyklę arba savąją programėlę.", @@ -318,9 +364,13 @@ "errors.unexpected_crash.copy_stacktrace": "Kopijuoti dėklo eigą į iškarpinę", "errors.unexpected_crash.report_issue": "Pranešti apie problemą", "explore.suggested_follows": "Žmonės", + "explore.title": "Populiaru", "explore.trending_links": "Naujienos", "explore.trending_statuses": "Įrašai", "explore.trending_tags": "Saitažodžiai", + "featured_carousel.current": "Įrašas {current, number} / {max, number}", + "featured_carousel.header": "{count, plural, one {Iškeltas įrašas} few {Iškelti įrašai} many {Iškeltų įrašų} other {Iškelti įrašai}}", + "featured_carousel.slide": "Įrašas {current, number} iš {max, number}", "filter_modal.added.context_mismatch_explanation": "Ši filtro kategorija netaikoma kontekstui, kuriame peržiūrėjai šį įrašą. Jei nori, kad įrašas būtų filtruojamas ir šiame kontekste, turėsi redaguoti filtrą.", "filter_modal.added.context_mismatch_title": "Konteksto neatitikimas.", "filter_modal.added.expired_explanation": "Ši filtro kategorija nustojo galioti. Kad ji būtų taikoma, turėsi pakeisti galiojimo datą.", @@ -363,6 +413,7 @@ "follow_suggestions.who_to_follow": "Ką sekti", "followed_tags": "Sekami saitažodžiai", "footer.about": "Apie", + "footer.about_this_server": "Apie", "footer.directory": "Profilių katalogas", "footer.get_app": "Gauti programėlę", "footer.keyboard_shortcuts": "Spartieji klavišai", @@ -373,6 +424,8 @@ "generic.saved": "Išsaugota", "getting_started.heading": "Kaip pradėti", "hashtag.admin_moderation": "Atverti prižiūrėjimo sąsają saitažodžiui #{name}", + "hashtag.browse": "Naršyti įrašus su #{hashtag}", + "hashtag.browse_from_account": "Naršyti @{name} įrašus su žyma #{hashtag}", "hashtag.column_header.tag_mode.all": "ir {additional}", "hashtag.column_header.tag_mode.any": "ar {additional}", "hashtag.column_header.tag_mode.none": "be {additional}", @@ -385,7 +438,10 @@ "hashtag.counter_by_accounts": "{count, plural, one {{counter} dalyvis} few {{counter} dalyviai} many {{counter} dalyvio} other {{counter} dalyvių}}", "hashtag.counter_by_uses": "{count, plural, one {{counter} įrašas} few {{counter} įrašai} many {{counter} įrašo} other {{counter} įrašų}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} įrašas} few {{counter} įrašai} many {{counter} įrašo} other {{counter} įrašų}} šiandien", + "hashtag.feature": "Rodyti profilyje", "hashtag.follow": "Sekti saitažodį", + "hashtag.mute": "Nutildyti žymą #{hashtag}", + "hashtag.unfeature": "Neberodyti profilyje", "hashtag.unfollow": "Nebesekti saitažodį", "hashtags.and_other": "…ir {count, plural, one {# daugiau} few {# daugiau} many {# daugiau}other {# daugiau}}", "hints.profiles.followers_may_be_missing": "Sekėjai šiai profiliui gali būti nepateikti.", @@ -394,6 +450,7 @@ "hints.profiles.see_more_followers": "Žiūrėti daugiau sekėjų serveryje {domain}", "hints.profiles.see_more_follows": "Žiūrėti daugiau sekimų serveryje {domain}", "hints.profiles.see_more_posts": "Žiūrėti daugiau įrašų serveryje {domain}", + "home.column_settings.show_quotes": "Rodyti paminėjimus", "home.column_settings.show_reblogs": "Rodyti pakėlimus", "home.column_settings.show_replies": "Rodyti atsakymus", "home.hide_announcements": "Slėpti skelbimus", @@ -414,10 +471,12 @@ "ignore_notifications_modal.private_mentions_title": "Ignoruoti pranešimus iš neprašytų privačių paminėjimų?", "info_button.label": "Žinynas", "info_button.what_is_alt_text": "

Kas yra alternatyvusis tekstas?

Alternatyvusis tekstas pateikia vaizdų aprašymus asmenims su regos sutrikimais, turintiems mažo pralaidumo ryšį arba ieškantiems papildomo konteksto.

Galite pagerinti prieinamumą ir suprantamumą visiems, jei parašysite aiškų, glaustą ir objektyvų alternatyvųjį tekstą.

  • Užfiksuokite svarbiausius elementus.
  • Apibendrinkite tekstą vaizduose.
  • Naudokite įprasta sakinio struktūrą.
  • Venkite nereikalingos informacijos.
  • Sutelkite dėmesį į tendencijas ir pagrindines išvadas sudėtinguose vaizdiniuose (tokiuose kaip diagramos ar žemėlapiai).
", + "interaction_modal.action": "Norėdami bendrauti su {name} įrašu, turite prisijungti prie savo paskyros bet kuriame Mastodon serveryje, kurį naudojate.", "interaction_modal.go": "Eiti", "interaction_modal.no_account_yet": "Dar neturite paskyros?", "interaction_modal.on_another_server": "Kitame serveryje", "interaction_modal.on_this_server": "Šiame serveryje", + "interaction_modal.title": "Jei norite tęsti, prisijunkite", "interaction_modal.username_prompt": "Pvz., {example}", "intervals.full.days": "{number, plural, one {# diena} few {# dienos} many {# dienos} other {# dienų}}", "intervals.full.hours": "{number, plural, one {# valanda} few {# valandos} many {# valandos} other {# valandų}}", @@ -438,6 +497,7 @@ "keyboard_shortcuts.home": "Atidaryti pagrindinį laiko skalę", "keyboard_shortcuts.hotkey": "Spartusis klavišas", "keyboard_shortcuts.legend": "Rodyti šią legendą", + "keyboard_shortcuts.load_more": "Fokusuoti „Įkelti daugiau“ mygtuką", "keyboard_shortcuts.local": "Atidaryti vietinę laiko skalę", "keyboard_shortcuts.mention": "Paminėti autorių (-ę)", "keyboard_shortcuts.muted": "Atidaryti nutildytų naudotojų sąrašą", @@ -446,6 +506,7 @@ "keyboard_shortcuts.open_media": "Atidaryti mediją", "keyboard_shortcuts.pinned": "Atverti prisegtų įrašų sąrašą", "keyboard_shortcuts.profile": "Atidaryti autoriaus (-ės) profilį", + "keyboard_shortcuts.quote": "Paminėti įrašą", "keyboard_shortcuts.reply": "Atsakyti į įrašą", "keyboard_shortcuts.requests": "Atidaryti sekimo prašymų sąrašą", "keyboard_shortcuts.search": "Fokusuoti paieškos juostą", @@ -454,9 +515,12 @@ "keyboard_shortcuts.toggle_hidden": "Rodyti / slėpti tekstą po TĮ", "keyboard_shortcuts.toggle_sensitivity": "Rodyti / slėpti mediją", "keyboard_shortcuts.toot": "Pradėti naują įrašą", + "keyboard_shortcuts.top": "Perkelti į sąrašo viršų", "keyboard_shortcuts.translate": "išversti įrašą", "keyboard_shortcuts.unfocus": "Nebefokusuoti rengykles teksto sritį / paiešką", "keyboard_shortcuts.up": "Perkelti į viršų sąraše", + "learn_more_link.got_it": "Supratau", + "learn_more_link.learn_more": "Sužinoti daugiau", "lightbox.close": "Uždaryti", "lightbox.next": "Kitas", "lightbox.previous": "Ankstesnis", @@ -506,8 +570,10 @@ "mute_modal.you_wont_see_mentions": "Nematysi įrašus, kuriuose jie paminimi.", "mute_modal.you_wont_see_posts": "Jie vis tiek gali matyti tavo įrašus, bet tu nematysi jų.", "navigation_bar.about": "Apie", + "navigation_bar.account_settings": "Slaptažodis ir saugumas", "navigation_bar.administration": "Administravimas", "navigation_bar.advanced_interface": "Atidaryti išplėstinę žiniatinklio sąsają", + "navigation_bar.automated_deletion": "Automatinis įrašų ištrynimas", "navigation_bar.blocks": "Užblokuoti naudotojai", "navigation_bar.bookmarks": "Žymės", "navigation_bar.direct": "Privatūs paminėjimai", @@ -517,13 +583,23 @@ "navigation_bar.follow_requests": "Sekimo prašymai", "navigation_bar.followed_tags": "Sekami saitažodžiai", "navigation_bar.follows_and_followers": "Sekimai ir sekėjai", + "navigation_bar.import_export": "Importas ir eksportas", "navigation_bar.lists": "Sąrašai", + "navigation_bar.live_feed_local": "Tiesioginis srautas (vietinis)", + "navigation_bar.live_feed_public": "Tiesioginis srautas (viešas)", "navigation_bar.logout": "Atsijungti", "navigation_bar.moderation": "Prižiūrėjimas", + "navigation_bar.more": "Daugiau", "navigation_bar.mutes": "Nutildyti naudotojai", "navigation_bar.opened_in_classic_interface": "Įrašai, paskyros ir kiti konkretūs puslapiai pagal numatytuosius nustatymus atidaromi klasikinėje žiniatinklio sąsajoje.", "navigation_bar.preferences": "Nuostatos", + "navigation_bar.privacy_and_reach": "Privatumas ir pasiekiamumas", "navigation_bar.search": "Ieškoti", + "navigation_bar.search_trends": "Paieška / Populiaru", + "navigation_panel.collapse_followed_tags": "Sutraukti sekamų žymių meniu", + "navigation_panel.collapse_lists": "Sutraukti sąrašo meniu", + "navigation_panel.expand_followed_tags": "Išskleisti sekamų žymių meniu", + "navigation_panel.expand_lists": "Išskleisti sąrašo meniu", "not_signed_in_indicator.not_signed_in": "Norint pasiekti šį išteklį, reikia prisijungti.", "notification.admin.report": "{name} pranešė {target}", "notification.admin.report_account": "{name} pranešė {count, plural, one {# įrašą} few {# įrašus} many {# įrašo} other {# įrašų}} iš {target} kategorijai {category}", @@ -535,13 +611,17 @@ "notification.annual_report.message": "Jūsų laukia {year} #Wrapstodon! Atskleiskite savo metų svarbiausius įvykius ir įsimintinas akimirkas platformoje „Mastodon“.", "notification.annual_report.view": "Peržiūrėti #Wrapstodon", "notification.favourite": "{name} pamėgo tavo įrašą", + "notification.favourite.name_and_others_with_link": "{name} ir {count, plural,one {dar kažkas} few {# kiti} other {# kitų}} pamėgo tavo įrašą", "notification.favourite_pm": "{name} pamėgo jūsų privatų paminėjimą", + "notification.favourite_pm.name_and_others_with_link": "{name} ir {count, plural,one {dar kažkas} few {# kiti} other {# kitų}} pamėgo tavo privatų paminėjimą", "notification.follow": "{name} seka tave", "notification.follow.name_and_others": "{name} ir {count, plural, one {# kitas} few {# kiti} many {# kito} other {# kitų}} seka tave", "notification.follow_request": "{name} paprašė tave sekti", + "notification.follow_request.name_and_others": "{name} ir {count, plural,one {dar kažkas} few {# kiti} other {# kitų}} pradėjo tave sekti", "notification.label.mention": "Paminėjimas", "notification.label.private_mention": "Privatus paminėjimas", "notification.label.private_reply": "Privatus atsakymas", + "notification.label.quote": "{name} paminėjo jūsų įrašą", "notification.label.reply": "Atsakymas", "notification.mention": "Paminėjimas", "notification.mentioned_you": "{name} paminėjo jus", @@ -556,7 +636,9 @@ "notification.moderation_warning.action_suspend": "Tavo paskyra buvo sustabdyta.", "notification.own_poll": "Tavo apklausa baigėsi", "notification.poll": "Baigėsi apklausa, kurioje balsavai", + "notification.quoted_update": "{name} redagavo jūsų cituotą įrašą", "notification.reblog": "{name} pakėlė tavo įrašą", + "notification.reblog.name_and_others_with_link": "{name} ir {count, plural,one {dar kažkas} few {# kiti} other {# kitų}} paryškino tavo įrašą", "notification.relationships_severance_event": "Prarasti sąryšiai su {name}", "notification.relationships_severance_event.learn_more": "Sužinoti daugiau", "notification.relationships_severance_event.user_domain_block": "Tu užblokavai {target}. Pašalinama {followersCount} savo sekėjų ir {followingCount, plural, one {# paskyrą} few {# paskyrai} many {# paskyros} other {# paskyrų}}, kurios seki.", @@ -596,6 +678,7 @@ "notifications.column_settings.mention": "Paminėjimai:", "notifications.column_settings.poll": "Balsavimo rezultatai:", "notifications.column_settings.push": "Tiesioginiai pranešimai", + "notifications.column_settings.quote": "Paminėjimai:", "notifications.column_settings.reblog": "Pakėlimai:", "notifications.column_settings.show": "Rodyti stulpelyje", "notifications.column_settings.sound": "Paleisti garsą", @@ -623,9 +706,10 @@ "notifications.policy.filter": "Filtruoti", "notifications.policy.filter_hint": "Siųsti į filtruotų pranešimų gautiejus", "notifications.policy.filter_limited_accounts_hint": "Apribota serverio prižiūrėtojų", - "notifications.policy.filter_limited_accounts_title": "Prižiūrėmi paskyrai", + "notifications.policy.filter_limited_accounts_title": "Moderuojamos paskyros", "notifications.policy.filter_new_accounts.hint": "Sukurta per {days, plural, one {vieną dieną} few {# dienas} many {# dienos} other {# dienų}}", "notifications.policy.filter_new_accounts_title": "Naujos paskyros", + "notifications.policy.filter_not_followers_hint": "Įskaitant žmones, kurie seka jus mažiau nei {days, plural, one {vieną dieną} few {# dienas} many {# dienų} other {# dienų}}", "notifications.policy.filter_not_following_hint": "Kol jų nepatvirtinsi rankiniu būdu", "notifications.policy.filter_not_following_title": "Žmonių, kuriuos neseki", "notifications.policy.filter_private_mentions_title": "Nepageidaujami privatūs paminėjimai", @@ -668,10 +752,19 @@ "privacy.private.short": "Sekėjai", "privacy.public.long": "Bet kas iš Mastodon ir ne Mastodon", "privacy.public.short": "Vieša", + "privacy.quote.anyone": "{visibility}, kiekvienas gali cituoti", + "privacy.quote.disabled": "{visibility}, paminėjimai išjungti", + "privacy.quote.limited": "{visibility}, paminėjimai apriboti", "privacy.unlisted.additional": "Tai veikia lygiai taip pat, kaip ir vieša, tik įrašas nebus rodomas tiesioginiuose srautuose, saitažodžiose, naršyme ar Mastodon paieškoje, net jei esi įtraukęs (-usi) visą paskyrą.", "privacy.unlisted.short": "Tyliai vieša", "privacy_policy.last_updated": "Paskutinį kartą atnaujinta {date}", "privacy_policy.title": "Privatumo politika", + "quote_error.edit": "Paminėjimai negali būti pridedami, kai keičiamas įrašas.", + "quote_error.poll": "Cituoti apklausose negalima.", + "quote_error.private_mentions": "Cituoti privačius paminėjus nėra leidžiama.", + "quote_error.quote": "Leidžiama pateikti tik vieną citatą vienu metu.", + "quote_error.unauthorized": "Jums neleidžiama cituoti šio įrašo.", + "quote_error.upload": "Cituoti ir pridėti papildomas bylas negalima.", "recommended": "Rekomenduojama", "refresh": "Atnaujinti", "regeneration_indicator.please_stand_by": "Laukite.", @@ -687,6 +780,9 @@ "relative_time.minutes": "{number} min.", "relative_time.seconds": "{number} sek.", "relative_time.today": "šiandien", + "remove_quote_hint.button_label": "Supratau", + "remove_quote_hint.message": "Tai galite padaryti iš {icon} parinkčių meniu.", + "remove_quote_hint.title": "Norite pašalinti savo citatą?", "reply_indicator.attachments": "{count, plural, one {# priedas} few {# priedai} many {# priedo} other {# priedų}}", "reply_indicator.cancel": "Atšaukti", "reply_indicator.poll": "Apklausa", @@ -748,7 +844,7 @@ "search.quick_action.open_url": "Atidaryti URL adresą Mastodon", "search.quick_action.status_search": "Pranešimai, atitinkantys {x}", "search.search_or_paste": "Ieškoti arba įklijuoti URL", - "search_popout.full_text_search_disabled_message": "Nepasiekima {domain}.", + "search_popout.full_text_search_disabled_message": "Paieška {domain} įrašuose išjungta.", "search_popout.full_text_search_logged_out_message": "Pasiekiama tik prisijungus.", "search_popout.language_code": "ISO kalbos kodas", "search_popout.options": "Paieškos nustatymai", @@ -777,10 +873,13 @@ "status.admin_account": "Atidaryti prižiūrėjimo sąsają @{name}", "status.admin_domain": "Atidaryti prižiūrėjimo sąsają {domain}", "status.admin_status": "Atidaryti šį įrašą prižiūrėjimo sąsajoje", + "status.all_disabled": "Įrašo pakėlimai ir paminėjimai išjungti", "status.block": "Blokuoti @{name}", "status.bookmark": "Pridėti į žymės", "status.cancel_reblog_private": "Nebepasidalinti", + "status.cannot_quote": "Jums neleidžiama paminėti šio įrašo", "status.cannot_reblog": "Šis įrašas negali būti pakeltas.", + "status.contains_quote": "Turi citatą", "status.continued_thread": "Tęsiama gijoje", "status.copy": "Kopijuoti nuorodą į įrašą", "status.delete": "Ištrinti", @@ -790,6 +889,7 @@ "status.edit": "Redaguoti", "status.edited": "Paskutinį kartą redaguota {date}", "status.edited_x_times": "Redaguota {count, plural, one {{count} kartą} few {{count} kartus} many {{count} karto} other {{count} kartų}}", + "status.embed": "Gaukite įterpimo kodą", "status.favourite": "Pamėgti", "status.filter": "Filtruoti šį įrašą", "status.history.created": "{name} sukurta {date}", @@ -804,18 +904,43 @@ "status.mute_conversation": "Nutildyti pokalbį", "status.open": "Išplėsti šį įrašą", "status.pin": "Prisegti prie profilio", + "status.quote": "Paminėjimai", + "status.quote.cancel": "Atšaukti paminėjimą", + "status.quote_error.blocked_account_hint.title": "Šis įrašas yra paslėptas, nes jūs esate užblokavę @{name}.", + "status.quote_error.blocked_domain_hint.title": "Šis įrašas yra paslėptas, nes jūs užblokavote {domain}.", + "status.quote_error.filtered": "Paslėpta dėl vieno iš jūsų filtrų", + "status.quote_error.limited_account_hint.action": "Vis tiek rodyti", + "status.quote_error.limited_account_hint.title": "Šis paskyra buvo paslėpta {domain} moderatorių.", + "status.quote_error.muted_account_hint.title": "Šis įrašas yra paslėptas, nes jūs esate užtildę @{name}.", + "status.quote_error.not_available": "Įrašas nepasiekiamas", + "status.quote_error.pending_approval": "Įrašas peržiūrimas", + "status.quote_error.pending_approval_popout.body": "„Mastodon“ galite kontroliuoti, ar kas nors gali jus cituoti (paminėti). Šis įrašas bus laukimo būsenoje, kol gausite originalaus įrašo autoriaus sutikimą.", + "status.quote_error.revoked": "Autorius pašalino įrašą", + "status.quote_followers_only": "Tik sekėjai gali cituoti šį įrašą", + "status.quote_manual_review": "Autorius atskirai įvertins paskelbimą", + "status.quote_noun": "Paminėjimas", + "status.quote_policy_change": "Keisti, kas gali cituoti", + "status.quote_post_author": "Paminėjo įrašą iš @{name}", + "status.quote_private": "Privačių įrašų negalima cituoti", + "status.quotes.empty": "Šio įrašo dar niekas nepaminėjo. Kai kas nors tai padarys, jie bus rodomi čia.", + "status.quotes.local_other_disclaimer": "Autoriaus atmesti įrašo paminėjimai nebus rodomi.", + "status.quotes.remote_other_disclaimer": "Čia bus rodoma tik paminėjimai iš {domain}. Autoriaus atmesti įrašo paminėjimai nebus rodomi.", "status.read_more": "Skaityti daugiau", "status.reblog": "Pakelti", + "status.reblog_or_quote": "Paryškinti arba cituoti", "status.reblogged_by": "{name} pakėlė", "status.reblogs.empty": "Šio įrašo dar niekas nepakėlė. Kai kas nors tai padarys, jie bus rodomi čia.", "status.redraft": "Ištrinti ir parengti iš naujo", "status.remove_bookmark": "Pašalinti žymę", "status.remove_favourite": "Šalinti iš mėgstamų", + "status.remove_quote": "Pašalinti", "status.replied_in_thread": "Atsakyta gijoje", "status.replied_to": "Atsakyta į {name}", "status.reply": "Atsakyti", "status.replyAll": "Atsakyti į giją", "status.report": "Pranešti apie @{name}", + "status.request_quote": "Citavimo sutikimas", + "status.revoke_quote": "Pašalinti mano įrašo citavimą iš @{name}’s įrašo", "status.sensitive_warning": "Jautrus turinys", "status.share": "Bendrinti", "status.show_less_all": "Rodyti mažiau visiems", @@ -850,6 +975,7 @@ "upload_button.label": "Pridėti vaizdų, vaizdo įrašą arba garso failą", "upload_error.limit": "Viršyta failo įkėlimo riba.", "upload_error.poll": "Failų įkėlimas neleidžiamas su apklausomis.", + "upload_error.quote": "Paminint įrašą bylos įkėlimas negalimas.", "upload_form.drag_and_drop.instructions": "Kad paimtum medijos priedą, paspausk tarpo arba įvedimo klavišą. Tempant naudok rodyklių klavišus, kad perkeltum medijos priedą bet kuria kryptimi. Dar kartą paspausk tarpo arba įvedimo klavišą, kad nuleistum medijos priedą naujoje vietoje, arba paspausk grįžimo klavišą, kad atšauktum.", "upload_form.drag_and_drop.on_drag_cancel": "Nutempimas buvo atšauktas. Medijos priedas {item} buvo nuleistas.", "upload_form.drag_and_drop.on_drag_end": "Medijos priedas {item} buvo nuleistas.", @@ -868,5 +994,15 @@ "video.mute": "Išjungti garsą", "video.pause": "Pristabdyti", "video.play": "Leisti", - "video.skip_backward": "Praleisti atgal" + "video.skip_backward": "Praleisti atgal", + "visibility_modal.direct_quote_warning.text": "Jei išsaugosite dabartinius nustatymus, įterpta citata bus konvertuota į nuorodą.", + "visibility_modal.direct_quote_warning.title": "Cituojami įrašai negali būti įterpiami į privačius paminėjimus", + "visibility_modal.helper.direct_quoting": "Privatūs paminėjimai, parašyti platformoje „Mastodon“, negali būti cituojami kitų.", + "visibility_modal.helper.privacy_private_self_quote": "Privačių įrašų paminėjimai negali būti skelbiami viešai.", + "visibility_modal.helper.private_quoting": "Tik sekėjams skirti įrašai, parašyti platformoje „Mastodon“, negali būti cituojami kitų.", + "visibility_modal.helper.unlisted_quoting": "Kai žmonės jus cituos, jų įrašai taip pat bus paslėpti iš populiariausių naujienų srauto.", + "visibility_modal.quote_followers": "Tik sekėjai", + "visibility_modal.quote_label": "Kas gali cituoti", + "visibility_modal.quote_nobody": "Tik aš", + "visibility_modal.quote_public": "Visi" } diff --git a/config/locales/de.yml b/config/locales/de.yml index ccd44a0c8d9433..2251de86586223 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1889,7 +1889,7 @@ de: profile: Öffentliches Profil relationships: Follower und Folge ich severed_relationships: Getrennte Beziehungen - statuses_cleanup: Automatische Löschung + statuses_cleanup: Automatisiertes Löschen strikes: Maßnahmen two_factor_authentication: Zwei-Faktor-Authentisierung webauthn_authentication: Sicherheitsschlüssel diff --git a/config/locales/lt.yml b/config/locales/lt.yml index d9eaf61e370c9b..7697697d1f8bd1 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -127,7 +127,7 @@ lt: remove_avatar: Pašalinti avatarą remove_header: Pašalinti antraštę removed_avatar_msg: Sėkmingai pašalintas %{username} avataro vaizdas. - removed_header_msg: Sėkmingai pašalintas %{username} antraštės vaizdas. + removed_header_msg: Sėkmingai pašalintas %{username} antraštės paveikslėlis resend_confirmation: already_confirmed: Šis naudotojas jau patvirtintas. send: Iš naujo siųsti patvirtinimo nuorodą @@ -617,11 +617,13 @@ lt: no_status_selected: Jokie įrašai nebuvo pakeisti, nes nė vienas buvo pasirinktas open: Atidaryti įrašą original_status: Originalus įrašas + quotes: Paminėjimai replied_to_html: Atsakyta į %{acct_link} status_title: Paskelbė @%{name} title: Paskyros įrašai – @%{name} trending: Tendencinga view_publicly: Peržiūrėti viešai + view_quoted_post: Peržiūrėti paminėtą įrašą with_media: Su medija system_checks: database_schema_check: @@ -876,6 +878,7 @@ lt: security: Apsauga set_new_password: Nustatyti naują slaptažodį setup: + email_below_hint_html: Patikrinkite savo šlamšto aplanką arba paprašykite naujo el. laiško. Jei el. pašto adresas neteisingas, galite jį pataisyti. email_settings_hint_html: Spustelėkite nuorodą, kurią atsiuntėme adresu %{email}, kad pradėtumėte naudoti „Mastodon“. Lauksime čia. link_not_received: Negavai nuorodos? title: Patikrinti pašto dėžutę @@ -930,7 +933,9 @@ lt: your_appeal_pending: Pateikei apeliaciją your_appeal_rejected: Tavo apeliacija buvo atmesta edit_profile: + basic_information: Pagrindinė informacija hint_html: "Tinkink tai, ką žmonės mato tavo viešame profilyje ir šalia įrašų. Kiti žmonės labiau linkę sekti atgal ir bendrauti su tavimi, jei tavo profilis yra užpildytas ir turi profilio nuotrauką." + other: Kita emoji_styles: auto: Automatinis native: Vietiniai @@ -1075,6 +1080,10 @@ lt: body: 'Tave %{name} paminėjo:' subject: Tave paminėjo %{name} title: Naujas paminėjimas + quote: + body: 'Tavo įrašą paminėjo %{name}:' + subject: "%{name} paminėjo jūsų įrašą" + title: Naujas paminėjimas reblog: body: 'Tavo įrašą pakėlė %{name}:' subject: "%{name} pakėlė tavo įrašą" @@ -1208,11 +1217,22 @@ lt: content_warning: 'Turinio įspėjimas: %{warning}' errors: quoted_status_not_found: Įrašas, kurį bandote cituoti, atrodo, neegzistuoja. + quoted_user_not_mentioned: Privačiame paminėjime negalima cituoti citatoje nepaminėto vartotojo. over_character_limit: pasiektas %{max} simbolių limitas pin_errors: limit: Jūs jau prisegėte maksimalų toot'ų skaičų ownership: Kitų vartotojų toot'ai negali būti prisegti reblog: Pakeltos žinutės negali būti prisegtos + quote_error: + not_available: Įrašas nepasiekiamas + pending_approval: Įrašas peržiūrimas + revoked: Autorius pašalino įrašą + quote_policies: + followers: Tik sekėjai + nobody: Tik aš + public: Visi + quote_post_author: Paminėjo %{acct} įrašą + title: '%{name}: "%{quote}"' visibilities: public: Vieša statuses_cleanup: diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index 7b2963095f2f0e..c6d2098d3900da 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -65,7 +65,7 @@ fi: setting_display_media_hide_all: Piilota mediasisältö aina setting_display_media_show_all: Näytä mediasisältö aina setting_emoji_style: Miten emojit näkyvät. ”Automaattinen” pyrkii käyttämään natiiveja emojeita, mutta Twemoji-emojeita käytetään varavaihtoehtoina vanhoissa selaimissa. - setting_quick_boosting_html: Kun käytössä, %{boost_icon} Tehosta-kuvakkeen napsauttaminen tehostaa välittömästi sen sijaan, että Tehosta/Lainaa-pudotusvalikko avautuisi. Siirtää lainaustoiminnon %{options_icon} (Valinnat) -⁠valikkoon. + setting_quick_boosting_html: Kun käytössä, %{boost_icon} Tehosta-kuvakkeen painaminen tehostaa välittömästi sen sijaan, että Tehosta/Lainaa-pudotusvalikko avautuisi. Siirtää lainaustoiminnon %{options_icon} (Valinnat) -⁠valikkoon. setting_system_scrollbars_ui: Koskee vain Safari- ja Chrome-pohjaisia työpöytäselaimia setting_use_blurhash: Liukuvärit perustuvat piilotettujen kuvien väreihin mutta sumentavat yksityiskohdat setting_use_pending_items: Piilota aikajanan päivitykset napsautuksen taakse syötteen automaattisen vierityksen sijaan diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index 4e88520f8465c4..7010597754ead0 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -41,7 +41,7 @@ lt: defaults: autofollow: Žmonės, kurie užsiregistruos per kvietimą, automatiškai seks tave avatar: WEBP, PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions} tšk. - bot: Signalizuoti kitiems, kad paskyroje daugiausia atliekami automatiniai veiksmai ir kad ji gali būti nestebima. + bot: Pranešti visiems, kad paskyroje daugiausia atliekami automatiniai veiksmai ir kad jos neverta sekti context: Vienas arba keli kontekstai, kuriems turėtų būti taikomas filtras current_password: Saugumo sumetimais įvesk dabartinės paskyros slaptažodį current_username: Kad patvirtintum, įvesk dabartinės paskyros naudotojo vardą @@ -56,11 +56,14 @@ lt: scopes: Prie kurių API programai bus leidžiama pasiekti. Pasirinkus aukščiausio lygio sritį, atskirų sričių pasirinkti nereikia. setting_aggregate_reblogs: Nerodyti naujų pakėlimų įrašams, kurie neseniai buvo pakelti (taikoma tik naujai gautiems pakėlimams). setting_always_send_emails: Paprastai el. laiško pranešimai nebus siunčiami, kai aktyviai naudoji Mastodon. + setting_default_quote_policy_private: Tik sekėjams skirti įrašai, paskelbti platformoje „Mastodon“, negali būti cituojami kitų. + setting_default_quote_policy_unlisted: Kai žmonės jus cituos, jų įrašai taip pat bus paslėpti iš populiariausių naujienų srauto. setting_default_sensitive: Jautrioji medija pagal numatytuosius nustatymus yra paslėpta ir gali būti atskleista spustelėjus. setting_display_media_default: Slėpti mediją, pažymėtą kaip jautrią setting_display_media_hide_all: Visada slėpti mediją setting_display_media_show_all: Visada rodyti mediją setting_emoji_style: Kaip rodyti emodžius. „Auto“ bandys naudoti vietinius jaustukus, bet senesnėse naršyklėse grįš prie Tvejaustukų. + setting_quick_boosting_html: Kai ši funkcija įjungta, paspaudus ant %{boost_icon} Paryškinimo piktogramos, įrašas bus iškart paryškintas, o ne atidarytas išskleidžiamasis meniu „paryškinti/cituoti“. Citavimo veiksmas perkeliamas į %{options_icon} meniu. setting_system_scrollbars_ui: Taikoma tik darbalaukio naršyklėms, karkasiniais „Safari“ ir „Chrome“. setting_use_blurhash: Gradientai pagrįsti paslėptų vizualizacijų spalvomis, bet užgožia bet kokias detales. setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio srauto slinkimo. @@ -79,6 +82,7 @@ lt: activity_api_enabled: Vietinių paskelbtų įrašų, aktyvių naudotojų ir naujų registracijų skaičiai kas savaitę app_icon: WEBP, PNG, GIF arba JPG. Pakeičia numatytąją programos piktogramą mobiliuosiuose įrenginiuose pasirinktine piktograma. backups_retention_period: Naudotojai gali generuoti savo įrašų archyvus, kuriuos vėliau galės atsisiųsti. Nustačius teigiamą reikšmę, šie archyvai po nurodyto dienų skaičiaus bus automatiškai ištrinti iš saugyklos. + bootstrap_timeline_accounts: Šios paskyros bus rekomenduojamos naujiems naudotojams Rekomenduojami sekimai skyrelyje. Pateikite rekomenduojamas paskyras, atskirtas kableliais. content_cache_retention_period: Visi įrašai iš kitų serverių (įskaitant pakėlimus ir atsakymus) bus ištrinti po nurodyto dienų skaičiaus, neatsižvelgiant į bet kokią vietinio naudotojo sąveiką su tais įrašais. Tai taikoma ir tiems įrašams, kuriuos vietinis naudotojas yra pažymėjęs kaip žymes ar mėgstamus. Privačios paminėjimai tarp naudotojų iš skirtingų instancijų taip pat bus prarastos ir jų bus neįmanoma atkurti. Šis nustatymas skirtas naudoti ypatingos paskirties instancijose, o įgyvendinus jį bendram naudojimui, pažeidžiami daugelio naudotojų lūkesčiai. favicon: WEBP, PNG, GIF arba JPG. Pakeičia numatytąją Mastodon svetaines piktogramą pasirinktine piktograma. mascot: Pakeičia išplėstinės žiniatinklio sąsajos iliustraciją. @@ -144,6 +148,7 @@ lt: email: El. pašto adresas expires_in: Nustoja galioti po fields: Papildomi laukai + header: Antraštės paveikslėlis irreversible: Mesti vietoj slėpti locale: Sąsajos kalba max_uses: Maksimalus naudojimo skaičius @@ -156,6 +161,7 @@ lt: setting_always_send_emails: Visada siųsti el. laiško pranešimus setting_auto_play_gif: Automatiškai leisti animuotų GIF setting_default_language: Skelbimo kalba + setting_default_quote_policy: Kas gali cituoti setting_default_sensitive: Visada žymėti mediją kaip jautrią setting_disable_hover_cards: Išjungti profilio peržiūrą užvedus setting_disable_swiping: Išjungti perbraukimo judėjimus @@ -187,7 +193,7 @@ lt: hide: Slėpti visiškai warn: Slėpti su įspėjimu form_admin_settings: - activity_api_enabled: Skelbti suvestinį statistiką apie naudotojų veiklą per API + activity_api_enabled: Skelbti suvestinę statistiką apie naudotojų veiklą per API app_icon: Programėlės piktograma bootstrap_timeline_accounts: Visada rekomenduoti šias paskyras naujiems naudotojams content_cache_retention_period: Nuotolinio turinio saugojimo laikotarpis @@ -216,6 +222,7 @@ lt: follow_request: Kažkas paprašė sekti tave mention: Kažkas paminėjo tave pending_account: Reikia peržiūros naujam paskyrui + quote: Kažkas jus paminėjo reblog: Kažkas pakėlė tavo įrašą software_updates: label: Yra nauja Mastodon versija From c5c8100d02290a366e1d1c8114a5c393ed0e207d Mon Sep 17 00:00:00 2001 From: Echo Date: Tue, 2 Dec 2025 11:30:08 +0100 Subject: [PATCH 072/123] Emoji: Update emoji categories with featured emoji (#37084) --- app/javascript/mastodon/api_types/custom_emoji.ts | 3 ++- app/javascript/mastodon/models/custom_emoji.ts | 1 + app/models/custom_emoji.rb | 4 ++++ app/models/custom_emoji_category.rb | 10 ++++++---- app/serializers/rest/custom_emoji_serializer.rb | 5 +++++ ...10_add_featured_emoji_to_custom_emoji_categories.rb | 8 ++++++++ ...te_add_featured_emoji_to_custom_emoji_categories.rb | 7 +++++++ db/schema.rb | 4 +++- 8 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb create mode 100644 db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb diff --git a/app/javascript/mastodon/api_types/custom_emoji.ts b/app/javascript/mastodon/api_types/custom_emoji.ts index 05144d6f68d0e8..099ef0b88b8f25 100644 --- a/app/javascript/mastodon/api_types/custom_emoji.ts +++ b/app/javascript/mastodon/api_types/custom_emoji.ts @@ -1,8 +1,9 @@ -// See app/serializers/rest/account_serializer.rb +// See app/serializers/rest/custom_emoji_serializer.rb export interface ApiCustomEmojiJSON { shortcode: string; static_url: string; url: string; category?: string; + featured?: boolean; visible_in_picker: boolean; } diff --git a/app/javascript/mastodon/models/custom_emoji.ts b/app/javascript/mastodon/models/custom_emoji.ts index 5297dcd4704286..19ca951a5cab21 100644 --- a/app/javascript/mastodon/models/custom_emoji.ts +++ b/app/javascript/mastodon/models/custom_emoji.ts @@ -11,6 +11,7 @@ export const CustomEmojiFactory = ImmutableRecord({ static_url: '', url: '', category: '', + featured: false, visible_in_picker: false, }); diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 25ba3d921b46e3..65a2faa9fd55a1 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -66,6 +66,10 @@ def object_type :emoji end + def featured? + category&.featured_emoji_id == id + end + def copy! copy = self.class.find_or_initialize_by(domain: nil, shortcode: shortcode) copy.image = image diff --git a/app/models/custom_emoji_category.rb b/app/models/custom_emoji_category.rb index dfcc156080ce4a..cc7db33ece60b9 100644 --- a/app/models/custom_emoji_category.rb +++ b/app/models/custom_emoji_category.rb @@ -4,14 +4,16 @@ # # Table name: custom_emoji_categories # -# id :bigint(8) not null, primary key -# name :string -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# name :string +# created_at :datetime not null +# updated_at :datetime not null +# featured_emoji_id :bigint(8) # class CustomEmojiCategory < ApplicationRecord has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category, dependent: nil + belongs_to :featured_emoji, class_name: 'CustomEmoji', optional: true, inverse_of: :category validates :name, presence: true, uniqueness: true diff --git a/app/serializers/rest/custom_emoji_serializer.rb b/app/serializers/rest/custom_emoji_serializer.rb index 33da69da530bd7..a173aeae19af59 100644 --- a/app/serializers/rest/custom_emoji_serializer.rb +++ b/app/serializers/rest/custom_emoji_serializer.rb @@ -8,6 +8,7 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer attributes :shortcode, :url, :static_url, :visible_in_picker attribute :category, if: :category_loaded? + attribute :featured, if: :category_loaded? def url full_asset_url(object.image.url) @@ -21,6 +22,10 @@ def category object.category.name end + def featured + object.featured? + end + def category_loaded? object.association(:category).loaded? && object.category.present? end diff --git a/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb b/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb new file mode 100644 index 00000000000000..149b0b994ecaf2 --- /dev/null +++ b/db/migrate/20251201154910_add_featured_emoji_to_custom_emoji_categories.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddFeaturedEmojiToCustomEmojiCategories < ActiveRecord::Migration[8.0] + def change + add_column :custom_emoji_categories, :featured_emoji_id, :bigint, null: true + add_foreign_key :custom_emoji_categories, :custom_emojis, column: :featured_emoji_id, on_delete: :nullify, validate: false + end +end diff --git a/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb b/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb new file mode 100644 index 00000000000000..3ff5ce7b5ef25a --- /dev/null +++ b/db/migrate/20251201155054_validate_add_featured_emoji_to_custom_emoji_categories.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ValidateAddFeaturedEmojiToCustomEmojiCategories < ActiveRecord::Migration[8.0] + def change + validate_foreign_key :custom_emoji_categories, :custom_emojis + end +end diff --git a/db/schema.rb b/db/schema.rb index e4e7db3868ca33..669a6dbf2e61e7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_11_19_093332) do +ActiveRecord::Schema[8.0].define(version: 2025_12_01_155054) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -404,6 +404,7 @@ t.string "name" t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false + t.bigint "featured_emoji_id" t.index ["name"], name: "index_custom_emoji_categories_on_name", unique: true end @@ -1426,6 +1427,7 @@ add_foreign_key "collections", "tags" add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade + add_foreign_key "custom_emoji_categories", "custom_emojis", column: "featured_emoji_id", on_delete: :nullify add_foreign_key "custom_filter_keywords", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "statuses", on_delete: :cascade From 100b20f290a17f5a1616b982a903dfda4ae6e9c8 Mon Sep 17 00:00:00 2001 From: Echo Date: Tue, 2 Dec 2025 11:35:29 +0100 Subject: [PATCH 073/123] Add UJS to buttons (#37091) --- app/javascript/mastodon/utils/links.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/utils/links.ts b/app/javascript/mastodon/utils/links.ts index 02b74bde4ddf7f..00821fa8d14de3 100644 --- a/app/javascript/mastodon/utils/links.ts +++ b/app/javascript/mastodon/utils/links.ts @@ -5,20 +5,26 @@ export function setupLinkListeners() { // We don't want to target links with data-confirm here, as those are handled already. on('click', 'a[data-method]:not([data-confirm])', handleMethodLink); + + // We also want to target buttons with data-confirm that are not inside forms. + on('click', ':not(form) button[data-confirm]:not([form])', handleConfirmLink); } function handleConfirmLink(event: MouseEvent) { - const anchor = event.currentTarget; - if (!(anchor instanceof HTMLAnchorElement)) { + const target = event.currentTarget; + if ( + !(target instanceof HTMLAnchorElement) && + !(target instanceof HTMLButtonElement) + ) { return; } - const message = anchor.dataset.confirm; + const message = target.dataset.confirm; if (!message || !window.confirm(message)) { event.preventDefault(); return; } - if (anchor.dataset.method) { + if (target.dataset.method) { handleMethodLink(event); } } From 2b25b65972c9d757a5afb47d3b49ed92e4d9e5ab Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 2 Dec 2025 11:52:08 +0100 Subject: [PATCH 074/123] Add missing translations for web push notifications (#37078) --- config/locales/en.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index ffe6028e7294fd..62b8823d9fb375 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1706,16 +1706,22 @@ en: body: 'You were mentioned by %{name} in:' subject: You were mentioned by %{name} title: New mention + moderation_warning: + subject: You have received a moderation warning poll: subject: A poll by %{name} has ended quote: body: 'Your post was quoted by %{name}:' subject: "%{name} quoted your post" title: New quote + quoted_update: + subject: "%{name} edited a post you have quoted" reblog: body: 'Your post was boosted by %{name}:' subject: "%{name} boosted your post" title: New boost + severed_relationships: + subject: You have lost connections due to a moderation decision status: subject: "%{name} just posted" update: From 9aec6936e5190324fec371452d6ba09f910671a0 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Tue, 2 Dec 2025 12:36:20 +0100 Subject: [PATCH 075/123] Add Wrapstodon timeline announcement component (#37093) --- .../announcement/announcement.stories.tsx | 38 +++++++++++++++ .../annual_report/announcement/index.tsx | 46 +++++++++++++++++++ .../announcement/styles.module.scss | 29 ++++++++++++ app/javascript/mastodon/locales/en.json | 4 ++ 4 files changed, 117 insertions(+) create mode 100644 app/javascript/mastodon/features/annual_report/announcement/announcement.stories.tsx create mode 100644 app/javascript/mastodon/features/annual_report/announcement/index.tsx create mode 100644 app/javascript/mastodon/features/annual_report/announcement/styles.module.scss diff --git a/app/javascript/mastodon/features/annual_report/announcement/announcement.stories.tsx b/app/javascript/mastodon/features/annual_report/announcement/announcement.stories.tsx new file mode 100644 index 00000000000000..926e28bcf2c89d --- /dev/null +++ b/app/javascript/mastodon/features/annual_report/announcement/announcement.stories.tsx @@ -0,0 +1,38 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { fn } from 'storybook/test'; + +import { AnnualReportAnnouncement } from '.'; + +const meta = { + title: 'Components/AnnualReportAnnouncement', + component: AnnualReportAnnouncement, + args: { + hasData: false, + isLoading: false, + year: '2025', + onRequestBuild: fn(), + onOpen: fn(), + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => , +}; + +export const Loading: Story = { + args: { + isLoading: true, + }, + render: Default.render, +}; + +export const WithData: Story = { + args: { + hasData: true, + }, + render: Default.render, +}; diff --git a/app/javascript/mastodon/features/annual_report/announcement/index.tsx b/app/javascript/mastodon/features/annual_report/announcement/index.tsx new file mode 100644 index 00000000000000..7cdb36e35f1f10 --- /dev/null +++ b/app/javascript/mastodon/features/annual_report/announcement/index.tsx @@ -0,0 +1,46 @@ +import { FormattedMessage } from 'react-intl'; + +import { Button } from '@/mastodon/components/button'; + +import styles from './styles.module.scss'; + +export const AnnualReportAnnouncement: React.FC<{ + year: string; + hasData: boolean; + isLoading: boolean; + onRequestBuild: () => void; + onOpen: () => void; +}> = ({ year, hasData, isLoading, onRequestBuild, onOpen }) => { + return ( +
+

+ +

+

+ +

+ {hasData ? ( + + ) : ( + + )} +
+ ); +}; diff --git a/app/javascript/mastodon/features/annual_report/announcement/styles.module.scss b/app/javascript/mastodon/features/annual_report/announcement/styles.module.scss new file mode 100644 index 00000000000000..a09c3425b649a7 --- /dev/null +++ b/app/javascript/mastodon/features/annual_report/announcement/styles.module.scss @@ -0,0 +1,29 @@ +.wrapper { + display: flex; + flex-direction: column; + align-items: center; + padding: 40px 60px; + text-align: center; + font-size: 15px; + line-height: 1.5; + color: var(--color-text-on-media); + background: var(--color-bg-media-base); + background: + radial-gradient(at 40% 87%, #240c9a99 0, transparent 50%), + radial-gradient(at 19% 10%, #6b0c9a99 0, transparent 50%), + radial-gradient(at 90% 27%, #9a0c8299 0, transparent 50%), + radial-gradient(at 16% 95%, #1e948299 0, transparent 50%) + var(--color-bg-media-base); + border-bottom: 1px solid var(--color-border-primary); + + h2 { + font-size: 20px; + font-weight: 500; + line-height: 1.2; + margin-bottom: 8px; + } + + p { + margin-bottom: 20px; + } +} diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index bc8dd40d9303d8..3a7add31c8326a 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Describe this for people with visual impairments…", "alt_text_modal.done": "Done", "announcement.announcement": "Announcement", + "annual_report.announcement.action_build": "Build my Wrapstodon", + "annual_report.announcement.action_view": "View my Wrapstodon", + "annual_report.announcement.description": "Discover more about your engagement on Mastodon over the past year.", + "annual_report.announcement.title": "Wrapstodon {year} has arrived", "annual_report.summary.archetype.booster": "The cool-hunter", "annual_report.summary.archetype.lurker": "The lurker", "annual_report.summary.archetype.oracle": "The oracle", From f8422e1fa4c602dc62f010b52aeed5897230b5e4 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 2 Dec 2025 14:37:05 +0100 Subject: [PATCH 076/123] Add API for on-demand generation of annual reports (#37055) --- .../api/v1/annual_reports_controller.rb | 55 ++++++- app/lib/annual_report.rb | 7 + app/serializers/rest/instance_serializer.rb | 6 +- app/workers/generate_annual_report_worker.rb | 4 + config/routes/api.rb | 2 + spec/requests/api/v1/annual_reports_spec.rb | 147 ++++++++++++++++++ spec/requests/api/v2/instance_spec.rb | 20 +++ 7 files changed, 236 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/v1/annual_reports_controller.rb b/app/controllers/api/v1/annual_reports_controller.rb index b1aee288dd8595..724c7658d7247b 100644 --- a/app/controllers/api/v1/annual_reports_controller.rb +++ b/app/controllers/api/v1/annual_reports_controller.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true class Api::V1::AnnualReportsController < Api::BaseController - before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index - before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index + include AsyncRefreshesConcern + + before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:read, :generate] + before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:read, :generate] before_action :require_user! - before_action :set_annual_report, except: :index + before_action :set_annual_report, only: [:show, :read] def index with_read_replica do @@ -28,14 +30,59 @@ def show relationships: @relationships end + def state + render json: { state: report_state } + end + + def generate + return render_empty unless year == AnnualReport.current_campaign + return render_empty if GeneratedAnnualReport.exists?(account_id: current_account.id, year: year) + + async_refresh = AsyncRefresh.new(refresh_key) + + if async_refresh.running? + add_async_refresh_header(async_refresh, retry_seconds: 2) + return head 202 + end + + add_async_refresh_header(AsyncRefresh.create(refresh_key), retry_seconds: 2) + + GenerateAnnualReportWorker.perform_async(current_account.id, year) + + head 202 + end + def read @annual_report.view! render_empty end + def refresh_key + "wrapstodon:#{current_account.id}:#{year}" + end + private + def report_state + return 'available' if GeneratedAnnualReport.exists?(account_id: current_account.id, year: year) + + async_refresh = AsyncRefresh.new(refresh_key) + + if async_refresh.running? + add_async_refresh_header(async_refresh, retry_seconds: 2) + 'generating' + elsif AnnualReport.current_campaign == year && AnnualReport.new(current_account, year).eligible? + 'eligible' + else + 'ineligible' + end + end + + def year + params[:id]&.to_i + end + def set_annual_report - @annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: params[:id]) + @annual_report = GeneratedAnnualReport.find_by!(account_id: current_account.id, year: year) end end diff --git a/app/lib/annual_report.rb b/app/lib/annual_report.rb index 035dc4cde75c31..6da9ecae1424d1 100644 --- a/app/lib/annual_report.rb +++ b/app/lib/annual_report.rb @@ -18,6 +18,13 @@ def self.table_name_prefix 'annual_report_' end + def self.current_campaign + return unless Mastodon::Feature.wrapstodon_enabled? + + datetime = Time.now.utc + datetime.year if datetime.month == 12 && (10..31).cover?(datetime.day) + end + def initialize(account, year) @account = account @year = year diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 36ddfae328634b..75d3acfea5895c 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -12,7 +12,7 @@ class ContactSerializer < ActiveModel::Serializer attributes :domain, :title, :version, :source_url, :description, :usage, :thumbnail, :icon, :languages, :configuration, - :registrations, :api_versions + :registrations, :api_versions, :wrapstodon has_one :contact, serializer: ContactSerializer has_many :rules, serializer: REST::RuleSerializer @@ -134,6 +134,10 @@ def api_versions Mastodon::Version.api_versions end + def wrapstodon + AnnualReport.current_campaign + end + private def registrations_enabled? diff --git a/app/workers/generate_annual_report_worker.rb b/app/workers/generate_annual_report_worker.rb index 7094c1ab9c7697..07e7298e6fff48 100644 --- a/app/workers/generate_annual_report_worker.rb +++ b/app/workers/generate_annual_report_worker.rb @@ -4,7 +4,11 @@ class GenerateAnnualReportWorker include Sidekiq::Worker def perform(account_id, year) + async_refresh = AsyncRefresh.new("wrapstodon:#{account_id}:#{year}}") + AnnualReport.new(Account.find(account_id), year).generate + + async_refresh&.finish! rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordNotUnique true end diff --git a/config/routes/api.rb b/config/routes/api.rb index 32685d791ffc25..48e960af44c21f 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -72,6 +72,8 @@ resources :annual_reports, only: [:index, :show] do member do post :read + post :generate + get :state end end diff --git a/spec/requests/api/v1/annual_reports_spec.rb b/spec/requests/api/v1/annual_reports_spec.rb index 88a7dbdd8251bd..482e91736c91a4 100644 --- a/spec/requests/api/v1/annual_reports_spec.rb +++ b/spec/requests/api/v1/annual_reports_spec.rb @@ -42,6 +42,153 @@ end end + describe 'GET /api/v1/annual_reports/:year/state' do + context 'when not authorized' do + it 'returns http unauthorized' do + get '/api/v1/annual_reports/2025/state' + + expect(response) + .to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') + end + end + + context 'with wrong scope' do + before do + get '/api/v1/annual_reports/2025/state', headers: headers + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts' + end + + context 'with correct scope' do + let(:scopes) { 'read:accounts' } + + context 'when a report is already generated' do + before do + Fabricate(:generated_annual_report, account: user.account, year: 2025) + end + + it 'returns http success and available status' do + get '/api/v1/annual_reports/2025/state', headers: headers + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(state: 'available') + end + end + + context 'when the feature is not enabled' do + it 'returns http success and ineligible status' do + get '/api/v1/annual_reports/2025/state', headers: headers + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(state: 'ineligible') + end + end + + context 'when the feature is enabled and time is within window', feature: :wrapstodon do + before do + travel_to Time.utc(2025, 12, 20) + + status = Fabricate(:status, visibility: :public, account: user.account) + status.tags << Fabricate(:tag) + end + + it 'returns http success and eligible status' do + get '/api/v1/annual_reports/2025/state', headers: headers + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(state: 'eligible') + end + end + + context 'when the feature is enabled but we are out of the time window', feature: :wrapstodon do + before do + travel_to Time.utc(2025, 6, 20) + + status = Fabricate(:status, visibility: :public, account: user.account) + status.tags << Fabricate(:tag) + end + + it 'returns http success and ineligible status' do + get '/api/v1/annual_reports/2025/state', headers: headers + + expect(response) + .to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(state: 'ineligible') + end + end + end + end + + describe 'POST /api/v1/annual_reports/:id/generate' do + context 'when not authorized' do + it 'returns http unauthorized' do + post '/api/v1/annual_reports/2025/generate' + + expect(response) + .to have_http_status(401) + expect(response.content_type) + .to start_with('application/json') + end + end + + context 'with wrong scope' do + before do + post '/api/v1/annual_reports/2025/generate', headers: headers + end + + it_behaves_like 'forbidden for wrong scope', 'read read:accounts' + end + + context 'with correct scope' do + let(:scopes) { 'write:accounts' } + + context 'when the feature is enabled and time is within window', feature: :wrapstodon do + before do + travel_to Time.utc(2025, 12, 20) + + status = Fabricate(:status, visibility: :public, account: user.account) + status.tags << Fabricate(:tag) + end + + it 'returns http accepted, create an async job and schedules a job' do + expect { post '/api/v1/annual_reports/2025/generate', headers: headers } + .to enqueue_sidekiq_job(GenerateAnnualReportWorker).with(user.account_id, 2025) + + expect(response) + .to have_http_status(202) + + expect(response.headers['Mastodon-Async-Refresh']).to be_present + end + end + end + end + describe 'POST /api/v1/annual_reports/:id/read' do context 'with correct scope' do let(:scopes) { 'write:accounts' } diff --git a/spec/requests/api/v2/instance_spec.rb b/spec/requests/api/v2/instance_spec.rb index 92a9744e416724..50798612e83b22 100644 --- a/spec/requests/api/v2/instance_spec.rb +++ b/spec/requests/api/v2/instance_spec.rb @@ -42,6 +42,26 @@ end end + context 'when wrapstodon is enabled', feature: :wrapstodon do + before do + travel_to Time.utc(2025, 12, 20) + end + + it 'returns http success and the wrapstodon year' do + get api_v2_instance_path + + expect(response) + .to have_http_status(200) + + expect(response.content_type) + .to start_with('application/json') + + expect(response.parsed_body) + .to be_present + .and include(wrapstodon: 2025) + end + end + def include_configuration_limits include( configuration: include( From e45ecc7d139cac40ef81a2013f3f8bfc8894c8b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:43:46 +0100 Subject: [PATCH 077/123] Update dependency express to v5.2.0 [SECURITY] (#37089) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 84 ++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7b91fd6ff1a792..909f834ac9daa5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5554,20 +5554,20 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:^2.2.0": - version: 2.2.0 - resolution: "body-parser@npm:2.2.0" +"body-parser@npm:^2.2.1": + version: 2.2.1 + resolution: "body-parser@npm:2.2.1" dependencies: bytes: "npm:^3.1.2" content-type: "npm:^1.0.5" - debug: "npm:^4.4.0" + debug: "npm:^4.4.3" http-errors: "npm:^2.0.0" - iconv-lite: "npm:^0.6.3" + iconv-lite: "npm:^0.7.0" on-finished: "npm:^2.4.1" qs: "npm:^6.14.0" - raw-body: "npm:^3.0.0" - type-is: "npm:^2.0.0" - checksum: 10c0/a9ded39e71ac9668e2211afa72e82ff86cc5ef94de1250b7d1ba9cc299e4150408aaa5f1e8b03dd4578472a3ce6d1caa2a23b27a6c18e526e48b4595174c116c + raw-body: "npm:^3.0.1" + type-is: "npm:^2.0.1" + checksum: 10c0/ce9608cff4114a908c09e8f57c7b358cd6fef66100320d01244d4c141448d7a6710c4051cc9d6191f8c6b3c7fa0f5b040c7aa1b6bbeb5462e27e668e64cb15bd languageName: node linkType: hard @@ -5644,7 +5644,7 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.2, bytes@npm:^3.1.2": +"bytes@npm:^3.1.2, bytes@npm:~3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e @@ -6385,7 +6385,7 @@ __metadata: languageName: node linkType: hard -"depd@npm:2.0.0, depd@npm:^2.0.0": +"depd@npm:^2.0.0, depd@npm:~2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c @@ -7346,16 +7346,17 @@ __metadata: linkType: hard "express@npm:^5.1.0": - version: 5.1.0 - resolution: "express@npm:5.1.0" + version: 5.2.1 + resolution: "express@npm:5.2.1" dependencies: accepts: "npm:^2.0.0" - body-parser: "npm:^2.2.0" + body-parser: "npm:^2.2.1" content-disposition: "npm:^1.0.0" content-type: "npm:^1.0.5" cookie: "npm:^0.7.1" cookie-signature: "npm:^1.2.1" debug: "npm:^4.4.0" + depd: "npm:^2.0.0" encodeurl: "npm:^2.0.0" escape-html: "npm:^1.0.3" etag: "npm:^1.8.1" @@ -7376,7 +7377,7 @@ __metadata: statuses: "npm:^2.0.1" type-is: "npm:^2.0.1" vary: "npm:^1.1.2" - checksum: 10c0/80ce7c53c5f56887d759b94c3f2283e2e51066c98d4b72a4cc1338e832b77f1e54f30d0239cc10815a0f849bdb753e6a284d2fa48d4ab56faf9c501f55d751d6 + checksum: 10c0/45e8c841ad188a41402ddcd1294901e861ee0819f632fb494f2ed344ef9c43315d294d443fb48d594e6586a3b779785120f43321417adaef8567316a55072949 languageName: node linkType: hard @@ -8123,16 +8124,16 @@ __metadata: languageName: node linkType: hard -"http-errors@npm:2.0.0, http-errors@npm:^2.0.0": - version: 2.0.0 - resolution: "http-errors@npm:2.0.0" +"http-errors@npm:^2.0.0, http-errors@npm:~2.0.1": + version: 2.0.1 + resolution: "http-errors@npm:2.0.1" dependencies: - depd: "npm:2.0.0" - inherits: "npm:2.0.4" - setprototypeof: "npm:1.2.0" - statuses: "npm:2.0.1" - toidentifier: "npm:1.0.1" - checksum: 10c0/fc6f2715fe188d091274b5ffc8b3657bd85c63e969daa68ccb77afb05b071a4b62841acb7a21e417b5539014dff2ebf9550f0b14a9ff126f2734a7c1387f8e19 + depd: "npm:~2.0.0" + inherits: "npm:~2.0.4" + setprototypeof: "npm:~1.2.0" + statuses: "npm:~2.0.2" + toidentifier: "npm:~1.0.1" + checksum: 10c0/fb38906cef4f5c83952d97661fe14dc156cb59fe54812a42cd448fa57b5c5dfcb38a40a916957737bd6b87aab257c0648d63eb5b6a9ca9f548e105b6072712d4 languageName: node linkType: hard @@ -8181,7 +8182,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.7.0": +"iconv-lite@npm:^0.7.0, iconv-lite@npm:~0.7.0": version: 0.7.0 resolution: "iconv-lite@npm:0.7.0" dependencies: @@ -8305,7 +8306,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:2.0.4": +"inherits@npm:2, inherits@npm:~2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 @@ -11177,15 +11178,15 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:^3.0.0": - version: 3.0.1 - resolution: "raw-body@npm:3.0.1" +"raw-body@npm:^3.0.1": + version: 3.0.2 + resolution: "raw-body@npm:3.0.2" dependencies: - bytes: "npm:3.1.2" - http-errors: "npm:2.0.0" - iconv-lite: "npm:0.7.0" - unpipe: "npm:1.0.0" - checksum: 10c0/892f4fbd21ecab7e2fed0f045f7af9e16df7e8050879639d4e482784a2f4640aaaa33d916a0e98013f23acb82e09c2e3c57f84ab97104449f728d22f65a7d79a + bytes: "npm:~3.1.2" + http-errors: "npm:~2.0.1" + iconv-lite: "npm:~0.7.0" + unpipe: "npm:~1.0.0" + checksum: 10c0/d266678d08e1e7abea62c0ce5864344e980fa81c64f6b481e9842c5beaed2cdcf975f658a3ccd67ad35fc919c1f6664ccc106067801850286a6cbe101de89f29 languageName: node linkType: hard @@ -12272,7 +12273,7 @@ __metadata: languageName: node linkType: hard -"setprototypeof@npm:1.2.0": +"setprototypeof@npm:~1.2.0": version: 1.2.0 resolution: "setprototypeof@npm:1.2.0" checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc @@ -12616,14 +12617,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:2.0.1": - version: 2.0.1 - resolution: "statuses@npm:2.0.1" - checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0 - languageName: node - linkType: hard - -"statuses@npm:^2.0.1, statuses@npm:^2.0.2": +"statuses@npm:^2.0.1, statuses@npm:^2.0.2, statuses@npm:~2.0.2": version: 2.0.2 resolution: "statuses@npm:2.0.2" checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f @@ -13313,7 +13307,7 @@ __metadata: languageName: node linkType: hard -"toidentifier@npm:1.0.1": +"toidentifier@npm:~1.0.1": version: 1.0.1 resolution: "toidentifier@npm:1.0.1" checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1 @@ -13477,7 +13471,7 @@ __metadata: languageName: node linkType: hard -"type-is@npm:^2.0.0, type-is@npm:^2.0.1": +"type-is@npm:^2.0.1": version: 2.0.1 resolution: "type-is@npm:2.0.1" dependencies: @@ -13712,7 +13706,7 @@ __metadata: languageName: node linkType: hard -"unpipe@npm:1.0.0": +"unpipe@npm:~1.0.0": version: 1.0.0 resolution: "unpipe@npm:1.0.0" checksum: 10c0/193400255bd48968e5c5383730344fbb4fa114cdedfab26e329e50dd2d81b134244bb8a72c6ac1b10ab0281a58b363d06405632c9d49ca9dfd5e90cbd7d0f32c From 9c3b41f0a40c5c2b7f8441deb6630bf4d1dc9b05 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 13:45:27 +0000 Subject: [PATCH 078/123] Update dependency vite-plugin-pwa to v1.2.0 (#37040) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 300 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 180 insertions(+), 120 deletions(-) diff --git a/yarn.lock b/yarn.lock index 909f834ac9daa5..3c2cc62f49538c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2581,6 +2581,22 @@ __metadata: languageName: node linkType: hard +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.0": + version: 5.0.0 + resolution: "@isaacs/brace-expansion@npm:5.0.0" + dependencies: + "@isaacs/balanced-match": "npm:^4.0.1" + checksum: 10c0/b4d4812f4be53afc2c5b6c545001ff7a4659af68d4484804e9d514e183d20269bb81def8682c01a22b17c4d6aed14292c8494f7d2ac664e547101c1a905aa977 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -6118,7 +6134,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.6": +"cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -7592,13 +7608,13 @@ __metadata: languageName: node linkType: hard -"foreground-child@npm:^3.1.0": - version: 3.1.1 - resolution: "foreground-child@npm:3.1.1" +"foreground-child@npm:^3.1.0, foreground-child@npm:^3.3.1": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" dependencies: - cross-spawn: "npm:^7.0.0" + cross-spawn: "npm:^7.0.6" signal-exit: "npm:^4.0.1" - checksum: 10c0/9700a0285628abaeb37007c9a4d92bd49f67210f09067638774338e146c8e9c825c5c877f072b2f75f41dc6a2d0be8664f79ffc03f6576649f54a84fb9b47de0 + checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 languageName: node linkType: hard @@ -7847,6 +7863,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^11.0.1": + version: 11.1.0 + resolution: "glob@npm:11.1.0" + dependencies: + foreground-child: "npm:^3.3.1" + jackspeak: "npm:^4.1.1" + minimatch: "npm:^10.1.1" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^2.0.0" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/1ceae07f23e316a6fa74581d9a74be6e8c2e590d2f7205034dd5c0435c53f5f7b712c2be00c3b65bf0a49294a1c6f4b98cd84c7637e29453b5aa13b79f1763a2 + languageName: node + linkType: hard + "glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -8829,6 +8861,15 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^4.1.1": + version: 4.1.1 + resolution: "jackspeak@npm:4.1.1" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + checksum: 10c0/84ec4f8e21d6514db24737d9caf65361511f75e5e424980eebca4199f400874f45e562ac20fa8aeb1dd20ca2f3f81f0788b6e9c3e64d216a5794fd6f30e0e042 + languageName: node + linkType: hard + "jake@npm:^10.8.5": version: 10.8.7 resolution: "jake@npm:10.8.7" @@ -9318,7 +9359,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.2": +"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.2": version: 11.2.2 resolution: "lru-cache@npm:11.2.2" checksum: 10c0/72d7831bbebc85e2bdefe01047ee5584db69d641c48d7a509e86f66f6ee111b30af7ec3bd68a967d47b69a4b1fa8bbf3872630bd06a63b6735e6f0a5f1c8e83d @@ -9545,6 +9586,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.1.1": + version: 10.1.1 + resolution: "minimatch@npm:10.1.1" + dependencies: + "@isaacs/brace-expansion": "npm:^5.0.0" + checksum: 10c0/c85d44821c71973d636091fddbfbffe62370f5ee3caf0241c5b60c18cd289e916200acb2361b7e987558cd06896d153e25d505db9fc1e43e6b4b6752e2702902 + languageName: node + linkType: hard + "minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -10195,6 +10245,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^2.0.0": + version: 2.0.1 + resolution: "path-scurry@npm:2.0.1" + dependencies: + lru-cache: "npm:^11.0.0" + minipass: "npm:^7.1.2" + checksum: 10c0/2a16ed0e81fbc43513e245aa5763354e25e787dab0d539581a6c3f0f967461a159ed6236b2559de23aa5b88e7dc32b469b6c47568833dd142a4b24b4f5cd2620 + languageName: node + linkType: hard + "path-to-regexp@npm:^1.7.0": version: 1.9.0 resolution: "path-to-regexp@npm:1.9.0" @@ -11922,7 +11982,7 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^2.43.1": +"rollup@npm:^2.79.2": version: 2.79.2 resolution: "rollup@npm:2.79.2" dependencies: @@ -13930,23 +13990,23 @@ __metadata: linkType: hard "vite-plugin-pwa@npm:^1.0.2": - version: 1.1.0 - resolution: "vite-plugin-pwa@npm:1.1.0" + version: 1.2.0 + resolution: "vite-plugin-pwa@npm:1.2.0" dependencies: debug: "npm:^4.3.6" pretty-bytes: "npm:^6.1.1" tinyglobby: "npm:^0.2.10" - workbox-build: "npm:^7.3.0" - workbox-window: "npm:^7.3.0" + workbox-build: "npm:^7.4.0" + workbox-window: "npm:^7.4.0" peerDependencies: "@vite-pwa/assets-generator": ^1.0.0 vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - workbox-build: ^7.3.0 - workbox-window: ^7.3.0 + workbox-build: ^7.4.0 + workbox-window: ^7.4.0 peerDependenciesMeta: "@vite-pwa/assets-generator": optional: true - checksum: 10c0/dc199ccbb3cd0a9f740edcbbc8efa7820f67481ae80a15340ca769c21d4f7452d9a9c1d184eac4f6e3fd1ecd9f7fdfd31cc8f9520e43e6795860fe187c77103a + checksum: 10c0/d037591fc6a44b9a97f45b2452f691fce34c1c8ece4721d4f1a068f6dd7c30991b63c090a2af87d5b78d351bf5f5eee9d9dbbfc4bb39a77d3bc2ab7d2ca5df6b languageName: node linkType: hard @@ -14309,28 +14369,28 @@ __metadata: languageName: node linkType: hard -"workbox-background-sync@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-background-sync@npm:7.3.0" +"workbox-background-sync@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-background-sync@npm:7.4.0" dependencies: idb: "npm:^7.0.1" - workbox-core: "npm:7.3.0" - checksum: 10c0/cc982d62702847fb16c4ef372a8bd243348a80c2d5da1649a860b0187b45060a799a65582c2d36f1a32e31d5d68dedcb037698c41d3b2f171ea5d54d73453cf1 + workbox-core: "npm:7.4.0" + checksum: 10c0/024dfad37c9ca28480857aaf7c0dd2e2f2b2ea416e100b51260eaf28f14ca5a558d0e12b1e95f862fec04df54432c090fa29582ab88b43d8778a4c821f21d13f languageName: node linkType: hard -"workbox-broadcast-update@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-broadcast-update@npm:7.3.0" +"workbox-broadcast-update@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-broadcast-update@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/25007acd3e845b5ca1f4c9ac9888ce661431723f7419cfa56b3029b6c56cbeca24902dae015c42a2d6f554f956274743e331d03ceeb4b0e3879cb7b908d0e82f + workbox-core: "npm:7.4.0" + checksum: 10c0/9e96d38cb1cfaccf72a37beeed7d36a66db8bacaa09e584bfa15f6b129aee5026c521320b4b68fa762f4f6ba0229f11c6ec6f7bc039419c8d1f1a9ed9f2a37b5 languageName: node linkType: hard -"workbox-build@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-build@npm:7.3.0" +"workbox-build@npm:^7.4.0": + version: 7.4.0 + resolution: "workbox-build@npm:7.4.0" dependencies: "@apideck/better-ajv-errors": "npm:^0.3.1" "@babel/core": "npm:^7.24.4" @@ -14345,157 +14405,157 @@ __metadata: common-tags: "npm:^1.8.0" fast-json-stable-stringify: "npm:^2.1.0" fs-extra: "npm:^9.0.1" - glob: "npm:^7.1.6" + glob: "npm:^11.0.1" lodash: "npm:^4.17.20" pretty-bytes: "npm:^5.3.0" - rollup: "npm:^2.43.1" + rollup: "npm:^2.79.2" source-map: "npm:^0.8.0-beta.0" stringify-object: "npm:^3.3.0" strip-comments: "npm:^2.0.1" tempy: "npm:^0.6.0" upath: "npm:^1.2.0" - workbox-background-sync: "npm:7.3.0" - workbox-broadcast-update: "npm:7.3.0" - workbox-cacheable-response: "npm:7.3.0" - workbox-core: "npm:7.3.0" - workbox-expiration: "npm:7.3.0" - workbox-google-analytics: "npm:7.3.0" - workbox-navigation-preload: "npm:7.3.0" - workbox-precaching: "npm:7.3.0" - workbox-range-requests: "npm:7.3.0" - workbox-recipes: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - workbox-strategies: "npm:7.3.0" - workbox-streams: "npm:7.3.0" - workbox-sw: "npm:7.3.0" - workbox-window: "npm:7.3.0" - checksum: 10c0/cb396f9c2a53429d1e11b4c1da2e21c9e1c98473ce15f20ae53277e47bd7ccbcb3f1f843694e588bb70b12d9332faafd098ca05b93abb0293d373f38a8de3ca8 + workbox-background-sync: "npm:7.4.0" + workbox-broadcast-update: "npm:7.4.0" + workbox-cacheable-response: "npm:7.4.0" + workbox-core: "npm:7.4.0" + workbox-expiration: "npm:7.4.0" + workbox-google-analytics: "npm:7.4.0" + workbox-navigation-preload: "npm:7.4.0" + workbox-precaching: "npm:7.4.0" + workbox-range-requests: "npm:7.4.0" + workbox-recipes: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + workbox-strategies: "npm:7.4.0" + workbox-streams: "npm:7.4.0" + workbox-sw: "npm:7.4.0" + workbox-window: "npm:7.4.0" + checksum: 10c0/673fb05a0b24bb5534667a8d7c42304e3a1394071d73c9ccbfec881e36a66c98ed59eba92e21669a2758289c79e76a72930077a611f20ba803cdc0d0a24da6b2 languageName: node linkType: hard -"workbox-cacheable-response@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-cacheable-response@npm:7.3.0" +"workbox-cacheable-response@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-cacheable-response@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/192c8a8878c53a205c55398bac78f2c32c0f36e55c95cab282d8a716ddf2fa72563afaed690d34d3438cc8df5fb0df4d98dcb2d93cc6d67c69a9ae592f7bf246 + workbox-core: "npm:7.4.0" + checksum: 10c0/4d4fabf3cbd7b2b0505e62ec653f933de40d4fa7600e49ac15cdf223e86b9e70580927f53c6ef27ddd027b655829918b90a77a3675593017c001811a122c6591 languageName: node linkType: hard -"workbox-core@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-core@npm:7.3.0" - checksum: 10c0/b7dce640cd9665ed207f65f5b08a50e2e24e5599790c6ea4fec987539b9d2ef81765d8c5f94acfee3a8a45d5ade8e1a4ebd0b8847a1471302ef75a5b93c7bd04 +"workbox-core@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-core@npm:7.4.0" + checksum: 10c0/ae7c762df084b57d3d10f42b83a508a5c0bec5663de4c9cefc2b5c2893922a816f451cf8ec45ee76d204a3a6e90ee52b6c550a6cba9109d9bb644da9e98fb9e8 languageName: node linkType: hard -"workbox-expiration@npm:7.3.0, workbox-expiration@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-expiration@npm:7.3.0" +"workbox-expiration@npm:7.4.0, workbox-expiration@npm:^7.3.0": + version: 7.4.0 + resolution: "workbox-expiration@npm:7.4.0" dependencies: idb: "npm:^7.0.1" - workbox-core: "npm:7.3.0" - checksum: 10c0/6040d72122ece901becfcc59974586e9cc9b6309840b83b652c9f9aafe32ff89783404a431cadf6f888f80e5371252820e425ced499742964d6d68687f6fad1a + workbox-core: "npm:7.4.0" + checksum: 10c0/d4f5e96e6d58d74a1be7d8c842e68e9fc2d4a22664f629ec71570039a1876e033ca1e54d68eccded83d901999e48fb388c1f807b14f706b9b83c7b3ab5815cf8 languageName: node linkType: hard -"workbox-google-analytics@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-google-analytics@npm:7.3.0" +"workbox-google-analytics@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-google-analytics@npm:7.4.0" dependencies: - workbox-background-sync: "npm:7.3.0" - workbox-core: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - workbox-strategies: "npm:7.3.0" - checksum: 10c0/5317a4bcc01f1aa87480f9708d7d382c15fb37d6119e71e0a2909dfd683f6060b5cc4f7b016a81fc67098f51a5d0cfd1cda20e228f2f3778ee3caf649b59996b + workbox-background-sync: "npm:7.4.0" + workbox-core: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + workbox-strategies: "npm:7.4.0" + checksum: 10c0/34a5ad1ea6fd6c65d5eb78b8c4bcf30b2624099b1c7c2e8f6a2af7135dcfcff00531d042f99b45960dcc42185b0f9c4b62c02a38325429fa15f060f30df61e51 languageName: node linkType: hard -"workbox-navigation-preload@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-navigation-preload@npm:7.3.0" +"workbox-navigation-preload@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-navigation-preload@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/69e4d43c68c06889987e9fa437995378b0632c83bad8c7044b4ed812b05b94b3a4aa8700ea4c26b2ecf68ee6858e94ff41dfa3279815c1bc385ac19c0edfb200 + workbox-core: "npm:7.4.0" + checksum: 10c0/9999d78683247dcbf1ca1e18664b3ecb19e8330c0ff9328403a513051d65eb3fc6578504dcc324fe603c7fb6d3d1054de1f55b4d2b9765236298cfebebefabe9 languageName: node linkType: hard -"workbox-precaching@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-precaching@npm:7.3.0" +"workbox-precaching@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-precaching@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - workbox-strategies: "npm:7.3.0" - checksum: 10c0/15c4c5cf5dfec684711ce3536bbfa6873f7af16b712d02ded81d3ff490ea4097e46602705548f5872c49f06e3516fd69f17e72a7fc60631ff6d68460e48f7648 + workbox-core: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + workbox-strategies: "npm:7.4.0" + checksum: 10c0/df1d3ac590f56418ae80595c6c6a718f15ce3d6b9622ef43058d9d56f7374d95202ef2932e5ef5410d949f4f4366f5eccaf7956c32f579846fcc4bd83578c246 languageName: node linkType: hard -"workbox-range-requests@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-range-requests@npm:7.3.0" +"workbox-range-requests@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-range-requests@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/d48e1484866442864d66b1891c4965b71e997a83a7634f11452ec1a73a30a5e642e6a95d5cff45578bef4dec7a5f57bc598aeedb6189d17ca210e2c5f2898244 + workbox-core: "npm:7.4.0" + checksum: 10c0/7b945fab877bfc226fa8b7aa8e87b1d2179565314eff424672bfa70c43eec5dce99fe79109f548b6c6b054629f4464fb73085c3357f3c52cea6befe8d36f4fb4 languageName: node linkType: hard -"workbox-recipes@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-recipes@npm:7.3.0" +"workbox-recipes@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-recipes@npm:7.4.0" dependencies: - workbox-cacheable-response: "npm:7.3.0" - workbox-core: "npm:7.3.0" - workbox-expiration: "npm:7.3.0" - workbox-precaching: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - workbox-strategies: "npm:7.3.0" - checksum: 10c0/c8146ece4247cbcbefba36a14f2cb65b5f74b2412f64cfc7955ff75ff653857161a1f1d94c987fbae4812f5b770eedcf99af965e512cc375fbc7fb5421bdc99c + workbox-cacheable-response: "npm:7.4.0" + workbox-core: "npm:7.4.0" + workbox-expiration: "npm:7.4.0" + workbox-precaching: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + workbox-strategies: "npm:7.4.0" + checksum: 10c0/2de53c679af2879921861d817b2f9b6a682aff0d871d38542519c16902ccfdfe7d5bf456f1e3a1f2fdb86824795f1794fec39a5b342a0d9866fccfd0e02a93a7 languageName: node linkType: hard -"workbox-routing@npm:7.3.0, workbox-routing@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-routing@npm:7.3.0" +"workbox-routing@npm:7.4.0, workbox-routing@npm:^7.3.0": + version: 7.4.0 + resolution: "workbox-routing@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/8ac1824211d0fbe0e916ecb2c2427bcb0ef8783f9225d8114fe22e6c326f2d8a040a089bead58064e8b096ec95abe070c04cd7353dd8830dba3ab8d608a053aa + workbox-core: "npm:7.4.0" + checksum: 10c0/62a2a3f24a5c54fc716e9275ccd032c5e35cf645d3bcf670c91531a2a8308025767a4ff799d36483eab50c007647748ab309ea1af76847ebcd78203ef4069ae6 languageName: node linkType: hard -"workbox-strategies@npm:7.3.0, workbox-strategies@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-strategies@npm:7.3.0" +"workbox-strategies@npm:7.4.0, workbox-strategies@npm:^7.3.0": + version: 7.4.0 + resolution: "workbox-strategies@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - checksum: 10c0/50f3c28b46b54885a9461ad6559010d9abb2a7e35e0128d05c268f3ea0a96b1a747934758121d0e821f7af63946d9db8f4d2d7e0146f12555fb05c768e6b82bb + workbox-core: "npm:7.4.0" + checksum: 10c0/c25771af342ce10aee006d749e7adba4e77e7379b39cd96dbd05557f0f4968ff1ac88001022fb588ce2c105e5dfded321ce9f14344291c19ab753db37e6611f8 languageName: node linkType: hard -"workbox-streams@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-streams@npm:7.3.0" +"workbox-streams@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-streams@npm:7.4.0" dependencies: - workbox-core: "npm:7.3.0" - workbox-routing: "npm:7.3.0" - checksum: 10c0/2ae541343d187eb7a50da2cfd74051f15771d1ddd1cad6856ffd530f7cccdb8eed9a8af94ff7540b710fef73eeec37d652123ae42b0206fbbd0679dc25e66ff4 + workbox-core: "npm:7.4.0" + workbox-routing: "npm:7.4.0" + checksum: 10c0/213ac3cebb6f499fd2bedf28ae75523c8193ca6c7ace797227347775811dba80551dd645fc0bf04fb1b36eea2cf030e4ce3648d886cc0e8017363bd3806c84f3 languageName: node linkType: hard -"workbox-sw@npm:7.3.0": - version: 7.3.0 - resolution: "workbox-sw@npm:7.3.0" - checksum: 10c0/9ae275e31dd5ec51245773b6d90fda16d0b7f70d59f3a71aec732814b5aedf08aedc7fcce57739e7e89d9e1479ef97e3a202a542a511d732cf5e8b5d1c293870 +"workbox-sw@npm:7.4.0": + version: 7.4.0 + resolution: "workbox-sw@npm:7.4.0" + checksum: 10c0/f87198a9f40da34e13f896ea308c5feedf9caf3ba10b2d40c301850396edf17cdba060775b224fc07697356858816af9912997b751041a715b6934ab14b35300 languageName: node linkType: hard -"workbox-window@npm:7.3.0, workbox-window@npm:^7.3.0": - version: 7.3.0 - resolution: "workbox-window@npm:7.3.0" +"workbox-window@npm:7.4.0, workbox-window@npm:^7.3.0, workbox-window@npm:^7.4.0": + version: 7.4.0 + resolution: "workbox-window@npm:7.4.0" dependencies: "@types/trusted-types": "npm:^2.0.2" - workbox-core: "npm:7.3.0" - checksum: 10c0/dbda33c4761ec40051cfe6e3f1701b2381b4f3b191f7a249c32f683503ea35cf8b42d1f99df5ba3b693fac78705d8ed0c191488bdd178c525d1291d0161ec8ff + workbox-core: "npm:7.4.0" + checksum: 10c0/6013d1019fe3bbb06d8e572b6a4ccfec6fc0879448337997c193fde08738c2fa420a4dca385b00220ef1feac3539e82b01896c85f77cd8c45cff379e7a6cb1d5 languageName: node linkType: hard From f393ff93cb655308d3400943be21b963fd8e9a6b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 13:45:33 +0000 Subject: [PATCH 079/123] Update dependency @optimize-lodash/rollup-plugin to v5.1.0 (#37039) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3c2cc62f49538c..43cc7f5a2eafdc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3026,14 +3026,14 @@ __metadata: linkType: hard "@optimize-lodash/rollup-plugin@npm:^5.0.2": - version: 5.0.2 - resolution: "@optimize-lodash/rollup-plugin@npm:5.0.2" + version: 5.1.0 + resolution: "@optimize-lodash/rollup-plugin@npm:5.1.0" dependencies: "@optimize-lodash/transform": "npm:3.0.6" "@rollup/pluginutils": "npm:^5.1.0" peerDependencies: rollup: ">= 4.x" - checksum: 10c0/ad3a6baafe1422a2bc0c2c2d1e32961214431ff714f4fff793b65174291b931a55aa2797507115e3e0cb8473c311611c817c7d70d2b591473c5930500bc9edd7 + checksum: 10c0/cb15712b82164fb63d569caebc278fea4972577009040370b43534edd75069f57974c29e884b3dc59896eda7ed4f28dfcb422c0481d8ea0fe5dfc4546af70774 languageName: node linkType: hard From 73294e2561732a80671da899f54f87068e92155b Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 3 Dec 2025 09:31:14 +0100 Subject: [PATCH 080/123] Fix typo in AsyncRefresh ID for GenerateAnnualReportWorker (#37096) --- app/workers/generate_annual_report_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/generate_annual_report_worker.rb b/app/workers/generate_annual_report_worker.rb index 07e7298e6fff48..7a02cb7fbf9be4 100644 --- a/app/workers/generate_annual_report_worker.rb +++ b/app/workers/generate_annual_report_worker.rb @@ -4,7 +4,7 @@ class GenerateAnnualReportWorker include Sidekiq::Worker def perform(account_id, year) - async_refresh = AsyncRefresh.new("wrapstodon:#{account_id}:#{year}}") + async_refresh = AsyncRefresh.new("wrapstodon:#{account_id}:#{year}") AnnualReport.new(Account.find(account_id), year).generate From 954f39774320e70527139049ec1582b28922aeb2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:11:20 +0100 Subject: [PATCH 081/123] New Crowdin Translations (automated) (#37098) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/da.json | 4 + app/javascript/mastodon/locales/de.json | 78 ++++++++------- app/javascript/mastodon/locales/el.json | 4 + app/javascript/mastodon/locales/en-GB.json | 5 + app/javascript/mastodon/locales/es-AR.json | 4 + app/javascript/mastodon/locales/es-MX.json | 3 + app/javascript/mastodon/locales/es.json | 3 + app/javascript/mastodon/locales/fa.json | 13 ++- app/javascript/mastodon/locales/fi.json | 108 +++++++++++---------- app/javascript/mastodon/locales/he.json | 4 + app/javascript/mastodon/locales/hu.json | 2 + app/javascript/mastodon/locales/is.json | 4 + app/javascript/mastodon/locales/lt.json | 2 +- app/javascript/mastodon/locales/nl.json | 6 ++ app/javascript/mastodon/locales/nn.json | 18 ++-- app/javascript/mastodon/locales/pt-BR.json | 48 ++++----- app/javascript/mastodon/locales/pt-PT.json | 4 + app/javascript/mastodon/locales/tok.json | 26 ++--- app/javascript/mastodon/locales/tr.json | 4 + app/javascript/mastodon/locales/vi.json | 4 + app/javascript/mastodon/locales/zh-CN.json | 4 + app/javascript/mastodon/locales/zh-TW.json | 4 + config/locales/activerecord.fa.yml | 6 ++ config/locales/activerecord.lt.yml | 8 +- config/locales/activerecord.nl.yml | 6 ++ config/locales/activerecord.nn.yml | 6 ++ config/locales/da.yml | 6 ++ config/locales/de.yml | 38 ++++---- config/locales/devise.fi.yml | 2 +- config/locales/el.yml | 6 ++ config/locales/en-GB.yml | 6 ++ config/locales/es-AR.yml | 6 ++ config/locales/es-MX.yml | 4 + config/locales/es.yml | 4 + config/locales/fa.yml | 6 ++ config/locales/fi.yml | 68 +++++++------ config/locales/he.yml | 6 ++ config/locales/hu.yml | 2 + config/locales/is.yml | 6 ++ config/locales/lt.yml | 22 +++-- config/locales/nl.yml | 6 ++ config/locales/nn.yml | 8 +- config/locales/pt-BR.yml | 7 ++ config/locales/pt-PT.yml | 6 ++ config/locales/simple_form.de.yml | 16 +-- config/locales/simple_form.fa.yml | 2 + config/locales/simple_form.fi.yml | 6 +- config/locales/simple_form.ja.yml | 1 + config/locales/simple_form.nl.yml | 2 + config/locales/simple_form.nn.yml | 6 +- config/locales/sq.yml | 6 ++ config/locales/tr.yml | 6 ++ config/locales/vi.yml | 6 ++ config/locales/zh-CN.yml | 6 ++ config/locales/zh-TW.yml | 6 ++ 55 files changed, 443 insertions(+), 207 deletions(-) diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json index 42b7817dcde2c6..3d3676395802de 100644 --- a/app/javascript/mastodon/locales/da.json +++ b/app/javascript/mastodon/locales/da.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Beskriv dette for personer med nedsat syn…", "alt_text_modal.done": "Færdig", "announcement.announcement": "Bekendtgørelse", + "annual_report.announcement.action_build": "Byg min Wrapstodon", + "annual_report.announcement.action_view": "Vis min Wrapstodon", + "annual_report.announcement.description": "Få mere at vide om dit engagement på Mastodon i det forgangne år.", + "annual_report.announcement.title": "Wrapstodon {year} er her", "annual_report.summary.archetype.booster": "Fremhæveren", "annual_report.summary.archetype.lurker": "Lureren", "annual_report.summary.archetype.oracle": "Oraklet", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index f959e7186da510..d8491118e721dc 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -1,13 +1,13 @@ { - "about.blocks": "Moderierte Server", + "about.blocks": "Eingeschränkte Server", "about.contact": "Kontakt:", "about.default_locale": "Standard", "about.disclaimer": "Mastodon ist eine freie, quelloffene Software und eine Marke der Mastodon gGmbH.", - "about.domain_blocks.no_reason_available": "Grund unbekannt", + "about.domain_blocks.no_reason_available": "Keinen Grund angegeben", "about.domain_blocks.preamble": "Mastodon erlaubt es dir grundsätzlich, alle Inhalte von allen Nutzer*innen auf allen Servern im Fediverse zu sehen und mit ihnen zu interagieren. Für diesen Server gibt es aber ein paar Ausnahmen.", "about.domain_blocks.silenced.explanation": "Standardmäßig werden von diesem Server keine Inhalte oder Profile angezeigt. Du kannst die Profile und Inhalte aber dennoch sehen, wenn du explizit nach diesen suchst oder diesen folgst.", - "about.domain_blocks.silenced.title": "Ausgeblendet", - "about.domain_blocks.suspended.explanation": "Es werden keine Daten von diesem Server verarbeitet, gespeichert oder ausgetauscht, sodass eine Interaktion oder Kommunikation mit Nutzer*innen dieses Servers nicht möglich ist.", + "about.domain_blocks.silenced.title": "Stummgeschaltet", + "about.domain_blocks.suspended.explanation": "Es werden keine Daten von diesem Server verarbeitet, gespeichert oder ausgetauscht, sodass eine Interaktion oder Kommunikation mit Profilen dieses Servers nicht möglich ist.", "about.domain_blocks.suspended.title": "Gesperrt", "about.language_label": "Sprache", "about.not_available": "Diese Informationen sind auf diesem Server nicht verfügbar.", @@ -15,21 +15,21 @@ "about.rules": "Serverregeln", "account.account_note_header": "Persönliche Notiz", "account.add_or_remove_from_list": "Hinzufügen oder Entfernen von Listen", - "account.badges.bot": "Automatisiert", + "account.badges.bot": "Bot", "account.badges.group": "Gruppe", "account.block": "@{name} blockieren", - "account.block_domain": "{domain} sperren", + "account.block_domain": "{domain} blockieren", "account.block_short": "Blockieren", "account.blocked": "Blockiert", "account.blocking": "Blockiert", - "account.cancel_follow_request": "Follower-Anfrage zurückziehen", + "account.cancel_follow_request": "Anfrage zurückziehen", "account.copy": "Link zum Profil kopieren", "account.direct": "@{name} privat erwähnen", - "account.disable_notifications": "Höre auf mich zu benachrichtigen wenn @{name} etwas postet", + "account.disable_notifications": "Benachrichtige mich nicht mehr, wenn @{name} etwas veröffentlicht", "account.domain_blocking": "Domain blockiert", "account.edit_profile": "Profil bearbeiten", "account.edit_profile_short": "Bearbeiten", - "account.enable_notifications": "Benachrichtige mich wenn @{name} etwas postet", + "account.enable_notifications": "Benachrichtige mich, wenn @{name} etwas veröffentlicht", "account.endorse": "Im Profil vorstellen", "account.familiar_followers_many": "Gefolgt von {name1}, {name2} und {othersCount, plural, one {einem weiteren Profil, das dir bekannt ist} other {# weiteren Profilen, die dir bekannt sind}}", "account.familiar_followers_one": "Gefolgt von {name1}", @@ -41,9 +41,9 @@ "account.featured_tags.last_status_never": "Keine Beiträge", "account.follow": "Folgen", "account.follow_back": "Ebenfalls folgen", - "account.follow_back_short": "Ebenfalls folgen", + "account.follow_back_short": "Zurückfolgen", "account.follow_request": "Anfrage zum Folgen", - "account.follow_request_cancel": "Anfrage abbrechen", + "account.follow_request_cancel": "Anfrage zurückziehen", "account.follow_request_cancel_short": "Abbrechen", "account.follow_request_short": "Anfragen", "account.followers": "Follower", @@ -58,8 +58,8 @@ "account.hide_reblogs": "Geteilte Beiträge von @{name} ausblenden", "account.in_memoriam": "Zum Andenken.", "account.joined_short": "Mitglied seit", - "account.languages": "Sprache ändern.", - "account.link_verified_on": "Das Profil mit dieser E-Mail-Adresse wurde bereits am {date} verifiziert", + "account.languages": "Ausgewählte Sprachen ändern", + "account.link_verified_on": "Das Profil mit dieser E-Mail-Adresse wurde bereits am {date} bestätigt", "account.locked_info": "Die Privatsphäre dieses Kontos wurde auf „geschützt“ gesetzt. Die Person bestimmt manuell, wer ihrem Profil folgen darf.", "account.media": "Medien", "account.mention": "@{name} erwähnen", @@ -73,7 +73,7 @@ "account.no_bio": "Keine Beschreibung verfügbar.", "account.open_original_page": "Ursprüngliche Seite öffnen", "account.posts": "Beiträge", - "account.posts_with_replies": "Beiträge und Antworten", + "account.posts_with_replies": "Beiträge & Antworten", "account.remove_from_followers": "{name} als Follower entfernen", "account.report": "@{name} melden", "account.requested_follow": "{name} möchte dir folgen", @@ -81,9 +81,9 @@ "account.share": "Profil von @{name} teilen", "account.show_reblogs": "Geteilte Beiträge von @{name} anzeigen", "account.statuses_counter": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}}", - "account.unblock": "{name} nicht mehr blockieren", + "account.unblock": "Blockierung von {name} aufheben", "account.unblock_domain": "Blockierung von {domain} aufheben", - "account.unblock_domain_short": "Entsperren", + "account.unblock_domain_short": "Blockierung aufheben", "account.unblock_short": "Blockierung aufheben", "account.unendorse": "Im Profil nicht mehr vorstellen", "account.unfollow": "Entfolgen", @@ -103,21 +103,25 @@ "alert.rate_limited.message": "Bitte versuche es nach {retry_time, time, medium} erneut.", "alert.rate_limited.title": "Anfragelimit überschritten", "alert.unexpected.message": "Ein unerwarteter Fehler ist aufgetreten.", - "alert.unexpected.title": "Oha!", + "alert.unexpected.title": "Ups!", "alt_text_badge.title": "Bildbeschreibung", "alt_text_modal.add_alt_text": "Bildbeschreibung hinzufügen", "alt_text_modal.add_text_from_image": "Text aus Bild hinzufügen", "alt_text_modal.cancel": "Abbrechen", "alt_text_modal.change_thumbnail": "Vorschaubild ändern", - "alt_text_modal.describe_for_people_with_hearing_impairments": "Beschreibe den Inhalt für Menschen mit Schwerhörigkeit …", + "alt_text_modal.describe_for_people_with_hearing_impairments": "Beschreibe den Inhalt für Menschen, die taub oder hörbehindert sind …", "alt_text_modal.describe_for_people_with_visual_impairments": "Beschreibe den Inhalt für Menschen, die blind oder sehbehindert sind …", "alt_text_modal.done": "Fertig", "announcement.announcement": "Ankündigung", + "annual_report.announcement.action_build": "Erstelle mein Wrapstodon", + "annual_report.announcement.action_view": "Mein Wrapstodon anschauen", + "annual_report.announcement.description": "Erfahre mehr über deine Aktivitäten auf Mastodon im vergangenen Jahr.", + "annual_report.announcement.title": "Wrapstodon {year} ist fertiggestellt", "annual_report.summary.archetype.booster": "Trendjäger*in", "annual_report.summary.archetype.lurker": "Beobachter*in", "annual_report.summary.archetype.oracle": "Universaltalent", "annual_report.summary.archetype.pollster": "Meinungsforscher*in", - "annual_report.summary.archetype.replier": "Sozialer Schmetterling", + "annual_report.summary.archetype.replier": "Gesellige*r", "annual_report.summary.followers.followers": "Follower", "annual_report.summary.followers.total": "{count} insgesamt", "annual_report.summary.here_it_is": "Dein Jahresrückblick für {year}:", @@ -148,7 +152,7 @@ "bundle_column_error.copy_stacktrace": "Fehlerbericht kopieren", "bundle_column_error.error.body": "Die angeforderte Seite konnte nicht dargestellt werden. Dies könnte auf einen Fehler in unserem Code oder auf ein Browser-Kompatibilitätsproblem zurückzuführen sein.", "bundle_column_error.error.title": "Oh nein!", - "bundle_column_error.network.body": "Beim Versuch, diese Seite zu laden, ist ein Fehler aufgetreten. Dies könnte auf ein vorübergehendes Problem mit Ihrer Internetverbindung oder diesem Server zurückzuführen sein.", + "bundle_column_error.network.body": "Beim Versuch, diese Seite zu laden, ist ein Fehler aufgetreten. Dies könnte auf ein vorübergehendes Problem mit deiner Internetverbindung oder diesem Server zurückzuführen sein.", "bundle_column_error.network.title": "Netzwerkfehler", "bundle_column_error.retry": "Erneut versuchen", "bundle_column_error.return": "Zurück zur Startseite", @@ -160,9 +164,9 @@ "carousel.current": "Seite {current, number}/{max, number}", "carousel.slide": "Seite {current, number} von {max, number}", "closed_registrations.other_server_instructions": "Da Mastodon dezentralisiert ist, kannst du ein Konto auf einem anderen Server erstellen und trotzdem mit diesem Server interagieren.", - "closed_registrations_modal.description": "Das Anlegen eines Kontos auf {domain} ist derzeit nicht möglich, aber bedenke, dass du kein extra Konto auf {domain} benötigst, um Mastodon nutzen zu können.", + "closed_registrations_modal.description": "Das Anlegen eines Kontos auf {domain} ist derzeit nicht möglich, aber bedenke, dass du nicht zwingend auf {domain} ein Konto benötigst, um Mastodon nutzen zu können.", "closed_registrations_modal.find_another_server": "Einen anderen Server auswählen", - "closed_registrations_modal.preamble": "Mastodon ist dezentralisiert, das heißt, unabhängig davon, wo du dein Konto erstellt hast, kannst du jedem Profil auf diesem Server folgen und mit ihm interagieren. Du kannst sogar deinen eigenen Server hosten!", + "closed_registrations_modal.preamble": "Mastodon ist dezentralisiert, das heißt, unabhängig davon, wo du dein Konto erstellst, kannst du jedem Profil auf diesem Server folgen und mit ihm interagieren. Du kannst sogar deinen eigenen Mastodon-Server hosten!", "closed_registrations_modal.title": "Bei Mastodon registrieren", "column.about": "Über", "column.blocks": "Blockierte Profile", @@ -175,7 +179,7 @@ "column.edit_list": "Liste bearbeiten", "column.favourites": "Favoriten", "column.firehose": "Live-Feeds", - "column.firehose_local": "Live-Feed für diesen Server", + "column.firehose_local": "Live-Feed dieses Servers", "column.firehose_singular": "Live-Feed", "column.follow_requests": "Follower-Anfragen", "column.home": "Startseite", @@ -203,7 +207,7 @@ "compose.published.open": "Öffnen", "compose.saved.body": "Beitrag gespeichert.", "compose_form.direct_message_warning_learn_more": "Mehr erfahren", - "compose_form.encryption_warning": "Beiträge auf Mastodon sind nicht Ende-zu-Ende-verschlüsselt. Teile keine sensiblen Informationen über Mastodon.", + "compose_form.encryption_warning": "Beiträge auf Mastodon sind nicht Ende-zu-Ende-verschlüsselt. Teile keine sensiblen Informationen über Mastodon, auch nicht als „private Erwähnung“.", "compose_form.hashtag_warning": "Dieser Beitrag wird unter keinem Hashtag sichtbar sein, weil er nicht öffentlich ist. Nur öffentliche Beiträge können nach Hashtags durchsucht werden.", "compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Andere können dir folgen und deine Beiträge sehen, die nur für Follower bestimmt sind.", "compose_form.lock_disclaimer.lock": "geschützt", @@ -267,8 +271,8 @@ "confirmations.revoke_quote.confirm": "Zitat entfernen", "confirmations.revoke_quote.message": "Diese Aktion kann nicht rückgängig gemacht werden.", "confirmations.revoke_quote.title": "Zitieren meines Beitrags entfernen?", - "confirmations.unblock.confirm": "Nicht mehr blockieren", - "confirmations.unblock.title": "{name} nicht mehr blockieren?", + "confirmations.unblock.confirm": "Blockierung aufheben", + "confirmations.unblock.title": "Blockierung von {name} aufheben?", "confirmations.unfollow.confirm": "Entfolgen", "confirmations.unfollow.title": "{name} entfolgen?", "confirmations.withdraw_request.confirm": "Anfrage zurückziehen", @@ -336,7 +340,7 @@ "empty_column.account_featured.other": "{acct} hat bisher noch nichts vorgestellt. Wusstest du, dass du deine häufig verwendeten Hashtags und sogar Profile von Freund*innen vorstellen kannst?", "empty_column.account_featured_other.unknown": "Dieses Profil hat bisher noch nichts vorgestellt.", "empty_column.account_hides_collections": "Das Konto hat sich dazu entschieden, diese Information nicht zu veröffentlichen", - "empty_column.account_suspended": "Konto gesperrt", + "empty_column.account_suspended": "Konto dauerhaft gesperrt", "empty_column.account_timeline": "Keine Beiträge vorhanden!", "empty_column.account_unavailable": "Profil nicht verfügbar", "empty_column.blocks": "Du hast bisher keine Profile blockiert.", @@ -349,14 +353,14 @@ "empty_column.favourited_statuses": "Du hast noch keine Beiträge favorisiert. Sobald du einen favorisierst, wird er hier erscheinen.", "empty_column.favourites": "Diesen Beitrag hat bisher noch niemand favorisiert. Sobald es jemand tut, wird das Profil hier erscheinen.", "empty_column.follow_requests": "Es liegen derzeit keine Follower-Anfragen vor. Sobald du eine erhältst, wird sie hier erscheinen.", - "empty_column.followed_tags": "Du folgst noch keinen Hashtags. Wenn du dies tust, werden sie hier erscheinen.", + "empty_column.followed_tags": "Du folgst noch keinen Hashtags. Sobald du Hashtags abonniert hast, werden sie hier angezeigt.", "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.", "empty_column.home": "Die Timeline deiner Startseite ist leer! Folge mehr Leuten, um sie zu füllen.", "empty_column.list": "Diese Liste ist derzeit leer. Wenn Konten auf dieser Liste neue Beiträge veröffentlichen, werden sie hier erscheinen.", "empty_column.mutes": "Du hast keine Profile stummgeschaltet.", "empty_column.notification_requests": "Alles klar! Hier gibt es nichts. Wenn Sie neue Mitteilungen erhalten, werden diese entsprechend Ihren Einstellungen hier angezeigt.", "empty_column.notifications": "Du hast noch keine Benachrichtigungen. Sobald andere Personen mit dir interagieren, wirst du hier darüber informiert.", - "empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Profilen von anderen Servern, um die Timeline aufzufüllen", + "empty_column.public": "Hier ist nichts zu sehen! Schreibe einen öffentlichen Beitrag oder folge Profilen von anderen Servern im Fediverse, um die Timeline zu füllen", "error.no_hashtag_feed_access": "Registriere dich oder melde dich an, um den Hashtag anzusehen und ihm zu folgen.", "error.unexpected_crash.explanation": "Wegen eines Fehlers in unserem Code oder aufgrund einer Browser-Inkompatibilität kann diese Seite nicht korrekt angezeigt werden.", "error.unexpected_crash.explanation_addons": "Diese Seite konnte nicht korrekt angezeigt werden. Dieser Fehler wird wahrscheinlich durch ein Browser-Add-on oder automatische Übersetzungswerkzeuge verursacht.", @@ -412,7 +416,7 @@ "follow_suggestions.similar_to_recently_followed_longer": "Ähnlich zu Profilen, denen du seit kurzem folgst", "follow_suggestions.view_all": "Alle anzeigen", "follow_suggestions.who_to_follow": "Empfohlene Profile", - "followed_tags": "Gefolgte Hashtags", + "followed_tags": "Abonnierte Hashtags", "footer.about": "Über", "footer.about_this_server": "Über", "footer.directory": "Profilverzeichnis", @@ -440,10 +444,10 @@ "hashtag.counter_by_uses": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}}", "hashtag.counter_by_uses_today": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}} heute", "hashtag.feature": "Im Profil vorstellen", - "hashtag.follow": "Hashtag folgen", + "hashtag.follow": "Abonnieren", "hashtag.mute": "#{hashtag} stummschalten", "hashtag.unfeature": "Im Profil nicht mehr vorstellen", - "hashtag.unfollow": "Hashtag entfolgen", + "hashtag.unfollow": "Abbestellen", "hashtags.and_other": "… und {count, plural, one{# weiterer} other {# weitere}}", "hints.profiles.followers_may_be_missing": "Möglicherweise werden für dieses Profil nicht alle Follower angezeigt.", "hints.profiles.follows_may_be_missing": "Möglicherweise werden für dieses Profil nicht alle gefolgten Profile angezeigt.", @@ -582,7 +586,7 @@ "navigation_bar.favourites": "Favoriten", "navigation_bar.filters": "Stummgeschaltete Wörter", "navigation_bar.follow_requests": "Follower-Anfragen", - "navigation_bar.followed_tags": "Gefolgte Hashtags", + "navigation_bar.followed_tags": "Abonnierte Hashtags", "navigation_bar.follows_and_followers": "Follower und Folge ich", "navigation_bar.import_export": "Importieren und exportieren", "navigation_bar.lists": "Listen", @@ -597,9 +601,9 @@ "navigation_bar.privacy_and_reach": "Datenschutz und Reichweite", "navigation_bar.search": "Suche", "navigation_bar.search_trends": "Suche / Angesagt", - "navigation_panel.collapse_followed_tags": "Menü für gefolgte Hashtags schließen", + "navigation_panel.collapse_followed_tags": "Menü für abonnierte Hashtags schließen", "navigation_panel.collapse_lists": "Listen-Menü schließen", - "navigation_panel.expand_followed_tags": "Menü für gefolgte Hashtags öffnen", + "navigation_panel.expand_followed_tags": "Menü für abonnierte Hashtags öffnen", "navigation_panel.expand_lists": "Listen-Menü öffnen", "not_signed_in_indicator.not_signed_in": "Du musst dich anmelden, um auf diesen Inhalt zugreifen zu können.", "notification.admin.report": "{name} meldete {target}", @@ -850,7 +854,7 @@ "search.quick_action.go_to_hashtag": "Hashtag {x} aufrufen", "search.quick_action.open_url": "URL in Mastodon öffnen", "search.quick_action.status_search": "Beiträge passend zu {x}", - "search.search_or_paste": "Suchen oder URL einfügen", + "search.search_or_paste": "Suche eingeben oder URL einfügen", "search_popout.full_text_search_disabled_message": "Auf {domain} nicht verfügbar.", "search_popout.full_text_search_logged_out_message": "Nur verfügbar, wenn angemeldet.", "search_popout.language_code": "ISO-Sprachcode", @@ -1004,7 +1008,7 @@ "upload_form.drag_and_drop.on_drag_start": "Der Medienanhang {item} wurde aufgenommen.", "upload_form.edit": "Bearbeiten", "upload_progress.label": "Wird hochgeladen …", - "upload_progress.processing": "Wird verarbeitet…", + "upload_progress.processing": "Wird verarbeitet …", "username.taken": "Dieser Profilname ist vergeben. Versuche einen anderen", "video.close": "Video schließen", "video.download": "Datei herunterladen", diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 5ffeffdf8b64bf..186bf33264e4b2 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Περιέγραψε αυτό για άτομα με προβλήματα όρασης…", "alt_text_modal.done": "Ολοκληρώθηκε", "announcement.announcement": "Ανακοίνωση", + "annual_report.announcement.action_build": "Φτιάξε το Wrapstodon μου", + "annual_report.announcement.action_view": "Προβολή του Wrapstodon μου", + "annual_report.announcement.description": "Ανακαλύψτε περισσότερα για την αλληλεπίδραση σας στο Mastodon κατά τη διάρκεια του περασμένου έτους.", + "annual_report.announcement.title": "Το Wrapstodon του {year} έφτασε", "annual_report.summary.archetype.booster": "Ο κυνηγός των φοβερών", "annual_report.summary.archetype.lurker": "Ο διακριτικός", "annual_report.summary.archetype.oracle": "Η Πυθία", diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 34c03215322bba..1be1ed615b81ab 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Describe this for people with visual impairments…", "alt_text_modal.done": "Done", "announcement.announcement": "Announcement", + "annual_report.announcement.action_build": "Build my Wrapstodon", + "annual_report.announcement.action_view": "View my Wrapstodon", + "annual_report.announcement.description": "Discover more about your engagement on Mastodon over the past year.", + "annual_report.announcement.title": "Wrapstodon {year} has arrived", "annual_report.summary.archetype.booster": "The cool-hunter", "annual_report.summary.archetype.lurker": "The lurker", "annual_report.summary.archetype.oracle": "The oracle", @@ -516,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toggle_sensitivity": "Show/hide media", "keyboard_shortcuts.toot": "to start a brand new post", + "keyboard_shortcuts.top": "Move to top of list", "keyboard_shortcuts.translate": "to translate a post", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "Move up in the list", diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json index e1f9d04c889def..e9fc2f6dd7ccaa 100644 --- a/app/javascript/mastodon/locales/es-AR.json +++ b/app/javascript/mastodon/locales/es-AR.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Describí esto para personas con dificultades visuales…", "alt_text_modal.done": "Listo", "announcement.announcement": "Anuncio", + "annual_report.announcement.action_build": "Construir mi MastodonAnual", + "annual_report.announcement.action_view": "Ver mi MastodonAnual", + "annual_report.announcement.description": "Descubrí más sobre tu participación en Mastodon durante el último año.", + "annual_report.announcement.title": "Llegó MastodonAnual {year}", "annual_report.summary.archetype.booster": "Corrió la voz", "annual_report.summary.archetype.lurker": "El acechador", "annual_report.summary.archetype.oracle": "El oráculo", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index f56df63cb6e208..a75bdee1eeec26 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -113,6 +113,9 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Describe esto para personas con discapacidad visual…", "alt_text_modal.done": "Hecho", "announcement.announcement": "Anuncio", + "annual_report.announcement.action_view": "Ver mi Wrapstodon", + "annual_report.announcement.description": "Descubre más sobre tu participación en Mastodon durante el último año.", + "annual_report.announcement.title": "Wrapstodon {year} ha llegado", "annual_report.summary.archetype.booster": "El cazador de tendencias", "annual_report.summary.archetype.lurker": "El merodeador", "annual_report.summary.archetype.oracle": "El oráculo", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 0d38f064c90db5..b755a627f35334 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -113,6 +113,9 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Descríbelo para personas con discapacidad visual…", "alt_text_modal.done": "Hecho", "announcement.announcement": "Comunicación", + "annual_report.announcement.action_view": "Ver mi Wrapstodon", + "annual_report.announcement.description": "Descubre más sobre tu participación en Mastodon durante el último año.", + "annual_report.announcement.title": "Wrapstodon {year} ha llegado", "annual_report.summary.archetype.booster": "El cazador de tendencias", "annual_report.summary.archetype.lurker": "El acechador", "annual_report.summary.archetype.oracle": "El oráculo", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 093984a09e134a..73afc7a231173a 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "شرح برای افرادی با مشکلات بینایی…", "alt_text_modal.done": "انجام شد", "announcement.announcement": "اعلامیه", + "annual_report.announcement.action_build": "ساخت خلاصهٔ ماستودونم", + "annual_report.announcement.action_view": "دیدن خلاصهٔ ماستودونم", + "annual_report.announcement.description": "کشف بیش‌تر دربارهٔ درگیریتان روی ماستودون در سال گذشته.", + "annual_report.announcement.title": "خلاصهٔ ماستودون {year} این‌جاست", "annual_report.summary.archetype.booster": "باحال‌یاب", "annual_report.summary.archetype.lurker": "کم‌پیدا", "annual_report.summary.archetype.oracle": "غیب‌گو", @@ -357,6 +361,7 @@ "empty_column.notification_requests": "همه چیز تمیز است! هیچ‌چیزی این‌جا نیست. هنگامی که آگاهی‌های جدیدی دریافت کنید، بسته به تنظیماتتان این‌جا ظاهر خواهند شد.", "empty_column.notifications": "هنوز هیچ آگاهی‌ای ندارید. هنگامی که دیگران با شما برهم‌کنش داشته باشند، این‌جا خواهید دیدش.", "empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران کارسازهای دیگر را پی‌گیری کنید تا این‌جا پُر شود", + "error.no_hashtag_feed_access": "پیوستن یا ورود برای مشاهده و پی‌گیری این برچسب.", "error.unexpected_crash.explanation": "به خاطر اشکالی در کدهای ما یا ناسازگاری با مرورگر شما، این صفحه به درستی نمایش نیافت.", "error.unexpected_crash.explanation_addons": "این صفحه نمی‌تواند درست نشان داده شود. احتمالاً این خطا ناشی از یک افزونهٔ مرورگر یا ابزار ترجمهٔ خودکار است.", "error.unexpected_crash.next_steps": "لطفاً صفحه را دوباره باز کنید. اگر کمکی نکرد، شاید همچنان بتوانید با ماستودون از راه یک مرورگر دیگر یا با یکی از کاره‌های آن کار کنید.", @@ -515,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "نمایش/نهفتن نوشتهٔ پشت هشدار محتوا", "keyboard_shortcuts.toggle_sensitivity": "نمایش/نهفتن رسانه", "keyboard_shortcuts.toot": "شروع یک فرستهٔ جدید", + "keyboard_shortcuts.top": "جابه‌جایی به بالای سیاهه", "keyboard_shortcuts.translate": "برای ترجمه یک پست", "keyboard_shortcuts.unfocus": "برداشتن تمرکز از ناحیهٔ نوشتن یا جست‌وجو", "keyboard_shortcuts.up": "بالا بردن در سیاهه", @@ -607,8 +613,8 @@ "notification.admin.report_statuses_other": "{name}، {target} را گزارش داد", "notification.admin.sign_up": "{name} ثبت نام کرد", "notification.admin.sign_up.name_and_others": "{name} و {count, plural, one {# نفر دیگر} other {# نفر دیگر}} ثبت‌نام کردند", - "notification.annual_report.message": "آمار ‪#Wrapstodon‬ ‏{year} تان منتظر است! لحظه‌های به یاد ماندنی و نقاط پررنگ سال را روی ماستودون رونمایی کنید!", - "notification.annual_report.view": "دیدن ‪#Wrapstodon‬", + "notification.annual_report.message": "#خلاصه_ماستودون {year} منتظرتان است! رونمایی از لحظه‌های به یاد ماندنی و نقاط پررنگ سال روی ماستودون!", + "notification.annual_report.view": "دیدن #خلاصه_ماستودون", "notification.favourite": "{name} فرسته‌تان را برگزید", "notification.favourite.name_and_others_with_link": "{name} و {count, plural, one {# نفر دیگر} other {# نفر دیگر}} فرسته‌تان را برگزیدند", "notification.favourite_pm": "{name} اشارهٔ خصوصیتان را برگزید", @@ -903,6 +909,7 @@ "status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد", "status.embed": "گرفتن کد تعبیه", "status.favourite": "برگزیده‌", + "status.favourites_count": "{count, plural, one {{counter} برگزیدن} other {{counter} برگزیدن}}", "status.filter": "پالایش این فرسته", "status.history.created": "توسط {name} در {date} ایجاد شد", "status.history.edited": "توسط {name} در {date} ویرایش شد", @@ -937,12 +944,14 @@ "status.quotes.empty": "هنوز کسی این فرسته را نقل نکرده. وقتی کسی چنین کند این‌جا نشان داده خواهد شد.", "status.quotes.local_other_disclaimer": "نقل‌هایی که به دست نگارنده رد شده باشند نشان داده نخواهند شد.", "status.quotes.remote_other_disclaimer": "تنها نقل‌ها از {domain} تضمین نمایش در این‌جا را دارند. نقل‌های رد شده به دست نگاره نشان داده نخواهند شد.", + "status.quotes_count": "{count, plural, one {{counter} نقل} other {{counter} نقل}}", "status.read_more": "بیشتر بخوانید", "status.reblog": "تقویت", "status.reblog_or_quote": "نقل یا تقویت", "status.reblog_private": "هم‌رسانی دوباره با پی‌گیرانتان", "status.reblogged_by": "‫{name}‬ تقویت کرد", "status.reblogs.empty": "هنوز هیچ کسی این فرسته را تقویت نکرده است. وقتی کسی چنین کاری کند، این‌جا نمایش داده خواهد شد.", + "status.reblogs_count": "{count, plural, one {{counter} تقویت} other {{counter} تقویت}}", "status.redraft": "حذف و بازنویسی", "status.remove_bookmark": "برداشتن نشانک", "status.remove_favourite": "حذف از موارد دلخواه", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 2429b22e107576..f6033a3a8fe8dd 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -80,7 +80,7 @@ "account.requests_to_follow_you": "Pyynnöt seurata sinua", "account.share": "Jaa käyttäjän @{name} profiili", "account.show_reblogs": "Näytä käyttäjän @{name} tehostukset", - "account.statuses_counter": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", + "account.statuses_counter": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", "account.unblock": "Kumoa käyttäjän @{name} esto", "account.unblock_domain": "Kumoa verkkotunnuksen {domain} esto", "account.unblock_domain_short": "Kumoa esto", @@ -100,7 +100,7 @@ "admin.impact_report.instance_followers": "Seuraajat, jotka käyttäjämme menettäisivät", "admin.impact_report.instance_follows": "Seuraajat, jotka heidän käyttäjänsä menettäisivät", "admin.impact_report.title": "Vaikutusten yhteenveto", - "alert.rate_limited.message": "Yritä uudelleen {retry_time, time, medium} jälkeen.", + "alert.rate_limited.message": "Yritä uudelleen kello {retry_time, time, medium} jälkeen.", "alert.rate_limited.title": "Pyyntömäärää rajoitettu", "alert.unexpected.message": "Tapahtui odottamaton virhe.", "alert.unexpected.title": "Hups!", @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Kuvaile tätä näkövammallisille ihmisille…", "alt_text_modal.done": "Valmis", "announcement.announcement": "Tiedote", + "annual_report.announcement.action_build": "Koosta oma Wrapstodon", + "annual_report.announcement.action_view": "Näytä oma Wrapstodon", + "annual_report.announcement.description": "Tutustu toimintaasi Mastodonissa kuluneen vuoden aikana.", + "annual_report.announcement.title": "Wrapstodon {year} on täällä", "annual_report.summary.archetype.booster": "Tehostaja", "annual_report.summary.archetype.lurker": "Lymyilijä", "annual_report.summary.archetype.oracle": "Oraakkeli", @@ -202,7 +206,7 @@ "compose.published.body": "Julkaisu lähetetty.", "compose.published.open": "Avaa", "compose.saved.body": "Julkaisu tallennettu.", - "compose_form.direct_message_warning_learn_more": "Lisätietoja", + "compose_form.direct_message_warning_learn_more": "Lue lisää", "compose_form.encryption_warning": "Mastodonin julkaisut eivät ole päästä päähän salattuja. Älä jaa arkaluonteisia tietoja Mastodonissa.", "compose_form.hashtag_warning": "Tätä julkaisua ei voi liittää aihetunnisteisiin, koska se ei ole julkinen. Vain näkyvyydeltään julkisiksi määritettyjä julkaisuja voidaan hakea aihetunnisteiden avulla.", "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.", @@ -298,7 +302,7 @@ "domain_block_modal.they_cant_follow": "Kukaan tältä palvelimelta ei voi seurata sinua.", "domain_block_modal.they_wont_know": "Hän ei saa tietää tulleensa estetyksi.", "domain_block_modal.title": "Estetäänkö verkkotunnus?", - "domain_block_modal.you_will_lose_num_followers": "Menetät {followersCount, plural, one {{followersCountDisplay} seuraajasi} other {{followersCountDisplay} seuraajaasi}} ja {followingCount, plural, one {{followingCountDisplay} seurattavasi} other {{followingCountDisplay} seurattavaasi}}.", + "domain_block_modal.you_will_lose_num_followers": "Menetät {followersCount, plural, one {{followersCountDisplay}:n seuraajasi} other {{followersCountDisplay} seuraajaasi}} ja {followingCount, plural, one {{followingCountDisplay}:n seurattavasi} other {{followingCountDisplay} seurattavaasi}}.", "domain_block_modal.you_will_lose_relationships": "Menetät kaikki tämän palvelimen seuraajasi ja seurattavasi.", "domain_block_modal.you_wont_see_posts": "Et enää näe julkaisuja etkä ilmoituksia tämän palvelimen käyttäjiltä.", "domain_pill.activitypub_lets_connect": "Sen avulla voit muodostaa yhteyden ja olla vuorovaikutuksessa ihmisten kanssa, ei vain Mastodonissa vaan myös muissa sosiaalisissa sovelluksissa.", @@ -338,7 +342,7 @@ "empty_column.account_hides_collections": "Käyttäjä on päättänyt pitää nämä tiedot yksityisinä", "empty_column.account_suspended": "Tili jäädytetty", "empty_column.account_timeline": "Ei julkaisuja täällä!", - "empty_column.account_unavailable": "Profiilia ei ole saatavilla", + "empty_column.account_unavailable": "Profiili ei saatavilla", "empty_column.blocks": "Et ole vielä estänyt käyttäjiä.", "empty_column.bookmarked_statuses": "Et ole vielä lisännyt julkaisuja kirjanmerkkeihisi. Kun lisäät yhden, se näkyy tässä.", "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista, niin homma lähtee käyntiin!", @@ -436,15 +440,15 @@ "hashtag.column_settings.tag_mode.any": "Mikä tahansa näistä", "hashtag.column_settings.tag_mode.none": "Ei mitään näistä", "hashtag.column_settings.tag_toggle": "Sisällytä lisätunnisteet tähän sarakkeeseen", - "hashtag.counter_by_accounts": "{count, plural, one {{counter} osallistuja} other {{counter} osallistujaa}}", - "hashtag.counter_by_uses": "{count, plural, one{{counter} julkaisu} other {{counter} julkaisua}}", - "hashtag.counter_by_uses_today": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}} tänään", + "hashtag.counter_by_accounts": "{count, plural, one {{counter} osallistuja} other {{counter} osallistujaa}}", + "hashtag.counter_by_uses": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", + "hashtag.counter_by_uses_today": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}} tänään", "hashtag.feature": "Suosittele profiilissa", "hashtag.follow": "Seuraa aihetunnistetta", "hashtag.mute": "Mykistä #{hashtag}", "hashtag.unfeature": "Kumoa suosittelu profiilissa", "hashtag.unfollow": "Lopeta aihetunnisteen seuraaminen", - "hashtags.and_other": "…ja {count, plural, other {# lisää}}", + "hashtags.and_other": "…ja {count, plural, other {# lisää}}", "hints.profiles.followers_may_be_missing": "Tämän profiilin seuraajia saattaa puuttua.", "hints.profiles.follows_may_be_missing": "Tämän profiilin seurattavia saattaa puuttua.", "hints.profiles.posts_may_be_missing": "Tämän profiilin julkaisuja saattaa puuttua.", @@ -479,9 +483,9 @@ "interaction_modal.on_this_server": "Tällä palvelimella", "interaction_modal.title": "Jatka kirjautumalla sisään", "interaction_modal.username_prompt": "Esim. {example}", - "intervals.full.days": "{number, plural, one {# päivä} other {# päivää}}", - "intervals.full.hours": "{number, plural, one {# tunti} other {# tuntia}}", - "intervals.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}}", + "intervals.full.days": "{number, plural, one {# päivä} other {# päivää}}", + "intervals.full.hours": "{number, plural, one {# tunti} other {# tuntia}}", + "intervals.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}}", "keyboard_shortcuts.back": "Siirry takaisin", "keyboard_shortcuts.blocked": "Avaa estettyjen käyttäjien luettelo", "keyboard_shortcuts.boost": "Tehosta julkaisua", @@ -531,7 +535,7 @@ "limited_account_hint.title": "Palvelimen {domain} moderaattorit ovat piilottaneet tämän profiilin.", "link_preview.author": "Tehnyt {name}", "link_preview.more_from_author": "Lisää tekijältä {name}", - "link_preview.shares": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", + "link_preview.shares": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}", "lists.add_member": "Lisää", "lists.add_to_list": "Lisää listaan", "lists.add_to_lists": "Lisää {name} listaan", @@ -544,7 +548,7 @@ "lists.exclusive": "Piilota jäsenet kotisyötteestä", "lists.exclusive_hint": "Jos joku on tässä listassa, piilota hänet kotisyötteestäsi, jotta et näe hänen julkaisujaan kahteen kertaan.", "lists.find_users_to_add": "Etsi lisättäviä käyttäjiä", - "lists.list_members_count": "{count, plural, one {# jäsen} other {# jäsentä}}", + "lists.list_members_count": "{count, plural, one {# jäsen} other {# jäsentä}}", "lists.list_name": "Listan nimi", "lists.new_list_name": "Uuden listan nimi", "lists.no_lists_yet": "Ei vielä listoja.", @@ -557,7 +561,7 @@ "lists.save": "Tallenna", "lists.search": "Haku", "lists.show_replies_to": "Sisällytä listan jäsenten vastaukset kohteeseen", - "load_pending": "{count, plural, one {# uusi kohde} other {# uutta kohdetta}}", + "load_pending": "{count, plural, one {# uusi kohde} other {# uutta kohdetta}}", "loading_indicator.label": "Ladataan…", "media_gallery.hide": "Piilota", "moved_to_account_banner.text": "Tilisi {disabledAccount} on tällä hetkellä poissa käytöstä, koska teit siirron tiliin {movedToAccount}.", @@ -616,7 +620,7 @@ "notification.favourite_pm": "{name} lisäsi yksityismainintasi suosikkeihinsa", "notification.favourite_pm.name_and_others_with_link": "{name} ja {count, plural, one {# muu} other {# muuta}} lisäsivät yksityismainintasi suosikkeihinsa", "notification.follow": "{name} seurasi sinua", - "notification.follow.name_and_others": "{name} ja {count, plural, one {# muu} other {# muuta}} seurasivat sinua", + "notification.follow.name_and_others": "{name} ja {count, plural, one {# muu} other {# muuta}} seurasivat sinua", "notification.follow_request": "{name} on pyytänyt lupaa seurata sinua", "notification.follow_request.name_and_others": "{name} ja {count, plural, one {# muu} other {# muuta}} pyysivät saada seurata sinua", "notification.label.mention": "Maininta", @@ -639,7 +643,7 @@ "notification.poll": "Äänestys, johon osallistuit, on päättynyt", "notification.quoted_update": "{name} muokkasi lainaamaasi julkaisua", "notification.reblog": "{name} tehosti julkaisuasi", - "notification.reblog.name_and_others_with_link": "{name} ja {count, plural, one {# muu} other {# muuta}} tehostivat julkaisuasi", + "notification.reblog.name_and_others_with_link": "{name} ja {count, plural, one {# muu} other {# muuta}} tehostivat julkaisuasi", "notification.relationships_severance_event": "Menetettiin yhteydet palvelimeen {name}", "notification.relationships_severance_event.account_suspension": "Palvelimen {from} ylläpitäjä on jäädyttänyt palvelimen {target} vuorovaikutuksen. Enää et voi siis vastaanottaa päivityksiä heiltä tai olla yhteyksissä heidän kanssaan.", "notification.relationships_severance_event.domain_block": "Palvelimen {from} ylläpitäjä on estänyt palvelimen {target} vuorovaikutuksen – mukaan lukien {followersCount} seuraajistasi ja {followingCount, plural, one {# seurattavistasi} other {# seurattavistasi}}.", @@ -648,7 +652,7 @@ "notification.status": "{name} julkaisi juuri", "notification.update": "{name} muokkasi julkaisua", "notification_requests.accept": "Hyväksy", - "notification_requests.accept_multiple": "{count, plural, one {Hyväksy # pyyntö…} other {Hyväksy # pyyntöä…}}", + "notification_requests.accept_multiple": "{count, plural, one {Hyväksy # pyyntö…} other {Hyväksy # pyyntöä…}}", "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Hyväksy pyyntö} other {Hyväksy pyynnöt}}", "notification_requests.confirm_accept_multiple.message": "Olet aikeissa hyväksyä {count, plural, one {ilmoituspyynnön} other {# ilmoituspyyntöä}}. Haluatko varmasti jatkaa?", "notification_requests.confirm_accept_multiple.title": "Hyväksytäänkö ilmoituspyynnöt?", @@ -712,7 +716,7 @@ "notifications.policy.filter_limited_accounts_title": "Moderoidut tilit", "notifications.policy.filter_new_accounts.hint": "Luotu {days, plural, one {viime päivän} other {viimeisen # päivän}} aikana", "notifications.policy.filter_new_accounts_title": "Uudet tilit", - "notifications.policy.filter_not_followers_hint": "Mukaan lukien alle {days, plural, one {päivän} other {# päivää}} sinua seuranneet", + "notifications.policy.filter_not_followers_hint": "Mukaan lukien alle {days, plural, one {päivän} other {# päivää}} sinua seuranneet", "notifications.policy.filter_not_followers_title": "Käyttäjät, jotka eivät seuraa sinua", "notifications.policy.filter_not_following_hint": "Kunnes hyväksyt heidät manuaalisesti", "notifications.policy.filter_not_following_title": "Käyttäjät, joita et seuraa", @@ -743,11 +747,11 @@ "poll.closed": "Päättynyt", "poll.refresh": "Päivitä", "poll.reveal": "Näytä tulokset", - "poll.total_people": "{count, plural, one {# käyttäjä} other {# käyttäjää}}", - "poll.total_votes": "{count, plural, one {# ääni} other {# ääntä}}", + "poll.total_people": "{count, plural, one {# käyttäjä} other {# käyttäjää}}", + "poll.total_votes": "{count, plural, one {# ääni} other {# ääntä}}", "poll.vote": "Äänestä", "poll.voted": "Äänestit tätä vastausta", - "poll.votes": "{votes, plural, one {# ääni} other {# ääntä}}", + "poll.votes": "{votes, plural, one {# ääni} other {# ääntä}}", "poll_button.add_poll": "Lisää äänestys", "poll_button.remove_poll": "Poista äänestys", "privacy.change": "Muuta julkaisun näkyvyyttä", @@ -757,7 +761,7 @@ "privacy.private.short": "Seuraajat", "privacy.public.long": "Kuka tahansa Mastodonissa ja sen ulkopuolella", "privacy.public.short": "Julkinen", - "privacy.quote.anyone": "{visibility}, kuka vain voi lainata", + "privacy.quote.anyone": "{visibility}, kuka tahansa voi lainata", "privacy.quote.disabled": "{visibility}, lainaukset poissa käytöstä", "privacy.quote.limited": "{visibility}, lainauksia rajoitettu", "privacy.unlisted.additional": "Tämä toimii muuten kuin julkinen, mutta julkaisut eivät näy livesyöte-, aihetunniste- tai selausnäkymissä eivätkä Mastodonin hakutuloksissa, vaikka ne olisivat käyttäjätililläsi yleisesti sallittuina.", @@ -766,7 +770,7 @@ "privacy_policy.last_updated": "Päivitetty viimeksi {date}", "privacy_policy.title": "Tietosuojakäytäntö", "quote_error.edit": "Lainauksia ei voi lisätä julkaisua muokattaessa.", - "quote_error.poll": "Äänestysten lainaaminen ei ole sallittua.", + "quote_error.poll": "Lainaaminen äänestyksen ohessa ei ole sallittua.", "quote_error.private_mentions": "Lainaaminen ei ole sallittua yksityismaininnoissa.", "quote_error.quote": "Vain yksi lainaus kerrallaan on sallittu.", "quote_error.unauthorized": "Sinulla ei ole valtuuksia lainata tätä julkaisua.", @@ -775,21 +779,21 @@ "refresh": "Päivitä", "regeneration_indicator.please_stand_by": "Ole valmiina.", "regeneration_indicator.preparing_your_home_feed": "Kotisyötettäsi valmistellaan…", - "relative_time.days": "{number} pv", - "relative_time.full.days": "{number, plural, one {# päivä} other {# päivää}} sitten", - "relative_time.full.hours": "{number, plural, one {# tunti} other {# tuntia}} sitten", + "relative_time.days": "{number} pv", + "relative_time.full.days": "{number, plural, one {# päivä} other {# päivää}} sitten", + "relative_time.full.hours": "{number, plural, one {# tunti} other {# tuntia}} sitten", "relative_time.full.just_now": "juuri nyt", - "relative_time.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} sitten", - "relative_time.full.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} sitten", - "relative_time.hours": "{number} t", + "relative_time.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} sitten", + "relative_time.full.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} sitten", + "relative_time.hours": "{number} t", "relative_time.just_now": "nyt", - "relative_time.minutes": "{number} min", - "relative_time.seconds": "{number} s", + "relative_time.minutes": "{number} min", + "relative_time.seconds": "{number} s", "relative_time.today": "tänään", "remove_quote_hint.button_label": "Selvä", "remove_quote_hint.message": "Voit tehdä sen {icon}-valikosta.", "remove_quote_hint.title": "Haluatko poistaa lainatun julkaisusi?", - "reply_indicator.attachments": "{count, plural, one {# liite} other {# liitettä}}", + "reply_indicator.attachments": "{count, plural, one {# liite} other {# liitettä}}", "reply_indicator.cancel": "Peruuta", "reply_indicator.poll": "Äänestys", "report.block": "Estä", @@ -832,7 +836,7 @@ "report.thanks.title_actionable": "Kiitos raportista – tutkimme asiaa.", "report.unfollow": "Lopeta käyttäjän @{name} seuraaminen", "report.unfollow_explanation": "Seuraat tätä tiliä. Jotta et enää näkisi sen julkaisuja kotisyötteessäsi, lopeta tilin seuraaminen.", - "report_notification.attached_statuses": "{count, plural, one {{count} julkaisu} other {{count} julkaisua}} liitteenä", + "report_notification.attached_statuses": "{count, plural, one {{count} julkaisu} other {{count} julkaisua}} liitteenä", "report_notification.categories.legal": "Lakiseikat", "report_notification.categories.legal_sentence": "laiton sisältö", "report_notification.categories.other": "Muu", @@ -848,11 +852,11 @@ "search.quick_action.account_search": "Profiilit haulla {x}", "search.quick_action.go_to_account": "Siirry profiiliin {x}", "search.quick_action.go_to_hashtag": "Siirry aihetunnisteeseen {x}", - "search.quick_action.open_url": "Avaa URL-osoite Mastodonissa", + "search.quick_action.open_url": "Avaa URL-⁠osoite Mastodonissa", "search.quick_action.status_search": "Julkaisut haulla {x}", - "search.search_or_paste": "Hae tai liitä URL-osoite", + "search.search_or_paste": "Hae tai liitä URL-⁠osoite", "search_popout.full_text_search_disabled_message": "Ei saatavilla palvelimella {domain}.", - "search_popout.full_text_search_logged_out_message": "Käytettävissä vain sisäänkirjautuneena.", + "search_popout.full_text_search_logged_out_message": "Saatavilla vain sisäänkirjautuneena.", "search_popout.language_code": "ISO-kielikoodi", "search_popout.options": "Hakuvalinnat", "search_popout.quick_actions": "Pikatoiminnot", @@ -863,7 +867,7 @@ "search_results.all": "Kaikki", "search_results.hashtags": "Aihetunnisteet", "search_results.no_results": "Ei tuloksia.", - "search_results.no_search_yet": "Kokeile hakea julkaisuja, profiileja tai aihetunnisteita.", + "search_results.no_search_yet": "Koeta hakea julkaisuja, profiileja tai aihetunnisteita.", "search_results.see_all": "Näytä kaikki", "search_results.statuses": "Julkaisut", "search_results.title": "Haku ”{q}”", @@ -902,10 +906,10 @@ "status.direct_indicator": "Yksityismaininta", "status.edit": "Muokkaa", "status.edited": "Viimeksi muokattu {date}", - "status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}", + "status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}", "status.embed": "Hanki upotuskoodi", "status.favourite": "Suosikki", - "status.favourites_count": "{count, plural, one {{counter} suosikki} other {{counter} suosikkia}}", + "status.favourites_count": "{count, plural, one {{counter} suosikki} other {{counter} suosikkia}}", "status.filter": "Suodata tämä julkaisu", "status.history.created": "{name} loi {date}", "status.history.edited": "{name} muokkasi {date}", @@ -940,14 +944,14 @@ "status.quotes.empty": "Kukaan ei ole vielä lainannut tätä julkaisua. Kun joku tekee niin, se tulee tähän näkyviin.", "status.quotes.local_other_disclaimer": "Tekijän hylkäämiä lainauksia ei näytetä.", "status.quotes.remote_other_disclaimer": "Vain palvelimen {domain} lainaukset näkyvät taatusti tässä. Tekijän hylkäämiä lainauksia ei näytetä.", - "status.quotes_count": "{count, plural, one {{counter} lainaus} other {{counter} lainausta}}", + "status.quotes_count": "{count, plural, one {{counter} lainaus} other {{counter} lainausta}}", "status.read_more": "Näytä enemmän", "status.reblog": "Tehosta", "status.reblog_or_quote": "Tehosta tai lainaa", "status.reblog_private": "Jaa uudelleen seuraajiesi kanssa", "status.reblogged_by": "{name} tehosti", "status.reblogs.empty": "Kukaan ei ole vielä tehostanut tätä julkaisua. Kun joku tekee niin, tulee hän tähän näkyviin.", - "status.reblogs_count": "{count, plural, one {{counter} tehostus} other {{counter} tehostusta}}", + "status.reblogs_count": "{count, plural, one {{counter} tehostus} other {{counter} tehostusta}}", "status.redraft": "Poista ja palauta muokattavaksi", "status.remove_bookmark": "Poista kirjanmerkki", "status.remove_favourite": "Poista suosikeista", @@ -964,10 +968,10 @@ "status.show_less_all": "Näytä kaikista vähemmän", "status.show_more_all": "Näytä kaikista enemmän", "status.show_original": "Näytä alkuperäinen", - "status.title.with_attachments": "{user} liitti {attachmentCount, plural, one {{attachmentCount} tiedoston} other {{attachmentCount} tiedostoa}}", + "status.title.with_attachments": "{user} julkaisi {attachmentCount, plural, one {liitteen} other {{attachmentCount} liitettä}}", "status.translate": "Käännä", - "status.translated_from_with": "Käännetty kielestä {lang} käyttäen palvelua {provider}", - "status.uncached_media_warning": "Esikatselu ei ole käytettävissä", + "status.translated_from_with": "Käännetty kielestä {lang} palvelulla {provider}", + "status.uncached_media_warning": "Esikatselu ei saatavilla", "status.unmute_conversation": "Kumoa keskustelun mykistys", "status.unpin": "Irrota profiilista", "subscribed_languages.lead": "Vain valituilla kielillä kirjoitetut julkaisut näkyvät koti- ja lista-aikajanoillasi muutoksen jälkeen. Älä valitse mitään, jos haluat nähdä julkaisuja kaikilla kielillä.", @@ -981,17 +985,17 @@ "terms_of_service.effective_as_of": "Tulee voimaan {date}", "terms_of_service.title": "Käyttöehdot", "terms_of_service.upcoming_changes_on": "Tulevia muutoksia {date}", - "time_remaining.days": "{number, plural, one {# päivä} other {# päivää}} jäljellä", - "time_remaining.hours": "{number, plural, one {# tunti} other {# tuntia}} jäljellä", - "time_remaining.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} jäljellä", + "time_remaining.days": "{number, plural, one {# päivä} other {# päivää}} jäljellä", + "time_remaining.hours": "{number, plural, one {# tunti} other {# tuntia}} jäljellä", + "time_remaining.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} jäljellä", "time_remaining.moments": "Hetkiä jäljellä", - "time_remaining.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} jäljellä", + "time_remaining.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} jäljellä", "trends.counter_by_accounts": "{count, plural, one {{counter} käyttäjä} other {{counter} käyttäjää}} {days, plural, one {viime päivänä} other {viimeisenä {days} päivänä}}", "trends.trending_now": "Suosittua nyt", "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.", - "units.short.billion": "{count} mrd.", - "units.short.million": "{count} milj.", - "units.short.thousand": "{count} t.", + "units.short.billion": "{count} mrd.", + "units.short.million": "{count} milj.", + "units.short.thousand": "{count} t.", "upload_area.title": "Lähetä raahaamalla ja pudottamalla tähän", "upload_button.label": "Lisää kuvia, video tai äänitiedosto", "upload_error.limit": "Tiedostolähetysten rajoitus ylitetty.", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index a207c23e42d5a4..29fe1176fccb08 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "תיאור התוכן לפגועי ראיה…", "alt_text_modal.done": "סיום", "announcement.announcement": "הכרזה", + "annual_report.announcement.action_build": "בנה לי את הסיכומודון שלי", + "annual_report.announcement.action_view": "לצפייה בסיכומודון שלי", + "annual_report.announcement.description": "ללמוד עוד על דבפוסי השימוש שלך במסטודון לאורך השנה החולפת.", + "annual_report.announcement.title": "סיכומודון {year} הגיע", "annual_report.summary.archetype.booster": "ההד-וניסט(ית)", "annual_report.summary.archetype.lurker": "השורץ.ת השקט.ה", "annual_report.summary.archetype.oracle": "כבוד הרב.ה", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 08082b53ba54dd..d1df41ed7be07c 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -113,6 +113,7 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Írd le a látássérültek számára…", "alt_text_modal.done": "Kész", "announcement.announcement": "Közlemény", + "annual_report.announcement.action_view": "A Wrapstodon-om megtekintése", "annual_report.summary.archetype.booster": "A cool-vadász", "annual_report.summary.archetype.lurker": "A settenkedő", "annual_report.summary.archetype.oracle": "Az orákulum", @@ -516,6 +517,7 @@ "keyboard_shortcuts.toggle_hidden": "Tartalmi figyelmeztetéssel ellátott szöveg megjelenítése/elrejtése", "keyboard_shortcuts.toggle_sensitivity": "Média megjelenítése/elrejtése", "keyboard_shortcuts.toot": "Új bejegyzés írása", + "keyboard_shortcuts.top": "Ugorj a lista elejére", "keyboard_shortcuts.translate": "Bejegyzés lefordítása", "keyboard_shortcuts.unfocus": "Szerkesztés/keresés fókuszból való kivétele", "keyboard_shortcuts.up": "Mozgás felfelé a listában", diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json index 6725d2b0cb038d..304ee7046baac5 100644 --- a/app/javascript/mastodon/locales/is.json +++ b/app/javascript/mastodon/locales/is.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Lýstu þessu fyrir fólk með skerta sjón…", "alt_text_modal.done": "Lokið", "announcement.announcement": "Auglýsing", + "annual_report.announcement.action_build": "Byggja Wrapstodon fyrir mig", + "annual_report.announcement.action_view": "Skoða minn Wrapstodon", + "annual_report.announcement.description": "Skoðaðu meira um virkni þína á Mastodon á síðastliðnu ári.", + "annual_report.announcement.title": "Wrapstodon {year} er komið", "annual_report.summary.archetype.booster": "Svali gaurinn", "annual_report.summary.archetype.lurker": "Lurkurinn", "annual_report.summary.archetype.oracle": "Völvan", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index 06026984dec8d5..09867bcf9f0819 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -351,7 +351,7 @@ "empty_column.followed_tags": "Dar neseki jokių saitažodžių. Kai tai padarysi, jie bus rodomi čia.", "empty_column.hashtag": "Nėra nieko šiame saitažodyje kol kas.", "empty_column.home": "Tavo pagrindinio laiko skalė tuščia. Sek daugiau žmonių, kad ją užpildytum.", - "empty_column.list": "Nėra nieko šiame sąraše kol kas. Kai šio sąrašo nariai paskelbs naujų įrašų, jie bus rodomi čia.", + "empty_column.list": "Šiame sąraše dar nieko nėra. Kai šio sąrašo nariai paskelbs naujus įrašus, jie bus rodomi čia.", "empty_column.mutes": "Dar nesi nutildęs (-usi) nė vieno naudotojo.", "empty_column.notification_requests": "Viskas švaru! Čia nieko nėra. Kai gausi naujų pranešimų, jie bus rodomi čia pagal tavo nustatymus.", "empty_column.notifications": "Dar neturi jokių pranešimų. Kai kiti žmonės su tavimi sąveikaus, matysi tai čia.", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index e3497da2ff5c2d..85a223cbfe9496 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Beschrijf dit voor blinden en slechtzienden…", "alt_text_modal.done": "Klaar", "announcement.announcement": "Mededeling", + "annual_report.announcement.action_build": "Bouw mijn Wrapstodon", + "annual_report.announcement.action_view": "Bekijk mijn Wrapstodon", + "annual_report.announcement.description": "Ontdek meer over jouw engagement op Mastodon over het afgelopen jaar.", + "annual_report.announcement.title": "Wrapstodon {year} is gearriveerd", "annual_report.summary.archetype.booster": "De cool-hunter", "annual_report.summary.archetype.lurker": "De lurker", "annual_report.summary.archetype.oracle": "Het orakel", @@ -357,6 +361,7 @@ "empty_column.notification_requests": "Helemaal leeg! Er is hier niets. Wanneer je nieuwe meldingen ontvangt, verschijnen deze hier volgens jouw instellingen.", "empty_column.notifications": "Je hebt nog geen meldingen. Begin met iemand een gesprek.", "empty_column.public": "Er is hier helemaal niks! Plaatst een openbaar bericht of volg mensen van andere servers om het te vullen", + "error.no_hashtag_feed_access": "Registreren of inloggen om de hashtag te bekijken of te volgen.", "error.unexpected_crash.explanation": "Als gevolg van een bug in onze broncode of als gevolg van een compatibiliteitsprobleem met jouw webbrowser, kan deze pagina niet goed worden weergegeven.", "error.unexpected_crash.explanation_addons": "Deze pagina kon niet correct geladen worden. Deze fout wordt waarschijnlijk door een browser-add-on of een automatische vertalingshulpmiddel veroorzaakt.", "error.unexpected_crash.next_steps": "Probeer deze pagina te vernieuwen. Wanneer dit niet helpt is het nog steeds mogelijk om Mastodon in een andere webbrowser of mobiele app te gebruiken.", @@ -515,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Inhoudswaarschuwing tonen/verbergen", "keyboard_shortcuts.toggle_sensitivity": "Media tonen/verbergen", "keyboard_shortcuts.toot": "Nieuw bericht schrijven", + "keyboard_shortcuts.top": "Naar het begin van de lijst verplaatsen", "keyboard_shortcuts.translate": "om een bericht te vertalen", "keyboard_shortcuts.unfocus": "Tekst- en zoekveld ontfocussen", "keyboard_shortcuts.up": "Naar boven in de lijst bewegen", diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json index 17c526140aca9c..6180bd4c6558e1 100644 --- a/app/javascript/mastodon/locales/nn.json +++ b/app/javascript/mastodon/locales/nn.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Skriv ei skildring for menneske med synsnedsetjingar…", "alt_text_modal.done": "Ferdig", "announcement.announcement": "Kunngjering", + "annual_report.announcement.action_build": "Set saman min Årstodon", + "annual_report.announcement.action_view": "Sjå min Årstodon", + "annual_report.announcement.description": "Sjå meir om kva du har gjort på Mastodon siste året.", + "annual_report.announcement.title": "Årstodon {year} er her", "annual_report.summary.archetype.booster": "Den som jaktar på noko kult", "annual_report.summary.archetype.lurker": "Den som heng på hjørnet", "annual_report.summary.archetype.oracle": "Orakelet", @@ -332,8 +336,8 @@ "emoji_button.search_results": "Søkeresultat", "emoji_button.symbols": "Symbol", "emoji_button.travel": "Reise & stader", - "empty_column.account_featured.me": "Du har ikkje valt ut noko enno. Visste du at du kan velja ut merkelappar du bruker mykje, og til og med venekontoar på profilen din?", - "empty_column.account_featured.other": "{acct} har ikkje valt ut noko enno. Visste du at du kan velja ut merkelappar du bruker mykje, og til og med venekontoar på profilen din?", + "empty_column.account_featured.me": "Du har ikkje valt ut noko enno. Visste du at du kan velja ut emneknaggar du bruker mykje, og til og med venekontoar på profilen din?", + "empty_column.account_featured.other": "{acct} har ikkje valt ut noko enno. Visste du at du kan velja ut emneknaggar du bruker mykje, og til og med venekontoar på profilen din?", "empty_column.account_featured_other.unknown": "Denne kontoen har ikkje valt ut noko enno.", "empty_column.account_hides_collections": "Denne brukaren har valt å ikkje gjere denne informasjonen tilgjengeleg", "empty_column.account_suspended": "Kontoen er utestengd", @@ -357,6 +361,7 @@ "empty_column.notification_requests": "Ferdig! Her er det ingenting. Når du får nye varsel, kjem dei opp her slik du har valt.", "empty_column.notifications": "Du har ingen varsel enno. Kommuniser med andre for å starte samtalen.", "empty_column.public": "Det er ingenting her! Skriv noko offentleg, eller fylg brukarar frå andre tenarar manuelt for å få meir her", + "error.no_hashtag_feed_access": "Bli medlem eller logg inn for å sjå og fylgja denne emneknaggen.", "error.unexpected_crash.explanation": "På grunn av eit nettlesarkompatibilitetsproblem eller ein feil i koden vår, kunne ikkje denne sida bli vist slik den skal.", "error.unexpected_crash.explanation_addons": "Denne sida kunne ikkje visast som den skulle. Feilen kjem truleg frå ei nettleserutviding eller frå automatiske omsetjingsverktøy.", "error.unexpected_crash.next_steps": "Prøv å lasta inn sida på nytt. Hjelper ikkje dette kan du framleis nytta Mastodon i ein annan nettlesar eller app.", @@ -515,6 +520,7 @@ "keyboard_shortcuts.toggle_hidden": "Vis/gøym tekst bak innhaldsvarsel", "keyboard_shortcuts.toggle_sensitivity": "Vis/gøym media", "keyboard_shortcuts.toot": "Lag nytt tut", + "keyboard_shortcuts.top": "Flytt til toppen av lista", "keyboard_shortcuts.translate": "å omsetje eit innlegg", "keyboard_shortcuts.unfocus": "for å fokusere vekk skrive-/søkefeltet", "keyboard_shortcuts.up": "Flytt opp på lista", @@ -595,9 +601,9 @@ "navigation_bar.privacy_and_reach": "Personvern og rekkjevidd", "navigation_bar.search": "Søk", "navigation_bar.search_trends": "Søk / Populært", - "navigation_panel.collapse_followed_tags": "Gøym menyen over merkelappar du fylgjer", + "navigation_panel.collapse_followed_tags": "Gøym menyen over emneknaggar du fylgjer", "navigation_panel.collapse_lists": "Gøym listemenyen", - "navigation_panel.expand_followed_tags": "Utvid menyen over merkelappar du fylgjer", + "navigation_panel.expand_followed_tags": "Utvid menyen over emneknaggar du fylgjer", "navigation_panel.expand_lists": "Utvid listemenyen", "not_signed_in_indicator.not_signed_in": "Du må logga inn for å få tilgang til denne ressursen.", "notification.admin.report": "{name} rapporterte {target}", @@ -758,7 +764,7 @@ "privacy.quote.anyone": "{visibility}, alle kan sitera", "privacy.quote.disabled": "{visibility}, ingen kan sitera", "privacy.quote.limited": "{visibility}, avgrensa sitat", - "privacy.unlisted.additional": "Dette er akkurat som offentleg, bortsett frå at innlegga ikkje dukkar opp i direktestraumar eller merkelappar, i oppdagingar eller Mastodon-søk, sjølv om du har sagt ja til at kontoen skal vera synleg.", + "privacy.unlisted.additional": "Dette er akkurat som offentleg, bortsett frå at innlegga ikkje dukkar opp i direktestraumar eller emneknaggar, i oppdagingar eller Mastodon-søk, sjølv om du har sagt ja til at kontoen skal vera synleg.", "privacy.unlisted.long": "Gøymt frå søkjeresultata på Mastodon, og frå populære og offentlege tidsliner", "privacy.unlisted.short": "Stille offentleg", "privacy_policy.last_updated": "Sist oppdatert {date}", @@ -861,7 +867,7 @@ "search_results.all": "Alt", "search_results.hashtags": "Emneknaggar", "search_results.no_results": "Ingen resultat.", - "search_results.no_search_yet": "Prøv å søkja etter innlegg, profilar eller merkelappar.", + "search_results.no_search_yet": "Prøv å søkja etter innlegg, profilar eller emneknaggar.", "search_results.see_all": "Sjå alle", "search_results.statuses": "Tut", "search_results.title": "Søk etter \"{q}\"", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 1c7ef074a2786a..158da029d71881 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -55,11 +55,11 @@ "account.follows.empty": "Nada aqui.", "account.follows_you": "Segue você", "account.go_to_profile": "Ir ao perfil", - "account.hide_reblogs": "Ocultar boosts de @{name}", + "account.hide_reblogs": "Ocultar impulsionamentos de @{name}", "account.in_memoriam": "Em memória.", "account.joined_short": "Entrou", "account.languages": "Mudar idiomas inscritos", - "account.link_verified_on": "link verificado em {date}", + "account.link_verified_on": "A propriedade deste link foi verificada em {date}", "account.locked_info": "Trancado. Seguir requer aprovação manual do perfil.", "account.media": "Mídia", "account.mention": "Mencionar @{name}", @@ -79,7 +79,7 @@ "account.requested_follow": "{name} quer te seguir", "account.requests_to_follow_you": "Pediu para seguir você", "account.share": "Compartilhar perfil de @{name}", - "account.show_reblogs": "Mostrar boosts de @{name}", + "account.show_reblogs": "Mostrar impulsionamentos de @{name}", "account.statuses_counter": "{count, plural, one {{counter} publicação} other {{counter} publicações}}", "account.unblock": "Desbloquear @{name}", "account.unblock_domain": "Desbloquear domínio {domain}", @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Descreva isto para pessoas com deficiências visuais…", "alt_text_modal.done": "Feito", "announcement.announcement": "Comunicados", + "annual_report.announcement.action_build": "Gerar meu Wrapstodon", + "annual_report.announcement.action_view": "Ver meu Wrapstodon", + "annual_report.announcement.description": "Descubra mais sobre seu engajamento no Mastodon ao longo do último ano.", + "annual_report.announcement.title": "Chegou o Wrapstodon de {year}", "annual_report.summary.archetype.booster": "Caçador legal", "annual_report.summary.archetype.lurker": "O espreitador", "annual_report.summary.archetype.oracle": "O oráculo", @@ -142,9 +146,9 @@ "block_modal.they_will_know": "Poderá ver que você bloqueou.", "block_modal.title": "Bloquear usuário?", "block_modal.you_wont_see_mentions": "Você não verá publicações que mencionem o usuário.", - "boost_modal.combo": "Pressione {combo} para pular isso na próxima vez", + "boost_modal.combo": "Pressione {combo} para pular isto na próxima vez", "boost_modal.reblog": "Impulsionar a publicação?", - "boost_modal.undo_reblog": "Retirar o impulsionamento do post?", + "boost_modal.undo_reblog": "Retirar o impulso do post?", "bundle_column_error.copy_stacktrace": "Copiar relatório do erro", "bundle_column_error.error.body": "A página solicitada não pôde ser renderizada. Pode ser devido a um erro no nosso código, ou um problema de compatibilidade do seu navegador.", "bundle_column_error.error.title": "Ah, não!", @@ -259,7 +263,7 @@ "confirmations.quiet_post_quote_info.message": "Ao citar uma publicação pública silenciosa, sua postagem será oculta das linhas de tempo em tendência.", "confirmations.quiet_post_quote_info.title": "Citando publicações públicas silenciadas", "confirmations.redraft.confirm": "Excluir e rascunhar", - "confirmations.redraft.message": "Você tem certeza de que quer apagar essa postagem e rascunhá-la? Favoritos e impulsos serão perdidos, e respostas à postagem original ficarão órfãs.", + "confirmations.redraft.message": "Você tem certeza de que quer apagar essa publicação e rascunhá-la? Favoritos e impulsos serão perdidos, e respostas à postagem original ficarão órfãs.", "confirmations.redraft.title": "Excluir e rascunhar publicação?", "confirmations.remove_from_followers.confirm": "Remover seguidor", "confirmations.remove_from_followers.message": "{name} vai parar de te seguir. Tem certeza de que deseja continuar?", @@ -293,7 +297,7 @@ "dismissable_banner.dismiss": "Dispensar", "dismissable_banner.public_timeline": "Estas são as publicações mais recentes das pessoas no fediverso que as pessoas do {domain} seguem.", "domain_block_modal.block": "Bloquear servidor", - "domain_block_modal.block_account_instead": "Bloquear @{name}", + "domain_block_modal.block_account_instead": "Bloquear @{name} em vez disso", "domain_block_modal.they_can_interact_with_old_posts": "Pessoas deste servidor podem interagir com suas publicações antigas.", "domain_block_modal.they_cant_follow": "Ninguém deste servidor pode lhe seguir.", "domain_block_modal.they_wont_know": "Eles não saberão que foram bloqueados.", @@ -452,7 +456,7 @@ "hints.profiles.see_more_follows": "Ver mais seguidos no {domain}", "hints.profiles.see_more_posts": "Ver mais publicações em {domain}", "home.column_settings.show_quotes": "Mostrar citações", - "home.column_settings.show_reblogs": "Mostrar boosts", + "home.column_settings.show_reblogs": "Mostrar impulsos", "home.column_settings.show_replies": "Mostrar respostas", "home.hide_announcements": "Ocultar comunicados", "home.pending_critical_update.body": "Por favor, atualize o seu servidor Mastodon o mais rápido possível!", @@ -484,7 +488,7 @@ "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}", "keyboard_shortcuts.back": "voltar", "keyboard_shortcuts.blocked": "abrir usuários bloqueados", - "keyboard_shortcuts.boost": "dar boost", + "keyboard_shortcuts.boost": "Impulsionar a publicação", "keyboard_shortcuts.column": "focar na coluna", "keyboard_shortcuts.compose": "focar no compositor", "keyboard_shortcuts.description": "Descrição", @@ -609,7 +613,7 @@ "notification.admin.report_statuses_other": "{name} denunciou {target}", "notification.admin.sign_up": "{name} se inscreveu", "notification.admin.sign_up.name_and_others": "{name} e {count, plural, one {# other} other {# outros}}", - "notification.annual_report.message": "O #Wrapstodon do seu {year} está esperando! Desvende seus destaques do ano e momentos memoráveis no Mastodon!", + "notification.annual_report.message": "O seu #Wrapstodon de {year} está esperando! Desvende seus destaques do ano e momentos memoráveis no Mastodon!", "notification.annual_report.view": "Ver #Wrapstodon", "notification.favourite": "{name} favoritou sua publicação", "notification.favourite.name_and_others_with_link": "{name} e {count, plural, one {# outro} other {# others}} favoritaram a publicação", @@ -638,7 +642,7 @@ "notification.own_poll": "Sua enquete terminou", "notification.poll": "Uma enquete que você votou terminou", "notification.quoted_update": "{name} editou uma pulicação que você citou", - "notification.reblog": "{name} deu boost no teu toot", + "notification.reblog": "{name} impulsionou sua publicação", "notification.reblog.name_and_others_with_link": "{name} e {count, plural, one {# outra} other {# outras}} impulsionaram a publicação", "notification.relationships_severance_event": "Conexões perdidas com {name}", "notification.relationships_severance_event.account_suspension": "Um administrador de {from} suspendeu {target}, o que significa que você não pode mais receber atualizações deles ou interagir com eles.", @@ -682,7 +686,7 @@ "notifications.column_settings.poll": "Enquetes:", "notifications.column_settings.push": "Notificações push", "notifications.column_settings.quote": "Citações:", - "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.reblog": "Impulsos:", "notifications.column_settings.show": "Mostrar na coluna", "notifications.column_settings.sound": "Tocar som", "notifications.column_settings.status": "Novos toots:", @@ -690,7 +694,7 @@ "notifications.column_settings.unread_notifications.highlight": "Destacar notificações não lidas", "notifications.column_settings.update": "Editar:", "notifications.filter.all": "Tudo", - "notifications.filter.boosts": "Boosts", + "notifications.filter.boosts": "Impulsionamentos", "notifications.filter.favourites": "Favoritos", "notifications.filter.follows": "Seguidores", "notifications.filter.mentions": "Menções", @@ -880,12 +884,12 @@ "status.admin_account": "Abrir interface de moderação para @{name}", "status.admin_domain": "Abrir interface de moderação para {domain}", "status.admin_status": "Abrir este toot na interface de moderação", - "status.all_disabled": "Acelerações e citações estão desabilitados", + "status.all_disabled": "Impulsos e citações estão desabilitados", "status.block": "Bloquear @{name}", "status.bookmark": "Salvar", - "status.cancel_reblog_private": "Desfazer boost", + "status.cancel_reblog_private": "Desfazer impulso", "status.cannot_quote": "Você não tem permissão para citar esta publicação", - "status.cannot_reblog": "Este toot não pode receber boost", + "status.cannot_reblog": "Esta publicação não pode ser impulsionada", "status.contains_quote": "Contém citação", "status.context.loading": "Carregando mais respostas", "status.context.loading_error": "Não foi possível carregar novas respostas", @@ -904,7 +908,7 @@ "status.edited": "Última edição em {date}", "status.edited_x_times": "Editado {count, plural, one {{count} hora} other {{count} vezes}}", "status.embed": "Obter código de incorporação", - "status.favourite": "Favorita", + "status.favourite": "Favoritar", "status.favourites_count": "{count, plural, one {{counter} favorito} other {{counter} favoritos}}", "status.filter": "Filtrar esta publicação", "status.history.created": "{name} criou {date}", @@ -942,12 +946,12 @@ "status.quotes.remote_other_disclaimer": "Apenas citações do {domain} têm a garantia de serem exibidas aqui. Citações rejeitadas pelo autor não serão exibidas.", "status.quotes_count": "{count, plural, one {{counter} citação} other {{counter} citações}}", "status.read_more": "Ler mais", - "status.reblog": "Dar boost", - "status.reblog_or_quote": "Dar boost ou citar", + "status.reblog": "Impulsionar", + "status.reblog_or_quote": "Impulsionar ou citar", "status.reblog_private": "Compartilhar novamente com seus seguidores", - "status.reblogged_by": "{name} deu boost", - "status.reblogs.empty": "Nada aqui. Quando alguém der boost, o usuário aparecerá aqui.", - "status.reblogs_count": "{count, plural, one {{counter} boost} other {{counter} boosts}}", + "status.reblogged_by": "{name} impulsionou", + "status.reblogs.empty": "Ninguém impulsionou esta publicação ainda. Quando alguém o fizer, o usuário aparecerá aqui.", + "status.reblogs_count": "{count, plural, one {{counter} impulso} other {{counter} impulsos}}", "status.redraft": "Excluir e rascunhar", "status.remove_bookmark": "Remover do Salvos", "status.remove_favourite": "Remover dos favoritos", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index d6ab3939e0c185..671930e673564b 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Descreve isto para pessoas com problemas de visão…", "alt_text_modal.done": "Concluído", "announcement.announcement": "Mensagem de manutenção", + "annual_report.announcement.action_build": "Criar o meu Wrapstodon", + "annual_report.announcement.action_view": "Ver o meu Wrapstodon", + "annual_report.announcement.description": "Descobre mais sobre o teu envolvimento com o Mastodon durante o último ano.", + "annual_report.announcement.title": "Chegou o Wrapstodon {year}", "annual_report.summary.archetype.booster": "O caçador de tendências", "annual_report.summary.archetype.lurker": "O espreitador", "annual_report.summary.archetype.oracle": "O oráculo", diff --git a/app/javascript/mastodon/locales/tok.json b/app/javascript/mastodon/locales/tok.json index 2f56042462438a..1c36079c7f3749 100644 --- a/app/javascript/mastodon/locales/tok.json +++ b/app/javascript/mastodon/locales/tok.json @@ -4,10 +4,10 @@ "about.default_locale": "ante ala", "about.disclaimer": "ilo Mastodon la jan ale li lawa e ona li pana e pona tawa ona. kulupu esun Mastodon gGmbH li lawa e nimi ona.", "about.domain_blocks.no_reason_available": "mi sona ala e tan", - "about.domain_blocks.preamble": "ilo Masoton li ken e ni: sina lukin e toki jan pi ma ilo mute. sina ken toki tawa ona lon kulupu ma. taso, ma ni li ken ala e ni tawa ma ni:", + "about.domain_blocks.preamble": "ilo Mastodon li ken e ni: sina lukin e toki jan pi ma ilo mute. sina ken toki tawa ona lon kulupu ma. taso, ma ni li ken ala e ni tawa ma ni:", "about.domain_blocks.silenced.explanation": "sina lukin ala e toki e jan tan ma ni. taso, sina wile la, sina ken ni.", "about.domain_blocks.silenced.title": "ken lukin lili ", - "about.domain_blocks.suspended.explanation": "sona ale pi ma ni li kama pali ala, li kama esun ala, li kama awen ala la sina ken ala toki tawa jan pi ma ni.", + "about.domain_blocks.suspended.explanation": "sona ale pi ma ni li kama pali ala, li kama esun ala, li kama awen ala la, sina ken ala toki tawa jan pi ma ni.", "about.domain_blocks.suspended.title": "weka", "about.language_label": "toki", "about.not_available": "lon kulupu ni la sina ken alasa ala e sona ni.", @@ -24,12 +24,12 @@ "account.blocking": "mi len e jan ni", "account.cancel_follow_request": "o kute ala", "account.copy": "o pali same e linja pi lipu jan", - "account.direct": "len la o mu e @{name}", + "account.direct": "o mu len e @{name}", "account.disable_notifications": "@{name} li toki la o mu ala e mi", "account.domain_blocking": "mi len e ma ni", "account.edit_profile": "o ante e lipu mi", "account.edit_profile_short": "o ante", - "account.enable_notifications": "@{name} li toki la o toki e toki ona tawa mi", + "account.enable_notifications": "@{name} li toki la o mu e mi", "account.endorse": "lipu jan la o suli e ni", "account.familiar_followers_many": "{name1} en {name2} en {othersCount, plural, other {jan ante #}} li kute e jan ni", "account.familiar_followers_one": "{name1} li kute e jan ni", @@ -41,10 +41,10 @@ "account.featured_tags.last_status_never": "toki ala li lon", "account.follow": "o kute", "account.follow_back": "jan ni li kute e sina. o kute", - "account.follow_back_short": "jan ni li kute e sina. o kute", - "account.follow_request": "toki e wile kute", - "account.follow_request_cancel": "toki ala e wile kute", - "account.follow_request_cancel_short": "ala", + "account.follow_back_short": "o kute", + "account.follow_request": "o wile kute", + "account.follow_request_cancel": "o wile ala kute", + "account.follow_request_cancel_short": "o wile ala kute", "account.followers": "jan kute", "account.followers.empty": "jan ala li kute e jan ni", "account.followers_counter": "{count, plural, other {jan {counter} li kute e ona}}", @@ -120,17 +120,17 @@ "annual_report.summary.followers.followers": "jan kute sina", "annual_report.summary.followers.total": "ale la {count}", "annual_report.summary.here_it_is": "toki lili la tenpo sike nanpa {year} li sama ni tawa sina:", - "annual_report.summary.highlighted_post.by_favourites": "toki pi pona suli", - "annual_report.summary.highlighted_post.by_reblogs": "toki pi pana suli", - "annual_report.summary.highlighted_post.by_replies": "toki li jo e toki kama pi nanpa wan", - "annual_report.summary.highlighted_post.possessive": "tan jan {name}", + "annual_report.summary.highlighted_post.by_favourites": "toki ni li pona suli tawa jan", + "annual_report.summary.highlighted_post.by_reblogs": "jan ante li pana suli e toki ni", + "annual_report.summary.highlighted_post.by_replies": "la jan ante li toki suli tan toki ni", + "annual_report.summary.highlighted_post.possessive": "{name}", "annual_report.summary.most_used_app.most_used_app": "ilo pi kepeken suli", "annual_report.summary.most_used_hashtag.most_used_hashtag": "kulupu toki pi kepeken suli", "annual_report.summary.most_used_hashtag.none": "ala", "annual_report.summary.new_posts.new_posts": "toki suli sin", "annual_report.summary.percentile.text": "ni la sina nanpa sewipi jan ale lon {domain}.", "annual_report.summary.percentile.we_wont_tell_bernie": "sona ni li len tawa ale.", - "annual_report.summary.thanks": "sina jan pi kulupu Mastodon la sina pona a!", + "annual_report.summary.thanks": "sina lon kulupu Mastodon la sina pona a!", "attachments_list.unprocessed": "(nasin open)", "audio.hide": "o len e kalama", "block_modal.remote_users_caveat": "mi pana e wile sina tawa ma {domain}. taso, o sona: ma li ken kepeken nasin len ante la pakala li ken lon. toki pi lukin ale la jan pi ma ala li ken lukin.", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 21f2b7ed6332da..c674509368fc47 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Bunu görme bozukluğu yaşayan kişiler için betimleyin…", "alt_text_modal.done": "Tamamlandı", "announcement.announcement": "Duyuru", + "annual_report.announcement.action_build": "Wrapstodon'umu Oluştur", + "annual_report.announcement.action_view": "Wrapstodon'umu Görüntüle", + "annual_report.announcement.description": "Mastodon'daki geçen yıldaki etkileşimleriniz hakkında daha fazlasını öğrenin.", + "annual_report.announcement.title": "{year} Wrapstodon'u yayında", "annual_report.summary.archetype.booster": "Trend takipçisi", "annual_report.summary.archetype.lurker": "Gizli meraklı", "annual_report.summary.archetype.oracle": "Kahin", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 5cc47ccf0478fe..5f4887267b03e0 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Mô tả cho người khiếm thị…", "alt_text_modal.done": "Xong", "announcement.announcement": "Có gì mới?", + "annual_report.announcement.action_build": "Tạo Wrapstodon của tôi", + "annual_report.announcement.action_view": "Xem Wrapstodon của tôi", + "annual_report.announcement.description": "Tìm hiểu thêm về mức độ tương tác của bạn trên Mastodon trong năm qua.", + "annual_report.announcement.title": "Wrapstodon {year} đã đến", "annual_report.summary.archetype.booster": "Hiệp sĩ ngầu", "annual_report.summary.archetype.lurker": "Kẻ rình mò", "annual_report.summary.archetype.oracle": "Nhà tiên tri", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index a0284620f5858e..1f65a846add681 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "请为视力障碍人士描述此内容…", "alt_text_modal.done": "完成", "announcement.announcement": "公告", + "annual_report.announcement.action_build": "构建我的 Wrapstodon 年度回顾", + "annual_report.announcement.action_view": "查看我的 Wrapstodon 年度回顾", + "annual_report.announcement.description": "探索更多关于您过去一年在 Mastodon 上的互动情况。", + "annual_report.announcement.title": "Wrapstodon {year} 年度回顾来啦", "annual_report.summary.archetype.booster": "潮流捕手", "annual_report.summary.archetype.lurker": "吃瓜群众", "annual_report.summary.archetype.oracle": "未卜先知", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index ff8f6e357d8f60..27a057c28b33f7 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -113,6 +113,10 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "替視覺障礙人士描述...", "alt_text_modal.done": "完成", "announcement.announcement": "公告", + "annual_report.announcement.action_build": "建立我的 Mastodon 年度回顧 (Wrapstodon)", + "annual_report.announcement.action_view": "檢視我的 Mastodon 年度回顧 (Wrapstodon)", + "annual_report.announcement.description": "探索更多關於您過去一年於 Mastodon 上的互動情況。", + "annual_report.announcement.title": "您的 Mastodon 年度回顧 {year} 已抵達", "annual_report.summary.archetype.booster": "酷炫獵人", "annual_report.summary.archetype.lurker": "潛水高手", "annual_report.summary.archetype.oracle": "先知", diff --git a/config/locales/activerecord.fa.yml b/config/locales/activerecord.fa.yml index c25ef6495967fb..a02395ba6cc6a1 100644 --- a/config/locales/activerecord.fa.yml +++ b/config/locales/activerecord.fa.yml @@ -32,6 +32,12 @@ fa: attributes: url: invalid: نشانی معتبری نیست + collection: + attributes: + collection_items: + too_many: بیش از حد. بیش از %{count} مجاز نیست + tag: + unusable: ممکن است استفاده نشود doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.lt.yml b/config/locales/activerecord.lt.yml index 1eec2782f4ae8e..001243ab901c0b 100644 --- a/config/locales/activerecord.lt.yml +++ b/config/locales/activerecord.lt.yml @@ -6,7 +6,7 @@ lt: expires_at: Galutinė data options: Pasirinkimai user: - agreement: Paslaugos sutartis + agreement: Paslaugų sąlygos email: El. laiško adresas locale: Lokali password: Slaptažodis @@ -32,6 +32,12 @@ lt: attributes: url: invalid: nėra tinkamas URL adresas. + collection: + attributes: + collection_items: + too_many: yra per daug, leidžiama ne daugiau kaip %{count} + tag: + unusable: negali būti panaudota doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.nl.yml b/config/locales/activerecord.nl.yml index b05d6680e73e81..0d6070ce619425 100644 --- a/config/locales/activerecord.nl.yml +++ b/config/locales/activerecord.nl.yml @@ -32,6 +32,12 @@ nl: attributes: url: invalid: is een ongeldige URL + collection: + attributes: + collection_items: + too_many: zijn er te veel, niet meer dan %{count} zijn toegestaan + tag: + unusable: mag niet worden gebruikt doorkeeper/application: attributes: website: diff --git a/config/locales/activerecord.nn.yml b/config/locales/activerecord.nn.yml index ae73057dcc4ae4..c5e3acf2ab6843 100644 --- a/config/locales/activerecord.nn.yml +++ b/config/locales/activerecord.nn.yml @@ -32,6 +32,12 @@ nn: attributes: url: invalid: er ikkje ein gyldig URL + collection: + attributes: + collection_items: + too_many: er for mange, du kan ikkje ha meir enn %{count} + tag: + unusable: kan ikkje brukast doorkeeper/application: attributes: website: diff --git a/config/locales/da.yml b/config/locales/da.yml index 1a900bb84915aa..caddba755d70a2 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1706,16 +1706,22 @@ da: body: 'Du blev nævnt af %{name} i:' subject: Du blev nævnt af %{name} title: Ny omtale + moderation_warning: + subject: Du har fået en moderationsadvarsel poll: subject: En afstemning fra %{name} er afsluttet quote: body: 'Dit indlæg blev citeret af %{name}:' subject: "%{name} citerede dit indlæg" title: Nyt citat + quoted_update: + subject: "%{name} redigerede et indlæg, du har citeret" reblog: body: 'Dit indlæg blev fremhævet af %{name}:' subject: "%{name} fremhævede dit indlæg" title: Ny fremhævelse + severed_relationships: + subject: Du har mistet forbindelser på grund af en moderationsbeslutning status: subject: "%{name} har netop postet" update: diff --git a/config/locales/de.yml b/config/locales/de.yml index 2251de86586223..d9473031c83be7 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -33,7 +33,7 @@ de: created_msg: Moderationshinweis erfolgreich abgespeichert! destroyed_msg: Moderationsnotiz erfolgreich entfernt! accounts: - add_email_domain_block: E-Mail-Domain sperren + add_email_domain_block: E-Mail-Domain blockieren approve: Genehmigen approved_msg: Antrag zur Registrierung von %{username} erfolgreich genehmigt are_you_sure: Bist du dir sicher? @@ -154,10 +154,10 @@ de: subscribe: Abonnieren suspend: Sperren suspended: Gesperrt - suspension_irreversible: Die Daten dieses Kontos wurden unwiderruflich gelöscht. Du kannst das Konto entsperren, um es wieder zu verwenden, aber es wird keine Daten wiederherstellen, die es davor hatte. + suspension_irreversible: Die Daten dieses Kontos wurden unwiderruflich gelöscht. Du kannst die Kontosperrung aufheben, damit es wieder aktiv ist und funktioniert, aber es werden keine früheren Daten wiederhergestellt. suspension_reversible_hint_html: Das Konto wurde gesperrt und die Daten werden am %{date} vollständig gelöscht. Bis dahin kann das Konto ohne irgendwelche negativen Auswirkungen wiederhergestellt werden. Wenn du alle Daten des Kontos sofort entfernen möchtest, kannst du das nachfolgend tun. title: Konten - unblock_email: E-Mail-Adresse entsperren + unblock_email: Blockierung der E-Mail-Adresse aufheben unblocked_email_msg: E-Mail-Adresse von %{username} erfolgreich entsperrt unconfirmed_email: Unbestätigte E-Mail-Adresse undo_sensitized: Inhaltswarnung aufheben @@ -184,7 +184,7 @@ de: create_canonical_email_block: E-Mail-Sperre erstellen create_custom_emoji: Eigene Emojis erstellen create_domain_allow: Domain erlauben - create_domain_block: Domain sperren + create_domain_block: Domain blockieren create_email_domain_block: E-Mail-Domain-Sperre erstellen create_ip_block: IP-Regel erstellen create_relay: Relais erstellen @@ -218,20 +218,20 @@ de: promote_user: Benutzer*in hochstufen publish_terms_of_service: Nutzungsbedingungen veröffentlichen reject_appeal: Einspruch ablehnen - reject_user: Benutzer*in ablehnen + reject_user: Profil ablehnen remove_avatar_user: Profilbild entfernen reopen_report: Meldung wieder eröffnen - resend_user: Bestätigungs-E-Mail erneut senden + resend_user: Bestätigungs-E-Mail erneut zuschicken reset_password_user: Passwort zurücksetzen resolve_report: Meldung klären sensitive_account: Konto mit erzwungener Inhaltswarnung silence_account: Konto stummschalten suspend_account: Konto sperren unassigned_report: Meldung widerrufen - unblock_email_account: E-Mail-Adresse entsperren + unblock_email_account: Blockierung der E-Mail-Adresse aufheben unsensitive_account: Konto mit erzwungener Inhaltswarnung rückgängig machen unsilence_account: Konto nicht mehr stummschalten - unsuspend_account: Konto entsperren + unsuspend_account: Kontosperre aufheben update_announcement: Ankündigung aktualisieren update_custom_emoji: Eigenes Emoji aktualisieren update_domain_block: Domain-Sperre aktualisieren @@ -241,7 +241,7 @@ de: update_user_role: Rolle bearbeiten update_username_block: Regel für Profilnamen aktualisieren actions: - approve_appeal_html: "%{name} hat den Einspruch gegen eine Moderationsentscheidung von %{target} genehmigt" + approve_appeal_html: "%{name} genehmigte den Einspruch gegen eine Moderationsentscheidung von %{target}" approve_user_html: "%{name} genehmigte die Registrierung von %{target}" assigned_to_self_report_html: "%{name} wies sich die Meldung %{target} selbst zu" change_email_user_html: "%{name} änderte die E-Mail-Adresse von %{target}" @@ -255,7 +255,7 @@ de: create_domain_block_html: "%{name} sperrte die Domain %{target}" create_email_domain_block_html: "%{name} sperrte die E-Mail-Domain %{target}" create_ip_block_html: "%{name} erstellte eine IP-Regel für %{target}" - create_relay_html: "%{name} erstellte ein Relay %{target}" + create_relay_html: "%{name} erstellte ein Relais %{target}" create_unavailable_domain_html: "%{name} beendete die Zustellung an die Domain %{target}" create_user_role_html: "%{name} erstellte die Rolle %{target}" create_username_block_html: "%{name} erstellte eine Regel für Profilnamen mit %{target}" @@ -285,7 +285,7 @@ de: memorialize_account_html: "%{name} wandelte das Konto von %{target} in eine Gedenkseite um" promote_user_html: "%{name} beförderte %{target}" publish_terms_of_service_html: "%{name} aktualisierte die Nutzungsbedingungen" - reject_appeal_html: "%{name} hat den Einspruch gegen eine Moderationsentscheidung von %{target} abgelehnt" + reject_appeal_html: "%{name} lehnte den Einspruch gegen eine Moderationsentscheidung von %{target} ab" reject_user_html: "%{name} hat die Registrierung von %{target} abgelehnt" remove_avatar_user_html: "%{name} entfernte das Profilbild von %{target}" reopen_report_html: "%{name} öffnete die Meldung %{target} wieder" @@ -296,14 +296,14 @@ de: silence_account_html: "%{name} schaltete das Konto von %{target} stumm" suspend_account_html: "%{name} sperrte das Konto von %{target}" unassigned_report_html: "%{name} entfernte die Zuweisung der Meldung %{target}" - unblock_email_account_html: "%{name} hat die E-Mail-Adresse von %{target} entsperrt" - unsensitive_account_html: "%{name} hat die Inhaltswarnung für Medien von %{target} aufgehoben" + unblock_email_account_html: "%{name} entsperrte die E-Mail-Adresse von %{target}" + unsensitive_account_html: "%{name} hob die Inhaltswarnung für Medien von %{target} auf" unsilence_account_html: "%{name} hob die Stummschaltung von %{target} auf" unsuspend_account_html: "%{name} entsperrte das Konto von %{target}" update_announcement_html: "%{name} überarbeitete die Ankündigung %{target}" update_custom_emoji_html: "%{name} bearbeitete das Emoji %{target}" update_domain_block_html: "%{name} aktualisierte die Domain-Sperre für %{target}" - update_ip_block_html: "%{name} änderte die Regel für die IP-Adresse %{target}" + update_ip_block_html: "%{name} änderte eine IP-Regel für %{target}" update_report_html: "%{name} überarbeitete die Meldung %{target}" update_status_html: "%{name} überarbeitete einen Beitrag von %{target}" update_user_role_html: "%{name} änderte die Rolle von %{target}" @@ -366,7 +366,7 @@ de: shortcode_hint: Mindestens 2 Zeichen, nur Buchstaben, Ziffern und Unterstriche title: Emojis uncategorized: Unkategorisiert - unlist: Nicht anzeigen + unlist: Ausblenden unlisted: Nicht sichtbar update_failed_msg: Konnte dieses Emoji nicht bearbeiten updated_msg: Emoji erfolgreich bearbeitet! @@ -522,7 +522,7 @@ de: status: Status suppress: Folgeempfehlung unterbinden suppressed: Unterdrückt - title: Folgeempfehlungen + title: Follower-Empfehlungen unsuppress: Folgeempfehlung nicht mehr unterbinden instances: audit_log: @@ -825,7 +825,7 @@ de: preamble: Passe das Webinterface von Mastodon an. title: Design branding: - preamble: Das Branding deines Servers unterscheidet ihn von anderen Servern im Netzwerk. Diese Informationen können in einer Vielzahl von Umgebungen angezeigt werden, z. B. in der Weboberfläche von Mastodon, in nativen Anwendungen, in Linkvorschauen auf anderen Websites und in Messaging-Apps und so weiter. Aus diesem Grund ist es am besten, diese Informationen klar, kurz und prägnant zu halten. + preamble: Das Branding deines Servers unterscheidet ihn von anderen Servern im Netzwerk. Diese Informationen können in einer Vielzahl von Umgebungen angezeigt werden, z. B. im Webinterface von Mastodon, in nativen Anwendungen, in der Linkvorschau auf anderen Websites, in Messaging-Apps und so weiter. Aus diesem Grund ist es am besten, diese Informationen klar, kurz und prägnant zu halten. title: Branding captcha_enabled: desc_html: Dies beruht auf externen Skripten von hCaptcha, die ein Sicherheits- und Datenschutzproblem darstellen könnten. Darüber hinaus kann das den Registrierungsprozess für manche Menschen (insbesondere für Menschen mit Behinderung) erheblich erschweren. Aus diesen Gründen solltest du alternative Maßnahmen in Betracht ziehen, z. B. eine Registrierung basierend auf einer Einladung oder auf Genehmigungen. @@ -1706,12 +1706,16 @@ de: body: 'Du wurdest von %{name} erwähnt:' subject: "%{name} erwähnte dich" title: Neue Erwähnung + moderation_warning: + subject: Die Moderator*innen haben dich verwarnt poll: subject: Eine Umfrage von %{name} ist beendet quote: body: 'Dein Beitrag wurde von %{name} zitiert:' subject: "%{name} zitierte deinen Beitrag" title: Neuer zitierter Beitrag + quoted_update: + subject: "%{name} bearbeitete einen von dir zitierten Beitrag" reblog: body: 'Dein Beitrag wurde von %{name} geteilt:' subject: "%{name} teilte deinen Beitrag" diff --git a/config/locales/devise.fi.yml b/config/locales/devise.fi.yml index d2465fe1acb2b7..0b256f85c0a238 100644 --- a/config/locales/devise.fi.yml +++ b/config/locales/devise.fi.yml @@ -47,7 +47,7 @@ fi: explanation: Pyysit tilillesi uuden salasanan. extra: Jos et tehnyt pyyntöä itse, voit jättää tämän viestin huomiotta. Salasanaasi ei vaihdeta, ennen kuin painat edellä olevaa linkkiä ja luot uuden salasanan. subject: 'Mastodon: ohjeet salasanan vaihtoon' - title: Salasanan vaihto + title: Salasanan palautus two_factor_disabled: explanation: Sisäänkirjautuminen on nyt mahdollista pelkällä sähköpostiosoitteella ja salasanalla. subject: 'Mastodon: kaksivaiheinen todennus poistettu käytöstä' diff --git a/config/locales/el.yml b/config/locales/el.yml index 47a558ada79f77..259a3f7ff1c989 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -1706,16 +1706,22 @@ el: body: 'Επισημάνθηκες από τον/την %{name} στο:' subject: Επισημάνθηκες από τον/την %{name} title: Νέα επισήμανση + moderation_warning: + subject: Έχετε λάβει μία προειδοποίηση συντονισμού poll: subject: Μια δημοσκόπηση του %{name} έληξε quote: body: 'Η ανάρτησή σου παρατέθηκε από %{name}:' subject: Ο/Η %{name} έκανε παράθεση της ανάρτησής σου title: Νέα παράθεση + quoted_update: + subject: Ο χρήστης %{name} επεξεργάστηκε μία ανάρτηση που παρέθεσες reblog: body: 'Η ανάρτησή σου ενισχύθηκε από τον/την %{name}:' subject: Ο/Η %{name} ενίσχυσε την ανάρτηση σου title: Νέα ενίσχυση + severed_relationships: + subject: Έχετε χάσει συνδέσεις λόγω μιας απόφασης συντονισμού status: subject: Ο/Η %{name} μόλις ανέρτησε κάτι update: diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 3d9f3722c560ec..4a921bedc23105 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -1706,16 +1706,22 @@ en-GB: body: 'You were mentioned by %{name} in:' subject: You were mentioned by %{name} title: New mention + moderation_warning: + subject: You have received a moderation warning poll: subject: A poll by %{name} has ended quote: body: 'Your post was quoted by %{name}:' subject: "%{name} quoted your post" title: New quote + quoted_update: + subject: "%{name} edited a post you have quoted" reblog: body: 'Your post was boosted by %{name}:' subject: "%{name} boosted your post" title: New boost + severed_relationships: + subject: You have lost connections due to a moderation decision status: subject: "%{name} just posted" update: diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index a2f3e7dcfd60e2..da522bee6a4bd7 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -1706,16 +1706,22 @@ es-AR: body: 'Fuiste mencionado por %{name} en:' subject: Fuiste mencionado por %{name} title: Nueva mención + moderation_warning: + subject: Recibiste una advertencia de moderación poll: subject: Terminó una encuesta de %{name} quote: body: 'Tu mensaje fue citado por %{name}:' subject: "%{name} citó tu mensaje" title: Nueva cita + quoted_update: + subject: "%{name} editó un mensaje que citaste" reblog: body: "%{name} adhirió a tu mensaje:" subject: "%{name} adhirió a tu mensaje" title: Nueva adhesión + severed_relationships: + subject: Perdiste seguidores y/o cuentas seguidas, debido a una decisión de moderación status: subject: "%{name} acaba de enviar un mensaje" update: diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index b39844b1945066..7b67ef01ff689e 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -1706,12 +1706,16 @@ es-MX: body: 'Fuiste mencionado por %{name} en:' subject: Fuiste mencionado por %{name} title: Nueva mención + moderation_warning: + subject: Has recibido una advertencia de moderación poll: subject: Una encuesta de %{name} ha terminado quote: body: 'Tu publicación fue citada por %{name}:' subject: "%{name} citó tu publicación" title: Nueva cita + quoted_update: + subject: "%{name} editó una publicación que has citado" reblog: body: 'Tu publicación fue impulsada por %{name}:' subject: "%{name} ha impulsado tu publicación" diff --git a/config/locales/es.yml b/config/locales/es.yml index 9dcb73e0061fdc..c7268deb92415d 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1706,12 +1706,16 @@ es: body: 'Fuiste mencionado por %{name} en:' subject: Fuiste mencionado por %{name} title: Nueva mención + moderation_warning: + subject: Has recibido una advertencia de moderación poll: subject: Una encuesta de %{name} ha terminado quote: body: 'Tu publicación ha sido citada por %{name}:' subject: "%{name} citó tu publicación" title: Nueva cita + quoted_update: + subject: "%{name} editó una publicación que has citado" reblog: body: 'Tu publicación fue impulsada por %{name}:' subject: "%{name} impulsó tu publicación" diff --git a/config/locales/fa.yml b/config/locales/fa.yml index eec1dd5048ac6f..ab5742a9fcae0a 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -1706,16 +1706,22 @@ fa: body: "%{name} در این‌جا به شما اشاره کرد:" subject: "%{name} به شما اشاره کرد" title: اشارهٔ جدید + moderation_warning: + subject: هشداری مدیریتی گرفته‌اید poll: subject: نظرسنجی‌ای از %{name} پایان یافت quote: body: 'فرسته‌تان توسط %{name} نقل شد:' subject: "%{name} فرسته‌تان را نقل کرد" title: نقل‌قول جدید + quoted_update: + subject: "‏%{name} فرسته‌ای که نقل کردید را ویراست" reblog: body: "%{name} فرستهٔ شما را تقویت کرد:" subject: "%{name} فرستهٔ شما را تقویت کرد" title: تقویت تازه + severed_relationships: + subject: بنا به تصمیمی مدیریتی ارتباطاتی را از دست داده‌اید status: subject: "%{name} چیزی فرستاد" update: diff --git a/config/locales/fi.yml b/config/locales/fi.yml index a963d57e728705..cc6c80fb0899fd 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -48,7 +48,7 @@ fi: title: Vaihda käyttäjän %{username} sähköposti-osoite change_role: changed_msg: Roolin vaihto onnistui! - edit_roles: Hallinnoi käyttäjien rooleja + edit_roles: Hallitse käyttäjärooleja label: Vaihda rooli no_role: Ei roolia title: Vaihda käyttäjän %{username} rooli @@ -76,10 +76,10 @@ fi: followers: Seuraajat follows: Seurattavat header: Otsakekuva - inbox_url: Postilaatikon osoite + inbox_url: Postilaatikon URL-⁠osoite invite_request_text: Syitä liittymiseen invited_by: Kutsuja - ip: IP-osoite + ip: IP-⁠osoite joined: Liittynyt location: all: Kaikki @@ -101,7 +101,7 @@ fi: title: Moderointi moderation_notes: Moderointimuistiinpanot most_recent_activity: Viimeisin toiminta - most_recent_ip: Viimeisin IP-osoite + most_recent_ip: Viimeisin IP-⁠osoite no_account_selected: Tilejä ei muutettu, koska yhtään ei ollut valittuna no_limits_imposed: Ei asetettuja rajoituksia no_role_assigned: Roolia ei asetettu @@ -124,7 +124,7 @@ fi: remote_suspension_reversible_hint_html: Tili on jäädytetty omalla palvelimellaan, ja kaikki tiedot poistetaan %{date}. Sitä ennen etäpalvelin voi palauttaa tilin ongelmitta. Jos haluat poistaa kaikki tilin tiedot heti, onnistuu se alta. remove_avatar: Poista profiilikuva remove_header: Poista otsakekuva - removed_avatar_msg: Käyttäjän %{username} avatar-kuva poistettiin onnistuneesti + removed_avatar_msg: Käyttäjän %{username} profiilikuva poistettiin onnistuneesti removed_header_msg: Käyttäjän %{username} otsakekuva poistettiin onnistuneesti resend_confirmation: already_confirmed: Tämä käyttäjä on jo vahvistettu @@ -136,14 +136,14 @@ fi: role: Rooli search: Hae search_same_email_domain: Muut käyttäjät, joilla on sama sähköpostiverkkotunnus - search_same_ip: Muut käyttäjät, joilla on sama IP-osoite + search_same_ip: Muut käyttäjät, joilla on sama IP-⁠osoite security: Turvallisuus security_measures: only_password: Vain salasana password_and_2fa: Salasana ja kaksivaiheinen todennus sensitive: Pakota arkaluonteiseksi sensitized: Merkitty arkaluonteiseksi - shared_inbox_url: Jaetun postilaatikon osoite + shared_inbox_url: Jaetun postilaatikon URL-⁠osoite show: created_reports: Tämän tilin luomat raportit targeted_reports: Tästä tilistä tehdyt raportit @@ -186,7 +186,7 @@ fi: create_domain_allow: Luo verkkotunnuksen salliminen create_domain_block: Luo verkkotunnuksen esto create_email_domain_block: Luo sähköpostiverkkotunnuksen esto - create_ip_block: Luo IP-sääntö + create_ip_block: Luo IP-⁠sääntö create_relay: Luo välittäjä create_unavailable_domain: Luo ei-saatavilla oleva verkkotunnus create_user_role: Luo rooli @@ -199,7 +199,7 @@ fi: destroy_domain_block: Poista verkkotunnuksen esto destroy_email_domain_block: Poista sähköpostiverkkotunnuksen esto destroy_instance: Tyhjennä verkkotunnus - destroy_ip_block: Poista IP-sääntö + destroy_ip_block: Poista IP-⁠sääntö destroy_relay: Poista välittäjä destroy_status: Poista julkaisu destroy_unavailable_domain: Poista ei-saatavilla oleva verkkotunnus @@ -224,18 +224,18 @@ fi: resend_user: Lähetä vahvistusviesti uudelleen reset_password_user: Palauta salasana resolve_report: Ratkaise raportti - sensitive_account: Pakota arkaluonteiseksi tiliksi + sensitive_account: Pakota tili arkaluonteiseksi silence_account: Rajoita tiliä suspend_account: Jäädytä tili unassigned_report: Poista raportti käsittelystä unblock_email_account: Kumoa sähköpostiosoitteen esto - unsensitive_account: Kumoa pakotus arkaluonteiseksi tiliksi + unsensitive_account: Kumoa tilin pakotus arkaluonteiseksi unsilence_account: Kumoa tilin rajoitus unsuspend_account: Kumoa tilin jäädytys update_announcement: Päivitä tiedote update_custom_emoji: Päivitä mukautettu emoji update_domain_block: Päivitä verkkotunnuksen esto - update_ip_block: Päivitä IP-sääntö + update_ip_block: Päivitä IP-⁠sääntö update_report: Päivitä raportti update_status: Päivitä julkaisu update_user_role: Päivitä rooli @@ -254,7 +254,7 @@ fi: create_domain_allow_html: "%{name} salli federoinnin verkkotunnuksen %{target} kanssa" create_domain_block_html: "%{name} esti verkkotunnuksen %{target}" create_email_domain_block_html: "%{name} esti sähköpostiverkkotunnuksen %{target}" - create_ip_block_html: "%{name} loi säännön IP-osoitteelle %{target}" + create_ip_block_html: "%{name} loi säännön IP-⁠osoitteelle %{target}" create_relay_html: "%{name} loi välittäjän %{target}" create_unavailable_domain_html: "%{name} pysäytti toimituksen verkkotunnukseen %{target}" create_user_role_html: "%{name} loi roolin %{target}" @@ -267,7 +267,7 @@ fi: destroy_domain_block_html: "%{name} kumosi verkkotunnuksen %{target} eston" destroy_email_domain_block_html: "%{name} kumosi sähköpostiverkkotunnuksen %{target} eston" destroy_instance_html: "%{name} tyhjensi verkkotunnuksen %{target}" - destroy_ip_block_html: "%{name} poisti säännön IP-osoitteelta %{target}" + destroy_ip_block_html: "%{name} poisti säännön IP-⁠osoitteelta %{target}" destroy_relay_html: "%{name} poisti välittäjän %{target}" destroy_status_html: "%{name} poisti käyttäjän %{target} julkaisun" destroy_unavailable_domain_html: "%{name} jatkoi toimitusta verkkotunnukseen %{target}" @@ -303,7 +303,7 @@ fi: update_announcement_html: "%{name} päivitti tiedotteen %{target}" update_custom_emoji_html: "%{name} päivitti emojin %{target}" update_domain_block_html: "%{name} päivitti verkkotunnuksen %{target} eston" - update_ip_block_html: "%{name} muutti IP-osoitteen %{target} sääntöä" + update_ip_block_html: "%{name} muutti IP-⁠osoitteen %{target} sääntöä" update_report_html: "%{name} päivitti raportin %{target}" update_status_html: "%{name} päivitti käyttäjän %{target} julkaisun" update_user_role_html: "%{name} muutti roolia %{target}" @@ -330,8 +330,8 @@ fi: title: Esikatsele tiedoteilmoitus publish: Julkaise published_msg: Tiedotteen julkaisu onnistui! - scheduled_for: Ajoitettu %{time} - scheduled_msg: Tiedotteen julkaisu ajoitettu! + scheduled_for: Ajastettu %{time} + scheduled_msg: Tiedotteen julkaisu ajastettu! title: Tiedotteet unpublish: Lopeta julkaisu unpublished_msg: Tiedotteen julkaisun lopetus onnistui! @@ -362,8 +362,8 @@ fi: no_emoji_selected: Emojeita ei muutettu, koska yhtään ei ollut valittuna not_permitted: Sinulla ei ole oikeutta suorittaa tätä toimintoa overwrite: Korvaa - shortcode: Lyhennekoodi - shortcode_hint: Vähintään 2 merkkiä, vain kirjaimia, numeroita ja alaviivoja + shortcode: Lyhytkoodi + shortcode_hint: Vähintään 2 merkkiä; vain kirjaimia, numeroita ja alaviivoja title: Mukautetut emojit uncategorized: Luokittelematon unlist: Poista listasta @@ -637,7 +637,7 @@ fi: enable: Ota käyttöön enable_hint: Kun tämä on otettu käyttöön, palvelimesi tilaa välittäjältä kaikki sen välittämät julkiset julkaisut ja alkaa lähettää omansa sille. enabled: Käytössä - inbox_url: Välittäjän URL + inbox_url: Välittäjän URL-⁠osoite pending: Odotetaan välittäjän hyväksyntää save_and_enable: Tallenna ja ota käyttöön setup: Määritä yhteys välittäjään @@ -748,7 +748,7 @@ fi: moderation: Moderointi special: Erityistä delete: Poista - description_html: "Käyttäjärooleilla voit mukauttaa, mihin Mastodonin toimintoihin ja alueisiin käyttäjäsi on pääsy." + description_html: "Käyttäjärooleilla voit mukauttaa sitä, mihin Mastodonin toimintoihin ja alueisiin käyttäjilläsi on pääsy." edit: Muokkaa roolia ”%{name}” everyone: Oletuskäyttöoikeudet everyone_full_description_html: Tämä on perusrooli, joka vaikuttaa kaikkiin käyttäjiin, jopa ilman asetettua roolia. Kaikki muut roolit perivät sen käyttöoikeudet. @@ -769,7 +769,7 @@ fi: manage_blocks: Hallita estoja manage_blocks_description: Sallii käyttäjien estää sähköpostipalveluntarjoajia ja IP-osoitteita manage_custom_emojis: Hallita mukautettuja emojeita - manage_custom_emojis_description: Sallii käyttäjien hallita mukautettuja emojeita palvelimella + manage_custom_emojis_description: Sallii käyttäjien hallita palvelimen mukautettuja emojeita manage_federation: Hallita federointia manage_federation_description: Sallii käyttäjien estää tai sallia federointi muiden verkkotunnusten kanssa ja hallita toimitusta manage_invites: Hallita kutsuja @@ -784,7 +784,7 @@ fi: manage_settings_description: Sallii käyttäjien muuttaa sivuston asetuksia manage_taxonomies: Hallita luokittelua manage_taxonomies_description: Sallii käyttäjien tarkistaa suositun sisällön ja päivittää aihetunnisteiden asetuksia - manage_user_access: Hallita käyttäjäoikeuksia + manage_user_access: Hallita käyttäjien oikeuksia manage_user_access_description: Sallii käyttäjien poistaa muiden käyttäjien kaksivaiheinen todennus käytöstä, vaihtaa heidän sähköpostiosoitteensa ja palauttaa heidän salasanansa manage_users: Hallita käyttäjiä manage_users_description: Sallii käyttäjien tarkastella muiden käyttäjien tietoja ja suorittaa moderointitoimia heitä kohtaan @@ -894,7 +894,7 @@ fi: statuses: account: Tekijä application: Sovellus - back_to_account: Takaisin tilin sivulle + back_to_account: Takaisin tilisivulle back_to_report: Takaisin raporttisivulle batch: add_to_report: Lisää raporttiin nro %{id} @@ -1412,7 +1412,7 @@ fi: not_found_multiple: käyttäjänimiä %{usernames} ei löytynyt exports: archive_takeout: - date: Päiväys + date: Päivämäärä download: Lataa arkisto hint_html: Voit pyytää arkistoa omista julkaisuista ja mediasta. Viedyt tiedot ovat ActivityPub-muodossa, ja ne voi lukea millä tahansa yhteensopivalla ohjelmalla. Voit pyytää arkistoa 7 päivän välein. in_progress: Arkistoa kootaan… @@ -1468,7 +1468,7 @@ fi: statuses: back_to_filter: Takaisin suodattimeen batch: - remove: Poista suodattimista + remove: Poista suodattimesta index: hint: Tämä suodatin koskee yksittäisten julkaisujen valintaa muista kriteereistä riippumatta. Voit lisätä lisää julkaisuja tähän suodattimeen selainkäyttöliittymästä. title: Suodatetut julkaisut @@ -1554,7 +1554,7 @@ fi: states: finished: Valmis in_progress: Käynnissä - scheduled: Ajoitettu + scheduled: Ajastettu unconfirmed: Varmistamaton status: Tila success: Tietojen lähettäminen onnistui, ja ne käsitellään aivan pian @@ -1706,16 +1706,22 @@ fi: body: "%{name} mainitsi sinut:" subject: "%{name} mainitsi sinut" title: Uusi maininta + moderation_warning: + subject: Olet saanut moderointivaroituksen poll: subject: Äänestys käyttäjältä %{name} on päättynyt quote: body: "%{name} lainasi julkaisuasi:" subject: "%{name} lainasi julkaisuasi" title: Uusi lainaus + quoted_update: + subject: "%{name} muokkasi lainaamaasi julkaisua" reblog: body: "%{name} tehosti julkaisuasi:" subject: "%{name} tehosti julkaisuasi" title: Uusi tehostus + severed_relationships: + subject: Olet menettänyt yhteyksiä moderointipäätöksen vuoksi status: subject: "%{name} julkaisi juuri" update: @@ -1750,7 +1756,7 @@ fi: truncate: "…" polls: errors: - already_voted: Olet jo äänestänyt tässä äänestyksessä + already_voted: Olet jo osallistunut tähän äänestykseen duplicate_options: sisältää kaksoiskappaleita duration_too_long: on liian kaukana tulevaisuudessa duration_too_short: on liian aikainen @@ -1817,8 +1823,8 @@ fi: account: Julkiset julkaisut tililtä @%{acct} tag: 'Julkiset julkaisut aihetunnisteella #%{hashtag}' scheduled_statuses: - over_daily_limit: Olet ylittänyt %{limit} ajoitetun julkaisun rajan tälle päivälle - over_total_limit: Olet ylittänyt %{limit} ajoitetun julkaisun rajan + over_daily_limit: Olet ylittänyt %{limit} ajastetun julkaisun rajan tälle päivälle + over_total_limit: Olet ylittänyt %{limit} ajastetun julkaisun rajan too_soon: päivämäärän on oltava tulevaisuudessa self_destruct: lead_html: Valitettavasti %{domain} sulkeutuu pysyvästi. Jos sinulla on siellä tili, et voi jatkaa sen käyttöä mutta voit yhä pyytää varmuuskopiota tiedoistasi. @@ -1847,7 +1853,7 @@ fi: unknown_browser: tuntematon selain weibo: Weibo current_session: Nykyinen istunto - date: Päiväys + date: Päivämäärä description: "%{browser} %{platform}" explanation: Nämä verkkoselaimet ovat parhaillaan kirjautuneena Mastodon-tilillesi. ip: IP-osoite diff --git a/config/locales/he.yml b/config/locales/he.yml index 53940fef5195cb..584193d981bb3a 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -1784,16 +1784,22 @@ he: body: 'התקבלה פניה עבורך מאת %{name} ב:' subject: התקבלה פניה עבורך מאת %{name} title: אזכור חדש + moderation_warning: + subject: קיבלת אזהרה מצוות ניהול התוכן poll: subject: סקר מאת %{name} הסתיים quote: body: 'הודעתך צוטטה על ידי %{name}:' subject: "%{name} ציטט.ה את הודעתך" title: ציטוט חדש + quoted_update: + subject: "%{name} ערך הודעה שהשתמשת בה בציטוט" reblog: body: 'הודעתך הודהדה על ידי %{name}:' subject: הודעתך הודהדה על ידי%{name} title: הדהוד חדש + severed_relationships: + subject: איבדת קשרים עם משתמשים אחרים עקב החלטת צוות ניהול התוכן status: subject: "%{name} בדיוק פרסם" update: diff --git a/config/locales/hu.yml b/config/locales/hu.yml index a957165f2693b1..ab49a9b231d94b 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -1706,6 +1706,8 @@ hu: body: "%{name} megemlített téged:" subject: "%{name} megemlített téged" title: Új említés + moderation_warning: + subject: Kaptál egy moderálási figyelmeztetést poll: subject: "%{name} szavazása véget ért" quote: diff --git a/config/locales/is.yml b/config/locales/is.yml index ba03643e1dbf83..cec2023a87707b 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -1710,16 +1710,22 @@ is: body: "%{name} minntist á þig í:" subject: "%{name} minntist á þig" title: Ný tilvísun + moderation_warning: + subject: Þú hefur fengið aðvörun frá umsjónarmanni poll: subject: Könnun frá %{name} er lokið quote: body: "%{name} vitnaði í færsluna þína:" subject: "%{name} vitnaði í færsluna þína" title: Ný tilvitnun + quoted_update: + subject: "%{name} breytti færslu sem þú hefur vitnað í" reblog: body: "%{name} endurbirti færsluna þína:" subject: "%{name} endurbirti færsluna þína" title: Ný endurbirting + severed_relationships: + subject: Þú hefur tapað tengingum vegna ákvörðunar umsjónarmanna status: subject: "%{name} sendi inn rétt í þessu" update: diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 7697697d1f8bd1..4a6964dfd56668 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -1,7 +1,7 @@ --- lt: about: - about_mastodon_html: 'Ateities socialinis tinklas: jokių reklamų ir įmonių sekimo, etiškas dizainas bei decentralizacija! Turėkite savo duomenis su „Mastodon“.' + about_mastodon_html: 'Ateities socialinis tinklas: jokių reklamų ir įmonių sekimo, etiškas dizainas bei decentralizacija! Turėkite savo duomenis su „Mastodon“!' contact_missing: Nenustatyta contact_unavailable: Nėra hosted_on: "„Mastodon“ talpinamas domene %{domain}" @@ -25,6 +25,7 @@ lt: one: Įrašas other: Įrašų posts_tab_heading: Įrašai + self_follow_error: Neleidžiama sekti savo pačių paskyros admin: account_actions: action: Atlikti veiksmą @@ -36,8 +37,9 @@ lt: created_msg: Prižiūrėjimo pastaba sėkmingai sukurta! destroyed_msg: Prižiūrėjimo pastaba sėkmingai sunaikinta! accounts: + add_email_domain_block: Blokuoti el. pašto domeną approve: Patvirtinti - approved_msg: Sėkmingai patvirtinta %{username} registracijos paraiška. + approved_msg: Sėkmingai patvirtinta %{username} registracijos paraiška are_you_sure: Ar esi įsitikinęs (-usi)? avatar: Avataras by_domain: Domenas @@ -47,13 +49,13 @@ lt: label: Keisti el. paštą new_email: Naujas el. paštas submit: Keisti el. paštą - title: Keisti el. paštą %{username} + title: Keisti el. paštą vartotjui %{username} change_role: changed_msg: Vaidmuo sėkmingai pakeistas. edit_roles: Tvarkyti naudotojų vaidmenis label: Keisti vaidmenį no_role: Jokios vaidmenį - title: Keisti vaidmenį %{username} + title: Keisti teises vartotojui %{username} confirm: Patvirtinti confirmed: Patvirtinta confirming: Patvirtinama @@ -61,7 +63,7 @@ lt: delete: Ištrinti duomenis deleted: Ištrinta demote: Pažeminti - destroyed_msg: "%{username} duomenys dabar laukia eilėje, kad būtų netrukus ištrinti." + destroyed_msg: "%{username} duomenys dabar laukia eilėje, kad būtų netrukus ištrinti" disable: Sustabdyti disable_two_factor_authentication: Išjungti 2FA disabled: Pristabdyta @@ -72,7 +74,7 @@ lt: email_status: El. pašto būsena enable: Panaikinti sustabdymą enabled: Įjungta - enabled_msg: Sėkmingai panaikintas %{username} paskyros sustabdymas. + enabled_msg: Sėkmingai %{username} paskyros sustabdymas panaikintas followers: Sekėjai follows: Seka header: Antraštė @@ -90,7 +92,7 @@ lt: media_attachments: Medijos priedai memorialize: Paversti į memorialinį memorialized: Memorializuota - memorialized_msg: Sėkmingai paversta %{username} į memorialinę paskyrą. + memorialized_msg: Sėkmingai %{username} paskyra paversta į memorialinę moderation: active: Aktyvus all: Visi @@ -119,14 +121,14 @@ lt: public: Viešas push_subscription_expires: PuSH prenumerata baigiasi redownload: Atnaujinti profilį - redownloaded_msg: Sėkmingai atnaujintas %{username} profilis iš kilmės šaltinio. + redownloaded_msg: Sėkmingai atnaujintas %{username} profilis iš pradinio šaltinio reject: Atmesti - rejected_msg: Sėkmingai atmesta %{username} registracijos paraiška. + rejected_msg: Sėkmingai atmesta %{username} registracijos paraiška remote_suspension_irreversible: Šios paskyros duomenys negrįžtamai ištrinti. remote_suspension_reversible_hint_html: Paskyra jų serveryje buvo pristabdyta, o duomenys bus visiškai pašalinti %{date}. Iki to laiko nuotolinis serveris gali atkurti šią paskyrą be jokių neigiamų pasekmių. Jei nori iš karto pašalinti visus paskyros duomenis, gali tai padaryti toliau. remove_avatar: Pašalinti avatarą remove_header: Pašalinti antraštę - removed_avatar_msg: Sėkmingai pašalintas %{username} avataro vaizdas. + removed_avatar_msg: Sėkmingai pašalintas %{username} avataro vaizdas removed_header_msg: Sėkmingai pašalintas %{username} antraštės paveikslėlis resend_confirmation: already_confirmed: Šis naudotojas jau patvirtintas. diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 19adee033ff5f5..b92cff5f90d573 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -1706,16 +1706,22 @@ nl: body: 'Je bent door %{name} vermeld in:' subject: Je bent vermeld door %{name} title: Nieuwe vermelding + moderation_warning: + subject: Je hebt een moderatie-waarschuwing ontvangen poll: subject: Een peiling van %{name} is beëindigd quote: body: 'Jouw bericht werd door %{name} geciteerd:' subject: "%{name} heeft jouw bericht geciteerd" title: Nieuw citaat + quoted_update: + subject: "%{name} bewerkte een door jou geciteerd bericht" reblog: body: 'Jouw bericht werd door %{name} geboost:' subject: "%{name} boostte jouw bericht" title: Nieuwe boost + severed_relationships: + subject: Door een moderatie-beslissing ben je volgers en/of door jou gevolgde accounts verloren status: subject: "%{name} heeft zojuist een bericht geplaatst" update: diff --git a/config/locales/nn.yml b/config/locales/nn.yml index aad56c92e91543..efcd226f319b5b 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -1090,7 +1090,7 @@ nn: tag_uses_measure: brukarar totalt description_html: Dette er emneknagger som for øyeblikket vises i mange innlegg som serveren din ser. Det kan hjelpe dine brukere med å finne ut hva folk snakker mest om i øyeblikket. Ingen emneknagger vises offentlig før du godkjenner dem. listable: Kan bli foreslått - no_tag_selected: Ingen merkelappar vart endra fordi ingen var valde + no_tag_selected: Ingen emneknaggar vart endra fordi ingen var valde not_listable: Vil ikke bli foreslått not_trendable: Kjem ikkje til å syna under trendar not_usable: Kan ikke brukes @@ -1706,16 +1706,22 @@ nn: body: 'Du vart nemnd av %{name} i:' subject: Du vart nemnd av %{name} title: Ny omtale + moderation_warning: + subject: Du har fått ei moderasjonsåtvaring poll: subject: Meiningsmålinga frå %{name} er avslutta quote: body: 'Innlegget ditt vart sitert av %{name}:' subject: "%{name} siterte innlegget ditt" title: Nytt sitat + quoted_update: + subject: "%{name} redigerte eit innlegg du har sitert" reblog: body: 'Statusen din vart framheva av %{name}:' subject: "%{name} framheva statusen din" title: Ny framheving + severed_relationships: + subject: Du har tapte tilkoplingar på grunn av ei avgjerd frå ein moderator status: subject: "%{name} postet nettopp" update: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 9bda819e7b4d6d..4dea9d51d2a526 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1706,16 +1706,22 @@ pt-BR: body: "%{name} te mencionou em:" subject: "%{name} te mencionou" title: Nova menção + moderation_warning: + subject: Você recebeu um aviso de moderação poll: subject: Uma enquete por %{name} terminou quote: body: 'Sua publicação foi Citada por %{name}:' subject: "%{name} citou sua publicação" title: Nova citação + quoted_update: + subject: "%{name} editou uma publicação que você citou" reblog: body: "%{name} impulsionou a sua publicação:" subject: "%{name} impulsionou a sua publicação" title: Novo impulso + severed_relationships: + subject: Você perdeu conexões devido a uma decisão da moderação status: subject: "%{name} acabou de publicar" update: @@ -1734,6 +1740,7 @@ pt-BR: quadrillion: QUA thousand: MIL trillion: TRI + unit: '' otp_authentication: code_hint: Digite o código gerado pelo seu aplicativo autenticador para confirmar description_html: Se você ativar a autenticação de dois fatores usando um aplicativo autenticador, ao se conectar será exigido que você esteja com o seu telefone, que gerará tokens para você entrar. diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index 32afa809e9e228..5aca8c4ec4cfee 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -1706,16 +1706,22 @@ pt-PT: body: 'Foste mencionado por %{name}:' subject: "%{name} mencionou-te" title: Nova menção + moderation_warning: + subject: Recebeste um aviso da moderação poll: subject: A sondagem de %{name} terminou quote: body: 'A sua publicação foi citada por %{name}:' subject: "%{name} citou a sua publicação" title: Nova citação + quoted_update: + subject: "%{name} editou uma publicação que citaste" reblog: body: 'A tua publicação foi partilhada por %{name}:' subject: "%{name} partilhou a sua publicação" title: Novo impulso + severed_relationships: + subject: Perdeste ligações devido a uma decisão da moderação status: subject: "%{name} acabou de publicar" update: diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 392d7076d17126..70d4296f2eb162 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -91,23 +91,23 @@ de: bootstrap_timeline_accounts: Diese Konten werden an den Anfang der Follow-Empfehlungen für neue Nutzer angeheftet. Gib eine durch Kommata getrennte Liste von Konten an. closed_registrations_message: Wird angezeigt, wenn Registrierungen deaktiviert sind content_cache_retention_period: Sämtliche Beiträge von anderen Servern (einschließlich geteilte Beiträge und Antworten) werden, unabhängig von der Interaktion der lokalen Nutzer*innen mit diesen Beiträgen, nach der festgelegten Anzahl von Tagen gelöscht. Das betrifft auch Beiträge, die von lokalen Nutzer*innen favorisiert oder als Lesezeichen gespeichert wurden. Private Erwähnungen zwischen Nutzer*innen von verschiedenen Servern werden ebenfalls verloren gehen und können nicht wiederhergestellt werden. Diese Option richtet sich ausschließlich an Server mit speziellen Zwecken und wird die allgemeine Nutzungserfahrung beeinträchtigen, wenn sie für den allgemeinen Gebrauch aktiviert ist. - custom_css: Du kannst benutzerdefinierte Stile auf die Web-Version von Mastodon anwenden. + custom_css: Du kannst eigene Stylesheets für das Webinterface von Mastodon verwenden. favicon: WEBP, PNG, GIF oder JPG. Überschreibt das Standard-Mastodon-Favicon mit einem eigenen Symbol. landing_page: Legt fest, welche Seite neue Besucher*innen sehen, wenn sie zum ersten Mal auf deinem Server ankommen. Für „Trends“ müssen die Trends in den Entdecken-Einstellungen aktiviert sein. Für „Lokaler Feed“ muss „Zugriff auf Live-Feeds, die lokale Beiträge beinhalten“ in den Entdecken-Einstellungen auf „Alle“ gesetzt werden. - mascot: Überschreibt die Abbildung in der erweiterten Weboberfläche. + mascot: Überschreibt die Abbildung im erweiterten Webinterface. media_cache_retention_period: Mediendateien aus Beiträgen von externen Nutzer*innen werden auf deinem Server zwischengespeichert. Wenn ein positiver Wert gesetzt ist, werden die Medien nach der festgelegten Anzahl von Tagen gelöscht. Sollten die Medien nach dem Löschvorgang wieder angefragt werden, werden sie erneut heruntergeladen, sofern der ursprüngliche Inhalt noch vorhanden ist. Es wird empfohlen, diesen Wert auf mindestens 14 Tage festzulegen, da die Häufigkeit der Abfrage von Linkvorschaukarten für Websites von Dritten begrenzt ist und die Linkvorschaukarten sonst nicht vor Ablauf dieser Zeit aktualisiert werden. min_age: Nutzer*innen werden bei der Registrierung aufgefordert, ihr Geburtsdatum zu bestätigen peers_api_enabled: Eine Liste von Domains, die diesem Server im Fediverse begegnet sind. Hierbei werden keine Angaben darüber gemacht, ob du mit einem bestimmten Server föderierst, sondern nur, dass dein Server davon weiß. Dies wird von Diensten verwendet, die allgemein Statistiken übers Ferdiverse sammeln. profile_directory: Dieses Verzeichnis zeigt alle Profile an, die sich dafür entschieden haben, entdeckt zu werden. - require_invite_text: Wenn Registrierungen eine manuelle Genehmigung erfordern, dann werden Nutzer einen Grund für ihre Registrierung angeben müssen + require_invite_text: Wenn Registrierungen eine manuelle Genehmigung erfordern, müssen Benutzer*innen ihren Beitrittswunsch begründen site_contact_email: Wie man dich bei rechtlichen oder Support-Anfragen erreichen kann. site_contact_username: Wie man dich auf Mastodon erreichen kann. site_extended_description: Alle zusätzlichen Informationen, die für Besucher*innen und deine Benutzer*innen nützlich sein könnten. Kann mit der Markdown-Syntax formatiert werden. site_short_description: Eine kurze Beschreibung zur eindeutigen Identifizierung des Servers. Wer betreibt ihn, für wen ist er bestimmt? - site_terms: Verwende eine eigene Datenschutzerklärung oder lasse das Feld leer, um die allgemeine Vorlage zu verwenden. Kann mit der Markdown-Syntax formatiert werden. + site_terms: Verwende eine eigene Datenschutzerklärung oder lass das Feld leer, um die allgemeine Vorlage zu verwenden. Kann mit der Markdown-Syntax formatiert werden. site_title: Wie Personen neben dem Domainnamen auf deinen Server verweisen können. status_page_url: Link zu einer Internetseite, auf der der Serverstatus während eines Ausfalls angezeigt wird - theme: Das Design, das abgemeldete Besucher und neue Benutzer sehen. + theme: Das Design, das nicht angemeldete Personen sehen. thumbnail: Ein Bild ungefähr im 2:1-Format, das neben den Server-Informationen angezeigt wird. trendable_by_default: Manuelles Überprüfen angesagter Inhalte überspringen. Einzelne Elemente können später noch aus den Trends entfernt werden. trends: Trends zeigen, welche Beiträge, Hashtags und Nachrichten auf deinem Server immer beliebter werden. @@ -161,7 +161,7 @@ de: color: Farbe, die für diese Rolle in der gesamten Benutzerschnittstelle verwendet wird, als RGB im Hexadezimalsystem highlighted: Dies macht die Rolle öffentlich im Profil sichtbar name: Name der Rolle, der auch öffentlich als Badge angezeigt wird, sofern dies unten aktiviert ist - permissions_as_keys: Benutzer*innen mit dieser Rolle haben Zugriff auf... + permissions_as_keys: Nutzer*innen mit dieser Rolle haben Zugriff auf … position: Höhere Rollen entscheiden über Konfliktlösungen zu gewissen Situationen. Bestimmte Aktionen können nur mit geringfügigeren Rollen durchgeführt werden username_block: allow_with_approval: Anstatt Registrierungen komplett zu verhindern, benötigen übereinstimmende Treffer eine Genehmigung @@ -282,7 +282,7 @@ de: activity_api_enabled: Aggregierte Nutzungsdaten über die API veröffentlichen app_icon: App-Symbol backups_retention_period: Aufbewahrungsfrist für Archive - bootstrap_timeline_accounts: Neuen Nutzern immer diese Konten empfehlen + bootstrap_timeline_accounts: Neuen Nutzer*innen immer diese Konten empfehlen closed_registrations_message: Nachricht, falls Registrierungen deaktiviert sind content_cache_retention_period: Aufbewahrungsfrist für externe Inhalte custom_css: Eigenes CSS @@ -290,7 +290,7 @@ de: landing_page: Landingpage für neue Besucher*innen local_live_feed_access: Zugriff auf Live-Feeds, die lokale Beiträge beinhalten local_topic_feed_access: Zugriff auf Hashtags und Links, die lokale Beiträge beinhalten - mascot: Benutzerdefiniertes Maskottchen (Legacy) + mascot: Eigenes Maskottchen (Veraltet) media_cache_retention_period: Aufbewahrungsfrist für Medien im Cache min_age: Erforderliches Mindestalter peers_api_enabled: Die entdeckten Server im Fediverse über die API veröffentlichen diff --git a/config/locales/simple_form.fa.yml b/config/locales/simple_form.fa.yml index 20afb20faefd1c..b41307f4fa3a35 100644 --- a/config/locales/simple_form.fa.yml +++ b/config/locales/simple_form.fa.yml @@ -372,7 +372,9 @@ fa: jurisdiction: صلاحیت قانونی min_age: کمینهٔ زمان user: + date_of_birth_1i: سال date_of_birth_2i: ماه + date_of_birth_3i: روز role: نقش time_zone: منطقهٔ زمانی user_role: diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index c6d2098d3900da..a89f4a86aaad9c 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -48,7 +48,7 @@ fi: digest: Lähetetään vain pitkän poissaolon jälkeen ja vain, jos olet saanut suoria viestejä poissaolosi aikana email: Sinulle lähetetään vahvistusviesti header: WEBP, PNG, GIF tai JPG. Enintään %{size}. Skaalataan kokoon %{dimensions} px - inbox_url: Kopioi URL-osoite haluamasi välittäjän etusivulta + inbox_url: Kopioi URL-⁠osoite haluamasi välittäjän etusivulta irreversible: Suodatetut julkaisut katoavat peruuttamattomasti, vaikka suodatin poistettaisiin myöhemmin locale: Käyttöliittymän, sähköpostiviestien ja puskuilmoitusten kieli password: Käytä vähintään 8:aa merkkiä @@ -203,7 +203,7 @@ fi: announcement: all_day: Koko päivän tapahtuma ends_at: Tapahtuman loppu - scheduled_at: Ajoita julkaisu + scheduled_at: Ajasta julkaisu starts_at: Tapahtuman alku text: Tiedote appeal: @@ -224,7 +224,7 @@ fi: fields: Lisäkentät header: Otsakekuva honeypot: "%{label} (älä täytä)" - inbox_url: Välittäjän postilaatikon URL-osoite + inbox_url: Välittäjän postilaatikon URL-⁠osoite irreversible: Pudota piilottamisen sijaan locale: Käyttöliittymän kieli max_uses: Käyttökertoja enintään diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index cb18817ecd7205..dc4bb1354fd583 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -62,6 +62,7 @@ ja: setting_display_media_hide_all: メディアを常に隠す setting_display_media_show_all: メディアを常に表示する setting_emoji_style: 絵文字の表示方法。「オート」の場合、可能ならネイティブの絵文字を使用し、レガシーなブラウザではTwemojiで代替します。 + setting_quick_boosting_html: 有効にすると、%{boost_icon} ブーストアイコンのクリックで即座にブーストされます。ブースト/引用ドロップダウンは開きません。引用アクションは %{options_icon} (オプション) メニューに移されます。 setting_system_scrollbars_ui: Safari/Chromeベースのデスクトップブラウザーでのみ有効です setting_use_blurhash: ぼかしはメディアの色を元に生成されますが、細部は見えにくくなっています setting_use_pending_items: 新着があってもタイムラインを自動的にスクロールしないようにします diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index c0aa3f692e5644..902e4d0b23fbdd 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -372,7 +372,9 @@ nl: jurisdiction: Jurisdictie min_age: Minimumleeftijd user: + date_of_birth_1i: Jaar date_of_birth_2i: Maand + date_of_birth_3i: Dag role: Rol time_zone: Tijdzone user_role: diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml index b7b1021cd1de73..640e1b7772c816 100644 --- a/config/locales/simple_form.nn.yml +++ b/config/locales/simple_form.nn.yml @@ -289,7 +289,7 @@ nn: favicon: Favorittikon landing_page: Startside for nye lesarar local_live_feed_access: Tilgang til direktestraumar med lokale innlegg - local_topic_feed_access: Tilgang til merkelapp- og lenkestraumar med lokale innlegg + local_topic_feed_access: Tilgang til emneknagg- og lenkestraumar med lokale innlegg mascot: Eigendefinert maskot (eldre funksjon) media_cache_retention_period: Oppbevaringsperiode for mediebuffer min_age: Minste aldersgrense @@ -297,7 +297,7 @@ nn: profile_directory: Aktiver profilkatalog registrations_mode: Kven kan registrera seg remote_live_feed_access: Tilgang til direktestraumar med eksterne innlegg - remote_topic_feed_access: Tilgang til merkelapp- og direktestraumar med eksterne innlegg + remote_topic_feed_access: Tilgang til emneknagg- og direktestraumar med eksterne innlegg require_invite_text: Krev ei grunngjeving for å få bli med show_domain_blocks: Vis domeneblokkeringar show_domain_blocks_rationale: Vis grunngjeving for domeneblokkeringar @@ -372,7 +372,9 @@ nn: jurisdiction: Rettskrins min_age: Minstealder user: + date_of_birth_1i: År date_of_birth_2i: Månad + date_of_birth_3i: Dag role: Rolle time_zone: Tidssone user_role: diff --git a/config/locales/sq.yml b/config/locales/sq.yml index 6b251a77790a45..8171dcea85c069 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -1691,16 +1691,22 @@ sq: body: 'U përmendët nga %{name} në:' subject: U përmendët nga %{name} title: Përmendje e re + moderation_warning: + subject: Ju është dhënë një sinjalizim moderimi poll: subject: Përfundoi një pyetësor nga %{name} quote: body: 'Postimi juaj u citua nga %{name}:' subject: "%{name} citoi postimin tuaj" title: Citim i ri + quoted_update: + subject: "%{name} përpunoi një postim që keni cituar" reblog: body: 'Gjendja juaj u përforcua nga %{name}:' subject: "%{name} përforcoi gjendjen tuaj" title: Përforcim i ri + severed_relationships: + subject: Keni humbur lidhjet, për shkak të një vendimi moderimi status: subject: "%{name} sapo postoi" update: diff --git a/config/locales/tr.yml b/config/locales/tr.yml index b56ba5d9ac4781..d0228c5cea1283 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1706,16 +1706,22 @@ tr: body: "%{name} senden bahsetti:" subject: "%{name} senden bahsetti" title: Yeni bahsetme + moderation_warning: + subject: Hesabınız bir denetim uyarısı aldı poll: subject: Anket %{name} tarafından sonlandırıldı quote: body: "%{name} durumunuzu yeniden paylaştı:" subject: "%{name} gönderini yeniden paylaştı" title: Yeni alıntı + quoted_update: + subject: "%{name} alıntıladığınız bir gönderiyi düzenledi" reblog: body: "%{name} durumunuzu yeniden paylaştı:" subject: "%{name} durumunuzu yeniden paylaştı" title: Yeni paylaşım + severed_relationships: + subject: Bir denetim kararı nedeniyle bağlantılarınızı kaybettiniz status: subject: "%{name} az önce gönderdi" update: diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 30476230fdbd8a..0e39f6bf0f02f2 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1667,16 +1667,22 @@ vi: body: 'Bạn vừa được nhắc đến bởi %{name} trong:' subject: Bạn vừa được nhắc đến bởi %{name} title: Lượt nhắc mới + moderation_warning: + subject: Bạn vừa nhận một cảnh báo kiểm duyệt poll: subject: Vốt của %{name} đã kết thúc quote: body: 'Tút của bạn được trích dẫn bởi %{name}:' subject: "%{name} vừa trích dẫn tút của bạn" title: Trích dẫn mới + quoted_update: + subject: "%{name} đã chỉnh sửa tút mà bạn trích dẫn" reblog: body: Tút của bạn vừa được %{name} đăng lại subject: "%{name} vừa đăng lại tút của bạn" title: Lượt đăng lại mới + severed_relationships: + subject: Bạn bị mất các mối quan hệ vì một quyết định kiểm duyệt status: subject: Bài đăng mới từ %{name} update: diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index f4b6dcd837ccbf..c7a1fc84a60e70 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -1667,16 +1667,22 @@ zh-CN: body: "%{name} 提到了你:" subject: "%{name} 提到了你" title: 新的提及 + moderation_warning: + subject: 你收到了一条管理警告 poll: subject: "%{name} 创建的一个投票已经结束" quote: body: 你的嘟文被 %{name} 引用了: subject: "%{name} 引用了你的嘟文" title: 新引用 + quoted_update: + subject: "%{name} 编辑了一条你之前引用过的嘟文" reblog: body: 你的嘟文被 %{name} 转嘟了: subject: "%{name} 转嘟了你的嘟文" title: 新的转嘟 + severed_relationships: + subject: 由于管理决定,您已失去网络上的关注关系 status: subject: "%{name} 刚刚发布嘟文" update: diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index bc12e97ac5be52..e7f8172aee9159 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1669,16 +1669,22 @@ zh-TW: body: "%{name} 於嘟文中提及您:" subject: "%{name} 於嘟文中提及您" title: 新的提及 + moderation_warning: + subject: 您已收到管理員警告 poll: subject: 由 %{name} 發起的投票已結束 quote: body: 您的嘟文被 %{name} 引用: subject: "%{name} 已引用您的嘟文" title: 新引用 + quoted_update: + subject: "%{name} 已編輯一則您曾引用之嘟文" reblog: body: 您的嘟文被 %{name} 轉嘟: subject: "%{name} 已轉嘟您的嘟文" title: 新的轉嘟 + severed_relationships: + subject: 由於管理員之決定,您已失去您的社交網路 status: subject: "%{name} 剛剛嘟文" update: From 5d849571176b2e7ec6ae55370bee89cbf0c1c57f Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 3 Dec 2025 12:00:41 +0100 Subject: [PATCH 082/123] Add shareable wrapstodon links (#37047) --- app/controllers/wrapstodon_controller.rb | 23 +++++++++++++++++++ app/lib/annual_report.rb | 3 ++- app/models/generated_annual_report.rb | 5 ++-- .../rest/annual_report_serializer.rb | 6 ++++- app/views/wrapstodon/show.html.haml | 9 ++++++++ config/locales/en.yml | 2 ++ config/routes.rb | 1 + ...d_share_key_to_generated_annual_reports.rb | 7 ++++++ db/schema.rb | 3 ++- 9 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 app/controllers/wrapstodon_controller.rb create mode 100644 app/views/wrapstodon/show.html.haml create mode 100644 db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb diff --git a/app/controllers/wrapstodon_controller.rb b/app/controllers/wrapstodon_controller.rb new file mode 100644 index 00000000000000..74f0dbb65ae775 --- /dev/null +++ b/app/controllers/wrapstodon_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class WrapstodonController < ApplicationController + include WebAppControllerConcern + include Authorization + include AccountOwnedConcern + + vary_by 'Accept, Accept-Language, Cookie' + + before_action :set_generated_annual_report + + skip_before_action :require_functional!, only: :show, unless: :limited_federation_mode? + + def show + expires_in 10.seconds, public: true if current_account.nil? + end + + private + + def set_generated_annual_report + @generated_annual_report = GeneratedAnnualReport.find_by!(account: @account, year: params[:year], share_key: params[:share_key]) + end +end diff --git a/app/lib/annual_report.rb b/app/lib/annual_report.rb index 6da9ecae1424d1..12d69dfba842ff 100644 --- a/app/lib/annual_report.rb +++ b/app/lib/annual_report.rb @@ -43,7 +43,8 @@ def generate account: @account, year: @year, schema_version: SCHEMA, - data: data + data: data, + share_key: SecureRandom.hex(8) ) end diff --git a/app/models/generated_annual_report.rb b/app/models/generated_annual_report.rb index aba0712fe40f1c..df78d4f5fe0bc8 100644 --- a/app/models/generated_annual_report.rb +++ b/app/models/generated_annual_report.rb @@ -5,13 +5,14 @@ # Table name: generated_annual_reports # # id :bigint(8) not null, primary key -# account_id :bigint(8) not null -# year :integer not null # data :jsonb not null # schema_version :integer not null +# share_key :string # viewed_at :datetime +# year :integer not null # created_at :datetime not null # updated_at :datetime not null +# account_id :bigint(8) not null # class GeneratedAnnualReport < ApplicationRecord diff --git a/app/serializers/rest/annual_report_serializer.rb b/app/serializers/rest/annual_report_serializer.rb index 1fb5ddb5c123c3..a7d611f8ef4d64 100644 --- a/app/serializers/rest/annual_report_serializer.rb +++ b/app/serializers/rest/annual_report_serializer.rb @@ -1,5 +1,9 @@ # frozen_string_literal: true class REST::AnnualReportSerializer < ActiveModel::Serializer - attributes :year, :data, :schema_version + attributes :year, :data, :schema_version, :share_url + + def share_url + public_wrapstodon_url(object.account, object.year, object.share_key) if object.share_key.present? + end end diff --git a/app/views/wrapstodon/show.html.haml b/app/views/wrapstodon/show.html.haml new file mode 100644 index 00000000000000..6978f231f9e63a --- /dev/null +++ b/app/views/wrapstodon/show.html.haml @@ -0,0 +1,9 @@ +- content_for :page_title, t('wrapstodon.title', name: display_name(@account), year: @generated_annual_report.year) + +- content_for :header_tags do + %meta{ name: 'robots', content: 'noindex, noarchive' }/ + + = opengraph 'og:site_name', site_title + = opengraph 'profile:username', acct(@account)[1..] + += render 'shared/web_app' diff --git a/config/locales/en.yml b/config/locales/en.yml index 62b8823d9fb375..0c44a7c3bf39a0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2186,3 +2186,5 @@ en: not_supported: This browser doesn't support security keys otp_required: To use security keys please enable two-factor authentication first. registered_on: Registered on %{date} + wrapstodon: + title: Wrapstodon %{year} for %{name} diff --git a/config/routes.rb b/config/routes.rb index 412372600ef87c..3685e695f91f52 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -163,6 +163,7 @@ def redirect_with_vary(path) get '/@:account_username/followers', to: 'follower_accounts#index' get '/@:account_username/:id', to: 'statuses#show', as: :short_account_status get '/@:account_username/:id/embed', to: 'statuses#embed', as: :embed_short_account_status + get '/@:account_username/wrapstodon/:year/:share_key', to: 'wrapstodon#show', as: :public_wrapstodon end get '/@:username_with_domain/(*any)', to: 'home#index', constraints: { username_with_domain: %r{([^/])+?} }, as: :account_with_domain, format: false diff --git a/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb b/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb new file mode 100644 index 00000000000000..aa4d15a15a26a0 --- /dev/null +++ b/db/migrate/20251202140424_add_share_key_to_generated_annual_reports.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddShareKeyToGeneratedAnnualReports < ActiveRecord::Migration[8.0] + def change + add_column :generated_annual_reports, :share_key, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 669a6dbf2e61e7..4e8a9f6efb34ff 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_12_01_155054) do +ActiveRecord::Schema[8.0].define(version: 2025_12_02_140424) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -616,6 +616,7 @@ t.datetime "viewed_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "share_key" t.index ["account_id", "year"], name: "index_generated_annual_reports_on_account_id_and_year", unique: true end From 9d10137c7c21714dd5a7123eae365f38ebbc9cb5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 12:15:47 +0100 Subject: [PATCH 083/123] Update dependency bundler-audit to v0.9.3 (#37061) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index daa470e2d4b688..ead5da76e761a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -135,8 +135,8 @@ GEM racc browser (6.2.0) builder (3.3.0) - bundler-audit (0.9.2) - bundler (>= 1.2.0, < 3) + bundler-audit (0.9.3) + bundler (>= 1.2.0) thor (~> 1.0) capybara (3.40.0) addressable From e2226645396008a4405dd08f2b4d98d82f648db6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:17:01 +0000 Subject: [PATCH 084/123] Update DefinitelyTyped types (non-major) (#37073) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/yarn.lock b/yarn.lock index 43cc7f5a2eafdc..5d048af49dfcd3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4124,13 +4124,13 @@ __metadata: linkType: hard "@types/express@npm:^5.0.5": - version: 5.0.5 - resolution: "@types/express@npm:5.0.5" + version: 5.0.6 + resolution: "@types/express@npm:5.0.6" dependencies: "@types/body-parser": "npm:*" "@types/express-serve-static-core": "npm:^5.0.0" - "@types/serve-static": "npm:^1" - checksum: 10c0/e96da91c121b43e0e84301a4cfe165908382d016234c11213aeb4f7401cf1a8694e16e3947d21b5c20b3389358d48d60a8c5c38657e041726ac9e8c884d2b8f0 + "@types/serve-static": "npm:^2" + checksum: 10c0/f1071e3389a955d4f9a38aae38634121c7cd9b3171ba4201ec9b56bd534aba07866839d278adc0dda05b942b05a901a02fd174201c3b1f70ce22b10b6c68f24b languageName: node linkType: hard @@ -4197,9 +4197,9 @@ __metadata: linkType: hard "@types/lodash@npm:^4.14.195": - version: 4.17.20 - resolution: "@types/lodash@npm:4.17.20" - checksum: 10c0/98cdd0faae22cbb8079a01a3bb65aa8f8c41143367486c1cbf5adc83f16c9272a2a5d2c1f541f61d0d73da543c16ee1d21cf2ef86cb93cd0cc0ac3bced6dd88f + version: 4.17.21 + resolution: "@types/lodash@npm:4.17.21" + checksum: 10c0/73cb006e047d8871e9d63f3a165543bf16c44a5b6fe3f9f6299e37cb8d67a7b1d55ac730959a81f9def510fd07232ff7e30e05413e5d5a12793baad84ebe36c3 languageName: node linkType: hard @@ -4397,12 +4397,12 @@ __metadata: linkType: hard "@types/react@npm:^18.2.7": - version: 18.3.26 - resolution: "@types/react@npm:18.3.26" + version: 18.3.27 + resolution: "@types/react@npm:18.3.27" dependencies: "@types/prop-types": "npm:*" - csstype: "npm:^3.0.2" - checksum: 10c0/7b62d91c33758f14637311921c92db6045b6328e2300666a35ef8130d06385e39acada005eaf317eee93228edc10ea5f0cd34a0385654d2014d24699a65bfeef + csstype: "npm:^3.2.2" + checksum: 10c0/a761d2f58de03d0714806cc65d32bb3d73fb33a08dd030d255b47a295e5fff2a775cf1c20b786824d8deb6454eaccce9bc6998d9899c14fc04bbd1b0b0b72897 languageName: node linkType: hard @@ -4437,7 +4437,7 @@ __metadata: languageName: node linkType: hard -"@types/send@npm:*, @types/send@npm:<1": +"@types/send@npm:*": version: 0.17.6 resolution: "@types/send@npm:0.17.6" dependencies: @@ -4447,14 +4447,13 @@ __metadata: languageName: node linkType: hard -"@types/serve-static@npm:^1": - version: 1.15.10 - resolution: "@types/serve-static@npm:1.15.10" +"@types/serve-static@npm:^2": + version: 2.2.0 + resolution: "@types/serve-static@npm:2.2.0" dependencies: "@types/http-errors": "npm:*" "@types/node": "npm:*" - "@types/send": "npm:<1" - checksum: 10c0/842fca14c9e80468f89b6cea361773f2dcd685d4616a9f59013b55e1e83f536e4c93d6d8e3ba5072d40c4e7e64085210edd6646b15d538ded94512940a23021f + checksum: 10c0/a3c6126bdbf9685e6c7dc03ad34639666eff32754e912adeed9643bf3dd3aa0ff043002a7f69039306e310d233eb8e160c59308f95b0a619f32366bbc48ee094 languageName: node linkType: hard @@ -6236,10 +6235,10 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^3.0.2": - version: 3.1.2 - resolution: "csstype@npm:3.1.2" - checksum: 10c0/32c038af259897c807ac738d9eab16b3d86747c72b09d5c740978e06f067f9b7b1737e1b75e407c7ab1fe1543dc95f20e202b4786aeb1b8d3bdf5d5ce655e6c6 +"csstype@npm:^3.0.2, csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce languageName: node linkType: hard From c97d25fcbd484256cb21f725b71ac63788da9542 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 3 Dec 2025 13:53:16 +0100 Subject: [PATCH 085/123] Fix serialization of 2025 wrapstodon reports (#37103) --- app/models/generated_annual_report.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/generated_annual_report.rb b/app/models/generated_annual_report.rb index df78d4f5fe0bc8..563dd9219cd486 100644 --- a/app/models/generated_annual_report.rb +++ b/app/models/generated_annual_report.rb @@ -29,7 +29,12 @@ def view! end def account_ids - data['most_reblogged_accounts'].pluck('account_id') + data['commonly_interacted_with_accounts'].pluck('account_id') + case schema_version + when 1 + data['most_reblogged_accounts'].pluck('account_id') + data['commonly_interacted_with_accounts'].pluck('account_id') + when 2 + [] + end end def status_ids From 08da9d8fc58180401c6d57b75af262733a4bfc7f Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 3 Dec 2025 14:05:33 +0100 Subject: [PATCH 086/123] Fix serialization of annual reports with share code (#37104) --- app/serializers/rest/annual_report_serializer.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/serializers/rest/annual_report_serializer.rb b/app/serializers/rest/annual_report_serializer.rb index a7d611f8ef4d64..99c313e6cb360c 100644 --- a/app/serializers/rest/annual_report_serializer.rb +++ b/app/serializers/rest/annual_report_serializer.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class REST::AnnualReportSerializer < ActiveModel::Serializer + include RoutingHelper + attributes :year, :data, :schema_version, :share_url def share_url From 234990cc375e93838ccbe3b69c1385ff7ef33699 Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 3 Dec 2025 14:54:58 +0100 Subject: [PATCH 087/123] Remove noreferrer from external links (#37107) --- app/javascript/mastodon/components/status/handled_link.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/status/handled_link.tsx b/app/javascript/mastodon/components/status/handled_link.tsx index 8b6db98fda0a04..5fcea5f8b9841a 100644 --- a/app/javascript/mastodon/components/status/handled_link.tsx +++ b/app/javascript/mastodon/components/status/handled_link.tsx @@ -75,7 +75,7 @@ export const HandledLink: FC> = ({ title={href} className={classNames('unhandled-link', className)} target='_blank' - rel='noreferrer noopener' + rel='noopener' translate='no' > {children} From e5e3a64a9be8d1ff5776556507f0c18c7766c695 Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 3 Dec 2025 14:58:38 +0100 Subject: [PATCH 088/123] Display Wrapstodon inline widget (#37106) --- app/javascript/mastodon/actions/server.js | 5 + app/javascript/mastodon/api/annual_report.ts | 38 ++++++ .../mastodon/components/status_list.jsx | 10 +- .../mastodon/features/annual_report/index.tsx | 2 +- .../features/annual_report/timeline.tsx | 44 +++++++ .../ui/containers/status_list_container.js | 9 +- .../mastodon/models/annual_report.ts | 30 ++++- app/javascript/mastodon/reducers/index.ts | 2 + .../mastodon/reducers/slices/annual_report.ts | 118 ++++++++++++++++++ .../mastodon/reducers/slices/index.ts | 5 + .../mastodon/store/typed_functions.ts | 2 +- 11 files changed, 252 insertions(+), 13 deletions(-) create mode 100644 app/javascript/mastodon/api/annual_report.ts create mode 100644 app/javascript/mastodon/features/annual_report/timeline.tsx create mode 100644 app/javascript/mastodon/reducers/slices/annual_report.ts create mode 100644 app/javascript/mastodon/reducers/slices/index.ts diff --git a/app/javascript/mastodon/actions/server.js b/app/javascript/mastodon/actions/server.js index 32ee093afa8423..47b6e7a1764dc9 100644 --- a/app/javascript/mastodon/actions/server.js +++ b/app/javascript/mastodon/actions/server.js @@ -1,3 +1,5 @@ +import { checkAnnualReport } from '@/mastodon/reducers/slices/annual_report'; + import api from '../api'; import { importFetchedAccount } from './importer'; @@ -29,6 +31,9 @@ export const fetchServer = () => (dispatch, getState) => { .get('/api/v2/instance').then(({ data }) => { if (data.contact.account) dispatch(importFetchedAccount(data.contact.account)); dispatch(fetchServerSuccess(data)); + if (data.wrapstodon) { + void dispatch(checkAnnualReport()); + } }).catch(err => dispatch(fetchServerFail(err))); }; diff --git a/app/javascript/mastodon/api/annual_report.ts b/app/javascript/mastodon/api/annual_report.ts new file mode 100644 index 00000000000000..dc080035d49f8a --- /dev/null +++ b/app/javascript/mastodon/api/annual_report.ts @@ -0,0 +1,38 @@ +import api, { apiRequestGet, getAsyncRefreshHeader } from '../api'; +import type { ApiAccountJSON } from '../api_types/accounts'; +import type { ApiStatusJSON } from '../api_types/statuses'; +import type { AnnualReport } from '../models/annual_report'; + +export type ApiAnnualReportState = + | 'available' + | 'generating' + | 'eligible' + | 'ineligible'; + +export const apiGetAnnualReportState = async (year: number) => { + const response = await api().get<{ state: ApiAnnualReportState }>( + `/api/v1/annual_reports/${year}/state`, + ); + + return { + state: response.data.state, + refresh: getAsyncRefreshHeader(response), + }; +}; + +export const apiRequestGenerateAnnualReport = async (year: number) => { + const response = await api().post(`/api/v1/annual_reports/${year}/generate`); + + return { + refresh: getAsyncRefreshHeader(response), + }; +}; + +export interface ApiAnnualReportResponse { + annual_reports: AnnualReport[]; + accounts: ApiAccountJSON[]; + statuses: ApiStatusJSON[]; +} + +export const apiGetAnnualReport = (year: number) => + apiRequestGet(`v1/annual_reports/${year}`); diff --git a/app/javascript/mastodon/components/status_list.jsx b/app/javascript/mastodon/components/status_list.jsx index cb2a7464cb094e..78e6fbcf5ff214 100644 --- a/app/javascript/mastodon/components/status_list.jsx +++ b/app/javascript/mastodon/components/status_list.jsx @@ -8,6 +8,8 @@ import { debounce } from 'lodash'; import { TIMELINE_GAP, TIMELINE_SUGGESTIONS } from 'mastodon/actions/timelines'; import { RegenerationIndicator } from 'mastodon/components/regeneration_indicator'; import { InlineFollowSuggestions } from 'mastodon/features/home_timeline/components/inline_follow_suggestions'; +import { AnnualReportTimeline } from 'mastodon/features/annual_report/timeline'; +import { TIMELINE_WRAPSTODON } from '@/mastodon/reducers/slices/annual_report'; import { StatusQuoteManager } from '../components/status_quoted'; @@ -63,10 +65,12 @@ export default class StatusList extends ImmutablePureComponent { switch(statusId) { case TIMELINE_SUGGESTIONS: return ( - + ); + case TIMELINE_WRAPSTODON: + return ( + + ) case TIMELINE_GAP: return ( { + const { state } = useAppSelector((state) => state.annualReport); + const year = useAppSelector(selectWrapstodonYear); + + const dispatch = useAppDispatch(); + const handleBuildRequest = useCallback(() => { + void dispatch(generateReport()); + }, [dispatch]); + + const handleOpen = useCallback(() => { + dispatch( + // TODO: Implement opening the annual report view when components are ready. + showAlert({ + message: 'Not yet implemented.', + }), + ); + }, [dispatch]); + + if (!year || !state || state === 'ineligible') { + return null; + } + + return ( + + ); +}; diff --git a/app/javascript/mastodon/features/ui/containers/status_list_container.js b/app/javascript/mastodon/features/ui/containers/status_list_container.js index d581ad5fe473ad..66e3b91c7b6d73 100644 --- a/app/javascript/mastodon/features/ui/containers/status_list_container.js +++ b/app/javascript/mastodon/features/ui/containers/status_list_container.js @@ -4,9 +4,10 @@ import { connect } from 'react-redux'; import { debounce } from 'lodash'; -import { scrollTopTimeline, loadPending } from '../../../actions/timelines'; -import StatusList from '../../../components/status_list'; -import { me } from '../../../initial_state'; +import { scrollTopTimeline, loadPending, TIMELINE_SUGGESTIONS } from '@/mastodon/actions/timelines'; +import StatusList from '@/mastodon/components/status_list'; +import { me } from '@/mastodon/initial_state'; +import { TIMELINE_WRAPSTODON } from '@/mastodon/reducers/slices/annual_report'; const makeGetStatusIds = (pending = false) => createSelector([ (state, { type }) => state.getIn(['settings', type], ImmutableMap()), @@ -14,7 +15,7 @@ const makeGetStatusIds = (pending = false) => createSelector([ (state) => state.get('statuses'), ], (columnSettings, statusIds, statuses) => { return statusIds.filter(id => { - if (id === null || id === 'inline-follow-suggestions') return true; + if (id === null || id === TIMELINE_SUGGESTIONS || id === TIMELINE_WRAPSTODON) return true; const statusForId = statuses.get(id); diff --git a/app/javascript/mastodon/models/annual_report.ts b/app/javascript/mastodon/models/annual_report.ts index c0a101e6c872da..a2c0c5178681cc 100644 --- a/app/javascript/mastodon/models/annual_report.ts +++ b/app/javascript/mastodon/models/annual_report.ts @@ -37,8 +37,30 @@ interface AnnualReportV1 { archetype: Archetype; } -export interface AnnualReport { - year: number; - schema_version: number; - data: AnnualReportV1; +interface AnnualReportV2 { + archetype: Archetype; + time_series: TimeSeriesMonth[]; + top_hashtags: NameAndCount[]; + top_statuses: TopStatuses; + most_used_apps: NameAndCount[]; + type_distribution: { + total: number; + reblogs: number; + replies: number; + standalone: number; + }; } + +export type AnnualReport = { + year: number; +} & ( + | { + schema_version: 1; + data: AnnualReportV1; + } + | { + schema_version: 2; + data: AnnualReportV2; + share_url: string | null; + } +); diff --git a/app/javascript/mastodon/reducers/index.ts b/app/javascript/mastodon/reducers/index.ts index 19ecbbfff406d3..7343f5e164f426 100644 --- a/app/javascript/mastodon/reducers/index.ts +++ b/app/javascript/mastodon/reducers/index.ts @@ -33,6 +33,7 @@ import { relationshipsReducer } from './relationships'; import { searchReducer } from './search'; import server from './server'; import settings from './settings'; +import { sliceReducers } from './slices'; import status_lists from './status_lists'; import statuses from './statuses'; import { suggestionsReducer } from './suggestions'; @@ -80,6 +81,7 @@ const reducers = { notificationPolicy: notificationPolicyReducer, notificationRequests: notificationRequestsReducer, navigation: navigationReducer, + ...sliceReducers, }; // We want the root state to be an ImmutableRecord, which is an object with a defined list of keys, diff --git a/app/javascript/mastodon/reducers/slices/annual_report.ts b/app/javascript/mastodon/reducers/slices/annual_report.ts new file mode 100644 index 00000000000000..db1c064e715dfb --- /dev/null +++ b/app/javascript/mastodon/reducers/slices/annual_report.ts @@ -0,0 +1,118 @@ +import { createSlice } from '@reduxjs/toolkit'; + +import { insertIntoTimeline } from '@/mastodon/actions/timelines'; +import type { ApiAnnualReportState } from '@/mastodon/api/annual_report'; +import { + apiGetAnnualReport, + apiGetAnnualReportState, + apiRequestGenerateAnnualReport, +} from '@/mastodon/api/annual_report'; +import type { AnnualReport } from '@/mastodon/models/annual_report'; + +import { + createAppSelector, + createAppThunk, + createDataLoadingThunk, +} from '../../store/typed_functions'; + +export const TIMELINE_WRAPSTODON = 'inline-wrapstodon'; + +interface AnnualReportState { + state?: ApiAnnualReportState; + report?: AnnualReport; +} + +const annualReportSlice = createSlice({ + name: 'annualReport', + initialState: {} as AnnualReportState, + reducers: {}, + extraReducers(builder) { + builder + .addCase(fetchReportState.fulfilled, (state, action) => { + state.state = action.payload; + }) + .addCase(generateReport.pending, (state) => { + state.state = 'generating'; + }) + .addCase(getReport.fulfilled, (state, action) => { + if (action.payload) { + state.report = action.payload; + } + }); + }, +}); + +export const annualReport = annualReportSlice.reducer; + +export const selectWrapstodonYear = createAppSelector( + [(state) => state.server.getIn(['server', 'wrapstodon'])], + (year: unknown) => (typeof year === 'number' && year > 2000 ? year : null), +); + +// This kicks everything off, and is called after fetching the server info. +export const checkAnnualReport = createAppThunk( + `${annualReportSlice.name}/checkAnnualReport`, + async (_arg: unknown, { dispatch, getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + return; + } + const state = await dispatch(fetchReportState()); + if ( + state.meta.requestStatus === 'fulfilled' && + state.payload !== 'ineligible' + ) { + dispatch(insertIntoTimeline('home', TIMELINE_WRAPSTODON, 1)); + } + }, +); + +const fetchReportState = createDataLoadingThunk( + `${annualReportSlice.name}/fetchReportState`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiGetAnnualReportState(year); + }, + ({ state, refresh }, { dispatch }) => { + if (state === 'generating' && refresh) { + window.setTimeout(() => { + void dispatch(fetchReportState()); + }, 1_000 * refresh.retry); + } else if (state === 'available') { + void dispatch(getReport()); + } + + return state; + }, + { useLoadingBar: false }, +); + +// Triggers the generation of the annual report. +export const generateReport = createDataLoadingThunk( + `${annualReportSlice.name}/generateReport`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiRequestGenerateAnnualReport(year); + }, + (_arg: unknown, { dispatch }) => { + void dispatch(fetchReportState()); + }, +); + +export const getReport = createDataLoadingThunk( + `${annualReportSlice.name}/getReport`, + async (_arg: unknown, { getState }) => { + const year = selectWrapstodonYear(getState()); + if (!year) { + throw new Error('Year is not set'); + } + return apiGetAnnualReport(year); + }, + (data) => data.annual_reports[0], +); diff --git a/app/javascript/mastodon/reducers/slices/index.ts b/app/javascript/mastodon/reducers/slices/index.ts new file mode 100644 index 00000000000000..dfea3951275ab2 --- /dev/null +++ b/app/javascript/mastodon/reducers/slices/index.ts @@ -0,0 +1,5 @@ +import { annualReport } from './annual_report'; + +export const sliceReducers = { + annualReport, +}; diff --git a/app/javascript/mastodon/store/typed_functions.ts b/app/javascript/mastodon/store/typed_functions.ts index 5ceb05909f0f0f..79bca08a52f06b 100644 --- a/app/javascript/mastodon/store/typed_functions.ts +++ b/app/javascript/mastodon/store/typed_functions.ts @@ -205,7 +205,7 @@ export function createDataLoadingThunk( thunkOptions?: AppThunkOptions, ): ReturnType>; -// Overload when the `onData` method returns nothing, then the mayload is the `onData` result +// Overload when the `onData` method returns nothing, then the payload is the `onData` result export function createDataLoadingThunk( name: string, loadData: LoadData, From 4e6d1892b9d84e1db6df16c509741982b9020d78 Mon Sep 17 00:00:00 2001 From: David Roetzel Date: Wed, 3 Dec 2025 15:33:27 +0100 Subject: [PATCH 089/123] Fix creation of duplicate conversations (#37108) --- app/models/conversation.rb | 2 +- app/models/status.rb | 10 ++++- spec/models/status_spec.rb | 76 +++++++++++++++++++++----------------- 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 30d6f13eda7c38..d5d3ddbaac3a29 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -17,7 +17,7 @@ class Conversation < ApplicationRecord has_many :statuses, dependent: nil - belongs_to :parent_status, class_name: 'Status', optional: true, inverse_of: :conversation + belongs_to :parent_status, class_name: 'Status', optional: true, inverse_of: :owned_conversation belongs_to :parent_account, class_name: 'Account', optional: true scope :local, -> { where(uri: nil) } diff --git a/app/models/status.rb b/app/models/status.rb index 0bff4f2825df36..59fd518a88d3b8 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -154,6 +154,7 @@ class Status < ApplicationRecord around_create Mastodon::Snowflake::Callbacks after_create :set_poll_id + after_create :update_conversation # The `prepend: true` option below ensures this runs before # the `dependent: destroy` callbacks remove relevant records @@ -448,11 +449,16 @@ def set_conversation self.in_reply_to_account_id = carried_over_reply_to_account_id self.conversation_id = thread.conversation_id if conversation_id.nil? elsif conversation_id.nil? - conversation = build_owned_conversation - self.conversation = conversation + build_conversation end end + def update_conversation + return if reply? + + conversation.update!(parent_status: self, parent_account: account) if conversation && conversation.parent_status.nil? + end + def carried_over_reply_to_account_id if thread.account_id == account_id && thread.reply? thread.in_reply_to_account_id diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index e0a2c3910464a0..1c653ae113d80e 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -512,34 +512,6 @@ end end - describe 'before_validation' do - it 'sets account being replied to correctly over intermediary nodes' do - first_status = Fabricate(:status, account: bob) - intermediary = Fabricate(:status, thread: first_status, account: alice) - final = Fabricate(:status, thread: intermediary, account: alice) - - expect(final.in_reply_to_account_id).to eq bob.id - end - - it 'creates new conversation for stand-alone status' do - expect(described_class.create(account: alice, text: 'First').conversation_id).to_not be_nil - end - - it 'keeps conversation of parent node' do - parent = Fabricate(:status, text: 'First') - expect(described_class.create(account: alice, thread: parent, text: 'Response').conversation_id).to eq parent.conversation_id - end - - it 'sets `local` to true for status by local account' do - expect(described_class.create(account: alice, text: 'foo').local).to be true - end - - it 'sets `local` to false for status by remote account' do - alice.update(domain: 'example.com') - expect(described_class.create(account: alice, text: 'foo').local).to be false - end - end - describe 'Validations' do context 'with a remote account' do subject { Fabricate.build :status, account: remote_account } @@ -588,13 +560,49 @@ end end end - end - describe 'after_create' do - it 'saves ActivityPub uri as uri for local status' do - status = described_class.create(account: alice, text: 'foo') - status.reload - expect(status.uri).to start_with('https://') + describe 'Wiring up replies and conversations' do + it 'sets account being replied to correctly over intermediary nodes' do + first_status = Fabricate(:status, account: bob) + intermediary = Fabricate(:status, thread: first_status, account: alice) + final = Fabricate(:status, thread: intermediary, account: alice) + + expect(final.in_reply_to_account_id).to eq bob.id + end + + it 'creates new conversation for stand-alone status' do + new_status = nil + expect do + new_status = described_class.create(account: alice, text: 'First') + end.to change(Conversation, :count).by(1) + + expect(new_status.conversation_id).to_not be_nil + expect(new_status.conversation.parent_status_id).to eq new_status.id + end + + it 'keeps conversation of parent node' do + parent = Fabricate(:status, text: 'First') + expect(described_class.create(account: alice, thread: parent, text: 'Response').conversation_id).to eq parent.conversation_id + end + end + + describe 'Setting the `local` flag correctly' do + it 'sets `local` to true for status by local account' do + expect(described_class.create(account: alice, text: 'foo').local).to be true + end + + it 'sets `local` to false for status by remote account' do + alice.update(domain: 'example.com') + expect(described_class.create(account: alice, text: 'foo').local).to be false + end + end + + describe 'after_create' do + it 'saves ActivityPub uri as uri for local status' do + status = described_class.create(account: alice, text: 'foo') + status.reload + expect(status.uri).to start_with('https://') + end end end end From b3b5bf26d14f809a562244642949826b6a91bade Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 3 Dec 2025 10:34:10 -0500 Subject: [PATCH 090/123] Remove duplicate `set_locale` around action in auth/registrations (#36455) --- app/controllers/auth/registrations_controller.rb | 2 +- spec/system/auth/registrations_spec.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 780c0be3191cbe..b8c21f3ccd7b51 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -135,7 +135,7 @@ def require_rules_acceptance! @accept_token = session[:accept_token] = SecureRandom.hex @invite_code = invite_code - set_locale { render :rules } + render :rules end def is_flashing_format? # rubocop:disable Naming/PredicatePrefix diff --git a/spec/system/auth/registrations_spec.rb b/spec/system/auth/registrations_spec.rb index 4c08bf47eea3e8..3a103667e6bd1b 100644 --- a/spec/system/auth/registrations_spec.rb +++ b/spec/system/auth/registrations_spec.rb @@ -3,6 +3,17 @@ require 'rails_helper' RSpec.describe 'Auth Registration' do + context 'when there are server rules' do + let!(:rule) { Fabricate :rule, text: 'You must be seven meters tall' } + + it 'shows rules page before proceeding with sign up' do + visit new_user_registration_path + expect(page) + .to have_title(I18n.t('auth.register')) + .and have_content(rule.text) + end + end + context 'when age verification is enabled' do before { Setting.min_age = 16 } From 7c730e9041b5cf1de0741925b7ebbaa43d7d0f22 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 3 Dec 2025 10:39:01 -0500 Subject: [PATCH 091/123] Use `normalizes` API for Tag `display_name` value (#35797) --- app/models/tag.rb | 7 +++---- spec/models/tag_spec.rb | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/models/tag.rb b/app/models/tag.rb index c59b0f36a87646..9924d132e64b25 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -67,6 +67,8 @@ class Tag < ApplicationRecord } scope :matches_name, ->(term) { where(arel_table[:name].lower.matches(arel_table.lower("#{sanitize_sql_like(Tag.normalize(term))}%"), nil, true)) } # Search with case-sensitive to use B-tree index + normalizes :display_name, with: ->(value) { value.gsub(HASHTAG_INVALID_CHARS_RE, '') } + update_index('tags', :self) def to_param @@ -113,10 +115,7 @@ def find_or_create_by_names(name_or_names) names.map do |(normalized_name, display_name)| tag = begin - matching_name(normalized_name).first || create!( - name: normalized_name, - display_name: display_name.gsub(HASHTAG_INVALID_CHARS_RE, '') - ) + matching_name(normalized_name).first || create!(name: normalized_name, display_name:) rescue ActiveRecord::RecordNotUnique find_normalized(normalized_name) end diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index 9a68ae36d63c23..06ed7a42c691a4 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -57,6 +57,11 @@ def previous_name_error_message end end + describe 'Normalizations' do + it { is_expected.to normalize(:display_name).from('#HelloWorld').to('HelloWorld') } + it { is_expected.to normalize(:display_name).from('Hello❤️World').to('HelloWorld') } + end + describe 'HASHTAG_RE' do subject { described_class::HASHTAG_RE } From 498e88f059326c4a84d0bdac64cda6f7daa29ca2 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Wed, 3 Dec 2025 16:43:26 +0100 Subject: [PATCH 092/123] Fix color contrast issues caused by new theme tokens (#37105) --- app/javascript/styles/mastodon-light/css_variables.scss | 1 + app/javascript/styles/mastodon/components.scss | 4 ++-- app/javascript/styles/mastodon/css_variables.scss | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/javascript/styles/mastodon-light/css_variables.scss b/app/javascript/styles/mastodon-light/css_variables.scss index 70745ec071ad8c..a96773f76ca99a 100644 --- a/app/javascript/styles/mastodon-light/css_variables.scss +++ b/app/javascript/styles/mastodon-light/css_variables.scss @@ -39,6 +39,7 @@ var(--color-text-brand) ); --color-text-on-brand-base: var(--color-white); + --color-text-brand-on-inverted: var(--color-indigo-400); --color-text-error: var(--color-red-600); --color-text-on-error-base: var(--color-white); --color-text-warning: var(--color-yellow-600); diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 01de2c7d7ace1e..ed2403b045b8b4 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -10267,7 +10267,7 @@ noscript { background: transparent; text-transform: uppercase; cursor: pointer; - color: var(--color-text-brand); + color: var(--color-text-brand-on-inverted); font-weight: 700; border-radius: 4px; padding: 0 4px; @@ -10751,7 +10751,7 @@ noscript { box-sizing: border-box; font-size: 14px; color: var(--color-text-secondary); - background: var(--color-bg-tertiary); + background: var(--color-bg-secondary); border: 1px solid var(--color-border-primary); border-top: 0; border-radius: 0 0 8px 8px; diff --git a/app/javascript/styles/mastodon/css_variables.scss b/app/javascript/styles/mastodon/css_variables.scss index b270bd337ff321..98b61d2f0b2f9b 100644 --- a/app/javascript/styles/mastodon/css_variables.scss +++ b/app/javascript/styles/mastodon/css_variables.scss @@ -39,6 +39,7 @@ var(--color-text-brand) ); --color-text-on-brand-base: var(--color-white); + --color-text-brand-on-inverted: var(--color-indigo-600); --color-text-error: var(--color-red-500); --color-text-on-error-base: var(--color-white); --color-text-warning: var(--color-yellow-400); @@ -60,7 +61,7 @@ // Neutrals --color-bg-primary: var(--color-grey-950); - --overlay-strength-secondary: 10%; + --overlay-strength-secondary: 8%; --color-bg-secondary-base: var(--color-indigo-200); --color-bg-secondary: #{utils.css-alpha( var(--color-bg-secondary-base), From 31c392b1bc816814d0ba3058ea87eb16bedec321 Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 3 Dec 2025 17:25:36 +0100 Subject: [PATCH 093/123] Wrapstodon modal with new share button (#37109) --- .../features/annual_report/archetype.tsx | 64 ++++++------ .../mastodon/features/annual_report/index.tsx | 98 +++++++++---------- .../features/annual_report/timeline.tsx | 9 +- .../ui/components/annual_report_modal.tsx | 7 +- app/javascript/mastodon/locales/en.json | 1 + .../mastodon/reducers/slices/annual_report.ts | 10 +- 6 files changed, 93 insertions(+), 96 deletions(-) diff --git a/app/javascript/mastodon/features/annual_report/archetype.tsx b/app/javascript/mastodon/features/annual_report/archetype.tsx index fffbc1803ecbe0..0c416c30c290d5 100644 --- a/app/javascript/mastodon/features/annual_report/archetype.tsx +++ b/app/javascript/mastodon/features/annual_report/archetype.tsx @@ -1,68 +1,64 @@ -import { FormattedMessage } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; import booster from '@/images/archetypes/booster.png'; import lurker from '@/images/archetypes/lurker.png'; import oracle from '@/images/archetypes/oracle.png'; import pollster from '@/images/archetypes/pollster.png'; import replier from '@/images/archetypes/replier.png'; -import type { Archetype as ArchetypeData } from 'mastodon/models/annual_report'; +import type { Archetype as ArchetypeData } from '@/mastodon/models/annual_report'; + +export const archetypeNames = defineMessages({ + booster: { + id: 'annual_report.summary.archetype.booster', + defaultMessage: 'The cool-hunter', + }, + replier: { + id: 'annual_report.summary.archetype.replier', + defaultMessage: 'The social butterfly', + }, + pollster: { + id: 'annual_report.summary.archetype.pollster', + defaultMessage: 'The pollster', + }, + lurker: { + id: 'annual_report.summary.archetype.lurker', + defaultMessage: 'The lurker', + }, + oracle: { + id: 'annual_report.summary.archetype.oracle', + defaultMessage: 'The oracle', + }, +}); export const Archetype: React.FC<{ data: ArchetypeData; }> = ({ data }) => { - let illustration, label; + const intl = useIntl(); + let illustration; switch (data) { case 'booster': illustration = booster; - label = ( - - ); break; case 'replier': illustration = replier; - label = ( - - ); break; case 'pollster': illustration = pollster; - label = ( - - ); break; case 'lurker': illustration = lurker; - label = ( - - ); break; case 'oracle': illustration = oracle; - label = ( - - ); break; } return (
-
{label}
+
+ {intl.formatMessage(archetypeNames[data])} +
); diff --git a/app/javascript/mastodon/features/annual_report/index.tsx b/app/javascript/mastodon/features/annual_report/index.tsx index 9c2c9d71c02190..e9f0b5f2d7e9e7 100644 --- a/app/javascript/mastodon/features/annual_report/index.tsx +++ b/app/javascript/mastodon/features/annual_report/index.tsx @@ -1,68 +1,38 @@ -import { useState, useEffect } from 'react'; +import { useCallback } from 'react'; +import type { FC } from 'react'; -import { FormattedMessage } from 'react-intl'; +import { defineMessage, FormattedMessage, useIntl } from 'react-intl'; -import { - importFetchedStatuses, - importFetchedAccounts, -} from 'mastodon/actions/importer'; -import { apiRequestGet, apiRequestPost } from 'mastodon/api'; -import { LoadingIndicator } from 'mastodon/components/loading_indicator'; -import { me } from 'mastodon/initial_state'; -import type { Account } from 'mastodon/models/account'; -import type { AnnualReport as AnnualReportData } from 'mastodon/models/annual_report'; -import type { Status } from 'mastodon/models/status'; -import { useAppSelector, useAppDispatch } from 'mastodon/store'; +import { focusCompose, resetCompose } from '@/mastodon/actions/compose'; +import { closeModal } from '@/mastodon/actions/modal'; +import { Button } from '@/mastodon/components/button'; +import { LoadingIndicator } from '@/mastodon/components/loading_indicator'; +import { me } from '@/mastodon/initial_state'; +import type { AnnualReport as AnnualReportData } from '@/mastodon/models/annual_report'; +import { useAppDispatch, useAppSelector } from '@/mastodon/store'; -import { Archetype } from './archetype'; +import { Archetype, archetypeNames } from './archetype'; import { Followers } from './followers'; import { HighlightedPost } from './highlighted_post'; import { MostUsedHashtag } from './most_used_hashtag'; import { NewPosts } from './new_posts'; -import { Percentile } from './percentile'; -interface AnnualReportResponse { - annual_reports: AnnualReportData[]; - accounts: Account[]; - statuses: Status[]; -} +const shareMessage = defineMessage({ + id: 'annual_report.summary.share_message', + defaultMessage: 'I got the {archetype} archetype!', +}); -export const AnnualReport: React.FC<{ - year: string; -}> = ({ year }) => { - const [response, setResponse] = useState(null); - const [loading, setLoading] = useState(true); +// Share = false when using the embedded version of the report. +export const AnnualReport: FC<{ share?: boolean }> = ({ share = true }) => { const currentAccount = useAppSelector((state) => me ? state.accounts.get(me) : undefined, ); - const dispatch = useAppDispatch(); - - useEffect(() => { - apiRequestGet(`v1/annual_reports/${year}`) - .then((data) => { - dispatch(importFetchedStatuses(data.statuses)); - dispatch(importFetchedAccounts(data.accounts)); - - setResponse(data); - setLoading(false); + const report = useAppSelector((state) => state.annualReport.report); - return apiRequestPost(`v1/annual_reports/${year}/read`); - }) - .catch(() => { - setLoading(false); - }); - }, [dispatch, year, setResponse, setLoading]); - - if (loading) { + if (!report) { return ; } - const report = response?.annual_reports[0]; - - if (!report || report.schema_version !== 1) { - return null; - } - return (
@@ -89,9 +59,37 @@ export const AnnualReport: React.FC<{ total={currentAccount?.followers_count} /> - + {share && }
); }; + +const ShareButton: FC<{ report: AnnualReportData }> = ({ report }) => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + const handleShareClick = useCallback(() => { + // Generate the share message. + const archetypeName = intl.formatMessage( + archetypeNames[report.data.archetype], + ); + const shareLines = [ + intl.formatMessage(shareMessage, { + archetype: archetypeName, + }), + ]; + // Share URL is only available for schema version 2. + if (report.schema_version === 2 && report.share_url) { + shareLines.push(report.share_url); + } + shareLines.push(`#Wrapstodon${report.year}`); + + // Reset the composer and focus it with the share message, then close the modal. + dispatch(resetCompose()); + dispatch(focusCompose(shareLines.join('\n\n'))); + dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false })); + }, [report, intl, dispatch]); + + return